Defining menu items

Char count, Button groups, toolbar container

Iframe, fullPage and use CodeMirror (^5.0.0)

User Functions

Toolbar : Responsive and more buttons

1200px

SUNEDITOR.create('responsive_Toolbar', {
    templates: [
        {
            name: 'Template-1',
            html: '<p>HTML source1</p>'
        },
        {
            name: 'Template-2',
            html: '<p>HTML source2</p>'
        }
    ],
    codeMirror: CodeMirror,
    katex: katex,
    buttonList: [
        // default
        ['undo', 'redo'],
        [':p-More Paragraph-default.more_paragraph', 'font', 'fontSize', 'formatBlock', 'paragraphStyle', 'blockquote'],
        ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript'],
        ['fontColor', 'hiliteColor', 'textStyle'],
        ['removeFormat'],
        ['outdent', 'indent'],
        ['align', 'horizontalRule', 'list', 'lineHeight'],
        ['-right', ':i-More Misc-default.more_vertical', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'print', 'save', 'template'],
        ['-right', ':r-More Rich-default.more_plus', 'table', 'math', 'imageGallery'],
        ['-right', 'image', 'video', 'audio', 'link'],
        // (min-width: 992)
        ['%992', [
            ['undo', 'redo'],
            [':p-More Paragraph-default.more_paragraph', 'font', 'fontSize', 'formatBlock', 'paragraphStyle', 'blockquote'],
            ['bold', 'underline', 'italic', 'strike'],
            [':t-More Text-default.more_text', 'subscript', 'superscript', 'fontColor', 'hiliteColor', 'textStyle'],
            ['removeFormat'],
            ['outdent', 'indent'],
            ['align', 'horizontalRule', 'list', 'lineHeight'],
            ['-right', ':i-More Misc-default.more_vertical', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'print', 'save', 'template'],
            ['-right', ':r-More Rich-default.more_plus', 'table', 'link', 'image', 'video', 'audio', 'math', 'imageGallery']
        ]],
        // (min-width: 767)
        ['%767', [
            ['undo', 'redo'],
            [':p-More Paragraph-default.more_paragraph', 'font', 'fontSize', 'formatBlock', 'paragraphStyle', 'blockquote'],
            [':t-More Text-default.more_text', 'bold', 'underline', 'italic', 'strike', 'subscript', 'superscript', 'fontColor', 'hiliteColor', 'textStyle'],
            ['removeFormat'],
            ['outdent', 'indent'],
            [':e-More Line-default.more_horizontal', 'align', 'horizontalRule', 'list', 'lineHeight'],
            [':r-More Rich-default.more_plus', 'table', 'link', 'image', 'video', 'audio', 'math', 'imageGallery'],
            ['-right', ':i-More Misc-default.more_vertical', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'print', 'save', 'template']
        ]],
        // (min-width: 480)
        ['%480', [
            ['undo', 'redo'],
            [':p-More Paragraph-default.more_paragraph', 'font', 'fontSize', 'formatBlock', 'paragraphStyle', 'blockquote'],
            [':t-More Text-default.more_text', 'bold', 'underline', 'italic', 'strike', 'subscript', 'superscript', 'fontColor', 'hiliteColor', 'textStyle', 'removeFormat'],
            [':e-More Line-default.more_horizontal', 'outdent', 'indent', 'align', 'horizontalRule', 'list', 'lineHeight'],
            [':r-More Rich-default.more_plus', 'table', 'link', 'image', 'video', 'audio', 'math', 'imageGallery'],
            ['-right', ':i-More Misc-default.more_vertical', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'print', 'save', 'template']
        ]]
    ]
})

Image file resize

var editorimageResize = SUNEDITOR.create('imageResize', {
    buttonList: [
        ['undo', 'redo'],
        ['image']
    ],
})

/** UploadBefore event
If undefined is returned, it waits until "uploadHandler" is executed.
"uploadHandler" is an upload function with "core" and "info" bound. (plugin.upload.bind(core, info))
[upload files] : uploadHandler(files or [new File(...),])
[error]        : uploadHandler("Error message")
[Just finish]  : uploadHandler()
[directly register] : uploadHandler(response) // Same format as "imageUploadUrl" response
                    ex) {
                        // "errorMessage": "insert error message",
                       "result": [ { "url": "...", "name": "...", "size": "999" }, ]
                    }
*/
editorimageResize.onImageUploadBefore = function (files, info, core, uploadHandler) {
    try {
        ResizeImage(files, uploadHandler)
    } catch (err) {
        uploadHandler(err.toString())
    }
};

// image resize
function ResizeImage (files, uploadHandler) {
    const uploadFile = files[0];
    const img = document.createElement('img');
    const canvas = document.createElement('canvas');
    const reader = new FileReader();

    reader.onload = function (e) {
        img.src = e.target.result
        img.onload = function () {
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);

            const MAX_WIDTH = 200;
            const MAX_HEIGHT = 100;
            let width = img.width;
            let height = img.height;

            if (width > height) {
                if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                }
            } else {
                if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                }
            }

            canvas.width = width;
            canvas.height = height;

            ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);

            canvas.toBlob(function (blob) {
                uploadHandler([new File([blob], uploadFile.name)])
            }, uploadFile.type, 1);
        }
    }

    reader.readAsDataURL(uploadFile);
}

Image management

