jodit
Version:
Jodit is an awesome and useful wysiwyg editor with filebrowser
364 lines (363 loc) • 13.6 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { STATUSES } from "../../core/component/index.js";
import * as consts from "../../core/constants.js";
import { IS_PROD } from "../../core/constants.js";
import { autobind, cache, cached, derive } from "../../core/decorators/index.js";
import { watch } from "../../core/decorators/watch/watch.js";
import { observable } from "../../core/event-emitter/index.js";
import { ConfigProto, error, isAbortError, isFunction, isString, trim } from "../../core/helpers/index.js";
import { Storage } from "../../core/storage/index.js";
import { Dlgs } from "../../core/traits/dlgs.js";
import { ViewWithToolbar } from "../../core/view/view-with-toolbar.js";
import { Config } from "../../config.js";
import "./config.js";
import { loadItems } from "./fetch/load-items.js";
import { loadTree } from "./fetch/load-tree.js";
import { nativeListeners } from "./listeners/native-listeners.js";
import { selfListeners } from "./listeners/self-listeners.js";
import { stateListeners } from "./listeners/state-listeners.js";
import { DEFAULT_SOURCE_NAME } from "./data-provider.js";
import { makeDataProvider } from "./factories.js";
import { FileBrowserFiles, FileBrowserTree } from "./ui/index.js";
let FileBrowser = class FileBrowser extends ViewWithToolbar {
/** @override */
className() {
return 'FileBrowser';
}
get dataProvider() {
return makeDataProvider(this, this.options);
}
onSelect(callback) {
return () => {
if (this.state.activeElements.length) {
const files = [];
const isImages = [];
this.state.activeElements.forEach(elm => {
const url = elm.fileURL;
if (url) {
files.push(url);
isImages.push(elm.isImage || false);
}
});
this.close();
const data = {
baseurl: '',
files,
isImages
};
if (isFunction(callback)) {
callback(data);
}
this.close();
}
return false;
};
}
get _dialog() {
var _a;
const dialog = this.dlg({
minWidth: Math.min(700, screen.width),
minHeight: 300,
buttons: (_a = this.o.headerButtons) !== null && _a !== void 0 ? _a : ['fullsize', 'dialog.close']
});
['beforeClose', 'afterClose', 'beforeOpen'].forEach(proxyEvent => dialog.events.on(dialog, proxyEvent, () => this.e.fire(proxyEvent)));
dialog.setSize(this.o.width, this.o.height);
return dialog;
}
/**
* Container for set/get value
*/
get storage() {
return Storage.makeStorage(Boolean(this.o.saveStateInStorage), this.componentName);
}
get isOpened() {
return this._dialog.isOpened && this.browser.style.display !== 'none';
}
/**
* It displays a message in the status bar of filebrowser
*
* @param message - The message that will be displayed
* @param success - true It will be shown a message light . If no option is specified ,
* ßan error will be shown the red
* @example
* ```javascript
* parent.filebrowser.status('There was an error uploading file', false);
* ```
*/
status(message, success) {
if (!message || isAbortError(message)) {
return;
}
if (!isString(message)) {
message = message.message;
}
if (!isString(message) || !trim(message).length) {
return;
}
this.message.message(message, success ? 'success' : 'error', this.o.howLongShowMsg);
}
/**
* It opens a web browser window
*
* @param callback - The function that will be called after the file selection in the browser
* @param onlyImages - Show only images
* @example
* ```javascript
* var fb = new Jodit.modules.FileBrowser(parent);
* fb.open(function (data) {
* var i;
* for (i = 0;i < data.files.length; i += 1) {
* parent.s.insertImage(data.baseurl + data.files[i]);
* }
* });
* ```
*/
open(callback = this.o
.defaultCallback, onlyImages = false) {
this.state.onlyImages = onlyImages;
return this.async
.promise((resolve, reject) => {
var _a;
if (!this.o.items || !this.o.items.url) {
throw error('Need set options.filebrowser.ajax.url');
}
let localTimeout = 0;
this.e
.off(this.files.container, 'dblclick')
.on(this.files.container, 'dblclick', this.onSelect(callback))
.on(this.files.container, 'touchstart', () => {
const now = new Date().getTime();
if (now - localTimeout <
consts.EMULATE_DBLCLICK_TIMEOUT) {
this.onSelect(callback)();
}
localTimeout = now;
})
.off('select.filebrowser')
.on('select.filebrowser', this.onSelect(callback));
const header = this.c.div();
(_a = this.toolbar) === null || _a === void 0 ? void 0 : _a.appendTo(header);
this.__updateToolbarButtons();
this._dialog.open(this.browser, header);
this.e.fire('sort.filebrowser', this.state.sortBy);
loadTree(this)
.then(resolve, reject)
.finally(() => {
var _a;
if (this.isInDestruct) {
return;
}
(_a = this === null || this === void 0 ? void 0 : this.e) === null || _a === void 0 ? void 0 : _a.fire('fileBrowserReady.filebrowser');
});
})
.catch((e) => {
if (!isAbortError(e) && !IS_PROD) {
throw e;
}
});
}
__getButtons() {
var _a;
const options = ((_a = this.o.buttons) !== null && _a !== void 0 ? _a : []);
return options.filter((btn) => {
if (!isString(btn)) {
return true;
}
switch (btn) {
case 'filebrowser.upload':
return this.dataProvider.canI('FileUpload');
case 'filebrowser.edit':
return (this.dataProvider.canI('ImageResize') ||
this.dataProvider.canI('ImageCrop'));
case 'filebrowser.remove':
return this.dataProvider.canI('FileRemove');
}
return true;
});
}
initUploader(editor) {
var _a;
const self = this, options = (_a = editor === null || editor === void 0 ? void 0 : editor.options) === null || _a === void 0 ? void 0 : _a.uploader, uploaderOptions = ConfigProto(options || {}, Config.defaultOptions.uploader);
const uploadHandler = () => loadItems(this);
self.uploader = self.getInstance('Uploader', uploaderOptions);
self.uploader
.setPath(self.state.currentPath)
.setSource(self.state.currentSource)
.bind(self.browser, uploadHandler, self.errorHandler);
this.state.on(['change.currentPath', 'change.currentSource'], () => {
this.uploader
.setPath(this.state.currentPath)
.setSource(this.state.currentSource);
});
self.e.on('bindUploader.filebrowser', (button) => {
self.uploader.bind(button, uploadHandler, self.errorHandler);
});
}
constructor(options) {
super(options);
this.browser = this.c.div(this.componentName);
this.status_line = this.c.div(this.getFullElName('status'));
this.tree = new FileBrowserTree(this);
this.files = new FileBrowserFiles(this);
this.state = observable({
currentPath: '',
currentSource: DEFAULT_SOURCE_NAME,
currentBaseUrl: '',
activeElements: [],
elements: [],
sources: [],
view: 'tiles',
sortBy: 'changed-desc',
filterWord: '',
onlyImages: false
});
this.errorHandler = (resp) => {
if (isAbortError(resp)) {
return;
}
if (resp instanceof Error) {
this.status(this.i18n(resp.message));
}
else {
this.status(this.dataProvider.getMessage(resp));
}
};
/**
* Close dialog
*/
this.close = () => {
this._dialog.close();
};
this.__prevButtons = [];
this.attachEvents(options);
const self = this;
self.options = ConfigProto(options || {}, Config.defaultOptions.filebrowser);
self.browser.component = this;
self.container = self.browser;
if (self.o.showFoldersPanel) {
self.browser.appendChild(self.tree.container);
}
self.browser.appendChild(self.files.container);
self.browser.appendChild(self.status_line);
selfListeners.call(self);
nativeListeners.call(self);
stateListeners.call(self);
const keys = [
'getLocalFileByUrl',
'crop',
'resize',
'create',
'fileMove',
'folderMove',
'fileRename',
'folderRename',
'fileRemove',
'folderRemove',
'folder',
'items',
'permissions'
];
keys.forEach(key => {
if (this.options[key] != null) {
this.options[key] = ConfigProto(this.options[key], this.o.ajax);
}
});
const { storeView, storeSortBy, storeLastOpenedFolder } = this.o
.saveStateInStorage || {
storeLastOpenedFolder: false,
storeView: false,
storeSortBy: false
};
const view = storeView && this.storage.get('view');
if (view && this.o.view == null) {
self.state.view = view === 'list' ? 'list' : 'tiles';
}
else {
self.state.view = self.o.view === 'list' ? 'list' : 'tiles';
}
self.files.setMod('view', self.state.view);
const sortBy = storeSortBy && self.storage.get('sortBy');
if (sortBy) {
const parts = sortBy.split('-');
self.state.sortBy = ['changed', 'name', 'size'].includes(parts[0])
? sortBy
: 'changed-desc';
}
else {
self.state.sortBy = self.o.sortBy || 'changed-desc';
}
if (storeLastOpenedFolder) {
const currentPath = self.storage.get('currentPath'), currentSource = self.storage.get('currentSource');
self.state.currentPath = currentPath !== null && currentPath !== void 0 ? currentPath : '';
self.state.currentSource = currentSource !== null && currentSource !== void 0 ? currentSource : '';
}
self.initUploader(self);
self.setStatus(STATUSES.ready);
}
destruct() {
var _a;
if (this.isInDestruct) {
return;
}
(_a = cached(this, '_dialog')) === null || _a === void 0 ? void 0 : _a.destruct();
super.destruct();
this.events && this.e.off('.filebrowser');
this.uploader && this.uploader.destruct();
}
__updateToolbarButtons() {
var _a;
const buttons = this.__getButtons();
if (isEqualButtonList(this.__prevButtons, buttons)) {
return;
}
this.__prevButtons = buttons;
(_a = this.toolbar) === null || _a === void 0 ? void 0 : _a.build(buttons);
}
};
__decorate([
cache
], FileBrowser.prototype, "dataProvider", null);
__decorate([
cache
], FileBrowser.prototype, "_dialog", null);
__decorate([
cache
], FileBrowser.prototype, "storage", null);
__decorate([
autobind
], FileBrowser.prototype, "status", null);
__decorate([
autobind
], FileBrowser.prototype, "open", null);
__decorate([
watch('dataProvider:changePermissions')
], FileBrowser.prototype, "__updateToolbarButtons", null);
FileBrowser = __decorate([
derive(Dlgs)
], FileBrowser);
export { FileBrowser };
function isEqualButtonList(prevButtons, buttons) {
if (prevButtons.length !== buttons.length) {
return false;
}
for (let i = 0; i < prevButtons.length; i++) {
if (prevButtons[i] !== buttons[i]) {
return false;
}
}
return true;
}