Merge lp:~rockstar/launchpad/update-review-table-on-comment into lp:launchpad/db-devel
- update-review-table-on-comment
- Merge into db-devel
Proposed by
Paul Hummer
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Paul Hummer | ||||||||
Approved revision: | not available | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | lp:~rockstar/launchpad/update-review-table-on-comment | ||||||||
Merge into: | lp:launchpad/db-devel | ||||||||
Prerequisite: | lp:~rockstar/launchpad/clean-code-windmill-tests | ||||||||
Diff against target: |
931 lines (+437/-396) 6 files modified
Makefile (+0/-1) lib/canonical/launchpad/javascript/lp/comment.js (+28/-0) lib/canonical/launchpad/javascript/lp/lp-mochi.js (+383/-0) lib/canonical/launchpad/javascript/lp/lp.js (+0/-392) lib/lp/app/templates/base-layout-macros.pt (+11/-3) lib/lp/code/windmill/tests/test_branchmergeproposal_review.py (+15/-0) |
||||||||
To merge this branch: | bzr merge lp:~rockstar/launchpad/update-review-table-on-comment | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey (community) | Approve | ||
Review via email: mp+17439@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2010-01-14 22:59:54 +0000 | |||
3 | +++ Makefile 2010-01-21 23:36:33 +0000 | |||
4 | @@ -126,7 +126,6 @@ | |||
5 | 126 | -n launchpad \ | 126 | -n launchpad \ |
6 | 127 | -s lib/canonical/launchpad/javascript \ | 127 | -s lib/canonical/launchpad/javascript \ |
7 | 128 | -b $(LP_BUILT_JS_ROOT) \ | 128 | -b $(LP_BUILT_JS_ROOT) \ |
8 | 129 | lib/canonical/launchpad/icing/MochiKit.js \ | ||
9 | 130 | $(shell $(HERE)/utilities/yui-deps.py) \ | 129 | $(shell $(HERE)/utilities/yui-deps.py) \ |
10 | 131 | lib/canonical/launchpad/icing/lazr/build/lazr.js | 130 | lib/canonical/launchpad/icing/lazr/build/lazr.js |
11 | 132 | 131 | ||
12 | 133 | 132 | ||
13 | === modified file 'lib/canonical/launchpad/javascript/lp/comment.js' | |||
14 | --- lib/canonical/launchpad/javascript/lp/comment.js 2009-12-15 02:54:01 +0000 | |||
15 | +++ lib/canonical/launchpad/javascript/lp/comment.js 2010-01-21 23:36:33 +0000 | |||
16 | @@ -89,9 +89,14 @@ | |||
17 | 89 | this.post_comment(bind(function(message_entry) { | 89 | this.post_comment(bind(function(message_entry) { |
18 | 90 | this.get_comment_HTML( | 90 | this.get_comment_HTML( |
19 | 91 | message_entry, bind(this.insert_comment_HTML, this)); | 91 | message_entry, bind(this.insert_comment_HTML, this)); |
20 | 92 | this._add_comment_success(); | ||
21 | 92 | }, this)); | 93 | }, this)); |
22 | 93 | }, | 94 | }, |
23 | 94 | /** | 95 | /** |
24 | 96 | * A callable hook for firing extra events. | ||
25 | 97 | */ | ||
26 | 98 | _add_comment_success: function() {}, | ||
27 | 99 | /** | ||
28 | 95 | * Post the comment to the Launchpad API | 100 | * Post the comment to the Launchpad API |
29 | 96 | * | 101 | * |
30 | 97 | * @method post_comment | 102 | * @method post_comment |
31 | @@ -390,6 +395,29 @@ | |||
32 | 390 | CodeReviewComment.superclass.syncUI.apply(this); | 395 | CodeReviewComment.superclass.syncUI.apply(this); |
33 | 391 | var review_type_disabled = (this.get_vote() === null); | 396 | var review_type_disabled = (this.get_vote() === null); |
34 | 392 | this.review_type.set('disabled', review_type_disabled); | 397 | this.review_type.set('disabled', review_type_disabled); |
35 | 398 | }, | ||
36 | 399 | /** | ||
37 | 400 | * A callable hook for firing extra events. | ||
38 | 401 | */ | ||
39 | 402 | _add_comment_success: function() { | ||
40 | 403 | var VOTES_TABLE_PATH = '+votes'; | ||
41 | 404 | Y.io(VOTES_TABLE_PATH, { | ||
42 | 405 | on: { | ||
43 | 406 | success: function(id, response) { | ||
44 | 407 | var target = Y.one('#votes-target'); | ||
45 | 408 | target.set('innerHTML', response.responseText); | ||
46 | 409 | |||
47 | 410 | var username = LP.client.links.me.substring(2); | ||
48 | 411 | var new_reviewer = Y.one('#review-' + username); | ||
49 | 412 | if (Y.lang.isValue(new_reviewer)) { | ||
50 | 413 | var anim = Y.lazr.anim.green_flash({ | ||
51 | 414 | node: new_reviewer}); | ||
52 | 415 | anim.run(); | ||
53 | 416 | } | ||
54 | 417 | }, | ||
55 | 418 | failure: function() {} | ||
56 | 419 | } | ||
57 | 420 | }); | ||
58 | 393 | } | 421 | } |
59 | 394 | }); | 422 | }); |
60 | 395 | Y.lp.CodeReviewComment = CodeReviewComment; | 423 | Y.lp.CodeReviewComment = CodeReviewComment; |
61 | 396 | 424 | ||
62 | === added file 'lib/canonical/launchpad/javascript/lp/lp-mochi.js' | |||
63 | --- lib/canonical/launchpad/javascript/lp/lp-mochi.js 1970-01-01 00:00:00 +0000 | |||
64 | +++ lib/canonical/launchpad/javascript/lp/lp-mochi.js 2010-01-21 23:36:33 +0000 | |||
65 | @@ -0,0 +1,383 @@ | |||
66 | 1 | // Copyright 2010 Canonical Ltd. All rights reserved. | ||
67 | 2 | // | ||
68 | 3 | // Launchpad JavaScript core functions that require the MochiKit library. | ||
69 | 4 | |||
70 | 5 | function getContentArea() { | ||
71 | 6 | // to end all doubt on where the content sits. It also felt a bit | ||
72 | 7 | // silly doing this over and over in every function, even if it is | ||
73 | 8 | // a tiny operation. Just guarding against someone changing the | ||
74 | 9 | // names again, in the name of semantics or something.... ;) | ||
75 | 10 | var node = document.getElementById('maincontent'); | ||
76 | 11 | if (!node) {node = $('content');} | ||
77 | 12 | if (!node) {node = $('mainarea');} | ||
78 | 13 | return node; | ||
79 | 14 | } | ||
80 | 15 | |||
81 | 16 | function toggleCollapsible(e) { | ||
82 | 17 | // this is the function that collapses/expands fieldsets. | ||
83 | 18 | |||
84 | 19 | // "this" is the node that the event is attached to | ||
85 | 20 | var node = this; | ||
86 | 21 | |||
87 | 22 | // walk up the node hierarchy until we find the <legend> element | ||
88 | 23 | while (node.nodeName.toLowerCase() != 'legend') { | ||
89 | 24 | node = node.parentNode; | ||
90 | 25 | if (!node) { | ||
91 | 26 | return false; | ||
92 | 27 | } | ||
93 | 28 | } | ||
94 | 29 | |||
95 | 30 | // the expander image is legend -> a -> img | ||
96 | 31 | var icon = node.firstChild.firstChild; | ||
97 | 32 | var legend = node; | ||
98 | 33 | |||
99 | 34 | if (icon.getAttribute('src').indexOf('/@@/treeCollapsed') != -1) { | ||
100 | 35 | // that was an ugly check, but IE rewrites image sources to | ||
101 | 36 | // absolute urls from some sick reason.... | ||
102 | 37 | icon.setAttribute('src','/@@/treeExpanded'); | ||
103 | 38 | swapElementClass( | ||
104 | 39 | legend.parentNode.lastChild, 'collapsed', 'expanded'); | ||
105 | 40 | swapElementClass( | ||
106 | 41 | legend.parentNode.childNodes[1], 'expanded', 'collapsed'); | ||
107 | 42 | } else { | ||
108 | 43 | icon.setAttribute('src','/@@/treeCollapsed'); | ||
109 | 44 | swapElementClass( | ||
110 | 45 | legend.parentNode.lastChild, 'expanded', 'collapsed'); | ||
111 | 46 | swapElementClass( | ||
112 | 47 | legend.parentNode.childNodes[1], 'collapsed', 'expanded'); | ||
113 | 48 | } | ||
114 | 49 | return false; | ||
115 | 50 | } | ||
116 | 51 | |||
117 | 52 | function activateCollapsibles() { | ||
118 | 53 | // a script that searches for sections that can be (or are | ||
119 | 54 | // already) collapsed - and enables the collapse-behavior | ||
120 | 55 | |||
121 | 56 | // usage : give the class "collapsible" to a fieldset also, give | ||
122 | 57 | // it a <legend> with some descriptive text. you can also add the | ||
123 | 58 | // class "collapsed" amounting to a total of | ||
124 | 59 | // <fieldset class="collapsible collapsed"> to make the section | ||
125 | 60 | // pre-collapsed | ||
126 | 61 | |||
127 | 62 | // terminate if we hit a non-compliant DOM implementation | ||
128 | 63 | if (!document.getElementsByTagName) { | ||
129 | 64 | return false; | ||
130 | 65 | } | ||
131 | 66 | if (!document.getElementById) { | ||
132 | 67 | return false; | ||
133 | 68 | } | ||
134 | 69 | |||
135 | 70 | // only search in the content-area | ||
136 | 71 | var contentarea = getContentArea(); | ||
137 | 72 | if (!contentarea) { | ||
138 | 73 | return false; | ||
139 | 74 | } | ||
140 | 75 | |||
141 | 76 | // gather all objects that are to be collapsed | ||
142 | 77 | // we only do fieldsets for now. perhaps DIVs later... | ||
143 | 78 | var collapsibles = contentarea.getElementsByTagName('fieldset'); | ||
144 | 79 | |||
145 | 80 | for (var i = 0; i < collapsibles.length; i++) { | ||
146 | 81 | if (collapsibles[i].className.indexOf('collapsible') == -1) { | ||
147 | 82 | continue; | ||
148 | 83 | } | ||
149 | 84 | |||
150 | 85 | var legends = collapsibles[i].getElementsByTagName('LEGEND'); | ||
151 | 86 | |||
152 | 87 | // get the legend | ||
153 | 88 | // if there is no legend, we do not touch the fieldset at all. | ||
154 | 89 | // we assume that if there is a legend, there is only | ||
155 | 90 | // one. nothing else makes any sense | ||
156 | 91 | if (!legends.length) { | ||
157 | 92 | continue; | ||
158 | 93 | } | ||
159 | 94 | var legend = legends[0]; | ||
160 | 95 | |||
161 | 96 | //create an anchor to handle click-events | ||
162 | 97 | var anchor = document.createElement('a'); | ||
163 | 98 | anchor.href = '#'; | ||
164 | 99 | anchor.onclick = toggleCollapsible; | ||
165 | 100 | |||
166 | 101 | // add the icon/button with its functionality to the legend | ||
167 | 102 | var icon = document.createElement('img'); | ||
168 | 103 | icon.setAttribute('src','/@@/treeExpanded'); | ||
169 | 104 | icon.setAttribute('class','collapseIcon'); | ||
170 | 105 | icon.setAttribute('height','14'); | ||
171 | 106 | icon.setAttribute('width','14'); | ||
172 | 107 | |||
173 | 108 | // insert the icon icon at the start of the anchor | ||
174 | 109 | anchor.appendChild(icon); | ||
175 | 110 | |||
176 | 111 | // reparent all the legend's children into a span, and the span | ||
177 | 112 | // into an anchor. The span is used to underline the legend | ||
178 | 113 | // text; because the img is inside the anchor, we can't | ||
179 | 114 | // underline the whole anchor. | ||
180 | 115 | var span = document.createElement('span'); | ||
181 | 116 | while (legend.hasChildNodes()) { | ||
182 | 117 | var child = legend.firstChild; | ||
183 | 118 | legend.removeChild(child); | ||
184 | 119 | span.appendChild(child); | ||
185 | 120 | } | ||
186 | 121 | anchor.appendChild(span); | ||
187 | 122 | |||
188 | 123 | // add the anchor to the legend | ||
189 | 124 | legend.appendChild(anchor); | ||
190 | 125 | |||
191 | 126 | // wrap the contents inside a div to make turning them on and | ||
192 | 127 | // off simpler. unless something very strange happens, this | ||
193 | 128 | // new div should always be the last childnode we'll give it a | ||
194 | 129 | // class to make sure. | ||
195 | 130 | |||
196 | 131 | var hiderWrapper = document.createElement('div'); | ||
197 | 132 | hiderWrapper.setAttribute('class','collapseWrapper'); | ||
198 | 133 | |||
199 | 134 | // also add a new div describing that the element is collapsed. | ||
200 | 135 | var collapsedDescription = document.createElement('div'); | ||
201 | 136 | collapsedDescription.setAttribute('class','collapsedText'); | ||
202 | 137 | collapsedDescription.style.display = 'none'; | ||
203 | 138 | |||
204 | 139 | // if the fieldset has the class of "collapsed", pre-collapse | ||
205 | 140 | // it. This can be used to preserve valuable UI-space | ||
206 | 141 | if (collapsibles[i].className.indexOf('collapsed') != -1 ) { | ||
207 | 142 | icon.setAttribute('src','/@@/treeCollapsed'); | ||
208 | 143 | collapsedDescription.style.display = 'block'; | ||
209 | 144 | setElementClass(hiderWrapper, 'collapsed'); | ||
210 | 145 | // Unhide the fieldset, now that all of its children are hidden: | ||
211 | 146 | removeElementClass(collapsibles[i], 'collapsed'); | ||
212 | 147 | } | ||
213 | 148 | |||
214 | 149 | // now we have the wrapper div.. Stuff all the contents inside it | ||
215 | 150 | var nl = collapsibles[i].childNodes.length; | ||
216 | 151 | for (var j = 0; j < nl; j++){ | ||
217 | 152 | var node = collapsibles[i].childNodes[0]; | ||
218 | 153 | if (node.nodeName == 'LEGEND') { | ||
219 | 154 | if (collapsibles[i].childNodes.length > 1) { | ||
220 | 155 | hiderWrapper.appendChild(collapsibles[i].childNodes[1]); | ||
221 | 156 | } | ||
222 | 157 | } else { | ||
223 | 158 | hiderWrapper.appendChild(collapsibles[i].childNodes[0]); | ||
224 | 159 | } | ||
225 | 160 | } | ||
226 | 161 | // and add it to the document | ||
227 | 162 | collapsibles[i].appendChild(hiderWrapper); | ||
228 | 163 | collapsibles[i].insertBefore(collapsedDescription, hiderWrapper); | ||
229 | 164 | } | ||
230 | 165 | } | ||
231 | 166 | |||
232 | 167 | function toggleFoldable(e) { | ||
233 | 168 | var ELEMENT_NODE = 1; | ||
234 | 169 | var node = this; | ||
235 | 170 | while (node.nextSibling) { | ||
236 | 171 | node = node.nextSibling; | ||
237 | 172 | if (node.nodeType != ELEMENT_NODE) { | ||
238 | 173 | continue; | ||
239 | 174 | } | ||
240 | 175 | if (node.className.indexOf('foldable') == -1) { | ||
241 | 176 | continue; | ||
242 | 177 | } | ||
243 | 178 | if (node.style.display == 'none') { | ||
244 | 179 | node.style.display = 'inline'; | ||
245 | 180 | } else { | ||
246 | 181 | node.style.display = 'none'; | ||
247 | 182 | } | ||
248 | 183 | } | ||
249 | 184 | } | ||
250 | 185 | |||
251 | 186 | function activateFoldables() { | ||
252 | 187 | // Create links to toggle the display of foldable content. | ||
253 | 188 | var included = getElementsByTagAndClassName( | ||
254 | 189 | 'span', 'foldable', document); | ||
255 | 190 | var quoted = getElementsByTagAndClassName( | ||
256 | 191 | 'span', 'foldable-quoted', document); | ||
257 | 192 | var elements = concat(included, quoted); | ||
258 | 193 | for (var i = 0; i < elements.length; i++) { | ||
259 | 194 | var span = elements[i]; | ||
260 | 195 | if (span.className == 'foldable-quoted') { | ||
261 | 196 | var quoted_lines = span.getElementsByTagName('br'); | ||
262 | 197 | if (quoted_lines && quoted_lines.length <= 11) { | ||
263 | 198 | // We do not hide short quoted passages (12 lines) by default. | ||
264 | 199 | continue; | ||
265 | 200 | } | ||
266 | 201 | } | ||
267 | 202 | |||
268 | 203 | var ellipsis = document.createElement('a'); | ||
269 | 204 | ellipsis.style.textDecoration = 'underline'; | ||
270 | 205 | ellipsis.href = VOID_URL; | ||
271 | 206 | ellipsis.onclick = toggleFoldable; | ||
272 | 207 | ellipsis.appendChild(document.createTextNode('[...]')); | ||
273 | 208 | |||
274 | 209 | span.parentNode.insertBefore(ellipsis, span); | ||
275 | 210 | span.insertBefore(document.createElement('br'), span.firstChild); | ||
276 | 211 | span.style.display = 'none'; | ||
277 | 212 | if (span.nextSibling) { | ||
278 | 213 | // Text lines follows this span. | ||
279 | 214 | var br = document.createElement('br'); | ||
280 | 215 | span.parentNode.insertBefore(br, span.nextSibling); | ||
281 | 216 | } | ||
282 | 217 | } | ||
283 | 218 | } | ||
284 | 219 | |||
285 | 220 | function convertTextInputToTextArea(text_input_id, rows) { | ||
286 | 221 | var current_text_input = getElement(text_input_id); | ||
287 | 222 | var new_text_area = document.createElement("textarea"); | ||
288 | 223 | var attributes = { | ||
289 | 224 | 'id': text_input_id, | ||
290 | 225 | 'rows': rows, | ||
291 | 226 | 'name': getNodeAttribute(current_text_input, 'name'), | ||
292 | 227 | 'lang': getNodeAttribute(current_text_input, 'lang'), | ||
293 | 228 | 'dir': getNodeAttribute(current_text_input, 'dir') | ||
294 | 229 | }; | ||
295 | 230 | |||
296 | 231 | updateNodeAttributes(new_text_area, attributes); | ||
297 | 232 | |||
298 | 233 | // we set the javascript events because updateNodeAttributes gets confused | ||
299 | 234 | // with those events, because it says that 'event' is not defined. 'event' | ||
300 | 235 | // is one of the arguments of the javascript call that is being copied. | ||
301 | 236 | new_text_area.setAttribute( | ||
302 | 237 | 'onKeyPress', getNodeAttribute(current_text_input, 'onkeypress')); | ||
303 | 238 | new_text_area.setAttribute( | ||
304 | 239 | 'onChange', getNodeAttribute(current_text_input, 'onchange')); | ||
305 | 240 | new_text_area.value = current_text_input.value; | ||
306 | 241 | swapDOM(current_text_input, new_text_area); | ||
307 | 242 | return new_text_area; | ||
308 | 243 | } | ||
309 | 244 | |||
310 | 245 | function upgradeToTextAreaForTranslation(text_input_id) { | ||
311 | 246 | var rows = 6; | ||
312 | 247 | var current_text_input = $(text_input_id); | ||
313 | 248 | var text_area = convertTextInputToTextArea(text_input_id, rows); | ||
314 | 249 | text_area.focus(); | ||
315 | 250 | } | ||
316 | 251 | |||
317 | 252 | function insertExpansionButton(expandable_field) { | ||
318 | 253 | var button = createDOM( | ||
319 | 254 | 'button', { | ||
320 | 255 | 'style': 'padding: 0;', | ||
321 | 256 | 'title': 'Makes the field larger, so you can see more text.' | ||
322 | 257 | } | ||
323 | 258 | ); | ||
324 | 259 | var icon = createDOM( | ||
325 | 260 | 'img', { | ||
326 | 261 | 'alt': 'Enlarge Field', | ||
327 | 262 | 'src': '/+icing/translations-add-more-lines.gif' | ||
328 | 263 | } | ||
329 | 264 | ); | ||
330 | 265 | appendChildNodes(button, icon); | ||
331 | 266 | function buttonOnClick(e) { | ||
332 | 267 | upgradeToTextAreaForTranslation(expandable_field.id); | ||
333 | 268 | e.preventDefault(); | ||
334 | 269 | removeElement(button); | ||
335 | 270 | return false; | ||
336 | 271 | } | ||
337 | 272 | connect(button, 'onclick', buttonOnClick); | ||
338 | 273 | insertSiblingNodesAfter(expandable_field, button); | ||
339 | 274 | } | ||
340 | 275 | |||
341 | 276 | function insertAllExpansionButtons() { | ||
342 | 277 | var expandable_fields = getElementsByTagAndClassName( | ||
343 | 278 | 'input', 'expandable'); | ||
344 | 279 | forEach(expandable_fields, insertExpansionButton); | ||
345 | 280 | } | ||
346 | 281 | |||
347 | 282 | function copyInnerHTMLById(from_id, to_id) { | ||
348 | 283 | var from = getElement(from_id); | ||
349 | 284 | var to = getElement(to_id); | ||
350 | 285 | |||
351 | 286 | // The replacement regex strips all tags from the html. | ||
352 | 287 | to.value = unescapeHTML(from.innerHTML.replace(/<\/?[^>]+>/gi, "")); | ||
353 | 288 | |||
354 | 289 | } | ||
355 | 290 | |||
356 | 291 | function writeTextIntoPluralTranslationFields(from_id, | ||
357 | 292 | to_id_pattern, nplurals) { | ||
358 | 293 | // skip when x is 0, as that is the singular | ||
359 | 294 | for (var x = 1; x < nplurals; x++) { | ||
360 | 295 | var to_id = to_id_pattern + x + "_new"; | ||
361 | 296 | var to_select = to_id_pattern + x + "_new_select"; | ||
362 | 297 | copyInnerHTMLById(from_id, to_id); | ||
363 | 298 | document.getElementById(to_select).checked = true; | ||
364 | 299 | } | ||
365 | 300 | } | ||
366 | 301 | |||
367 | 302 | function activateConstrainBugExpiration() { | ||
368 | 303 | // Constrain enable_bug_expiration to the Launchpad Bugs radio input. | ||
369 | 304 | // The Launchpad bug tracker is either the first item in a product's | ||
370 | 305 | // bugtracker field, or it is a distribution's official_malone field. | ||
371 | 306 | var bug_tracker_input = getElement('field.bugtracker.0'); | ||
372 | 307 | if (! bug_tracker_input) { | ||
373 | 308 | bug_tracker_input = getElement('field.official_malone'); | ||
374 | 309 | } | ||
375 | 310 | var bug_expiration_input = getElement('field.enable_bug_expiration'); | ||
376 | 311 | if (! bug_tracker_input || ! bug_expiration_input) { | ||
377 | 312 | return; | ||
378 | 313 | } | ||
379 | 314 | // Disable enable_bug_expiration onload if Launchpad is not the | ||
380 | 315 | // bug tracker. | ||
381 | 316 | if (! bug_tracker_input.checked) { | ||
382 | 317 | bug_expiration_input.disabled = true; | ||
383 | 318 | } | ||
384 | 319 | constraint = function (e) { | ||
385 | 320 | if (bug_tracker_input.checked) { | ||
386 | 321 | bug_expiration_input.disabled = false; | ||
387 | 322 | bug_expiration_input.checked = true; | ||
388 | 323 | } else { | ||
389 | 324 | bug_expiration_input.checked = false; | ||
390 | 325 | bug_expiration_input.disabled = true; | ||
391 | 326 | } | ||
392 | 327 | }; | ||
393 | 328 | var inputs = document.getElementsByTagName('input'); | ||
394 | 329 | for (var i = 0; i < inputs.length; i++) { | ||
395 | 330 | if (inputs[i].name == 'field.bugtracker' || | ||
396 | 331 | inputs[i].name == 'field.official_malone') { | ||
397 | 332 | inputs[i].onclick = constraint; | ||
398 | 333 | } | ||
399 | 334 | } | ||
400 | 335 | } | ||
401 | 336 | |||
402 | 337 | function collapseRemoteCommentReply(comment_index) { | ||
403 | 338 | var prefix = 'remote_comment_reply_'; | ||
404 | 339 | $(prefix + 'tree_icon_' + comment_index).src = '/@@/treeCollapsed'; | ||
405 | 340 | $(prefix + 'div_' + comment_index).style.display = 'none'; | ||
406 | 341 | } | ||
407 | 342 | |||
408 | 343 | function expandRemoteCommentReply(comment_index) { | ||
409 | 344 | var prefix = 'remote_comment_reply_'; | ||
410 | 345 | $(prefix + 'tree_icon_' + comment_index).src = '/@@/treeExpanded'; | ||
411 | 346 | $(prefix + 'div_' + comment_index).style.display = 'block'; | ||
412 | 347 | } | ||
413 | 348 | |||
414 | 349 | function toggleRemoteCommentReply(comment_index) { | ||
415 | 350 | var imgname = $('remote_comment_reply_tree_icon_' + comment_index) | ||
416 | 351 | .src.split('/') | ||
417 | 352 | .pop(); | ||
418 | 353 | var expanded = (imgname == 'treeExpanded'); | ||
419 | 354 | if (expanded) { | ||
420 | 355 | collapseRemoteCommentReply(comment_index); | ||
421 | 356 | } else { | ||
422 | 357 | expandRemoteCommentReply(comment_index); | ||
423 | 358 | } | ||
424 | 359 | } | ||
425 | 360 | |||
426 | 361 | function connectRemoteCommentReply(comment_index) { | ||
427 | 362 | YUI().use('event', function(Y) { | ||
428 | 363 | var toggleFunc = function() { | ||
429 | 364 | toggleRemoteCommentReply(comment_index); | ||
430 | 365 | return false; | ||
431 | 366 | }; | ||
432 | 367 | |||
433 | 368 | var prefix = 'remote_comment_reply_expand_link_'; | ||
434 | 369 | |||
435 | 370 | Y.on('load', function(e) { | ||
436 | 371 | $(prefix + comment_index).onclick = toggleFunc; | ||
437 | 372 | }, window); | ||
438 | 373 | }); | ||
439 | 374 | } | ||
440 | 375 | |||
441 | 376 | function unescapeHTML(unescaped_string) { | ||
442 | 377 | // Based on prototype's unescapeHTML method. | ||
443 | 378 | // See launchpad bug #78788 for details. | ||
444 | 379 | var div = document.createElement('div'); | ||
445 | 380 | div.innerHTML = unescaped_string; | ||
446 | 381 | return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; | ||
447 | 382 | } | ||
448 | 383 | |||
449 | 0 | 384 | ||
450 | === modified file 'lib/canonical/launchpad/javascript/lp/lp.js' | |||
451 | --- lib/canonical/launchpad/javascript/lp/lp.js 2009-12-07 12:26:37 +0000 | |||
452 | +++ lib/canonical/launchpad/javascript/lp/lp.js 2010-01-21 23:36:33 +0000 | |||
453 | @@ -245,221 +245,6 @@ | |||
454 | 245 | }); | 245 | }); |
455 | 246 | } | 246 | } |
456 | 247 | 247 | ||
457 | 248 | function getContentArea() { | ||
458 | 249 | // to end all doubt on where the content sits. It also felt a bit | ||
459 | 250 | // silly doing this over and over in every function, even if it is | ||
460 | 251 | // a tiny operation. Just guarding against someone changing the | ||
461 | 252 | // names again, in the name of semantics or something.... ;) | ||
462 | 253 | var node = document.getElementById('maincontent'); | ||
463 | 254 | if (!node) {node = $('content');} | ||
464 | 255 | if (!node) {node = $('mainarea');} | ||
465 | 256 | return node; | ||
466 | 257 | } | ||
467 | 258 | |||
468 | 259 | function toggleCollapsible(e) { | ||
469 | 260 | // this is the function that collapses/expands fieldsets. | ||
470 | 261 | |||
471 | 262 | // "this" is the node that the event is attached to | ||
472 | 263 | var node = this; | ||
473 | 264 | |||
474 | 265 | // walk up the node hierarchy until we find the <legend> element | ||
475 | 266 | while (node.nodeName.toLowerCase() != 'legend') { | ||
476 | 267 | node = node.parentNode; | ||
477 | 268 | if (!node) { | ||
478 | 269 | return false; | ||
479 | 270 | } | ||
480 | 271 | } | ||
481 | 272 | |||
482 | 273 | // the expander image is legend -> a -> img | ||
483 | 274 | var icon = node.firstChild.firstChild; | ||
484 | 275 | var legend = node; | ||
485 | 276 | |||
486 | 277 | if (icon.getAttribute('src').indexOf('/@@/treeCollapsed') != -1) { | ||
487 | 278 | // that was an ugly check, but IE rewrites image sources to | ||
488 | 279 | // absolute urls from some sick reason.... | ||
489 | 280 | icon.setAttribute('src','/@@/treeExpanded'); | ||
490 | 281 | swapElementClass( | ||
491 | 282 | legend.parentNode.lastChild, 'collapsed', 'expanded'); | ||
492 | 283 | swapElementClass( | ||
493 | 284 | legend.parentNode.childNodes[1], 'expanded', 'collapsed'); | ||
494 | 285 | } else { | ||
495 | 286 | icon.setAttribute('src','/@@/treeCollapsed'); | ||
496 | 287 | swapElementClass( | ||
497 | 288 | legend.parentNode.lastChild, 'expanded', 'collapsed'); | ||
498 | 289 | swapElementClass( | ||
499 | 290 | legend.parentNode.childNodes[1], 'collapsed', 'expanded'); | ||
500 | 291 | } | ||
501 | 292 | return false; | ||
502 | 293 | } | ||
503 | 294 | |||
504 | 295 | function activateCollapsibles() { | ||
505 | 296 | // a script that searches for sections that can be (or are | ||
506 | 297 | // already) collapsed - and enables the collapse-behavior | ||
507 | 298 | |||
508 | 299 | // usage : give the class "collapsible" to a fieldset also, give | ||
509 | 300 | // it a <legend> with some descriptive text. you can also add the | ||
510 | 301 | // class "collapsed" amounting to a total of | ||
511 | 302 | // <fieldset class="collapsible collapsed"> to make the section | ||
512 | 303 | // pre-collapsed | ||
513 | 304 | |||
514 | 305 | // terminate if we hit a non-compliant DOM implementation | ||
515 | 306 | if (!document.getElementsByTagName) { | ||
516 | 307 | return false; | ||
517 | 308 | } | ||
518 | 309 | if (!document.getElementById) { | ||
519 | 310 | return false; | ||
520 | 311 | } | ||
521 | 312 | |||
522 | 313 | // only search in the content-area | ||
523 | 314 | var contentarea = getContentArea(); | ||
524 | 315 | if (!contentarea) { | ||
525 | 316 | return false; | ||
526 | 317 | } | ||
527 | 318 | |||
528 | 319 | // gather all objects that are to be collapsed | ||
529 | 320 | // we only do fieldsets for now. perhaps DIVs later... | ||
530 | 321 | var collapsibles = contentarea.getElementsByTagName('fieldset'); | ||
531 | 322 | |||
532 | 323 | for (var i = 0; i < collapsibles.length; i++) { | ||
533 | 324 | if (collapsibles[i].className.indexOf('collapsible') == -1) { | ||
534 | 325 | continue; | ||
535 | 326 | } | ||
536 | 327 | |||
537 | 328 | var legends = collapsibles[i].getElementsByTagName('LEGEND'); | ||
538 | 329 | |||
539 | 330 | // get the legend | ||
540 | 331 | // if there is no legend, we do not touch the fieldset at all. | ||
541 | 332 | // we assume that if there is a legend, there is only | ||
542 | 333 | // one. nothing else makes any sense | ||
543 | 334 | if (!legends.length) { | ||
544 | 335 | continue; | ||
545 | 336 | } | ||
546 | 337 | var legend = legends[0]; | ||
547 | 338 | |||
548 | 339 | //create an anchor to handle click-events | ||
549 | 340 | var anchor = document.createElement('a'); | ||
550 | 341 | anchor.href = '#'; | ||
551 | 342 | anchor.onclick = toggleCollapsible; | ||
552 | 343 | |||
553 | 344 | // add the icon/button with its functionality to the legend | ||
554 | 345 | var icon = document.createElement('img'); | ||
555 | 346 | icon.setAttribute('src','/@@/treeExpanded'); | ||
556 | 347 | icon.setAttribute('class','collapseIcon'); | ||
557 | 348 | icon.setAttribute('height','14'); | ||
558 | 349 | icon.setAttribute('width','14'); | ||
559 | 350 | |||
560 | 351 | // insert the icon icon at the start of the anchor | ||
561 | 352 | anchor.appendChild(icon); | ||
562 | 353 | |||
563 | 354 | // reparent all the legend's children into a span, and the span | ||
564 | 355 | // into an anchor. The span is used to underline the legend | ||
565 | 356 | // text; because the img is inside the anchor, we can't | ||
566 | 357 | // underline the whole anchor. | ||
567 | 358 | var span = document.createElement('span'); | ||
568 | 359 | while (legend.hasChildNodes()) { | ||
569 | 360 | var child = legend.firstChild; | ||
570 | 361 | legend.removeChild(child); | ||
571 | 362 | span.appendChild(child); | ||
572 | 363 | } | ||
573 | 364 | anchor.appendChild(span); | ||
574 | 365 | |||
575 | 366 | // add the anchor to the legend | ||
576 | 367 | legend.appendChild(anchor); | ||
577 | 368 | |||
578 | 369 | // wrap the contents inside a div to make turning them on and | ||
579 | 370 | // off simpler. unless something very strange happens, this | ||
580 | 371 | // new div should always be the last childnode we'll give it a | ||
581 | 372 | // class to make sure. | ||
582 | 373 | |||
583 | 374 | var hiderWrapper = document.createElement('div'); | ||
584 | 375 | hiderWrapper.setAttribute('class','collapseWrapper'); | ||
585 | 376 | |||
586 | 377 | // also add a new div describing that the element is collapsed. | ||
587 | 378 | var collapsedDescription = document.createElement('div'); | ||
588 | 379 | collapsedDescription.setAttribute('class','collapsedText'); | ||
589 | 380 | collapsedDescription.style.display = 'none'; | ||
590 | 381 | |||
591 | 382 | // if the fieldset has the class of "collapsed", pre-collapse | ||
592 | 383 | // it. This can be used to preserve valuable UI-space | ||
593 | 384 | if (collapsibles[i].className.indexOf('collapsed') != -1 ) { | ||
594 | 385 | icon.setAttribute('src','/@@/treeCollapsed'); | ||
595 | 386 | collapsedDescription.style.display = 'block'; | ||
596 | 387 | setElementClass(hiderWrapper, 'collapsed'); | ||
597 | 388 | // Unhide the fieldset, now that all of its children are hidden: | ||
598 | 389 | removeElementClass(collapsibles[i], 'collapsed'); | ||
599 | 390 | } | ||
600 | 391 | |||
601 | 392 | // now we have the wrapper div.. Stuff all the contents inside it | ||
602 | 393 | var nl = collapsibles[i].childNodes.length; | ||
603 | 394 | for (var j = 0; j < nl; j++){ | ||
604 | 395 | var node = collapsibles[i].childNodes[0]; | ||
605 | 396 | if (node.nodeName == 'LEGEND') { | ||
606 | 397 | if (collapsibles[i].childNodes.length > 1) { | ||
607 | 398 | hiderWrapper.appendChild(collapsibles[i].childNodes[1]); | ||
608 | 399 | } | ||
609 | 400 | } else { | ||
610 | 401 | hiderWrapper.appendChild(collapsibles[i].childNodes[0]); | ||
611 | 402 | } | ||
612 | 403 | } | ||
613 | 404 | // and add it to the document | ||
614 | 405 | collapsibles[i].appendChild(hiderWrapper); | ||
615 | 406 | collapsibles[i].insertBefore(collapsedDescription, hiderWrapper); | ||
616 | 407 | } | ||
617 | 408 | } | ||
618 | 409 | |||
619 | 410 | function toggleFoldable(e) { | ||
620 | 411 | var ELEMENT_NODE = 1; | ||
621 | 412 | var node = this; | ||
622 | 413 | while (node.nextSibling) { | ||
623 | 414 | node = node.nextSibling; | ||
624 | 415 | if (node.nodeType != ELEMENT_NODE) { | ||
625 | 416 | continue; | ||
626 | 417 | } | ||
627 | 418 | if (node.className.indexOf('foldable') == -1) { | ||
628 | 419 | continue; | ||
629 | 420 | } | ||
630 | 421 | if (node.style.display == 'none') { | ||
631 | 422 | node.style.display = 'inline'; | ||
632 | 423 | } else { | ||
633 | 424 | node.style.display = 'none'; | ||
634 | 425 | } | ||
635 | 426 | } | ||
636 | 427 | } | ||
637 | 428 | |||
638 | 429 | function activateFoldables() { | ||
639 | 430 | // Create links to toggle the display of foldable content. | ||
640 | 431 | var included = getElementsByTagAndClassName( | ||
641 | 432 | 'span', 'foldable', document); | ||
642 | 433 | var quoted = getElementsByTagAndClassName( | ||
643 | 434 | 'span', 'foldable-quoted', document); | ||
644 | 435 | var elements = concat(included, quoted); | ||
645 | 436 | for (var i = 0; i < elements.length; i++) { | ||
646 | 437 | var span = elements[i]; | ||
647 | 438 | if (span.className == 'foldable-quoted') { | ||
648 | 439 | var quoted_lines = span.getElementsByTagName('br'); | ||
649 | 440 | if (quoted_lines && quoted_lines.length <= 11) { | ||
650 | 441 | // We do not hide short quoted passages (12 lines) by default. | ||
651 | 442 | continue; | ||
652 | 443 | } | ||
653 | 444 | } | ||
654 | 445 | |||
655 | 446 | var ellipsis = document.createElement('a'); | ||
656 | 447 | ellipsis.style.textDecoration = 'underline'; | ||
657 | 448 | ellipsis.href = VOID_URL; | ||
658 | 449 | ellipsis.onclick = toggleFoldable; | ||
659 | 450 | ellipsis.appendChild(document.createTextNode('[...]')); | ||
660 | 451 | |||
661 | 452 | span.parentNode.insertBefore(ellipsis, span); | ||
662 | 453 | span.insertBefore(document.createElement('br'), span.firstChild); | ||
663 | 454 | span.style.display = 'none'; | ||
664 | 455 | if (span.nextSibling) { | ||
665 | 456 | // Text lines follows this span. | ||
666 | 457 | var br = document.createElement('br'); | ||
667 | 458 | span.parentNode.insertBefore(br, span.nextSibling); | ||
668 | 459 | } | ||
669 | 460 | } | ||
670 | 461 | } | ||
671 | 462 | |||
672 | 463 | function toggleExpandableTableRow(element_id) { | 248 | function toggleExpandableTableRow(element_id) { |
673 | 464 | var row = document.getElementById(element_id); | 249 | var row = document.getElementById(element_id); |
674 | 465 | var view_icon = document.getElementById(element_id + "-arrow"); | 250 | var view_icon = document.getElementById(element_id + "-arrow"); |
675 | @@ -557,98 +342,6 @@ | |||
676 | 557 | document.getElementById(widget_name).checked = true; | 342 | document.getElementById(widget_name).checked = true; |
677 | 558 | } | 343 | } |
678 | 559 | 344 | ||
679 | 560 | |||
680 | 561 | function convertTextInputToTextArea(text_input_id, rows) { | ||
681 | 562 | var current_text_input = getElement(text_input_id); | ||
682 | 563 | var new_text_area = document.createElement("textarea"); | ||
683 | 564 | var attributes = { | ||
684 | 565 | 'id': text_input_id, | ||
685 | 566 | 'rows': rows, | ||
686 | 567 | 'name': getNodeAttribute(current_text_input, 'name'), | ||
687 | 568 | 'lang': getNodeAttribute(current_text_input, 'lang'), | ||
688 | 569 | 'dir': getNodeAttribute(current_text_input, 'dir') | ||
689 | 570 | }; | ||
690 | 571 | |||
691 | 572 | updateNodeAttributes(new_text_area, attributes); | ||
692 | 573 | |||
693 | 574 | // we set the javascript events because updateNodeAttributes gets confused | ||
694 | 575 | // with those events, because it says that 'event' is not defined. 'event' | ||
695 | 576 | // is one of the arguments of the javascript call that is being copied. | ||
696 | 577 | new_text_area.setAttribute( | ||
697 | 578 | 'onKeyPress', getNodeAttribute(current_text_input, 'onkeypress')); | ||
698 | 579 | new_text_area.setAttribute( | ||
699 | 580 | 'onChange', getNodeAttribute(current_text_input, 'onchange')); | ||
700 | 581 | new_text_area.value = current_text_input.value; | ||
701 | 582 | swapDOM(current_text_input, new_text_area); | ||
702 | 583 | return new_text_area; | ||
703 | 584 | } | ||
704 | 585 | |||
705 | 586 | |||
706 | 587 | function upgradeToTextAreaForTranslation(text_input_id) { | ||
707 | 588 | var rows = 6; | ||
708 | 589 | var current_text_input = $(text_input_id); | ||
709 | 590 | var text_area = convertTextInputToTextArea(text_input_id, rows); | ||
710 | 591 | text_area.focus(); | ||
711 | 592 | } | ||
712 | 593 | |||
713 | 594 | function insertExpansionButton(expandable_field) { | ||
714 | 595 | var button = createDOM( | ||
715 | 596 | 'button', { | ||
716 | 597 | 'style': 'padding: 0;', | ||
717 | 598 | 'title': 'Makes the field larger, so you can see more text.' | ||
718 | 599 | } | ||
719 | 600 | ); | ||
720 | 601 | var icon = createDOM( | ||
721 | 602 | 'img', { | ||
722 | 603 | 'alt': 'Enlarge Field', | ||
723 | 604 | 'src': '/+icing/translations-add-more-lines.gif' | ||
724 | 605 | } | ||
725 | 606 | ); | ||
726 | 607 | appendChildNodes(button, icon); | ||
727 | 608 | function buttonOnClick(e) { | ||
728 | 609 | upgradeToTextAreaForTranslation(expandable_field.id); | ||
729 | 610 | e.preventDefault(); | ||
730 | 611 | removeElement(button); | ||
731 | 612 | return false; | ||
732 | 613 | } | ||
733 | 614 | connect(button, 'onclick', buttonOnClick); | ||
734 | 615 | insertSiblingNodesAfter(expandable_field, button); | ||
735 | 616 | } | ||
736 | 617 | |||
737 | 618 | function insertAllExpansionButtons() { | ||
738 | 619 | var expandable_fields = getElementsByTagAndClassName( | ||
739 | 620 | 'input', 'expandable'); | ||
740 | 621 | forEach(expandable_fields, insertExpansionButton); | ||
741 | 622 | } | ||
742 | 623 | |||
743 | 624 | function unescapeHTML(unescaped_string) { | ||
744 | 625 | // Based on prototype's unescapeHTML method. | ||
745 | 626 | // See launchpad bug #78788 for details. | ||
746 | 627 | var div = document.createElement('div'); | ||
747 | 628 | div.innerHTML = unescaped_string; | ||
748 | 629 | return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; | ||
749 | 630 | } | ||
750 | 631 | |||
751 | 632 | function copyInnerHTMLById(from_id, to_id) { | ||
752 | 633 | var from = getElement(from_id); | ||
753 | 634 | var to = getElement(to_id); | ||
754 | 635 | |||
755 | 636 | // The replacement regex strips all tags from the html. | ||
756 | 637 | to.value = unescapeHTML(from.innerHTML.replace(/<\/?[^>]+>/gi, "")); | ||
757 | 638 | |||
758 | 639 | } | ||
759 | 640 | |||
760 | 641 | function writeTextIntoPluralTranslationFields(from_id, | ||
761 | 642 | to_id_pattern, nplurals) { | ||
762 | 643 | // skip when x is 0, as that is the singular | ||
763 | 644 | for (var x = 1; x < nplurals; x++) { | ||
764 | 645 | var to_id = to_id_pattern + x + "_new"; | ||
765 | 646 | var to_select = to_id_pattern + x + "_new_select"; | ||
766 | 647 | copyInnerHTMLById(from_id, to_id); | ||
767 | 648 | document.getElementById(to_select).checked = true; | ||
768 | 649 | } | ||
769 | 650 | } | ||
770 | 651 | |||
771 | 652 | function switchBugBranchFormAndWhiteboard(id) { | 345 | function switchBugBranchFormAndWhiteboard(id) { |
772 | 653 | var div = document.getElementById('bugbranch' + id); | 346 | var div = document.getElementById('bugbranch' + id); |
773 | 654 | var wb = document.getElementById('bugbranch' + id + '-wb'); | 347 | var wb = document.getElementById('bugbranch' + id + '-wb'); |
774 | @@ -703,92 +396,7 @@ | |||
775 | 703 | return false; | 396 | return false; |
776 | 704 | } | 397 | } |
777 | 705 | 398 | ||
778 | 706 | function activateConstrainBugExpiration() { | ||
779 | 707 | // Constrain enable_bug_expiration to the Launchpad Bugs radio input. | ||
780 | 708 | // The Launchpad bug tracker is either the first item in a product's | ||
781 | 709 | // bugtracker field, or it is a distribution's official_malone field. | ||
782 | 710 | var bug_tracker_input = getElement('field.bugtracker.0'); | ||
783 | 711 | if (! bug_tracker_input) { | ||
784 | 712 | bug_tracker_input = getElement('field.official_malone'); | ||
785 | 713 | } | ||
786 | 714 | var bug_expiration_input = getElement('field.enable_bug_expiration'); | ||
787 | 715 | if (! bug_tracker_input || ! bug_expiration_input) { | ||
788 | 716 | return; | ||
789 | 717 | } | ||
790 | 718 | // Disable enable_bug_expiration onload if Launchpad is not the | ||
791 | 719 | // bug tracker. | ||
792 | 720 | if (! bug_tracker_input.checked) { | ||
793 | 721 | bug_expiration_input.disabled = true; | ||
794 | 722 | } | ||
795 | 723 | constraint = function (e) { | ||
796 | 724 | if (bug_tracker_input.checked) { | ||
797 | 725 | bug_expiration_input.disabled = false; | ||
798 | 726 | bug_expiration_input.checked = true; | ||
799 | 727 | } else { | ||
800 | 728 | bug_expiration_input.checked = false; | ||
801 | 729 | bug_expiration_input.disabled = true; | ||
802 | 730 | } | ||
803 | 731 | }; | ||
804 | 732 | var inputs = document.getElementsByTagName('input'); | ||
805 | 733 | for (var i = 0; i < inputs.length; i++) { | ||
806 | 734 | if (inputs[i].name == 'field.bugtracker' || | ||
807 | 735 | inputs[i].name == 'field.official_malone') { | ||
808 | 736 | inputs[i].onclick = constraint; | ||
809 | 737 | } | ||
810 | 738 | } | ||
811 | 739 | } | ||
812 | 740 | |||
813 | 741 | function updateField(field, enabled) | 399 | function updateField(field, enabled) |
814 | 742 | { | 400 | { |
815 | 743 | field.disabled = !enabled; | 401 | field.disabled = !enabled; |
816 | 744 | } | 402 | } |
817 | 745 | |||
818 | 746 | |||
819 | 747 | function collapseRemoteCommentReply(comment_index) { | ||
820 | 748 | var prefix = 'remote_comment_reply_'; | ||
821 | 749 | $(prefix + 'tree_icon_' + comment_index).src = '/@@/treeCollapsed'; | ||
822 | 750 | $(prefix + 'div_' + comment_index).style.display = 'none'; | ||
823 | 751 | } | ||
824 | 752 | |||
825 | 753 | function expandRemoteCommentReply(comment_index) { | ||
826 | 754 | var prefix = 'remote_comment_reply_'; | ||
827 | 755 | $(prefix + 'tree_icon_' + comment_index).src = '/@@/treeExpanded'; | ||
828 | 756 | $(prefix + 'div_' + comment_index).style.display = 'block'; | ||
829 | 757 | } | ||
830 | 758 | |||
831 | 759 | function toggleRemoteCommentReply(comment_index) { | ||
832 | 760 | var imgname = $('remote_comment_reply_tree_icon_' + comment_index) | ||
833 | 761 | .src.split('/') | ||
834 | 762 | .pop(); | ||
835 | 763 | var expanded = (imgname == 'treeExpanded'); | ||
836 | 764 | if (expanded) { | ||
837 | 765 | collapseRemoteCommentReply(comment_index); | ||
838 | 766 | } else { | ||
839 | 767 | expandRemoteCommentReply(comment_index); | ||
840 | 768 | } | ||
841 | 769 | } | ||
842 | 770 | |||
843 | 771 | function connectRemoteCommentReply(comment_index) { | ||
844 | 772 | YUI().use('event', function(Y) { | ||
845 | 773 | var toggleFunc = function() { | ||
846 | 774 | toggleRemoteCommentReply(comment_index); | ||
847 | 775 | return false; | ||
848 | 776 | }; | ||
849 | 777 | |||
850 | 778 | var prefix = 'remote_comment_reply_expand_link_'; | ||
851 | 779 | |||
852 | 780 | Y.on('load', function(e) { | ||
853 | 781 | $(prefix + comment_index).onclick = toggleFunc; | ||
854 | 782 | }, window); | ||
855 | 783 | }); | ||
856 | 784 | } | ||
857 | 785 | |||
858 | 786 | function switchDisplay(tag_id1, tag_id2) { | ||
859 | 787 | var tag1 = getElement(tag_id1); | ||
860 | 788 | var tag2 = getElement(tag_id2); | ||
861 | 789 | var display = tag1.style.display; | ||
862 | 790 | tag1.style.display = tag2.style.display; | ||
863 | 791 | tag2.style.display = display; | ||
864 | 792 | return false; | ||
865 | 793 | } | ||
866 | 794 | |||
867 | 795 | 403 | ||
868 | === modified file 'lib/lp/app/templates/base-layout-macros.pt' | |||
869 | --- lib/lp/app/templates/base-layout-macros.pt 2010-01-08 21:39:00 +0000 | |||
870 | +++ lib/lp/app/templates/base-layout-macros.pt 2010-01-21 23:36:33 +0000 | |||
871 | @@ -52,6 +52,16 @@ | |||
872 | 52 | do any initialization. | 52 | do any initialization. |
873 | 53 | </tal:comment> | 53 | </tal:comment> |
874 | 54 | 54 | ||
875 | 55 | <tal:comment> | ||
876 | 56 | XXX mars 2010-01-21 | ||
877 | 57 | We have to load MochiKit outside of the main javascript rollup so that the | ||
878 | 58 | rollup's size does not exceed 512Kb. That magic number triggers a bug | ||
879 | 59 | somewhere in the automated test system that will prevent Windmill from | ||
880 | 60 | executing. | ||
881 | 61 | </tal:comment> | ||
882 | 62 | <script type="text/javascript" | ||
883 | 63 | tal:attributes="src string:${icingroot}/MochiKit.js"></script> | ||
884 | 64 | |||
885 | 55 | <tal:devmode condition="devmode"> | 65 | <tal:devmode condition="devmode"> |
886 | 56 | 66 | ||
887 | 57 | <tal:comment replace="nothing"> | 67 | <tal:comment replace="nothing"> |
888 | @@ -80,7 +90,7 @@ | |||
889 | 80 | <script type="text/javascript" | 90 | <script type="text/javascript" |
890 | 81 | tal:attributes="src string:${yui}/event/event.js"></script> | 91 | tal:attributes="src string:${yui}/event/event.js"></script> |
891 | 82 | <script type="text/javascript" | 92 | <script type="text/javascript" |
893 | 83 | tal:attributes="src string:${yui}/event/event-key.js"></script> | 93 | tal:attributes="src string:${yui}/event/event-key.js"></script> |
894 | 84 | <script type="text/javascript" | 94 | <script type="text/javascript" |
895 | 85 | tal:attributes="src string:${yui}/event-custom/event-custom.js"></script> | 95 | tal:attributes="src string:${yui}/event-custom/event-custom.js"></script> |
896 | 86 | <script type="text/javascript" | 96 | <script type="text/javascript" |
897 | @@ -133,8 +143,6 @@ | |||
898 | 133 | tal:attributes="src string:${yui}/node-menunav/node-menunav.js"></script> | 143 | tal:attributes="src string:${yui}/node-menunav/node-menunav.js"></script> |
899 | 134 | 144 | ||
900 | 135 | <script type="text/javascript" | 145 | <script type="text/javascript" |
901 | 136 | tal:attributes="src string:${icingroot}/MochiKit.js"></script> | ||
902 | 137 | <script type="text/javascript" | ||
903 | 138 | tal:attributes="src string:${lazr_js}/lazr/lazr.js"></script> | 146 | tal:attributes="src string:${lazr_js}/lazr/lazr.js"></script> |
904 | 139 | <script type="text/javascript" | 147 | <script type="text/javascript" |
905 | 140 | tal:attributes="src string:${lazr_js}/anim/anim.js"></script> | 148 | tal:attributes="src string:${lazr_js}/anim/anim.js"></script> |
906 | 141 | 149 | ||
907 | === modified file 'lib/lp/code/windmill/tests/test_branchmergeproposal_review.py' | |||
908 | --- lib/lp/code/windmill/tests/test_branchmergeproposal_review.py 2010-01-14 07:46:09 +0000 | |||
909 | +++ lib/lp/code/windmill/tests/test_branchmergeproposal_review.py 2010-01-21 23:36:33 +0000 | |||
910 | @@ -106,6 +106,21 @@ | |||
911 | 106 | xpath='//div[@id="conversation"]//div[@class="boardCommentBody"]' | 106 | xpath='//div[@id="conversation"]//div[@class="boardCommentBody"]' |
912 | 107 | '/pre[contains(., "%s")]' % new_comment_text) | 107 | '/pre[contains(., "%s")]' % new_comment_text) |
913 | 108 | 108 | ||
914 | 109 | def test_merge_proposal_reviewing(self): | ||
915 | 110 | """Comment on a merge proposal.""" | ||
916 | 111 | client = WindmillTestClient('Code review commenting') | ||
917 | 112 | lpuser.NO_PRIV.ensure_login(client) | ||
918 | 113 | |||
919 | 114 | proposal = self.factory.makeBranchMergeProposal() | ||
920 | 115 | self.open_proposal_page(client, proposal) | ||
921 | 116 | client.waits.forElement(xpath=ADD_COMMENT_BUTTON) | ||
922 | 117 | |||
923 | 118 | new_comment_text = generate_uuid() | ||
924 | 119 | client.type(text=new_comment_text, id="field.comment") | ||
925 | 120 | client.select(id=u'field.vote', val=u'APPROVE') | ||
926 | 121 | client.click(xpath=ADD_COMMENT_BUTTON) | ||
927 | 122 | client.waits.forElement(id=u'review-no-priv', timeout=u'40000') | ||
928 | 123 | |||
929 | 109 | 124 | ||
930 | 110 | def test_suite(): | 125 | def test_suite(): |
931 | 111 | return unittest.TestLoader().loadTestsFromName(__name__) | 126 | return unittest.TestLoader().loadTestsFromName(__name__) |
Hi Tim-
This branch updates the branch reviewer's table when someone uses the inline
form to review. If the comment has a review, it also gives it a green flash
for the new review.
Cheers,
Paul