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 plugins/image-properties
*/
import type { ImagePropertiesOptions } from "./interface";
declare module 'jodit/config' {
interface Config {
image: ImagePropertiesOptions;
}
}

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 { Config } from "../../config.js";
Config.prototype.image = {
dialogWidth: 600,
openOnDblClick: true,
editSrc: true,
useImageEditor: true,
editTitle: true,
editAlt: true,
editLink: true,
editSize: true,
editBorderRadius: true,
editMargins: true,
editClass: true,
availableClasses: [],
editStyle: true,
editId: true,
editAlign: true,
showPreview: true,
selectImageAfterClose: true
};

View File

@@ -0,0 +1,62 @@
/*!
* 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:plugins/image-properties/README.md]]
* @packageDocumentation
* @module plugins/image-properties
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../core/plugin/plugin";
import "./config";
import type { ImagePropertiesState } from "./interface";
/**
* Plug-in for image editing window
*
* @example
* ```javascript
* const editor = Jodit.make('#editor', {
* image: {
* editSrc: false,
* editLink: false
* }
* });
* ```
*/
/**
* Show dialog with image's options
*/
export declare class imageProperties extends Plugin {
protected state: ImagePropertiesState;
private activeTabState;
private get form();
/**
* Dialog for form
*/
private get dialog();
private get __buttons();
/**
* Open dialog editing image properties
*
* @example
* ```javascript
* const editor = Jodit.makeJodit('#editor');
* img = editor.createInside.element('img');
*
* img.setAttribute('src', 'images/some-image.png');
* editor.s.insertImage(img);
* // open the properties of the editing window
* editor.events.fire('openImageProperties', img);
* ```
*/
protected open(): void | false;
private __lock;
private __unlock;
/** @override **/
protected afterInit(editor: IJodit): void;
protected onStateValuesImageSrcChange(): Promise<void>;
/** @override */
protected beforeDestruct(editor: IJodit): void;
}

View File

@@ -0,0 +1,246 @@
/*!
* 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;
};
import { cache, cached, watch } from "../../core/decorators/index.js";
import { Dom } from "../../core/dom/dom.js";
import { pluginSystem } from "../../core/global.js";
import { isAbortError, isNumeric, markOwner } from "../../core/helpers/index.js";
import { Plugin } from "../../core/plugin/plugin.js";
import { Button } from "../../core/ui/button/index.js";
import "./config.js";
import { UIImagePropertiesForm } from "./ui/ui-image-form.js";
import { openImageEditorDialog } from "./utils/open-image-editor.js";
import { openImagePopup } from "./utils/open-image-popup.js";
import { readValuesFromImage } from "./readers/index.js";
import { applyValuesToImage } from "./writers/index.js";
/**
* Plug-in for image editing window
*
* @example
* ```javascript
* const editor = Jodit.make('#editor', {
* image: {
* editSrc: false,
* editLink: false
* }
* });
* ```
*/
/**
* Show dialog with image's options
*/
export class imageProperties extends Plugin {
constructor() {
super(...arguments);
this.state = {
image: new Image(),
sourceImage: new Image(),
get ratio() {
const { naturalWidth, naturalHeight } = this.image;
return naturalWidth / naturalHeight || 1;
},
sizeIsLocked: true,
marginIsLocked: true,
values: {
style: '',
imageSrc: '',
borderRadius: 0,
imageTitle: '',
imageAlt: '',
imageLink: '',
imageLinkOpenInNewTab: false,
imageWidth: 0,
imageHeight: 0,
marginTop: 0,
marginRight: 0,
marginBottom: 0,
marginLeft: 0,
classes: '',
id: '',
align: ''
}
};
this.activeTabState = {
activeTab: 'Image'
};
}
get form() {
return new UIImagePropertiesForm(this.j, this.state, this.activeTabState, {
openImageEditor: () => openImageEditorDialog(this.j, this.state),
openImagePopup: target => openImagePopup(this.j, this.dialog, this.state, target)
});
}
/**
* Dialog for form
*/
get dialog() {
const { j } = this;
const dialog = j.dlg({
minWidth: Math.min(400, screen.width),
minHeight: 590,
buttons: ['fullsize', 'dialog.close']
});
const buttons = this.__buttons;
buttons.check.onAction(() => {
applyValuesToImage(j, this.state, this.state.sourceImage);
j.synchronizeValues();
dialog.close();
});
buttons.remove.onAction(() => {
j.s.removeNode(this.state.sourceImage);
dialog.close();
});
buttons.cancel.onAction(() => {
dialog.close();
});
dialog.setHeader(j.i18n('Image properties'));
dialog.setContent(this.form);
dialog.setFooter([[buttons.cancel, buttons.remove], buttons.check]);
j.e.on(dialog, 'afterClose', () => {
if (this.state.image.parentNode &&
j.o.image.selectImageAfterClose) {
j.s.select(this.state.sourceImage);
}
});
dialog.setSize(j.o.image.dialogWidth);
markOwner(j, dialog.container);
return dialog;
}
get __buttons() {
const { j } = this;
return {
check: Button(j, 'ok', 'Apply', 'primary'),
remove: Button(j, 'bin', 'Delete'),
cancel: Button(j, 'cancel', 'Cancel')
};
}
/**
* Open dialog editing image properties
*
* @example
* ```javascript
* const editor = Jodit.makeJodit('#editor');
* img = editor.createInside.element('img');
*
* img.setAttribute('src', 'images/some-image.png');
* editor.s.insertImage(img);
* // open the properties of the editing window
* editor.events.fire('openImageProperties', img);
* ```
*/
open() {
this.activeTabState.activeTab = 'Image';
this.__lock();
this.dialog.open().setModal(true).setPosition();
this.async
.promise((resolve, reject) => readValuesFromImage(this.j, this.state).then(resolve, reject))
.catch((e) => {
if (!isAbortError(e)) {
this.dialog.message.error(e.message);
}
})
.finally(() => this.__unlock());
return false;
}
__lock() {
this.dialog.lock();
this.form.setMod('lock', true);
Object.values(this.__buttons).forEach(b => (b.state.disabled = true));
}
__unlock() {
this.dialog.unlock();
this.form.setMod('lock', false);
Object.values(this.__buttons).forEach(b => (b.state.disabled = false));
}
/** @override **/
afterInit(editor) {
const self = this;
editor.e
.on('afterConstructor changePlace', () => {
editor.e
.off(editor.editor, '.imageproperties')
.on(editor.editor, 'dblclick.imageproperties', (e) => {
const image = e.target;
if (!Dom.isTag(image, 'img')) {
return;
}
if (editor.o.image.openOnDblClick) {
if (this.j.e.fire('openOnDblClick', image) ===
false) {
return;
}
self.state.sourceImage = image;
self.state.image = image.cloneNode(true);
if (!editor.o.readonly) {
e.stopImmediatePropagation();
e.preventDefault();
self.open();
}
}
else {
e.stopImmediatePropagation();
editor.s.select(image);
}
});
})
.on('openImageProperties.imageproperties', (image) => {
self.state.sourceImage = image;
this.state.image = image.cloneNode(true);
this.open();
});
}
async onStateValuesImageSrcChange() {
const { image, values } = this.state;
if (!image.src) {
return;
}
try {
this.__lock();
await image.decode();
if (this.state.sizeIsLocked && isNumeric(values.imageWidth)) {
const w = parseFloat(values.imageWidth.toString());
values.imageHeight = Math.round(w / this.state.ratio);
}
this.j.e.fire('updateImageProperties.imageproperties', image);
}
catch (e) {
this.j.alert(e.message);
}
finally {
this.__unlock();
}
}
/** @override */
beforeDestruct(editor) {
var _a, _b, _c;
Object.values((_a = cached(this, '__buttons')) !== null && _a !== void 0 ? _a : {}).forEach(b => b.destruct());
(_b = cached(this, 'dialog')) === null || _b === void 0 ? void 0 : _b.destruct();
(_c = cached(this, 'form')) === null || _c === void 0 ? void 0 : _c.destruct();
editor.e.off(editor.editor, '.imageproperties').off('.imageproperties');
}
}
__decorate([
cache
], imageProperties.prototype, "form", null);
__decorate([
cache
], imageProperties.prototype, "dialog", null);
__decorate([
cache
], imageProperties.prototype, "__buttons", null);
__decorate([
watch('state.image')
], imageProperties.prototype, "onStateValuesImageSrcChange", null);
pluginSystem.add('imageProperties', imageProperties);

