webcode-tk 1.0.2__tar.gz → 1.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/PKG-INFO +1 -1
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/pyproject.toml +1 -1
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/__init__.py +1 -1
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/cascade_tools.py +145 -16
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/css_tools.py +61 -14
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/LICENSE.rst +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/README.rst +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/color_keywords.py +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/color_tools.py +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/font_tools.py +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/html_tools.py +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/sample_report.json +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/ux_tools.py +0 -0
- {webcode_tk-1.0.2 → webcode_tk-1.0.6}/webcode_tk/validator_tools.py +0 -0
|
@@ -21,8 +21,8 @@ from webcode_tk import font_tools as fonts
|
|
|
21
21
|
from webcode_tk import html_tools
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
default_global_color = "#
|
|
25
|
-
default_global_background =
|
|
24
|
+
default_global_color = "#000000"
|
|
25
|
+
default_global_background = "#ffffff"
|
|
26
26
|
default_link_color = "#0000EE"
|
|
27
27
|
default_link_visited = "#551A8B"
|
|
28
28
|
root_font_size = 16
|
|
@@ -111,6 +111,13 @@ class Element(object):
|
|
|
111
111
|
"specificity": "",
|
|
112
112
|
"applied_by": "default",
|
|
113
113
|
}
|
|
114
|
+
self.visited_background = {
|
|
115
|
+
"value": "",
|
|
116
|
+
"sheet": "",
|
|
117
|
+
"selector": "",
|
|
118
|
+
"specificity": "",
|
|
119
|
+
"applied_by": "default",
|
|
120
|
+
}
|
|
114
121
|
self.hover_color = {
|
|
115
122
|
"value": "",
|
|
116
123
|
"sheet": "",
|
|
@@ -134,6 +141,13 @@ class Element(object):
|
|
|
134
141
|
"large_aaa": True,
|
|
135
142
|
"large_aa": True,
|
|
136
143
|
}
|
|
144
|
+
self.hover_contrast_data = {
|
|
145
|
+
"ratio": 21.0,
|
|
146
|
+
"normal_aaa": True,
|
|
147
|
+
"normal_aa": True,
|
|
148
|
+
"large_aaa": True,
|
|
149
|
+
"large_aa": True,
|
|
150
|
+
}
|
|
137
151
|
self.visited_contrast_data = {
|
|
138
152
|
"ratio": 0.0,
|
|
139
153
|
"normal_aaa": False,
|
|
@@ -156,6 +170,10 @@ class Element(object):
|
|
|
156
170
|
self.visited_color["value"] = default_link_visited
|
|
157
171
|
self.visited_color["sheet"] = "user-agent"
|
|
158
172
|
self.visited_color["specificity"] = "000"
|
|
173
|
+
self.visited_background["value"] = default_global_background
|
|
174
|
+
self.visited_background["sheet"] = "user-agent"
|
|
175
|
+
self.visited_background["specificity"] = "000"
|
|
176
|
+
self.visited_background["applied_by"] = "context"
|
|
159
177
|
|
|
160
178
|
def ammend_color_styles(self, new_styles: dict, filename: str) -> None:
|
|
161
179
|
"""adjust color styles based on specificity.
|
|
@@ -202,6 +220,25 @@ class Element(object):
|
|
|
202
220
|
# get contrast for color & bg_color
|
|
203
221
|
self.get_contrast_data("standard")
|
|
204
222
|
else:
|
|
223
|
+
# be sure to change at least the background color for pseudo-states
|
|
224
|
+
if targets_bg_color:
|
|
225
|
+
# check hover background
|
|
226
|
+
hover_bg = self.hover_background.get("value")
|
|
227
|
+
hover_specificity = self.hover_background.get("specificity")
|
|
228
|
+
new_bg = new_styles.get("background-color")
|
|
229
|
+
if not hover_bg or new_specificity >= hover_specificity:
|
|
230
|
+
self.hover_background["value"] = new_bg
|
|
231
|
+
self.hover_background["specificity"] = new_specificity
|
|
232
|
+
|
|
233
|
+
# check visited background
|
|
234
|
+
visited_bg = self.visited_background.get("value")
|
|
235
|
+
visited_specificity = self.visited_background.get(
|
|
236
|
+
"specificity"
|
|
237
|
+
)
|
|
238
|
+
if not visited_bg or new_specificity >= visited_specificity:
|
|
239
|
+
self.visited_background["value"] = new_bg
|
|
240
|
+
self.visited_background["specificity"] = new_specificity
|
|
241
|
+
|
|
205
242
|
# Do we have a link selector?
|
|
206
243
|
is_link_selector = css.is_link_selector(selector)
|
|
207
244
|
if is_link_selector:
|
|
@@ -247,6 +284,11 @@ class Element(object):
|
|
|
247
284
|
# If it was not directly set, we can change it
|
|
248
285
|
if not bg_was_directly_set:
|
|
249
286
|
self.background_color["value"] = bg_color
|
|
287
|
+
if self.children:
|
|
288
|
+
for kid in self.children:
|
|
289
|
+
kid.ammend_bg_color(
|
|
290
|
+
bg_color, new_selector, new_specificity, filename
|
|
291
|
+
)
|
|
250
292
|
|
|
251
293
|
def get_contrast_data(self, type="default") -> None:
|
|
252
294
|
"""get color contrast for the type specified
|
|
@@ -615,11 +657,17 @@ class Element(object):
|
|
|
615
657
|
)
|
|
616
658
|
self.background_color["gradient_colors"] = colors
|
|
617
659
|
# set color, specificity, and directly applied
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
660
|
+
if ":hover" in selector:
|
|
661
|
+
background_color_dict = self.hover_background
|
|
662
|
+
elif ":visited" in selector:
|
|
663
|
+
background_color_dict = self.visited_background
|
|
664
|
+
else:
|
|
665
|
+
background_color_dict = self.background_color
|
|
666
|
+
background_color_dict["value"] = val
|
|
667
|
+
background_color_dict["sheet"] = filename
|
|
668
|
+
background_color_dict["specificity"] = new_specificity
|
|
669
|
+
background_color_dict["applied_by"] = "directly"
|
|
670
|
+
background_color_dict["selector"] = selector
|
|
623
671
|
else:
|
|
624
672
|
if not previously_set:
|
|
625
673
|
self.background_color["value"] = val
|
|
@@ -808,7 +856,14 @@ class CSSAppliedTree:
|
|
|
808
856
|
for ruleset in color_rules:
|
|
809
857
|
# get filename
|
|
810
858
|
filename = clerk.get_file_name(sheet.href)
|
|
811
|
-
|
|
859
|
+
selector_source = ""
|
|
860
|
+
if body.name == "body":
|
|
861
|
+
selector_source = "body"
|
|
862
|
+
else:
|
|
863
|
+
input("Wait, what?")
|
|
864
|
+
self.__adjust_colors(
|
|
865
|
+
body, ruleset, filename, selector_src=selector_source
|
|
866
|
+
)
|
|
812
867
|
|
|
813
868
|
def targets_body(self, ruleset: dict) -> bool:
|
|
814
869
|
"""returns whether the ruleset is targetting the body or not.
|
|
@@ -873,7 +928,12 @@ class CSSAppliedTree:
|
|
|
873
928
|
return
|
|
874
929
|
|
|
875
930
|
def __adjust_colors(
|
|
876
|
-
self,
|
|
931
|
+
self,
|
|
932
|
+
element: Element,
|
|
933
|
+
ruleset: dict,
|
|
934
|
+
filename: str,
|
|
935
|
+
applied_by="default",
|
|
936
|
+
selector_src="",
|
|
877
937
|
) -> None:
|
|
878
938
|
"""recursively adjust color where necessary to all elements that
|
|
879
939
|
apply.
|
|
@@ -924,6 +984,8 @@ class CSSAppliedTree:
|
|
|
924
984
|
else:
|
|
925
985
|
if ":hover" in selector:
|
|
926
986
|
color = element.hover_color
|
|
987
|
+
elif ":visited" in selector:
|
|
988
|
+
color = element.visited_color
|
|
927
989
|
else:
|
|
928
990
|
color = element.color
|
|
929
991
|
color["value"] = declaration.get("color")
|
|
@@ -932,8 +994,36 @@ class CSSAppliedTree:
|
|
|
932
994
|
color["specificity"] = specificity
|
|
933
995
|
color["applied_by"] = "directly"
|
|
934
996
|
for child in element.children:
|
|
935
|
-
|
|
936
|
-
|
|
997
|
+
element_name = element.name
|
|
998
|
+
self.__adjust_child_colors(
|
|
999
|
+
child, ruleset, filename, element_name
|
|
1000
|
+
)
|
|
1001
|
+
elif is_background_prop:
|
|
1002
|
+
# background - if not set directly - apply by context
|
|
1003
|
+
value = declaration.get("background-color")
|
|
1004
|
+
if not value:
|
|
1005
|
+
value = declaration.get("background")
|
|
1006
|
+
if property == "background-image":
|
|
1007
|
+
value = declaration.get("background-image")
|
|
1008
|
+
|
|
1009
|
+
# adjust if value is not the same as current value
|
|
1010
|
+
applied_by = element.background_color.get("applied_by")
|
|
1011
|
+
current_value = element.background_color.get("value")
|
|
1012
|
+
time_to_adjust = (
|
|
1013
|
+
applied_by == "default"
|
|
1014
|
+
or applied_by == "context"
|
|
1015
|
+
and value != current_value
|
|
1016
|
+
)
|
|
1017
|
+
# But only if the value was applied to an ancestor
|
|
1018
|
+
# For example, a th is not a parent of a td
|
|
1019
|
+
applied_to_ancestor = False
|
|
1020
|
+
for ancestor in element.ancestors:
|
|
1021
|
+
ancestor_tag = ancestor[0]
|
|
1022
|
+
if ancestor_tag == selector_src:
|
|
1023
|
+
applied_to_ancestor = True
|
|
1024
|
+
time_to_adjust = time_to_adjust and applied_to_ancestor
|
|
1025
|
+
if time_to_adjust:
|
|
1026
|
+
print("Wait")
|
|
937
1027
|
can_inherit = self.can_inherit_styles(element, selector, ruleset)
|
|
938
1028
|
if can_inherit and applies:
|
|
939
1029
|
declaration = ruleset.get(selector)
|
|
@@ -945,7 +1035,11 @@ class CSSAppliedTree:
|
|
|
945
1035
|
self.__adjust_colors(child, ruleset, filename)
|
|
946
1036
|
|
|
947
1037
|
def __adjust_child_colors(
|
|
948
|
-
self,
|
|
1038
|
+
self,
|
|
1039
|
+
child: Element,
|
|
1040
|
+
ruleset: dict,
|
|
1041
|
+
filename: str,
|
|
1042
|
+
applied_by="default",
|
|
949
1043
|
) -> None:
|
|
950
1044
|
"""adjusts colors for child of an element.
|
|
951
1045
|
|
|
@@ -953,10 +1047,16 @@ class CSSAppliedTree:
|
|
|
953
1047
|
to see if the child element is a link or not (as well as the selector)
|
|
954
1048
|
and check the specificity of the ruleset.
|
|
955
1049
|
|
|
1050
|
+
To help with background colors, we need to know where it was applied
|
|
1051
|
+
from (name of tag) - that way we can check if a declaration was applied
|
|
1052
|
+
by one of the tag's parents. If it was, then we apply that color by
|
|
1053
|
+
context taking the background from the nearest parent.
|
|
1054
|
+
|
|
956
1055
|
Args:
|
|
957
1056
|
child: the child element in question
|
|
958
1057
|
ruleset: the CSS rule being applied
|
|
959
1058
|
filename: name of the file where the styles came from
|
|
1059
|
+
applied_from: the name of the element that was targetted
|
|
960
1060
|
"""
|
|
961
1061
|
# Adjust colors if necessary - if there was a change,
|
|
962
1062
|
# adjust colors for children
|
|
@@ -1504,7 +1604,8 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
|
|
|
1504
1604
|
rating: the rating of contrast we check for (AAA or AA). The default
|
|
1505
1605
|
is AAA.
|
|
1506
1606
|
Returns:
|
|
1507
|
-
results
|
|
1607
|
+
results
|
|
1608
|
+
"""
|
|
1508
1609
|
results = []
|
|
1509
1610
|
filename = tree.filename
|
|
1510
1611
|
# Loop recursively through the tree
|
|
@@ -1547,6 +1648,7 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
|
|
|
1547
1648
|
passes = contrast_data.get("normal_aaa")
|
|
1548
1649
|
else:
|
|
1549
1650
|
passes = contrast_data.get("normal_aa")
|
|
1651
|
+
|
|
1550
1652
|
# only add results if failure
|
|
1551
1653
|
if not passes:
|
|
1552
1654
|
if el.is_large:
|
|
@@ -1556,9 +1658,10 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
|
|
|
1556
1658
|
selector = el.background_color.get("selector")
|
|
1557
1659
|
if not selector:
|
|
1558
1660
|
selector = el.color.get("selector")
|
|
1559
|
-
msg = f"
|
|
1560
|
-
msg += f"
|
|
1661
|
+
msg = f"selector: {selector} fails color contrast {size}"
|
|
1662
|
+
msg += f"{rating}"
|
|
1561
1663
|
results.append(msg)
|
|
1664
|
+
|
|
1562
1665
|
if not children:
|
|
1563
1666
|
return
|
|
1564
1667
|
else:
|
|
@@ -1570,7 +1673,7 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
|
|
|
1570
1673
|
if results:
|
|
1571
1674
|
adjusted = []
|
|
1572
1675
|
for result in results:
|
|
1573
|
-
adjusted.append("fail: " + result)
|
|
1676
|
+
adjusted.append(f"fail: ({filename}) " + result)
|
|
1574
1677
|
results = adjusted
|
|
1575
1678
|
else:
|
|
1576
1679
|
# It's a success! Let's let them know
|
|
@@ -1580,6 +1683,32 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
|
|
|
1580
1683
|
return results
|
|
1581
1684
|
|
|
1582
1685
|
|
|
1686
|
+
def get_color_contrast_report(dir_path: str, rating="AAA") -> list:
|
|
1687
|
+
"""returns a list of all failures or a pass based on rating
|
|
1688
|
+
|
|
1689
|
+
Args:
|
|
1690
|
+
dir_path: the path to the directory in question
|
|
1691
|
+
rating: the rating of contrast we check for (AAA or AA). The default
|
|
1692
|
+
is AAA.
|
|
1693
|
+
Returns:
|
|
1694
|
+
results: a list of pass or fail messages.
|
|
1695
|
+
"""
|
|
1696
|
+
results = []
|
|
1697
|
+
files = css.get_styles_by_html_files(dir_path)
|
|
1698
|
+
for file in files:
|
|
1699
|
+
filepath = file.get("file")
|
|
1700
|
+
stylesheets = file.get("stylesheets")
|
|
1701
|
+
tree = CSSAppliedTree(filepath, stylesheets)
|
|
1702
|
+
|
|
1703
|
+
report = get_color_contrast_details(tree, rating)
|
|
1704
|
+
for item in report:
|
|
1705
|
+
if item not in results:
|
|
1706
|
+
results.append(item)
|
|
1707
|
+
# Do one more pass on hyperlinks and double check contrast
|
|
1708
|
+
print(results)
|
|
1709
|
+
return results
|
|
1710
|
+
|
|
1711
|
+
|
|
1583
1712
|
if __name__ == "__main__":
|
|
1584
1713
|
project_path = "tests/test_files/attribute_selector_file"
|
|
1585
1714
|
project_path = "tests/test_files/large_project/"
|
|
@@ -2312,14 +2312,15 @@ def fonts_applied_report(project_dir: str, min=1, max=2) -> list:
|
|
|
2312
2312
|
report = []
|
|
2313
2313
|
all_file_data = get_styles_by_html_files(project_dir)
|
|
2314
2314
|
for file in all_file_data:
|
|
2315
|
+
font_families_applied = []
|
|
2315
2316
|
number_of_fonts = 0
|
|
2316
2317
|
filename = clerk.get_file_name(file.get("file"))
|
|
2317
2318
|
stylesheets = file.get("stylesheets")
|
|
2318
|
-
font_styles = []
|
|
2319
2319
|
for sheet in stylesheets:
|
|
2320
2320
|
font_details = get_font_families(sheet)
|
|
2321
2321
|
number_of_fonts += len(font_details)
|
|
2322
2322
|
for item in font_details:
|
|
2323
|
+
results = None
|
|
2323
2324
|
selector = item.get("selector")
|
|
2324
2325
|
family = item.get("family")
|
|
2325
2326
|
if "," in family:
|
|
@@ -2328,22 +2329,36 @@ def fonts_applied_report(project_dir: str, min=1, max=2) -> list:
|
|
|
2328
2329
|
first_font = family
|
|
2329
2330
|
first_font = first_font.replace("'", "")
|
|
2330
2331
|
first_font = first_font.replace('"', "")
|
|
2332
|
+
if first_font not in font_families_applied:
|
|
2333
|
+
font_families_applied.append(first_font)
|
|
2331
2334
|
if first_font.lower() == "times new roman":
|
|
2332
2335
|
results = f"fail: {filename}: {selector} element was set "
|
|
2333
2336
|
results += "to the default font"
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2337
|
+
num_fonts = len(font_families_applied)
|
|
2338
|
+
if num_fonts >= min and num_fonts <= max:
|
|
2339
|
+
results = f"pass: {filename} applied {num_fonts} "
|
|
2340
|
+
if num_fonts == 1:
|
|
2341
|
+
current_font = font_families_applied[0]
|
|
2342
|
+
results += f"font: {current_font}"
|
|
2343
|
+
else:
|
|
2344
|
+
results += "fonts:"
|
|
2345
|
+
for i in range(num_fonts):
|
|
2346
|
+
current_font = font_families_applied[i]
|
|
2347
|
+
if current_font == font_families_applied[-1]:
|
|
2348
|
+
results += f"and {current_font}"
|
|
2349
|
+
elif num_fonts > 2:
|
|
2350
|
+
results += f" {current_font}, "
|
|
2351
|
+
else:
|
|
2352
|
+
results += f" {current_font} "
|
|
2353
|
+
results += "."
|
|
2354
|
+
elif num_fonts < min:
|
|
2355
|
+
results = f"fail: {filename} did not apply {min} fonts, "
|
|
2356
|
+
results += f"instead, it applied {num_fonts} fonts."
|
|
2357
|
+
elif num_fonts > max:
|
|
2358
|
+
results = f"fail: {filename} applied too many fonts; "
|
|
2359
|
+
results += f" it applied {num_fonts} fonts."
|
|
2360
|
+
if results and results not in report:
|
|
2361
|
+
report.append(results)
|
|
2347
2362
|
if not report:
|
|
2348
2363
|
report.append("fail: no html files to apply font styling to")
|
|
2349
2364
|
return report
|
|
@@ -2461,6 +2476,38 @@ def get_heading_color_report(project_dir: str) -> list:
|
|
|
2461
2476
|
return report
|
|
2462
2477
|
|
|
2463
2478
|
|
|
2479
|
+
def get_project_color_contrast_report(project_dir: str, level="AAA") -> list:
|
|
2480
|
+
"""returns a report of pass or fail for each element targetting color.
|
|
2481
|
+
|
|
2482
|
+
Args:
|
|
2483
|
+
project_dir: the project folder where the html and css files are found.
|
|
2484
|
+
level: the level for the report (AAA or AA)
|
|
2485
|
+
Returns:
|
|
2486
|
+
report: a report of every targetted color and whether it passes or
|
|
2487
|
+
fails.
|
|
2488
|
+
"""
|
|
2489
|
+
report = []
|
|
2490
|
+
large = "Large " + level
|
|
2491
|
+
normal = "Normal " + level
|
|
2492
|
+
results = get_project_color_contrast(project_dir, normal, large)
|
|
2493
|
+
for result in results:
|
|
2494
|
+
filename = clerk.get_file_name(result[0])
|
|
2495
|
+
selector = result[1]
|
|
2496
|
+
level = result[2]
|
|
2497
|
+
color = result[3]
|
|
2498
|
+
bg_color = result[4]
|
|
2499
|
+
ratio = result[5]
|
|
2500
|
+
passes = result[6]
|
|
2501
|
+
if passes:
|
|
2502
|
+
msg = f"pass: {filename} selector: {selector} meets color contrast"
|
|
2503
|
+
else:
|
|
2504
|
+
msg = f"fail: {filename} selector: {selector} DOES NOT meet"
|
|
2505
|
+
msg += f" report (level {level} for colors ({color} & {bg_color})"
|
|
2506
|
+
msg += f" at a ratio of {ratio}."
|
|
2507
|
+
report.append(msg)
|
|
2508
|
+
return report
|
|
2509
|
+
|
|
2510
|
+
|
|
2464
2511
|
if __name__ == "__main__":
|
|
2465
2512
|
insane_gradient = """
|
|
2466
2513
|
-moz-radial-gradient(0% 200%, ellipse cover,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|