pressroom 0.1.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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +172 -0
  5. data/Rakefile +8 -0
  6. data/app/assets/javascripts/pressroom/application.js +18 -0
  7. data/app/assets/javascripts/pressroom/controllers/editor_controller.js +146 -0
  8. data/app/assets/javascripts/pressroom/vendor/code.js +181 -0
  9. data/app/assets/javascripts/pressroom/vendor/delimiter.js +98 -0
  10. data/app/assets/javascripts/pressroom/vendor/editorjs.js +11217 -0
  11. data/app/assets/javascripts/pressroom/vendor/header.js +321 -0
  12. data/app/assets/javascripts/pressroom/vendor/image.js +1103 -0
  13. data/app/assets/javascripts/pressroom/vendor/inline-code.js +99 -0
  14. data/app/assets/javascripts/pressroom/vendor/list.js +2091 -0
  15. data/app/assets/javascripts/pressroom/vendor/marker.js +95 -0
  16. data/app/assets/javascripts/pressroom/vendor/quote.js +990 -0
  17. data/app/assets/javascripts/pressroom/vendor/stimulus.js +2563 -0
  18. data/app/assets/javascripts/pressroom/vendor/turbo.js +7256 -0
  19. data/app/assets/stylesheets/pressroom/admin.css +216 -0
  20. data/app/assets/stylesheets/pressroom/content.css +18 -0
  21. data/app/controllers/pressroom/application_controller.rb +41 -0
  22. data/app/controllers/pressroom/categories_controller.rb +50 -0
  23. data/app/controllers/pressroom/posts_controller.rb +98 -0
  24. data/app/controllers/pressroom/tags_controller.rb +39 -0
  25. data/app/controllers/pressroom/uploads_controller.rb +52 -0
  26. data/app/helpers/pressroom/application_helper.rb +4 -0
  27. data/app/helpers/pressroom/blocks_helper.rb +13 -0
  28. data/app/jobs/pressroom/application_job.rb +4 -0
  29. data/app/mailers/pressroom/application_mailer.rb +6 -0
  30. data/app/models/concerns/pressroom/authorable.rb +23 -0
  31. data/app/models/concerns/pressroom/sluggable.rb +66 -0
  32. data/app/models/pressroom/application_record.rb +7 -0
  33. data/app/models/pressroom/category.rb +54 -0
  34. data/app/models/pressroom/post.rb +90 -0
  35. data/app/models/pressroom/tag.rb +30 -0
  36. data/app/models/pressroom/tagging.rb +12 -0
  37. data/app/views/layouts/pressroom/admin.html.erb +34 -0
  38. data/app/views/layouts/pressroom/application.html.erb +17 -0
  39. data/app/views/pressroom/blocks/_code.html.erb +2 -0
  40. data/app/views/pressroom/blocks/_delimiter.html.erb +1 -0
  41. data/app/views/pressroom/blocks/_header.html.erb +5 -0
  42. data/app/views/pressroom/blocks/_image.html.erb +18 -0
  43. data/app/views/pressroom/blocks/_list.html.erb +6 -0
  44. data/app/views/pressroom/blocks/_list_items.html.erb +13 -0
  45. data/app/views/pressroom/blocks/_paragraph.html.erb +1 -0
  46. data/app/views/pressroom/blocks/_quote.html.erb +6 -0
  47. data/app/views/pressroom/categories/_form.html.erb +31 -0
  48. data/app/views/pressroom/categories/edit.html.erb +3 -0
  49. data/app/views/pressroom/categories/index.html.erb +34 -0
  50. data/app/views/pressroom/categories/new.html.erb +3 -0
  51. data/app/views/pressroom/posts/_form.html.erb +49 -0
  52. data/app/views/pressroom/posts/_post_row.html.erb +17 -0
  53. data/app/views/pressroom/posts/edit.html.erb +34 -0
  54. data/app/views/pressroom/posts/index.html.erb +38 -0
  55. data/app/views/pressroom/posts/new.html.erb +3 -0
  56. data/app/views/pressroom/tags/edit.html.erb +24 -0
  57. data/app/views/pressroom/tags/index.html.erb +27 -0
  58. data/config/editorjs_manifest.yml +53 -0
  59. data/config/importmap.rb +18 -0
  60. data/config/locales/pressroom.en.yml +16 -0
  61. data/config/locales/transliteration.ru.yml +70 -0
  62. data/config/locales/transliteration.uk.yml +70 -0
  63. data/config/routes.rb +17 -0
  64. data/db/migrate/20260821000001_create_pressroom_posts.rb +42 -0
  65. data/db/migrate/20260821000002_create_pressroom_taxonomy.rb +33 -0
  66. data/lib/pressroom/blocks/definition.rb +103 -0
  67. data/lib/pressroom/blocks/registry.rb +56 -0
  68. data/lib/pressroom/blocks/renderer.rb +97 -0
  69. data/lib/pressroom/blocks/sanitizer.rb +36 -0
  70. data/lib/pressroom/blocks/schema.rb +247 -0
  71. data/lib/pressroom/blocks/text_extractor.rb +46 -0
  72. data/lib/pressroom/built_in_blocks.rb +97 -0
  73. data/lib/pressroom/configuration.rb +64 -0
  74. data/lib/pressroom/engine.rb +33 -0
  75. data/lib/pressroom/errors.rb +11 -0
  76. data/lib/pressroom/feed.rb +69 -0
  77. data/lib/pressroom/transliteration.rb +213 -0
  78. data/lib/pressroom/version.rb +3 -0
  79. data/lib/pressroom.rb +70 -0
  80. data/lib/tasks/pressroom_tasks.rake +4 -0
  81. data/lib/tasks/pressroom_vendor.rake +31 -0
  82. metadata +171 -0
