UNPKG

jodit

Version:

Jodit is awesome and usefully wysiwyg editor with filebrowser

680 lines (619 loc) 20.6 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Licensed under GNU General Public License version 2 or later or a commercial license or MIT; * For GPL see LICENSE-GPL.txt in the project root for license information. * For MIT see LICENSE-MIT.txt in the project root for license information. * For commercial licenses see https://xdsoft.net/jodit/commercial/ * Copyright (c) 2013-2019 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import { Config } from '../../Config'; import { ToolbarIcon } from '../toolbar/icon'; import { IFileBrowser, IFileBrowserAnswer, IFileBrowserItem, IFileBrowserOptions, ISource, ISourceFile } from '../../types/fileBrowser'; import { IControlType } from '../../types/toolbar'; import { IDictionary } from '../../types/types'; import { IUploader } from '../../types/uploader'; import { IViewBased } from '../../types/view'; import { debounce } from '../helpers/async/debounce'; import { humanSizeToBytes } from '../helpers'; import { ITEM_CLASS as IC } from './fileBrowser'; declare module '../../Config' { interface Config { /** * Filebrowser module settings * * @property{int} filebrowser.howLongShowMsg=3000 How long toWYSIWYG show an error message * in the status bar (ms) * @property{boolean} filebrowser.sort=function (a, b, sortBy, parent) { return b.changed - a.changed;} * Items sort functions * @property{boolean} filebrowser.sortBy='changed-desc' Sort by field * @property{boolean} filebrowser.filter=function (item, searchWord) * { return item.name.toLowerCase().indexOf(searchWord.toLowerCase()) !== -1} Filter items * @property{boolean} filebrowser.showFileName=true Show filename in thumbs * @property{boolean} filebrowser.showFileSize=true Show filesize in thumbs * @property{boolean} filebrowser.showFileChangeTime=true Show the last modification time in thumbs * * @property {boolean} filebrowser.editImage=true use * {@link ImageEditor|Image editor module} - crop and resize image * @property {boolean} filebrowser.preview=true Show preview button in context menu * @property {boolean} filebrowser.showPreviewNavigation=true Show navigation buttons in preview * @property {boolean} filebrowser.showSelectButtonInPreview=true Show select button in preview * @property {boolean} filebrowser.contextMenu=true use context menu * @property {boolean} filebrowser.createNewFolder=true * The ability toWYSIWYG create a directory of the web browser * @property {boolean} filebrowser.deleteFolder=true * The ability toWYSIWYG delete directories from the web browser * @property {boolean} filebrowser.moveFolder=true The ability toWYSIWYG move directories from the web browser * @property {boolean} filebrowser.moveFile=true The ability toWYSIWYG move file from the web browser * @property {boolean} filebrowser.showFoldersPanel=true Show folders panel * @property {int|string} filebrowser.width=763px The width of the web browser * @property {int|string} filebrowser.height=400px The height of the file browser * @property {Array<string>} filebrowser.buttons="[ * 'filebrowser.upload', * 'filebrowser.remove', * 'filebrowser.update', * 'filebrowser.select', * 'filebrowser.edit', * '|', * 'filebrowser.tiles', * 'filebrowser.list', * '|', * 'filebrowser.filter', * '|', * 'filebrowser.sort', * ]" Toolbar browser * @example * ```javascript * var editor = new Jodit('#editor', { * filebrowser: { * buttons: ['filebrowser.upload', 'filebrowser.remove', 'filebrowser.update', * { * name: 'deleteall', * icon: 'remove', * exec: function (editor) { * $files.find('a').each(function () { * editor.filebrowserюremove(editor.filebrowser.currentPath, $(this).data('name')); * }); * editor.filebrowser.loadTree(); * }, * }], * } * }) * ``` * @property{function} filebrowser.isSuccess method toWYSIWYG check - whether the response positive * @property{function} filebrowser.getMessage method for receiving a message from the response * @example * ```javascript * new Jodit('#editor', { * filebrowser: { * isSuccess: function (resp) { * return resp.status == 1; * }, * getMessage: function (resp) { * return resp.message; * }, * } * }) * ``` * @property{string} filebrowser.view='tiles' Filelist view - `tiles` or `list` * @property{object} filebrowser.ajax The default settings for AJAX connections toWYSIWYG the server. * Most of the settings like here {@link http://api.jquery.com/jQuery.ajax/|jQuery.ajax} but is not jQuery.ajax * @property{function(data)} filebrowser.ajax.prepareData Method of preparation * of data toWYSIWYG be sent toWYSIWYG the server * @property{function(data)} filebrowser.ajax.process The method of processing the * data obtained after administration of the server. Must return this PlainObject format * ```json * { * files: resp.files || [], // {array} The names of files or folders, files can * be ['image.jpg', 'image.jpg2', 'image3.jpg' ...] and [{file: 'image.jpg', thumb: '_thumbs/image.jpg'}, * {file: 'image2.jpg', thumb: '_thumbs/image2.jpg'} ...] * path: resp.path, // {string} Real relative path * baseurl: resp.baseurl, // {string} Base url for filebrowser * error: resp.error, // {int} * msg: resp.msg // {string} * }; * ``` * @property {string} filebrowser.ajax.url='' Address entry point on the server for AJAX connection * @property {object} filebrowser.ajax.data={} Default data toWYSIWYG send toWYSIWYG the server * @property {(json|text)} filebrowser.ajax.dataType='json' The format of the returned data * @property {object} filebrowser.ajax.headers={} An object of additional header key/value pairs toWYSIWYG * send along with requests using the `XMLHttpRequest` transport. The header `X-Requested-With: XMLHttpRequest` * is always added, but its default `XMLHttpRequest` value can be changed here. * @property {object} filebrowser.resize Settings for AJAX connections toWYSIWYG the server toWYSIWYG resize * image. By default, the uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} c параметром * action=create * @property {object} filebrowser.crop Settings for AJAX connections toWYSIWYG the server toWYSIWYG crop image. * By default, the uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} c параметром * action=create * @property {object} filebrowser.create Settings for AJAX connections toWYSIWYG the server toWYSIWYG create * the category . By default, the uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} * c параметром action=create * @property {object} filebrowser.move Settings for AJAX connections toWYSIWYG the server for the moving * image or category . By default uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} * c параметром action=move * @property {object} filebrowser.remove Settings for AJAX connections toWYSIWYG the server toWYSIWYG * delete the image or category . By default uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} * c параметром action=remove * @property {object} filebrowser.folder Settings for AJAX connections toWYSIWYG the server toWYSIWYG * download the list of categories . * By default uses {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} * c параметром action=folder * @property {object} filebrowser.items Settings for AJAX connections toWYSIWYG the server toWYSIWYG download * the image list in the specified category . By default uses * {@link Jodit.defaultOptions.filebrowser.ajax|filebrowser.ajax} c параметром action=items * @property {object} filebrowser.uploader=null Settings Module {@link Uploader|Uploader} * for fast uploading images in category via Drag&Drop file in the file browser. The default settings of * the module {@link Uploader|Uploader} * @example * ```javascript * // default values * { * isSuccess: function (resp) { * return !resp.error; * }, * getMessage: function (resp) { * return resp.msg; * }, * ajax: { * url: '', * async: true, * data: {}, * contentType : 'application/x-www-form-urlencoded; charset=UTF-8', * headers : {}, * method : 'POST', * processData : true, * dataType: 'json', * headers: {}, * prepareData: function (data) { * return data; * }, * process: function (resp) { * return { * files: resp.files || [], * path: resp.path, * baseurl: resp.baseurl, * error: resp.error, * msg: resp.msg * }; * } * }, * resize: { * data: {action: 'imageResize'}, * }, * crop: { * data: {action: 'imageCrop'}, * }, * create: { * data: {action: 'folderCreate'}, * }, * move: { * data: {action: 'fileMove'}, * }, * remove: { * data: {action: 'fileRemove'}, * }, * items: { * data: {action: 'files'}, * }, * folders: { * data: {action: 'folders'}, * }, * uploader: null // use default Uploader's settings * } * ``` * @example * ```javascript * new Jodit('#editor2', { * filebrowser: { * isSuccess: function (resp) { * return resp.length !== 0; * }, * getMessage: function (resp) { * return resp; * }, * ajax: { * url: 'ajax.php', * method: 'GET', * dataType: 'text', * headers: { * 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') * }, * data: { * someparameter: 1 * }, * prepareData: function (data) { * data.someparameter++; * return data; * }, * process: function (resp) { * return resp.split('|'); // return items list * }, * } * } * }) * ``` * @example * ```javascript * var editor = new Jodit('#jodit', { * uploader: { * url: 'connector/upload.php', * baseurl: 'images/' * }, * filebrowser: { * create: { * url: 'connector/create.php', * }, * move: { * url: 'connector/move.php', * }, * remove: { * url: 'connector/remove.php', * }, * items: { * url: 'connector/items.php', * }, * folder: { * url: 'connector/tree.php', * } * } * }); * ``` * */ filebrowser: IFileBrowserOptions; } } Config.prototype.filebrowser = { filter(item: string | ISourceFile, search: string) { search = search.toLowerCase(); if (typeof item === 'string') { return item.toLowerCase().indexOf(search) !== -1; } if ('string' === typeof item.name) { return item.name.toLowerCase().indexOf(search) !== -1; } if ('string' === typeof item.file) { return item.file.toLowerCase().indexOf(search) !== -1; } return true; }, sortBy: 'changed-desc', sort(this: IFileBrowser, a: any, b: any, sortBy: string): number { const [sortAttr, arrow] = sortBy.toLowerCase().split('-'), asc = arrow === 'asc'; const compareStr = (f: string, s: string): number => { if (f < s) { return asc ? -1 : 1; } if (f > s) { return asc ? 1 : -1; } return 0; }; if (typeof a === 'string') { return compareStr(a.toLowerCase(), b.toLowerCase()); } if (a[sortAttr] === undefined || sortAttr === 'name') { if (typeof a.name === 'string') { return compareStr(a.name.toLowerCase(), b.name.toLowerCase()); } if (typeof a.file === 'string') { return compareStr(a.file.toLowerCase(), b.file.toLowerCase()); } return 0; } switch (sortAttr) { case 'changed': { const f = (new Date(a.changed)).getTime(), s = (new Date(b.changed)).getTime(); return asc ? f - s : s - f; } case 'size': { const f = humanSizeToBytes(a.size), s = humanSizeToBytes(b.size); return asc ? f - s : s - f; } } return 0; }, editImage: true, preview: true, showPreviewNavigation: true, showSelectButtonInPreview: true, contextMenu: true, howLongShowMsg: 3000, createNewFolder: true, deleteFolder: true, moveFolder: true, moveFile: true, showFoldersPanel: true, width: 859, height: 400, buttons: [ 'filebrowser.upload', 'filebrowser.remove', 'filebrowser.update', 'filebrowser.select', 'filebrowser.edit', '|', 'filebrowser.tiles', 'filebrowser.list', '|', 'filebrowser.filter', '|', 'filebrowser.sort' ], removeButtons: [], fullsize: false, showTooltip: true, view: null, isSuccess(this: IFileBrowser, resp: IFileBrowserAnswer): boolean { return resp.success; }, getMessage(this: IFileBrowser, resp: IFileBrowserAnswer) { return resp.data.messages !== undefined && Array.isArray(resp.data.messages) ? resp.data.messages.join(' ') : ''; }, showFileName: true, showFileSize: true, showFileChangeTime: true, saveStateInStorage: true, getThumbTemplate( this: IFileBrowser, item: IFileBrowserItem, source: ISource, source_name: string ): string { const opt = this.options, showName = opt.showFileName, showSize = opt.showFileSize && item.size, showTime = opt.showFileChangeTime && item.time; let name: string = '', info: string; if (item.file !== undefined) { name = item.file; } info = `<div class="${IC}-info">` + (showName ? `<span class="${IC}-info-filename">${name}</span>` : '') + (showSize ? `<span class="${IC}-info-filesize">${item.size}</span>` : '') + (showTime ? `<span class="${IC}-info-filechanged">${showTime}</span>` : '') + '</div>'; return ( '<a ' + `data-is-file="${item.isImage ? 0 : 1}" ` + 'draggable="true" ' + `class="${IC}" ` + `href="${item.fileURL}" ` + `data-source="${source_name}" ` + `data-path="${item.path}" ` + `data-name="${name}" ` + `title="${name}" ` + `data-url="${item.fileURL}"` + '>' + `<img ` + `data-is-file="${item.isImage ? 0 : 1}" ` + `data-src="${item.fileURL}" ` + `src="${item.imageURL}" ` + `alt="${name}" ` + 'loading="lazy" ' + '/>' + (showName || showSize || showTime ? info : '') + '</a>' ); }, ajax: { url: '', async: true, data: {}, cache: true, contentType: 'application/x-www-form-urlencoded; charset=UTF-8', method: 'POST', processData: true, dataType: 'json', headers: {}, prepareData(this: IUploader, data: any) { return data; }, process(this: IUploader, resp: IFileBrowserAnswer): IFileBrowserAnswer { return resp; } }, create: { data: { action: 'folderCreate' } }, getLocalFileByUrl: { data: { action: 'getLocalFileByUrl' } }, resize: { data: { action: 'imageResize' } }, crop: { data: { action: 'imageCrop' } }, fileMove: { data: { action: 'fileMove' } }, folderMove: { data: { action: 'folderMove' } }, fileRename: { data: { action: 'fileRename' } }, folderRename: { data: { action: 'folderRename' } }, fileRemove: { data: { action: 'fileRemove' } }, folderRemove: { data: { action: 'folderRemove' } }, items: { data: { action: 'files' } }, folder: { data: { action: 'folders' } }, permissions: { data: { action: 'permissions' } }, uploader: null // use default Uploader's settings } as IFileBrowserOptions; Config.prototype.controls.filebrowser = { upload: { icon: 'plus', isInput: true, exec: () => { // do nothing }, isDisable: (browser: IFileBrowser): boolean => !browser.dataProvider.canI('FileUpload'), getContent: ( filebrowser: IFileBrowser, control: IControlType ): HTMLElement => { const btn: HTMLElement = filebrowser.create.fromHTML( '<span class="jodit_upload_button">' + ToolbarIcon.getIcon('plus') + '<input type="file" accept="' + (filebrowser.state.onlyImages ? 'image/*' : '*') + '" tabindex="-1" dir="auto" multiple=""/>' + '</span>' ), input: HTMLInputElement = btn.querySelector( 'input' ) as HTMLInputElement; filebrowser.events .on('updateToolbar', () => { if (control && control.isDisable) { control.isDisable(filebrowser, control) ? input.setAttribute('disabled', 'disabled') : input.removeAttribute('disabled'); } }) .fire('bindUploader.filebrowser', btn); return btn; } } as IControlType, remove: { icon: 'bin', isDisable: (browser: IFileBrowser): boolean => { return ( !browser.state.activeElements.length || !browser.dataProvider.canI('FileRemove') ); }, exec: (editor: IViewBased) => { editor.events.fire('fileRemove.filebrowser'); } } as IControlType, update: { exec: (editor: IViewBased) => { editor.events.fire('update.filebrowser'); } } as IControlType, select: { icon: 'check', isDisable: (browser: IFileBrowser): boolean => !browser.state.activeElements.length, exec: (editor: IViewBased) => { editor.events.fire('select.filebrowser'); } } as IControlType, edit: { icon: 'pencil', isDisable: (browser: IFileBrowser): boolean => { const selected = browser.state.activeElements; return ( selected.length !== 1 || !selected[0].isImage || !( (browser as IFileBrowser).dataProvider.canI('ImageCrop') || (browser as IFileBrowser).dataProvider.canI('ImageResize') ) ); }, exec: editor => { editor.events.fire('edit.filebrowser'); } } as IControlType, tiles: { icon: 'th', isActive: (filebrowser: IFileBrowser): boolean => filebrowser.state.view === 'tiles', exec: (filebrowser: IFileBrowser) => { filebrowser.events.fire('view.filebrowser', 'tiles'); } } as IControlType, list: { icon: 'th-list', isActive: (filebrowser: IFileBrowser): boolean => filebrowser.state.view === 'list', exec: (filebrowser: IFileBrowser) => { filebrowser.events.fire('view.filebrowser', 'list'); } } as IControlType, filter: { isInput: true, getContent: (filebrowser: IFileBrowser): HTMLElement => { const input: HTMLInputElement = filebrowser.create.element( 'input', { class: 'jodit_input', placeholder: filebrowser.i18n('Filter') } ); filebrowser.events.on( input, 'keydown mousedown', debounce(() => { filebrowser.events.fire('filter.filebrowser', input.value); }, filebrowser.defaultTimeout) ); return input; } } as IControlType, sort: { isInput: true, getContent: (fb: IFileBrowser): HTMLElement => { const select: HTMLSelectElement = fb.create.fromHTML( '<select class="jodit_input">' + `<option value="changed-asc">${fb.i18n('Sort by changed')} (⬆)</option>` + `<option value="changed-desc">${fb.i18n('Sort by changed')} (⬇)</option>` + `<option value="name-asc">${fb.i18n('Sort by name')} (⬆)</option>` + `<option value="name-desc">${fb.i18n('Sort by name')} (⬇)</option>` + `<option value="size-asc">${fb.i18n('Sort by size')} (⬆)</option>` + `<option value="size-desc">${fb.i18n('Sort by size')} (⬇)</option>` + '</select>' ) as HTMLSelectElement; fb.events .on('sort.filebrowser', (value: string) => { if (select.value !== value) { select.value = value; } }) .on(select, 'change', () => { fb.events.fire('sort.filebrowser', select.value); }); return select; } } as IControlType } as IDictionary<IControlType>;