al_img_tools 1.0.0 → 1.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f6550e1c0bb52d5441af3ff5694fe09b246860aaab960b27dffe8465d60a5d3
4
- data.tar.gz: 797301afab6ee75d003251f6bbe9500863c87a0eb1f33df7f5375dd3c634f8f7
3
+ metadata.gz: ec966661d5fe3b82c8c518b693066dfe82cde38a68c2ad7c4b171e8fff8a0840
4
+ data.tar.gz: acbcc9a376c6314c4426e38fc140fd40565688d2c550b541194a58f642ca781b
5
5
  SHA512:
6
- metadata.gz: 7dfcadad0d3e1d810e559f4e5cfa201cef6bd96584a40aff6bd5378fd486ef24a0067dc7d4285647f9a8b6fb634024eb98e941fc106826c429b8f05b95dd782d
7
- data.tar.gz: 782ff6c34c4f358063421e707afe9635b82f0b667099c8138b1f3f6587c945335fb8bd578660ba7c322c2fe83b2fafb573f158309901fdc27fb04ecf7cdc44a2
6
+ metadata.gz: f49455fd21eaa46032f28fe98d949a151f55a953763574cd0e6af2c7539fba5f6e5f6048274d22ba15bf653c46f5ede19fb03a5c122c2b27758f86ea401cb7da
7
+ data.tar.gz: b982464ec52704b12592536ddd0f3ca417df17d1591cb1ba2eafb6c152d0f5d0f066e8d6421477d5890fac53365fb61cf6a571e0c098a95778288a59ef8a0487
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.3 - 2026-07-24
4
+
5
+ ### Security
6
+
7
+ - Bumped Swiper from `11.0.5` to `12.1.2` to remediate a prototype-pollution vulnerability (CVE-2026-27212 / GHSA-hmx5-qpq5-p643, CVSS 9.4) affecting Swiper `>= 6.5.1, < 12.1.2`. Updated the pinned CDN version and the CSS / JS / source-map SRI integrity hashes to match the new release. The image slider consumes the `swiper-element` Web Component bundle, whose `<swiper-container>` custom-element API is unchanged across this major bump, so no template or setup-script changes are required.
8
+
9
+ ## 1.0.2 - 2026-02-17
10
+
11
+ - Finalized plugin-owned lightbox runtime by shipping a vanilla Lightbox2-compatible adapter path.
12
+ - Ensured static asset generator picks up all plugin runtime assets under `lib/assets/al_img_tools/**`.
13
+ - Added test coverage for adapter CSS/JS packaging contracts.
14
+ - Reduced noisy static-file logging by treating unchanged asset copies as debug-level skips.
15
+
16
+ ## 1.0.1 - 2026-02-17
17
+
18
+ - Replaced Lightbox2 CDN runtime dependency with a plugin-owned vanilla lightbox adapter.
19
+ - Added plugin-owned lightbox adapter CSS/JS assets and static asset registration.
20
+ - Removed jQuery dependency from medium zoom initialization.
21
+
3
22
  ## 0.1.0 - 2026-02-07
4
23
 
5
24
  - Initial gem release.
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # Al-Img-Tools
1
+ # al-img-tools
2
2
 
3
- A Jekyll plugin that provides various image manipulation features for al-folio sites.
3
+ `al_img_tools` provides image/gallery tooling for `al-folio` v1.x and compatible Jekyll sites.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Image comparison sliders
8
- - Lightbox galleries
8
+ - Lightbox-style galleries (vanilla adapter for `data-lightbox` markup)
9
9
  - Medium zoom
10
10
  - Image sliders
11
11
  - PhotoSwipe galleries
@@ -14,62 +14,46 @@ A Jekyll plugin that provides various image manipulation features for al-folio s
14
14
 
15
15
  ## Installation
16
16
 
17
- Add this line to your Jekyll site's Gemfile:
18
-
19
17
  ```ruby
20
18
  gem 'al_img_tools'
21
19
  ```
22
20
 
23
- And then execute:
24
-
25
- ```bash
26
- $ bundle install
27
- ```
28
-
29
- ## Usage
30
-
31
- 1. Add the plugin to your site's `_config.yml`:
32
-
33
21
  ```yaml
34
22
  plugins:
35
23
  - al_img_tools
36
24
  ```
37
25
 
38
- 2. Use image features in your pages:
26
+ ## Usage
27
+
28
+ Per-page front matter example:
39
29
 
