Exemple #1
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');
var intro = require('gcli/ui/intro');

exports.items = [
  {
    item: 'converter',
    from: 'intro',
    to: 'view',
    exec: intro.createView
  },
  {
    item: 'command',
    name: 'intro',
    description: l10n.lookup('introDesc'),
    manual: l10n.lookup('introManual'),
    returnType: 'intro',
    exec: function(args, context) {
      // The intro command is pure formatting - no data
    }
  }
];

});
Exemple #2
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');

/**
 * 'context' command
 */
var context = {
  item: 'command',
  name: 'context',
  description: l10n.lookup('contextDesc'),
  manual: l10n.lookup('contextManual'),
  params: [
   {
     name: 'prefix',
     type: 'command',
     description: l10n.lookup('contextPrefixDesc'),
     defaultValue: null
   }
  ],
  returnType: 'string',
  noRemote: true,
  exec: function echo(args, context) {
    // Do not copy this code
    var requisition = context.__dlhjshfw;

    if (args.prefix == null) {
      requisition.prefix = null;
      return l10n.lookup('contextEmptyReply');
    }

    if (args.prefix.exec != null) {
      throw new Error(l10n.lookupFormat('contextNotParentError',
                                        [ args.prefix.name ]));
    }

    requisition.prefix = args.prefix.name;
    return l10n.lookupFormat('contextReply', [ args.prefix.name ]);
  }
};

exports.items = [ context ];

});
Exemple #3
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');
var canon = require('gcli/canon');
var converters = require('gcli/converters');
var intro = require('gcli/ui/intro');

var introConverterSpec = {
  from: 'intro',
  to: 'view',
  exec: function(ignore, converterContext) {
    return intro.createView(converterContext);
  }
};

/**
 * 'intro' command
 */
var introCommandSpec = {
  name: 'intro',
  description: l10n.lookup('introDesc'),
  manual: l10n.lookup('introManual'),
  returnType: 'intro',
  exec: function(args, context) {
    // The intro command is pure formatting - no data
  }
};

/**
 * Registration and de-registration.
 */
exports.startup = function() {
  canon.addCommand(introCommandSpec);
  converters.addConverter(introConverterSpec);
};

exports.shutdown = function() {
  canon.removeCommand(introCommandSpec);
  converters.removeConverter(introConverterSpec);
};

});
Exemple #4
0
/**
 * Implement the localization algorithm for any documentation objects (i.e.
 * description and manual) in a command.
 * @param data The data assigned to a description or manual property
 * @param onUndefined If data == null, should we return the data untouched or
 * lookup a 'we don't know' key in it's place.
 */
function lookup(data, onUndefined) {
  if (data == null) {
    if (onUndefined) {
      return l10n.lookup(onUndefined);
    }

    return data;
  }

  if (typeof data === 'string') {
    return data;
  }

  if (typeof data === 'object') {
    if (data.key) {
      return l10n.lookup(data.key);
    }

    var locales = l10n.getPreferredLocales();
    var translated;
    locales.some(function(locale) {
      translated = data[locale];
      return translated != null;
    });
    if (translated != null) {
      return translated;
    }

    console.error('Can\'t find locale in descriptions: ' +
            'locales=' + JSON.stringify(locales) + ', ' +
            'description=' + JSON.stringify(data));
    return '(No description)';
  }

  return l10n.lookup(onUndefined);
}
Exemple #5
0
ParamType.prototype.parse = function(arg, requisition) { //Orion-20140210
  if (this.isIncompleteName) {
    return SelectionType.prototype.parse.call(this, arg);
  }
  else {
    var message = l10n.lookup('cliUnusedArg');
    if (requisition) { //Orion-20140210
    	var conversion = requisition.commandAssignment.conversion; //Orion-20140210
    	if (conversion.getStatus() === Status.ERROR && conversion.message) { //Orion-20140210
    		message = conversion.message; //Orion-20140210
    	} //Orion-20140210
    } //Orion-20140210
    return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
  }
};
Exemple #6
0
  exec: function echo(args, context) {
    // Do not copy this code
    var requisition = context.__dlhjshfw;

    if (args.prefix == null) {
      requisition.prefix = null;
      return l10n.lookup('contextEmptyReply');
    }

    if (args.prefix.exec != null) {
      throw new Error(l10n.lookupFormat('contextNotParentError',
                                        [ args.prefix.name ]));
    }

    requisition.prefix = args.prefix.name;
    return l10n.lookupFormat('contextReply', [ args.prefix.name ]);
  }