View File

@@ -0,0 +1,121 @@
/*!
* 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 plugins/image-properties
*/
import type { ImageHAlign } from "../../types/index";
export type EditValues = {
style: string;
imageSrc: string;
borderRadius: number;
imageTitle: string;
imageAlt: string;
imageLink: string;
imageLinkOpenInNewTab: boolean;
imageWidth: number | string;
imageHeight: number | string;
marginTop: number | string;
marginRight: number | string;
marginBottom: number | string;
marginLeft: number | string;
classes: string;
id: string;
align: ImageHAlign;
};
export interface ImagePropertiesState {
image: HTMLImageElement;
sourceImage: HTMLImageElement;
ratio: number;
sizeIsLocked: boolean;
marginIsLocked: boolean;
values: EditValues;
}
export interface ImagePropertiesAPI {
openImageEditor: () => void;
openImagePopup: (target: HTMLElement) => void;
}
export interface ImagePropertiesOptions {
dialogWidth: number;
/**
* Open editing dialog after double click on image
*/
openOnDblClick: boolean;
/**
* Show edit 'src' input
*/
editSrc: boolean;
/**
* Show crop/resize btn
*/
useImageEditor: boolean;
/**
* Show edit 'title' input
*/
editTitle: boolean;
/**
* Show edit 'alt' input
*/
editAlt: boolean;
/**
* Show edit image link's options
*/
editLink: boolean;
/**
* Show edit image size's inputs
*/
editSize: boolean;
/**
* Show edit margin inputs
*/
editMargins: boolean;
editBorderRadius: boolean;
/**
* Show edit classNames input
*/
editClass: boolean;
/**
* Pre-define available classes to select from
*
* Classes can be provided as list of strings or as list of tuples
* `["classname", "human label"]`.
*
* @example
* ```javascript
* new Jodit('#editor', {
* image: {
* availableClasses: [
* "rte-image-width-50",
* ["rte-image-width-75", "75 % width"]
* ]
* }
* })
* ```
*/
availableClasses: [
string,
string
][] | string[];
/**
* Show style edit input
*/
editStyle: boolean;
/**
* Show edit ID input
*/
editId: boolean;
/**
* Show Alignment selector
*/
editAlign: boolean;
/**
* Show preview image
*/
showPreview: boolean;
/**
* Select image after close dialog
*/
selectImageAfterClose: boolean;
}

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
*/
import type { EditValues } from "../interface";
/**
* @private
*/
export declare function readAlign(image: HTMLImageElement, values: EditValues): void;

View File

@@ -0,0 +1,26 @@
/*!
* 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 { css } from "../../../core/helpers/utils/css.js";
/**
* @private
*/
export function readAlign(image, values) {
// Align
if (image.style.cssFloat &&
['left', 'right'].indexOf(image.style.cssFloat.toLowerCase()) !== -1) {
values.align = css(image, 'float');
}
else {
if (css(image, 'display') === 'block' &&
image.style.marginLeft === 'auto' &&
image.style.marginRight === 'auto') {
values.align = 'center';
}
else {
values.align = '';
}
}
}

View File

@@ -0,0 +1,15 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import type { ImagePropertiesState } from "../interface";
/**
* Read values from image and set it to state
* @private
*/
export declare function readValuesFromImage(j: IJodit, state: ImagePropertiesState): Promise<void>;

View File

