ng-prism
Version:
An Angular2 codeblock highlighting component using Prismjs.
367 lines (366 loc) • 11.9 kB
JavaScript
"use strict";
// tslint:disable:max-file-line-count
var core_1 = require('@angular/core');
var code_renderer_component_1 = require('./code-renderer.component');
var CodeblockComponent = (function () {
function CodeblockComponent(_elementRef) {
this.DEFAULT_THEME = 'standard';
this.DEFAULT_SHELL_THEME = 'okaidia';
/**
* The prompt to display in a shell codeblock. Default is $.
*
* Example:
* ```
* <codeblock shell="bash" prompt="#">
* cd ..
* </codeblock>
* ```
* Result:
* ```
* # cd ..
* ```
*/
this.prompt = '$';
this._code = '';
this._showingMessage = false;
this._languageSet = false;
this._lineNumbers = true;
this._changed = false;
this._shellType = null;
this._elementRef = _elementRef;
}
Object.defineProperty(CodeblockComponent.prototype, "lineNumbers", {
get: function () {
return this._lineNumbers;
},
/* Inputs */
/**
* Display line numbers for the codeblock. The numbers start at 1 and are
* not selected when selecting the main code text.
*
* Example:
* ```
* <codeblock [lineNumbers]="true" markup>
* <h1>Hello</h1>
* <h1>Hi</h1>
* <h1>Aloha</h1>
* </codeblock>
* ```
*
* Result:
* ```
* 1 <h1>Hello</h1>
* 2 <h1>Hi</h1>
* 3 <h1>Aloha</h1>
* ```
*
* @param {boolean} value - whether or not to show line numbers
*/
set: function (value) {
if (this._lineNumbers !== value) {
this._changed = true;
this._lineNumbers = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeblockComponent.prototype, "language", {
get: function () {
return this._showingMessage ? undefined : this._language;
},
/**
* Set the language used to highlight the code within the codeblock.
* Consider using a language directive instead if the language is not
* going to change dynamically.
*
* Example:
* <codeblock language="markup">
* <h1>This is HTML</h1>
* </codeblock>
*
* @param lang
*/
set: function (lang) {
if (this.isShell()) {
return;
}
this._languageSet = !!(lang && lang.length > 0);
this._language = Prism.languages[lang] ? lang : undefined;
this._changed = true;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeblockComponent.prototype, "theme", {
get: function () {
if (this._theme) {
return this._theme;
}
return this.isShell() ? this.DEFAULT_SHELL_THEME : this.DEFAULT_THEME;
},
/**
* The theme for styling the codeblock. All prismjs themes are available.
*
* @param {string} theme - A prismjs theme. Defaults to 'standard'.
*/
set: function (theme) {
this._theme = theme;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeblockComponent.prototype, "shell", {
get: function () {
return this._shellType;
},
/**
* Turn this codeblock into a shell display with a prompt.
*
* Example:
* ```
* <codeblock shell="bash">
* cd ..
* mkdir project
* </codeblock>
* ```
* Result:
* ```
* $ cd ..
* $ mkdir project
* ```
*
* @param {string} shellType - One of CodeblockComponent.SHELL_TYPES
*/
set: function (shellType) {
if (shellType && CodeblockComponent.SHELL_TYPES.indexOf(shellType) !== -1) {
this._language = shellType;
this._languageSet = true;
this._shellType = shellType;
this.lineNumbers = false;
this._changed = true;
}
else {
this._shellType = null;
}
},
enumerable: true,
configurable: true
});
/* Lifecycle Events **/
/**
* Update code when content changes
*/
CodeblockComponent.prototype.ngAfterContentChecked = function () {
if (!this._sourced && !this._showingMessage) {
this.code = this.content;
}
};
/**
* Render code when any input changes
*/
CodeblockComponent.prototype.ngAfterViewChecked = function () {
if (this._changed) {
this._changed = false;
this.codeRenderer.render();
}
};
Object.defineProperty(CodeblockComponent.prototype, "content", {
/* Attributes */
/**
* The current content of the codeblock.
*
* Example:
* ```
* <codeblock javascript>
* // Inside the codeblock
* </codeblock>
* ```
* Result:
* ```
* // Inside the codeblock
* ```
*
* @return {string} - innerHTML of this codeblock's nativeElement
*/
get: function () {
return this.contentEl ? this.contentEl.nativeElement.innerHTML : '';
},
enumerable: true,
configurable: true
});
Object.defineProperty(CodeblockComponent.prototype, "code", {
get: function () {
return this._code;
},
/**
* The code to display in the codeblock. Automatically set to this.content
* unless a src attribute is present.
*/
set: function (code) {
if (this._code !== code) {
this._changed = true;
// this._showingMessage = false;
this._code = code;
}
},
enumerable: true,
configurable: true
});
/**
* The source has been changed
*/
CodeblockComponent.prototype.sourceChanged = function (url) {
this.message('Loading ...');
};
/**
* Given the downloaded source, set the language and code to match it.
*
* @param {Response} res
*/
CodeblockComponent.prototype.sourceReceived = function (res) {
var ext = res.url.match(/\.(\w+)$/)[1];
var fileLang = CodeblockComponent.EXTENSION_MAP[ext] || ext;
var text = res.text();
if (!this._languageSet) {
this._language = fileLang;
if (fileLang === 'typescript') {
text = this._replaceTagsInMultiline(res.text());
}
}
this.code = text;
this._showingMessage = false;
this._sourced = true;
};
/**
* An error occured while downloading from the src url
*
* @param {Error} error
*/
CodeblockComponent.prototype.sourceError = function (error) {
this._sourced = false;
this.message(error.message ? error.message : 'An error occured.');
};
/**
* Is this displayed as a shell?
*/
CodeblockComponent.prototype.isShell = function () {
return this._shellType !== null;
};
/**
* @return {boolean} - whether or not lineNumbers should be displayed
*/
CodeblockComponent.prototype.shouldDisplayLineNumbers = function () {
return this.lineNumbers && !this._showingMessage;
};
/*** Truncation ***/
// @Input() truncationSize: number = 100000;
// @Input() truncationMessage: string = "\n--- File Truncated ---\n";
/*** Methods ***/
/**
* Display the text inside the codeblock instead of code. Used for errors
* and warnings during file loading. The language of the codeblock will
* be set to undefined when displaying a message.
*
* @param {string} text - The message to display.
*/
CodeblockComponent.prototype.message = function (text) {
this._showingMessage = true;
this.code = text;
};
/**
* Returns a double curly-braced version of the input string.
* Use this inside a template to display a binding.
*
* Example:
* ```
* <codeblock markup #cb><span>{{cb.bind('name')}}</span></codeblock>
* ```
* Result:
* ```
* <span>{{name}}</span>
* ```
*
* @param {string} text - the text to wrap in curly braces
*/
CodeblockComponent.prototype.bind = function (text) {
return "{{" + text + "}}";
};
/************ Private **************/
/**
* Use html < for leading < tags in multiline typescript strings
*
* @param {string} text - the code
* @return {string} - the code with < replaced by < inside ``s
*/
CodeblockComponent.prototype._replaceTagsInMultiline = function (text) {
return text.replace(/`((.|[\r\n])*?)`/g, function (match) {
return match.replace(/(<)([!\/A-Za-z].*?>)/g, '<$2');
});
};
/**
* Map of file extensions to highlighting languages
*/
CodeblockComponent.EXTENSION_MAP = {
js: 'javascript',
ts: 'typescript',
html: 'markup',
svg: 'markup',
xml: 'markup',
md: 'markdown',
py: 'python',
rb: 'ruby',
ps1: 'powershell',
psm1: 'powershell'
};
/**
* Possible shell types
*/
CodeblockComponent.SHELL_TYPES = ['bash', 'powershell'];
/**
* A list of the valid codeblock themes.
*
* Example:
* ```
* <select name="select" [(ngModel)]="selectedTheme">
* <option *ngFor="#theme of CodeblockComponent.THEMES"
* value="{{theme}}">{{theme}}</option>
* </select>
* ```
*/
CodeblockComponent.THEMES = [
'standard',
'coy',
'dark',
'funky',
'okaidia',
'solarizedlight',
'tomorrow',
'twilight'
];
CodeblockComponent.decorators = [
{ type: core_1.Component, args: [{
selector: 'codeblock',
template: "\n <div #contentEl class=\"codeblock-content\" style=\"display: none\"><ng-content></ng-content></div>\n <div class=\"codeblock {{theme}}\">\n <code-renderer\n [code]=\"code\"\n [language]=\"language\"\n [lineNumbers]=\"shouldDisplayLineNumbers()\"\n [shell]=\"shell\"\n [prompt]=\"prompt\"\n [outputLines]=\"outputLines\">\n </code-renderer>\n </div>\n ",
// necessary to make component styles apply because unique ng attributes
// aren't applied to elements added by Prism.highlight
encapsulation: core_1.ViewEncapsulation.None
},] },
];
/** @nocollapse */
CodeblockComponent.ctorParameters = function () { return [
{ type: core_1.ElementRef, },
]; };
CodeblockComponent.propDecorators = {
'lineNumbers': [{ type: core_1.Input },],
'language': [{ type: core_1.Input },],
'theme': [{ type: core_1.Input },],
'prompt': [{ type: core_1.Input },],
'outputLines': [{ type: core_1.Input },],
'shell': [{ type: core_1.Input },],
'contentEl': [{ type: core_1.ViewChild, args: ['contentEl',] },],
'codeRenderer': [{ type: core_1.ViewChild, args: [code_renderer_component_1.CodeRendererComponent,] },],
};
return CodeblockComponent;
}());
exports.CodeblockComponent = CodeblockComponent;