jekyll-image-links 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +195 -0
- data/assets/jekyll-image-links/image_links.css +180 -0
- data/assets/jekyll-image-links/image_links.js +468 -0
- data/lib/jekyll/image_links/asset_file.rb +17 -0
- data/lib/jekyll/image_links/generator.rb +34 -0
- data/lib/jekyll/image_links/hooks.rb +61 -0
- data/lib/jekyll/image_links/map_renderer.rb +395 -0
- data/lib/jekyll/image_links/tags.rb +47 -0
- data/lib/jekyll/image_links/version.rb +5 -0
- data/lib/jekyll-image-links.rb +8 -0
- metadata +70 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
require "digest"
|
|
3
|
+
require "json"
|
|
4
|
+
require "yaml"
|
|
5
|
+
|
|
6
|
+
module Jekyll
|
|
7
|
+
module ImageLinks
|
|
8
|
+
class MapRenderer
|
|
9
|
+
PORTABLE_FIGURE = /<figure\b[^>]*\bdata-jil-map="true"[^>]*>[\s\S]*?<\/figure>/i
|
|
10
|
+
PORTABLE_IMAGE = /<img\b[^>]*\bclass="[^"]*\bjil-map-image\b[^"]*"[^>]*\/?>(?:\s*<script\b[^>]*\bclass="jil-regions-data"[^>]*>[\s\S]*?<\/script>)?/i
|
|
11
|
+
REGIONS_SCRIPT = /<script\b[^>]*\bclass="jil-regions-data"[^>]*>([\s\S]*?)<\/script>/i
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def portable_markup?(html)
|
|
15
|
+
return true if html.include?('data-jil-map="true"')
|
|
16
|
+
|
|
17
|
+
html.scan(PORTABLE_IMAGE).any? { |fragment| portable_image?(fragment) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def enhance_html(html, site:, page:, cfg:)
|
|
21
|
+
return html unless portable_markup?(html)
|
|
22
|
+
|
|
23
|
+
html = html.gsub(PORTABLE_FIGURE) do |figure_html|
|
|
24
|
+
enhance_portable_markup(figure_html, site: site, page: page, cfg: cfg)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
Jekyll.logger.warn("jekyll-image-links:", "Failed to enhance portable figure: #{e.message}")
|
|
27
|
+
figure_html
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
html.gsub(PORTABLE_IMAGE) do |image_html|
|
|
31
|
+
next image_html unless portable_image?(image_html)
|
|
32
|
+
|
|
33
|
+
enhance_portable_markup(image_html, site: site, page: page, cfg: cfg)
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
Jekyll.logger.warn("jekyll-image-links:", "Failed to enhance portable image map: #{e.message}")
|
|
36
|
+
image_html
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def render_portable_figure(attrs, site:, page: nil, liquid_context: nil, cfg: {}, regions_body: nil)
|
|
41
|
+
src = attrs["src"].to_s
|
|
42
|
+
title = attrs["title"]
|
|
43
|
+
alt = attrs["alt"] || title
|
|
44
|
+
regions_file = attrs["file"]
|
|
45
|
+
viewer = attrs["viewer"]
|
|
46
|
+
inline = attrs["inline"]
|
|
47
|
+
labels = attrs["labels"]
|
|
48
|
+
|
|
49
|
+
resolved_src = resolve_url(src, site: site, page: page, liquid_context: liquid_context)
|
|
50
|
+
map_source = load_map_source(
|
|
51
|
+
site: site,
|
|
52
|
+
regions_file: regions_file,
|
|
53
|
+
regions_body: regions_body
|
|
54
|
+
)
|
|
55
|
+
native_from_source = native_dimensions_present?(map_source)
|
|
56
|
+
display_style = build_display_style(attrs, native_from_source: native_from_source)
|
|
57
|
+
|
|
58
|
+
regions_attr = regions_file ? %( data-jil-regions="#{escape_attr(regions_file)}") : ""
|
|
59
|
+
viewer_attr = %( data-jil-viewer="#{viewer}") unless viewer.nil? || viewer.empty?
|
|
60
|
+
inline_attr = %( data-jil-inline="#{inline}") unless inline.nil? || inline.empty?
|
|
61
|
+
labels_attr = %( data-jil-labels="#{labels}") unless labels.nil? || labels.empty?
|
|
62
|
+
title_attr = title ? %( data-jil-title="#{escape_attr(title)}") : ""
|
|
63
|
+
|
|
64
|
+
regions_script = ""
|
|
65
|
+
if regions_body && !regions_body.strip.empty? && regions_file.to_s.strip.empty?
|
|
66
|
+
regions_script = %(\n<script type="application/yaml" class="jil-regions-data">#{regions_body.strip}\n</script>)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
img_markup = render_img_attrs(
|
|
70
|
+
src: resolved_src,
|
|
71
|
+
alt: alt,
|
|
72
|
+
native_width: native_from_source ? nil : attrs["width"],
|
|
73
|
+
native_height: native_from_source ? nil : attrs["height"]
|
|
74
|
+
)
|
|
75
|
+
style_attr = display_style.empty? ? "" : %( style="#{escape_attr(display_style)}")
|
|
76
|
+
|
|
77
|
+
<<~HTML.strip
|
|
78
|
+
<img
|
|
79
|
+
class="jil-map-image"
|
|
80
|
+
#{img_markup}#{style_attr}
|
|
81
|
+
loading="lazy"#{title_attr}#{regions_attr}#{viewer_attr}#{inline_attr}#{labels_attr}
|
|
82
|
+
/>#{regions_script}
|
|
83
|
+
HTML
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def render_interactive(map_data, viewer:, inline:, labels:, alt:, display_style: nil)
|
|
87
|
+
map_json = JSON.generate(map_data)
|
|
88
|
+
id = "jil-map-#{Digest::MD5.hexdigest(map_json)[0, 8]}"
|
|
89
|
+
title = map_data["title"]
|
|
90
|
+
caption_html = title ? %(<div class="jil-caption">#{escape_html(title)}</div>) : ""
|
|
91
|
+
img_attrs = render_img_attrs(
|
|
92
|
+
src: map_data["src"],
|
|
93
|
+
alt: alt,
|
|
94
|
+
native_width: nil,
|
|
95
|
+
native_height: nil
|
|
96
|
+
)
|
|
97
|
+
host_class = "jil-map-host"
|
|
98
|
+
host_class += " jil-height-constrained" if display_style.to_s.match?(/(?:^|;|\s)max-height\s*:/i)
|
|
99
|
+
host_style_attr = display_style.to_s.strip.empty? ? "" : %( style="#{escape_attr(display_style)}")
|
|
100
|
+
|
|
101
|
+
<<~HTML.strip
|
|
102
|
+
<div class="jil-figure" data-jil-image-map="true"><div class="#{host_class}" id="#{id}" data-jil-map="#{escape_attr(map_json)}" data-jil-viewer="#{viewer}" data-jil-inline="#{inline}" data-jil-labels="#{labels}"#{host_style_attr.empty? ? "" : " #{host_style_attr}"}><img class="jil-map-image" #{img_attrs} loading="lazy" /></div>#{caption_html}</div>
|
|
103
|
+
HTML
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def enhance_portable_markup(markup_html, site:, page:, cfg:)
|
|
109
|
+
figure_tag = markup_html[/\A<figure\b[^>]*>/i]
|
|
110
|
+
img_tag = markup_html[/<img\b[^>]*>/i] || ""
|
|
111
|
+
container_tag = figure_tag || img_tag
|
|
112
|
+
container_attrs = parse_html_attrs(container_tag)
|
|
113
|
+
img_attrs = parse_html_attrs(img_tag)
|
|
114
|
+
|
|
115
|
+
merged = {
|
|
116
|
+
"src" => container_attrs["data-jil-src"] || img_attrs["src"],
|
|
117
|
+
"width" => container_attrs["data-jil-width"] || img_attrs["width"],
|
|
118
|
+
"height" => container_attrs["data-jil-height"] || img_attrs["height"],
|
|
119
|
+
"style" => img_attrs["style"],
|
|
120
|
+
"data-jil-max-width" => img_attrs["data-jil-max-width"],
|
|
121
|
+
"data-jil-max-height" => img_attrs["data-jil-max-height"],
|
|
122
|
+
"title" => container_attrs["data-jil-title"],
|
|
123
|
+
"alt" => img_attrs["alt"],
|
|
124
|
+
"file" => container_attrs["data-jil-regions"] || img_attrs["data-jil-regions"],
|
|
125
|
+
"viewer" => container_attrs["data-jil-viewer"] || img_attrs["data-jil-viewer"],
|
|
126
|
+
"inline" => container_attrs["data-jil-inline"] || img_attrs["data-jil-inline"],
|
|
127
|
+
"labels" => container_attrs["data-jil-labels"] || img_attrs["data-jil-labels"],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
merged["title"] ||= img_attrs["data-jil-title"] || merged["alt"]
|
|
131
|
+
|
|
132
|
+
regions_body = markup_html[REGIONS_SCRIPT, 1]
|
|
133
|
+
render_interactive_from_attrs(merged, site: site, page: page, cfg: cfg, regions_body: regions_body)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def portable_image?(fragment)
|
|
137
|
+
return false unless fragment.match?(/\bclass="[^"]*\bjil-map-image\b/i)
|
|
138
|
+
|
|
139
|
+
fragment.include?("data-jil-regions=") ||
|
|
140
|
+
fragment.include?('data-jil-map="true"') ||
|
|
141
|
+
fragment.match?(REGIONS_SCRIPT)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def render_interactive_from_attrs(attrs, site:, page:, cfg:, regions_body: nil, liquid_context: nil)
|
|
145
|
+
src = resolve_url(attrs["src"], site: site, page: page, liquid_context: liquid_context)
|
|
146
|
+
title = attrs["title"]
|
|
147
|
+
alt = attrs["alt"] || title
|
|
148
|
+
viewer = parse_bool(attrs["viewer"], default: cfg.fetch("viewer_by_default", true))
|
|
149
|
+
inline = parse_bool(attrs["inline"], default: cfg.fetch("inline_by_default", true))
|
|
150
|
+
labels = parse_bool(attrs["labels"], default: cfg.fetch("labels_by_default", false))
|
|
151
|
+
|
|
152
|
+
map_source = load_map_source(
|
|
153
|
+
site: site,
|
|
154
|
+
regions_file: attrs["file"],
|
|
155
|
+
regions_body: regions_body
|
|
156
|
+
)
|
|
157
|
+
regions = map_source["regions"]
|
|
158
|
+
validate_regions!(regions)
|
|
159
|
+
|
|
160
|
+
width, height = resolve_native_dimensions(map_source, attrs)
|
|
161
|
+
validate_dimensions!(width, height, src)
|
|
162
|
+
|
|
163
|
+
native_from_source = native_dimensions_present?(map_source)
|
|
164
|
+
display_style = build_display_style(attrs, native_from_source: native_from_source)
|
|
165
|
+
|
|
166
|
+
map_data = {
|
|
167
|
+
"src" => src,
|
|
168
|
+
"width" => width,
|
|
169
|
+
"height" => height,
|
|
170
|
+
"title" => title,
|
|
171
|
+
"regions" => normalize_regions(regions, site: site, page: page, liquid_context: liquid_context),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
render_interactive(
|
|
175
|
+
map_data,
|
|
176
|
+
viewer: viewer,
|
|
177
|
+
inline: inline,
|
|
178
|
+
labels: labels,
|
|
179
|
+
alt: alt,
|
|
180
|
+
display_style: display_style
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def load_map_source(site:, regions_file:, regions_body:)
|
|
185
|
+
data =
|
|
186
|
+
if regions_file && !regions_file.to_s.strip.empty?
|
|
187
|
+
path = regions_file.to_s
|
|
188
|
+
full_path = Jekyll.sanitized_path(site.source, path)
|
|
189
|
+
raise ArgumentError, "image_map file not found: #{path}" unless File.file?(full_path)
|
|
190
|
+
|
|
191
|
+
YAML.safe_load(File.read(full_path), permitted_classes: [Date, Time, Symbol], aliases: true)
|
|
192
|
+
elsif regions_body.to_s.strip.empty?
|
|
193
|
+
{}
|
|
194
|
+
else
|
|
195
|
+
YAML.safe_load(regions_body, permitted_classes: [Date, Time, Symbol], aliases: true)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
normalize_map_source(data)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def normalize_map_source(data)
|
|
202
|
+
case data
|
|
203
|
+
when Hash
|
|
204
|
+
{
|
|
205
|
+
"regions" => data["regions"] || data[:regions] || [],
|
|
206
|
+
"width" => data["width"] || data[:width],
|
|
207
|
+
"height" => data["height"] || data[:height],
|
|
208
|
+
}
|
|
209
|
+
when Array
|
|
210
|
+
{ "regions" => data, "width" => nil, "height" => nil }
|
|
211
|
+
when nil
|
|
212
|
+
{ "regions" => [], "width" => nil, "height" => nil }
|
|
213
|
+
else
|
|
214
|
+
raise ArgumentError, "image_map regions must be a YAML list or a mapping with regions:"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def native_dimensions_present?(map_source)
|
|
219
|
+
map_source["width"].to_i.positive? && map_source["height"].to_i.positive?
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def resolve_native_dimensions(map_source, attrs)
|
|
223
|
+
width = map_source["width"].to_i
|
|
224
|
+
height = map_source["height"].to_i
|
|
225
|
+
|
|
226
|
+
if width <= 0 && attrs["width"].to_s.match?(/\A\d+\z/)
|
|
227
|
+
width = attrs["width"].to_i
|
|
228
|
+
end
|
|
229
|
+
if height <= 0 && attrs["height"].to_s.match?(/\A\d+\z/)
|
|
230
|
+
height = attrs["height"].to_i
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
[width, height]
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def build_display_style(attrs, native_from_source:)
|
|
237
|
+
rules = {}
|
|
238
|
+
|
|
239
|
+
merge_style_rules!(rules, attrs["style"]) if attrs["style"]
|
|
240
|
+
|
|
241
|
+
[
|
|
242
|
+
["data-jil-max-width", "max-width"],
|
|
243
|
+
["data-jil-max-height", "max-height"],
|
|
244
|
+
].each do |attr, prop|
|
|
245
|
+
value = attrs[attr]
|
|
246
|
+
rules[prop] = value if value && !value.to_s.strip.empty?
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
%w[width height].each do |dim|
|
|
250
|
+
value = attrs[dim].to_s.strip
|
|
251
|
+
next if value.empty?
|
|
252
|
+
next if !native_from_source && value.match?(/\A\d+\z/)
|
|
253
|
+
|
|
254
|
+
rules[dim] = format_css_size(value)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
rules.map { |prop, value| "#{prop}: #{value}" }.join("; ")
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def merge_style_rules!(rules, style)
|
|
261
|
+
style.to_s.split(";").each do |declaration|
|
|
262
|
+
prop, value = declaration.split(":", 2).map(&:strip)
|
|
263
|
+
next if prop.nil? || prop.empty? || value.nil? || value.empty?
|
|
264
|
+
|
|
265
|
+
rules[prop] = value
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def format_css_size(value)
|
|
270
|
+
value = value.to_s.strip
|
|
271
|
+
return value if value.match?(/\A[\d.]+%\z/) || value.match?(/\A[\d.]+px\z/i) || value == "auto"
|
|
272
|
+
|
|
273
|
+
return "#{value}px" if value.match?(/\A\d+\z/)
|
|
274
|
+
|
|
275
|
+
value
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def render_img_attrs(src:, alt:, native_width:, native_height:)
|
|
279
|
+
parts = [
|
|
280
|
+
%(src="#{escape_attr(src)}"),
|
|
281
|
+
%(alt="#{escape_attr(alt)}"),
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
if native_width && native_height &&
|
|
285
|
+
native_width.to_s.match?(/\A\d+\z/) && native_height.to_s.match?(/\A\d+\z/)
|
|
286
|
+
parts << %(width="#{escape_attr(native_width)}")
|
|
287
|
+
parts << %(height="#{escape_attr(native_height)}")
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
parts.join(" ")
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def validate_regions!(regions)
|
|
294
|
+
raise ArgumentError, "image_map requires at least one region" if regions.nil? || regions.empty?
|
|
295
|
+
|
|
296
|
+
regions.each_with_index do |region, index|
|
|
297
|
+
region = stringify_keys(region)
|
|
298
|
+
raise ArgumentError, "region #{index + 1} is missing href" if region["href"].to_s.strip.empty?
|
|
299
|
+
raise ArgumentError, "region #{index + 1} is missing points" unless region["points"].is_a?(Array) && !region["points"].empty?
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def validate_dimensions!(width, height, src)
|
|
304
|
+
unless width.positive? && height.positive?
|
|
305
|
+
raise ArgumentError, "image_map requires native width and height in the YAML file or numeric width/height attributes"
|
|
306
|
+
end
|
|
307
|
+
raise ArgumentError, "image_map requires src attribute" if src.to_s.strip.empty?
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def normalize_regions(regions, site:, page:, liquid_context: nil)
|
|
311
|
+
regions.map do |region|
|
|
312
|
+
region = stringify_keys(region)
|
|
313
|
+
{
|
|
314
|
+
"href" => resolve_url(region["href"], site: site, page: page, liquid_context: liquid_context),
|
|
315
|
+
"title" => region["title"] || region["label"] || region["name"],
|
|
316
|
+
"label" => region["label"] || region["title"] || region["name"],
|
|
317
|
+
"points" => normalize_points(region["points"]),
|
|
318
|
+
}
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def normalize_points(points)
|
|
323
|
+
points.map do |point|
|
|
324
|
+
case point
|
|
325
|
+
when Array
|
|
326
|
+
[point[0].to_i, point[1].to_i]
|
|
327
|
+
when Hash
|
|
328
|
+
[(point["x"] || point[:x]).to_i, (point["y"] || point[:y]).to_i]
|
|
329
|
+
else
|
|
330
|
+
raise ArgumentError, "image_map points must be [x, y] pairs"
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def resolve_url(url, site:, page: nil, liquid_context: nil)
|
|
336
|
+
return "" if url.nil?
|
|
337
|
+
|
|
338
|
+
url = Liquid::Template.parse(url.to_s).render(liquid_context).strip if liquid_context
|
|
339
|
+
url = url.to_s.strip
|
|
340
|
+
return url if url.start_with?("http://", "https://", "mailto:", "#")
|
|
341
|
+
|
|
342
|
+
baseurl = site.baseurl.to_s
|
|
343
|
+
baseurl = "" if baseurl == "/"
|
|
344
|
+
|
|
345
|
+
page_url = page_url_for(page)
|
|
346
|
+
|
|
347
|
+
if url.start_with?("/")
|
|
348
|
+
"#{baseurl}#{url}"
|
|
349
|
+
else
|
|
350
|
+
page_dir = page_url ? File.dirname(page_url) : ""
|
|
351
|
+
"#{baseurl}#{File.join(page_dir, url)}"
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def page_url_for(page)
|
|
356
|
+
return page.url if page.respond_to?(:url) && page.url
|
|
357
|
+
return page["url"] if page.respond_to?(:[]) && page["url"]
|
|
358
|
+
|
|
359
|
+
nil
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def parse_bool(value, default:)
|
|
363
|
+
return default if value.nil? || value.to_s.empty?
|
|
364
|
+
%w[true 1 yes on].include?(value.to_s.downcase)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def parse_html_attrs(tag_open)
|
|
368
|
+
attrs = {}
|
|
369
|
+
tag_open.scan(/([\w-]+)\s*=\s*"([^"]*)"/) { |key, value| attrs[key] = value }
|
|
370
|
+
tag_open.scan(/([\w-]+)\s*=\s*'([^']*)'/) { |key, value| attrs[key] = value }
|
|
371
|
+
attrs
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def stringify_keys(value)
|
|
375
|
+
return value unless value.is_a?(Hash)
|
|
376
|
+
|
|
377
|
+
value.each_with_object({}) do |(key, val), out|
|
|
378
|
+
out[key.to_s] = val
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def escape_attr(value)
|
|
383
|
+
value.to_s
|
|
384
|
+
.gsub("&", "&")
|
|
385
|
+
.gsub('"', """)
|
|
386
|
+
.gsub("<", "<")
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def escape_html(value)
|
|
390
|
+
CGI.escapeHTML(value.to_s)
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Jekyll
|
|
2
|
+
module ImageLinks
|
|
3
|
+
class ImageMapTag < Liquid::Block
|
|
4
|
+
def initialize(tag_name, markup, tokens)
|
|
5
|
+
super
|
|
6
|
+
@attrs = parse_attrs(markup)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def blank?
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(context)
|
|
14
|
+
site = context.registers[:site]
|
|
15
|
+
cfg = (site.config["image_links"] || {})
|
|
16
|
+
regions_body = @attrs["file"] ? nil : super
|
|
17
|
+
|
|
18
|
+
figure = MapRenderer.render_portable_figure(
|
|
19
|
+
@attrs,
|
|
20
|
+
site: site,
|
|
21
|
+
page: context.registers[:page],
|
|
22
|
+
liquid_context: context,
|
|
23
|
+
cfg: cfg,
|
|
24
|
+
regions_body: regions_body
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
<<~HTML
|
|
28
|
+
{::nomarkdown}
|
|
29
|
+
#{figure}
|
|
30
|
+
{:/nomarkdown}
|
|
31
|
+
HTML
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def parse_attrs(markup)
|
|
37
|
+
attrs = {}
|
|
38
|
+
markup.scan(/(\w+)\s*=\s*"([^"]*)"/) { |key, value| attrs[key] = value }
|
|
39
|
+
markup.scan(/(\w+)\s*=\s*'([^']*)'/) { |key, value| attrs[key] = value }
|
|
40
|
+
attrs["file"] ||= markup[/\Afile:\s*(\S+)/, 1] if markup.include?("file:")
|
|
41
|
+
attrs
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Liquid::Template.register_tag("image_map", Jekyll::ImageLinks::ImageMapTag)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
require "jekyll"
|
|
2
|
+
|
|
3
|
+
require_relative "jekyll/image_links/version"
|
|
4
|
+
require_relative "jekyll/image_links/asset_file"
|
|
5
|
+
require_relative "jekyll/image_links/map_renderer"
|
|
6
|
+
require_relative "jekyll/image_links/generator"
|
|
7
|
+
require_relative "jekyll/image_links/hooks"
|
|
8
|
+
require_relative "jekyll/image_links/tags"
|
metadata
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jekyll-image-links
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- directsun
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: jekyll
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.7'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '5.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '3.7'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '5.0'
|
|
32
|
+
email: []
|
|
33
|
+
executables: []
|
|
34
|
+
extensions: []
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- LICENSE
|
|
38
|
+
- README.md
|
|
39
|
+
- assets/jekyll-image-links/image_links.css
|
|
40
|
+
- assets/jekyll-image-links/image_links.js
|
|
41
|
+
- lib/jekyll-image-links.rb
|
|
42
|
+
- lib/jekyll/image_links/asset_file.rb
|
|
43
|
+
- lib/jekyll/image_links/generator.rb
|
|
44
|
+
- lib/jekyll/image_links/hooks.rb
|
|
45
|
+
- lib/jekyll/image_links/map_renderer.rb
|
|
46
|
+
- lib/jekyll/image_links/tags.rb
|
|
47
|
+
- lib/jekyll/image_links/version.rb
|
|
48
|
+
homepage: https://github.com/sunflowermans/image-links
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata: {}
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '3.0'
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
requirements: []
|
|
66
|
+
rubygems_version: 3.6.9
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Jekyll plugin for clickable polygon regions on images, inspired by 5etools
|
|
69
|
+
map viewer.
|
|
70
|
+
test_files: []
|