grimen-packr 3.1.2

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.
@@ -0,0 +1,20 @@
1
+ === 3.1.1 / 2011-07-19
2
+
3
+ * Remove dependency on Hoe
4
+
5
+ === 3.1.0 / 2009-02-22
6
+
7
+ * Project is now a gem, not a Rails plugin
8
+ * Improved local variable compression
9
+ * Private field obfuscation
10
+ * Changed variable protection, protected variables must be supplied
11
+ as options to Packr#pack.
12
+
13
+ === 1.0.2 / 2007-12-13
14
+
15
+ * Added variable protection, protecting $super for use with Prototype
16
+
17
+ === 1.0.0 / 2007-12-04
18
+
19
+ * Initial release, compatible with Packer 3.0
20
+
@@ -0,0 +1,108 @@
1
+ = PackR
2
+
3
+ * http://github.com/jcoglan/packr
4
+ * http://dean.edwards.name/packer/
5
+ * http://base2.googlecode.com
6
+
7
+
8
+ == Description
9
+
10
+ PackR is a Ruby version of Dean Edwards' JavaScript compressor.
11
+
12
+
13
+ == Features
14
+
15
+ * Whitespace and comment removal
16
+ * Compression of local variable names
17
+ * Compression and obfuscation of 'private' (_underscored) identifiers
18
+ * Base-62 encoding
19
+
20
+
21
+ == Synopsis
22
+
23
+ To call from within a Ruby program:
24
+
25
+ require 'packr'
26
+
27
+ code = File.read('my_script.js')
28
+ compressed = Packr.pack(code)
29
+ File.open('my_script.min.js', 'wb') { |f| f.write(compressed) }
30
+
31
+ This method takes a number of options to control compression, for example:
32
+
33
+ compressed = Packr.pack(code, :shrink_vars => true, :base62 => true)
34
+
35
+ The full list of available options is:
36
+
37
+ * <tt>:shrink_vars</tt> -- set to +true+ to compress local variable names
38
+ * <tt>:private</tt> -- set to +true+ to obfuscate 'private' identifiers, i.e.
39
+ names beginning with a single underscore
40
+ * <tt>:base62</tt> -- encode the program using base 62
41
+ * <tt>:protect</tt> -- an array of variable names to protect from compression, e.g.
42
+
43
+ compressed = Packr.pack(code, :shrink_vars => true,
44
+ :protect => %w[$super self])
45
+
46
+ To call from the command line (use <tt>packr --help</tt> to see available
47
+ options):
48
+
49
+ packr my_script.js > my_script.min.js
50
+
51
+
52
+ == Notes
53
+
54
+ This program is not a JavaScript parser, and rewrites your files using regular
55
+ expressions. Be sure to include semicolons and braces everywhere they are
56
+ required so that your program will work correctly when packed down to a single
57
+ line.
58
+
59
+ By far the most efficient way to serve JavaScript over the web is to use PackR
60
+ with the --shrink-vars flag, combined with gzip compression. If you don't have
61
+ access to your server config to set up mod_deflate, you can generate gzip files
62
+ using (on Unix-like systems):
63
+
64
+ packr -s my-file.js | gzip > my-file.js.gz
65
+
66
+ You can then get Apache to serve the files by putting this in your .htaccess
67
+ file:
68
+
69
+ AddEncoding gzip .gz
70
+ RewriteCond %{HTTP:Accept-encoding} gzip
71
+ RewriteCond %{HTTP_USER_AGENT} !Safari
72
+ RewriteCond %{REQUEST_FILENAME}.gz -f
73
+ RewriteRule ^(.*)$ $1.gz [QSA,L]
74
+
75
+ If you really cannot serve gzip files, use the --base62 option to further
76
+ compress your code. This mode is at its best when compressing large files with
77
+ many repeated tokens.
78
+
79
+ The --private option can be used to stop other programs calling private methods
80
+ in your code by renaming anything beginning with a single underscore. Beware
81
+ that you should not use this if the generated file contains 'private' methods
82
+ that need to be accessible by other files. Also know that all the files that
83
+ access any particular private method must be compressed together so they all get
84
+ the same rewritten name for the private method.
85
+
86
+
87
+ == License
88
+
89
+ (The MIT License)
90
+
91
+ Copyright (c) 2004-2011 Dean Edwards, James Coglan
92
+
93
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
94
+ this software and associated documentation files (the 'Software'), to deal in
95
+ the Software without restriction, including without limitation the rights to use,
96
+ copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
97
+ Software, and to permit persons to whom the Software is furnished to do so,
98
+ subject to the following conditions:
99
+
100
+ The above copyright notice and this permission notice shall be included in all
101
+ copies or substantial portions of the Software.
102
+
103
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
104
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
105
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
106
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
107
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
108
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'oyster'
5
+ require File.expand_path('../../lib/packr', __FILE__)
6
+
7
+ spec = Oyster.spec do
8
+ name "packr -- JavaScript code compressor based on Dean Edwards' Packer"
9
+
10
+ synopsis <<-EOS
11
+ packr [OPTIONS] INPUT_FILES > OUTPUT_FILE
12
+ cat INPUT_FILES | packr [OPTIONS] > OUTPUT_FILE
13
+ EOS
14
+
15
+ description <<-EOS
16
+ PackR is a program for compressing JavaScript programs. It can remove
17
+ whitespace and comments, compress local variable names, compress/obfuscate
18
+ private identifiers, and encode the program in base-62.
19
+
20
+ When invoked from the command line, it concatenates all the code in
21
+ INPUT_FILES (or from standard input) and compresses the code using the given
22
+ options, printing the result to standard output. You can pipe this output into
23
+ another file to save it.
24
+ EOS
25
+
26
+ flag :'shrink-vars', :default => true,
27
+ :desc => 'Shrink local variable names inside functions'
28
+
29
+ flag :private, :default => false,
30
+ :desc => 'Obfuscate private identifiers, i.e. names beginning with a single underscore'
31
+
32
+ flag :base62, :default => false,
33
+ :desc => 'Encode the program using base 62'
34
+
35
+ array :protect, :default => [],
36
+ :desc => 'List of variable names to protect from compression when using --shrink-vars'
37
+
38
+ notes <<-EOS
39
+ This program is not a JavaScript parser, and rewrites your files using regular
40
+ expressions. Be sure to include semicolons and braces everywhere they are
41
+ required so that your program will work correctly when packed down to a single
42
+ line.
43
+
44
+ By far the most efficient way to serve JavaScript over the web is to use PackR
45
+ with the --shrink-vars flag, combined with gzip compression. If you don't have
46
+ access to your server config to set up mod_deflate, you can generate gzip
47
+ files using (on Unix-like systems):
48
+
49
+ packr -s my-file.js | gzip > my-file.js.gz
50
+
51
+ You can then get Apache to serve the files by putting this in your .htaccess
52
+ file:
53
+
54
+ AddEncoding gzip .gz
55
+ RewriteCond %{HTTP:Accept-encoding} gzip
56
+ RewriteCond %{HTTP_USER_AGENT} !Safari
57
+ RewriteCond %{REQUEST_FILENAME}.gz -f
58
+ RewriteRule ^(.*)$ $1.gz [QSA,L]
59
+
60
+ If you really cannot serve gzip files, use the --base62 option to further
61
+ compress your code. This mode is at its best when compressing large files with
62
+ many repeated tokens.
63
+
64
+ The --private option can be used to stop other programs calling private
65
+ methods in your code by renaming anything beginning with a single underscore.
66
+ Beware that you should not use this if the generated file contains 'private'
67
+ methods that need to be accessible by other files. Also know that all the
68
+ files that access any particular private method must be compressed together so
69
+ they all get the same rewritten name for the private method.
70
+ EOS
71
+
72
+ author <<-EOS
73
+ Original JavaScript version by Dean Edwards, Ruby port by James Coglan <jcoglan@googlemail.com>
74
+ EOS
75
+
76
+ copyright <<-EOS
77
+ Copyright (c) 2004-2011 Dean Edwards, James Coglan. This program is free
78
+ software, distributed under the MIT license.
79
+ EOS
80
+ end
81
+
82
+ begin
83
+ opts = spec.parse
84
+
85
+ inputs = opts[:unclaimed]
86
+ code = inputs.empty? ?
87
+ $stdin.read :
88
+ inputs.map { |f| File.read(f) }.join("\n")
89
+
90
+ $stdout.puts Packr.pack(code,
91
+ :shrink_vars => !!opts[:'shrink-vars'],
92
+ :protect => opts[:protect],
93
+ :private => !!opts[:private],
94
+ :base62 => !!opts[:base62])
95
+
96
+ rescue Oyster::HelpRendered
97
+ end
@@ -0,0 +1,55 @@
1
+ module Packr
2
+
3
+ autoload :Map, 'packr/map'
4
+ autoload :Collection, 'packr/collection'
5
+ autoload :RegexpGroup, 'packr/regexp_group'
6
+ autoload :Encoder, 'packr/encoder'
7
+ autoload :Parser, 'packr/parser'
8
+ autoload :Minifier, 'packr/minifier'
9
+ autoload :Privates, 'packr/privates'
10
+ autoload :Shrinker, 'packr/shrinker'
11
+ autoload :Words, 'packr/words'
12
+ autoload :Base62, 'packr/base62'
13
+ autoload :Engine, 'packr/engine'
14
+
15
+ IGNORE = RegexpGroup::IGNORE
16
+ REMOVE = ""
17
+ SPACE = " "
18
+
19
+ DATA = Parser.new.
20
+ put("STRING1", IGNORE).
21
+ put('STRING2', IGNORE).
22
+ put("CONDITIONAL", IGNORE). # conditional comments
23
+ put("(OPERATOR)\\s*(REGEXP)", "\\1\\2")
24
+
25
+ def self.encode62(c)
26
+ (c < 62 ? '' : encode62((c / 62.0).to_i)) +
27
+ ((c = c % 62) > 35 ? (c+29).chr : c.to_s(36))
28
+ end
29
+
30
+ def self.encode52(c)
31
+ # Base52 encoding (a-Z)
32
+ encode = lambda do |d|
33
+ (d < 52 ? '' : encode.call((d / 52.0).to_i)) +
34
+ ((d = d % 52) > 25 ? (d + 39).chr : (d + 97).chr)
35
+ end
36
+ encoded = encode.call(c.to_i)
37
+ encoded = encoded[1..-1] + '0' if encoded =~ /^(do|if|in)$/
38
+ encoded
39
+ end
40
+
41
+ def self.rescape(string)
42
+ string.gsub(/([\/()\[\]{}|*+-.,^$?\\])/) { |m| "\\#{$1}" }
43
+ end
44
+
45
+ def self.pack(script, options = {})
46
+ @packr ||= Packr::Engine.new
47
+ @packr.pack(script, options)
48
+ end
49
+
50
+ def self.new
51
+ Packr::Engine.new
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,150 @@
1
+ module Packr
2
+ class Base62 < Encoder
3
+
4
+ WORDS = /\b[\da-zA-Z]\b|\w{2,}/
5
+
6
+ ENCODE10 = "String"
7
+ ENCODE36 = "function(c){return c.toString(36)}"
8
+ ENCODE62 = "function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))}"
9
+
10
+ UNPACK = lambda do |p,a,c,k,e,r|
11
+ "eval(function(p,a,c,k,e,r){e=#{e};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];" +
12
+ "k=[function(e){return r[e]||e}];e=function(){return'#{r}'};c=1};while(c--)if(k[c])p=p." +
13
+ "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('#{p}',#{a},#{c},'#{k}'.split('|'),0,{}))"
14
+ end
15
+
16
+ def encode(script)
17
+ words = search(script)
18
+ words.sort!
19
+
20
+ encoded = Collection.new # a dictionary of base62 -> base10
21
+ words.size.times { |i| encoded.put(Packr.encode62(i), i) }
22
+
23
+ replacement = lambda { |word| words.get(word).replacement }
24
+
25
+ index = 0
26
+ words.each do |word, key|
27
+ if encoded.has?(word)
28
+ word.index = encoded.get(word)
29
+ def word.to_s; ""; end
30
+ else
31
+ index += 1 while words.has?(Packr.encode62(index))
32
+ word.index = index
33
+ index += 1
34
+ if word.count == 1
35
+ def word.to_s; ""; end
36
+ end
37
+ end
38
+ word.replacement = Packr.encode62(word.index)
39
+ if word.replacement.length == word.to_s.length
40
+ def word.to_s; ""; end
41
+ end
42
+ end
43
+
44
+ # sort by encoding
45
+ words.sort! { |word1, word2| word1.index - word2.index }
46
+
47
+ # trim unencoded words
48
+ words = words.slice(0, get_key_words(words).split("|").length)
49
+
50
+ script = script.gsub(get_pattern(words), &replacement)
51
+
52
+ # build the packed script
53
+
54
+ p = escape(script)
55
+ a = "[]"
56
+ c = get_count(words)
57
+ k = get_key_words(words)
58
+ e = get_encoder(words)
59
+ d = get_decoder(words)
60
+
61
+ # the whole thing
62
+ UNPACK.call(p,a,c,k,e,d)
63
+ end
64
+
65
+ def search(script)
66
+ words = Words.new
67
+ script.scan(WORDS).each { |word| words.add(word) }
68
+ words
69
+ end
70
+
71
+ def escape(script)
72
+ # Single quotes wrap the final string so escape them.
73
+ # Also, escape new lines (required by conditional comments).
74
+ script.gsub(/([\\'])/) { |match| "\\#{$1}" }.gsub(/[\r\n]+/, "\\n")
75
+ end
76
+
77
+ def get_count(words)
78
+ size = words.size
79
+ size.zero? ? 1 : size
80
+ end
81
+
82
+ def get_decoder(words)
83
+ # returns a pattern used for fast decoding of the packed script
84
+ trim = RegexpGroup.new.
85
+ put("(\\d)(\\|\\d)+\\|(\\d)", "\\1-\\3").
86
+ put("([a-z])(\\|[a-z])+\\|([a-z])", "\\1-\\3").
87
+ put("([A-Z])(\\|[A-Z])+\\|([A-Z])", "\\1-\\3").
88
+ put("\\|", "")
89
+
90
+ pattern = trim.exec(words.map { |word, key|
91
+ word.to_s.empty? ? "" : word.replacement
92
+ }[0...62].join("|"))
93
+
94
+ return "^$" if pattern.empty?
95
+
96
+ pattern = "[#{pattern}]"
97
+
98
+ size = words.size
99
+ if size > 62
100
+ pattern = "(#{pattern}|"
101
+ c = Packr.encode62(size)[0].chr
102
+ if c > "9"
103
+ pattern += "[\\\\d"
104
+ if c >= "a"
105
+ pattern += "a"
106
+ if c >= "z"
107
+ pattern += "-z"
108
+ if c >= "A"
109
+ pattern += "A"
110
+ pattern += "-#{c}" if c > "A"
111
+ end
112
+ elsif c == "b"
113
+ pattern += "-#{c}"
114
+ end
115
+ end
116
+ pattern += "]"
117
+ elsif c == "9"
118
+ pattern += "\\\\d"
119
+ elsif c == "2"
120
+ pattern += "[12]"
121
+ elsif c == "1"
122
+ pattern += "1"
123
+ else
124
+ pattern += "[1-#{c}]"
125
+ end
126
+
127
+ pattern += "\\\\w)"
128
+ end
129
+ pattern
130
+ end
131
+
132
+ def get_encoder(words)
133
+ c = words.size
134
+ self.class.const_get("ENCODE#{c > 10 ? (c > 36 ? 62 : 36) : 10}")
135
+ end
136
+
137
+ def get_key_words(words)
138
+ words.map { |word, key| word.to_s }.join("|").gsub(/\|+$/, "")
139
+ end
140
+
141
+ def get_pattern(words)
142
+ words = words.map { |word, key| word.to_s }.join("|").gsub(/\|{2,}/, "|").gsub(/^\|+|\|+$/, "")
143
+ words = "\\x0" if words == ""
144
+ string = "\\b(#{words})\\b"
145
+ %r{#{string}}
146
+ end
147
+
148
+ end
149
+ end
150
+
@@ -0,0 +1,147 @@
1
+ module Packr
2
+ # A Map that is more array-like (accessible by index).
3
+ class Collection < Map
4
+
5
+ attr_reader :values
6
+ attr_writer :keys
7
+
8
+ def initialize(values = nil)
9
+ @keys = []
10
+ super(values)
11
+ end
12
+
13
+ def add(key, item = nil)
14
+ # Duplicates not allowed using add().
15
+ # But you can still overwrite entries using put().
16
+ return if has?(key)
17
+ put(key, item)
18
+ end
19
+
20
+ def clear
21
+ super
22
+ @keys.clear
23
+ end
24
+
25
+ def copy
26
+ copy = super
27
+ copy.keys = @keys.dup
28
+ copy
29
+ end
30
+
31
+ def each
32
+ @keys.each { |key| yield(get(key), key) }
33
+ end
34
+
35
+ def get_at(index)
36
+ index += @keys.length if index < 0 # starting from the end
37
+ key = @keys[index]
38
+ key.nil? ? nil : @values[key.to_s]
39
+ end
40
+
41
+ def get_keys
42
+ @keys.dup
43
+ end
44
+
45
+ def index_of(key)
46
+ @keys.index(key.to_s)
47
+ end
48
+
49
+ def insert_at(index, key, item = nil)
50
+ return if @keys[index].nil?
51
+ @keys.insert(index, key.to_s)
52
+ @values[key.to_s] = nil # placeholder
53
+ put(key, item)
54
+ end
55
+
56
+ def item(key_or_index)
57
+ __send__(key_or_index.is_a?(Numeric) ? :get_at : :get, key_or_index)
58
+ end
59
+
60
+ def map
61
+ @keys.map { |key| yield(get(key), key) }
62
+ end
63
+
64
+ def merge(*args)
65
+ args.each do |values|
66
+ values.is_a?(Collection) ?
67
+ values.each { |item, key| put(key, item) } :
68
+ super(values)
69
+ end
70
+ self
71
+ end
72
+
73
+ # TODO update this method
74
+ def put(key, item = nil)
75
+ item ||= key
76
+ @keys << key.to_s unless has?(key.to_s)
77
+ begin; klass = self.class::Item; rescue; end
78
+ item = self.class.create(key, item) if klass and !item.is_a?(klass)
79
+ @values[key.to_s] = item
80
+ self
81
+ end
82
+
83
+ def put_at(index, item = nil)
84
+ key = @keys[index]
85
+ return if key.nil?
86
+ put(key, item)
87
+ end
88
+
89
+ def remove(key)
90
+ if has?(key)
91
+ @keys.delete(key.to_s)
92
+ @values.delete(key.to_s)
93
+ end
94
+ end
95
+
96
+ def remove_at(index)
97
+ key = @keys.delete_at(index)
98
+ @values.delete(key)
99
+ end
100
+
101
+ def reverse!
102
+ @keys.reverse!
103
+ self
104
+ end
105
+
106
+ def size
107
+ @keys.length
108
+ end
109
+
110
+ def slice(start, fin)
111
+ sliced = copy
112
+ if start
113
+ keys, removed = @keys, @keys
114
+ sliced.keys = @keys[start...fin]
115
+ if sliced.size.nonzero?
116
+ removed = removed[0...start]
117
+ removed = removed + keys[fin..-1] if fin
118
+ end
119
+ removed.each do |remov|
120
+ sliced.values.delete(remov)
121
+ end
122
+ end
123
+ sliced
124
+ end
125
+
126
+ def sort!(&compare)
127
+ if block_given?
128
+ @keys.sort! do |key1, key2|
129
+ compare.call(@values[key1], @values[key2], key1, key2)
130
+ end
131
+ else
132
+ @keys.sort!
133
+ end
134
+ self
135
+ end
136
+
137
+ def to_s
138
+ "(#{@keys.join(',')})"
139
+ end
140
+
141
+ def self.create(key, item)
142
+ begin; klass = self::Item; rescue; end
143
+ klass ? klass.new(key, item) : item
144
+ end
145
+
146
+ end
147
+ end