/script-kiddie/002_script_kiddie/script-kiddie/bower_components/js-beautify/python/cssbeautifier/__init__.py |
@@ -0,0 +1,539 @@ |
from __future__ import print_function |
import sys |
import re |
import copy |
from cssbeautifier.__version__ import __version__ |
|
# |
# The MIT License (MIT) |
|
# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, 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.preserve_newlines = False |
self.selector_separator_newline = True |
self.end_with_newline = False |
self.newline_between_rules = True |
self.space_around_combinator = False |
self.eol = 'auto' |
|
self.css = None |
self.js = None |
self.html = None |
|
# deprecated |
self.space_around_selector_separator = False |
|
def mergeOpts(self, targetType): |
finalOpts = copy.copy(self) |
|
local = getattr(finalOpts, targetType) |
if (local): |
delattr(finalOpts, targetType) |
for key in local: |
setattr(finalOpts, key, local[key]) |
|
return finalOpts |
|
|
def __repr__(self): |
return \ |
"""indent_size = %d |
indent_char = [%s] |
indent_with_tabs = [%s] |
preserve_newlines = [%s] |
separate_selectors_newline = [%s] |
end_with_newline = [%s] |
newline_between_rules = [%s] |
space_around_combinator = [%s] |
""" % (self.indent_size, self.indent_char, self.indent_with_tabs, self.preserve_newlines, |
self.selector_separator_newline, self.end_with_newline, self.newline_between_rules, |
self.space_around_combinator) |
|
|
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, beautifier, indent_char, indent_size, default_indent=""): |
self.beautifier = beautifier |
self.newlines_from_last_ws_eat = 0 |
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("{") |
if self.beautifier.eatWhitespace(True) == 0: |
self.newLine() |
|
def closeBracket(self,newLine): |
if newLine: |
self.newLine() |
self.output.append("}") |
self.beautifier.eatWhitespace(True) |
if self.beautifier.newlines_from_last_ws_eat == 0: |
self.newLine() |
|
def semicolon(self): |
self.output.append(";") |
|
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() |
elif self.output[-1] == self.baseIndentString: |
self.output.pop() |
|
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") |
|
# in javascript, these two differ |
# in python they are the same, different methods are called on them |
self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]")) |
self.allLineBreaks = self.lineBreak |
|
if not source_text: |
source_text = '' |
|
opts = opts.mergeOpts('css') |
|
# Continue to accept deprecated option |
opts.space_around_combinator = opts.space_around_combinator or opts.space_around_selector_separator |
|
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 |
|
if self.opts.eol == 'auto': |
self.opts.eol = '\n' |
if self.lineBreak.search(source_text or ''): |
self.opts.eol = self.lineBreak.search(source_text).group() |
|
self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n') |
|
# HACK: newline parsing inconsistent. This brute force normalizes the input newlines. |
self.source_text = re.sub(self.allLineBreaks, '\n', source_text) |
|
# 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"] |
|
m = re.search("^[\t ]*", self.source_text) |
baseIndentString = m.group(0) |
self.printer = Printer(self, self.indentChar, self.indentSize, baseIndentString) |
|
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, pn=False): |
result = 0 |
while WHITE_RE.search(self.peek()) is not None: |
self.next() |
if self.ch == "\n" and pn and self.opts.preserve_newlines: |
self.printer.newLine(True) |
result += 1 |
self.newlines_from_last_ws_eat = result |
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): |
printer = self.printer |
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) |
|
# deal with less propery mixins @{...} |
if self.peek(True) == '{': |
printer.push(self.eatString('}')); |
else: |
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.closeBracket(False) |
if self.newlines_from_last_ws_eat < 2 and 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(True) |
insideRule = False |
insidePropertyValue = False |
if printer.nestedLevel: |
printer.nestedLevel -= 1 |
if self.newlines_from_last_ws_eat < 2 and 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()) and \ |
not self.lookBack('('): |
# 'property: value' delimiter |
# which could be in a conditional group query |
printer.push(":") |
if not insidePropertyValue: |
insidePropertyValue = True |
printer.singleSpace() |
else: |
# sass/less parent reference don't use a space |
# sass nested pseudo-class don't use a space |
|
# preserve space before pseudoclasses/pseudoelements, as it means "in any child" |
if (self.lookBack(' ')) and (printer.output[-1] != ' '): |
printer.push(" ") |
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() |
if self.eatWhitespace(True) == 0: |
printer.newLine() |
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) |
if self.eatWhitespace(True) == 0 and not insidePropertyValue and self.opts.selector_separator_newline and parenLevel < 1: |
printer.newLine() |
else: |
printer.singleSpace() |
elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \ |
not insidePropertyValue and parenLevel < 1: |
# handle combinator spacing |
if self.opts.space_around_combinator: |
printer.singleSpace() |
printer.push(self.ch) |
printer.singleSpace() |
else: |
printer.push(self.ch) |
self.eatWhitespace() |
# squash extra whitespace |
if self.ch and WHITE_RE.search(self.ch): |
self.ch = '' |
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() |
printer.push('=') |
if WHITE_RE.search(self.ch): |
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 |