source: contrib/MailArchiver/sources/src/VAADIN/widgetsets/serpro.mailarchiver.config.MailArchiverWidgetSet/ace/cockpit-uncompressed.js @ 6785

Revision 6785, 110.9 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Mozilla Skywriter.
15 *
16 * The Initial Developer of the Original Code is
17 * Mozilla.
18 * Portions created by the Initial Developer are Copyright (C) 2009
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 *      Kevin Dangoor (kdangoor@mozilla.com)
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38define('cockpit/index', ['require', 'exports', 'module' , 'pilot/index', 'cockpit/cli', 'cockpit/ui/settings', 'cockpit/ui/cli_view', 'cockpit/commands/basic'], function(require, exports, module) {
39
40
41exports.startup = function(data, reason) {
42  require('pilot/index');
43  require('cockpit/cli').startup(data, reason);
44  // window.testCli = require('cockpit/test/testCli');
45
46  require('cockpit/ui/settings').startup(data, reason);
47  require('cockpit/ui/cli_view').startup(data, reason);
48  require('cockpit/commands/basic').startup(data, reason);
49};
50
51/*
52exports.shutdown(data, reason) {
53};
54*/
55
56
57});
58/* ***** BEGIN LICENSE BLOCK *****
59 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
60 *
61 * The contents of this file are subject to the Mozilla Public License Version
62 * 1.1 (the "License"); you may not use this file except in compliance with
63 * the License. You may obtain a copy of the License at
64 * http://www.mozilla.org/MPL/
65 *
66 * Software distributed under the License is distributed on an "AS IS" basis,
67 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
68 * for the specific language governing rights and limitations under the
69 * License.
70 *
71 * The Original Code is Skywriter.
72 *
73 * The Initial Developer of the Original Code is
74 * Mozilla.
75 * Portions created by the Initial Developer are Copyright (C) 2009
76 * the Initial Developer. All Rights Reserved.
77 *
78 * Contributor(s):
79 *   Joe Walker (jwalker@mozilla.com)
80 *
81 * Alternatively, the contents of this file may be used under the terms of
82 * either the GNU General Public License Version 2 or later (the "GPL"), or
83 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
84 * in which case the provisions of the GPL or the LGPL are applicable instead
85 * of those above. If you wish to allow use of your version of this file only
86 * under the terms of either the GPL or the LGPL, and not to allow others to
87 * use your version of this file under the terms of the MPL, indicate your
88 * decision by deleting the provisions above and replace them with the notice
89 * and other provisions required by the GPL or the LGPL. If you do not delete
90 * the provisions above, a recipient may use your version of this file under
91 * the terms of any one of the MPL, the GPL or the LGPL.
92 *
93 * ***** END LICENSE BLOCK ***** */
94
95define('cockpit/cli', ['require', 'exports', 'module' , 'pilot/console', 'pilot/lang', 'pilot/oop', 'pilot/event_emitter', 'pilot/types', 'pilot/canon'], function(require, exports, module) {
96
97
98var console = require('pilot/console');
99var lang = require('pilot/lang');
100var oop = require('pilot/oop');
101var EventEmitter = require('pilot/event_emitter').EventEmitter;
102
103//var keyboard = require('keyboard/keyboard');
104var types = require('pilot/types');
105var Status = require('pilot/types').Status;
106var Conversion = require('pilot/types').Conversion;
107var canon = require('pilot/canon');
108
109/**
110 * Normally type upgrade is done when the owning command is registered, but
111 * out commandParam isn't part of a command, so it misses out.
112 */
113exports.startup = function(data, reason) {
114    canon.upgradeType('command', commandParam);
115};
116
117/**
118 * The information required to tell the user there is a problem with their
119 * input.
120 * TODO: There a several places where {start,end} crop up. Perhaps we should
121 * have a Cursor object.
122 */
123function Hint(status, message, start, end, predictions) {
124    this.status = status;
125    this.message = message;
126
127    if (typeof start === 'number') {
128        this.start = start;
129        this.end = end;
130        this.predictions = predictions;
131    }
132    else {
133        var arg = start;
134        this.start = arg.start;
135        this.end = arg.end;
136        this.predictions = arg.predictions;
137    }
138}
139Hint.prototype = {
140};
141/**
142 * Loop over the array of hints finding the one we should display.
143 * @param hints array of hints
144 */
145Hint.sort = function(hints, cursor) {
146    // Calculate 'distance from cursor'
147    if (cursor !== undefined) {
148        hints.forEach(function(hint) {
149            if (hint.start === Argument.AT_CURSOR) {
150                hint.distance = 0;
151            }
152            else if (cursor < hint.start) {
153                hint.distance = hint.start - cursor;
154            }
155            else if (cursor > hint.end) {
156                hint.distance = cursor - hint.end;
157            }
158            else {
159                hint.distance = 0;
160            }
161        }, this);
162    }
163    // Sort
164    hints.sort(function(hint1, hint2) {
165        // Compare first based on distance from cursor
166        if (cursor !== undefined) {
167            var diff = hint1.distance - hint2.distance;
168            if (diff != 0) {
169                return diff;
170            }
171        }
172        // otherwise go with hint severity
173        return hint2.status - hint1.status;
174    });
175    // tidy-up
176    if (cursor !== undefined) {
177        hints.forEach(function(hint) {
178            delete hint.distance;
179        }, this);
180    }
181    return hints;
182};
183exports.Hint = Hint;
184
185/**
186 * A Hint that arose as a result of a Conversion
187 */
188function ConversionHint(conversion, arg) {
189    this.status = conversion.status;
190    this.message = conversion.message;
191    if (arg) {
192        this.start = arg.start;
193        this.end = arg.end;
194    }
195    else {
196        this.start = 0;
197        this.end = 0;
198    }
199    this.predictions = conversion.predictions;
200};
201oop.inherits(ConversionHint, Hint);
202
203
204/**
205 * We record where in the input string an argument comes so we can report errors
206 * against those string positions.
207 * We publish a 'change' event when-ever the text changes
208 * @param emitter Arguments use something else to pass on change events.
209 * Currently this will be the creating Requisition. This prevents dependency
210 * loops and prevents us from needing to merge listener lists.
211 * @param text The string (trimmed) that contains the argument
212 * @param start The position of the text in the original input string
213 * @param end See start
214 * @param prefix Knowledge of quotation marks and whitespace used prior to the
215 * text in the input string allows us to re-generate the original input from
216 * the arguments.
217 * @param suffix Any quotation marks and whitespace used after the text.
218 * Whitespace is normally placed in the prefix to the succeeding argument, but
219 * can be used here when this is the last argument.
220 * @constructor
221 */
222function Argument(emitter, text, start, end, prefix, suffix) {
223    this.emitter = emitter;
224    this.setText(text);
225    this.start = start;
226    this.end = end;
227    this.prefix = prefix;
228    this.suffix = suffix;
229}
230Argument.prototype = {
231    /**
232     * Return the result of merging these arguments.
233     * TODO: What happens when we're merging arguments for the single string
234     * case and some of the arguments are in quotation marks?
235     */
236    merge: function(following) {
237        if (following.emitter != this.emitter) {
238            throw new Error('Can\'t merge Arguments from different EventEmitters');
239        }
240        return new Argument(
241            this.emitter,
242            this.text + this.suffix + following.prefix + following.text,
243            this.start, following.end,
244            this.prefix,
245            following.suffix);
246    },
247
248    /**
249     * See notes on events in Assignment. We might need to hook changes here
250     * into a CliRequisition so they appear of the command line.
251     */
252    setText: function(text) {
253        if (text == null) {
254            throw new Error('Illegal text for Argument: ' + text);
255        }
256        var ev = { argument: this, oldText: this.text, text: text };
257        this.text = text;
258        this.emitter._dispatchEvent('argumentChange', ev);
259    },
260
261    /**
262     * Helper when we're putting arguments back together
263     */
264    toString: function() {
265        // TODO: There is a bug here - we should re-escape escaped characters
266        // But can we do that reliably?
267        return this.prefix + this.text + this.suffix;
268    }
269};
270
271/**
272 * Merge an array of arguments into a single argument.
273 * All Arguments in the array are expected to have the same emitter
274 */
275Argument.merge = function(argArray, start, end) {
276    start = (start === undefined) ? 0 : start;
277    end = (end === undefined) ? argArray.length : end;
278
279    var joined;
280    for (var i = start; i < end; i++) {
281        var arg = argArray[i];
282        if (!joined) {
283            joined = arg;
284        }
285        else {
286            joined = joined.merge(arg);
287        }
288    }
289    return joined;
290};
291
292/**
293 * We sometimes need a way to say 'this error occurs where ever the cursor is'
294 */
295Argument.AT_CURSOR = -1;
296
297
298/**
299 * A link between a parameter and the data for that parameter.
300 * The data for the parameter is available as in the preferred type and as
301 * an Argument for the CLI.
302 * <p>We also record validity information where applicable.
303 * <p>For values, null and undefined have distinct definitions. null means
304 * that a value has been provided, undefined means that it has not.
305 * Thus, null is a valid default value, and common because it identifies an
306 * parameter that is optional. undefined means there is no value from
307 * the command line.
308 * @constructor
309 */
310function Assignment(param, requisition) {
311    this.param = param;
312    this.requisition = requisition;
313    this.setValue(param.defaultValue);
314};
315Assignment.prototype = {
316    /**
317     * The parameter that we are assigning to
318     * @readonly
319     */
320    param: undefined,
321
322    /**
323     * Report on the status of the last parse() conversion.
324     * @see types.Conversion
325     */
326    conversion: undefined,
327
328    /**
329     * The current value in a type as specified by param.type
330     */
331    value: undefined,
332
333    /**
334     * The string version of the current value
335     */
336    arg: undefined,
337
338    /**
339     * The current value (i.e. not the string representation)
340     * Use setValue() to mutate
341     */
342    value: undefined,
343    setValue: function(value) {
344        if (this.value === value) {
345            return;
346        }
347
348        if (value === undefined) {
349            this.value = this.param.defaultValue;
350            this.conversion = this.param.getDefault ?
351                    this.param.getDefault() :
352                    this.param.type.getDefault();
353            this.arg = undefined;
354        } else {
355            this.value = value;
356            this.conversion = undefined;
357            var text = (value == null) ? '' : this.param.type.stringify(value);
358            if (this.arg) {
359                this.arg.setText(text);
360            }
361        }
362
363        this.requisition._assignmentChanged(this);
364    },
365
366    /**
367     * The textual representation of the current value
368     * Use setValue() to mutate
369     */
370    arg: undefined,
371    setArgument: function(arg) {
372        if (this.arg === arg) {
373            return;
374        }
375        this.arg = arg;
376        this.conversion = this.param.type.parse(arg.text);
377        this.conversion.arg = arg; // TODO: make this automatic?
378        this.value = this.conversion.value;
379        this.requisition._assignmentChanged(this);
380    },
381
382    /**
383     * Create a list of the hints associated with this parameter assignment.
384     * Generally there will be only one hint generated because we're currently
385     * only displaying one hint at a time, ordering by distance from cursor
386     * and severity. Since distance from cursor will be the same for all hints
387     * from this assignment all but the most severe will ever be used. It might
388     * make sense with more experience to alter this to function to be getHint()
389     */
390    getHint: function() {
391        // Allow the parameter to provide documentation
392        if (this.param.getCustomHint && this.value && this.arg) {
393            var hint = this.param.getCustomHint(this.value, this.arg);
394            if (hint) {
395                return hint;
396            }
397        }
398
399        // If there is no argument, use the cursor position
400        var message = '<strong>' + this.param.name + '</strong>: ';
401        if (this.param.description) {
402            // TODO: This should be a short description - do we need to trim?
403            message += this.param.description.trim();
404
405            // Ensure the help text ends with '. '
406            if (message.charAt(message.length - 1) !== '.') {
407                message += '.';
408            }
409            if (message.charAt(message.length - 1) !== ' ') {
410                message += ' ';
411            }
412        }
413        var status = Status.VALID;
414        var start = this.arg ? this.arg.start : Argument.AT_CURSOR;
415        var end = this.arg ? this.arg.end : Argument.AT_CURSOR;
416        var predictions;
417
418        // Non-valid conversions will have useful information to pass on
419        if (this.conversion) {
420            status = this.conversion.status;
421            if (this.conversion.message) {
422                message += this.conversion.message;
423            }
424            predictions = this.conversion.predictions;
425        }
426
427        // Hint if the param is required, but not provided
428        var argProvided = this.arg && this.arg.text !== '';
429        var dataProvided = this.value !== undefined || argProvided;
430        if (this.param.defaultValue === undefined && !dataProvided) {
431            status = Status.INVALID;
432            message += '<strong>Required<\strong>';
433        }
434
435        return new Hint(status, message, start, end, predictions);
436    },
437
438    /**
439     * Basically <tt>setValue(conversion.predictions[0])</tt> done in a safe
440     * way.
441     */
442    complete: function() {
443        if (this.conversion && this.conversion.predictions &&
444                this.conversion.predictions.length > 0) {
445            this.setValue(this.conversion.predictions[0]);
446        }
447    },
448
449    /**
450     * If the cursor is at 'position', do we have sufficient data to start
451     * displaying the next hint. This is both complex and important.
452     * For example, if the user has just typed:<ul>
453     * <li>'set tabstop ' then they clearly want to know about the valid
454     *     values for the tabstop setting, so the hint is based on the next
455     *     parameter.
456     * <li>'set tabstop' (without trailing space) - they will probably still
457     *     want to know about the valid values for the tabstop setting because
458     *     there is no confusion about the setting in question.
459     * <li>'set tabsto' they've not finished typing a setting name so the hint
460     *     should be based on the current parameter.
461     * <li>'set tabstop' (when there is an additional tabstopstyle setting) we
462     *     can't make assumptions about the setting - we're not finished.
463     * </ul>
464     * <p>Note that the input for 2 and 4 is identical, only the configuration
465     * has changed, so hint display is environmental.
466     *
467     * <p>This function works out if the cursor is before the end of this
468     * assignment (assuming that we've asked the same thing of the previous
469     * assignment) and then attempts to work out if we should use the hint from
470     * the next assignment even though technically the cursor is still inside
471     * this one due to the rules above.
472     */
473    isPositionCaptured: function(position) {
474        if (!this.arg) {
475            return false;
476        }
477
478        // Note we don't check if position >= this.arg.start because that's
479        // implied by the fact that we're asking the assignments in turn, and
480        // we want to avoid thing falling between the cracks, but we do need
481        // to check that the argument does have a position
482        if (this.arg.start === -1) {
483            return false;
484        }
485
486        // We're clearly done if the position is past the end of the text
487        if (position > this.arg.end) {
488            return false;
489        }
490
491        // If we're AT the end, the position is captured if either the status
492        // is not valid or if there are other valid options including current
493        if (position === this.arg.end) {
494            return this.conversion.status !== Status.VALID ||
495                    this.conversion.predictions.length !== 0;
496        }
497
498        // Otherwise we're clearly inside
499        return true;
500    },
501
502    /**
503     * Replace the current value with the lower value if such a concept
504     * exists.
505     */
506    decrement: function() {
507        var replacement = this.param.type.decrement(this.value);
508        if (replacement != null) {
509            this.setValue(replacement);
510        }
511    },
512
513    /**
514     * Replace the current value with the higher value if such a concept
515     * exists.
516     */
517    increment: function() {
518        var replacement = this.param.type.increment(this.value);
519        if (replacement != null) {
520            this.setValue(replacement);
521        }
522    },
523
524    /**
525     * Helper when we're rebuilding command lines.
526     */
527    toString: function() {
528        return this.arg ? this.arg.toString() : '';
529    }
530};
531exports.Assignment = Assignment;
532
533
534/**
535 * This is a special parameter to reflect the command itself.
536 */
537var commandParam = {
538    name: '__command',
539    type: 'command',
540    description: 'The command to execute',
541
542    /**
543     * Provide some documentation for a command.
544     */
545    getCustomHint: function(command, arg) {
546        var docs = [];
547        docs.push('<strong><tt> &gt; ');
548        docs.push(command.name);
549        if (command.params && command.params.length > 0) {
550            command.params.forEach(function(param) {
551                if (param.defaultValue === undefined) {
552                    docs.push(' [' + param.name + ']');
553                }
554                else {
555                    docs.push(' <em>[' + param.name + ']</em>');
556                }
557            }, this);
558        }
559        docs.push('</tt></strong><br/>');
560
561        docs.push(command.description ? command.description : '(No description)');
562        docs.push('<br/>');
563
564        if (command.params && command.params.length > 0) {
565            docs.push('<ul>');
566            command.params.forEach(function(param) {
567                docs.push('<li>');
568                docs.push('<strong><tt>' + param.name + '</tt></strong>: ');
569                docs.push(param.description ? param.description : '(No description)');
570                if (param.defaultValue === undefined) {
571                    docs.push(' <em>[Required]</em>');
572                }
573                else if (param.defaultValue === null) {
574                    docs.push(' <em>[Optional]</em>');
575                }
576                else {
577                    docs.push(' <em>[Default: ' + param.defaultValue + ']</em>');
578                }
579                docs.push('</li>');
580            }, this);
581            docs.push('</ul>');
582        }
583
584        return new Hint(Status.VALID, docs.join(''), arg);
585    }
586};
587
588/**
589 * A Requisition collects the information needed to execute a command.
590 * There is no point in a requisition for parameter-less commands because there
591 * is no information to collect. A Requisition is a collection of assignments
592 * of values to parameters, each handled by an instance of Assignment.
593 * CliRequisition adds functions for parsing input from a command line to this
594 * class.
595 * <h2>Events<h2>
596 * We publish the following events:<ul>
597 * <li>argumentChange: The text of some argument has changed. It is likely that
598 * any UI component displaying this argument will need to be updated. (Note that
599 * this event is actually published by the Argument itself - see the docs for
600 * Argument for more details)
601 * The event object looks like: { argument: A, oldText: B, text: B }
602 * <li>commandChange: The command has changed. It is likely that a UI
603 * structure will need updating to match the parameters of the new command.
604 * The event object looks like { command: A }
605 * @constructor
606 */
607function Requisition(env) {
608    this.env = env;
609    this.commandAssignment = new Assignment(commandParam, this);
610}
611
612Requisition.prototype = {
613    /**
614     * The command that we are about to execute.
615     * @see setCommandConversion()
616     * @readonly
617     */
618    commandAssignment: undefined,
619
620    /**
621     * The count of assignments. Excludes the commandAssignment
622     * @readonly
623     */
624    assignmentCount: undefined,
625
626    /**
627     * The object that stores of Assignment objects that we are filling out.
628     * The Assignment objects are stored under their param.name for named
629     * lookup. Note: We make use of the property of Javascript objects that
630     * they are not just hashmaps, but linked-list hashmaps which iterate in
631     * insertion order.
632     * Excludes the commandAssignment.
633     */
634    _assignments: undefined,
635
636    /**
637     * The store of hints generated by the assignments. We are trying to prevent
638     * the UI from needing to access this in broad form, but instead use
639     * methods that query part of this structure.
640     */
641    _hints: undefined,
642
643    /**
644     * When the command changes, we need to keep a bunch of stuff in sync
645     */
646    _assignmentChanged: function(assignment) {
647        // This is all about re-creating Assignments
648        if (assignment.param.name !== '__command') {
649            return;
650        }
651
652        this._assignments = {};
653
654        if (assignment.value) {
655            assignment.value.params.forEach(function(param) {
656                this._assignments[param.name] = new Assignment(param, this);
657            }, this);
658        }
659
660        this.assignmentCount = Object.keys(this._assignments).length;
661        this._dispatchEvent('commandChange', { command: assignment.value });
662    },
663
664    /**
665     * Assignments have an order, so we need to store them in an array.
666     * But we also need named access ...
667     */
668    getAssignment: function(nameOrNumber) {
669        var name = (typeof nameOrNumber === 'string') ?
670            nameOrNumber :
671            Object.keys(this._assignments)[nameOrNumber];
672        return this._assignments[name];
673    },
674
675    /**
676     * Where parameter name == assignment names - they are the same.
677     */
678    getParameterNames: function() {
679        return Object.keys(this._assignments);
680    },
681
682    /**
683     * A *shallow* clone of the assignments.
684     * This is useful for systems that wish to go over all the assignments
685     * finding values one way or another and wish to trim an array as they go.
686     */
687    cloneAssignments: function() {
688        return Object.keys(this._assignments).map(function(name) {
689            return this._assignments[name];
690        }, this);
691    },
692
693    /**
694     * Collect the statuses from the Assignments.
695     * The hints returned are sorted by severity
696     */
697    _updateHints: function() {
698        // TODO: work out when to clear this out for the plain Requisition case
699        // this._hints = [];
700        this.getAssignments(true).forEach(function(assignment) {
701            this._hints.push(assignment.getHint());
702        }, this);
703        Hint.sort(this._hints);
704
705        // We would like to put some initial help here, but for anyone but
706        // a complete novice a 'type help' message is very annoying, so we
707        // need to find a way to only display this message once, or for
708        // until the user click a 'close' button or similar
709        // TODO: Add special case for '' input
710    },
711
712    /**
713     * Returns the most severe status
714     */
715    getWorstHint: function() {
716        return this._hints[0];
717    },
718
719    /**
720     * Extract the names and values of all the assignments, and return as
721     * an object.
722     */
723    getArgsObject: function() {
724        var args = {};
725        this.getAssignments().forEach(function(assignment) {
726            args[assignment.param.name] = assignment.value;
727        }, this);
728        return args;
729    },
730
731    /**
732     * Access the arguments as an array.
733     * @param includeCommand By default only the parameter arguments are
734     * returned unless (includeCommand === true), in which case the list is
735     * prepended with commandAssignment.arg
736     */
737    getAssignments: function(includeCommand) {
738        var args = [];
739        if (includeCommand === true) {
740            args.push(this.commandAssignment);
741        }
742        Object.keys(this._assignments).forEach(function(name) {
743            args.push(this.getAssignment(name));
744        }, this);
745        return args;
746    },
747
748    /**
749     * Reset all the assignments to their default values
750     */
751    setDefaultValues: function() {
752        this.getAssignments().forEach(function(assignment) {
753            assignment.setValue(undefined);
754        }, this);
755    },
756
757    /**
758     * Helper to call canon.exec
759     */
760    exec: function() {
761        canon.exec(this.commandAssignment.value,
762              this.env,
763              "cli",
764              this.getArgsObject(),
765              this.toCanonicalString());
766    },
767
768    /**
769     * Extract a canonical version of the input
770     */
771    toCanonicalString: function() {
772        var line = [];
773        line.push(this.commandAssignment.value.name);
774        Object.keys(this._assignments).forEach(function(name) {
775            var assignment = this._assignments[name];
776            var type = assignment.param.type;
777            // TODO: This will cause problems if there is a non-default value
778            // after a default value. Also we need to decide when to use
779            // named parameters in place of positional params. Both can wait.
780            if (assignment.value !== assignment.param.defaultValue) {
781                line.push(' ');
782                line.push(type.stringify(assignment.value));
783            }
784        }, this);
785        return line.join('');
786    }
787};
788oop.implement(Requisition.prototype, EventEmitter);
789exports.Requisition = Requisition;
790
791
792/**
793 * An object used during command line parsing to hold the various intermediate
794 * data steps.
795 * <p>The 'output' of the update is held in 2 objects: input.hints which is an
796 * array of hints to display to the user. In the future this will become a
797 * single value.
798 * <p>The other output value is input.requisition which gives access to an
799 * args object for use in executing the final command.
800 *
801 * <p>The majority of the functions in this class are called in sequence by the
802 * constructor. Their task is to add to <tt>hints</tt> fill out the requisition.
803 * <p>The general sequence is:<ul>
804 * <li>_tokenize(): convert _typed into _parts
805 * <li>_split(): convert _parts into _command and _unparsedArgs
806 * <li>_assign(): convert _unparsedArgs into requisition
807 * </ul>
808 *
809 * @param typed {string} The instruction as typed by the user so far
810 * @param options {object} A list of optional named parameters. Can be any of:
811 * <b>flags</b>: Flags for us to check against the predicates specified with the
812 * commands. Defaulted to <tt>keyboard.buildFlags({ });</tt>
813 * if not specified.
814 * @constructor
815 */
816function CliRequisition(env, options) {
817    Requisition.call(this, env);
818
819    if (options && options.flags) {
820        /**
821         * TODO: We were using a default of keyboard.buildFlags({ });
822         * This allowed us to have commands that only existed in certain contexts
823         * - i.e. Javascript specific commands.
824         */
825        this.flags = options.flags;
826    }
827}
828oop.inherits(CliRequisition, Requisition);
829(function() {
830    /**
831     * Called by the UI when ever the user interacts with a command line input
832     * @param input A structure that details the state of the input field.
833     * It should look something like: { typed:a, cursor: { start:b, end:c } }
834     * Where a is the contents of the input field, and b and c are the start
835     * and end of the cursor/selection respectively.
836     */
837    CliRequisition.prototype.update = function(input) {
838        this.input = input;
839        this._hints = [];
840
841        var args = this._tokenize(input.typed);
842        this._split(args);
843
844        if (this.commandAssignment.value) {
845            this._assign(args);
846        }
847
848        this._updateHints();
849    };
850
851    /**
852     * Return an array of Status scores so we can create a marked up
853     * version of the command line input.
854     */
855    CliRequisition.prototype.getInputStatusMarkup = function() {
856        // 'scores' is an array which tells us what chars are errors
857        // Initialize with everything VALID
858        var scores = this.toString().split('').map(function(ch) {
859            return Status.VALID;
860        });
861        // For all chars in all hints, check and upgrade the score
862        this._hints.forEach(function(hint) {
863            for (var i = hint.start; i <= hint.end; i++) {
864                if (hint.status > scores[i]) {
865                    scores[i] = hint.status;
866                }
867            }
868        }, this);
869        return scores;
870    };
871
872    /**
873     * Reconstitute the input from the args
874     */
875    CliRequisition.prototype.toString = function() {
876        return this.getAssignments(true).map(function(assignment) {
877            return assignment.toString();
878        }, this).join('');
879    };
880
881    var superUpdateHints = CliRequisition.prototype._updateHints;
882    /**
883     * Marks up hints in a number of ways:
884     * - Makes INCOMPLETE hints that are not near the cursor INVALID since
885     *   they can't be completed by typing
886     * - Finds the most severe hint, and annotates the array with it
887     * - Finds the hint to display, and also annotates the array with it
888     * TODO: I'm wondering if array annotation is evil and we should replace
889     * this with an object. Need to find out more.
890     */
891    CliRequisition.prototype._updateHints = function() {
892        superUpdateHints.call(this);
893
894        // Not knowing about cursor positioning, the requisition and assignments
895        // can't know this, but anything they mark as INCOMPLETE is actually
896        // INVALID unless the cursor is actually inside that argument.
897        var c = this.input.cursor;
898        this._hints.forEach(function(hint) {
899            var startInHint = c.start >= hint.start && c.start <= hint.end;
900            var endInHint = c.end >= hint.start && c.end <= hint.end;
901            var inHint = startInHint || endInHint;
902            if (!inHint && hint.status === Status.INCOMPLETE) {
903                 hint.status = Status.INVALID;
904            }
905        }, this);
906
907        Hint.sort(this._hints);
908    };
909
910    /**
911     * Accessor for the hints array.
912     * While we could just use the hints property, using getHints() is
913     * preferred for symmetry with Requisition where it needs a function due to
914     * lack of an atomic update system.
915     */
916    CliRequisition.prototype.getHints = function() {
917        return this._hints;
918    };
919
920    /**
921     * Look through the arguments attached to our assignments for the assignment
922     * at the given position.
923     */
924    CliRequisition.prototype.getAssignmentAt = function(position) {
925        var assignments = this.getAssignments(true);
926        for (var i = 0; i < assignments.length; i++) {
927            var assignment = assignments[i];
928            if (!assignment.arg) {
929                // There is no argument in this assignment, we've fallen off
930                // the end of the obvious answers - it must be this one.
931                return assignment;
932            }
933            if (assignment.isPositionCaptured(position)) {
934                return assignment;
935            }
936        }
937
938        return assignment;
939    };
940
941    /**
942     * Split up the input taking into account ' and "
943     */
944    CliRequisition.prototype._tokenize = function(typed) {
945        // For blank input, place a dummy empty argument into the list
946        if (typed == null || typed.length === 0) {
947            return [ new Argument(this, '', 0, 0, '', '') ];
948        }
949
950        var OUTSIDE = 1;     // The last character was whitespace
951        var IN_SIMPLE = 2;   // The last character was part of a parameter
952        var IN_SINGLE_Q = 3; // We're inside a single quote: '
953        var IN_DOUBLE_Q = 4; // We're inside double quotes: "
954
955        var mode = OUTSIDE;
956
957        // First we un-escape. This list was taken from:
958        // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Unicode
959        // We are generally converting to their real values except for \', \"
960        // and '\ ' which we are converting to unicode private characters so we
961        // can distinguish them from ', " and ' ', which have special meaning.
962        // They need swapping back post-split - see unescape2()
963        typed = typed
964                .replace(/\\\\/g, '\\')
965                .replace(/\\b/g, '\b')
966                .replace(/\\f/g, '\f')
967                .replace(/\\n/g, '\n')
968                .replace(/\\r/g, '\r')
969                .replace(/\\t/g, '\t')
970                .replace(/\\v/g, '\v')
971                .replace(/\\n/g, '\n')
972                .replace(/\\r/g, '\r')
973                .replace(/\\ /g, '\uF000')
974                .replace(/\\'/g, '\uF001')
975                .replace(/\\"/g, '\uF002');
976
977        function unescape2(str) {
978            return str
979                .replace(/\uF000/g, ' ')
980                .replace(/\uF001/g, '\'')
981                .replace(/\uF002/g, '"');
982        }
983
984        var i = 0;
985        var start = 0; // Where did this section start?
986        var prefix = '';
987        var args = [];
988
989        while (true) {
990            if (i >= typed.length) {
991                // There is nothing else to read - tidy up
992                if (mode !== OUTSIDE) {
993                    var str = unescape2(typed.substring(start, i));
994                    args.push(new Argument(this, str, start, i, prefix, ''));
995                }
996                else {
997                    if (i !== start) {
998                        // There's a bunch of whitespace at the end of the
999                        // command add it to the last argument's suffix,
1000                        // creating an empty argument if needed.
1001                        var extra = typed.substring(start, i);
1002                        var lastArg = args[args.length - 1];
1003                        if (!lastArg) {
1004                            lastArg = new Argument(this, '', i, i, extra, '');
1005                            args.push(lastArg);
1006                        }
1007                        else {
1008                            lastArg.suffix += extra;
1009                        }
1010                    }
1011                }
1012                break;
1013            }
1014
1015            var c = typed[i];
1016            switch (mode) {
1017                case OUTSIDE:
1018                    if (c === '\'') {
1019                        prefix = typed.substring(start, i + 1);
1020                        mode = IN_SINGLE_Q;
1021                        start = i + 1;
1022                    }
1023                    else if (c === '"') {
1024                        prefix = typed.substring(start, i + 1);
1025                        mode = IN_DOUBLE_Q;
1026                        start = i + 1;
1027                    }
1028                    else if (/ /.test(c)) {
1029                        // Still whitespace, do nothing
1030                    }
1031                    else {
1032                        prefix = typed.substring(start, i);
1033                        mode = IN_SIMPLE;
1034                        start = i;
1035                    }
1036                    break;
1037
1038                case IN_SIMPLE:
1039                    // There is an edge case of xx'xx which we are assuming to
1040                    // be a single parameter (and same with ")
1041                    if (c === ' ') {
1042                        var str = unescape2(typed.substring(start, i));
1043                        args.push(new Argument(this, str,
1044                                start, i, prefix, ''));
1045                        mode = OUTSIDE;
1046                        start = i;
1047                        prefix = '';
1048                    }
1049                    break;
1050
1051                case IN_SINGLE_Q:
1052                    if (c === '\'') {
1053                        var str = unescape2(typed.substring(start, i));
1054                        args.push(new Argument(this, str,
1055                                start - 1, i + 1, prefix, c));
1056                        mode = OUTSIDE;
1057                        start = i + 1;
1058                        prefix = '';
1059                    }
1060                    break;
1061
1062                case IN_DOUBLE_Q:
1063                    if (c === '"') {
1064                        var str = unescape2(typed.substring(start, i));
1065                        args.push(new Argument(this, str,
1066                                start - 1, i + 1, prefix, c));
1067                        mode = OUTSIDE;
1068                        start = i + 1;
1069                        prefix = '';
1070                    }
1071                    break;
1072            }
1073
1074            i++;
1075        }
1076
1077        return args;
1078    };
1079
1080    /**
1081     * Looks in the canon for a command extension that matches what has been
1082     * typed at the command line.
1083     */
1084    CliRequisition.prototype._split = function(args) {
1085        var argsUsed = 1;
1086        var arg;
1087
1088        while (argsUsed <= args.length) {
1089            var arg = Argument.merge(args, 0, argsUsed);
1090            this.commandAssignment.setArgument(arg);
1091
1092            if (!this.commandAssignment.value) {
1093                // Not found. break with value == null
1094                break;
1095            }
1096
1097            /*
1098            // Previously we needed a way to hide commands depending context.
1099            // We have not resurrected that feature yet.
1100            if (!keyboard.flagsMatch(command.predicates, this.flags)) {
1101                // If the predicates say 'no match' then go LA LA LA
1102                command = null;
1103                break;
1104            }
1105            */
1106
1107            if (this.commandAssignment.value.exec) {
1108                // Valid command, break with command valid
1109                for (var i = 0; i < argsUsed; i++) {
1110                    args.shift();
1111                }
1112                break;
1113            }
1114
1115            argsUsed++;
1116        }
1117    };
1118
1119    /**
1120     * Work out which arguments are applicable to which parameters.
1121     * <p>This takes #_command.params and #_unparsedArgs and creates a map of
1122     * param names to 'assignment' objects, which have the following properties:
1123     * <ul>
1124     * <li>param - The matching parameter.
1125     * <li>index - Zero based index into where the match came from on the input
1126     * <li>value - The matching input
1127     * </ul>
1128     */
1129    CliRequisition.prototype._assign = function(args) {
1130        if (args.length === 0) {
1131            this.setDefaultValues();
1132            return;
1133        }
1134
1135        // Create an error if the command does not take parameters, but we have
1136        // been given them ...
1137        if (this.assignmentCount === 0) {
1138            // TODO: previously we were doing some extra work to avoid this if
1139            // we determined that we had args that were all whitespace, but
1140            // probably given our tighter tokenize() this won't be an issue?
1141            this._hints.push(new Hint(Status.INVALID,
1142                    this.commandAssignment.value.name +
1143                    ' does not take any parameters',
1144                    Argument.merge(args)));
1145            return;
1146        }
1147
1148        // Special case: if there is only 1 parameter, and that's of type
1149        // text we put all the params into the first param
1150        if (this.assignmentCount === 1) {
1151            var assignment = this.getAssignment(0);
1152            if (assignment.param.type.name === 'text') {
1153                assignment.setArgument(Argument.merge(args));
1154                return;
1155            }
1156        }
1157
1158        var assignments = this.cloneAssignments();
1159        var names = this.getParameterNames();
1160
1161        // Extract all the named parameters
1162        var used = [];
1163        assignments.forEach(function(assignment) {
1164            var namedArgText = '--' + assignment.name;
1165
1166            var i = 0;
1167            while (true) {
1168                var arg = args[i];
1169                if (namedArgText !== arg.text) {
1170                    i++;
1171                    if (i >= args.length) {
1172                        break;
1173                    }
1174                    continue;
1175                }
1176
1177                // boolean parameters don't have values, default to false
1178                if (assignment.param.type.name === 'boolean') {
1179                    assignment.setValue(true);
1180                }
1181                else {
1182                    if (i + 1 < args.length) {
1183                        // Missing value portion of this named param
1184                        this._hints.push(new Hint(Status.INCOMPLETE,
1185                                'Missing value for: ' + namedArgText,
1186                                args[i]));
1187                    }
1188                    else {
1189                        args.splice(i + 1, 1);
1190                        assignment.setArgument(args[i + 1]);
1191                    }
1192                }
1193
1194                lang.arrayRemove(names, assignment.name);
1195                args.splice(i, 1);
1196                // We don't need to i++ if we splice
1197            }
1198        }, this);
1199
1200        // What's left are positional parameters assign in order
1201        names.forEach(function(name) {
1202            var assignment = this.getAssignment(name);
1203            if (args.length === 0) {
1204                // No more values
1205                assignment.setValue(undefined); // i.e. default
1206            }
1207            else {
1208                var arg = args[0];
1209                args.splice(0, 1);
1210                assignment.setArgument(arg);
1211            }
1212        }, this);
1213
1214        if (args.length > 0) {
1215            var remaining = Argument.merge(args);
1216            this._hints.push(new Hint(Status.INVALID,
1217                    'Input \'' + remaining.text + '\' makes no sense.',
1218                    remaining));
1219        }
1220    };
1221
1222})();
1223exports.CliRequisition = CliRequisition;
1224
1225
1226});
1227/* ***** BEGIN LICENSE BLOCK *****
1228 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1229 *
1230 * The contents of this file are subject to the Mozilla Public License Version
1231 * 1.1 (the "License"); you may not use this file except in compliance with
1232 * the License. You may obtain a copy of the License at
1233 * http://www.mozilla.org/MPL/
1234 *
1235 * Software distributed under the License is distributed on an "AS IS" basis,
1236 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1237 * for the specific language governing rights and limitations under the
1238 * License.
1239 *
1240 * The Original Code is Mozilla Skywriter.
1241 *
1242 * The Initial Developer of the Original Code is
1243 * Mozilla.
1244 * Portions created by the Initial Developer are Copyright (C) 2009
1245 * the Initial Developer. All Rights Reserved.
1246 *
1247 * Contributor(s):
1248 *   Joe Walker (jwalker@mozilla.com)
1249 *
1250 * Alternatively, the contents of this file may be used under the terms of
1251 * either the GNU General Public License Version 2 or later (the "GPL"), or
1252 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1253 * in which case the provisions of the GPL or the LGPL are applicable instead
1254 * of those above. If you wish to allow use of your version of this file only
1255 * under the terms of either the GPL or the LGPL, and not to allow others to
1256 * use your version of this file under the terms of the MPL, indicate your
1257 * decision by deleting the provisions above and replace them with the notice
1258 * and other provisions required by the GPL or the LGPL. If you do not delete
1259 * the provisions above, a recipient may use your version of this file under
1260 * the terms of any one of the MPL, the GPL or the LGPL.
1261 *
1262 * ***** END LICENSE BLOCK ***** */
1263
1264define('cockpit/ui/settings', ['require', 'exports', 'module' , 'pilot/types', 'pilot/types/basic'], function(require, exports, module) {
1265
1266
1267var types = require("pilot/types");
1268var SelectionType = require('pilot/types/basic').SelectionType;
1269
1270var direction = new SelectionType({
1271    name: 'direction',
1272    data: [ 'above', 'below' ]
1273});
1274
1275var hintDirectionSetting = {
1276    name: "hintDirection",
1277    description: "Are hints shown above or below the command line?",
1278    type: "direction",
1279    defaultValue: "above"
1280};
1281
1282var outputDirectionSetting = {
1283    name: "outputDirection",
1284    description: "Is the output window shown above or below the command line?",
1285    type: "direction",
1286    defaultValue: "above"
1287};
1288
1289var outputHeightSetting = {
1290    name: "outputHeight",
1291    description: "What height should the output panel be?",
1292    type: "number",
1293    defaultValue: 300
1294};
1295
1296exports.startup = function(data, reason) {
1297    types.registerType(direction);
1298    data.env.settings.addSetting(hintDirectionSetting);
1299    data.env.settings.addSetting(outputDirectionSetting);
1300    data.env.settings.addSetting(outputHeightSetting);
1301};
1302
1303exports.shutdown = function(data, reason) {
1304    types.unregisterType(direction);
1305    data.env.settings.removeSetting(hintDirectionSetting);
1306    data.env.settings.removeSetting(outputDirectionSetting);
1307    data.env.settings.removeSetting(outputHeightSetting);
1308};
1309
1310
1311});
1312/* ***** BEGIN LICENSE BLOCK *****
1313 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1314 *
1315 * The contents of this file are subject to the Mozilla Public License Version
1316 * 1.1 (the "License"); you may not use this file except in compliance with
1317 * the License. You may obtain a copy of the License at
1318 * http://www.mozilla.org/MPL/
1319 *
1320 * Software distributed under the License is distributed on an "AS IS" basis,
1321 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1322 * for the specific language governing rights and limitations under the
1323 * License.
1324 *
1325 * The Original Code is Skywriter.
1326 *
1327 * The Initial Developer of the Original Code is
1328 * Mozilla.
1329 * Portions created by the Initial Developer are Copyright (C) 2009
1330 * the Initial Developer. All Rights Reserved.
1331 *
1332 * Contributor(s):
1333 *   Joe Walker (jwalker@mozilla.com)
1334 *
1335 * Alternatively, the contents of this file may be used under the terms of
1336 * either the GNU General Public License Version 2 or later (the "GPL"), or
1337 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1338 * in which case the provisions of the GPL or the LGPL are applicable instead
1339 * of those above. If you wish to allow use of your version of this file only
1340 * under the terms of either the GPL or the LGPL, and not to allow others to
1341 * use your version of this file under the terms of the MPL, indicate your
1342 * decision by deleting the provisions above and replace them with the notice
1343 * and other provisions required by the GPL or the LGPL. If you do not delete
1344 * the provisions above, a recipient may use your version of this file under
1345 * the terms of any one of the MPL, the GPL or the LGPL.
1346 *
1347 * ***** END LICENSE BLOCK ***** */
1348
1349define('cockpit/ui/cli_view', ['require', 'exports', 'module' , 'text!cockpit/ui/cli_view.css', 'pilot/event', 'pilot/dom', 'pilot/keys', 'pilot/canon', 'pilot/types', 'cockpit/cli', 'cockpit/ui/request_view'], function(require, exports, module) {
1350
1351
1352var editorCss = require("text!cockpit/ui/cli_view.css");
1353var event = require("pilot/event");
1354var dom = require("pilot/dom");
1355
1356dom.importCssString(editorCss);
1357
1358var event = require("pilot/event");
1359var keys = require("pilot/keys");
1360var canon = require("pilot/canon");
1361var Status = require('pilot/types').Status;
1362
1363var CliRequisition = require('cockpit/cli').CliRequisition;
1364var Hint = require('cockpit/cli').Hint;
1365var RequestView = require('cockpit/ui/request_view').RequestView;
1366
1367var NO_HINT = new Hint(Status.VALID, '', 0, 0);
1368
1369/**
1370 * On startup we need to:
1371 * 1. Add 3 sets of elements to the DOM for:
1372 * - command line output
1373 * - input hints
1374 * - completion
1375 * 2. Attach a set of events so the command line works
1376 */
1377exports.startup = function(data, reason) {
1378    var cli = new CliRequisition(data.env);
1379    var cliView = new CliView(cli, data.env);
1380    data.env.cli = cli;
1381};
1382
1383/**
1384 * A class to handle the simplest UI implementation
1385 */
1386function CliView(cli, env) {
1387    cli.cliView = this;
1388    this.cli = cli;
1389    this.doc = document;
1390    this.win = dom.getParentWindow(this.doc);
1391    this.env = env;
1392
1393    // TODO: we should have a better way to specify command lines???
1394    this.element = this.doc.getElementById('cockpitInput');
1395    if (!this.element) {
1396        // console.log('No element with an id of cockpit. Bailing on cli');
1397        return;
1398    }
1399
1400    this.settings = env.settings;
1401    this.hintDirection = this.settings.getSetting('hintDirection');
1402    this.outputDirection = this.settings.getSetting('outputDirection');
1403    this.outputHeight = this.settings.getSetting('outputHeight');
1404
1405    // If the requisition tells us something has changed, we use this to know
1406    // if we should ignore it
1407    this.isUpdating = false;
1408
1409    this.createElements();
1410    this.update();
1411}
1412CliView.prototype = {
1413    /**
1414     * Create divs for completion, hints and output
1415     */
1416    createElements: function() {
1417        var input = this.element;
1418
1419        this.element.spellcheck = false;
1420
1421        this.output = this.doc.getElementById('cockpitOutput');
1422        this.popupOutput = (this.output == null);
1423        if (!this.output) {
1424            this.output = this.doc.createElement('div');
1425            this.output.id = 'cockpitOutput';
1426            this.output.className = 'cptOutput';
1427            input.parentNode.insertBefore(this.output, input.nextSibling);
1428
1429            var setMaxOutputHeight = function() {
1430                this.output.style.maxHeight = this.outputHeight.get() + 'px';
1431            }.bind(this);
1432            this.outputHeight.addEventListener('change', setMaxOutputHeight);
1433            setMaxOutputHeight();
1434        }
1435
1436        this.completer = this.doc.createElement('div');
1437        this.completer.className = 'cptCompletion VALID';
1438
1439        this.completer.style.color = dom.computedStyle(input, "color");
1440        this.completer.style.fontSize = dom.computedStyle(input, "fontSize");
1441        this.completer.style.fontFamily = dom.computedStyle(input, "fontFamily");
1442        this.completer.style.fontWeight = dom.computedStyle(input, "fontWeight");
1443        this.completer.style.fontStyle = dom.computedStyle(input, "fontStyle");
1444        input.parentNode.insertBefore(this.completer, input.nextSibling);
1445
1446        // Transfer background styling to the completer.
1447        this.completer.style.backgroundColor = input.style.backgroundColor;
1448        input.style.backgroundColor = 'transparent';
1449
1450        this.hinter = this.doc.createElement('div');
1451        this.hinter.className = 'cptHints';
1452        input.parentNode.insertBefore(this.hinter, input.nextSibling);
1453
1454        var resizer = this.resizer.bind(this);
1455        event.addListener(this.win, 'resize', resizer);
1456        this.hintDirection.addEventListener('change', resizer);
1457        this.outputDirection.addEventListener('change', resizer);
1458        resizer();
1459
1460        canon.addEventListener('output',  function(ev) {
1461            new RequestView(ev.request, this);
1462        }.bind(this));
1463        event.addCommandKeyListener(input, this.onCommandKey.bind(this));
1464        event.addListener(input, 'keyup', this.onKeyUp.bind(this));
1465
1466        // cursor position affects hint severity. TODO: shortcuts for speed
1467        event.addListener(input, 'mouseup', function(ev) {
1468            this.isUpdating = true;
1469            this.update();
1470            this.isUpdating = false;
1471        }.bind(this));
1472
1473        this.cli.addEventListener('argumentChange', this.onArgChange.bind(this));
1474
1475        event.addListener(input, "focus", function() {
1476            dom.addCssClass(this.output, "cptFocusPopup");
1477            dom.addCssClass(this.hinter, "cptFocusPopup");
1478        }.bind(this));
1479
1480        function hideOutput() {
1481            dom.removeCssClass(this.output, "cptFocusPopup");
1482            dom.removeCssClass(this.hinter, "cptFocusPopup");
1483        };
1484        event.addListener(input, "blur", hideOutput.bind(this));
1485        hideOutput.call(this);
1486    },
1487
1488    /**
1489     * We need to see the output of the latest command entered
1490     */
1491    scrollOutputToBottom: function() {
1492        // Certain browsers have a bug such that scrollHeight is too small
1493        // when content does not fill the client area of the element
1494        var scrollHeight = Math.max(this.output.scrollHeight, this.output.clientHeight);
1495        this.output.scrollTop = scrollHeight - this.output.clientHeight;
1496    },
1497
1498    /**
1499     * To be called on window resize or any time we want to align the elements
1500     * with the input box.
1501     */
1502    resizer: function() {
1503        var rect = this.element.getClientRects()[0];
1504
1505        this.completer.style.top = rect.top + 'px';
1506        var height = rect.bottom - rect.top;
1507        this.completer.style.height = height + 'px';
1508        this.completer.style.lineHeight = height + 'px';
1509        this.completer.style.left = rect.left + 'px';
1510        var width = rect.right - rect.left;
1511        this.completer.style.width = width + 'px';
1512
1513        if (this.hintDirection.get() === 'below') {
1514            this.hinter.style.top = rect.bottom + 'px';
1515            this.hinter.style.bottom = 'auto';
1516        }
1517        else {
1518            this.hinter.style.top = 'auto';
1519            this.hinter.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';
1520        }
1521        this.hinter.style.left = (rect.left + 30) + 'px';
1522        this.hinter.style.maxWidth = (width - 110) + 'px';
1523
1524        if (this.popupOutput) {
1525            if (this.outputDirection.get() === 'below') {
1526                this.output.style.top = rect.bottom + 'px';
1527                this.output.style.bottom = 'auto';
1528            }
1529            else {
1530                this.output.style.top = 'auto';
1531                this.output.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';
1532            }
1533            this.output.style.left = rect.left + 'px';
1534            this.output.style.width = (width - 80) + 'px';
1535        }
1536    },
1537
1538    /**
1539     * Ensure that TAB isn't handled by the browser
1540     */
1541onCommandKey: function(ev, hashId, keyCode) {
1542        var stopEvent;
1543        if (keyCode === keys.TAB ||
1544                keyCode === keys.UP ||
1545                keyCode === keys.DOWN) {
1546            stopEvent = true;
1547        } else if (hashId != 0 || keyCode != 0) {
1548            stopEvent = canon.execKeyCommand(this.env, 'cli', hashId, keyCode);
1549        }
1550        stopEvent && event.stopEvent(ev);
1551    },
1552
1553    /**
1554     * The main keyboard processing loop
1555     */
1556    onKeyUp: function(ev) {
1557        var handled;
1558        /*
1559        var handled = keyboardManager.processKeyEvent(ev, this, {
1560            isCommandLine: true, isKeyUp: true
1561        });
1562        */
1563
1564        // RETURN does a special exec/highlight thing
1565        if (ev.keyCode === keys.RETURN) {
1566            var worst = this.cli.getWorstHint();
1567            // Deny RETURN unless the command might work
1568            if (worst.status === Status.VALID) {
1569                this.cli.exec();
1570                this.element.value = '';
1571            }
1572            else {
1573                // If we've denied RETURN because the command was not VALID,
1574                // select the part of the command line that is causing problems
1575                // TODO: if there are 2 errors are we picking the right one?
1576                dom.setSelectionStart(this.element, worst.start);
1577                dom.setSelectionEnd(this.element, worst.end);
1578            }
1579        }
1580
1581        this.update();
1582
1583        // Special actions which delegate to the assignment
1584        var current = this.cli.getAssignmentAt(dom.getSelectionStart(this.element));
1585        if (current) {
1586            // TAB does a special complete thing
1587            if (ev.keyCode === keys.TAB) {
1588                current.complete();
1589                this.update();
1590            }
1591
1592            // UP/DOWN look for some history
1593            if (ev.keyCode === keys.UP) {
1594                current.increment();
1595                this.update();
1596            }
1597            if (ev.keyCode === keys.DOWN) {
1598                current.decrement();
1599                this.update();
1600            }
1601        }
1602
1603        return handled;
1604    },
1605
1606    /**
1607     * Actually parse the input and make sure we're all up to date
1608     */
1609    update: function() {
1610        this.isUpdating = true;
1611        var input = {
1612            typed: this.element.value,
1613            cursor: {
1614                start: dom.getSelectionStart(this.element),
1615                end: dom.getSelectionEnd(this.element.selectionEnd)
1616            }
1617        };
1618        this.cli.update(input);
1619
1620        var display = this.cli.getAssignmentAt(input.cursor.start).getHint();
1621
1622        // 1. Update the completer with prompt/error marker/TAB info
1623        dom.removeCssClass(this.completer, Status.VALID.toString());
1624        dom.removeCssClass(this.completer, Status.INCOMPLETE.toString());
1625        dom.removeCssClass(this.completer, Status.INVALID.toString());
1626
1627        var completion = '<span class="cptPrompt">&gt;</span> ';
1628        if (this.element.value.length > 0) {
1629            var scores = this.cli.getInputStatusMarkup();
1630            completion += this.markupStatusScore(scores);
1631        }
1632
1633        // Display the "-> prediction" at the end of the completer
1634        if (this.element.value.length > 0 &&
1635                display.predictions && display.predictions.length > 0) {
1636            var tab = display.predictions[0];
1637            completion += ' &nbsp;&#x21E5; ' + (tab.name ? tab.name : tab);
1638        }
1639        this.completer.innerHTML = completion;
1640        dom.addCssClass(this.completer, this.cli.getWorstHint().status.toString());
1641
1642        // 2. Update the hint element
1643        var hint = '';
1644        if (this.element.value.length !== 0) {
1645            hint += display.message;
1646            if (display.predictions && display.predictions.length > 0) {
1647                hint += ': [ ';
1648                display.predictions.forEach(function(prediction) {
1649                    hint += (prediction.name ? prediction.name : prediction);
1650                    hint += ' | ';
1651                }, this);
1652                hint = hint.replace(/\| $/, ']');
1653            }
1654        }
1655
1656        this.hinter.innerHTML = hint;
1657        if (hint.length === 0) {
1658            dom.addCssClass(this.hinter, 'cptNoPopup');
1659        }
1660        else {
1661            dom.removeCssClass(this.hinter, 'cptNoPopup');
1662        }
1663
1664        this.isUpdating = false;
1665    },
1666
1667    /**
1668     * Markup an array of Status values with spans
1669     */
1670    markupStatusScore: function(scores) {
1671        var completion = '';
1672        // Create mark-up
1673        var i = 0;
1674        var lastStatus = -1;
1675        while (true) {
1676            if (lastStatus !== scores[i]) {
1677                completion += '<span class=' + scores[i].toString() + '>';
1678                lastStatus = scores[i];
1679            }
1680            completion += this.element.value[i];
1681            i++;
1682            if (i === this.element.value.length) {
1683                completion += '</span>';
1684                break;
1685            }
1686            if (lastStatus !== scores[i]) {
1687                completion += '</span>';
1688            }
1689        }
1690
1691        return completion;
1692    },
1693
1694    /**
1695     * Update the input element to reflect the changed argument
1696     */
1697    onArgChange: function(ev) {
1698        if (this.isUpdating) {
1699            return;
1700        }
1701
1702        var prefix = this.element.value.substring(0, ev.argument.start);
1703        var suffix = this.element.value.substring(ev.argument.end);
1704        var insert = typeof ev.text === 'string' ? ev.text : ev.text.name;
1705        this.element.value = prefix + insert + suffix;
1706        // Fix the cursor.
1707        var insertEnd = (prefix + insert).length;
1708        this.element.selectionStart = insertEnd;
1709        this.element.selectionEnd = insertEnd;
1710    }
1711};
1712exports.CliView = CliView;
1713
1714
1715});
1716/* ***** BEGIN LICENSE BLOCK *****
1717 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1718 *
1719 * The contents of this file are subject to the Mozilla Public License Version
1720 * 1.1 (the "License"); you may not use this file except in compliance with
1721 * the License. You may obtain a copy of the License at
1722 * http://www.mozilla.org/MPL/
1723 *
1724 * Software distributed under the License is distributed on an "AS IS" basis,
1725 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1726 * for the specific language governing rights and limitations under the
1727 * License.
1728 *
1729 * The Original Code is Skywriter.
1730 *
1731 * The Initial Developer of the Original Code is
1732 * Mozilla.
1733 * Portions created by the Initial Developer are Copyright (C) 2009
1734 * the Initial Developer. All Rights Reserved.
1735 *
1736 * Contributor(s):
1737 *   Joe Walker (jwalker@mozilla.com)
1738 *
1739 * Alternatively, the contents of this file may be used under the terms of
1740 * either the GNU General Public License Version 2 or later (the "GPL"), or
1741 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1742 * in which case the provisions of the GPL or the LGPL are applicable instead
1743 * of those above. If you wish to allow use of your version of this file only
1744 * under the terms of either the GPL or the LGPL, and not to allow others to
1745 * use your version of this file under the terms of the MPL, indicate your
1746 * decision by deleting the provisions above and replace them with the notice
1747 * and other provisions required by the GPL or the LGPL. If you do not delete
1748 * the provisions above, a recipient may use your version of this file under
1749 * the terms of any one of the MPL, the GPL or the LGPL.
1750 *
1751 * ***** END LICENSE BLOCK ***** */
1752
1753define('cockpit/ui/request_view', ['require', 'exports', 'module' , 'pilot/dom', 'pilot/event', 'text!cockpit/ui/request_view.html', 'pilot/domtemplate', 'text!cockpit/ui/request_view.css'], function(require, exports, module) {
1754
1755var dom = require("pilot/dom");
1756var event = require("pilot/event");
1757var requestViewHtml = require("text!cockpit/ui/request_view.html");
1758var Templater = require("pilot/domtemplate").Templater;
1759
1760var requestViewCss = require("text!cockpit/ui/request_view.css");
1761dom.importCssString(requestViewCss);
1762
1763/**
1764 * Pull the HTML into the DOM, but don't add it to the document
1765 */
1766var templates = document.createElement('div');
1767templates.innerHTML = requestViewHtml;
1768var row = templates.querySelector('.cptRow');
1769
1770/**
1771 * Work out the path for images.
1772 * TODO: This should probably live in some utility area somewhere
1773 */
1774function imageUrl(path) {
1775    var dataUrl;
1776    try {
1777        dataUrl = require('text!cockpit/ui/' + path);
1778    } catch (e) { }
1779    if (dataUrl) {
1780        return dataUrl;
1781    }
1782
1783    var filename = module.id.split('/').pop() + '.js';
1784    var imagePath;
1785
1786    if (module.uri.substr(-filename.length) !== filename) {
1787        console.error('Can\'t work out path from module.uri/module.id');
1788        return path;
1789    }
1790
1791    if (module.uri) {
1792        var end = module.uri.length - filename.length - 1;
1793        return module.uri.substr(0, end) + "/" + path;
1794    }
1795
1796    return filename + path;
1797}
1798
1799
1800/**
1801 * Adds a row to the CLI output display
1802 */
1803function RequestView(request, cliView) {
1804    this.request = request;
1805    this.cliView = cliView;
1806    this.imageUrl = imageUrl;
1807
1808    // Elements attached to this by the templater. For info only
1809    this.rowin = null;
1810    this.rowout = null;
1811    this.output = null;
1812    this.hide = null;
1813    this.show = null;
1814    this.duration = null;
1815    this.throb = null;
1816
1817    new Templater().processNode(row.cloneNode(true), this);
1818
1819    this.cliView.output.appendChild(this.rowin);
1820    this.cliView.output.appendChild(this.rowout);
1821
1822    this.request.addEventListener('output', this.onRequestChange.bind(this));
1823};
1824
1825RequestView.prototype = {
1826    /**
1827     * A single click on an invocation line in the console copies the command to
1828     * the command line
1829     */
1830    copyToInput: function() {
1831        this.cliView.element.value = this.request.typed;
1832    },
1833
1834    /**
1835     * A double click on an invocation line in the console executes the command
1836     */
1837    executeRequest: function(ev) {
1838        this.cliView.cli.update({
1839            typed: this.request.typed,
1840            cursor: { start:0, end:0 }
1841        });
1842        this.cliView.cli.exec();
1843    },
1844
1845    hideOutput: function(ev) {
1846        this.output.style.display = 'none';
1847        dom.addCssClass(this.hide, 'cmd_hidden');
1848        dom.removeCssClass(this.show, 'cmd_hidden');
1849
1850        event.stopPropagation(ev);
1851    },
1852
1853    showOutput: function(ev) {
1854        this.output.style.display = 'block';
1855        dom.removeCssClass(this.hide, 'cmd_hidden');
1856        dom.addCssClass(this.show, 'cmd_hidden');
1857
1858        event.stopPropagation(ev);
1859    },
1860
1861    remove: function(ev) {
1862        this.cliView.output.removeChild(this.rowin);
1863        this.cliView.output.removeChild(this.rowout);
1864        event.stopPropagation(ev);
1865    },
1866
1867    onRequestChange: function(ev) {
1868        this.duration.innerHTML = this.request.duration ?
1869            'completed in ' + (this.request.duration / 1000) + ' sec ' :
1870            '';
1871
1872        this.output.innerHTML = '';
1873        this.request.outputs.forEach(function(output) {
1874            var node;
1875            if (typeof output == 'string') {
1876                node = document.createElement('p');
1877                node.innerHTML = output;
1878            } else {
1879                node = output;
1880            }
1881            this.output.appendChild(node);
1882        }, this);
1883        this.cliView.scrollOutputToBottom();
1884
1885        dom.setCssClass(this.output, 'cmd_error', this.request.error);
1886
1887        this.throb.style.display = this.request.completed ? 'none' : 'block';
1888    }
1889};
1890exports.RequestView = RequestView;
1891
1892
1893});
1894/* ***** BEGIN LICENSE BLOCK *****
1895 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
1896 *
1897 * The contents of this file are subject to the Mozilla Public License Version
1898 * 1.1 (the "License"); you may not use this file except in compliance with
1899 * the License. You may obtain a copy of the License at
1900 * http://www.mozilla.org/MPL/
1901 *
1902 * Software distributed under the License is distributed on an "AS IS" basis,
1903 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1904 * for the specific language governing rights and limitations under the
1905 * License.
1906 *
1907 * The Original Code is DomTemplate.
1908 *
1909 * The Initial Developer of the Original Code is Mozilla.
1910 * Portions created by the Initial Developer are Copyright (C) 2009
1911 * the Initial Developer. All Rights Reserved.
1912 *
1913 * Contributor(s):
1914 *   Joe Walker (jwalker@mozilla.com) (original author)
1915 *
1916 * Alternatively, the contents of this file may be used under the terms of
1917 * either the GNU General Public License Version 2 or later (the "GPL"), or
1918 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1919 * in which case the provisions of the GPL or the LGPL are applicable instead
1920 * of those above. If you wish to allow use of your version of this file only
1921 * under the terms of either the GPL or the LGPL, and not to allow others to
1922 * use your version of this file under the terms of the MPL, indicate your
1923 * decision by deleting the provisions above and replace them with the notice
1924 * and other provisions required by the GPL or the LGPL. If you do not delete
1925 * the provisions above, a recipient may use your version of this file under
1926 * the terms of any one of the MPL, the GPL or the LGPL.
1927 *
1928 * ***** END LICENSE BLOCK ***** */
1929
1930define('pilot/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
1931
1932
1933// WARNING: do not 'use_strict' without reading the notes in envEval;
1934
1935/**
1936 * A templater that allows one to quickly template DOM nodes.
1937 */
1938function Templater() {
1939  this.scope = [];
1940};
1941
1942/**
1943 * Recursive function to walk the tree processing the attributes as it goes.
1944 * @param node the node to process. If you pass a string in instead of a DOM
1945 * element, it is assumed to be an id for use with document.getElementById()
1946 * @param data the data to use for node processing.
1947 */
1948Templater.prototype.processNode = function(node, data) {
1949  if (typeof node === 'string') {
1950    node = document.getElementById(node);
1951  }
1952  if (data === null || data === undefined) {
1953    data = {};
1954  }
1955  this.scope.push(node.nodeName + (node.id ? '#' + node.id : ''));
1956  try {
1957    // Process attributes
1958    if (node.attributes && node.attributes.length) {
1959      // We need to handle 'foreach' and 'if' first because they might stop
1960      // some types of processing from happening, and foreach must come first
1961      // because it defines new data on which 'if' might depend.
1962      if (node.hasAttribute('foreach')) {
1963        this.processForEach(node, data);
1964        return;
1965      }
1966      if (node.hasAttribute('if')) {
1967        if (!this.processIf(node, data)) {
1968          return;
1969        }
1970      }
1971      // Only make the node available once we know it's not going away
1972      data.__element = node;
1973      // It's good to clean up the attributes when we've processed them,
1974      // but if we do it straight away, we mess up the array index
1975      var attrs = Array.prototype.slice.call(node.attributes);
1976      for (var i = 0; i < attrs.length; i++) {
1977        var value = attrs[i].value;
1978        var name = attrs[i].name;
1979        this.scope.push(name);
1980        try {
1981          if (name === 'save') {
1982            // Save attributes are a setter using the node
1983            value = this.stripBraces(value);
1984            this.property(value, data, node);
1985            node.removeAttribute('save');
1986          } else if (name.substring(0, 2) === 'on') {
1987            // Event registration relies on property doing a bind
1988            value = this.stripBraces(value);
1989            var func = this.property(value, data);
1990            if (typeof func !== 'function') {
1991              this.handleError('Expected ' + value +
1992                ' to resolve to a function, but got ' + typeof func);
1993            }
1994            node.removeAttribute(name);
1995            var capture = node.hasAttribute('capture' + name.substring(2));
1996            node.addEventListener(name.substring(2), func, capture);
1997            if (capture) {
1998              node.removeAttribute('capture' + name.substring(2));
1999            }
2000          } else {
2001            // Replace references in all other attributes
2002            var self = this;
2003            var newValue = value.replace(/\$\{[^}]*\}/g, function(path) {
2004              return self.envEval(path.slice(2, -1), data, value);
2005            });
2006            // Remove '_' prefix of attribute names so the DOM won't try
2007            // to use them before we've processed the template
2008            if (name.charAt(0) === '_') {
2009              node.removeAttribute(name);
2010              node.setAttribute(name.substring(1), newValue);
2011            } else if (value !== newValue) {
2012              attrs[i].value = newValue;
2013            }
2014          }
2015        } finally {
2016          this.scope.pop();
2017        }
2018      }
2019    }
2020
2021    // Loop through our children calling processNode. First clone them, so the
2022    // set of nodes that we visit will be unaffected by additions or removals.
2023    var childNodes = Array.prototype.slice.call(node.childNodes);
2024    for (var j = 0; j < childNodes.length; j++) {
2025      this.processNode(childNodes[j], data);
2026    }
2027
2028    if (node.nodeType === Node.TEXT_NODE) {
2029      this.processTextNode(node, data);
2030    }
2031  } finally {
2032    this.scope.pop();
2033  }
2034};
2035
2036/**
2037 * Handle <x if="${...}">
2038 * @param node An element with an 'if' attribute
2039 * @param data The data to use with envEval
2040 * @returns true if processing should continue, false otherwise
2041 */
2042Templater.prototype.processIf = function(node, data) {
2043  this.scope.push('if');
2044  try {
2045    var originalValue = node.getAttribute('if');
2046    var value = this.stripBraces(originalValue);
2047    var recurse = true;
2048    try {
2049      var reply = this.envEval(value, data, originalValue);
2050      recurse = !!reply;
2051    } catch (ex) {
2052      this.handleError('Error with \'' + value + '\'', ex);
2053      recurse = false;
2054    }
2055    if (!recurse) {
2056      node.parentNode.removeChild(node);
2057    }
2058    node.removeAttribute('if');
2059    return recurse;
2060  } finally {
2061    this.scope.pop();
2062  }
2063};
2064
2065/**
2066 * Handle <x foreach="param in ${array}"> and the special case of
2067 * <loop foreach="param in ${array}">
2068 * @param node An element with a 'foreach' attribute
2069 * @param data The data to use with envEval
2070 */
2071Templater.prototype.processForEach = function(node, data) {
2072  this.scope.push('foreach');
2073  try {
2074    var originalValue = node.getAttribute('foreach');
2075    var value = originalValue;
2076
2077    var paramName = 'param';
2078    if (value.charAt(0) === '$') {
2079      // No custom loop variable name. Use the default: 'param'
2080      value = this.stripBraces(value);
2081    } else {
2082      // Extract the loop variable name from 'NAME in ${ARRAY}'
2083      var nameArr = value.split(' in ');
2084      paramName = nameArr[0].trim();
2085      value = this.stripBraces(nameArr[1].trim());
2086    }
2087    node.removeAttribute('foreach');
2088    try {
2089      var self = this;
2090      // Process a single iteration of a loop
2091      var processSingle = function(member, clone, ref) {
2092        ref.parentNode.insertBefore(clone, ref);
2093        data[paramName] = member;
2094        self.processNode(clone, data);
2095        delete data[paramName];
2096      };
2097
2098      // processSingle is no good for <loop> nodes where we want to work on
2099      // the childNodes rather than the node itself
2100      var processAll = function(scope, member) {
2101        self.scope.push(scope);
2102        try {
2103          if (node.nodeName === 'LOOP') {
2104            for (var i = 0; i < node.childNodes.length; i++) {
2105              var clone = node.childNodes[i].cloneNode(true);
2106              processSingle(member, clone, node);
2107            }
2108          } else {
2109            var clone = node.cloneNode(true);
2110            clone.removeAttribute('foreach');
2111            processSingle(member, clone, node);
2112          }
2113        } finally {
2114          self.scope.pop();
2115        }
2116      };
2117
2118      var reply = this.envEval(value, data, originalValue);
2119      if (Array.isArray(reply)) {
2120        reply.forEach(function(data, i) {
2121          processAll('' + i, data);
2122        }, this);
2123      } else {
2124        for (var param in reply) {
2125          if (reply.hasOwnProperty(param)) {
2126            processAll(param, param);
2127          }
2128        }
2129      }
2130      node.parentNode.removeChild(node);
2131    } catch (ex) {
2132      this.handleError('Error with \'' + value + '\'', ex);
2133    }
2134  } finally {
2135    this.scope.pop();
2136  }
2137};
2138
2139/**
2140 * Take a text node and replace it with another text node with the ${...}
2141 * sections parsed out. We replace the node by altering node.parentNode but
2142 * we could probably use a DOM Text API to achieve the same thing.
2143 * @param node The Text node to work on
2144 * @param data The data to use in calls to envEval
2145 */
2146Templater.prototype.processTextNode = function(node, data) {
2147  // Replace references in other attributes
2148  var value = node.data;
2149  // We can't use the string.replace() with function trick (see generic
2150  // attribute processing in processNode()) because we need to support
2151  // functions that return DOM nodes, so we can't have the conversion to a
2152  // string.
2153  // Instead we process the string as an array of parts. In order to split
2154  // the string up, we first replace '${' with '\uF001$' and '}' with '\uF002'
2155  // We can then split using \uF001 or \uF002 to get an array of strings
2156  // where scripts are prefixed with $.
2157  // \uF001 and \uF002 are just unicode chars reserved for private use.
2158  value = value.replace(/\$\{([^}]*)\}/g, '\uF001$$$1\uF002');
2159  var parts = value.split(/\uF001|\uF002/);
2160  if (parts.length > 1) {
2161    parts.forEach(function(part) {
2162      if (part === null || part === undefined || part === '') {
2163        return;
2164      }
2165      if (part.charAt(0) === '$') {
2166        part = this.envEval(part.slice(1), data, node.data);
2167      }
2168      // It looks like this was done a few lines above but see envEval
2169      if (part === null) {
2170        part = "null";
2171      }
2172      if (part === undefined) {
2173        part = "undefined";
2174      }
2175      // if (isDOMElement(part)) { ... }
2176      if (typeof part.cloneNode !== 'function') {
2177        part = node.ownerDocument.createTextNode(part.toString());
2178      }
2179      node.parentNode.insertBefore(part, node);
2180    }, this);
2181    node.parentNode.removeChild(node);
2182  }
2183};
2184
2185/**
2186 * Warn of string does not begin '${' and end '}'
2187 * @param str the string to check.
2188 * @return The string stripped of ${ and }, or untouched if it does not match
2189 */
2190Templater.prototype.stripBraces = function(str) {
2191  if (!str.match(/\$\{.*\}/g)) {
2192    this.handleError('Expected ' + str + ' to match ${...}');
2193    return str;
2194  }
2195  return str.slice(2, -1);
2196};
2197
2198/**
2199 * Combined getter and setter that works with a path through some data set.
2200 * For example:
2201 * <ul>
2202 * <li>property('a.b', { a: { b: 99 }}); // returns 99
2203 * <li>property('a', { a: { b: 99 }}); // returns { b: 99 }
2204 * <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
2205 * input data to be { a: { b: 42 }}
2206 * </ul>
2207 * @param path An array of strings indicating the path through the data, or
2208 * a string to be cut into an array using <tt>split('.')</tt>
2209 * @param data An object to look in for the <tt>path</tt> argument
2210 * @param newValue (optional) If defined, this value will replace the
2211 * original value for the data at the path specified.
2212 * @return The value pointed to by <tt>path</tt> before any
2213 * <tt>newValue</tt> is applied.
2214 */
2215Templater.prototype.property = function(path, data, newValue) {
2216  this.scope.push(path);
2217  try {
2218    if (typeof path === 'string') {
2219      path = path.split('.');
2220    }
2221    var value = data[path[0]];
2222    if (path.length === 1) {
2223      if (newValue !== undefined) {
2224        data[path[0]] = newValue;
2225      }
2226      if (typeof value === 'function') {
2227        return function() {
2228          return value.apply(data, arguments);
2229        };
2230      }
2231      return value;
2232    }
2233    if (!value) {
2234      this.handleError('Can\'t find path=' + path);
2235      return null;
2236    }
2237    return this.property(path.slice(1), value, newValue);
2238  } finally {
2239    this.scope.pop();
2240  }
2241};
2242
2243/**
2244 * Like eval, but that creates a context of the variables in <tt>env</tt> in
2245 * which the script is evaluated.
2246 * WARNING: This script uses 'with' which is generally regarded to be evil.
2247 * The alternative is to create a Function at runtime that takes X parameters
2248 * according to the X keys in the env object, and then call that function using
2249 * the values in the env object. This is likely to be slow, but workable.
2250 * @param script The string to be evaluated.
2251 * @param env The environment in which to eval the script.
2252 * @param context Optional debugging string in case of failure
2253 * @return The return value of the script, or the error message if the script
2254 * execution failed.
2255 */
2256Templater.prototype.envEval = function(script, env, context) {
2257  with (env) {
2258    try {
2259      this.scope.push(context);
2260      return eval(script);
2261    } catch (ex) {
2262      this.handleError('Template error evaluating \'' + script + '\'', ex);
2263      return script;
2264    } finally {
2265      this.scope.pop();
2266    }
2267  }
2268};
2269
2270/**
2271 * A generic way of reporting errors, for easy overloading in different
2272 * environments.
2273 * @param message the error message to report.
2274 * @param ex optional associated exception.
2275 */
2276Templater.prototype.handleError = function(message, ex) {
2277  this.logError(message);
2278  this.logError('In: ' + this.scope.join(' > '));
2279  if (ex) {
2280    this.logError(ex);
2281  }
2282};
2283
2284
2285/**
2286 * A generic way of reporting errors, for easy overloading in different
2287 * environments.
2288 * @param message the error message to report.
2289 */
2290Templater.prototype.logError = function(message) {
2291  window.console && window.console.log && console.log(message);
2292};
2293
2294exports.Templater = Templater;
2295
2296
2297});
2298/* ***** BEGIN LICENSE BLOCK *****
2299 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
2300 *
2301 * The contents of this file are subject to the Mozilla Public License Version
2302 * 1.1 (the "License"); you may not use this file except in compliance with
2303 * the License. You may obtain a copy of the License at
2304 * http://www.mozilla.org/MPL/
2305 *
2306 * Software distributed under the License is distributed on an "AS IS" basis,
2307 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
2308 * for the specific language governing rights and limitations under the
2309 * License.
2310 *
2311 * The Original Code is Skywriter.
2312 *
2313 * The Initial Developer of the Original Code is
2314 * Mozilla.
2315 * Portions created by the Initial Developer are Copyright (C) 2009
2316 * the Initial Developer. All Rights Reserved.
2317 *
2318 * Contributor(s):
2319 *   Skywriter Team (skywriter@mozilla.com)
2320 *
2321 * Alternatively, the contents of this file may be used under the terms of
2322 * either the GNU General Public License Version 2 or later (the "GPL"), or
2323 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
2324 * in which case the provisions of the GPL or the LGPL are applicable instead
2325 * of those above. If you wish to allow use of your version of this file only
2326 * under the terms of either the GPL or the LGPL, and not to allow others to
2327 * use your version of this file under the terms of the MPL, indicate your
2328 * decision by deleting the provisions above and replace them with the notice
2329 * and other provisions required by the GPL or the LGPL. If you do not delete
2330 * the provisions above, a recipient may use your version of this file under
2331 * the terms of any one of the MPL, the GPL or the LGPL.
2332 *
2333 * ***** END LICENSE BLOCK ***** */
2334
2335define('cockpit/commands/basic', ['require', 'exports', 'module' , 'pilot/canon'], function(require, exports, module) {
2336
2337
2338var canon = require('pilot/canon');
2339
2340/**
2341 * '!' command
2342 */
2343var bangCommandSpec = {
2344    name: 'sh',
2345    description: 'Execute a system command (requires server support)',
2346    params: [
2347        {
2348            name: 'command',
2349            type: 'text',
2350            description: 'The string to send to the os shell.'
2351        }
2352    ],
2353    exec: function(env, args, request) {
2354        var req = new XMLHttpRequest();
2355        req.open('GET', '/exec?args=' + args.command, true);
2356        req.onreadystatechange = function(ev) {
2357          if (req.readyState == 4) {
2358            if (req.status == 200) {
2359              request.done('<pre>' + req.responseText + '</pre>');
2360            }
2361          }
2362        };
2363        req.send(null);
2364    }
2365};
2366
2367var canon = require('pilot/canon');
2368
2369exports.startup = function(data, reason) {
2370    canon.addCommand(bangCommandSpec);
2371};
2372
2373exports.shutdown = function(data, reason) {
2374    canon.removeCommand(bangCommandSpec);
2375};
2376
2377
2378});
2379define("text!cockpit/ui/cli_view.css", [], "" +
2380  "#cockpitInput { padding-left: 16px; }" +
2381  "" +
2382  ".cptOutput { overflow: auto; position: absolute; z-index: 999; display: none; }" +
2383  "" +
2384  ".cptCompletion { padding: 0; position: absolute; z-index: -1000; }" +
2385  ".cptCompletion.VALID { background: #FFF; }" +
2386  ".cptCompletion.INCOMPLETE { background: #DDD; }" +
2387  ".cptCompletion.INVALID { background: #DDD; }" +
2388  ".cptCompletion span { color: #FFF; }" +
2389  ".cptCompletion span.INCOMPLETE { color: #DDD; border-bottom: 2px dotted #F80; }" +
2390  ".cptCompletion span.INVALID { color: #DDD; border-bottom: 2px dotted #F00; }" +
2391  "span.cptPrompt { color: #66F; font-weight: bold; }" +
2392  "" +
2393  "" +
2394  ".cptHints {" +
2395  "  color: #000;" +
2396  "  position: absolute;" +
2397  "  border: 1px solid rgba(230, 230, 230, 0.8);" +
2398  "  background: rgba(250, 250, 250, 0.8);" +
2399  "  -moz-border-radius-topleft: 10px;" +
2400  "  -moz-border-radius-topright: 10px;" +
2401  "  border-top-left-radius: 10px; border-top-right-radius: 10px;" +
2402  "  z-index: 1000;" +
2403  "  padding: 8px;" +
2404  "  display: none;" +
2405  "}" +
2406  "" +
2407  ".cptFocusPopup { display: block; }" +
2408  ".cptFocusPopup.cptNoPopup { display: none; }" +
2409  "" +
2410  ".cptHints ul { margin: 0; padding: 0 15px; }" +
2411  "" +
2412  ".cptGt { font-weight: bold; font-size: 120%; }" +
2413  "");
2414
2415define("text!cockpit/ui/request_view.css", [], "" +
2416  ".cptRowIn {" +
2417  "  display: box; display: -moz-box; display: -webkit-box;" +
2418  "  box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-orient: horizontal;" +
2419  "  box-align: center; -moz-box-align: center; -webkit-box-align: center;" +
2420  "  color: #333;" +
2421  "  background-color: #EEE;" +
2422  "  width: 100%;" +
2423  "  font-family: consolas, courier, monospace;" +
2424  "}" +
2425  ".cptRowIn > * { padding-left: 2px; padding-right: 2px; }" +
2426  ".cptRowIn > img { cursor: pointer; }" +
2427  ".cptHover { display: none; }" +
2428  ".cptRowIn:hover > .cptHover { display: block; }" +
2429  ".cptRowIn:hover > .cptHover.cptHidden { display: none; }" +
2430  ".cptOutTyped {" +
2431  "  box-flex: 1; -moz-box-flex: 1; -webkit-box-flex: 1;" +
2432  "  font-weight: bold; color: #000; font-size: 120%;" +
2433  "}" +
2434  ".cptRowOutput { padding-left: 10px; line-height: 1.2em; }" +
2435  ".cptRowOutput strong," +
2436  ".cptRowOutput b," +
2437  ".cptRowOutput th," +
2438  ".cptRowOutput h1," +
2439  ".cptRowOutput h2," +
2440  ".cptRowOutput h3 { color: #000; }" +
2441  ".cptRowOutput a { font-weight: bold; color: #666; text-decoration: none; }" +
2442  ".cptRowOutput a: hover { text-decoration: underline; cursor: pointer; }" +
2443  ".cptRowOutput input[type=password]," +
2444  ".cptRowOutput input[type=text]," +
2445  ".cptRowOutput textarea {" +
2446  "  color: #000; font-size: 120%;" +
2447  "  background: transparent; padding: 3px;" +
2448  "  border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;" +
2449  "}" +
2450  ".cptRowOutput table," +
2451  ".cptRowOutput td," +
2452  ".cptRowOutput th { border: 0; padding: 0 2px; }" +
2453  ".cptRowOutput .right { text-align: right; }" +
2454  "");
2455
2456define("text!cockpit/ui/request_view.html", [], "" +
2457  "<div class=cptRow>" +
2458  "  <!-- The div for the input (i.e. what was typed) -->" +
2459  "  <div class=\"cptRowIn\" save=\"${rowin}\"" +
2460  "      onclick=\"${copyToInput}\"" +
2461  "      ondblclick=\"${executeRequest}\">" +
2462  "" +
2463  "    <!-- What the user actually typed -->" +
2464  "    <div class=\"cptGt\">&gt; </div>" +
2465  "    <div class=\"cptOutTyped\">${request.typed}</div>" +
2466  "" +
2467  "    <!-- The extra details that appear on hover -->" +
2468  "    <div class=cptHover save=\"${duration}\"></div>" +
2469  "    <img class=cptHover onclick=\"${hideOutput}\" save=\"${hide}\"" +
2470  "        alt=\"Hide command output\" _src=\"${imageUrl('images/minus.png')}\"/>" +
2471  "    <img class=\"cptHover cptHidden\" onclick=\"${showOutput}\" save=\"${show}\"" +
2472  "        alt=\"Show command output\" _src=\"${imageUrl('images/plus.png')}\"/>" +
2473  "    <img class=cptHover onclick=\"${remove}\"" +
2474  "        alt=\"Remove this command from the history\"" +
2475  "        _src=\"${imageUrl('images/closer.png')}\"/>" +
2476  "" +
2477  "  </div>" +
2478  "" +
2479  "  <!-- The div for the command output -->" +
2480  "  <div class=\"cptRowOut\" save=\"${rowout}\">" +
2481  "    <div class=\"cptRowOutput\" save=\"${output}\"></div>" +
2482  "    <img _src=\"${imageUrl('images/throbber.gif')}\" save=\"${throb}\"/>" +
2483  "  </div>" +
2484  "</div>" +
2485  "");
2486
2487define("text!cockpit/ui/images/closer.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAj9JREFUeNp0ks+LUlEUx7/vV1o8Z8wUx3IEHcQmiBiQlomjRNCiZpEuEqF/oEUwq/6EhvoHggmRcJUQBM1CRJAW0aLIaGQimZJxJsWxyV/P9/R1zzWlFl04vPvOPZ9z7rnnK5imidmKRCIq+zxgdoPZ1T/ut8xeM3tcKpW6s1hhBkaj0Qj7bDebTX+324WmadxvsVigqipcLleN/d4rFoulORiLxTZY8ItOp8MBCpYkiYPj8Xjus9vtlORWoVB4KcTjcQc732dLpSRXvCZaAws6Q4WDdqsO52kNH+oCRFGEz+f7ydwBKRgMPmTXi49GI1x2D/DsznesB06ws2eDbI7w9HYN6bVjvGss4KAjwDAMq81mM2SW5Wa/3weBbz42UL9uYnVpiO2Nr9ANHSGXib2Wgm9tCYIggGKJEVkvlwgi5/FQRmTLxO6hgJVzI1x0T/fJrBtHJxPeL6tI/fsZLA6ot8lkQi8HRVbw94gkWYI5MaHrOjcCGSNRxZosy9y5cErDzn0Dqx7gcwO8WtBp4PndI35GMYqiUMUvBL5yOBz8yRfFNpbPmqgcCFh/IuHa1nR/YXGM8+oUpFhihEQiwcdRLpfVRqOBtWXWq34Gra6AXq8Hp2piZcmKT4cKnE4nwuHwdByVSmWQz+d32WCTlHG/qaHHREN9kgi0sYQfv0R4PB4EAgESQDKXy72fSy6VSnHJVatVf71eR7vd5n66mtfrRSgU4pLLZrOlf7RKK51Ok8g3/yPyR5lMZi7y3wIMAME4EigHWgKnAAAAAElFTkSuQmCC");
2488
2489define("text!cockpit/ui/images/dot_clear.gif", [], "data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7");
2490
2491define("text!cockpit/ui/images/minus.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4xMrIJw5EAAAHcSURBVCjPhZIxSxtxGMZ/976XhJA/RA5EAyJcFksnp64hjUPBoXRyCYLQTyD0UxScu0nFwalCQSgFCVk7dXAwUAiBDA2RO4W7yN1x9+9gcyhU+pteHt4H3pfncay1LOl0OgY4BN4Ar/7KP4BvwNFwOIyWu87S2O12O8DxfD73oygiSRIAarUaxhhWV1fHwMFgMBiWxl6v9y6Koi+3t7ckSUKtVkNVAcjzvNRWVlYwxry9vLz86uzs7HjAZDKZGGstjUaDfxHHMSLC5ubmHdB2VfVwNpuZ5clxHPMcRVFwc3PTXFtbO3RFZHexWJCmabnweAaoVqvlv4vFAhHZdVX1ZZqmOI5DURR8fz/lxbp9Yrz+7bD72SfPcwBU1XdF5N5aWy2KgqIoeBzPEnWVLMseYnAcRERdVR27rrsdxzGqyutP6898+GBsNBqo6i9XVS88z9sOggAR4X94noeqXoiIHPm+H9XrdYIgIAxDwjAkTVPCMESzBy3LMprNJr7v34nIkV5dXd2fn59fG2P2siwjSRIqlQrWWlSVJFcqlQqtVot2u40xZu/s7OxnWbl+v98BjkejkT+dTgmCoDxtY2ODra2tMXBweno6fNJVgP39fQN8eKbkH09OTsqS/wHFRdHPfTSfjwAAAABJRU5ErkJggg==");
2492
2493define("text!cockpit/ui/images/pinaction.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAClklEQVQ4EX1TXUhUQRQ+Z3Zmd+9uN1q2P3UpZaEwcikKekkqLKggKHJ96MHe9DmLkCDa9U198Id8kErICmIlRAN96UdE6QdBW/tBA5Uic7E0zN297L17p5mb1zYjD3eYc+d83zlnON8g5xzWNUSEdUBkHTJasRWySPP7fw3hfwkk2GoNsc0vOaJRHo1GV/GiMctkTIJRFlpZli8opK+htmf83gXeG63oteOtra0u25e7TYJIJELb26vYCACTgUe1lXV86BTn745l+MsyHqs53S/Aq4VEUa9Y6ko14eYY4u3AyM3HYwdKU35DZyblGR2+qq6W0X2Nnh07xynnVYpHORx/E1/GvvqaAZUayjMjdM2f/Lgr5E+fV93zR4u3zKCLughsZqKwAzAxaz6dPY6JgjLUF+eSP5OpjmAw2E8DvldHSvJMKPg08aRor1tc4BuALu6mOwGWdQC3mKIqRsC8mKd8wYfD78/earzSYzdMDW9QgKb0Is8CBY1mQXOiaXAHEpMDE5XTJqIq4EiyxUqKlpfkF0pyV1OTAoFAhmTmyCCoDsZNZvIkUjELQpipo0sQqYZAswZHwsEEE10M0pq2SSZY9HqNcDicJcNTpBvQJz40UbSOTh1B8bDpuY0w9Hb3kkn9lPAlBLfhfD39XTtX/blFJqiqrjbkTi63Hbofj2uL4GMsmzFgbDJ/vmMgv/lB4syJ0oXO7d3j++vio6GFsYmD6cHJreWc3/jRVVHhsOYvM8iZ36mtjPDBk/xDZE8CoHlbrlAssbTxDdDJvdb536L7I6S7Vy++6Gi4Xi9BsUthJRaLOYSPz4XALKI4j4iObd/e5UtDKUjZzYyYRyGAJv01Zj8kC5cbs5WY83hQnv0DzCXl+r8APElkq0RU6oMAAAAASUVORK5CYII=");
2494
2495define("text!cockpit/ui/images/pinin.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQ4Ea2TPUsDQRCGZ89Eo4FACkULEQs1CH4Uamfjn7GxEYJFIFXgChFsbPwzNnZioREkaiHBQtEiEEiMRm/dZ8OEGAxR4sBxx877Pju7M2estTJIxLrNuVwuMxQEx0ZkzcFHyRtjXt02559RtB2GYanTYzoryOfz+6l4Nbszf2niwffKmpGRo9sVW22mDgqFwp5C2gDMm+P32a3JB1N+n5JifUGeP9JeNxGryPLYjcwMP8rJ07Q9fZltQzyAstOJ2vVu5sKc1ZZkRBrOcKeb+HexPidvkpCN5JUcllZtpZFc5DgBWc5M2eysZuMuofMBSA4NWjx4PUCsXefMlI0QY3ewRg4NWi4ZTQsgrjYXema+e4VqtEMK6KXvu+4B9Bklt90vVKMeD2BI6DOt4rZ/Gk7WyKFBi4fNPIAJY0joM61SCCZ9tI1o0OIB8D+DBIkYaJRbCBH9mZgNt+bb++ufSSF/eX8BYcDeAzuQJVUAAAAASUVORK5CYII=");
2496
2497define("text!cockpit/ui/images/pinout.png", [], "data:image/png;base64,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");
2498
2499define("text!cockpit/ui/images/pins.png", [], "data:image/png;base64,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");
2500
2501define("text!cockpit/ui/images/plus.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4yFTwuJTkAAAH7SURBVCjPdZKxa1NRFMZ/956XZMgFyyMlCZRA4hBx6lBcQ00GoYi4tEstFPwLAs7iLDi7FWuHThaUggihBDI5OWRoQAmBQFISQgvvpbwX3rsOaR4K+o2H8zvfOZxPWWtZqVarGaAJPAEe3ZW/A1+Bd+1221v1qhW4vb1dA44mk0nZ8zyCIAAgk8lgjGF9fb0PHF5cXLQTsF6vP/c879P19TVBEJDJZBARAKIoSmpra2sYY561Wq3PqtFouMBgMBgYay3ZbJZ/yfd9tNaUSqUboOKISPPq6sqsVvZ9H4AvL34B8PTj/QSO45jpdHovn883Ha31znw+JwzDpCEMQx4UloM8zyOdTif3zudztNY7jog8DMMQpRRxHPPt5TCBAEZvxlyOFTsfykRRBICIlB2t9a21Nh3HMXEc8+d7VhJHWCwWyzcohdZaHBHpO46z6fs+IsLj94XECaD4unCHL8FsNouI/HRE5Nx13c3ZbIbWOnG5HKtl+53TSq7rIiLnand31wUGnU7HjEYjlFLJZN/3yRnL1FMYY8jlcmxtbd0AFel2u7dnZ2eXxpi9xWJBEASkUimstYgIQSSkUimKxSKVSgVjzN7p6emPJHL7+/s14KjX65WHwyGz2SxZbWNjg2q12gcOT05O2n9lFeDg4MAAr/4T8rfHx8dJyH8DvvbYGzKvWukAAAAASUVORK5CYII=");
2502
2503define("text!cockpit/ui/images/throbber.gif", [], "data:image/gif;base64,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");
2504
Note: See TracBrowser for help on using the repository browser.