awestructx 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/awestruct +8 -0
- data/lib/awestruct/astruct.rb +22 -0
- data/lib/awestruct/astruct_mixin.rb +71 -0
- data/lib/awestruct/cli/auto.rb +20 -0
- data/lib/awestruct/cli/generate.rb +26 -0
- data/lib/awestruct/cli/invoker.rb +109 -0
- data/lib/awestruct/cli/options.rb +116 -0
- data/lib/awestruct/cli/server.rb +24 -0
- data/lib/awestruct/config.rb +30 -0
- data/lib/awestruct/context.rb +22 -0
- data/lib/awestruct/context_helper.rb +68 -0
- data/lib/awestruct/engine.rb +254 -0
- data/lib/awestruct/extensions/assets.rb +39 -0
- data/lib/awestruct/extensions/atomizer.rb +44 -0
- data/lib/awestruct/extensions/cachebuster.rb +12 -0
- data/lib/awestruct/extensions/coffeescripttransform.rb +42 -0
- data/lib/awestruct/extensions/data_dir.rb +31 -0
- data/lib/awestruct/extensions/disqus.rb +62 -0
- data/lib/awestruct/extensions/extend_string.rb +97 -0
- data/lib/awestruct/extensions/flattr.rb +42 -0
- data/lib/awestruct/extensions/google_analytics.rb +38 -0
- data/lib/awestruct/extensions/gsub.rb +20 -0
- data/lib/awestruct/extensions/indexifier.rb +17 -0
- data/lib/awestruct/extensions/intense_debate.rb +38 -0
- data/lib/awestruct/extensions/minify.rb +178 -0
- data/lib/awestruct/extensions/obfuscate.rb +32 -0
- data/lib/awestruct/extensions/paginator.rb +105 -0
- data/lib/awestruct/extensions/partial.rb +25 -0
- data/lib/awestruct/extensions/pipeline.rb +50 -0
- data/lib/awestruct/extensions/posts.rb +70 -0
- data/lib/awestruct/extensions/relative.rb +11 -0
- data/lib/awestruct/extensions/remotePartial.rb +17 -0
- data/lib/awestruct/extensions/sitemap.rb +85 -0
- data/lib/awestruct/extensions/sitemap.xml.haml +16 -0
- data/lib/awestruct/extensions/tag_cloud.html.haml +7 -0
- data/lib/awestruct/extensions/tag_cloud.rb +34 -0
- data/lib/awestruct/extensions/tagger.rb +107 -0
- data/lib/awestruct/extensions/template.atom.haml +39 -0
- data/lib/awestruct/handler_chain.rb +28 -0
- data/lib/awestruct/handler_chains.rb +65 -0
- data/lib/awestruct/handlers/base_handler.rb +92 -0
- data/lib/awestruct/handlers/base_sass_handler.rb +42 -0
- data/lib/awestruct/handlers/file_handler.rb +61 -0
- data/lib/awestruct/handlers/front_matter_handler.rb +80 -0
- data/lib/awestruct/handlers/haml_handler.rb +42 -0
- data/lib/awestruct/handlers/interpolation_handler.rb +28 -0
- data/lib/awestruct/handlers/layout_handler.rb +61 -0
- data/lib/awestruct/handlers/markdown_handler.rb +36 -0
- data/lib/awestruct/handlers/no_op_handler.rb +34 -0
- data/lib/awestruct/handlers/sass_handler.rb +14 -0
- data/lib/awestruct/handlers/scss_handler.rb +14 -0
- data/lib/awestruct/handlers/string_handler.rb +29 -0
- data/lib/awestruct/handlers/textile_handler.rb +43 -0
- data/lib/awestruct/handlers/yaml_handler.rb +25 -0
- data/lib/awestruct/layouts.rb +15 -0
- data/lib/awestruct/page.rb +128 -0
- data/lib/awestruct/page_loader.rb +72 -0
- data/lib/awestruct/pipeline.rb +49 -0
- data/lib/awestruct/site.rb +51 -0
- data/lib/awestruct/util/default_inflections.rb +45 -0
- data/lib/awestruct/util/inflector.rb +242 -0
- data/lib/awestruct/version.rb +4 -0
- data/lib/guard/awestruct.rb +38 -0
- metadata +427 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module Awestruct
|
2
|
+
module Extensions
|
3
|
+
class DataDir
|
4
|
+
|
5
|
+
def initialize(data_dir="_data")
|
6
|
+
@data_dir = data_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def watch(watched_dirs)
|
10
|
+
watched_dirs << @data_dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(site)
|
14
|
+
Dir[ "#{site.dir}/#{@data_dir}/*" ].each do |entry|
|
15
|
+
if ( File.directory?( entry ) )
|
16
|
+
data_key = File.basename( entry )
|
17
|
+
data_map = {}
|
18
|
+
Dir[ "#{entry}/*" ].each do |chunk|
|
19
|
+
File.basename( chunk ) =~ /^([^\.]+)/
|
20
|
+
key = $1.to_sym
|
21
|
+
chunk_page = site.engine.load_page( chunk )
|
22
|
+
data_map[ key ] = chunk_page
|
23
|
+
end
|
24
|
+
site.send( "#{data_key}=", data_map )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Awestruct
|
4
|
+
module Extensions
|
5
|
+
class Disqus
|
6
|
+
|
7
|
+
def execute(site)
|
8
|
+
site.pages.each{ |p| p.extend Disqus }
|
9
|
+
end
|
10
|
+
|
11
|
+
module Disqus
|
12
|
+
def disqus_comments()
|
13
|
+
identifier = "null"
|
14
|
+
if self.disqus_identifier or site.disqus_generate_id
|
15
|
+
identifier = %Q("#{self.resolve_disqus_identifier()}")
|
16
|
+
end
|
17
|
+
%Q{
|
18
|
+
<div id="disqus_thread"></div>
|
19
|
+
<script type="text/javascript">
|
20
|
+
var disqus_shortname = '#{site.disqus}';
|
21
|
+
var disqus_url = "#{site.base_url}#{self.url}";
|
22
|
+
var disqus_developer = #{site.disqus_developer ? 1 : "null"};
|
23
|
+
var disqus_identifier = #{identifier};
|
24
|
+
(function() {
|
25
|
+
var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
|
26
|
+
dsq.src = "http://#{site.disqus}.disqus.com/embed.js";
|
27
|
+
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
|
28
|
+
})();
|
29
|
+
</script>
|
30
|
+
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript=#{site.disqus}">comments powered by Disqus.</a></noscript>
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def disqus_comments_link()
|
35
|
+
identifier = ''
|
36
|
+
if self.disqus_identifier or site.disqus_generate_id
|
37
|
+
identifier = %Q{ data-disqus-identifier="#{self.resolve_disqus_identifier()}"}
|
38
|
+
end
|
39
|
+
%Q{ <a href="#{self.url}#disqus_thread"#{identifier}>Comments</a> }
|
40
|
+
end
|
41
|
+
|
42
|
+
def disqus_comments_count()
|
43
|
+
%Q{
|
44
|
+
<script type="text/javascript">
|
45
|
+
var disqus_shortname = '#{site.disqus}';
|
46
|
+
(function () {
|
47
|
+
var s = document.createElement('script'); s.async = true;
|
48
|
+
s.src = "http://disqus.com/forums/#{site.disqus}/count.js";
|
49
|
+
(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
|
50
|
+
}());
|
51
|
+
</script>
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def resolve_disqus_identifier()
|
56
|
+
self.disqus_identifier ? self.disqus_identifier : Digest::SHA1.hexdigest(self.date.strftime('%Y-%m-%d-') + self.slug)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# RemoveAccents version 1.0.3 (c) 2008-2009 Solutions Informatiques Techniconseils inc.
|
2
|
+
#
|
3
|
+
# This module adds 2 methods to the string class.
|
4
|
+
# Up-to-date version and documentation available at:
|
5
|
+
#
|
6
|
+
# http://www.techniconseils.ca/en/scripts-remove-accents-ruby.php
|
7
|
+
#
|
8
|
+
# This script is available under the following license :
|
9
|
+
# Creative Commons Attribution-Share Alike 2.5.
|
10
|
+
#
|
11
|
+
# See full license and details at :
|
12
|
+
# http://creativecommons.org/licenses/by-sa/2.5/ca/
|
13
|
+
#
|
14
|
+
# Version history:
|
15
|
+
# * 1.0.3 : July 23 2009
|
16
|
+
# Corrected some incorrect character codes. Source is now wikipedia at:
|
17
|
+
# http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Related_character_maps
|
18
|
+
# Thanks to Raimon Fernandez for pointing out the incorrect codes.
|
19
|
+
# * 1.0.2 : October 29 2008
|
20
|
+
# Slightly optimized version of urlize - Jonathan Grenier (jgrenier@techniconseils.ca)
|
21
|
+
# * 1.0.1 : October 29 2008
|
22
|
+
# First public revision - Jonathan Grenier (jgrenier@techniconseils.ca)
|
23
|
+
#
|
24
|
+
|
25
|
+
class String
|
26
|
+
# The extended characters map used by removeaccents. The accented characters
|
27
|
+
# are coded here using their numerical equivalent to sidestep encoding issues.
|
28
|
+
# These correspond to ISO-8859-1 encoding.
|
29
|
+
ACCENTS_MAPPING = {
|
30
|
+
'E' => [200,201,202,203],
|
31
|
+
'e' => [232,233,234,235],
|
32
|
+
'A' => [192,193,194,195,196,197],
|
33
|
+
'a' => [224,225,226,227,228,229,230],
|
34
|
+
'C' => [199],
|
35
|
+
'c' => [231],
|
36
|
+
'O' => [210,211,212,213,214,216],
|
37
|
+
'o' => [242,243,244,245,246,248],
|
38
|
+
'I' => [204,205,206,207],
|
39
|
+
'i' => [236,237,238,239],
|
40
|
+
'U' => [217,218,219,220],
|
41
|
+
'u' => [249,250,251,252],
|
42
|
+
'N' => [209],
|
43
|
+
'n' => [241],
|
44
|
+
'Y' => [221],
|
45
|
+
'y' => [253,255],
|
46
|
+
'AE' => [306],
|
47
|
+
'ae' => [346],
|
48
|
+
'OE' => [188],
|
49
|
+
'oe' => [189]
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
# Remove the accents from the string. Uses String::ACCENTS_MAPPING as the source map.
|
54
|
+
def removeaccents
|
55
|
+
str = String.new(self)
|
56
|
+
String::ACCENTS_MAPPING.each {|letter,accents|
|
57
|
+
packed = accents.pack('U*')
|
58
|
+
rxp = Regexp.new("[#{packed}]", nil, 'U')
|
59
|
+
str.gsub!(rxp, letter)
|
60
|
+
}
|
61
|
+
|
62
|
+
str
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Convert a string to a format suitable for a URL without ever using escaped characters.
|
67
|
+
# It calls strip, removeaccents, downcase (optional) then removes the spaces (optional)
|
68
|
+
# and finally removes any characters matching the default regexp (/[^-_A-Za-z0-9]/).
|
69
|
+
#
|
70
|
+
# Options
|
71
|
+
#
|
72
|
+
# * :downcase => call downcase on the string (defaults to true)
|
73
|
+
# * :convert_spaces => Convert space to underscore (defaults to false)
|
74
|
+
# * :regexp => The regexp matching characters that will be converting to an empty string (defaults to /[^-_A-Za-z0-9]/)
|
75
|
+
def urlize(options = {})
|
76
|
+
options[:downcase] ||= true
|
77
|
+
options[:convert_spaces] ||= false
|
78
|
+
options[:regexp] ||= /[^-_A-Za-z0-9]/
|
79
|
+
|
80
|
+
str = self.strip.removeaccents
|
81
|
+
str.downcase! if options[:downcase]
|
82
|
+
str.gsub!(/\ /,'-') if options[:convert_spaces]
|
83
|
+
str.gsub(options[:regexp], '')
|
84
|
+
end
|
85
|
+
|
86
|
+
# This follows the generated ID rules
|
87
|
+
def anchorize(options = {})
|
88
|
+
options[:downcase] ||= true
|
89
|
+
options[:convert_spaces] ||= false
|
90
|
+
options[:regexp] ||= /[^-_A-Za-z0-9]/
|
91
|
+
|
92
|
+
str = self.strip.removeaccents
|
93
|
+
str.downcase! if options[:downcase]
|
94
|
+
str.gsub!(/\ /,'_') if options[:convert_spaces]
|
95
|
+
str.gsub(options[:regexp], '')
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Awestruct
|
2
|
+
module Extensions
|
3
|
+
class Flattr
|
4
|
+
|
5
|
+
def execute(site)
|
6
|
+
site.pages.each{|p| p.extend Flattrable }
|
7
|
+
end
|
8
|
+
|
9
|
+
module Flattrable
|
10
|
+
def flattr_javascript()
|
11
|
+
html = %Q|<script type='text/javascript'> /* <![CDATA[ */ (function() {\n|
|
12
|
+
html += %Q|var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];|
|
13
|
+
html += %Q|s.type = 'text/javascript';\n|
|
14
|
+
html += %Q|s.async = true;\n|
|
15
|
+
html += %Q|s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto&uid=#{site.flattr_username}&category=text';\n|
|
16
|
+
html += %Q|t.parentNode.insertBefore(s, t);\n|
|
17
|
+
html += %Q|})(); /* ]]> */ </script>|
|
18
|
+
html
|
19
|
+
end
|
20
|
+
def flattr_large_counter(param={})
|
21
|
+
url = param[:url] ? param[:url] : site.base_url + self.url
|
22
|
+
title = param[:title] ? param[:title] : self.title
|
23
|
+
category = param[:category] ? param[:category] : "text"
|
24
|
+
tags = param[:tags] ? "tags:" + param[:tags] + ";" : ""
|
25
|
+
html = %Q|<a class="FlattrButton" style="display:none;" href="#{url}" title="#{title}" |
|
26
|
+
html += %Q|rev="flattr;uid:#{site.flattr_username};category:#{category};#{tags}"></a>|
|
27
|
+
html
|
28
|
+
end
|
29
|
+
def flattr_compact_counter(param={})
|
30
|
+
url = param[:url] ? param[:url] : site.base_url + self.url
|
31
|
+
title = param[:title] ? param[:title] : self.title
|
32
|
+
category = param[:category] ? param[:category] : "text"
|
33
|
+
tags = param[:tags] ? "tags:" + param[:tags] + ";" : ""
|
34
|
+
html = %Q|<a class="FlattrButton" style="display:none;" href="#{url}" title="#{title}" |
|
35
|
+
html += %Q|rev="flattr;button:compact;uid:#{site.flattr_username};category:#{category};#{tags}"></a>|
|
36
|
+
html
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Awestruct
|
2
|
+
module Extensions
|
3
|
+
module GoogleAnalytics
|
4
|
+
|
5
|
+
def google_analytics()
|
6
|
+
html = ''
|
7
|
+
html += %Q(<script type="text/javascript">\n)
|
8
|
+
html += %Q(var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");\n)
|
9
|
+
html += %Q(document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));\n)
|
10
|
+
html += %Q(</script>\n)
|
11
|
+
html += %Q(<script type="text/javascript">\n)
|
12
|
+
html += %Q(try {\n)
|
13
|
+
html += %Q(var pageTracker = _gat._getTracker("#{site.google_analytics}");\n)
|
14
|
+
html += %Q(pageTracker._trackPageview();\n)
|
15
|
+
html += %Q(} catch(err) {}</script>\n)
|
16
|
+
html
|
17
|
+
end
|
18
|
+
|
19
|
+
def google_analytics_async()
|
20
|
+
html = ''
|
21
|
+
html += %Q(<script type="text/javascript">\n)
|
22
|
+
html += %Q(var _gaq = _gaq || [];\n)
|
23
|
+
html += %Q(_gaq.push(['_setAccount','#{site.google_analytics}']);\n)
|
24
|
+
if site.google_analytics_anonymize
|
25
|
+
html += %Q(_gaq.push(['_gat._anonymizeIp']);\n)
|
26
|
+
end
|
27
|
+
html += %Q(_gaq.push(['_trackPageview']);\n)
|
28
|
+
html += %Q[(function() {\n]
|
29
|
+
html += %Q( var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\n)
|
30
|
+
html += %Q( ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\n)
|
31
|
+
html += %Q( var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\n)
|
32
|
+
html += %Q[})();\n</script>\n]
|
33
|
+
html
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Awestruct
|
2
|
+
module Extensions
|
3
|
+
class Gsub
|
4
|
+
|
5
|
+
def initialize(pattern, replacement, options = {})
|
6
|
+
@pattern = pattern
|
7
|
+
@replacement = replacement
|
8
|
+
@gsub_required = options[:gsub_required] || lambda { |site, page| page.output_path.end_with?(".html") }
|
9
|
+
end
|
10
|
+
|
11
|
+
def transform(site, page, rendered)
|
12
|
+
if (@gsub_required.call(site, page))
|
13
|
+
rendered = rendered.gsub(@pattern, @replacement)
|
14
|
+
end
|
15
|
+
rendered
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Awestruct
|
2
|
+
module Extensions
|
3
|
+
class Indexifier
|
4
|
+
|
5
|
+
def execute(site)
|
6
|
+
site.pages.each do |page|
|
7
|
+
if ( page.inhibit_indexifier || ( page.output_path =~ /^(.*\/)?index.html$/ ) )
|
8
|
+
# skip it!
|
9
|
+
else
|
10
|
+
page.output_path = page.output_path.gsub( /.html$/, '/index.html' )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Awestruct
|
4
|
+
module Extensions
|
5
|
+
class IntenseDebate
|
6
|
+
|
7
|
+
def execute(site)
|
8
|
+
site.pages.each{|p| p.extend IntenseDebatable }
|
9
|
+
end
|
10
|
+
|
11
|
+
module IntenseDebatable
|
12
|
+
def intense_debate_comments()
|
13
|
+
post_id = self.post_id ? self.post_id : Digest::SHA1.hexdigest( self.url )
|
14
|
+
html = %Q(<script>\n)
|
15
|
+
html += %Q( var idcomments_acct='#{site.intense_debate_acct}';\n)
|
16
|
+
html += %Q( var idcomments_post_id='#{post_id}';\n )
|
17
|
+
html += %Q( var idcomments_post_url='#{site.intense_debate_base_url || site.base_url}#{self.url}';\n)
|
18
|
+
html += %Q(</script>\n)
|
19
|
+
html += %Q(<span id="IDCommentsPostTitle" style="display:none"></span>\n)
|
20
|
+
html += %Q(<script type='text/javascript' src='http://www.intensedebate.com/js/genericCommentWrapperV2.js'></script>\n)
|
21
|
+
html
|
22
|
+
end
|
23
|
+
|
24
|
+
def intense_debate_comments_link()
|
25
|
+
post_id = self.post_id ? self.post_id : Digest::SHA1.hexdigest( self.url )
|
26
|
+
html = %Q(<script>\n)
|
27
|
+
html += %Q( var idcomments_acct='#{site.intense_debate_acct}';\n)
|
28
|
+
html += %Q( var idcomments_post_id='#{post_id}';\n )
|
29
|
+
html += %Q( var idcomments_post_url='#{self.url}';\n)
|
30
|
+
html += %Q(</script>\n)
|
31
|
+
html += %Q(<script type='text/javascript' src='http://www.intensedebate.com/js/genericLinkWrapperV2.js'></script>\n)
|
32
|
+
html
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Awestruct:Extensions:Minify is a transformer that minimizes JavaScript, CSS and HTML files.
|
5
|
+
# The transform runs on the rendered stream before it's written to the output path.
|
6
|
+
#
|
7
|
+
# Minification is performed by the following three libraries:
|
8
|
+
#
|
9
|
+
# htmlcompressor (minifies HTML): http://code.google.com/p/htmlcompressor/
|
10
|
+
# yuicompressor (minifies JavaScript and CSS): http://developer.yahoo.com/yui/compressor/
|
11
|
+
# pngcrush (minifies PNG): http://pmt.sourceforge.net/pngcrush/
|
12
|
+
#
|
13
|
+
# These commands must be available on your PATH in order to use them.
|
14
|
+
#
|
15
|
+
# This class is loaded as a transformer in the Awestruct pipeline. The
|
16
|
+
# constructor accepts an array of symbols representing the file types to minimize.
|
17
|
+
#
|
18
|
+
# extension Awestruct::Extensions::Minify.new
|
19
|
+
#
|
20
|
+
# This transform recognizes the following symbols:
|
21
|
+
#
|
22
|
+
# :css - CSS files with extension .css
|
23
|
+
# :js - JavaScript files with extension .js
|
24
|
+
# :html - HTML files with extension .html
|
25
|
+
# :png - PNG files with extension .png
|
26
|
+
#
|
27
|
+
# If no types are specified, the default value [:css, :js] is used.
|
28
|
+
#
|
29
|
+
# In addition to registering the transformer in the pipeline, it must be enabled
|
30
|
+
# by setting the following site property in _ext/config.yml:
|
31
|
+
#
|
32
|
+
# minify: true
|
33
|
+
#
|
34
|
+
# You can limit activation to one or more profiles:
|
35
|
+
#
|
36
|
+
# profiles:
|
37
|
+
# production:
|
38
|
+
# minify: true
|
39
|
+
#
|
40
|
+
# You can also configure the option arguments passed to the compressor programs. Here's
|
41
|
+
# how you specify options arguments for the htmlcompressor command:
|
42
|
+
#
|
43
|
+
# minify_html_opts:
|
44
|
+
# remove_intertag_spaces: true
|
45
|
+
# compress_js: true
|
46
|
+
# compress_css: true
|
47
|
+
#
|
48
|
+
# Note that any hypen (-) must be represented as an underscore (_) in the configuration.
|
49
|
+
|
50
|
+
module Awestruct
|
51
|
+
module Extensions
|
52
|
+
class Minify
|
53
|
+
|
54
|
+
def initialize(types = [ :css, :js ])
|
55
|
+
@types = types
|
56
|
+
end
|
57
|
+
|
58
|
+
def transform(site, page, input)
|
59
|
+
if site.minify
|
60
|
+
ext = File.extname(page.output_path)[1..-1].to_sym
|
61
|
+
if @types.include?(ext)
|
62
|
+
case ext
|
63
|
+
when :html
|
64
|
+
print "minifying html #{page.output_path}"
|
65
|
+
htmlcompressor(page, input, site.minify_html_opts)
|
66
|
+
when :css
|
67
|
+
print "minifying css #{page.output_path}"
|
68
|
+
yuicompressor(page, input, :css)
|
69
|
+
when :js
|
70
|
+
print "minifying js #{page.output_path}"
|
71
|
+
yuicompressor(page, input, :js)
|
72
|
+
when :png
|
73
|
+
print "minifying png #{page.output_path}"
|
74
|
+
pngcrush(page, input)
|
75
|
+
else
|
76
|
+
input
|
77
|
+
end
|
78
|
+
else
|
79
|
+
input
|
80
|
+
end
|
81
|
+
else
|
82
|
+
input
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def htmlcompressor(page, input, minify_html_opts)
|
89
|
+
output = ''
|
90
|
+
cmd = "htmlcompressor"
|
91
|
+
if minify_html_opts
|
92
|
+
minify_html_opts.each do |p, v|
|
93
|
+
# boolean options are simple flags (i.e., do not accept values)
|
94
|
+
if v === true
|
95
|
+
cmd += " --#{p.gsub('_', '-')}"
|
96
|
+
elsif not v === false
|
97
|
+
cmd += " --#{p.gsub('_', '-')}=#{v}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr|
|
102
|
+
threads = []
|
103
|
+
threads << Thread.new(stdout) do |o|
|
104
|
+
while ( ! o.eof? )
|
105
|
+
output << o.readline
|
106
|
+
end
|
107
|
+
end
|
108
|
+
threads << Thread.new(stdin) do |i|
|
109
|
+
i.write input
|
110
|
+
i.close
|
111
|
+
end
|
112
|
+
threads.each{ |t|t.join }
|
113
|
+
end
|
114
|
+
|
115
|
+
input_len = input.length
|
116
|
+
output_len = output.length
|
117
|
+
|
118
|
+
if input_len > output_len
|
119
|
+
puts " %d bytes -> %d bytes = %.1f%%" % [ input_len, output_len, 100 * output_len/input_len ]
|
120
|
+
output
|
121
|
+
else
|
122
|
+
puts " no gain"
|
123
|
+
input
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def yuicompressor(page, input, type)
|
128
|
+
output = ''
|
129
|
+
Open3.popen3("yuicompressor --type #{type}") do |stdin, stdout, stderr|
|
130
|
+
threads = []
|
131
|
+
threads << Thread.new(stdout) do |o|
|
132
|
+
while ( ! o.eof? )
|
133
|
+
output << o.readline
|
134
|
+
end
|
135
|
+
end
|
136
|
+
threads << Thread.new(stdin) do |i|
|
137
|
+
i.write input
|
138
|
+
i.close
|
139
|
+
end
|
140
|
+
threads.each{ |t|t.join }
|
141
|
+
end
|
142
|
+
|
143
|
+
input_len = input.length
|
144
|
+
output_len = output.length
|
145
|
+
|
146
|
+
if input_len > output_len
|
147
|
+
puts " %d bytes -> %d bytes = %.1f%%" % [ input_len, output_len, 100 * output_len/input_len ]
|
148
|
+
output
|
149
|
+
else
|
150
|
+
puts " no gain"
|
151
|
+
input
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def pngcrush(page, input)
|
156
|
+
filename = page.source_path
|
157
|
+
cmd = "pngcrush #{filename} /tmp/pngcrush"
|
158
|
+
`#{cmd}`
|
159
|
+
if $?.exitstatus != 0
|
160
|
+
raise "Failed to execute pngcrush: #{cmd}"
|
161
|
+
end
|
162
|
+
output = File.read('/tmp/pngcrush')
|
163
|
+
|
164
|
+
input_len = File.stat(filename).size
|
165
|
+
output_len = output.length
|
166
|
+
|
167
|
+
if input_len > output_len
|
168
|
+
puts " %d bytes -> %d bytes = %.1f%%" % [ input_len, output_len, 100 * output_len/input_len ]
|
169
|
+
output
|
170
|
+
else
|
171
|
+
puts " no gain"
|
172
|
+
input
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|