Exemple #7
0
  var predictions = matches.map(function(match) {
    var description;
    var incomplete = true;

    if (this._isSafeProperty(scope, match.prop)) {
      description = '(property getter)';
    }
    else {
      try {
        var value = scope[match.prop];

        if (typeof value === 'function') {
          description = '(function)';
        }
        else if (typeof value === 'boolean' || typeof value === 'number') {
          description = '= ' + value;
          incomplete = false;
        }
        else if (typeof value === 'string') {
          if (value.length > 40) {
            value = value.substring(0, 37) + '…';
          }
          description = '= \'' + value + '\'';
          incomplete = false;
        }
        else {
          description = '(' + typeof value + ')';
        }
      }
      catch (ex) {
        description = '(' + l10n.lookup('jstypeParseError') + ')';
      }
    }

    return {
      name: prefix + match.prop,
      value: {
        name: prefix + match.prop,
        description: description
      },
      description: description,
      incomplete: incomplete
    };
  }, this);
Exemple #8
0
define(function(require, exports, module) {

'use strict';

var cli = require('gcli/cli');
var host = require('util/host');
var l10n = require('util/l10n');

/**
 * 'cd' command
 */
var cd = {
  item: 'command',
  name: 'cd',
  description: l10n.lookup('cdDesc'),
  manual: l10n.lookup('cdManual'),
  params: [
    {
      name: 'directory',
      type: {
        name: 'file',
        filetype: 'directory',
        existing: 'yes'
      },
      description: l10n.lookup('cdDirectoryDesc'),
    }
  ],
  returnType: 'string',
  exec: function(args, context) {
    context.shell.cwd = args.directory;
    return 'Working directory is now ' + context.shell.cwd;
  }
};

/**
 * 'exec' command
 */
var exec = {
  item: 'command',
  name: 'exec',
  description: l10n.lookup('execDesc'),
  manual: l10n.lookup('execManual'),
  params: [
    {
      name: 'command',
      type: 'string',
      description: l10n.lookup('execCommandDesc'),
    }
  ],
  returnType: 'output',
  exec: function(args, context) {
    var cmdArgs = cli.tokenize(args.command).map(function(arg) {
      return arg.text;
    });
    var cmd = cmdArgs.shift();

    var execSpec = {
      cmd: cmd,
      args: cmdArgs,
      env: context.shell.env,
      cwd: context.shell.cwd
    };

    return host.exec(execSpec).then(function(output) {
      if (output.code === 0) {
        return output;
      }

      throw output.data;
    }, function(output) {
      throw output.data;
    });
  }
};

/**
 * How we display the output of a generic exec command: we have to assume that
 * it is a string to be displayed on a terminal - i.e. in a monospaced font
 */
var outputToView = {
  item: 'converter',
  from: 'output',
  to: 'view',
  exec: function(output, context) {
    return context.createView({
      html: '<pre>${output.data}</pre>',
      data: { output: output }
    });
  }
};

/**
 * How we display the output of a generic exec command: we have to assume that
 * it is a string to be displayed on a terminal - i.e. in a monospaced font
 */
var outputToString = {
  item: 'converter',
  from: 'output',
  to: 'string',
  exec: function(output, context) {
    return output.data;
  }
};

exports.items = [ outputToView, outputToString, cd, exec ];


});
Exemple #9
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');
var canon = require('gcli/canon');
var connector = require('util/connect/connector');

/**
 * A lookup of the current connection
 */
var connections = {};

/**
 * 'connection' type
 */
var connection = {
  item: 'type',
  name: 'connection',
  parent: 'selection',
  lookup: function() {
    return Object.keys(connections).map(function(prefix) {
      return { name: prefix, value: connections[prefix] };
    });
  }
};

/**
 * 'connect' command
 */