@@ -0,0 +1,38 @@
/*!
* 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 { attr } from "../../../core/helpers/utils/attr.js";
import { readAlign } from "./align.js";
import { readLink } from "./link.js";
import { readMargins } from "./margin.js";
import { readSizes } from "./size.js";
/**
* Read values from image and set it to state
* @private
*/
export async function readValuesFromImage(j, state) {
const { sourceImage: image, values } = state;
readAlign(image, values);
// Border radius
values.borderRadius = parseInt(image.style.borderRadius || '0', 10) || 0;
// Id
values.id = attr(image, 'id') || '';
// Title
values.imageTitle = attr(image, 'title') || '';
// Alt
values.imageAlt = attr(image, 'alt') || '';
// Style
values.style = attr(image, 'style') || '';
// Classes
values.classes = (attr(image, 'class') || '').replace(/jodit_focused_image[\s]*/, '');
// Margins
readMargins(image, values, state);
// Link
readLink(state, j, values);
// Src
values.imageSrc = attr(image, 'src') || '';
// Image size
return readSizes(image, values, state);
}

View File

@@ -0,0 +1,12 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import type { EditValues, ImagePropertiesState } from "../interface";
/** @private */
export declare function readLink(state: ImagePropertiesState, j: IJodit, values: EditValues): void;

View File

@@ -0,0 +1,19 @@
/*!
* 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 { Dom } from "../../../core/dom/dom.js";
import { attr } from "../../../core/helpers/utils/attr.js";
/** @private */
export function readLink(state, j, values) {
const a = Dom.closest(state.sourceImage, 'a', j.editor);
if (a) {
values.imageLink = attr(a, 'href') || '';
values.imageLinkOpenInNewTab = attr(a, 'target') === '_blank';
}
else {
values.imageLink = '';
values.imageLinkOpenInNewTab = false;
}
}

View File

@@ -0,0 +1,8 @@
/*!
* 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 type { EditValues, ImagePropertiesState } from "../interface";
/** @private */
export declare function readMargins(image: HTMLImageElement, values: EditValues, state: ImagePropertiesState): void;

View File

@@ -0,0 +1,31 @@
/*!
* 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 plugins/image-properties
*/
import { kebabCase } from "../../../core/helpers/string/kebab-case.js";
/** @private */
export function readMargins(image, values, state) {
// Margins
let equal = true, wasEmptyField = false;
['marginTop', 'marginRight', 'marginBottom', 'marginLeft'].forEach(id => {
let value = image.style.getPropertyValue(kebabCase(id));
if (!value) {
wasEmptyField = true;
values[id] = 0;
return;
}
if (/^[0-9]+(px)?$/.test(value)) {
value = parseInt(value, 10);
}
values[id] = value;
if ((wasEmptyField && values[id]) ||
(equal && id !== 'marginTop' && values[id] !== values.marginTop)) {
equal = false;
}
});
state.marginIsLocked = equal;
}

View File

@@ -0,0 +1,8 @@
/*!
* 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 type { EditValues, ImagePropertiesState } from "../interface";
/** @private */
export declare function readSizes(image: HTMLImageElement, values: EditValues, state: ImagePropertiesState): Promise<void>;

View File

@@ -0,0 +1,44 @@
/*!
* 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 plugins/image-properties
*/
import { isNumeric } from "../../../core/helpers/checker/is-numeric.js";
import { attr } from "../../../core/helpers/utils/attr.js";
import { css } from "../../../core/helpers/utils/css.js";
import { normalSizeFromString } from "../utils/utils.js";
/** @private */
export async function readSizes(image, values, state) {
await image.decode();
const width = css(image, 'width', true) || attr(image, 'width') || false;
const height = css(image, 'height', true) || attr(image, 'height') || false;
values.imageWidth =
width !== false
? normalSizeFromString(width)
: image.offsetWidth || image.naturalWidth;
if (isNumeric(values.imageWidth)) {
values.imageHeight =
height !== false
? normalSizeFromString(height)
: image.offsetHeight || image.naturalHeight;
}
else {
values.imageHeight = height || '';
}
const { imageWidth, imageHeight } = values;
const w = parseFloat(imageWidth.toString());
if (!isNumeric(imageWidth) || !isNumeric(imageHeight)) {
state.sizeIsLocked = false;
return;
}
if (height === false) {
values.imageHeight = Math.round(w / state.ratio);
state.sizeIsLocked = true;
return;
}
const h = parseFloat(imageHeight.toString());
state.sizeIsLocked = Math.abs(w - h * state.ratio) < 1;
}

View File

@@ -0,0 +1,31 @@
/*!
* 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 plugins/image-properties
*/
import type { IComponent, IContainer, IElms, IJodit, Nullable } from "../../../types/index";
import { UIGroup } from "../../../core/ui/group/group";
import type { ImagePropertiesAPI, ImagePropertiesState } from "../interface";
/** @private */
export declare class UIImagePropertiesForm extends UIGroup<IJodit> {
private state;
private handlers;
className(): string;
appendChildToContainer(): void;
getElm<T extends IComponent & IContainer & IElms>(elementName: string): Nullable<HTMLElement>;
private __mainTab;
private __positionTab;
constructor(jodit: IJodit, state: ImagePropertiesState, activeTabState: {
activeTab: 'Image' | 'Advanced';
}, handlers: ImagePropertiesAPI);
protected render(): string;
protected onChangeSizeIsLocked(): void;
protected onLockSizeClick(): void;
protected onStateValuesSizeChange(): void;
protected onImageWidthChange(e: Event): void;
protected onStateValuesImageSrcChange(): void;
protected hideFieldByOptions(): void;
}

View File

