• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jcubic / jquery.terminal / 25376898073

05 May 2026 12:39PM UTC coverage: 82.944% (-0.3%) from 83.197%
25376898073

push

github

jcubic
update contributors

4912 of 6133 branches covered (80.09%)

5641 of 6801 relevant lines covered (82.94%)

15689.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.64
/js/pipe.js
1
/**@license
2
 *       __ _____                     ________                              __
3
 *      / // _  /__ __ _____ ___ __ _/__  ___/__ ___ ______ __ __  __ ___  / /
4
 *  __ / // // // // // _  // _// // / / // _  // _//     // //  \/ // _ \/ /
5
 * /  / // // // // // ___// / / // / / // ___// / / / / // // /\  // // / /__
6
 * \___//____ \\___//____//_/ _\_  / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/
7
 *           \/              /____/
8
 * http://terminal.jcubic.pl
9
 *
10
 * This is object enchancment that will add pipe operator and redirects to commands
11
 *
12
 * Copyright (c) 2014-2026 Jakub Jankiewicz <https://jcubic.pl/me>
13
 * Released under the MIT license
14
 *
15
 */
16
/* global define */
17
(function(factory) {
2✔
18
    var root;
19
    if (typeof window !== 'undefined') {
2!
20
        root = window;
2✔
21
    } else if (typeof self !== 'undefined') {
×
22
        root = self;
×
23
    } else if (typeof global !== 'undefined') {
×
24
        root = global;
×
25
    } else {
26
        throw new Error('Unknow context');
×
27
    }
28
    if (typeof define === 'function' && define.amd) {
2!
29
        // AMD. Register as an anonymous module.
30
        // istanbul ignore next
31
        define(['jquery', 'jquery.terminal'], factory);
32
    } else if (typeof module === 'object' && module.exports) {
2!
33
        // Node/CommonJS
34
        module.exports = function(root, jQuery) {
2✔
35
            if (jQuery === undefined) {
2!
36
                // require('jQuery') returns a factory that requires window to
37
                // build a jQuery instance, we normalize how we use modules
38
                // that require this pattern but the window provided is a noop
39
                // if it's defined (how jquery works)
40
                if (typeof window !== 'undefined') {
2!
41
                    jQuery = require('jquery');
2✔
42
                } else {
43
                    jQuery = require('jquery')(root);
×
44
                }
45
            }
46
            if (!jQuery.fn.terminal) {
2!
47
                if (typeof window !== 'undefined') {
×
48
                    require('jquery.terminal');
×
49
                } else {
50
                    require('jquery.terminal')(jQuery);
×
51
                }
52
            }
53
            factory(jQuery);
2✔
54
            return jQuery;
2✔
55
        };
56
    } else {
57
        // Browser
58
        // istanbul ignore next
59
        factory(root.jQuery);
60
    }
61
})(function($) {
62
    var DEBUG = false;
2✔
63
    function log(str) {
64
        if (DEBUG) {
74!
65
            // eslint-disable-next-line
66
            console.log(str);
×
67
        }
68
    }
69
    // -----------------------------------------------------------------------------------
70
    // :: split over array - returns array of arrays
71
    // -----------------------------------------------------------------------------------
72
    function array_split(array, splitter) {
73
        var test_fn;
74
        if (typeof splitter === 'function') {
68✔
75
            test_fn = splitter;
50✔
76
        } else if (splitter instanceof RegExp) {
18!
77
            test_fn = function(item) {
18✔
78
                if (item instanceof RegExp) {
66✔
79
                    item = item.source;
6✔
80
                } else if (typeof item !== 'string') {
60✔
81
                    item = item.toString();
2✔
82
                }
83
                var m = item.match(splitter);
66✔
84
                if (m) {
66✔
85
                    if (m.length > 1) {
14!
86
                        return m.slice(1);
14✔
87
                    }
88
                    return true;
×
89
                }
90
            };
91
        } else {
92
            test_fn = function(item) {
×
93
                return splitter === item;
×
94
            };
95
        }
96
        var output = [];
68✔
97
        var sub = [];
68✔
98
        array.forEach(function(item, i) {
68✔
99
            var check = test_fn(item, i);
262✔
100
            if (check) {
262✔
101
                output.push(sub);
48✔
102
                sub = [];
48✔
103
                if (check instanceof Array) {
48✔
104
                    output.push(check);
14✔
105
                }
106
            } else {
107
                sub.push(item);
214✔
108
            }
109
        });
110
        output.push(sub);
68✔
111
        return output;
68✔
112
    }
113
    // -----------------------------------------------------------------------------------
114
    function is_function(obj) {
115
        return typeof obj === 'function';
212✔
116
    }
117
    // -----------------------------------------------------------------------------------
118
    $.terminal.defaults.strings.pipeNestedInterpreterError = 'You can\'t pipe nested ' +
2✔
119
        'interpreter';
120
    // -----------------------------------------------------------------------------------
121
    $.terminal.pipe = function pipe(interpreter, options) {
2✔
122
        var settings = $.extend({
26✔
123
            processArguments: true,
124
            overwrite: undefined,
125
            redirects: []
126
        }, options);
127
        var overwrite_buffer;
128
        var term;
129
        var orig;
130
        var command_index;
131
        var process_redirect = false;
26✔
132
        // -------------------------------------------------------------------------------
133
        function parse_redirect(args, re) {
134
            var redirects = [];
14✔
135
            if (args.length) {
14!
136
                var cmd_redirect, settings_redirect;
137
                for (var i = 0; i < args.length; ++i) {
14✔
138
                    var operator = null;
28✔
139
                    if (args[i].length === 1 && args[i][0].match(re)) {
28✔
140
                        operator = args[i][0];
14✔
141
                    }
142
                    if (operator) {
28✔
143
                        for (var j = settings.redirects.length; j--;) {
14✔
144
                            if (operator === settings.redirects[j].name) {
20✔
145
                                settings_redirect = settings.redirects[j];
14✔
146
                                break;
14✔
147
                            }
148
                        }
149
                        cmd_redirect = {
14✔
150
                            fn: settings_redirect.callback,
151
                            args: []
152
                        };
153
                        redirects.push(cmd_redirect);
14✔
154
                    } else {
155
                        cmd_redirect.args = args[i];
14✔
156
                    }
157
                }
158
            }
159
            return redirects;
14✔
160
        }
161
        // -------------------------------------------------------------------------------
162
        function is_pipe(cmd) {
163
            var quotes = [''].concat(cmd.args_quotes);
50✔
164
            return function(string, i) {
50✔
165
                return string === '|' && !quotes[i];
196✔
166
            };
167
        }
168
        // -------------------------------------------------------------------------------
169
        function tokens(cmd) {
170
            return [cmd.name].concat(cmd.args);
50✔
171
        }
172
        // -------------------------------------------------------------------------------
173
        function parse_command(command) {
174
            var cmd;
175
            if (term.settings().processArguments) {
50✔
176
                cmd = $.terminal.parse_command(command);
48✔
177
            } else {
178
                cmd = $.terminal.split_command(command);
2✔
179
            }
180
            var index = 1;
50✔
181
            return array_split(tokens(cmd), is_pipe(cmd)).map(function(args, i) {
50✔
182
                var result = {
84✔
183
                    redirects: []
184
                };
185
                var len = args.length;
84✔
186
                if (settings.redirects.length) {
84✔
187
                    var redirect_names = settings.redirects.map(function(redirect) {
18✔
188
                        return $.terminal.escape_regex(redirect.name);
34✔
189
                    });
190
                    var re = new RegExp('^(' + redirect_names.join('|') + ')$');
18✔
191
                    var split_args = array_split(args, re);
18✔
192
                    if (split_args.length > 1) {
18✔
193
                        args = split_args[0];
14✔
194
                        result.redirects = parse_redirect(split_args.slice(1), re);
14✔
195
                    }
196
                }
197
                if (i === 0) {
84✔
198
                    result.args_quotes = args.map(function(_, i) {
50✔
199
                        return cmd.args_quotes[i];
68✔
200
                    });
201
                } else {
202
                    result.args_quotes = args.map(function(_, i) {
34✔
203
                        return cmd.args_quotes[i + index];
52✔
204
                    }).slice(1);
205
                }
206
                index += len;
84✔
207
                result.name = args[0];
84✔
208
                result.args = args.slice(1);
84✔
209
                return result;
84✔
210
            });
211
        }
212
        // -------------------------------------------------------------------------------
213
        function overwrite(command) {
214
            return command.overwrite === true ||
70!
215
                typeof command.overwrite === 'undefined' ||
216
                settings.overwrite === true;
217
        }
218
        // -------------------------------------------------------------------------------
219
        function redirects(command) {
220
            var defer = $.Deferred();
70✔
221
            if (overwrite(command)) {
70!
222
                overwrite_buffer = true;
70✔
223
            }
224
            function resolve() {
225
                overwrite_buffer = false;
70✔
226
                defer.resolve();
70✔
227
            }
228
            if (command.redirects.length) {
70✔
229
                var i = 0;
14✔
230
                (function loop() {
14✔
231
                    var redirect = command.redirects[i++];
28✔
232
                    if (redirect) {
28✔
233
                        var fn = redirect.fn;
14✔
234
                        var args = redirect.args;
14✔
235
                        continuation(fn.apply(term, args), function(value) {
14✔
236
                            echo_non_empty(value);
14✔
237
                            loop();
14✔
238
                        }, 'redirect loop');
239
                    } else {
240
                        resolve();
14✔
241
                    }
242
                })();
243
            } else {
244
                resolve();
56✔
245
            }
246
            return defer.promise();
70✔
247
        }
248
        // -------------------------------------------------------------------------------
249
        function continuation(promise, callback, debug_log) {
250
            if (debug_log) {
134✔
251
                log(debug_log);
74✔
252
            }
253
            if (promise && promise.then) {
134✔
254
                promise.then(callback);
108✔
255
                return promise;
108✔
256
            } else {
257
                callback();
26✔
258
            }
259
        }
260
        // -------------------------------------------------------------------------------
261
        function echo_non_empty(value) {
262
            if (typeof value !== 'undefined') {
74✔
263
                term.echo(value);
10✔
264
            }
265
        }
266
        // -------------------------------------------------------------------------------
267
        function single_command(commands) {
268
            return commands.length === 1 && !commands[0].redirects.length;
50✔
269
        }
270
        // -------------------------------------------------------------------------------
271
        function make_tty() {
272
            var tty = {
50✔
273
                buffer: [],
274
                read: function(message, callback) {
275
                    // in case we return read() call from interpreter
276
                    // we can't access force_awake flag in term (it's invoked later)
277
                    if (term.paused()) {
42✔
278
                        term.resume();
28✔
279
                    }
280
                    if ((command_index === 0 && !tty.buffer.length) || process_redirect) {
42✔
281
                        term.push = orig.push;
4✔
282
                        term.echo = orig.echo;
4✔
283
                        var ret = orig.read.apply(term, arguments);
4✔
284
                        return ret.then(function(value) {
4✔
285
                            term.push = tty.push;
4✔
286
                            term.echo = tty.echo;
4✔
287
                            return value;
4✔
288
                        });
289
                    } else {
290
                        var text;
291
                        if (tty.buffer.length) {
38✔
292
                            text = tty.buffer.join('\n');
36✔
293
                            tty.buffer.length = 0;
36✔
294
                        }
295
                        var d = new $.Deferred();
38✔
296
                        if (is_function(callback)) {
38!
297
                            callback(text);
×
298
                        }
299
                        d.resolve(text);
38✔
300
                        return d.promise();
38✔
301
                    }
302
                },
303
                echo: function(string, options) {
304
                    if (overwrite_buffer) {
68✔
305
                        overwrite_buffer = false;
14✔
306
                        tty.buffer.length = 0;
14✔
307
                    }
308
                    tty.options = tty.options || [];
68✔
309
                    tty.options.push(options);
68✔
310
                    tty.buffer.push(string);
68✔
311
                },
312
                last_index: function() {
313
                    return orig.last_index() + tty.buffer.length;
×
314
                },
315
                push: function() {
316
                    this.error(strings(this).pipeNestedInterpreterError);
2✔
317
                }
318
            };
319
            return tty;
50✔
320
        }
321
        // -------------------------------------------------------------------------------
322
        function strings(term) {
323
            var term_settings = term.settings();
10✔
324
            return $.extend(
10✔
325
                {},
326
                $.terminal.defaults.strings,
327
                term_settings && term_settings.strings || {}
10!
328
            );
329
        }
330
        // -------------------------------------------------------------------------------
331
        function error(message) {
332
            var echo = term.echo;
8✔
333
            term.echo = orig.echo;
8✔
334
            term.error(message);
8✔
335
            term.echo = echo;
8✔
336
        }
337
        // -------------------------------------------------------------------------------
338
        function unparse(string) {
339
            if (term.settings().processArguments) {
2!
340
                return string.replace(/(['"()])/g, '\\$1');
2✔
341
            } else {
342
                return string.replace(/ /g, '\\ ');
×
343
            }
344
        }
345
        // -------------------------------------------------------------------------------
346
        function stringify(cmd) {
347
            if (!cmd.name) {
4!
348
                return '';
×
349
            }
350
            return cmd.name + ' ' + cmd.args.map(function(arg, i) {
4✔
351
                if (cmd.args_quotes[i]) {
2!
352
                    var quote = cmd.args_quotes[i];
2✔
353
                    var result = unparse(arg);
2✔
354
                    if (quote === '"' && result.match(/\\'/)) {
2!
355
                        result = result.replace(/\\'/g, "'");
×
356
                    }
357
                    return quote + result + quote;
2✔
358
                }
359
                if (typeof arg === 'string') {
×
360
                    return arg.replace(/ /g, '\\ ');
×
361
                }
362
                return arg;
×
363
            }).join(' ');
364
        }
365
        // -------------------------------------------------------------------------------
366
        return function(command, t) {
26✔
367
            command_index = -1;
50✔
368
            term = t; // for echo_non_empty and function that call it
50✔
369
            var term_settings = term.settings();
50✔
370
            if (!orig) {
50✔
371
                orig = {
26✔
372
                    echo: term.echo,
373
                    read: term.read,
374
                    push: term.push,
375
                    last_index: term.last_index
376
                };
377
            }
378
            var tty = make_tty();
50✔
379
            var commands = parse_command(command);
50✔
380
            function loop(callback) {
381
                var i = 0;
36✔
382
                var d = new $.Deferred();
36✔
383
                return (function inner() {
36✔
384
                    var cmd = commands[i++];
106✔
385
                    if (cmd) {
106✔
386
                        process_redirect = true;
70✔
387
                        redirects(cmd).then(function() {
70✔
388
                            process_redirect = false;
70✔
389
                            if (!commands[i]) {
70✔
390
                                $.extend(term, {echo: orig.echo, push: orig.push});
36✔
391
                            }
392
                            command_index++;
70✔
393
                            var ret = callback(cmd);
70✔
394
                            if (ret === false) {
70✔
395
                                return inner();
10✔
396
                            } else {
397
                                return continuation(ret, inner, 'inner');
60✔
398
                            }
399
                        });
400
                    } else {
401
                        d.resolve();
36✔
402
                    }
403
                    return d.promise();
106✔
404
                })();
405
            }
406
            if (single_command(commands)) {
50✔
407
                var cmd = commands[0];
14✔
408
                if (is_function(interpreter)) {
14✔
409
                    return interpreter.call(term, command, term);
4✔
410
                } else if (is_function(interpreter[cmd.name])) {
10✔
411
                    return interpreter[cmd.name].apply(term, cmd.args);
4✔
412
                } else if ($.isPlainObject(interpreter[cmd.name])) {
6✔
413
                    term.push(interpreter[cmd.name], {
2✔
414
                        prompt: cmd.name + '> ',
415
                        name: cmd.name,
416
                        completion: Object.keys(interpreter[cmd.name])
417
                    });
418
                } else if (cmd.name) {
4!
419
                    if (is_function(term_settings.onCommandNotFound)) {
4✔
420
                        term_settings.onCommandNotFound.call(term, command, term);
2✔
421
                    } else {
422
                        error(sprintf(strings(term).commandNotFound, cmd.name));
2✔
423
                    }
424
                }
425
            } else {
426
                //term = term.duplicate();
427
                $.extend(term, tty);
36✔
428
                var stop_error = false;
36✔
429
                var promise = loop(function(cmd) {
36✔
430
                    var ret;
431
                    if (is_function(interpreter)) {
70✔
432
                        // this branch will be always invoked with new API
433
                        // using pipe option
434
                        var str = stringify(cmd);
4✔
435
                        // this resume and exec is needed for rpc
436
                        // because it use pause/resume and not promises
437
                        // TODO: refactor RPC in terminal
438
                        term.resume();
4✔
439
                        ret = term.exec(str, true);
4✔
440
                        return continuation(ret, echo_non_empty);
4✔
441
                    } else {
442
                        var inter = interpreter[cmd.name];
66✔
443
                        if (is_function(inter)) {
66✔
444
                            ret = inter.apply(term, cmd.args);
56✔
445
                            return continuation(ret, echo_non_empty);
56✔
446
                        } else {
447
                            if ($.isPlainObject(inter)) {
10✔
448
                                error(strings(term).pipeNestedInterpreterError);
2✔
449
                            } else if (is_function(term_settings.onCommandNotFound)) {
8✔
450
                                if (!stop_error) {
4✔
451
                                    term_settings.onCommandNotFound.call(
2✔
452
                                        term,
453
                                        command,
454
                                        term
455
                                    );
456
                                    stop_error = true;
2✔
457
                                }
458
                            } else {
459
                                error(sprintf(strings(term).commandNotFound, cmd.name));
4✔
460
                            }
461
                            return false;
10✔
462
                        }
463
                    }
464
                });
465
                return promise.then(function() {
36✔
466
                    $.extend(term, orig);
36✔
467
                    var orig_echo = orig.echo;
36✔
468
                    term.echo = function(arg, options, deferred) {
36✔
469
                        try {
14✔
470
                            var settings = options;
14✔
471
                            if (tty.options && tty.options.length) {
14✔
472
                                var onClear = function() {
10✔
473
                                    if (settings && settings.onClear) {
×
474
                                        settings.onClear();
×
475
                                    }
476
                                    tty.options.forEach(function(options) {
×
477
                                        if (options && options.onClear) {
×
478
                                            options.onClear();
×
479
                                        }
480
                                    });
481
                                };
482
                                options = $.extend({onClear: onClear}, options);
10✔
483
                            }
484
                            var ret = orig.echo(arg, options, deferred);
14✔
485
                            term.echo = orig_echo;
14✔
486
                            return ret;
14✔
487
                        } catch (e) {
488
                            /* eslint-disable no-console */
489
                            console.error(e.message);
×
490
                            console.error(e.stack);
×
491
                            /* eslint-enable no-console */
492
                            return term;
×
493
                        }
494
                    };
495
                });
496
            }
497
        };
498
    };
499
    // -------------------------------------------------------------------------
500
    var init = $.fn.terminal;
2✔
501
    $.fn.terminal = function(interpreter, options) {
2✔
502
        if (options && options.pipe) {
632✔
503
            var settings = $.extend({}, options, {
2✔
504
                onInit: function() {
505
                    var defer = $.Deferred();
2✔
506
                    if (options && options.pipe) {
2!
507
                        var interpreter = $.terminal.pipe(this.commands(), options);
2✔
508
                        this.set_interpreter(interpreter).then(defer.resolve);
2✔
509
                    } else {
510
                        defer.resolve();
×
511
                    }
512
                    if (options && is_function(options.onInit)) {
2!
513
                        var self = this;
×
514
                        defer.then(function() {
×
515
                            options.onInit.call(self, self);
×
516
                        });
517
                    }
518
                }
519
            });
520
        }
521
        return init.call(this, interpreter, settings || options);
632✔
522
    };
523
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc