aboutsummaryrefslogtreecommitdiff
path: root/doc/todo/access_keys.mdwn
blob: fb23cf90078002a555147cf9bb6c27acf829c00a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
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&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]]