, options?: EditorJsOptions) {
this.jsonContent = jsonContent;
if(options && typeof options == 'object') {
if(options.withDefaultStyle) {
this.options.withDefaultStyle = options.withDefaultStyle;
}
if(options.codeTheme) {
this.options.codeTheme = options.codeTheme;
}
}
this.parser();
}
/**
* Parser of json list items in editorJs content.
*
*/
parser() {
let result: string = "";
this.jsonContent.map((jsonItem, index) => {
result+= `
`;
switch(jsonItem.type) {
case "paragraph": result+= this.parseParagraph(jsonItem); break;
case "text": result+= this.parseParagraph(jsonItem); break;
case "header": result+= this.parseHeader(jsonItem); break;
case "table": result+= this.parseTable(jsonItem); break;
case "image": result+= this.parseImage(jsonItem); break;
case "quote": result+= this.parseQuote(jsonItem); break;
case "list": result+= this.parseList(jsonItem); break;
case "link": result+= this.parseLink(jsonItem); break;
case "delimiter": result+= this.parseDelimiter(jsonItem); break;
case "checklist": result+= this.parseChecklist(jsonItem); break;
case "warning": result+= this.parseWarning(jsonItem); break;
case "code": result+= this.parseCode(jsonItem); break;
case "embed": result+= this.parseEmbed(jsonItem); break;
case "personality": result+= this.parsePersonality(jsonItem); break;
case "attaches": result+= this.parseAttaches(jsonItem); break;
}
result+= '
';
});
result+= '
';
result = result.replace(/(\r\n|\n|\r|\t)/gm, "");
this.html = result;
this.calculateReadingTime();
}
/**
* Render the html content in specific element
*
* @param selector
*/
render(selector: string): void {
const element: Element|null = document.querySelector(selector);
if(element != null) {
if(this.html) {
element.innerHTML = this.html;
HtmlViewer.applyHandlers();
}
else {
console.error('The html content is empty !');
}
}
else {
console.error('The element is not found !');
}
}
/**
* Parse paragraph item type to html.
*
* @param jsonItem
*/
parseParagraph(jsonItem: ParagraphElement): string {
const data = jsonItem.data;
if(data.text.length == 0) return '
';
this.addPlainText(data.text);
return `${data.text}
`;
}
/**
* Parse header item type to html.
*
* @param jsonItem
*/
parseHeader(jsonItem: HeaderElement): string {
const data = jsonItem.data;
const level: String = (data.level)? data.level : "1";
this.addPlainText(data.text);
const headerClass = (this.options.withDefaultStyle)? `class="h${level}"` : '';
return `${data.text}`;
}
/**
* Parse table item type with it's rows and columns to html.
*
* @param jsonItem
*/
parseTable(jsonItem: TableElement): string {
const data = jsonItem.data;
let rows = data.content;
let table: string = '';
if(data.withHeadings) {
let firstRow = data.content[0];
let heading = '';
firstRow.map((col, index) => {
heading+= `| ${col} | `;
this.addPlainText(col);
});
heading+= '
';
table+= heading;
rows = rows.slice(1, rows.length);
}
table+= '';
rows.map((row) => {
table+= '';
row.map((col) => {
table+= `| ${col} | `;
this.addPlainText(col);
});
table+= '
';
});
table+= '';
table+= '
';
return table;
}
/**
* Parse image item type with it's styles and caption to html.
*
* @param jsonItem
*/
parseImage(jsonItem: ImageElement): string {
const data = jsonItem.data;
let imageLayout = ``+
'
';
imageLayout+= `

`;
if(data.caption) {
imageLayout+= `
${data.caption}
`;
}
imageLayout+= "
";
return imageLayout;
}
/**
* Parse image item type with it's styles and caption to html.
*
* @param jsonItem
*/
parseQuote(jsonItem: QuoteElement): string {
const data = jsonItem.data;
let alignment = data.alignment || 'left';
const beginIcon = ``;
const endIcon = ``;
let quote = `
${beginIcon}
${data.text}
${endIcon}
${data.caption}
`;
this.addPlainText(`${data.text} ${data.caption}`);
return quote;
}
/**
* Parse list element to html.
*
* @param jsonItem
*/
parseList(jsonItem: ListElement): string {
const data = jsonItem.data;
const listClass = (this.options.withDefaultStyle)? 'class="list"' : '';
let beginList = `${data.style == 'ordered'? `` : ``}`;
let endList = `${data.style == 'ordered'? '
' : ''}`;
let list = beginList;
let plainText = "";
function renderListItem(item: string|NestedListItem): string {
let listItem = '';
if(typeof(item) == 'string') {
listItem+= `${item}`;
plainText+= " " + item;
}
else if('content' in item) {
listItem+= `${item.content}`;
if(item.items && item.items.length > 0) {
listItem+= `${beginList}`;
item.items.map((nestedItem: NestedListItem) => {
listItem+= renderListItem(nestedItem);
});
listItem+= `${endList}`;
}
listItem+= ``;
plainText+= " " + item.content;
}
return listItem;
}
data.items.map((item: string|NestedListItem) => {
list+= renderListItem(item);
});
list+= endList;
this.addPlainText(plainText);
return list;
}
/**
* Parse preview link element to html.
*
* @param jsonItem
*/
parseLink(jsonItem: LinkElement): string {
const data = jsonItem.data;
let linkLayoutStyle = "display: flex;"+
"justify-content: space-between;"+
"align-items: center;"+
"background: white;"+
"padding: 11px;"+
"border: 1px solid #f5f5f5;"+
"border-radius: 8px;"+
"box-shadow: 1px 1px 2px #e5e5e5;"+
"text-decoration: none;"+
"color: black;";
let linkLayout = ``;
linkLayout+= '';
linkLayout+= `
${data.meta.title}
`;
linkLayout+= (data.meta.description)? `
${data.meta.description}
` : '';
linkLayout+= `
${data.link}`;
linkLayout+= '
';
if(data.meta.image.url) {
linkLayout+= '';
linkLayout+= `

`;
linkLayout+= '
';
}
linkLayout+= '';
this.addPlainText(`${data.meta.title} ${data.meta.description || ''}`);
return linkLayout;
}
/**
* get delimiter html content.
*
*/
parseDelimiter(jsonItem: EditorJsElement): string {
return "* * *
";
}
/**
* Parse check list items with checked property.
*
*/
parseChecklist(jsonItem: CheckListElement): string {
const data = jsonItem.data;
let plainText = "";
let checkList = '';
data.items.forEach((item) => {
checkList+= '
';
checkList+= `
${
(item.checked == true)?
'' :
''
}
`;
checkList+= `
${item.text}
`;
checkList+= '
';
plainText+= ` ${item.text}`;
});
checkList+= '
';
this.addPlainText(plainText);
return checkList;
}
/**
* Parse warning item.
*
*/
parseWarning(jsonItem: WarningElement): string {
const data = jsonItem.data;
let warning = '';
warning+= `
👉 ${data.title}
`;
warning+= `
${data.message}
`;
warning+= '
'
this.addPlainText(`${data.title} ${data.message}`);
return warning;
}
/**
* Parse code element to html.
*
* @param jsonItem
*/
parseCode(jsonItem: CodeElement): string {
const data = jsonItem.data;
let code = ``;
let copyBtn = '
';
code+= copyBtn;
const codeContent = data.code.replace(/\n/gi, '
');
code+= ``;
code+= `
${codeContent}
`;
code+= '
';
this.addPlainText(data.code);
return code;
}
/**
* Parse embed link to html.
*
* @param jsonItem
*/
parseEmbed(jsonItem: EmbedElement): string {
const data = jsonItem.data;
let embedLayout = '';
embedLayout+= `
`;
if(data.caption) {
embedLayout+= `
${data.caption}
`;
this.addPlainText(data.caption);
}
embedLayout+= '
';
return embedLayout;
}
/**
* Parse personality card to html.
*
* @param jsonItem
*/
parsePersonality(jsonItem: PersonalityElement): string {
const data = jsonItem.data;
let personality = ``;
personality+= `
${data.name}
${(data.description)? `
${data.description}
`:''}
${(data.link)? `
${data.link}`:''}
`;
if(data.photo) {
personality+= `

`
}
personality+= '
';
this.addPlainText(`${data.name} ${data.description}`);
return personality;
}
/**
* Parse attchments to html
*
* @param jsonItem
*/
parseAttaches(jsonItem: AttachesElement): string {
const data = jsonItem.data;
let attachment = ``+
`${data.file.extension}`+
``+
`
${data.file.name}
`+
`${(data.file.size)? `
${data.file.size} MiB` : ''}`+
`
`+
'Show'+
``;
return attachment;
}
/**
* Apply some handlers to let features work correctly.
*
*/
static applyHandlers() {
if(typeof document == 'undefined') {
throw new Error('document is not defined, you can\'t call the applyHandlers function from server side.');
}
this.registerCopyHandler();
this.registerScaleImageHandler();
}
/**
* Register copy handler, to copy code text in code feature.
*
*/
private static registerCopyHandler(): void {
const copyBtns = document.querySelectorAll('.ede .copy-code-btn');
copyBtns.forEach((btn, index) => {
btn.addEventListener('click', function() {
const value = btn.parentElement?.querySelector('.code-value')?.innerHTML;
if(value) {
navigator.clipboard.writeText(value);
}
const svg = btn.querySelector('svg');
if(svg) {
svg.style.display = "none";
}
let copiedNote = document.createElement('span');
copiedNote.className = "copied-note";
copiedNote.innerHTML = 'copied 👍';
btn.append(copiedNote);
setTimeout(() => {
if(svg) {
svg.style.display = "inline-block";
copiedNote.remove();
}
}, 1500)
});
});
}
/**
* Register scale image handler.
*
*/
private static registerScaleImageHandler(): void {
const scaleBtns = document.querySelectorAll('.ede .scale-image-btn');
scaleBtns.forEach((btn, index) => {
btn.addEventListener('click', function() {
if(btn.parentElement!.classList.contains('scaled')) {
btn.innerHTML = '';
btn.parentElement!.classList.remove('scaled');
}
else {
btn.parentElement!.classList.add('scaled');
btn.innerHTML = '';
}
});
});
}
/**
* Add the plain text
*/
private addPlainText(text: String): void {
this.plainText+= ` ${text}`;
}
/**
* Calculate the reading time based on words count
* and word per minute
*/
private calculateReadingTime(): void {
const wordsCount: number = this.plainText.split(' ').length;
if(wordsCount > 0) {
const minutes: number = Math.ceil(wordsCount / this.wpm);
this.readingTime.wordsCount = wordsCount;
this.readingTime.minutes = minutes;
}
}
/**
* Reading time getter
*
*/
public getReadingTime(): ReadingTime {
return this.readingTime;
}
public toString = (): String|undefined => {
return this.html;
}
}
export default HtmlViewer;