287 lines
8.5 KiB
Markdown
287 lines
8.5 KiB
Markdown
Access keys (i.e., keyboard shortcuts) can be defined for common
|
|
features. Something like the following:
|
|
|
|
* 1 - Homepage
|
|
* 2 - Search box
|
|
* E - Edit
|
|
* R - RecentChanges
|
|
* H - History
|
|
* P - Preferences
|
|
* D - Discussion
|
|
* S - Save the current page (when editing)
|
|
* C - Cancel the current edit
|
|
* V - Preview the current page
|
|
|
|
Then, for example, in Firefox one could press Alt+Shift+E to edit the
|
|
page.
|
|
|
|
For links, this is implemented as:
|
|
|
|
<a href="recentchanges/" accesskey="r">RecentChanges</a>
|
|
|
|
and for forms buttons:
|
|
|
|
<input type="submit" value="Submit" accesskey="s"/>
|
|
|
|
--[[JasonBlevins]], March 21, 2008 18:05 EDT
|
|
|
|
- - -
|
|
|
|
There were also a few thoughts about access keys on the
|
|
[[main_discussion_page|index/discussion]]. Now moved to here:
|
|
|
|
> Would anyone else find this a valuable addition. In oddmuse and instiki (the only other
|
|
> wiki engines I am currently using, the edit, home, and submit link tags have an
|
|
> accesskey attribute. I find it nice not to have to resort to the mouse for those
|
|
> actions. However, it may not be something everyone appreciates. Any thoughts?
|
|
> --[Mazirian](http://mazirian.com)
|
|
>
|
|
> > Maybe, although it would need to take the critisism at
|
|
> > <http://www.cs.tut.fi/~jkorpela/forms/accesskey.html> into account.
|
|
>
|
|
> >> Thank you for that link. Given that the edit link is the first thing you tab to
|
|
> >> in the current layout, I guess it isn't all that necessary. I have had a
|
|
> >> a user complaint recently that Alt-e in oddmuse was overriding his access
|
|
> >> to the browser menu.
|
|
|
|
----
|
|
|
|
The main criticism there it
|
|
seems is that some browsers implement access keys in a way (via the Alt
|
|
key) that allows them to override built-in keyboard shortcuts. I
|
|
believe this is not a problem any longer in Firefox (which uses the
|
|
Shift+Alt prefix) but I suppose it could still be a problem in other
|
|
browsers.
|
|
|
|
Another criticism is that most browsers do not display the access keys
|
|
that are defined. The [article][] cited on the main discussion page
|
|
suggests underlining the relevant mnemonic. I think it would be
|
|
sufficient to just list them in the basewiki documentation somewhere.
|
|
|
|
[article]: http://www.cs.tut.fi/~jkorpela/forms/accesskey.html
|
|
|
|
It's an unfortunate situation—I'd like an alternative to the
|
|
rodent but there are quite a few downsides to using access keys.
|
|
Tabbing isn't quite the same as a nice shortcut key. There's always
|
|
Conkeror...
|
|
|
|
--[[JasonBlevins]], March 22, 2008 10:35 EDT
|
|
|
|
----
|
|
|
|
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.
|
|
|
|
As I am new to ikiwiki plugin writing, feedback is greatly appreciated.
|
|
|
|
[[!toggle id="accesskeys" text="Toggle: accesskeys.pm"]]
|
|
|
|
[[!toggleable id="accesskeys" text="""
|
|
|
|
#!/usr/bin/perl
|
|
|
|
package IkiWiki::Plugin::accesskeys;
|
|
|
|
use warnings;
|
|
use strict;
|
|
use IkiWiki 3.00;
|
|
use CGI::FormBuilder;
|
|
|
|
=head1 NAME
|
|
|
|
accesskeys.pm - IkiWiki module to implement access keys (keyboard shortcuts)
|
|
|
|
=head1 VERSION
|
|
|
|
v.5.0 - initial version
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Access keys are defined on a page called B<accesskeys>, using the C<accesskey> directive.
|
|
Example:
|
|
|
|
[[!accesskey command="Save Page" key="s"]]
|
|
|
|
B<command> may contain only alphanumeric characters (and spaces), and must be a complete
|
|
match to the target link or submit button's display name.
|
|
|
|
B<key> may only be a single alphanumeric character.
|
|
|
|
The access key is applied to the first matching link on a page (including header), or the
|
|
first matching submit button in the @buttons array.
|
|
|
|
The wiki must be completely rebuilt every time the B<accesskeys> page changes.
|
|
|
|
=head2 Sample accesskeys page
|
|
|
|
[[!if test="enabled(accesskeys)"
|
|
then="This wiki has accesskeys **enabled**."
|
|
else="This wiki has accesskeys **disabled**."]]
|
|
|
|
This page controls what access keys the wiki uses.
|
|
|
|
* [[!accesskey command="Save Page" key="s"]]
|
|
* [[!accesskey command="Cancel" key="c"]]
|
|
* [[!accesskey command="Preview" key="v"]]
|
|
* [[!accesskey command="Edit" key="e"]]
|
|
* [[!accesskey command="RecentChanges" key="c"]]
|
|
* [[!accesskey command="Preferences" key="p"]]
|
|
* [[!accesskey command="Discussion" key="d"]]
|
|
|
|
=head1 IMPLEMENTATION
|
|
|
|
This plugin uses the following flow:
|
|
|
|
=over 1
|
|
|
|
=item 1. Override default CGI::FormBuilder::submit function
|
|
|
|
FormBuilder does not support any arbitrary modification of it's submit buttons, so
|
|
in order to add the necessary attributes you have to intercept the internal function
|
|
call which generates the formatted html for the submit buttons. Not pretty, but it
|
|
works.
|
|
|
|
=item 2. Get list of keys
|
|
|
|
During the B<checkconfig> stage the B<accesskeys> source file is read (default
|
|
F<accesskeys.mdwn>) to generate a list of defined keys.
|
|
|
|
=item 3. Insert keys (links)
|
|
|
|
Keys are inserted into links during the format stage. All defined commands are checked
|
|
against the page's links and if there is a match the key is inserted. Only the first
|
|
match for each command is processed.
|
|
|
|
=item 4. Insert keys (FormBuilder buttons)
|
|
|
|
FormBuilder pages are intercepted during formatting. Keys are inserted as above.
|
|
|
|
=back
|
|
|
|
=head1 TODO
|
|
|
|
=over 1
|
|
|
|
=item * non-existant page links ex: ?Discussion
|
|
|
|
=item * Support non-submit array buttons (like those added after the main group for attachments)
|
|
|
|
=item * Support form fields (search box)
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
#=head1 HISTORY
|
|
|
|
=head1 AUTHOR
|
|
|
|
Written by Damian Small.
|
|
|
|
=cut
|
|
|
|
my %accesskeys = ();
|
|
|
|
# Initialize original function pointer to FormBuilder::submit
|
|
my $original_submit_function = \&{'CGI::FormBuilder::submit'};
|
|
# Override default submit function in FormBuilder
|
|
{
|
|
no strict 'refs';
|
|
no warnings;
|
|
*{'CGI::FormBuilder::submit'} = \&submit_override;
|
|
}
|
|
|
|
sub submit_override {
|
|
# Call the original function, and get the results
|
|
my $contents = $original_submit_function->(@_);
|
|
|
|
# Hack the results to add accesskeys
|
|
foreach my $buttonName (keys %accesskeys) {
|
|
$contents =~ s/(<input id="_submit[^>]+ value="$buttonName")( \/>)/$1 title="$buttonName [$accesskeys{$buttonName}]" accesskey="$accesskeys{$buttonName}"$2/;
|
|
}
|
|
|
|
return $contents;
|
|
}
|
|
|
|
sub import {
|
|
hook(type => "getsetup", id => "accesskeys", call => \&getsetup);
|
|
hook(type => "checkconfig", id => "accesskeys", call => \&checkconfig);
|
|
hook(type => "preprocess", id => "accesskey", call => \&preprocess_accesskey);
|
|
hook(type => "format", id => "accesskeys", call => \&format);
|
|
}
|
|
|
|
sub getsetup () {
|
|
return
|
|
plugin => {
|
|
safe => 1,
|
|
rebuild => 1,
|
|
section => "widget",
|
|
},
|
|
}
|
|
|
|
sub checkconfig () {
|
|
if (defined $config{srcdir} && length $config{srcdir}) {
|
|
# Preprocess the accesskeys page to get all the access keys
|
|
# defined before other pages are rendered.
|
|
my $srcfile=srcfile("accesskeys.".$config{default_pageext}, 1);
|
|
if (! defined $srcfile) {
|
|
$srcfile=srcfile("accesskeys.mdwn", 1);
|
|
}
|
|
if (! defined $srcfile) {
|
|
print STDERR sprintf(gettext("accesskeys plugin will not work without %s"),
|
|
"accesskeys.".$config{default_pageext})."\n";
|
|
}
|
|
else {
|
|
IkiWiki::preprocess("accesskeys", "accesskeys", readfile($srcfile));
|
|
}
|
|
}
|
|
}
|
|
|
|
sub preprocess_accesskey (@) {
|
|
my %params=@_;
|
|
|
|
if (! defined $params{command} || ! defined $params{key}) {
|
|
error gettext("missing command or key parameter");
|
|
}
|
|
|
|
# check the key
|
|
if ($params{key} !~ /^[a-zA-Z0-9]$/) {
|
|
error gettext("key parameter is not a single character");
|
|
}
|
|
# check the command
|
|
if ($params{command} !~ /^[a-zA-Z0-9 _]+$/) {
|
|
error gettext("command parameter is not an alphanumeric string");
|
|
}
|
|
# Add the access key:
|
|
$accesskeys{$params{command}} = $params{key};
|
|
|
|
return sprintf(gettext("[%s] is the access key for command '<i>%s</i>'"), $params{key}, $params{command});
|
|
}
|
|
|
|
sub format (@) {
|
|
my %params = @_;
|
|
my $contents = $params{content};
|
|
|
|
# If the accesskey page changes, all pages will need to be updated
|
|
#debug("Adding dependency: for " . $params{page} . " to AccessKeys");
|
|
add_depends($params{page}, "AccessKeys");
|
|
|
|
# insert access keys
|
|
foreach my $command (keys %accesskeys) {
|
|
$contents =~ s/(<a href=[^>]+)(>$command<\/a>)/$1 accesskey="$accesskeys{$command}"$2/;
|
|
}
|
|
# may need special handling for non-existant discussion links (and possibly other similar cases?)
|
|
#$contents =~ s/(<a href=[^>]+)(>\?<\/a>Discussion)/$1 accesskey="d"$2/;
|
|
|
|
return $contents;
|
|
}
|
|
|
|
1
|
|
|
|
|
|
[[!toggle id="accesskeys" text="hide accesskeys.pm"]]
|
|
"""]]
|
|
|
|
--[[DamianSmall]]
|
|
|
|
[[!tag wishlist]]
|