Skip to content

Instantly share code, notes, and snippets.

@StSav012
Created October 28, 2018 01:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StSav012/0af7948966e0795ecf798ac6629fd071 to your computer and use it in GitHub Desktop.
Save StSav012/0af7948966e0795ecf798ac6629fd071 to your computer and use it in GitHub Desktop.
# -*- 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