@@ -0,0 +1,171 @@
/*!
* 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;
};
import { debounce, hook, watch } from "../../../core/decorators/index.js";
import { component } from "../../../core/decorators/component/component.js";
import { attr, css, isNumeric } from "../../../core/helpers/index.js";
import { UIGroup } from "../../../core/ui/group/group.js";
import { Icon } from "../../../core/ui/icon.js";
import { TabsWidget } from "../../../modules/widget/index.js";
import { UIImageMainTab } from "./ui-image-main-tab.js";
import { UIImagePositionTab } from "./ui-image-position-tab.js";
/** @private */
let UIImagePropertiesForm = class UIImagePropertiesForm extends UIGroup {
className() {
return 'UIImagePropertiesForm';
}
appendChildToContainer() { }
getElm(elementName) {
const selfElm = super.getElm(elementName);
if (selfElm) {
return selfElm;
}
for (const child of this.elements) {
const elm = child.getElm(elementName);
if (elm) {
return elm;
}
}
return null;
}
constructor(jodit, state, activeTabState, handlers) {
super(jodit);
this.state = state;
this.handlers = handlers;
this.__mainTab = new UIImageMainTab(this.jodit, this.state, this.handlers);
this.__positionTab = new UIImagePositionTab(this.jodit, this.state, this.handlers);
this.getElm('tabsBox').appendChild(TabsWidget(jodit, [
{ name: 'Image', content: this.__mainTab },
{ name: 'Advanced', content: this.__positionTab }
], activeTabState));
this.setMod('lock-size', this.state.sizeIsLocked);
this.append(this.__mainTab).append(this.__positionTab);
}
render() {
return `<form>
<div class="jodit-grid jodit-grid_xs-column">
<div class="jodit_col-lg-2-5 jodit_col-xs-5-5">
<div class="&__view-box">
<div class="&__imageView">
<img class="&__imageViewSrc" src="" alt=""/>
</div>
<div class="jodit-form__group &__imageSizes">
<input type="text" class="jodit-input &__imageWidth"/>
<a class="&__lockSize">${Icon.get('lock')}</a>
<input type="text" class="&__imageHeight jodit-input"/>
</div>
</div>
</div>
<div class="jodit_col-lg-3-5 jodit_col-xs-5-5 &__tabsBox"></div>
</div>
</form>`;
}
onChangeSizeIsLocked() {
const lockSize = this.getElm('lockSize');
const imageWidth = this.getElm('imageWidth');
lockSize.innerHTML = Icon.get(this.state.sizeIsLocked ? 'lock' : 'unlock');
this.setMod('lock-size', this.state.sizeIsLocked);
this.j.e.fire(imageWidth, 'change');
}
onLockSizeClick() {
this.state.sizeIsLocked = !this.state.sizeIsLocked;
}
onStateValuesSizeChange() {
const imageWidth = this.getElm('imageWidth');
const imageHeight = this.getElm('imageHeight');
if (imageWidth !== this.j.od.activeElement) {
imageWidth.value = this.state.values.imageWidth.toString();
}
if (imageHeight !== this.j.od.activeElement) {
imageHeight.value = this.state.values.imageHeight.toString();
}
}
onImageWidthChange(e) {
const imageWidth = this.getElm('imageWidth');
const imageHeight = this.getElm('imageHeight');
if (!this.state.sizeIsLocked ||
!isNumeric(imageWidth.value) ||
!isNumeric(imageHeight.value)) {
this.state.values.imageWidth = imageWidth.value;
this.state.values.imageHeight = imageHeight.value;
return;
}
const w = parseFloat(imageWidth.value), h = parseFloat(imageHeight.value);
if (e.target === imageWidth) {
this.state.values.imageWidth = w;
this.state.values.imageHeight = Math.round(w / this.state.ratio);
}
else {
this.state.values.imageWidth = Math.round(h * this.state.ratio);
this.state.values.imageHeight = h;
}
}
onStateValuesImageSrcChange() {
const { imageSrc } = this.state.values;
if (!imageSrc) {
return;
}
const imageViewSrc = this.getElm('imageViewSrc');
attr(imageViewSrc, 'src', imageSrc);
const image = new Image();
image.src = imageSrc;
this.state.image = image;
}
hideFieldByOptions() {
const opt = this.j.o.image;
[
['editSize', 'imageSizes'],
['showPreview', 'imageView']
].forEach(([optKey, elmKey]) => {
const elm = this.getElm(elmKey);
css(elm, 'display', opt[optKey] ? null : 'none');
});
}
};
__decorate([
hook('ready'),
watch('state.sizeIsLocked')
], UIImagePropertiesForm.prototype, "onChangeSizeIsLocked", null);
__decorate([
watch('lockSize:click')
], UIImagePropertiesForm.prototype, "onLockSizeClick", null);
__decorate([
hook('ready'),
watch(['state.values.imageWidth', 'state.values.imageHeight'])
], UIImagePropertiesForm.prototype, "onStateValuesSizeChange", null);
__decorate([
watch([
'imageWidth:change',
'imageHeight:change',
'imageWidth:keydown',
'imageHeight:keydown',
'imageWidth:mousedown',
'imageHeight:mousedown',
'imageWidth:paste',
'imageHeight:paste'
]),
debounce()
], UIImagePropertiesForm.prototype, "onImageWidthChange", null);
__decorate([
hook('ready'),
watch('state.values.imageSrc')
], UIImagePropertiesForm.prototype, "onStateValuesImageSrcChange", null);
__decorate([
hook('ready')
], UIImagePropertiesForm.prototype, "hideFieldByOptions", null);
UIImagePropertiesForm = __decorate([
component
], UIImagePropertiesForm);
export { UIImagePropertiesForm };

View File

@@ -0,0 +1,39 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import { UIGroup } from "../../../core/ui/group/group";
import type { ImagePropertiesAPI, ImagePropertiesState } from "../interface";
/** @private */
export declare class UIImageMainTab extends UIGroup<IJodit> {
private state;
private handlers;
className(): string;
appendChildToContainer(): void;
constructor(view: IJodit, state: ImagePropertiesState, handlers: ImagePropertiesAPI);
protected render(): string;
protected onStateImageSrcChange(): Promise<void>;
protected onImageSrcChange(): void;
/**
* Open image editor
*/
protected onEditImageClick(e: MouseEvent): void;
/**
* Open popup with filebrowser/uploader buttons for image
*/
protected onChangeImageClick(e: MouseEvent): void;
protected onStateTitleChange(): void;
protected onTitleChange(): void;
protected onStateAltChange(): void;
protected onAltChange(): void;
protected onStateImageLinkChange(): void;
protected onImageLinkChange(): void;
protected onStateImageLinkOpenInNewTabChange(): void;
protected onImageLinkOpenInNewTabChange(): void;
protected hideFieldByOptions(): void;
}

View File

