def _minify(self, file_type, files, position, out): cache = Env.get('cache_dir') out_name = 'minified_' + out out = os.path.join(cache, out_name) raw = [] for file_path in files: f = open(file_path, 'r').read() if file_type == 'script': data = jsmin(f) else: data = cssprefixer.process(f, debug = False, minify = True) data = data.replace('../images/', '../static/images/') data = data.replace('../fonts/', '../static/fonts/') data = data.replace('../../static/', '../static/') # Replace inside plugins raw.append({'file': file_path, 'date': int(os.path.getmtime(file_path)), 'data': data}) # Combine all files together with some comments data = '' for r in raw: data += self.comment.get(file_type) % (r.get('file'), r.get('date')) data += r.get('data') + '\n\n' self.createFile(out, ss(data.strip())) if not self.minified.get(file_type): self.minified[file_type] = {} if not self.minified[file_type].get(position): self.minified[file_type][position] = [] minified_url = 'api/%s/file.cache/%s?%s' % (Env.setting('api_key'), out_name, tryInt(os.path.getmtime(out))) self.minified[file_type][position].append(minified_url)
def test_inline_comment(self): #TODO: it would be nice if comments on the same line remained there, but this may not be possible because #cssutils tears everything apart into objects and then we rebuild it. self.assertEqual(cssprefixer.process('''article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block;/* HTML5 display-role reset for older browsers */ }''', minify=False), '''article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; /* HTML5 display-role reset for older browsers */ }''') self.assertEqual(cssprefixer.process('''article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { /* HTML5 display-role reset for older browsers */ display: block; }''', minify=False), '''article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { /* HTML5 display-role reset for older browsers */ display: block }''')
def test_comment(self): self.assertEqual(cssprefixer.process('''/* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block }''', minify=False), '''/* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block }''')
def test_opacity(self): self.assertEqual(cssprefixer.process('''a { opacity: 0.25; }''', minify=False), '''a { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=25)"; filter: alpha(opacity=25); opacity: 0.25 }''')
def test_transition(self): self.assertEqual(cssprefixer.process('''div { -webkit-transition: color .25s linear, -webkit-transform .15s linear .1s; }''', minify=False), '''div { -moz-transition: color 0.25s linear, -moz-transform 0.15s linear 0.1s; -o-transition: color 0.25s linear, -o-transform 0.15s linear 0.1s; -webkit-transition: color 0.25s linear, -webkit-transform 0.15s linear 0.1s; transition: color 0.25s linear, transform 0.15s linear 0.1s }''')
def test_linear_no_pos(self): self.assertEqual(cssprefixer.process('''.box_gradient { background-image: linear-gradient(#444444, #999999); }''', minify=False), '''.box_gradient { background-image: -moz-linear-gradient(#444, #999); background-image: -o-linear-gradient(#444, #999); background-image: -webkit-linear-gradient(#444, #999); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)); background-image: linear-gradient(#444, #999) }''')
def test_webkit_gradient_mixed(self): self.assertEqual(cssprefixer.process('''.box_gradient { background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)), -webkit-linear-gradient(top, black, white); }''', minify=False), '''.box_gradient { background-image: -moz-linear-gradient(#444, #999), -moz-linear-gradient(top, black, white); background-image: -o-linear-gradient(#444, #999), -o-linear-gradient(top, black, white); background-image: -webkit-linear-gradient(#444, #999), -webkit-linear-gradient(top, black, white); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)), -webkit-gradient(linear, left top, left bottom, color-stop(0, black), color-stop(1, white)); background-image: linear-gradient(#444, #999), linear-gradient(top, black, white) }''')
def test_background_multiple_images_and_gradients(self): self.assertEqual(cssprefixer.process('''.box_gradient { background: linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png), -moz-linear-gradient(top, #444444, #999999); }''', minify=False), '''.box_gradient { background: -moz-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png), -moz-linear-gradient(top, #444, #999); background: -o-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png), -o-linear-gradient(top, #444, #999); background: -webkit-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png), -webkit-linear-gradient(top, #444, #999); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, black), color-stop(1, white)), url(images/gradient.png) top center no-repeat, url(images/background.png), -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)); background: linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png), linear-gradient(top, #444, #999) }''')
def test_webkit_gradient_mixed(self): self.assertEqual(cssprefixer.process('''.box_gradient { background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)), -webkit-linear-gradient(top, black, white); }''', minify=False), '''.box_gradient { background-image: -webkit-linear-gradient(#444, #999), -webkit-linear-gradient(top, black, white); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444), color-stop(1, #999)), -webkit-gradient(linear, left top, left bottom, color-stop(0, black), color-stop(1, white)); background-image: -moz-linear-gradient(#444, #999), -moz-linear-gradient(top, black, white); background-image: -o-linear-gradient(#444, #999), -o-linear-gradient(top, black, white); filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=#444, EndColorStr=#999)"; background-image: linear-gradient(#444, #999), linear-gradient(top, black, white) }''')
def test_background_multiple_images_and_gradient(self): self.assertEqual(cssprefixer.process('''.box_gradient { background: linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png); }''', minify=False), '''.box_gradient { background: -webkit-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, black), color-stop(1, white)), url(images/gradient.png) top center no-repeat, url(images/background.png); background: -moz-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png); background: -o-linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png); filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=black, EndColorStr=white)"; background: linear-gradient(top, black, white), url(images/gradient.png) top center no-repeat, url(images/background.png) }''')
def render(self, context): theme_dir = pathjoin(theme_root, context['theme']) print "compiling", theme_dir cssdefs_path = os.path.join(theme_dir, 'theme.py') theme_data = {} execfile(cssdefs_path, {}, theme_data) print theme_data context.update(theme_data) out = super(CSSTemplate, self).render(context) out = cssprefixer.process(out, debug=False, minify=(not DEBUG)) return out
def test_image(self): #I don't think this test produces valid css but it shows that data and order is being preserved. self.assertEqual(cssprefixer.process('''.box_gradient { background-image: url(images/background.png), linear-gradient(top, black, white); }''', minify=False), '''.box_gradient { background-image: url(images/background.png), -moz-linear-gradient(top, black, white); background-image: url(images/background.png), -o-linear-gradient(top, black, white); background-image: url(images/background.png), -webkit-linear-gradient(top, black, white); background-image: url(images/background.png), -webkit-gradient(linear, left top, left bottom, color-stop(0, black), color-stop(1, white)); background-image: url(images/background.png), linear-gradient(top, black, white) }''')
def test_media_no_mini(self): self.assertEqual(cssprefixer.process('''@media screen and (max-device-width: 480px){ #book{ border-radius: 1em; } }''', minify=False), '''@media screen and (max-device-width: 480px) { #book { -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em } }''')
def _test_background_multiple_images_and_gradients(self): self.assertEqual(cssprefixer.process('''.box_gradient { background-image: url('../img/arrow.png'), -webkit-gradient( linear, left top, left bottom, from(rgb(240, 240, 240)), to(rgb(210, 210, 210)) )}''', minify=False), '''.box_gradient { }''')
def test_transition_property(self): self.assertEqual(cssprefixer.process('''div { -webkit-transition-property: -webkit-transform, opacity, left; -webkit-transition-duration: rotatey(45deg), 2s, 4s; }''', minify=False), '''div { -moz-transition-property: -moz-transform, opacity, left; -o-transition-property: -o-transform, opacity, left; -webkit-transition-property: -webkit-transform, opacity, left; transition-property: transform, opacity, left; -moz-transition-duration: rotatey(45deg), 2s, 4s; -o-transition-duration: rotatey(45deg), 2s, 4s; -webkit-transition-duration: rotatey(45deg), 2s, 4s; transition-duration: rotatey(45deg), 2s, 4s }''')
def test_keyframes(self): self.assertEqual(cssprefixer.process('''@keyframes round { from {border-radius: 2px} to {border-radius: 10px} }''', minify=False), "@-webkit-keyframes round {\nfrom {\n -webkit-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -webkit-border-radius: 10px;\n border-radius: 10px\n }\n}\n@-moz-keyframes round {\nfrom {\n -moz-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -moz-border-radius: 10px;\n border-radius: 10px\n }\n}\n@keyframes round {\n from {\n border-radius: 2px\n } to {\n border-radius: 10px\n }\n }") # Test with comment within the keyframes definition. self.assertEqual(cssprefixer.process('''@keyframes round /* comment */ { from {border-radius: 2px} to {border-radius: 10px} }''', minify=False), "@-webkit-keyframes round {\nfrom {\n -webkit-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -webkit-border-radius: 10px;\n border-radius: 10px\n }\n}\n@-moz-keyframes round {\nfrom {\n -moz-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -moz-border-radius: 10px;\n border-radius: 10px\n }\n}\n@keyframes round {\n from {\n border-radius: 2px\n } to {\n border-radius: 10px\n }\n }") # Test with percentage values. self.assertEqual(cssprefixer.process('''@keyframes round { 0% {border-radius: 2px} 100% {border-radius: 10px} }''', minify=False), "@-webkit-keyframes round {\0% {\n -webkit-border-radius: 2px;\n border-radius: 2px\n }\n100% {\n -webkit-border-radius: 10px;\n border-radius: 10px\n }\n}\n@-moz-keyframes round {\nfrom {\n -moz-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -moz-border-radius: 10px;\n border-radius: 10px\n }\n}\n@keyframes round {\n from {\n border-radius: 2px\n } to {\n border-radius: 10px\n }\n }") # Test with vendor-prefix in input. self.assertEqual(cssprefixer.process('''@-webkit-keyframes round { 0% {border-radius: 2px} 100% {border-radius: 10px} }''', minify=False), "@-webkit-keyframes round {\nfrom {\n -webkit-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -webkit-border-radius: 10px;\n border-radius: 10px\n }\n}\n@-moz-keyframes round {\nfrom {\n -moz-border-radius: 2px;\n border-radius: 2px\n }\nto {\n -moz-border-radius: 10px;\n border-radius: 10px\n }\n}\n@keyframes round {\n from {\n border-radius: 2px\n } to {\n border-radius: 10px\n }\n }")
def test_multi_transition(self): self.assertEqual(cssprefixer.process('''div { transition: color .25s linear; transition: background-color .15s linear .1; }''', minify=False), '''div { -moz-transition: color 0.25s linear; -o-transition: color 0.25s linear; -webkit-transition: color 0.25s linear; transition: color 0.25s linear; -moz-transition: background-color 0.15s linear 0.1; -o-transition: background-color 0.15s linear 0.1; -webkit-transition: background-color 0.15s linear 0.1; transition: background-color 0.15s linear 0.1 }''')
def test_keyframes(self): self.maxDiff = None self.assertEqual(cssprefixer.process('''@keyframes round { from {border-radius: 2px} to {border-radius: 10px} }''', minify=False), '''@keyframes round { from { border-radius: 2px } to { border-radius: 10px } } @-webkit-keyframes round { from { -webkit-border-radius: 2px; border-radius: 2px } to { -webkit-border-radius: 10px; border-radius: 10px } } @-moz-keyframes round { from { -moz-border-radius: 2px; border-radius: 2px } to { -moz-border-radius: 10px; border-radius: 10px } } @-ms-keyframes round { from { border-radius: 2px } to { border-radius: 10px } } @-o-keyframes round { from { border-radius: 2px } to { border-radius: 10px } }''')
def process(resource): data = resource.source_file.read_all() try: import cssprefixer out = cssprefixer.process(data, debug=False, minify=False) except ImportError: try: data = urllib.urlencode({"css": resource.source_file.read_all()}) req = urllib2.Request("http://cssprefixer.appspot.com/process/", data) out = urllib2.urlopen(req).read() except urllib2.HTTPError, e: print 'HTTP Error %s when calling remote CSSPrefixer' % e.code return False except urllib2.URLError, e: print 'Error when calling remote CSSPrefixer:', e.reason return False
def test_no_mini(self): self.assertEqual(cssprefixer.process('''.my-class, #my-id { border-radius: 1em; transition: all 1s ease; box-shadow: #123456 0 0 10px; display: box; }''', minify=False), '''.my-class, #my-id { -webkit-border-radius: 1em; -moz-border-radius: 1em; border-radius: 1em; -webkit-transition: all 1s ease; -moz-transition: all 1s ease; -o-transition: all 1s ease; transition: all 1s ease; -webkit-box-shadow: #123456 0 0 10px; -moz-box-shadow: #123456 0 0 10px; box-shadow: #123456 0 0 10px; display: -webkit-box; display: -moz-box; display: box }''')
def test_undefined(self): #test prefixed styles that don't have a rule yet, we use a fake property #for this test becuase we will never have a rule for this self.assertEqual( cssprefixer.process('a{-webkit-faker: black}', minify=True), 'a{-webkit-faker:black}')
def test_mixed_duplicate(self): self.assertEqual(cssprefixer.process('a{-moz-border-radius: 1em;border-radius: 1em;-webkit-border-radius: 1em}', minify=True), 'a{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}')
def test_mq(self): self.assertEqual(cssprefixer.process('@media screen and (min-width:480px){a{color:red}}', minify=True), '@media screen and (min-width:480px){a{color:red}}')
def test_flexbox(self): self.assertEqual(cssprefixer.process('a{display: flex;}', minify=True), 'a{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-ms-flexbox;display:flex}')
def test_moz_border_radius(self): self.assertEqual(cssprefixer.process('a{border-top-left-radius: 1em;border-top-right-radius: 1em;border-bottom-right-radius: 1em;border-bottom-left-radius: 1em;}', minify=True), 'a{-webkit-border-top-left-radius:1em;-moz-border-radius-topleft:1em;border-top-left-radius:1em;-webkit-border-top-right-radius:1em;-moz-border-radius-topright:1em;border-top-right-radius:1em;-webkit-border-bottom-right-radius:1em;-moz-border-radius-bottomright:1em;border-bottom-right-radius:1em;-webkit-border-bottom-left-radius:1em;-moz-border-radius-bottomleft:1em;border-bottom-left-radius:1em}')
def test_empty(self): self.assertEqual(cssprefixer.process('a{}', minify=True), '') self.assertEqual(cssprefixer.process('a{}', minify=False), '')
def test_common_and_opera(self): self.assertEqual( cssprefixer.process('a{transform: rotate(10deg)}', minify=True), 'a{-moz-transform:rotate(10deg);-ms-transform:rotate(10deg);-o-transform:rotate(10deg);-webkit-transform:rotate(10deg);transform:rotate(10deg)}' )
def test_webkit(self): self.assertEqual( cssprefixer.process('a{-webkit-background-clip: padding-box}', minify=True), 'a{-webkit-background-clip:padding-box;background-clip:padding-box}' )
def test_flexbox(self): self.assertEqual( cssprefixer.process('a{-webkit-display: box;}', minify=True), 'a{display:-moz-box;display:-webkit-box;display:box}')
def text_resource_complete(self, resource, text): if not resource.source_file.kind in ("less", "css"): return return cssprefixer.process(text, debug=False, minify=True)
def test_appearance(self): #test prefixed styles that don't have a rule yet, we use a fake property #for this test becuase we will never have a rule for this self.assertEqual(cssprefixer.process('a{-webkit-appearance: none;}', minify=True), 'a{-webkit-appearance:none;appearance:none}')
def test_ie_and_opera(self): self.assertEqual(cssprefixer.process('a{text-overflow: ellipsis}', minify=True), 'a{-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}')
def test_moz_border_radius(self): self.assertEqual(cssprefixer.process('a{border-top-left-radius: 1em;border-top-right-radius: 1em;border-bottom-right-radius: 1em;border-bottom-left-radius: 1em;}', minify=True), 'a{-moz-border-radius-topleft:1em;-moz-border-radius-topright:1em;-moz-border-radius-bottomright:1em;-moz-border-radius-bottomleft:1em;-webkit-border-bottom-left-radius:1em;-webkit-border-bottom-right-radius:1em;-webkit-border-top-right-radius:1em;-webkit-border-top-left-radius:1em;border-top-left-radius:1em;border-top-right-radius:1em;border-bottom-right-radius:1em;border-bottom-left-radius:1em}')
def test_cursor_zoom_out(self): self.assertEqual(cssprefixer.process('a{cursor: zoom-out;}', minify=True), 'a{cursor:-moz-zoom-out;cursor:-webkit-zoom-out;cursor:zoom-out}')
def test_displaybox(self): self.assertEqual(cssprefixer.process('a{display: display;}', minify=True), 'a{display:display}')
def test_displaybox(self): self.assertEqual( cssprefixer.process('a{display: display;}', minify=True), 'a{display:display}')
def test_mq_common(self): self.assertEqual(cssprefixer.process('@media screen and (min-width:480px){a{border-radius: 1em}}', minify=True), '@media screen and (min-width:480px){a{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}}')
def test_cursor_zoom_out(self): self.assertEqual( cssprefixer.process('a{cursor: zoom-out;}', minify=True), 'a{cursor:-moz-zoom-out;cursor:-webkit-zoom-out;cursor:zoom-out}')
def test_appearance(self): #test prefixed styles that don't have a rule yet, we use a fake property #for this test becuase we will never have a rule for this self.assertEqual( cssprefixer.process('a{-webkit-appearance: none;}', minify=True), 'a{-webkit-appearance:none;appearance:none}')
def test_common(self): self.assertEqual( cssprefixer.process('a{-moz-border-radius: 1em}', minify=True), 'a{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em}' )
def process(resource): import cssprefixer data = resource.source_file.read_all() out = cssprefixer.process(data, debug=False, minify=False) resource.source_file.write(out)