') {
html = null;
}
let modelValue = html;
const format = getFormat(this.format, this.config.format);
if (format === 'text') {
modelValue = quillEditor.getText();
} else if (format === 'object') {
modelValue = quillEditor.getContents();
} else if (format === 'json') {
try {
modelValue = JSON.stringify(quillEditor.getContents());
} catch (e) {
modelValue = quillEditor.getText();
}
}
return modelValue;
}
@Input() valueSetter = (quillEditor: any, value: any): any => {
const format = getFormat(this.format, this.config.format);
if (format === 'html') {
if (this.sanitize) {
value = this.domSanitizer.sanitize(SecurityContext.HTML, value);
}
value = value ? (value + '').replace(/\s\s/g, ' ') : '';
return quillEditor.clipboard.convert(value);
} else if (format === 'json') {
try {
return JSON.parse(value);
} catch (e) {
return [{ insert: value }];
}
}
return value;
}
constructor(
private elementRef: ElementRef,
private domSanitizer: DomSanitizer,
@Inject(DOCUMENT) private doc: any,
// tslint:disable-next-line:ban-types
@Inject(PLATFORM_ID) private platformId: Object,
private renderer: Renderer2,
private zone: NgZone,
@Inject(QUILL_CONFIG_TOKEN) private config: QuillConfig
) { }
// tslint:disable-next-line:no-empty
onModelChange(_modelValue?: any) { }
// tslint:disable-next-line:no-empty
onModelTouched() { }
ngAfterViewInit(): void {
this.editorCreate();
}
setToolbar(toolbar) {
console.log(toolbar);
}
private editorCreate() {
if (isPlatformServer(this.platformId)) {
return;
}
this.elementRef.nativeElement.insertAdjacentHTML(
this.customToolbarPosition === 'top' ? 'beforeend' : 'afterbegin',
this.preserveWhitespace ? '
' : '
'
);
this.editorElem = this.elementRef.nativeElement.querySelector('[html-editor-element]');
const toolbarElem = this.elementRef.nativeElement.querySelector('[html-editor-toolbar]');
const modules = this.modules || this.config.modules || defaultModules;
if (modules.toolbar === undefined) {
modules.toolbar = defaultModules.toolbar;
}
if (this.customToolbar) {
if (this.toolbar && this.toolbar.length) {
modules.toolbar = Convert2HtmlEditorToolbars('concise', this.toolbar);
} else {
modules.toolbar = this.buildToolbar(toolbarOptions);
}
}
if (modules.tooltip === undefined) {
modules.tooltip = defaultModules.tooltip;
}
if (modules.imageDrop === undefined) {
modules.imageDrop = defaultModules.imageDrop;
}
if (modules.imageResize === undefined) {
modules.imageResize = defaultModules.imageResize;
}
let placeholder = this.placeholder !== undefined ? this.placeholder : this.config.placeholder;
if (placeholder === undefined) {
placeholder = '';
}
if (toolbarElem) {
// tslint:disable-next-line:no-string-literal
modules['toolbar'] = toolbarElem;
}
if (this.styles) {
Object.keys(this.styles).forEach((key: string) => {
this.renderer.setStyle(this.editorElem, key, this.styles[key]);
});
}
this.customOptions.forEach(customOption => {
const newCustomOption = Quill.import(customOption.import);
newCustomOption.whitelist = customOption.whitelist;
Quill.register(newCustomOption, true);
});
let bounds = this.bounds && this.bounds === 'self' ? this.editorElem : this.bounds;
if (!bounds) {
bounds = this.config.bounds ? this.config.bounds : this.doc.body;
}
let debug = this.debug;
if (!debug && debug !== false && this.config.debug) {
debug = this.config.debug;
}
let readOnly = this.readonly;
if (!readOnly && this.readonly !== false) {
readOnly = this.config.readOnly !== undefined ? this.config.readOnly : false;
}
let scrollingContainer = this.scrollingContainer;
if (!scrollingContainer && this.scrollingContainer !== null) {
scrollingContainer =
this.config.scrollingContainer === null || this.config.scrollingContainer
? this.config.scrollingContainer
: null;
}
let formats = this.formats;
if (!formats && formats === undefined) {
formats = this.config.formats || this.config.formats === null ? this.config.formats : undefined;
}
// 初始化quill
this.quillEditor = new Quill(this.editorElem, {
bounds,
debug,
formats,
modules,
placeholder,
readOnly,
scrollingContainer,
strict: this.strict,
theme: this.theme || (this.config.theme ? this.config.theme : 'snow')
});
if (this.content) {
const format = getFormat(this.format, this.config.format);
if (format === 'object') {
this.quillEditor.setContents(this.content, 'silent');
} else if (format === 'text') {
this.quillEditor.setText(this.content, 'silent');
} else if (format === 'json') {
try {
this.quillEditor.setContents(JSON.parse(this.content), 'silent');
} catch (e) {
this.quillEditor.setText(this.content, 'silent');
}
} else {
if (this.sanitize) {
this.content = this.domSanitizer.sanitize(SecurityContext.HTML, this.content);
}
const contents = this.quillEditor.clipboard.convert(this.content);
this.quillEditor.setContents(contents, 'silent');
}
this.quillEditor.history.clear();
}
// initialize _disabled status based on this._disabled as default value
// this.setDisabledState(this.disabled);
this.setStatusEditor(this.readonly, this.disabled);
this.onEditorCreated.emit(this.quillEditor);
// mark model as touched if editor lost focus
this.quillEditor.on('selection-change', this.selectionChangeHandler);
// update model if text changes
this.quillEditor.on('text-change', this.textChangeHandler);
}
random_id() {
return Math.random().toString(36).slice(2);
}
selectionChangeHandler = (range: Range | null, oldRange: Range | null, source: string) => {
this.zone.run(() => {
if (range === null) {
this.onBlur.emit({
editor: this.quillEditor,
source
});
} else if (oldRange === null) {
this.onFocus.emit({
editor: this.quillEditor,
source
});
}
this.onSelectionChanged.emit({
editor: this.quillEditor,
oldRange,
range,
source
});
if (!range && this.onModelTouched) {
this.onModelTouched();
}
});
}
textChangeHandler = (delta: any, oldDelta: any, source: string): void => {
// only emit changes emitted by user interactions
const text = this.quillEditor.getText();
const content = this.quillEditor.getContents();
// tslint:disable-next-line: no-non-null-assertion
let html: string | null = this.editorElem!.querySelector('.ql-editor')!.innerHTML;
if (html === '
' || html === '
') {
html = null;
}
this.zone.run(() => {
const trackChanges = this.trackChanges || this.config.trackChanges;
if ((source === Quill.sources.USER || (trackChanges && trackChanges === 'all')) && this.onModelChange) {
this.onModelChange(this.valueGetter(this.quillEditor, this.editorElem!));
}
this.onContentChanged.emit({
content,
delta,
editor: this.quillEditor,
html,
oldDelta,
source,
text
});
});
}
endEdit() {
this.onModelTouched();
}
buildToolbar(options: any) {
const rtn = [];
Object.keys(options).forEach(e => {
if (this[e] === true) {
rtn.push(options[e]);
} else if (this[e]) {
rtn.push(this[e]);
}
});
return rtn;
}
ngOnDestroy() {
if (this.quillEditor) {
this.quillEditor.off('selection-change', this.selectionChangeHandler);
this.quillEditor.off('text-change', this.textChangeHandler);
}
}
ngOnChanges(changes: SimpleChanges): void {
if (!this.quillEditor) {
return;
}
if (changes['disabled']) {
this.setStatusEditor(false, this.disabled);
}
// tslint:disable:no-string-literal
if (changes['readonly']) {
this.quillEditor.enable(!changes['readonly'].currentValue);
this.disableToolbar(changes['readonly'].currentValue);
}
if (changes['placeholder']) {
this.quillEditor.root.dataset.placeholder = changes['placeholder'].currentValue;
}
if (changes['styles']) {
const currentStyling = changes['styles'].currentValue;
const previousStyling = changes['styles'].previousValue;
if (previousStyling) {
Object.keys(previousStyling).forEach((key: string) => {
this.renderer.removeStyle(this.editorElem, key);
});
}
if (currentStyling) {
Object.keys(currentStyling).forEach((key: string) => {
this.renderer.setStyle(this.editorElem, key, this.styles[key]);
});
}
}
// tslint:enable:no-string-literal
}
writeValue(currentValue: any) {
this.content = currentValue;
const format = getFormat(this.format, this.config.format);
if (this.quillEditor) {
if (currentValue) {
if (format === 'text') {
this.quillEditor.setText(currentValue);
} else {
this.quillEditor.setContents(this.valueSetter(this.quillEditor, this.content));
}
return;
}
this.quillEditor.setText('');
}
}
disableToolbar(readonly: boolean) {
const toolbarElem = this.elementRef.nativeElement.querySelector('.ql-toolbar');
if (toolbarElem) {
toolbarElem.style.display = readonly ? 'none' : 'block';
}
}
setStatusEditor(readonly: boolean = false, disable: boolean) {
// if (disable || readonly) {
// this.elementRef.nativeElement.style.border = 'none';
// }
this.disableToolbar(disable || readonly);
this.setDisabledState(disable);
}
setDisabledState(isDisabled: boolean = this._disabled): void {
// store initial value to set appropriate _disabled status after ViewInit
this._disabled = isDisabled;
if (this.quillEditor) {
if (isDisabled) {
this.quillEditor.disable();
this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'disabled');
} else {
if (!this.readonly) {
this.quillEditor.enable();
}
this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
}
}
}
registerOnChange(fn: (modelValue: any) => void): void {
this.onModelChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onModelTouched = fn;
}
validate() {
return null;
// if (!this.quillEditor) {
// }
// const err: {
// minLengthError?: {
// given: number;
// minLength: number;
// };
// maxLengthError?: {
// given: number;
// maxLength: number;
// };
// requiredError?: { empty: boolean };
// } = {};
// let valid = true;
// const textLength = this.quillEditor.getText().trim().length;
// if (this.minLength && textLength && textLength < this.minLength) {
// err.minLengthError = {
// given: textLength,
// minLength: this.minLength
// };
// valid = false;
// }
// if (this.maxLength && textLength > this.maxLength) {
// err.maxLengthError = {
// given: textLength,
// maxLength: this.maxLength
// };
// valid = false;
// }
// if (this.required && !textLength) {
// err.requiredError = {
// empty: true
// };
// valid = false;
// }
// return valid ? null : err;
}
}