inital commit

This commit is contained in:
2026-01-01 15:25:19 +05:30
commit f0ae49465a
36361 changed files with 4894111 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
/*!
* 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
*/
/**
* @module modules/image-editor
*/
import type { ImageEditorOptions } from "../../types/index";
declare module 'jodit/config' {
interface Config {
imageeditor: ImageEditorOptions;
}
}

25
node_modules/jodit/esm/modules/image-editor/config.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
/*!
* 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 { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import cropIcon from "./icons/crop.svg.js";
import resizeIcon from "./icons/resize.svg.js";
Config.prototype.imageeditor = {
min_width: 20,
min_height: 20,
closeAfterSave: false,
width: '85%',
height: '85%',
crop: true,
resize: true,
resizeUseRatio: true,
resizeMinWidth: 20,
resizeMinHeight: 20,
cropUseRatio: true,
cropDefaultWidth: '70%',
cropDefaultHeight: '70%'
};
Icon.set('crop', cropIcon).set('resize', resizeIcon);

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M621 1280h595v-595zm-45-45l595-595h-595v595zm1152 77v192q0 14-9 23t-23 9h-224v224q0 14-9 23t-23 9h-192q-14 0-23-9t-9-23v-224h-864q-14 0-23-9t-9-23v-864h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224v-224q0-14 9-23t23-9h192q14 0 23 9t9 23v224h851l246-247q10-9 23-9t23 9q9 10 9 23t-9 23l-247 246v851h224q14 0 23 9t9 23z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 24 24\"> <g transform=\"translate(-251.000000, -443.000000)\"> <g transform=\"translate(215.000000, 119.000000)\"/> <path d=\"M252,448 L256,448 L256,444 L252,444 L252,448 Z M257,448 L269,448 L269,446 L257,446 L257,448 Z M257,464 L269,464 L269,462 L257,462 L257,464 Z M270,444 L270,448 L274,448 L274,444 L270,444 Z M252,462 L252,466 L256,466 L256,462 L252,462 Z M270,462 L270,466 L274,466 L274,462 L270,462 Z M254,461 L256,461 L256,449 L254,449 L254,461 Z M270,461 L272,461 L272,449 L270,449 L270,461 Z\"/> </g> </svg> ";

View File

@@ -0,0 +1,119 @@
/*!
* 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
*/
/**
* [[include:modules/image-editor/README.md]]
* @packageDocumentation
* @module modules/image-editor
*/
import type { IDialog, IDlgs, IFileBrowserDataProvider, ImageEditorActionBox, ImageEditorOptions, IViewWithToolbar } from "../../types/index";
import { ViewComponent } from "../../core/component/index";
import "./config";
interface onSave {
(
/**
* new filename
*/
newname: string | void, box: ImageEditorActionBox,
/**
* called after success operation
*/
success: () => void,
/**
* called after failed operation
*/
failed: (error: Error) => void): void;
}
/**
* The module allows you to edit the image: resize or cut any part of it
*
*/
export declare class ImageEditor extends ViewComponent<IViewWithToolbar & IDlgs> {
/** @override */
className(): string;
options: ImageEditorOptions;
get o(): this['options'];
private onSave;
/**
* Hide image editor
*/
hide(): void;
/**
* Open image editor
* @example
* ```javascript
* const jodit = Jodit.make('.editor', {
* imageeditor: {
* crop: false,
* closeAfterSave: true,
* width: 500
* }
* });
* jodit.imageeditor.open('https://xdsoft.net/jodit/images/test.png', function (name, data, success, failed) {
* var img = jodit.node.c('img');
* img.setAttribute('src', 'https://xdsoft.net/jodit/images/test.png');
* if (box.action !== 'resize') {
* return failed('Sorry it is work only in resize mode. For croping use FileBrowser');
* }
* img.style.width = data.w;
* img.style.height = data.h;
* jodit.s.insertNode(img);
* success();
* });
* ```
*/
open(url: string, save: onSave): Promise<IDialog>;
private resizeUseRatio;
private cropUseRatio;
private readonly _dialog;
private image;
private cropImage;
private clicked;
private target;
private start_x;
private start_y;
private top_x;
private top_y;
private width;
private height;
private activeTab;
private naturalWidth;
private naturalHeight;
private ratio;
private new_h;
private new_w;
private diff_x;
private diff_y;
private readonly buttons;
private readonly editor;
private readonly resize_box;
private readonly crop_box;
private sizes;
private readonly resizeHandler;
private readonly cropHandler;
private readonly cropBox;
private readonly resizeBox;
private static calcValueByPercent;
private calcCropBox;
private showCrop;
private updateCropBox;
private updateResizeBox;
private setHandlers;
private onTitleModeClick;
private onChangeSizeInput;
private onResizeHandleMouseDown;
private onGlobalMouseUp;
private onGlobalMouseMove;
constructor(editor: IViewWithToolbar & IDlgs);
/** @override */
destruct(): any;
}
/**
* Open Image Editor
*/
export declare function openImageEditor(this: IViewWithToolbar & {
dataProvider: IFileBrowserDataProvider;
}, href: string, name: string, path: string, source: string, onSuccess?: () => void, onFailed?: (error: Error) => void): Promise<IDialog>;
export {};