40
30
  ```yaml
41
31
  ---
42
32
  layout: page
43
33
  title: Gallery
44
34
  images:
45
- lightbox2: true # Enable Lightbox2 gallery (or use `gallery: true`)
46
- compare: true # Enable image comparison slider
47
- slider: true # Enable image slider
48
- photoswipe: true # Enable PhotoSwipe gallery
49
- spotlight: true # Enable Spotlight gallery
50
- venobox: true # Enable VenoBox gallery
51
- medium_zoom: true # Optional per-page medium zoom override
35
+ lightbox2: true
36
+ compare: true
37
+ slider: true
38
+ photoswipe: true
39
+ spotlight: true
40
+ venobox: true
41
+ medium_zoom: true
52
42
  ---
53
43
  ```
54
44
 
55
- 3. Add the image tags to your layout:
56
-
57
- - In the `<head>` (for CSS):
45
+ Render plugin assets:
58
46
 
59
47
  ```liquid
60
48
  {% al_img_tools_styles %}
61
- ```
62
-
63
- - Before `</body>` (for JavaScript):
64
-
65
- ```liquid
66
49
  {% al_img_tools_scripts %}
67
50
  ```
68
51
 
69
- ## Development
52
+ ## Ecosystem context
70
53
 
71
- After checking out the repo, run `bundle install` to install dependencies.
54
+ - Starter examples/docs live in `al-folio`.
55
+ - Gallery/lightbox runtime behavior is owned here.
72
56
 
73
57
  ## Contributing
74
58
 
75
- Bug reports and pull requests are welcome on GitHub.
59
+ Image runtime/adapter behavior changes should be proposed in this repository.
data/lib/al_img_tools.rb CHANGED
@@ -4,8 +4,8 @@ module AlImgTools
4
4
  # Directory structure constants
5
5
  PLUGIN_NAME = 'al_img_tools'
6
6
  ASSETS_DIR = 'assets'
7
+ CSS_DIR = 'css'
7
8
  JS_DIR = 'js'
8
- JS_FILES = ['zoom.js', 'venobox-setup.js'].freeze
9
9
 
10
10
  # Custom StaticFile class to track when files are written
11
11
  class PluginStaticFile < Jekyll::StaticFile
@@ -15,7 +15,7 @@ module AlImgTools
15
15
  if result
16
16
  Jekyll.logger.info("AlImgTools:", "Successfully copied #{name} from #{path} to #{destination(dest)}")
17
17
  else
18
- Jekyll.logger.error("AlImgTools:", "Failed to copy #{name} from #{path}")
18
+ Jekyll.logger.debug("AlImgTools:", "Skipped unchanged asset #{name} from #{path}")
19
19
  end
20
20
  end
21
21
  end
@@ -26,36 +26,14 @@ module AlImgTools
26
26
  priority :low
27
27
 
28
28
  def generate(site)
29
- # Get the plugin's assets directory path - now includes the full desired structure
30
29
  plugin_lib_path = File.expand_path('.', __dir__)
31
- Jekyll.logger.info("AlImgTools:", "Plugin lib directory: #{plugin_lib_path}")
32
-
33
- # Register each JS file
34
- JS_FILES.each do |js_file|
35
- # Construct source path - now includes the full path structure we want in the output
36
- source_path = File.join(plugin_lib_path, ASSETS_DIR, PLUGIN_NAME, JS_DIR, js_file)
37
- Jekyll.logger.info("AlImgTools:", "Looking for source file: #{source_path}")
38
-
39
- # Only add the file if it exists
40
- if File.exist?(source_path)
41
- # Create a static file with:
42
- # - site: the Jekyll site instance
43
- # - base: the base directory containing the file
44
- # - dir: the directory relative to base (used to construct the destination path)
45
- # - name: the file name
46
- static_file = PluginStaticFile.new(
47
- site,
48
- plugin_lib_path, # base directory
49
- File.join(ASSETS_DIR, PLUGIN_NAME, JS_DIR), # source dir relative to base
50
- js_file # filename
51
- )
52
-
53
- # The file will be copied to: _site/assets/al_img_tools/js/[filename]
54
- site.static_files << static_file
55
- Jekyll.logger.info("AlImgTools:", "Registered #{js_file} for copying to #{ASSETS_DIR}/#{PLUGIN_NAME}/#{JS_DIR}/")
56
- else
57
- Jekyll.logger.warn("AlImgTools:", "JavaScript file not found: #{source_path}")
58
- end
30
+ assets_root = File.join(plugin_lib_path, ASSETS_DIR, PLUGIN_NAME)
31
+
32
+ Dir.glob(File.join(assets_root, '**', '*')).sort.each do |source_path|
33
+ next if File.directory?(source_path)
34
+
35
+ relative_dir = File.dirname(source_path).sub("#{plugin_lib_path}/", '')
36
+ site.static_files << PluginStaticFile.new(site, plugin_lib_path, relative_dir, File.basename(source_path))
59
37
  end