@@ -0,0 +1,179 @@
/*!
* 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;
};
import { component } from "../../../core/decorators/component/component.js";
import { hook } from "../../../core/decorators/hook/hook.js";
import { watch } from "../../../core/decorators/watch/watch.js";
import { css } from "../../../core/helpers/index.js";
import { UIGroup } from "../../../core/ui/group/group.js";
/** @private */
let UIImageMainTab = class UIImageMainTab extends UIGroup {
className() {
return 'UIImageMainTab';
}
appendChildToContainer() {
// Do nothing
}
constructor(view, state, handlers) {
super(view);
this.state = state;
this.handlers = handlers;
}
render() {
return `<div class="jodit-form__group &__editSrc">
<label>~Src~</label>
<div class="jodit-input_group">
<input class="jodit-input &__imageSrc" type="text"/>
<div class="jodit-input_group-buttons &__fixImage">
<a class="jodit-button &__changeImage">*image*</a>
<a class="jodit-button &__editImage">*crop*</a>
</div>
</div>
</div>
<div class="jodit-form__group &__editTitle">
<label>~Title~</label>
<input type="text" class="jodit-input &__imageTitle"/>
</div>
<div class="jodit-form__group &__editAlt">
<label>~Alternative~</label>
<input type="text" class="jodit-input &__imageAlt"/>
</div>
<div class="jodit-form__group &__editLink">
<label>~Link~</label>
<input type="text" class="jodit-input &__imageLink"/>
</div>
<div class="jodit-form__group &__editLinkTarget">
<label class="jodit_vertical_middle">
<input type="checkbox" class="jodit-checkbox &__imageLinkOpenInNewTab"/>
<span>~Open link in new tab~</span>
</label>
</div>`;
}
async onStateImageSrcChange() {
const imageSrc = this.getElm('imageSrc');
imageSrc.value = this.state.values.imageSrc;
}
onImageSrcChange() {
this.state.values.imageSrc = this.getElm('imageSrc').value;
}
/**
* Open image editor
*/
onEditImageClick(e) {
this.handlers.openImageEditor();
e.stopPropagation();
}
/**
* Open popup with filebrowser/uploader buttons for image
*/
onChangeImageClick(e) {
this.handlers.openImagePopup(this.getElm('changeImage'));
e.stopPropagation();
}
onStateTitleChange() {
const title = this.getElm('imageTitle');
title.value = this.state.values.imageTitle;
}
onTitleChange() {
this.state.values.imageTitle = this.getElm('imageTitle').value;
}
onStateAltChange() {
const alt = this.getElm('imageAlt');
alt.value = this.state.values.imageAlt;
}
onAltChange() {
this.state.values.imageAlt = this.getElm('imageAlt').value;
}
onStateImageLinkChange() {
const imageLink = this.getElm('imageLink');
imageLink.value = this.state.values.imageLink;
}
onImageLinkChange() {
this.state.values.imageLink = this.getElm('imageLink').value;
}
onStateImageLinkOpenInNewTabChange() {
const imageLinkOpenInNewTab = this.getElm('imageLinkOpenInNewTab');
imageLinkOpenInNewTab.checked = this.state.values.imageLinkOpenInNewTab;
}
onImageLinkOpenInNewTabChange() {
this.state.values.imageLinkOpenInNewTab = this.getElm('imageLinkOpenInNewTab').checked;
}
hideFieldByOptions() {
const o = this.j.o;
const opt = o.image;
[
['editSrc', 'editSrc'],
['editTitle', 'editTitle'],
['editAlt', 'editAlt'],
['editLink', 'editLink'],
['editLink', 'editLinkTarget'],
['useImageEditor', 'editImage']
].forEach(([optKey, elmKey]) => {
const elm = this.getElm(elmKey);
css(elm, 'display', opt[optKey] ? null : 'none');
});
const changeImage = this.getElm('changeImage');
const needShowChangeImage = Boolean(o.filebrowser.ajax.url || o.uploader.url);
css(changeImage, 'display', needShowChangeImage ? null : 'none');
const editImage = this.getElm('editImage');
const needShowEditImage = Boolean(o.filebrowser.ajax.url) && opt.useImageEditor;
css(editImage, 'display', needShowEditImage ? null : 'none');
const fixImage = this.getElm('fixImage');
css(fixImage, 'display', needShowChangeImage || needShowEditImage ? null : 'none');
}
};
__decorate([
watch('state.values.imageSrc')
], UIImageMainTab.prototype, "onStateImageSrcChange", null);
__decorate([
watch('imageSrc:change')
], UIImageMainTab.prototype, "onImageSrcChange", null);
__decorate([
watch('editImage:click')
], UIImageMainTab.prototype, "onEditImageClick", null);
__decorate([
watch('changeImage:click')
], UIImageMainTab.prototype, "onChangeImageClick", null);
__decorate([
watch('state.values.imageTitle')
], UIImageMainTab.prototype, "onStateTitleChange", null);
__decorate([
watch('imageTitle:change')
], UIImageMainTab.prototype, "onTitleChange", null);
__decorate([
watch('state.values.imageAlt')
], UIImageMainTab.prototype, "onStateAltChange", null);
__decorate([
watch('imageAlt:change')
], UIImageMainTab.prototype, "onAltChange", null);
__decorate([
watch('state.values.imageLink')
], UIImageMainTab.prototype, "onStateImageLinkChange", null);
__decorate([
watch('imageLink:change')
], UIImageMainTab.prototype, "onImageLinkChange", null);
__decorate([
watch('state.values.imageLinkOpenInNewTab')
], UIImageMainTab.prototype, "onStateImageLinkOpenInNewTabChange", null);
__decorate([
watch('imageLinkOpenInNewTab:change')
], UIImageMainTab.prototype, "onImageLinkOpenInNewTabChange", null);
__decorate([
hook('ready')
], UIImageMainTab.prototype, "hideFieldByOptions", null);
UIImageMainTab = __decorate([
component
], UIImageMainTab);
export { UIImageMainTab };

View File

