Merge lp:~edwin-grubbs/launchpad/auto-gen-sprites into lp:launchpad/db-devel

Proposed by Edwin Grubbs
Status: Merged
Approved by: Brad Crittenden
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~edwin-grubbs/launchpad/auto-gen-sprites
Merge into: lp:launchpad/db-devel
Diff against target: 1796 lines (+1493/-146)
12 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/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:~edwin-grubbs/launchpad/auto-gen-sprites
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+18903@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Summary
-------

Added the ability to easily generate the sprite image file
and the css using it. The immediate benefit is to increase the
margin between the images, so that elements that wrap to multiple
lines will not show the next sprite in the file.

Implementation details
----------------------

Added sprite_image and sprite_css targets. sprite_css is a dependency
of css_combine, so it is automatically built for "make run" unless
build/style-3-0.css already exists.
    Makefile

Added bin/sprite-util to support the Makefile targets.
    buildout-templates/bin/sprite-util.in

The SpriteUtil class:
    lib/lp/services/spriteutils.py

The icing/style-3-0.css file was moved to the icing/build/
directory, since it is now autogenerated, and this will
help prevent people from opening and editing it instead
of sprite-3-0.css.in.
    buildout-templates/bin/combine-css.in

"make sprite_css" takes this file and icon-sprites.positioning
as inputs to generate icing/build/style-3-0.css.
    lib/canonical/launchpad/icing/style-3-0.css.in

These two files are automatically generated by "make sprite_image".
    lib/canonical/launchpad/icing/icon-sprites
    lib/canonical/launchpad/icing/icon-sprites.positioning

Testing:
    lib/lp/services/doc/sprites.txt
    lib/lp/services/tests/test_doc.py
    lib/lp/services/tests/testfiles/template.css

Since the margin has increased, more results can be shown
in the picker.
    lib/canonical/launchpad/browser/vocabulary.py

The search field at the bottom of all the launchpad pages had
an search sprite stuck at the end of the form element. It wasn't
clickable, and the css for ".footer .lp-arcana .search-icon"
seemed unnecesarily specific to that usage.
    lib/lp/app/templates/base-layout-macros.pt

Tests
-----

./bin/test -vv -t sprites

Demo and Q/A
------------

* Open http://launchpad.dev/bazaar
    * Check that all the sprites look right.
    * Check that you can click on the search icon in the footer.

Revision history for this message
Brad Crittenden (bac) wrote :
Download full text (23.0 KiB)

Hi Edwin,

This new system looks very well thought out. I've got a few
suggestions but it should be landable quickly.

Also note that the MP is targeted to db-devel, which messed up your diff. I assume you do plan to land against devel, so be careful.

--Brad

> === modified file 'Makefile'
> --- Makefile 2010-01-29 16:03:04 +0000
+++ Makefile 2010-02-09 17:18:07 +0000
> @@ -18,7 +18,8 @@
> LPCONFIG=development
>
> JSFLAGS=
> -LP_BUILT_JS_ROOT=lib/canonical/launchpad/icing/build
> +ICING=lib/canonical/launchpad/icing
> +LP_BUILT_JS_ROOT=${ICING}/build
> LAZR_BUILT_JS_ROOT=lazr-js/build
>
> MINS_TO_SHUTDOWN=15
> @@ -111,9 +112,19 @@
>
> build: $(BZR_VERSION_INFO) compile apidoc jsbuild css_combine
>
> -css_combine:
> +css_combine: sprite_css
> ${SHHH} bin/combine-css
>
> +sprite_css: ${LP_BUILT_JS_ROOT}/style-3-0.css
> +
> +${LP_BUILT_JS_ROOT}/style-3-0.css: ${ICING}/style-3-0.css.in ${ICING}/icon-sprites.positioning
> + ${SHHH} bin/sprite-util create-css
> +
> +${ICING}/icon-sprites:
> + ${SHHH} bin/sprite-util create-image
> +
> +sprite_image: ${ICING}/icon-sprites

This should have a dependency rule so it isn't run every time or it
should not be part of the 'make build' chain if it is just to be run
on demand.

> +
> jsbuild_lazr:
> # We absolutely do not want to include the lazr.testing module and its
> # jsTestDriver test harness modifications in the lazr.js and launchpad.js

