@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
JavaScript
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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