X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/e8ff29fa330aef5e8d30eeef44218b87c4a9d97e..3a72fd87c7214a508b67ac8dc3567a427240c9c8:/doc/todo/access_keys.mdwn

diff --git a/doc/todo/access_keys.mdwn b/doc/todo/access_keys.mdwn
index d3a09578c..fb23cf900 100644
--- a/doc/todo/access_keys.mdwn
+++ b/doc/todo/access_keys.mdwn
@@ -24,3 +24,263 @@ 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&mdash;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]]