diff --git a/browser/devtools/webconsole/GcliCommands.jsm b/browser/devtools/webconsole/GcliCommands.jsm index e5c3d17b6e5..227b0db5d26 100644 --- a/browser/devtools/webconsole/GcliCommands.jsm +++ b/browser/devtools/webconsole/GcliCommands.jsm @@ -62,40 +62,6 @@ gcli.addCommand({ }); -let canon = gcli._internal.require("gcli/canon"); - -/** - * 'help' command - */ -gcli.addCommand({ - name: "help", - returnType: "html", - description: gcli.lookup("helpDesc"), - exec: function Command_help(args, context) { - let output = []; - - output.push("" + gcli.lookup("helpAvailable") + ":
"); - - let commandNames = canon.getCommandNames(); - commandNames.sort(); - - output.push(""); - for (let i = 0; i < commandNames.length; i++) { - let command = canon.getCommand(commandNames[i]); - if (!command.hidden && command.description) { - output.push(""); - output.push('"); - output.push(""); - output.push(""); - } - } - output.push("
' + command.name + "→ " + command.description + "
"); - - return output.join(""); - } -}); - - /** * 'console' command */ diff --git a/browser/devtools/webconsole/HUDService.jsm b/browser/devtools/webconsole/HUDService.jsm index 7a95438dc09..a580d7acfd0 100644 --- a/browser/devtools/webconsole/HUDService.jsm +++ b/browser/devtools/webconsole/HUDService.jsm @@ -92,6 +92,12 @@ XPCOMUtils.defineLazyGetter(this, "NetUtil", function () { return obj.NetUtil; }); +XPCOMUtils.defineLazyGetter(this, "Templater", function () { + var obj = {}; + Cu.import("resource:///modules/devtools/Templater.jsm", obj); + return obj.Templater; +}); + XPCOMUtils.defineLazyGetter(this, "PropertyPanel", function () { var obj = {}; try { @@ -6854,14 +6860,36 @@ GcliTerm.prototype = { let output = aEvent.output.output; if (aEvent.output.command.returnType == "html" && typeof output == "string") { - let frag = this.document.createRange().createContextualFragment( + output = this.document.createRange().createContextualFragment( '
' + - output + '
'); - - output = this.document.createElementNS(HTML_NS, "div"); - output.appendChild(frag); + output + '').firstChild; } - this.writeOutput(output); + + let element = this.document.createRange().createContextualFragment( + '' + + ' ').firstChild; + + let hud = HUDService.getHudReferenceById(this.hudId); + let timestamp = ConsoleUtils.timestamp(); + new Templater().processNode(element, { + iconContainerStyle: "margin-left=" + (hud.groupDepth * GROUP_INDENT) + "px", + output: output, + timestamp: timestamp, + timestampString: ConsoleUtils.timestampString(timestamp), + clipboardText: output.innerText, + id: "console-msg-" + HUDService.sequenceId() + }); + + ConsoleUtils.setMessageType(element, CATEGORY_OUTPUT, SEVERITY_LOG); + ConsoleUtils.outputMessageNode(element, this.hudId); }, /** diff --git a/browser/devtools/webconsole/gcli.jsm b/browser/devtools/webconsole/gcli.jsm index cf57f6abd5a..a4b85f278b1 100644 --- a/browser/devtools/webconsole/gcli.jsm +++ b/browser/devtools/webconsole/gcli.jsm @@ -686,7 +686,7 @@ var mozl10n = {}; })(mozl10n); -define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/ui/display'], function(require, exports, module) { +define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/commands/help', 'gcli/ui/display'], function(require, exports, module) { // The API for use by command authors exports.addCommand = require('gcli/canon').addCommand; @@ -699,6 +699,7 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types require('gcli/types/javascript').startup(); require('gcli/types/node').startup(); require('gcli/cli').startup(); + require('gcli/commands/help').startup(); var Requisition = require('gcli/cli').Requisition; var Display = require('gcli/ui/display').Display; @@ -1029,7 +1030,8 @@ canon.removeCommand = function removeCommand(commandOrName) { * @param name The name of the command to retrieve */ canon.getCommand = function getCommand(name) { - return commands[name]; + // '|| undefined' is to silence 'reference to undefined property' warnings + return commands[name] || undefined; }; /** @@ -1190,8 +1192,16 @@ exports.createEvent = function(name) { var dom = {}; +/** + * XHTML namespace + */ dom.NS_XHTML = 'http://www.w3.org/1999/xhtml'; +/** + * XUL namespace + */ +dom.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; + /** * Create an HTML or XHTML element depending on whether the document is HTML * or XML based. Where HTML/XHTML elements are distinguished by whether they @@ -1249,7 +1259,7 @@ dom.setInnerHtml = function(elem, html) { dom.clearElement(elem); html = '
' + html + '
'; var range = elem.ownerDocument.createRange(); - var child = range.createContextualFragment(html).childNodes[0]; + var child = range.createContextualFragment(html).firstChild; while (child.hasChildNodes()) { elem.appendChild(child.firstChild); } @@ -4527,7 +4537,7 @@ Requisition.prototype.exec = function(input) { }).bind(this); try { - var context = new ExecutionContext(this.environment, this.document); + var context = new ExecutionContext(this); var reply = command.exec(args, context); if (reply != null && reply.isPromise) { @@ -5012,9 +5022,10 @@ exports.Requisition = Requisition; /** * Functions and data related to the execution of a command */ -function ExecutionContext(environment, document) { - this.environment = environment; - this.document = document; +function ExecutionContext(requisition) { + this.requisition = requisition; + this.environment = requisition.environment; + this.document = requisition.document; } ExecutionContext.prototype.createPromise = function() { @@ -5041,6 +5052,269 @@ define('gcli/promise', ['require', 'exports', 'module' ], function(require, expo * http://opensource.org/licenses/BSD-3-Clause */ +define('gcli/commands/help', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/l10n', 'gcli/ui/domtemplate', 'text!gcli/commands/help.css', 'text!gcli/commands/help_intro.html', 'text!gcli/commands/help_list.html', 'text!gcli/commands/help_man.html'], function(require, exports, module) { +var help = exports; + + +var canon = require('gcli/canon'); +var util = require('gcli/util'); +var l10n = require('gcli/l10n'); + +var Templater = require('gcli/ui/domtemplate').Templater; + +var helpCss = require('text!gcli/commands/help.css'); +var helpStyle = undefined; +var helpIntroHtml = require('text!gcli/commands/help_intro.html'); +var helpIntroTemplate = undefined; +var helpListHtml = require('text!gcli/commands/help_list.html'); +var helpListTemplate = undefined; +var helpManHtml = require('text!gcli/commands/help_man.html'); +var helpManTemplate = undefined; + +/** + * 'help' command + * We delay definition of helpCommandSpec until help.startup() to ensure that + * the l10n strings have been loaded + */ +var helpCommandSpec; + +/** + * Registration and de-registration. + */ +help.startup = function() { + + helpCommandSpec = { + name: 'help', + description: l10n.lookup('helpDesc'), + manual: l10n.lookup('helpManual'), + params: [ + { + name: 'search', + type: 'string', + description: l10n.lookup('helpSearchDesc'), + manual: l10n.lookup('helpSearchManual'), + defaultValue: null + } + ], + returnType: 'html', + + exec: function(args, context) { + help.onFirstUseStartup(context.document); + + var match = canon.getCommand(args.search); + if (match) { + var clone = helpManTemplate.cloneNode(true); + new Templater().processNode(clone, getManTemplateData(match, context)); + return clone; + } + + var parent = util.dom.createElement(context.document, 'div'); + if (!args.search) { + parent.appendChild(helpIntroTemplate.cloneNode(true)); + } + parent.appendChild(helpListTemplate.cloneNode(true)); + new Templater().processNode(parent, getListTemplateData(args, context)); + return parent; + } + }; + + canon.addCommand(helpCommandSpec); +}; + +help.shutdown = function() { + canon.removeCommand(helpCommandSpec); + + helpListTemplate = undefined; + helpStyle.parentElement.removeChild(helpStyle); + helpStyle = undefined; +}; + +/** + * Called when the command is executed + */ +help.onFirstUseStartup = function(document) { + if (!helpIntroTemplate) { + helpIntroTemplate = util.dom.createElement(document, 'div'); + util.dom.setInnerHtml(helpIntroTemplate, helpIntroHtml); + } + if (!helpListTemplate) { + helpListTemplate = util.dom.createElement(document, 'div'); + util.dom.setInnerHtml(helpListTemplate, helpListHtml); + } + if (!helpManTemplate) { + helpManTemplate = util.dom.createElement(document, 'div'); + util.dom.setInnerHtml(helpManTemplate, helpManHtml); + } + if (!helpStyle && helpCss != null) { + helpStyle = util.dom.importCss(helpCss, document); + } +}; + +/** + * Find an element within the passed element with the class gcli-help-command + * and update the requisition to contain this text. + */ +function updateCommand(element, context) { + context.requisition.update({ + typed: element.querySelector('.gcli-help-command').textContent + }); +} + +/** + * Find an element within the passed element with the class gcli-help-command + * and execute this text. + */ +function executeCommand(element, context) { + context.requisition.exec({ + visible: true, + typed: element.querySelector('.gcli-help-command').textContent + }); +} + +/** + * Create a block of data suitable to be passed to the help_list.html template + */ +function getListTemplateData(args, context) { + return { + onclick: function(ev) { + updateCommand(ev.currentTarget, context); + }, + + ondblclick: function(ev) { + executeCommand(ev.currentTarget, context); + }, + + getHeading: function() { + return args.search == null ? + 'Available Commands:' : + 'Commands starting with \'' + args.search + '\':'; + }, + + getMatchingCommands: function() { + var matching = canon.getCommands().filter(function(command) { + if (args.search && command.name.indexOf(args.search) !== 0) { + // Filtered out because they don't match the search + return false; + } + if (!args.search && command.name.indexOf(' ') != -1) { + // We don't show sub commands with plain 'help' + return false; + } + return true; + }); + matching.sort(); + return matching; + } + }; +} + +/** + * Create a block of data suitable to be passed to the help_man.html template + */ +function getManTemplateData(command, context) { + return { + command: command, + + onclick: function(ev) { + updateCommand(ev.currentTarget, context); + }, + + getTypeDescription: function(param) { + var input = ''; + if (param.defaultValue === undefined) { + input = 'required'; + } + else if (param.defaultValue === null) { + input = 'optional'; + } + else { + input = param.defaultValue; + } + return '(' + param.type.name + ', ' + input + ')'; + } + }; +} + +}); +/* + * Copyright 2009-2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE.txt or: + * http://opensource.org/licenses/BSD-3-Clause + */ + +define('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) { + + Components.utils.import("resource:///modules/devtools/Templater.jsm"); + exports.Templater = Templater; + +}); +define("text!gcli/commands/help.css", [], void 0); +define("text!gcli/commands/help_intro.html", [], "\n" + + "

Welcome to GCLI

\n" + + "\n" + + "

GCLI is an experiment to create a highly usable JavaScript command line for developers.

\n" + + "\n" + + "

\n" + + " Useful links:\n" + + " source (BSD),\n" + + " documentation (for users/embedders),\n" + + " Mozilla feature page (for GCLI in the web console).\n" + + "

\n" + + ""); + +define("text!gcli/commands/help_list.html", [], "\n" + + "

${getHeading()}

\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
${command.name}\n" + + " ${command.description}\n" + + " help ${command.name}\n" + + "
\n" + + ""); + +define("text!gcli/commands/help_man.html", [], "\n" + + "

${command.name}

\n" + + "\n" + + "

\n" + + " Synopsis:\n" + + " \n" + + " ${command.name}\n" + + " \n" + + " ${param.defaultValue !== undefined ? '[' + param.name + ']' : param.name}\n" + + " \n" + + " \n" + + "

\n" + + "\n" + + "

Description:

\n" + + "\n" + + "

\n" + + " ${command.manual || command.description}\n" + + "

\n" + + "\n" + + "

Parameters:

\n" + + "\n" + + "\n" + + ""); + +/* + * Copyright 2009-2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE.txt or: + * http://opensource.org/licenses/BSD-3-Clause + */ + define('gcli/ui/display', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/arg_fetch', 'gcli/ui/menu', 'gcli/ui/focus'], function(require, exports, module) { var Inputter = require('gcli/ui/inputter').Inputter; @@ -6984,18 +7258,6 @@ CommandMenu.prototype.onCommandChange = function(ev) { exports.CommandMenu = CommandMenu; -}); -/* - * Copyright 2009-2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE.txt or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -define('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) { - - Components.utils.import("resource:///modules/devtools/Templater.jsm"); - exports.Templater = Templater; - }); define("text!gcli/ui/menu.css", [], void 0); define("text!gcli/ui/menu.html", [], "\n" + diff --git a/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js b/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js index a806a8ca09c..58ed6b7f843 100644 --- a/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js +++ b/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js @@ -86,9 +86,9 @@ function testCallCommands() { gcliterm.opts.display.inputter.setInput("echo hello world"); gcliterm.opts.requisition.exec(); - let nodes = hud.outputNode.querySelectorAll("description"); + let nodes = hud.outputNode.querySelectorAll(".gcliterm-msg-body"); - is(nodes.length, 2, "Right number of output nodes"); + is(nodes.length, 1, "Right number of output nodes"); ok(/hello world/.test(nodes[0].textContent), "the command's output is correct."); gcliterm.clearOutput(); diff --git a/browser/locales/en-US/chrome/browser/devtools/gcli.properties b/browser/locales/en-US/chrome/browser/devtools/gcli.properties index 2f0c73a40a8..aa55bc5aab4 100644 --- a/browser/locales/en-US/chrome/browser/devtools/gcli.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties @@ -94,3 +94,24 @@ nodeParseMultiple=Too many matches (%S) # displayed. nodeParseNone=No matches +# LOCALIZATION NOTE (helpDesc): A very short description of the 'help' +# command. This string is designed to be shown in a menu alongside the command +# name, which is why it should be as short as possible. See helpManual for a +# fuller description of what it does. +helpDesc=Get help on the available commands + +# LOCALIZATION NOTE (helpManual): A fuller description of the 'help' command. +# Displayed when the user asks for help on what it does. +helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found). + +# LOCALIZATION NOTE (helpSearchDesc): A very short description of the 'search' +# parameter to the 'help' command. See helpSearchManual for a fuller +# description of what it does. This string is designed to be shown in a dialog +# with restricted space, which is why it should be as short as possible. +helpSearchDesc=Search string + +# LOCALIZATION NOTE (helpSearchManual): A fuller description of the 'search' +# parameter to the 'help' command. Displayed when the user asks for help on +# what it does. +helpSearchManual=A search string to use in narrowing down the list of commands that are displayed to the user. Any part of the string can match, regular expressions are not supported. + diff --git a/browser/themes/gnomestripe/devtools/gcli.css b/browser/themes/gnomestripe/devtools/gcli.css index a1ab57724d0..8ec87ba9521 100644 --- a/browser/themes/gnomestripe/devtools/gcli.css +++ b/browser/themes/gnomestripe/devtools/gcli.css @@ -109,10 +109,6 @@ border-bottom: 1px solid threedshadow; } -.gcli-help-right { - text-align: right; -} - .gcliterm-menu { display: -moz-box; -moz-box-flex: 1; @@ -122,6 +118,32 @@ display: none; } +.gcliterm-msg-body { + margin-top: 0; + margin-bottom: 3px; + -moz-margin-start: 3px; + -moz-margin-end: 6px; +} + +/* Extract from display.css, we only want these 2 rules */ + +.gcli-out-shortcut { + border: 1px solid #999; + border-radius: 3px; + padding: 0 4px; + margin: 0 4px; + font-size: 70%; + color: #666; + cursor: pointer; + vertical-align: bottom; +} + +.gcli-out-shortcut:before { + color: #66F; + content: '\bb'; + padding: 0 2px; +} + /* * The language of a console is not en_US or any other common language * (i.e we don't attempt to translate 'console.log(x)') @@ -151,6 +173,10 @@ /* From: $GCLI/mozilla/gcli/ui/gcliterm-gnomestripe.css */ +.gcli-out-shortcut { + font-family: "DejaVu Sans Mono", monospace; +} + /* From: $GCLI/lib/gcli/ui/arg_fetch.css */ .gcli-argfetch { @@ -284,3 +310,45 @@ color: #66F; font-weight: bold; } + +/* From: $GCLI/lib/gcli/commands/help.css */ + +.gcli-help-name { + text-align: end; +} + +.gcli-help-arrow { + font-size: 70%; + color: #AAA; +} + +.gcli-help-synopsis { + font-family: monospace; + font-weight: normal; + padding: 0 3px; + margin: 0 10px; + border: 1px solid #999; + border-radius: 3px; + color: #666; + cursor: pointer; + display: inline-block; +} + +.gcli-help-synopsis:before { + color: #66F; + content: '\bb'; +} + +.gcli-help-description { + margin: 0 20px; + padding: 0; +} + +.gcli-help-parameter { + margin: 0 30px; + padding: 0; +} + +.gcli-help-header { + margin: 10px 0 6px; +} diff --git a/browser/themes/pinstripe/devtools/gcli.css b/browser/themes/pinstripe/devtools/gcli.css index 7c262437ffb..a3f0ee6d8ee 100644 --- a/browser/themes/pinstripe/devtools/gcli.css +++ b/browser/themes/pinstripe/devtools/gcli.css @@ -109,10 +109,6 @@ border-bottom: 1px solid threedshadow; } -.gcli-help-right { - text-align: right; -} - .gcliterm-menu { display: -moz-box; -moz-box-flex: 1; @@ -122,6 +118,32 @@ display: none; } +.gcliterm-msg-body { + margin-top: 0; + margin-bottom: 3px; + -moz-margin-start: 3px; + -moz-margin-end: 6px; +} + +/* Extract from display.css, we only want these 2 rules */ + +.gcli-out-shortcut { + border: 1px solid #999; + border-radius: 3px; + padding: 0 4px; + margin: 0 4px; + font-size: 70%; + color: #666; + cursor: pointer; + vertical-align: bottom; +} + +.gcli-out-shortcut:before { + color: #66F; + content: '\bb'; + padding: 0 2px; +} + /* * The language of a console is not en_US or any other common language * (i.e we don't attempt to translate 'console.log(x)') @@ -155,6 +177,10 @@ padding-top: 6px !important; } +.gcli-out-shortcut { + font-family: Menlo, Monaco, monospace; +} + /* From: $GCLI/lib/gcli/ui/arg_fetch.css */ .gcli-argfetch { @@ -288,3 +314,45 @@ color: #66F; font-weight: bold; } + +/* From: $GCLI/lib/gcli/commands/help.css */ + +.gcli-help-name { + text-align: end; +} + +.gcli-help-arrow { + font-size: 70%; + color: #AAA; +} + +.gcli-help-synopsis { + font-family: monospace; + font-weight: normal; + padding: 0 3px; + margin: 0 10px; + border: 1px solid #999; + border-radius: 3px; + color: #666; + cursor: pointer; + display: inline-block; +} + +.gcli-help-synopsis:before { + color: #66F; + content: '\bb'; +} + +.gcli-help-description { + margin: 0 20px; + padding: 0; +} + +.gcli-help-parameter { + margin: 0 30px; + padding: 0; +} + +.gcli-help-header { + margin: 10px 0 6px; +} diff --git a/browser/themes/winstripe/devtools/gcli.css b/browser/themes/winstripe/devtools/gcli.css index 044fa7d5cd7..a8cc778276b 100644 --- a/browser/themes/winstripe/devtools/gcli.css +++ b/browser/themes/winstripe/devtools/gcli.css @@ -109,10 +109,6 @@ border-bottom: 1px solid threedshadow; } -.gcli-help-right { - text-align: right; -} - .gcliterm-menu { display: -moz-box; -moz-box-flex: 1; @@ -122,6 +118,32 @@ display: none; } +.gcliterm-msg-body { + margin-top: 0; + margin-bottom: 3px; + -moz-margin-start: 3px; + -moz-margin-end: 6px; +} + +/* Extract from display.css, we only want these 2 rules */ + +.gcli-out-shortcut { + border: 1px solid #999; + border-radius: 3px; + padding: 0 4px; + margin: 0 4px; + font-size: 70%; + color: #666; + cursor: pointer; + vertical-align: bottom; +} + +.gcli-out-shortcut:before { + color: #66F; + content: '\bb'; + padding: 0 2px; +} + /* * The language of a console is not en_US or any other common language * (i.e we don't attempt to translate 'console.log(x)') @@ -151,6 +173,10 @@ /* From: $GCLI/mozilla/gcli/ui/gcliterm-winstripe.css */ +.gcli-out-shortcut { + font-family: Consolas, Inconsolata, "Courier New", monospace; +} + /* From: $GCLI/lib/gcli/ui/arg_fetch.css */ .gcli-argfetch { @@ -284,3 +310,45 @@ color: #66F; font-weight: bold; } + +/* From: $GCLI/lib/gcli/commands/help.css */ + +.gcli-help-name { + text-align: end; +} + +.gcli-help-arrow { + font-size: 70%; + color: #AAA; +} + +.gcli-help-synopsis { + font-family: monospace; + font-weight: normal; + padding: 0 3px; + margin: 0 10px; + border: 1px solid #999; + border-radius: 3px; + color: #666; + cursor: pointer; + display: inline-block; +} + +.gcli-help-synopsis:before { + color: #66F; + content: '\bb'; +} + +.gcli-help-description { + margin: 0 20px; + padding: 0; +} + +.gcli-help-parameter { + margin: 0 30px; + padding: 0; +} + +.gcli-help-header { + margin: 10px 0 6px; +}