View File

@@ -0,0 +1,577 @@
/*!
* 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
*/
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;
};
var ImageEditor_1;
import { ViewComponent } from "../../core/component/index.js";
import { autobind, component, debounce, throttle } from "../../core/decorators/index.js";
import { Dom } from "../../core/dom/index.js";
import { $$, attr, call, css, refs, toArray, trim } from "../../core/helpers/index.js";
import { Button } from "../../core/ui/button/index.js";
import { Config } from "../../config.js";
import "./config.js";
import { form } from "./templates/form.js";
const jie = 'jodit-image-editor';
const TABS = {
resize: 'resize',
crop: 'crop'
};
/**
* The module allows you to edit the image: resize or cut any part of it
*
*/
let ImageEditor = ImageEditor_1 = class ImageEditor extends ViewComponent {
/** @override */
className() {
return 'ImageEditor';
}
get o() {
return this.options;
}
/**
* Hide image editor
*/
hide() {
this._dialog.close();
}
/**
* Open image editor
* @example
* ```javascript
* const jodit = Jodit.make('.editor', {
* imageeditor: {
* crop: false,
* closeAfterSave: true,
* width: 500
* }
* });
* jodit.imageeditor.open('https://xdsoft.net/jodit/images/test.png', function (name, data, success, failed) {
* var img = jodit.node.c('img');
* img.setAttribute('src', 'https://xdsoft.net/jodit/images/test.png');
* if (box.action !== 'resize') {
* return failed('Sorry it is work only in resize mode. For croping use FileBrowser');
* }
* img.style.width = data.w;
* img.style.height = data.h;
* jodit.s.insertNode(img);
* success();
* });
* ```
*/
open(url, save) {
return this.j.async.promise((resolve) => {
const timestamp = new Date().getTime();
this.image = this.j.c.element('img');
$$('img,.jodit-icon_loader', this.resize_box).forEach(Dom.safeRemove);
$$('img,.jodit-icon_loader', this.crop_box).forEach(Dom.safeRemove);
css(this.cropHandler, 'background', 'transparent');
this.onSave = save;
this.resize_box.appendChild(this.j.c.element('i', { class: 'jodit-icon_loader' }));
this.crop_box.appendChild(this.j.c.element('i', { class: 'jodit-icon_loader' }));
if (/\?/.test(url)) {
url += '&_tst=' + timestamp;
}
else {
url += '?_tst=' + timestamp;
}
this.image.setAttribute('src', url);
this._dialog.open();
const { widthInput, heightInput } = refs(this.editor);
const onload = () => {
if (this.isDestructed) {
return;
}
this.image.removeEventListener('load', onload);
this.naturalWidth = this.image.naturalWidth;
this.naturalHeight = this.image.naturalHeight;
widthInput.value = this.naturalWidth.toString();
heightInput.value = this.naturalHeight.toString();
this.ratio = this.naturalWidth / this.naturalHeight;
this.resize_box.appendChild(this.image);
this.cropImage = this.image.cloneNode(true);
this.crop_box.appendChild(this.cropImage);
Dom.safeRemove.apply(null, $$('.jodit-icon_loader', this.editor));
if (this.activeTab === TABS.crop) {
this.showCrop();
}
this.j.e.fire(this.resizeHandler, 'updatesize');
this.j.e.fire(this.cropHandler, 'updatesize');
this._dialog.setPosition();
this.j.e.fire('afterImageEditor');
resolve(this._dialog);
};
this.image.addEventListener('load', onload);
if (this.image.complete) {
onload();
}
});
}
onTitleModeClick(e) {
const self = this, title = e.target;
const slide = title === null || title === void 0 ? void 0 : title.parentElement;
if (!slide) {
return;
}
$$(`.${jie}__slider,.${jie}__area`, self.editor).forEach(elm => elm.classList.remove(`${jie}_active`));
slide.classList.add(`${jie}_active`);
this.activeTab = attr(slide, '-area') || TABS.resize;
const tab = self.editor.querySelector(`.${jie}__area.${jie}__area_` + self.activeTab);
if (tab) {
tab.classList.add(`${jie}_active`);
}
if (self.activeTab === TABS.crop) {
self.showCrop();
}
}
onChangeSizeInput(e) {
const self = this, input = e.target, { widthInput, heightInput } = refs(this.editor), isWidth = attr(input, 'data-ref') === 'widthInput', x = parseInt(input.value, 10), minX = isWidth ? self.o.min_width : self.o.min_height, minY = !isWidth ? self.o.min_width : self.o.min_height;
let y;
if (x > minX) {
css(self.image, isWidth ? 'width' : 'height', x);
if (self.resizeUseRatio) {
y = isWidth
? Math.round(x / self.ratio)
: Math.round(x * self.ratio);
if (y > minY) {
css(self.image, !isWidth ? 'width' : 'height', y);
if (isWidth) {
heightInput.value = y.toString();
}
else {
widthInput.value = y.toString();
}
}
}
}
this.j.e.fire(self.resizeHandler, 'updatesize');
}
onResizeHandleMouseDown(e) {
const self = this;
self.target = e.target;
e.preventDefault();
e.stopImmediatePropagation();
self.clicked = true;
self.start_x = e.clientX;
self.start_y = e.clientY;
if (self.activeTab === TABS.crop) {
self.top_x = css(self.cropHandler, 'left');
self.top_y = css(self.cropHandler, 'top');
self.width = self.cropHandler.offsetWidth;
self.height = self.cropHandler.offsetHeight;
}
else {
self.width = self.image.offsetWidth;
self.height = self.image.offsetHeight;
}
self.j.e
.on(this.j.ow, 'mousemove', this.onGlobalMouseMove)
.one(this.j.ow, 'mouseup', this.onGlobalMouseUp);
}
onGlobalMouseUp(e) {
if (this.clicked) {
this.clicked = false;
e.stopImmediatePropagation();
this.j.e.off(this.j.ow, 'mousemove', this.onGlobalMouseMove);
}
}
onGlobalMouseMove(e) {
const self = this;
if (!self.clicked) {
return;
}
const { widthInput, heightInput } = refs(this.editor);
self.diff_x = e.clientX - self.start_x;
self.diff_y = e.clientY - self.start_y;
if ((self.activeTab === TABS.resize && self.resizeUseRatio) ||
(self.activeTab === TABS.crop && self.cropUseRatio)) {
if (self.diff_x) {
self.new_w = self.width + self.diff_x;
self.new_h = Math.round(self.new_w / self.ratio);
}
else {
self.new_h = self.height + self.diff_y;
self.new_w = Math.round(self.new_h * self.ratio);
}
}
else {
self.new_w = self.width + self.diff_x;
self.new_h = self.height + self.diff_y;
}
if (self.activeTab === TABS.resize) {
if (self.new_w > self.o.resizeMinWidth) {
css(self.image, 'width', self.new_w + 'px');
widthInput.value = self.new_w.toString();
}
if (self.new_h > self.o.resizeMinHeight) {
css(self.image, 'height', self.new_h + 'px');
heightInput.value = self.new_h.toString();
}
this.j.e.fire(self.resizeHandler, 'updatesize');
}
else {
if (self.target !== self.cropHandler) {
if (self.top_x + self.new_w > self.cropImage.offsetWidth) {
self.new_w = self.cropImage.offsetWidth - self.top_x;
}
if (self.top_y + self.new_h > self.cropImage.offsetHeight) {
self.new_h = self.cropImage.offsetHeight - self.top_y;
}
css(self.cropHandler, {
width: self.new_w,
height: self.new_h
});
}
else {
if (self.top_x + self.diff_x + self.cropHandler.offsetWidth >
self.cropImage.offsetWidth) {
self.diff_x =
self.cropImage.offsetWidth -
self.top_x -
self.cropHandler.offsetWidth;
}
css(self.cropHandler, 'left', self.top_x + self.diff_x);
if (self.top_y + self.diff_y + self.cropHandler.offsetHeight >
self.cropImage.offsetHeight) {
self.diff_y =
self.cropImage.offsetHeight -
self.top_y -
self.cropHandler.offsetHeight;
}
css(self.cropHandler, 'top', self.top_y + self.diff_y);
}
this.j.e.fire(self.cropHandler, 'updatesize');
}
}
constructor(editor) {
super(editor);
this.resizeUseRatio = true;
this.cropUseRatio = true;
this.clicked = false;
this.start_x = 0;
this.start_y = 0;
this.top_x = 0;
this.top_y = 0;
this.width = 0;
this.height = 0;
this.activeTab = TABS.resize;
this.naturalWidth = 0;
this.naturalHeight = 0;
this.ratio = 0;
this.new_h = 0;
this.new_w = 0;
this.diff_x = 0;
this.diff_y = 0;
this.cropBox = {
x: 0,
y: 0,
w: 0,
h: 0
};
this.resizeBox = {
w: 0,
h: 0
};
this.calcCropBox = () => {
const node = this.crop_box.parentNode, w = node.offsetWidth * 0.8, h = node.offsetHeight * 0.8;
let wn = w, hn = h;
const { naturalWidth: nw, naturalHeight: nh } = this;
if (w > nw && h > nh) {
wn = nw;
hn = nh;
}
else if (this.ratio > w / h) {
wn = w;
hn = nh * (w / nw);
}
else {
wn = nw * (h / nh);
hn = h;
}
css(this.crop_box, {
width: wn,
height: hn
});
};
this.showCrop = () => {
if (!this.cropImage) {
return;
}
this.calcCropBox();
const w = this.cropImage.offsetWidth ||
this.image.offsetWidth ||
this.image.naturalWidth;
this.new_w = ImageEditor_1.calcValueByPercent(w, this.o.cropDefaultWidth);
const h = this.cropImage.offsetHeight ||
this.image.offsetHeight ||
this.image.naturalHeight;
if (this.cropUseRatio) {
this.new_h = this.new_w / this.ratio;
}
else {
this.new_h = ImageEditor_1.calcValueByPercent(h, this.o.cropDefaultHeight);
}
css(this.cropHandler, {
backgroundImage: 'url(' + attr(this.cropImage, 'src') + ')',
width: this.new_w,
height: this.new_h,
left: w / 2 - this.new_w / 2,
top: h / 2 - this.new_h / 2
});
this.j.e.fire(this.cropHandler, 'updatesize');
};
this.updateCropBox = () => {
if (!this.cropImage) {
return;
}
const ratioX = this.cropImage.offsetWidth / this.naturalWidth, ratioY = this.cropImage.offsetHeight / this.naturalHeight;
this.cropBox.x = css(this.cropHandler, 'left') / ratioX;
this.cropBox.y = css(this.cropHandler, 'top') / ratioY;
this.cropBox.w = this.cropHandler.offsetWidth / ratioX;
this.cropBox.h = this.cropHandler.offsetHeight / ratioY;
this.sizes.textContent =
this.cropBox.w.toFixed(0) + 'x' + this.cropBox.h.toFixed(0);
};
this.updateResizeBox = () => {
this.resizeBox.w = this.image.offsetWidth || this.naturalWidth;
this.resizeBox.h = this.image.offsetHeight || this.naturalHeight;
};
this.setHandlers = () => {
const self = this;
const { widthInput, heightInput } = refs(this.editor);
self.j.e
.on([
self.editor.querySelector('.jodit_bottomright'),
self.cropHandler
], `mousedown.${jie}`, this.onResizeHandleMouseDown)
.on(this.j.ow, `resize.${jie}`, () => {
this.j.e.fire(self.resizeHandler, 'updatesize');
self.showCrop();
this.j.e.fire(self.cropHandler, 'updatesize');
});
self.j.e
.on(toArray(this.editor.querySelectorAll(`.${jie}__slider-title`)), 'click', this.onTitleModeClick)
.on([widthInput, heightInput], 'input', this.onChangeSizeInput);
const { keepAspectRatioResize, keepAspectRatioCrop } = refs(this.editor);
if (keepAspectRatioResize) {
keepAspectRatioResize.addEventListener('change', () => {
this.resizeUseRatio = keepAspectRatioResize.checked;
});
}
if (keepAspectRatioCrop) {
keepAspectRatioCrop.addEventListener('change', () => {
this.cropUseRatio = keepAspectRatioCrop.checked;
});
}
self.j.e
.on(self.resizeHandler, 'updatesize', () => {
css(self.resizeHandler, {
top: 0,
left: 0,
width: self.image.offsetWidth || self.naturalWidth,
height: self.image.offsetHeight || self.naturalHeight
});
this.updateResizeBox();
})
.on(self.cropHandler, 'updatesize', () => {
if (!self.cropImage) {
return;
}
let new_x = css(self.cropHandler, 'left'), new_y = css(self.cropHandler, 'top'), new_width = self.cropHandler.offsetWidth, new_height = self.cropHandler.offsetHeight;
if (new_x < 0) {
new_x = 0;
}
if (new_y < 0) {
new_y = 0;
}
if (new_x + new_width > self.cropImage.offsetWidth) {
new_width = self.cropImage.offsetWidth - new_x;
if (self.cropUseRatio) {
new_height = new_width / self.ratio;
}
}
if (new_y + new_height > self.cropImage.offsetHeight) {
new_height = self.cropImage.offsetHeight - new_y;
if (self.cropUseRatio) {
new_width = new_height * self.ratio;
}
}
css(self.cropHandler, {
width: new_width,
height: new_height,
left: new_x,
top: new_y,
backgroundPosition: -new_x - 1 + 'px ' + (-new_y - 1) + 'px',
backgroundSize: self.cropImage.offsetWidth +
'px ' +
self.cropImage.offsetHeight +
'px'
});
self.updateCropBox();
});
Object.values(self.buttons).forEach(button => {
button.onAction(() => {
const data = {
action: self.activeTab,
box: self.activeTab === TABS.resize
? self.resizeBox
: self.cropBox
};
switch (button) {
case self.buttons.saveas:
self.j.prompt('Enter new name', 'Save in new file', (name) => {
if (!trim(name)) {
self.j.alert('The name should not be empty');
return false;
}
self.onSave(name, data, self.hide, (e) => {
self.j.alert(e.message);
});
});
break;
case self.buttons.save:
self.onSave(undefined, data, self.hide, (e) => {
self.j.alert(e.message);
});
break;
case self.buttons.reset:
if (self.activeTab === TABS.resize) {
css(self.image, {
width: null,
height: null
});
widthInput.value = self.naturalWidth.toString();
heightInput.value = self.naturalHeight.toString();
self.j.e.fire(self.resizeHandler, 'updatesize');
}
else {
self.showCrop();
}
break;
}
});
});
};
this.options =
editor && editor.o && editor.o.imageeditor
? editor.o.imageeditor
: Config.defaultOptions.imageeditor;
const o = this.options;
this.resizeUseRatio = o.resizeUseRatio;
this.cropUseRatio = o.cropUseRatio;
this.buttons = {
reset: Button(this.j, 'update', 'Reset'),
save: Button(this.j, 'save', 'Save'),
saveas: Button(this.j, 'save', 'Save as ...')
};
this.activeTab = o.resize ? TABS.resize : TABS.crop;
this.editor = form(this.j, this.options);
const { resizeBox, cropBox } = refs(this.editor);
this.resize_box = resizeBox;
this.crop_box = cropBox;
this.sizes = this.editor.querySelector(`.${jie}__area.${jie}__area_crop .jodit-image-editor__sizes`);
this.resizeHandler = this.editor.querySelector(`.${jie}__resizer`);
this.cropHandler = this.editor.querySelector(`.${jie}__croper`);
this._dialog = this.j.dlg({
buttons: ['fullsize', 'dialog.close']
});
this._dialog.setContent(this.editor);
this._dialog.setSize(this.o.width, this.o.height);
this._dialog.setHeader([
this.buttons.reset,
this.buttons.save,
this.buttons.saveas
]);
this.setHandlers();
}
/** @override */
destruct() {
if (this.isDestructed) {
return;
}
if (this._dialog && !this._dialog.isInDestruct) {
this._dialog.destruct();
}
Dom.safeRemove(this.editor);
if (this.j.e) {
this.j.e
.off(this.j.ow, 'mousemove', this.onGlobalMouseMove)
.off(this.j.ow, 'mouseup', this.onGlobalMouseUp)
.off(this.ow, `.${jie}`)
.off(`.${jie}`);
}
super.destruct();
}
};
ImageEditor.calcValueByPercent = (value, percent) => {
const percentStr = percent.toString();
const valueNbr = parseFloat(value.toString());
let match;
match = /^[-+]?[0-9]+(px)?$/.exec(percentStr);
if (match) {
return parseInt(percentStr, 10);
}
match = /^([-+]?[0-9.]+)%$/.exec(percentStr);
if (match) {
return Math.round(valueNbr * (parseFloat(match[1]) / 100));
}
return valueNbr || 0;
};
__decorate([
autobind
], ImageEditor.prototype, "hide", null);
__decorate([
autobind
], ImageEditor.prototype, "open", null);
__decorate([
autobind
], ImageEditor.prototype, "onTitleModeClick", null);
__decorate([
debounce(),
autobind
], ImageEditor.prototype, "onChangeSizeInput", null);
__decorate([
autobind
], ImageEditor.prototype, "onResizeHandleMouseDown", null);
__decorate([
autobind
], ImageEditor.prototype, "onGlobalMouseUp", null);
__decorate([
throttle(10)
], ImageEditor.prototype, "onGlobalMouseMove", null);
ImageEditor = ImageEditor_1 = __decorate([
component
], ImageEditor);
export { ImageEditor };
/**
* Open Image Editor
*/
export function openImageEditor(href, name, path, source, onSuccess, onFailed) {
return this.getInstance('ImageEditor', this.o).open(href, (newname, box, success, failed) => call(box.action === 'resize'
? this.dataProvider.resize
: this.dataProvider.crop, path, source, name, newname, box.box)
.then(ok => {
if (ok) {
success();
if (onSuccess) {
onSuccess();
}
}
})
.catch(error => {
failed(error);
if (onFailed) {
onFailed(error);
}
}));
}