> === added file 'buildout-templates/bin/sprite-util.in'
> --- buildout-templates/bin/sprite-util.in 1970-01-01 00:00:00 +0000
> +++ buildout-templates/bin/sprite-util.in 2010-02-09 16:59:33 +0000
> @@ -0,0 +1,41 @@
> +#! /usr/bin/env ${buildout:directory}/bin/py

Why the space between ! and /? I know you're following the pattern
but it's ugly.

> +
> +import os
> +import sys
> +
> +from lp.services.spriteutils import SpriteUtil
> +
> +command_options = ('create-image', 'create-css')
> +
> +if len(sys.argv) != 2:
> + print >> sys.stderr, "Expected a single argument."
> + print >> sys.stderr, " Usage: %s %s" % (
> + sys.argv[0], '|'.join(command_options))
> + sys.exit(1)
> +else:
> + command = sys.argv[1]
> + if command not in command_options:
> + print >> sys.stderr, "Unknown argument: %s" % command
> + print >> sys.stderr, " Usage: %s %s" % (
> + sys.argv[0], '|'.join(command_options))
> + sys.exit(2)

Refactor a usage() method.

> +
> +root = '${buildout:directory}'
> +icing = os.path.join(root, 'lib/canonical/launchpad/icing')
> +combined_image_file = os.path.join(icing, 'icon-sprites')
> +positioning_file = os.path.join(icing, 'icon-sprites.positioning')
> +css_template_file = os.path.join(icing, 'style-3-0.css.in')
> +css_file = os.path.join(icing, 'build/style-3-0.css')

I'm concerned that the paths are here and in the Makefile. Could the
makefile pass it in?

> === renamed file 'lib/canonical/launchpad/icing/style-3-0.css' => 'lib/canonical/launchpad/icing/style-3-0.css.in'
> --- lib/canonical/launchpad/icing/style-3-0.css 2010-02-05 09:48:47 +0000
> +++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-02-09 16:59:33 +0000

Were these changes don...

review: Approve (code)
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (29.1 KiB)

> Hi Edwin,
>
> This new system looks very well thought out. I've got a few
> suggestions but it should be landable quickly.
>
> Also note that the MP is targeted to db-devel, which messed up your diff. I
> assume you do plan to land against devel, so be careful.
>
> --Brad

Hi Brad,

Thanks for the review. Sorry about the bad diff.

>
> > === modified file 'Makefile'
> > --- Makefile 2010-01-29 16:03:04 +0000
> +++ Makefile 2010-02-09 17:18:07 +0000
> > @@ -18,7 +18,8 @@
> > LPCONFIG=development
> >
> > JSFLAGS=
> > -LP_BUILT_JS_ROOT=lib/canonical/launchpad/icing/build
> > +ICING=lib/canonical/launchpad/icing
> > +LP_BUILT_JS_ROOT=${ICING}/build
> > LAZR_BUILT_JS_ROOT=lazr-js/build
> >
> > MINS_TO_SHUTDOWN=15
> > @@ -111,9 +112,19 @@
> >
> > build: $(BZR_VERSION_INFO) compile apidoc jsbuild css_combine
> >
> > -css_combine:
> > +css_combine: sprite_css
> > ${SHHH} bin/combine-css
> >
> > +sprite_css: ${LP_BUILT_JS_ROOT}/style-3-0.css
> > +
> > +${LP_BUILT_JS_ROOT}/style-3-0.css: ${ICING}/style-3-0.css.in ${ICING}/icon-
> sprites.positioning
> > + ${SHHH} bin/sprite-util create-css
> > +
> > +${ICING}/icon-sprites:
> > + ${SHHH} bin/sprite-util create-image
> > +
> > +sprite_image: ${ICING}/icon-sprites
>
> This should have a dependency rule so it isn't run every time or it
> should not be part of the 'make build' chain if it is just to be run
> on demand.

sprite_css is a dependency of css_combine, but sprite_image is not a dependency
of anything, so it is only run manually when a new image needs to be added.

