ng-prism
Version:
An Angular2 codeblock highlighting component using Prismjs.
257 lines (256 loc) • 9.23 kB
JavaScript
;
var core_1 = require('@angular/core');
require('prismjs/components/prism-bash');
require('prismjs/components/prism-powershell');
require('prismjs/components/prism-javascript');
require('prismjs/plugins/line-numbers/prism-line-numbers');
require('prismjs/plugins/command-line/prism-command-line');
require('prismjs/plugins/normalize-whitespace/prism-normalize-whitespace');
/**
* Represent template tags added by angular structural directives
*/
var TEMPLATE_REGEX = /<!--template\sbindings={[^\}]*}-->/g;
/**
* Code highlighting component
*
* Used internally by a codeblock to perform the actual highlighting.
*/
var CodeRendererComponent = (function () {
function CodeRendererComponent(_renderer) {
this._renderer = _renderer;
}
CodeRendererComponent.prototype.render = function () {
this._replaceCode();
this._highlight();
};
/**
* Clear the code.
*/
CodeRendererComponent.prototype.empty = function () {
if (this._pre) {
this._pre.nativeElement.innerHTML = '';
}
};
/**
* Place the new code element in the template
*/
CodeRendererComponent.prototype._replaceCode = function () {
this._renderer.setElementProperty(this._pre.nativeElement, 'innerHTML', this._buildCodeElement());
};
/**
* Perform the actual highlighting
*/
CodeRendererComponent.prototype._highlight = function () {
// this._truncateLargeFiles();
Prism.highlightElement(this._pre.nativeElement.querySelector('code'), false, null);
if (this.shell && this.outputLines) {
this._fixPromptOutputPadding();
}
};
Object.defineProperty(CodeRendererComponent.prototype, "_processedCode", {
/**
* Code prepared for highlighting and display
*/
get: function () {
return this._isMarkup(this.language)
? this._processMarkup(this.code)
: this.code;
},
enumerable: true,
configurable: true
});
/**
* Format markup for display.
*/
CodeRendererComponent.prototype._processMarkup = function (text) {
return this._replaceTags(this._removeAngularMarkup(text));
};
/**
* Change all opening < changed to < to render markup correctly inside pre
* tags
*/
CodeRendererComponent.prototype._replaceTags = function (text) {
return text.replace(/(<)([!\/A-Za-z](.|[\n\r])*?>)/g, '<$2');
};
/**
* Remove both template tags and styling attributes added by the angular2
* parser and fix indentation within code elements created by structural
* directives.
*/
CodeRendererComponent.prototype._removeAngularMarkup = function (html) {
// remove styling attributes (_ngcontent etc.)
html = html.replace(/\s_ng[^-]+-[^-]+-[^=]+="[^"]*"/g, '');
var lines = this._fixIndentation(html);
// remove empty <!--template--> lines
lines = lines.filter(function (line) {
if (line.trim() === '') {
return true;
}
var replaced = line.replace(TEMPLATE_REGEX, '')
.trim();
return replaced !== '';
});
html = lines.join('\n');
// remove <!--template--> tags on lines with code
return html.replace(TEMPLATE_REGEX, '');
};
/**
* Is the language given a markup language?
*/
CodeRendererComponent.prototype._isMarkup = function (language) {
return language === 'markup' || language === 'markdown';
};
/**
* Create a <code> element with the proper classes and formatted code
*/
CodeRendererComponent.prototype._buildCodeElement = function () {
return "<code class=\"" + this.codeClasses + "\">" + this._processedCode + "</code>";
};
Object.defineProperty(CodeRendererComponent.prototype, "languageClass", {
/**
* Styling classes
*/
get: function () {
return 'language-' + this.language;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeRendererComponent.prototype, "lineNumbersClass", {
get: function () {
return this.lineNumbers ? 'line-numbers' : '';
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeRendererComponent.prototype, "shellClass", {
get: function () {
return this.shell ? 'command-line' : '';
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeRendererComponent.prototype, "codeClasses", {
get: function () {
return this.languageClass + ' ' + this.language;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeRendererComponent.prototype, "preClasses", {
get: function () {
return this.lineNumbersClass + ' ' + this.languageClass + ' ' + this.shellClass;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeRendererComponent.prototype, "_codeEl", {
/* Code Styling **/
/**
* The code element within <pre>
*/
get: function () {
return this._pre.nativeElement.querySelector('code');
},
enumerable: true,
configurable: true
});
/**
* Adds back padding on output shells because of floated left prompt
*/
CodeRendererComponent.prototype._fixPromptOutputPadding = function () {
if (this._codeEl) {
var clp = this._codeEl.querySelector('.command-line-prompt');
if (clp) {
var promptWidth = this._codeEl.querySelector('.command-line-prompt').clientWidth;
var prePadding = parseInt(this._getStyle(this._pre.nativeElement, 'padding-left')
.replace('px', ''), 10);
this._pre.nativeElement.style.paddingRight = (2 * prePadding + promptWidth / 2) + 'px';
}
}
};
/**
* Get the actually applied style of an element
*/
CodeRendererComponent.prototype._getStyle = function (oElm, strCssRule) {
var strValue = '';
if (document.defaultView && document.defaultView.getComputedStyle) {
strValue = document.defaultView.getComputedStyle(oElm, '')
.getPropertyValue(strCssRule);
}
else if (oElm.currentStyle) {
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
};
// _truncateLargeFiles() {
// if (this._codeEl.innerHTML.length > this.truncationSize) {
// this._codeEl.innerHTML = this._codeEl.innerHTML.slice(0,
// this.truncationSize) + "\n" + this.truncationMessage + "\n"; } }
/**
* Remove extra indentation in ngSwitches
*/
CodeRendererComponent.prototype._fixIndentation = function (html) {
var indent = 0;
var diff = 0;
var removeLines = [];
var lines = html.split('\n')
.map(function (line, index) {
if (line.trim() === '') {
if (indent > 0) {
removeLines.push(index);
}
indent = 0;
return '';
}
var a = line.replace(TEMPLATE_REGEX, '')
.trim();
if (a === '') {
indent = line.match(/^\s*/)[0].length;
return line;
}
else if (indent > 0) {
length = line.match(/^\s*/)[0].length;
if (diff === 0) {
diff = length - indent;
}
if (length >= indent) {
return line.slice(diff);
}
else {
indent = 0;
}
}
return line;
});
// remove empty lines added by ngSwitch
removeLines.forEach(function (removalIndex) {
lines.splice(removalIndex, 1);
});
return lines;
};
CodeRendererComponent.decorators = [
{ type: core_1.Component, args: [{
selector: 'code-renderer',
template: "\n<pre #preEl [class]=\"preClasses\" [attr.data-prompt]=\"prompt\" [attr.data-output]=\"outputLines\"></pre>\n "
},] },
];
/** @nocollapse */
CodeRendererComponent.ctorParameters = function () { return [
{ type: core_1.Renderer, },
]; };
CodeRendererComponent.propDecorators = {
'code': [{ type: core_1.Input },],
'language': [{ type: core_1.Input },],
'lineNumbers': [{ type: core_1.Input },],
'shell': [{ type: core_1.Input },],
'prompt': [{ type: core_1.Input },],
'outputLines': [{ type: core_1.Input },],
'_pre': [{ type: core_1.ViewChild, args: ['preEl',] },],
};
return CodeRendererComponent;
}());
exports.CodeRendererComponent = CodeRendererComponent;