UNPKG

suneditor

Version:

Vanilla JavaScript WYSIWYG web editor (2.x legacy version, actively maintained)

326 lines (288 loc) 14.9 kB
/* * wysiwyg web editor * * suneditor.js * Copyright 2017 JiHong Lee. * MIT license. */ 'use strict'; (function (global, factory) { if (typeof module === 'object' && typeof module.exports === 'object') { module.exports = global.document ? factory(global, true) : function (w) { if (!w.document) { throw new Error('SUNEDITOR_MODULES a window with a document'); } return factory(w); }; } else { factory(global); } }(typeof window !== 'undefined' ? window : this, function (window, noGlobal) { const fileManager = { name: 'fileManager', _xmlHttp: null, _checkMediaComponent: function (tag) { if (/IMG/i.test(tag)) { return !/FIGURE/i.test(tag.parentElement.nodeName) || !/FIGURE/i.test(tag.parentElement.parentElement.nodeName); } return true; }, /** * @description Upload the file to the server. * @param {String} uploadUrl Upload server url * @param {Object|null} uploadHeader Request header * @param {FormData} formData FormData in body * @param {Function|null} callBack Success call back function * @param {Function|null} errorCallBack Error call back function * @example this.plugins.fileManager.upload.call(this, imageUploadUrl, this.options.imageUploadHeader, formData, this.plugins.image.callBack_imgUpload.bind(this, info), this.functions.onImageUploadError); */ upload: function (uploadUrl, uploadHeader, formData, callBack, errorCallBack) { this.showLoading(); const filePlugin = this.plugins.fileManager; const xmlHttp = filePlugin._xmlHttp = this.util.getXMLHttpRequest(); xmlHttp.onreadystatechange = filePlugin._callBackUpload.bind(this, xmlHttp, callBack, errorCallBack); xmlHttp.open('post', uploadUrl, true); if(uploadHeader !== null && typeof uploadHeader === 'object' && this._w.Object.keys(uploadHeader).length > 0){ for(let key in uploadHeader){ xmlHttp.setRequestHeader(key, uploadHeader[key]); } } xmlHttp.send(formData); }, _callBackUpload: function (xmlHttp, callBack, errorCallBack) { if (xmlHttp.readyState === 4) { if (xmlHttp.status === 200) { try { callBack(xmlHttp); } catch (e) { throw Error('[SUNEDITOR.fileManager.upload.callBack.fail] cause : "' + e.message + '"'); } finally { this.closeLoading(); } } else { // exception this.closeLoading(); const res = !xmlHttp.responseText ? xmlHttp : JSON.parse(xmlHttp.responseText); if (typeof errorCallBack !== 'function' || errorCallBack('', res, this)) { const err = '[SUNEDITOR.fileManager.upload.serverException] status: ' + xmlHttp.status + ', response: ' + (res.errorMessage || xmlHttp.responseText); this.functions.noticeOpen(err); throw Error(err); } } } }, /** * @description Checke the file's information and modify the tag that does not fit the format. * @param {String} pluginName Plugin name * @param {Array} tagNames Tag array to check * @param {Function|null} uploadEventHandler Event handler to process updated file info after checking (used in "setInfo") * @param {Function} modifyHandler A function to modify a tag that does not fit the format (Argument value: Tag element) * @param {Boolean} resizing True if the plugin is using a resizing module * @example * const modifyHandler = function (tag) { * imagePlugin.onModifyMode.call(this, tag, null); * imagePlugin.openModify.call(this, true); * imagePlugin.update_image.call(this, true, false, true); * }.bind(this); * this.plugins.fileManager.checkInfo.call(this, 'image', ['img'], this.functions.onImageUpload, modifyHandler, true); */ checkInfo: function (pluginName, tagNames, uploadEventHandler, modifyHandler, resizing) { let tags = []; for (let i = 0, len = tagNames.length; i < len; i++) { tags = tags.concat([].slice.call(this.context.element.wysiwyg.querySelectorAll(tagNames[i] + ':not([data-se-embed="true"])'))); } const fileManagerPlugin = this.plugins.fileManager; const context = this.context[pluginName]; const infoList = context._infoList; const setFileInfo = fileManagerPlugin.setInfo.bind(this); if (tags.length === infoList.length) { // reset if (this._componentsInfoReset) { for (let i = 0, len = tags.length; i < len; i++) { setFileInfo(pluginName, tags[i], uploadEventHandler, null, resizing); } return ; } else { let infoUpdate = false; for (let i = 0, len = infoList.length, info; i < len; i++) { info = infoList[i]; if (tags.filter(function (t) { return info.src === t.src && info.index.toString() === t.getAttribute('data-index'); }).length === 0) { infoUpdate = true; break; } } // pass if (!infoUpdate) return; } } // check const _resize_plugin = resizing ? this.context.resizing._resize_plugin : ''; if (resizing) this.context.resizing._resize_plugin = pluginName; const currentTags = []; const infoIndex = []; for (let i = 0, len = infoList.length; i < len; i++) { infoIndex[i] = infoList[i].index; } context.__updateTags = tags; while (tags.length > 0) { const tag = tags.shift(); if (!this.util.getParentElement(tag, this.util.isMediaComponent) || !fileManagerPlugin._checkMediaComponent(tag)) { currentTags.push(context._infoIndex); modifyHandler(tag); } else if (!tag.getAttribute('data-index') || infoIndex.indexOf(tag.getAttribute('data-index') * 1) < 0) { currentTags.push(context._infoIndex); tag.removeAttribute('data-index'); setFileInfo(pluginName, tag, uploadEventHandler, null, resizing); } else { currentTags.push(tag.getAttribute('data-index') * 1); } } for (let i = 0, dataIndex; i < infoList.length; i++) { dataIndex = infoList[i].index; if (currentTags.indexOf(dataIndex) > -1) continue; infoList.splice(i, 1); if (typeof uploadEventHandler === 'function') uploadEventHandler(null, dataIndex, 'delete', null, 0, this); i--; } if (resizing) this.context.resizing._resize_plugin = _resize_plugin; }, /** * @description Create info object of file and add it to "_infoList" (this.context[pluginName]._infoList[]) * @param {String} pluginName Plugin name * @param {Element} element * @param {Function|null} uploadEventHandler Event handler to process updated file info (created in setInfo) * @param {Object|null} file * @param {Boolean} resizing True if the plugin is using a resizing module * @example * uploadCallBack {.. file = { name: fileList[i].name, size: fileList[i].size }; * this.plugins.fileManager.setInfo.call(this, 'image', oImg, this.functions.onImageUpload, file, true); */ setInfo: function (pluginName, element, uploadEventHandler, file, resizing) { const _resize_plugin = resizing ? this.context.resizing._resize_plugin : ''; if (resizing) this.context.resizing._resize_plugin = pluginName; const plguin = this.plugins[pluginName]; const context = this.context[pluginName]; const infoList = context._infoList; let dataIndex = element.getAttribute('data-index'); let info = null; let state = ''; if (!file) { file = { 'name': element.getAttribute('data-file-name') || (typeof element.src === 'string' ? element.src.split('/').pop() : ''), 'size': element.getAttribute('data-file-size') || 0 }; } // create if (!dataIndex || this._componentsInfoInit) { state = 'create'; dataIndex = context._infoIndex++; element.setAttribute('data-index', dataIndex); element.setAttribute('data-file-name', file.name); element.setAttribute('data-file-size', file.size); info = { src: element.src, index: dataIndex * 1, name: file.name, size: file.size }; infoList.push(info); } else { // update state = 'update'; dataIndex *= 1; for (let i = 0, len = infoList.length; i < len; i++) { if (dataIndex === infoList[i].index) { info = infoList[i]; break; } } if (!info) { dataIndex = context._infoIndex++; info = { index: dataIndex }; infoList.push(info); } info.src = element.src; info.name = element.getAttribute("data-file-name"); info.size = element.getAttribute("data-file-size") * 1; } // method bind info.element = element; info.delete = plguin.destroy.bind(this, element); info.select = function (element) { element.scrollIntoView(true); this._w.setTimeout(plguin.select.bind(this, element)); }.bind(this, element); if (resizing) { if (!element.getAttribute('origin-size') && element.naturalWidth) { element.setAttribute('origin-size', element.naturalWidth + ',' + element.naturalHeight); } if (!element.getAttribute('data-origin')) { const container = this.util.getParentElement(element, this.util.isMediaComponent); const cover = this.util.getParentElement(element, 'FIGURE'); const w = this.plugins.resizing._module_getSizeX.call(this, context, element, cover, container); const h = this.plugins.resizing._module_getSizeY.call(this, context, element, cover, container); element.setAttribute('data-origin', w + ',' + h); element.setAttribute('data-size', w + ',' + h); } if (!element.style.width) { const size = (element.getAttribute('data-size') || element.getAttribute('data-origin') || '').split(','); plguin.onModifyMode.call(this, element, null); plguin.applySize.call(this, size[0], size[1]); } this.context.resizing._resize_plugin = _resize_plugin; } if (typeof uploadEventHandler === 'function') uploadEventHandler(element, dataIndex, state, info, --context._uploadFileLength < 0 ? 0 : context._uploadFileLength, this); }, /** * @description Delete info object at "_infoList" * @param {String} pluginName Plugin name * @param {Number} index index of info object (this.context[pluginName]._infoList[].index) * @param {Function|null} uploadEventHandler Event handler to process updated file info (created in setInfo) */ deleteInfo: function (pluginName, index, uploadEventHandler) { if (index >= 0) { const infoList = this.context[pluginName]._infoList; for (let i = 0, len = infoList.length; i < len; i++) { if (index === infoList[i].index) { infoList.splice(i, 1); if (typeof uploadEventHandler === 'function') uploadEventHandler(null, index, 'delete', null, 0, this); return; } } } }, /** * @description Reset info object and "_infoList = []", "_infoIndex = 0" * @param {String} pluginName Plugin name * @param {Function|null} uploadEventHandler Event handler to process updated file info (created in setInfo) */ resetInfo: function (pluginName, uploadEventHandler) { const context = this.context[pluginName]; if (typeof uploadEventHandler === 'function') { const infoList = context._infoList; for (let i = 0, len = infoList.length; i < len; i++) { uploadEventHandler(null, infoList[i].index, 'delete', null, 0, this); } } context._infoList = []; context._infoIndex = 0; } }; if (typeof noGlobal === typeof undefined) { if (!window.SUNEDITOR_MODULES) { Object.defineProperty(window, 'SUNEDITOR_MODULES', { enumerable: true, writable: false, configurable: false, value: {} }); } Object.defineProperty(window.SUNEDITOR_MODULES, 'fileManager', { enumerable: true, writable: false, configurable: false, value: fileManager }); } return fileManager; }));