qms-angular 1.0.39 → 1.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/qms-angular.umd.js +2845 -253
- package/bundles/qms-angular.umd.js.map +1 -1
- package/esm2015/lib/model/en.js +14 -1
- package/esm2015/lib/model/no.js +18 -5
- package/esm2015/lib/qms-ckeditor-components/common/constants/ckeditorEvent.constant.js +3 -1
- package/esm2015/lib/qms-ckeditor-components/common/constants/iconSvg.constants.js +8 -0
- package/esm2015/lib/qms-ckeditor-components/common/enums/image-mode.enum.js +9 -0
- package/esm2015/lib/qms-ckeditor-components/common/enums/protocol-type.enum.js +11 -3
- package/esm2015/lib/qms-ckeditor-components/common/enums/target-type.enum.js +8 -1
- package/esm2015/lib/qms-ckeditor-components/common/functions/common.function.js +3 -1
- package/esm2015/lib/qms-ckeditor-components/common/models/qms-ckeditor-data.model.js +1 -1
- package/esm2015/lib/qms-ckeditor-components/common/models/qms-ckeditor-input.model.js +1 -9
- package/esm2015/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.component.js +524 -0
- package/esm2015/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.lib.js +1948 -0
- package/esm2015/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.validator.js +7 -0
- package/esm2015/lib/qms-ckeditor-components/components/qms-ckeditor-link/qms-ckeditor-link.component.js +4 -4
- package/esm2015/lib/qms-ckeditor-components/models/qms-ckeditor-imagemap.model.js +22 -0
- package/esm2015/lib/qms-ckeditor-components/qms-ckeditor.component.js +45 -12
- package/esm2015/lib/qms-ckeditor-components/qms-ckeditor.module.js +11 -1
- package/esm2015/qms-angular.js +3 -1
- package/fesm2015/qms-angular.js +2822 -252
- package/fesm2015/qms-angular.js.map +1 -1
- package/lib/model/en.d.ts +13 -0
- package/lib/model/no.d.ts +13 -0
- package/lib/qms-ckeditor-components/common/constants/ckeditorEvent.constant.d.ts +2 -0
- package/lib/qms-ckeditor-components/common/constants/iconSvg.constants.d.ts +7 -0
- package/lib/qms-ckeditor-components/common/enums/image-mode.enum.d.ts +7 -0
- package/lib/qms-ckeditor-components/common/enums/protocol-type.enum.d.ts +9 -2
- package/lib/qms-ckeditor-components/common/enums/target-type.enum.d.ts +6 -0
- package/lib/qms-ckeditor-components/common/models/qms-ckeditor-data.model.d.ts +1 -0
- package/lib/qms-ckeditor-components/common/models/qms-ckeditor-input.model.d.ts +7 -7
- package/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.component.d.ts +123 -0
- package/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.lib.d.ts +542 -0
- package/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.validator.d.ts +2 -0
- package/lib/qms-ckeditor-components/models/qms-ckeditor-imagemap.model.d.ts +20 -0
- package/lib/qms-ckeditor-components/qms-ckeditor.component.d.ts +5 -4
- package/package.json +1 -1
- package/qms-angular.d.ts +2 -0
- package/qms-angular.metadata.json +1 -1
- package/src/assets/qms-ckeditor-plugin/build/ckeditor.js +2 -2
- package/src/assets/qms-ckeditor-plugin/build/ckeditor.js.map +1 -1
- package/src/assets/qms-ckeditor-plugin/package-lock.json +12177 -9
- package/src/assets/qms-ckeditor-plugin/src/ckeditor.js +3 -2
- package/src/assets/qms-ckeditor-plugin/src/plugins/common/qmsCKEditorConstant.js +4 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/common/qmsCKEditorService.js +10 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/fullscreen/dist/qmsCKEditorFullscreenPlugin.dev.js +87 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/converters.js +378 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/imagemap.js +14 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/imagemapediting.js +183 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/imagemapui.js +49 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/insertimagemapcommand.js +127 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/resizeimagemapcommand.js +82 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/imagemap/utils.js +234 -0
- package/src/assets/qms-ckeditor-plugin/src/plugins/link/linkediting.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/link/linkimage.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/link/ui/linkactionsview.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/link/ui/linkformview.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/tooltip/inserttooltipcommand.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/tooltip/tooltipediting.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/plugins/tooltip/ui/actionsview.js +1 -1
- package/src/assets/qms-ckeditor-plugin/src/themes/icons/imagemap.svg +56 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/anchor.css +0 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/link.css +0 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/linkactions.css +0 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/linkform.css +0 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/linkimage.css +0 -0
- package/src/assets/qms-ckeditor-plugin/src/themes/{tyles → styles}/tooltip.css +0 -0
- package/src/lib/qms-ckeditor-components/components/qms-ckeditor-imagemap/qms-ckeditor-imagemap.component.scss +27 -0
- package/src/themes/_color.scss +8 -0
- package/src/themes/core/_colors.scss +2 -0
- package/src/themes/core/_tab.scss +50 -49
@@ -0,0 +1,183 @@
|
|
1
|
+
import { Plugin } from '@ckeditor/ckeditor5-core';
|
2
|
+
import InsertImageMapCommand from './insertimagemapcommand';
|
3
|
+
import ResizeImageMapCommand from './resizeimagemapcommand';
|
4
|
+
import { ImageMapAttributes, ImageMapElements, debounced, trimStart, drawMap, ImageMapCommands, HtmlAttributes, HtmlElements, findMapNodes } from './utils';
|
5
|
+
import ImageLoadObserver from '@ckeditor/ckeditor5-image/src/image/imageloadobserver';
|
6
|
+
import { downcastImageWidthAttribute, downcastImageHeightAttribute, downcastImageSavedSrcAttribute, downcastImageMapAttribute,
|
7
|
+
upcastImageSavedSrcAttribute, upcastMapElement, dowcastMapNameAttribute, dowcastMapWidthAttribute, dowcastMapHeightAttribute,
|
8
|
+
dowcastMapAreaAttribute, upcastImageMapAttribute, upcastImageHeightAttribute, upcastImageWidthAttribute, downcastMapElement
|
9
|
+
} from './converters';
|
10
|
+
import { isWidget } from '@ckeditor/ckeditor5-widget/src/utils';
|
11
|
+
|
12
|
+
export default class ImageMapEditing extends Plugin {
|
13
|
+
static get requires() {
|
14
|
+
return ['ImageResize', 'Delete'];
|
15
|
+
}
|
16
|
+
|
17
|
+
static get pluginName() {
|
18
|
+
return 'ImageMapEditing';
|
19
|
+
}
|
20
|
+
|
21
|
+
init() {
|
22
|
+
const editor = this.editor;
|
23
|
+
const model = editor.model;
|
24
|
+
const schema = model.schema;
|
25
|
+
const conversion = editor.conversion;
|
26
|
+
const editingView = editor.editing.view;
|
27
|
+
|
28
|
+
editingView.addObserver(ImageLoadObserver);
|
29
|
+
|
30
|
+
schema.extend(ImageMapElements.IMAGE, {
|
31
|
+
allowAttributes: [
|
32
|
+
ImageMapAttributes.IMAGE_USE_MAP,
|
33
|
+
ImageMapAttributes.IMAGE_WIDTH,
|
34
|
+
ImageMapAttributes.IMAGE_HEIGHT,
|
35
|
+
ImageMapAttributes.IMAGE_SAVED_SRC
|
36
|
+
]
|
37
|
+
});
|
38
|
+
|
39
|
+
schema.register(ImageMapElements.MAP, {
|
40
|
+
isObject: true,
|
41
|
+
allowWhere: '$block',
|
42
|
+
allowAttributes: [
|
43
|
+
ImageMapAttributes.MAP_NAME,
|
44
|
+
ImageMapAttributes.MAP_AREAS,
|
45
|
+
ImageMapAttributes.MAP_WIDTH,
|
46
|
+
ImageMapAttributes.MAP_HEIGHT
|
47
|
+
]
|
48
|
+
});
|
49
|
+
|
50
|
+
conversion.for('dataDowncast').add(downcastImageWidthAttribute());
|
51
|
+
conversion.for('dataDowncast').add(downcastImageHeightAttribute());
|
52
|
+
conversion.for('dataDowncast').add(downcastImageSavedSrcAttribute(true));
|
53
|
+
conversion.for('editingDowncast').add(downcastImageSavedSrcAttribute(false));
|
54
|
+
|
55
|
+
conversion.for('downcast').add(downcastImageMapAttribute());
|
56
|
+
conversion.for('downcast').add(downcastMapElement());
|
57
|
+
|
58
|
+
conversion.for('downcast')
|
59
|
+
.add(dowcastMapNameAttribute())
|
60
|
+
.add(dowcastMapWidthAttribute())
|
61
|
+
.add(dowcastMapHeightAttribute())
|
62
|
+
.add(dowcastMapAreaAttribute());
|
63
|
+
|
64
|
+
conversion.for('upcast').add(upcastImageMapAttribute());
|
65
|
+
conversion.for('upcast').add(upcastImageHeightAttribute());
|
66
|
+
conversion.for('upcast').add(upcastImageWidthAttribute());
|
67
|
+
conversion.for('upcast').add(upcastImageSavedSrcAttribute());
|
68
|
+
conversion.for('upcast').add(upcastMapElement());
|
69
|
+
|
70
|
+
editor.commands.get('resizeImage').on('execute', debounced(() => {
|
71
|
+
editor.execute(ImageMapCommands.RESIZE);
|
72
|
+
}, 100));
|
73
|
+
|
74
|
+
// this.listenTo(editingView.document, 'delete', ( evt, data ) => {
|
75
|
+
// this.allowDeleteMap = true;
|
76
|
+
// }, { context: [isWidget] });
|
77
|
+
|
78
|
+
// this.listenTo(model.document, 'change:data', (event) => {
|
79
|
+
// if(this.allowDeleteMap == true){
|
80
|
+
// this.allowDeleteMap == false;
|
81
|
+
// OnImageDeleted(model, event);
|
82
|
+
// }
|
83
|
+
// });
|
84
|
+
|
85
|
+
this.listenTo(editingView.document, 'imageLoaded', (evt, domEvent) => {
|
86
|
+
onImageLoaded(domEvent);
|
87
|
+
});
|
88
|
+
|
89
|
+
editor.commands.add(ImageMapCommands.INSERT, new InsertImageMapCommand(editor));
|
90
|
+
editor.commands.add(ImageMapCommands.RESIZE, new ResizeImageMapCommand(editor));
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
function OnImageDeleted(model, event){
|
95
|
+
const differ = event.source.differ;
|
96
|
+
// if no difference
|
97
|
+
if (differ.isEmpty) { return; }
|
98
|
+
const changes = differ.getChanges({ includeChangesInGraveyard: true });
|
99
|
+
if (changes.length === 0) { return; }
|
100
|
+
|
101
|
+
let hasNoImageRemoved = true;
|
102
|
+
// check any image remove or not
|
103
|
+
for (let i = 0; i < changes.length; i++){
|
104
|
+
const change = changes[i]
|
105
|
+
// if image remove exists
|
106
|
+
if (change && change.type === 'remove' && change.name === 'image') {
|
107
|
+
hasNoImageRemoved = false
|
108
|
+
break
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
// if not image remove stop execution
|
113
|
+
if (hasNoImageRemoved) { return; }
|
114
|
+
// get removed nodes
|
115
|
+
const removedNodes = changes.filter(change => (change.type === 'insert' && change.name === 'image'))
|
116
|
+
|
117
|
+
// removed image nodes
|
118
|
+
const removedMapNames = []
|
119
|
+
removedNodes.forEach(node => {
|
120
|
+
let nodeAfter = node.position.nodeAfter;
|
121
|
+
if(nodeAfter && nodeAfter.hasAttribute(ImageMapAttributes.IMAGE_USE_MAP)){
|
122
|
+
let mapname = nodeAfter.getAttribute(ImageMapAttributes.IMAGE_USE_MAP) || '';
|
123
|
+
removedMapNames.push(trimStart(mapname, '#'));
|
124
|
+
}
|
125
|
+
});
|
126
|
+
|
127
|
+
if(removedMapNames && removedMapNames.length > 0){
|
128
|
+
const range = model.createRangeIn(model.document.getRoot());
|
129
|
+
const existingMapElements = findMapNodes(removedMapNames, range);
|
130
|
+
|
131
|
+
if(existingMapElements && existingMapElements.length > 0){
|
132
|
+
const redoingBatch = model.createBatch( 'transparent' );
|
133
|
+
model.enqueueChange(redoingBatch, writer => {
|
134
|
+
existingMapElements.forEach(element => {
|
135
|
+
writer.remove(element);
|
136
|
+
});
|
137
|
+
} );
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
function onImageLoaded(domEvent){
|
143
|
+
//Must be attached only to images loaded by the `ImageInsert`, `ImageUpload` or `LinkImage` plugins.
|
144
|
+
if (!domEvent.target.matches('figure.image.ck-widget > img, figure.image.ck-widget > a > img')) {
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
if(domEvent.target.hasAttribute(HtmlAttributes.USE_MAP)
|
148
|
+
&& domEvent.target.getAttribute(HtmlAttributes.USE_MAP)
|
149
|
+
&& !domEvent.target.hasAttribute(HtmlAttributes.SAVED_SRC)){
|
150
|
+
|
151
|
+
const originalSrc = domEvent.target.getAttribute(HtmlAttributes.SRC);
|
152
|
+
const mapName = trimStart(domEvent.target.getAttribute(HtmlAttributes.USE_MAP), '#');
|
153
|
+
const selector = `${HtmlElements.MAP}[${HtmlAttributes.NAME}='${mapName}']`;
|
154
|
+
const map = document.querySelector(selector);
|
155
|
+
if(map && map.childNodes){
|
156
|
+
const areas = [];
|
157
|
+
map.childNodes.forEach((child) =>{
|
158
|
+
if(child.nodeName && child.nodeName.toLowerCase() === HtmlElements.AREA){
|
159
|
+
let area = {};
|
160
|
+
area[HtmlAttributes.COORDS] = child.getAttribute(HtmlAttributes.COORDS);
|
161
|
+
area[HtmlAttributes.SHAPE] = child.getAttribute(HtmlAttributes.SHAPE) || '';
|
162
|
+
areas.push(area);
|
163
|
+
}
|
164
|
+
});
|
165
|
+
if(areas.length > 0){
|
166
|
+
const width = map.getAttribute(HtmlAttributes.WIDTH) || domEvent.target.width;
|
167
|
+
const height = map.getAttribute(HtmlAttributes.HEIGHT) || domEvent.target.height;
|
168
|
+
const target = domEvent.target;
|
169
|
+
drawMap(originalSrc, width, height, areas, (oldsrc, newsrc) => {
|
170
|
+
if(target){
|
171
|
+
target.setAttribute(HtmlAttributes.SRC, newsrc);
|
172
|
+
target.setAttribute(HtmlAttributes.SAVED_SRC, oldsrc);
|
173
|
+
}
|
174
|
+
|
175
|
+
if(map){
|
176
|
+
map.setAttribute(HtmlAttributes.WIDTH, width);
|
177
|
+
map.setAttribute(HtmlAttributes.HEIGHT, height);
|
178
|
+
}
|
179
|
+
})
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { Plugin } from '@ckeditor/ckeditor5-core';
|
2
|
+
import { ButtonView, ContextualBalloon } from '@ckeditor/ckeditor5-ui';
|
3
|
+
import imageMapIcon from '../../themes/icons/imagemap.svg';
|
4
|
+
import { ImageMapCommands } from './utils';
|
5
|
+
|
6
|
+
export default class ImageMapUI extends Plugin {
|
7
|
+
|
8
|
+
static get requires() {
|
9
|
+
return [ ContextualBalloon];
|
10
|
+
}
|
11
|
+
|
12
|
+
static get pluginName() {
|
13
|
+
return 'ImageMapUI';
|
14
|
+
}
|
15
|
+
|
16
|
+
init() {
|
17
|
+
this._createButton();
|
18
|
+
|
19
|
+
}
|
20
|
+
|
21
|
+
destroy() {
|
22
|
+
super.destroy();
|
23
|
+
}
|
24
|
+
|
25
|
+
_createButton() {
|
26
|
+
const editor = this.editor;
|
27
|
+
const t = editor.t;
|
28
|
+
|
29
|
+
editor.ui.componentFactory.add('imageMap', locale => {
|
30
|
+
const command = editor.commands.get(ImageMapCommands.INSERT);
|
31
|
+
const view = new ButtonView(locale);
|
32
|
+
|
33
|
+
view.set({
|
34
|
+
label: t('Image Map'),
|
35
|
+
icon: imageMapIcon,
|
36
|
+
tooltip: true
|
37
|
+
});
|
38
|
+
|
39
|
+
view.bind('isEnabled').to(command, 'isEnabled');
|
40
|
+
|
41
|
+
this.listenTo(view, 'execute', () => {
|
42
|
+
editor.execute(ImageMapCommands.INSERT, {});
|
43
|
+
});
|
44
|
+
|
45
|
+
return view;
|
46
|
+
});
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
import { Command } from '@ckeditor/ckeditor5-core';
|
3
|
+
import { isImage } from '@ckeditor/ckeditor5-image/src/image/utils';
|
4
|
+
import * as QMSCKEditorConstant from '../common/qmsCKEditorConstant';
|
5
|
+
import * as QMSCKEditorService from '../common/qmsCKEditorService';
|
6
|
+
import { ImageMapAttributes, ImageMapElements, trimStart, parsePixelToFloat, findFirstMapNode, findMapNodes, drawMap } from './utils';
|
7
|
+
import { getSelectedImageWidget } from '@ckeditor/ckeditor5-image/src/image/utils';
|
8
|
+
|
9
|
+
export default class InsertImageMapCommand extends Command {
|
10
|
+
|
11
|
+
refresh() {
|
12
|
+
const element = this.editor.model.document.selection.getSelectedElement();
|
13
|
+
this.isEnabled = isImage(element);
|
14
|
+
}
|
15
|
+
|
16
|
+
execute() {
|
17
|
+
const editor = this.editor;
|
18
|
+
const model = editor.model;
|
19
|
+
const view = editor.editing.view;
|
20
|
+
const selectedElement = model.document.selection.getSelectedElement();
|
21
|
+
|
22
|
+
const mapName = selectedElement.getAttribute(ImageMapAttributes.IMAGE_USE_MAP);
|
23
|
+
let originalImageSrc = selectedElement.getAttribute(ImageMapAttributes.IMAGE_SRC);
|
24
|
+
if(selectedElement.hasAttribute(ImageMapAttributes.IMAGE_SAVED_SRC) && selectedElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC)){
|
25
|
+
originalImageSrc = selectedElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC);
|
26
|
+
}
|
27
|
+
|
28
|
+
const data = {'imageUrl': originalImageSrc, 'areas': [], imageWidth: null, imageHeight: null};
|
29
|
+
const imageWidget = getSelectedImageWidget( view.document.selection);
|
30
|
+
const widgetResizePlugin = editor.plugins.get('WidgetResize');
|
31
|
+
const resizer = widgetResizePlugin.getResizerByViewElement( imageWidget );
|
32
|
+
|
33
|
+
if(resizer){
|
34
|
+
const resizerWrapper = resizer._viewResizerWrapper;
|
35
|
+
data.imageWidth = parsePixelToFloat(resizerWrapper.getStyle('width', 0));
|
36
|
+
data.imageHeight = parsePixelToFloat(resizerWrapper.getStyle('height', 0));
|
37
|
+
}
|
38
|
+
|
39
|
+
if(!!mapName){
|
40
|
+
const range = model.createRangeIn(model.document.getRoot());
|
41
|
+
const existingMapElement = findFirstMapNode(trimStart(mapName, '#'), range);
|
42
|
+
if(existingMapElement){
|
43
|
+
data.areas = JSON.parse(existingMapElement.getAttribute(ImageMapAttributes.MAP_AREAS) || []);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
QMSCKEditorService.imageMapNotify(data);
|
48
|
+
window[QMSCKEditorConstant.QMSCK_IMAGEMAP_IS_PROCESSING] = false;
|
49
|
+
window.addEventListener(QMSCKEditorConstant.QMSCK_IMAGEMAP_PLUGIN_RESP, (evt) => {
|
50
|
+
if (window[QMSCKEditorConstant.QMSCK_IMAGEMAP_IS_PROCESSING]) {
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
window[QMSCKEditorConstant.QMSCK_IMAGEMAP_IS_PROCESSING] = true;
|
54
|
+
model.change(writer => {
|
55
|
+
var newMapName = trimStart(evt.detail.name, '#');
|
56
|
+
const areas = evt.detail.areas;
|
57
|
+
const width = evt.detail.imageWidth;
|
58
|
+
const height = evt.detail.imageHeight;
|
59
|
+
const imageElement = model.document.selection.getSelectedElement();
|
60
|
+
const oldMapName = imageElement.getAttribute(ImageMapAttributes.IMAGE_USE_MAP);
|
61
|
+
|
62
|
+
if(!!oldMapName){
|
63
|
+
const range = model.createRangeIn(model.document.getRoot());
|
64
|
+
const existingMapElements = findMapNodes([trimStart(oldMapName, '#')], range);
|
65
|
+
if(existingMapElements && existingMapElements.length > 0){
|
66
|
+
existingMapElements.forEach(element => {
|
67
|
+
writer.remove(element);
|
68
|
+
});
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
if(!!areas && areas.length > 0)
|
73
|
+
{
|
74
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_USE_MAP, '#' + newMapName, imageElement);
|
75
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_WIDTH, width, imageElement);
|
76
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_HEIGHT, height, imageElement);
|
77
|
+
|
78
|
+
let originalImageSrc = imageElement.getAttribute(ImageMapAttributes.IMAGE_SRC);
|
79
|
+
if(!imageElement.hasAttribute(ImageMapAttributes.IMAGE_SAVED_SRC) || !imageElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC)){
|
80
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_SAVED_SRC, originalImageSrc, imageElement);
|
81
|
+
}
|
82
|
+
else{
|
83
|
+
originalImageSrc = imageElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC);
|
84
|
+
}
|
85
|
+
|
86
|
+
drawMap(originalImageSrc, width, height, areas, (oldsrc, newsrc) =>{
|
87
|
+
model.enqueueChange(writer => {
|
88
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_SRC, newsrc, imageElement);
|
89
|
+
} );
|
90
|
+
});
|
91
|
+
|
92
|
+
let attrs = {};
|
93
|
+
attrs[ImageMapAttributes.MAP_NAME] = newMapName;
|
94
|
+
attrs[ImageMapAttributes.MAP_AREAS] = JSON.stringify(areas);
|
95
|
+
attrs[ImageMapAttributes.MAP_WIDTH] = width;
|
96
|
+
attrs[ImageMapAttributes.MAP_HEIGHT] = height;
|
97
|
+
const mapElement = writer.createElement(ImageMapElements.MAP, attrs);
|
98
|
+
|
99
|
+
writer.insert(mapElement, writer.createPositionAt(imageElement, "after"));
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
if(imageElement.hasAttribute(ImageMapAttributes.IMAGE_USE_MAP)){
|
103
|
+
writer.removeAttribute(ImageMapAttributes.IMAGE_USE_MAP, imageElement);
|
104
|
+
}
|
105
|
+
|
106
|
+
if(imageElement.hasAttribute(ImageMapAttributes.IMAGE_SAVED_SRC)){
|
107
|
+
|
108
|
+
let src = imageElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC);
|
109
|
+
if(!!src){
|
110
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_SRC, src, imageElement);
|
111
|
+
}
|
112
|
+
writer.removeAttribute(ImageMapAttributes.IMAGE_SAVED_SRC, imageElement);
|
113
|
+
}
|
114
|
+
|
115
|
+
if(imageElement.hasAttribute(ImageMapAttributes.IMAGE_HEIGHT)){
|
116
|
+
writer.removeAttribute(ImageMapAttributes.IMAGE_HEIGHT, imageElement);
|
117
|
+
}
|
118
|
+
|
119
|
+
if(imageElement.hasAttribute(ImageMapAttributes.IMAGE_WIDTH)){
|
120
|
+
writer.removeAttribute(ImageMapAttributes.IMAGE_WIDTH, imageElement);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
});
|
124
|
+
});
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
import { Command } from '@ckeditor/ckeditor5-core';
|
3
|
+
import { isImage } from '@ckeditor/ckeditor5-image/src/image/utils';
|
4
|
+
import { ImageMapAttributes, trimStart, parsePixelToFloat, findFirstMapNode, drawMap, resizeAreaWidth, resizeAreaHeight } from './utils';
|
5
|
+
import { getSelectedImageWidget} from '@ckeditor/ckeditor5-image/src/image/utils';
|
6
|
+
|
7
|
+
export default class ResizeImageMapCommand extends Command {
|
8
|
+
|
9
|
+
refresh() {
|
10
|
+
const element = this.editor.model.document.selection.getSelectedElement();
|
11
|
+
this.isEnabled = isImage(element) && element.hasAttribute(ImageMapAttributes.IMAGE_USE_MAP);
|
12
|
+
}
|
13
|
+
|
14
|
+
execute() {
|
15
|
+
const editor = this.editor;
|
16
|
+
const model = editor.model;
|
17
|
+
const view = this.editor.editing.view;
|
18
|
+
const imageElement = model.document.selection.getSelectedElement();
|
19
|
+
const mapName = imageElement.getAttribute(ImageMapAttributes.IMAGE_USE_MAP);
|
20
|
+
if(mapName){
|
21
|
+
const imageWidget = getSelectedImageWidget( view.document.selection);
|
22
|
+
const widgetResizePlugin = editor.plugins.get('WidgetResize');
|
23
|
+
const resizer = widgetResizePlugin.getResizerByViewElement( imageWidget );
|
24
|
+
if(resizer){
|
25
|
+
const resizerWrapper = resizer._viewResizerWrapper;
|
26
|
+
const newWidth = parsePixelToFloat(resizerWrapper.getStyle('width', 0));
|
27
|
+
const newHeight = parsePixelToFloat(resizerWrapper.getStyle('height', 0));
|
28
|
+
|
29
|
+
const range = model.createRangeIn(model.document.getRoot());
|
30
|
+
const existingMapElement = findFirstMapNode(trimStart(mapName, '#'), range);
|
31
|
+
if(existingMapElement){
|
32
|
+
const areas = JSON.parse(existingMapElement.getAttribute(ImageMapAttributes.MAP_AREAS) || []);
|
33
|
+
const width = parsePixelToFloat(existingMapElement.getAttribute(ImageMapAttributes.MAP_WIDTH, 0));
|
34
|
+
const height = parsePixelToFloat(existingMapElement.getAttribute(ImageMapAttributes.MAP_HEIGHT, 0));
|
35
|
+
|
36
|
+
const widthChanged = newWidth != width && width != 0 ;
|
37
|
+
const heightChanged = newHeight != height && height != 0 ;
|
38
|
+
|
39
|
+
if(widthChanged || heightChanged){
|
40
|
+
if(widthChanged){
|
41
|
+
const widthRatio = (newWidth * 1.0) / width;
|
42
|
+
for (var i = 0; i < areas.length; ++i) {
|
43
|
+
areas[i] = resizeAreaWidth(areas[i], widthRatio);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
if(heightChanged){
|
48
|
+
const heightRatio = (newHeight * 1.0) / height;
|
49
|
+
for (var i = 0; i < areas.length; ++i) {
|
50
|
+
areas[i] = resizeAreaHeight(areas[i], heightRatio, widthChanged);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
const areasAsString = JSON.stringify(areas);
|
55
|
+
model.change(writer => {
|
56
|
+
let originalImageSrc = imageElement.getAttribute(ImageMapAttributes.IMAGE_SRC);
|
57
|
+
if(!imageElement.hasAttribute(ImageMapAttributes.IMAGE_SAVED_SRC) || !imageElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC)){
|
58
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_SAVED_SRC, originalImageSrc, imageElement);
|
59
|
+
}
|
60
|
+
else{
|
61
|
+
originalImageSrc = imageElement.getAttribute(ImageMapAttributes.IMAGE_SAVED_SRC);
|
62
|
+
}
|
63
|
+
|
64
|
+
writer.setAttribute(ImageMapAttributes.MAP_AREAS, areasAsString, existingMapElement);
|
65
|
+
writer.setAttribute(ImageMapAttributes.MAP_WIDTH, newWidth, existingMapElement);
|
66
|
+
writer.setAttribute(ImageMapAttributes.MAP_HEIGHT, newHeight, existingMapElement);
|
67
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_WIDTH, newWidth, imageElement);
|
68
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_HEIGHT, newHeight, imageElement);
|
69
|
+
|
70
|
+
drawMap(originalImageSrc, newWidth, newHeight, areas, (oldsrc, newsrc) =>{
|
71
|
+
model.enqueueChange(writer => {
|
72
|
+
writer.setAttribute(ImageMapAttributes.IMAGE_SRC, newsrc, imageElement);
|
73
|
+
});
|
74
|
+
});
|
75
|
+
});
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
@@ -0,0 +1,234 @@
|
|
1
|
+
export const HtmlAttributes = {
|
2
|
+
COORDS: 'coords',
|
3
|
+
HREF: 'href',
|
4
|
+
SHAPE: 'shape',
|
5
|
+
NAME: 'name',
|
6
|
+
TARGET: 'target',
|
7
|
+
USE_MAP: 'usemap',
|
8
|
+
ALT: 'alt',
|
9
|
+
WIDTH: 'width',
|
10
|
+
HEIGHT: 'height',
|
11
|
+
SRC: 'src',
|
12
|
+
ID: 'id',
|
13
|
+
SAVED_SRC: 'savedsrc'
|
14
|
+
}
|
15
|
+
|
16
|
+
|
17
|
+
export const HtmlElements = {
|
18
|
+
MAP: 'map',
|
19
|
+
IMAGE: 'img',
|
20
|
+
AREA: 'area'
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
export const ImageMapAttributes = {
|
25
|
+
MAP_AREAS: 'mapareas',
|
26
|
+
MAP_NAME: 'mapname' ,
|
27
|
+
MAP_WIDTH: 'mapwidth',
|
28
|
+
MAP_HEIGHT: 'mapheight',
|
29
|
+
IMAGE_USE_MAP: 'imageusemap',
|
30
|
+
IMAGE_SAVED_SRC: 'imagesavedsrc',
|
31
|
+
IMAGE_SRC: 'src',//It is already defined by the image plugin
|
32
|
+
IMAGE_WIDTH: 'imagewidth',
|
33
|
+
IMAGE_HEIGHT: 'imageheight',
|
34
|
+
}
|
35
|
+
|
36
|
+
export const ImageMapCommands = {
|
37
|
+
INSERT: 'insertImageMap',
|
38
|
+
RESIZE: 'resizeImageMap'
|
39
|
+
}
|
40
|
+
|
41
|
+
export const ImageMapElements = {
|
42
|
+
MAP: 'npmap',
|
43
|
+
IMAGE: 'image'//It is already defined by the image plugin
|
44
|
+
}
|
45
|
+
|
46
|
+
export function trimStart(text, charToRemove){
|
47
|
+
if(text && text.length > 0){
|
48
|
+
if(text.charAt(0) == charToRemove){
|
49
|
+
return text.substring(1);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
return text;
|
53
|
+
}
|
54
|
+
|
55
|
+
export function parsePixelToFloat(pixel, fractionDigits){
|
56
|
+
if(pixel){
|
57
|
+
pixel = pixel.toString().replaceAll('px', '');
|
58
|
+
try
|
59
|
+
{
|
60
|
+
return Number.parseFloat(pixel).toFixed(fractionDigits);
|
61
|
+
}
|
62
|
+
catch{
|
63
|
+
|
64
|
+
}
|
65
|
+
}
|
66
|
+
return 0;
|
67
|
+
}
|
68
|
+
|
69
|
+
function isMapNode(lowerCaseNameObject, node){
|
70
|
+
return node.is('element', ImageMapElements.MAP)
|
71
|
+
&& node.hasAttribute(ImageMapAttributes.MAP_NAME)
|
72
|
+
&& lowerCaseNameObject[getLowerCaseMapName(node)];
|
73
|
+
}
|
74
|
+
|
75
|
+
function getLowerCaseMapName(node){
|
76
|
+
return node.getAttribute(ImageMapAttributes.MAP_NAME).toLowerCase();
|
77
|
+
}
|
78
|
+
|
79
|
+
export function findMapNodes(names, range) {
|
80
|
+
if(!names) { return []; }
|
81
|
+
|
82
|
+
const lowerCaseNameObject = {};
|
83
|
+
names.forEach(name => {
|
84
|
+
let lowerCase = name.toLowerCase();
|
85
|
+
lowerCaseNameObject[lowerCase] = lowerCase;
|
86
|
+
});
|
87
|
+
|
88
|
+
const nodes = {};
|
89
|
+
for ( const value of range.getWalker() ) {
|
90
|
+
const node = value.item;
|
91
|
+
if (isMapNode(lowerCaseNameObject, node)) {
|
92
|
+
let mapname = getLowerCaseMapName(node);
|
93
|
+
nodes[mapname] = node;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
return Object.keys(nodes).map(key => nodes[key]);
|
97
|
+
};
|
98
|
+
|
99
|
+
|
100
|
+
export function findFirstMapNode(name, range) {
|
101
|
+
if(!name) { return null; }
|
102
|
+
|
103
|
+
const lowerCaseNameObject = {};
|
104
|
+
let lowerCase = name.toLowerCase();
|
105
|
+
lowerCaseNameObject[lowerCase] = lowerCase;
|
106
|
+
|
107
|
+
for ( const value of range.getWalker() ) {
|
108
|
+
const node = value.item;
|
109
|
+
if (isMapNode(lowerCaseNameObject, node)) {
|
110
|
+
return node;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
return null;
|
114
|
+
};
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
export function resizeAreaWidth(area, ratio) {
|
119
|
+
var coords = area.coords.split(',');
|
120
|
+
switch (area.shape) {
|
121
|
+
case 'circle':
|
122
|
+
coords[0] = Math.round(coords[0] * ratio); // Moves center point's left coordinate
|
123
|
+
coords[2] = Math.round(coords[2] * ratio); // Ajust radius
|
124
|
+
area.coords = coords.join(',');
|
125
|
+
return area;
|
126
|
+
case 'rect':
|
127
|
+
case 'poly':
|
128
|
+
// Moves each point's left coordinate
|
129
|
+
for (var i = 0; i < coords.length; i += 2) {
|
130
|
+
if (i >= coords.length) { break; }
|
131
|
+
coords[i] = Math.round(coords[i] * ratio);
|
132
|
+
}
|
133
|
+
area.coords = coords.join(',');
|
134
|
+
return area;
|
135
|
+
}
|
136
|
+
};
|
137
|
+
|
138
|
+
export function resizeAreaHeight(area, ratio, widthChanged) {
|
139
|
+
var coords = area.coords.split(',');
|
140
|
+
switch (area.shape) {
|
141
|
+
case 'circle':
|
142
|
+
coords[1] = Math.round(coords[1] * ratio); // Moves center point's top coordinate
|
143
|
+
if (!widthChanged) {
|
144
|
+
coords[2] = Math.round(coords[2] * ratio); // Ajust radius
|
145
|
+
}
|
146
|
+
area.coords = coords.join(',');
|
147
|
+
return area;
|
148
|
+
case 'rect':
|
149
|
+
case 'poly':
|
150
|
+
// Moves each point's top coordinate
|
151
|
+
for (var i = 1; i < coords.length; i += 2) {
|
152
|
+
if (i >= coords.length) { break; }
|
153
|
+
coords[i] = Math.round(coords[i] * ratio);
|
154
|
+
}
|
155
|
+
area.coords = coords.join(',');
|
156
|
+
return area;
|
157
|
+
}
|
158
|
+
};
|
159
|
+
|
160
|
+
export function debounced(fn, delay) {
|
161
|
+
let timerId;
|
162
|
+
return function (...args) {
|
163
|
+
if (timerId) {
|
164
|
+
clearTimeout(timerId);
|
165
|
+
}
|
166
|
+
timerId = setTimeout(() => {
|
167
|
+
fn(...args);
|
168
|
+
timerId = null;
|
169
|
+
}, delay);
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
export function drawMap(imageSrc, imageWidth, imageHeight, areas, callback){
|
174
|
+
if(areas && areas.length > 0){
|
175
|
+
try
|
176
|
+
{
|
177
|
+
var tempImage = new Image(imageWidth, imageHeight);
|
178
|
+
tempImage.src = imageSrc;
|
179
|
+
tempImage.addEventListener("load", () =>{
|
180
|
+
var canvas = document.createElement('canvas');
|
181
|
+
var context = canvas.getContext('2d');
|
182
|
+
|
183
|
+
canvas.width = imageWidth;
|
184
|
+
canvas.height = imageHeight;
|
185
|
+
context.drawImage(tempImage, 0, 0, imageWidth, imageHeight);
|
186
|
+
context.strokeStyle = "#FF0000";
|
187
|
+
context.lineWidth = 1;
|
188
|
+
|
189
|
+
for (var i = 0; i < areas.length; i++) {
|
190
|
+
const area = areas[i];
|
191
|
+
const coords = area.coords.split(',');
|
192
|
+
switch (area.shape) {
|
193
|
+
case 'circle':
|
194
|
+
context.beginPath();
|
195
|
+
context.arc(coords[0], coords[1], coords[2], 0, Math.PI * 2, true);
|
196
|
+
context.closePath();
|
197
|
+
context.stroke();
|
198
|
+
break;
|
199
|
+
|
200
|
+
case 'poly':
|
201
|
+
context.beginPath();
|
202
|
+
context.moveTo(coords[0], coords[1]);
|
203
|
+
for (var j = 2; j < coords.length; j += 2) {
|
204
|
+
context.lineTo(coords[j], coords[j + 1]);
|
205
|
+
}
|
206
|
+
context.closePath();
|
207
|
+
context.stroke();
|
208
|
+
break;
|
209
|
+
|
210
|
+
default:
|
211
|
+
context.strokeRect(coords[0], coords[1], coords[2] - coords[0], coords[3] - coords[1]);
|
212
|
+
break;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
let newSrc = imageSrc;
|
216
|
+
let oldSrc = imageSrc;
|
217
|
+
try
|
218
|
+
{
|
219
|
+
newSrc = canvas.toDataURL();
|
220
|
+
}
|
221
|
+
catch(err){
|
222
|
+
|
223
|
+
}
|
224
|
+
if(callback){
|
225
|
+
callback(oldSrc, newSrc);
|
226
|
+
}
|
227
|
+
|
228
|
+
}, false);
|
229
|
+
}
|
230
|
+
catch(err){
|
231
|
+
|
232
|
+
}
|
233
|
+
}
|
234
|
+
}
|
@@ -18,7 +18,7 @@ import UnlinkCommand from './unlinkcommand';
|
|
18
18
|
import ManualDecorator from './utils/manualdecorator';
|
19
19
|
import { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators } from './utils';
|
20
20
|
import * as QMSCKEditorConstant from '../common/qmsCKEditorConstant';
|
21
|
-
import '../../themes/
|
21
|
+
import '../../themes/styles/link.css';
|
22
22
|
import { isLinkElement } from './utils';
|
23
23
|
|
24
24
|
const HIGHLIGHT_CLASS = 'ck-link_selected';
|
@@ -11,7 +11,7 @@ import { Plugin } from '@ckeditor/ckeditor5-core';
|
|
11
11
|
import LinkImageEditing from './linkimageediting';
|
12
12
|
import LinkImageUI from './linkimageui';
|
13
13
|
|
14
|
-
import '../../themes/
|
14
|
+
import '../../themes/styles/linkimage.css';
|
15
15
|
|
16
16
|
/**
|
17
17
|
* The `LinkImage` plugin.
|