Merge lp:~matthew.revell/launchpad/tags-help-484259 into lp:launchpad/db-devel

Proposed by Matthew Revell
Status: Rejected
Rejected by: Matthew Revell
Proposed branch: lp:~matthew.revell/launchpad/tags-help-484259
Merge into: lp:launchpad/db-devel
Diff against target: 1852 lines (+1526/-146)
14 files modified
Makefile (+11/-2)
buildout-templates/bin/combine-css.in (+2/-2)
buildout-templates/bin/sprite-util.in (+43/-0)
lib/canonical/launchpad/browser/vocabulary.py (+1/-1)
lib/canonical/launchpad/icing/icon-sprites.positioning (+472/-0)
lib/canonical/launchpad/icing/style-3-0.css.in (+491/-132)
lib/lp/app/browser/tests/base-layout.txt (+7/-8)
lib/lp/app/templates/base-layout-macros.pt (+1/-1)
lib/lp/bugs/help/tag-help.html (+31/-0)
lib/lp/bugs/templates/bugtask-index.pt (+2/-0)
lib/lp/services/doc/sprites.txt (+185/-0)
lib/lp/services/spriteutils.py (+243/-0)
lib/lp/services/tests/test_doc.py (+17/-0)
lib/lp/services/tests/testfiles/template.css (+20/-0)
To merge this branch: bzr merge lp:~matthew.revell/launchpad/tags-help-484259
Reviewer Review Type Date Requested Status
Matthew Revell (community) Needs Resubmitting
Abel Deuring (community) code Approve
Review via email: mp+19114@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Matthew Revell (matthew.revell) wrote :

This adds pop-up help explaining the basics of bug tags and, in particular, that official project bug tags will autocomplete.

The help pop-up is accessed through a help icon next to the tags.

You can view this on any bugs page. Here are two examples:

Without tags: https://bugs.launchpad.dev/redfish/+bug/15
With tags: https://bugs.launchpad.dev/ubuntu/+source/linux-source-2.6.15/+bug/10

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Matthew,

nice work! I spotted one typo, see below.

Aside from this, I wondered if it would make sense to ask people on
the help page to use for official tags if they exist and if they
seem half way appropriate instead of adding new tags.

IIRC, we added official tags in order to avoid an "inflation" of
similar tags.

Abel

> This adds pop-up help explaining the basics of bug tags and, in particular, that official project bug tags will autocomplete.
>
> The help pop-up is accessed through a help icon next to the tags.
>
> You can view this on any bugs page. Here are two examples:
>
> Without tags: https://bugs.launchpad.dev/redfish/+bug/15
> With tags: https://bugs.launchpad.dev/ubuntu/+source/linux-source-2.6.15/+bug/10=== added file 'lib/lp/bugs/help/tag-help.html'
> --- lib/lp/bugs/help/tag-help.html 1970-01-01 00:00:00 +0000
> +++ lib/lp/bugs/help/tag-help.html 2010-02-12 11:35:50 +0000
> @@ -0,0 +1,31 @@
> +<html>
> + <head>
> + <title>Bug tag search</title>
> + <link rel="stylesheet" type="text/css"
> + href="/+icing/yui/cssreset/reset.css" />
> + <link rel="stylesheet" type="text/css"
> + href="/+icing/yui/cssfonts/fonts.css" />
> + <link rel="stylesheet" type="text/css"
> + href="/+icing/yui/cssbase/base.css" />
> + </head>
> + <body>
> + <h1>Using bug tags</h1>
> +
> + <p>
> + A bug tag can be anything that you think will help better categorise the
> + bug report.
> +
> + For example, if it's a user interface bug you might tag it with
> + <code>UI</code>.
> +
> + </p>
> +
> + <h2>Official bug tags</h2>
> +
> + Each project can define official bug tags. When you're adding a bug tag
> + to a bug report, Launchpad will offer to automcomplete matching official

s/automcomplete/autocomplete/

review: Approve (code)
Revision history for this message
Matthew Revell (matthew.revell) wrote :
review: Needs Resubmitting

Unmerged revisions

10305. By Matthew Revell

