ng-realmark
Version:
Real-time Markdown W/ Markdown three way merge
225 lines • 9.94 kB
JavaScript
import { Injectable, Sanitizer, SecurityContext } from '@angular/core';
import * as Showdown from 'showdown';
import 'rxjs/add/operator/toPromise';
import { showdownPrism } from './lib/showdownPrism';
import { ShowdownConfig } from '../config';
import { diff3Merge } from './lib/diff3';
var ComparedLine = /** @class */ (function () {
function ComparedLine() {
}
return ComparedLine;
}());
export { ComparedLine };
var RealMarkService = /** @class */ (function () {
function RealMarkService(config, sanitizer) {
this.sanitizer = sanitizer;
if (config) {
this.flavor = config.flavor;
this.headerLinks = config.headerLinks;
}
}
/**
* legacy entry point used for the directive
*/
RealMarkService.prototype.fromInput = function (raw) {
var html = this.process(raw);
return html;
};
/**
* legacy Promise entry point
*/
RealMarkService.prototype.fromInputPromise = function (raw) {
var html = this.process(raw);
return Promise.resolve(html);
};
RealMarkService.prototype.getTableOfContents = function () {
return this.tableOfContents;
};
/**
* main function of class, converts markdown to html with showdown.
*/
RealMarkService.prototype.process = function (markdown) {
Showdown.extension('showdown-prism', showdownPrism);
var extensions = ['showdown-prism'];
var converter = new Showdown.Converter({ extensions: extensions });
converter.setFlavor(this.flavor);
var unsafe = converter.makeHtml(markdown);
var HTMLOutput = "" + this.sanitizer.sanitize(SecurityContext.HTML, unsafe);
var HTMLOutputFinal = HTMLOutput;
var tableOfContents = [];
if (this.headerLinks) {
var linkIcon = '<svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>';
var currentURL = "";
if (typeof (window) !== 'undefined') {
currentURL = window.location.href.split('#')[0]; // remove current hash
}
var checkNumber = {};
var regex = new RegExp(/(<h([1-5]))>(.+?)(?=\<\/h\2>)(<\/h\2>)/, 'g');
var match;
while ((match = regex.exec(HTMLOutput)) != null) {
var idName = match[3].replace(/<\/?[^>]+(>|$)/g, "").replace(/\s/g, '-').toLowerCase();
var content = match[3].replace(/<\/?[^>]+(>|$)/g, "");
var tableOfContentsCheck = tableOfContents.map(function (v) { return v.link; });
if (tableOfContentsCheck.indexOf(idName) !== -1) {
checkNumber[idName] !== undefined ? checkNumber[idName] = checkNumber[idName] + 1 : checkNumber[idName] = 2;
idName += "-" + checkNumber[idName];
}
var linkBtn = '<a class="anchor" href="' + currentURL + '#' + idName + '" aria-hidden="true">' + linkIcon + '</a>';
tableOfContents.push({ value: content, depth: match[2], link: idName });
HTMLOutputFinal = HTMLOutputFinal.replace(match[0], match[1] + ' id=\"' + idName + '\">' + linkBtn + match[3] + match[4]);
}
}
this.tableOfContents = tableOfContents;
return HTMLOutputFinal;
};
RealMarkService.prototype.mergeMarkdown = function (patchVersion, originalVersion, liveVersion) {
var s1Parts = [""];
var s2Parts = [""];
var s3Parts = [""];
if (liveVersion && liveVersion.content && liveVersion.content.split("\n")) {
s1Parts = liveVersion.content.split("\n");
}
if (originalVersion && originalVersion.content && originalVersion.content.split("\n")) {
s2Parts = originalVersion.content.split("\n");
}
if (patchVersion && patchVersion.content && patchVersion.content.split("\n")) {
s3Parts = patchVersion.content.split("\n");
}
var result = diff3Merge(s1Parts, s2Parts, s3Parts);
var rtn = [], conflict;
for (var i = 0; i < result.length; i++) {
if (result[i].ok === undefined) {
rtn.push("|>>>>>>>>>>> PATCH: " + patchVersion.revision + "\n"
+ result[i].conflict.b.join("\n") + "\n"
+ "===========" + "\n"
+ result[i].conflict.a.join("\n") + "\n"
+ "<<<<<<<<<<<< LIVE: " + liveVersion.revision);
}
else {
rtn.push(result[i].ok);
}
}
return { content: rtn.join('\n'), conflicts: conflict };
};
/**
* public function to compare each line one by one for changes. Return resolved promise
*/
RealMarkService.prototype.compareMarkdown = function (content, compared, showDeleted, raw, codeBlock) {
var showLog = false;
var returnOut = [];
// console.log("Called compareMarkdown()");
this.codeBlock = codeBlock ? codeBlock : "";
var conflict = [];
// if(compared !== content){
if (!content && !compared) {
return [];
}
var s1Parts = [""];
var s2Parts = [""];
if (content && content.split("\n")) {
s1Parts = content.split("\n");
}
if (compared && compared.split("\n")) {
s2Parts = compared.split("\n");
}
var count = s2Parts.length > s1Parts.length ? s2Parts.length : s1Parts.length, j = 0;
for (var i = 0; i < count;) {
if (showLog) {
console.warn(count, "=", s1Parts[i], i, "::", s2Parts[j], j);
}
if (s1Parts[i] === s2Parts[j]) {
if (s1Parts[i] !== undefined) {
if (showLog) {
console.log("KEEP", s1Parts[i]);
}
returnOut.push(this.buildReturnLine("line", s1Parts[i], i, j, raw));
}
j++;
i++;
}
else if ((s1Parts[i] === s2Parts[j + 1] || s1Parts[i + 1] === s2Parts[j + 2]) && s2Parts[j] !== undefined) {
if (showLog) {
console.log("DELETED", s2Parts[j]);
}
if (showDeleted) {
returnOut.push(this.buildReturnLine("deleted", s2Parts[j], i, j, raw));
}
j++;
}
else if (s1Parts[i + 1] === s2Parts[j + 1] && s1Parts[i] !== undefined && s2Parts[j] !== undefined) {
if (showLog) {
console.log("REPLACED", s2Parts[j], "WITH", s1Parts[i]);
}
if (showDeleted) {
returnOut.push(this.buildReturnLine("deleted", s2Parts[j], i, j, raw));
}
returnOut.push(this.buildReturnLine("added", s1Parts[i], i, j, raw));
j++;
i++;
}
else if (s1Parts[i + 1] === s2Parts[j] && s1Parts[i - 1] === s2Parts[j - 1] && s1Parts[i] !== undefined && s2Parts[j] !== undefined) {
if (showLog) {
console.log("INSERT BETWEEN", s2Parts[j], "AND", s2Parts[j - 1]);
}
returnOut.push(this.buildReturnLine("added", s1Parts[i], i, j, raw));
i++;
}
else {
// console.warn("COULDNT MATCH LINE");
if (s2Parts[j] === undefined) {
if (showLog) {
console.log("COMPLETE NEW LINE", s1Parts[i]);
}
returnOut.push(this.buildReturnLine("added", s1Parts[i], i, j, raw));
j--;
}
else {
if (s1Parts[i] !== undefined) {
if (showLog) {
console.log("MOST LIKLEY REPLACED", s2Parts[j], "WITH", s1Parts[i]);
}
if (showDeleted) {
returnOut.push(this.buildReturnLine("deleted", s2Parts[j], i, j, raw));
}
returnOut.push(this.buildReturnLine("added", s1Parts[i], i, j, raw));
}
else {
if (showDeleted) {
if (showLog) {
console.log("DELETED LOW", s1Parts[i], s1Parts[i + 1], s2Parts[j]);
}
returnOut.push(this.buildReturnLine("deleted", s2Parts[j], i, j, raw));
}
}
}
j++;
i++;
}
}
// }
return returnOut;
};
/**
* builds return object for each line
*/
RealMarkService.prototype.buildReturnLine = function (type, text, originalLine, newLine, raw) {
return {
"type": type,
"text": text,
"originalLine": originalLine + 1,
"newLine": newLine + 1,
"format": this.codeBlock ? "code" : raw ? "text" : "markdown"
};
};
RealMarkService.decorators = [
{ type: Injectable },
];
/** @nocollapse */
RealMarkService.ctorParameters = function () { return [
{ type: ShowdownConfig },
{ type: Sanitizer }
]; };
return RealMarkService;
}());
export { RealMarkService };
//# sourceMappingURL=realmark.service.js.map