-
Notifications
You must be signed in to change notification settings - Fork 14
Add less, sass and escaped filters #55
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,13 @@ | ||
from __future__ import unicode_literals | ||
""" | ||
Core HamlPy filters. | ||
|
||
The implementation of these should match https://github.com/haml/haml/blob/master/lib/haml/filters.rb as closely as | ||
possible. Where we differ is that we don't compile Stylus, Coffeescript etc into CSS or Javascript - but place the | ||
content into suitable <style> and <script> that can be transformed later by something like django-compressor. | ||
""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should consider this.. that is turning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like I only just about understand how a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We of course need the - compress js
:coffee
foo=bar
- compress css
:sass
.mystyle {...} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ya, I think what I was thinking of was closer to:
In that you want it converted and inserted directly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure and that means the sass compilation happens every time that template is requested? Maybe that's not a big deal and/or caching of templates works? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well Simpla will def be compressing offline. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant just template caching - to avoid haml→html compilation for every request There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well now I'm confused, won't the offline compression do that? Or is that not converting haml to html at that point? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that just extracted the js and css? Each request still requires compiling the Haml into html. Now I'm confused There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you are right.. well sheesh, that seems like something we should fix. I don't even like that happening with per-process caching. |
||
|
||
import sys | ||
import textwrap | ||
|
||
# Required on Python 2 to accept non-unicode output | ||
try: | ||
|
@@ -31,67 +37,69 @@ | |
from future.utils import raise_from | ||
|
||
from .core import ParseException | ||
from .utils import html_escape | ||
|
||
|
||
# ---------------------------------------------------------------------------------- | ||
# Core filters | ||
# ---------------------------------------------------------------------------------- | ||
|
||
def plain(text, options): | ||
return text | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently each filter handles indentation separately but now, like the Ruby implementation, indentation is handled outside of the filter. |
||
|
||
|
||
def preserve(text, options): | ||
text = text.rstrip() | ||
text = text.replace('\n', '
') | ||
return text.replace('\r', '') | ||
|
||
def plain(content, indent, options): | ||
return textwrap.dedent(content) | ||
|
||
def escaped(text, options): | ||
return html_escape(text) | ||
|
||
def preserve(content, indent, options): | ||
s = content.rstrip() | ||
s = s.replace('\n', '
') | ||
s = s.replace('\r', '') | ||
return textwrap.dedent(s) | ||
|
||
def cdata(text, options): | ||
text = '\n' + text.rstrip() | ||
text = text.replace("\n", "\n ") | ||
return '<![CDATA[%s\n]]>' % text | ||
|
||
def cdata(content, indent, options): | ||
return indent + '<![CDATA[\n' \ | ||
+ content + '\n' \ | ||
+ indent + ']]>' | ||
|
||
def css(text, options): | ||
return style_filter(text, 'text/css', options) | ||
|
||
def css(content, indent, options): | ||
return '<style type=%(attr_wrapper)stext/css%(attr_wrapper)s>\n' \ | ||
'/*<![CDATA[*/\n' % {'attr_wrapper': options['attr_wrapper']} \ | ||
+ content + '\n' \ | ||
+ '/*]]>*/\n</style>' | ||
|
||
def stylus(text, options): | ||
return style_filter(text, 'text/stylus', options) | ||
|
||
def stylus(content, indent, options): | ||
return indent + '<style type=%(attr_wrapper)stext/stylus%(attr_wrapper)s>\n' \ | ||
'/*<![CDATA[*/\n' % {'attr_wrapper': options['attr_wrapper']} \ | ||
+ textwrap.dedent(content) + '\n' \ | ||
+ indent + '/*]]>*/\n</style>' | ||
|
||
def less(text, options): | ||
return style_filter(text, 'text/less', options) | ||
|
||
def javascript(content, indent, options): | ||
return '<script type=%(attr_wrapper)stext/javascript%(attr_wrapper)s>\n' \ | ||
'// <![CDATA[\n' % {'attr_wrapper': options['attr_wrapper']} \ | ||
+ (content + '\n' if content else '') \ | ||
+ '// ]]>\n</script>' | ||
|
||
def sass(text, options): | ||
return style_filter(text, 'text/sass', options) | ||
|
||
def coffeescript(content, indent, options): | ||
return '<script type=%(attr_wrapper)stext/coffeescript%(attr_wrapper)s>\n' \ | ||
'#<![CDATA[\n' % {'attr_wrapper': options['attr_wrapper']} \ | ||
+ (content + '\n' if content else '') \ | ||
+ '#]]>\n</script>' | ||
|
||
def javascript(text, options): | ||
return script_filter(text, 'text/javascript', '//', options) | ||
|
||
def markdown(content, indent, options): | ||
|
||
def coffee(text, options): | ||
return script_filter(text, 'text/coffeescript', '#', options) | ||
|
||
|
||
def markdown(content, options): | ||
if not _markdown_available: | ||
raise ParseException("Markdown is not available") | ||
|
||
return markdown_lib(textwrap.dedent(content)) | ||
return markdown_lib(content) | ||
|
||
|
||
def highlight(content, indent, options): | ||
def highlight(content, options): | ||
if not _pygments_available: | ||
raise ParseException("Pygments is not available") | ||
|
||
if content: | ||
content = textwrap.dedent(content) | ||
|
||
# let Pygments try to guess syntax but default to Python | ||
try: | ||
lexer = guess_lexer(content) | ||
|
@@ -103,9 +111,8 @@ def highlight(content, indent, options): | |
return '' | ||
|
||
|
||
def python(content, indent, options): | ||
def python(content, options): | ||
if content: | ||
content = textwrap.dedent(content) | ||
compiled_code = compile(content, "", "exec") | ||
output_buffer = StringIO() | ||
sys.stdout = output_buffer | ||
|
@@ -123,21 +130,56 @@ def python(content, indent, options): | |
return '' | ||
|
||
|
||
# ---------------------------------------------------------------------------------- | ||
# Helper functions | ||
# ---------------------------------------------------------------------------------- | ||
|
||
def style_filter(text, mime_type, options): | ||
indent = ' ' if options['cdata'] else ' ' | ||
text = text.rstrip().replace('\n', '\n' + indent) | ||
type_attr = ' type=%(attr_wrapper)s%(mime_type)s%(attr_wrapper)s' % \ | ||
{'attr_wrapper': options['attr_wrapper'], 'mime_type': mime_type} | ||
before, after = (' /*<![CDATA[*/\n', ' /*]]>*/\n') if options['cdata'] else ('', '') | ||
|
||
return '<style%s>\n%s%s%s\n%s</style>' % (type_attr, before, indent, text, after) | ||
|
||
|
||
def script_filter(text, mime_type, comment, options): | ||
indent = ' ' if options['cdata'] else ' ' | ||
text = text.rstrip().replace('\n', '\n' + indent) | ||
type_attr = ' type=%(attr_wrapper)s%(mime_type)s%(attr_wrapper)s' % \ | ||
{'attr_wrapper': options['attr_wrapper'], 'mime_type': mime_type} | ||
before, after = (' %s<![CDATA[\n' % comment, ' %s]]>\n' % comment) if options['cdata'] else ('', '') | ||
|
||
return '<script%s>\n%s%s%s\n%s</script>' % (type_attr, before, indent, text, after) | ||
|
||
|
||
# ---------------------------------------------------------------------------------- | ||
# Filter registration | ||
# ---------------------------------------------------------------------------------- | ||
|
||
FILTERS = { | ||
'plain': plain, | ||
'preserve': preserve, | ||
'escaped': escaped, | ||
'cdata': cdata, | ||
'css': css, | ||
'stylus': stylus, | ||
'less': less, | ||
'sass': sass, | ||
'javascript': javascript, | ||
'coffee': coffeescript, | ||
'coffeescript': coffeescript, | ||
'coffee': coffee, | ||
'coffeescript': coffee, | ||
'markdown': markdown, | ||
'highlight': highlight, | ||
'python': python | ||
} | ||
|
||
|
||
def register_filter(name, callback): | ||
FILTERS[name] = callback | ||
|
||
|
||
def get_filter(name): | ||
if name not in FILTERS: | ||
raise ParseException("No such filter: " + name) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from __future__ import unicode_literals | ||
|
||
|
||
def html_escape(s): | ||
""" | ||
Escapes HTML entities, matching substitutions used the Ruby Haml library | ||
""" | ||
s = s.replace("&", "&") | ||
s = s.replace("<", "<") | ||
s = s.replace(">", ">") | ||
s = s.replace('"', """) | ||
s = s.replace("'", "'") | ||
return s |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,14 +9,14 @@ | |
|
||
should } not be interpreted as a multiline string | ||
:css | ||
.test { | ||
display: inline; | ||
} | ||
.test { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is mostly replacing tabs with spaces |
||
display: inline; | ||
} | ||
:javascript | ||
These { | ||
Braces should { | ||
also | ||
} be { ignored | ||
These { | ||
Braces should { | ||
also | ||
} be { ignored | ||
|
||
.multilinetest2{id:'{{myId}}', | ||
class:'{{myClass}}', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,78 +2,77 @@ | |
These { { braces | ||
|
||
should } not be interpreted as a multiline string | ||
These { { braces

 should } not be interpreted as a multiline string | ||
These { { braces

should } not be interpreted as a multiline string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is consistent with how the Haml filter works |
||
<style type='text/css'> | ||
/*<![CDATA[*/ | ||
.test { | ||
display: inline; | ||
} | ||
/*]]>*/ | ||
/*<![CDATA[*/ | ||
.test { | ||
display: inline; | ||
} | ||
/*]]>*/ | ||
</style> | ||
<script type='text/javascript'> | ||
// <![CDATA[ | ||
These { | ||
Braces should { | ||
also | ||
} be { ignored | ||
|
||
// ]]> | ||
//<![CDATA[ | ||
These { | ||
Braces should { | ||
also | ||
} be { ignored | ||
//]]> | ||
</script> | ||
<div id='{{myId}}' class='multilinetest2 {{myClass}}' alt=''></div> | ||
<!-- The following is from hjonathan, issue #67 --> | ||
<head> | ||
<div class='blah'> | ||
<script type='text/javascript'> | ||
// <![CDATA[ | ||
<script type='text/javascript'> | ||
//<![CDATA[ | ||
$(document).ready(function(){ | ||
$("#form{{form.initial.id}}").submit(form_submit); | ||
//Double nesting | ||
$(function() { | ||
blahblahblah | ||
}); | ||
|
||
// Javascript comment | ||
}); | ||
//]]> | ||
</script> | ||
</div> | ||
<script type='text/javascript'> | ||
//<![CDATA[ | ||
$(document).ready(function(){ | ||
$("#form{{form.initial.id}}").submit(form_submit); | ||
//Double nesting | ||
$(function() { | ||
blahblahblah | ||
}); | ||
|
||
// Javascript comment | ||
}); | ||
// ]]> | ||
</script> | ||
</div> | ||
<script type='text/javascript'> | ||
// <![CDATA[ | ||
$(document).ready(function(){ | ||
$("#form{{form.initial.id}}").submit(form_submit); | ||
// Javascript comment | ||
}); | ||
// ]]> | ||
</script> | ||
<style type='text/css'> | ||
/*<![CDATA[*/ | ||
.someClass { | ||
width: 100px; | ||
} | ||
/*]]>*/ | ||
</style> | ||
//]]> | ||
</script> | ||
<style type='text/css'> | ||
/*<![CDATA[*/ | ||
.someClass { | ||
width: 100px; | ||
} | ||
/*]]>*/ | ||
</style> | ||
<![CDATA[ | ||
if (a < b && a < 0) | ||
{ | ||
return 1; | ||
} | ||
if (a < b && a < 0) | ||
{ | ||
return 1; | ||
} | ||
]]> | ||
</head> | ||
<script type='text/javascript'> | ||
// <![CDATA[ | ||
( | ||
{ | ||
a | ||
} | ||
); | ||
// ]]> | ||
//<![CDATA[ | ||
( | ||
{ | ||
a | ||
} | ||
); | ||
//]]> | ||
</script> | ||
<script type='text/javascript'> | ||
// <![CDATA[ | ||
//<![CDATA[ | ||
|
||
{ | ||
a | ||
} | ||
) | ||
// ]]> | ||
{ | ||
a | ||
} | ||
) | ||
//]]> | ||
</script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CDATA usage isn't required in HTML5 so making it optional here like in Ruby Haml