1 | 'use strict';
|
2 |
|
3 | const basicHTML = require('basichtml');
|
4 | const deepmerge = require('deepmerge');
|
5 | const clonedeep = require('lodash.clonedeep');
|
6 | const overwriteMerge = (destinationArray, sourceArray, options) => sourceArray;
|
7 |
|
8 | const defaultSettings = {
|
9 | selector: ':not(picture) img[src]:not([srcset]):not([src$=".svg"])',
|
10 | resizedImageUrl: (src, width) =>
|
11 | src.replace(/^(.*)(\.[^\.]+)$/, '$1-' + width + '$2'),
|
12 | runBefore: (image) => image,
|
13 | runAfter: (image) => image,
|
14 | fallbackWidth: 640,
|
15 | minWidth: 320,
|
16 | maxWidth: 2560,
|
17 | steps: 5,
|
18 | sizes: '100vw',
|
19 | classes: [],
|
20 | attributes: {},
|
21 | };
|
22 |
|
23 | const imagesResponsiver = (html, options = {}) => {
|
24 |
|
25 | let globalSettings = defaultSettings;
|
26 |
|
27 |
|
28 | if (options.default !== undefined) {
|
29 | globalSettings = deepmerge(globalSettings, options.default, {
|
30 | arrayMerge: overwriteMerge,
|
31 | });
|
32 | }
|
33 |
|
34 | const { document } = basicHTML.init({
|
35 | selector: {
|
36 |
|
37 |
|
38 | name: 'sizzle',
|
39 |
|
40 | $(Sizzle, element, css) {
|
41 | return Sizzle(css, element);
|
42 | },
|
43 | },
|
44 | });
|
45 |
|
46 | document.documentElement.innerHTML = html;
|
47 |
|
48 | [...document.querySelectorAll(globalSettings.selector)]
|
49 | .filter((image) => {
|
50 |
|
51 | return (
|
52 | image.getAttribute('src') !== null &&
|
53 | !image.getAttribute('src').match(/\.svg$/) &&
|
54 | image.getAttribute('srcset') === null
|
55 | );
|
56 | })
|
57 | .forEach((image) => {
|
58 | let imageSettings = clonedeep(globalSettings);
|
59 |
|
60 | imageSettings.runBefore(image, document);
|
61 |
|
62 |
|
63 | if ('responsiver' in image.dataset) {
|
64 |
|
65 | image.dataset.responsiver.split(' ').forEach((preset) => {
|
66 | if (options[preset] !== undefined) {
|
67 | let presetClasses = options[preset].classes || [];
|
68 | let existingClasses = imageSettings.classes;
|
69 | imageSettings = deepmerge(imageSettings, options[preset], {
|
70 | arrayMerge: overwriteMerge,
|
71 | });
|
72 | imageSettings.classes = [...existingClasses, ...presetClasses];
|
73 | }
|
74 | });
|
75 | delete image.dataset.responsiver;
|
76 | }
|
77 |
|
78 | const imageSrc = image.getAttribute('src');
|
79 |
|
80 | let imageWidth = image.getAttribute('width');
|
81 | if (imageWidth !== null) {
|
82 | imageSettings.minWidth = Math.min(imageSettings.minWidth, imageWidth);
|
83 | imageSettings.maxWidth = Math.min(imageSettings.maxWidth, imageWidth);
|
84 | imageSettings.fallbackWidth = Math.min(
|
85 | imageSettings.fallbackWidth,
|
86 | imageWidth
|
87 | );
|
88 | }
|
89 |
|
90 | if (imageSettings.classes.length > 0) {
|
91 | image.classList.add(...imageSettings.classes);
|
92 | }
|
93 |
|
94 |
|
95 | image.setAttribute(
|
96 | 'src',
|
97 | imageSettings.resizedImageUrl(imageSrc, imageSettings.fallbackWidth)
|
98 | );
|
99 |
|
100 |
|
101 | let srcset = [];
|
102 | for (let i = 0; i < imageSettings.steps; i++) {
|
103 | let width = Math.ceil(
|
104 | imageSettings.minWidth +
|
105 | ((imageSettings.maxWidth - imageSettings.minWidth) /
|
106 | (imageSettings.steps - 1)) *
|
107 | i
|
108 | );
|
109 | srcset.push(
|
110 | `${imageSettings.resizedImageUrl(imageSrc, width)} ${width}w`
|
111 | );
|
112 | }
|
113 | image.setAttribute('srcset', srcset.join(', '));
|
114 |
|
115 |
|
116 | image.setAttribute('sizes', imageSettings.sizes);
|
117 |
|
118 |
|
119 | image.dataset.pristine = imageSrc;
|
120 |
|
121 |
|
122 | if (Object.keys(imageSettings.attributes).length > 0) {
|
123 | for (const attribute in imageSettings.attributes) {
|
124 | if (imageSettings.attributes[attribute] !== null) {
|
125 | image.setAttribute(attribute, imageSettings.attributes[attribute]);
|
126 | }
|
127 | }
|
128 | }
|
129 |
|
130 | imageSettings.runAfter(image, document);
|
131 | });
|
132 |
|
133 | return document.toString();
|
134 | };
|
135 |
|
136 | module.exports = imagesResponsiver;
|