Merge lp:~adiroiban/launchpad/bug-359180 into lp:launchpad
- bug-359180
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Aaron Bentley | ||||
Approved revision: | not available | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~adiroiban/launchpad/bug-359180 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
576 lines (+278/-61) 11 files modified
lib/canonical/launchpad/javascript/translations/pofile.js (+194/-2) lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt (+4/-0) lib/lp/app/templates/base-layout-macros.pt (+2/-0) lib/lp/translations/browser/pofile.py (+22/-2) lib/lp/translations/browser/translationmessage.py (+22/-0) lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt (+2/-2) lib/lp/translations/stories/standalone/xx-pofile-translate.txt (+2/-1) lib/lp/translations/templates/currenttranslationmessage-translate-one.pt (+1/-6) lib/lp/translations/templates/pofile-translate.pt (+12/-46) lib/lp/translations/templates/translationmessage-translate.pt (+8/-2) lib/lp/translations/templates/translations-macros.pt (+9/-0) |
||||
To merge this branch: | bzr merge lp:~adiroiban/launchpad/bug-359180 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Aaron Bentley (community) | Approve | ||
Review via email: mp+16422@code.launchpad.net |
Commit message
Description of the change
Adi Roiban (adiroiban) wrote : | # |
Aaron Bentley (abentley) wrote : | # |
This looks mostly good. Please rename tabindex_chain to something else (we discussed "translations_
The current interaction of autofocus_html_id and Y.lp.pofile.
Adi Roiban (adiroiban) wrote : | # |
Thanks for the review!
Here is the latest diff.
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -86,7 +86,7 @@
};
-var setFocus = function(e, field) {
+self.setFocus = function(field) {
//Y.log(e.type + ":" + e.keyCode + ': ' + field);
// if there is nofield, do nothing
if (Y.one('#' + field)) {
@@ -96,7 +96,7 @@
var setNextFocus = function(e, field) {
- setFocus(e,field);
+ self.setFocus(
// stopPropagation() and preventDefault()
e.halt();
};
@@ -106,8 +106,8 @@
// Original singular test is focused first to make sure
// it is visible when scrolling up
- setFocus(e, original);
- setFocus(e, field);
+ self.setFocus(
+ self.setFocus(
// stopPropagation() and preventDefault()
e.halt();
};
@@ -116,7 +116,6 @@
var copyOriginalTextOne = function(from_id, to_id, select_id) {
var from = Y.one('#' + from_id);
var to = Y.one('#' + to_id);
-
// The replacement regex strips all tags from the html.
to.
@@ -182,7 +181,13 @@
*/
self.initializ
- var fields = tabindex_
+ if (translations_
+ // If no translations fiels are displayed on the page
+ // don't initialize the translations order
+ return;
+ }
+
+ var fields = translations_
// The last field is Save & Continue button
fields.
@@ -192,7 +197,7 @@
var html_parts = fields[
var original_stem = html_parts[0] + '_' + html_parts[1];
- var translation_stem = original_stem + '_' + html_parts[2];
+ var translation_stem = fields[
var select_widget = (
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -935,17 +935,17 @@
return first_field[
except IndexError:
- return False
+ return ""
@property
- def tabindex_
+ def translations_
try:
- tabindex = []
+ order = []
for message in self.translatio
- tabindex.append(
+ order.append(
- return ' '.join(tabindex)
+ return ' '.join(order)
except IndexError:
return ""
=== modified file 'lib/lp/
Aaron Bentley (abentley) : | # |
Preview Diff
1 | === modified file 'lib/canonical/launchpad/javascript/translations/pofile.js' | |||
2 | --- lib/canonical/launchpad/javascript/translations/pofile.js 2009-11-23 19:29:02 +0000 | |||
3 | +++ lib/canonical/launchpad/javascript/translations/pofile.js 2009-12-25 11:06:20 +0000 | |||
4 | @@ -1,7 +1,7 @@ | |||
5 | 1 | /** Copyright (c) 2009, Canonical Ltd. All rights reserved. | 1 | /** Copyright (c) 2009, Canonical Ltd. All rights reserved. |
6 | 2 | * | 2 | * |
7 | 3 | * @module lp.pofile | 3 | * @module lp.pofile |
9 | 4 | * @requires event, node | 4 | * @requires event, event-key, node, cookie, anim |
10 | 5 | */ | 5 | */ |
11 | 6 | 6 | ||
12 | 7 | YUI.add('lp.pofile', function(Y) { | 7 | YUI.add('lp.pofile', function(Y) { |
13 | @@ -41,4 +41,196 @@ | |||
14 | 41 | } | 41 | } |
15 | 42 | }; | 42 | }; |
16 | 43 | 43 | ||
18 | 44 | }, "0.1", {"requires": ["event", "node"]}); | 44 | var hide_notification = function(node) { |
19 | 45 | var hide_anim = new Y.Anim({ | ||
20 | 46 | node: node, | ||
21 | 47 | to: { height: 0, | ||
22 | 48 | marginTop: 0, marginBottom: 0, | ||
23 | 49 | paddingTop: 0, paddingBottom: 0 } | ||
24 | 50 | }); | ||
25 | 51 | node.setStyle('border', 'none'); | ||
26 | 52 | hide_anim.set('duration', 0.4); | ||
27 | 53 | hide_anim.on('end', function(e) { | ||
28 | 54 | node.setStyle('display', 'none'); | ||
29 | 55 | }); | ||
30 | 56 | hide_anim.run(); | ||
31 | 57 | }; | ||
32 | 58 | |||
33 | 59 | |||
34 | 60 | self.updateNotificationBox = function(e) { | ||
35 | 61 | var notice = Y.one('.important-notice-container'); | ||
36 | 62 | if (notice === null) { | ||
37 | 63 | return; | ||
38 | 64 | } | ||
39 | 65 | var balloon = notice.one('.important-notice-balloon'); | ||
40 | 66 | var dismiss_notice_cookie = ('translation-docs-for-' + | ||
41 | 67 | documentation_cookie); | ||
42 | 68 | |||
43 | 69 | // Check the cookie to see if the user has already dismissed | ||
44 | 70 | // the notification box for this session. | ||
45 | 71 | var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean); | ||
46 | 72 | if (already_seen) { | ||
47 | 73 | notice.setStyle('display', 'none'); | ||
48 | 74 | } | ||
49 | 75 | |||
50 | 76 | var cancel_button = notice.one( | ||
51 | 77 | '.important-notice-cancel-button'); | ||
52 | 78 | // Cancel button starts out hidden. If user has JavaScript, | ||
53 | 79 | // then we want to show it. | ||
54 | 80 | cancel_button.setStyle('visibility', 'visible'); | ||
55 | 81 | cancel_button.on('click', function(e) { | ||
56 | 82 | e.halt(); | ||
57 | 83 | hide_notification(balloon); | ||
58 | 84 | Y.Cookie.set(dismiss_notice_cookie, true); | ||
59 | 85 | }); | ||
60 | 86 | }; | ||
61 | 87 | |||
62 | 88 | |||
63 | 89 | self.setFocus = function(field) { | ||
64 | 90 | //Y.log(e.type + ":" + e.keyCode + ': ' + field); | ||
65 | 91 | // if there is nofield, do nothing | ||
66 | 92 | if (Y.one('#' + field)) { | ||
67 | 93 | Y.one('#' + field).focus(); | ||
68 | 94 | } | ||
69 | 95 | }; | ||
70 | 96 | |||
71 | 97 | |||
72 | 98 | var setNextFocus = function(e, field) { | ||
73 | 99 | self.setFocus(field); | ||
74 | 100 | // stopPropagation() and preventDefault() | ||
75 | 101 | e.halt(); | ||
76 | 102 | }; | ||
77 | 103 | |||
78 | 104 | |||
79 | 105 | var setPreviousFocus = function(e, field, original) { | ||
80 | 106 | |||
81 | 107 | // Original singular test is focused first to make sure | ||
82 | 108 | // it is visible when scrolling up | ||
83 | 109 | self.setFocus(original); | ||
84 | 110 | self.setFocus(field); | ||
85 | 111 | // stopPropagation() and preventDefault() | ||
86 | 112 | e.halt(); | ||
87 | 113 | }; | ||
88 | 114 | |||
89 | 115 | |||
90 | 116 | var copyOriginalTextOne = function(from_id, to_id, select_id) { | ||
91 | 117 | var from = Y.one('#' + from_id); | ||
92 | 118 | var to = Y.one('#' + to_id); | ||
93 | 119 | // The replacement regex strips all tags from the html. | ||
94 | 120 | to.set('value', unescapeHTML( | ||
95 | 121 | from.get('innerHTML').replace(/<\/?[^>]+>/gi, ""))); | ||
96 | 122 | selectWidget(select_id); | ||
97 | 123 | }; | ||
98 | 124 | |||
99 | 125 | |||
100 | 126 | var copyOriginalTextPlural = function (from_id, | ||
101 | 127 | to_id_pattern, nplurals) { | ||
102 | 128 | // skip when x is 0, as that is the singular | ||
103 | 129 | for (var x = 1; x < nplurals; x++) { | ||
104 | 130 | var to_id = to_id_pattern + x + "_new"; | ||
105 | 131 | var to_select = to_id_pattern + x + "_new_select"; | ||
106 | 132 | copyOriginalTextOne(from_id, to_id, to_select); | ||
107 | 133 | } | ||
108 | 134 | }; | ||
109 | 135 | |||
110 | 136 | |||
111 | 137 | var copyOriginalTextAll = function(e, original_stem, translation_stem) { | ||
112 | 138 | |||
113 | 139 | var original_singular = original_stem + '_singular'; | ||
114 | 140 | var original_plural = original_stem + '_plural'; | ||
115 | 141 | var singular_select = translation_stem + '_translation_0_new_select'; | ||
116 | 142 | var translation_singular = translation_stem + '_translation_0_new'; | ||
117 | 143 | var translation_plural = translation_stem + '_translation_'; | ||
118 | 144 | //Y.log(e.type + ":" + e.keyCode + ': ' + singular_select); | ||
119 | 145 | // Copy singular text | ||
120 | 146 | copyOriginalTextOne( | ||
121 | 147 | original_singular, translation_singular, singular_select); | ||
122 | 148 | |||
123 | 149 | // Copy plural text if needed | ||
124 | 150 | if (Y.one('#' + translation_plural + '1')) { | ||
125 | 151 | copyOriginalTextPlural( | ||
126 | 152 | original_plural, translation_plural, plural_forms); | ||
127 | 153 | } | ||
128 | 154 | // stopPropagation() and preventDefault() | ||
129 | 155 | e.halt(); | ||
130 | 156 | }; | ||
131 | 157 | |||
132 | 158 | |||
133 | 159 | var selectWidget = function(widget) { | ||
134 | 160 | if (Y.one('#' + widget)) { | ||
135 | 161 | Y.one('#' + widget).set('checked', true); | ||
136 | 162 | } | ||
137 | 163 | }; | ||
138 | 164 | |||
139 | 165 | |||
140 | 166 | var selectTranslation = function(e, widget) { | ||
141 | 167 | //Y.log(e.type + ":" + e.keyCode + ': ' + widget); | ||
142 | 168 | // Don't select when tabbing, navigating Up or Down and simply pressing | ||
143 | 169 | // enter to submit the form. | ||
144 | 170 | if (e.keyCode == 9 || e.keyCode == 13 || | ||
145 | 171 | e.keyCode == 40 || e.keyCode == 38) { | ||
146 | 172 | return; | ||
147 | 173 | } | ||
148 | 174 | selectWidget(widget); | ||
149 | 175 | }; | ||
150 | 176 | |||
151 | 177 | |||
152 | 178 | /** | ||
153 | 179 | * Initialize event-key bindings such as moving to the next or previous | ||
154 | 180 | * field, or copying original text | ||
155 | 181 | */ | ||
156 | 182 | self.initializeKeyBindings = function(e) { | ||
157 | 183 | |||
158 | 184 | if (translations_order.length < 1) { | ||
159 | 185 | // If no translations fiels are displayed on the page | ||
160 | 186 | // don't initialize the translations order | ||
161 | 187 | return; | ||
162 | 188 | } | ||
163 | 189 | |||
164 | 190 | var fields = translations_order.split(' '); | ||
165 | 191 | // The last field is Save & Continue button | ||
166 | 192 | fields.push('save_and_continue_button'); | ||
167 | 193 | |||
168 | 194 | for (var key = 0; key < fields.length; key++) { | ||
169 | 195 | var next = key + 1; | ||
170 | 196 | var previous = key - 1; | ||
171 | 197 | |||
172 | 198 | var html_parts = fields[key].split('_'); | ||
173 | 199 | var original_stem = html_parts[0] + '_' + html_parts[1]; | ||
174 | 200 | var translation_stem = fields[key].replace(/_translation_(\d)+_new/,""); | ||
175 | 201 | var select_widget = ( | ||
176 | 202 | translation_stem + '_' + html_parts[3] + '_' + | ||
177 | 203 | html_parts[4] + '_new_select'); | ||
178 | 204 | |||
179 | 205 | Y.on( | ||
180 | 206 | 'change', selectTranslation, | ||
181 | 207 | '#' + fields[key], Y, select_widget); | ||
182 | 208 | Y.on( | ||
183 | 209 | 'keypress', selectTranslation, | ||
184 | 210 | '#' + fields[key], Y, select_widget); | ||
185 | 211 | |||
186 | 212 | // Set next field and copy text for all but last field | ||
187 | 213 | // (last is Save & Continue button) | ||
188 | 214 | if (key < fields.length - 1) { | ||
189 | 215 | Y.on( | ||
190 | 216 | 'key', setNextFocus, '#' + fields[key], | ||
191 | 217 | 'down:40+shift+alt', Y, fields[next]); | ||
192 | 218 | Y.on( | ||
193 | 219 | 'key', copyOriginalTextAll, '#' + fields[key], | ||
194 | 220 | 'down:67+shift+alt', Y, original_stem, translation_stem); | ||
195 | 221 | } | ||
196 | 222 | |||
197 | 223 | // Set previous field for all but first field | ||
198 | 224 | if (key > 0) { | ||
199 | 225 | var parts = fields[previous].split('_'); | ||
200 | 226 | var singular_copy_text = ( | ||
201 | 227 | parts[0] + '_' + parts[1] + '_singular_copy_text'); | ||
202 | 228 | Y.on( | ||
203 | 229 | 'key', setPreviousFocus, '#' + fields[key], | ||
204 | 230 | 'down:38+shift+alt', Y, fields[previous], | ||
205 | 231 | singular_copy_text); | ||
206 | 232 | } | ||
207 | 233 | } | ||
208 | 234 | }; | ||
209 | 235 | |||
210 | 236 | }, "0.1", {"requires": ["event", "event-key", "node", 'cookie', 'anim']}); | ||
211 | 45 | 237 | ||
212 | === modified file 'lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt' | |||
213 | --- lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2009-07-17 17:59:07 +0000 | |||
214 | +++ lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2009-12-25 11:06:20 +0000 | |||
215 | @@ -29,6 +29,7 @@ | |||
216 | 29 | tal:attributes="href first_page_url" | 29 | tal:attributes="href first_page_url" |
217 | 30 | class="first" | 30 | class="first" |
218 | 31 | rel="first" | 31 | rel="first" |
219 | 32 | accesskey="A" | ||
220 | 32 | >First</a> | 33 | >First</a> |
221 | 33 | <span tal:condition="not:first_page_url" class="first inactive" | 34 | <span tal:condition="not:first_page_url" class="first inactive" |
222 | 34 | >First</span> | 35 | >First</span> |
223 | @@ -38,6 +39,7 @@ | |||
224 | 38 | tal:attributes="href prev_page_url" | 39 | tal:attributes="href prev_page_url" |
225 | 39 | class="previous" | 40 | class="previous" |
226 | 40 | rel="previous" | 41 | rel="previous" |
227 | 42 | accesskey="P" | ||
228 | 41 | >Previous</a> | 43 | >Previous</a> |
229 | 42 | <span tal:condition="not:prev_page_url" class="previous inactive" | 44 | <span tal:condition="not:prev_page_url" class="previous inactive" |
230 | 43 | >Previous</span> | 45 | >Previous</span> |
231 | @@ -47,6 +49,7 @@ | |||
232 | 47 | tal:attributes="href next_page_url" | 49 | tal:attributes="href next_page_url" |
233 | 48 | class="next" | 50 | class="next" |
234 | 49 | rel="next" | 51 | rel="next" |
235 | 52 | accesskey="N" | ||
236 | 50 | ><strong>Next</strong></a> | 53 | ><strong>Next</strong></a> |
237 | 51 | <span tal:condition="not:next_page_url" class="next inactive" | 54 | <span tal:condition="not:next_page_url" class="next inactive" |
238 | 52 | ><strong>Next</strong></span> | 55 | ><strong>Next</strong></span> |
239 | @@ -57,6 +60,7 @@ | |||
240 | 57 | tal:attributes="href last_page_url" | 60 | tal:attributes="href last_page_url" |
241 | 58 | class="last" | 61 | class="last" |
242 | 59 | rel="last" | 62 | rel="last" |
243 | 63 | accesskey="L" | ||
244 | 60 | >Last</a> | 64 | >Last</a> |
245 | 61 | <span tal:condition="not:last_page_url" class="last inactive" | 65 | <span tal:condition="not:last_page_url" class="last inactive" |
246 | 62 | >Last</span> | 66 | >Last</span> |
247 | 63 | 67 | ||
248 | === modified file 'lib/lp/app/templates/base-layout-macros.pt' | |||
249 | --- lib/lp/app/templates/base-layout-macros.pt 2009-12-16 19:59:06 +0000 | |||
250 | +++ lib/lp/app/templates/base-layout-macros.pt 2009-12-25 11:06:20 +0000 | |||
251 | @@ -80,6 +80,8 @@ | |||
252 | 80 | <script type="text/javascript" | 80 | <script type="text/javascript" |
253 | 81 | tal:attributes="src string:${yui}/event/event.js"></script> | 81 | tal:attributes="src string:${yui}/event/event.js"></script> |
254 | 82 | <script type="text/javascript" | 82 | <script type="text/javascript" |
255 | 83 | tal:attributes="src string:${yui}/event/event-key.js"></script> | ||
256 | 84 | <script type="text/javascript" | ||
257 | 83 | tal:attributes="src string:${yui}/event-custom/event-custom.js"></script> | 85 | tal:attributes="src string:${yui}/event-custom/event-custom.js"></script> |
258 | 84 | <script type="text/javascript" | 86 | <script type="text/javascript" |
259 | 85 | tal:attributes="src string:${yui}/event-simulate/event-simulate.js"></script> | 87 | tal:attributes="src string:${yui}/event-simulate/event-simulate.js"></script> |
260 | 86 | 88 | ||
261 | === modified file 'lib/lp/translations/browser/pofile.py' | |||
262 | --- lib/lp/translations/browser/pofile.py 2009-09-17 19:05:29 +0000 | |||
263 | +++ lib/lp/translations/browser/pofile.py 2009-12-25 11:06:20 +0000 | |||
264 | @@ -503,8 +503,6 @@ | |||
265 | 503 | 503 | ||
266 | 504 | DEFAULT_BATCH_SIZE = 50 | 504 | DEFAULT_BATCH_SIZE = 50 |
267 | 505 | 505 | ||
268 | 506 | page_title = "Contributions" | ||
269 | 507 | |||
270 | 508 | @property | 506 | @property |
271 | 509 | def _person_name(self): | 507 | def _person_name(self): |
272 | 510 | """Person's display name. Graceful about unknown persons.""" | 508 | """Person's display name. Graceful about unknown persons.""" |
273 | @@ -930,6 +928,28 @@ | |||
274 | 930 | def completeness(self): | 928 | def completeness(self): |
275 | 931 | return '%.0f%%' % self.context.translatedPercentage() | 929 | return '%.0f%%' % self.context.translatedPercentage() |
276 | 932 | 930 | ||
277 | 931 | @property | ||
278 | 932 | def autofocus_html_id(self): | ||
279 | 933 | try: | ||
280 | 934 | first_message = self.translationmessage_views[0] | ||
281 | 935 | first_field = first_message.translation_dictionaries[0] | ||
282 | 936 | return first_field['html_id_translation'] | ||
283 | 937 | except IndexError: | ||
284 | 938 | return "" | ||
285 | 939 | |||
286 | 940 | @property | ||
287 | 941 | def translations_order(self): | ||
288 | 942 | try: | ||
289 | 943 | order = [] | ||
290 | 944 | for message in self.translationmessage_views: | ||
291 | 945 | for dictionary in message.translation_dictionaries: | ||
292 | 946 | order.append( | ||
293 | 947 | dictionary['html_id_translation'] + '_new') | ||
294 | 948 | return ' '.join(order) | ||
295 | 949 | |||
296 | 950 | except IndexError: | ||
297 | 951 | return "" | ||
298 | 952 | |||
299 | 933 | 953 | ||
300 | 934 | class POExportView(BaseExportView): | 954 | class POExportView(BaseExportView): |
301 | 935 | 955 | ||
302 | 936 | 956 | ||
303 | === modified file 'lib/lp/translations/browser/translationmessage.py' | |||
304 | --- lib/lp/translations/browser/translationmessage.py 2009-09-17 08:41:05 +0000 | |||
305 | +++ lib/lp/translations/browser/translationmessage.py 2009-12-25 11:06:20 +0000 | |||
306 | @@ -829,6 +829,28 @@ | |||
307 | 829 | self._redirectToNextPage() | 829 | self._redirectToNextPage() |
308 | 830 | return True | 830 | return True |
309 | 831 | 831 | ||
310 | 832 | @property | ||
311 | 833 | def autofocus_html_id(self): | ||
312 | 834 | try: | ||
313 | 835 | first_message = self.translationmessage_view | ||
314 | 836 | first_field = first_message.translation_dictionaries[0] | ||
315 | 837 | return first_field['html_id_translation'] | ||
316 | 838 | except IndexError: | ||
317 | 839 | return "" | ||
318 | 840 | |||
319 | 841 | @property | ||
320 | 842 | def translations_order(self): | ||
321 | 843 | try: | ||
322 | 844 | order = [] | ||
323 | 845 | message = self.translationmessage_view | ||
324 | 846 | for dictionary in message.translation_dictionaries: | ||
325 | 847 | order.append(dictionary['html_id_translation'] + '_new') | ||
326 | 848 | return ' '.join(order) | ||
327 | 849 | |||
328 | 850 | except IndexError: | ||
329 | 851 | return "" | ||
330 | 852 | |||
331 | 853 | |||
332 | 832 | class CurrentTranslationMessageView(LaunchpadView): | 854 | class CurrentTranslationMessageView(LaunchpadView): |
333 | 833 | """Holds all data needed to show an ITranslationMessage. | 855 | """Holds all data needed to show an ITranslationMessage. |
334 | 834 | 856 | ||
335 | 835 | 857 | ||
336 | === modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt' | |||
337 | --- lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2009-07-01 20:45:39 +0000 | |||
338 | +++ lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2009-12-25 11:06:20 +0000 | |||
339 | @@ -43,7 +43,7 @@ | |||
340 | 43 | >>> print browser.url | 43 | >>> print browser.url |
341 | 44 | http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1 | 44 | http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1 |
342 | 45 | >>> print find_tag_by_id(browser.contents, 'msgset_149_es_translation_0_new') | 45 | >>> print find_tag_by_id(browser.contents, 'msgset_149_es_translation_0_new') |
344 | 46 | <textarea ... name="msgset_149_es_translation_0_new" ...> | 46 | <textarea ... name="msgset_149_es_translation_0_new"...> |
345 | 47 | 47 | ||
346 | 48 | foo | 48 | foo |
347 | 49 | 49 | ||
348 | @@ -63,7 +63,7 @@ | |||
349 | 63 | >>> print find_tag_by_id( | 63 | >>> print find_tag_by_id( |
350 | 64 | ... browser.contents, | 64 | ... browser.contents, |
351 | 65 | ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE | 65 | ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE |
353 | 66 | <textarea ... name="msgset_149_es_translation_0_new" ...> | 66 | <textarea ... name="msgset_149_es_translation_0_new"...> |
354 | 67 | foo</textarea> | 67 | foo</textarea> |
355 | 68 | 68 | ||
356 | 69 | Now, Check that even though the user forgot the trailing new line char, | 69 | Now, Check that even though the user forgot the trailing new line char, |
357 | 70 | 70 | ||
358 | === modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate.txt' | |||
359 | --- lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2009-12-07 18:42:21 +0000 | |||
360 | +++ lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2009-12-25 11:06:20 +0000 | |||
361 | @@ -158,7 +158,7 @@ | |||
362 | 158 | ... print id | 158 | ... print id |
363 | 159 | msgset_130 | 159 | msgset_130 |
364 | 160 | ... | 160 | ... |
366 | 161 | msgset_130_singular | 161 | msgset_130_singular... |
367 | 162 | 162 | ||
368 | 163 | HTML element identifiers for suggestions and translations on this form are | 163 | HTML element identifiers for suggestions and translations on this form are |
369 | 164 | constructed as an underscore-separated sequence of: | 164 | constructed as an underscore-separated sequence of: |
370 | @@ -181,6 +181,7 @@ | |||
371 | 181 | msgset_130_es_suggestion_562_0_origin | 181 | msgset_130_es_suggestion_562_0_origin |
372 | 182 | msgset_130_es_suggestion_562_0_radiobutton | 182 | msgset_130_es_suggestion_562_0_radiobutton |
373 | 183 | msgset_130_singular | 183 | msgset_130_singular |
374 | 184 | msgset_130_singular_copy_text | ||
375 | 184 | 185 | ||
376 | 185 | Radio buttons are grouped by their name attribute. The translate page shows | 186 | Radio buttons are grouped by their name attribute. The translate page shows |
377 | 186 | each translatable message with one radiobutton to select the existing | 187 | each translatable message with one radiobutton to select the existing |
378 | 187 | 188 | ||
379 | === modified file 'lib/lp/translations/templates/currenttranslationmessage-translate-one.pt' | |||
380 | --- lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2009-12-07 18:42:21 +0000 | |||
381 | +++ lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2009-12-25 11:06:20 +0000 | |||
382 | @@ -42,6 +42,7 @@ | |||
383 | 42 | <a href="" | 42 | <a href="" |
384 | 43 | tal:condition="not:context/potmsgset/is_translation_credit" | 43 | tal:condition="not:context/potmsgset/is_translation_credit" |
385 | 44 | tal:attributes=" | 44 | tal:attributes=" |
386 | 45 | id string:${view/html_id}_singular_copy_text; | ||
387 | 45 | onClick string: | 46 | onClick string: |
388 | 46 | javascript:copyInnerHTMLById( | 47 | javascript:copyInnerHTMLById( |
389 | 47 | '${view/html_id}_singular', | 48 | '${view/html_id}_singular', |
390 | @@ -504,8 +505,6 @@ | |||
391 | 504 | lang context/pofile/language/dashedcode; | 505 | lang context/pofile/language/dashedcode; |
392 | 505 | dir context/pofile/language/abbreviated_text_dir; | 506 | dir context/pofile/language/abbreviated_text_dir; |
393 | 506 | value translation_dictionary/submitted_translation; | 507 | value translation_dictionary/submitted_translation; |
394 | 507 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
395 | 508 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
396 | 509 | " | 508 | " |
397 | 510 | class="translate expandable" | 509 | class="translate expandable" |
398 | 511 | /> | 510 | /> |
399 | @@ -528,8 +527,6 @@ | |||
400 | 528 | name string:${translation_dictionary/html_id_translation}_new; | 527 | name string:${translation_dictionary/html_id_translation}_new; |
401 | 529 | lang context/pofile/language/dashedcode; | 528 | lang context/pofile/language/dashedcode; |
402 | 530 | dir context/pofile/language/abbreviated_text_dir; | 529 | dir context/pofile/language/abbreviated_text_dir; |
403 | 531 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
404 | 532 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
405 | 533 | "> | 530 | "> |
406 | 534 | <tal:content replace="translation_dictionary/submitted_translation" /></textarea> | 531 | <tal:content replace="translation_dictionary/submitted_translation" /></textarea> |
407 | 535 | </tal:with-content> | 532 | </tal:with-content> |
408 | @@ -548,8 +545,6 @@ | |||
409 | 548 | name string:${translation_dictionary/html_id_translation}_new; | 545 | name string:${translation_dictionary/html_id_translation}_new; |
410 | 549 | lang context/pofile/language/dashedcode; | 546 | lang context/pofile/language/dashedcode; |
411 | 550 | dir context/pofile/language/abbreviated_text_dir; | 547 | dir context/pofile/language/abbreviated_text_dir; |
412 | 551 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
413 | 552 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
414 | 553 | "></textarea> | 548 | "></textarea> |
415 | 554 | </tal:without-content> | 549 | </tal:without-content> |
416 | 555 | </tal:multi-line> | 550 | </tal:multi-line> |
417 | 556 | 551 | ||
418 | === modified file 'lib/lp/translations/templates/pofile-translate.pt' | |||
419 | --- lib/lp/translations/templates/pofile-translate.pt 2009-12-10 13:08:33 +0000 | |||
420 | +++ lib/lp/translations/templates/pofile-translate.pt 2009-12-25 11:06:20 +0000 | |||
421 | @@ -17,53 +17,16 @@ | |||
422 | 17 | tal:condition="devmode" | 17 | tal:condition="devmode" |
423 | 18 | tal:define="lp_js string:${icingroot}/build" | 18 | tal:define="lp_js string:${icingroot}/build" |
424 | 19 | tal:attributes="src string:${lp_js}/translations/pofile.js"></script> | 19 | tal:attributes="src string:${lp_js}/translations/pofile.js"></script> |
425 | 20 | |||
426 | 20 | <script type="text/javascript"> | 21 | <script type="text/javascript"> |
427 | 21 | registerLaunchpadFunction(insertAllExpansionButtons); | 22 | registerLaunchpadFunction(insertAllExpansionButtons); |
471 | 22 | 23 | LPS.use('lp.pofile', function(Y) { | |
429 | 23 | LPS.use('node', 'cookie', 'anim', 'lp.pofile', function(Y) { | ||
430 | 24 | |||
431 | 25 | var hide_notification = function(node) { | ||
432 | 26 | var hide_anim = new Y.Anim({ | ||
433 | 27 | node: node, | ||
434 | 28 | to: { height: 0, | ||
435 | 29 | marginTop: 0, marginBottom: 0, | ||
436 | 30 | paddingTop: 0, paddingBottom: 0 } | ||
437 | 31 | }); | ||
438 | 32 | node.setStyle('border', 'none'); | ||
439 | 33 | hide_anim.set('duration', 0.4); | ||
440 | 34 | hide_anim.on('end', function(e) { | ||
441 | 35 | node.setStyle('display', 'none'); | ||
442 | 36 | }); | ||
443 | 37 | hide_anim.run(); | ||
444 | 38 | } | ||
445 | 39 | |||
446 | 40 | var updateNotificationBox = function(e) { | ||
447 | 41 | var notice = Y.one('.important-notice-container'); | ||
448 | 42 | var balloon = notice.one('.important-notice-balloon'); | ||
449 | 43 | var dismiss_notice_cookie = ('translation-docs-for-' + | ||
450 | 44 | documentation_cookie); | ||
451 | 45 | |||
452 | 46 | // Check the cookie to see if the user has already dismissed | ||
453 | 47 | // the notification box for this session. | ||
454 | 48 | var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean); | ||
455 | 49 | if (already_seen) { | ||
456 | 50 | notice.setStyle('display', 'none'); | ||
457 | 51 | } | ||
458 | 52 | |||
459 | 53 | var cancel_button = notice.one( | ||
460 | 54 | '.important-notice-cancel-button'); | ||
461 | 55 | // Cancel button starts out hidden. If user has JavaScript, | ||
462 | 56 | // then we want to show it. | ||
463 | 57 | cancel_button.setStyle('visibility', 'visible'); | ||
464 | 58 | cancel_button.on('click', function(e) { | ||
465 | 59 | e.halt(); | ||
466 | 60 | hide_notification(balloon); | ||
467 | 61 | Y.Cookie.set(dismiss_notice_cookie, true); | ||
468 | 62 | }); | ||
469 | 63 | }; | ||
470 | 64 | |||
472 | 65 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); | 24 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); |
474 | 66 | Y.on('domready', updateNotificationBox); | 25 | Y.on('domready', Y.lp.pofile.initializeKeyBindings); |
475 | 26 | Y.on('domready', Y.lp.pofile.updateNotificationBox); | ||
476 | 27 | Y.on('domready', function(e) { | ||
477 | 28 | Y.lp.pofile.setFocus(autofocus_field); | ||
478 | 29 | }); | ||
479 | 67 | }); | 30 | }); |
480 | 68 | </script> | 31 | </script> |
481 | 69 | </div> | 32 | </div> |
482 | @@ -134,7 +97,7 @@ | |||
483 | 134 | <input type="hidden" name="batch" value="" | 97 | <input type="hidden" name="batch" value="" |
484 | 135 | tal:attributes="value view/size" /> | 98 | tal:attributes="value view/size" /> |
485 | 136 | <input type="hidden" name="show" value="all" /> | 99 | <input type="hidden" name="show" value="all" /> |
487 | 137 | <label for="search_box">Search:</label> | 100 | <label for="search_box" accesskey="F">Search:</label> |
488 | 138 | <input id="search_box" type="text" name="search" | 101 | <input id="search_box" type="text" name="search" |
489 | 139 | title="Enter text to search for" | 102 | title="Enter text to search for" |
490 | 140 | tal:attributes="value view/search_text" /> | 103 | tal:attributes="value view/search_text" /> |
491 | @@ -262,6 +225,8 @@ | |||
492 | 262 | <td style="text-align: right;"> | 225 | <td style="text-align: right;"> |
493 | 263 | <input type="submit" | 226 | <input type="submit" |
494 | 264 | name="submit_translations" | 227 | name="submit_translations" |
495 | 228 | accesskey="S" | ||
496 | 229 | id="save_and_continue_button" | ||
497 | 265 | value="Save & Continue" | 230 | value="Save & Continue" |
498 | 266 | /> | 231 | /> |
499 | 267 | </td> | 232 | </td> |
500 | @@ -269,7 +234,6 @@ | |||
501 | 269 | </tbody> | 234 | </tbody> |
502 | 270 | </table> | 235 | </table> |
503 | 271 | </form> | 236 | </form> |
504 | 272 | |||
505 | 273 | <!-- Paging doodads. --> | 237 | <!-- Paging doodads. --> |
506 | 274 | <tal:navigation | 238 | <tal:navigation |
507 | 275 | replace="structure view/batchnav/@@+navigation-links-lower" /> | 239 | replace="structure view/batchnav/@@+navigation-links-lower" /> |
508 | @@ -277,6 +241,8 @@ | |||
509 | 277 | <tal:status replace="structure context/@@+access" /> | 241 | <tal:status replace="structure context/@@+access" /> |
510 | 278 | <tal:contributors replace="structure context/@@+contributors" /> | 242 | <tal:contributors replace="structure context/@@+contributors" /> |
511 | 279 | </tal:havepluralforms> | 243 | </tal:havepluralforms> |
512 | 244 | <metal:pofile-js-footer | ||
513 | 245 | use-macro="context/@@+translations-macros/pofile-js-footer" /> | ||
514 | 280 | </div> | 246 | </div> |
515 | 281 | </body> | 247 | </body> |
516 | 282 | </html> | 248 | </html> |
517 | 283 | 249 | ||
518 | === modified file 'lib/lp/translations/templates/translationmessage-translate.pt' | |||
519 | --- lib/lp/translations/templates/translationmessage-translate.pt 2009-12-10 13:08:33 +0000 | |||
520 | +++ lib/lp/translations/templates/translationmessage-translate.pt 2009-12-25 11:06:20 +0000 | |||
521 | @@ -20,6 +20,10 @@ | |||
522 | 20 | <script type="text/javascript"> | 20 | <script type="text/javascript"> |
523 | 21 | LPS.use('node', 'lp.pofile', function(Y) { | 21 | LPS.use('node', 'lp.pofile', function(Y) { |
524 | 22 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); | 22 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); |
525 | 23 | Y.on('domready', Y.lp.pofile.initializeKeyBindings); | ||
526 | 24 | Y.on('domready', function(e) { | ||
527 | 25 | Y.one('#' + autofocus_field).focus(); | ||
528 | 26 | }); | ||
529 | 23 | }); | 27 | }); |
530 | 24 | </script> | 28 | </script> |
531 | 25 | </div> | 29 | </div> |
532 | @@ -76,12 +80,13 @@ | |||
533 | 76 | <th colspan="5" style="text-align: right;"> | 80 | <th colspan="5" style="text-align: right;"> |
534 | 77 | <input type="submit" | 81 | <input type="submit" |
535 | 78 | name="submit_translations" | 82 | name="submit_translations" |
536 | 83 | accesskey="S" | ||
537 | 84 | id="save_and_continue_button" | ||
538 | 79 | value="Save & Continue" /> | 85 | value="Save & Continue" /> |
539 | 80 | </th> | 86 | </th> |
540 | 81 | </tr> | 87 | </tr> |
541 | 82 | </tfoot> | 88 | </tfoot> |
542 | 83 | </table> | 89 | </table> |
543 | 84 | |||
544 | 85 | </form> | 90 | </form> |
545 | 86 | 91 | ||
546 | 87 | <!-- Paging doodads. --> | 92 | <!-- Paging doodads. --> |
547 | @@ -89,8 +94,9 @@ | |||
548 | 89 | replace="structure view/batchnav/@@+navigation-links-lower" /> | 94 | replace="structure view/batchnav/@@+navigation-links-lower" /> |
549 | 90 | <tal:status replace="structure context/pofile/@@+access" /> | 95 | <tal:status replace="structure context/pofile/@@+access" /> |
550 | 91 | </tal:havepluralforms> | 96 | </tal:havepluralforms> |
551 | 97 | <metal:pofile-js-footer | ||
552 | 98 | use-macro="context/@@+translations-macros/pofile-js-footer" /> | ||
553 | 92 | </div> | 99 | </div> |
554 | 93 | |||
555 | 94 | </body> | 100 | </body> |
556 | 95 | 101 | ||
557 | 96 | </html> | 102 | </html> |
558 | 97 | 103 | ||
559 | === modified file 'lib/lp/translations/templates/translations-macros.pt' | |||
560 | --- lib/lp/translations/templates/translations-macros.pt 2009-12-07 18:42:21 +0000 | |||
561 | +++ lib/lp/translations/templates/translations-macros.pt 2009-12-25 11:06:20 +0000 | |||
562 | @@ -129,6 +129,15 @@ | |||
563 | 129 | </ul> | 129 | </ul> |
564 | 130 | </metal:nav-pofile-subpages> | 130 | </metal:nav-pofile-subpages> |
565 | 131 | 131 | ||
566 | 132 | <metal:pofile-js-footer define-macro="pofile-js-footer"> | ||
567 | 133 | <script type="text/javascript" | ||
568 | 134 | tal:content=" | ||
569 | 135 | structure string:<!-- | ||
570 | 136 | var autofocus_field = '${view/autofocus_html_id}_new'; | ||
571 | 137 | var translations_order = '${view/translations_order}'; | ||
572 | 138 | var plural_forms = ${context/plural_forms}; | ||
573 | 139 | // -->" /> | ||
574 | 140 | </metal:pofile-js-footer> | ||
575 | 132 | 141 | ||
576 | 133 | </tal:root> | 142 | </tal:root> |
577 | 134 | 143 |
= Bug 359180 =
Would be useful if the First - Previous - Next - Last link in the translation interface had a keyboard shortcut for fast accessing them. Don't need to be visible, but at least documented somewhere.
Maybe even the Save & Continue button.
== Pre-implementation notes ==
Talking with Danilo we agreed not to use tabindex as it may disturb screen readers.
HTML Accesskeys were used for links, while YUI3 key-event here used for navigation between input fields and copying the original text.
== Implementation details ==
Shift+Alt+a - First (using accesskey)
Shift+Alt+n - Next (using accesskey)
Shift+Alt+p - Previous (using accesskey)
Shift+Alt+l - Last (using accesskey)
Shift+Alt+S - Save and continue (using accesskey)
Shift+Alt+Down - Next field (using YUI3 key-event)
Shift+Alt+Up - Previous field (using YUI3 key-event)
Shift+Alt+C - Copy original text (both singular and plural) (using YUI3 key-event)
First field is autofocused.
I went for Shift+Alt as they are used for Acceskey, and I assume browsers will try to reserve them.
Once keybindings are agreed they should be documented on help.launchpad.net
Accesskeys were tested on Epiphany and Chromium. en.wikipedia. org/wiki/ Access_ key
Wikipedia says that Accesskeys in Firefox are enabled with Shift+Alt but I was not able to activate them on my Firefox.
http://
-----
The legacy JS code from canonical/ launchpad/ icing/build/ lp/lp.js and from pofile-tranlate.pt was moved to javascript/ translations/ pofile. js.
Those functions from lp.js was rewritten using YUI3. Maybe we can no delete them from lp.js.
== Tests ==
I was not able to produce a reasonable windmill test since it is not trivial to find the current focused node or to see if a node is focused.
This requires adding onFocus and onBlur trigger for all DOM nodes.
== Demo and Q/A ==
Log in as admin or as a person with rights on adding translations to a pofile.
Go to a pofile translate page: /translations. launchpad. dev/ubuntu/ hoary/+ source/ evolution/ +pots/evolution -2.2/es/ +translate? start=0
https:/
The first field should have focus and you can start translating right away.
Adding a new translation should automatically select the radio button in front of it.
Pressing Alt + Shift + C will copy the original text. Current text will be discarded.
Pressing Alt + Shift + Down will set the focus to the next input field. Last field is Save and Continue button.
Pressing Alt + Shift + Up will set the focus to the previous input field. Nothing will happen when pressing on the first field.
Pressing Alt + Shift + A from anywhere in the page will point the browser to the fist page in the batch navigation.
Pressing Alt + Shift + L from anywhere in the page will point the browser to the last page in the batch navigation.
Pressing Alt + Shift + N from anywhere in the page will point the browser to the next page in the batch navigation.
Pressing Alt + Shift + P from anywhere in the page will point the browser to the previous page in the batch navigation.
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files:
lib/canon...