View File

@@ -0,0 +1,10 @@
/*!
* 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
*/
/**
* @module modules/image-editor
*/
import type { ImageEditorOptions, IViewBased } from "../../../types/index";
export declare const form: (editor: IViewBased, o: ImageEditorOptions) => HTMLElement;

View File

@@ -0,0 +1,81 @@
/*!
* 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 { Icon } from "../../../core/ui/index.js";
const jie = 'jodit-image-editor';
const gi = Icon.get.bind(Icon);
const act = (el, className = 'jodti-image-editor_active') => el ? className : '';
export const form = (editor, o) => {
const i = editor.i18n.bind(editor);
const switcher = (label, ref, active = true) => `<div class="jodit-form__group">
<label class="jodit-switcher-wrapper">
<span class='jodit-switcher'>
<input ${act(active, 'checked')} data-ref="${ref}" type="checkbox"/>
<span class="jodit-switcher__slider"></span>
</span>
<span>${i(label)}</span>
</label>
</div>`;
return editor.create.fromHTML(`<form class="${jie} jodit-properties">
<div class="jodit-grid jodit-grid_xs-column">
<div class="jodit_col-lg-3-4 jodit_col-sm-5-5">
${o.resize
? `<div class="${jie}__area ${jie}__area_resize ${jie}_active">
<div data-ref="resizeBox" class="${jie}__box"></div>
<div class="${jie}__resizer">
<i class="jodit_bottomright"></i>
</div>
</div>`
: ''}
${o.crop
? `<div class="${jie}__area ${jie}__area_crop ${act(!o.resize)}">
<div data-ref="cropBox" class="${jie}__box">
<div class="${jie}__croper">
<i class="jodit_bottomright"></i>
<i class="${jie}__sizes"></i>
</div>
</div>
</div>`
: ''}
</div>
<div class="jodit_col-lg-1-4 jodit_col-sm-5-5">
${o.resize
? `<div data-area="resize" class="${jie}__slider ${jie}_active">
<div class="${jie}__slider-title">
${gi('resize')}
${i('Resize')}
</div>
<div class="${jie}__slider-content">
<div class="jodit-form__group">
<label>
${i('Width')}
</label>
<input type="number" data-ref="widthInput" class="jodit-input"/>
</div>
<div class="jodit-form__group">
<label>
${i('Height')}
</label>
<input type="number" data-ref="heightInput" class="jodit-input"/>
</div>
${switcher('Keep Aspect Ratio', 'keepAspectRatioResize')}
</div>
</div>`
: ''}
${o.crop
? `<div data-area="crop" class="${jie}__slider ${act(!o.resize)}'">
<div class="${jie}__slider-title">
${gi('crop')}
${i('Crop')}
</div>
<div class="${jie}__slider-content">
${switcher('Keep Aspect Ratio', 'keepAspectRatioCrop')}
</div>
</div>`
: ''}
</div>
</div>
</form>`);
};