jekyll-image-links 0.1.2 → 0.3.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9d85919e65b8d1b2cf32c8d1ffccc9b149f8d84e7950020f5d291e0262dee60
|
|
4
|
+
data.tar.gz: f933adffe5a1970b78669ae40edd0f5595342c01e43e7f1f53c50072a1f5236b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4454cc915358b308486232099c78a6db594d6c86318e4608cf490ba9a2b6de954a76f276c0e2b91a61127115535ca1eb550ab901a2218cd42be86acd5b7ac83d
|
|
7
|
+
data.tar.gz: 59a5bbac2e96705896e0ddbcedefe36b215f3fc6fbe541c562b4e3a3b91f8f604df03fd0570daf86bcc0898059715eae29a22fb95c7c489457c27a603ff6e76f
|
|
@@ -17,42 +17,140 @@
|
|
|
17
17
|
return !!getHoverPopup();
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function logError(message, error) {
|
|
21
|
+
if (error) console.error(`[jil] ${message}`, error);
|
|
22
|
+
else console.error(`[jil] ${message}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function reportMapProblems(img, mapData) {
|
|
26
|
+
const src = mapData.src || img.getAttribute("src") || "image map";
|
|
27
|
+
const width = Number(mapData.width);
|
|
28
|
+
const height = Number(mapData.height);
|
|
29
|
+
|
|
30
|
+
if (!width || !height) {
|
|
31
|
+
logError(`${src}: map data is missing width or height`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const regions = mapData.regions;
|
|
35
|
+
if (!Array.isArray(regions) || regions.length === 0) {
|
|
36
|
+
logError(`${src}: map data has no regions`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const checkSize = () => {
|
|
40
|
+
const naturalWidth = img.naturalWidth;
|
|
41
|
+
const naturalHeight = img.naturalHeight;
|
|
42
|
+
if (!naturalWidth || !naturalHeight) {
|
|
43
|
+
logError(`${src}: image failed to load`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!width || !height) return;
|
|
47
|
+
if (naturalWidth === width && naturalHeight === height) return;
|
|
48
|
+
logError(`${src}: image is ${naturalWidth}×${naturalHeight} but map data is ${width}×${height}`);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
img.addEventListener("error", () => {
|
|
52
|
+
logError(`${src}: image failed to load`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (img.complete) checkSize();
|
|
56
|
+
else img.addEventListener("load", checkSize);
|
|
57
|
+
|
|
58
|
+
(regions || []).forEach((region, index) => {
|
|
59
|
+
const name = region.title || region.href || `region ${index + 1}`;
|
|
60
|
+
const points = region.points;
|
|
61
|
+
if (!region.href) logError(`${src}: "${name}" is missing href`);
|
|
62
|
+
if (!Array.isArray(points) || points.length < 3) {
|
|
63
|
+
logError(`${src}: "${name}" needs at least 3 points`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!width || !height) return;
|
|
67
|
+
|
|
68
|
+
const xs = points.map((point) => point[0]);
|
|
69
|
+
const ys = points.map((point) => point[1]);
|
|
70
|
+
const outside = xs.some((x) => x < 0 || x > width) || ys.some((y) => y < 0 || y > height);
|
|
71
|
+
if (!outside) return;
|
|
72
|
+
logError(
|
|
73
|
+
`${src}: "${name}" is outside ${width}×${height} (x ${Math.min(...xs)}–${Math.max(...xs)}, y ${Math.min(...ys)}–${Math.max(...ys)})`
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
20
78
|
function init() {
|
|
21
|
-
document.querySelectorAll("[data-jil-
|
|
79
|
+
document.querySelectorAll("img.jil-map-image[data-jil-regions]").forEach((img) => {
|
|
80
|
+
if (img.closest("[data-jil-map]")) return;
|
|
81
|
+
logError(
|
|
82
|
+
`${img.getAttribute("src") || "image"} was not enhanced (${img.getAttribute("data-jil-regions")})`
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
document.querySelectorAll("[data-jil-map]").forEach((host) => {
|
|
87
|
+
try {
|
|
88
|
+
initMapHost(host);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
logError(`Failed to init ${host.id || "image map"}`, error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
22
93
|
}
|
|
23
94
|
|
|
24
95
|
function initMapHost(host) {
|
|
25
96
|
if (host.dataset.jilReady === "true") return;
|
|
26
97
|
host.dataset.jilReady = "true";
|
|
27
98
|
|
|
28
|
-
|
|
99
|
+
let mapData;
|
|
100
|
+
try {
|
|
101
|
+
mapData = JSON.parse(host.dataset.jilMap);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logError(`Invalid map JSON on ${host.id || "image map"}`, error);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
29
107
|
const inline = host.dataset.jilInline !== "false";
|
|
30
108
|
const viewer = host.dataset.jilViewer !== "false";
|
|
31
109
|
const labels = host.dataset.jilLabels === "true";
|
|
32
110
|
const img = host.querySelector(".jil-map-image");
|
|
33
111
|
|
|
34
|
-
if (!img)
|
|
112
|
+
if (!img) {
|
|
113
|
+
logError(`${mapData.src || host.id || "image map"}: missing .jil-map-image`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
35
116
|
|
|
117
|
+
reportMapProblems(img, mapData);
|
|
36
118
|
if (labels) renderLabels(host, mapData, img);
|
|
37
119
|
if (inline) bindInlineClicks(host, mapData, img);
|
|
38
120
|
if (viewer) addViewerButton(host, mapData);
|
|
39
121
|
}
|
|
40
122
|
|
|
123
|
+
function bindOverlayGeometry(overlay, host, mapData, img) {
|
|
124
|
+
const update = () => {
|
|
125
|
+
const geom = getMapGeometry(img, mapData);
|
|
126
|
+
if (!geom || !geom.width || !geom.height) return;
|
|
127
|
+
overlay.style.left = `${geom.offsetX}px`;
|
|
128
|
+
overlay.style.top = `${geom.offsetY}px`;
|
|
129
|
+
overlay.style.width = `${geom.width}px`;
|
|
130
|
+
overlay.style.height = `${geom.height}px`;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
if (img.complete) update();
|
|
134
|
+
img.addEventListener("load", update);
|
|
135
|
+
window.addEventListener("resize", update);
|
|
136
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
137
|
+
const observer = new ResizeObserver(update);
|
|
138
|
+
observer.observe(host);
|
|
139
|
+
observer.observe(img);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
41
143
|
function renderLabels(host, mapData, img) {
|
|
42
144
|
const overlay = document.createElement("div");
|
|
43
145
|
overlay.className = "jil-label-layer";
|
|
44
146
|
host.appendChild(overlay);
|
|
147
|
+
bindOverlayGeometry(overlay, host, mapData, img);
|
|
45
148
|
|
|
46
149
|
const update = () => {
|
|
47
150
|
overlay.innerHTML = "";
|
|
48
151
|
const geom = getMapGeometry(img, mapData);
|
|
49
152
|
if (!geom || !geom.width || !geom.height) return;
|
|
50
153
|
|
|
51
|
-
overlay.style.left = `${geom.offsetX}px`;
|
|
52
|
-
overlay.style.top = `${geom.offsetY}px`;
|
|
53
|
-
overlay.style.width = `${geom.width}px`;
|
|
54
|
-
overlay.style.height = `${geom.height}px`;
|
|
55
|
-
|
|
56
154
|
mapData.regions.forEach((region) => {
|
|
57
155
|
const center = polygonCenter(region.points);
|
|
58
156
|
if (!center) return;
|
|
@@ -248,7 +346,21 @@
|
|
|
248
346
|
});
|
|
249
347
|
zoomFit.addEventListener("click", fitZoom);
|
|
250
348
|
|
|
349
|
+
let dragStart = null;
|
|
350
|
+
let didPan = false;
|
|
351
|
+
|
|
352
|
+
const cursorForPoint = (event) => {
|
|
353
|
+
const point = canvasPointFromEvent(canvas, state.zoom, event);
|
|
354
|
+
const region = firstIntersectedRegion(mapData.regions, point);
|
|
355
|
+
return region ? "pointer" : "grab";
|
|
356
|
+
};
|
|
357
|
+
|
|
251
358
|
canvas.addEventListener("click", (event) => {
|
|
359
|
+
if (didPan) {
|
|
360
|
+
didPan = false;
|
|
361
|
+
event.preventDefault();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
252
364
|
const point = canvasPointFromEvent(canvas, state.zoom, event);
|
|
253
365
|
const region = firstIntersectedRegion(mapData.regions, point);
|
|
254
366
|
if (!region) return;
|
|
@@ -257,15 +369,21 @@
|
|
|
257
369
|
});
|
|
258
370
|
|
|
259
371
|
canvas.addEventListener("mousemove", (event) => {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
canvas.style.cursor = region ? "pointer" : "grab";
|
|
372
|
+
if (dragStart) return;
|
|
373
|
+
canvas.style.cursor = cursorForPoint(event);
|
|
263
374
|
});
|
|
264
375
|
|
|
265
|
-
let dragStart = null;
|
|
266
376
|
canvas.addEventListener("mousedown", (event) => {
|
|
267
|
-
|
|
377
|
+
// Left-drag empty areas to pan; right-drag pans from anywhere.
|
|
378
|
+
if (event.button === 0) {
|
|
379
|
+
const point = canvasPointFromEvent(canvas, state.zoom, event);
|
|
380
|
+
if (firstIntersectedRegion(mapData.regions, point)) return;
|
|
381
|
+
} else if (event.button !== 2) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
268
385
|
event.preventDefault();
|
|
386
|
+
didPan = false;
|
|
269
387
|
dragStart = {
|
|
270
388
|
x: event.clientX,
|
|
271
389
|
y: event.clientY,
|
|
@@ -275,15 +393,25 @@
|
|
|
275
393
|
canvas.style.cursor = "grabbing";
|
|
276
394
|
});
|
|
277
395
|
|
|
278
|
-
window.addEventListener("mouseup", () => {
|
|
396
|
+
window.addEventListener("mouseup", (event) => {
|
|
397
|
+
if (!dragStart) return;
|
|
279
398
|
dragStart = null;
|
|
280
|
-
canvas.style.cursor =
|
|
399
|
+
canvas.style.cursor = cursorForPoint(event);
|
|
400
|
+
// Clear after the trailing click so a pan doesn't open a region.
|
|
401
|
+
if (didPan) {
|
|
402
|
+
setTimeout(() => {
|
|
403
|
+
didPan = false;
|
|
404
|
+
}, 0);
|
|
405
|
+
}
|
|
281
406
|
});
|
|
282
407
|
|
|
283
408
|
window.addEventListener("mousemove", (event) => {
|
|
284
409
|
if (!dragStart) return;
|
|
285
|
-
|
|
286
|
-
|
|
410
|
+
const dx = dragStart.x - event.clientX;
|
|
411
|
+
const dy = dragStart.y - event.clientY;
|
|
412
|
+
if (Math.abs(dx) > 2 || Math.abs(dy) > 2) didPan = true;
|
|
413
|
+
scroll.scrollLeft = dragStart.scrollLeft + dx;
|
|
414
|
+
scroll.scrollTop = dragStart.scrollTop + dy;
|
|
287
415
|
});
|
|
288
416
|
|
|
289
417
|
canvas.addEventListener("contextmenu", (event) => event.preventDefault());
|
|
@@ -299,6 +427,9 @@
|
|
|
299
427
|
state.image = image;
|
|
300
428
|
fitZoom();
|
|
301
429
|
};
|
|
430
|
+
image.onerror = () => {
|
|
431
|
+
logError(`${mapData.src || "viewer image"}: viewer image failed to load`);
|
|
432
|
+
};
|
|
302
433
|
image.src = mapData.src;
|
|
303
434
|
|
|
304
435
|
return {
|
|
@@ -324,13 +455,22 @@
|
|
|
324
455
|
const jhp = useHoverPopup() ? getHoverPopup() : null;
|
|
325
456
|
if (jhp) {
|
|
326
457
|
event.preventDefault();
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
458
|
+
try {
|
|
459
|
+
await jhp.openLink({
|
|
460
|
+
href: region.href,
|
|
461
|
+
title: region.title || region.label || region.href,
|
|
462
|
+
clientX: event.clientX,
|
|
463
|
+
clientY: event.clientY,
|
|
464
|
+
isPermanent: true,
|
|
465
|
+
});
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logError(`Failed to open ${region.href}`, error);
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (!region.href) {
|
|
473
|
+
logError("Region is missing href");
|
|
334
474
|
return;
|
|
335
475
|
}
|
|
336
476
|
|
|
@@ -448,7 +588,10 @@
|
|
|
448
588
|
}
|
|
449
589
|
|
|
450
590
|
function start() {
|
|
451
|
-
if (!document.querySelector(ROOT_SELECTOR))
|
|
591
|
+
if (!document.querySelector(ROOT_SELECTOR)) {
|
|
592
|
+
logError("image_links.js loaded without data-jil-root");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
452
595
|
|
|
453
596
|
const run = () => init();
|
|
454
597
|
|
|
@@ -17,7 +17,10 @@ module Jekyll
|
|
|
17
17
|
html = MapRenderer.enhance_html(html, site: site, page: doc, cfg: cfg)
|
|
18
18
|
doc.output = inject_assets(html, assets_path: assets_path, site: site, cfg: cfg)
|
|
19
19
|
rescue StandardError => e
|
|
20
|
-
Jekyll.logger.
|
|
20
|
+
Jekyll.logger.error(
|
|
21
|
+
"jekyll-image-links:",
|
|
22
|
+
"Failed to process #{doc.relative_path}: #{e.class}: #{e.message}"
|
|
23
|
+
)
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
end
|
|
@@ -23,7 +23,7 @@ module Jekyll
|
|
|
23
23
|
html = html.gsub(PORTABLE_FIGURE) do |figure_html|
|
|
24
24
|
enhance_portable_markup(figure_html, site: site, page: page, cfg: cfg)
|
|
25
25
|
rescue StandardError => e
|
|
26
|
-
|
|
26
|
+
log_error("#{page_label(page)} #{markup_context(figure_html)}: #{e.class}: #{e.message}")
|
|
27
27
|
figure_html
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ module Jekyll
|
|
|
32
32
|
|
|
33
33
|
enhance_portable_markup(image_html, site: site, page: page, cfg: cfg)
|
|
34
34
|
rescue StandardError => e
|
|
35
|
-
|
|
35
|
+
log_error("#{page_label(page)} #{markup_context(image_html)}: #{e.class}: #{e.message}")
|
|
36
36
|
image_html
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -170,6 +170,8 @@ module Jekyll
|
|
|
170
170
|
"title" => title,
|
|
171
171
|
"regions" => normalize_regions(regions, site: site, page: page, liquid_context: liquid_context),
|
|
172
172
|
}
|
|
173
|
+
warn_if_regions_out_of_bounds!(map_data["regions"], width, height, src)
|
|
174
|
+
warn_if_image_dimensions_differ!(site, page, attrs["src"], width, height)
|
|
173
175
|
|
|
174
176
|
render_interactive(
|
|
175
177
|
map_data,
|
|
@@ -300,6 +302,170 @@ module Jekyll
|
|
|
300
302
|
end
|
|
301
303
|
end
|
|
302
304
|
|
|
305
|
+
def warn_if_regions_out_of_bounds!(regions, width, height, src)
|
|
306
|
+
regions.each do |region|
|
|
307
|
+
points = region["points"] || []
|
|
308
|
+
next if points.empty?
|
|
309
|
+
|
|
310
|
+
xs = points.map { |point| point[0] }
|
|
311
|
+
ys = points.map { |point| point[1] }
|
|
312
|
+
outside = xs.any? { |x| x.negative? || x > width } || ys.any? { |y| y.negative? || y > height }
|
|
313
|
+
next unless outside
|
|
314
|
+
|
|
315
|
+
name = region["title"] || region["href"]
|
|
316
|
+
log_error(
|
|
317
|
+
"#{src}: region \"#{name}\" is outside #{width}×#{height} (x #{xs.min}–#{xs.max}, y #{ys.min}–#{ys.max})"
|
|
318
|
+
)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def warn_if_image_dimensions_differ!(site, page, src, width, height)
|
|
323
|
+
path = local_image_path(site, page, src)
|
|
324
|
+
if path.nil?
|
|
325
|
+
log_error("#{src}: image file not found") unless remote_url?(src)
|
|
326
|
+
return
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
actual = read_image_size(path)
|
|
330
|
+
return unless actual
|
|
331
|
+
|
|
332
|
+
actual_width, actual_height = actual
|
|
333
|
+
return if actual_width == width && actual_height == height
|
|
334
|
+
|
|
335
|
+
log_error("#{src}: image file is #{actual_width}×#{actual_height} but map data is #{width}×#{height}")
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def local_image_path(site, page, src)
|
|
339
|
+
return nil if src.to_s.strip.empty? || remote_url?(src)
|
|
340
|
+
|
|
341
|
+
path = src.to_s
|
|
342
|
+
baseurl = site.baseurl.to_s
|
|
343
|
+
baseurl = "" if baseurl == "/"
|
|
344
|
+
path = path.delete_prefix(baseurl) if !baseurl.empty? && (path == baseurl || path.start_with?("#{baseurl}/"))
|
|
345
|
+
|
|
346
|
+
candidates = [path]
|
|
347
|
+
if !path.start_with?("/") && page&.respond_to?(:relative_path) && page.relative_path
|
|
348
|
+
candidates << File.join(File.dirname(page.relative_path), path)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
candidates.each do |candidate|
|
|
352
|
+
full_path = Jekyll.sanitized_path(site.source, candidate)
|
|
353
|
+
return full_path if File.file?(full_path)
|
|
354
|
+
end
|
|
355
|
+
nil
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def remote_url?(src)
|
|
359
|
+
src.to_s.start_with?("http://", "https://", "data:")
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def read_image_size(path)
|
|
363
|
+
File.open(path, "rb") do |file|
|
|
364
|
+
header = file.read(32)
|
|
365
|
+
return nil unless header && header.bytesize >= 24
|
|
366
|
+
|
|
367
|
+
header = header.b
|
|
368
|
+
if header.start_with?("\x89PNG\r\n\x1A\n".b)
|
|
369
|
+
return [header[16, 4].unpack1("N"), header[20, 4].unpack1("N")]
|
|
370
|
+
end
|
|
371
|
+
if header.start_with?("GIF87a".b, "GIF89a".b)
|
|
372
|
+
return header[6, 4].unpack("v2")
|
|
373
|
+
end
|
|
374
|
+
if header.start_with?("RIFF".b) && header[8, 4] == "WEBP".b
|
|
375
|
+
return read_webp_size(file, header)
|
|
376
|
+
end
|
|
377
|
+
return read_jpeg_size(file) if header.start_with?("\xFF\xD8".b)
|
|
378
|
+
end
|
|
379
|
+
nil
|
|
380
|
+
rescue StandardError => e
|
|
381
|
+
log_error("#{path}: could not read image dimensions: #{e.class}: #{e.message}")
|
|
382
|
+
nil
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def read_webp_size(file, header)
|
|
386
|
+
format = header[12, 4]
|
|
387
|
+
if format == "VP8X".b
|
|
388
|
+
file.seek(24)
|
|
389
|
+
data = file.read(6)
|
|
390
|
+
return nil unless data&.bytesize == 6
|
|
391
|
+
|
|
392
|
+
bytes = data.bytes
|
|
393
|
+
width = 1 + (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16))
|
|
394
|
+
height = 1 + (bytes[3] | (bytes[4] << 8) | (bytes[5] << 16))
|
|
395
|
+
[width, height]
|
|
396
|
+
elsif format == "VP8 ".b
|
|
397
|
+
file.seek(26)
|
|
398
|
+
data = file.read(4)
|
|
399
|
+
return nil unless data&.bytesize == 4
|
|
400
|
+
|
|
401
|
+
width = data[0, 2].unpack1("v") & 0x3fff
|
|
402
|
+
height = data[2, 2].unpack1("v") & 0x3fff
|
|
403
|
+
[width, height]
|
|
404
|
+
elsif format == "VP8L".b
|
|
405
|
+
file.seek(21)
|
|
406
|
+
data = file.read(4)
|
|
407
|
+
return nil unless data&.bytesize == 4
|
|
408
|
+
|
|
409
|
+
b0, b1, b2, b3 = data.bytes
|
|
410
|
+
width = 1 + (((b1 & 0x3F) << 8) | b0)
|
|
411
|
+
height = 1 + (((b3 & 0x0F) << 10) | (b2 << 2) | ((b1 & 0xC0) >> 6))
|
|
412
|
+
[width, height]
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def read_jpeg_size(file)
|
|
417
|
+
file.seek(2)
|
|
418
|
+
loop do
|
|
419
|
+
byte = file.read(1)
|
|
420
|
+
return nil unless byte == "\xFF".b
|
|
421
|
+
|
|
422
|
+
marker = nil
|
|
423
|
+
loop do
|
|
424
|
+
byte = file.read(1)
|
|
425
|
+
return nil unless byte
|
|
426
|
+
next if byte == "\xFF".b
|
|
427
|
+
|
|
428
|
+
marker = byte.ord
|
|
429
|
+
break
|
|
430
|
+
end
|
|
431
|
+
next if marker == 0xD8 || marker == 0xD9
|
|
432
|
+
return nil if marker == 0xDA
|
|
433
|
+
|
|
434
|
+
length_bytes = file.read(2)
|
|
435
|
+
return nil unless length_bytes&.bytesize == 2
|
|
436
|
+
|
|
437
|
+
length = length_bytes.unpack1("n")
|
|
438
|
+
return nil if length < 2
|
|
439
|
+
|
|
440
|
+
if [0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF].include?(marker)
|
|
441
|
+
data = file.read(5)
|
|
442
|
+
return nil unless data&.bytesize == 5
|
|
443
|
+
|
|
444
|
+
_precision, height, width = data.unpack("Cnn")
|
|
445
|
+
return [width, height]
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
file.seek(length - 2, IO::SEEK_CUR)
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def page_label(page)
|
|
453
|
+
return page.relative_path if page.respond_to?(:relative_path) && page.relative_path
|
|
454
|
+
return page.path if page.respond_to?(:path) && page.path
|
|
455
|
+
|
|
456
|
+
"page"
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def markup_context(markup)
|
|
460
|
+
src = markup[/src="([^"]*)"/, 1]
|
|
461
|
+
regions = markup[/data-jil-regions="([^"]*)"/, 1]
|
|
462
|
+
[regions, src].compact.reject(&:empty?).join(" ")
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def log_error(message)
|
|
466
|
+
Jekyll.logger.error("jekyll-image-links:", message)
|
|
467
|
+
end
|
|
468
|
+
|
|
303
469
|
def validate_dimensions!(width, height, src)
|
|
304
470
|
unless width.positive? && height.positive?
|
|
305
471
|
raise ArgumentError, "image_map requires native width and height in the YAML file or numeric width/height attributes"
|
|
@@ -15,14 +15,22 @@ module Jekyll
|
|
|
15
15
|
cfg = (site.config["image_links"] || {})
|
|
16
16
|
regions_body = @attrs["file"] ? nil : super
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
begin
|
|
19
|
+
figure = MapRenderer.render_portable_figure(
|
|
20
|
+
@attrs,
|
|
21
|
+
site: site,
|
|
22
|
+
page: context.registers[:page],
|
|
23
|
+
liquid_context: context,
|
|
24
|
+
cfg: cfg,
|
|
25
|
+
regions_body: regions_body
|
|
26
|
+
)
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
page = context.registers[:page]
|
|
29
|
+
page_label = page.respond_to?(:relative_path) ? page.relative_path : "page"
|
|
30
|
+
src = @attrs["src"] || @attrs["file"]
|
|
31
|
+
Jekyll.logger.error("jekyll-image-links:", "#{page_label} #{src}: #{e.class}: #{e.message}")
|
|
32
|
+
raise
|
|
33
|
+
end
|
|
26
34
|
|
|
27
35
|
<<~HTML
|
|
28
36
|
{::nomarkdown}
|