Merge lp:~adiroiban/launchpad/bug-359180 into lp:launchpad

Proposed by Adi Roiban
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
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+16422@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (3.4 KiB)

= 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.
Wikipedia says that Accesskeys in Firefox are enabled with Shift+Alt but I was not able to activate them on my Firefox.
http://en.wikipedia.org/wiki/Access_key

-----

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:
https://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=0

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...

Read more...

Revision history for this message
Aaron Bentley (abentley) wrote :

This looks mostly good. Please rename tabindex_chain to something else (we discussed "translations_order") since it doesn't affect tabbing.

The current interaction of autofocus_html_id and Y.lp.pofile.initializeKeyBindings means that javascript errors will happen if no autofocus_html_id is present. Please fix this (e.g. by addding empty variables, as you suggested).

review: Needs Fixing (code)
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (6.0 KiB)

Thanks for the review!

Here is the latest diff.

=== modified file 'lib/canonical/launchpad/javascript/translations/pofile.js'
--- lib/canonical/launchpad/javascript/translations/pofile.js 2009-12-21 11:36:04 +0000
+++ lib/canonical/launchpad/javascript/translations/pofile.js 2009-12-22 07:44:31 +0000
@@ -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(field);
     // 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(original);
+ self.setFocus(field);
     // 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.set('value', unescapeHTML(
         from.get('innerHTML').replace(/<\/?[^>]+>/gi, "")));
