html_press 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ /.project
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in html_press.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/Readme.md ADDED
@@ -0,0 +1,66 @@
1
+ #html_press
2
+
3
+ ## Alternatives
4
+
5
+ ###Ruby
6
+
7
+ - https://github.com/completelynovel/html_compressor
8
+ - https://github.com/MadRabbit/frontcompiler
9
+
10
+ ###Other
11
+
12
+ - http://code.google.com/p/htmlcompressor/
13
+ - smarty `strip` tag
14
+ - W3 total cache (WP plugin from smashingmagazine contains html minifier)
15
+
16
+ ## TODO
17
+
18
+ - bin
19
+ - Support other minifiers (Closure, YUI compressor)
20
+ - htmlTydi
21
+
22
+ ## Usage
23
+
24
+ ### Jekyll
25
+
26
+ Gemfile
27
+
28
+ ```ruby
29
+ gem "jekyll"
30
+ gem "html_press"
31
+ ```
32
+
33
+ _plugins/strip_tag.rb
34
+
35
+ ```ruby
36
+ module Jekyll
37
+ class StripTag < Liquid::Block
38
+ begin
39
+ require 'html_press'
40
+ def render(context)
41
+ text = super
42
+ HtmlPress.compress text
43
+ end
44
+ rescue LoadError => e
45
+ p "Unable to load 'html_press'"
46
+ end
47
+ end
48
+ end
49
+
50
+ Liquid::Template.register_tag('strip', Jekyll::StripTag)
51
+ ```
52
+
53
+ In templates
54
+
55
+ ```liquid
56
+ {% strip %}
57
+ here goes text...
58
+ {% endstrip %}
59
+ ```
60
+
61
+ Run
62
+
63
+ ```
64
+ bundle install
65
+ bundle exec jelyll
66
+ ```
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "html_press/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "html_press"
7
+ s.version = HtmlPress::VERSION
8
+ s.authors = ["stereobooster"]
9
+ s.email = ["stereobooster@gmail.com"]
10
+ s.homepage = "https://github.com/stereobooster/html_press"
11
+ s.summary = %q{Compress html}
12
+ s.description = %q{Ruby gem for compressing html}
13
+
14
+ s.rubyforge_project = "html_press"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # s.add_dependency "nokogiri"
22
+
23
+ s.add_development_dependency "rspec"
24
+ s.add_development_dependency "rake"
25
+
26
+ s.add_runtime_dependency "rainpress"
27
+ s.add_runtime_dependency "uglifier"
28
+ end
@@ -0,0 +1,181 @@
1
+ module HtmlPress
2
+ class Html
3
+
4
+ def self.compress text
5
+ Html.new.compile text
6
+ end
7
+
8
+ def compile (html)
9
+
10
+ out = html + ""
11
+
12
+ @replacement_hash = 'MINIFYHTML' + Time.now.to_i.to_s
13
+ @placeholders = []
14
+ @strip_crlf = false
15
+
16
+ # replace SCRIPTs (and minify) with placeholders
17
+ out.gsub! /\s*(<script\b[^>]*?>[\s\S]*?<\/script>)\s*/i do |m|
18
+ m.gsub!(/^\s+|\s+$/, '')
19
+ js = m.gsub(/\s*<script\b[^>]*?>([\s\S]*?)<\/script>\s*/i , "\\1")
20
+ begin
21
+ js_compressed = HtmlPress.js_compressor js
22
+ m.gsub!(js, js_compressed)
23
+ rescue Exception => e
24
+ # p e.message
25
+ end
26
+ reserve m
27
+ end
28
+
29
+ # replace STYLEs (and minify) with placeholders
30
+ out.gsub! /\s*(<style\b[^>]*?>[\s\S]*?<\/style>)\s*/i do |m|
31
+ m.gsub!(/^\s+|\s+$/, '')
32
+ css = m.gsub(/\s*<style\b[^>]*?>([\s\S]*?)<\/style>\s*/i, "\\1")
33
+ begin
34
+ css_compressed = HtmlPress.css_compressor css
35
+ m.gsub!(css, css_compressed)
36
+ rescue Exception => e
37
+ # p e.message
38
+ end
39
+ reserve m
40
+ end
41
+
42
+ # IE conditional comments
43
+ out.gsub! /\s*(<!--\[[^\]]+\]>[\s\S]*?<!\[[^\]]+\]-->)\s*/ do |m|
44
+ m.gsub!(/^\s+|\s+$/, '')
45
+ comment = m.gsub(/\s*<!--\[[^\]]+\]>([\s\S]*?)<!\[[^\]]+\]-->\s*/, "\\1")
46
+ comment_compressed = Html.new.compile(comment)
47
+ m.gsub!(comment, comment_compressed)
48
+ reserve m
49
+ end
50
+
51
+ # remove out comments (not containing IE conditional comments).
52
+ out.gsub! /<!--([\s\S]*?)-->/ do |m|
53
+ ''
54
+ end
55
+
56
+ # replace PREs with placeholders
57
+ out.gsub! /\s*(<pre\b[^>]*?>[\s\S]*?<\/pre>)\s*/i do |m|
58
+ pre = m.gsub(/\s*<pre\b[^>]*?>([\s\S]*?)<\/pre>\s*/i, "\\1")
59
+ pre_compressed = pre.gsub(/\s+$/, '')
60
+ m.gsub!(pre, pre_compressed)
61
+ reserve m
62
+ end
63
+
64
+ # replace TEXTAREAs with placeholders
65
+ out.gsub! /\s*(<textarea\b[^>]*?>[\s\S]*?<\/textarea>)\s*/i do |m|
66
+ reserve m
67
+ end
68
+
69
+ # trim each line.
70
+ # @todo take into account attribute values that span multiple lines.
71
+ out.gsub!(/^\s+|\s+$/m, '')
72
+
73
+ re = '\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' +
74
+ '|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' +
75
+ '|frame(?:set)?|h[1-6]|head|hr|out|legend|li|link|map|menu|meta' +
76
+ '|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h|r|foot|itle)' +
77
+ '|ul)\\b[^>]*>)'
78
+
79
+ re = Regexp.new(re)
80
+ out.gsub!(re, '\\1')
81
+
82
+ # remove ws outside of all elements
83
+ out.gsub! />([^<]+)</ do |m|
84
+ m.gsub(/^\s+|\s+$/, ' ')
85
+ end
86
+
87
+ # use newlines before 1st attribute in open tags (to limit line lengths)
88
+ # out.gsub!(/(<[a-z\-:]+)\s+([^>]+>)/i, "\\1\n\\2")
89
+
90
+ # match attributes
91
+ out.gsub! /<[a-z\-:]+\s([^>]+)>/i do |m|
92
+ reserve attrs(m, '[a-z\-:]+', true)
93
+ end
94
+
95
+ out.gsub!(/[\r\n]+/, @strip_crlf ? ' ' : "\n")
96
+
97
+ out.gsub!(/\s+/, ' ')
98
+
99
+ # fill placeholders
100
+ re = Regexp.new('%' + @replacement_hash + '%(\d+)%')
101
+ out.gsub! re do |m|
102
+ m.gsub!(re, "\\1")
103
+ @placeholders[m.to_i]
104
+ end
105
+
106
+ out
107
+ end
108
+
109
+ def reserve(content)
110
+ @placeholders.push content
111
+ '%' + @replacement_hash + '%' + (@placeholders.size - 1).to_s + '%'
112
+ end
113
+
114
+ def attrs (m, tag_name, r)
115
+ re = "<(" + tag_name + ")(\s[^>]+)?>"
116
+ re = Regexp.new(re, true)
117
+ attributes = m.gsub(re, "\\2")
118
+ if r
119
+ tag = m.gsub(re, "\\1")
120
+ else
121
+ tag = tag_name
122
+ end
123
+
124
+ if attributes.size > 0
125
+ attributes_compressed = attributes.gsub(/\s*([a-z\-_]+(="[^"]*")?(='[^']*')?)\s*/i, " \\1")
126
+
127
+ attributes_compressed.gsub! /([a-z\-_]+="[^"]*")/ do |k|
128
+ attr k, "\"", tag
129
+ end
130
+
131
+ attributes_compressed.gsub! /([a-z\-_]+='[^']*')/ do |k|
132
+ attr k, "'", tag
133
+ end
134
+
135
+ return m.gsub(attributes, attributes_compressed)
136
+ end
137
+
138
+ return m
139
+ end
140
+
141
+ def attr(attribute, delimiter, tag)
142
+ re = "([a-z\\-_]+)(=" + delimiter + "[^" + delimiter + "]*" + delimiter + ")?"
143
+ re = Regexp.new re
144
+ value = attribute.gsub(re, "\\2")
145
+
146
+ if tag == "script"
147
+ name = attribute.gsub(re, "\\1")
148
+ if name == "type" || name == "language"
149
+ return ""
150
+ end
151
+ end
152
+
153
+ if value.size != 0
154
+
155
+ name = attribute.gsub(re, "\\1")
156
+
157
+ re = "^=" + delimiter + "|" + delimiter + "$"
158
+ re = Regexp.new re
159
+ value.gsub!(re, "")
160
+
161
+ if name == "style"
162
+ value = HtmlPress.css_compressor value
163
+ end
164
+
165
+ if name == "class"
166
+ value.gsub!(/\s+/, " ")
167
+ value.gsub!(/^\s+|\s+$/, "")
168
+ end
169
+
170
+ # if name == "onclick"
171
+ # value = HtmlPress.js_compressor value
172
+ # end
173
+
174
+ attribute = name + "=" + delimiter + value + delimiter
175
+ end
176
+
177
+ attribute
178
+ end
179
+
180
+ end
181
+ end
@@ -0,0 +1,12 @@
1
+ module HtmlPress
2
+ begin
3
+ require 'rainpress'
4
+ def self.css_compressor (text)
5
+ Rainpress.compress(text).gsub(/^\s+/m, '')
6
+ end
7
+ rescue LoadError => e
8
+ def self.css_compressor (text)
9
+ text
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module HtmlPress
2
+ begin
3
+ require 'uglifier'
4
+ def self.js_compressor (text)
5
+ Uglifier.new.compile(text).gsub(/;$/,'')
6
+ end
7
+ rescue LoadError => e
8
+ def self.js_compressor (text)
9
+ text
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module HtmlPress
2
+ VERSION = "0.0.1"
3
+ end
data/lib/html_press.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "html_press/version"
2
+ require "html_press/rainpress"
3
+ require "html_press/uglifier"
4
+ require "html_press/html"
5
+
6
+ module HtmlPress
7
+ def self.compress(text)
8
+ self::Html.new.compile text
9
+ end
10
+ end
@@ -0,0 +1,98 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative "../lib/html_press"
4
+
5
+ describe HtmlPress do
6
+ before :each do
7
+ end
8
+
9
+ it "should leave only one whitespace between inline tags" do
10
+ HtmlPress.compress("<p>lorem <b>ipsum</b> <i>dolor</i> </p>").should eql "<p>lorem <b>ipsum</b> <i>dolor</i></p>"
11
+ end
12
+
13
+ it "should leave no whitespaces between block tags" do
14
+ HtmlPress.compress("<div></div> \t\r\n <div></div>").should eql "<div></div><div></div>"
15
+ end
16
+
17
+ it "should leave only one whitespace in text" do
18
+ HtmlPress.compress("<p>a a</p>").should eql "<p>a a</p>"
19
+ end
20
+
21
+ it "should leave newlines in pre tags and remove trailing spaces" do
22
+ HtmlPress.compress("<pre>a \t </pre>").should eql "<pre>a</pre>"
23
+ HtmlPress.compress("<pre>qwe \nasd </pre>").should eql "<pre>qwe\nasd</pre>"
24
+ end
25
+
26
+ it "should leave textareas as is" do
27
+ text = "<textarea> \t </textarea>"
28
+ HtmlPress.compress(text).should eql text
29
+ end
30
+
31
+ it "should compress js in script tags" do
32
+ script = " (function(undefined){ \t\n var long_name = ' '; }()) \n \r"
33
+ compressed_script = "<script>" + HtmlPress.js_compressor(script) + "</script>"
34
+ script = " <script>" + script + "</script> "
35
+ HtmlPress.compress(script).should eql compressed_script
36
+ end
37
+
38
+ it "should compress css in style tags" do
39
+ style = " div { margin: 0px 0px; \n} "
40
+ compressed_style = "<style>" + HtmlPress.css_compressor(style) + "</style>"
41
+ style = " <style>" + style + "</style> "
42
+ HtmlPress.compress(style).should eql compressed_style
43
+ end
44
+
45
+ it "should remove html comments" do
46
+ HtmlPress.compress("<p></p><!-- comment --><p></p>").should eql "<p></p><p></p>"
47
+ end
48
+
49
+ it "should leave IE conditional comments" do
50
+ text = "<!--[if IE]><html class=\"ie\"><![endif]--><div></div>"
51
+ HtmlPress.compress(text).should eql text
52
+ end
53
+
54
+ it "should remove unnecessary whitespaces between attributes" do
55
+ HtmlPress.compress("<p class=\"a\" id=\"b\"></p>").should eql "<p class=\"a\" id=\"b\"></p>"
56
+ end
57
+
58
+ it "should leave whitespaces everywhere else" do
59
+ text = "<a onclick=\"alert(' ')\" unknown_attr=' a a'>a</a>"
60
+ HtmlPress.compress(text).should eql text
61
+ end
62
+
63
+ it "should work with special utf-8 symbols" do
64
+ HtmlPress.compress("✪<p></p> <p></p>").should eql "✪<p></p><p></p>"
65
+ end
66
+
67
+ it "should work with tags in upper case" do
68
+ HtmlPress.compress("<P> </p>").should eql "<P></p>"
69
+ end
70
+
71
+ it "should remove whitespaces between IE conditional comments" do
72
+ text = "<p></p> <!--[if IE]><html class=\"ie\"> <![endif]--> <!--[if IE]><html class=\"ie1\"><![endif]-->"
73
+ text2 = "<p></p><!--[if IE]><html class=\"ie\"><![endif]--><!--[if IE]><html class=\"ie1\"><![endif]-->"
74
+ HtmlPress.compress(text).should eql text2
75
+ end
76
+
77
+ it "should treat text inside IE conditional comments as it was without comments" do
78
+ text = "<div class=\"a\" id=\"b\"> </div> <p></p>"
79
+ text2 = HtmlPress.compress(text)
80
+ text = "<!--[if IE]>" + text + "<![endif]-->"
81
+ text2 = "<!--[if IE]>" + text2 + "<![endif]-->"
82
+ HtmlPress.compress(text).should eql text2
83
+ end
84
+
85
+ it "should remove unnecessary whitespaces in html attributes (class)" do
86
+ HtmlPress.compress("<p class=\"a b\"></p>").should eql "<p class=\"a b\"></p>"
87
+ HtmlPress.compress("<p class='a b'></p>").should eql "<p class='a b'></p>"
88
+ end
89
+
90
+ it "should remove unnecessary whitespaces in html attributes (style)" do
91
+ HtmlPress.compress("<p style=\"display: none;\"></p>").should eql "<p style=\"display:none;\"></p>"
92
+ end
93
+
94
+ # it "should remove unnecessary attributes" do
95
+ # HtmlPress.compress("<script type=\"text/javascript\">var a;</script>").should eql "<script>var a;</script>"
96
+ # end
97
+
98
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: html_press
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - stereobooster
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &19832364 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *19832364
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &20531592 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *20531592
36
+ - !ruby/object:Gem::Dependency
37
+ name: rainpress
38
+ requirement: &20756364 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *20756364
47
+ - !ruby/object:Gem::Dependency
48
+ name: uglifier
49
+ requirement: &21077148 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *21077148
58
+ description: Ruby gem for compressing html
59
+ email:
60
+ - stereobooster@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - Rakefile
68
+ - Readme.md
69
+ - html_press.gemspec
70
+ - lib/html_press.rb
71
+ - lib/html_press/html.rb
72
+ - lib/html_press/rainpress.rb
73
+ - lib/html_press/uglifier.rb
74
+ - lib/html_press/version.rb
75
+ - spec/html_press_spec.rb
76
+ homepage: https://github.com/stereobooster/html_press
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project: html_press
96
+ rubygems_version: 1.8.15
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: Compress html
100
+ test_files: []