ep_images_extended 1.0.0
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/CONTRIBUTING.md +135 -0
- package/LICENSE.md +14 -0
- package/README.md +83 -0
- package/editbar.js +10 -0
- package/ep.json +34 -0
- package/exportHTML.js +81 -0
- package/index.js +183 -0
- package/locales/en.json +6 -0
- package/package.json +30 -0
- package/settings.js +11 -0
- package/static/css/ace.css +346 -0
- package/static/css/ep_images_extended.css +15 -0
- package/static/js/clientHooks.js +1848 -0
- package/static/js/contentCollection.js +96 -0
- package/static/js/toolbar.js +177 -0
- package/templates/editbarButton.ejs +7 -0
- package/templates/imageFormatMenu.ejs +29 -0
- package/templates/modal.ejs +13 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/* Import Bootstrap Icons if not already available */
|
|
2
|
+
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css');
|
|
3
|
+
|
|
4
|
+
/* Bootstrap Icons support */
|
|
5
|
+
.image-format-button i {
|
|
6
|
+
font-size: 18px;
|
|
7
|
+
line-height: 1;
|
|
8
|
+
}
|
|
9
|
+
.image {
|
|
10
|
+
display: inline-block;
|
|
11
|
+
position: relative;
|
|
12
|
+
line-height: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.image>img {
|
|
16
|
+
height: auto !important;
|
|
17
|
+
-moz-user-select: -moz-none;
|
|
18
|
+
-khtml-user-select: none;
|
|
19
|
+
-webkit-user-select: none;
|
|
20
|
+
user-select: none;
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Styles for the new handle spans */
|
|
25
|
+
.image span.image-resize-handle {
|
|
26
|
+
content: '';
|
|
27
|
+
position: absolute;
|
|
28
|
+
width: 12px;
|
|
29
|
+
height: 12px;
|
|
30
|
+
background-color: #1a73e8; /* Google Docs blue square */
|
|
31
|
+
border: 1px solid #fff; /* White border for visibility */
|
|
32
|
+
z-index: 1;
|
|
33
|
+
cursor: nw-resize; /* Double-sided diagonal arrow */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.image span.image-resize-handle.br { bottom: -4px; right: -4px; cursor: nw-resize; }
|
|
37
|
+
|
|
38
|
+
/* Hide the handles if the image is very small */
|
|
39
|
+
.image[style*="width: 10%;"] span.image-resize-handle,
|
|
40
|
+
.image[style*="width: 11%;"] span.image-resize-handle,
|
|
41
|
+
.image[style*="width: 12%;"] span.image-resize-handle,
|
|
42
|
+
.image[style*="width: 13%;"] span.image-resize-handle,
|
|
43
|
+
.image[style*="width: 14%;"] span.image-resize-handle,
|
|
44
|
+
.image[style*="width: 15%;"] span.image-resize-handle {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Rules moved from clientHooks.js aceEditorCSS */
|
|
49
|
+
.ace-line .inline-image.white-space-pre-wrap {
|
|
50
|
+
white-space: pre-wrap !important;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
span.inline-image.character {
|
|
54
|
+
white-space: pre-wrap !important;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* CSS Pseudo-element approach for image display */
|
|
58
|
+
span.inline-image.image-placeholder {
|
|
59
|
+
/* Hide original text content (placeholder character or "undefined") */
|
|
60
|
+
color: transparent; /* Make text invisible */
|
|
61
|
+
line-height: 0; /* Try to collapse height */
|
|
62
|
+
display: inline-block;
|
|
63
|
+
vertical-align: middle; /* Align the container span */
|
|
64
|
+
/* Prevent selection of the underlying space/character */
|
|
65
|
+
-webkit-user-select: none;
|
|
66
|
+
-moz-user-select: none;
|
|
67
|
+
-ms-user-select: none;
|
|
68
|
+
user-select: none;
|
|
69
|
+
position: relative; /* Added */
|
|
70
|
+
max-width: 100%; /* ADDED to make placeholder respect its container's width */
|
|
71
|
+
|
|
72
|
+
/* Prevent text input into the image placeholder */
|
|
73
|
+
-webkit-user-modify: read-only;
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Allow pointer events on inner spans for image interaction */
|
|
78
|
+
span.inline-image.image-placeholder span.image-inner,
|
|
79
|
+
span.inline-image.image-placeholder span.image-resize-handle {
|
|
80
|
+
pointer-events: auto;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Style the INNER span to display the image */
|
|
84
|
+
span.inline-image.image-placeholder span.image-inner {
|
|
85
|
+
content: ''; /* Necessary for pseudo-element */
|
|
86
|
+
display: inline-block; /* Or block, if placeholder only contains this and handles */
|
|
87
|
+
max-width: 100%; /* Keep: ensures inner span respects outer placeholder's width */
|
|
88
|
+
height: auto; /* ADDED: Allow aspect-ratio to determine height */
|
|
89
|
+
aspect-ratio: var(--image-css-aspect-ratio, 1 / 1); /* ADDED: Use CSS var, fallback to 1:1 */
|
|
90
|
+
background-image: var(--image-src); /* Use the CSS variable */
|
|
91
|
+
background-size: contain; /* Use contain to fit image */
|
|
92
|
+
background-repeat: no-repeat;
|
|
93
|
+
background-position: center center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* === START Styles for resize handles (ported from old plugin) === */
|
|
97
|
+
|
|
98
|
+
/* Base style for handle spans */
|
|
99
|
+
span.inline-image.image-placeholder span.image-resize-handle {
|
|
100
|
+
content: '';
|
|
101
|
+
position: absolute;
|
|
102
|
+
width: 12px;
|
|
103
|
+
height: 12px;
|
|
104
|
+
background-color: #1a73e8; /* Google Docs blue square */
|
|
105
|
+
border: 1px solid #fff; /* White border for visibility */
|
|
106
|
+
z-index: 1;
|
|
107
|
+
display: none; /* Initially hidden */
|
|
108
|
+
cursor: nw-resize; /* Double-sided diagonal arrow */
|
|
109
|
+
|
|
110
|
+
/* Prevent text input and selection */
|
|
111
|
+
-webkit-user-select: none;
|
|
112
|
+
-moz-user-select: none;
|
|
113
|
+
-ms-user-select: none;
|
|
114
|
+
user-select: none;
|
|
115
|
+
|
|
116
|
+
/* Ensure contenteditable false is respected */
|
|
117
|
+
-webkit-user-modify: read-only;
|
|
118
|
+
|
|
119
|
+
/* Prevent any text content from showing */
|
|
120
|
+
color: transparent !important;
|
|
121
|
+
font-size: 0 !important;
|
|
122
|
+
line-height: 0 !important;
|
|
123
|
+
|
|
124
|
+
/* Override any inherited text styles */
|
|
125
|
+
text-decoration: none !important;
|
|
126
|
+
font-weight: normal !important;
|
|
127
|
+
font-style: normal !important;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Selection styling is handled via dynamic CSS injection in JavaScript */
|
|
131
|
+
/* This completely avoids triggering content collection hooks */
|
|
132
|
+
|
|
133
|
+
/* Default state - all images unselected */
|
|
134
|
+
span.inline-image.image-placeholder span.image-inner {
|
|
135
|
+
outline: 0;
|
|
136
|
+
outline-offset: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
span.inline-image.image-placeholder span.image-resize-handle {
|
|
140
|
+
display: none;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Dynamic CSS rules are injected into the document head to show selection */
|
|
144
|
+
/* Format: span.inline-image.image-placeholder[data-image-id="ID"] span.image-inner { outline: ... } */
|
|
145
|
+
|
|
146
|
+
/* Image formatting menu */
|
|
147
|
+
.image-format-menu {
|
|
148
|
+
position: absolute;
|
|
149
|
+
background: white;
|
|
150
|
+
border: 1px solid #c4c7c5;
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
153
|
+
padding: 6px;
|
|
154
|
+
display: none;
|
|
155
|
+
z-index: 1001;
|
|
156
|
+
width: auto;
|
|
157
|
+
min-width: 200px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.image-format-menu.visible {
|
|
161
|
+
display: block;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.image-format-menu-section {
|
|
165
|
+
display: inline-block;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.image-format-menu-divider {
|
|
169
|
+
width: 1px;
|
|
170
|
+
height: 20px;
|
|
171
|
+
background: #c4c7c5;
|
|
172
|
+
margin: 0 4px;
|
|
173
|
+
display: inline-block;
|
|
174
|
+
vertical-align: middle;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.image-format-menu-buttons {
|
|
178
|
+
display: inline-flex;
|
|
179
|
+
gap: 3px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.image-format-button {
|
|
183
|
+
width: 28px;
|
|
184
|
+
height: 28px;
|
|
185
|
+
border: 1px solid transparent;
|
|
186
|
+
border-radius: 3px;
|
|
187
|
+
background: white;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
justify-content: center;
|
|
192
|
+
transition: all 0.15s;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.image-format-button:hover {
|
|
196
|
+
background: #f1f3f4;
|
|
197
|
+
border-color: #dadce0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.image-format-button.active {
|
|
201
|
+
background: #e8f0fe;
|
|
202
|
+
border-color: #1a73e8;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.image-format-button i {
|
|
206
|
+
color: #202124;
|
|
207
|
+
transition: color 0.15s;
|
|
208
|
+
font-weight: 500;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.image-format-button.active i {
|
|
212
|
+
color: #1a73e8;
|
|
213
|
+
font-weight: 600;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.image-format-button.image-delete-button:hover {
|
|
217
|
+
background: #fce8e6;
|
|
218
|
+
border-color: #ea4335;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.image-format-button.image-delete-button:hover i {
|
|
222
|
+
color: #ea4335;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* Copy button styling */
|
|
226
|
+
.image-format-button[data-action="copy"]:hover {
|
|
227
|
+
background: #e8f0fe; /* Light blue background */
|
|
228
|
+
border-color: #1a73e8; /* Google blue border */
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.image-format-button[data-action="copy"]:hover i {
|
|
232
|
+
color: #1a73e8; /* Google blue icon */
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Cut button styling */
|
|
236
|
+
.image-format-button[data-action="cut"]:hover {
|
|
237
|
+
background: #e8f0fe; /* Light blue background */
|
|
238
|
+
border-color: #1a73e8; /* Google blue border */
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.image-format-button[data-action="cut"]:hover i {
|
|
242
|
+
color: #1a73e8; /* Google blue icon */
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Improve active state for wrap buttons to look like Google Drive */
|
|
246
|
+
.image-format-button[data-wrap].active {
|
|
247
|
+
background: #1a73e8;
|
|
248
|
+
border-color: #1a73e8;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.image-format-button[data-wrap].active i {
|
|
252
|
+
color: white;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Position the bottom-right handle only */
|
|
256
|
+
span.inline-image.image-placeholder span.image-resize-handle.br { bottom: -4px; right: -4px; cursor: nw-resize; }
|
|
257
|
+
|
|
258
|
+
/* Float functionality for images */
|
|
259
|
+
span.inline-image.image-placeholder.image-float-none {
|
|
260
|
+
float: none;
|
|
261
|
+
display: inline-block;
|
|
262
|
+
margin: 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
span.inline-image.image-placeholder.image-float-left {
|
|
266
|
+
float: left;
|
|
267
|
+
display: inline-block; /* Keep inline-block to allow text wrapping */
|
|
268
|
+
margin: 0 10px 10px 0;
|
|
269
|
+
/* Remove clear to allow horizontal stacking */
|
|
270
|
+
/* clear: left; */
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
span.inline-image.image-placeholder.image-float-right {
|
|
274
|
+
float: right;
|
|
275
|
+
display: inline-block; /* Keep inline-block to allow text wrapping */
|
|
276
|
+
margin: 0 0 10px 10px;
|
|
277
|
+
/* Remove clear to allow horizontal stacking */
|
|
278
|
+
/* clear: right; */
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* Optional: Add spacing between consecutive floated images */
|
|
282
|
+
span.inline-image.image-placeholder.image-float-left + span.inline-image.image-placeholder.image-float-left {
|
|
283
|
+
margin-left: 5px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
span.inline-image.image-placeholder.image-float-right + span.inline-image.image-placeholder.image-float-right {
|
|
287
|
+
margin-right: 5px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Hide the handles if the image inner span is very small (example thresholds) */
|
|
291
|
+
/* This might need JS adjustment based on calculated width */
|
|
292
|
+
|
|
293
|
+
/* === END Styles for resize handles === */
|
|
294
|
+
|
|
295
|
+
/* === START Float containment fix - minimal approach === */
|
|
296
|
+
|
|
297
|
+
/* Clear floated content after the last line */
|
|
298
|
+
.ace-line:last-child::after {
|
|
299
|
+
content: "";
|
|
300
|
+
display: table;
|
|
301
|
+
clear: both;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* === END Float containment fix === */
|
|
305
|
+
|
|
306
|
+
/* === Modal loader styling === */
|
|
307
|
+
#imageUploadModalLoader.popup,
|
|
308
|
+
#imageUploadModalError.popup {
|
|
309
|
+
/* Position horizontally centered near the top of the viewport */
|
|
310
|
+
top: 20px !important;
|
|
311
|
+
left: 50% !important;
|
|
312
|
+
transform: translateX(-50%) !important; /* only horizontal centering */
|
|
313
|
+
right: auto !important;
|
|
314
|
+
margin: 0 !important;
|
|
315
|
+
position: fixed !important;
|
|
316
|
+
z-index: 10000;
|
|
317
|
+
/* Add a subtle border around the whole modal */
|
|
318
|
+
border: 1px solid #dadce0;
|
|
319
|
+
border-radius: 8px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Ensure both modals share the same inner-box styling */
|
|
323
|
+
#imageUploadModalLoader .popup-content,
|
|
324
|
+
#imageUploadModalError .popup-content {
|
|
325
|
+
border: none; /* border applied on outer container */
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Style the inner content box */
|
|
329
|
+
#imageUploadModalLoader .popup-content {
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
justify-content: center;
|
|
333
|
+
padding: 24px 32px;
|
|
334
|
+
background: #fff;
|
|
335
|
+
border-radius: 8px;
|
|
336
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
337
|
+
min-width: 200px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* Provide user-visible feedback */
|
|
341
|
+
#imageUploadModalLoader .loadingAnimation::before {
|
|
342
|
+
content: "Uploading image...";
|
|
343
|
+
font-size: 1.1rem;
|
|
344
|
+
font-weight: 500;
|
|
345
|
+
color: #202124;
|
|
346
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#imageUploadModalError, #imageUploadModalLoader {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 150px;
|
|
4
|
+
right: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
margin-left: auto;
|
|
7
|
+
margin-right: auto;
|
|
8
|
+
z-index: 1;
|
|
9
|
+
max-height: 600px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#imageUploadModalError .error {
|
|
13
|
+
color: #900;
|
|
14
|
+
text-align: center;
|
|
15
|
+
}
|