packr 1.0.2 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +16 -0
- data/Manifest.txt +19 -0
- data/README.txt +112 -0
- data/Rakefile +12 -0
- data/bin/packr +91 -0
- data/lib/packr.rb +68 -240
- data/lib/packr/base62.rb +150 -0
- data/lib/packr/collection.rb +147 -0
- data/lib/packr/constants.rb +8 -0
- data/lib/packr/encoder.rb +35 -0
- data/lib/packr/map.rb +66 -0
- data/lib/packr/minifier.rb +80 -0
- data/lib/packr/parser.rb +21 -0
- data/lib/packr/privates.rb +19 -0
- data/lib/packr/regexp_group.rb +122 -122
- data/lib/packr/shrinker.rb +123 -0
- data/lib/packr/words.rb +39 -84
- data/lib/string.rb +6 -5
- data/test/test_packr.rb +140 -0
- metadata +79 -51
- data/README +0 -118
- data/test/assets/packed/controls.js +0 -1
- data/test/assets/packed/dragdrop.js +0 -1
- data/test/assets/packed/effects.js +0 -1
- data/test/assets/packed/prototype.js +0 -1
- data/test/assets/packed/prototype_shrunk.js +0 -1
- data/test/assets/src/controls.js +0 -833
- data/test/assets/src/dragdrop.js +0 -942
- data/test/assets/src/effects.js +0 -1088
- data/test/assets/src/prototype.js +0 -2515
- data/test/packr_test.rb +0 -68
data/lib/packr/base62.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
class 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
|
+
class 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
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Packr
|
2
|
+
class Encoder
|
3
|
+
|
4
|
+
def initialize(pattern = nil, encoder = nil, ignore = nil)
|
5
|
+
@parser = Parser.new(ignore)
|
6
|
+
@parser.put(pattern, "") if pattern
|
7
|
+
@encoder = encoder
|
8
|
+
end
|
9
|
+
|
10
|
+
def search(script)
|
11
|
+
words = Words.new
|
12
|
+
@parser.put_at(-1, lambda { |word, *args|
|
13
|
+
words.add(word)
|
14
|
+
})
|
15
|
+
@parser.exec(script)
|
16
|
+
words
|
17
|
+
end
|
18
|
+
|
19
|
+
def encode(script)
|
20
|
+
words = search(script)
|
21
|
+
words.sort!
|
22
|
+
index = 0
|
23
|
+
words.each do |word, key|
|
24
|
+
word.encoded = @encoder.call(index)
|
25
|
+
index += 1
|
26
|
+
end
|
27
|
+
@parser.put_at(-1, lambda { |word, *args|
|
28
|
+
words.get(word).encoded
|
29
|
+
})
|
30
|
+
@parser.exec(script)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/lib/packr/map.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
class Packr
|
2
|
+
# This is effectively a wrapper for Hash instances - we're including it
|
3
|
+
# to maintain similarity with the JavaScript version for easier maintainance.
|
4
|
+
class Map
|
5
|
+
|
6
|
+
def initialize(values = nil)
|
7
|
+
@values = {}
|
8
|
+
merge(values) unless values.nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def clear
|
12
|
+
@values.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy
|
16
|
+
self.class.new(@values)
|
17
|
+
end
|
18
|
+
|
19
|
+
def each
|
20
|
+
@values.each { |key, value| yield(value, key) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(key)
|
24
|
+
@values[key.to_s]
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_keys
|
28
|
+
@values.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_values
|
32
|
+
@values.values
|
33
|
+
end
|
34
|
+
|
35
|
+
def has?(key)
|
36
|
+
@values.has_key?(key.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge(*args)
|
40
|
+
args.each do |values|
|
41
|
+
values = values.get_values if values.is_a?(Map)
|
42
|
+
values.each { |key, value| put(key, value) }
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove(key)
|
48
|
+
@values.delete(key.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
def put(key, value = nil)
|
52
|
+
value ||= key
|
53
|
+
# Create the new entry (or overwrite the old entry).
|
54
|
+
@values[key.to_s] = value
|
55
|
+
end
|
56
|
+
|
57
|
+
def size
|
58
|
+
@values.length
|
59
|
+
end
|
60
|
+
|
61
|
+
def union(*values)
|
62
|
+
copy.merge(*values)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class Packr
|
2
|
+
class Minifier
|
3
|
+
|
4
|
+
def self.conditional_comments
|
5
|
+
@@conditional_comments
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@concat = CONCAT.union(DATA)
|
10
|
+
|
11
|
+
def @concat.exec(script)
|
12
|
+
parsed = super(script)
|
13
|
+
while parsed != script
|
14
|
+
script = parsed
|
15
|
+
parsed = super(script)
|
16
|
+
end
|
17
|
+
parsed
|
18
|
+
end
|
19
|
+
|
20
|
+
@comments = DATA.union(COMMENTS)
|
21
|
+
@clean = DATA.union(CLEAN)
|
22
|
+
@whitespace = DATA.union(WHITESPACE)
|
23
|
+
|
24
|
+
@@conditional_comments = @comments.copy
|
25
|
+
@@conditional_comments.put_at(-1, " \\3")
|
26
|
+
@whitespace.remove_at(2) # conditional comments
|
27
|
+
@comments.remove_at(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
def minify(script)
|
31
|
+
# packing with no additional options
|
32
|
+
script += "\n"
|
33
|
+
script = script.gsub(CONTINUE, "")
|
34
|
+
script = @comments.exec(script)
|
35
|
+
script = @clean.exec(script)
|
36
|
+
script = @whitespace.exec(script)
|
37
|
+
script = @concat.exec(script)
|
38
|
+
script
|
39
|
+
end
|
40
|
+
|
41
|
+
CONTINUE = /\\\r?\n/
|
42
|
+
|
43
|
+
CLEAN = Parser.new.
|
44
|
+
put("\\(\\s*([^;)]*)\\s*;\\s*([^;)]*)\\s*;\\s*([^;)]*)\\)", "(\\1;\\2;\\3)"). # for (;;) loops
|
45
|
+
put("throw[^};]+[};]", IGNORE). # a safari 1.3 bug
|
46
|
+
put(";+\\s*([};])", "\\1")
|
47
|
+
|
48
|
+
COMMENTS = Parser.new.
|
49
|
+
put(";;;[^\\n]*\\n", REMOVE).
|
50
|
+
put("(COMMENT1)\\n\\s*(REGEXP)?", "\n\\3").
|
51
|
+
put("(COMMENT2)\\s*(REGEXP)?", lambda do |*args|
|
52
|
+
match, comment, b, regexp = args[0..3]
|
53
|
+
if comment =~ /^\/\*@/ and comment =~ /@\*\/$/
|
54
|
+
# comments = Minifier.conditional_comments.exec(comment)
|
55
|
+
else
|
56
|
+
comment = ""
|
57
|
+
end
|
58
|
+
comment + " " + (regexp || "")
|
59
|
+
end)
|
60
|
+
|
61
|
+
CONCAT = Parser.new.
|
62
|
+
put("(STRING1)\\+(STRING1)", lambda { |*args| args[1][0...-1] + args[3][1..-1] }).
|
63
|
+
put("(STRING2)\\+(STRING2)", lambda { |*args| args[1][0...-1] + args[3][1..-1] })
|
64
|
+
|
65
|
+
WHITESPACE = Parser.new.
|
66
|
+
put("/\\/\\/@[^\\n]*\\n", IGNORE).
|
67
|
+
put("@\\s+\\b", "@ "). # protect conditional comments
|
68
|
+
put("\\b\\s+@", " @").
|
69
|
+
put("(\\d)\\s+(\\.\\s*[a-z\\$_\\[(])", "\\1 \\2"). # http://dean.edwards.name/weblog/2007/04/packer3/#comment84066
|
70
|
+
put("([+-])\\s+([+-])", "\\1 \\2"). # c = a++ +b;
|
71
|
+
put("\\b\\s+\\$\\s+\\b", " $ "). # var $ in
|
72
|
+
put("\\$\\s+\\b", "$ "). # object$ in
|
73
|
+
put("\\b\\s+\\$", " $"). # return $object
|
74
|
+
# put("\\b\\s+#", " #"). # CSS
|
75
|
+
put("\\b\\s+\\b", SPACE).
|
76
|
+
put("\\s+", REMOVE)
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|