@@ -0,0 +1,39 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import { UIElement } from "../../../core/ui/element";
import type { ImagePropertiesAPI, ImagePropertiesState } from "../interface";
/** @private */
export declare class UIImagePositionTab extends UIElement<IJodit> {
private state;
protected handlers: ImagePropertiesAPI;
className(): string;
constructor(jodit: IJodit, state: ImagePropertiesState, handlers: ImagePropertiesAPI);
protected render({ availableClasses }: {
availableClasses?: string[] | Array<[
string,
string
]>;
}): string;
protected onStateAlignChange(): void;
protected onChangeAlign(): void;
protected onStateValuesBorderRadiusChange(): void;
protected onChangeBorderRadius(): void;
protected onStateValuesIdChange(): void;
protected onChangeId(): void;
protected onStateValuesStyleChange(): void;
protected onChangeStyle(): void;
protected onStateValuesClassesChange(): void;
protected onChangClasses(): void;
protected onLockMarginClick(e: MouseEvent): void;
protected onChangeMarginIsLocked(): void;
protected onStateValuesMarginChange(): void;
protected onChangeMargin(): void;
protected hideFieldByOptions(): void;
}

View File

@@ -0,0 +1,261 @@
/*!
* 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;
};
import { component } from "../../../core/decorators/component/component.js";
import { hook } from "../../../core/decorators/hook/hook.js";
import { watch } from "../../../core/decorators/watch/watch.js";
import { css } from "../../../core/helpers/index.js";
import { isString } from "../../../core/helpers/checker/is-string.js";
import { attr } from "../../../core/helpers/utils/attr.js";
import { UIElement } from "../../../core/ui/element.js";
import { Icon } from "../../../core/ui/icon.js";
import { normalSizeFromString } from "../utils/utils.js";
/** @private */
let UIImagePositionTab = class UIImagePositionTab extends UIElement {
className() {
return 'UIImagePositionTab';
}
constructor(jodit, state, handlers) {
super(jodit, {
availableClasses: jodit.o.image.availableClasses
});
this.state = state;
this.handlers = handlers;
}
render({ availableClasses }) {
return `<div class="jodit-form__group &__editMargins">
<label>~Margins~</label>
<div class="jodit-grid jodit_vertical_middle">
<input class="jodit_col-lg-1-5 jodit-input &__marginTop" type="text" placeholder="~top~"/>
<a style="text-align: center;" class="jodit-properties__lock jodit_col-lg-1-5 &__lockMargin">*lock*</a>
<input disabled="disabled" class="jodit_col-lg-1-5 jodit-input &__marginRight" type="text" placeholder="~right~"/>
<input disabled="disabled" class="jodit_col-lg-1-5 jodit-input &__marginBottom" type="text" placeholder="~bottom~"/>
<input disabled="disabled" class="jodit_col-lg-1-5 jodit-input &__marginLeft" type="text" placeholder="~left~"/>
</div>
</div>
<div class="jodit-form__group &__editAlign">
<label>~Align~</label>
<select class="jodit-select &__align">
<option value="">~--Not Set--~</option>
<option value="left">~Left~</option>
<option value="center">~Center~</option>
<option value="right">~Right~</option>
</select>
</div>
<div class="jodit-form__group &__editStyle">
<label>~Styles~</label>
<input type="text" class="jodit-input &__style"/>
</div>
<div class="jodit-form__group &__editClass">
<label>~Classes~</label>
${(() => {
const classInput = [];
if (availableClasses && availableClasses.length > 0) {
classInput.push('<select class="jodit-input jodit-select &__classes">');
availableClasses.forEach(item => {
if (isString(item)) {
classInput.push(`<option value="${item}">${item}</option>`);
}
else {
classInput.push(`<option value="${item[0]}">${item[1]}</option>`);
}
});
classInput.push('</select>');
}
else {
classInput.push('<input type="text" class="jodit-input &__classes"/>');
}
return classInput.join('');
})()}
</div>
<div class="jodit-form__group &__editId">
<label>Id</label>
<input type="text" class="jodit-input &__id"/>
</div>
<div
class="jodit-form__group &__editBorderRadius"
>
<label>~Border radius~</label>
<input type="number" class="jodit-input &__borderRadius"/>
</div>`;
}
onStateAlignChange() {
const align = this.getElm('align');
align.value = this.state.values.align;
}
onChangeAlign() {
const align = this.getElm('align');
this.state.values.align = align.value;
}
onStateValuesBorderRadiusChange() {
const borderRadius = this.getElm('borderRadius');
borderRadius.value = this.state.values.borderRadius.toString();
}
onChangeBorderRadius() {
const borderRadius = this.getElm('borderRadius');
this.state.values.borderRadius = parseFloat(borderRadius.value);
}
onStateValuesIdChange() {
const id = this.getElm('id');
id.value = this.state.values.id;
}
onChangeId() {
const id = this.getElm('id');
this.state.values.id = id.value;
}
onStateValuesStyleChange() {
const style = this.getElm('style');
style.value = this.state.values.style;
}
onChangeStyle() {
const style = this.getElm('style');
this.state.values.style = style.value;
}
onStateValuesClassesChange() {
const classes = this.getElm('classes');
classes.value = this.state.values.classes;
}
onChangClasses() {
const classes = this.getElm('classes');
this.state.values.classes = classes.value;
}
onLockMarginClick(e) {
this.state.marginIsLocked = !this.state.marginIsLocked;
e.preventDefault();
}
onChangeMarginIsLocked() {
const marginBottom = this.getElm('marginBottom');
const marginRight = this.getElm('marginRight');
const marginLeft = this.getElm('marginLeft');
const lockMargin = this.getElm('lockMargin');
[marginRight, marginBottom, marginLeft].forEach(elm => {
attr(elm, 'disabled', this.state.marginIsLocked || null);
});
lockMargin.innerHTML = Icon.get(this.state.marginIsLocked ? 'lock' : 'unlock');
if (this.state.marginIsLocked) {
const marginTop = this.state.values.marginTop;
this.state.values.marginRight = marginTop;
this.state.values.marginBottom = marginTop;
this.state.values.marginLeft = marginTop;
}
}
onStateValuesMarginChange() {
const marginTop = this.getElm('marginTop');
const marginRight = this.getElm('marginRight');
const marginBottom = this.getElm('marginBottom');
const marginLeft = this.getElm('marginLeft');
marginTop.value = this.state.values.marginTop.toString();
marginRight.value = this.state.values.marginRight.toString();
marginBottom.value = this.state.values.marginBottom.toString();
marginLeft.value = this.state.values.marginLeft.toString();
}
onChangeMargin() {
const marginTop = this.getElm('marginTop');
const marginRight = this.getElm('marginRight');
const marginBottom = this.getElm('marginBottom');
const marginLeft = this.getElm('marginLeft');
this.state.values.marginTop = normalSizeFromString(marginTop.value);
if (this.state.marginIsLocked) {
this.state.values.marginRight = this.state.values.marginTop;
this.state.values.marginBottom = this.state.values.marginTop;
this.state.values.marginLeft = this.state.values.marginTop;
}
else {
this.state.values.marginRight = normalSizeFromString(marginRight.value);
this.state.values.marginBottom = normalSizeFromString(marginBottom.value);
this.state.values.marginLeft = normalSizeFromString(marginLeft.value);
}
}
hideFieldByOptions() {
const opt = this.j.o.image;
[
['editMargins', 'editMargins'],
['editAlign', 'editAlign'],
['editStyle', 'editStyle'],
['editClass', 'editClass'],
['editId', 'editId'],
['editBorderRadius', 'editBorderRadius']
].forEach(([optKey, elmKey]) => {
const elm = this.getElm(elmKey);
css(elm, 'display', opt[optKey] ? null : 'none');
});
}
};
__decorate([
hook('ready'),
watch('state.values.align')
], UIImagePositionTab.prototype, "onStateAlignChange", null);
__decorate([
watch('align:change')
], UIImagePositionTab.prototype, "onChangeAlign", null);
__decorate([
hook('ready'),
watch('state.values.borderRadius')
], UIImagePositionTab.prototype, "onStateValuesBorderRadiusChange", null);
__decorate([
watch('borderRadius:change')
], UIImagePositionTab.prototype, "onChangeBorderRadius", null);
__decorate([
hook('ready'),
watch('state.values.id')
], UIImagePositionTab.prototype, "onStateValuesIdChange", null);
__decorate([
watch('id:change')
], UIImagePositionTab.prototype, "onChangeId", null);
__decorate([
hook('ready'),
watch('state.values.style')
], UIImagePositionTab.prototype, "onStateValuesStyleChange", null);
__decorate([
watch('style:change')
], UIImagePositionTab.prototype, "onChangeStyle", null);
__decorate([
hook('ready'),
watch('state.values.classes')
], UIImagePositionTab.prototype, "onStateValuesClassesChange", null);
__decorate([
watch('classes:change')
], UIImagePositionTab.prototype, "onChangClasses", null);
__decorate([
watch('lockMargin:click')
], UIImagePositionTab.prototype, "onLockMarginClick", null);
__decorate([
hook('ready'),
watch('state.marginIsLocked')
], UIImagePositionTab.prototype, "onChangeMarginIsLocked", null);
__decorate([
hook('ready'),
watch([
'state.values.marginTop',
'state.values.marginRight',
'state.values.marginBottom',
'state.values.marginLeft'
])
], UIImagePositionTab.prototype, "onStateValuesMarginChange", null);
__decorate([
watch([
'marginTop:change',
'marginRight:change',
'marginBottom:change',
'marginLeft:change'
])
], UIImagePositionTab.prototype, "onChangeMargin", null);
__decorate([
hook('ready')
], UIImagePositionTab.prototype, "hideFieldByOptions", null);
UIImagePositionTab = __decorate([
component
], UIImagePositionTab);
export { UIImagePositionTab };