Adds help icon next to tags on bug page. Also adds pop-up help that explains that official bug tags autocomplete.

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-29 16:03:04 +0000
3+++ Makefile 2010-02-11 16:45:26 +0000
4@@ -18,7 +18,8 @@
5 LPCONFIG=development
6
7 JSFLAGS=
8-LP_BUILT_JS_ROOT=lib/canonical/launchpad/icing/build
9+ICING=lib/canonical/launchpad/icing
10+LP_BUILT_JS_ROOT=${ICING}/build
11 LAZR_BUILT_JS_ROOT=lazr-js/build
12
13 MINS_TO_SHUTDOWN=15
14@@ -111,9 +112,17 @@
15
16 build: $(BZR_VERSION_INFO) compile apidoc jsbuild css_combine
17
18-css_combine:
19+css_combine: sprite_css
20 ${SHHH} bin/combine-css
21
22+sprite_css: ${LP_BUILT_JS_ROOT}/style-3-0.css
23+
24+${LP_BUILT_JS_ROOT}/style-3-0.css: ${ICING}/style-3-0.css.in ${ICING}/icon-sprites.positioning
25+ ${SHHH} bin/sprite-util create-css
26+
27+sprite_image:
28+ ${SHHH} bin/sprite-util create-image
29+
30 jsbuild_lazr:
31 # We absolutely do not want to include the lazr.testing module and its
32 # jsTestDriver test harness modifications in the lazr.js and launchpad.js
33
34=== modified file 'buildout-templates/bin/combine-css.in'
35--- buildout-templates/bin/combine-css.in 2009-12-17 14:32:03 +0000
36+++ buildout-templates/bin/combine-css.in 2010-02-11 16:45:26 +0000
37@@ -1,4 +1,4 @@
38-#! /usr/bin/env ${buildout:directory}/bin/py
39+#!/usr/bin/env ${buildout:directory}/bin/py
40
41 import os
42
43@@ -26,7 +26,7 @@
44 'lazr/build/choiceedit/assets/choiceedit-core.css',
45 # This one goes at the end because it's our main stylesheet and should
46 # take precedence over the others.
47- 'style-3-0.css']
48+ 'build/style-3-0.css']
49 result = ''
50 for content in combine_files(names, icing):
51 result += content
52
53=== added file 'buildout-templates/bin/sprite-util.in'
54--- buildout-templates/bin/sprite-util.in 1970-01-01 00:00:00 +0000
55+++ buildout-templates/bin/sprite-util.in 2010-02-11 16:45:26 +0000
56@@ -0,0 +1,43 @@
57+#!/usr/bin/env ${buildout:directory}/bin/py
58+
59+import os
60+import sys
61+
62+from lp.services.spriteutils import SpriteUtil
63+
64+command_options = ('create-image', 'create-css')
65+
66+def usage():
67+ return " Usage: %s %s" % (sys.argv[0], '|'.join(command_options))
68+
69+if len(sys.argv) != 2:
70+ print >> sys.stderr, "Expected a single argument."
71+ print >> sys.stderr, usage()
72+ sys.exit(1)
73+else:
74+ command = sys.argv[1]
75+ if command not in command_options:
76+ print >> sys.stderr, "Unknown argument: %s" % command
77+ print >> sys.stderr, usage()
78+ sys.exit(2)
79+
80+root = '${buildout:directory}'
81+icing = os.path.join(root, 'lib/canonical/launchpad/icing')
82+combined_image_file = os.path.join(icing, 'icon-sprites')
83+positioning_file = os.path.join(icing, 'icon-sprites.positioning')
84+css_template_file = os.path.join(icing, 'style-3-0.css.in')
85+css_file = os.path.join(icing, 'build/style-3-0.css')
86+
87+sprite_util = SpriteUtil(
88+ css_template_file, 'icon-sprites',
89+ url_prefix_substitutions={'/@@/': '../images/'})
90+
91+if command == 'create-image':
92+ sprite_util.combineImages(icing)
93+ sprite_util.savePNG(combined_image_file)
94+ sprite_util.savePositioning(positioning_file)
95+elif command == 'create-css':
96+ sprite_util.loadPositioning(positioning_file)
97+ # The icing/icon-sprites file is relative to the css file
98+ # in the icing/build/ directory.
99+ sprite_util.saveConvertedCSS(css_file, '../icon-sprites')
100
101=== modified file 'lib/canonical/launchpad/browser/vocabulary.py'
102--- lib/canonical/launchpad/browser/vocabulary.py 2010-01-18 19:46:26 +0000
103+++ lib/canonical/launchpad/browser/vocabulary.py 2010-02-11 16:45:26 +0000
104@@ -43,7 +43,7 @@
105 # This limits the output to one line of text, since the sprite class
106 # cannot clip the background image effectively for vocabulary items
107 # with more than single line description below the title.
108-MAX_DESCRIPTION_LENGTH = 55
109+MAX_DESCRIPTION_LENGTH = 120
110
111
112 class IPickerEntry(Interface):
113
114=== modified file 'lib/canonical/launchpad/icing/icon-sprites'
115Binary files lib/canonical/launchpad/icing/icon-sprites 2009-06-02 18:15:08 +0000 and lib/canonical/launchpad/icing/icon-sprites 2010-02-11 16:45:26 +0000 differ
116=== removed file 'lib/canonical/launchpad/icing/icon-sprites-2'
117Binary files lib/canonical/launchpad/icing/icon-sprites-2 2009-07-30 02:26:46 +0000 and lib/canonical/launchpad/icing/icon-sprites-2 1970-01-01 00:00:00 +0000 differ
118=== removed file 'lib/canonical/launchpad/icing/icon-sprites-3'
119Binary files lib/canonical/launchpad/icing/icon-sprites-3 2009-12-10 10:22:04 +0000 and lib/canonical/launchpad/icing/icon-sprites-3 1970-01-01 00:00:00 +0000 differ
120=== removed file 'lib/canonical/launchpad/icing/icon-sprites-large'
121Binary files lib/canonical/launchpad/icing/icon-sprites-large 2009-06-02 18:15:08 +0000 and lib/canonical/launchpad/icing/icon-sprites-large 1970-01-01 00:00:00 +0000 differ
122=== removed file 'lib/canonical/launchpad/icing/icon-sprites-logo'
123Binary files lib/canonical/launchpad/icing/icon-sprites-logo 2009-06-02 18:15:08 +0000 and lib/canonical/launchpad/icing/icon-sprites-logo 1970-01-01 00:00:00 +0000 differ
124=== removed file 'lib/canonical/launchpad/icing/icon-sprites-logo-2'
125Binary files lib/canonical/launchpad/icing/icon-sprites-logo-2 2009-06-02 18:15:08 +0000 and lib/canonical/launchpad/icing/icon-sprites-logo-2 1970-01-01 00:00:00 +0000 differ
126=== added file 'lib/canonical/launchpad/icing/icon-sprites.positioning'
127--- lib/canonical/launchpad/icing/icon-sprites.positioning 1970-01-01 00:00:00 +0000
128+++ lib/canonical/launchpad/icing/icon-sprites.positioning 2010-02-11 16:45:26 +0000
129@@ -0,0 +1,472 @@
130+/* DO NOT EDIT THIS FILE BY HAND!!! */
131+/* It is autogenerated by spriteutils. */
132+{
133+ "../images/arrowLeft.png": [
134+ 0,
135+ -14590
136+ ],
137+ "../images/cancel.png": [
138+ 0,
139+ -7376
140+ ],
141+ "../images/milestone.png": [
142+ 0,
143+ -3276
144+ ],
145+ "../images/zoom-out.png": [
146+ 0,
147+ -11802
148+ ],
149+ "../images/team.png": [
150+ 0,
151+ -2130
152+ ],
153+ "../images/bug-undecided.png": [
154+ 0,
155+ -7872
156+ ],
157+ "../images/blueprint-low.png": [
158+ 0,
159+ -5736
160+ ],
161+ "../images/meeting.png": [
162+ 0,
163+ -10326
164+ ],
165+ "../images/no.png": [
166+ 0,
167+ -1312
168+ ],
169+ "../images/distribution-badge.png": [
170+ 0,
171+ -9020
172+ ],
173+ "../images/arrowTop.png": [
174+ 0,
175+ -14262
176+ ],
177+ "../images/zoom-in.png": [
178+ 0,
179+ -11638
180+ ],
181+ "../images/team-badge.png": [
182+ 0,
183+ -2294
184+ ],
185+ "../images/blue-bar.png": [
186+ 0,
187+ -14754
188+ ],
189+ "../images/arrowStart.png": [
190+ 0,
191+ -13934
192+ ],
193+ "../images/ppa-icon-inactive.png": [
194+ 0,
195+ -12294
196+ ],
197+ "../images/build-needed.png": [
198+ 0,
199+ -13114
200+ ],
201+ "../images/purple-bar.png": [
202+ 0,
203+ -15082
204+ ],
205+ "../images/bullet.png": [
206+ 0,
207+ -11474
208+ ],
209+ "../images/info-large.png": [
210+ 0,
211+ -17012
212+ ],
213+ "../images/trash-logo.png": [
214+ 0,
215+ -20030
216+ ],
217+ "../images/warning.png": [
218+ 0,
219+ -9998
220+ ],
221+ "../images/mail.png": [
222+ 0,
223+ -3768
224+ ],
225+ "../images/build-failure.png": [
226+ 0,
227+ -13278
228+ ],
229+ "../images/branch-large.png": [
230+ 0,
231+ -15738
232+ ],
233+ "../images/download-large.png": [
234+ 0,
235+ -16830
236+ ],
237+ "../images/private-large.png": [
238+ 0,
239+ -17922
240+ ],
241+ "../images/launchpad-large.png": [
242+ 0,
243+ -17194
244+ ],
245+ "../images/translation-file.png": [
246+ 0,
247+ -10654
248+ ],
249+ "../images/read-only.png": [
250+ 0,
251+ -9834
252+ ],
253+ "../images/project-logo.png": [
254+ 0,
255+ -18532
256+ ],
257+ "../images/bug-medium.png": [
258+ 0,
259+ -4588
260+ ],
261+ "../images/architecture.png": [
262+ 0,
263+ -11966
264+ ],
265+ "../images/trash-icon.png": [
266+ 0,
267+ -10982
268+ ],
269+ "../images/person-inactive.png": [
270+ 0,
271+ -6558
272+ ],
273+ "../images/arrowBottom.png": [
274+ 0,
275+ -14426
276+ ],
277+ "../images/project.png": [
278+ 0,
279+ -9344
280+ ],
281+ "../images/crowd.png": [
282+ 0,
283+ -1640
284+ ],
285+ "../images/info.png": [
286+ 0,
287+ -492
288+ ],
289+ "../images/flame-icon.png": [
290+ 0,
291+ -7708
292+ ],
293+ "../images/ubuntu-icon.png": [
294+ 0,
295+ -6392
296+ ],
297+ "../images/link.png": [
298+ 0,
299+ -3604
300+ ],
301+ "../images/stop.png": [
302+ 0,
303+ -11146
304+ ],
305+ "../images/person-logo.png": [
306+ 0,
307+ -18960
308+ ],
309+ "../images/distribution-logo.png": [
310+ 0,
311+ -18318
312+ ],
313+ "../images/retry.png": [
314+ 0,
315+ -8856
316+ ],
317+ "../images/rss.png": [
318+ 0,
319+ -6228
320+ ],
321+ "../images/private.png": [
322+ 0,
323+ -10162
324+ ],
325+ "../images/merge-proposal-icon.png": [
326+ 0,
327+ -12622
328+ ],
329+ "../images/download.png": [
330+ 0,
331+ -984
332+ ],
333+ "../images/arrowDown.png": [
334+ 0,
335+ -13770
336+ ],
337+ "../images/package-binary.png": [
338+ 0,
339+ -8692
340+ ],
341+ "../images/maybe.png": [
342+ 0,
343+ -7048
344+ ],
345+ "../images/bug-status-expand.png": [
346+ 0,
347+ -12458
348+ ],
349+ "../images/crowd-large.png": [
350+ 0,
351+ -16102
352+ ],
353+ "../images/blueprint.png": [
354+ 0,
355+ -5080
356+ ],
357+ "../images/project-badge.png": [
358+ 0,
359+ -9182
360+ ],
361+ "../images/bug-high.png": [
362+ 0,
363+ -4424
364+ ],
365+ "../images/blueprint-undefined.png": [
366+ 0,
367+ -5900
368+ ],
369+ "../images/blueprint-not.png": [
370+ 0,
371+ -6064
372+ ],
373+ "../images/mentoring.png": [
374+ 0,
375+ -6884
376+ ],
377+ "../images/flame-large.png": [
378+ 0,
379+ -16648
380+ ],
381+ "../images/bug-dupe-icon.png": [
382+ 0,
383+ -8364
384+ ],
385+ "../images/bug-critical.png": [
386+ 0,
387+ -4260
388+ ],
389+ "../images/build-success.png": [
390+ 0,
391+ -12950
392+ ],
393+ "../images/haspatch-icon.png": [
394+ 0,
395+ -15574
396+ ],
397+ "../images/person-inactive-badge.png": [
398+ 0,
399+ -6722
400+ ],
401+ "../images/ppa-icon.png": [
402+ 0,
403+ -12130
404+ ],
405+ "../images/yes.png": [
406+ 0,
407+ -1476
408+ ],
409+ "../images/team-logo.png": [
410+ 0,
411+ -19388
412+ ],
413+ "../images/arrowRight.png": [
414+ 0,
415+ -2456
416+ ],
417+ "../images/blueprint-high.png": [
418+ 0,
419+ -5408
420+ ],
421+ "../images/product.png": [
422+ 0,
423+ -9670
424+ ],
425+ "../images/bug-low.png": [
426+ 0,
427+ -4752
428+ ],
429+ "../images/package-source.png": [
430+ 0,
431+ -3112
432+ ],
433+ "../images/language.png": [
434+ 0,
435+ -3440
436+ ],
437+ "../images/person.png": [
438+ 0,
439+ -1804
440+ ],
441+ "../images/arrowUp.png": [
442+ 0,
443+ -13606
444+ ],
445+ "../images/distribution.png": [
446+ 0,
447+ -2948
448+ ],
449+ "../images/error-large.png": [
450+ 0,
451+ -16466
452+ ],
453+ "../images/news.png": [
454+ 0,
455+ -15410
456+ ],
457+ "../images/treeExpanded.png": [
458+ 0,
459+ -2784
460+ ],
461+ "../images/build-depwait.png": [
462+ 0,
463+ -13442
464+ ],
465+ "../images/blueprint-essential.png": [
466+ 0,
467+ -5244
468+ ],
469+ "../images/question.png": [
470+ 0,
471+ -656
472+ ],
473+ "../images/error.png": [
474+ 0,
475+ -7212
476+ ],
477+ "../images/bug-unknown.png": [
478+ 0,
479+ -8200
480+ ],
481+ "../images/product-logo.png": [
482+ 0,
483+ -18746
484+ ],
485+ "../images/blueprint-medium.png": [
486+ 0,
487+ -5572
488+ ],
489+ "../images/product-badge.png": [
490+ 0,
491+ -9508
492+ ],
493+ "../images/list.png": [
494+ 0,
495+ -11310
496+ ],
497+ "../images/launchpad-logo.png": [
498+ 0,
499+ -18104
500+ ],
501+ "../images/flame-logo.png": [
502+ 0,
503+ -19816
504+ ],
505+ "../images/translation-template.png": [
506+ 0,
507+ -10818
508+ ],
509+ "../images/bugtracker-icon.png": [
510+ 0,
511+ -8528
512+ ],
513+ "../images/meeting-logo.png": [
514+ 0,
515+ -19602
516+ ],
517+ "../images/treeCollapsed.png": [
518+ 0,
519+ -2620
520+ ],
521+ "../images/green-bar.png": [
522+ 0,
523+ -14918
524+ ],
525+ "../images/build-superseded.png": [
526+ 0,
527+ -12786
528+ ],
529+ "../images/trash-large.png": [
530+ 0,
531+ -17740
532+ ],
533+ "../images/red-bar.png": [
534+ 0,
535+ -15246
536+ ],
537+ "../images/add.png": [
538+ 0,
539+ 0
540+ ],
541+ "../images/remove.png": [
542+ 0,
543+ -328
544+ ],
545+ "../images/person-inactive-logo.png": [
546+ 0,
547+ -19174
548+ ],
549+ "../images/edit.png": [
550+ 0,
551+ -164
552+ ],
553+ "../images/bug-wishlist.png": [
554+ 0,
555+ -4916
556+ ],
557+ "../images/warning-large.png": [
558+ 0,
559+ -15920
560+ ],
561+ "../images/arrowEnd.png": [
562+ 0,
563+ -14098
564+ ],
565+ "../images/cve.png": [
566+ 0,
567+ -3932
568+ ],
569+ "../images/merge-proposal-large.png": [
570+ 0,
571+ -17558
572+ ],
573+ "../images/person-badge.png": [
574+ 0,
575+ -1968
576+ ],
577+ "../images/mentoring-large.png": [
578+ 0,
579+ -17376
580+ ],
581+ "../images/bug.png": [
582+ 0,
583+ -4096
584+ ],
585+ "../images/bug-remote.png": [
586+ 0,
587+ -8036
588+ ],
589+ "../images/translation.png": [
590+ 0,
591+ -10490
592+ ],
593+ "../images/confirm.png": [
594+ 0,
595+ -7542
596+ ],
597+ "../images/search.png": [
598+ 0,
599+ -1148
600+ ]
601+}
602\ No newline at end of file
603
604=== renamed file 'lib/canonical/launchpad/icing/style-3-0.css' => 'lib/canonical/launchpad/icing/style-3-0.css.in'
605--- lib/canonical/launchpad/icing/style-3-0.css 2010-02-05 09:48:47 +0000
606+++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-02-11 16:45:26 +0000
607@@ -31,9 +31,6 @@
608 .footer .lp-arcana img {
609 vertical-align: middle;
610 }
611-.footer .lp-arcana .search-icon {
612- background: url(icon-sprites) 100% -191px no-repeat;
613- }
614 .footer .sitemessage {
615 text-align: right;
616 }
617@@ -813,6 +810,15 @@
618 padding: 2px 0 5px 18px;
619 line-height: 18px;
620 }
621+input[type="submit"].icon-only {
622+ vertical-align: middle;
623+ border: 0;
624+ padding: 0;
625+ height: 16px;
626+ width: 16px;
627+ background-color: inherit;
628+ cursor: pointer;
629+}
630 .vertical .sprite {
631 /* XXX: EdwinGrubbs 2009-10-29 bug=463032
632 The reduced padding is only necessary to prevent parts of the
633@@ -846,140 +852,493 @@
634 */
635 .yui-picker-results li.sprite {
636 padding:2px 0 0 18px;
637- white-space: nowrap;
638- overflow: hidden;
639-}
640-
641-.add {background:url(icon-sprites) 0 0 no-repeat;}
642-.edit {background:url(icon-sprites) 0 -32px no-repeat;}
643-.remove {background:url(icon-sprites) 0 -64px no-repeat;}
644-.info {background:url(icon-sprites) 0 -96px no-repeat;}
645-.question {background:url(icon-sprites) 0 -128px no-repeat;}
646-.download-icon {background:url(icon-sprites) 0 -160px no-repeat;}
647-.download {background:url(icon-sprites) 0 -160px no-repeat;}
648-.search-icon {background:url(icon-sprites) 0 -187px no-repeat;}
649-.no {background:url(icon-sprites) 0 -224px no-repeat;}
650-.yes {background:url(icon-sprites) 0 -256px no-repeat;}
651-.crowd {background:url(icon-sprites) 0 -288px no-repeat;}
652-.person {background:url(icon-sprites) 0 -320px no-repeat;}
653-.person-tabs {background:url(icon-sprites) 5px -314px no-repeat;}
654-.person-badge {background:url(icon-sprites) 0 -352px no-repeat;}
655-.team {background:url(icon-sprites) 0 -384px no-repeat;}
656-.team-badge {background:url(icon-sprites) 0 -416px no-repeat;}
657-.arrowRight {background:url(icon-sprites) 0 -448px no-repeat;}
658-.treeCollapsed {background:url(icon-sprites) 0 -480px no-repeat;}
659-.treeExpanded {background:url(icon-sprites) 0 -512px no-repeat;}
660-.branch, .bzr-favicon {background:url(icon-sprites) 0 -544px no-repeat;}
661-.bzr-favicon {background:url(icon-sprites) 5px -538px no-repeat;}
662-.distribution {background:url(icon-sprites) 0 -576px no-repeat;}
663-.package-source {background:url(icon-sprites) 0 -608px no-repeat;}
664-.milestone {background:url(icon-sprites) 0 -640px no-repeat;}
665-.language {background:url(icon-sprites) 0 -672px no-repeat;}
666-.external-link {background:url(icon-sprites) 0 -704px no-repeat;}
667-.mail {background:url(icon-sprites) 0 -736px no-repeat;}
668-.cve {background:url(icon-sprites) 0 -768px no-repeat;}
669-.cves {float: right; text-align: right;}
670-.bug {background:url(icon-sprites) 0 -800px no-repeat;}
671-.bug-critical {background:url(icon-sprites) 0 -832px no-repeat;}
672-.bug-high {background:url(icon-sprites) 0 -864px no-repeat;}
673-.bug-medium {background:url(icon-sprites) 0 -896px no-repeat;}
674-.bug-low {background:url(icon-sprites) 0 -928px no-repeat;}
675-.bug-wishlist {background:url(icon-sprites) 0 -960px no-repeat;}
676-.blueprint {background:url(icon-sprites) 0 -992px no-repeat;}
677-.blueprint-essential {background:url(icon-sprites) 0 -1024px no-repeat;}
678-.blueprint-high {background:url(icon-sprites) 0 -1056px no-repeat;}
679-.blueprint-medium {background:url(icon-sprites) 0 -1088px no-repeat;}
680-.blueprint-low {background:url(icon-sprites) 0 -1120px no-repeat;}
681-.blueprint-undefined {background:url(icon-sprites) 0 -1152px no-repeat;}
682-.blueprint-not {background:url(icon-sprites) 0 -1184px no-repeat;}
683-.rss {background:url(icon-sprites) 0 -1216px no-repeat;}
684-.ubuntu-logo {background:url(icon-sprites-2) 0 0px no-repeat;}
685-.person-inactive {background:url(icon-sprites-2) 0 -32px no-repeat;}
686-.person-inactive-badge {background:url(icon-sprites-2) 0 -64px no-repeat;}
687-.mentoring {background:url(icon-sprites-2) 0 -96px no-repeat;}
688-.undecided {background:url(icon-sprites-2) 0 -128px no-repeat;}
689-.error-icon {background:url(icon-sprites-2) 0 -160px no-repeat;}
690-.cancel {background:url(icon-sprites-2) 0 -192px no-repeat;}
691-.confirm {background:url(icon-sprites-2) 0 -224px no-repeat;}
692-.flame {background:url(icon-sprites-2) 0 -256px no-repeat;}
693-.bug-undecided {background:url(icon-sprites-2) 0 -288px no-repeat;}
694-.bug-remote {background:url(icon-sprites-2) 0 -320px no-repeat;}
695-.bug-unknown {background:url(icon-sprites-2) 0 -352px no-repeat;}
696-.bug-dupe {background:url(icon-sprites-2) 0 -384px no-repeat;}
697-.bug-tracker {background:url(icon-sprites-2) 0 -416px no-repeat;}
698-.package-binary {background:url(icon-sprites-2) 0 -448px no-repeat;}
699-.retry {background:url(icon-sprites-2) 0 -480px no-repeat;}
700-.distribution-badge {background:url(icon-sprites-2) 0 -512px no-repeat;}
701-.project-badge {background:url(icon-sprites-2) 0 -544px no-repeat;}
702-.project {background:url(icon-sprites-2) 0 -576px no-repeat;}
703-.product-badge {background:url(icon-sprites-2) 0 -608px no-repeat;}
704-.product {background:url(icon-sprites-2) 0 -640px no-repeat;}
705-.read-only {background:url(icon-sprites-2) 0 -672px no-repeat;}
706-.warning-icon, .security {background:url(icon-sprites-2) 0 -704px no-repeat;}
707-.private {background:url(icon-sprites-2) 0 -736px no-repeat;}
708-.meeting {background:url(icon-sprites-2) 0 -768px no-repeat;}
709-.translate-icon {background:url(icon-sprites-2) 0 -800px no-repeat;}
710-.translation-file {background:url(icon-sprites-2) 0 -832px no-repeat;}
711-.translation-template {background:url(icon-sprites-2) 0 -864px no-repeat;}
712-.trash-icon {background:url(icon-sprites-2) 0 -896px no-repeat;}
713-.stop {background:url(icon-sprites-2) 0 -928px no-repeat;}
714-.list {background:url(icon-sprites-2) 0 -992px no-repeat;}
715-.bullet {background:url(icon-sprites-2) 0 -1024px no-repeat;}
716-.zoom-in {background:url(icon-sprites-2) 0 -1056px no-repeat;}
717-.zoom-out {background:url(icon-sprites-2) 0 -1088px no-repeat;}
718-.architecture {background:url(icon-sprites-2) 0 -1120px no-repeat;}
719-.ppa-icon {background:url(icon-sprites-2) 0 -1147px no-repeat;}
720-.ppa-icon-inactive {background:url(icon-sprites-2) 0 -1171px no-repeat;}
721-
722-.bug-status-expand {background:url(icon-sprites-3) 0 -10px no-repeat;}
723+}
724+
725+.add {
726+ background-image: url(/@@/add.png); /* sprite-ref: icon-sprites */
727+ background-repeat: no-repeat;
728+}
729+.edit {
730+ background-image: url(/@@/edit.png); /* sprite-ref: icon-sprites */
731+ background-repeat: no-repeat;
732+}
733+.remove {
734+ background-image: url(/@@/remove.png); /* sprite-ref: icon-sprites */
735+ background-repeat: no-repeat;
736+}
737+.info {
738+ background-image: url(/@@/info.png); /* sprite-ref: icon-sprites */
739+ background-repeat: no-repeat;
740+}
741+.question {
742+ background-image: url(/@@/question.png); /* sprite-ref: icon-sprites */
743+ background-repeat: no-repeat;
744+}
745+.download-icon {
746+ background-image: url(/@@/download.png); /* sprite-ref: icon-sprites */
747+ background-repeat: no-repeat;
748+}
749+.download {
750+ background-image: url(/@@/download.png); /* sprite-ref: icon-sprites */
751+ background-repeat: no-repeat;
752+}
753+.search-icon {
754+ background-image: url(/@@/search.png); /* sprite-ref: icon-sprites */
755+ background-repeat: no-repeat;
756+}
757+.no {
758+ background-image: url(/@@/no.png); /* sprite-ref: icon-sprites */
759+ background-repeat: no-repeat;
760+}
761+.yes {
762+ background-image: url(/@@/yes.png); /* sprite-ref: icon-sprites */
763+ background-repeat: no-repeat;
764+}
765+.crowd {
766+ background-image: url(/@@/crowd.png); /* sprite-ref: icon-sprites */
767+ background-repeat: no-repeat;
768+}
769+.person {
770+ background-image: url(/@@/person.png); /* sprite-ref: icon-sprites */
771+ background-repeat: no-repeat;
772+}
773+.person-badge {
774+ background-image: url(/@@/person-badge.png); /* sprite-ref: icon-sprites */
775+ background-repeat: no-repeat;
776+}
777+.team {
778+ background-image: url(/@@/team.png); /* sprite-ref: icon-sprites */
779+ background-repeat: no-repeat;
780+}
781+.team-badge {
782+ background-image: url(/@@/team-badge.png); /* sprite-ref: icon-sprites */
783+ background-repeat: no-repeat;
784+}
785+.arrowRight {
786+ background-image: url(/@@/arrowRight.png); /* sprite-ref: icon-sprites */
787+ background-repeat: no-repeat;
788+}
789+.treeCollapsed {
790+ background-image: url(/@@/treeCollapsed.png); /* sprite-ref: icon-sprites */
791+ background-repeat: no-repeat;
792+}
793+.treeExpanded {
794+ background-image: url(/@@/treeExpanded.png); /* sprite-ref: icon-sprites */
795+ background-repeat: no-repeat;
796+}
797+.distribution {
798+ background-image: url(/@@/distribution.png); /* sprite-ref: icon-sprites */
799+ background-repeat: no-repeat;
800+}
801+.package-source {
802+ background-image: url(/@@/package-source.png); /* sprite-ref: icon-sprites */
803+ background-repeat: no-repeat;
804+}
805+.milestone {
806+ background-image: url(/@@/milestone.png); /* sprite-ref: icon-sprites */
807+ background-repeat: no-repeat;
808+}
809+.language {
810+ background-image: url(/@@/language.png); /* sprite-ref: icon-sprites */
811+ background-repeat: no-repeat;
812+}
813+.external-link {
814+ background-image: url(/@@/link.png); /* sprite-ref: icon-sprites */
815+ background-repeat: no-repeat;
816+}
817+.mail {
818+ background-image: url(/@@/mail.png); /* sprite-ref: icon-sprites */
819+ background-repeat: no-repeat;
820+}
821+.cve {
822+ background-image: url(/@@/cve.png); /* sprite-ref: icon-sprites */
823+ background-repeat: no-repeat;
824+}
825+.bug {
826+ background-image: url(/@@/bug.png); /* sprite-ref: icon-sprites */
827+ background-repeat: no-repeat;
828+}
829+.bug-critical {
830+ background-image: url(/@@/bug-critical.png); /* sprite-ref: icon-sprites */
831+ background-repeat: no-repeat;
832+}
833+.bug-high {
834+ background-image: url(/@@/bug-high.png); /* sprite-ref: icon-sprites */
835+ background-repeat: no-repeat;
836+}
837+.bug-medium {
838+ background-image: url(/@@/bug-medium.png); /* sprite-ref: icon-sprites */
839+ background-repeat: no-repeat;
840+}
841+.bug-low {
842+ background-image: url(/@@/bug-low.png); /* sprite-ref: icon-sprites */
843+ background-repeat: no-repeat;
844+}
845+.bug-wishlist {
846+ background-image: url(/@@/bug-wishlist.png); /* sprite-ref: icon-sprites */
847+ background-repeat: no-repeat;
848+}
849+.blueprint {
850+ background-image: url(/@@/blueprint.png); /* sprite-ref: icon-sprites */
851+ background-repeat: no-repeat;
852+}
853+.blueprint-essential {
854+ background-image: url(/@@/blueprint-essential.png); /* sprite-ref: icon-sprites */
855+ background-repeat: no-repeat;
856+}
857+.blueprint-high {
858+ background-image: url(/@@/blueprint-high.png); /* sprite-ref: icon-sprites */
859+ background-repeat: no-repeat;
860+}
861+.blueprint-medium {
862+ background-image: url(/@@/blueprint-medium.png); /* sprite-ref: icon-sprites */
863+ background-repeat: no-repeat;
864+}
865+.blueprint-low {
866+ background-image: url(/@@/blueprint-low.png); /* sprite-ref: icon-sprites */
867+ background-repeat: no-repeat;
868+}
869+.blueprint-undefined {
870+ background-image: url(/@@/blueprint-undefined.png); /* sprite-ref: icon-sprites */
871+ background-repeat: no-repeat;
872+}
873+.blueprint-not {
874+ background-image: url(/@@/blueprint-not.png); /* sprite-ref: icon-sprites */
875+ background-repeat: no-repeat;
876+}
877+.rss {
878+ background-image: url(/@@/rss.png); /* sprite-ref: icon-sprites */
879+ background-repeat: no-repeat;
880+}
881+
882+.ubuntu-logo {
883+ background-image: url(/@@/ubuntu-icon.png); /* sprite-ref: icon-sprites */
884+ background-repeat: no-repeat;
885+}
886+.person-inactive {
887+ background-image: url(/@@/person-inactive.png); /* sprite-ref: icon-sprites */
888+ background-repeat: no-repeat;
889+}
890+.person-inactive-badge {
891+ background-image: url(/@@/person-inactive-badge.png); /* sprite-ref: icon-sprites */
892+ background-repeat: no-repeat;
893+}
894+.mentoring {
895+ background-image: url(/@@/mentoring.png); /* sprite-ref: icon-sprites */
896+ background-repeat: no-repeat;
897+}
898+.undecided {
899+ background-image: url(/@@/maybe.png); /* sprite-ref: icon-sprites */
900+ background-repeat: no-repeat;
901+}
902+.error-icon {
903+ background-image: url(/@@/error.png); /* sprite-ref: icon-sprites */
904+ background-repeat: no-repeat;
905+}
906+.cancel {
907+ background-image: url(/@@/cancel.png); /* sprite-ref: icon-sprites */
908+ background-repeat: no-repeat;
909+}
910+.confirm {
911+ background-image: url(/@@/confirm.png); /* sprite-ref: icon-sprites */
912+ background-repeat: no-repeat;
913+}
914+.flame {
915+ background-image: url(/@@/flame-icon.png); /* sprite-ref: icon-sprites */
916+ background-repeat: no-repeat;
917+}
918+.bug-undecided {
919+ background-image: url(/@@/bug-undecided.png); /* sprite-ref: icon-sprites */
920+ background-repeat: no-repeat;
921+}
922+.bug-remote {
923+ background-image: url(/@@/bug-remote.png); /* sprite-ref: icon-sprites */
924+ background-repeat: no-repeat;
925+}
926+.bug-unknown {
927+ background-image: url(/@@/bug-unknown.png); /* sprite-ref: icon-sprites */
928+ background-repeat: no-repeat;
929+}
930+.bug-dupe {
931+ background-image: url(/@@/bug-dupe-icon.png); /* sprite-ref: icon-sprites */
932+ background-repeat: no-repeat;
933+}
934+.bug-tracker {
935+ background-image: url(/@@/bugtracker-icon.png); /* sprite-ref: icon-sprites */
936+ background-repeat: no-repeat;
937+}
938+.package-binary {
939+ background-image: url(/@@/package-binary.png); /* sprite-ref: icon-sprites */
940+ background-repeat: no-repeat;
941+}
942+.retry {
943+ background-image: url(/@@/retry.png); /* sprite-ref: icon-sprites */
944+ background-repeat: no-repeat;
945+}
946+.distribution-badge {
947+ background-image: url(/@@/distribution-badge.png); /* sprite-ref: icon-sprites */
948+ background-repeat: no-repeat;
949+}
950+.project-badge {
951+ background-image: url(/@@/project-badge.png); /* sprite-ref: icon-sprites */
952+ background-repeat: no-repeat;
953+}
954+.project {
955+ background-image: url(/@@/project.png); /* sprite-ref: icon-sprites */
956+ background-repeat: no-repeat;
957+}
958+.product-badge {
959+ background-image: url(/@@/product-badge.png); /* sprite-ref: icon-sprites */
960+ background-repeat: no-repeat;
961+}
962+.product {
963+ background-image: url(/@@/product.png); /* sprite-ref: icon-sprites */
964+ background-repeat: no-repeat;
965+}
966+.read-only {
967+ background-image: url(/@@/read-only.png); /* sprite-ref: icon-sprites */
968+ background-repeat: no-repeat;
969+}
970+.warning-icon, .security {
971+ background-image: url(/@@/warning.png); /* sprite-ref: icon-sprites */
972+ background-repeat: no-repeat;
973+}
974+.private {
975+ background-image: url(/@@/private.png); /* sprite-ref: icon-sprites */
976+ background-repeat: no-repeat;
977+}
978+.meeting {
979+ background-image: url(/@@/meeting.png); /* sprite-ref: icon-sprites */
980+ background-repeat: no-repeat;
981+}
982+.translate-icon {
983+ background-image: url(/@@/translation.png); /* sprite-ref: icon-sprites */
984+ background-repeat: no-repeat;
985+}
986+.translation-file {
987+ background-image: url(/@@/translation-file.png); /* sprite-ref: icon-sprites */
988+ background-repeat: no-repeat;
989+}
990+.translation-template {
991+ background-image: url(/@@/translation-template.png); /* sprite-ref: icon-sprites */
992+ background-repeat: no-repeat;
993+}
994+.trash-icon {
995+ background-image: url(/@@/trash-icon.png); /* sprite-ref: icon-sprites */
996+ background-repeat: no-repeat;
997+}
998+.stop {
999+ background-image: url(/@@/stop.png); /* sprite-ref: icon-sprites */
1000+ background-repeat: no-repeat;
1001+}
1002+.list {
1003+ background-image: url(/@@/list.png); /* sprite-ref: icon-sprites */
1004+ background-repeat: no-repeat;
1005+}
1006+.bullet {
1007+ background-image: url(/@@/bullet.png); /* sprite-ref: icon-sprites */
1008+ background-repeat: no-repeat;
1009+}
1010+.zoom-in {
1011+ background-image: url(/@@/zoom-in.png); /* sprite-ref: icon-sprites */
1012+ background-repeat: no-repeat;
1013+}
1014+.zoom-out {
1015+ background-image: url(/@@/zoom-out.png); /* sprite-ref: icon-sprites */
1016+ background-repeat: no-repeat;
1017+}
1018+.architecture {
1019+ background-image: url(/@@/architecture.png); /* sprite-ref: icon-sprites */
1020+ background-repeat: no-repeat;
1021+}
1022+.ppa-icon {
1023+ background-image: url(/@@/ppa-icon.png); /* sprite-ref: icon-sprites */
1024+ background-repeat: no-repeat;
1025+}
1026+.ppa-icon-inactive {
1027+ background-image: url(/@@/ppa-icon-inactive.png); /* sprite-ref: icon-sprites */
1028+ background-repeat: no-repeat;
1029+}
1030+
1031+.bug-status-expand {
1032+ background-image: url(/@@/bug-status-expand.png); /* sprite-ref: icon-sprites */
1033+ background-repeat: no-repeat;
1034+}
1035 .merge-proposal {
1036- background:url(icon-sprites-3) 0 -46px no-repeat;
1037- padding-left: 22px}
1038-.build-superseded {background:url(icon-sprites-3) 0 -80px no-repeat;}
1039-.build-sucess {background:url(icon-sprites-3) 0 -112px no-repeat;}
1040-.build-needed {background:url(icon-sprites-3) 0 -144px no-repeat;}
1041-.build-failure {background:url(icon-sprites-3) 0 -176px no-repeat;}
1042-.build-depwait {background:url(icon-sprites-3) 0 -208px no-repeat;}
1043-.arrowUp {background:url(icon-sprites-3) 0 -240px no-repeat;}
1044-.arrowDown {background:url(icon-sprites-3) 0 -256px no-repeat;}
1045-.arrowStart {background:url(icon-sprites-3) 0 -288px no-repeat;}
1046-.arrowEnd {background:url(icon-sprites-3) 0 -304px no-repeat;}
1047-.arrowTop {background:url(icon-sprites-3) 0 -328px no-repeat;}
1048-.arrowBottom {background:url(icon-sprites-3) 0 -356px no-repeat;}
1049-.arrowLeft {background:url(icon-sprites-3) 0 -384px no-repeat;}
1050-.bluebar {background:url(icon-sprites-3) 0 -416px repeat-x;}
1051-.redbar {background:url(icon-sprites-3) 0 -505px repeat-x;}
1052-.greenbar {background:url(icon-sprites-3) 0 -446px repeat-x;}
1053-.purplebar {background:url(icon-sprites-3) 0 -476px repeat-x;}
1054-.favorite-yes {background:url(icon-sprites-3) 0 -530px no-repeat;}
1055-.haspatch-icon {background:url(icon-sprites-3) 0 -562px no-repeat;}
1056+ background-image: url(/@@/merge-proposal-icon.png); /* sprite-ref: icon-sprites */
1057+ background-repeat: no-repeat;
1058+ padding-left: 22px
1059+}
1060+.build-superseded {
1061+ background-image: url(/@@/build-superseded.png); /* sprite-ref: icon-sprites */
1062+ background-repeat: no-repeat;
1063+}
1064+.build-success {
1065+ background-image: url(/@@/build-success.png); /* sprite-ref: icon-sprites */
1066+ background-repeat: no-repeat;
1067+}
1068+.build-needed {
1069+ background-image: url(/@@/build-needed.png); /* sprite-ref: icon-sprites */
1070+ background-repeat: no-repeat;
1071+}
1072+.build-failure {
1073+ background-image: url(/@@/build-failure.png); /* sprite-ref: icon-sprites */
1074+ background-repeat: no-repeat;
1075+}
1076+.build-depwait {
1077+ background-image: url(/@@/build-depwait.png); /* sprite-ref: icon-sprites */
1078+ background-repeat: no-repeat;
1079+}
1080+.arrowUp {
1081+ background-image: url(/@@/arrowUp.png); /* sprite-ref: icon-sprites */
1082+ background-repeat: no-repeat;
1083+}
1084+.arrowDown {
1085+ background-image: url(/@@/arrowDown.png); /* sprite-ref: icon-sprites */
1086+ background-repeat: no-repeat;
1087+}
1088+.arrowStart {
1089+ background-image: url(/@@/arrowStart.png); /* sprite-ref: icon-sprites */
1090+ background-repeat: no-repeat;
1091+}
1092+.arrowEnd {
1093+ background-image: url(/@@/arrowEnd.png); /* sprite-ref: icon-sprites */
1094+ background-repeat: no-repeat;
1095+}
1096+.arrowTop {
1097+ background-image: url(/@@/arrowTop.png); /* sprite-ref: icon-sprites */
1098+ background-repeat: no-repeat;
1099+}
1100+.arrowBottom {
1101+ background-image: url(/@@/arrowBottom.png); /* sprite-ref: icon-sprites */
1102+ background-repeat: no-repeat;
1103+}
1104+.arrowLeft {
1105+ background-image: url(/@@/arrowLeft.png); /* sprite-ref: icon-sprites */
1106+ background-repeat: no-repeat;
1107+}
1108+.bluebar {
1109+ background-image: url(/@@/blue-bar.png); /* sprite-ref: icon-sprites */
1110+ background-repeat: repeat-x;
1111+}
1112+.greenbar {
1113+ background-image: url(/@@/green-bar.png); /* sprite-ref: icon-sprites */
1114+ background-repeat: repeat-x;
1115+}
1116+.purplebar {
1117+ background-image: url(/@@/purple-bar.png); /* sprite-ref: icon-sprites */
1118+ background-repeat: repeat-x;
1119+}
1120+.redbar {
1121+ background-image: url(/@@/red-bar.png); /* sprite-ref: icon-sprites */
1122+ background-repeat: repeat-x;
1123+}
1124+.favorite-yes {
1125+ background-image: url(/@@/news.png); /* sprite-ref: icon-sprites */
1126+ background-repeat: no-repeat;
1127+}
1128+.haspatch-icon {
1129+ background-image: url(/@@/haspatch-icon.png); /* sprite-ref: icon-sprites */
1130+ background-repeat: no-repeat;
1131+}
1132+
1133
1134 /*large*/
1135-.large-branch {background:url(icon-sprites-large) 0 0 no-repeat;}
1136-.large-warning {background:url(icon-sprites-large) 0 -48px no-repeat;}
1137-.large-crowd {background:url(icon-sprites-large) 0 -96px no-repeat;}
1138-.large-download {background:url(icon-sprites-large) 0 -144px no-repeat;}
1139-.large-error {background:url(icon-sprites-large) 0 -144px no-repeat;}
1140-.large-flame {background:url(icon-sprites-large) 0 -192px no-repeat;}
1141-.large-download {background:url(icon-sprites-large) 0 -240px no-repeat;}
1142-.large-info {background:url(icon-sprites-large) 0 -288px no-repeat;}
1143-.large-launchpad {background:url(icon-sprites-large) 0 -336px no-repeat;}
1144-.large-mentoring {background:url(icon-sprites-large) 0 -384px no-repeat;}
1145-.large-proposal {background:url(icon-sprites-large) 0 -432px no-repeat;}
1146-.large-trash {background:url(icon-sprites-large) 0 -480px no-repeat;}
1147-.large-private {background:url(icon-sprites-large) 0 -528px no-repeat;}
1148+.large-branch {
1149+ background-image: url(/@@/branch-large.png); /* sprite-ref: icon-sprites */
1150+ background-repeat: no-repeat;
1151+}
1152+.large-warning {
1153+ background-image: url(/@@/warning-large.png); /* sprite-ref: icon-sprites */
1154+ background-repeat: no-repeat;
1155+}
1156+.large-crowd {
1157+ background-image: url(/@@/crowd-large.png); /* sprite-ref: icon-sprites */
1158+ background-repeat: no-repeat;
1159+}
1160+.large-download {
1161+ background-image: url(/@@/download-large.png); /* sprite-ref: icon-sprites */
1162+ background-repeat: no-repeat;
1163+}
1164+.large-error {
1165+ background-image: url(/@@/error-large.png); /* sprite-ref: icon-sprites */
1166+ background-repeat: no-repeat;
1167+}
1168+.large-flame {
1169+ background-image: url(/@@/flame-large.png); /* sprite-ref: icon-sprites */
1170+ background-repeat: no-repeat;
1171+}
1172+.large-download {
1173+ background-image: url(/@@/download-large.png); /* sprite-ref: icon-sprites */
1174+ background-repeat: no-repeat;
1175+}
1176+.large-info {
1177+ background-image: url(/@@/info-large.png); /* sprite-ref: icon-sprites */
1178+ background-repeat: no-repeat;
1179+}
1180+.large-launchpad {
1181+ background-image: url(/@@/launchpad-large.png); /* sprite-ref: icon-sprites */
1182+ background-repeat: no-repeat;
1183+}
1184+.large-mentoring {
1185+ background-image: url(/@@/mentoring-large.png); /* sprite-ref: icon-sprites */
1186+ background-repeat: no-repeat;
1187+}
1188+.large-proposal {
1189+ background-image: url(/@@/merge-proposal-large.png); /* sprite-ref: icon-sprites */
1190+ background-repeat: no-repeat;
1191+}
1192+.large-trash {
1193+ background-image: url(/@@/trash-large.png); /* sprite-ref: icon-sprites */
1194+ background-repeat: no-repeat;
1195+}
1196+.large-private {
1197+ background-image: url(/@@/private-large.png); /* sprite-ref: icon-sprites */
1198+ background-repeat: no-repeat;
1199+}
1200
1201 /*logo*/
1202-.logo-launchpad {background:url(icon-sprites-logo) 0 0 no-repeat;}
1203-.logo-distribution {background:url(icon-sprites-logo) 0 -80px no-repeat;}
1204-.logo-project {background:url(icon-sprites-logo) 0 -160px no-repeat;}
1205-.logo-product {background:url(icon-sprites-logo) 0 -240 no-repeat;}
1206-.logo-person {background:url(icon-sprites-logo) 0 -320 no-repeat;}
1207-.logo-inactive {background:url(icon-sprites-logo-2) 0 0 no-repeat;}
1208-.logo-team {background:url(icon-sprites-logo-2) 0 -80px no-repeat;}
1209-.logo-meeting {background:url(icon-sprites-logo-2) 0 -160px no-repeat;}
1210-.logo-flame {background:url(icon-sprites-logo-2) 0 -240 no-repeat;}
1211-.logo-trash {background:url(icon-sprites-logo-2) 0 -320 no-repeat;}
1212+.logo-launchpad {
1213+ background-image: url(/@@/launchpad-logo.png); /* sprite-ref: icon-sprites */
1214+ background-repeat: no-repeat;
1215+}
1216+.logo-distribution {
1217+ background-image: url(/@@/distribution-logo.png); /* sprite-ref: icon-sprites */
1218+ background-repeat: no-repeat;
1219+}
1220+.logo-project {
1221+ background-image: url(/@@/project-logo.png); /* sprite-ref: icon-sprites */
1222+ background-repeat: no-repeat;
1223+}
1224+.logo-product {
1225+ background-image: url(/@@/product-logo.png); /* sprite-ref: icon-sprites */
1226+ background-repeat: no-repeat;
1227+}
1228+.logo-person {
1229+ background-image: url(/@@/person-logo.png); /* sprite-ref: icon-sprites */
1230+ background-repeat: no-repeat;
1231+}
1232+
1233+.logo-inactive {
1234+ background-image: url(/@@/person-inactive-logo.png); /* sprite-ref: icon-sprites */
1235+ background-repeat: no-repeat;
1236+}
1237+.logo-team {
1238+ background-image: url(/@@/team-logo.png); /* sprite-ref: icon-sprites */
1239+ background-repeat: no-repeat;
1240+}
1241+.logo-meeting {
1242+ background-image: url(/@@/meeting-logo.png); /* sprite-ref: icon-sprites */
1243+ background-repeat: no-repeat;
1244+}
1245+.logo-flame {
1246+ background-image: url(/@@/flame-logo.png); /* sprite-ref: icon-sprites */
1247+ background-repeat: no-repeat;
1248+}
1249+.logo-trash {
1250+ background-image: url(/@@/trash-logo.png); /* sprite-ref: icon-sprites */
1251+ background-repeat: no-repeat;
1252+}
1253
1254 /* Code styles */
1255 .branchstatusMATURE, .branchstatusMATURE a {color: #090;}
1256
1257=== modified file 'lib/canonical/launchpad/images/branch-large.png'
1258Binary files lib/canonical/launchpad/images/branch-large.png 2007-04-28 17:55:27 +0000 and lib/canonical/launchpad/images/branch-large.png 2010-02-11 16:45:26 +0000 differ
1259=== added file 'lib/canonical/launchpad/images/build-failure.png'
1260Binary files lib/canonical/launchpad/images/build-failure.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/build-failure.png 2010-02-11 16:45:26 +0000 differ
1261=== added file 'lib/canonical/launchpad/images/haspatch-icon.png'
1262Binary files lib/canonical/launchpad/images/haspatch-icon.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/haspatch-icon.png 2010-02-11 16:45:26 +0000 differ
1263=== modified file 'lib/canonical/launchpad/images/mentoring-large.png'
1264Binary files lib/canonical/launchpad/images/mentoring-large.png 2007-04-27 21:21:18 +0000 and lib/canonical/launchpad/images/mentoring-large.png 2010-02-11 16:45:26 +0000 differ
1265=== modified file 'lib/canonical/launchpad/images/mentoring.png'
1266Binary files lib/canonical/launchpad/images/mentoring.png 2007-04-27 21:21:18 +0000 and lib/canonical/launchpad/images/mentoring.png 2010-02-11 16:45:26 +0000 differ
1267=== modified file 'lib/canonical/launchpad/images/rss-large.png'
1268Binary files lib/canonical/launchpad/images/rss-large.png 2007-11-15 06:14:42 +0000 and lib/canonical/launchpad/images/rss-large.png 2010-02-11 16:45:26 +0000 differ
1269=== modified file 'lib/lp/app/browser/tests/base-layout.txt'
1270--- lib/lp/app/browser/tests/base-layout.txt 2010-01-30 22:39:54 +0000
1271+++ lib/lp/app/browser/tests/base-layout.txt 2010-02-11 16:45:26 +0000
1272@@ -218,14 +218,13 @@
1273
1274 >>> print html
1275 <!DOCTYPE html ...
1276- ...
1277- <form id="globalsearch" method="get"
1278- accept-charset="UTF-8"
1279- class="sprite-after search-icon"
1280- action="http://launchpad.dev/+search">
1281- <input type="search" id="search-text" name="field.text" />
1282- </form>
1283- </div>
1284+ <form id="globalsearch" method="get"
1285+ accept-charset="UTF-8"
1286+ action="http://launchpad.dev/+search">
1287+ <input type="search" id="search-text" name="field.text" />
1288+ <input type="submit" value="" class="icon-only sprite search-icon" />
1289+ </form>
1290+ </div>
1291 ...
1292
1293 Footer.
1294
1295=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
1296--- lib/lp/app/templates/base-layout-macros.pt 2010-01-30 18:15:36 +0000
1297+++ lib/lp/app/templates/base-layout-macros.pt 2010-02-11 16:45:26 +0000
1298@@ -338,10 +338,10 @@
1299 <a href="https://help.launchpad.net/">Read the guide</a>
1300 &nbsp;
1301 <form id="globalsearch" method="get" accept-charset="UTF-8"
1302- class="sprite-after search-icon"
1303 tal:condition="view/macro:pagehas/globalsearch"
1304 tal:attributes="action string:${rooturl}+search">
1305 <input type="search" id="search-text" name="field.text" />
1306+ <input type="submit" value="" class="icon-only sprite search-icon" />
1307 </form>
1308 </div>
1309 <metal:site-message
1310
1311=== added file 'lib/lp/bugs/help/tag-help.html'
1312--- lib/lp/bugs/help/tag-help.html 1970-01-01 00:00:00 +0000
1313+++ lib/lp/bugs/help/tag-help.html 2010-02-11 16:45:26 +0000
1314@@ -0,0 +1,31 @@
1315+<html>
1316+ <head>
1317+ <title>Bug tag search</title>
1318+ <link rel="stylesheet" type="text/css"
1319+ href="/+icing/yui/cssreset/reset.css" />
1320+ <link rel="stylesheet" type="text/css"
1321+ href="/+icing/yui/cssfonts/fonts.css" />
1322+ <link rel="stylesheet" type="text/css"
1323+ href="/+icing/yui/cssbase/base.css" />
1324+ </head>
1325+ <body>
1326+ <h1>Using bug tags</h1>
1327+
1328+ <p>
1329+ A bug tag can be anything that you think will help better categorise the
1330+ bug report.
1331+
1332+ For example, if it's a user interface bug you might tag it with
1333+ <code>UI</code>.
1334+
1335+ </p>
1336+
1337+ <h2>Official bug tags</h2>
1338+
1339+ Each project can define official bug tags. When you're adding a bug tag
1340+ to a bug report, Launchpad will offer to automcomplete matching official
1341+ bug tags.
1342+ </p>
1343+
1344+ </body>
1345+</html>
1346
1347=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
1348--- lib/lp/bugs/templates/bugtask-index.pt 2010-01-22 03:16:44 +0000
1349+++ lib/lp/bugs/templates/bugtask-index.pt 2010-02-11 16:45:26 +0000
1350@@ -185,6 +185,7 @@
1351 id="edit-tags-cancel" style="display: none"
1352 type="button">Cancel</button>
1353 </form>
1354+ <a href="/+help/tag-help.html" target="help"><img style="border: 0;" src="https://edge.launchpad.net/@@/maybe" alt="help" /></a>
1355 </div>
1356 <div id="add-bug-tags"
1357 tal:attributes="class python: ('tags-container ' +
1358@@ -193,6 +194,7 @@
1359 <a href="+edit" title="Add tags" id="add-tags-trigger" class="sprite add">
1360 Add tags
1361 </a>
1362+ <a href="/+help/tag-help.html" target="help"><img style="border: 0;" src="https://edge.launchpad.net/@@/maybe" alt="help" /></a>
1363 </div>
1364
1365 <div class="clearfix"></div>
1366
1367=== added directory 'lib/lp/services/doc'
1368=== added file 'lib/lp/services/doc/sprites.txt'
1369--- lib/lp/services/doc/sprites.txt 1970-01-01 00:00:00 +0000
1370+++ lib/lp/services/doc/sprites.txt 2010-02-11 16:45:26 +0000
1371@@ -0,0 +1,185 @@
1372+Sprites
1373+=======
1374+
1375+Many small images in Launchpad are combined into a single image file,
1376+so that browsers do not have to make as many requests when they first
1377+view a page. An individual sprite is displayed as a background image
1378+on an element by setting the background position.
1379+
1380+A new image can be added to the combined file with the command:
1381+ make sprite_image
1382+
1383+The resulting icon-sprites and icon-sprites.positioning files must
1384+be committed to the repository. Any changes to the CSS template will
1385+be automatically picked up when Launchpad is restarted. If you don't
1386+want to restart launchpad.dev, you can run:
1387+ make css_combine
1388+
1389+
1390+CSS Template
1391+------------
1392+
1393+SpriteUtil takes a template css file that contains special comments
1394+indicating that the background-image for a given rule should be
1395+added to the combined image file and the rule should be updated.
1396+
1397+For example:
1398+ .add {
1399+ background-image: url(/@@/edit.png); /* sprite-ref: group1 */
1400+ }
1401+would become
1402+ .add {
1403+ background-image: url(foo/combined_image.png);
1404+ background-position: 0px 234px;
1405+ }
1406+
1407+The sprite-ref parameter specifies not only that the given image
1408+should become a sprite but also that all the images with sprite-ref
1409+value (in this case: "group1") will be combined to a single file.
1410+A SpriteUtil object currently can only process a single group.
1411+
1412+Loading the CSS Template
1413+------------------------
1414+
1415+Instatiating a new SpriteUtil object will parse the css template and
1416+find all the css rules for the specified group. The url_prefix_substitutions
1417+parameter allows you to convert url prefixes into file paths relative
1418+to the directory passed into combineImages(). The space between sprites
1419+in the file can be changed with the margin parameter.
1420+
1421+ >>> import os, tempfile
1422+ >>> import Image
1423+ >>> from lp.services.spriteutils import SpriteUtil
1424+ >>> root = os.path.abspath(os.path.join(__file__, '../../../../..'))
1425+ >>> icing = os.path.join(root, 'lib/canonical/launchpad/icing')
1426+ >>> new_png_file = tempfile.NamedTemporaryFile()
1427+ >>> new_positioning_file = tempfile.NamedTemporaryFile()
1428+ >>> new_css_file = tempfile.NamedTemporaryFile()
1429+ >>> def get_sprite_util(margin=0):
1430+ ... return SpriteUtil(
1431+ ... os.path.join(
1432+ ... root, 'lib/lp/services/tests/testfiles/template.css'),
1433+ ... 'group1',
1434+ ... url_prefix_substitutions={'/@@/': '../images/'},
1435+ ... margin=margin)
1436+ >>> sprite_util = get_sprite_util()
1437+
1438+
1439+Generate Image File
1440+-------------------
1441+
1442+The combined image will have a width equal to that of the widest image,
1443+and the height will be the sum of all the image heights, since the margin
1444+is currently zero.
1445+
1446+ >>> sprite_util.combineImages(icing)
1447+ >>> sprite_util.savePNG(new_png_file.name)
1448+ >>> image = Image.open(new_png_file.name)
1449+ >>> print image.size
1450+ (32, 87)
1451+
1452+The height will increase when the margin is increased.
1453+
1454+ >>> sprite_util = get_sprite_util(margin=100)
1455+ >>> sprite_util.combineImages(icing)
1456+ >>> sprite_util.savePNG(new_png_file.name)
1457+ >>> image = Image.open(new_png_file.name)
1458+ >>> print image.size
1459+ (32, 587)
1460+
1461+
1462+Positioning File
1463+----------------
1464+
1465+The positioning file contains locations of each sprite in the combined
1466+image file. This allows the css file to be regenerated when the template
1467+changes without requiring the combined image file to be recreated.
1468+
1469+ >>> sprite_util.savePositioning(new_positioning_file.name)
1470+ >>> print new_positioning_file.read()
1471+ /*...
1472+ {
1473+ "../images/edit.png": [
1474+ 0,
1475+ -228
1476+ ],
1477+ "../images/flame-large.png": [
1478+ 0,
1479+ -456
1480+ ],
1481+ "../images/blue-bar.png": [
1482+ 0,
1483+ -342
1484+ ],
1485+ "../images/add.png": [
1486+ 0,
1487+ -114
1488+ ]
1489+ }
1490+
1491+The positions attribute can be cleared and loaded from the file.
1492+
1493+ >>> print pretty(sprite_util.positions)
1494+ {u'../images/add.png': (0, -114),
1495+ u'../images/blue-bar.png': (0, -342),
1496+ u'../images/edit.png': (0, -228),
1497+ u'../images/flame-large.png': (0, -456)}
1498+ >>> sprite_util.positions = None
1499+ >>> sprite_util.loadPositioning(new_positioning_file.name)
1500+ >>> print pretty(sprite_util.positions)
1501+ {'../images/add.png': [0, -114],
1502+ '../images/blue-bar.png': [0, -342],
1503+ '../images/edit.png': [0, -228],
1504+ '../images/flame-large.png': [0, -456]}
1505+
1506+
1507+Generate CSS File
1508+-----------------
1509+
1510+When the css file is generated, the second parameter is the relative
1511+path from the css file to the combined image file. The .add and .foo
1512+classes have the same background-position, since they both originally
1513+referenced /@@/add.png, which was only added once to the combined file.
1514+.bar and .info do not have a background-position and the background-image
1515+is not group1.png, since its sprite-ref is "group2".
1516+
1517+ >>> sprite_util.saveConvertedCSS(new_css_file.name, 'group1.png')
1518+ >>> print new_css_file.read()
1519+ /*...
1520+ .add {
1521+ background-image: url(group1.png);
1522+ /* sprite-ref: group1 */
1523+ background-position: 0 -114px
1524+ }
1525+ .foo {
1526+ background-image: url(group1.png);
1527+ /* sprite-ref: group1 */
1528+ background-position: 0 -114px
1529+ }
1530+ .bar {
1531+ background-image: url(/@@/add.png);
1532+ /* sprite-ref: group2 */
1533+ }
1534+ .edit {
1535+ background-image: url(group1.png);
1536+ /* sprite-ref: group1 */
1537+ background-repeat: no-repeat;
1538+ background-position: 0 -228px
1539+ }
1540+ .info {
1541+ background-image: url(/@@/info.png);
1542+ /* sprite-ref: group2 */
1543+ background-repeat: no-repeat
1544+ }
1545+ .bluebar {
1546+ background-image: url(group1.png);
1547+ /* sprite-ref: group1 */
1548+ background-repeat: repeat-x;
1549+ background-position: 0 -342px
1550+ }
1551+ .large-flame {
1552+ background-image: url(group1.png);
1553+ /* sprite-ref: group1 */
1554+ background-repeat: no-repeat;
1555+ background-position: 0 -456px
1556+ }
1557
1558=== added file 'lib/lp/services/spriteutils.py'
1559--- lib/lp/services/spriteutils.py 1970-01-01 00:00:00 +0000
1560+++ lib/lp/services/spriteutils.py 2010-02-11 16:45:26 +0000
1561@@ -0,0 +1,243 @@
1562+# Copyright 2010 Canonical Ltd. This software is licensed under the
1563+# GNU Affero General Public License version 3 (see the file LICENSE).
1564+#
1565+# Derived from make_master.py by Oran Looney.
1566+# http://oranlooney.com/make-css-sprites-python-image-library/
1567+
1568+"""Library to create sprites."""
1569+
1570+from __future__ import with_statement
1571+
1572+__metaclass__ = type
1573+
1574+__all__ = [
1575+ 'SpriteUtil',
1576+ ]
1577+
1578+import os
1579+import sys
1580+import re
1581+import cssutils
1582+import Image
1583+import simplejson
1584+from textwrap import dedent
1585+
1586+
1587+class SpriteUtil:
1588+ EDIT_WARNING = dedent("""\
1589+ /* DO NOT EDIT THIS FILE BY HAND!!! */
1590+ /* It is autogenerated by spriteutils. */
1591+ """)
1592+
1593+ def __init__(self, css_template_file, group_name,
1594+ url_prefix_substitutions=None, margin=150):
1595+ """Initialize with the specified css template file.
1596+
1597+ :param css_template_file: (str) Name of the file containing
1598+ css rules with a background-image style that needs to be
1599+ combined into the sprite file, a comment allowing sprites to
1600+ be grouped into different image files, and a
1601+ background-repeat style if necessary. Currently, "repeat-y"
1602+ is not supported since the file is combined vertically, so
1603+ repeat-y would show the entire combined image file.
1604+
1605+ Example css template:
1606+ edit-icon {
1607+ background-image: url(../edit.png)
1608+ /* sprite-ref: group1 */
1609+ }
1610+ blue-bar {
1611+ background-image: url(../blue-bar.png)
1612+ /* sprite-ref: group1 */
1613+ background-repeat: repeat-x
1614+ }
1615+
1616+ :param group_name: (str) Only add sprites to the
1617+ combined image file whose sprite-ref comment in the
1618+ css template match this group-name.
1619+
1620+ :param url_prefix_substitutions: (dict) The css template
1621+ will contain references to image files by their url
1622+ path, but the filesystem path relative to the css
1623+ template is needed.
1624+
1625+ :param margin: (int) The number of pixels between each sprite.
1626+ Be aware that Firefox will ignore extremely large images,
1627+ for example 64x34000 pixels.
1628+
1629+ If the css_template_file has been modified, a new
1630+ css file using an existing combined image and positioning
1631+ file can be generated using:
1632+ sprite_util = SpriteUtil(...)
1633+ sprite_util.loadPositioning(...)
1634+ sprite_util.saveConvertedCSS(...)
1635+
1636+ If a new image file needs to be added to the combined image
1637+ and the positioning file, they can be regenerated with:
1638+ sprite_util = SpriteUtil(...)
1639+ sprite_util.combineImages(...)
1640+ sprite_util.savePNG(...)
1641+ sprite_util.savePositioning(...)
1642+
1643+ If the image file is regenerated any time the css file is
1644+ regenerated, then the step for saving and loading the positioning
1645+ information could be removed. For example:
1646+ sprite_util = SpriteUtil(...)
1647+ sprite_util.combineImages(...)
1648+ sprite_util.savePNG(...)
1649+ sprite_util.saveConvertedCSS(...)
1650+ """
1651+ self.combined_image = None
1652+ self.positions = None
1653+ self.group_name = group_name
1654+ self.margin = margin
1655+ self._loadCSSTemplate(
1656+ css_template_file, group_name, url_prefix_substitutions)
1657+
1658+ def _loadCSSTemplate(self, css_template_file, group_name,
1659+ url_prefix_substitutions=None):
1660+ """See `__init__`."""
1661+ smartsprites_exp = re.compile(
1662+ r'/\*+([^*]*sprite-ref: [^*]*)\*/')
1663+ self.css_object = cssutils.parseFile(css_template_file)
1664+ self.sprite_info = []
1665+ for rule in self.css_object:
1666+ if rule.cssText is None:
1667+ continue
1668+ match = smartsprites_exp.search(rule.cssText)
1669+ if match is not None:
1670+ smartsprites_info = match.group(1)
1671+ parameters = self._parseCommentParameters(match.group(1))
1672+ # Currently, only a single combined image is supported.
1673+ if parameters['sprite-ref'] == group_name:
1674+ filename = self._getSpriteImagePath(
1675+ rule, url_prefix_substitutions)
1676+ self.sprite_info.append(
1677+ dict(filename=filename, rule=rule))
1678+
1679+ if len(self.sprite_info) == 0:
1680+ raise AssertionError(
1681+ "No sprite-ref comments for group %r found" % group_name)
1682+
1683+ def _getSpriteImagePath(self, rule, url_prefix_substitutions=None):
1684+ """Convert the url path to a filesystem path."""
1685+ # Remove url() from string.
1686+ filename = rule.style.backgroundImage[4:-1]
1687+ # Convert urls to paths relative to the css
1688+ # file, e.g. '/@@/foo.png' => '../images/foo.png'.
1689+ if url_prefix_substitutions is not None:
1690+ for old, new in url_prefix_substitutions.items():
1691+ if filename.startswith(old):
1692+ filename = new + filename[len(old):]
1693+ return filename
1694+
1695+ def _parseCommentParameters(self, parameter_string):
1696+ """Parse parameters out of javascript comments.
1697+
1698+ Currently only used for the group name specified
1699+ by "sprite-ref".
1700+ """
1701+ results = {}
1702+ for parameter in parameter_string.split(';'):
1703+ if parameter.strip() != '':
1704+ name, value = parameter.split(':')
1705+ name = name.strip()
1706+ value = value.strip()
1707+ results[name] = value
1708+ return results
1709+
1710+ def combineImages(self, css_dir):
1711+ """Copy all the sprites into a single PIL image."""
1712+
1713+ # Open all the sprite images.
1714+ sprite_images = {}
1715+ max_sprite_width = 0
1716+ total_sprite_height = 0
1717+ for sprite in self.sprite_info:
1718+ abs_filename = os.path.join(css_dir, sprite['filename'])
1719+ sprite_images[sprite['filename']] = Image.open(abs_filename)
1720+ width, height = sprite_images[sprite['filename']].size
1721+ max_sprite_width = max(width, max_sprite_width)
1722+ total_sprite_height += height
1723+
1724+ # The combined image is the height of all the sprites
1725+ # plus the margin between each of them.
1726+ combined_image_height = (
1727+ total_sprite_height + (self.margin * len(self.sprite_info) - 1))
1728+ transparent = (0, 0, 0, 0)
1729+ combined_image = Image.new(
1730+ mode='RGBA',
1731+ size=(max_sprite_width, combined_image_height),
1732+ color=transparent)
1733+
1734+ # Paste each sprite into the combined image.
1735+ y = 0
1736+ positions = {}
1737+ for index, sprite in enumerate(self.sprite_info):
1738+ sprite_image = sprite_images[sprite['filename']]
1739+ try:
1740+ position = [0, y]
1741+ combined_image.paste(sprite_image, tuple(position))
1742+ # An icon in a vertically combined image can be repeated
1743+ # horizontally, but we have to repeat it in the combined
1744+ # image so that we don't repeat white space.
1745+ if sprite['rule'].style.backgroundRepeat == 'repeat-x':
1746+ width = sprite_image.size[0]
1747+ for x_position in range(width, max_sprite_width, width):
1748+ position[0] = x_position
1749+ combined_image.paste(sprite_image, tuple(position))
1750+ except:
1751+ print >> sys.stderr, (
1752+ "Error with image file %s" % sprite['filename'])
1753+ raise
1754+ # This is the position of the combined image on an HTML
1755+ # element. Therefore, it subtracts the position of the
1756+ # sprite in the file to move it to the top of the element.
1757+ positions[sprite['filename']] = (0, -y)
1758+ y += sprite_image.size[1] + self.margin
1759+
1760+ # If there is an exception earlier, these attributes will remain None.
1761+ self.positions = positions
1762+ self.combined_image = combined_image
1763+
1764+ def savePNG(self, filename):
1765+ """Save the PIL image object to disk."""
1766+ self.combined_image.save(filename, format='png', optimize=True)
1767+
1768+ def savePositioning(self, filename):
1769+ """Save the positions of sprites in the combined image.
1770+
1771+ This allows the final css to be generated after making
1772+ changes to the css template without recreating the combined
1773+ image file.
1774+ """
1775+ fp = open(filename, 'w')
1776+ fp.write(self.EDIT_WARNING)
1777+ simplejson.dump(self.positions, fp=fp, indent=4)
1778+
1779+ def loadPositioning(self, filename):
1780+ """Load file with the positions of sprites in the combined image."""
1781+ json = open(filename).read()
1782+ # Remove comments from the beginning of the file.
1783+ start = json.index('{')
1784+ json = json[start:]
1785+ self.positions = simplejson.loads(json)
1786+
1787+ def saveConvertedCSS(self, css_file, combined_image_url_path):
1788+ """Generate new css from the template and the positioning info.
1789+
1790+ Example css template:
1791+ background-image: url(../edit.png); /* sprite-ref: group1 */
1792+ Example css output:
1793+ background-image: url(combined_image_url_path)
1794+ background-position: 0px 2344px
1795+ """
1796+ for sprite in self.sprite_info:
1797+ rule = sprite['rule']
1798+ rule.style.backgroundImage = 'url(%s)' % combined_image_url_path
1799+ position = self.positions[sprite['filename']]
1800+ rule.style.backgroundPosition = '%dpx %dpx' % tuple(position)
1801+
1802+ with open(css_file, 'w') as fp:
1803+ fp.write(self.EDIT_WARNING)
1804+ fp.write(self.css_object.cssText)
1805
1806=== added file 'lib/lp/services/tests/test_doc.py'
1807--- lib/lp/services/tests/test_doc.py 1970-01-01 00:00:00 +0000
1808+++ lib/lp/services/tests/test_doc.py 2010-02-11 16:45:26 +0000
1809@@ -0,0 +1,17 @@
1810+# Copyright 2010 Canonical Ltd. This software is licensed under the
1811+# GNU Affero General Public License version 3 (see the file LICENSE).
1812+
1813+"""
1814+Run the doctests and pagetests.
1815+"""
1816+
1817+import os
1818+
1819+from lp.services.testing import build_test_suite
1820+
1821+
1822+here = os.path.dirname(os.path.realpath(__file__))
1823+
1824+
1825+def test_suite():
1826+ return build_test_suite(here)
1827
1828=== added directory 'lib/lp/services/tests/testfiles'
1829=== added file 'lib/lp/services/tests/testfiles/template.css'
1830--- lib/lp/services/tests/testfiles/template.css 1970-01-01 00:00:00 +0000
1831+++ lib/lp/services/tests/testfiles/template.css 2010-02-11 16:45:26 +0000
1832@@ -0,0 +1,20 @@
1833+.add { background-image: url(/@@/add.png); /* sprite-ref: group1 */ }
1834+.foo { background-image: url(/@@/add.png); /* sprite-ref: group1 */ }
1835+.bar { background-image: url(/@@/add.png); /* sprite-ref: group2 */ }
1836+.edit {
1837+ background-image: url(/@@/edit.png);
1838+ /* sprite-ref: group1 */
1839+ background-repeat: no-repeat;
1840+}
1841+.info {
1842+ background-image: url(/@@/info.png); /* sprite-ref: group2 */
1843+ background-repeat: no-repeat;
1844+}
1845+.bluebar {
1846+ background-image: url(/@@/blue-bar.png); /* sprite-ref: group1 */
1847+ background-repeat: repeat-x;
1848+}
1849+.large-flame {
1850+ background-image: url(/@@/flame-large.png); /* sprite-ref: group1 */
1851+ background-repeat: no-repeat;
1852+}

Subscribers

People subscribed via source and target branches

to status/vote changes: