mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 903149 - Part 1: Add jsmin Python package; r=glandium
Exported source code from changeset fdfa1b187681 from https://bitbucket.org/dcs/jsmin without modifications.
This commit is contained in:
parent
ee89582a3d
commit
3a87989877
195
python/jsmin/jsmin/__init__.py
Normal file
195
python/jsmin/jsmin/__init__.py
Normal file
@ -0,0 +1,195 @@
|
||||
# This code is original from jsmin by Douglas Crockford, it was translated to
|
||||
# Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2013 Dave St.Germain
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
|
||||
import sys
|
||||
is_3 = sys.version_info >= (3, 0)
|
||||
if is_3:
|
||||
import io
|
||||
else:
|
||||
import StringIO
|
||||
try:
|
||||
import cStringIO
|
||||
except ImportError:
|
||||
cStringIO = None
|
||||
|
||||
|
||||
__all__ = ['jsmin', 'JavascriptMinify']
|
||||
__version__ = '2.0.3'
|
||||
|
||||
|
||||
def jsmin(js):
|
||||
"""
|
||||
returns a minified version of the javascript string
|
||||
"""
|
||||
if not is_3:
|
||||
if cStringIO and not isinstance(js, unicode):
|
||||
# strings can use cStringIO for a 3x performance
|
||||
# improvement, but unicode (in python2) cannot
|
||||
klass = cStringIO.StringIO
|
||||
else:
|
||||
klass = StringIO.StringIO
|
||||
else:
|
||||
klass = io.StringIO
|
||||
ins = klass(js)
|
||||
outs = klass()
|
||||
JavascriptMinify(ins, outs).minify()
|
||||
return outs.getvalue()
|
||||
|
||||
|
||||
class JavascriptMinify(object):
|
||||
"""
|
||||
Minify an input stream of javascript, writing
|
||||
to an output stream
|
||||
"""
|
||||
|
||||
def __init__(self, instream=None, outstream=None):
|
||||
self.ins = instream
|
||||
self.outs = outstream
|
||||
|
||||
def minify(self, instream=None, outstream=None):
|
||||
if instream and outstream:
|
||||
self.ins, self.outs = instream, outstream
|
||||
write = self.outs.write
|
||||
read = self.ins.read
|
||||
|
||||
space_strings = "abcdefghijklmnopqrstuvwxyz"\
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
|
||||
starters, enders = '{[(+-', '}])+-"\''
|
||||
newlinestart_strings = starters + space_strings
|
||||
newlineend_strings = enders + space_strings
|
||||
do_newline = False
|
||||
do_space = False
|
||||
doing_single_comment = False
|
||||
previous_before_comment = ''
|
||||
doing_multi_comment = False
|
||||
in_re = False
|
||||
in_quote = ''
|
||||
quote_buf = []
|
||||
|
||||
previous = read(1)
|
||||
next1 = read(1)
|
||||
if previous == '/':
|
||||
if next1 == '/':
|
||||
doing_single_comment = True
|
||||
elif next1 == '*':
|
||||
doing_multi_comment = True
|
||||
else:
|
||||
write(previous)
|
||||
elif not previous:
|
||||
return
|
||||
elif previous >= '!':
|
||||
if previous in "'\"":
|
||||
in_quote = previous
|
||||
write(previous)
|
||||
previous_non_space = previous
|
||||
else:
|
||||
previous_non_space = ' '
|
||||
if not next1:
|
||||
return
|
||||
|
||||
while 1:
|
||||
next2 = read(1)
|
||||
if not next2:
|
||||
last = next1.strip()
|
||||
if not (doing_single_comment or doing_multi_comment)\
|
||||
and last not in ('', '/'):
|
||||
write(last)
|
||||
break
|
||||
if doing_multi_comment:
|
||||
if next1 == '*' and next2 == '/':
|
||||
doing_multi_comment = False
|
||||
next2 = read(1)
|
||||
elif doing_single_comment:
|
||||
if next1 in '\r\n':
|
||||
doing_single_comment = False
|
||||
while next2 in '\r\n':
|
||||
next2 = read(1)
|
||||
if not next2:
|
||||
break
|
||||
if previous_before_comment in ')}]':
|
||||
do_newline = True
|
||||
elif previous_before_comment in space_strings:
|
||||
write('\n')
|
||||
elif in_quote:
|
||||
quote_buf.append(next1)
|
||||
|
||||
if next1 == in_quote:
|
||||
numslashes = 0
|
||||
for c in reversed(quote_buf[:-1]):
|
||||
if c != '\\':
|
||||
break
|
||||
else:
|
||||
numslashes += 1
|
||||
if numslashes % 2 == 0:
|
||||
in_quote = ''
|
||||
write(''.join(quote_buf))
|
||||
elif next1 in '\r\n':
|
||||
if previous_non_space in newlineend_strings \
|
||||
or previous_non_space > '~':
|
||||
while 1:
|
||||
if next2 < '!':
|
||||
next2 = read(1)
|
||||
if not next2:
|
||||
break
|
||||
else:
|
||||
if next2 in newlinestart_strings \
|
||||
or next2 > '~' or next2 == '/':
|
||||
do_newline = True
|
||||
break
|
||||
elif next1 < '!' and not in_re:
|
||||
if (previous_non_space in space_strings \
|
||||
or previous_non_space > '~') \
|
||||
and (next2 in space_strings or next2 > '~'):
|
||||
do_space = True
|
||||
elif next1 == '/':
|
||||
if in_re:
|
||||
if previous != '\\':
|
||||
in_re = False
|
||||
write('/')
|
||||
elif next2 == '/':
|
||||
doing_single_comment = True
|
||||
previous_before_comment = previous_non_space
|
||||
elif next2 == '*':
|
||||
doing_multi_comment = True
|
||||
else:
|
||||
in_re = previous_non_space in '(,=:[?!&|'
|
||||
write('/')
|
||||
else:
|
||||
if do_space:
|
||||
do_space = False
|
||||
write(' ')
|
||||
if do_newline:
|
||||
write('\n')
|
||||
do_newline = False
|
||||
write(next1)
|
||||
if not in_re and next1 in "'\"":
|
||||
in_quote = next1
|
||||
quote_buf = []
|
||||
previous = next1
|
||||
next1 = next2
|
||||
|
||||
if previous >= '!':
|
||||
previous_non_space = previous
|
252
python/jsmin/jsmin/test.py
Normal file
252
python/jsmin/jsmin/test.py
Normal file
@ -0,0 +1,252 @@
|
||||
import unittest
|
||||
import jsmin
|
||||
import sys
|
||||
|
||||
class JsTests(unittest.TestCase):
|
||||
def _minify(self, js):
|
||||
return jsmin.jsmin(js)
|
||||
|
||||
def assertEqual(self, thing1, thing2):
|
||||
if thing1 != thing2:
|
||||
print(repr(thing1), repr(thing2))
|
||||
raise AssertionError
|
||||
return True
|
||||
|
||||
def assertMinified(self, js_input, expected):
|
||||
minified = jsmin.jsmin(js_input)
|
||||
assert minified == expected, "%r != %r" % (minified, expected)
|
||||
|
||||
def testQuoted(self):
|
||||
js = r'''
|
||||
Object.extend(String, {
|
||||
interpret: function(value) {
|
||||
return value == null ? '' : String(value);
|
||||
},
|
||||
specialChar: {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'\\': '\\\\'
|
||||
}
|
||||
});
|
||||
|
||||
'''
|
||||
expected = r"""Object.extend(String,{interpret:function(value){return value==null?'':String(value);},specialChar:{'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','\\':'\\\\'}});"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testSingleComment(self):
|
||||
js = r'''// use native browser JS 1.6 implementation if available
|
||||
if (Object.isFunction(Array.prototype.forEach))
|
||||
Array.prototype._each = Array.prototype.forEach;
|
||||
|
||||
if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
|
||||
|
||||
// hey there
|
||||
function() {// testing comment
|
||||
foo;
|
||||
//something something
|
||||
|
||||
location = 'http://foo.com;'; // goodbye
|
||||
}
|
||||
//bye
|
||||
'''
|
||||
expected = r"""
|
||||
if(Object.isFunction(Array.prototype.forEach))
|
||||
Array.prototype._each=Array.prototype.forEach;if(!Array.prototype.indexOf)Array.prototype.indexOf=function(item,i){ function(){ foo; location='http://foo.com;';}"""
|
||||
# print expected
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testEmpty(self):
|
||||
self.assertMinified('', '')
|
||||
self.assertMinified(' ', '')
|
||||
self.assertMinified('\n', '')
|
||||
self.assertMinified('\r\n', '')
|
||||
self.assertMinified('\t', '')
|
||||
|
||||
|
||||
def testMultiComment(self):
|
||||
js = r"""
|
||||
function foo() {
|
||||
print('hey');
|
||||
}
|
||||
/*
|
||||
if(this.options.zindex) {
|
||||
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
||||
this.element.style.zIndex = this.options.zindex;
|
||||
}
|
||||
*/
|
||||
another thing;
|
||||
"""
|
||||
expected = r"""function foo(){print('hey');}
|
||||
another thing;"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testLeadingComment(self):
|
||||
js = r"""/* here is a comment at the top
|
||||
|
||||
it ends here */
|
||||
function foo() {
|
||||
alert('crud');
|
||||
}
|
||||
|
||||
"""
|
||||
expected = r"""function foo(){alert('crud');}"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testJustAComment(self):
|
||||
self.assertMinified(' // a comment', '')
|
||||
|
||||
def testRe(self):
|
||||
js = r'''
|
||||
var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
|
||||
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
|
||||
});'''
|
||||
expected = r"""var str=this.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"/g,'');return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);});"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testIgnoreComment(self):
|
||||
js = r"""
|
||||
var options_for_droppable = {
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
tree: options.tree,
|
||||
hoverclass: options.hoverclass,
|
||||
onHover: Sortable.onHover
|
||||
}
|
||||
|
||||
var options_for_tree = {
|
||||
onHover: Sortable.onEmptyHover,
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass
|
||||
}
|
||||
|
||||
// fix for gecko engine
|
||||
Element.cleanWhitespace(element);
|
||||
"""
|
||||
expected = r"""var options_for_droppable={overlap:options.overlap,containment:options.containment,tree:options.tree,hoverclass:options.hoverclass,onHover:Sortable.onHover}
|
||||
var options_for_tree={onHover:Sortable.onEmptyHover,overlap:options.overlap,containment:options.containment,hoverclass:options.hoverclass}
|
||||
Element.cleanWhitespace(element);"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testHairyRe(self):
|
||||
js = r"""
|
||||
inspect: function(useDoubleQuotes) {
|
||||
var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
|
||||
var character = String.specialChar[match[0]];
|
||||
return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
|
||||
});
|
||||
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
|
||||
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
return this.inspect(true);
|
||||
},
|
||||
|
||||
unfilterJSON: function(filter) {
|
||||
return this.sub(filter || Prototype.JSONFilter, '#{1}');
|
||||
},
|
||||
"""
|
||||
expected = r"""inspect:function(useDoubleQuotes){var escapedString=this.gsub(/[\x00-\x1f\\]/,function(match){var character=String.specialChar[match[0]];return character?character:'\\u00'+match[0].charCodeAt().toPaddedString(2,16);});if(useDoubleQuotes)return'"'+escapedString.replace(/"/g,'\\"')+'"';return"'"+escapedString.replace(/'/g,'\\\'')+"'";},toJSON:function(){return this.inspect(true);},unfilterJSON:function(filter){return this.sub(filter||Prototype.JSONFilter,'#{1}');},"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testNoBracesWithComment(self):
|
||||
js = r"""
|
||||
onSuccess: function(transport) {
|
||||
var js = transport.responseText.strip();
|
||||
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
||||
throw 'Server returned an invalid collection representation.';
|
||||
this._collection = eval(js);
|
||||
this.checkForExternalText();
|
||||
}.bind(this),
|
||||
onFailure: this.onFailure
|
||||
});
|
||||
"""
|
||||
expected = r"""onSuccess:function(transport){var js=transport.responseText.strip();if(!/^\[.*\]$/.test(js))
|
||||
throw'Server returned an invalid collection representation.';this._collection=eval(js);this.checkForExternalText();}.bind(this),onFailure:this.onFailure});"""
|
||||
self.assertMinified(js, expected)
|
||||
|
||||
def testSpaceInRe(self):
|
||||
js = r"""
|
||||
num = num.replace(/ /g,'');
|
||||
"""
|
||||
self.assertMinified(js, "num=num.replace(/ /g,'');")
|
||||
|
||||
def testEmptyString(self):
|
||||
js = r'''
|
||||
function foo('') {
|
||||
|
||||
}
|
||||
'''
|
||||
self.assertMinified(js, "function foo(''){}")
|
||||
|
||||
def testDoubleSpace(self):
|
||||
js = r'''
|
||||
var foo = "hey";
|
||||
'''
|
||||
self.assertMinified(js, 'var foo="hey";')
|
||||
|
||||
def testLeadingRegex(self):
|
||||
js = r'/[d]+/g '
|
||||
self.assertMinified(js, js.strip())
|
||||
|
||||
def testLeadingString(self):
|
||||
js = r"'a string in the middle of nowhere'; // and a comment"
|
||||
self.assertMinified(js, "'a string in the middle of nowhere';")
|
||||
|
||||
def testSingleCommentEnd(self):
|
||||
js = r'// a comment\n'
|
||||
self.assertMinified(js, '')
|
||||
|
||||
def testInputStream(self):
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
ins = StringIO(r'''
|
||||
function foo('') {
|
||||
|
||||
}
|
||||
''')
|
||||
outs = StringIO()
|
||||
m = jsmin.JavascriptMinify()
|
||||
m.minify(ins, outs)
|
||||
output = outs.getvalue()
|
||||
assert output == "function foo(''){}"
|
||||
|
||||
def testUnicode(self):
|
||||
instr = u'\u4000 //foo'
|
||||
expected = u'\u4000'
|
||||
output = jsmin.jsmin(instr)
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def testCommentBeforeEOF(self):
|
||||
self.assertMinified("//test\r\n", "")
|
||||
|
||||
def testCommentInObj(self):
|
||||
self.assertMinified("""{
|
||||
a: 1,//comment
|
||||
}""", "{a:1,}")
|
||||
|
||||
def testCommentInObj2(self):
|
||||
self.assertMinified("{a: 1//comment\r\n}", "{a:1\n}")
|
||||
|
||||
def testImplicitSemicolon(self):
|
||||
# return \n 1 is equivalent with return; 1
|
||||
# so best make sure jsmin retains the newline
|
||||
self.assertMinified("return;//comment\r\na", "return;a")
|
||||
|
||||
def testImplicitSemicolon2(self):
|
||||
self.assertMinified("return//comment...\r\na", "return\na")
|
||||
|
||||
def testSingleComment2(self):
|
||||
self.assertMinified('x.replace(/\//, "_")// slash to underscore',
|
||||
'x.replace(/\//,"_")')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
5
python/jsmin/setup.cfg
Normal file
5
python/jsmin/setup.cfg
Normal file
@ -0,0 +1,5 @@
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
35
python/jsmin/setup.py
Normal file
35
python/jsmin/setup.py
Normal file
@ -0,0 +1,35 @@
|
||||
from setuptools import setup
|
||||
|
||||
import os, sys, re
|
||||
|
||||
os.environ['COPYFILE_DISABLE'] = 'true' # this disables including resource forks in tar files on os x
|
||||
|
||||
|
||||
extra = {}
|
||||
if sys.version_info >= (3,0):
|
||||
extra['use_2to3'] = True
|
||||
|
||||
setup(
|
||||
name="jsmin",
|
||||
version=re.search(r'__version__ = ["\']([^"\']+)', open('jsmin/__init__.py').read()).group(1),
|
||||
packages=['jsmin'],
|
||||
description='JavaScript minifier.',
|
||||
author='Dave St.Germain',
|
||||
author_email='dave@st.germa.in',
|
||||
test_suite='jsmin.test.JsTests',
|
||||
license='MIT License',
|
||||
url='https://bitbucket.org/dcs/jsmin/',
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
'Topic :: Software Development :: Pre-processors',
|
||||
'Topic :: Text Processing :: Filters',
|
||||
],
|
||||
**extra
|
||||
)
|
Loading…
Reference in New Issue
Block a user