Last active
December 5, 2018 07:48
-
-
Save ninetyone/73b927dc0fff012a1f4892460aa46bde to your computer and use it in GitHub Desktop.
Quill.js: Convert Delta to HTML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var editorProcesing = (function() { | |
function getEditorHTML(deltaData) { | |
var intermediateHTMLData = getIntermediateHTMLFromDelta(deltaData); | |
var properHTMLData = getProperHTMLFromIntermediateHTML(intermediateHTMLData); | |
sendErrorsIfProperHTMLDataHasIntermediateHTMLTags(properHTMLData, deltaData); | |
return properHTMLData; | |
} | |
function getIntermediateHTMLFromDelta(deltaData) { | |
if (!deltaData) deltaData = quill.getContents(); | |
var intermediateHTMLData = ''; | |
if (!deltaData.ops) return intermediateHTMLData; | |
for (var i = 0; i < deltaData.ops.length; i += 1) { | |
var thisDelta = deltaData.ops[i]; | |
if (!!thisDelta.insert) { | |
var partOfIntermediateHTMLData = getIntermediateHTMLByProcessingDeltaObject(thisDelta); | |
intermediateHTMLData += partOfIntermediateHTMLData; | |
} | |
} | |
return intermediateHTMLData; | |
} | |
function getAllAttributeKeysForThisDeltaObject(thisDelta, isInsertElementAnObject) { | |
var attributeKeys = []; | |
if (!!thisDelta.attributes) { | |
Array.prototype.push.apply(attributeKeys, Object.keys(thisDelta.attributes)); | |
} | |
if (isInsertElementAnObject) { | |
Array.prototype.push.apply(attributeKeys, Object.keys(thisDelta.insert)); | |
} | |
attributeKeys.sort(); //sort attribute keys alphabetically | |
return attributeKeys; | |
} | |
function isThisDeltasInsertElementAnObject(thisDelta) { | |
return typeof thisDelta.insert === 'object'; | |
} | |
function getInsertDataForThisDeltaObject(thisDelta, isInsertElementAnObject) { | |
if (isInsertElementAnObject) { | |
return ''; | |
} else { | |
return sanitizeHTML(thisDelta.insert); | |
} | |
} | |
function getAttributesMappedObjectForThisDeltaObject(thisDelta, isInsertElementAnObject) { | |
var obj1 = thisDelta.attributes; | |
var obj2 = {}; | |
if (isInsertElementAnObject) { | |
obj2 = thisDelta.insert; | |
} | |
var obj3 = {}; | |
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } | |
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } | |
return obj3; | |
} | |
function getIntermediateHTMLByProcessingDeltaObject(thisDelta) { | |
var isInsertElementAnObject = isThisDeltasInsertElementAnObject(thisDelta); | |
var attributeKeys = getAllAttributeKeysForThisDeltaObject(thisDelta, isInsertElementAnObject); | |
var attributeObject = getAttributesMappedObjectForThisDeltaObject(thisDelta, isInsertElementAnObject); | |
var data = getInsertDataForThisDeltaObject(thisDelta, isInsertElementAnObject); | |
for (var j = attributeKeys.length - 1; j > -1; j -= 1) { | |
data = getIntermediateHTMLByProcessingEachDeltaAttribute(attributeKeys[j], attributeObject, data); | |
} | |
return data; | |
} | |
function getIntermediateHTMLByProcessingEachDeltaAttribute(thisAttribute, attributeObject, data) { | |
switch (thisAttribute) { | |
case 'bold': | |
case 'italic': | |
case 'underline': | |
case 'strike': | |
var tag = getTagMappingForThisBlot(thisAttribute); | |
if (!!tag) { | |
data = '<' + tag +'>' + data + '</' + tag +'>'; | |
} | |
break; | |
case 'header': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<placeholder h-' + thisAttributeVal + '>' + data + '<placeholder newline>'; | |
break; | |
case 'align': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<placeholder align-' + thisAttributeVal + '>' + data; | |
break; | |
case 'blockquote': | |
data = '<placeholder blockquote>' + data + '<placeholder newline>'; | |
break; | |
case 'list': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
if (data === '\n') { | |
if (thisAttributeVal === 'ordered') { | |
data = '<placeholder list-ol>'; | |
} else { | |
data = '<placeholder list-ul>'; | |
} | |
} else { | |
var listArray = data.split('\n'); | |
var listData = ''; | |
listData += listArray[0]; | |
for (var z = 1; z < listArray.length; z += 1) { | |
if (thisAttributeVal === 'ordered') { | |
listData += '<placeholder list-ol>' + listArray[z]; | |
} else { | |
listData += '<placeholder list-ul>' + listArray[z]; | |
} | |
} | |
data = listData; | |
} | |
break; | |
case 'divider': | |
data = '<placeholder newline><hr></hr><placeholder newline>'; | |
break; | |
case 'link': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<a href="' + thisAttributeVal + '" target="_blank">' + data + '</a>'; | |
break; | |
case 'image': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<placeholder newline><img src="' + thisAttributeVal + '" alt="'+ (attributeObject.alt || "") +'" /><placeholder newline>'; | |
break; | |
case 'caption': | |
data = '<placeholder figcaption>' + data; | |
break; | |
case 'video': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<placeholder newline><iframe style="max-width:100%" width="500" height="281" src="' + thisAttributeVal + '" frameborder="0" allowfullscreen></iframe><placeholder newline>'; | |
break; | |
case 'embed': | |
var thisAttributeVal = attributeObject[thisAttribute]; | |
data = '<placeholder newline><div class="sportskeeda-embed" data-src="' + thisAttributeVal.src + '">' + thisAttributeVal.html + '</div><placeholder newline>'; | |
break; | |
case 'color': | |
case 'width': | |
case 'height': | |
case 'background': | |
case 'alt': | |
case 'indent': | |
case 'size': | |
case 'code-block': | |
case 'font': | |
case 'script'://super or subscript | |
case 'grammarly-inline': | |
break; | |
default: | |
console.error('EDIOTR HTML PARSING ERROR: attribute type: ' + thisAttribute + ' AND data: ' + JSON.stringify(attributeObject)); | |
Rollbar.error('EDIOTR HTML PARSING ERROR: attribute type: ' + thisAttribute + ' AND data: ' + JSON.stringify(attributeObject)); | |
} | |
return data; | |
} | |
function getProperHTMLFromIntermediateHTML(intermediateHTMLData) { | |
if (!!!intermediateHTMLData) { | |
return ''; | |
} | |
intermediateHTMLData = preProcessData(intermediateHTMLData); | |
var contentBlocksArray = getContentBlocksArray(intermediateHTMLData); | |
var processedData = ''; | |
for (let i = 0; i < contentBlocksArray.length; i++) { | |
if (checkIfLineAnyEnclosingTag(contentBlocksArray[i].trim())) { | |
if (contentBlocksArray[i].indexOf('<placeholder ') > -1) { | |
var oneBlockElementData = contentBlocksArray[i]; | |
oneBlockElementData = handleHeaderElements(oneBlockElementData); | |
oneBlockElementData = handleAlignElements(oneBlockElementData); | |
oneBlockElementData = handleListElements(oneBlockElementData); | |
oneBlockElementData = handleBlockquoteElements(oneBlockElementData); | |
oneBlockElementData = handleFigCaptionElements(oneBlockElementData); | |
//Append oneBlockElementData to processedData | |
processedData += oneBlockElementData; | |
} else { | |
if (contentsAreJustTags(contentBlocksArray[i])) { | |
processedData += contentBlocksArray[i].trim(); | |
} else { | |
processedData += '<p>' + contentBlocksArray[i] + '</p>'; | |
} | |
} | |
} else { | |
processedData += contentBlocksArray[i].trim(); | |
} | |
} | |
processedData = postProcessHTML(processedData.trim()); | |
return processedData; | |
} | |
function contentsAreJustTags(content) { | |
return content == "<strong>" || content == "<em>" || content == "<s>" || | |
content == "<u>" || content == "</strong>" || content == "</em>" || | |
content == "</s>" || content == "</u>"; | |
} | |
function sanitizeHTML(input) { | |
if(!input) { | |
return ""; | |
} | |
return input.replace(/\</g,"<").replace(/\>/g,">"); | |
} | |
function checkIfLineAnyEnclosingTag(line) { | |
var pattern = /^(<(\/|)div)|(<(\/|)hr>)|(<(\/|)img)/i; //['<div', '</div', '<hr>', '</hr>']; | |
return !pattern.test(line.trim()); | |
} | |
function postProcessHTML(processedData) { | |
processedData = processedData.replace(/\u200b/g, ''); | |
if (processedData.trim() === "<p></p><p></p>") { | |
return ""; | |
} | |
processedData = processedData.replace(/<p><\/p>/g, '<p>​</p>'); | |
processedData = processedData.replace(/<p>​<\/p><p>​<\/p>$/, '<p>​<\/p>'); | |
//Encapsulate img and figcaption tags inside figure tag | |
processedData = processedData.replace(/((<img.*?\/>)(<figcaption>.*?<\/figcaption>)*)/g, '<figure class= "image">$1</figure>'); | |
return processedData | |
} | |
function preProcessData(intermediateHTMLData) { | |
intermediateHTMLData = intermediateHTMLData.replace(/(\n<placeholder newline>)|(<placeholder newline>\n)/g, '<placeholder newline>'); | |
intermediateHTMLData = intermediateHTMLData.replace(/(<placeholder newline>)+/g, '<placeholder newline>'); | |
return intermediateHTMLData; | |
} | |
function getContentBlocksArray(intermediateHTMLData) { | |
var contentBlocksArray = intermediateHTMLData.split(/\n|<placeholder newline>/); | |
var newContentBlocksArray = []; | |
for (var z = 0; z < contentBlocksArray.length; z++) { | |
var oneBlockElementData = contentBlocksArray[z]; | |
if (oneBlockElementData.indexOf('<placeholder list-') > -1) { | |
var lastElementIndex = oneBlockElementData.lastIndexOf("<placeholder list-") + | |
"<placeholder list-".length + 3; | |
var list = oneBlockElementData.substring(0, lastElementIndex); | |
var afterList = oneBlockElementData.substring(lastElementIndex); | |
newContentBlocksArray.push(list); | |
if (afterList.length > 0) { | |
newContentBlocksArray.push(afterList); | |
} | |
} else { | |
newContentBlocksArray.push(oneBlockElementData); | |
} | |
} | |
return newContentBlocksArray; | |
} | |
function handleHeaderElements(oneBlockElementData) { | |
while (oneBlockElementData.indexOf('<placeholder h-') > -1) { | |
var index = oneBlockElementData.indexOf('<placeholder h-') + "<placeholder h-".length; | |
var headerVal = oneBlockElementData.substring(index, index + 1); | |
oneBlockElementData = oneBlockElementData.replace('<placeholder h-' + headerVal + '>', ''); | |
oneBlockElementData = '<h' + headerVal + '>' + oneBlockElementData + '</h' + headerVal + '>'; | |
} | |
return oneBlockElementData; | |
} | |
function handleBlockquoteElements(oneBlockElementData) { | |
while (oneBlockElementData.indexOf('<placeholder blockquote>') > -1) { | |
oneBlockElementData = oneBlockElementData.replace('<placeholder blockquote>', ''); | |
oneBlockElementData = '<blockquote>' + oneBlockElementData + '</blockquote>'; | |
} | |
return oneBlockElementData; | |
} | |
function handleFigCaptionElements(oneBlockElementData) { | |
while (oneBlockElementData.indexOf('<placeholder figcaption>') > -1) { | |
oneBlockElementData = oneBlockElementData.replace('<placeholder figcaption>', ''); | |
oneBlockElementData = '<figcaption>' + oneBlockElementData + '</figcaption>'; | |
} | |
return oneBlockElementData; | |
} | |
function handleAlignElements(oneBlockElementData) { | |
while (oneBlockElementData.indexOf('<placeholder align-') > -1) { | |
var indexStart = oneBlockElementData.indexOf('<placeholder align-') + "<placeholder align-".length; | |
var indexEnd = indexStart + oneBlockElementData.substring(indexStart).indexOf('>'); | |
var alignVal = oneBlockElementData.substring(indexStart, indexEnd); | |
oneBlockElementData = oneBlockElementData.replace('<placeholder align-' + alignVal + '>', ''); | |
oneBlockElementData = '<p align="' + alignVal + '">' + oneBlockElementData + '</p>'; | |
} | |
return oneBlockElementData; | |
} | |
function handleListElements(oneBlockElementData) { | |
if (oneBlockElementData.indexOf('<placeholder list-') > -1) { | |
var elementAfterListData = oneBlockElementData; | |
var myRegexp = /(.*?)<placeholder list-(.*?)>/g; | |
var listElemArr = []; | |
var match; | |
while ((match = myRegexp.exec(oneBlockElementData)) !== null) { | |
var elem = []; | |
elem['type'] = match[2]; | |
elem['data'] = match[1] | |
listElemArr.push(elem); | |
elementAfterListData = elementAfterListData.replace(match[0], ''); | |
} | |
var currentListType = listElemArr[0].type; | |
var listData = '<' + currentListType + '>'; | |
for (let i = 0; i < listElemArr.length; i++) { | |
listElem = listElemArr[i]; | |
if (listElem.type === currentListType) { | |
listData += '<li>' + listElem.data + '</li>'; | |
} else { | |
listData += '</' + currentListType + '>' + '<' + listElem.type + '>'; | |
listData += '<li>' + listElem.data + '</li>'; | |
currentListType = listElem.type; | |
} | |
} | |
listData += '</' + currentListType + '>'; | |
if (elementAfterListData.trim().length > 0) { | |
if (!checkIfLineAnyEnclosingTag(elementAfterListData.trim())) { | |
elementAfterListData = elementAfterListData; | |
} else { | |
elementAfterListData = '<p>' + elementAfterListData + '</p>'; | |
} | |
} | |
oneBlockElementData = listData + elementAfterListData; | |
} | |
return oneBlockElementData; | |
} | |
function sendErrorsIfProperHTMLDataHasIntermediateHTMLTags(properHTMLData, deltaData) { | |
if (properHTMLData.indexOf('<placeholder') > -1) { | |
if (!deltaData) deltaData = quill.getContents(); | |
console.error('EDIOTR HTML PARSING ERROR: HTML has intermediate tags. HTML: ' + properHTMLData + ' AND delta: ' + JSON.stringify(deltaData)); | |
Rollbar.error('EDIOTR HTML PARSING ERROR: HTML has intermediate tags. HTML: ' + properHTMLData + ' AND delta: ' + JSON.stringify(deltaData)); | |
} | |
} | |
function getTagMappingForThisBlot(blot) { | |
var blotToTagMapping = { | |
bold: 'strong', | |
italic: 'em', | |
underline: 'u', | |
strike: 's', | |
}; | |
return blotToTagMapping[blot]; | |
} | |
return { | |
getEditorHTML: getEditorHTML, | |
sanitizeHTML: sanitizeHTML | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment