X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/3b49868eb98e33204ca52ba944be4023f9f3f821..1d3b2674c87c9cac395228dac9b60c751d4e06a1:/doc/todo/access_keys.mdwn diff --git a/doc/todo/access_keys.mdwn b/doc/todo/access_keys.mdwn index e2bdab97c..fb23cf900 100644 --- a/doc/todo/access_keys.mdwn +++ b/doc/todo/access_keys.mdwn @@ -27,8 +27,26 @@ and for forms buttons: - - - -There are also a few thoughts about access keys on the -[[main_discussion_page|index/discussion]]. The main criticism there it +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 +> > 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 @@ -48,3 +66,221 @@ 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, using the C directive. + Example: + + [[!accesskey command="Save Page" key="s"]] + + B may contain only alphanumeric characters (and spaces), and must be a complete + match to the target link or submit button's display name. + + B 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 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 stage the B source file is read (default + F) 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/()/$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 '%s'"), $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/(]+)(>$command<\/a>)/$1 accesskey="$accesskeys{$command}"$2/; + } + # may need special handling for non-existant discussion links (and possibly other similar cases?) + #$contents =~ s/(]+)(>\?<\/a>Discussion)/$1 accesskey="d"$2/; + + return $contents; + } + + 1 + + +[[!toggle id="accesskeys" text="hide accesskeys.pm"]] +"""]] + +--[[DamianSmall]] + +[[!tag wishlist]]