Refactor duplicate code for calling into media controls
[WebKit-https.git] / Websites / perf.webkit.org / tools / jsmin.py
1 # This code is original from jsmin by Douglas Crockford, it was translated to
2 # Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
3 #
4 # The MIT License (MIT)
5 #
6 # Copyright (c) 2013 Dave St.Germain
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 # THE SOFTWARE.
25
26
27 import sys
28 is_3 = sys.version_info >= (3, 0)
29 if is_3:
30     import io
31 else:
32     import StringIO
33     try:
34         import cStringIO
35     except ImportError:
36         cStringIO = None
37
38
39 __all__ = ['jsmin', 'JavascriptMinify']
40 __version__ = '2.0.9'
41
42
43 def jsmin(js):
44     """
45     returns a minified version of the javascript string
46     """
47     if not is_3:
48         if cStringIO and not isinstance(js, unicode):
49             # strings can use cStringIO for a 3x performance
50             # improvement, but unicode (in python2) cannot
51             klass = cStringIO.StringIO
52         else:
53             klass = StringIO.StringIO
54     else:
55         klass = io.StringIO
56     ins = klass(js)
57     outs = klass()
58     JavascriptMinify(ins, outs).minify()
59     return outs.getvalue()
60
61
62 class JavascriptMinify(object):
63     """
64     Minify an input stream of javascript, writing
65     to an output stream
66     """
67
68     def __init__(self, instream=None, outstream=None):
69         self.ins = instream
70         self.outs = outstream
71
72     def minify(self, instream=None, outstream=None):
73         if instream and outstream:
74             self.ins, self.outs = instream, outstream
75
76         self.is_return = False
77         self.return_buf = ''
78
79         def write(char):
80             # all of this is to support literal regular expressions.
81             # sigh
82             if char in 'return':
83                 self.return_buf += char
84                 self.is_return = self.return_buf == 'return'
85             self.outs.write(char)
86             if self.is_return:
87                 self.return_buf = ''
88
89         read = self.ins.read
90
91         space_strings = "abcdefghijklmnopqrstuvwxyz"\
92         "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
93         starters, enders = '{[(+-', '}])+-"\''
94         newlinestart_strings = starters + space_strings
95         newlineend_strings = enders + space_strings
96         do_newline = False
97         do_space = False
98         escape_slash_count = 0
99         doing_single_comment = False
100         previous_before_comment = ''
101         doing_multi_comment = False
102         in_re = False
103         in_quote = ''
104         quote_buf = []
105
106         previous = read(1)
107         if previous == '\\':
108             escape_slash_count += 1
109         next1 = read(1)
110         if previous == '/':
111             if next1 == '/':
112                 doing_single_comment = True
113             elif next1 == '*':
114                 doing_multi_comment = True
115                 previous = next1
116                 next1 = read(1)
117             else:
118                 write(previous)
119         elif not previous:
120             return
121         elif previous >= '!':
122             if previous in "'\"":
123                 in_quote = previous
124             write(previous)
125             previous_non_space = previous
126         else:
127             previous_non_space = ' '
128         if not next1:
129             return
130
131         while 1:
132             next2 = read(1)
133             if not next2:
134                 last = next1.strip()
135                 if not (doing_single_comment or doing_multi_comment)\
136                     and last not in ('', '/'):
137                     if in_quote:
138                         write(''.join(quote_buf))
139                     write(last)
140                 break
141             if doing_multi_comment:
142                 if next1 == '*' and next2 == '/':
143                     doing_multi_comment = False
144                     next2 = read(1)
145             elif doing_single_comment:
146                 if next1 in '\r\n':
147                     doing_single_comment = False
148                     while next2 in '\r\n':
149                         next2 = read(1)
150                         if not next2:
151                             break
152                     if previous_before_comment in ')}]':
153                         do_newline = True
154                     elif previous_before_comment in space_strings:
155                         write('\n')
156             elif in_quote:
157                 quote_buf.append(next1)
158
159                 if next1 == in_quote:
160                     numslashes = 0
161                     for c in reversed(quote_buf[:-1]):
162                         if c != '\\':
163                             break
164                         else:
165                             numslashes += 1
166                     if numslashes % 2 == 0:
167                         in_quote = ''
168                         write(''.join(quote_buf))
169             elif next1 in '\r\n':
170                 if previous_non_space in newlineend_strings \
171                     or previous_non_space > '~':
172                     while 1:
173                         if next2 < '!':
174                             next2 = read(1)
175                             if not next2:
176                                 break
177                         else:
178                             if next2 in newlinestart_strings \
179                                 or next2 > '~' or next2 == '/':
180                                 do_newline = True
181                             break
182             elif next1 < '!' and not in_re:
183                 if (previous_non_space in space_strings \
184                     or previous_non_space > '~') \
185                     and (next2 in space_strings or next2 > '~'):
186                     do_space = True
187                 elif previous_non_space in '-+' and next2 == previous_non_space:
188                     # protect against + ++ or - -- sequences
189                     do_space = True
190                 elif self.is_return and next2 == '/':
191                     # returning a regex...
192                     write(' ')
193             elif next1 == '/':
194                 if do_space:
195                     write(' ')
196                 if in_re:
197                     if previous != '\\' or (not escape_slash_count % 2) or next2 in 'gimy':
198                         in_re = False
199                     write('/')
200                 elif next2 == '/':
201                     doing_single_comment = True
202                     previous_before_comment = previous_non_space
203                 elif next2 == '*':
204                     doing_multi_comment = True
205                     previous = next1
206                     next1 = next2
207                     next2 = read(1)
208                 else:
209                     in_re = previous_non_space in '(,=:[?!&|' or self.is_return  # literal regular expression
210                     write('/')
211             else:
212                 if do_space:
213                     do_space = False
214                     write(' ')
215                 if do_newline:
216                     write('\n')
217                     do_newline = False
218
219                 write(next1)
220                 if not in_re and next1 in "'\"`":
221                     in_quote = next1
222                     quote_buf = []
223
224             previous = next1
225             next1 = next2
226
227             if previous >= '!':
228                 previous_non_space = previous
229
230             if previous == '\\':
231                 escape_slash_count += 1
232             else:
233                 escape_slash_count = 0
234
235 if __name__ == '__main__':
236     minifier = JavascriptMinify(sys.stdin, sys.stdout)
237     minifier.minify()
238     sys.stdout.write('\n')