Attached files Attach image 0KB
Video list
<!-- image list -->
<div id="image_wrapper" class="component-list">
    <div class="file-list-info">
        <span>Attached files</span>
        <span class="xefu-btn">
            <span class="files-text">Attach image</span>
        </span>
        <input type="file" id="files_upload" accept="image/*" multiple="multiple" class="files-text files-input"/>
        <span id="image_size" class="total-size text-small-2">0KB</span>
        <button class="btn btn-md btn-danger" id="image_remove" disabled onclick="deleteCheckedImages()">Remove</button>
    </div>
    <div class="file-list">
        <ul id="image_list">
        </ul>
    </div>
</div>
<!-- video list -->
<div class="component-list">
    <div class="file-list-info">
        <span>Video list</span>
    </div>
    <div class="component-file-list">
        <ul id="video_list">
        </ul>
    </div>
</div>
const imageWrapper = document.getElementById('image_wrapper');
const imageSize = document.getElementById('image_size');
const imageRemove = document.getElementById('image_remove');
const imageTable = document.getElementById('image_list');
let imageList = [];
let selectedImages = [];

const videoTable = document.getElementById('video_list');
let videoList = [];

// Array.prototype.findIndex (IE)
function findIndex(arr, index) {
    let idx = -1;

    arr.some(function (a, i) {
        if ((typeof a === 'number' ? a : a.index) === index) {
            idx = i;
            return true;
        }
        return false;
    })

    return idx;
}

/** event registration */
editorImageSample.onImageUpload = imageUpload;
editorImageSample.onVideoUpload = videoUpload;

/** --- image list --- */
const editorImageSample = SUNEDITOR.create('imageManagement', {
    buttonList: [
        ['undo', 'redo'],
        ['formatBlock'],
        ['horizontalRule', 'list', 'table'],
        ['image', 'video'],
        ['showBlocks', 'fullScreen', 'preview', 'print']
    ],
})

editorImageSample.onImageUpload = function (targetElement, index, state, imageInfo, remainingFilesCount) {
    if (state === 'delete') {
        imageList.splice(findIndex(imageList, index), 1)
    } else {
        if (state === 'create') {
            imageList.push(imageInfo)
        } else { // update }
    }

    if (remainingFilesCount === 0) {
        setImageList(imageList)
    }
}

// Upload from outside the editor
document.getElementById('files_upload').addEventListener('change', function (e) {
    if (e.target.files) {
        editorImageSample.insertImage(e.target.files)
        e.target.value = ''
    }
})

// Edit image list
function setImageList () {
    let list = '';
    let size = 0;

    for (let i = 0, image, fixSize; i &lt; imageList.length; i++) {
        image = imageList[i];
        fixSize = (image.size / 1000).toFixed(1) * 1
            
        list += '&lt;li id="img_' + image.index + '"&gt;' +
                    '&lt;div onclick="checkImage(' + image.index + ')"&gt;' +
                        '&lt;div class="image-wrapper"&gt;&lt;img src="' + image.src + '"&gt;&lt;/div&gt;' +
                    '&lt;/div&gt;' +
                    '&lt;a href="javascript:void(0)" onclick="selectImage(\'select\',' + image.index + ')" class="image-size"&gt;' + fixSize + 'KB&lt;/a&gt;' +
                    '&lt;div class="image-check"&gt;&lt;svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-fa-i2svg=""&gt;&lt;path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;' +
                '&lt;/li&gt;';
        
        size += fixSize;
    }

    imageSize.innerText = size.toFixed(1) + 'KB';
    imageTable.innerHTML = list;
}

// Click the file size
function selectImage (type, index) {
    imageList[findIndex(imageList, index)][type]();
}

// Image check
function checkImage (index) {
    const li = imageTable.querySelector('#img_' + index);
    const currentImageIdx = findIndex(selectedImages, index)

    if (currentImageIdx &gt; -1) {
        selectedImages.splice(currentImageIdx, 1)
        li.className = '';
    } else {
        selectedImages.push(index)
        li.className = 'checked';
    }

    if (selectedImages.length &gt; 0) {
        imageRemove.removeAttribute('disabled');
    } else {
        imageRemove.setAttribute('disabled', true);
    }
}

// Click the remove button
function deleteCheckedImages() {
    const iamgesInfo = editorImageSample.getImagesInfo();

    for (let i = 0; i &lt; iamgesInfo.length; i++) {
        if (selectedImages.indexOf(iamgesInfo[i].index) &gt; -1) {
            iamgesInfo[i].delete();
            i--;
        }
    }

    selectedImages = []
}

/** --- video list --- */
function videoUpload (targetElement, index, state, videoInfo, remainingFilesCount) {
    console.log('videoInfo', videoInfo);

    if (state === 'delete') {
        videoList.splice(findIndex(videoList, index), 1)
    } else {
        if (state === 'create') {
            videoList.push(videoInfo)
        } else { // update
            //
        }
    }

    if (remainingFilesCount === 0) {
        console.log('videoList', videoList)
        setVideoList(videoList)
    }
}

function setVideoList () {
    let list = '';

    for (let i = 0, video; i < videoList.length; i++) {
        video = videoList[i];
            
        list += '<li>' +
                    '<button title="delete" onclick="selectVideo(\'delete\',' + video.index + ')">X</button>' +
                    '<a href="javascript:void(0)" onclick="selectVideo(\'select\',' + video.index + ')">' + video.src + '</a>' +
                '</li>';
    }

    videoTable.innerHTML = list;
}

function selectVideo (type, index) {
    videoList[findIndex(videoList, index)][type]();
}