Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,2 @@
include js-beautify
include js-beautify-test

View file

@ -0,0 +1,467 @@
from __future__ import print_function
import sys
import re
from cssbeautifier.__version__ import __version__
#
# The MIT License (MIT)
# Copyright (c) 2013 Einar Lielmanis and contributors.
# 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.
class BeautifierOptions:
def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.selector_separator_newline = True
self.end_with_newline = False
self.newline_between_rules = True
self.eol = '\n'
def __repr__(self):
return \
"""indent_size = %d
indent_char = [%s]
indent_with_tabs = [%s]
separate_selectors_newline = [%s]
end_with_newline = [%s]
newline_between_rules = [%s]
""" % (self.indent_size, self.indent_char, self.indent_with_tabs,
self.selector_separator_newline, self.end_with_newline, self.newline_between_rules)
def default_options():
return BeautifierOptions()
def beautify(string, opts=default_options()):
b = Beautifier(string, opts)
return b.beautify()
def beautify_file(file_name, opts=default_options()):
if file_name == '-': # stdin
stream = sys.stdin
else:
stream = open(file_name)
content = ''.join(stream.readlines())
b = Beautifier(content, opts)
return b.beautify()
def usage(stream=sys.stdout):
print("cssbeautifier.py@" + __version__ + """
CSS beautifier (http://jsbeautifier.org/)
""", file=stream)
if stream == sys.stderr:
return 1
else:
return 0
WHITE_RE = re.compile("^\s+$")
WORD_RE = re.compile("[\w$\-_]")
class Printer:
def __init__(self, indent_char, indent_size, default_indent=""):
self.indentSize = indent_size
self.singleIndent = (indent_size) * indent_char
self.indentLevel = 0
self.nestedLevel = 0
self.baseIndentString = default_indent
self.output = []
def __lastCharWhitespace(self):
return len(self.output) > 0 and WHITE_RE.search(self.output[-1]) is not None
def indent(self):
self.indentLevel += 1
self.baseIndentString += self.singleIndent
def outdent(self):
if self.indentLevel:
self.indentLevel -= 1
self.baseIndentString = self.baseIndentString[:-(len(self.singleIndent))]
def push(self, string):
self.output.append(string)
def openBracket(self):
self.singleSpace()
self.output.append("{")
self.newLine()
def closeBracket(self):
self.newLine()
self.output.append("}")
self.newLine()
def semicolon(self):
self.output.append(";")
self.newLine()
def comment(self, comment):
self.output.append(comment)
def newLine(self, keepWhitespace=False):
if len(self.output) > 0 :
if not keepWhitespace and self.output[-1] != '\n':
self.trim()
self.output.append("\n")
if len(self.baseIndentString) > 0:
self.output.append(self.baseIndentString)
def trim(self):
while self.__lastCharWhitespace():
self.output.pop()
def singleSpace(self):
if len(self.output) > 0 and not self.__lastCharWhitespace():
self.output.append(" ")
def preserveSingleSpace(self,isAfterSpace):
if isAfterSpace:
self.singleSpace()
def result(self):
if self.baseIndentString:
return self.baseIndentString + "".join(self.output);
else:
return "".join(self.output)
class Beautifier:
def __init__(self, source_text, opts=default_options()):
# This is not pretty, but given how we did the version import
# it is the only way to do this without having setup.py fail on a missing six dependency.
self.six = __import__("six")
if not source_text:
source_text = ''
# HACK: newline parsing inconsistent. This brute force normalizes the input newlines.
lineBreak = re.compile(self.six.u("\r\n|[\r\u2028\u2029]"))
source_text = re.sub(lineBreak, '\n', source_text)
self.source_text = source_text
self.opts = opts
self.indentSize = opts.indent_size
self.indentChar = opts.indent_char
self.pos = -1
self.ch = None
if self.opts.indent_with_tabs:
self.indentChar = "\t"
self.indentSize = 1
self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
# https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
# also in CONDITIONAL_GROUP_RULE below
self.NESTED_AT_RULE = [ \
"@page", \
"@font-face", \
"@keyframes", \
"@media", \
"@supports", \
"@document"]
self.CONDITIONAL_GROUP_RULE = [ \
"@media", \
"@supports", \
"@document"]
def next(self):
self.pos = self.pos + 1
if self.pos < len(self.source_text):
self.ch = self.source_text[self.pos]
else:
self.ch = ''
return self.ch
def peek(self,skipWhitespace=False):
start = self.pos
if skipWhitespace:
self.eatWhitespace()
result = ""
if self.pos + 1 < len(self.source_text):
result = self.source_text[self.pos + 1]
if skipWhitespace:
self.pos = start - 1
self.next()
return result
def eatString(self, endChars):
start = self.pos
while self.next():
if self.ch == "\\":
self.next()
elif self.ch in endChars:
break
elif self.ch == "\n":
break
return self.source_text[start:self.pos] + self.ch
def peekString(self, endChar):
start = self.pos
st = self.eatString(endChar)
self.pos = start - 1
self.next()
return st
def eatWhitespace(self):
result = ''
while WHITE_RE.search(self.peek()) is not None:
self.next()
result += self.ch
return result
def skipWhitespace(self):
result = ''
if self.ch and WHITE_RE.search(self.ch):
result = self.ch
while WHITE_RE.search(self.next()) is not None:
result += self.ch
return result
def eatComment(self):
start = self.pos
singleLine = self.peek() == "/"
self.next()
while self.next():
if not singleLine and self.ch == "*" and self.peek() == "/":
self.next()
break
elif singleLine and self.ch == "\n":
return self.source_text[start:self.pos]
return self.source_text[start:self.pos] + self.ch
def lookBack(self, string):
past = self.source_text[self.pos - len(string):self.pos]
return past.lower() == string
# Nested pseudo-class if we are insideRule
# and the next special character found opens
# a new block
def foundNestedPseudoClass(self):
i = self.pos + 1
openParen = 0
while i < len(self.source_text):
ch = self.source_text[i]
if ch == "{":
return True
elif ch == "(":
# pseudoclasses can contain ()
openParen += 1
elif ch == ")":
if openParen == 0:
return False
openParen -= 1
elif ch == ";" or ch == "}":
return False
i += 1;
return False
def beautify(self):
m = re.search("^[\t ]*", self.source_text)
baseIndentString = m.group(0)
printer = Printer(self.indentChar, self.indentSize, baseIndentString)
insideRule = False
insidePropertyValue = False
enteringConditionalGroup = False
top_ch = ''
last_top_ch = ''
parenLevel = 0
while True:
whitespace = self.skipWhitespace()
isAfterSpace = whitespace != ''
isAfterNewline = '\n' in whitespace
last_top_ch = top_ch
top_ch = self.ch
if not self.ch:
break
elif self.ch == '/' and self.peek() == '*':
header = printer.indentLevel == 0
if not isAfterNewline or header:
printer.newLine()
comment = self.eatComment()
printer.comment(comment)
printer.newLine()
if header:
printer.newLine(True)
elif self.ch == '/' and self.peek() == '/':
if not isAfterNewline and last_top_ch != '{':
printer.trim()
printer.singleSpace()
printer.comment(self.eatComment())
printer.newLine()
elif self.ch == '@':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
# strip trailing space, if present, for hash property check
variableOrRule = self.peekString(": ,;{}()[]/='\"")
if variableOrRule[-1] in ": ":
# wwe have a variable or pseudo-class, add it and insert one space before continuing
self.next()
variableOrRule = self.eatString(": ")
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
printer.push(variableOrRule)
printer.singleSpace();
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
# might be a nesting at-rule
if variableOrRule in self.NESTED_AT_RULE:
printer.nestedLevel += 1
if variableOrRule in self.CONDITIONAL_GROUP_RULE:
enteringConditionalGroup = True
elif self.ch == '#' and self.peek() == '{':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.eatString('}'));
elif self.ch == '{':
if self.peek(True) == '}':
self.eatWhitespace()
self.next()
printer.singleSpace()
printer.push("{}")
printer.newLine()
if self.opts.newline_between_rules and printer.indentLevel == 0:
printer.newLine(True)
else:
printer.indent()
printer.openBracket()
# when entering conditional groups, only rulesets are allowed
if enteringConditionalGroup:
enteringConditionalGroup = False
insideRule = printer.indentLevel > printer.nestedLevel
else:
# otherwise, declarations are also allowed
insideRule = printer.indentLevel >= printer.nestedLevel
elif self.ch == '}':
printer.outdent()
printer.closeBracket()
insideRule = False
insidePropertyValue = False
if printer.nestedLevel:
printer.nestedLevel -= 1
if self.opts.newline_between_rules and printer.indentLevel == 0:
printer.newLine(True)
elif self.ch == ":":
self.eatWhitespace()
if (insideRule or enteringConditionalGroup) and \
not (self.lookBack('&') or self.foundNestedPseudoClass()):
# 'property: value' delimiter
# which could be in a conditional group query
insidePropertyValue = True
printer.push(":")
printer.singleSpace()
else:
# sass/less parent reference don't use a space
# sass nested pseudo-class don't use a space
if self.peek() == ":":
# pseudo-element
self.next()
printer.push("::")
else:
# pseudo-element
printer.push(":")
elif self.ch == '"' or self.ch == '\'':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.eatString(self.ch))
elif self.ch == ';':
insidePropertyValue = False
printer.semicolon()
elif self.ch == '(':
# may be a url
if self.lookBack("url"):
printer.push(self.ch)
self.eatWhitespace()
if self.next():
if self.ch is not ')' and self.ch is not '"' \
and self.ch is not '\'':
printer.push(self.eatString(')'))
else:
self.pos = self.pos - 1
else:
parenLevel += 1
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
self.eatWhitespace()
elif self.ch == ')':
printer.push(self.ch)
parenLevel -= 1
elif self.ch == ',':
printer.push(self.ch)
self.eatWhitespace()
if not insidePropertyValue and self.opts.selector_separator_newline and parenLevel < 1:
printer.newLine()
else:
printer.singleSpace()
elif self.ch == ']':
printer.push(self.ch)
elif self.ch == '[':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
elif self.ch == '=':
# no whitespace before or after
self.eatWhitespace()
self.ch = '='
printer.push(self.ch)
else:
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
sweet_code = re.sub('[\r\n\t ]+$', '', printer.result())
# establish end_with_newline
if self.opts.end_with_newline:
sweet_code += '\n'
if not self.opts.eol == '\n':
sweet_code = sweet_code.replace('\n', self.opts.eol)
return sweet_code

