grimen-packr 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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