2 * (C) Copyright 2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
4 * (C) Copyright 2008 David Glasser
6 * Use, modification, and distribution are subject to the terms specified in the
10 require("special-buffer.js");
11 require("interactive.js");
13 function help_buffer(window
, element
) {
15 conkeror
.buffer
.call(this, window
, element
, forward_keywords(arguments
));
18 help_buffer
.prototype = {
19 constructor: help_buffer
,
21 __proto__
: special_buffer
.prototype
24 function where_is_command(buffer
, command
) {
25 var list
= find_command_in_keymap(buffer
, command
);
28 msg
= command
+ " is not on any key";
30 msg
= command
+ " is on " + list
.join(", ");
31 buffer
.window
.minibuffer
.message(msg
);
33 interactive("where-is", null, function (I
) {
34 where_is_command(I
.buffer
,
35 (yield I
.minibuffer
.read_command($prompt
= "Where is command:")));
38 function help_document_generator(document
, buffer
) {
39 dom_generator
.call(this, document
, XHTML_NS
);
42 help_document_generator
.prototype = {
43 __proto__
: dom_generator
.prototype,
45 key_binding : function(str
, parent
) {
46 var node
= this.element("span", "class", "key-binding");
49 parent
.appendChild(node
);
53 source_code_reference : function(ref
, parent
) {
54 var f
= this.document
.createDocumentFragment();
55 var module_name
= ref
.module_name
;
56 //f.appendChild(this.text(module_name != null ? "module " : "file "));
57 var x
= this.element("a",
58 "class", "source-code-reference",
59 "href", "javascript:");
60 x
.addEventListener("click", function (event
) {
61 co_call(ref
.open_in_editor());
62 event
.preventDefault();
63 event
.stopPropagation();
64 }, false /* capture */, false /* allow untrusted */);
65 x
.textContent
= (module_name
!= null ? module_name
: ref
.file_name
);
68 parent
.appendChild(f
);
72 command_name : function(name
, parent
) {
73 var node
= this.element("span", "class", "command");
74 this.text(name
, node
);
76 parent
.appendChild(node
);
80 command_reference : function(name
, parent
) {
81 var node
= this.element("a",
83 "href", "javascript:");
84 var buffer
= this.buffer
;
85 node
.addEventListener("click", function (event
) {
86 /* FIXME: don't hardcode browse target */
87 describe_command(buffer
, name
, OPEN_NEW_BUFFER
);
88 event
.preventDefault();
89 event
.stopPropagation();
90 }, false /* capture */, false /* allow untrusted */);
91 this.text(name
, node
);
93 parent
.appendChild(node
);
97 variable_reference : function(name
, parent
) {
98 var node
= this.element("a", "class", "variable", "href", "#");
99 /* FIXME: make this work */
100 this.text(name
, node
);
102 parent
.appendChild(node
);
106 help_text : function(str
, parent
) {
107 var paras
= str
.split("\n");
108 var f
= this.document
.createDocumentFragment();
109 for (var i
= 0; i
< paras
.length
; ++i
) {
111 if (para
.length
== 0)
114 var p
= this.element("p", f
);
116 var regexp
= /`([a-zA-Z0-9_\-$]+)\'/g;
120 while ((match
= regexp
.exec(para
)) != null) {
121 this.text(para
.substring(last_index
, match
.index
), p
);
122 var command
= match
[1];
123 /* FIXME: check if it is a valid command */
124 this.command_reference(command
, p
);
125 last_index
= regexp
.lastIndex
;
127 if (last_index
< para
.length
)
128 this.text(para
.substring(last_index
), p
);
131 parent
.appendChild(f
);
135 add_help_stylesheet : function () {
136 this.add_stylesheet("chrome://conkeror/content/help.css");
140 define_keywords("$binding_list");
141 function describe_bindings_buffer(window
, element
) {
142 this.constructor_begin();
144 special_buffer
.call(this, window
, element
, forward_keywords(arguments
));
145 this.binding_list
= arguments
.$binding_list
;
146 this.constructor_end();
149 describe_bindings_buffer
.prototype = {
152 return help_buffer_keymap
;
155 title
: "Key bindings",
157 description
: "*bindings*",
159 generate : function () {
160 var d
= this.document
;
161 var list
= this.binding_list
;
162 delete this.binding_list
;
164 var list_by_keymap
= {};
165 var keymap_list
= [];
166 for each (let x
in list
) {
167 let name
= x
.bound_in
|| "";
169 if (name
in list_by_keymap
)
170 km
= list_by_keymap
[name
];
172 km
= list_by_keymap
[name
] = {list_by_category
: {}, category_list
: [], name
: name
};
173 keymap_list
.push(km
);
175 let catname
= x
.category
|| "";
177 if (catname
in km
.list_by_category
)
178 cat
= km
.list_by_category
[catname
];
180 cat
= km
.list_by_category
[catname
] = [];
183 km
.category_list
.unshift(cat
);
185 km
.category_list
.push(cat
);
190 var g
= new help_document_generator(d
, this);
191 g
.add_help_stylesheet();
193 d
.body
.setAttribute("class", "help-list");
195 for each (let km
in keymap_list
) {
196 g
.text(km
.name
, g
.element("h1", d
.body
));
197 for each (let cat
in km
.category_list
) {
199 g
.text(cat
.name
, g
.element("h2", d
.body
));
201 let table
= g
.element("table", d
.body
);
202 for (var i
= 0; i
< cat
.length
; ++i
) {
204 let tr
= g
.element("tr", table
, "class", (i
% 2 == 0) ? "even" : "odd");
205 let seq_td
= g
.element("td", tr
, "class", "key-binding");
206 g
.text(bind
.seq
, seq_td
);
207 let command_td
= g
.element("td", tr
, "class", "command");
209 if (bind
.command
!= null) {
210 if (typeof(bind
.command
) == "function") {
211 g
.text("[function]", command_td
);
213 g
.text(bind
.command
, command_td
);
214 let cmd
= interactive_commands
.get(bind
.command
);
216 help_str
= cmd
.shortdoc
;
219 else if (bind
.fallthrough
)
220 g
.text("[pass through]", command_td
);
221 let help_td
= g
.element("td", tr
, "class", "help");
222 g
.text(help_str
|| "", help_td
);
228 __proto__
: special_buffer
.prototype
232 function describe_bindings(buffer
, target
) {
234 for_each_key_binding(buffer
, function (binding_stack
) {
235 var last
= binding_stack
[binding_stack
.length
- 1];
236 if (last
.command
== null && !last
.fallthrough
)
240 for (let i
= binding_stack
.length
- 1; i
>= 0; --i
) {
241 bound_in
= binding_stack
[i
].bound_in
;
245 bound_in
= bound_in
.bound_in
;
248 var bind
= {seq
: format_binding_sequence(binding_stack
),
249 fallthrough
: last
.fallthrough
,
250 command
: last
.command
,
251 bound_in
: bound_in
.name
,
252 category
: last
.category
256 create_buffer(buffer
.window
, buffer_creator(describe_bindings_buffer
,
257 $configuration
= buffer
.configuration
,
258 $binding_list
= list
),
261 interactive("describe-bindings", null, function (I
) {describe_bindings(I
.buffer
, I
.browse_target("describe-bindings"));});
262 default_browse_targets
["describe-bindings"] = "find-url-new-buffer";
265 define_keywords("$command_list");
266 function apropos_command_buffer(window
, element
) {
267 this.constructor_begin();
269 special_buffer
.call(this, window
, element
, forward_keywords(arguments
));
270 this.command_list
= arguments
.$command_list
;
271 this.constructor_end();
274 apropos_command_buffer
.prototype = {
277 return help_buffer_keymap
;
280 title
: "Apropos commands",
282 description
: "*Apropos*",
284 generate : function () {
285 var d
= this.document
;
286 var list
= this.command_list
;
287 delete this.command_list
;
289 var g
= new help_document_generator(d
, this);
290 g
.add_help_stylesheet();
292 d
.body
.setAttribute("class", "help-list");
294 var table
= d
.createElementNS(XHTML_NS
, "table");
295 for (var i
= 0; i
< list
.length
; ++i
) {
296 var binding
= list
[i
];
297 var tr
= d
.createElementNS(XHTML_NS
, "tr");
298 tr
.setAttribute("class", (i
% 2 == 0) ? "even" : "odd");
300 var command_td
= d
.createElementNS(XHTML_NS
,"td");
301 g
.command_reference(binding
.name
, command_td
);
304 if (binding
.cmd
.shortdoc
!= null)
305 shortdoc
= binding
.cmd
.shortdoc
;
306 tr
.appendChild(command_td
);
308 var shortdoc_td
= d
.createElementNS(XHTML_NS
, "td");
309 shortdoc_td
.setAttribute("class", "help");
310 shortdoc_td
.textContent
= shortdoc
;
311 tr
.appendChild(shortdoc_td
);
313 table
.appendChild(tr
);
315 d
.body
.appendChild(table
);
318 __proto__
: special_buffer
.prototype
322 /* TODO: support regexps/etc. */
323 function apropos_command(buffer, substring, target) {
325 interactive_commands.for_each(function (name, cmd) {
326 if (name.indexOf(substring) != -1) {
327 var binding = {name: name, cmd: cmd};
331 list.sort(function (a,b) {
338 create_buffer(buffer.window, buffer_creator(apropos_command_buffer,
339 $configuration = buffer.configuration,
340 $command_list = list),
344 interactive("apropos-command", "List commands whose names contain a given substring.",
346 apropos_command(I.buffer,
347 (yield I.minibuffer.read($prompt = "Apropos command:",
348 $history = "apropos")),
349 I.browse_target("apropos-command"));
351 default_browse_targets["apropos-command"] = "find-url-new-buffer";
355 define_keywords("$command", "$bindings");
356 function describe_command_buffer(window, element) {
357 this.constructor_begin();
359 special_buffer.call(this, window, element, forward_keywords(arguments));
360 this.bindings = arguments.$bindings;
361 this.command = arguments.$command;
362 this.cmd = interactive_commands.get(this.command);
363 this.source_code_reference = this.cmd.source_code_reference;
364 this.constructor_end();
367 describe_command_buffer.prototype = {
370 return help_buffer_keymap;
373 get title() { return "Command help: " + this.command; },
375 description : "*help*",
377 generate : function () {
378 var d = this.document;
380 var g = new help_document_generator(d, this);
382 g.add_help_stylesheet();
383 d.body.setAttribute("class", "describe-command");
387 p = g.element("p", d.body);
388 g.command_reference(this.command, p);
389 var cmd = interactive_commands.get(this.command);
390 if (cmd.source_code_reference) {
391 g.text(" is an interactive command in ", p);
392 g.source_code_reference(cmd.source_code_reference, p);
395 g.text(" is an interactive command.", p);
398 if (this.bindings.length > 0) {
399 p = g.element("p", d.body);
400 g.text("It is bound to ", p);
401 for (var i = 0; i < this.bindings.length; ++i) {
404 g.key_binding(this.bindings[i], p);
410 g.help_text(cmd.doc, d.body);
413 __proto__: special_buffer.prototype
417 function describe_command(buffer, command, target) {
418 var bindings = find_command_in_keymap(buffer, command);
419 create_buffer(buffer.window,
420 buffer_creator(describe_command_buffer,
421 $configuration = buffer.configuration,
423 $bindings = bindings),
426 interactive("describe-command", null, function (I) {
427 describe_command(I.buffer, (yield I.minibuffer.read_command($prompt = "Describe command:")),
428 I.browse_target("describe-command"));
430 default_browse_targets["describe-command"] = "find-url-new-buffer";
437 function view_referenced_source_code(buffer) {
438 if (buffer.source_code_reference == null)
439 throw interactive_error("Command not valid in current buffer.");
440 yield buffer.source_code_reference.open_in_editor();
442 interactive("view-referenced-source-code", null, function (I) {yield view_referenced_source_code(I.buffer);});
445 define_keywords("$binding", "$other_bindings", "$key_sequence");
446 function describe_key_buffer(window, element) {
447 this.constructor_begin();
449 special_buffer.call(this, window, element, forward_keywords(arguments));
450 this.key_sequence = arguments.$key_sequence;
451 this.bindings = arguments.$other_bindings;
452 this.bind = arguments.$binding;
453 this.source_code_reference = this.bind.source_code_reference;
454 this.constructor_end();
457 describe_key_buffer.prototype = {
460 return help_buffer_keymap;
463 get title() { return "Key help: " + this.key_sequence; },
465 description : "*help*",
467 generate : function () {
468 var d = this.document;
470 var g = new help_document_generator(d, this);
472 g.add_help_stylesheet();
473 d.body.setAttribute("class", "describe-key");
477 p = g.element("p", d.body);
478 g.key_binding(this.key_sequence, p);
479 g.text(" is bound to the command ", p);
480 var command = this.bind.command;
482 g.command_name("[pass through]", p);
484 g.command_reference(command, p);
485 if (this.source_code_reference) {
487 g.source_code_reference(this.source_code_reference, p);
491 if (command != null) {
492 p = g.element("p", d.body);
493 g.command_reference(command, p);
494 var cmd = interactive_commands.get(command);
495 if (cmd.source_code_reference) {
496 g.text(" is an interactive command in ", p);
497 g.source_code_reference(cmd.source_code_reference, p);
500 g.text(" is an interactive command.", p);
503 if (this.bindings.length > 0) {
504 p = g.element("p", d.body);
505 g.text("It is bound to ", p);
506 for (var i = 0; i < this.bindings.length; ++i) {
509 g.key_binding(this.bindings[i], p);
515 g.help_text(cmd.doc, d.body);
519 __proto__: special_buffer.prototype
523 function describe_key(buffer, key_info, target) {
525 var seq = key_info[0];
526 var bind = key_info[1];
529 bindings = find_command_in_keymap(buffer, bind.command);
533 create_buffer(buffer.window,
534 buffer_creator(describe_key_buffer,
535 $configuration = buffer.configuration,
536 $key_sequence = seq.join(" "),
537 $other_bindings = bindings,
542 function describe_key_briefly(buffer, key_info) {
544 var seq = key_info[0];
545 var bind = key_info[1];
547 buffer.window.minibuffer.message(seq.join(" ") + " runs the command " + bind.command);
550 interactive("describe-key", null, function (I) {
551 describe_key(I.buffer,
552 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)),
553 I.browse_target("describe-key"));
555 interactive("describe-key-briefly", null, function (I) {
556 describe_key_briefly(I.buffer,
557 (yield I.minibuffer.read_key_binding($prompt = "Describe key:", $buffer = I.buffer)));
559 default_browse_targets["describe-key"] = "find-url-new-buffer";
564 define_keywords("$variable");
565 function describe_variable_buffer(window, element) {
566 this.constructor_begin();
568 special_buffer.call(this, window, element, forward_keywords(arguments));
569 this.variable = arguments.$variable;
570 this.cmd = user_variables.get(this.variable);
571 this.source_code_reference = this.cmd.source_code_reference;
572 this.constructor_end();
575 function pretty_print_value(value) {
576 if (value === undefined)
580 if (typeof(value) == "object")
581 return value.toSource();
582 if (typeof(value) == "function")
583 return value.toString();
584 if (typeof(value) == "string") {
585 let s = value.toSource();
586 // toSource returns: (new String("<blah>"))
587 // we want just: "<blah>"
588 return s.substring(12, s.length - 2);
590 return new String(value);
593 describe_variable_buffer.prototype = {
596 return help_buffer_keymap;
599 get title() { return "Variable help: " + this.variable; },
601 description : "*help*",
603 generate : function () {
604 var d = this.document;
606 var g = new help_document_generator(d, this);
608 g.add_help_stylesheet();
609 d.body.setAttribute("class", "describe-variable");
613 p = g.element("p", d.body);
614 g.variable_reference(this.variable, p);
615 var uvar = user_variables.get(this.variable);
616 if (uvar.source_code_reference) {
617 g.text(" is a user variable in ", p);
618 g.source_code_reference(uvar.source_code_reference, p);
621 g.text(" is a user variable.", p);
624 p = g.element("p", d.body);
625 g.text("Its value is: ", p);
627 let s = pretty_print_value(conkeror[this.variable]);
628 let pre = g.element("pre", p);
632 if (uvar.doc != null)
633 g.help_text(uvar.doc, d.body);
635 if (uvar.default_value !== undefined) {
636 p = g.element("p", d.body);
637 g.text("Its default value is: ", p);
639 let s = pretty_print_value(uvar.default_value);
640 let pre = g.element("pre", p);
646 __proto__: special_buffer.prototype
650 function describe_variable(buffer, variable, target) {
651 create_buffer(buffer.window,
652 buffer_creator(describe_variable_buffer,
653 $configuration = buffer.configuration,
654 $variable = variable),
657 interactive("describe-variable", null, function (I) {
658 describe_variable(I.buffer, (yield I.minibuffer.read_user_variable($prompt = "Describe variable:")),
659 I.browse_target("describe-variable"));
661 default_browse_targets["describe-variable"] = "find-url-new-buffer";
663 function describe_preference(buffer, preference, target) {
664 let key = preference.charAt(0).toUpperCase() + preference.substring(1);
665 let url = "http://kb.mozillazine.org/" + key;
666 browser_element_follow(buffer, target, url);
668 interactive("describe-preference", null, function (I) {
669 describe_preference(I.buffer, (yield I.minibuffer.read_preference($prompt = "Describe preference:")),
670 I.browse_target("describe-preference"));
672 default_browse_targets["describe-preference"] = "find-url-new-buffer";