60
38
  end
61
39
  end
@@ -75,17 +53,6 @@ module AlImgTools
75
53
  },
76
54
  'version' => '8.0.6'
77
55
  },
78
- 'lightbox2' => {
79
- 'integrity' => {
80
- 'css' => 'sha256-uypRbsAiJcFInM/ndyI/JHpzNe6DtUNXaWEUWEPfMGo=',
81
- 'js' => 'sha256-A6jI5V9s1JznkWwsBaRK8kSeXLgIqQfxfnvdDOZEURY='
82
- },
83
- 'url' => {
84
- 'css' => 'https://cdn.jsdelivr.net/npm/lightbox2@{{version}}/dist/css/lightbox.min.css',
85
- 'js' => 'https://cdn.jsdelivr.net/npm/lightbox2@{{version}}/dist/js/lightbox.min.js'
86
- },
87
- 'version' => '2.11.5'
88
- },
89
56
  'medium_zoom' => {
90
57
  'integrity' => {
91
58
  'js' => 'sha256-ZgMyDAIYDYGxbcpJcfUnYwNevG/xi9OHKaR/8GK+jWc='
@@ -116,16 +83,16 @@ module AlImgTools
116
83
  },
117
84
  'swiper' => {
118
85
  'integrity' => {
119
- 'css' => 'sha256-yUoNxsvX+Vo8Trj3lZ/Y5ZBf8HlBFsB6Xwm7rH75/9E=',
120
- 'js' => 'sha256-BPrwikijIybg9OQC5SYFFqhBjERYOn97tCureFgYH1E=',
121
- 'map' => 'sha256-lbF5CsospW93otqvWOIbbhj80CjazrZXvamD7nC7TBI='
86
+ 'css' => 'sha256-luxVrnBnR9z2CvS7noxOPcUPX9nt8w0l4LscODUm5/k=',
87
+ 'js' => 'sha256-J5Bi68Hj65rj5tUW3iI6qEJFxBuP5ncTmqL1+3NFqO0=',
88
+ 'map' => 'sha256-1oHeaqu+exvac/w5nr1IRk0x38JOQxOuaIvgqduHQxo='
122
89
  },
123
90
  'url' => {
124
91
  'css' => 'https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-bundle.min.css',
125
92
  'js' => 'https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-element-bundle.min.js',
126
93
  'map' => 'https://cdn.jsdelivr.net/npm/swiper@{{version}}/swiper-element-bundle.min.js.map'
127
94
  },
128
- 'version' => '11.0.5'
95
+ 'version' => '12.1.2'
129
96
  },
130
97
  'spotlight' => {
131
98
  'integrity' => {
@@ -174,8 +141,8 @@ module AlImgTools
174
141
  !!(images['lightbox2'] || images['gallery'])
175
142
  end
176
143
 
177
- def asset_url(context, file_name)
178
- "#{base_url(context)}/#{ASSETS_DIR}/#{PLUGIN_NAME}/#{JS_DIR}/#{file_name}"
144
+ def asset_url(context, file_name, dir = JS_DIR)
145
+ "#{base_url(context)}/#{ASSETS_DIR}/#{PLUGIN_NAME}/#{dir}/#{file_name}"
179
146
  end
180
147
 
181
148
  def library_url(library, asset_type)
@@ -212,12 +179,10 @@ module AlImgTools
212
179
 
213
180
  if lightbox_enabled?(images)
214
181
  output << <<~HTML
215
- <!-- Lightbox2 -->
182
+ <!-- Lightbox adapter -->
216
183
  <link
217
184
  rel="stylesheet"
218
- href="#{library_url('lightbox2', 'css')}"
219
- integrity="#{library_integrity('lightbox2', 'css')}"
220
- crossorigin="anonymous"
185
+ href="#{asset_url(context, 'lightbox2-adapter.css', CSS_DIR)}"
221
186
  >
222
187
  HTML
223
188
  end
@@ -304,12 +269,7 @@ module AlImgTools
304
269
 
305
270
  if lightbox_enabled?(images)
306
271
  output << <<~HTML
307
- <script
308
- defer
309
- src="#{library_url('lightbox2', 'js')}"
310
- integrity="#{library_integrity('lightbox2', 'js')}"
311
- crossorigin="anonymous"
312
- ></script>
272
+ <script defer src="#{asset_url(context, 'lightbox2-adapter.js')}"></script>
313
273
  HTML
314
274
  end
315
275
 
@@ -0,0 +1,68 @@
1
+ .al-lightbox-open {
2
+ overflow: hidden;
3
+ }
4
+
5
+ .al-lightbox-overlay {
6
+ position: fixed;
7
+ inset: 0;
8
+ z-index: 1200;
9
+ display: none;
10
+ align-items: center;
11
+ justify-content: center;
12
+ background: rgba(0, 0, 0, 0.88);
13
+ padding: 2rem;
14
+ }
15
+
16
+ .al-lightbox-overlay.is-open {
17
+ display: flex;
18
+ }
19
+
20
+ .al-lightbox-figure {
21
+ margin: 0;
22
+ max-width: min(90vw, 1200px);
23
+ max-height: 90vh;
24
+ text-align: center;
25
+ }
26
+
27
+ .al-lightbox-image {
28
+ max-width: 100%;
29
+ max-height: calc(90vh - 3rem);
30
+ object-fit: contain;
31
+ }
32
+
33
+ .al-lightbox-caption {
34
+ margin-top: 0.75rem;
35
+ color: #f8f8f8;
36
+ font-size: 0.95rem;
37
+ }
38
+
39
+ .al-lightbox-close,
40
+ .al-lightbox-nav {
41
+ position: absolute;
42
+ border: 0;
43
+ background: transparent;
44
+ color: #fff;
45
+ cursor: pointer;
46
+ line-height: 1;
47
+ }
48
+
49
+ .al-lightbox-close {
50
+ top: 1rem;
51
+ right: 1rem;
52
+ font-size: 2rem;
53
+ }
54
+
55
+ .al-lightbox-nav {
56
+ top: 50%;
57
+ transform: translateY(-50%);
58
+ font-size: 2.25rem;
59
+ padding: 0.5rem;
60
+ }
61
+
62
+ .al-lightbox-prev {
63
+ left: 0.75rem;
64
+ }
65
+
66
+ .al-lightbox-next {
67
+ right: 0.75rem;
68
+ }
@@ -0,0 +1,101 @@
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ const links = Array.from(document.querySelectorAll("a[data-lightbox]"));
3
+ if (!links.length) {
4
+ return;
5
+ }
6
+
7
+ const overlay = document.createElement("div");
8
+ overlay.className = "al-lightbox-overlay";
9
+ overlay.setAttribute("role", "dialog");
10
+ overlay.setAttribute("aria-modal", "true");
11
+ overlay.setAttribute("aria-label", "Image preview");
12
+ overlay.innerHTML = `
13
+ <button type="button" class="al-lightbox-close" aria-label="Close preview">&times;</button>
14
+ <button type="button" class="al-lightbox-nav al-lightbox-prev" aria-label="Previous image">&#10094;</button>
15
+ <figure class="al-lightbox-figure">
16
+ <img class="al-lightbox-image" alt="">
17
+ <figcaption class="al-lightbox-caption"></figcaption>
18
+ </figure>
19
+ <button type="button" class="al-lightbox-nav al-lightbox-next" aria-label="Next image">&#10095;</button>
20
+ `;
21
+ document.body.appendChild(overlay);
22
+
23
+ const image = overlay.querySelector(".al-lightbox-image");
24
+ const caption = overlay.querySelector(".al-lightbox-caption");
25
+ const closeBtn = overlay.querySelector(".al-lightbox-close");
26
+ const prevBtn = overlay.querySelector(".al-lightbox-prev");
27
+ const nextBtn = overlay.querySelector(".al-lightbox-next");
28
+
29
+ let currentGroup = [];
30
+ let currentIndex = 0;
31
+
32
+ const readCaption = (link) => {
33
+ return link.getAttribute("data-title") || link.querySelector("img")?.getAttribute("alt") || "";
34
+ };
35
+
36
+ const updateView = () => {
37
+ const link = currentGroup[currentIndex];
38
+ if (!link) {
39
+ return;
40
+ }
41
+ image.src = link.href;
42
+ image.alt = readCaption(link);
43
+ caption.textContent = readCaption(link);
44
+ prevBtn.disabled = currentGroup.length <= 1;
45
+ nextBtn.disabled = currentGroup.length <= 1;
46
+ };
47
+
48
+ const open = (group, index) => {
49
+ currentGroup = group;
50
+ currentIndex = index;
51
+ updateView();
52
+ overlay.classList.add("is-open");
53
+ document.body.classList.add("al-lightbox-open");
54
+ };
55
+
56
+ const close = () => {
57
+ overlay.classList.remove("is-open");
58
+ image.src = "";
59
+ document.body.classList.remove("al-lightbox-open");
60
+ };
61
+
62
+ const step = (delta) => {
63
+ if (currentGroup.length <= 1) {
64
+ return;
65
+ }
66
+ currentIndex = (currentIndex + delta + currentGroup.length) % currentGroup.length;
67
+ updateView();
68
+ };
69
+
70
+ links.forEach((link) => {
71
+ link.addEventListener("click", (event) => {
72
+ event.preventDefault();
73
+ const groupName = link.getAttribute("data-lightbox") || "__default";
74
+ const group = links.filter((candidate) => (candidate.getAttribute("data-lightbox") || "__default") === groupName);
75
+ const index = group.indexOf(link);
76
+ open(group, index < 0 ? 0 : index);
77
+ });
78
+ });
79
+
80
+ closeBtn.addEventListener("click", close);
81
+ prevBtn.addEventListener("click", () => step(-1));
82
+ nextBtn.addEventListener("click", () => step(1));
83
+ overlay.addEventListener("click", (event) => {
84
+ if (event.target === overlay) {
85
+ close();
86
+ }
87
+ });
88
+
89
+ document.addEventListener("keydown", (event) => {
90
+ if (!overlay.classList.contains("is-open")) {
91
+ return;
92
+ }
93
+ if (event.key === "Escape") {
94
+ close();
95
+ } else if (event.key === "ArrowLeft") {
96
+ step(-1);
97
+ } else if (event.key === "ArrowRight") {
98
+ step(1);
99
+ }
100
+ });
101
+ });
@@ -1,6 +1,9 @@
1
- // Initialize medium zoom.
2
- $(document).ready(function () {
3
- medium_zoom = mediumZoom("[data-zoomable]", {
4
- background: getComputedStyle(document.documentElement).getPropertyValue("--global-bg-color") + "ee", // + 'ee' for trasparency.
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ if (typeof mediumZoom !== "function") {
3
+ return;
4
+ }
5
+ mediumZoom("[data-zoomable]", {
6
+ // Append alpha to theme background color to keep content visible under zoom.
7
+ background: `${getComputedStyle(document.documentElement).getPropertyValue("--global-bg-color")}ee`,
5
8
  });
6
- });
9
+ });
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: al_img_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - al-org
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-16 00:00:00.000000000 Z
11
+ date: 2026-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -53,7 +53,7 @@ dependencies:
53
53
  version: '2.0'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '3.0'
56
+ version: '5.0'
57
57
  type: :development
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -63,7 +63,7 @@ dependencies:
63
63
  version: '2.0'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '3.0'
66
+ version: '5.0'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -90,6 +90,8 @@ files:
90
90
  - LICENSE
91
91
  - README.md
92
92
  - lib/al_img_tools.rb
93
+ - lib/assets/al_img_tools/css/lightbox2-adapter.css
94
+ - lib/assets/al_img_tools/js/lightbox2-adapter.js
93
95
  - lib/assets/al_img_tools/js/venobox-setup.js
94
96
  - lib/assets/al_img_tools/js/zoom.js
95
97
  homepage: https://github.com/al-org-dev/al-img-tools
@@ -114,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
116
  - !ruby/object:Gem::Version
115
117
  version: '0'
116
118
  requirements: []
117
- rubygems_version: 3.4.10
119
+ rubygems_version: 3.5.22
118
120
  signing_key:
119
121
  specification_version: 4
120
122
  summary: Image interaction tags and assets for Jekyll