vditor
Version:
♏ 易于使用的 Markdown 编辑器,为适配不同的应用场景而生
274 lines (243 loc) • 10.5 kB
text/typescript
import { getEditorRange, setSelectionFocus } from "../util/selection";
import { getElement } from "./getElement";
import { setHeaders } from "./setHeaders";
class Upload {
public element: HTMLElement;
public isUploading: boolean;
public range: Range;
constructor() {
this.isUploading = false;
this.element = document.createElement("div");
this.element.className = "vditor-upload";
}
}
const validateFile = (vditor: IVditor, files: File[]) => {
vditor.tip.hide();
const uploadFileList: File[] = [];
let errorTip = "";
let uploadingStr = "";
const lang: keyof II18n | "" = vditor.options.lang;
const options: IOptions = vditor.options;
for (let iMax = files.length, i = 0; i < iMax; i++) {
const file = files[i];
let validate = true;
if (!file.name) {
errorTip += `<li>${window.VditorI18n.nameEmpty}</li>`;
validate = false;
}
if (file.size > vditor.options.upload.max) {
errorTip += `<li>${file.name} ${window.VditorI18n.over} ${vditor.options.upload.max / 1024 / 1024}M</li>`;
validate = false;
}
const lastIndex = file.name.lastIndexOf(".");
const fileExt = file.name.substr(lastIndex);
const filename = vditor.options.upload.filename(file.name.substr(0, lastIndex)) + fileExt;
if (vditor.options.upload.accept) {
const isAccept = vditor.options.upload.accept.split(",").some((item) => {
const type = item.trim();
if (type.indexOf(".") === 0) {
if (fileExt.toLowerCase() === type.toLowerCase()) {
return true;
}
} else {
if (file.type.split("/")[0] === type.split("/")[0]) {
return true;
}
}
return false;
});
if (!isAccept) {
errorTip += `<li>${file.name} ${window.VditorI18n.fileTypeError}</li>`;
validate = false;
}
}
if (validate) {
uploadFileList.push(file);
uploadingStr += `<li>${filename} ${window.VditorI18n.uploading} <a class="vditorCancelUpload" href="javascript:void(0)">${window.VditorI18n.cancelUpload}</a></li>`;
}
}
vditor.tip.show(`<ul>${errorTip}${uploadingStr}</ul>`);
if (vditor.options.upload.cancel) {
const vditorCancelUploadElement = vditor.tip.element.querySelector(".vditorCancelUpload");
if (vditorCancelUploadElement) {
vditorCancelUploadElement.addEventListener("click", () => {
vditor.options.upload.cancel(uploadFileList);
vditor.tip.hide();
vditor.upload.isUploading = false;
});
}
}
return uploadFileList;
};
const genUploadedLabel = (responseText: string, vditor: IVditor) => {
const editorElement = getElement(vditor);
editorElement.focus();
const response = JSON.parse(responseText);
let errorTip = "";
if (response.code === 1) {
errorTip = `${response.msg}`;
}
if (response.data.errFiles && response.data.errFiles.length > 0) {
errorTip = `<ul><li>${errorTip}</li>`;
response.data.errFiles.forEach((data: string) => {
const lastIndex = data.lastIndexOf(".");
const filename = vditor.options.upload.filename(data.substr(0, lastIndex)) + data.substr(lastIndex);
errorTip += `<li>${filename} ${window.VditorI18n.uploadError}</li>`;
});
errorTip += "</ul>";
}
if (errorTip) {
vditor.tip.show(errorTip);
} else {
vditor.tip.hide();
}
let succFileText = "";
Object.keys(response.data.succMap).forEach((key) => {
const path = response.data.succMap[key];
const lastIndex = key.lastIndexOf(".");
let type = key.substr(lastIndex);
const filename = vditor.options.upload.filename(key.substr(0, lastIndex)) + type;
type = type.toLowerCase();
if (type.indexOf(".wav") === 0 || type.indexOf(".mp3") === 0 || type.indexOf(".ogg") === 0) {
if (vditor.currentMode === "wysiwyg") {
succFileText += `<div class="vditor-wysiwyg__block" data-type="html-block"
data-block="0"><pre><code><audio controls="controls" src="${path}"></audio></code></pre><pre class="vditor-wysiwyg__preview" data-render="1"><audio controls="controls" src="${path}"></audio></pre></div>\n`;
} else if (vditor.currentMode === "ir") {
succFileText += `<audio controls="controls" src="${path}"></audio>\n`;
} else {
succFileText += `[${filename}](${path})\n`;
}
} else if (type.indexOf(".apng") === 0
|| type.indexOf(".bmp") === 0
|| type.indexOf(".gif") === 0
|| type.indexOf(".ico") === 0 || type.indexOf(".cur") === 0
|| type.indexOf(".jpg") === 0 || type.indexOf(".jpeg") === 0 || type.indexOf(".jfif") === 0 || type.indexOf(".pjp") === 0 || type.indexOf(".pjpeg") === 0
|| type.indexOf(".png") === 0
|| type.indexOf(".svg") === 0
|| type.indexOf(".webp") === 0) {
if (vditor.currentMode === "wysiwyg") {
succFileText += `<img alt="${filename}" src="${path}">\n`;
} else {
succFileText += `\n`;
}
} else {
if (vditor.currentMode === "wysiwyg") {
succFileText += `<a href="${path}">${filename}</a>\n`;
} else {
succFileText += `[${filename}](${path})\n`;
}
}
});
setSelectionFocus(vditor.upload.range);
document.execCommand("insertHTML", false, succFileText);
vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
};
const uploadFiles =
async (vditor: IVditor, files: FileList | DataTransferItemList | File[], element?: HTMLInputElement) => {
// FileList | DataTransferItemList | File[] => File[]
let fileList = [];
const filesMax = vditor.options.upload.multiple === true ? files.length : 1;
for (let i = 0; i < filesMax; i++) {
let fileItem = files[i];
if (fileItem instanceof DataTransferItem) {
fileItem = fileItem.getAsFile();
}
fileList.push(fileItem);
}
if (vditor.options.upload.handler) {
const isValidate = await vditor.options.upload.handler(fileList);
if (element) {
element.value = "";
}
if (typeof isValidate === "string") {
vditor.tip.show(isValidate);
return;
}
return;
}
if (!vditor.options.upload.url || !vditor.upload) {
if (element) {
element.value = "";
}
vditor.tip.show("please config: options.upload.url");
return;
}
if (vditor.options.upload.file) {
fileList = await vditor.options.upload.file(fileList);
}
if (vditor.options.upload.validate) {
const isValidate = vditor.options.upload.validate(fileList);
if (typeof isValidate === "string") {
vditor.tip.show(isValidate);
return;
}
}
const editorElement = getElement(vditor);
vditor.upload.range = getEditorRange(vditor);
const validateResult = validateFile(vditor, fileList);
if (validateResult.length === 0) {
if (element) {
element.value = "";
}
return;
}
const formData = new FormData();
const extraData = vditor.options.upload.extraData;
for (const key of Object.keys(extraData)) {
formData.append(key, extraData[key]);
}
for (let i = 0, iMax = validateResult.length; i < iMax; i++) {
formData.append(vditor.options.upload.fieldName, validateResult[i]);
}
const xhr = new XMLHttpRequest();
vditor.upload.xhr = xhr;
xhr.open("POST", vditor.options.upload.url);
if (vditor.options.upload.token) {
xhr.setRequestHeader("X-Upload-Token", vditor.options.upload.token);
}
if (vditor.options.upload.withCredentials) {
xhr.withCredentials = true;
}
setHeaders(vditor, xhr);
vditor.upload.isUploading = true;
editorElement.setAttribute("contenteditable", "false");
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
vditor.upload.isUploading = false;
editorElement.setAttribute("contenteditable", "true");
if (xhr.status >= 200 && xhr.status < 300) {
if (vditor.options.upload.success) {
vditor.options.upload.success(editorElement, xhr.responseText);
} else {
let responseText = xhr.responseText;
if (vditor.options.upload.format) {
responseText = vditor.options.upload.format(files as File [], xhr.responseText);
}
genUploadedLabel(responseText, vditor);
}
} else {
if (vditor.options.upload.error) {
vditor.options.upload.error(xhr.responseText);
} else {
vditor.tip.show(xhr.responseText);
}
}
if (element) {
element.value = "";
}
vditor.upload.element.style.display = "none";
vditor.upload.xhr = undefined;
}
};
xhr.upload.onprogress = (event: ProgressEvent) => {
if (!event.lengthComputable) {
return;
}
const progress = event.loaded / event.total * 100;
vditor.upload.element.style.display = "block";
const progressBar = vditor.upload.element;
progressBar.style.width = progress + "%";
};
xhr.send(formData);
};
export { Upload, uploadFiles };