> > +
> > jsbuild_lazr:
> > # We absolutely do not want to include the lazr.testing module and its
> > # jsTestDriver test harness modifications in the lazr.js and
> launchpad.js
>
> > === added file 'buildout-templates/bin/sprite-util.in'
> > --- buildout-templates/bin/sprite-util.in 1970-01-01 00:00:00 +0000
> > +++ buildout-templates/bin/sprite-util.in 2010-02-09 16:59:33 +0000
> > @@ -0,0 +1,41 @@
> > +#! /usr/bin/env ${buildout:directory}/bin/py
>
> Why the space between ! and /? I know you're following the pattern
> but it's ugly.

Yeah, I just copied another file and never looked at that. Fixed.

> > +
> > +import os
> > +import sys
> > +
> > +from lp.services.spriteutils import SpriteUtil
> > +
> > +command_options = ('create-image', 'create-css')
> > +
> > +if len(sys.argv) != 2:
> > + print >> sys.stderr, "Expected a single argument."
> > + print >> sys.stderr, " Usage: %s %s" % (
> > + sys.argv[0], '|'.join(command_options))
> > + sys.exit(1)
> > +else:
> > + command = sys.argv[1]
> > + if command not in command_options:
> > + print >> sys.stderr, "Unknown argument: %s" % command
> > + print >> sys.stderr, " Usage: %s %s" % (
> > + sys.argv[0], '|'.join(command_options))
> > + sys.exit(2)
>
> Refactor a usage() method.

Fixed.

