UNPKG

hexo-lazyload-element

Version:

Lazyload plugin for elements like img, video and iframe.

293 lines (269 loc) 9.6 kB
"use strict"; function getElemAttributes(elemText) { // Regex to pick out start tag from start of element's HTML. var re_start_tag = /^<\w+\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[\w\-.:]+))?)*\s*\/?>/; var start_tag = elemText.match(re_start_tag); start_tag = start_tag ? start_tag[0] : ""; // Regex to pick out attribute name and (optional) value from start tag. var re_attribs = /\s+([\w\-.:]+)(\s*=\s*(?:"([^"]*)"|'([^']*)'|([\w\-.:]+)))?/g; var attribs = {}; // Store attribute name=value pairs in object. var match = re_attribs.exec(start_tag); while (match != null) { var attrib = match[1]; // Attribute name in $1. var value = match[1]; // Assume no value specified. if (match[2]) { // If match[2] is set, then attribute has a value. value = match[3] ? match[3] // Attribute value is in $3, $4 or $5. : match[4] ? match[4] : match[5]; } attribs[attrib] = value; match = re_attribs.exec(start_tag); } return attribs; } function formatAttributes(str) { let res = {}; let attributesObj = getElemAttributes(str); const styleAttr = attributesObj.style || ""; const styleStringToObject = (input) => { var result = {}, attributes = input.split(";"); for (var i = 0; i < attributes.length; i++) { var entry = attributes[i].split(":"); result[entry.splice(0, 1)[0].trim()] = entry.join(":").trim(); } return result; }; const getAspectRatio = (returnStr) => { let aspectRatio = ""; let percentage = ""; if (styleAttr) { const styleObj = styleStringToObject( styleAttr.replace(/style="(.*?)"/gi, "$1") ); if (styleObj["aspect-ratio"]) { aspectRatio = styleObj["aspect-ratio"]; } } const aspectRatioRes = /aspect-ratio=(\S+)=aspect-ratio/gi.exec( attributesObj.alt ); if (aspectRatioRes && aspectRatioRes[1]) { aspectRatio = aspectRatioRes[1]; } if (returnStr) { return aspectRatio; } if (aspectRatio) { if (/\//.test(aspectRatio)) { percentage = ( (Number(aspectRatio.split("/")[1]) / Number(aspectRatio.split("/")[0])) * 100 ).toFixed(5) + "%"; } else { percentage = 100 / Number(aspectRatio) + "%"; } } return percentage; }; const getPlaceholderImage = (returnStr) => { let placeholderImage = attributesObj["data-placeholderimg"] || ""; var placeholderImageRes = /placeholder=(\S+)=placeholder/gi.exec( attributesObj.alt ); if (placeholderImageRes && placeholderImageRes[1]) { placeholderImage = placeholderImageRes[1]; } const isBlurHash = placeholderImage.startsWith("blurhash:"); if (returnStr) { return isBlurHash ? "" : placeholderImage; } return isBlurHash ? "" : /http|data:image/.test(placeholderImage) ? `url(${placeholderImage})` : placeholderImage; }; const getAltText = () => { let alt = (attributesObj.alt || "").trim().replace(/^alt$/, ""); if (alt) { alt = alt .replace(/\$placeholder=(\S+)=placeholder/gi, "") .replace(/\$aspect-ratio=(\S+)=aspect-ratio/gi, "") .replace(/(\$)?no-lazy/, ""); } return alt.trim(); }; res.alt = getAltText(); res.paddingBottom = getAspectRatio(); res.aspectRatioStr = getAspectRatio(true); res.placeholderImage = getPlaceholderImage(); res.placeholderImageStr = getPlaceholderImage(true); res.style = styleAttr; return res; } const getformatedStr = (str, attrs, placeholerStyle = '') => { let formatedStr = str; let noScriptElementStr = str .replace(/\$placeholder=(\S+)=placeholder/gi, "") .replace(/\$aspect-ratio=(\S+)=aspect-ratio/gi, "") .replace(/(\$)?no-lazy/, ""); const { placeholderImageStr, aspectRatioStr, style } = attrs; if (placeholderImageStr) { if (/data-placeholderimg/.test(formatedStr)) { formatedStr = formatedStr.replace( `data-placeholderimg="${placeholderImageStr}"`, "" ); noScriptElementStr = noScriptElementStr.replace( `data-placeholderimg="${placeholderImageStr}"`, "" ); } if (/\$placeholder=/.test(formatedStr)) { formatedStr = formatedStr.replace( `$placeholder=${placeholderImageStr}=placeholder`, "" ); noScriptElementStr = noScriptElementStr.replace( `$placeholder=${placeholderImageStr}=placeholder`, "" ); } } if (aspectRatioStr) { if (/\$aspect-ratio=/.test(formatedStr)) { formatedStr = formatedStr.replace( `$aspect-ratio=${aspectRatioStr}=aspect-ratio`, "" ); } } if (style) { formatedStr = formatedStr.replace(`style="${style}"`, /<video|<iframe/.test(str)?placeholerStyle: ''); } return { formatedStr, noScriptElementStr }; }; function lazyProcess(htmlContent) { const theme = this.config.theme; const showAltText = this.config.lazyload.showAltText === undefined ? true : !!this.config.lazyload.showAltText; return htmlContent .replace( /<img(.*?)src="(.*?)"(.*?)(>|<\/img>)/gi, function (str, p1, src, attrStr) { if (/no-lazy/gi.test(str)) { return str.replace(/\$?no-lazy/, ""); } const formatedAttributes = formatAttributes(str); const wrapStyle = formatedAttributes.style; let placeholerStyle = ""; if ( formatedAttributes.paddingBottom || formatedAttributes.placeholderImage ) { placeholerStyle = `style="${ formatedAttributes.paddingBottom ? `padding-bottom: ${formatedAttributes.paddingBottom};` : "" }${ formatedAttributes.placeholderImage ? `background-image: ${formatedAttributes.placeholderImage}` : "" }"`; } const { formatedStr, noScriptElementStr } = getformatedStr( str, formatedAttributes ); return `<span class="lazyload-outer-wrap" style="width: 100%;"><a class="gallery-item" href="${src}" target="_blank" rel="noopener" ${ theme === "landscape" ? 'data-fancybox="gallery"' : "" }><noscript>${noScriptElementStr}</noscript><span class="lazyload-wrap" data-content="${encodeURIComponent( formatedStr )}" ${ wrapStyle ? `style="${wrapStyle}"` : "" }><span class="placeholder" ${placeholerStyle}></span></span></a>${ showAltText && formatedAttributes.alt ? `<span class="caption">${formatedAttributes.alt}</span>` : `` }</span>`; } ) .replace(/<iframe(.*?)src="(.*?)"(.*?)<\/iframe>/gi, function (str) { if (/no-lazy/gi.test(str)) { return str; } const formatedAttributes = formatAttributes(str); const wrapStyle = formatedAttributes.style; let placeholerStyle = ""; if ( formatedAttributes.paddingBottom || formatedAttributes.placeholderImage ) { placeholerStyle = `style="${ formatedAttributes.paddingBottom ? `padding-bottom: ${formatedAttributes.paddingBottom};` : "" }${ formatedAttributes.placeholderImage ? `background-image: ${formatedAttributes.placeholderImage}` : "" }"`; } const { formatedStr } = getformatedStr( str, formatedAttributes, formatedAttributes.placeholderImage ? `style="background-image: ${formatedAttributes.placeholderImage}; background-size: 100% 100%;"` : '' ); return `<span class="lazyload-outer-wrap" style="width: 100%;"><span class="lazyload-wrap" data-content="${encodeURIComponent( formatedStr )}" ${wrapStyle ? `style="${wrapStyle}"` : ""}><span class="placeholder" ${placeholerStyle}></span></span><noscript>[This iframe content needs to be loaded by JavaScript.]</noscript></span>`; }) .replace(/<video([\S\s]*?)(<\/video>|\/>)/gi, function (str) { if (/no-lazy/gi.test(str)) { return str; } const formatedAttributes = formatAttributes(str); const wrapStyle = formatedAttributes.style; let placeholerStyle = ""; if ( formatedAttributes.paddingBottom || formatedAttributes.placeholderImage ) { placeholerStyle = `style="${ formatedAttributes.paddingBottom ? `padding-bottom: ${formatedAttributes.paddingBottom};` : "" }${ formatedAttributes.placeholderImage ? `background-image: ${formatedAttributes.placeholderImage}` : "" }"`; } const { formatedStr, noScriptElementStr } = getformatedStr( str, formatedAttributes, formatedAttributes.placeholderImage ? `style="background-image: ${formatedAttributes.placeholderImage}; background-size: 100% 100%;"` : '' ); return `<span class="lazyload-outer-wrap" style="width: 100%;"><span class="lazyload-wrap" data-content="${encodeURIComponent( formatedStr )}" ${wrapStyle ? `style="${wrapStyle}"` : ""}><span class="placeholder" ${placeholerStyle}></span></span><noscript>${noScriptElementStr}</noscript></span>`; }); } module.exports.processPost = function (data) { data.content = lazyProcess.call(this, data.content); return data; }; module.exports.processAll = function (data) { data = lazyProcess.call(this, data); return data; };