@@ -182,7 +181,13 @@
  */
 self.initializeKeyBindings = function(e) {

- var fields = tabindex_chain.split(' ');
+ if (translations_order.length < 1) {
+ // If no translations fiels are displayed on the page
+ // don't initialize the translations order
+ return;
+ }
+
+ var fields = translations_order.split(' ');
     // The last field is Save & Continue button
     fields.push('save_and_continue_button');

@@ -192,7 +197,7 @@

         var html_parts = fields[key].split('_');
         var original_stem = html_parts[0] + '_' + html_parts[1];
- var translation_stem = original_stem + '_' + html_parts[2];
+ var translation_stem = fields[key].replace(/_translation_(\d)+_new/,"");
         var select_widget = (
             translation_stem + '_' + html_parts[3] + '_' +
             html_parts[4] + '_new_select');

=== modified file 'lib/lp/translations/browser/pofile.py'
--- lib/lp/translations/browser/pofile.py 2009-12-20 11:12:16 +0000
+++ lib/lp/translations/browser/pofile.py 2009-12-22 06:16:13 +0000
@@ -935,17 +935,17 @@
             first_field = first_message.translation_dictionaries[0]
             return first_field['html_id_translation']
         except IndexError:
- return False
+ return ""

     @property
- def tabindex_chain(self):
+ def translations_order(self):
         try:
- tabindex = []
+ order = []
             for message in self.translationmessage_views:
                 for dictionary in message.translation_dictionaries:
- tabindex.append(
+ order.append(
                         dictionary['html_id_translation'] + '_new')
- return ' '.join(tabindex)
+ return ' '.join(order)

         except IndexError:
             return ""

=== modified file 'lib/lp/translations/b...

Read more...

Revision history for this message
Aaron Bentley (abentley) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/javascript/translations/pofile.js'
--- lib/canonical/launchpad/javascript/translations/pofile.js 2009-11-23 19:29:02 +0000
+++ lib/canonical/launchpad/javascript/translations/pofile.js 2009-12-25 11:06:20 +0000
@@ -1,7 +1,7 @@
1/** Copyright (c) 2009, Canonical Ltd. All rights reserved.1/** Copyright (c) 2009, Canonical Ltd. All rights reserved.
2 *2 *
3 * @module lp.pofile3 * @module lp.pofile
4 * @requires event, node4 * @requires event, event-key, node, cookie, anim
5 */5 */
66
7YUI.add('lp.pofile', function(Y) {7YUI.add('lp.pofile', function(Y) {
@@ -41,4 +41,196 @@
41 }41 }
42};42};
4343
44}, "0.1", {"requires": ["event", "node"]});44var hide_notification = function(node) {
45 var hide_anim = new Y.Anim({
46 node: node,
47 to: { height: 0,
48 marginTop: 0, marginBottom: 0,
49 paddingTop: 0, paddingBottom: 0 }
50 });
51 node.setStyle('border', 'none');
52 hide_anim.set('duration', 0.4);
53 hide_anim.on('end', function(e) {
54 node.setStyle('display', 'none');
55 });
56 hide_anim.run();
57};
58
59
60self.updateNotificationBox = function(e) {
61 var notice = Y.one('.important-notice-container');
62 if (notice === null) {
63 return;
64 }
65 var balloon = notice.one('.important-notice-balloon');
66 var dismiss_notice_cookie = ('translation-docs-for-' +
67 documentation_cookie);
68
69 // Check the cookie to see if the user has already dismissed
70 // the notification box for this session.
71 var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean);
72 if (already_seen) {
73 notice.setStyle('display', 'none');
74 }
75
76 var cancel_button = notice.one(
77 '.important-notice-cancel-button');
78 // Cancel button starts out hidden. If user has JavaScript,
79 // then we want to show it.
80 cancel_button.setStyle('visibility', 'visible');
81 cancel_button.on('click', function(e) {
82 e.halt();
83 hide_notification(balloon);
84 Y.Cookie.set(dismiss_notice_cookie, true);
85 });
86};
87
88
89self.setFocus = function(field) {
90 //Y.log(e.type + ":" + e.keyCode + ': ' + field);
91 // if there is nofield, do nothing
92 if (Y.one('#' + field)) {
93 Y.one('#' + field).focus();
94 }
95};
96
97
98var setNextFocus = function(e, field) {
99 self.setFocus(field);
100 // stopPropagation() and preventDefault()
101 e.halt();
102};
103
104
105var setPreviousFocus = function(e, field, original) {
106
107 // Original singular test is focused first to make sure
108 // it is visible when scrolling up
109 self.setFocus(original);
110 self.setFocus(field);
111 // stopPropagation() and preventDefault()
112 e.halt();
113};
114
115
116var copyOriginalTextOne = function(from_id, to_id, select_id) {
117 var from = Y.one('#' + from_id);
118 var to = Y.one('#' + to_id);
119 // The replacement regex strips all tags from the html.
120 to.set('value', unescapeHTML(
121 from.get('innerHTML').replace(/<\/?[^>]+>/gi, "")));
122 selectWidget(select_id);
123};
124
125
126var copyOriginalTextPlural = function (from_id,
127 to_id_pattern, nplurals) {
128 // skip when x is 0, as that is the singular
129 for (var x = 1; x < nplurals; x++) {
130 var to_id = to_id_pattern + x + "_new";
131 var to_select = to_id_pattern + x + "_new_select";
132 copyOriginalTextOne(from_id, to_id, to_select);
133 }
134};
135
136
137var copyOriginalTextAll = function(e, original_stem, translation_stem) {
138
139 var original_singular = original_stem + '_singular';
140 var original_plural = original_stem + '_plural';
141 var singular_select = translation_stem + '_translation_0_new_select';
142 var translation_singular = translation_stem + '_translation_0_new';
143 var translation_plural = translation_stem + '_translation_';
144 //Y.log(e.type + ":" + e.keyCode + ': ' + singular_select);
145 // Copy singular text
146 copyOriginalTextOne(
147 original_singular, translation_singular, singular_select);
148
149 // Copy plural text if needed
150 if (Y.one('#' + translation_plural + '1')) {
151 copyOriginalTextPlural(
152 original_plural, translation_plural, plural_forms);
153 }
154 // stopPropagation() and preventDefault()
155 e.halt();
156};
157
158
159var selectWidget = function(widget) {
160 if (Y.one('#' + widget)) {
161 Y.one('#' + widget).set('checked', true);
162 }
163};
164
165
166var selectTranslation = function(e, widget) {
167 //Y.log(e.type + ":" + e.keyCode + ': ' + widget);
168 // Don't select when tabbing, navigating Up or Down and simply pressing
169 // enter to submit the form.
170 if (e.keyCode == 9 || e.keyCode == 13 ||
171 e.keyCode == 40 || e.keyCode == 38) {
172 return;
173 }
174 selectWidget(widget);
175};
176
177
178/**
179 * Initialize event-key bindings such as moving to the next or previous
180 * field, or copying original text
181 */
182self.initializeKeyBindings = function(e) {
183
184 if (translations_order.length < 1) {
185 // If no translations fiels are displayed on the page
186 // don't initialize the translations order
187 return;
188 }
189
190 var fields = translations_order.split(' ');
191 // The last field is Save & Continue button
192 fields.push('save_and_continue_button');
193
194 for (var key = 0; key < fields.length; key++) {
195 var next = key + 1;
196 var previous = key - 1;
197
198 var html_parts = fields[key].split('_');
199 var original_stem = html_parts[0] + '_' + html_parts[1];
200 var translation_stem = fields[key].replace(/_translation_(\d)+_new/,"");
201 var select_widget = (
202 translation_stem + '_' + html_parts[3] + '_' +
203 html_parts[4] + '_new_select');
204
205 Y.on(
206 'change', selectTranslation,
207 '#' + fields[key], Y, select_widget);
208 Y.on(
209 'keypress', selectTranslation,
210 '#' + fields[key], Y, select_widget);
211
212 // Set next field and copy text for all but last field
213 // (last is Save & Continue button)
214 if (key < fields.length - 1) {
215 Y.on(
216 'key', setNextFocus, '#' + fields[key],
217 'down:40+shift+alt', Y, fields[next]);
218 Y.on(
219 'key', copyOriginalTextAll, '#' + fields[key],
220 'down:67+shift+alt', Y, original_stem, translation_stem);
221 }
222
223 // Set previous field for all but first field
224 if (key > 0) {
225 var parts = fields[previous].split('_');
226 var singular_copy_text = (
227 parts[0] + '_' + parts[1] + '_singular_copy_text');
228 Y.on(
229 'key', setPreviousFocus, '#' + fields[key],
230 'down:38+shift+alt', Y, fields[previous],
231 singular_copy_text);
232 }
233 }
234};
235
236}, "0.1", {"requires": ["event", "event-key", "node", 'cookie', 'anim']});
45237
=== modified file 'lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt'
--- lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2009-07-17 17:59:07 +0000
+++ lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2009-12-25 11:06:20 +0000
@@ -29,6 +29,7 @@
29 tal:attributes="href first_page_url"29 tal:attributes="href first_page_url"
30 class="first"30 class="first"
31 rel="first"31 rel="first"
32 accesskey="A"
32 >First</a>33 >First</a>
33 <span tal:condition="not:first_page_url" class="first inactive"34 <span tal:condition="not:first_page_url" class="first inactive"
34 >First</span>35 >First</span>
@@ -38,6 +39,7 @@
38 tal:attributes="href prev_page_url"39 tal:attributes="href prev_page_url"
39 class="previous"40 class="previous"
40 rel="previous"41 rel="previous"
42 accesskey="P"
41 >Previous</a>43 >Previous</a>
42 <span tal:condition="not:prev_page_url" class="previous inactive"44 <span tal:condition="not:prev_page_url" class="previous inactive"
43 >Previous</span>45 >Previous</span>
@@ -47,6 +49,7 @@
47 tal:attributes="href next_page_url"49 tal:attributes="href next_page_url"
48 class="next"50 class="next"
49 rel="next"51 rel="next"
52 accesskey="N"
50 ><strong>Next</strong></a>53 ><strong>Next</strong></a>
51 <span tal:condition="not:next_page_url" class="next inactive"54 <span tal:condition="not:next_page_url" class="next inactive"
52 ><strong>Next</strong></span>55 ><strong>Next</strong></span>
@@ -57,6 +60,7 @@
57 tal:attributes="href last_page_url"60 tal:attributes="href last_page_url"
58 class="last"61 class="last"
59 rel="last"62 rel="last"
63 accesskey="L"
60 >Last</a>64 >Last</a>
61 <span tal:condition="not:last_page_url" class="last inactive"65 <span tal:condition="not:last_page_url" class="last inactive"
62 >Last</span>66 >Last</span>
6367
=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt 2009-12-16 19:59:06 +0000
+++ lib/lp/app/templates/base-layout-macros.pt 2009-12-25 11:06:20 +0000
@@ -80,6 +80,8 @@
80 <script type="text/javascript"80 <script type="text/javascript"
81 tal:attributes="src string:${yui}/event/event.js"></script>81 tal:attributes="src string:${yui}/event/event.js"></script>
82 <script type="text/javascript"82 <script type="text/javascript"
83 tal:attributes="src string:${yui}/event/event-key.js"></script>
84 <script type="text/javascript"
83 tal:attributes="src string:${yui}/event-custom/event-custom.js"></script>85 tal:attributes="src string:${yui}/event-custom/event-custom.js"></script>
84 <script type="text/javascript"86 <script type="text/javascript"
85 tal:attributes="src string:${yui}/event-simulate/event-simulate.js"></script>87 tal:attributes="src string:${yui}/event-simulate/event-simulate.js"></script>
8688
=== modified file 'lib/lp/translations/browser/pofile.py'
--- lib/lp/translations/browser/pofile.py 2009-09-17 19:05:29 +0000
+++ lib/lp/translations/browser/pofile.py 2009-12-25 11:06:20 +0000
@@ -503,8 +503,6 @@
503503
504 DEFAULT_BATCH_SIZE = 50504 DEFAULT_BATCH_SIZE = 50
505505
506 page_title = "Contributions"
507
508 @property506 @property
509 def _person_name(self):507 def _person_name(self):
510 """Person's display name. Graceful about unknown persons."""508 """Person's display name. Graceful about unknown persons."""
@@ -930,6 +928,28 @@
930 def completeness(self):928 def completeness(self):
931 return '%.0f%%' % self.context.translatedPercentage()929 return '%.0f%%' % self.context.translatedPercentage()
932930
931 @property
932 def autofocus_html_id(self):
933 try:
934 first_message = self.translationmessage_views[0]
935 first_field = first_message.translation_dictionaries[0]
936 return first_field['html_id_translation']
937 except IndexError:
938 return ""
939
940 @property
941 def translations_order(self):
942 try:
943 order = []
944 for message in self.translationmessage_views:
945 for dictionary in message.translation_dictionaries:
946 order.append(
947 dictionary['html_id_translation'] + '_new')
948 return ' '.join(order)
949
950 except IndexError:
951 return ""
952
933953
934class POExportView(BaseExportView):954class POExportView(BaseExportView):
935955
936956
=== modified file 'lib/lp/translations/browser/translationmessage.py'
--- lib/lp/translations/browser/translationmessage.py 2009-09-17 08:41:05 +0000
+++ lib/lp/translations/browser/translationmessage.py 2009-12-25 11:06:20 +0000
@@ -829,6 +829,28 @@
829 self._redirectToNextPage()829 self._redirectToNextPage()
830 return True830 return True
831831
832 @property
833 def autofocus_html_id(self):
834 try:
835 first_message = self.translationmessage_view
836 first_field = first_message.translation_dictionaries[0]
837 return first_field['html_id_translation']
838 except IndexError:
839 return ""
840
841 @property
842 def translations_order(self):
843 try:
844 order = []
845 message = self.translationmessage_view
846 for dictionary in message.translation_dictionaries:
847 order.append(dictionary['html_id_translation'] + '_new')
848 return ' '.join(order)
849
850 except IndexError:
851 return ""
852
853
832class CurrentTranslationMessageView(LaunchpadView):854class CurrentTranslationMessageView(LaunchpadView):
833 """Holds all data needed to show an ITranslationMessage.855 """Holds all data needed to show an ITranslationMessage.
834856
835857
=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2009-07-01 20:45:39 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2009-12-25 11:06:20 +0000
@@ -43,7 +43,7 @@
43 >>> print browser.url43 >>> print browser.url
44 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=144 http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1
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')
46 <textarea ... name="msgset_149_es_translation_0_new" ...>46 <textarea ... name="msgset_149_es_translation_0_new"...>
4747
48 foo48 foo
4949
@@ -63,7 +63,7 @@
63 >>> print find_tag_by_id(63 >>> print find_tag_by_id(
64 ... browser.contents,64 ... browser.contents,
65 ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE65 ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE
66 <textarea ... name="msgset_149_es_translation_0_new" ...>66 <textarea ... name="msgset_149_es_translation_0_new"...>
67 foo</textarea>67 foo</textarea>
6868
69Now, Check that even though the user forgot the trailing new line char,69Now, Check that even though the user forgot the trailing new line char,
7070
=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2009-12-07 18:42:21 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2009-12-25 11:06:20 +0000
@@ -158,7 +158,7 @@
158 ... print id158 ... print id
159 msgset_130159 msgset_130
160 ...160 ...
161 msgset_130_singular161 msgset_130_singular...
162162
163HTML element identifiers for suggestions and translations on this form are163HTML element identifiers for suggestions and translations on this form are
164constructed as an underscore-separated sequence of:164constructed as an underscore-separated sequence of:
@@ -181,6 +181,7 @@
181 msgset_130_es_suggestion_562_0_origin181 msgset_130_es_suggestion_562_0_origin
182 msgset_130_es_suggestion_562_0_radiobutton182 msgset_130_es_suggestion_562_0_radiobutton
183 msgset_130_singular183 msgset_130_singular
184 msgset_130_singular_copy_text
184185
185Radio buttons are grouped by their name attribute. The translate page shows186Radio buttons are grouped by their name attribute. The translate page shows
186each translatable message with one radiobutton to select the existing187each translatable message with one radiobutton to select the existing
187188
=== modified file 'lib/lp/translations/templates/currenttranslationmessage-translate-one.pt'
--- lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2009-12-07 18:42:21 +0000
+++ lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2009-12-25 11:06:20 +0000
@@ -42,6 +42,7 @@
42 <a href=""42 <a href=""
43 tal:condition="not:context/potmsgset/is_translation_credit"43 tal:condition="not:context/potmsgset/is_translation_credit"
44 tal:attributes="44 tal:attributes="
45 id string:${view/html_id}_singular_copy_text;
45 onClick string:46 onClick string:
46 javascript:copyInnerHTMLById(47 javascript:copyInnerHTMLById(
47 '${view/html_id}_singular',48 '${view/html_id}_singular',
@@ -504,8 +505,6 @@
504 lang context/pofile/language/dashedcode;505 lang context/pofile/language/dashedcode;
505 dir context/pofile/language/abbreviated_text_dir;506 dir context/pofile/language/abbreviated_text_dir;
506 value translation_dictionary/submitted_translation;507 value translation_dictionary/submitted_translation;
507 onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
508 onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
509 "508 "
510 class="translate expandable"509 class="translate expandable"
511 />510 />
@@ -528,8 +527,6 @@
528 name string:${translation_dictionary/html_id_translation}_new;527 name string:${translation_dictionary/html_id_translation}_new;
529 lang context/pofile/language/dashedcode;528 lang context/pofile/language/dashedcode;
530 dir context/pofile/language/abbreviated_text_dir;529 dir context/pofile/language/abbreviated_text_dir;
531 onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
532 onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
533 ">530 ">
534<tal:content replace="translation_dictionary/submitted_translation" /></textarea>531<tal:content replace="translation_dictionary/submitted_translation" /></textarea>
535 </tal:with-content>532 </tal:with-content>
@@ -548,8 +545,6 @@
548 name string:${translation_dictionary/html_id_translation}_new;545 name string:${translation_dictionary/html_id_translation}_new;
549 lang context/pofile/language/dashedcode;546 lang context/pofile/language/dashedcode;
550 dir context/pofile/language/abbreviated_text_dir;547 dir context/pofile/language/abbreviated_text_dir;
551 onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
552 onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event);
553 "></textarea>548 "></textarea>
554 </tal:without-content>549 </tal:without-content>
555 </tal:multi-line>550 </tal:multi-line>
556551
=== modified file 'lib/lp/translations/templates/pofile-translate.pt'
--- lib/lp/translations/templates/pofile-translate.pt 2009-12-10 13:08:33 +0000
+++ lib/lp/translations/templates/pofile-translate.pt 2009-12-25 11:06:20 +0000
@@ -17,53 +17,16 @@
17 tal:condition="devmode"17 tal:condition="devmode"
18 tal:define="lp_js string:${icingroot}/build"18 tal:define="lp_js string:${icingroot}/build"
19 tal:attributes="src string:${lp_js}/translations/pofile.js"></script>19 tal:attributes="src string:${lp_js}/translations/pofile.js"></script>
20
20 <script type="text/javascript">21 <script type="text/javascript">
21 registerLaunchpadFunction(insertAllExpansionButtons);22 registerLaunchpadFunction(insertAllExpansionButtons);
2223 LPS.use('lp.pofile', function(Y) {
23 LPS.use('node', 'cookie', 'anim', 'lp.pofile', function(Y) {
24
25 var hide_notification = function(node) {
26 var hide_anim = new Y.Anim({
27 node: node,
28 to: { height: 0,
29 marginTop: 0, marginBottom: 0,
30 paddingTop: 0, paddingBottom: 0 }
31 });
32 node.setStyle('border', 'none');
33 hide_anim.set('duration', 0.4);
34 hide_anim.on('end', function(e) {
35 node.setStyle('display', 'none');
36 });
37 hide_anim.run();
38 }
39
40 var updateNotificationBox = function(e) {
41 var notice = Y.one('.important-notice-container');
42 var balloon = notice.one('.important-notice-balloon');
43 var dismiss_notice_cookie = ('translation-docs-for-' +
44 documentation_cookie);
45
46 // Check the cookie to see if the user has already dismissed
47 // the notification box for this session.
48 var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean);
49 if (already_seen) {
50 notice.setStyle('display', 'none');
51 }
52
53 var cancel_button = notice.one(
54 '.important-notice-cancel-button');
55 // Cancel button starts out hidden. If user has JavaScript,
56 // then we want to show it.
57 cancel_button.setStyle('visibility', 'visible');
58 cancel_button.on('click', function(e) {
59 e.halt();
60 hide_notification(balloon);
61 Y.Cookie.set(dismiss_notice_cookie, true);
62 });
63 };
64
65 Y.on('domready', Y.lp.pofile.setupSuggestionDismissal);24 Y.on('domready', Y.lp.pofile.setupSuggestionDismissal);
66 Y.on('domready', updateNotificationBox);25 Y.on('domready', Y.lp.pofile.initializeKeyBindings);
26 Y.on('domready', Y.lp.pofile.updateNotificationBox);
27 Y.on('domready', function(e) {
28 Y.lp.pofile.setFocus(autofocus_field);
29 });
67 });30 });
68 </script>31 </script>
69 </div>32 </div>
@@ -134,7 +97,7 @@
134 <input type="hidden" name="batch" value=""97 <input type="hidden" name="batch" value=""
135 tal:attributes="value view/size" />98 tal:attributes="value view/size" />
136 <input type="hidden" name="show" value="all" />99 <input type="hidden" name="show" value="all" />
137 <label for="search_box">Search:</label>100 <label for="search_box" accesskey="F">Search:</label>
138 <input id="search_box" type="text" name="search"101 <input id="search_box" type="text" name="search"
139 title="Enter text to search for"102 title="Enter text to search for"
140 tal:attributes="value view/search_text" />103 tal:attributes="value view/search_text" />
@@ -262,6 +225,8 @@
262 <td style="text-align: right;">225 <td style="text-align: right;">
263 <input type="submit"226 <input type="submit"
264 name="submit_translations"227 name="submit_translations"
228 accesskey="S"
229 id="save_and_continue_button"
265 value="Save &amp; Continue"230 value="Save &amp; Continue"
266 />231 />
267 </td>232 </td>
@@ -269,7 +234,6 @@
269 </tbody>234 </tbody>
270 </table>235 </table>
271 </form>236 </form>
272
273 <!-- Paging doodads. -->237 <!-- Paging doodads. -->
274 <tal:navigation238 <tal:navigation
275 replace="structure view/batchnav/@@+navigation-links-lower" />239 replace="structure view/batchnav/@@+navigation-links-lower" />
@@ -277,6 +241,8 @@
277 <tal:status replace="structure context/@@+access" />241 <tal:status replace="structure context/@@+access" />
278 <tal:contributors replace="structure context/@@+contributors" />242 <tal:contributors replace="structure context/@@+contributors" />
279 </tal:havepluralforms>243 </tal:havepluralforms>
244 <metal:pofile-js-footer
245 use-macro="context/@@+translations-macros/pofile-js-footer" />
280 </div>246 </div>
281 </body>247 </body>
282</html>248</html>
283249
=== modified file 'lib/lp/translations/templates/translationmessage-translate.pt'
--- lib/lp/translations/templates/translationmessage-translate.pt 2009-12-10 13:08:33 +0000
+++ lib/lp/translations/templates/translationmessage-translate.pt 2009-12-25 11:06:20 +0000
@@ -20,6 +20,10 @@
20 <script type="text/javascript">20 <script type="text/javascript">
21 LPS.use('node', 'lp.pofile', function(Y) {21 LPS.use('node', 'lp.pofile', function(Y) {
22 Y.on('domready', Y.lp.pofile.setupSuggestionDismissal);22 Y.on('domready', Y.lp.pofile.setupSuggestionDismissal);
23 Y.on('domready', Y.lp.pofile.initializeKeyBindings);
24 Y.on('domready', function(e) {
25 Y.one('#' + autofocus_field).focus();
26 });
23 });27 });
24 </script>28 </script>
25 </div>29 </div>
@@ -76,12 +80,13 @@
76 <th colspan="5" style="text-align: right;">80 <th colspan="5" style="text-align: right;">
77 <input type="submit"81 <input type="submit"
78 name="submit_translations"82 name="submit_translations"
83 accesskey="S"
84 id="save_and_continue_button"
79 value="Save &amp; Continue" />85 value="Save &amp; Continue" />
80 </th>86 </th>
81 </tr>87 </tr>
82 </tfoot>88 </tfoot>
83 </table>89 </table>
84
85 </form>90 </form>
8691
87 <!-- Paging doodads. -->92 <!-- Paging doodads. -->
@@ -89,8 +94,9 @@
89 replace="structure view/batchnav/@@+navigation-links-lower" />94 replace="structure view/batchnav/@@+navigation-links-lower" />
90 <tal:status replace="structure context/pofile/@@+access" />95 <tal:status replace="structure context/pofile/@@+access" />
91 </tal:havepluralforms>96 </tal:havepluralforms>
97 <metal:pofile-js-footer
98 use-macro="context/@@+translations-macros/pofile-js-footer" />
92 </div>99 </div>
93
94 </body>100 </body>
95101
96</html>102</html>
97103
=== modified file 'lib/lp/translations/templates/translations-macros.pt'
--- lib/lp/translations/templates/translations-macros.pt 2009-12-07 18:42:21 +0000
+++ lib/lp/translations/templates/translations-macros.pt 2009-12-25 11:06:20 +0000
@@ -129,6 +129,15 @@
129 </ul>129 </ul>
130</metal:nav-pofile-subpages>130</metal:nav-pofile-subpages>
131131
132<metal:pofile-js-footer define-macro="pofile-js-footer">
133 <script type="text/javascript"
134 tal:content="
135 structure string:<!--
136 var autofocus_field = '${view/autofocus_html_id}_new';
137 var translations_order = '${view/translations_order}';
138 var plural_forms = ${context/plural_forms};
139 // -->" />
140</metal:pofile-js-footer>
132141
133</tal:root>142</tal:root>
134143