mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-06-22 04:58:40 +00:00
Merge branch 'master' of https://github.com/naturalcrit/homebrewery into add-image-preview
This commit is contained in:
@@ -29,7 +29,6 @@ const TOOLBAR_STATE_KEY = 'HB_renderer_toolbarState';
|
|||||||
|
|
||||||
const INITIAL_CONTENT = dedent`
|
const INITIAL_CONTENT = dedent`
|
||||||
<!DOCTYPE html><html><head>
|
<!DOCTYPE html><html><head>
|
||||||
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
|
||||||
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
|
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
|
||||||
<link href="${brewRendererStylesUrl}" rel="stylesheet" />
|
<link href="${brewRendererStylesUrl}" rel="stylesheet" />
|
||||||
<link href="${headerNavStylesUrl}" rel="stylesheet" />
|
<link href="${headerNavStylesUrl}" rel="stylesheet" />
|
||||||
@@ -42,6 +41,7 @@ const BrewPage = (props)=>{
|
|||||||
props = {
|
props = {
|
||||||
contents : '',
|
contents : '',
|
||||||
index : 0,
|
index : 0,
|
||||||
|
hoisted : false,
|
||||||
...props
|
...props
|
||||||
};
|
};
|
||||||
const pageRef = useRef(null);
|
const pageRef = useRef(null);
|
||||||
@@ -221,7 +221,8 @@ const BrewRenderer = (props)=>{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPages = ()=>{
|
const renderPages = (checkHoists = false)=>{
|
||||||
|
|
||||||
if(props.errors && props.errors.length)
|
if(props.errors && props.errors.length)
|
||||||
return renderedPages;
|
return renderedPages;
|
||||||
|
|
||||||
@@ -233,10 +234,16 @@ const BrewRenderer = (props)=>{
|
|||||||
renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
|
renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
|
||||||
|
|
||||||
_.forEach(rawPages, (page, index)=>{
|
_.forEach(rawPages, (page, index)=>{
|
||||||
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
const varsOnPageRegex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g; // Find out if there are any vars on the page.
|
||||||
|
const forceRender = checkHoists &&
|
||||||
|
!props.hoisted &&
|
||||||
|
(page.match(varsOnPageRegex)); // forceRender forces pages outside of the PPR range to render if true.
|
||||||
|
// This is necessary on the first load to fully populate the variable table.
|
||||||
|
if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){
|
||||||
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if(!props.hoisted) { props.hoisted = true; } // Only fully hoist once.
|
||||||
return renderedPages;
|
return renderedPages;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -276,7 +283,7 @@ const BrewRenderer = (props)=>{
|
|||||||
window.addEventListener('hashchange', ()=>scrollToHash(window.location.hash));
|
window.addEventListener('hashchange', ()=>scrollToHash(window.location.hash));
|
||||||
|
|
||||||
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
|
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
|
||||||
renderPages(); //Make sure page is renderable before showing
|
renderPages(true); //Make sure page is renderable before showing
|
||||||
setState((prevState)=>({
|
setState((prevState)=>({
|
||||||
...prevState,
|
...prevState,
|
||||||
isMounted : true,
|
isMounted : true,
|
||||||
|
|||||||
@@ -1,9 +1,25 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Nav from './nav.jsx';
|
import Nav from './nav.jsx';
|
||||||
import { printCurrentBrew } from '@shared/helpers.js';
|
import { printCurrentBrew } from '@shared/helpers.js';
|
||||||
|
|
||||||
export default function(){
|
export default function(){
|
||||||
|
const [printing, setPrinting] = useState(false);
|
||||||
|
|
||||||
|
// listen for print cycle events to display "loading" message since it can take some time.
|
||||||
|
useEffect(()=>{
|
||||||
|
document.addEventListener('print:startprep', handlePrintStartPrep);
|
||||||
|
document.addEventListener('print:finishedprep', handlePrintPrepFinished);
|
||||||
|
return ()=>{
|
||||||
|
document.removeEventListener('print:startprep', handlePrintStartPrep);
|
||||||
|
document.removeEventListener('print:finishedprep', handlePrintPrepFinished);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePrintStartPrep = ()=>{ setPrinting(true); };
|
||||||
|
|
||||||
|
const handlePrintPrepFinished = ()=>{ setPrinting(false); };
|
||||||
|
|
||||||
return <Nav.item onClick={printCurrentBrew} color='purple' icon='far fa-file-pdf'>
|
return <Nav.item onClick={printCurrentBrew} color='purple' icon='far fa-file-pdf'>
|
||||||
get PDF
|
{printing ? 'loading' : 'get PDF'}
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
||||||
<link
|
|
||||||
href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700"
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css" />
|
|
||||||
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
|
|||||||
Generated
+318
-302
File diff suppressed because it is too large
Load Diff
+23
-2
@@ -105,14 +105,35 @@ const splitTextStyleAndMetadata = (brew)=>{
|
|||||||
if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : [];
|
if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const printCurrentBrew = ()=>{
|
const printCurrentBrew = async ()=>{
|
||||||
if(window.typeof !== 'undefined') {
|
if(window.typeof !== 'undefined') {
|
||||||
|
// fire a custom event for the print cycle
|
||||||
|
document.dispatchEvent(new CustomEvent('print:startprep'));
|
||||||
|
try {
|
||||||
|
const iframeDoc = window.frames['BrewRenderer'].contentDocument;
|
||||||
|
|
||||||
|
// get all img elements with lazy loading (currently only elements generated through MarkedJS)
|
||||||
|
const lazyImages = [...iframeDoc.querySelectorAll('img[loading="lazy"]')];
|
||||||
|
lazyImages.forEach((img)=>{ img.loading = 'eager'; });
|
||||||
|
|
||||||
|
// waits for images to load before resolving promise and opening print dialog
|
||||||
|
await Promise.all(
|
||||||
|
lazyImages
|
||||||
|
.filter((img)=>!img.complete)
|
||||||
|
.map((img)=>new Promise((resolve)=>{ img.onload = resolve; img.onerror = resolve; }))
|
||||||
|
);
|
||||||
|
|
||||||
window.frames['BrewRenderer'].contentWindow.print();
|
window.frames['BrewRenderer'].contentWindow.print();
|
||||||
|
|
||||||
//Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear
|
//Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear
|
||||||
const node = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0);
|
const node = iframeDoc.getElementsByClassName('brewRenderer').item(0);
|
||||||
node.style.display='none';
|
node.style.display='none';
|
||||||
node.offsetHeight; // accessing this is enough to trigger a reflow
|
node.offsetHeight; // accessing this is enough to trigger a reflow
|
||||||
node.style.display='';
|
node.style.display='';
|
||||||
|
} finally {
|
||||||
|
// when lazy load images have all been loaded, and the doc re-rendered for print preview, emit 'finished' event.
|
||||||
|
document.dispatchEvent(new CustomEvent('print:finishedprep'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ renderer.image = function (token) {
|
|||||||
if(href === null)
|
if(href === null)
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
let out = `<img src="${href}" alt="${text}" style="--HB_src:url(${href});"`;
|
let out = `<img loading="lazy" src="${href}" alt="${text}" style="--HB_src:url(${href});"`;
|
||||||
if(title)
|
if(title)
|
||||||
out += ` title="${title}"`;
|
out += ` title="${title}"`;
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,7 @@
|
|||||||
@import './animations.less';
|
@import './animations.less';
|
||||||
@import './colors.less';
|
@import './colors.less';
|
||||||
@import './tooltip.less';
|
@import './tooltip.less';
|
||||||
@font-face {
|
@import './fonts/fonts.css';
|
||||||
font-family : 'CodeLight';
|
|
||||||
src : url('./CODE Light.otf') format('opentype');
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family : 'CodeBold';
|
|
||||||
src : url('./CODE Bold.otf') format('opentype');
|
|
||||||
}
|
|
||||||
html,body, #reactRoot {
|
html,body, #reactRoot {
|
||||||
height : 100vh;
|
height : 100vh;
|
||||||
min-height : 100vh;
|
min-height : 100vh;
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/* open-sans-latin-wght-normal */
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family : 'Open Sans';
|
||||||
|
font-style : normal;
|
||||||
|
font-weight : normal;
|
||||||
|
src : url('open-sans-latin-400-normal.woff2') format('woff2');
|
||||||
|
font-display : swap;
|
||||||
|
unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nowhere is font-weight: 700 actually used with Open Sans, everything is set to 800.
|
||||||
|
* But, 800 *is* too bold. And since we don't have an 800 font file, it's just using the
|
||||||
|
* 700 font file and it looks fine. Not sure it's worth changing everything to 700?
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family : 'Open Sans';
|
||||||
|
font-style : normal;
|
||||||
|
font-weight : bold;
|
||||||
|
src : url('open-sans-latin-700-normal.woff2') format('woff2');
|
||||||
|
font-display : swap;
|
||||||
|
unicode-range : U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family : 'CodeLight';
|
||||||
|
font-style : normal;
|
||||||
|
src : url('./CODE Light.otf') format('opentype');
|
||||||
|
font-display : block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family : 'CodeBold';
|
||||||
|
font-style : normal;
|
||||||
|
src : url('./CODE Bold.otf') format('opentype');
|
||||||
|
font-display : block;
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -324,7 +324,7 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
it('Renders an image element with injected style', function() {
|
it('Renders an image element with injected style', function() {
|
||||||
const source = '{position:absolute}';
|
const source = '{position:absolute}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute;" src="https://i.imgur.com/hMna6G0.png" alt="alt text"></p>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute;" loading="lazy" src="https://i.imgur.com/hMna6G0.png" alt="alt text"></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders an element modified by only the first of two consecutive injections', function() {
|
it('Renders an element modified by only the first of two consecutive injections', function() {
|
||||||
@@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{
|
|||||||
it('Renders an image with added attributes', function() {
|
it('Renders an image with added attributes', function() {
|
||||||
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute; bottom:20px; left:130px; width:220px;" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e"></p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute; bottom:20px; left:130px; width:220px;" loading="lazy" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e"></p>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders an image with "=" in the url, and added attributes', function() {
|
it('Renders an image with "=" in the url, and added attributes', function() {
|
||||||
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png?auth=12345&height=1024); position:absolute; bottom:20px; left:130px; width:220px;" src="https://i.imgur.com/hMna6G0.png?auth=12345&height=1024" alt="homebrew mug" a="b and c" d="e"></p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png?auth=12345&height=1024); position:absolute; bottom:20px; left:130px; width:220px;" loading="lazy" src="https://i.imgur.com/hMna6G0.png?auth=12345&height=1024" alt="homebrew mug" a="b and c" d="e"></p>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders an image and added attributes with "=" in the value, ', function() {
|
it('Renders an image and added attributes with "=" in the value, ', function() {
|
||||||
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
|
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute; bottom:20px; left:130px; width:220px;" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e" otherUrl="url?auth=12345"></p>`);
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p><img style="--HB_src:url(https://i.imgur.com/hMna6G0.png); position:absolute; bottom:20px; left:130px; width:220px;" loading="lazy" src="https://i.imgur.com/hMna6G0.png" alt="homebrew mug" a="b and c" d="e" otherUrl="url?auth=12345"></p>`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{
|
|||||||
const source = ``;
|
const source = ``;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
<p><img src="url" alt="alt text" style="--HB_src:url(url);"></p>`.trimReturns());
|
<p><img loading="lazy" src="url" alt="alt text" style="--HB_src:url(url);"></p>`.trimReturns());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders normal images with a title', function() {
|
it('Renders normal images with a title', function() {
|
||||||
const source = 'An image !';
|
const source = 'An image !';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
<p>An image <img src="url" alt="alt text" style="--HB_src:url(url);" title="and title">!</p>`.trimReturns());
|
<p>An image <img loading="lazy" src="url" alt="alt text" style="--HB_src:url(url);" title="and title">!</p>`.trimReturns());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Applies curly injectors to images', function() {
|
it('Applies curly injectors to images', function() {
|
||||||
const source = `{width:100px}`;
|
const source = `{width:100px}`;
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
|
||||||
<p><img style="--HB_src:url(url); width:100px;" src="url" alt="alt text"></p>`.trimReturns());
|
<p><img style="--HB_src:url(url); width:100px;" loading="lazy" src="url" alt="alt text"></p>`.trimReturns());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders normal links', function() {
|
it('Renders normal links', function() {
|
||||||
@@ -438,25 +438,25 @@ describe('Regression Tests', ()=>{
|
|||||||
it('Handle Extra spaces in image alt-text 1', function(){
|
it('Handle Extra spaces in image alt-text 1', function(){
|
||||||
const source='';
|
const source='';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered).toBe('<p><img src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
expect(rendered).toBe('<p><img loading="lazy" src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Handle Extra spaces in image alt-text 2', function(){
|
it('Handle Extra spaces in image alt-text 2', function(){
|
||||||
const source='';
|
const source='';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered).toBe('<p><img src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
expect(rendered).toBe('<p><img loading="lazy" src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Handle Extra spaces in image alt-text 3', function(){
|
it('Handle Extra spaces in image alt-text 3', function(){
|
||||||
const source='';
|
const source='';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered).toBe('<p><img src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
expect(rendered).toBe('<p><img loading="lazy" src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\"></p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Handle Extra spaces in image alt-text 4', function(){
|
it('Handle Extra spaces in image alt-text 4', function(){
|
||||||
const source='{height=20%,width=20%}';
|
const source='{height=20%,width=20%}';
|
||||||
const rendered = Markdown.render(source).trimReturns();
|
const rendered = Markdown.render(source).trimReturns();
|
||||||
expect(rendered).toBe('<p><img style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\" src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" height=\"20%\" width=\"20%\"></p>');
|
expect(rendered).toBe('<p><img style=\"--HB_src:url(http://i.imgur.com/hMna6G0.png);\" loading="lazy" src=\"http://i.imgur.com/hMna6G0.png\" alt=\"where is my image??\" height=\"20%\" width=\"20%\"></p>');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user