Created
October 28, 2018 01:42
-
-
Save StSav012/0af7948966e0795ecf798ac6629fd071 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from typing import List, Dict | |
import urllib.request | |
import urllib.error | |
import os.path | |
FILES: List[str] = [ | |
# 'test', | |
# 'aes_light', | |
'apps', | |
'audio', | |
'bookmarks', | |
'common', | |
'datepicker', | |
# 'dev', | |
'docs', | |
'feed', | |
'fonts_cnt', | |
'groups', | |
'im', | |
'market', | |
'module', | |
'mrtarg', | |
'notifier', | |
'page', | |
'page_help', | |
'photos', | |
'photos_add', | |
'photoview', | |
'post', | |
'privacy', | |
'profile', | |
'public', | |
'search', | |
'settings', | |
'stories', | |
'tooltips', | |
'ui_common', | |
'ui_controls', | |
'ui_media_selector', | |
'video', | |
'videocat', | |
'videoplayer', | |
'videoview', | |
'wide_dd', | |
'wk', | |
'wk_editor', | |
'writebox', | |
] | |
SELECTOR: str = 'selector' | |
DECLARATIONS: str = 'declarations' | |
def invert_color_hex3(color: str) -> str: | |
""" inverts color represented by 3-digit hex code """ | |
new_color: str = f'{0xfff - int(color, 0x10):03x}' | |
return new_color[::-1] | |
def invert_color_hex6(color: str) -> str: | |
""" inverts color represented by 6-digit hex code """ | |
new_color: str = f'{0xffffff - int(color, 0x10):06x}' | |
return ''.join(map(''.join, zip(new_color[::2][::-1], new_color[1::2][::-1]))) | |
def invert_color_rgb(color: str) -> str: | |
""" inverts color represented by an rgb or rgba tuple """ | |
css_color: List[str] = color.split(')', maxsplit=1) | |
color_type, color_value = tuple(css_color[0].split('(')) | |
components: List[str] = color_value.split(',') | |
try: | |
new_components: List[str] = list(map(lambda c: str(255 - int(c)), components[:3]))[::-1] | |
except ValueError: | |
print(color, components) | |
exit(0) | |
return '' | |
if len(components) >= 4: | |
new_components.extend(components[3:]) | |
new_color_value = ','.join(new_components) | |
return f'{color_type}({new_color_value}){css_color[1]}' | |
def split_rules(css: str) -> List[str]: | |
rules_list: List[str] = [] | |
brace_order: int = 0 | |
brace_position: int = 0 | |
for index, symbol in enumerate(css): | |
if symbol == '{': | |
brace_order += 1 | |
elif symbol == '}': | |
brace_order -= 1 | |
if brace_order == 0: | |
rules_list.append(css[brace_position:index]) | |
brace_position = index + 1 | |
return list(filter(lambda r: bool(r), rules_list)) | |
def split_selector(css_rule: str) -> Dict[str, List[str]]: | |
if '{' not in css_rule: | |
return {SELECTOR: css_rule, DECLARATIONS: []} | |
selector, css_declarations = tuple(css_rule.split('{', maxsplit=1)) | |
declarations: List[str] = [] | |
start_index: int = 0 | |
parenthesis_order: int = 0 | |
brace_order: int = 0 | |
quotation_order: bool = False | |
for index, symbol in enumerate(css_declarations): | |
if symbol == '"': | |
quotation_order = not quotation_order | |
if symbol == '(': | |
parenthesis_order += 1 | |
elif symbol == ')': | |
parenthesis_order -= 1 | |
if symbol == '{': | |
brace_order += 1 | |
elif symbol == '}': | |
brace_order -= 1 | |
elif symbol == ';' and not quotation_order and parenthesis_order == 0 and brace_order == 0: | |
if index - start_index > 1: | |
declarations.append(css_declarations[start_index:index]) | |
start_index = index + 1 | |
declarations.append(css_declarations[start_index:]) | |
return {SELECTOR: selector, DECLARATIONS: declarations} | |
def split_words(css_value: str) -> List[str]: | |
word_list: List[str] = [] | |
start_index: int = 0 | |
parenthesis_order: int = 0 | |
brace_order: int = 0 | |
quotation_order: bool = False | |
for index, symbol in enumerate(css_value): | |
if symbol == '"': | |
quotation_order = not quotation_order | |
if symbol == '(': | |
parenthesis_order += 1 | |
elif symbol == ')': | |
parenthesis_order -= 1 | |
if symbol == '{': | |
brace_order += 1 | |
elif symbol == '}': | |
brace_order -= 1 | |
elif symbol == ' ' and not quotation_order and parenthesis_order == 0 and brace_order == 0: | |
if index - start_index >= 1: | |
word_list.append(css_value[start_index:index]) | |
start_index = index + 1 | |
word_list.append(css_value[start_index:]) | |
return word_list | |
rules: List[Dict[str, List[str]]] = [] | |
for fn in FILES: | |
if not os.path.exists(f'{fn}.css'): | |
try: | |
urllib.request.urlretrieve(f'https://vk.com/css/al/{fn}.css', f'{fn}.css') | |
except urllib.error.HTTPError: | |
pass | |
if not os.path.exists(f'{fn}.css'): | |
try: | |
urllib.request.urlretrieve(f'https://vk.com/css/{fn}.css', f'{fn}.css') | |
except urllib.error.HTTPError: | |
pass | |
if not os.path.exists(f'{fn}.css'): | |
print(f'failed to get {fn}.css') | |
continue | |
with open(f'{fn}.css', 'r') as fin: | |
# split into a dictionary of selectors and diclaration omitting empty parts | |
rules += [split_selector(r) for r in split_rules(' '.join(fin.readlines()))] | |
# hotfix to avoid huge fonts definitions | |
rules = list(filter(lambda r: not r[SELECTOR].startswith('@font-face'), rules)) | |
rule: Dict[str, List[str]] | |
# reduce the list leaving only the lines that look like colors | |
for rule in rules: # ↓↓↓ hotfix | |
rule[DECLARATIONS] = list(filter(lambda r: ('#' in r[2:]) or ('rgb' in r), | |
rule[DECLARATIONS])) | |
rules = list(filter(lambda r: r[DECLARATIONS], rules)) | |
# reduce the list removing odd properties starting with ‘*’ | |
for rule in rules: | |
rule[DECLARATIONS] = list(filter(lambda r: not r.startswith('*'), rule[DECLARATIONS])) | |
rules = list(filter(lambda r: r[DECLARATIONS], rules)) | |
# look through the rules and invert colors | |
for rule in rules: | |
new_declarations: List[str] = [] | |
declaration: str | |
for declaration in rule[DECLARATIONS]: | |
try: | |
prop, value = tuple(declaration.split(':', maxsplit=1)) | |
except ValueError: | |
new_declarations.append(declaration) | |
continue | |
new_value: List[str] = [] | |
words: List[str] = split_words(value) | |
for word in words: | |
new_word: str = word | |
if word.startswith('#'): | |
if len(word) == 7: | |
new_word = word[0] + invert_color_hex6(word[1:]) | |
elif len(word) == 4: | |
new_word = word[0] + invert_color_hex3(word[1:]) | |
elif word.startswith('rgb'): | |
new_word = invert_color_rgb(word) | |
elif word.startswith('linear-gradient(') or word.startswith('-o-linear-gradient('): | |
# we are free to change the colors despite the parentheses | |
subfunc, subvalue = tuple(word.split('(', maxsplit=1)) | |
new_subvalue: List[str] = [] | |
for subword in subvalue.split(): | |
new_subword: str = subword | |
if subword.startswith('#'): | |
if len(subword) == 7: | |
new_subword = subword[0] + invert_color_hex6(subword[1:]) | |
elif len(subword) == 4: | |
new_subword = subword[0] + invert_color_hex3(subword[1:]) | |
elif subword.startswith('rgb'): | |
new_subword = invert_color_rgb(subword) | |
new_subvalue.append(new_subword) | |
new_word = f'{subfunc}({" ".join(new_subvalue)}' | |
new_value.append(new_word) | |
new_declarations.append(': '.join([prop, ' '.join(new_value)])) | |
rule[DECLARATIONS] = new_declarations | |
# TODO: apply the same algorithm to the inner rules and to SVG | |
with open(f'_inverted.css', 'w') as fout: | |
# fout.write(f'/* {fn}.css inverted */\n') | |
for rule in rules: | |
fout.write(f'{rule[SELECTOR]}'' {\n') | |
declaration: str | |
for declaration in rule[DECLARATIONS]: | |
fout.write(f' {declaration}') | |
if not rule[SELECTOR].startswith('@'): | |
fout.write(';') | |
fout.write('\n') | |
fout.write('}\n\n') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment