/*! * Jodit Editor (https://xdsoft.net/jodit/) * Released under MIT see LICENSE.txt in the project root for license information. * Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import { IS_PROD } from "../../core/constants.js"; import { abort, ConfigProto, error, isFunction, normalizeRelativePath, set } from "../../core/helpers/index.js"; import { Ajax } from "../../core/request/index.js"; import { FileBrowserItem } from "./builders/item.js"; export const DEFAULT_SOURCE_NAME = 'default'; const possibleRules = new Set([ 'allowFiles', 'allowFileMove', 'allowFileUpload', 'allowFileUploadRemote', 'allowFileRemove', 'allowFileRename', 'allowFolders', 'allowFolderMove', 'allowFolderCreate', 'allowFolderRemove', 'allowFolderRename', 'allowImageResize', 'allowImageCrop' ]); export default class DataProvider { constructor(parent, options) { this.parent = parent; this.options = options; this.__currentPermissions = null; this.__ajaxInstances = new Map(); this.progressHandler = (ignore) => { }; } /** * Alias for options */ get o() { return this.options; } get(name) { const instances = this.__ajaxInstances; if (instances.has(name)) { const ajax = instances.get(name); ajax === null || ajax === void 0 ? void 0 : ajax.abort(); instances.delete(name); } const opts = ConfigProto(this.options[name] !== undefined ? this.options[name] : {}, ConfigProto({ onProgress: this.progressHandler }, this.o.ajax)); if (opts.prepareData) { opts.data = opts.prepareData.call(this, opts.data); } const ajax = new Ajax(opts); instances.set(name, ajax); const promise = ajax.send(); promise .finally(() => { ajax.destruct(); instances.delete(name); this.progressHandler(100); }) .catch(() => null); return promise .then(resp => resp.json()) .then(resp => { if (resp && !this.isSuccess(resp)) { throw new Error(this.getMessage(resp)); } return resp; }); } onProgress(callback) { this.progressHandler = callback; } /** * Load permissions for path and source */ async permissions(path, source) { if (!this.o.permissions) { return null; } this.o.permissions.data.path = path; this.o.permissions.data.source = source; if (this.o.permissions.url) { return this.get('permissions').then(resp => { if (this.parent.isInDestruct) { throw abort(); } let process = this.o.permissions.process; if (!process) { process = this.o.ajax.process; } if (process) { const respData = process.call(self, resp); if (respData.data.permissions) { this.parent.events.fire(this, 'changePermissions', this.__currentPermissions, respData.data.permissions); this.__currentPermissions = respData.data.permissions; } } return this.__currentPermissions; }); } return null; } canI(action) { const rule = 'allow' + action; if (!IS_PROD) { if (!possibleRules.has(rule)) { throw error('Wrong action ' + action); } } const presetValue = this.o.permissionsPresets[rule]; if (presetValue !== undefined) { return presetValue; } return (this.__currentPermissions == null || this.__currentPermissions[rule] === undefined || this.__currentPermissions[rule]); } __items(path, source, mods, onResult) { const opt = this.options; if (!opt.items) { return Promise.reject(Error('Set Items api options')); } opt.items.data.path = path; opt.items.data.source = source; opt.items.data.mods = mods; return this.get('items').then(resp => { let process = this.o.items.process; if (!process) { process = this.o.ajax.process; } if (process) { resp = process.call(self, resp); } return onResult(resp); }); } /** * Load items list by path and source */ items(path, source, mods = {}) { return this.__items(path, source, mods, resp => this.generateItemsList(resp.data.sources, mods)); } /** * Load items list by path and source */ itemsEx(path, source, mods = {}) { const calcTotal = (sources) => sources.reduce((acc, source) => acc + source.files.length, 0); return this.__items(path, source, mods, resp => ({ items: this.generateItemsList(resp.data.sources, mods), loadedTotal: calcTotal(resp.data.sources) })); } generateItemsList(sources, mods = {}) { const elements = []; const canBeFile = (item) => item.type === 'folder' || !mods.onlyImages || item.isImage === undefined || item.isImage; const inFilter = (item) => { var _a; return !((_a = mods.filterWord) === null || _a === void 0 ? void 0 : _a.length) || this.o.filter === undefined || this.o.filter(item, mods.filterWord); }; sources.forEach(source => { if (source.files && source.files.length) { const { sort } = this.o; if (isFunction(sort) && mods.sortBy) { source.files.sort((a, b) => sort(a, b, mods.sortBy)); } source.files.forEach((item) => { if (inFilter(item) && canBeFile(item)) { elements.push(FileBrowserItem.create({ ...item, sourceName: source.name, source })); } }); } }); return elements; } async tree(path, source) { path = normalizeRelativePath(path); if (!this.o.folder) { return Promise.reject(Error('Set Folder Api options')); } await this.permissions(path, source); this.o.folder.data.path = path; this.o.folder.data.source = source; return this.get('folder').then(resp => { let process = this.o.folder.process; if (!process) { process = this.o.ajax.process; } if (process) { resp = process.call(self, resp); } return resp.data.sources; }); } /** * Get path by url. You can use this method in another modules */ getPathByUrl(url) { set('options.getLocalFileByUrl.data.url', url, this); return this.get('getLocalFileByUrl').then(resp => { if (this.isSuccess(resp)) { return resp.data; } throw error(this.getMessage(resp)); }); } /** * Create a directory on the server * * @param name - Name the new folder * @param path - Relative directory in which you want create a folder * @param source - Server source key */ createFolder(name, path, source) { const { create } = this.o; if (!create) { throw error('Set Create api options'); } create.data.source = source; create.data.path = path; create.data.name = name; return this.get('create').then(resp => { if (this.isSuccess(resp)) { return true; } throw error(this.getMessage(resp)); }); } /** * Move a file / directory on the server * * @param filepath - The relative path to the file / folder source * @param path - Relative to the directory where you want to move the file / folder */ move(filepath, path, source, isFile) { const mode = isFile ? 'fileMove' : 'folderMove'; const option = this.options[mode]; if (!option) { throw error('Set Move api options'); } option.data.from = filepath; option.data.path = path; option.data.source = source; return this.get(mode).then(resp => { if (this.isSuccess(resp)) { return true; } throw error(this.getMessage(resp)); }); } /** * Deleting item * * @param path - Relative path * @param file - The filename * @param source - Source */ remove(action, path, file, source) { const fr = this.o[action]; if (!fr) { throw error(`Set "${action}" api options`); } fr.data.path = path; fr.data.name = file; fr.data.source = source; return this.get(action).then(resp => { if (fr.process) { resp = fr.process.call(this, resp); } return this.getMessage(resp); }); } /** * Deleting a file * * @param path - Relative path * @param file - The filename * @param source - Source */ fileRemove(path, file, source) { return this.remove('fileRemove', path, file, source); } /** * Deleting a folder * * @param path - Relative path * @param file - The filename * @param source - Source */ folderRemove(path, file, source) { return this.remove('folderRemove', path, file, source); } /** * Rename action * * @param path - Relative path * @param name - Old name * @param newname - New name * @param source - Source */ rename(action, path, name, newname, source) { const fr = this.o[action]; if (!fr) { throw error(`Set "${action}" api options`); } fr.data.path = path; fr.data.name = name; fr.data.newname = newname; fr.data.source = source; return this.get(action).then(resp => { if (fr.process) { resp = fr.process.call(self, resp); } return this.getMessage(resp); }); } /** * Rename folder */ folderRename(path, name, newname, source) { return this.rename('folderRename', path, name, newname, source); } /** * Rename file */ fileRename(path, name, newname, source) { return this.rename('fileRename', path, name, newname, source); } changeImage(type, path, source, name, newname, box) { if (!this.o[type]) { this.o[type] = { data: {} }; } const query = this.o[type]; if (query.data === undefined) { query.data = { action: type }; } query.data.newname = newname || name; if (box) { query.data.box = box; } query.data.path = path; query.data.name = name; query.data.source = source; return this.get(type).then(() => { return true; }); } /** * Send command to server to crop image */ crop(path, source, name, newname, box) { return this.changeImage('crop', path, source, name, newname, box); } /** * Send command to server to resize image */ resize(path, source, name, newname, box) { return this.changeImage('resize', path, source, name, newname, box); } getMessage(resp) { return this.options.getMessage(resp); } isSuccess(resp) { return this.options.isSuccess(resp); } destruct() { this.__ajaxInstances.forEach(a => a.destruct()); this.__ajaxInstances.clear(); } }