var connect = {
  item: 'command',
  name: 'connect',
  description: l10n.lookup('connectDesc'),
  manual: l10n.lookup('connectManual'),
  params: [
    {
      name: 'prefix',
      type: 'string',
      description: l10n.lookup('connectPrefixDesc')
    },
    {
      name: 'host',
      short: 'h',
      type: 'string',
      description: l10n.lookup('connectHostDesc'),
      defaultValue: 'localhost',
      option: true
    },
    {
      name: 'port',
      short: 'p',
      type: { name: 'number', max: 65536, min: 0 },
      description: l10n.lookup('connectPortDesc'),
      defaultValue: connector.defaultPort,
      option: true
    }
  ],
  returnType: 'string',

  exec: function(args, context) {
    if (connections[args.prefix] != null) {
      throw new Error(l10n.lookupFormat('connectDupReply', [ args.prefix ]));
    }

    var cxp = connector.connect(args.prefix, args.host, args.port);
    return cxp.then(function(connection) {
      connections[args.prefix] = connection;

      return connection.getCommandSpecs().then(function(commandSpecs) {
        var remoter = this.createRemoter(args.prefix, connection);
        canon.addProxyCommands(args.prefix, commandSpecs, remoter);

        // commandSpecs doesn't include the parent command that we added
        return l10n.lookupFormat('connectReply',
                                 [ Object.keys(commandSpecs).length + 1 ]);
      }.bind(this));
    }.bind(this));
  },

  /**
   * When we register a set of remote commands, we need to provide the canon
   * with a proxy executor. This is that executor.
   */
  createRemoter: function(prefix, connection) {
    return function(cmdArgs, context) {
      var typed = context.typed;

      // If we've been called using a 'context' then there will be no prefix
      // otherwise we need to remove it
      if (typed.indexOf(prefix) === 0) {
        typed = typed.substring(prefix.length).replace(/^ */, '');
      }

      return connection.execute(typed, cmdArgs).then(function(reply) {
        var typedData = context.typedData(reply.type, reply.data);
        if (!reply.error) {
          return typedData;
        }
        else {
          throw typedData;
        }
      });
    }.bind(this);
  }
};

/**
 * 'disconnect' command
 */
var disconnect = {
  item: 'command',
  name: 'disconnect',
  description: l10n.lookup('disconnectDesc2'),
  manual: l10n.lookup('disconnectManual2'),
  params: [
    {
      name: 'prefix',
      type: 'connection',
      description: l10n.lookup('disconnectPrefixDesc'),
    },
    {
      name: 'force',
      type: 'boolean',
      description: l10n.lookup('disconnectForceDesc'),
      hidden: connector.disconnectSupportsForce,
      option: true
    }
  ],
  returnType: 'string',

  exec: function(args, context) {
    return args.prefix.disconnect(args.force).then(function() {
      var removed = canon.removeProxyCommands(args.prefix.prefix);
      delete connections[args.prefix.prefix];
      return l10n.lookupFormat('disconnectReply', [ removed.length ]);
    });
  }
};

