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,35 @@
1
+ module 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
+
@@ -0,0 +1,20 @@
1
+ module Packr
2
+ class Engine
3
+
4
+ def initialize
5
+ @minifier = Minifier.new
6
+ @shrinker = Shrinker.new
7
+ @privates = Privates.new
8
+ @base62 = Base62.new
9
+ end
10
+
11
+ def pack(script, options = {})
12
+ script = @minifier.minify(script)
13
+ script = @shrinker.shrink(script, options[:protect]) if options[:shrink_vars]
14
+ script = @privates.encode(script) if options[:private]
15
+ script = @base62.encode(script) if options[:base62]
16
+ script
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,66 @@
1
+ module 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
+ module Packr
2
+ class Minifier
3
+
4
+ def self.conditional_comments
5
+ @@conditional_comments
6
+ end
7
+
8
+ def initialize
9
+ @concat = Packr::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
+ Packr::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
+
@@ -0,0 +1,21 @@
1
+ module Packr
2
+ class Parser < RegexpGroup
3
+
4
+ def put(expression, replacement)
5
+ expression = DICTIONARY.exec(expression) if expression.is_a?(String)
6
+ super(expression, replacement)
7
+ end
8
+
9
+ # STRING1 requires backslashes to fix concat bug
10
+ DICTIONARY = RegexpGroup.new.
11
+ put(:OPERATOR, /return|typeof|[\[(\^=,{}:;&|!*?]/.source).
12
+ put(:CONDITIONAL, /\/\*@\w*|\w*@\*\/|\/\/@\w*|@\w+/.source).
13
+ put(:COMMENT1, /\/\/[^\n]*/.source).
14
+ put(:COMMENT2, /\/\*[^*]*\*+([^\/][^*]*\*+)*\//.source).
15
+ put(:REGEXP, /\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*/.source).
16
+ put(:STRING1, /\'(\\.|[^\'\\])*\'/.source).
17
+ put(:STRING2, /"(\\.|[^"\\])*"/.source)
18
+
19
+ end
20
+ end
21
+
@@ -0,0 +1,19 @@
1
+ module Packr
2
+ class Privates < Encoder
3
+
4
+ IGNORE = {
5
+ :CONDITIONAL => Packr::IGNORE,
6
+ "(OPERATOR)(REGXEP)" => Packr::IGNORE
7
+ }
8
+
9
+ PATTERN = /\b_[\da-zA-Z$][\w$]*\b/
10
+
11
+ def initialize
12
+ super(PATTERN, lambda { |index|
13
+ "_" + Packr.encode62(index)
14
+ }, IGNORE)
15
+ end
16
+
17
+ end
18
+ end
19
+
@@ -0,0 +1,122 @@
1
+ module Packr
2
+ class RegexpGroup < Collection
3
+
4
+ IGNORE = "\\0"
5
+ BACK_REF = /\\(\d+)/
6
+ ESCAPE_CHARS = /\\./
7
+ ESCAPE_BRACKETS = /\(\?[:=!]|\[[^\]]+\]/
8
+ BRACKETS = /\(/
9
+ LOOKUP = /\\(\d+)/
10
+ LOOKUP_SIMPLE = /^\\\d+$/
11
+
12
+ def initialize(values = nil, ignore_case = false)
13
+ super(values)
14
+ @ignore_case = !!ignore_case
15
+ end
16
+
17
+ def exec(string, override = nil)
18
+ string = string.to_s # type-safe
19
+ return string if @keys.empty?
20
+ override = 0 if override == IGNORE
21
+ string.gsub(Regexp.new(self.to_s, @ignore_case && Regexp::IGNORECASE)) do |match|
22
+ offset, i, result = 1, 0, match
23
+ arguments = [match] + $~.captures + [$~.begin(0), string]
24
+ # Loop through the items.
25
+ each do |item, key|
26
+ nxt = offset + item.length + 1
27
+ if arguments[offset] # do we have a result?
28
+ replacement = override.nil? ? item.replacement : override
29
+ case replacement
30
+ when Proc
31
+ result = replacement.call(*arguments[offset...nxt])
32
+ when Numeric
33
+ result = arguments[offset + replacement]
34
+ else
35
+ result = replacement
36
+ end
37
+ end
38
+ offset = nxt
39
+ end
40
+ result
41
+ end
42
+ end
43
+
44
+ def insert_at(index, expression, replacement)
45
+ expression = expression.is_a?(Regexp) ? expression.source : expression.to_s
46
+ super(index, expression, replacement)
47
+ end
48
+
49
+ def test(string)
50
+ exec(string) != string
51
+ end
52
+
53
+ def to_s
54
+ offset = 1
55
+ "(" + map { |item, key|
56
+ # Fix back references.
57
+ expression = item.to_s.gsub(BACK_REF) { |m| "\\" + (offset + $1.to_i) }
58
+ offset += item.length + 1
59
+ expression
60
+ }.join(")|(") + ")"
61
+ end
62
+
63
+ class Item
64
+ attr_accessor :expression, :length, :replacement
65
+
66
+ def initialize(expression, replacement = nil)
67
+ @expression = expression
68
+
69
+ if replacement.nil?
70
+ replacement = IGNORE
71
+ elsif replacement.respond_to?(:replacement)
72
+ replacement = replacement.replacement
73
+ elsif !replacement.is_a?(Proc)
74
+ replacement = replacement.to_s
75
+ end
76
+
77
+ # does the pattern use sub-expressions?
78
+ if replacement.is_a?(String) and replacement =~ LOOKUP
79
+ # a simple lookup? (e.g. "\2")
80
+ if replacement.gsub(/\n/, " ") =~ LOOKUP_SIMPLE
81
+ # store the index (used for fast retrieval of matched strings)
82
+ replacement = replacement[1..-1].to_i
83
+ else # a complicated lookup (e.g. "Hello \2 \1")
84
+ # build a function to do the lookup
85
+ # Improved version by Alexei Gorkov:
86
+ q = '"'
87
+ replacement_string = replacement.
88
+ gsub(/\\/, "\\\\").
89
+ gsub(/"/, "\\x22").
90
+ gsub(/\n/, "\\n").
91
+ gsub(/\r/, "\\r").
92
+ gsub(/\\(\d+)/, q + "+(args[\\1]||" + q+q + ")+" + q).
93
+ gsub(/(['"])\1\+(.*)\+\1\1$/, '\1')
94
+ replacement = lambda { |*args| eval(q + replacement_string + q) }
95
+
96
+ # My old crappy version:
97
+ # q = (replacement.gsub(/\\./, "") =~ /'/) ? '"' : "'"
98
+ # replacement = replacement.gsub(/\r/, "\\r").gsub(/\\(\d+)/,
99
+ # q + "+(args[\\1]||" + q+q + ")+" + q)
100
+ # replacement_string = q + replacement.gsub(/(['"])\1\+(.*)\+\1\1$/, '\1') + q
101
+ # replacement = lambda { |*args| eval(replacement_string) }
102
+ end
103
+ end
104
+
105
+ @length = RegexpGroup.count(@expression)
106
+ @replacement = replacement
107
+ end
108
+
109
+ def to_s
110
+ @expression.respond_to?(:source) ? @expression.source : @expression.to_s
111
+ end
112
+ end
113
+
114
+ def self.count(expression)
115
+ # Count the number of sub-expressions in a Regexp/RegexpGroup::Item.
116
+ expression = expression.to_s.gsub(ESCAPE_CHARS, "").gsub(ESCAPE_BRACKETS, "")
117
+ expression.scan(BRACKETS).length
118
+ end
119
+
120
+ end
121
+ end
122
+
@@ -0,0 +1,123 @@
1
+ module Packr
2
+ class Shrinker
3
+
4
+ ENCODED_DATA = /~\^(\d+)\^~/
5
+ PREFIX = '@'
6
+ SHRUNK = /\@\d+\b/
7
+
8
+ def decode_data(script)
9
+ # put strings and regular expressions back
10
+ script.gsub(ENCODED_DATA) { |match| @strings[$1.to_i] }
11
+ end
12
+
13
+ def encode_data(script)
14
+ # encode strings and regular expressions
15
+ @strings = [] # encoded strings and regular expressions
16
+ DATA.exec(script, lambda { |match, *args|
17
+ operator, regexp = args[0].to_s, args[1].to_s
18
+ replacement = "~^#{@strings.length}^~"
19
+ unless regexp.empty?
20
+ replacement = operator + replacement
21
+ match = regexp
22
+ end
23
+ @strings << match
24
+ replacement
25
+ })
26
+ end
27
+
28
+ def shrink(script, protected_names = [])
29
+ script = encode_data(script)
30
+ protected_names ||= []
31
+ protected_names = protected_names.map { |s| s.to_s }
32
+
33
+ # identify blocks, particularly identify function blocks (which define scope)
34
+ __block = /((catch|do|if|while|with|function)\b[^~{};]*(\(\s*[^{};]*\s*\))\s*)?(\{[^{}]*\})/
35
+ __brackets = /\{[^{}]*\}|\[[^\[\]]*\]|\([^\(\)]*\)|~[^~]+~/
36
+ __encoded_block = /~#?(\d+)~/
37
+ __identifier = /[a-zA-Z_$][\w\$]*/
38
+ __scoped = /~#(\d+)~/
39
+ __var = /\bvar\b/
40
+ __vars = /\bvar\s+[\w$]+[^;#]*|\bfunction\s+[\w$]+/
41
+ __var_tidy = /\b(var|function)\b|\sin\s+[^;]+/
42
+ __var_equal = /\s*=[^,;]*/
43
+
44
+ blocks = [] # store program blocks (anything between braces {})
45
+ total = 0
46
+ # decoder for program blocks
47
+ decode_blocks = lambda do |script, encoded|
48
+ script = script.gsub(encoded) { |match| blocks[$1.to_i] } while script =~ encoded
49
+ script
50
+ end
51
+
52
+ # encoder for program blocks
53
+ encode_blocks = lambda do |match|
54
+ prefix, block_type, args, block = $1 || "", $2, $3, $4
55
+ if block_type == 'function'
56
+ # decode the function block (THIS IS THE IMPORTANT BIT)
57
+ # We are retrieving all sub-blocks and will re-parse them in light
58
+ # of newly shrunk variables
59
+ block = args + decode_blocks.call(block, __scoped)
60
+ prefix = prefix.gsub(__brackets, "")
61
+
62
+ # create the list of variable and argument names
63
+ args = args[1...-1]
64
+
65
+ if args != '_no_shrink_'
66
+ vars = block.scan(__vars).join(";").gsub(__var, ";var")
67
+ vars = vars.gsub(__brackets, "") while vars =~ __brackets
68
+ vars = vars.gsub(__var_tidy, "").gsub(__var_equal, "")
69
+ end
70
+ block = decode_blocks.call(block, __encoded_block)
71
+
72
+ # process each identifier
73
+ if args != '_no_shrink_'
74
+ count, short_id = 0, nil
75
+ ids = [args, vars].join(",").scan(__identifier)
76
+ processed = {}
77
+ ids.each do |id|
78
+ if !processed['#' + id] and !protected_names.include?(id)
79
+ processed['#' + id] = true
80
+ id = Packr.rescape(id)
81
+ # encode variable names
82
+ count += 1 while block =~ Regexp.new("#{PREFIX}#{count}\\b")
83
+ reg = Regexp.new("([^\\w$.])#{id}([^\\w$:])")
84
+ block = block.gsub(reg, "\\1#{PREFIX}#{count}\\2") while block =~ reg
85
+ reg = Regexp.new("([^{,\\w$.])#{id}:")
86
+ block = block.gsub(reg, "\\1#{PREFIX}#{count}:")
87
+ count += 1
88
+ end
89
+ end
90
+ total = [total, count].max
91
+ end
92
+ replacement = "#{prefix}~#{blocks.length}~"
93
+ blocks << block
94
+ else
95
+ replacement = "~##{blocks.length}~"
96
+ blocks << (prefix + block)
97
+ end
98
+ replacement
99
+ end
100
+
101
+ # encode blocks, as we encode we replace variable and argument names
102
+ script = script.gsub(__block, &encode_blocks) while script =~ __block
103
+
104
+ # put the blocks back
105
+ script = decode_blocks.call(script, __encoded_block)
106
+
107
+ short_id, count = nil, 0
108
+ shrunk = Encoder.new(SHRUNK, lambda { |object|
109
+ # find the next free short name
110
+ begin
111
+ short_id = Packr.encode52(count)
112
+ count += 1
113
+ end while script =~ Regexp.new("[^\\w$.]#{short_id}[^\\w$:]")
114
+ short_id
115
+ })
116
+ script = shrunk.encode(script)
117
+
118
+ decode_data(script)
119
+ end
120
+
121
+ end
122
+ end
123
+