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,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
+