diff options
-rwxr-xr-x | CSSParser.py | 155 | ||||
-rwxr-xr-x | cmpcss | 125 |
2 files changed, 280 insertions, 0 deletions
diff --git a/CSSParser.py b/CSSParser.py new file mode 100755 index 0000000..72c643e --- /dev/null +++ b/CSSParser.py | |||
@@ -0,0 +1,155 @@ | |||
1 | #!/usr/bin/env python | ||
2 | |||
3 | import sys, re | ||
4 | |||
5 | class CSSUtils: | ||
6 | ColorRegex = re.compile('', re.I | re.M | re.U) | ||
7 | |||
8 | @staticmethod | ||
9 | def compress_color(color): | ||
10 | pass | ||
11 | |||
12 | @staticmethod | ||
13 | def is_color(color): | ||
14 | pass | ||
15 | |||
16 | class CSSContainer: | ||
17 | selector = '' | ||
18 | |||
19 | def __init__(self): | ||
20 | pass | ||
21 | |||
22 | class CSSParser: | ||
23 | ValidProperties = ( | ||
24 | 'azimuth', | ||
25 | 'background', | ||
26 | 'background-color', | ||
27 | 'background-image', | ||
28 | 'background-repeat', | ||
29 | 'background-attachment', | ||
30 | 'background-position', | ||
31 | 'border', | ||
32 | 'border-top', | ||
33 | 'border-right', | ||
34 | 'border-left', | ||
35 | 'border-bottom', | ||
36 | 'border-color', | ||
37 | 'border-top-color', | ||
38 | 'border-right-color', | ||
39 | 'border-bottom-color', | ||
40 | 'border-left-color', | ||
41 | 'border-style', | ||
42 | 'border-top-style', | ||
43 | 'border-right-style', | ||
44 | 'border-bottom-style', | ||
45 | 'border-left-style', | ||
46 | 'border-width', | ||
47 | 'border-top-width', | ||
48 | 'border-left-width', | ||
49 | 'border-right-width', | ||
50 | 'border-bottom-width', | ||
51 | 'border-collapse', | ||
52 | 'border-spacing', | ||
53 | 'bottom', | ||
54 | 'clear', | ||
55 | 'clip', | ||
56 | 'color', | ||
57 | 'cursor', | ||
58 | 'direction', | ||
59 | 'display', | ||
60 | 'float', | ||
61 | 'font', | ||
62 | 'font-family', | ||
63 | 'font-size', | ||
64 | 'font-style', | ||
65 | 'font-variant', | ||
66 | 'font-weight', | ||
67 | 'height', | ||
68 | 'left', | ||
69 | 'letter-spacing', | ||
70 | 'line-height', | ||
71 | 'list-style', | ||
72 | 'list-style-image', | ||
73 | 'list-style-position', | ||
74 | 'list-style-type', | ||
75 | 'margin', | ||
76 | 'margin-top', | ||
77 | 'margin-right', | ||
78 | 'margin-bottom', | ||
79 | 'margin-left', | ||
80 | 'marker-offset', | ||
81 | 'marks', | ||
82 | 'overflow', | ||
83 | 'padding', | ||
84 | 'padding-top', | ||
85 | 'padding-bottom', | ||
86 | 'padding-right', | ||
87 | 'padding-left', | ||
88 | 'page-break-before', | ||
89 | 'position', | ||
90 | 'right', | ||
91 | 'size', | ||
92 | 'src', | ||
93 | 'table-layout', | ||
94 | 'text-align', | ||
95 | 'text-decoration', | ||
96 | 'text-indent', | ||
97 | 'text-transform', | ||
98 | 'top', | ||
99 | 'vertical-align', | ||
100 | 'visibility', | ||
101 | 'white-space', | ||
102 | 'width', | ||
103 | 'z-index' | ||
104 | ) | ||
105 | |||
106 | CSSRegex = re.compile(r'([^{]+)\s?{\s?([^}]*)\s?}', re.I | re.M | re.U) | ||
107 | PropertyRegex = re.compile(r'([^:]+):([^;]+);', re.I | re.M | re.U) | ||
108 | CommentRegex = re.compile(r'/\*(.|[\r\n])*?\*/', re.I | re.M | re.U) | ||
109 | |||
110 | def __init__(self): | ||
111 | pass | ||
112 | |||
113 | def parse(self): | ||
114 | pass | ||
115 | |||
116 | def duplicate_check(self): | ||
117 | pass | ||
118 | |||
119 | def load_file(filename): | ||
120 | """ Loads a file line by line into a variable and returns it.""" | ||
121 | infile = '' | ||
122 | |||
123 | try: | ||
124 | theFile = file(filename, "r") | ||
125 | except IOError: | ||
126 | sys.exit() | ||
127 | |||
128 | for line in theFile: | ||
129 | infile += line | ||
130 | |||
131 | return infile | ||
132 | |||
133 | if __name__ == '__main__': | ||
134 | output = CSSParser.CommentRegex.sub(' ', load_file(sys.argv[1])) | ||
135 | output = CSSParser.CSSRegex.findall(output) | ||
136 | |||
137 | containers = 0 | ||
138 | errors = 0 | ||
139 | |||
140 | for item in output: | ||
141 | containers += 1 | ||
142 | properties = re.sub('\s+', ' ', item[1]) | ||
143 | proparray = CSSParser.PropertyRegex.findall(properties) | ||
144 | container = item[0].strip() | ||
145 | |||
146 | #print "Container: %s" % item[0].strip() | ||
147 | #print "Contents: %s" % properties | ||
148 | print "Property Array: %s \n\n" % proparray | ||
149 | |||
150 | for item in proparray: | ||
151 | if item[0].strip().lower() not in CSSParser.ValidProperties: | ||
152 | errors += 1 | ||
153 | print "WARNING: Invalid property '%s' in container '%s'" % (item[0].strip(), container) | ||
154 | |||
155 | print "---\n\nFound %s containers and %s errors" % (containers, errors) | ||
@@ -0,0 +1,125 @@ | |||
1 | #!/usr/bin/python | ||
2 | ############### | ||
3 | # cmpcss | ||
4 | # Compress a CSS file in prepration for production use. | ||
5 | # | ||
6 | # AUTHOR | ||
7 | # Michael Crute (mcrute@gmail.com) | ||
8 | # DATE | ||
9 | # 16 June 2006 | ||
10 | # VERSION | ||
11 | # 1.0 | ||
12 | # PURPOSE | ||
13 | # An implementation of a CSS compressor in Python. Written to | ||
14 | # prepare development CSS to production ready CSS. Does the | ||
15 | # following: | ||
16 | # * Removes spaces intelligently | ||
17 | # * Removes charset declarations | ||
18 | # * Removes extra semicolons | ||
19 | # * Flattens the file (tabs and line breaks) | ||
20 | # USAGE | ||
21 | # cmpcss site_dev.css > site_prod.css | ||
22 | # LICENSE | ||
23 | # You may copy and redistribute this program as you see fit, with no | ||
24 | # restrictions. | ||
25 | # WARRANTY | ||
26 | # This program comes with NO warranty, real or implied. If it eats | ||
27 | # your CSS, it's not my problem, you should have kept a backup. | ||
28 | ############### | ||
29 | |||
30 | import sys | ||
31 | import re | ||
32 | #import string | ||
33 | |||
34 | # The chewy caramel center of the program | ||
35 | # Don't bitch about the short variable names, it makes it easy to read | ||
36 | def cleanline(theLine): | ||
37 | # Kills line breaks, tabs, and double spaces | ||
38 | p = re.compile('(\n|\r|\t|\f|\v)+') | ||
39 | m = p.sub('',theLine) | ||
40 | |||
41 | # Kills double spaces | ||
42 | p = re.compile('( )+') | ||
43 | m = p.sub(' ',m) | ||
44 | |||
45 | # Removes last semicolon before } | ||
46 | p = re.compile('(; }|;})+') | ||
47 | m = p.sub('}',m) | ||
48 | |||
49 | # Removes space before { | ||
50 | p = re.compile('({ )+') | ||
51 | m = p.sub('{',m) | ||
52 | |||
53 | # Removes all comments | ||
54 | p = re.compile('/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/') | ||
55 | m = p.sub('',m) | ||
56 | |||
57 | # Strip off the Charset | ||
58 | p = re.compile('@CHARSET .*;') | ||
59 | m = p.sub('',m) | ||
60 | |||
61 | # Strip spaces before the { | ||
62 | p = re.compile(' {') | ||
63 | m = p.sub('{',m) | ||
64 | |||
65 | # Strip space after : | ||
66 | p = re.compile(': ') | ||
67 | m = p.sub(':',m) | ||
68 | |||
69 | # Strip space after , | ||
70 | p = re.compile(', ') | ||
71 | m = p.sub(',',m) | ||
72 | |||
73 | # Strip space after ; | ||
74 | p = re.compile('; ') | ||
75 | m = p.sub(';',m) | ||
76 | |||
77 | # May return to add color code compression | ||
78 | # not a huge space saver so also not high priority | ||
79 | # p = re.compile('.*#[0-9A-F]{6}.*') | ||
80 | # cc = p.match(m) | ||
81 | |||
82 | # if cc: | ||
83 | # cc = compcolor(m) | ||
84 | |||
85 | return m | ||
86 | |||
87 | #def compcolor(m): | ||
88 | |||
89 | # Boring usage info for people too dumb to use the program | ||
90 | def printusage(): | ||
91 | print '' | ||
92 | print 'SoftGroup CSS Compressor' | ||
93 | print '' | ||
94 | print 'This is a compressor for CSS files.' | ||
95 | print 'As far as I know it doesn\'t break the standards' | ||
96 | print 'compliance of the file. If you can find a way' | ||
97 | print 'to make it break a file please email me.' | ||
98 | print '' | ||
99 | print 'Usage: cmpcss <filename>' | ||
100 | sys.exit() | ||
101 | |||
102 | # The main routine | ||
103 | if ( __name__ == "__main__" ): | ||
104 | if (len(sys.argv) <= 1): | ||
105 | printusage() | ||
106 | |||
107 | # Open the file for reading | ||
108 | try: | ||
109 | theFile = file(sys.argv[1], "r") | ||
110 | except IOError: | ||
111 | print 'I couldn\'t open your file.' | ||
112 | sys.exit() | ||
113 | |||
114 | # Initialize temp so it's not scoped to the for loop | ||
115 | temp = '' | ||
116 | |||
117 | # Loop through the file line by line and clean it | ||
118 | for line in theFile: | ||
119 | temp = temp + cleanline(line) | ||
120 | |||
121 | # Once more, clean the entire file string | ||
122 | print cleanline(temp) | ||
123 | |||
124 | # Cleanup after ourselves | ||
125 | theFile.close() | ||