UNPKG

@mdefy/ngx-markdown-editor

Version:

An Angular Markdown Editor in WYSIWYG style with extensive functionality, high customizability and an integrated material theme.

703 lines 103 kB
import { Component, ElementRef, Host, HostBinding, Input, Output, SimpleChange, ViewChild, ViewEncapsulation, } from '@angular/core'; import { MatIconRegistry } from '@angular/material/icon'; import { MAT_TOOLTIP_DEFAULT_OPTIONS } from '@angular/material/tooltip'; import { DomSanitizer } from '@angular/platform-browser'; import { DEFAULT_OPTIONS, MarkdownEditor } from '@mdefy/markdown-editor-core'; import { MarkdownService } from 'ngx-markdown'; import { Observable, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { Keybindings } from '../services/keybindings.service'; import { StatusbarService } from '../services/statusbar.service'; import { ToolbarService } from '../services/toolbar.service'; import { ObservableEmitter } from '../types/observable-emitter'; import { fromCmEvent } from '../util/from-cm-event'; const markdownEditorTooltipDefaults = { showDelay: 1000, hideDelay: 0, touchendHideDelay: 1000, }; const ɵ0 = markdownEditorTooltipDefaults; export class MarkdownEditorComponent { constructor(iconRegistry, domSanitizer, hotkeys, hostElement, markdownService, toolbarService, statusbarService) { this.iconRegistry = iconRegistry; this.domSanitizer = domSanitizer; this.hotkeys = hotkeys; this.hostElement = hostElement; this.markdownService = markdownService; this.toolbarService = toolbarService; this.statusbarService = statusbarService; /** * Data string to set as content of the editor. */ this.data = ''; /** * Options to configure _Ngx Markdown Editor_. * * Basically `MarkdownEditorOptions` from _Markdown Editor Core_ are forwarded, * including some adjustments and extensions. */ this.options = {}; /** * Custom set of toolbar items. */ this.toolbar = []; /** * Custom set of statusbar items. */ this.statusbar = []; /** * The current language applied to internationalized items. */ this.language = 'en'; /** * Specifies whether the editor is a required form field. An asterisk will be appended to the label. */ this.required = false; /** * Specifies whether and which Angular Material style is used. */ this.materialStyle = false; /** * Specifies whether the editor is disabled. */ this.disabled = false; /** * Specifies whether the toolbar is rendered. */ this.showToolbar = true; /** * Specifies whether the statusbar is rendered. */ this.showStatusbar = true; /** * Specifies whether tooltips are shown for toolbar items. */ this.showTooltips = true; /** * Specifies whether the key combination is included in the tooltip. */ this.shortcutsInTooltips = true; /** * Emits when the editor's content changes. */ this.contentChange = new ObservableEmitter(); /** * Emits when the editor's cursor is moved. */ this.cursorActivity = new ObservableEmitter(); /** * Emits when the editor receives focus. */ this.editorFocus = new ObservableEmitter(); /** * Emits when the editor loses focus. */ this.editorBlur = new ObservableEmitter(); /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.normalizedToolbarItems = []; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.activeToolbarItems = []; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.toolbarItemTooltips = []; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.normalizedStatusbarItems = []; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.showPreview = false; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.showSideBySidePreview = false; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.blockBlur = false; /** * _Not intended to be used outside the component. Only made public for access inside template._ */ this.focused = false; this.shortcutResetter = new Subject(); // Render checkbox dummies which can be replaced by an `<input type="checkbox"> later, // because the checkboxes rendered by marked.js inside ngx-markdown are removed by Angular sanitizer. this.markdownService.renderer.checkbox = (checked) => (checked ? '[x] ' : '[ ] '); } get disabledStyle() { return this.disabled; } get default() { return !this.options.editorThemes && !this.materialStyle; } get material() { return this.materialStyle; } get class() { return this.options.editorThemes; } get appearanceStandard() { return this.materialStyle === true || this.materialStyle === 'standard'; } get appearanceFill() { return this.materialStyle === 'fill'; } get appearanceLegacy() { return this.materialStyle === 'legacy'; } get focusedStyle() { return this.focused; } /** * @inheritdoc */ ngOnInit() { const wrapper = this.hostElement.nativeElement.querySelector('.ngx-markdown-editor-text-editor'); this.mde = new MarkdownEditor(wrapper, this.mapOptions(this.options)); this.contentChange.emitObservable(fromCmEvent(this.mde.cm, 'changes')); this.cursorActivity.emitObservable(fromCmEvent(this.mde.cm, 'cursorActivity')); this.editorFocus.emitObservable(fromCmEvent(this.mde.cm, 'focus')); this.editorBlur.emitObservable(fromCmEvent(this.mde.cm, 'blur')); this.toolbarService.defineDefaultItems(this); this.statusbarService.defineDefaultItems(this.mde); // Necessary to apply `this.mde` instance to default toolbar items // as `ngOnChanges()` is executed before `ngOnInit()`. this.ngOnChanges({ data: new SimpleChange(undefined, this.data, true) }); this.mde.cm.clearHistory(); fromCmEvent(this.mde.cm, 'focus').subscribe(() => { this.focused = true; }); fromCmEvent(this.mde.cm, 'blur').subscribe(() => { if (!this.blockBlur) this.focused = false; }); fromCmEvent(this.mde.cm, 'cursorActivity').subscribe(() => { this.determineActiveButtons(); }); } /** * @inheritdoc */ ngOnChanges(changes) { if (this.mde) { if (this.showToolbar) { this.applyToolbarItems(); } if (this.showStatusbar) { this.applyStatusbarItems(); } this.applyDisabled(); this.mde.setOptions(this.mapOptions(this.options)); if (changes.data) { this.mde.setContent(changes.data.currentValue); } this.createTooltips(); this.determineActiveButtons(); this.setCodeMirrorClasses(); this.applyMaterialStyle(); } } /** * @inheritdoc */ ngOnDestroy() { this.shortcutResetter.next(); this.shortcutResetter.complete(); } /** * Toggles the full-size preview. */ togglePreview() { this.showPreview = !this.showPreview; this.showSideBySidePreview = false; if (this.showPreview) { // Necessary to wait until Angular change detector has finished setTimeout(() => { var _a; return (_a = this.hostElement.nativeElement.querySelector('.ngx-markdown-editor-wrapper')) === null || _a === void 0 ? void 0 : _a.focus(); }, 100); } else { // Necessary to wait until Angular change detector has finished setTimeout(() => this.mde.focus(), 100); } } /** * Toggles the side-by-side preview. */ toggleSideBySidePreview() { this.showSideBySidePreview = !this.showSideBySidePreview; this.showPreview = false; // Timeout necessary until Angular change detector has finished setTimeout(() => this.mde.focus(), 100); } /** * Triggered when a toolbar button is clicked. * * _Not intended to be used outside the component. Only made public for access inside template._ */ onButtonClick(item) { item.action(); this.mde.focus(); this.determineActiveButtons(); } /** * Resolves the shortcut for the specified item and appends it to the item's tooltip text, * if `shortcutsInTooltips` is enabled. * * _Not intended to be used outside the component. Only made public for access inside template._ */ createTooltip(item) { let shortcut = this.mde.getShortcuts()[item.name] || item.shortcut; if (item.name === 'undo') shortcut = 'Ctrl-Z'; else if (item.name === 'redo') shortcut = 'Shift-Ctrl-Z'; if (/Mac/.test(navigator.platform)) shortcut = shortcut === null || shortcut === void 0 ? void 0 : shortcut.replace(/Ctrl/gi, 'Cmd'); const shortcutString = this.shortcutsInTooltips && shortcut ? ' (' + shortcut + ')' : ''; return item.tooltip + shortcutString; } /** * Replaces the checkbox dummies rendered inside the preview with actual checkboxes (also see constructor). * * _Not intended to be used outside the component. Only made public for access inside template._ */ replaceCheckboxDummies() { var _a; (_a = this.markdown) === null || _a === void 0 ? void 0 : _a.element.nativeElement.querySelectorAll('li').forEach((el) => el.childNodes.forEach((node) => { var _a, _b; if (node.nodeType === 3) { if (/^\[ \] /.test(node.nodeValue || '')) { const input = document.createElement('input'); input.setAttribute('type', 'checkbox'); input.setAttribute('disabled', ''); el.insertBefore(input, node); node.nodeValue = ((_a = node.nodeValue) === null || _a === void 0 ? void 0 : _a.replace(/^\[ \]/, '')) || null; } else if (/^\[x\] /.test(node.nodeValue || '')) { const input = document.createElement('input'); input.setAttribute('type', 'checkbox'); input.setAttribute('disabled', ''); input.setAttribute('checked', ''); el.insertBefore(input, node); node.nodeValue = ((_b = node.nodeValue) === null || _b === void 0 ? void 0 : _b.replace(/^\[x\]/, '')) || null; } } })); } /** * Maps `NgxMdeOptions` to `MarkdownEditorOptions`. */ mapOptions(options) { if (!options) { return undefined; } const getMarkdownGuideUrl = (url) => { if (!url) return undefined; if (typeof url === 'string') { return url; } else { return url[this.language] || url.default; } }; const markupTheme = options.markupThemes || []; let editorThemes = options.editorThemes || []; if (this.materialStyle) { editorThemes = editorThemes ? editorThemes.concat('mde-material') : ['mde-material']; } else { if (editorThemes) { const index = editorThemes.findIndex((t) => t === 'mde-material'); if (index > -1) editorThemes.splice(index, 1); } } const shortcuts = {}; for (const actionName in DEFAULT_OPTIONS.shortcuts) { if (options.shortcuts) { shortcuts[actionName] = options.shortcuts[actionName]; } } return Object.assign(Object.assign({}, options), { shortcuts, disabled: this.disabled, themes: editorThemes.concat(markupTheme), markdownGuideUrl: getMarkdownGuideUrl(options.markdownGuideUrl) }); } /** * Applies the custom toolbar or the default toolbar as fallback. */ applyToolbarItems() { let items; if (this.toolbar.length) { items = this.toolbar; } else { items = this.toolbarService.DEFAULT_TOOLBAR; } this.normalizedToolbarItems = []; for (const toolbarItem of items) { const item = this.getNormalizedItem(toolbarItem); if (!item) { console.warn(`No default item defined for name "${toolbarItem}"`); continue; } this.addSvgIcon(item); this.normalizedToolbarItems.push(item); } this.applyShortcuts(this.normalizedToolbarItems); } /** * Returns a complete item for all combinations of how a toolbar item can be specified and * resolves the current value of internationalized properties. Only returns `undefined` for * items specified by name and no such item can be found. * * In detail, item normalization means (in addition to i18n resolution): * - For built-in items specified by name string, resolves the default item. * - For built-in items specified partly, completes the object with default values for the missing properties. * - For custom items specified partly, completes the object with empty values for the missing properties. * - For custom items specified fully, returns as is. * - For unknown items specified by name string, returns `undefined`. */ getNormalizedItem(toolbarItem) { const getTooltip = (tooltip) => { if (typeof tooltip === 'string') { return tooltip; } else { return tooltip[this.language] || tooltip.default; } }; const getIcon = (icon) => { if ('format' in icon) { return icon; } else { return icon[this.language] || icon.default; } }; if (typeof toolbarItem === 'string') { return this.toolbarService.getDefaultItem(toolbarItem); } else { let defaultItem = this.toolbarService.getDefaultItem(toolbarItem.name); if (!defaultItem) { defaultItem = { name: '', action: () => { }, tooltip: '', icon: { format: 'material', iconName: '' }, disableOnPreview: false, }; } return { name: toolbarItem.name, action: toolbarItem.action || defaultItem.action, shortcut: toolbarItem.shortcut || defaultItem.shortcut, isActive: toolbarItem.isActive || defaultItem.isActive, tooltip: (toolbarItem.tooltip && getTooltip(toolbarItem.tooltip)) || defaultItem.tooltip, icon: (toolbarItem.icon && getIcon(toolbarItem.icon)) || defaultItem.icon, disableOnPreview: toolbarItem.disableOnPreview || (defaultItem === null || defaultItem === void 0 ? void 0 : defaultItem.disableOnPreview), }; } } /** * Creates tooltips for all configured toolbar items and stores them in `this.toolbarItemTooltips`. */ createTooltips() { this.toolbarItemTooltips = new Array(this.normalizedToolbarItems.length); for (let i = 0; i < this.normalizedToolbarItems.length; i++) { const item = this.normalizedToolbarItems[i]; this.toolbarItemTooltips[i] = this.showTooltips ? this.createTooltip(item) : ''; } } /** * Applies custom shortcuts. * * For items, whose actions originate in _Markdown Editor Core_, `options.shortcuts` is * modified. For items that are specific to _Ngx Markdown Editor_ keybindings are applied to * the `<ngx-markdown-editor>` element. */ applyShortcuts(items) { var _a, _b, _c, _d; if (this.options.shortcutsEnabled === 'none') { return; } const applySetHeadingLevelShortcut = (shortcut) => { const s = shortcut.replace(/(\w)-/gi, '$1.').replace(/Ctrl/gi, 'Control').replace(/Cmd/gi, 'Meta'); return this.hotkeys .addKeybinding(this.hostElement.nativeElement, s) .pipe(takeUntil(this.shortcutResetter)) .subscribe(() => { this.blockBlur = true; this.setHeadingLevelDropdown.open(); this.setHeadingLevelDropdown.focus(); }); }; const applyShortcut = (shortcut, action) => { const s = shortcut.replace(/(\w)-/gi, '$1.').replace(/Ctrl/gi, 'Control').replace(/Cmd/gi, 'Meta'); return this.hotkeys .addKeybinding(this.hostElement.nativeElement, s) .pipe(takeUntil(this.shortcutResetter)) .subscribe(() => { action(); this.determineActiveButtons(); }); }; this.shortcutResetter.next(); const shortcuts = {}; const appliedNgxMdeShortcuts = {}; if (this.options.shortcutsEnabled !== 'customOnly') { const previewItem = this.toolbarService.getDefaultItem('togglePreview'); if (previewItem === null || previewItem === void 0 ? void 0 : previewItem.shortcut) { const subscription = applyShortcut(previewItem.shortcut, previewItem.action); appliedNgxMdeShortcuts[previewItem.name] = subscription; } const sideBySidePreviewItem = this.toolbarService.getDefaultItem('toggleSideBySidePreview'); if (sideBySidePreviewItem === null || sideBySidePreviewItem === void 0 ? void 0 : sideBySidePreviewItem.shortcut) { const subscription = applyShortcut(sideBySidePreviewItem.shortcut, sideBySidePreviewItem.action); appliedNgxMdeShortcuts[sideBySidePreviewItem.name] = subscription; } } for (const item of items) { if (item.name === 'setHeadingLevel' && item.shortcut) { const subscription = applySetHeadingLevelShortcut(item.shortcut); appliedNgxMdeShortcuts[item.name] = subscription; } else if (item.name in DEFAULT_OPTIONS.shortcuts) { shortcuts[item.name] = item.shortcut; } else if (item.shortcut) { (_a = appliedNgxMdeShortcuts[item.name]) === null || _a === void 0 ? void 0 : _a.unsubscribe(); const subscription = applyShortcut(item.shortcut, item.action); appliedNgxMdeShortcuts[item.name] = subscription; } } for (const actionName in this.options.shortcuts) { if (this.options.shortcuts[actionName]) { const shortcut = this.options.shortcuts[actionName]; if (actionName === 'setHeadingLevel') { const item = items.find((i) => i.name === actionName); if (item) { (_b = appliedNgxMdeShortcuts[actionName]) === null || _b === void 0 ? void 0 : _b.unsubscribe(); applySetHeadingLevelShortcut(shortcut); item.shortcut = shortcut; } } else if (actionName in DEFAULT_OPTIONS.shortcuts) { shortcuts[actionName] = shortcut; } else { const item = items.find((i) => i.name === actionName); const defaultItem = this.toolbarService.getDefaultItem(actionName); if (item) { (_c = appliedNgxMdeShortcuts[actionName]) === null || _c === void 0 ? void 0 : _c.unsubscribe(); applyShortcut(shortcut, item.action); item.shortcut = shortcut; } else if (defaultItem) { (_d = appliedNgxMdeShortcuts[actionName]) === null || _d === void 0 ? void 0 : _d.unsubscribe(); applyShortcut(shortcut, defaultItem.action); } } } } this.options.shortcuts = shortcuts; } /** * Adds the SVG specified inside `item.icon` to the injected `MatIconRegistry` instance. */ addSvgIcon(item) { switch (item.icon.format) { case 'svgString': this.iconRegistry.addSvgIconLiteral(item.icon.iconName, this.domSanitizer.bypassSecurityTrustHtml(item.icon.svgHtmlString)); break; case 'svgFile': this.iconRegistry.addSvgIcon(item.icon.iconName, this.domSanitizer.bypassSecurityTrustResourceUrl(item.icon.runtimePath)); break; } } /** * Applies the custom statusbar or the default statusbar as fallback. */ applyStatusbarItems() { let items; if (this.statusbar.length) { items = this.statusbar; } else { items = this.statusbarService.DEFAULT_STATUSBAR; } this.normalizedStatusbarItems = []; for (const toolbarItem of items) { const item = this.getNormalizedStatusbarItem(toolbarItem); if (!item) { console.warn(`No default item defined for name "${toolbarItem}"`); continue; } this.normalizedStatusbarItems.push(item); } } /** * Returns a complete item for all combinations of how a status item can be specified and * resolves the current value of internationalized properties. Only returns `undefined` for * items specified by name and no such item can be found. * * In detail, item normalization means (in addition to i18n resolution): * - For built-in items specified by name string, resolves the default item. * - For custom items, returns as is. * - For unknown items specified by name string, returns `undefined`. */ getNormalizedStatusbarItem(statusbarItem) { const getValue = (value) => { if (value instanceof Observable) { return value; } else { return value[this.language] || value.default; } }; if (typeof statusbarItem === 'string') { return this.statusbarService.getDefaultItem(statusbarItem); } else { return { name: statusbarItem.name, value: getValue(statusbarItem.value), }; } } /** * Executes the `item.isActive()` function for all toolbar items and saves the state in `activeItems`. */ determineActiveButtons() { this.activeToolbarItems = new Array(this.normalizedToolbarItems.length); for (let i = 0; i < this.normalizedToolbarItems.length; i++) { const item = this.normalizedToolbarItems[i]; if (item.isActive) { this.activeToolbarItems[i] = item.isActive(); } else { this.activeToolbarItems[i] = false; } } } /** * Applies the disabled state. If disabled, the full-size preview activated. */ applyDisabled() { if (this.disabled) { this.showPreview = true; this.showSideBySidePreview = false; this.shortcutResetter.next(); } else { this.showPreview = false; this.showSideBySidePreview = false; } } /** * Applies the material theme to both the editor and the preview. Particularly adds / removes * an underline element known from Angular Material `mat-input` form fields. */ applyMaterialStyle() { const codemirror = this.hostElement.nativeElement.querySelector('.CodeMirror'); if (codemirror) { const underline = codemirror.querySelector('.underline'); if (this.materialStyle) { if (!underline) { const newUnderline = document.createElement('div'); newUnderline.setAttribute('class', 'underline'); const newRipple = document.createElement('div'); newRipple.setAttribute('class', 'ripple'); newUnderline.append(newRipple); codemirror.append(newUnderline); } } else { underline === null || underline === void 0 ? void 0 : underline.remove(); } } const preview = this.hostElement.nativeElement.querySelector('.ngx-markdown-editor-preview'); if (preview) { const underline = preview.querySelector('.underline'); if (this.materialStyle) { if (!underline) { const newUnderline = document.createElement('div'); newUnderline.setAttribute('class', 'underline'); preview.append(newUnderline); } } else { underline === null || underline === void 0 ? void 0 : underline.remove(); } } } /** * Adds and removes classes to/from the CodeMirror element according to configuration. */ setCodeMirrorClasses() { const codemirror = this.hostElement.nativeElement.querySelector('.CodeMirror'); if (this.options.lineNumbers) { codemirror === null || codemirror === void 0 ? void 0 : codemirror.classList.add('CodeMirror-lineNumbers'); } else { codemirror === null || codemirror === void 0 ? void 0 : codemirror.classList.remove('CodeMirror-lineNumbers'); } } } MarkdownEditorComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-markdown-editor', template: "<div *ngIf=\"label\" class=\"ngx-markdown-editor-label\">\n {{ label }} <span class=\"required-marker\">{{ required ? '*' : '' }}</span>\n</div>\n<div *ngIf=\"showToolbar\" class=\"ngx-markdown-editor-toolbar\">\n <ng-container *ngFor=\"let item of normalizedToolbarItems; index as i\" [ngSwitch]=\"item.name\">\n <ng-template [ngSwitchCase]=\"'|'\">\n <div class=\"mde-toolbar-separator\"></div>\n </ng-template>\n\n <ng-template [ngSwitchCase]=\"'setHeadingLevel'\">\n <mat-form-field floatLabel=\"never\" [matTooltip]=\"toolbarItemTooltips[i]\">\n <mat-label>\n <mat-icon *ngIf=\"item.icon.format === 'material'\">\n {{ item.icon.iconName }}\n </mat-icon>\n <mat-icon *ngIf=\"item.icon.format !== 'material'\" [svgIcon]=\"item.icon.iconName\"> </mat-icon>\n </mat-label>\n <div (mousedown)=\"blockBlur = true\">\n <mat-select\n [value]=\"activeToolbarItems[i]\"\n [disabled]=\"disabled || (showPreview && item.disableOnPreview)\"\n (selectionChange)=\"item.action($event.value)\"\n (closed)=\"mde.cm.focus(); blockBlur = false\"\n #setHeadingLevel\n >\n <mat-option value=\"0\">---</mat-option>\n <mat-option *ngFor=\"let j of [1, 2, 3, 4, 5, 6]\" [value]=\"j\">H{{ j }}</mat-option>\n </mat-select>\n </div>\n </mat-form-field>\n </ng-template>\n\n <ng-template ngSwitchDefault>\n <button\n mat-button\n (mousedown)=\"blockBlur = true\"\n (mouseup)=\"blockBlur = false\"\n (click)=\"onButtonClick(item)\"\n [matTooltip]=\"toolbarItemTooltips[i]\"\n [class.active]=\"activeToolbarItems[i]\"\n [disabled]=\"disabled || (showPreview && item.disableOnPreview)\"\n >\n <mat-icon *ngIf=\"item.icon.format === 'material'\">\n {{ item.icon.iconName }}\n </mat-icon>\n <mat-icon *ngIf=\"item.icon.format !== 'material'\" [svgIcon]=\"item.icon.iconName\"> </mat-icon>\n </button>\n </ng-template>\n </ng-container>\n</div>\n<div class=\"ngx-markdown-editor-wrapper\" tabindex=\"-1\">\n <div [ngStyle]=\"{ display: showPreview ? 'none' : '' }\" class=\"ngx-markdown-editor-text-editor\"></div>\n <div [ngStyle]=\"{ display: showPreview || showSideBySidePreview ? '' : 'none' }\" class=\"ngx-markdown-editor-preview\">\n <div class=\"preview-wrapper\">\n <markdown [data]=\"mde.getContent()\" (ready)=\"replaceCheckboxDummies()\" #markdown></markdown>\n </div>\n </div>\n</div>\n<div *ngIf=\"showStatusbar\" class=\"ngx-markdown-editor-statusbar\">\n <ng-container *ngFor=\"let item of normalizedStatusbarItems\" [ngSwitch]=\"item.name\">\n <div *ngSwitchCase=\"'|'\" class=\"mde-statusbar-separator\"></div>\n <div *ngSwitchDefault class=\"mde-statusbar-item\">{{ item.value | async }}</div>\n </ng-container>\n</div>\n", providers: [ { provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: ɵ0 }, ToolbarService, StatusbarService, ], encapsulation: ViewEncapsulation.None, styles: [".CodeMirror{color:#000;direction:ltr;font-family:monospace;height:300px}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:transparent}.CodeMirror-gutters{background-color:#f7f7f7;border-right:1px solid #ddd;white-space:nowrap}.CodeMirror-linenumber{color:#999;min-width:20px;padding:0 3px 0 5px;text-align:right;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{background:#7e7;border:0!important;width:auto}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,.5)}.cm-animate-fat-cursor,.cm-fat-cursor-mark{-webkit-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{background-color:#7e7;border:0;width:auto}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{bottom:0;left:0;overflow:hidden;position:absolute;right:0;top:-50px}.CodeMirror-ruler{border-left:1px solid #ccc;bottom:0;position:absolute;top:0}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{background:#fff;overflow:hidden;position:relative}.CodeMirror-scroll{height:100%;margin-bottom:-50px;margin-right:-50px;outline:none;overflow:scroll!important;padding-bottom:50px;position:relative}.CodeMirror-sizer{border-right:50px solid transparent;position:relative}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{display:none;outline:none;position:absolute;z-index:6}.CodeMirror-vscrollbar{overflow-x:hidden;overflow-y:scroll;right:0;top:0}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-x:scroll;overflow-y:hidden}.CodeMirror-scrollbar-filler{bottom:0;right:0}.CodeMirror-gutter-filler{bottom:0;left:0}.CodeMirror-gutters{left:0;min-height:100%;position:absolute;top:0;z-index:3}.CodeMirror-gutter{display:inline-block;height:100%;margin-bottom:-50px;vertical-align:top;white-space:normal}.CodeMirror-gutter-wrapper{background:none!important;border:none!important;position:absolute;z-index:4}.CodeMirror-gutter-background{bottom:0;position:absolute;top:0;z-index:4}.CodeMirror-gutter-elt{cursor:default;position:absolute;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-webkit-tap-highlight-color:transparent;background:transparent;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-variant-ligatures:contextual;line-height:inherit;margin:0;overflow:visible;position:relative;white-space:pre;word-wrap:normal;z-index:2}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{white-space:pre-wrap;word-break:normal;word-wrap:break-word}.CodeMirror-linebackground{bottom:0;left:0;position:absolute;right:0;top:0;z-index:0}.CodeMirror-linewidget{padding:.1px;position:relative;z-index:2}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{box-sizing:content-box}.CodeMirror-measure{height:0;overflow:hidden;position:absolute;visibility:hidden;width:100%}.CodeMirror-cursor{pointer-events:none;position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{position:relative;visibility:hidden;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:\"\"}span.CodeMirror-selectedtext{background:none}ngx-markdown-editor{box-sizing:border-box;display:flex;flex-direction:column}.ngx-markdown-editor-toolbar .mat-button{border:1px solid transparent;line-height:28px;margin:3px 2px;min-width:30px;padding:0}.ngx-markdown-editor-toolbar .mat-button.active{background-color:rgba(0,0,0,.05);border-color:#a9a9a9}.ngx-markdown-editor-toolbar .mat-form-field{font-size:12px;margin:-10px 3px -15px;min-width:3em;text-align:center;width:3em}.ngx-markdown-editor-toolbar .mat-form-field:first-child{margin-left:10px}.ngx-markdown-editor-toolbar .mat-form-field.mat-form-field-disabled .mat-icon{color:rgba(0,0,0,.26)}.ngx-markdown-editor-toolbar .mde-toolbar-separator{border-left:1px solid #a9a9a9;display:inline-block;height:24px;vertical-align:middle}.ngx-markdown-editor-toolbar .mat-button .mat-icon{font-size:20px;height:20px;width:20px}.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=column],.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=file_code],.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=upload]{height:16px}.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=format_heading]{height:13px;margin-top:-2px}.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=format_heading_decrease],.ngx-markdown-editor-toolbar .mat-button .mat-icon[data-mat-icon-name=format_heading_increase]{height:18px;margin-top:-2px}.ngx-markdown-editor-toolbar .mat-form-field .mat-icon[data-mat-icon-name=format_heading]{height:13px}.ngx-markdown-editor-toolbar svg{vertical-align:top}.ngx-markdown-editor-wrapper{box-sizing:border-box;display:flex;flex:1 1 100%;flex-direction:row;overflow:hidden}.ngx-markdown-editor-wrapper:focus{outline:none}.ngx-markdown-editor-text-editor{box-sizing:border-box;display:flex;flex:1 1 100%;flex-direction:column}.ngx-markdown-editor-text-editor .CodeMirror{height:100%;word-wrap:break-word}.ngx-markdown-editor-text-editor .CodeMirror .CodeMirror-placeholder{color:rgba(0,0,0,.6)}.ngx-markdown-editor-preview{background-color:#f0f0f0;box-sizing:border-box;flex:1 1 100%;overflow-y:auto;word-wrap:break-word}.ngx-markdown-editor-preview>.preview-wrapper{height:100%;overflow:auto;padding:0 10px}.ngx-markdown-editor-statusbar{color:rgba(0,0,0,.6);font-size:.75em;margin-top:5px;text-align:right}.ngx-markdown-editor-statusbar *{display:inline-block;margin:0 7px}.ngx-markdown-editor-statusbar .mde-statusbar-separator{border-left:1px solid #a9a9a9;height:14px;margin:0 4px;vertical-align:middle}ngx-markdown-editor.default .ngx-markdown-editor-label{font-weight:700;padding-left:8px;text-decoration:underline}ngx-markdown-editor.default .ngx-markdown-editor-wrapper>*{border:1px solid #ddd}ngx-markdown-editor.default .ngx-markdown-editor-wrapper :first-child[style=\"display: none;\"]+*,ngx-markdown-editor.default .ngx-markdown-editor-wrapper>:first-child{border-bottom-left-radius:5px;border-top-left-radius:5px}ngx-markdown-editor.default .ngx-markdown-editor-wrapper>:last-child{border-bottom-right-radius:5px;border-top-right-radius:5px}ngx-markdown-editor.default .ngx-markdown-editor-wrapper .CodeMirror{border-radius:5px}ngx-markdown-editor.default .ngx-markdown-editor-wrapper .CodeMirror .CodeMirror-scroll{padding-left:8px;padding-top:8px}ngx-markdown-editor.default .ngx-markdown-editor-wrapper .CodeMirror.CodeMirror-lineNumbers .CodeMirror-scroll{padding-left:0}.CodeMirror.cm-s-preview-like-markup{font-family:Roboto,Helvetica Neue,sans-serif}.CodeMirror.cm-s-preview-like-markup .cm-header{color:#000;font-weight:700}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-1{font-size:2em}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-2{font-size:1.5em}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-3{font-size:1.17em}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-4{font-size:1em}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-5{font-size:.83em}.CodeMirror.cm-s-preview-like-markup .cm-header.cm-header-6{font-size:.67em}.CodeMirror.cm-s-preview-like-markup .cm-code{font-family:monospace}.CodeMirror.cm-s-preview-like-markup .cm-link-text{color:#0366d6;text-decoration:underline}.CodeMirror.cm-s-preview-like-markup .cm-link-href{color:#afafaf;font-size:.8em;text-decoration:none}"] },] } ]; MarkdownEditorComponent.ctorParameters = () => [ { type: MatIconRegistry }, { type: DomSanitizer }, { type: Keybindings }, { type: ElementRef }, { type: MarkdownService }, { type: ToolbarService, decorators: [{ type: Host }] }, { type: StatusbarService, decorators: [{ type: Host }] } ]; MarkdownEditorComponent.propDecorators = { data: [{ type: Input }], options: [{ type: Input }], toolbar: [{ type: Input }], statusbar: [{ type: Input }], language: [{ type: Input }], label: [{ type: Input }], required: [{ type: Input }], materialStyle: [{ type: Input }], disabled: [{ type: Input }], showToolbar: [{ type: Input }], showStatusbar: [{ type: Input }], showTooltips: [{ type: Input }], shortcutsInTooltips: [{ type: Input }], contentChange: [{ type: Output }], cursorActivity: [{ type: Output }], editorFocus: [{ type: Output }], editorBlur: [{ type: Output }], setHeadingLevelDropdown: [{ type: ViewChild, args: ['setHeadingLevel',] }], markdown: [{ type: ViewChild, args: ['markdown',] }], disabledStyle: [{ type: HostBinding, args: ['class.disabled',] }], default: [{ type: HostBinding, args: ['class.default',] }], material: [{ type: HostBinding, args: ['class.material',] }], class: [{ type: HostBinding, args: ['class',] }], appearanceStandard: [{ type: HostBinding, args: ['class.appearance-standard',] }], appearanceFill: [{ type: HostBinding, args: ['class.appearance-fill',] }], appearanceLegacy: [{ type: HostBinding, args: ['class.appearance-legacy',] }], focusedStyle: [{ type: HostBinding, args: ['class.focused',] }] }; export { ɵ0 }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFya2Rvd24tZWRpdG9yLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1tYXJrZG93bi1lZGl0b3Ivc3JjL2xpYi9jb21wb25lbnQvbWFya2Rvd24tZWRpdG9yLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULFVBQVUsRUFDVixJQUFJLEVBQ0osV0FBVyxFQUNYLEtBQUssRUFJTCxNQUFNLEVBQ04sWUFBWSxFQUVaLFNBQVMsRUFDVCxpQkFBaUIsR0FDbEIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRXpELE9BQU8sRUFBNEIsMkJBQTJCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNsRyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDekQsT0FBTyxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQXlCLE1BQU0sNkJBQTZCLENBQUM7QUFFckcsT0FBTyxFQUFxQixlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDbEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQ3pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRTdELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBS2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVwRCxNQUFNLDZCQUE2QixHQUE2QjtJQUM5RCxTQUFTLEVBQUUs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