View File

@@ -0,0 +1,15 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import type { ImagePropertiesState } from "../interface";
/**
* Open image editor dialog
* @private
*/
export declare function openImageEditorDialog(j: IJodit, state: ImagePropertiesState): void;

View File

@@ -0,0 +1,52 @@
/*!
* 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 { isString } from "../../../core/helpers/checker/is-string.js";
import { attr } from "../../../core/helpers/utils/attr.js";
import { openImageEditor } from "../../../modules/image-editor/image-editor.js";
/**
* Open image editor dialog
* @private
*/
export function openImageEditorDialog(j, state) {
const url = attr(state.image, 'src') || '', a = j.c.element('a'), loadExternal = () => {
if (a.host !== location.host) {
j.confirm('You can only edit your own images. Download this image on the host?', yes => {
if (yes && j.uploader) {
j.uploader.uploadRemoteImage(a.href.toString(), resp => {
j.alert('The image has been successfully uploaded to the host!', () => {
if (isString(resp.newfilename)) {
state.values.imageSrc =
resp.baseurl +
resp.newfilename;
}
});
}, error => {
j.alert('There was an error loading %s', error.message);
});
}
});
return;
}
};
a.href = url;
j.filebrowser.dataProvider
.getPathByUrl(a.href.toString())
.then(resp => {
openImageEditor.call(j.filebrowser, a.href, resp.name, resp.path, resp.source, () => {
const timestamp = new Date().getTime();
state.values.imageSrc =
url +
(url.indexOf('?') !== -1 ? '' : '?') +
'&_tmp=' +
timestamp.toString();
}, error => {
j.alert(error.message);
});
})
.catch(error => {
j.alert(error.message, loadExternal);
});
}

View File

@@ -0,0 +1,12 @@
/*!
* 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 plugins/image-properties
*/
import type { IDialog, IJodit } from "../../../types/index";
import type { ImagePropertiesState } from "../interface";
/** @private */
export declare function openImagePopup(j: IJodit, dialog: IDialog, state: ImagePropertiesState, button: HTMLElement): void;

View File

@@ -0,0 +1,34 @@
/*!
* 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 { isArray } from "../../../core/helpers/checker/is-array.js";
import { position } from "../../../core/helpers/size/position.js";
import { Popup } from "../../../core/ui/popup/popup.js";
import { FileSelectorWidget } from "../../../modules/widget/file-selector/file-selector.js";
/** @private */
export function openImagePopup(j, dialog, state, button) {
const popup = new Popup(dialog);
const closePopup = () => {
popup.close();
popup.destruct();
};
popup
.setContent(FileSelectorWidget(j, {
upload: (data) => {
if (data.files && data.files.length) {
state.values.imageSrc =
data.baseurl + data.files[0];
}
closePopup();
},
filebrowser: async (data) => {
if (data && isArray(data.files) && data.files.length) {
state.values.imageSrc = data.files[0];
closePopup();
}
}
}, state.image, closePopup))
.open(() => position(button));
}