@@ -0,0 +1,1103 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode('.image-tool{--bg-color: #cdd1e0;--front-color: #388ae5;--border-color: #e8e8eb}.image-tool__image{border-radius:3px;overflow:hidden;margin-bottom:10px;padding-bottom:0}.image-tool__image-picture{max-width:100%;vertical-align:bottom;display:block}.image-tool__image-preloader{width:50px;height:50px;border-radius:50%;background-size:cover;margin:auto;position:relative;background-color:var(--bg-color);background-position:center center}.image-tool__image-preloader:after{content:"";position:absolute;z-index:3;width:60px;height:60px;border-radius:50%;border:2px solid var(--bg-color);border-top-color:var(--front-color);left:50%;top:50%;margin-top:-30px;margin-left:-30px;animation:image-preloader-spin 2s infinite linear;box-sizing:border-box}.image-tool__caption{visibility:hidden;position:absolute;bottom:0;left:0;margin-bottom:10px}.image-tool__caption[contentEditable=true][data-placeholder]:before{position:absolute!important;content:attr(data-placeholder);color:#707684;font-weight:400;display:none}.image-tool__caption[contentEditable=true][data-placeholder]:empty:before{display:block}.image-tool__caption[contentEditable=true][data-placeholder]:empty:focus:before{display:none}.image-tool--empty .image-tool__image,.image-tool--empty .image-tool__image-preloader{display:none}.image-tool--empty .image-tool__caption,.image-tool--uploading .image-tool__caption{visibility:hidden!important}.image-tool .cdx-button{display:flex;align-items:center;justify-content:center}.image-tool .cdx-button svg{height:auto;margin:0 6px 0 0}.image-tool--filled .cdx-button,.image-tool--filled .image-tool__image-preloader{display:none}.image-tool--uploading .image-tool__image{min-height:200px;display:flex;border:1px solid var(--border-color);background-color:#fff}.image-tool--uploading .image-tool__image-picture,.image-tool--uploading .cdx-button{display:none}.image-tool--withBorder .image-tool__image{border:1px solid var(--border-color)}.image-tool--withBackground .image-tool__image{padding:15px;background:var(--bg-color)}.image-tool--withBackground .image-tool__image-picture{max-width:60%;margin:0 auto}.image-tool--stretched .image-tool__image-picture{width:100%}.image-tool--caption .image-tool__caption{visibility:visible}.image-tool--caption{padding-bottom:50px}@keyframes image-preloader-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}')),document.head.appendChild(o)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
+ const R = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19V19C9.13623 19 8.20435 19 7.46927 18.6955C6.48915 18.2895 5.71046 17.5108 5.30448 16.5307C5 15.7956 5 14.8638 5 13V12C5 9.19108 5 7.78661 5.67412 6.77772C5.96596 6.34096 6.34096 5.96596 6.77772 5.67412C7.78661 5 9.19108 5 12 5H13.5C14.8956 5 15.5933 5 16.1611 5.17224C17.4395 5.56004 18.44 6.56046 18.8278 7.83886C19 8.40666 19 9.10444 19 10.5V10.5"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 13V16M16 19V16M19 16H16M16 16H13"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6.5 17.5L17.5 6.5"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.9919 10.5H19.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.9919 19H11.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13L13 5"/></svg>', I = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.9919 9.5H19.0015"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.5 5H14.5096"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M14.625 5H15C17.2091 5 19 6.79086 19 9V9.375"/><path stroke="currentColor" stroke-width="2" d="M9.375 5L9 5C6.79086 5 5 6.79086 5 9V9.375"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.3725 5H9.38207"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 9.5H5.00957"/><path stroke="currentColor" stroke-width="2" d="M9.375 19H9C6.79086 19 5 17.2091 5 15V14.625"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.3725 19H9.38207"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 14.55H5.00957"/><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M16 13V16M16 19V16M19 16H16M16 16H13"/></svg>', L = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><rect width="14" height="14" x="5" y="5" stroke="currentColor" stroke-width="2" rx="4"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.13968 15.32L8.69058 11.5661C9.02934 11.2036 9.48873 11 9.96774 11C10.4467 11 10.9061 11.2036 11.2449 11.5661L15.3871 16M13.5806 14.0664L15.0132 12.533C15.3519 12.1705 15.8113 11.9668 16.2903 11.9668C16.7693 11.9668 17.2287 12.1705 17.5675 12.533L18.841 13.9634"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.7778 9.33331H13.7867"/></svg>', x = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9L20 12L17 15"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 12H20"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 9L4 12L7 15"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 12H10"/></svg>', B = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M8 9V7.2C8 7.08954 8.08954 7 8.2 7L12 7M16 9V7.2C16 7.08954 15.9105 7 15.8 7L12 7M12 7L12 17M12 17H10M12 17H14"/></svg>';
3
+ function S(C, i = null, a = {}) {
4
+ const s = document.createElement(C);
5
+ Array.isArray(i) ? s.classList.add(...i) : i !== null && s.classList.add(i);
6
+ for (const r in a)
7
+ a.hasOwnProperty(r) && (s[r] = a[r]);
8
+ return s;
9
+ }
10
+ var _ = /* @__PURE__ */ ((C) => (C.Empty = "empty", C.Uploading = "uploading", C.Filled = "filled", C))(_ || {});
11
+ class D {
12
+ /**
13
+ * @param ui - image tool Ui module
14
+ * @param ui.api - Editor.js API
15
+ * @param ui.config - user config
16
+ * @param ui.onSelectFile - callback for clicks on Select file button
17
+ * @param ui.readOnly - read-only mode flag
18
+ */
19
+ constructor({ api: i, config: a, onSelectFile: s, readOnly: r }) {
20
+ this.api = i, this.config = a, this.onSelectFile = s, this.readOnly = r, this.nodes = {
21
+ wrapper: S("div", [this.CSS.baseClass, this.CSS.wrapper]),
22
+ imageContainer: S("div", [this.CSS.imageContainer]),
23
+ fileButton: this.createFileButton(),
24
+ imageEl: void 0,
25
+ imagePreloader: S("div", this.CSS.imagePreloader),
26
+ caption: S("div", [this.CSS.input, this.CSS.caption], {
27
+ contentEditable: !this.readOnly
28
+ })
29
+ }, this.nodes.caption.dataset.placeholder = this.config.captionPlaceholder, this.nodes.imageContainer.appendChild(this.nodes.imagePreloader), this.nodes.wrapper.appendChild(this.nodes.imageContainer), this.nodes.wrapper.appendChild(this.nodes.caption), this.nodes.wrapper.appendChild(this.nodes.fileButton);
30
+ }
31
+ /**
32
+ * Apply visual representation of activated tune
33
+ * @param tuneName - one of available tunes {@link Tunes.tunes}
34
+ * @param status - true for enable, false for disable
35
+ */
36
+ applyTune(i, a) {
37
+ this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${i}`, a);
38
+ }
39
+ /**
40
+ * Renders tool UI
41
+ */
42
+ render() {
43
+ return this.toggleStatus(
44
+ "empty"
45
+ /* Empty */
46
+ ), this.nodes.wrapper;
47
+ }
48
+ /**
49
+ * Shows uploading preloader
50
+ * @param src - preview source
51
+ */
52
+ showPreloader(i) {
53
+ this.nodes.imagePreloader.style.backgroundImage = `url(${i})`, this.toggleStatus(
54
+ "uploading"
55
+ /* Uploading */
56
+ );
57
+ }
58
+ /**
59
+ * Hide uploading preloader
60
+ */
61
+ hidePreloader() {
62
+ this.nodes.imagePreloader.style.backgroundImage = "", this.toggleStatus(
63
+ "empty"
64
+ /* Empty */
65
+ );
66
+ }
67
+ /**
68
+ * Shows an image
69
+ * @param url - image source
70
+ */
71
+ fillImage(i) {
72
+ const a = /\.mp4$/.test(i) ? "VIDEO" : "IMG", s = {
73
+ src: i
74
+ };
75
+ let r = "load";
76
+ a === "VIDEO" && (s.autoplay = !0, s.loop = !0, s.muted = !0, s.playsinline = !0, r = "loadeddata"), this.nodes.imageEl = S(a, this.CSS.imageEl, s), this.nodes.imageEl.addEventListener(r, () => {
77
+ this.toggleStatus(
78
+ "filled"
79
+ /* Filled */
80
+ ), this.nodes.imagePreloader !== void 0 && (this.nodes.imagePreloader.style.backgroundImage = "");
81
+ }), this.nodes.imageContainer.appendChild(this.nodes.imageEl);
82
+ }
83
+ /**
84
+ * Shows caption input
85
+ * @param text - caption content text
86
+ */
87
+ fillCaption(i) {
88
+ this.nodes.caption !== void 0 && (this.nodes.caption.innerHTML = i);
89
+ }
90
+ /**
91
+ * Changes UI status
92
+ * @param status - see {@link Ui.status} constants
93
+ */
94
+ toggleStatus(i) {
95
+ for (const a in _)
96
+ if (Object.prototype.hasOwnProperty.call(_, a)) {
97
+ const s = _[a];
98
+ this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${s}`, s === i);
99
+ }
100
+ }
101
+ /**
102
+ * CSS classes
103
+ */
104
+ get CSS() {
105
+ return {
106
+ baseClass: this.api.styles.block,
107
+ loading: this.api.styles.loader,
108
+ input: this.api.styles.input,
109
+ button: this.api.styles.button,
110
+ /**
111
+ * Tool's classes
112
+ */
113
+ wrapper: "image-tool",
114
+ imageContainer: "image-tool__image",
115
+ imagePreloader: "image-tool__image-preloader",
116
+ imageEl: "image-tool__image-picture",
117
+ caption: "image-tool__caption"
118
+ };
119
+ }
120
+ /**
121
+ * Creates upload-file button
122
+ */
123
+ createFileButton() {
124
+ const i = S("div", [this.CSS.button]);
125
+ return i.innerHTML = this.config.buttonContent ?? `${L} ${this.api.i18n.t("Select an Image")}`, i.addEventListener("click", () => {
126
+ this.onSelectFile();
127
+ }), i;
128
+ }
129
+ }
130
+ function U(C) {
131
+ return C && C.__esModule && Object.prototype.hasOwnProperty.call(C, "default") ? C.default : C;
132
+ }
133
+ var H = { exports: {} };
134
+ (function(C, i) {
135
+ (function(a, s) {
136
+ C.exports = s();
137
+ })(window, function() {
138
+ return function(a) {
139
+ var s = {};
140
+ function r(o) {
141
+ if (s[o]) return s[o].exports;
142
+ var e = s[o] = { i: o, l: !1, exports: {} };
143
+ return a[o].call(e.exports, e, e.exports, r), e.l = !0, e.exports;
144
+ }
145
+ return r.m = a, r.c = s, r.d = function(o, e, d) {
146
+ r.o(o, e) || Object.defineProperty(o, e, { enumerable: !0, get: d });
147
+ }, r.r = function(o) {
148
+ typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(o, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(o, "__esModule", { value: !0 });
149
+ }, r.t = function(o, e) {
150
+ if (1 & e && (o = r(o)), 8 & e || 4 & e && typeof o == "object" && o && o.__esModule) return o;
151
+ var d = /* @__PURE__ */ Object.create(null);
152
+ if (r.r(d), Object.defineProperty(d, "default", { enumerable: !0, value: o }), 2 & e && typeof o != "string") for (var v in o) r.d(d, v, (function(l) {
153
+ return o[l];
154
+ }).bind(null, v));
155
+ return d;
156
+ }, r.n = function(o) {
157
+ var e = o && o.__esModule ? function() {
158
+ return o.default;
159
+ } : function() {
160
+ return o;
161
+ };
162
+ return r.d(e, "a", e), e;
163
+ }, r.o = function(o, e) {
164
+ return Object.prototype.hasOwnProperty.call(o, e);
165
+ }, r.p = "", r(r.s = 3);
166
+ }([function(a, s) {
167
+ var r;
168
+ r = /* @__PURE__ */ function() {
169
+ return this;
170
+ }();
171
+ try {
172
+ r = r || new Function("return this")();
173
+ } catch {
174
+ typeof window == "object" && (r = window);
175
+ }
176
+ a.exports = r;
177
+ }, function(a, s, r) {
178
+ (function(o) {
179
+ var e = r(2), d = setTimeout;
180
+ function v() {
181
+ }
182
+ function l(n) {
183
+ if (!(this instanceof l)) throw new TypeError("Promises must be constructed via new");
184
+ if (typeof n != "function") throw new TypeError("not a function");
185
+ this._state = 0, this._handled = !1, this._value = void 0, this._deferreds = [], t(n, this);
186
+ }
187
+ function f(n, c) {
188
+ for (; n._state === 3; ) n = n._value;
189
+ n._state !== 0 ? (n._handled = !0, l._immediateFn(function() {
190
+ var u = n._state === 1 ? c.onFulfilled : c.onRejected;
191
+ if (u !== null) {
192
+ var g;
193
+ try {
194
+ g = u(n._value);
195
+ } catch (m) {
196
+ return void y(c.promise, m);
197
+ }
198
+ p(c.promise, g);
199
+ } else (n._state === 1 ? p : y)(c.promise, n._value);
200
+ })) : n._deferreds.push(c);
201
+ }
202
+ function p(n, c) {
203
+ try {
204
+ if (c === n) throw new TypeError("A promise cannot be resolved with itself.");
205
+ if (c && (typeof c == "object" || typeof c == "function")) {
206
+ var u = c.then;
207
+ if (c instanceof l) return n._state = 3, n._value = c, void w(n);
208
+ if (typeof u == "function") return void t((g = u, m = c, function() {
209
+ g.apply(m, arguments);
210
+ }), n);
211
+ }
212
+ n._state = 1, n._value = c, w(n);
213
+ } catch (h) {
214
+ y(n, h);
215
+ }
216
+ var g, m;
217
+ }
218
+ function y(n, c) {
219
+ n._state = 2, n._value = c, w(n);
220
+ }
221
+ function w(n) {
222
+ n._state === 2 && n._deferreds.length === 0 && l._immediateFn(function() {
223
+ n._handled || l._unhandledRejectionFn(n._value);
224
+ });
225
+ for (var c = 0, u = n._deferreds.length; c < u; c++) f(n, n._deferreds[c]);
226
+ n._deferreds = null;
227
+ }
228
+ function b(n, c, u) {
229
+ this.onFulfilled = typeof n == "function" ? n : null, this.onRejected = typeof c == "function" ? c : null, this.promise = u;
230
+ }
231
+ function t(n, c) {
232
+ var u = !1;
233
+ try {
234
+ n(function(g) {
235
+ u || (u = !0, p(c, g));
236
+ }, function(g) {
237
+ u || (u = !0, y(c, g));
238
+ });
239
+ } catch (g) {
240
+ if (u) return;
241
+ u = !0, y(c, g);
242
+ }
243
+ }
244
+ l.prototype.catch = function(n) {
245
+ return this.then(null, n);
246
+ }, l.prototype.then = function(n, c) {
247
+ var u = new this.constructor(v);
248
+ return f(this, new b(n, c, u)), u;
249
+ }, l.prototype.finally = e.a, l.all = function(n) {
250
+ return new l(function(c, u) {
251
+ if (!n || n.length === void 0) throw new TypeError("Promise.all accepts an array");
252
+ var g = Array.prototype.slice.call(n);
253
+ if (g.length === 0) return c([]);
254
+ var m = g.length;
255
+ function h(T, E) {
256
+ try {
257
+ if (E && (typeof E == "object" || typeof E == "function")) {
258
+ var M = E.then;
259
+ if (typeof M == "function") return void M.call(E, function(F) {
260
+ h(T, F);
261
+ }, u);
262
+ }
263
+ g[T] = E, --m == 0 && c(g);
264
+ } catch (F) {
265
+ u(F);
266
+ }
267
+ }
268
+ for (var k = 0; k < g.length; k++) h(k, g[k]);
269
+ });
270
+ }, l.resolve = function(n) {
271
+ return n && typeof n == "object" && n.constructor === l ? n : new l(function(c) {
272
+ c(n);
273
+ });
274
+ }, l.reject = function(n) {
275
+ return new l(function(c, u) {
276
+ u(n);
277
+ });
278
+ }, l.race = function(n) {
279
+ return new l(function(c, u) {
280
+ for (var g = 0, m = n.length; g < m; g++) n[g].then(c, u);
281
+ });
282
+ }, l._immediateFn = typeof o == "function" && function(n) {
283
+ o(n);
284
+ } || function(n) {
285
+ d(n, 0);
286
+ }, l._unhandledRejectionFn = function(n) {
287
+ typeof console < "u" && console && console.warn("Possible Unhandled Promise Rejection:", n);
288
+ }, s.a = l;
289
+ }).call(this, r(5).setImmediate);
290
+ }, function(a, s, r) {
291
+ s.a = function(o) {
292
+ var e = this.constructor;
293
+ return this.then(function(d) {
294
+ return e.resolve(o()).then(function() {
295
+ return d;
296
+ });
297
+ }, function(d) {
298
+ return e.resolve(o()).then(function() {
299
+ return e.reject(d);
300
+ });
301
+ });
302
+ };
303
+ }, function(a, s, r) {
304
+ function o(t) {
305
+ return (o = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(n) {
306
+ return typeof n;
307
+ } : function(n) {
308
+ return n && typeof Symbol == "function" && n.constructor === Symbol && n !== Symbol.prototype ? "symbol" : typeof n;
309
+ })(t);
310
+ }
311
+ r(4);
312
+ var e, d, v, l, f, p, y, w = r(8), b = (d = function(t) {
313
+ return new Promise(function(n, c) {
314
+ t = l(t), (t = f(t)).beforeSend && t.beforeSend();
315
+ var u = window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("Microsoft.XMLHTTP");
316
+ u.open(t.method, t.url), u.setRequestHeader("X-Requested-With", "XMLHttpRequest"), Object.keys(t.headers).forEach(function(m) {
317
+ var h = t.headers[m];
318
+ u.setRequestHeader(m, h);
319
+ });
320
+ var g = t.ratio;
321
+ u.upload.addEventListener("progress", function(m) {
322
+ var h = Math.round(m.loaded / m.total * 100), k = Math.ceil(h * g / 100);
323
+ t.progress(Math.min(k, 100));
324
+ }, !1), u.addEventListener("progress", function(m) {
325
+ var h = Math.round(m.loaded / m.total * 100), k = Math.ceil(h * (100 - g) / 100) + g;
326
+ t.progress(Math.min(k, 100));
327
+ }, !1), u.onreadystatechange = function() {
328
+ if (u.readyState === 4) {
329
+ var m = u.response;
330
+ try {
331
+ m = JSON.parse(m);
332
+ } catch {
333
+ }
334
+ var h = w.parseHeaders(u.getAllResponseHeaders()), k = { body: m, code: u.status, headers: h };
335
+ y(u.status) ? n(k) : c(k);
336
+ }
337
+ }, u.send(t.data);
338
+ });
339
+ }, v = function(t) {
340
+ return t.method = "POST", d(t);
341
+ }, l = function() {
342
+ var t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
343
+ if (t.url && typeof t.url != "string") throw new Error("Url must be a string");
344
+ if (t.url = t.url || "", t.method && typeof t.method != "string") throw new Error("`method` must be a string or null");
345
+ if (t.method = t.method ? t.method.toUpperCase() : "GET", t.headers && o(t.headers) !== "object") throw new Error("`headers` must be an object or null");
346
+ if (t.headers = t.headers || {}, t.type && (typeof t.type != "string" || !Object.values(e).includes(t.type))) throw new Error("`type` must be taken from module's «contentType» library");
347
+ if (t.progress && typeof t.progress != "function") throw new Error("`progress` must be a function or null");
348
+ if (t.progress = t.progress || function(n) {
349
+ }, t.beforeSend = t.beforeSend || function(n) {
350
+ }, t.ratio && typeof t.ratio != "number") throw new Error("`ratio` must be a number");
351
+ if (t.ratio < 0 || t.ratio > 100) throw new Error("`ratio` must be in a 0-100 interval");
352
+ if (t.ratio = t.ratio || 90, t.accept && typeof t.accept != "string") throw new Error("`accept` must be a string with a list of allowed mime-types");
353
+ if (t.accept = t.accept || "*/*", t.multiple && typeof t.multiple != "boolean") throw new Error("`multiple` must be a true or false");
354
+ if (t.multiple = t.multiple || !1, t.fieldName && typeof t.fieldName != "string") throw new Error("`fieldName` must be a string");
355
+ return t.fieldName = t.fieldName || "files", t;
356
+ }, f = function(t) {
357
+ switch (t.method) {
358
+ case "GET":
359
+ var n = p(t.data, e.URLENCODED);
360
+ delete t.data, t.url = /\?/.test(t.url) ? t.url + "&" + n : t.url + "?" + n;
361
+ break;
362
+ case "POST":
363
+ case "PUT":
364
+ case "DELETE":
365
+ case "UPDATE":
366
+ var c = function() {
367
+ return (arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}).type || e.JSON;
368
+ }(t);
369
+ (w.isFormData(t.data) || w.isFormElement(t.data)) && (c = e.FORM), t.data = p(t.data, c), c !== b.contentType.FORM && (t.headers["content-type"] = c);
370
+ }
371
+ return t;
372
+ }, p = function() {
373
+ var t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
374
+ switch (arguments.length > 1 ? arguments[1] : void 0) {
375
+ case e.URLENCODED:
376
+ return w.urlEncode(t);
377
+ case e.JSON:
378
+ return w.jsonEncode(t);
379
+ case e.FORM:
380
+ return w.formEncode(t);
381
+ default:
382
+ return t;
383
+ }
384
+ }, y = function(t) {
385
+ return t >= 200 && t < 300;
386
+ }, { contentType: e = { URLENCODED: "application/x-www-form-urlencoded; charset=utf-8", FORM: "multipart/form-data", JSON: "application/json; charset=utf-8" }, request: d, get: function(t) {
387
+ return t.method = "GET", d(t);
388
+ }, post: v, transport: function(t) {
389
+ return t = l(t), w.selectFiles(t).then(function(n) {
390
+ for (var c = new FormData(), u = 0; u < n.length; u++) c.append(t.fieldName, n[u], n[u].name);
391
+ w.isObject(t.data) && Object.keys(t.data).forEach(function(m) {
392
+ var h = t.data[m];
393
+ c.append(m, h);
394
+ });
395
+ var g = t.beforeSend;
396
+ return t.beforeSend = function() {
397
+ return g(n);
398
+ }, t.data = c, v(t);
399
+ });
400
+ }, selectFiles: function(t) {
401
+ return delete (t = l(t)).beforeSend, w.selectFiles(t);
402
+ } });
403
+ a.exports = b;
404
+ }, function(a, s, r) {
405
+ r.r(s);
406
+ var o = r(1);
407
+ window.Promise = window.Promise || o.a;
408
+ }, function(a, s, r) {
409
+ (function(o) {
410
+ var e = o !== void 0 && o || typeof self < "u" && self || window, d = Function.prototype.apply;
411
+ function v(l, f) {
412
+ this._id = l, this._clearFn = f;
413
+ }
414
+ s.setTimeout = function() {
415
+ return new v(d.call(setTimeout, e, arguments), clearTimeout);
416
+ }, s.setInterval = function() {
417
+ return new v(d.call(setInterval, e, arguments), clearInterval);
418
+ }, s.clearTimeout = s.clearInterval = function(l) {
419
+ l && l.close();
420
+ }, v.prototype.unref = v.prototype.ref = function() {
421
+ }, v.prototype.close = function() {
422
+ this._clearFn.call(e, this._id);
423
+ }, s.enroll = function(l, f) {
424
+ clearTimeout(l._idleTimeoutId), l._idleTimeout = f;
425
+ }, s.unenroll = function(l) {
426
+ clearTimeout(l._idleTimeoutId), l._idleTimeout = -1;
427
+ }, s._unrefActive = s.active = function(l) {
428
+ clearTimeout(l._idleTimeoutId);
429
+ var f = l._idleTimeout;
430
+ f >= 0 && (l._idleTimeoutId = setTimeout(function() {
431
+ l._onTimeout && l._onTimeout();
432
+ }, f));
433
+ }, r(6), s.setImmediate = typeof self < "u" && self.setImmediate || o !== void 0 && o.setImmediate || this && this.setImmediate, s.clearImmediate = typeof self < "u" && self.clearImmediate || o !== void 0 && o.clearImmediate || this && this.clearImmediate;
434
+ }).call(this, r(0));
435
+ }, function(a, s, r) {
436
+ (function(o, e) {
437
+ (function(d, v) {
438
+ if (!d.setImmediate) {
439
+ var l, f, p, y, w, b = 1, t = {}, n = !1, c = d.document, u = Object.getPrototypeOf && Object.getPrototypeOf(d);
440
+ u = u && u.setTimeout ? u : d, {}.toString.call(d.process) === "[object process]" ? l = function(h) {
441
+ e.nextTick(function() {
442
+ m(h);
443
+ });
444
+ } : function() {
445
+ if (d.postMessage && !d.importScripts) {
446
+ var h = !0, k = d.onmessage;
447
+ return d.onmessage = function() {
448
+ h = !1;
449
+ }, d.postMessage("", "*"), d.onmessage = k, h;
450
+ }
451
+ }() ? (y = "setImmediate$" + Math.random() + "$", w = function(h) {
452
+ h.source === d && typeof h.data == "string" && h.data.indexOf(y) === 0 && m(+h.data.slice(y.length));
453
+ }, d.addEventListener ? d.addEventListener("message", w, !1) : d.attachEvent("onmessage", w), l = function(h) {
454
+ d.postMessage(y + h, "*");
455
+ }) : d.MessageChannel ? ((p = new MessageChannel()).port1.onmessage = function(h) {
456
+ m(h.data);
457
+ }, l = function(h) {
458
+ p.port2.postMessage(h);
459
+ }) : c && "onreadystatechange" in c.createElement("script") ? (f = c.documentElement, l = function(h) {
460
+ var k = c.createElement("script");
461
+ k.onreadystatechange = function() {
462
+ m(h), k.onreadystatechange = null, f.removeChild(k), k = null;
463
+ }, f.appendChild(k);
464
+ }) : l = function(h) {
465
+ setTimeout(m, 0, h);
466
+ }, u.setImmediate = function(h) {
467
+ typeof h != "function" && (h = new Function("" + h));
468
+ for (var k = new Array(arguments.length - 1), T = 0; T < k.length; T++) k[T] = arguments[T + 1];
469
+ var E = { callback: h, args: k };
470
+ return t[b] = E, l(b), b++;
471
+ }, u.clearImmediate = g;
472
+ }
473
+ function g(h) {
474
+ delete t[h];
475
+ }
476
+ function m(h) {
477
+ if (n) setTimeout(m, 0, h);
478
+ else {
479
+ var k = t[h];
480
+ if (k) {
481
+ n = !0;
482
+ try {
483
+ (function(T) {
484
+ var E = T.callback, M = T.args;
485
+ switch (M.length) {
486
+ case 0:
487
+ E();
488
+ break;
489
+ case 1:
490
+ E(M[0]);
491
+ break;
492
+ case 2:
493
+ E(M[0], M[1]);
494
+ break;
495
+ case 3:
496
+ E(M[0], M[1], M[2]);
497
+ break;
498
+ default:
499
+ E.apply(v, M);
500
+ }
501
+ })(k);
502
+ } finally {
503
+ g(h), n = !1;
504
+ }
505
+ }
506
+ }
507
+ }
508
+ })(typeof self > "u" ? o === void 0 ? this : o : self);
509
+ }).call(this, r(0), r(7));
510
+ }, function(a, s) {
511
+ var r, o, e = a.exports = {};
512
+ function d() {
513
+ throw new Error("setTimeout has not been defined");
514
+ }
515
+ function v() {
516
+ throw new Error("clearTimeout has not been defined");
517
+ }
518
+ function l(u) {
519
+ if (r === setTimeout) return setTimeout(u, 0);
520
+ if ((r === d || !r) && setTimeout) return r = setTimeout, setTimeout(u, 0);
521
+ try {
522
+ return r(u, 0);
523
+ } catch {
524
+ try {
525
+ return r.call(null, u, 0);
526
+ } catch {
527
+ return r.call(this, u, 0);
528
+ }
529
+ }
530
+ }
531
+ (function() {
532
+ try {
533
+ r = typeof setTimeout == "function" ? setTimeout : d;
534
+ } catch {
535
+ r = d;
536
+ }
537
+ try {
538
+ o = typeof clearTimeout == "function" ? clearTimeout : v;
539
+ } catch {
540
+ o = v;
541
+ }
542
+ })();
543
+ var f, p = [], y = !1, w = -1;
544
+ function b() {
545
+ y && f && (y = !1, f.length ? p = f.concat(p) : w = -1, p.length && t());
546
+ }
547
+ function t() {
548
+ if (!y) {
549
+ var u = l(b);
550
+ y = !0;
551
+ for (var g = p.length; g; ) {
552
+ for (f = p, p = []; ++w < g; ) f && f[w].run();
553
+ w = -1, g = p.length;
554
+ }
555
+ f = null, y = !1, function(m) {
556
+ if (o === clearTimeout) return clearTimeout(m);
557
+ if ((o === v || !o) && clearTimeout) return o = clearTimeout, clearTimeout(m);
558
+ try {
559
+ o(m);
560
+ } catch {
561
+ try {
562
+ return o.call(null, m);
563
+ } catch {
564
+ return o.call(this, m);
565
+ }
566
+ }
567
+ }(u);
568
+ }
569
+ }
570
+ function n(u, g) {
571
+ this.fun = u, this.array = g;
572
+ }
573
+ function c() {
574
+ }
575
+ e.nextTick = function(u) {
576
+ var g = new Array(arguments.length - 1);
577
+ if (arguments.length > 1) for (var m = 1; m < arguments.length; m++) g[m - 1] = arguments[m];
578
+ p.push(new n(u, g)), p.length !== 1 || y || l(t);
579
+ }, n.prototype.run = function() {
580
+ this.fun.apply(null, this.array);
581
+ }, e.title = "browser", e.browser = !0, e.env = {}, e.argv = [], e.version = "", e.versions = {}, e.on = c, e.addListener = c, e.once = c, e.off = c, e.removeListener = c, e.removeAllListeners = c, e.emit = c, e.prependListener = c, e.prependOnceListener = c, e.listeners = function(u) {
582
+ return [];
583
+ }, e.binding = function(u) {
584
+ throw new Error("process.binding is not supported");
585
+ }, e.cwd = function() {
586
+ return "/";
587
+ }, e.chdir = function(u) {
588
+ throw new Error("process.chdir is not supported");
589
+ }, e.umask = function() {
590
+ return 0;
591
+ };
592
+ }, function(a, s, r) {
593
+ function o(d, v) {
594
+ for (var l = 0; l < v.length; l++) {
595
+ var f = v[l];
596
+ f.enumerable = f.enumerable || !1, f.configurable = !0, "value" in f && (f.writable = !0), Object.defineProperty(d, f.key, f);
597
+ }
598
+ }
599
+ var e = r(9);
600
+ a.exports = function() {
601
+ function d() {
602
+ (function(p, y) {
603
+ if (!(p instanceof y)) throw new TypeError("Cannot call a class as a function");
604
+ })(this, d);
605
+ }
606
+ var v, l, f;
607
+ return v = d, f = [{ key: "urlEncode", value: function(p) {
608
+ return e(p);
609
+ } }, { key: "jsonEncode", value: function(p) {
610
+ return JSON.stringify(p);
611
+ } }, { key: "formEncode", value: function(p) {
612
+ if (this.isFormData(p)) return p;
613
+ if (this.isFormElement(p)) return new FormData(p);
614
+ if (this.isObject(p)) {
615
+ var y = new FormData();
616
+ return Object.keys(p).forEach(function(w) {
617
+ var b = p[w];
618
+ y.append(w, b);
619
+ }), y;
620
+ }
621
+ throw new Error("`data` must be an instance of Object, FormData or <FORM> HTMLElement");
622
+ } }, { key: "isObject", value: function(p) {
623
+ return Object.prototype.toString.call(p) === "[object Object]";
624
+ } }, { key: "isFormData", value: function(p) {
625
+ return p instanceof FormData;
626
+ } }, { key: "isFormElement", value: function(p) {
627
+ return p instanceof HTMLFormElement;
628
+ } }, { key: "selectFiles", value: function() {
629
+ var p = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
630
+ return new Promise(function(y, w) {
631
+ var b = document.createElement("INPUT");
632
+ b.type = "file", p.multiple && b.setAttribute("multiple", "multiple"), p.accept && b.setAttribute("accept", p.accept), b.style.display = "none", document.body.appendChild(b), b.addEventListener("change", function(t) {
633
+ var n = t.target.files;
634
+ y(n), document.body.removeChild(b);
635
+ }, !1), b.click();
636
+ });
637
+ } }, { key: "parseHeaders", value: function(p) {
638
+ var y = p.trim().split(/[\r\n]+/), w = {};
639
+ return y.forEach(function(b) {
640
+ var t = b.split(": "), n = t.shift(), c = t.join(": ");
641
+ n && (w[n] = c);
642
+ }), w;
643
+ } }], (l = null) && o(v.prototype, l), f && o(v, f), d;
644
+ }();
645
+ }, function(a, s) {
646
+ var r = function(e) {
647
+ return encodeURIComponent(e).replace(/[!'()*]/g, escape).replace(/%20/g, "+");
648
+ }, o = function(e, d, v, l) {
649
+ return d = d || null, v = v || "&", l = l || null, e ? function(f) {
650
+ for (var p = new Array(), y = 0; y < f.length; y++) f[y] && p.push(f[y]);
651
+ return p;
652
+ }(Object.keys(e).map(function(f) {
653
+ var p, y, w = f;
654
+ if (l && (w = l + "[" + w + "]"), typeof e[f] == "object" && e[f] !== null) p = o(e[f], null, v, w);
655
+ else {
656
+ d && (y = w, w = !isNaN(parseFloat(y)) && isFinite(y) ? d + Number(w) : w);
657
+ var b = e[f];
658
+ b = (b = (b = (b = b === !0 ? "1" : b) === !1 ? "0" : b) === 0 ? "0" : b) || "", p = r(w) + "=" + r(b);
659
+ }
660
+ return p;
661
+ })).join(v).replace(/[!'()*]/g, "") : "";
662
+ };
663
+ a.exports = o;
664
+ }]);
665
+ });
666
+ })(H);
667
+ var q = H.exports;
668
+ const j = /* @__PURE__ */ U(q);
669
+ function O(C) {
670
+ return C !== void 0 && typeof C.then == "function";
671
+ }
672
+ class A {
673
+ /**
674
+ * @param params - uploader module params
675
+ * @param params.config - image tool config
676
+ * @param params.onUpload - one callback for all uploading (file, url, d-n-d, pasting)
677
+ * @param params.onError - callback for uploading errors
678
+ */
679
+ constructor({ config: i, onUpload: a, onError: s }) {
680
+ this.config = i, this.onUpload = a, this.onError = s;
681
+ }
682
+ /**
683
+ * Handle clicks on the upload file button
684
+ * Fires ajax.transport()
685
+ * @param onPreview - callback fired when preview is ready
686
+ */
687
+ uploadSelectedFile({ onPreview: i }) {
688
+ const a = function(r) {
689
+ const o = new FileReader();
690
+ o.readAsDataURL(r), o.onload = (e) => {
691
+ i(e.target.result);
692
+ };
693
+ };
694
+ let s;
695
+ if (this.config.uploader && typeof this.config.uploader.uploadByFile == "function") {
696
+ const r = this.config.uploader.uploadByFile;
697
+ s = j.selectFiles({ accept: this.config.types ?? "image/*" }).then((o) => {
698
+ a(o[0]);
699
+ const e = r(o[0]);
700
+ return O(e) || console.warn("Custom uploader method uploadByFile should return a Promise"), e;
701
+ });
702
+ } else
703
+ s = j.transport({
704
+ url: this.config.endpoints.byFile,
705
+ data: this.config.additionalRequestData,
706
+ accept: this.config.types ?? "image/*",
707
+ headers: this.config.additionalRequestHeaders,
708
+ beforeSend: (r) => {
709
+ a(r[0]);
710
+ },
711
+ fieldName: this.config.field ?? "image"
712
+ }).then((r) => r.body);
713
+ s.then((r) => {
714
+ this.onUpload(r);
715
+ }).catch((r) => {
716
+ this.onError(r);
717
+ });
718
+ }
719
+ /**
720
+ * Handle clicks on the upload file button
721
+ * Fires ajax.post()
722
+ * @param url - image source url
723
+ */
724
+ uploadByUrl(i) {
725
+ let a;
726
+ this.config.uploader && typeof this.config.uploader.uploadByUrl == "function" ? (a = this.config.uploader.uploadByUrl(i), O(a) || console.warn("Custom uploader method uploadByUrl should return a Promise")) : a = j.post({
727
+ url: this.config.endpoints.byUrl,
728
+ data: Object.assign({
729
+ url: i
730
+ }, this.config.additionalRequestData),
731
+ type: j.contentType.JSON,
732
+ headers: this.config.additionalRequestHeaders
733
+ }).then((s) => s.body), a.then((s) => {
734
+ this.onUpload(s);
735
+ }).catch((s) => {
736
+ this.onError(s);
737
+ });
738
+ }
739
+ /**
740
+ * Handle clicks on the upload file button
741
+ * Fires ajax.post()
742
+ * @param file - file pasted by drag-n-drop
743
+ * @param onPreview - file pasted by drag-n-drop
744
+ */
745
+ uploadByFile(i, { onPreview: a }) {
746
+ const s = new FileReader();
747
+ s.readAsDataURL(i), s.onload = (o) => {
748
+ a(o.target.result);
749
+ };
750
+ let r;
751
+ if (this.config.uploader && typeof this.config.uploader.uploadByFile == "function")
752
+ r = this.config.uploader.uploadByFile(i), O(r) || console.warn("Custom uploader method uploadByFile should return a Promise");
753
+ else {
754
+ const o = new FormData();
755
+ o.append(this.config.field ?? "image", i), this.config.additionalRequestData && Object.keys(this.config.additionalRequestData).length && Object.entries(this.config.additionalRequestData).forEach(([e, d]) => {
756
+ o.append(e, d);
757
+ }), r = j.post({
758
+ url: this.config.endpoints.byFile,
759
+ data: o,
760
+ type: j.contentType.JSON,
761
+ headers: this.config.additionalRequestHeaders
762
+ }).then((e) => e.body);
763
+ }
764
+ r.then((o) => {
765
+ this.onUpload(o);
766
+ }).catch((o) => {
767
+ this.onError(o);
768
+ });
769
+ }
770
+ }
771
+ /**
772
+ * Image Tool for the Editor.js
773
+ * @author CodeX <team@codex.so>
774
+ * @license MIT
775
+ * @see {@link https://github.com/editor-js/image}
776
+ *
777
+ * To developers.
778
+ * To simplify Tool structure, we split it to 4 parts:
779
+ * 1) index.ts — main Tool's interface, public API and methods for working with data
780
+ * 2) uploader.ts — module that has methods for sending files via AJAX: from device, by URL or File pasting
781
+ * 3) ui.ts — module for UI manipulations: render, showing preloader, etc
782
+ *
783
+ * For debug purposes there is a testing server
784
+ * that can save uploaded files and return a Response {@link UploadResponseFormat}
785
+ *
786
+ * $ node dev/server.js
787
+ *
788
+ * It will expose 8008 port, so you can pass http://localhost:8008 with the Tools config:
789
+ *
790
+ * image: {
791
+ * class: ImageTool,
792
+ * config: {
793
+ * endpoints: {
794
+ * byFile: 'http://localhost:8008/uploadFile',
795
+ * byUrl: 'http://localhost:8008/fetchUrl',
796
+ * }
797
+ * },
798
+ * },
799
+ */
800
+ class P {
801
+ /**
802
+ * @param tool - tool properties got from editor.js
803
+ * @param tool.data - previously saved data
804
+ * @param tool.config - user config for Tool
805
+ * @param tool.api - Editor.js API
806
+ * @param tool.readOnly - read-only mode flag
807
+ * @param tool.block - current Block API
808
+ */
809
+ constructor({ data: i, config: a, api: s, readOnly: r, block: o }) {
810
+ this.isCaptionEnabled = null, this.api = s, this.block = o, this.config = {
811
+ endpoints: a.endpoints,
812
+ additionalRequestData: a.additionalRequestData,
813
+ additionalRequestHeaders: a.additionalRequestHeaders,
814
+ field: a.field,
815
+ types: a.types,
816
+ captionPlaceholder: this.api.i18n.t(a.captionPlaceholder ?? "Caption"),
817
+ buttonContent: a.buttonContent,
818
+ uploader: a.uploader,
819
+ actions: a.actions,
820
+ features: a.features || {}
821
+ }, this.uploader = new A({
822
+ config: this.config,
823
+ onUpload: (e) => this.onUpload(e),
824
+ onError: (e) => this.uploadingFailed(e)
825
+ }), this.ui = new D({
826
+ api: s,
827
+ config: this.config,
828
+ onSelectFile: () => {
829
+ this.uploader.uploadSelectedFile({
830
+ onPreview: (e) => {
831
+ this.ui.showPreloader(e);
832
+ }
833
+ });
834
+ },
835
+ readOnly: r
836
+ }), this._data = {
837
+ caption: "",
838
+ withBorder: !1,
839
+ withBackground: !1,
840
+ stretched: !1,
841
+ file: {
842
+ url: ""
843
+ }
844
+ }, this.data = i;
845
+ }
846
+ /**
847
+ * Notify core that read-only mode is supported
848
+ */
849
+ static get isReadOnlySupported() {
850
+ return !0;
851
+ }
852
+ /**
853
+ * Get Tool toolbox settings
854
+ * icon - Tool icon's SVG
855
+ * title - title to show in toolbox
856
+ */
857
+ static get toolbox() {
858
+ return {
859
+ icon: L,
860
+ title: "Image"
861
+ };
862
+ }
863
+ /**
864
+ * Available image tools
865
+ */
866
+ static get tunes() {
867
+ return [
868
+ {
869
+ name: "withBorder",
870
+ icon: I,
871
+ title: "With border",
872
+ toggle: !0
873
+ },
874
+ {
875
+ name: "stretched",
876
+ icon: x,
877
+ title: "Stretch image",
878
+ toggle: !0
879
+ },
880
+ {
881
+ name: "withBackground",
882
+ icon: R,
883
+ title: "With background",
884
+ toggle: !0
885
+ }
886
+ ];
887
+ }
888
+ /**
889
+ * Renders Block content
890
+ */
891
+ render() {
892
+ var i, a, s;
893
+ return (((i = this.config.features) == null ? void 0 : i.caption) === !0 || ((a = this.config.features) == null ? void 0 : a.caption) === void 0 || ((s = this.config.features) == null ? void 0 : s.caption) === "optional" && this.data.caption) && (this.isCaptionEnabled = !0, this.ui.applyTune("caption", !0)), this.ui.render();
894
+ }
895
+ /**
896
+ * Validate data: check if Image exists
897
+ * @param savedData — data received after saving
898
+ * @returns false if saved data is not correct, otherwise true
899
+ */
900
+ validate(i) {
901
+ return !!i.file.url;
902
+ }
903
+ /**
904
+ * Return Block data
905
+ */
906
+ save() {
907
+ const i = this.ui.nodes.caption;
908
+ return this._data.caption = i.innerHTML, this.data;
909
+ }
910
+ /**
911
+ * Returns configuration for block tunes: add background, add border, stretch image
912
+ * @returns TunesMenuConfig
913
+ */
914
+ renderSettings() {
915
+ var o;
916
+ const i = P.tunes.concat(this.config.actions || []), a = {
917
+ border: "withBorder",
918
+ background: "withBackground",
919
+ stretch: "stretched",
920
+ caption: "caption"
921
+ };
922
+ ((o = this.config.features) == null ? void 0 : o.caption) === "optional" && i.push({
923
+ name: "caption",
924
+ icon: B,
925
+ title: "With caption",
926
+ toggle: !0
927
+ });
928
+ const s = i.filter((e) => {
929
+ var v, l;
930
+ const d = Object.keys(a).find((f) => a[f] === e.name);
931
+ return d === "caption" ? ((v = this.config.features) == null ? void 0 : v.caption) !== !1 : d == null || ((l = this.config.features) == null ? void 0 : l[d]) !== !1;
932
+ }), r = (e) => {
933
+ let d = this.data[e.name];
934
+ return e.name === "caption" && (d = this.isCaptionEnabled ?? d), d;
935
+ };
936
+ return s.map((e) => ({
937
+ icon: e.icon,
938
+ label: this.api.i18n.t(e.title),
939
+ name: e.name,
940
+ toggle: e.toggle,
941
+ isActive: r(e),
942
+ onActivate: () => {
943
+ if (typeof e.action == "function") {
944
+ e.action(e.name);
945
+ return;
946
+ }
947
+ let d = !r(e);
948
+ e.name === "caption" && (this.isCaptionEnabled = !(this.isCaptionEnabled ?? !1), d = this.isCaptionEnabled), this.tuneToggled(e.name, d);
949
+ }
950
+ }));
951
+ }
952
+ /**
953
+ * Fires after clicks on the Toolbox Image Icon
954
+ * Initiates click on the Select File button
955
+ */
956
+ appendCallback() {
957
+ this.ui.nodes.fileButton.click();
958
+ }
959
+ /**
960
+ * Specify paste substitutes
961
+ * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
962
+ */
963
+ static get pasteConfig() {
964
+ return {
965
+ /**
966
+ * Paste HTML into Editor
967
+ */
968
+ tags: [
969
+ {
970
+ img: { src: !0 }
971
+ }
972
+ ],
973
+ /**
974
+ * Paste URL of image into the Editor
975
+ */
976
+ patterns: {
977
+ image: /https?:\/\/\S+\.(gif|jpe?g|tiff|png|svg|webp)(\?[a-z0-9=]*)?$/i
978
+ },
979
+ /**
980
+ * Drag n drop file from into the Editor
981
+ */
982
+ files: {
983
+ mimeTypes: ["image/*"]
984
+ }
985
+ };
986
+ }
987
+ /**
988
+ * Specify paste handlers
989
+ * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
990
+ * @param event - editor.js custom paste event
991
+ * {@link https://github.com/codex-team/editor.js/blob/master/types/tools/paste-events.d.ts}
992
+ */
993
+ async onPaste(i) {
994
+ switch (i.type) {
995
+ case "tag": {
996
+ const a = i.detail.data;
997
+ if (/^blob:/.test(a.src)) {
998
+ const r = await (await fetch(a.src)).blob();
999
+ this.uploadFile(r);
1000
+ break;
1001
+ }
1002
+ this.uploadUrl(a.src);
1003
+ break;
1004
+ }
1005
+ case "pattern": {
1006
+ const a = i.detail.data;
1007
+ this.uploadUrl(a);
1008
+ break;
1009
+ }
1010
+ case "file": {
1011
+ const a = i.detail.file;
1012
+ this.uploadFile(a);
1013
+ break;
1014
+ }
1015
+ }
1016
+ }
1017
+ /**
1018
+ * Private methods
1019
+ * ̿̿ ̿̿ ̿̿ ̿'̿'\̵͇̿̿\з= ( ▀ ͜͞ʖ▀) =ε/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿ ̿̿
1020
+ */
1021
+ /**
1022
+ * Stores all Tool's data
1023
+ * @param data - data in Image Tool format
1024
+ */
1025
+ set data(i) {
1026
+ var a;
1027
+ this.image = i.file, this._data.caption = i.caption || "", this.ui.fillCaption(this._data.caption), P.tunes.forEach(({ name: s }) => {
1028
+ const r = typeof i[s] < "u" ? i[s] === !0 || i[s] === "true" : !1;
1029
+ this.setTune(s, r);
1030
+ }), i.caption ? this.setTune("caption", !0) : ((a = this.config.features) == null ? void 0 : a.caption) === !0 && this.setTune("caption", !0);
1031
+ }
1032
+ /**
1033
+ * Return Tool data
1034
+ */
1035
+ get data() {
1036
+ return this._data;
1037
+ }
1038
+ /**
1039
+ * Set new image file
1040
+ * @param file - uploaded file data
1041
+ */
1042
+ set image(i) {
1043
+ this._data.file = i || { url: "" }, i && i.url && this.ui.fillImage(i.url);
1044
+ }
1045
+ /**
1046
+ * File uploading callback
1047
+ * @param response - uploading server response
1048
+ */
1049
+ onUpload(i) {
1050
+ i.success && i.file ? this.image = i.file : this.uploadingFailed("incorrect response: " + JSON.stringify(i));
1051
+ }
1052
+ /**
1053
+ * Handle uploader errors
1054
+ * @param errorText - uploading error info
1055
+ */
1056
+ uploadingFailed(i) {
1057
+ console.log("Image Tool: uploading failed because of", i), this.api.notifier.show({
1058
+ message: this.api.i18n.t("Couldn’t upload image. Please try another."),
1059
+ style: "error"
1060
+ }), this.ui.hidePreloader();
1061
+ }
1062
+ /**
1063
+ * Callback fired when Block Tune is activated
1064
+ * @param tuneName - tune that has been clicked
1065
+ * @param state - new state
1066
+ */
1067
+ tuneToggled(i, a) {
1068
+ i === "caption" ? (this.ui.applyTune(i, a), a == !1 && (this._data.caption = "", this.ui.fillCaption(""))) : this.setTune(i, a);
1069
+ }
1070
+ /**
1071
+ * Set one tune
1072
+ * @param tuneName - {@link Tunes.tunes}
1073
+ * @param value - tune state
1074
+ */
1075
+ setTune(i, a) {
1076
+ this._data[i] = a, this.ui.applyTune(i, a), i === "stretched" && Promise.resolve().then(() => {
1077
+ this.block.stretched = a;
1078
+ }).catch((s) => {
1079
+ console.error(s);
1080
+ });
1081
+ }
1082
+ /**
1083
+ * Show preloader and upload image file
1084
+ * @param file - file that is currently uploading (from paste)
1085
+ */
1086
+ uploadFile(i) {
1087
+ this.uploader.uploadByFile(i, {
1088
+ onPreview: (a) => {
1089
+ this.ui.showPreloader(a);
1090
+ }
1091
+ });
1092
+ }
1093
+ /**
1094
+ * Show preloader and upload image by target url
1095
+ * @param url - url pasted
1096
+ */
1097
+ uploadUrl(i) {
1098
+ this.ui.showPreloader(i), this.uploader.uploadByUrl(i);
1099
+ }
1100
+ }
1101
+ export {
1102
+ P as default
1103
+ };