Project

General

Profile

1
#!/usr/bin/python
2

    
3
# This code is original from jsmin by Douglas Crockford, it was translated to
4
# Python by Baruch Even. The original code had the following copyright and
5
# license.
6
#
7
# /* jsmin.c
8
#    2003-04-21
9
# 
10
# Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
11
# 
12
# Permission is hereby granted, free of charge, to any person obtaining a copy of
13
# this software and associated documentation files (the "Software"), to deal in
14
# the Software without restriction, including without limitation the rights to
15
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16
# of the Software, and to permit persons to whom the Software is furnished to do
17
# so, subject to the following conditions:
18
# 
19
# The above copyright notice and this permission notice shall be included in all
20
# copies or substantial portions of the Software.
21
# 
22
# The Software shall be used for Good, not Evil.
23
# 
24
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
# SOFTWARE.
31
# */
32

    
33
from StringIO import StringIO
34

    
35
def jsmin(js):
36
	ins = StringIO(js)
37
	outs = StringIO()
38
	JavascriptMinify().minify(ins, outs)
39
	str = outs.getvalue()
40
	if len(str) > 0 and str[0] == '\n':
41
		str = str[1:]
42
	return str
43

    
44
def isAlphanum(c):
45
	"""return true if the character is a letter, digit, underscore,
46
           dollar sign, or non-ASCII character.
47
	"""
48
	return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
49
	        (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
50

    
51
class UnterminatedComment(Exception):
52
	pass
53

    
54
class UnterminatedStringLiteral(Exception):
55
	pass
56

    
57
class UnterminatedRegularExpression(Exception):
58
	pass
59

    
60
class JavascriptMinify(object):
61

    
62
	def _outA(self):
63
		self.outstream.write(self.theA)
64
	def _outB(self):
65
		self.outstream.write(self.theB)
66

    
67
	def _get(self):
68
		"""return the next character from stdin. Watch out for lookahead. If
69
		   the character is a control character, translate it to a space or
70
		   linefeed.
71
		"""
72
		c = self.theLookahead
73
		self.theLookahead = None
74
		if c == None:
75
			c = self.instream.read(1)
76
		if c >= ' ' or c == '\n':
77
			return c
78
		if c == '': # EOF
79
			return '\000'
80
		if c == '\r':
81
			return '\n'
82
		return ' '
83

    
84
	def _peek(self):
85
		self.theLookahead = self._get()
86
		return self.theLookahead
87

    
88
	def _next(self):
89
		"""get the next character, excluding comments. peek() is used to see
90
		   if a '/' is followed by a '/' or '*'.
91
		"""
92
		c = self._get()
93
		if c == '/':
94
			p = self._peek()
95
			if p == '/':
96
				c = self._get()
97
				while c > '\n':
98
					c = self._get()
99
				return c
100
			if p == '*':
101
				c = self._get()
102
				while 1:
103
					c = self._get()
104
					if c == '*':
105
						if self._peek() == '/':
106
							self._get()
107
							return ' '
108
					if c == '\000':
109
						raise UnterminatedComment()
110

    
111
		return c
112

    
113
	def _action(self, action):
114
		"""do something! What you do is determined by the argument:
115
		   1   Output A. Copy B to A. Get the next B.
116
		   2   Copy B to A. Get the next B. (Delete A).
117
		   3   Get the next B. (Delete B).
118
		   action treats a string as a single character. Wow!
119
		   action recognizes a regular expression if it is preceded by ( or , or =.
120
		"""
121
		if action <= 1:
122
			self._outA()
123
			
124
		if action <= 2:
125
			self.theA = self.theB
126
			if self.theA == "'" or self.theA == '"':
127
				while 1:
128
					self._outA()
129
					self.theA = self._get()
130
					if self.theA == self.theB:
131
						break
132
					if self.theA <= '\n':
133
						raise UnterminatedStringLiteral()
134
					if self.theA == '\\':
135
						self._outA()
136
						self.theA = self._get()
137

    
138

    
139
		if action <= 3:
140
			self.theB = self._next()
141
			if self.theB == '/' and (self.theA == '(' or self.theA == ',' or self.theA == '='):
142
				self._outA()
143
				self._outB()
144
				while 1:
145
					self.theA = self._get()
146
					if self.theA == '/':
147
						break
148
					elif self.theA == '\\':
149
						self._outA()
150
						self.theA = self._get()
151
					elif self.theA <= '\n':
152
						raise UnterminatedRegularExpression()
153
					self._outA()
154
				self.theB = self._next()
155

    
156

    
157
	def _jsmin(self):
158
		"""Copy the input to the output, deleting the characters which are
159
		   insignificant to JavaScript. Comments will be removed. Tabs will be
160
		   replaced with spaces. Carriage returns will be replaced with linefeeds.
161
		   Most spaces and linefeeds will be removed.
162
		"""
163
		self.theA = '\n'
164
		self._action(3)
165

    
166
		while self.theA != '\000':
167
			if self.theA == ' ':
168
				if isAlphanum(self.theB):
169
					self._action(1)
170
				else:
171
					self._action(2)
172
			elif self.theA == '\n':
173
				if self.theB in ['{', '[', '(', '+', '-']:
174
					self._action(1)
175
				elif self.theB == ' ':
176
					self._action(3)
177
				else:
178
					if isAlphanum(self.theB):
179
						self._action(1)
180
					else:
181
						self._action(2)
182
			else:
183
				if self.theB == ' ':
184
					if isAlphanum(self.theA):
185
						self._action(1)
186
					else:
187
						self._action(3)
188
				elif self.theB == '\n':
189
					if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
190
						self._action(1)
191
					else:
192
						if isAlphanum(self.theA):
193
							self._action(1)
194
						else:
195
							self._action(3)
196
				else:
197
					self._action(1)
198

    
199
	def minify(self, instream, outstream):
200
		self.instream = instream
201
		self.outstream = outstream
202
		self.theA = None
203
		self.thaB = None
204
		self.theLookahead = None
205

    
206
		self._jsmin()
207
		self.instream.close()
208

    
209
if __name__ == '__main__':
210
	import sys
211
	jsm = JavascriptMinify()
212
	jsm.minify(sys.stdin, sys.stdout)
(3-3/6)