> > +
> > +root = '${buildout:directory}'
> > +icing = os.path.join(root, 'lib/canonical/launchpad/icing')
> > +combined_image_file = os.path.join(icing, 'icon-sprites')
> > +positioning_file = os.path.join(icing, 'icon-sprites.positioning')
> > +css_template_file = os.path.join(icing, '...

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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 05:00:37 +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 directory 'lib/lp/services/doc'
1312=== added file 'lib/lp/services/doc/sprites.txt'
1313--- lib/lp/services/doc/sprites.txt 1970-01-01 00:00:00 +0000
1314+++ lib/lp/services/doc/sprites.txt 2010-02-11 05:00:37 +0000
1315@@ -0,0 +1,185 @@
1316+Sprites
1317+=======
1318+
1319+Many small images in Launchpad are combined into a single image file,
1320+so that browsers do not have to make as many requests when they first
1321+view a page. An individual sprite is displayed as a background image
1322+on an element by setting the background position.
1323+
1324+A new image can be added to the combined file with the command:
1325+ make sprite_image
1326+
1327+The resulting icon-sprites and icon-sprites.positioning files must
1328+be committed to the repository. Any changes to the CSS template will
1329+be automatically picked up when Launchpad is restarted. If you don't
1330+want to restart launchpad.dev, you can run:
1331+ make css_combine
1332+
1333+
1334+CSS Template
1335+------------
1336+
1337+SpriteUtil takes a template css file that contains special comments
1338+indicating that the background-image for a given rule should be
1339+added to the combined image file and the rule should be updated.
1340+
1341+For example:
1342+ .add {
1343+ background-image: url(/@@/edit.png); /* sprite-ref: group1 */
1344+ }
1345+would become
1346+ .add {
1347+ background-image: url(foo/combined_image.png);
1348+ background-position: 0px 234px;
1349+ }
1350+
1351+The sprite-ref parameter specifies not only that the given image
1352+should become a sprite but also that all the images with sprite-ref
1353+value (in this case: "group1") will be combined to a single file.
1354+A SpriteUtil object currently can only process a single group.
1355+
1356+Loading the CSS Template
1357+------------------------
1358+
1359+Instatiating a new SpriteUtil object will parse the css template and
1360+find all the css rules for the specified group. The url_prefix_substitutions
1361+parameter allows you to convert url prefixes into file paths relative
1362+to the directory passed into combineImages(). The space between sprites
1363+in the file can be changed with the margin parameter.
1364+
1365+ >>> import os, tempfile
1366+ >>> import Image
1367+ >>> from lp.services.spriteutils import SpriteUtil
1368+ >>> root = os.path.abspath(os.path.join(__file__, '../../../../..'))
1369+ >>> icing = os.path.join(root, 'lib/canonical/launchpad/icing')
1370+ >>> new_png_file = tempfile.NamedTemporaryFile()
1371+ >>> new_positioning_file = tempfile.NamedTemporaryFile()
1372+ >>> new_css_file = tempfile.NamedTemporaryFile()
1373+ >>> def get_sprite_util(margin=0):
1374+ ... return SpriteUtil(
1375+ ... os.path.join(
1376+ ... root, 'lib/lp/services/tests/testfiles/template.css'),
1377+ ... 'group1',
1378+ ... url_prefix_substitutions={'/@@/': '../images/'},
1379+ ... margin=margin)
1380+ >>> sprite_util = get_sprite_util()
1381+
1382+
1383+Generate Image File
1384+-------------------
1385+
1386+The combined image will have a width equal to that of the widest image,
1387+and the height will be the sum of all the image heights, since the margin
1388+is currently zero.
1389+
1390+ >>> sprite_util.combineImages(icing)
1391+ >>> sprite_util.savePNG(new_png_file.name)
1392+ >>> image = Image.open(new_png_file.name)
1393+ >>> print image.size
1394+ (32, 87)
1395+
1396+The height will increase when the margin is increased.
1397+
1398+ >>> sprite_util = get_sprite_util(margin=100)
1399+ >>> sprite_util.combineImages(icing)
1400+ >>> sprite_util.savePNG(new_png_file.name)
1401+ >>> image = Image.open(new_png_file.name)
1402+ >>> print image.size
1403+ (32, 587)
1404+
1405+
1406+Positioning File
1407+----------------
1408+
1409+The positioning file contains locations of each sprite in the combined
1410+image file. This allows the css file to be regenerated when the template
1411+changes without requiring the combined image file to be recreated.
1412+
1413+ >>> sprite_util.savePositioning(new_positioning_file.name)
1414+ >>> print new_positioning_file.read()
1415+ /*...
1416+ {
1417+ "../images/edit.png": [
1418+ 0,
1419+ -228
1420+ ],
1421+ "../images/flame-large.png": [
1422+ 0,
1423+ -456
1424+ ],
1425+ "../images/blue-bar.png": [
1426+ 0,
1427+ -342
1428+ ],
1429+ "../images/add.png": [
1430+ 0,
1431+ -114
1432+ ]
1433+ }
1434+
1435+The positions attribute can be cleared and loaded from the file.
1436+
1437+ >>> print pretty(sprite_util.positions)
1438+ {u'../images/add.png': (0, -114),
1439+ u'../images/blue-bar.png': (0, -342),
1440+ u'../images/edit.png': (0, -228),
1441+ u'../images/flame-large.png': (0, -456)}
1442+ >>> sprite_util.positions = None
1443+ >>> sprite_util.loadPositioning(new_positioning_file.name)
1444+ >>> print pretty(sprite_util.positions)
1445+ {'../images/add.png': [0, -114],
1446+ '../images/blue-bar.png': [0, -342],
1447+ '../images/edit.png': [0, -228],
1448+ '../images/flame-large.png': [0, -456]}
1449+
1450+
1451+Generate CSS File
1452+-----------------
1453+
1454+When the css file is generated, the second parameter is the relative
1455+path from the css file to the combined image file. The .add and .foo
1456+classes have the same background-position, since they both originally
1457+referenced /@@/add.png, which was only added once to the combined file.
1458+.bar and .info do not have a background-position and the background-image
1459+is not group1.png, since its sprite-ref is "group2".
1460+
1461+ >>> sprite_util.saveConvertedCSS(new_css_file.name, 'group1.png')
1462+ >>> print new_css_file.read()
1463+ /*...
1464+ .add {
1465+ background-image: url(group1.png);
1466+ /* sprite-ref: group1 */
1467+ background-position: 0 -114px
1468+ }
1469+ .foo {
1470+ background-image: url(group1.png);
1471+ /* sprite-ref: group1 */
1472+ background-position: 0 -114px
1473+ }
1474+ .bar {
1475+ background-image: url(/@@/add.png);
1476+ /* sprite-ref: group2 */
1477+ }
1478+ .edit {
1479+ background-image: url(group1.png);
1480+ /* sprite-ref: group1 */
1481+ background-repeat: no-repeat;
1482+ background-position: 0 -228px
1483+ }
1484+ .info {
1485+ background-image: url(/@@/info.png);
1486+ /* sprite-ref: group2 */
1487+ background-repeat: no-repeat
1488+ }
1489+ .bluebar {
1490+ background-image: url(group1.png);
1491+ /* sprite-ref: group1 */
1492+ background-repeat: repeat-x;
1493+ background-position: 0 -342px
1494+ }
1495+ .large-flame {
1496+ background-image: url(group1.png);
1497+ /* sprite-ref: group1 */
1498+ background-repeat: no-repeat;
1499+ background-position: 0 -456px
1500+ }
1501
1502=== added file 'lib/lp/services/spriteutils.py'
1503--- lib/lp/services/spriteutils.py 1970-01-01 00:00:00 +0000
1504+++ lib/lp/services/spriteutils.py 2010-02-11 05:00:37 +0000
1505@@ -0,0 +1,243 @@
1506+# Copyright 2010 Canonical Ltd. This software is licensed under the
1507+# GNU Affero General Public License version 3 (see the file LICENSE).
1508+#
1509+# Derived from make_master.py by Oran Looney.
1510+# http://oranlooney.com/make-css-sprites-python-image-library/
1511+
1512+"""Library to create sprites."""
1513+
1514+from __future__ import with_statement
1515+
1516+__metaclass__ = type
1517+
1518+__all__ = [
1519+ 'SpriteUtil',
1520+ ]
1521+
1522+import os
1523+import sys
1524+import re
1525+import cssutils
1526+import Image
1527+import simplejson
1528+from textwrap import dedent
1529+
1530+
1531+class SpriteUtil:
1532+ EDIT_WARNING = dedent("""\
1533+ /* DO NOT EDIT THIS FILE BY HAND!!! */
1534+ /* It is autogenerated by spriteutils. */
1535+ """)
1536+
1537+ def __init__(self, css_template_file, group_name,
1538+ url_prefix_substitutions=None, margin=150):
1539+ """Initialize with the specified css template file.
1540+
1541+ :param css_template_file: (str) Name of the file containing
1542+ css rules with a background-image style that needs to be
1543+ combined into the sprite file, a comment allowing sprites to
1544+ be grouped into different image files, and a
1545+ background-repeat style if necessary. Currently, "repeat-y"
1546+ is not supported since the file is combined vertically, so
1547+ repeat-y would show the entire combined image file.
1548+
1549+ Example css template:
1550+ edit-icon {
1551+ background-image: url(../edit.png)
1552+ /* sprite-ref: group1 */
1553+ }
1554+ blue-bar {
1555+ background-image: url(../blue-bar.png)
1556+ /* sprite-ref: group1 */
1557+ background-repeat: repeat-x
1558+ }
1559+
1560+ :param group_name: (str) Only add sprites to the
1561+ combined image file whose sprite-ref comment in the
1562+ css template match this group-name.
1563+
1564+ :param url_prefix_substitutions: (dict) The css template
1565+ will contain references to image files by their url
1566+ path, but the filesystem path relative to the css
1567+ template is needed.
1568+
1569+ :param margin: (int) The number of pixels between each sprite.
1570+ Be aware that Firefox will ignore extremely large images,
1571+ for example 64x34000 pixels.
1572+
1573+ If the css_template_file has been modified, a new
1574+ css file using an existing combined image and positioning
1575+ file can be generated using:
1576+ sprite_util = SpriteUtil(...)
1577+ sprite_util.loadPositioning(...)
1578+ sprite_util.saveConvertedCSS(...)
1579+
1580+ If a new image file needs to be added to the combined image
1581+ and the positioning file, they can be regenerated with:
1582+ sprite_util = SpriteUtil(...)
1583+ sprite_util.combineImages(...)
1584+ sprite_util.savePNG(...)
1585+ sprite_util.savePositioning(...)
1586+
1587+ If the image file is regenerated any time the css file is
1588+ regenerated, then the step for saving and loading the positioning
1589+ information could be removed. For example:
1590+ sprite_util = SpriteUtil(...)
1591+ sprite_util.combineImages(...)
1592+ sprite_util.savePNG(...)
1593+ sprite_util.saveConvertedCSS(...)
1594+ """
1595+ self.combined_image = None
1596+ self.positions = None
1597+ self.group_name = group_name
1598+ self.margin = margin
1599+ self._loadCSSTemplate(
1600+ css_template_file, group_name, url_prefix_substitutions)
1601+
1602+ def _loadCSSTemplate(self, css_template_file, group_name,
1603+ url_prefix_substitutions=None):
1604+ """See `__init__`."""
1605+ smartsprites_exp = re.compile(
1606+ r'/\*+([^*]*sprite-ref: [^*]*)\*/')
1607+ self.css_object = cssutils.parseFile(css_template_file)
1608+ self.sprite_info = []
1609+ for rule in self.css_object:
1610+ if rule.cssText is None:
1611+ continue
1612+ match = smartsprites_exp.search(rule.cssText)
1613+ if match is not None:
1614+ smartsprites_info = match.group(1)
1615+ parameters = self._parseCommentParameters(match.group(1))
1616+ # Currently, only a single combined image is supported.
1617+ if parameters['sprite-ref'] == group_name:
1618+ filename = self._getSpriteImagePath(
1619+ rule, url_prefix_substitutions)
1620+ self.sprite_info.append(
1621+ dict(filename=filename, rule=rule))
1622+
1623+ if len(self.sprite_info) == 0:
1624+ raise AssertionError(
1625+ "No sprite-ref comments for group %r found" % group_name)
1626+
1627+ def _getSpriteImagePath(self, rule, url_prefix_substitutions=None):
1628+ """Convert the url path to a filesystem path."""
1629+ # Remove url() from string.
1630+ filename = rule.style.backgroundImage[4:-1]
1631+ # Convert urls to paths relative to the css
1632+ # file, e.g. '/@@/foo.png' => '../images/foo.png'.
1633+ if url_prefix_substitutions is not None:
1634+ for old, new in url_prefix_substitutions.items():
1635+ if filename.startswith(old):
1636+ filename = new + filename[len(old):]
1637+ return filename
1638+
1639+ def _parseCommentParameters(self, parameter_string):
1640+ """Parse parameters out of javascript comments.
1641+
1642+ Currently only used for the group name specified
1643+ by "sprite-ref".
1644+ """
1645+ results = {}
1646+ for parameter in parameter_string.split(';'):
1647+ if parameter.strip() != '':
1648+ name, value = parameter.split(':')
1649+ name = name.strip()
1650+ value = value.strip()
1651+ results[name] = value
1652+ return results
1653+
1654+ def combineImages(self, css_dir):
1655+ """Copy all the sprites into a single PIL image."""
1656+
1657+ # Open all the sprite images.
1658+ sprite_images = {}
1659+ max_sprite_width = 0
1660+ total_sprite_height = 0
1661+ for sprite in self.sprite_info:
1662+ abs_filename = os.path.join(css_dir, sprite['filename'])
1663+ sprite_images[sprite['filename']] = Image.open(abs_filename)
1664+ width, height = sprite_images[sprite['filename']].size
1665+ max_sprite_width = max(width, max_sprite_width)
1666+ total_sprite_height += height
1667+
1668+ # The combined image is the height of all the sprites
1669+ # plus the margin between each of them.
1670+ combined_image_height = (
1671+ total_sprite_height + (self.margin * len(self.sprite_info) - 1))
1672+ transparent = (0, 0, 0, 0)
1673+ combined_image = Image.new(
1674+ mode='RGBA',
1675+ size=(max_sprite_width, combined_image_height),
1676+ color=transparent)
1677+
1678+ # Paste each sprite into the combined image.
1679+ y = 0
1680+ positions = {}
1681+ for index, sprite in enumerate(self.sprite_info):
1682+ sprite_image = sprite_images[sprite['filename']]
1683+ try:
1684+ position = [0, y]
1685+ combined_image.paste(sprite_image, tuple(position))
1686+ # An icon in a vertically combined image can be repeated
1687+ # horizontally, but we have to repeat it in the combined
1688+ # image so that we don't repeat white space.
1689+ if sprite['rule'].style.backgroundRepeat == 'repeat-x':
1690+ width = sprite_image.size[0]
1691+ for x_position in range(width, max_sprite_width, width):
1692+ position[0] = x_position
1693+ combined_image.paste(sprite_image, tuple(position))
1694+ except:
1695+ print >> sys.stderr, (
1696+ "Error with image file %s" % sprite['filename'])
1697+ raise
1698+ # This is the position of the combined image on an HTML
1699+ # element. Therefore, it subtracts the position of the
1700+ # sprite in the file to move it to the top of the element.
1701+ positions[sprite['filename']] = (0, -y)
1702+ y += sprite_image.size[1] + self.margin
1703+
1704+ # If there is an exception earlier, these attributes will remain None.
1705+ self.positions = positions
1706+ self.combined_image = combined_image
1707+
1708+ def savePNG(self, filename):
1709+ """Save the PIL image object to disk."""
1710+ self.combined_image.save(filename, format='png', optimize=True)
1711+
1712+ def savePositioning(self, filename):
1713+ """Save the positions of sprites in the combined image.
1714+
1715+ This allows the final css to be generated after making
1716+ changes to the css template without recreating the combined
1717+ image file.
1718+ """
1719+ fp = open(filename, 'w')
1720+ fp.write(self.EDIT_WARNING)
1721+ simplejson.dump(self.positions, fp=fp, indent=4)
1722+
1723+ def loadPositioning(self, filename):
1724+ """Load file with the positions of sprites in the combined image."""
1725+ json = open(filename).read()
1726+ # Remove comments from the beginning of the file.
1727+ start = json.index('{')
1728+ json = json[start:]
1729+ self.positions = simplejson.loads(json)
1730+
1731+ def saveConvertedCSS(self, css_file, combined_image_url_path):
1732+ """Generate new css from the template and the positioning info.
1733+
1734+ Example css template:
1735+ background-image: url(../edit.png); /* sprite-ref: group1 */
1736+ Example css output:
1737+ background-image: url(combined_image_url_path)
1738+ background-position: 0px 2344px
1739+ """
1740+ for sprite in self.sprite_info:
1741+ rule = sprite['rule']
1742+ rule.style.backgroundImage = 'url(%s)' % combined_image_url_path
1743+ position = self.positions[sprite['filename']]
1744+ rule.style.backgroundPosition = '%dpx %dpx' % tuple(position)
1745+
1746+ with open(css_file, 'w') as fp:
1747+ fp.write(self.EDIT_WARNING)
1748+ fp.write(self.css_object.cssText)
1749
1750=== added file 'lib/lp/services/tests/test_doc.py'
1751--- lib/lp/services/tests/test_doc.py 1970-01-01 00:00:00 +0000
1752+++ lib/lp/services/tests/test_doc.py 2010-02-11 05:00:37 +0000
1753@@ -0,0 +1,17 @@
1754+# Copyright 2010 Canonical Ltd. This software is licensed under the
1755+# GNU Affero General Public License version 3 (see the file LICENSE).
1756+
1757+"""
1758+Run the doctests and pagetests.
1759+"""
1760+
1761+import os
1762+
1763+from lp.services.testing import build_test_suite
1764+
1765+
1766+here = os.path.dirname(os.path.realpath(__file__))
1767+
1768+
1769+def test_suite():
1770+ return build_test_suite(here)
1771
1772=== added directory 'lib/lp/services/tests/testfiles'
1773=== added file 'lib/lp/services/tests/testfiles/template.css'
1774--- lib/lp/services/tests/testfiles/template.css 1970-01-01 00:00:00 +0000
1775+++ lib/lp/services/tests/testfiles/template.css 2010-02-11 05:00:37 +0000
1776@@ -0,0 +1,20 @@
1777+.add { background-image: url(/@@/add.png); /* sprite-ref: group1 */ }
1778+.foo { background-image: url(/@@/add.png); /* sprite-ref: group1 */ }
1779+.bar { background-image: url(/@@/add.png); /* sprite-ref: group2 */ }
1780+.edit {
1781+ background-image: url(/@@/edit.png);
1782+ /* sprite-ref: group1 */
1783+ background-repeat: no-repeat;
1784+}
1785+.info {
1786+ background-image: url(/@@/info.png); /* sprite-ref: group2 */
1787+ background-repeat: no-repeat;
1788+}
1789+.bluebar {
1790+ background-image: url(/@@/blue-bar.png); /* sprite-ref: group1 */
1791+ background-repeat: repeat-x;
1792+}
1793+.large-flame {
1794+ background-image: url(/@@/flame-large.png); /* sprite-ref: group1 */
1795+ background-repeat: no-repeat;
1796+}

Subscribers

People subscribed via source and target branches

to status/vote changes: