| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- <link rel="stylesheet" href="~/lib/ckeditor/browser/ckeditor5.css" asp-append-version="true" />
- <script type="module">
- import {
- ClassicEditor, Essentials, Paragraph, Bold, Italic, Underline, Strikethrough, Code, Subscript, Superscript, RemoveFormat, List, TodoList,
- Indent, Heading, Font, Highlight, Alignment, Link, Image, ImageToolbar, ImageCaption, ImageStyle, ImageResize, ImageUpload, MediaEmbed, CodeBlock,
- HtmlEmbed, SpecialCharacters, HorizontalLine, PageBreak, SourceEditing, FindAndReplace, SelectAll, BlockQuote, Table, TableToolbar, TextPartLanguage
- } from "/lib/ckeditor/browser/ckeditor5.js";
- class CustomImageAttributesPlugin {
- constructor(editor) {
- this.editor = editor;
- }
- init() {
- const editor = this.editor;
- const schema = editor.model.schema;
- // data-* 속성을 추가
- const attributes = ['data-name', 'data-extension', 'data-size', 'data-type', 'data-width', 'data-height'];
- schema.extend('imageBlock', {allowAttributes: attributes});
- schema.extend('imageInline', {allowAttributes: attributes});
- editor.plugins.get('ImageUploadEditing').on('uploadComplete', (evt, { data, imageElement }) => {
- editor.model.change((writer) => {
- writer.setAttribute('data-name', data.name, imageElement);
- writer.setAttribute('data-extension', data.extension, imageElement);
- writer.setAttribute('data-size', data.size, imageElement);
- writer.setAttribute('data-type', data.type, imageElement);
- });
- });
- attributes.forEach(attr => {
- editor.conversion.for('upcast').attributeToAttribute({ model: attr, view: attr });
- });
- attributes.forEach(attr => {
- editor.conversion.for('downcast').attributeToAttribute({ model: attr, view: attr });
- editor.conversion.for('editingDowncast').attributeToAttribute({ model: attr, view: attr });
- });
- const updateDataAttribute = (evt, data, conversionApi) => {
- const viewWriter = conversionApi.writer;
- const viewElement = conversionApi.mapper.toViewElement(data.item);
- if (viewElement) {
- const imgElement = viewElement.getChild(0);
- if (imgElement && imgElement.is('element', 'img')) {
- viewWriter.setAttribute(data.attributeKey, data.attributeNewValue, imgElement);
- }
- }
- };
- attributes.forEach(attribute => {
- editor.conversion.for('downcast').add(dispatcher => {
- dispatcher.on(`attribute:${attribute}:imageBlock`, (evt, data, conversionApi) => {
- updateDataAttribute(evt, data, conversionApi);
- });
- });
- editor.conversion.for('editingDowncast').add(dispatcher => {
- dispatcher.on(`attribute:${attribute}:imageInline`, (evt, data, conversionApi) => {
- updateDataAttribute(evt, data, conversionApi);
- });
- });
- });
- }
- }
- class CustomUploadAdapter {
- constructor(loader) {
- this.loader = loader;
- }
- upload() {
- return this.loader.file.then(file => {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => {
- resolve({
- default: reader.result,
- name: file.name,
- extension: file.name.split('.').pop(),
- size: file.size,
- type: file.type
- });
- };
- reader.onerror = error => reject(error);
- reader.readAsDataURL(file);
- });
- });
- }
- abort() {
- console.log('업로드가 취소되었습니다.');
- }
- }
- // CKEditor 저장, 전역 변수로 해서 다른 곳에서 접근할 수 있도록 함
- window.editorInstances = window.editorInstances || new Map();
- // CKEditor 초기화
- window.initEditor = async function(domID, config = {})
- {
- const el = document.getElementById(domID);
- if (!el || window.editorInstances.has(domID)) {
- return;
- }
- return ClassicEditor
- .create(el, {
- licenseKey: 'GPL',
- extraPlugins: [CustomUploadAdapter, CustomImageAttributesPlugin],
- plugins: [
- Essentials, Paragraph, Bold, Italic, Underline, Strikethrough, Code,
- Subscript, Superscript, RemoveFormat, List, TodoList, Indent, Heading,
- Font, Highlight, Alignment, Link, Image, ImageToolbar, ImageCaption,
- ImageStyle, ImageResize, ImageUpload, MediaEmbed, CodeBlock, HtmlEmbed,
- SpecialCharacters, HorizontalLine, PageBreak, SourceEditing,
- FindAndReplace, SelectAll, BlockQuote, Table, TableToolbar, TextPartLanguage
- ],
- toolbar: {
- items: [
- 'findAndReplace', 'selectAll', '|',
- 'heading', '|',
- 'bold', 'italic', 'strikethrough', 'underline', 'code', 'subscript', 'superscript', 'removeFormat', '|',
- 'bulletedList', 'numberedList', 'todoList', '|',
- 'outdent', 'indent', '|',
- 'undo', 'redo', '|',
- 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'highlight', '|',
- 'alignment', '|',
- 'link', 'insertImage', 'blockQuote', 'insertTable', 'mediaEmbed', 'codeBlock', 'htmlEmbed', '|',
- 'specialCharacters', 'horizontalLine', 'pageBreak', '|',
- 'textPartLanguage', '|',
- 'sourceEditing'
- ],
- shouldNotGroupWhenFull: true
- },
- list: {
- properties: {
- styles: true,
- startIndex: true,
- reversed: true
- }
- },
- heading: {
- options: [
- { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
- { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
- { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
- { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
- { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
- { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
- { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
- ]
- },
- placeholder: '내용을 입력해주세요.',
- fontFamily: {
- options: [
- 'default',
- 'Arial, Helvetica, sans-serif',
- 'Courier New, Courier, monospace',
- 'Georgia, serif',
- 'Lucida Sans Unicode, Lucida Grande, sans-serif',
- 'Tahoma, Geneva, sans-serif',
- 'Times New Roman, Times, serif',
- 'Trebuchet MS, Helvetica, sans-serif',
- 'Verdana, Geneva, sans-serif'
- ],
- supportAllValues: true
- },
- fontSize: {
- options: [10, 12, 14, 'default', 18, 20, 22],
- supportAllValues: true
- },
- htmlSupport: {
- allow: [
- {
- name: /.*/,
- attributes: true,
- classes: true,
- styles: true
- }
- ]
- },
- htmlEmbed: {
- showPreviews: true
- },
- link: {
- decorators: {
- addTargetToExternalLinks: true,
- defaultProtocol: 'https://',
- toggleDownloadable: {
- mode: 'manual',
- label: 'Downloadable',
- attributes: {
- download: 'file'
- }
- }
- }
- },
- image: {
- toolbar: [
- 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|',
- 'toggleImageCaption', 'imageTextAlternative'
- ]
- },
- table: {
- contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells']
- },
- simpleUpload: {
- uploadUrl: null
- },
- ...config
- })
- .then(editor => {
- editor.plugins.get('FileRepository').createUploadAdapter = loader => new CustomUploadAdapter(loader);
- // 에디터 저장
- window.editorInstances.set(domID, editor);
- })
- .catch(error => {
- console.error('Error initializing CKEditor:', error);
- });
- };
- // 에디터 제거
- window.destroyEditor = async function(domID) {
- const editor = window.editorInstances.get(domID);
- if (editor) {
- await editor.destroy();
- window.editorInstances.delete(domID);
- }
- };
- document.querySelectorAll('.ck-editor').forEach(e => {
- initEditor(e.id);
- });
- </script>
|