packr 1.0.2 → 3.1.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/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
|
+
|