View file

@ -0,0 +1 @@
__version__ = '1.0.0'

View file

@ -0,0 +1,342 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import cssbeautifier
class CSSBeautifierTest(unittest.TestCase):
def resetOptions(self):
false = False
true = True
self.options = cssbeautifier.default_options()
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
def testGenerated(self):
self.resetOptions()
test_fragment = self.decodesto
t = self.decodesto
false = False
true = True
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
# End With Newline - (eof = "\n")
self.options.end_with_newline = true
test_fragment('', '\n')
test_fragment(' .tabs{}', ' .tabs {}\n')
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}\n')
test_fragment('\n')
# End With Newline - (eof = "")
self.options.end_with_newline = false
test_fragment('')
test_fragment(' .tabs{}', ' .tabs {}')
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}')
test_fragment('\n', '')
# Empty braces
t('.tabs{}', '.tabs {}')
t('.tabs { }', '.tabs {}')
t('.tabs { }', '.tabs {}')
t('.tabs \n{\n \n }', '.tabs {}')
#
t('#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}', '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}')
# Selector Separator - (separator = " ", separator1 = " ")
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}')
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = " ", separator1 = " ")
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}')
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}')
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}')
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}')
# Newline Between Rules - (separator = "\n")
self.options.newline_between_rules = true
t('.div {}\n.span {}', '.div {}\n\n.span {}')
t('.div{}\n \n.span{}', '.div {}\n\n.span {}')
t('.div {} \n \n.span { } \n', '.div {}\n\n.span {}')
t('.div {\n \n} \n .span {\n } ', '.div {}\n\n.span {}')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n\n.div {\n\theight: 15px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n\n.div {\n\theight: 15px;\n}')
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}')
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
# Newline Between Rules - (separator = "")
self.options.newline_between_rules = false
t('.div {}\n.span {}')
t('.div{}\n \n.span{}', '.div {}\n.span {}')
t('.div {} \n \n.span { } \n', '.div {}\n.span {}')
t('.div {\n \n} \n .span {\n } ', '.div {}\n.span {}')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div {\n\theight: 15px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n.div {\n\theight: 15px;\n}')
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}')
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}')
# Functions braces
t('.tabs(){}', '.tabs() {}')
t('.tabs (){}', '.tabs () {}')
t('.tabs (pa, pa(1,2)), .cols { }', '.tabs (pa, pa(1, 2)),\n.cols {}')
t('.tabs(pa, pa(1,2)), .cols { }', '.tabs(pa, pa(1, 2)),\n.cols {}')
t('.tabs ( ) { }', '.tabs () {}')
t('.tabs( ) { }', '.tabs() {}')
t('.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}')
t('.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}')
# Comments
t('/* test */')
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}')
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}')
t('/* header */.tabs {}', '/* header */\n\n.tabs {}')
t('.tabs {\n/* non-header */\nwidth:10px;}', '.tabs {\n\t/* non-header */\n\twidth: 10px;\n}')
t('/* header')
t('// comment')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}')
# single line comment support (less/sass)
t('.tabs{\n// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}')
t('.tabs{// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}')
t('//comment\n.tabs{width:10px;}', '//comment\n.tabs {\n\twidth: 10px;\n}')
t('.tabs{//comment\n//2nd single line comment\nwidth:10px;}', '.tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}')
t('.tabs{width:10px;//end of line comment\n}', '.tabs {\n\twidth: 10px; //end of line comment\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}')
# Psuedo-classes vs Variables
t('@page :first {}')
# Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.
t('@page:first {}', '@page: first {}')
t('@page: first {}')
# SASS/SCSS
# Basic Interpolation
t('p {\n\t$font-size: 12px;\n\t$line-height: 30px;\n\tfont: #{$font-size}/#{$line-height};\n}')
t('p.#{$name} {}')
t(
'@mixin itemPropertiesCoverItem($items, $margin) {\n' +
'\twidth: calc((100% - ((#{$items} - 1) * #{$margin}rem)) / #{$items});\n' +
'\tmargin: 1.6rem #{$margin}rem 1.6rem 0;\n' +
'}')
#
def testNewline(self):
self.resetOptions()
t = self.decodesto
self.options.end_with_newline = True
t("", "\n")
t("\n", "\n")
t(".tabs{}\n", ".tabs {}\n")
t(".tabs{}", ".tabs {}\n")
def testBasics(self):
self.resetOptions()
t = self.decodesto
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
t(".tabs{}", ".tabs {}")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}")
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
# lead-in whitespace determines base-indent.
# lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testSeperateSelectors(self):
self.resetOptions()
t = self.decodesto
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testBlockNesting(self):
self.resetOptions()
t = self.decodesto
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
# @font-face {
# font-family: 'Bitstream Vera Serif Bold';
# src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
# }
# @media screen {
# #foo:hover {
# background-image: url(foo.png);
# }
# @media screen and (min-device-pixel-ratio: 2) {
# @font-face {
# font-family: 'Helvetica Neue'
# }
# #foo:hover {
# background-image: url(foo@2x.png);
# }
# }
# }
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}")
def testOptions(self):
self.resetOptions()
self.options.indent_size = 2
self.options.indent_char = ' '
self.options.selector_separator_newline = False
t = self.decodesto
# pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}")
t("#foo *:hover {\n color: purple\n}")
t("::selection {\n color: #ff0000;\n}")
# TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}")
# particular edge case with braces and semicolons inside tags that allows custom text
t( "a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}")
def testLessCss(self):
self.resetOptions()
t = self.decodesto
t('.well{ \n @well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}')
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}')
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}')
# Not sure if this is sensible
# but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
# import
t('@import "test";');
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}")
# nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}")
# variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
def decodesto(self, input, expectation=None):
if expectation == None:
expectation = input
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
if not expectation != input:
self.assertMultiLineEqual(
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
input = input.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,7 @@
#! /usr/bin/env python
#
# Stub script to run jsbeautifier
#
import sys
from jsbeautifier import main
sys.exit(main())

View file

@ -0,0 +1,16 @@
#! /usr/bin/env python
import sys
import unittest
#Speedup things...
try:
import cProfile as profile
except ImportError:
import profile
def run():
sys.argv.append('discover')
unittest.main()
profile.run('run()')

View file

@ -0,0 +1,10 @@
#! /bin/sh
#
# Test suite launcher
#
if [ -z $PYTHON ]; then
env python -m unittest discover "$@"
else
env $PYTHON -m unittest discover "$@"
fi

View file

@ -0,0 +1 @@
__version__ = '1.5.10'

View file

@ -0,0 +1,137 @@
#!/bin/bash
REL_SCRIPT_DIR="`dirname \"$0\"`"
SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`"
test_cli_common()
{
echo ----------------------------------------
echo Testing common cli behavior...
CLI_SCRIPT_NAME=${1:?missing_param}
CLI_SCRIPT=$SCRIPT_DIR/../../$CLI_SCRIPT_NAME
echo Script: $CLI_SCRIPT
# should find the minimal help output
$CLI_SCRIPT 2>&1 | grep -q "Must define at least one file\." || {
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
$CLI_SCRIPT -h > /dev/null || {
echo "[$CLI_SCRIPT_NAME -h] Return code should be success."
exit 1
}
$CLI_SCRIPT -v > /dev/null || {
echo "[$CLI_SCRIPT_NAME -v] Return code should be success."
exit 1
}
MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file"
MISSING_FILE_MESSAGE="No such file or directory"
$CLI_SCRIPT $MISSING_FILE 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error."
exit 1
}
$CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message."
exit 1
}
if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text."
exit 1
fi
}
test_cli_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify cli behavior...
CLI_SCRIPT=$SCRIPT_DIR/../../js-beautify
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
}
rm -rf /tmp/js-beautify-mkdir
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js should have been created in /tmp/js-beautify-mkdir/js-beautify.js."
exit 1
}
# ensure unchanged files are not overwritten
cp -p /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js
touch -A -05 /tmp/js-beautify-mkdir/js-beautify.js
touch -A -01 /tmp/js-beautify-mkdir/js-beautify-old.js
$CLI_SCRIPT -r /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -r"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and same file name"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and different file name"
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js | diff -q $SCRIPT_DIR/../../../js/bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected to be different."
exit 1
}
}
test_smoke_js_beautify()
{
echo ----------------------------------------
echo Testing beautify functionality...
PYTHON=python $SCRIPT_DIR/../../js-beautify-test || exit 1
}
test_perf_js_beautify()
{
echo ----------------------------------------
echo Testing beautify performance...
# PYTHON=python $SCRIPT_DIR/../../js-beautify-profile || exit 1
python $SCRIPT_DIR/test-perf-jsbeautifier.py || exit 1
}
#test_cli_common css-beautify
#test_cli_common html-beautify
test_cli_common js-beautify
test_cli_js_beautify
test_smoke_js_beautify
test_perf_js_beautify
echo ----------------------------------------
echo $0 - PASSED.
echo ----------------------------------------

View file

@ -0,0 +1,36 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import copy
import jsbeautifier
options = jsbeautifier.default_options()
options.wrap_line_length = 80
data = ''
data_min = ''
def beautifier_test_underscore():
jsbeautifier.beautify(data, options)
def beautifier_test_underscore_min():
jsbeautifier.beautify(data_min, options)
def report_perf(fn):
import timeit
iter = 50
time = timeit.timeit(fn + "()", setup="from __main__ import " + fn + "; gc.enable()", number=iter)
print(fn + ": " + str(iter/time) + " cycles/sec")
if __name__ == '__main__':
dirname = os.path.dirname(os.path.abspath(__file__))
underscore_file = os.path.join(dirname, "../../../", "test/underscore.js")
underscore_min_file = os.path.join(dirname, "../../../", "test/underscore-min.js")
data = copy.copy(''.join(open(underscore_file).readlines()))
data_min = copy.copy(''.join(open(underscore_min_file).readlines()))
# warm up
beautifier_test_underscore()
beautifier_test_underscore_min()
report_perf("beautifier_test_underscore")
report_perf("beautifier_test_underscore_min")

View file

@ -0,0 +1,43 @@
import re
import unittest
import jsbeautifier
class TestJSBeautifierIndentation(unittest.TestCase):
def test_tabs(self):
test_fragment = self.decodesto
self.options.indent_with_tabs = 1;
test_fragment('{tabs()}', "{\n\ttabs()\n}");
def test_function_indent(self):
test_fragment = self.decodesto
self.options.indent_with_tabs = 1;
self.options.keep_function_indentation = 1;
test_fragment('var foo = function(){ bar() }();', "var foo = function() {\n\tbar()\n}();");
self.options.tabs = 1;
self.options.keep_function_indentation = 0;
test_fragment('var foo = function(){ baz() }();', "var foo = function() {\n\tbaz()\n}();");
def decodesto(self, input, expectation=None):
self.assertEqual(
jsbeautifier.beautify(input, self.options), expectation or input)
@classmethod
def setUpClass(cls):
options = jsbeautifier.default_options()
options.indent_size = 4
options.indent_char = ' '
options.preserve_newlines = True
options.jslint_happy = False
options.keep_array_indentation = False
options.brace_style = 'collapse'
options.indent_level = 0
cls.options = options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,25 @@
# UNPACKERS SPECIFICATIONS
Nothing very difficult: an unpacker is a submodule placed in the directory
where this file was found. Each unpacker must define three symbols:
* `PRIORITY` : integer number expressing the priority in applying this
unpacker. Lower number means higher priority.
Makes sense only if a source file has been packed with
more than one packer.
* `detect(source)` : returns `True` if source is packed, otherwise, `False`.
* `unpack(source)` : takes a `source` string and unpacks it. Must always return
valid JavaScript. That is to say, your code should look
like:
```
if detect(source):
return do_your_fancy_things_with(source)
else:
return source
```
*You can safely define any other symbol in your module, as it will be ignored.*
`__init__` code will automatically load new unpackers, without any further step
to be accomplished. Simply drop it in this directory.

View file

@ -0,0 +1,67 @@
#
# General code for JSBeautifier unpackers infrastructure. See README.specs
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""General code for JSBeautifier unpackers infrastructure."""
import pkgutil
import re
from jsbeautifier.unpackers import evalbased
# NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js!
BLACKLIST = ['jsbeautifier.unpackers.evalbased']
class UnpackingError(Exception):
"""Badly packed source or general error. Argument is a
meaningful description."""
pass
def getunpackers():
"""Scans the unpackers dir, finds unpackers and add them to UNPACKERS list.
An unpacker will be loaded only if it is a valid python module (name must
adhere to naming conventions) and it is not blacklisted (i.e. inserted
into BLACKLIST."""
path = __path__
prefix = __name__ + '.'
unpackers = []
interface = ['unpack', 'detect', 'PRIORITY']
for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix):
if 'tests' not in modname and modname not in BLACKLIST:
try:
module = __import__(modname, fromlist=interface)
except ImportError:
raise UnpackingError('Bad unpacker: %s' % modname)
else:
unpackers.append(module)
return sorted(unpackers, key = lambda mod: mod.PRIORITY)
UNPACKERS = getunpackers()
def run(source, evalcode=False):
"""Runs the applicable unpackers and return unpacked source as a string."""
for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]:
source = unpacker.unpack(source)
if evalcode and evalbased.detect(source):
source = evalbased.unpack(source)
return source
def filtercomments(source):
"""NOT USED: strips trailing comments and put them at the top."""
trailing_comments = []
comment = True
while comment:
if re.search(r'^\s*\/\*', source):
comment = source[0, source.index('*/') + 2]
elif re.search(r'^\s*\/\/', source):
comment = re.search(r'^\s*\/\/', source).group(0)
else:
comment = None
if comment:
source = re.sub(r'^\s+', '', source[len(comment):])
trailing_comments.append(comment)
return '\n'.join(trailing_comments) + source

View file

@ -0,0 +1,39 @@
#
# Unpacker for eval() based packers, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for eval() based packers: runs JS code and returns result.
Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and
properly set up on host."""
from subprocess import PIPE, Popen
PRIORITY = 3
def detect(source):
"""Detects if source is likely to be eval() packed."""
return source.strip().lower().startswith('eval(function(')
def unpack(source):
"""Runs source and return resulting code."""
return jseval('print %s;' % source[4:]) if detect(source) else source
# In case of failure, we'll just return the original, without crashing on user.
def jseval(script):
"""Run code in the JS interpreter and return output."""
try:
interpreter = Popen(['js'], stdin=PIPE, stdout=PIPE)
except OSError:
return script
result, errors = interpreter.communicate(script)
if interpreter.poll() or errors:
return script
return result

View file

@ -0,0 +1,58 @@
#
# simple unpacker/deobfuscator for scripts messed up with
# javascriptobfuscator.com
#
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# if javascriptobfuscator.detect(some_string):
# some_string = javascriptobfuscator.unpack(some_string)
#
"""deobfuscator for scripts messed up with JavascriptObfuscator.com"""
import re
PRIORITY = 1
def smartsplit(code):
"""Split `code` at " symbol, only if it is not escaped."""
strings = []
pos = 0
while pos < len(code):
if code[pos] == '"':
word = '' # new word
pos += 1
while pos < len(code):
if code[pos] == '"':
break
if code[pos] == '\\':
word += '\\'
pos += 1
word += code[pos]
pos += 1
strings.append('"%s"' % word)
pos += 1
return strings
def detect(code):
"""Detects if `code` is JavascriptObfuscator.com packed."""
# prefer `is not` idiom, so that a true boolean is returned
return (re.search(r'^var _0x[a-f0-9]+ ?\= ?\[', code) is not None)
def unpack(code):
"""Unpacks JavascriptObfuscator.com packed code."""
if detect(code):
matches = re.search(r'var (_0x[a-f\d]+) ?\= ?\[(.*?)\];', code)
if matches:
variable = matches.group(1)
dictionary = smartsplit(matches.group(2))
code = code[len(matches.group(0)):]
for key, value in enumerate(dictionary):
code = code.replace(r'%s[%s]' % (variable, key), value)
return code

View file

@ -0,0 +1,86 @@
#
# deobfuscator for scripts messed up with myobfuscate.com
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
# CAVEAT by Einar Lielmanis
#
# You really don't want to obfuscate your scripts there: they're tracking
# your unpackings, your script gets turned into something like this,
# as of 2011-08-26:
#
# var _escape = 'your_script_escaped';
# var _111 = document.createElement('script');
# _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' +
# '&ref=' + encodeURIComponent(document.referrer) +
# '&url=' + encodeURIComponent(document.URL);
# var 000 = document.getElementsByTagName('head')[0];
# 000.appendChild(_111);
# document.write(unescape(_escape));
#
"""Deobfuscator for scripts messed up with MyObfuscate.com"""
import re
import base64
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
from jsbeautifier.unpackers import UnpackingError
PRIORITY = 1
CAVEAT = """//
// Unpacker warning: be careful when using myobfuscate.com for your projects:
// scripts obfuscated by the free online version call back home.
//
"""
SIGNATURE = (r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F'
r'\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65'
r'\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75'
r'\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B'
r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78'
r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","'
r'\x6C\x65\x6E\x67\x74\x68"]')
def detect(source):
"""Detects MyObfuscate.com packer."""
return SIGNATURE in source
def unpack(source):
"""Unpacks js code packed with MyObfuscate.com"""
if not detect(source):
return source
payload = unquote(_filter(source))
match = re.search(r"^var _escape\='<script>(.*)<\/script>'",
payload, re.DOTALL)
polished = match.group(1) if match else source
return CAVEAT + polished
def _filter(source):
"""Extracts and decode payload (original file) from `source`"""
try:
varname = re.search(r'eval\(\w+\(\w+\((\w+)\)\)\);', source).group(1)
reverse = re.search(r"var +%s *\= *'(.*)';" % varname, source).group(1)
except AttributeError:
raise UnpackingError('Malformed MyObfuscate data.')
try:
return base64.b64decode(reverse[::-1].encode('utf8')).decode('utf8')
except TypeError:
raise UnpackingError('MyObfuscate payload is not base64-encoded.')

View file

@ -0,0 +1,112 @@
#
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
import re
import string
from jsbeautifier.unpackers import UnpackingError
PRIORITY = 1
def detect(source):
"""Detects whether `source` is P.A.C.K.E.R. coded."""
return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,')
def unpack(source):
"""Unpacks P.A.C.K.E.R. packed js code."""
payload, symtab, radix, count = _filterargs(source)
if count != len(symtab):
raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
try:
unbase = Unbaser(radix)
except TypeError:
raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')
def lookup(match):
"""Look up symbols in the synthetic symtab."""
word = match.group(0)
return symtab[unbase(word)] or word
source = re.sub(r'\b\w+\b', lookup, payload)
return _replacestrings(source)
def _filterargs(source):
"""Juice from a source file the four args needed by decoder."""
juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"),
(r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"),
]
for juicer in juicers:
args = re.search(juicer, source, re.DOTALL)
if args:
a = args.groups()
try:
return a[0], a[3].split('|'), int(a[1]), int(a[2])
except ValueError:
raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
# could not find a satisfying regex
raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)')
def _replacestrings(source):
"""Strip string lookup table (list) and replace values in source."""
match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
if match:
varname, strings = match.groups()
startpoint = len(match.group(0))
lookup = strings.split('","')
variable = '%s[%%d]' % varname
for index, value in enumerate(lookup):
source = source.replace(variable % index, '"%s"' % value)
return source[startpoint:]
return source
class Unbaser(object):
"""Functor for a given base. Will efficiently convert
strings to natural numbers."""
ALPHABET = {
62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
}
def __init__(self, base):
self.base = base
# If base can be handled by int() builtin, let it do it for us
if 2 <= base <= 36:
self.unbase = lambda string: int(string, base)
else:
# Build conversion dictionary cache
try:
self.dictionary = dict((cipher, index) for
index, cipher in enumerate(self.ALPHABET[base]))
except KeyError:
raise TypeError('Unsupported base encoding.')
self.unbase = self._dictunbaser
def __call__(self, string):
return self.unbase(string)
def _dictunbaser(self, string):
"""Decodes a value to an integer."""
ret = 0
for index, cipher in enumerate(string[::-1]):
ret += (self.base ** index) * self.dictionary[cipher]
return ret

View file

@ -0,0 +1,2 @@
# Empty file :)
# pylint: disable=C0111

View file

@ -0,0 +1,65 @@
//
// Unpacker warning: be careful when using myobfuscate.com for your projects:
// scripts obfuscated by the free online version call back home.
//
//
// Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// Coincidentally, it can defeat a couple of other eval-based compressors.
//
// usage:
//
// if (P_A_C_K_E_R.detect(some_string)) {
// var unpacked = P_A_C_K_E_R.unpack(some_string);
// }
//
//
var P_A_C_K_E_R = {
detect: function (str) {
return P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval(function(') ||
P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval((function(') ;
},
unpack: function (str) {
var unpacked_source = '';
if (P_A_C_K_E_R.detect(str)) {
try {
eval('unpacked_source = ' + str.substring(4) + ';')
if (typeof unpacked_source == 'string' && unpacked_source) {
str = unpacked_source;
}
} catch (error) {
// well, it failed. we'll just return the original, instead of crashing on user.
}
}
return str;
},
_starts_with: function (str, what) {
return str.substr(0, what.length) === what;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
t.test_function(P_A_C_K_E_R.detect, "P_A_C_K_E_R.detect");
t.expect('', false);
t.expect('var a = b', false);
t.expect('eval(function(p,a,c,k,e,r', true);
t.expect('eval ( function(p, a, c, k, e, r', true);
t.test_function(P_A_C_K_E_R.unpack, 'P_A_C_K_E_R.unpack');
t.expect("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',3,3,'var||a'.split('|'),0,{}))",
'var a=1');
var starts_with_a = function(what) { return P_A_C_K_E_R._starts_with(what, 'a'); }
t.test_function(starts_with_a, "P_A_C_K_E_R._starts_with(?, a)");
t.expect('abc', true);
t.expect('bcd', false);
t.expect('a', true);
t.expect('', false);
return t;
}
}

View file

@ -0,0 +1 @@
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 H=(j["Y"]["n"]("1h")!=-1)?s:v;9 I=(j["Y"]["w"]()["n"]("1i")!=-1)?s:v;9 J=(j["K"]["n"]("1j")!=-1)?s:v;p Z(){9 a;9 b;9 c;y{b=o z("l.l.7");a=b.P("$Q")}A(c){};i(!a){y{b=o z("l.l.6");a="R 6,0,21,0";b["1k"]="1l";a=b.P("$Q")}A(c){}};i(!a){y{b=o z("l.l.3");a=b.P("$Q")}A(c){}};i(!a){y{b=o z("l.l.3");a="R 3,0,18,0"}A(c){}};i(!a){y{b=o z("l.l");a="R 2,0,0,11"}A(c){a=-1}};m a};p 10(){9 a=-1;i(j["t"]!=12&&j["t"]["13"]>0){i(j["t"]["L M 2.0"]||j["t"]["L M"]){9 b=j["t"]["L M 2.0"]?" 2.0":"";9 c=j["t"]["L M"+b]["1m"];9 d=c["B"](" ");9 e=d[2]["B"](".");9 f=e[0];9 g=e[1];9 h=d[3];i(h==""){h=d[4]};i(h[0]=="d"){h=h["S"](1)}k{i(h[0]=="r"){h=h["S"](1);i(h["n"]("d")>0){h=h["S"](0,h["n"]("d"))}}};9 a=f+"."+g+"."+h}}k{i(j["K"]["w"]()["n"]("T/2.6")!=-1){a=4}k{i(j["K"]["w"]()["n"]("T/2.5")!=-1){a=3}k{i(j["K"]["w"]()["n"]("T")!=-1){a=2}k{i(H&&I&&!J){a=Z()}}}}};m a};p 1n(a,b,c){C=10();i(C==-1){m v}k{i(C!=0){i(H&&I&&!J){14=C["B"](" ");15=14[1];D=15["B"](",")}k{D=C["B"](".")};9 d=D[0];9 e=D[1];9 f=D[2];i(d>E(a)){m s}k{i(d==E(a)){i(e>E(b)){m s}k{i(e==E(b)){i(f>=E(c)){m s}}}}};m v}}};p 16(a,b){i(a["n"]("?")!=-1){m a["1o"](/\\?/,b+"?")}k{m a+b}};p U(a,b,c){9 d="";i(H&&I&&!J){d+="<17 ";N(9 e V a){d+=e+"=""+a[e]+"" "};d+=">";N(9 e V b){d+="<1p 19=""+e+"" 1q=""+b[e]+"" /> "};d+="</17>"}k{d+="<1a ";N(9 e V c){d+=e+"=""+c[e]+"" "};d+="> </1a>"};1r["1s"](d)};p 1t(){9 a=W(1b,".1u","1c","1d:1v-1w-1x-1y-1e","1z/x-1A-1B");U(a["u"],a["F"],a["q"])};p 1C(){9 a=W(1b,".1D","X","1d:1E-1F-1G-1H-1e",12);U(a["u"],a["F"],a["q"])};p W(a,b,c,d,e){9 f=o O();f["q"]=o O();f["F"]=o O();f["u"]=o O();N(9 g=0;g<a["13"];g=g+2){9 h=a[g]["w"]();1I(h){8"1f":G;8"1J":f["q"][a[g]]=a[g+1];G;8"X":;8"1c":a[g+1]=16(a[g+1],b);f["q"]["X"]=a[g+1];f["F"][c]=a[g+1];G;8"1K":;8"1L":;8"1M":;8"1N":;8"1O":;8"1P":;8"1Q":;8"1R":;8"1S":;8"1T":;8"1U":;8"1V":;8"1W":;8"1X":;8"1Y":;8"1Z":;8"20":;8"22":;8"23":;8"24":;8"25":;8"26":;8"27":;8"28":;8"29":;8"2a":;8"2b":;8"2c":;8"2d":;8"2e":;8"2f":;8"2g":;8"2h":;8"2i":;8"2j":;8"2k":;8"2l":;8"1g":;8"2m":;8"2n":f["u"][a[g]]=a[g+1];G;8"2o":;8"2p":;8"2q":;8"2r":;8"2s":;8"2t":;8"2u":;8"2v":;8"19":;8"2w":f["q"][a[g]]=f["u"][a[g]]=a[g+1];G;2x:f["q"][a[g]]=f["F"][a[g]]=a[g+1]}};f["u"]["1f"]=d;i(e){f["q"]["1g"]=e};m f};',62,158,'||||||||case|var|||||||||if|navigator|else|ShockwaveFlash|return|indexOf|new|function|embedAttrs||true|plugins|objAttrs|false|toLowerCase||try|ActiveXObject|catch|split|versionStr|versionArray|parseFloat|params|break|isIE|isWin|isOpera|userAgent|Shockwave|Flash|for|Object|GetVariable|version|WIN|substring|webtv|AC_Generateobj|in|AC_GetArgs|src|appVersion|ControlVersion|GetSwfVer||null|length|tempArray|tempString|AC_AddExtension|object||name|embed|arguments|movie|clsid|444553540000|classid|type|MSIE|win|Opera|AllowScriptAccess|always|description|DetectFlashVer|replace|param|value|document|write|AC_FL_RunContent|swf|d27cdb6e|ae6d|11cf|96b8|application|shockwave|flash|AC_SW_RunContent|dcr|166B1BCA|3F9C|11CF|8075|switch|pluginspage|onafterupdate|onbeforeupdate|onblur|oncellchange|onclick|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondrop|onfinish|onfocus|onhelp|onmousedown|onmouseup||onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup|onload|onlosecapture|onpropertychange|onreadystatechange|onrowsdelete|onrowenter|onrowexit|onrowsinserted|onstart|onscroll|onbeforeeditfocus|onactivate|onbeforedeactivate|ondeactivate|codebase|id|width|height|align|vspace|hspace|class|title|accesskey|tabindex|default'.split('|'),0,{}))

View file

@ -0,0 +1 @@
var isIE=(navigator["appVersion"]["indexOf"]("MSIE")!=-1)?true:false;var isWin=(navigator["appVersion"]["toLowerCase"]()["indexOf"]("win")!=-1)?true:false;var isOpera=(navigator["userAgent"]["indexOf"]("Opera")!=-1)?true:false;function ControlVersion(){var a;var b;var c;try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");a=b.GetVariable("$version")}catch(c){};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");a="WIN 6,0,21,0";b["AllowScriptAccess"]="always";a=b.GetVariable("$version")}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");a=b.GetVariable("$version")}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");a="WIN 3,0,18,0"}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");a="WIN 2,0,0,11"}catch(c){a=-1}};return a};function GetSwfVer(){var a=-1;if(navigator["plugins"]!=null&&navigator["plugins"]["length"]>0){if(navigator["plugins"]["Shockwave Flash 2.0"]||navigator["plugins"]["Shockwave Flash"]){var b=navigator["plugins"]["Shockwave Flash 2.0"]?" 2.0":"";var c=navigator["plugins"]["Shockwave Flash"+b]["description"];var d=c["split"](" ");var e=d[2]["split"](".");var f=e[0];var g=e[1];var h=d[3];if(h==""){h=d[4]};if(h[0]=="d"){h=h["substring"](1)}else{if(h[0]=="r"){h=h["substring"](1);if(h["indexOf"]("d")>0){h=h["substring"](0,h["indexOf"]("d"))}}};var a=f+"."+g+"."+h}}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv/2.6")!=-1){a=4}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv/2.5")!=-1){a=3}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv")!=-1){a=2}else{if(isIE&&isWin&&!isOpera){a=ControlVersion()}}}}};return a};function DetectFlashVer(a,b,c){versionStr=GetSwfVer();if(versionStr==-1){return false}else{if(versionStr!=0){if(isIE&&isWin&&!isOpera){tempArray=versionStr["split"](" ");tempString=tempArray[1];versionArray=tempString["split"](",")}else{versionArray=versionStr["split"](".")};var d=versionArray[0];var e=versionArray[1];var f=versionArray[2];if(d>parseFloat(a)){return true}else{if(d==parseFloat(a)){if(e>parseFloat(b)){return true}else{if(e==parseFloat(b)){if(f>=parseFloat(c)){return true}}}}};return false}}};function AC_AddExtension(a,b){if(a["indexOf"]("?")!=-1){return a["replace"](/\?/,b+"?")}else{return a+b}};function AC_Generateobj(a,b,c){var d="";if(isIE&&isWin&&!isOpera){d+="<object ";for(var e in a){d+=e+"=""+a[e]+"" "};d+=">";for(var e in b){d+="<param name=""+e+"" value=""+b[e]+"" /> "};d+="</object>"}else{d+="<embed ";for(var e in c){d+=e+"=""+c[e]+"" "};d+="> </embed>"};document["write"](d)};function AC_FL_RunContent(){var a=AC_GetArgs(arguments,".swf","movie","clsid:d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash");AC_Generateobj(a["objAttrs"],a["params"],a["embedAttrs"])};function AC_SW_RunContent(){var a=AC_GetArgs(arguments,".dcr","src","clsid:166B1BCA-3F9C-11CF-8075-444553540000",null);AC_Generateobj(a["objAttrs"],a["params"],a["embedAttrs"])};function AC_GetArgs(a,b,c,d,e){var f=new Object();f["embedAttrs"]=new Object();f["params"]=new Object();f["objAttrs"]=new Object();for(var g=0;g<a["length"];g=g+2){var h=a[g]["toLowerCase"]();switch(h){case"classid":break;case"pluginspage":f["embedAttrs"][a[g]]=a[g+1];break;case"src":;case"movie":a[g+1]=AC_AddExtension(a[g+1],b);f["embedAttrs"]["src"]=a[g+1];f["params"][c]=a[g+1];break;case"onafterupdate":;case"onbeforeupdate":;case"onblur":;case"oncellchange":;case"onclick":;case"ondblclick":;case"ondrag":;case"ondragend":;case"ondragenter":;case"ondragleave":;case"ondragover":;case"ondrop":;case"onfinish":;case"onfocus":;case"onhelp":;case"onmousedown":;case"onmouseup":;case"onmouseover":;case"onmousemove":;case"onmouseout":;case"onkeypress":;case"onkeydown":;case"onkeyup":;case"onload":;case"onlosecapture":;case"onpropertychange":;case"onreadystatechange":;case"onrowsdelete":;case"onrowenter":;case"onrowexit":;case"onrowsinserted":;case"onstart":;case"onscroll":;case"onbeforeeditfocus":;case"onactivate":;case"onbeforedeactivate":;case"ondeactivate":;case"type":;case"codebase":;case"id":f["objAttrs"][a[g]]=a[g+1];break;case"width":;case"height":;case"align":;case"vspace":;case"hspace":;case"class":;case"title":;case"accesskey":;case"name":;case"tabindex":f["embedAttrs"][a[g]]=f["objAttrs"][a[g]]=a[g+1];break;default:f["embedAttrs"][a[g]]=f["params"][a[g]]=a[g+1]}};f["objAttrs"]["classid"]=d;if(e){f["embedAttrs"]["type"]=e};return f};

View file

@ -0,0 +1,46 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for JavaScriptObfuscator unpacker."""
import unittest
from jsbeautifier.unpackers.javascriptobfuscator import (
unpack, detect, smartsplit)
# pylint: disable=R0904
class TestJavascriptObfuscator(unittest.TestCase):
"""JavascriptObfuscator.com test case."""
def test_smartsplit(self):
"""Test smartsplit() function."""
split = smartsplit
equals = lambda data, result: self.assertEqual(split(data), result)
equals('', [])
equals('"a", "b"', ['"a"', '"b"'])
equals('"aaa","bbbb"', ['"aaa"', '"bbbb"'])
equals('"a", "b\\\""', ['"a"', '"b\\\""'])
def test_detect(self):
"""Test detect() function."""
positive = lambda source: self.assertTrue(detect(source))
negative = lambda source: self.assertFalse(detect(source))
negative('')
negative('abcd')
negative('var _0xaaaa')
positive('var _0xaaaa = ["a", "b"]')
positive('var _0xaaaa=["a", "b"]')
positive('var _0x1234=["a","b"]')
def test_unpack(self):
"""Test unpack() function."""
decodeto = lambda ob, original: self.assertEqual(unpack(ob), original)
decodeto('var _0x8df3=[];var a=10;', 'var a=10;')
decodeto('var _0xb2a7=["\x74\x27\x65\x73\x74"];var i;for(i=0;i<10;++i)'
'{alert(_0xb2a7[0]);} ;', 'var i;for(i=0;i<10;++i){alert'
'("t\'est");} ;')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,40 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for MyObfuscate unpacker."""
import unittest
import os
from jsbeautifier.unpackers.myobfuscate import detect, unpack
from jsbeautifier.unpackers.tests import __path__ as path
INPUT = os.path.join(path[0], 'test-myobfuscate-input.js')
OUTPUT = os.path.join(path[0], 'test-myobfuscate-output.js')
# pylint: disable=R0904
class TestMyObfuscate(unittest.TestCase):
# pylint: disable=C0103
"""MyObfuscate obfuscator testcase."""
@classmethod
def setUpClass(cls):
"""Load source files (encoded and decoded version) for tests."""
with open(INPUT, 'r') as data:
cls.input = data.read()
with open(OUTPUT, 'r') as data:
cls.output = data.read()
def test_detect(self):
"""Test detect() function."""
detected = lambda source: self.assertTrue(detect(source))
detected(self.input)
def test_unpack(self):
"""Test unpack() function."""
check = lambda inp, out: self.assertEqual(unpack(inp), out)
check(self.input, self.output)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,34 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for P.A.C.K.E.R. unpacker."""
import unittest
from jsbeautifier.unpackers.packer import detect, unpack
# pylint: disable=R0904
class TestPacker(unittest.TestCase):
"""P.A.C.K.E.R. testcase."""
def test_detect(self):
"""Test detect() function."""
positive = lambda source: self.assertTrue(detect(source))
negative = lambda source: self.assertFalse(detect(source))
negative('')
negative('var a = b')
positive('eval(function(p,a,c,k,e,r')
positive('eval ( function(p, a, c, k, e, r')
def test_unpack(self):
"""Test unpack() function."""
check = lambda inp, out: self.assertEqual(unpack(inp), out)
check("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)"
"){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e="
"function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace("
"new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',"
"62,3,'var||a'.split('|'),0,{}))", 'var a=1')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,36 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for urlencoded unpacker."""
import unittest
from jsbeautifier.unpackers.urlencode import detect, unpack
# pylint: disable=R0904
class TestUrlencode(unittest.TestCase):
"""urlencode test case."""
def test_detect(self):
"""Test detect() function."""
encoded = lambda source: self.assertTrue(detect(source))
unencoded = lambda source: self.assertFalse(detect(source))
unencoded('')
unencoded('var a = b')
encoded('var%20a+=+b')
encoded('var%20a=b')
encoded('var%20%21%22')
def test_unpack(self):
"""Test unpack function."""
equals = lambda source, result: self.assertEqual(unpack(source), result)
equals('', '')
equals('abcd', 'abcd')
equals('var a = b', 'var a = b')
equals('var%20a=b', 'var a=b')
equals('var%20a+=+b', 'var a = b')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,34 @@
#
# Trivial bookmarklet/escaped script detector for the javascript beautifier
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# some_string = urlencode.unpack(some_string)
#
"""Bookmarklet/escaped script unpacker."""
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote_plus
except ImportError:
from urllib.parse import unquote_plus
PRIORITY = 0
def detect(code):
"""Detects if a scriptlet is urlencoded."""
# the fact that script doesn't contain any space, but has %20 instead
# should be sufficient check for now.
return ' ' not in code and ('%20' in code or code.count('%') > 3)
def unpack(code):
"""URL decode `code` source string."""
return unquote_plus(code) if detect(code) else code

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python
from distutils.core import setup
from jsbeautifier.__version__ import __version__
setup(name='jsbeautifier',
version=__version__,
description='JavaScript unobfuscator and beautifier.',
long_description=('Beautify, unpack or deobfuscate JavaScript. '
'Handles popular online obfuscators.'),
author='Einar Lielmanis, Stefano Sanfilippo et al.',
author_email='einar@jsbeautifier.org',
url='http://jsbeautifier.org',
scripts=['js-beautify'],
packages=['jsbeautifier', 'jsbeautifier.tests',
'jsbeautifier.unpackers', 'jsbeautifier.unpackers.tests'],
install_requires=["six>=1.6.1"],
license='MIT',
)