View File

@@ -0,0 +1,9 @@
/*!
* 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
*/
/** @private */
export declare const normalSizeFromString: (value: string | number) => string | number;
/** @private */
export declare const normalSizeToString: (value: string | number) => string;

View File

@@ -0,0 +1,24 @@
/*!
* 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 plugins/image-properties
*/
import { isNumber } from "../../../core/helpers/checker/is-number.js";
import { trim } from "../../../core/helpers/string/trim.js";
/** @private */
export const normalSizeFromString = (value) => {
return /^[-+]?[0-9.]+(px)?$/.test(value.toString())
? parseFloat(value.toString())
: value;
};
/** @private */
export const normalSizeToString = (value) => {
if (isNumber(value)) {
return value ? value + 'px' : value.toString();
}
value = trim(value);
return /^[0-9]+$/.test(value) ? value + 'px' : value;
};

View File

@@ -0,0 +1,15 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
import type { ImagePropertiesState } from "../interface";
/**
* Apply form's values to image
* @private
*/
export declare function applyValuesToImage(j: IJodit, state: ImagePropertiesState, image: HTMLImageElement): void;

View File

@@ -0,0 +1,53 @@
/*!
* 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 { Dom } from "../../../core/dom/dom.js";
import { attr, hAlignElement } from "../../../core/helpers/utils/index.js";
import { applyLink } from "./link.js";
import { applyMargin } from "./margin.js";
import { applySize } from "./size.js";
/**
* Apply form's values to image
* @private
*/
export function applyValuesToImage(j, state, image) {
const { style, imageSrc, borderRadius, imageTitle, imageAlt, imageLink, imageWidth, imageHeight, marginTop, marginRight, marginBottom, marginLeft, imageLinkOpenInNewTab, align, classes, id } = state.values;
const opt = j.o;
// styles
if (opt.image.editStyle) {
attr(image, 'style', style || null);
}
// Src
if (imageSrc) {
attr(image, 'src', imageSrc);
}
else {
Dom.safeRemove(image);
return;
}
// Border radius
image.style.borderRadius = borderRadius ? borderRadius + 'px' : '';
// Title
attr(image, 'title', imageTitle || null);
// Alt
attr(image, 'alt', imageAlt || null);
// Link
applyLink(j, image, imageLink, imageLinkOpenInNewTab);
// Size
applySize(image, imageWidth, imageHeight, state.sizeIsLocked);
// Margin
if (j.o.image.editMargins) {
applyMargin(j, marginTop, marginRight, marginBottom, marginLeft, image, state.marginIsLocked);
}
if (opt.image.editClass) {
attr(image, 'class', classes || null);
}
if (opt.image.editId) {
attr(image, 'id', id || null);
}
if (opt.image.editAlign) {
hAlignElement(image, align);
}
}

View File

@@ -0,0 +1,11 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
/** @private */
export declare function applyLink(j: IJodit, image: HTMLImageElement, imageLink: string, imageLinkOpenInNewTab: boolean): void;

View File

@@ -0,0 +1,24 @@
/*!
* 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 { Dom } from "../../../core/dom/dom.js";
import { attr } from "../../../core/helpers/utils/attr.js";
/** @private */
export function applyLink(j, image, imageLink, imageLinkOpenInNewTab) {
// Link
let link = Dom.closest(image, 'a', j.editor);
if (imageLink) {
if (!link) {
link = Dom.wrap(image, 'a', j.createInside);
}
attr(link, 'href', imageLink);
attr(link, 'target', imageLinkOpenInNewTab ? '_blank' : null);
}
else {
if (link && link.parentNode) {
link.parentNode.replaceChild(image, link);
}
}
}

View File

@@ -0,0 +1,11 @@
/*!
* 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 plugins/image-properties
*/
import type { IJodit } from "../../../types/index";
/** @private */
export declare function applyMargin(j: IJodit, marginTop: number | string, marginRight: number | string, marginBottom: number | string, marginLeft: number | string, image: HTMLImageElement, marginIsLocked: boolean): void;

View File

@@ -0,0 +1,33 @@
/*!
* 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 { css } from "../../../core/helpers/utils/css.js";
import { normalSizeToString } from "../utils/utils.js";
/** @private */
export function applyMargin(j, marginTop, marginRight, marginBottom, marginLeft, image, marginIsLocked) {
const margins = [marginTop, marginRight, marginBottom, marginLeft];
const applyMargin = (key, value) => {
const oldValue = css(image, key);
const v = normalSizeToString(value);
if (oldValue.toString() !== v.toString()) {
css(image, key, v);
}
};
if (!marginIsLocked) {
const sides = [
'margin-top',
'margin-right',
'margin-bottom',
'margin-left'
];
margins.forEach((margin, index) => {
const side = sides[index];
applyMargin(side, margin);
});
}
else {
applyMargin('margin', marginTop);
}
}

View File

@@ -0,0 +1,7 @@
/*!
* 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
*/
/** @private */
export declare function applySize(image: HTMLImageElement, imageWidth: number | string, imageHeight: number | string, sizeIsLocked: boolean): void;

View File

@@ -0,0 +1,34 @@
/*!
* 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 plugins/image-properties
*/
import { isNumeric } from "../../../core/helpers/checker/is-numeric.js";
import { attr } from "../../../core/helpers/utils/attr.js";
import { css } from "../../../core/helpers/utils/css.js";
import { normalSizeToString } from "../utils/utils.js";
/** @private */
export function applySize(image, imageWidth, imageHeight, sizeIsLocked) {
// Size
if (imageWidth !== image.offsetWidth ||
imageHeight !== image.offsetHeight) {
const updatedWidth = imageWidth ? normalSizeToString(imageWidth) : null;
let updatedHeight = imageHeight
? normalSizeToString(imageHeight)
: null;
css(image, {
width: updatedWidth,
height: updatedWidth && sizeIsLocked ? null : updatedHeight
});
attr(image, 'width', updatedWidth && isNumeric(imageWidth) && attr(image, 'width')
? updatedWidth
: null);
if (!attr(image, 'width') || sizeIsLocked) {
updatedHeight = null;
}
attr(image, 'height', updatedHeight);
}
}