UNPKG

ng-prism

Version:

An Angular2 codeblock highlighting component using Prismjs.

367 lines (366 loc) 11.9 kB
"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 &lt; for leading < tags in multiline typescript strings * * @param {string} text - the code * @return {string} - the code with < replaced by &lt; inside ``s */ CodeblockComponent.prototype._replaceTagsInMultiline = function (text) { return text.replace(/`((.|[\r\n])*?)`/g, function (match) { return match.replace(/(<)([!\/A-Za-z].*?>)/g, '&lt;$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;