1 Access keys (i.e., keyboard shortcuts) can be defined for common
2 features. Something like the following:
11 * S - Save the current page (when editing)
12 * C - Cancel the current edit
13 * V - Preview the current page
15 Then, for example, in Firefox one could press Alt+Shift+E to edit the
18 For links, this is implemented as:
20 <a href="recentchanges/" accesskey="r">RecentChanges</a>
22 and for forms buttons:
24 <input type="submit" value="Submit" accesskey="s"/>
26 --[[JasonBlevins]], March 21, 2008 18:05 EDT
30 There were also a few thoughts about access keys on the
31 [[main_discussion_page|index/discussion]]. Now moved to here:
33 > Would anyone else find this a valuable addition. In oddmuse and instiki (the only other
34 > wiki engines I am currently using, the edit, home, and submit link tags have an
35 > accesskey attribute. I find it nice not to have to resort to the mouse for those
36 > actions. However, it may not be something everyone appreciates. Any thoughts?
37 > --[Mazirian](http://mazirian.com)
39 > > Maybe, although it would need to take the critisism at
40 > > <http://www.cs.tut.fi/~jkorpela/forms/accesskey.html> into account.
42 > >> Thank you for that link. Given that the edit link is the first thing you tab to
43 > >> in the current layout, I guess it isn't all that necessary. I have had a
44 > >> a user complaint recently that Alt-e in oddmuse was overriding his access
45 > >> to the browser menu.
49 The main criticism there it
50 seems is that some browsers implement access keys in a way (via the Alt
51 key) that allows them to override built-in keyboard shortcuts. I
52 believe this is not a problem any longer in Firefox (which uses the
53 Shift+Alt prefix) but I suppose it could still be a problem in other
56 Another criticism is that most browsers do not display the access keys
57 that are defined. The [article][] cited on the main discussion page
58 suggests underlining the relevant mnemonic. I think it would be
59 sufficient to just list them in the basewiki documentation somewhere.
61 [article]: http://www.cs.tut.fi/~jkorpela/forms/accesskey.html
63 It's an unfortunate situation—I'd like an alternative to the
64 rodent but there are quite a few downsides to using access keys.
65 Tabbing isn't quite the same as a nice shortcut key. There's always
68 --[[JasonBlevins]], March 22, 2008 10:35 EDT
72 I've written a plugin to implement access keys, configured using a wiki page similar to [[shortcuts]]. It works for links and most form submit buttons.
74 As I am new to ikiwiki plugin writing, feedback is greatly appreciated.
76 [[!toggle id="accesskeys" text="Toggle: accesskeys.pm"]]
78 [[!toggleable id="accesskeys" text="""
82 package IkiWiki::Plugin::accesskeys;
91 accesskeys.pm - IkiWiki module to implement access keys (keyboard shortcuts)
95 v.5.0 - initial version
99 Access keys are defined on a page called B<accesskeys>, using the C<accesskey> directive.
102 [[!accesskey command="Save Page" key="s"]]
104 B<command> may contain only alphanumeric characters (and spaces), and must be a complete
105 match to the target link or submit button's display name.
107 B<key> may only be a single alphanumeric character.
109 The access key is applied to the first matching link on a page (including header), or the
110 first matching submit button in the @buttons array.
112 The wiki must be completely rebuilt every time the B<accesskeys> page changes.
114 =head2 Sample accesskeys page
116 [[!if test="enabled(accesskeys)"
117 then="This wiki has accesskeys **enabled**."
118 else="This wiki has accesskeys **disabled**."]]
120 This page controls what access keys the wiki uses.
122 * [[!accesskey command="Save Page" key="s"]]
123 * [[!accesskey command="Cancel" key="c"]]
124 * [[!accesskey command="Preview" key="v"]]
125 * [[!accesskey command="Edit" key="e"]]
126 * [[!accesskey command="RecentChanges" key="c"]]
127 * [[!accesskey command="Preferences" key="p"]]
128 * [[!accesskey command="Discussion" key="d"]]
130 =head1 IMPLEMENTATION
132 This plugin uses the following flow:
136 =item 1. Override default CGI::FormBuilder::submit function
138 FormBuilder does not support any arbitrary modification of it's submit buttons, so
139 in order to add the necessary attributes you have to intercept the internal function
140 call which generates the formatted html for the submit buttons. Not pretty, but it
143 =item 2. Get list of keys
145 During the B<checkconfig> stage the B<accesskeys> source file is read (default
146 F<accesskeys.mdwn>) to generate a list of defined keys.
148 =item 3. Insert keys (links)
150 Keys are inserted into links during the format stage. All defined commands are checked
151 against the page's links and if there is a match the key is inserted. Only the first
152 match for each command is processed.
154 =item 4. Insert keys (FormBuilder buttons)
156 FormBuilder pages are intercepted during formatting. Keys are inserted as above.
164 =item * non-existant page links ex: ?Discussion
166 =item * Support non-submit array buttons (like those added after the main group for attachments)
168 =item * Support form fields (search box)
178 Written by Damian Small.
184 # Initialize original function pointer to FormBuilder::submit
185 my $original_submit_function = \&{'CGI::FormBuilder::submit'};
186 # Override default submit function in FormBuilder
190 *{'CGI::FormBuilder::submit'} = \&submit_override;
193 sub submit_override {
194 # Call the original function, and get the results
195 my $contents = $original_submit_function->(@_);
197 # Hack the results to add accesskeys
198 foreach my $buttonName (keys %accesskeys) {
199 $contents =~ s/(<input id="_submit[^>]+ value="$buttonName")( \/>)/$1 title="$buttonName [$accesskeys{$buttonName}]" accesskey="$accesskeys{$buttonName}"$2/;
206 hook(type => "getsetup", id => "accesskeys", call => \&getsetup);
207 hook(type => "checkconfig", id => "accesskeys", call => \&checkconfig);
208 hook(type => "preprocess", id => "accesskey", call => \&preprocess_accesskey);
209 hook(type => "format", id => "accesskeys", call => \&format);
222 if (defined $config{srcdir} && length $config{srcdir}) {
223 # Preprocess the accesskeys page to get all the access keys
224 # defined before other pages are rendered.
225 my $srcfile=srcfile("accesskeys.".$config{default_pageext}, 1);
226 if (! defined $srcfile) {
227 $srcfile=srcfile("accesskeys.mdwn", 1);
229 if (! defined $srcfile) {
230 print STDERR sprintf(gettext("accesskeys plugin will not work without %s"),
231 "accesskeys.".$config{default_pageext})."\n";
234 IkiWiki::preprocess("accesskeys", "accesskeys", readfile($srcfile));
239 sub preprocess_accesskey (@) {
242 if (! defined $params{command} || ! defined $params{key}) {
243 error gettext("missing command or key parameter");
247 if ($params{key} !~ /^[a-zA-Z0-9]$/) {
248 error gettext("key parameter is not a single character");
251 if ($params{command} !~ /^[a-zA-Z0-9 _]+$/) {
252 error gettext("command parameter is not an alphanumeric string");
254 # Add the access key:
255 $accesskeys{$params{command}} = $params{key};
257 return sprintf(gettext("[%s] is the access key for command '<i>%s</i>'"), $params{key}, $params{command});
262 my $contents = $params{content};
264 # If the accesskey page changes, all pages will need to be updated
265 #debug("Adding dependency: for " . $params{page} . " to AccessKeys");
266 add_depends($params{page}, "AccessKeys");
269 foreach my $command (keys %accesskeys) {
270 $contents =~ s/(<a href=[^>]+)(>$command<\/a>)/$1 accesskey="$accesskeys{$command}"$2/;
272 # may need special handling for non-existant discussion links (and possibly other similar cases?)
273 #$contents =~ s/(<a href=[^>]+)(>\?<\/a>Discussion)/$1 accesskey="d"$2/;
281 [[!toggle id="accesskeys" text="hide accesskeys.pm"]]