exports.items = [ connection, connect, disconnect ];

});
Exemple #10
0
JavascriptType.prototype.parse = function(arg, context) {
  var typed = arg.text;
  var scope = globalObject;

  // No input is undefined
  if (typed === '') {
    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
  }
  // Just accept numbers
  if (!isNaN(parseFloat(typed)) && isFinite(typed)) {
    return Promise.resolve(new Conversion(typed, arg));
  }
  // Just accept constants like true/false/null/etc
  if (typed.trim().match(/(null|undefined|NaN|Infinity|true|false)/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // Analyze the input text and find the beginning of the last part that
  // should be completed.
  var beginning = this._findCompletionBeginning(typed);

  // There was an error analyzing the string.
  if (beginning.err) {
    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, beginning.err));
  }

  // If the current state is ParseState.COMPLEX, then we can't do completion.
  // so bail out now
  if (beginning.state === ParseState.COMPLEX) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // If the current state is not ParseState.NORMAL, then we are inside of a
  // string which means that no completion is possible.
  if (beginning.state !== ParseState.NORMAL) {
    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
  }

  var completionPart = typed.substring(beginning.startPos);
  var properties = completionPart.split('.');
  var matchProp;
  var prop = undefined;

  if (properties.length > 1) {
    matchProp = properties.pop().trimLeft();
    for (var i = 0; i < properties.length; i++) {
      prop = properties[i].trim();

      // We can't complete on null.foo, so bail out
      if (scope == null) {
        return Promise.resolve(new Conversion(typed, arg, Status.ERROR,
                                        l10n.lookup('jstypeParseScope')));
      }

      if (prop === '') {
        return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
      }

      // Check if prop is a getter function on 'scope'. Functions can change
      // other stuff so we can't execute them to get the next object. Stop here.
      if (this._isSafeProperty(scope, prop)) {
        return Promise.resolve(new Conversion(typed, arg));
      }

      try {
        scope = scope[prop];
      }
      catch (ex) {
        // It would be nice to be able to report this error in some way but
        // as it can happen just when someone types '{sessionStorage.', it
        // almost doesn't really count as an error, so we ignore it
        return Promise.resolve(new Conversion(typed, arg, Status.VALID, ''));
      }
    }
  }
  else {
    matchProp = properties[0].trimLeft();
  }

  // If the reason we just stopped adjusting the scope was a non-simple string,
  // then we're not sure if the input is valid or invalid, so accept it
  if (prop && !prop.match(/^[0-9A-Za-z]*$/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // However if the prop was a simple string, it is an error
  if (scope == null) {
    var message = l10n.lookupFormat('jstypeParseMissing', [ prop ]);
    return Promise.resolve(new Conversion(typed, arg, Status.ERROR, message));
  }

  // If the thing we're looking for isn't a simple string, then we're not going
  // to find it, but we're not sure if it's valid or invalid, so accept it
  if (!matchProp.match(/^[0-9A-Za-z]*$/)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  // Skip Iterators and Generators.
  if (this._isIteratorOrGenerator(scope)) {
    return Promise.resolve(new Conversion(typed, arg));
  }

  var matchLen = matchProp.length;
  var prefix = matchLen === 0 ? typed : typed.slice(0, -matchLen);
  var status = Status.INCOMPLETE;
  var message = '';

  // We really want an array of matches (for sorting) but it's easier to
  // detect existing members if we're using a map initially
  var matches = {};

  // We only display a maximum of MAX_COMPLETION_MATCHES, so there is no point
  // in digging up the prototype chain for matches that we're never going to
  // use. Initially look for matches directly on the object itself and then
  // look up the chain to find more
  var distUpPrototypeChain = 0;
  var root = scope;
  try {
    while (root != null &&
        Object.keys(matches).length < JavascriptType.MAX_COMPLETION_MATCHES) {

      Object.keys(root).forEach(function(property) {
        // Only add matching properties. Also, as we're walking up the
        // prototype chain, properties on 'higher' prototypes don't override
        // similarly named properties lower down
        if (property.indexOf(matchProp) === 0 && !(property in matches)) {
          matches[property] = {
            prop: property,
            distUpPrototypeChain: distUpPrototypeChain
          };
        }
      });

      distUpPrototypeChain++;
      root = Object.getPrototypeOf(root);
    }
  }
  catch (ex) {
    return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
  }

  // Convert to an array for sorting, and while we're at it, note if we got
  // an exact match so we know that this input is valid
  matches = Object.keys(matches).map(function(property) {
    if (property === matchProp) {
      status = Status.VALID;
    }
    return matches[property];
  });

  // The sort keys are:
  // - Being on the object itself, not in the prototype chain
  // - The lack of existence of a vendor prefix
  // - The name
  matches.sort(function(m1, m2) {
    if (m1.distUpPrototypeChain !== m2.distUpPrototypeChain) {
      return m1.distUpPrototypeChain - m2.distUpPrototypeChain;
    }
    // Push all vendor prefixes to the bottom of the list
    return isVendorPrefixed(m1.prop) ?
      (isVendorPrefixed(m2.prop) ? m1.prop.localeCompare(m2.prop) : 1) :
      (isVendorPrefixed(m2.prop) ? -1 : m1.prop.localeCompare(m2.prop));
  });

  // Trim to size. There is a bug for doing a better job of finding matches
  // (bug 682694), but in the mean time there is a performance problem
  // associated with creating a large number of DOM nodes that few people will
  // ever read, so trim ...
  if (matches.length > JavascriptType.MAX_COMPLETION_MATCHES) {
    matches = matches.slice(0, JavascriptType.MAX_COMPLETION_MATCHES - 1);
  }

  // Decorate the matches with:
  // - a description
  // - a value (for the menu) and,
  // - an incomplete flag which reports if we should assume that the user isn't
  //   going to carry on the JS expression with this input so far
  var predictions = matches.map(function(match) {
    var description;
    var incomplete = true;

    if (this._isSafeProperty(scope, match.prop)) {
      description = '(property getter)';
    }
    else {
      try {
        var value = scope[match.prop];

        if (typeof value === 'function') {
          description = '(function)';
        }
        else if (typeof value === 'boolean' || typeof value === 'number') {
          description = '= ' + value;
          incomplete = false;
        }
        else if (typeof value === 'string') {
          if (value.length > 40) {
            value = value.substring(0, 37) + '…';
          }
          description = '= \'' + value + '\'';
          incomplete = false;
        }
        else {
          description = '(' + typeof value + ')';
        }
      }
      catch (ex) {
        description = '(' + l10n.lookup('jstypeParseError') + ')';
      }
    }

    return {
      name: prefix + match.prop,
      value: {
        name: prefix + match.prop,
        description: description
      },
      description: description,
      incomplete: incomplete
    };
  }, this);

  if (predictions.length === 0) {
    status = Status.ERROR;
    message = l10n.lookupFormat('jstypeParseMissing', [ matchProp ]);
  }

  // If the match is the only one possible, and its VALID, predict nothing
  if (predictions.length === 1 && status === Status.VALID) {
    predictions = [];
  }

  return Promise.resolve(new Conversion(typed, arg, status, message,
                                  Promise.resolve(predictions)));
};
Exemple #11
0
JavascriptType.prototype._findCompletionBeginning = function(text) {
  var bodyStack = [];

  var state = ParseState.NORMAL;
  var start = 0;
  var c;
  var complex = false;

  for (var i = 0; i < text.length; i++) {
    c = text[i];
    if (!simpleChars.test(c)) {
      complex = true;
    }

    switch (state) {
      // Normal JS state.
      case ParseState.NORMAL:
        if (c === '"') {
          state = ParseState.DQUOTE;
        }
        else if (c === '\'') {
          state = ParseState.QUOTE;
        }
        else if (c === ';') {
          start = i + 1;
        }
        else if (c === ' ') {
          start = i + 1;
        }
        else if (OPEN_BODY.indexOf(c) != -1) {
          bodyStack.push({
            token: c,
            start: start
          });
          start = i + 1;
        }
        else if (CLOSE_BODY.indexOf(c) != -1) {
          var last = bodyStack.pop();
          if (!last || OPEN_CLOSE_BODY[last.token] != c) {
            return { err: l10n.lookup('jstypeBeginSyntax') };
          }
          if (c === '}') {
            start = i + 1;
          }
          else {
            start = last.start;
          }
        }
        break;

      // Double quote state > " <
      case ParseState.DQUOTE:
        if (c === '\\') {
          i ++;
        }
        else if (c === '\n') {
          return { err: l10n.lookup('jstypeBeginUnterm') };
        }
        else if (c === '"') {
          state = ParseState.NORMAL;
        }
        break;

      // Single quote state > ' <
      case ParseState.QUOTE:
        if (c === '\\') {
          i ++;
        }
        else if (c === '\n') {
          return { err: l10n.lookup('jstypeBeginUnterm') };
        }
        else if (c === '\'') {
          state = ParseState.NORMAL;
        }
        break;
    }
  }

  if (state === ParseState.NORMAL && complex) {
    state = ParseState.COMPLEX;
  }

  return {
    state: state,
    startPos: start
  };
};
Exemple #12
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');
var types = require('gcli/types');
var canon = require('gcli/canon');
var connector = require('util/connect/connector');

/**
 * A lookup of the current connection
 */
var connections = {};

/**
 * 'connect' command
 */
var connect = {
  name: 'connect',
  description: l10n.lookup('connectDesc'),
  manual: l10n.lookup('connectManual'),
  params: [
    {
      name: 'prefix',
      type: 'string',
      description: l10n.lookup('connectPrefixDesc')
    },
    {
      name: 'host',
      type: 'string',
      description: l10n.lookup('connectHostDesc'),
      defaultValue: 'localhost',
      option: true
    },
    {
      name: 'port',
      type: { name: 'number', max: 65536, min: 0 },
      description: l10n.lookup('connectPortDesc'),
      defaultValue: connector.defaultPort,
      option: true
    }
  ],
  returnType: 'string',

  exec: function(args, context) {
    if (connections[args.prefix] != null) {
      throw new Error(l10n.lookupFormat('connectDupReply', [ args.prefix ]));
    }

    var cxp = connector.connect(args.prefix, args.host, args.port);
    return cxp.then(function(connection) {
      connections[args.prefix] = connection;

      return connection.getCommandSpecs().then(function(commandSpecs) {
        var remoter = this.createRemoter(args.prefix, connection);
        canon.addProxyCommands(args.prefix, commandSpecs, remoter);

        // commandSpecs doesn't include the parent command that we added
        return l10n.lookupFormat('connectReply',
                                 [ Object.keys(commandSpecs).length + 1 ]);
      }.bind(this));
    }.bind(this));
  },

  /**
   * When we register a set of remote commands, we need to provide the canon
   * with a proxy executor. This is that executor.
   */
  createRemoter: function(prefix, connection) {
    return function(cmdArgs, context) {
      var typed = context.typed;
      if (typed.indexOf(prefix) !== 0) {
        throw new Error("Missing prefix");
      }
      typed = typed.substring(prefix.length).replace(/^ */, "");

      return connection.execute(typed, cmdArgs).then(function(reply) {
        var typedData = context.typedData(reply.type, reply.data);
        if (!reply.error) {
          return typedData;
        }
        else {
          throw typedData;
        }
      });
    }.bind(this);
  }
};

/**
 * 'connection' type
 */
var connection = {
  name: 'connection',
  parent: 'selection',
  lookup: function() {
    return Object.keys(connections).map(function(prefix) {
      return { name: prefix, value: connections[prefix] };
    });
  }
};

/**
 * 'disconnect' command
 */
var disconnect = {
  name: 'disconnect',
  description: l10n.lookup('disconnectDesc'),
  manual: l10n.lookup('disconnectManual'),
  params: [
    {
      name: 'prefix',
      type: 'connection',
      description: l10n.lookup('disconnectPrefixDesc'),
    }
  ],
  returnType: 'string',

  exec: function(args, context) {
    return args.prefix.disconnect().then(function() {
      var removed = canon.removeProxyCommands(args.prefix.prefix);
      delete connections[args.prefix.prefix];
      return l10n.lookupFormat('disconnectReply', [ removed.length ]);
    });
  }
};


/**
 * Registration and de-registration.
 */
exports.startup = function() {
  types.addType(connection);

  canon.addCommand(connect);
  canon.addCommand(disconnect);
};

exports.shutdown = function() {
  canon.removeCommand(connect);
  canon.removeCommand(disconnect);

  types.removeType(connection);
};


});
Exemple #13
0
define(function(require, exports, module) {

'use strict';

var l10n = require('util/l10n');
var settings = require('gcli/settings');
var view = require('gcli/ui/view');
var Output = require('gcli/cli').Output;

/**
 * Record if the user has clicked on 'Got It!'
 */
exports.items = [
  {
    item: 'setting',
    name: 'hideIntro',
    type: 'boolean',
    description: l10n.lookup('hideIntroDesc'),
    defaultValue: false
  }
];

/**
 * Called when the UI is ready to add a welcome message to the output
 */
exports.maybeShowIntro = function(commandOutputManager, conversionContext) {
  var hideIntro = settings.getSetting('hideIntro');
  if (hideIntro.value) {
    return;
  }

  var output = new Output();
  output.type = 'view';
  commandOutputManager.onOutput({ output: output });

  var viewData = this.createView(null, conversionContext, output);

  output.complete({ isTypedData: true, type: 'view', data: viewData });
};

/**
 * Called when the UI is ready to add a welcome message to the output
 */
exports.createView = function(ignore, conversionContext, output) {
  return view.createView({
    html: require('text!gcli/ui/intro.html'),
    options: { stack: 'intro.html' },
    data: {
      l10n: l10n.propertyLookup,
      onclick: conversionContext.update,
      ondblclick: conversionContext.updateExec,
      showHideButton: (output != null),
      onGotIt: function(ev) {
        var hideIntro = settings.getSetting('hideIntro');
        hideIntro.value = true;
        output.onClose();
      }
    }
  });
};

});