import { EDITOR_CONTENT_MAX_LENGTH } from "../common/constants/settings/notice";
export default class HtmlConverter {
/**
* editor사용 시 서버로 전송할 때 style tag 추가하거나 변경함(모바일에서도 동일하게 보여지기 위해 추가).
*
* '
' -> '
'
* '
' -> ''
* '' -> ''
*
* @public
* @static
* @return string
* @param content
*/
static additionTagStyle(content: string) {
let tagStyleWithContent = content;
while (tagStyleWithContent.includes("
')[0];
tagStyleWithContent = tagStyleWithContent.replaceAll(
`class="ql-align-${align}"`,
`style="text-align:-webkit-${align}; white-space:pre-wrap;"`
);
}
while (tagStyleWithContent.includes("ql-indent-")) {
const indentSize = Number(
tagStyleWithContent.split("ql-indent-")[1].split('">')[0]
);
tagStyleWithContent = tagStyleWithContent.replaceAll(
`class="ql-indent-${indentSize}"`,
`style="text-indent:${indentSize * 3}em; white-space:pre-wrap;"`
);
}
return tagStyleWithContent;
}
/**
* 서버에서 html tag를 받아와서 보여줄 때 사용(모바일에 보여주기 위해 추가했던 로직 변경)하며 web에서 보여지기 위한 css들이 추가된다.
*
* '' -> ''
* "hello world
" -> "hello world
"
*
* @public
* @return string
* @param content
*/
static displayHTML(content: string) {
let editorContent = content;
if (
(!editorContent.includes("text-align") &&
!editorContent.includes("text-indent")) ||
editorContent.includes("font-family: nanum-regular, sans-serif;")
) {
return editorContent;
}
while (editorContent.includes("text-align")) {
const alignAttribute = editorContent.split("text-align:-webkit-")[1];
const align = alignAttribute.split("; white-space:pre-wrap;")[0];
editorContent = editorContent.replaceAll(
`style="text-align:-webkit-${align}; white-space:pre-wrap;"`,
`class="ql-align-${align}"`
);
}
while (editorContent.includes("text-indent")) {
const indentSize = Number(
editorContent.split("text-indent:")[1].split("em")[0]
);
editorContent = editorContent.replaceAll(
`style="text-indent:${indentSize}em; white-space:pre-wrap;"`,
`class="ql-indent-${indentSize / 3}"`
);
}
return editorContent;
}
/**
* content에 img태그에 src가 base64로 되어있으면 크기를 재서 return한다.
*
* '
' -> {base64: ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mN8AAAAASUVORK5CYII=",]}
*
* @public
* @return string
* @param htmlString
*/
static findImageTagsFromHTML(htmlString: string) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, "text/html");
const imageTags = doc.getElementsByTagName("img");
const imageSrcList: {
base64: string[];
} = { base64: [] };
for (let i = 0; i < imageTags.length; i++) {
if (imageTags[i].src.includes("data:image")) {
imageSrcList.base64.push(imageTags[i].src);
}
}
return imageSrcList;
}
/**
* editor content 길이를 재서 return한다.
*
* '
' -> 99
*
* @public
* @return string
* @param value
* @param imageList
*/
static getEditorContentLength(
value: string,
imageList?: { url: string; size: number }[]
) {
let imageSize = 0;
let textSize = 0;
if (value.includes("
]+>/g, "");
textSize += new Blob([plainText]).size;
if (textSize + imageSize > EDITOR_CONTENT_MAX_LENGTH) {
return;
}
return textSize + imageSize;
}
/**
* img src에 들어있는 base64 string을 blob으로 바꿔 size를 return한다.
*
* "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mN8AAAAASUVORK5CYII=" -> 99
*
* @static
* @public
* @return string
* @param base64String
*/
static getImageSizeFromBase64(base64String: string) {
const byteNumbers = new Array(base64String.length);
for (let i = 0; i < base64String.length; i++) {
byteNumbers[i] = base64String.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: "image/jpg" });
return blob.size;
}
/**
* 웹에서 img를 제외한 다른 태그들을 복사할 경우 컨텐츠 뒤에 붙여 return한다.
*
* @static
* @public
* @return string
* @param event
* @param content
* @param beforeContent
*/
static protectWebImagePaste(
event: ClipboardEvent,
content: string,
beforeContent: string
) {
const clipboardData: any = event.clipboardData;
let newContent;
const pastedHTML = clipboardData
.getData("text/html")
.replace(//g, "");
const plainText = clipboardData.getData("text/plain");
if (pastedHTML && pastedHTML.includes("![]()