diff --git a/CHANGELOG b/CHANGELOG index c03b14c..d6071d0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +mkdocs-glightbox-0.3.3 (2023-04-20) + + * Refactored processing logic with regex (#14) + mkdocs-glightbox-0.3.2 (2023-03-19) * Supported image without extension (#13) diff --git a/README.md b/README.md index 66fb0c9..794c84e 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ GLightbox is a pure javascript lightbox library with mobile support. ## Dependency -1. Python Package - 1. beautifulsoup4>=4.11.1 -2. GLightbox javascript file and CSS file +1. GLightbox javascript file and CSS file 1. GLightbox==3.2.0 ## Usage diff --git a/mkdocs_glightbox/plugin.py b/mkdocs_glightbox/plugin.py index f0a9296..28290ae 100644 --- a/mkdocs_glightbox/plugin.py +++ b/mkdocs_glightbox/plugin.py @@ -1,8 +1,8 @@ import json import logging import os +import re -from bs4 import BeautifulSoup from mkdocs import utils from mkdocs.config import config_options from mkdocs.plugins import BasePlugin @@ -40,57 +40,53 @@ def on_post_page(self, output, page, config, **kwargs): if "glightbox" in page.meta and page.meta.get("glightbox", True) is False: return output - soup = BeautifulSoup(output, "html.parser") - - if soup.head: - css_link = soup.new_tag("link") - css_link.attrs["href"] = utils.get_relative_url( - utils.normalize_url("assets/stylesheets/glightbox.min.css"), page.url - ) - css_link.attrs["rel"] = "stylesheet" - soup.head.append(css_link) - - css_patch = soup.new_tag("style") - css_patch.string = """ - html.glightbox-open { overflow: initial; height: 100%; } - .gslide-title { margin-top: 0px; user-select: text; } - .gslide-desc { color: #666; user-select: text; } - .gslide-image img { background: white; } + # Define regular expressions for matching the relevant sections of the HTML code + head_regex = re.compile(r"(.*?)<\/head>", flags=re.DOTALL) + body_regex = re.compile(r"", flags=re.DOTALL) + + # Modify the CSS link + css_link = f'' + output = head_regex.sub(f"\\1 {css_link}", output) + + # Modify the CSS patch + css_patch = """ + html.glightbox-open { overflow: initial; height: 100%; } + .gslide-title { margin-top: 0px; user-select: text; } + .gslide-desc { color: #666; user-select: text; } + .gslide-image img { background: white; } + """ + if config["theme"].name == "material": + css_patch += """ + .gscrollbar-fixer { padding-right: 15px; } + .gdesc-inner { font-size: 0.75rem; } + body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color);} + body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color);} + body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);} """ - if config["theme"].name == "material": - css_patch.string += """ - .gscrollbar-fixer { padding-right: 15px; } - .gdesc-inner { font-size: 0.75rem; } - body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color);} - body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color);} - body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color);} - """ - soup.head.append(css_patch) - - js_script = soup.new_tag("script") - js_script.attrs["src"] = utils.get_relative_url( - utils.normalize_url("assets/javascripts/glightbox.min.js"), page.url - ) - soup.head.append(js_script) - - js_code = soup.new_tag("script") - plugin_config = dict(self.config) - lb_config = { - k: plugin_config[k] - for k in ["touchNavigation", "loop", "zoomable", "draggable"] - } - lb_config["openEffect"] = plugin_config.get("effect", "zoom") - lb_config["closeEffect"] = plugin_config.get("effect", "zoom") - lb_config["slideEffect"] = plugin_config.get("slide_effect", "slide") - js_code.string = f"const lightbox = GLightbox({json.dumps(lb_config)});" - if config["theme"].name == "material" or "navigation.instant" in config[ - "theme" - ]._vars.get("features", []): - # support compatible with mkdocs-material Instant loading feature - js_code.string = "document$.subscribe(() => {" + js_code.string + "})" - soup.body.append(js_code) - - return str(soup) + output = head_regex.sub(f"\\1", output) + + # Modify the JS script + js_script = f'' + output = head_regex.sub(f"\\1 {js_script}", output) + + # Modify the JS code + plugin_config = dict(self.config) + lb_config = { + k: plugin_config[k] + for k in ["touchNavigation", "loop", "zoomable", "draggable"] + } + lb_config["openEffect"] = plugin_config.get("effect", "zoom") + lb_config["closeEffect"] = plugin_config.get("effect", "zoom") + lb_config["slideEffect"] = plugin_config.get("slide_effect", "slide") + js_code = f"const lightbox = GLightbox({json.dumps(lb_config)});" + if config["theme"].name == "material" or "navigation.instant" in config[ + "theme" + ]._vars.get("features", []): + # support compatible with mkdocs-material Instant loading feature + js_code = "document$.subscribe(() => {" + js_code + "})" + output = body_regex.sub(f"{js_code}", output) + + return output def on_page_content(self, html, page, config, **kwargs): """Wrap img tag with anchor tag with glightbox class and attributes from config""" @@ -102,47 +98,76 @@ def on_page_content(self, html, page, config, **kwargs): skip_class = ["emojione", "twemoji", "gemoji"] # skip image with off-glb and specific class skip_class += ["off-glb"] + self.config["skip_classes"] - soup = BeautifulSoup(html, "html.parser") - imgs = soup.find_all("img") - for img in imgs: - if set(skip_class) & set(img.get("class", [])) or img.parent.name == "a": - continue - a = soup.new_tag("a") - a["class"] = "glightbox" - a["href"] = img.get("src", "") - a["data-type"] = "image" - # setting data-width and data-height with plugin options - for k, v in plugin_config.items(): - a[f"data-{k}"] = v - slide_options = ["title", "description", "caption-position", "gallery"] - for option in slide_options: - attr = f"data-{option}" - if attr == "data-title": - # alt as title when auto_caption is enabled - if self.config["auto_caption"] or ( - "glightbox.auto_caption" in page.meta - and page.meta.get("glightbox.auto_caption", False) is True - ): - val = img.get("data-title", img.get("alt", "")) - else: - val = img.get("data-title", "") - elif attr == "data-caption-position": - # plugin option caption_position as default - val = img.get( - "data-caption-position", self.config["caption_position"] + + # Use regex to find image tags that need to be wrapped with anchor tags and image tags already wrapped with anchor tags + pattern = re.compile( + r"]*>(?:\s*<[^>]+>\s*)*]*>(?:\s*<[^>]+>\s*)*|.*?)>" + ) + html = pattern.sub( + lambda match: self.wrap_img_with_anchor( + match, plugin_config, skip_class, page.meta + ), + html, + ) + + return html + + def wrap_img_with_anchor(self, match, plugin_config, skip_class, meta): + """Wrap image tags with anchor tags""" + a_pattern = re.compile(r".*?)>") + if a_pattern.match(match.group(0)): + return match.group(0) + + img_tag = match.group(0) + img_attr = match.group("attr") + classes = re.findall(r'class="([^"]+)"', img_attr) + classes = [c for match in classes for c in match.split()] + + if set(skip_class) & set(classes): + return img_tag + + src = re.search(r"src=[\"\']([^\"\']+)", img_attr).group(1) + a_tag = f'{img_tag}" + return a_tag def on_post_build(self, config, **kwargs): """Copy glightbox"s css and js files to assets directory""" diff --git a/setup.py b/setup.py index ffcf783..09e8036 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="mkdocs-glightbox", - version="0.3.2", + version="0.3.3", author="Blueswen", author_email="blueswen.tw@gmail.com", url="https://blueswen.github.io/mkdocs-glightbox", @@ -18,7 +18,6 @@ description="MkDocs plugin supports image lightbox with GLightbox.", long_description=long_description, long_description_content_type="text/markdown", - install_requires=["beautifulsoup4>=4.11.1"], include_package_data=True, entry_points={ "mkdocs.plugins": [ diff --git a/tests/test_builds.py b/tests/test_builds.py index 90276d2..930aa5f 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -122,14 +122,14 @@ def validate_static(html_content: str, path: str = "", exist: bool = True): """ assert exist == ( re.search( - rf'', + rf'', html_content, ) is not None ) assert exist == ( re.search( - rf'