@themarioga/grid-editor 9.0.0-beta.1 → 9.0.0-beta.2
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/CHANGELOG.md +14 -0
- package/README.md +18 -0
- package/UPGRADING.md +5 -0
- package/dist/grideditor.bundle.min.js +2 -2
- package/dist/grideditor.d.ts +3 -1
- package/dist/grideditor.esm.js +36 -1
- package/dist/grideditor.js +36 -1
- package/dist/grideditor.min.js +1 -1
- package/dist/grideditor.min.js.map +3 -3
- package/dist/plugins/grideditor.clipboard.esm.js +116 -2
- package/dist/plugins/grideditor.clipboard.js +116 -2
- package/dist/plugins/grideditor.clipboard.min.js +1 -1
- package/dist/plugins/grideditor.clipboard.min.js.map +3 -3
- package/docs/events.md +1 -1
- package/docs/plugins.md +4 -0
- package/package.json +1 -1
- package/src/js/grideditor.js +21 -0
- package/src/js/plugins/grideditor.clipboard.js +125 -2
- package/types/grideditor.d.ts +3 -1
|
@@ -19,6 +19,9 @@ function children(node, selector) {
|
|
|
19
19
|
function child(node, selector) {
|
|
20
20
|
return children(node, selector)[0] || null;
|
|
21
21
|
}
|
|
22
|
+
function is(node, selector) {
|
|
23
|
+
return !!node && node.nodeType === 1 && node.matches(selector);
|
|
24
|
+
}
|
|
22
25
|
function addClass(node, names) {
|
|
23
26
|
split(names).forEach(function(name) {
|
|
24
27
|
node.classList.add(name);
|
|
@@ -31,6 +34,9 @@ function removeClass(node, names) {
|
|
|
31
34
|
});
|
|
32
35
|
return node;
|
|
33
36
|
}
|
|
37
|
+
function hasClass(node, name) {
|
|
38
|
+
return !!node && node.nodeType === 1 && node.classList.contains(name);
|
|
39
|
+
}
|
|
34
40
|
function split(names) {
|
|
35
41
|
return String(names || "").split(/\s+/).filter(Boolean);
|
|
36
42
|
}
|
|
@@ -118,9 +124,34 @@ window.addEventListener("storage", function(e) {
|
|
|
118
124
|
announce();
|
|
119
125
|
}
|
|
120
126
|
});
|
|
127
|
+
var listeningEditors = [];
|
|
128
|
+
var lastEditor = null;
|
|
129
|
+
function track(e) {
|
|
130
|
+
lastEditor = listeningEditors.filter(function(ge) {
|
|
131
|
+
return ge.contains(e.target);
|
|
132
|
+
})[0] || null;
|
|
133
|
+
}
|
|
134
|
+
document.addEventListener("pointerdown", track, true);
|
|
135
|
+
document.addEventListener("focusin", track, true);
|
|
136
|
+
function shortcutKey(e) {
|
|
137
|
+
var key = (e.key || "").toLowerCase();
|
|
138
|
+
if (!/^[a-z]$/.test(key)) {
|
|
139
|
+
key = { KeyC: "c", KeyV: "v" }[e.code] || "";
|
|
140
|
+
}
|
|
141
|
+
return key === "c" || key === "v" ? key : null;
|
|
142
|
+
}
|
|
143
|
+
function typing() {
|
|
144
|
+
var focus = document.activeElement;
|
|
145
|
+
return !!focus && (is(focus, "input, textarea, select") || focus.isContentEditable);
|
|
146
|
+
}
|
|
147
|
+
function textSelected() {
|
|
148
|
+
var selection = window.getSelection ? window.getSelection() : null;
|
|
149
|
+
return !!selection && !selection.isCollapsed && selection.toString() !== "";
|
|
150
|
+
}
|
|
121
151
|
GridEditor.features.clipboard = function(ge) {
|
|
122
152
|
var shown = null;
|
|
123
153
|
var listening = false;
|
|
154
|
+
var shortcuts = shortcutsSetting();
|
|
124
155
|
function used(name) {
|
|
125
156
|
return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;
|
|
126
157
|
}
|
|
@@ -145,10 +176,26 @@ GridEditor.features.clipboard = function(ge) {
|
|
|
145
176
|
}
|
|
146
177
|
return ["row", "column", "section", "text", "element"].indexOf(kind) !== -1 ? kind : null;
|
|
147
178
|
}
|
|
148
|
-
function
|
|
179
|
+
function shortcutsSetting() {
|
|
180
|
+
var wanted = (ge.settings.clipboard || {}).shortcuts;
|
|
181
|
+
var target = !!ge.settings.active_target;
|
|
182
|
+
if (wanted !== void 0 && typeof wanted !== "boolean") {
|
|
183
|
+
ge.warn("clipboard.shortcuts is true or false: " + (target ? "on" : "off") + ", as with active_target, is used");
|
|
184
|
+
wanted = void 0;
|
|
185
|
+
}
|
|
186
|
+
if (wanted === void 0) {
|
|
187
|
+
return target;
|
|
188
|
+
}
|
|
189
|
+
if (wanted && !target) {
|
|
190
|
+
ge.warn('clipboard.shortcuts needs active_target, or settings_panel "sidebar": without it, Ctrl+C and Ctrl+V do nothing');
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return wanted;
|
|
194
|
+
}
|
|
195
|
+
function copy(node, kind, source) {
|
|
149
196
|
var html = ge.nodeHtml(node);
|
|
150
197
|
write({ version: VERSION, category: categoryOf(kind), kind, html });
|
|
151
|
-
ge.emit("after-copy", ge.payloadFor(kind, node, { source: "tool" }));
|
|
198
|
+
ge.emit("after-copy", ge.payloadFor(kind, node, { source: source || "tool" }));
|
|
152
199
|
var tool = child(ge.drawerOf(node), ".ge-copy");
|
|
153
200
|
if (!tool) {
|
|
154
201
|
return;
|
|
@@ -172,6 +219,66 @@ GridEditor.features.clipboard = function(ge) {
|
|
|
172
219
|
}
|
|
173
220
|
ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: "paste" });
|
|
174
221
|
}
|
|
222
|
+
function pasteByShortcut() {
|
|
223
|
+
var clip = read();
|
|
224
|
+
var target = ge.getActiveTarget();
|
|
225
|
+
var node = target || ge.getSelected();
|
|
226
|
+
if (!clip || !node) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
var placement = { appendTo: node, source: "paste" };
|
|
230
|
+
if (target && clip.category === "section") {
|
|
231
|
+
if (!available(clip)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
var top = target;
|
|
235
|
+
while (top.parentElement && top.parentElement !== ge.canvas) {
|
|
236
|
+
top = top.parentElement;
|
|
237
|
+
}
|
|
238
|
+
placement = { insertAfter: top, source: "paste" };
|
|
239
|
+
} else if (!fits(clip, TARGETS[ge.kindOf(node)] || [])) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
var made = ge.place(ge.freshNode(clip.html), clip.kind, placement);
|
|
243
|
+
if (made && made.scrollIntoView) {
|
|
244
|
+
made.scrollIntoView({ behavior: "smooth" });
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
function copyByShortcut() {
|
|
249
|
+
if (textSelected()) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
var node = ge.getSelected() || ge.getActiveTarget();
|
|
253
|
+
var kind = node && ge.kindOf(node);
|
|
254
|
+
if (!kind || !categoryOf(kind)) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
copy(node, kind, "shortcut");
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
function onKeydown(e) {
|
|
261
|
+
if (lastEditor !== ge || e.repeat || e.shiftKey || e.altKey || !(e.ctrlKey || e.metaKey)) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
var key = shortcutKey(e);
|
|
265
|
+
if (!key || !hasClass(ge.canvas, "ge-editing") || typing() || ge.panelOpen()) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (key === "c" ? copyByShortcut() : pasteByShortcut()) {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function listenForShortcuts(on) {
|
|
273
|
+
var at = listeningEditors.indexOf(ge);
|
|
274
|
+
if (on && at === -1) {
|
|
275
|
+
listeningEditors.push(ge);
|
|
276
|
+
document.addEventListener("keydown", onKeydown);
|
|
277
|
+
} else if (!on && at !== -1) {
|
|
278
|
+
listeningEditors.splice(at, 1);
|
|
279
|
+
document.removeEventListener("keydown", onKeydown);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
175
282
|
function refresh() {
|
|
176
283
|
var clip = read();
|
|
177
284
|
all(ge.canvas, ".ge-paste").forEach(function(tool) {
|
|
@@ -229,10 +336,17 @@ GridEditor.features.clipboard = function(ge) {
|
|
|
229
336
|
document.addEventListener(CHANGE, refresh);
|
|
230
337
|
listening = true;
|
|
231
338
|
}
|
|
339
|
+
listenForShortcuts(shortcuts);
|
|
232
340
|
},
|
|
233
341
|
onDeinit: function() {
|
|
234
342
|
document.removeEventListener(CHANGE, refresh);
|
|
235
343
|
listening = false;
|
|
344
|
+
listenForShortcuts(false);
|
|
345
|
+
},
|
|
346
|
+
onDestroy: function() {
|
|
347
|
+
if (lastEditor === ge) {
|
|
348
|
+
lastEditor = null;
|
|
349
|
+
}
|
|
236
350
|
},
|
|
237
351
|
// Nothing of its own on the canvas: the tools are in the drawers
|
|
238
352
|
cleanMarkup: function() {
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
function child(node, selector) {
|
|
24
24
|
return children(node, selector)[0] || null;
|
|
25
25
|
}
|
|
26
|
+
function is(node, selector) {
|
|
27
|
+
return !!node && node.nodeType === 1 && node.matches(selector);
|
|
28
|
+
}
|
|
26
29
|
function addClass(node, names) {
|
|
27
30
|
split(names).forEach(function(name) {
|
|
28
31
|
node.classList.add(name);
|
|
@@ -35,6 +38,9 @@
|
|
|
35
38
|
});
|
|
36
39
|
return node;
|
|
37
40
|
}
|
|
41
|
+
function hasClass(node, name) {
|
|
42
|
+
return !!node && node.nodeType === 1 && node.classList.contains(name);
|
|
43
|
+
}
|
|
38
44
|
function split(names) {
|
|
39
45
|
return String(names || "").split(/\s+/).filter(Boolean);
|
|
40
46
|
}
|
|
@@ -122,9 +128,34 @@
|
|
|
122
128
|
announce();
|
|
123
129
|
}
|
|
124
130
|
});
|
|
131
|
+
var listeningEditors = [];
|
|
132
|
+
var lastEditor = null;
|
|
133
|
+
function track(e) {
|
|
134
|
+
lastEditor = listeningEditors.filter(function(ge) {
|
|
135
|
+
return ge.contains(e.target);
|
|
136
|
+
})[0] || null;
|
|
137
|
+
}
|
|
138
|
+
document.addEventListener("pointerdown", track, true);
|
|
139
|
+
document.addEventListener("focusin", track, true);
|
|
140
|
+
function shortcutKey(e) {
|
|
141
|
+
var key = (e.key || "").toLowerCase();
|
|
142
|
+
if (!/^[a-z]$/.test(key)) {
|
|
143
|
+
key = { KeyC: "c", KeyV: "v" }[e.code] || "";
|
|
144
|
+
}
|
|
145
|
+
return key === "c" || key === "v" ? key : null;
|
|
146
|
+
}
|
|
147
|
+
function typing() {
|
|
148
|
+
var focus = document.activeElement;
|
|
149
|
+
return !!focus && (is(focus, "input, textarea, select") || focus.isContentEditable);
|
|
150
|
+
}
|
|
151
|
+
function textSelected() {
|
|
152
|
+
var selection = window.getSelection ? window.getSelection() : null;
|
|
153
|
+
return !!selection && !selection.isCollapsed && selection.toString() !== "";
|
|
154
|
+
}
|
|
125
155
|
GridEditor.features.clipboard = function(ge) {
|
|
126
156
|
var shown = null;
|
|
127
157
|
var listening = false;
|
|
158
|
+
var shortcuts = shortcutsSetting();
|
|
128
159
|
function used(name) {
|
|
129
160
|
return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;
|
|
130
161
|
}
|
|
@@ -149,10 +180,26 @@
|
|
|
149
180
|
}
|
|
150
181
|
return ["row", "column", "section", "text", "element"].indexOf(kind) !== -1 ? kind : null;
|
|
151
182
|
}
|
|
152
|
-
function
|
|
183
|
+
function shortcutsSetting() {
|
|
184
|
+
var wanted = (ge.settings.clipboard || {}).shortcuts;
|
|
185
|
+
var target = !!ge.settings.active_target;
|
|
186
|
+
if (wanted !== void 0 && typeof wanted !== "boolean") {
|
|
187
|
+
ge.warn("clipboard.shortcuts is true or false: " + (target ? "on" : "off") + ", as with active_target, is used");
|
|
188
|
+
wanted = void 0;
|
|
189
|
+
}
|
|
190
|
+
if (wanted === void 0) {
|
|
191
|
+
return target;
|
|
192
|
+
}
|
|
193
|
+
if (wanted && !target) {
|
|
194
|
+
ge.warn('clipboard.shortcuts needs active_target, or settings_panel "sidebar": without it, Ctrl+C and Ctrl+V do nothing');
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
return wanted;
|
|
198
|
+
}
|
|
199
|
+
function copy(node, kind, source) {
|
|
153
200
|
var html = ge.nodeHtml(node);
|
|
154
201
|
write({ version: VERSION, category: categoryOf(kind), kind, html });
|
|
155
|
-
ge.emit("after-copy", ge.payloadFor(kind, node, { source: "tool" }));
|
|
202
|
+
ge.emit("after-copy", ge.payloadFor(kind, node, { source: source || "tool" }));
|
|
156
203
|
var tool = child(ge.drawerOf(node), ".ge-copy");
|
|
157
204
|
if (!tool) {
|
|
158
205
|
return;
|
|
@@ -176,6 +223,66 @@
|
|
|
176
223
|
}
|
|
177
224
|
ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: "paste" });
|
|
178
225
|
}
|
|
226
|
+
function pasteByShortcut() {
|
|
227
|
+
var clip = read();
|
|
228
|
+
var target = ge.getActiveTarget();
|
|
229
|
+
var node = target || ge.getSelected();
|
|
230
|
+
if (!clip || !node) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
var placement = { appendTo: node, source: "paste" };
|
|
234
|
+
if (target && clip.category === "section") {
|
|
235
|
+
if (!available(clip)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
var top = target;
|
|
239
|
+
while (top.parentElement && top.parentElement !== ge.canvas) {
|
|
240
|
+
top = top.parentElement;
|
|
241
|
+
}
|
|
242
|
+
placement = { insertAfter: top, source: "paste" };
|
|
243
|
+
} else if (!fits(clip, TARGETS[ge.kindOf(node)] || [])) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
var made = ge.place(ge.freshNode(clip.html), clip.kind, placement);
|
|
247
|
+
if (made && made.scrollIntoView) {
|
|
248
|
+
made.scrollIntoView({ behavior: "smooth" });
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
function copyByShortcut() {
|
|
253
|
+
if (textSelected()) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
var node = ge.getSelected() || ge.getActiveTarget();
|
|
257
|
+
var kind = node && ge.kindOf(node);
|
|
258
|
+
if (!kind || !categoryOf(kind)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
copy(node, kind, "shortcut");
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
function onKeydown(e) {
|
|
265
|
+
if (lastEditor !== ge || e.repeat || e.shiftKey || e.altKey || !(e.ctrlKey || e.metaKey)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
var key = shortcutKey(e);
|
|
269
|
+
if (!key || !hasClass(ge.canvas, "ge-editing") || typing() || ge.panelOpen()) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (key === "c" ? copyByShortcut() : pasteByShortcut()) {
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function listenForShortcuts(on) {
|
|
277
|
+
var at = listeningEditors.indexOf(ge);
|
|
278
|
+
if (on && at === -1) {
|
|
279
|
+
listeningEditors.push(ge);
|
|
280
|
+
document.addEventListener("keydown", onKeydown);
|
|
281
|
+
} else if (!on && at !== -1) {
|
|
282
|
+
listeningEditors.splice(at, 1);
|
|
283
|
+
document.removeEventListener("keydown", onKeydown);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
179
286
|
function refresh() {
|
|
180
287
|
var clip = read();
|
|
181
288
|
all(ge.canvas, ".ge-paste").forEach(function(tool) {
|
|
@@ -233,10 +340,17 @@
|
|
|
233
340
|
document.addEventListener(CHANGE, refresh);
|
|
234
341
|
listening = true;
|
|
235
342
|
}
|
|
343
|
+
listenForShortcuts(shortcuts);
|
|
236
344
|
},
|
|
237
345
|
onDeinit: function() {
|
|
238
346
|
document.removeEventListener(CHANGE, refresh);
|
|
239
347
|
listening = false;
|
|
348
|
+
listenForShortcuts(false);
|
|
349
|
+
},
|
|
350
|
+
onDestroy: function() {
|
|
351
|
+
if (lastEditor === ge) {
|
|
352
|
+
lastEditor = null;
|
|
353
|
+
}
|
|
240
354
|
},
|
|
241
355
|
// Nothing of its own on the canvas: the tools are in the drawers
|
|
242
356
|
cleanMarkup: function() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var
|
|
1
|
+
(()=>{var c=window.GridEditor;if(!c||typeof c.get!="function")throw new Error("grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, its locales and grideditor.jquery.js");function O(t,n){return t?Array.prototype.slice.call(t.querySelectorAll(n)):[]}function k(t,n){return t?t.querySelector(n):null}function V(t,n){return t?Array.prototype.filter.call(t.children,function(o){return!n||o.matches(n)}):[]}function L(t,n){return V(t,n)[0]||null}function B(t,n){return!!t&&t.nodeType===1&&t.matches(n)}function N(t,n){return K(n).forEach(function(o){t.classList.add(o)}),t}function I(t,n){return K(n).forEach(function(o){t.classList.remove(o)}),t}function j(t,n){return!!t&&t.nodeType===1&&t.classList.contains(n)}function K(t){return String(t||"").split(/\s+/).filter(Boolean)}var f={};function q(t){var n=t.nodeName;if(!f[n]){var o=document.body.appendChild(document.createElement(n));f[n]=getComputedStyle(o).display,o.remove(),f[n]==="none"&&(f[n]="block")}return f[n]}function z(t){return t.style.removeProperty("display"),getComputedStyle(t).display==="none"&&(t.style.display=q(t)),t}function J(t){return t.style.display="none",t}function p(t,n){return n===void 0&&(n=!W(t)),n?z(t):J(t)}function W(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}Object.assign(c.locales.en,{"tool.copy":"Copy","tool.paste":"Paste","clipboard.paste_row":"Paste row","clipboard.paste_section":"Paste section"});var b="grideditor.clipboard",w="grideditor-clipboard",H=1,h=null,_={column:["row","text","container","element"],row:["column"],section:["row"]};function d(){var t;try{t=window.localStorage.getItem(b)}catch(o){return h}if(t===null)return h;try{var n=JSON.parse(t);return n&&n.version===H&&n.html?n:null}catch(o){return null}}function Y(t){try{window.localStorage.setItem(b,JSON.stringify(t)),h=null}catch(n){h=t}R()}function R(){document.dispatchEvent(new CustomEvent(w))}window.addEventListener("storage",function(t){t.key===b&&R()});var m=[],v=null;function D(t){v=m.filter(function(n){return n.contains(t.target)})[0]||null}document.addEventListener("pointerdown",D,!0);document.addEventListener("focusin",D,!0);function $(t){var n=(t.key||"").toLowerCase();return/^[a-z]$/.test(n)||(n={KeyC:"c",KeyV:"v"}[t.code]||""),n==="c"||n==="v"?n:null}function Q(){var t=document.activeElement;return!!t&&(B(t,"input, textarea, select")||t.isContentEditable)}function Z(){var t=window.getSelection?window.getSelection():null;return!!t&&!t.isCollapsed&&t.toString()!==""}c.features.clipboard=function(t){var n=null,o=!1,P=G();function g(e){return!t.settings.plugins||t.settings.plugins.indexOf(e)!==-1}function E(e){return e.category==="container"?!!c.containers[e.kind]&&g(e.kind):e.category==="section"?!!c.features.sections&&g("sections"):e.category==="element"?!!c.features.elements&&g("elements"):!0}function s(e,r){return!!e&&r.indexOf(e.category)!==-1&&E(e)}function y(e){return c.containers[e]?"container":["row","column","section","text","element"].indexOf(e)!==-1?e:null}function G(){var e=(t.settings.clipboard||{}).shortcuts,r=!!t.settings.active_target;return e!==void 0&&typeof e!="boolean"&&(t.warn("clipboard.shortcuts is true or false: "+(r?"on":"off")+", as with active_target, is used"),e=void 0),e===void 0?r:e&&!r?(t.warn('clipboard.shortcuts needs active_target, or settings_panel "sidebar": without it, Ctrl+C and Ctrl+V do nothing'),!1):e}function C(e,r,i){var u=t.nodeHtml(e);Y({version:H,category:y(r),kind:r,html:u}),t.emit("after-copy",t.payloadFor(r,e,{source:i||"tool"}));var a=L(t.drawerOf(e),".ge-copy");if(a){var l=k(a,"i");N(a,"ge-copied"),l&&l.setAttribute("class","bi bi-check2"),setTimeout(function(){I(a,"ge-copied"),l&&l.setAttribute("class","bi bi-copy")},1200)}}function F(e,r){var i=d();s(i,r)&&t.place(t.freshNode(i.html),i.kind,{appendTo:e,source:"paste"})}function M(){var e=d(),r=t.getActiveTarget(),i=r||t.getSelected();if(!e||!i)return!1;var u={appendTo:i,source:"paste"};if(r&&e.category==="section"){if(!E(e))return!1;for(var a=r;a.parentElement&&a.parentElement!==t.canvas;)a=a.parentElement;u={insertAfter:a,source:"paste"}}else if(!s(e,_[t.kindOf(i)]||[]))return!1;var l=t.place(t.freshNode(e.html),e.kind,u);return l&&l.scrollIntoView&&l.scrollIntoView({behavior:"smooth"}),!0}function U(){if(Z())return!1;var e=t.getSelected()||t.getActiveTarget(),r=e&&t.kindOf(e);return!r||!y(r)?!1:(C(e,r,"shortcut"),!0)}function S(e){if(!(v!==t||e.repeat||e.shiftKey||e.altKey||!(e.ctrlKey||e.metaKey))){var r=$(e);!r||!j(t.canvas,"ge-editing")||Q()||t.panelOpen()||(r==="c"?U():M())&&e.preventDefault()}}function T(e){var r=m.indexOf(t);e&&r===-1?(m.push(t),document.addEventListener("keydown",S)):!e&&r!==-1&&(m.splice(r,1),document.removeEventListener("keydown",S))}function x(){var e=d();O(t.canvas,".ge-paste").forEach(function(r){p(r,s(e,r.getAttribute("data-ge-paste").split(" ")))}),t.toolbarItems("clipboard").forEach(function(r){var i=A[parseInt(r.getAttribute("data-ge-item"),10)];p(r,s(e,[i.kind]))}),n=e}var A=[{kind:"row",labelKey:"clipboard.paste_row"},{kind:"section",labelKey:"clipboard.paste_section"}].map(function(e){return Object.assign(e,{iconClass:"bi bi-clipboard-plus",align:"end",source:"paste",hidden:function(){return!s(d(),[e.kind])},create:function(){return t.freshNode(n.html)}})});return{drawerTools:function(e,r,i){y(i)&&t.createTool(e,t.t("tool.copy"),"ge-copy","bi bi-copy",function(){C(r,i)});var u=_[i];if(u){var a=t.createTool(e,t.t("tool.paste"),"ge-paste","bi bi-clipboard-plus",function(){F(r,u)});a.setAttribute("data-ge-paste",u.join(" ")),p(a,s(d(),u))}},toolbar:A,onInit:function(){x(),o||(document.addEventListener(w,x),o=!0),T(P)},onDeinit:function(){document.removeEventListener(w,x),o=!1,T(!1)},onDestroy:function(){v===t&&(v=null)},cleanMarkup:function(){}}};})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/js/register.js", "../../src/js/dom.js", "../../src/js/plugins/grideditor.clipboard.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * What a plugin's or a locale's classic script imports in place of the\n * editor: the GridEditor the page already loaded.\n *\n * The build points `import { GridEditor } from '../grideditor.js'` here for\n * the classic scripts, so each registers on the editor the page has rather\n * than bringing its own. Loaded before the editor there is nothing to\n * register on, and saying so is better than a TypeError three calls later.\n */\nvar GridEditor = window.GridEditor;\n\nif (!GridEditor || typeof GridEditor.get !== 'function') {\n throw new Error('grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, ' +\n 'its locales and grideditor.jquery.js');\n}\n\nexport { GridEditor };\nexport default GridEditor;\n", "/**\n * The DOM, the way grid-editor uses it.\n *\n * Small helpers over the browser's own API, for the core and the shipped\n * plugins. Not a public API: a plugin of a host's own is free to write its\n * DOM code however it likes, and this file makes no promise to it.\n *\n * Stateless on purpose. The build puts a copy of this file in each plugin's\n * classic script, so anything kept here would be kept once per copy; a\n * module that needs to remember something about a node keeps its own\n * WeakMap.\n */\n\n/** Every element under `root` matching `selector`, as an array. */\nexport function all(root, selector) {\n return root ? Array.prototype.slice.call(root.querySelectorAll(selector)) : [];\n}\n\n/** The first element under `root` matching `selector`, or null. */\nexport function one(root, selector) {\n return root ? root.querySelector(selector) : null;\n}\n\n/** `root` itself when it matches, then every element under it that does. */\nexport function selfAndAll(root, selector) {\n if (!root) { return []; }\n\n var found = all(root, selector);\n if (root.nodeType === 1 && root.matches(selector)) { found.unshift(root); }\n return found;\n}\n\n/** The element children of `node`, those matching `selector` if one is given. */\nexport function children(node, selector) {\n if (!node) { return []; }\n\n return Array.prototype.filter.call(node.children, function(each) {\n return !selector || each.matches(selector);\n });\n}\n\n/** The first element child matching `selector`, or null. */\nexport function child(node, selector) {\n return children(node, selector)[0] || null;\n}\n\n/** The nearest ancestor-or-self matching `selector` below `stopAt`, or null. */\nexport function closest(node, selector, stopAt) {\n var found = node && node.nodeType === 1 ? node.closest(selector) : null;\n\n if (found && stopAt && found !== stopAt && !stopAt.contains(found)) { return null; }\n return found;\n}\n\n/** Whether `node` is an element matching `selector`. */\nexport function is(node, selector) {\n return !!node && node.nodeType === 1 && node.matches(selector);\n}\n\n/** Whether `node` is in the document. */\nexport function attached(node) {\n return !!node && document.documentElement.contains(node);\n}\n\n/** The element siblings after `node` that match `selector`. */\nexport function nextAll(node, selector) {\n var found = [];\n\n for (var next = node.nextElementSibling; next; next = next.nextElementSibling) {\n if (!selector || next.matches(selector)) { found.push(next); }\n }\n return found;\n}\n\n/** The ancestors of `node` up to, and not including, `stop`, nearest first. */\nexport function parentsUntil(node, stop) {\n var found = [];\n\n for (var parent = node.parentElement; parent && parent !== stop; parent = parent.parentElement) {\n found.push(parent);\n }\n return found;\n}\n\n/**\n * The nodes the html parses into, detached. Parsed the way innerHTML parses,\n * so a <script> in it never runs, here or once it is in the page.\n */\nexport function parse(html) {\n var template = document.createElement('template');\n template.innerHTML = html;\n return Array.prototype.slice.call(template.content.childNodes);\n}\n\n/** The one element the html is, detached. */\nexport function create(html) {\n var nodes = parse(html.trim());\n\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].nodeType === 1) { return nodes[i]; }\n }\n return null;\n}\n\n/** An element of `tag`, with `attributes` and `text`. */\nexport function element(tag, attributes, text) {\n var node = document.createElement(tag);\n\n Object.keys(attributes || {}).forEach(function(name) {\n var value = attributes[name];\n if (value !== null && value !== undefined && value !== false) { node.setAttribute(name, value); }\n });\n if (text !== undefined && text !== null) { node.textContent = text; }\n\n return node;\n}\n\n/** Replace what `node` holds with `html`. A <script> in it does not run. */\nexport function setHtml(node, html) {\n node.innerHTML = html === undefined || html === null ? '' : String(html);\n return node;\n}\n\n/** Add one or several space separated classes. */\nexport function addClass(node, names) {\n split(names).forEach(function(name) { node.classList.add(name); });\n return node;\n}\n\nexport function removeClass(node, names) {\n split(names).forEach(function(name) { node.classList.remove(name); });\n return node;\n}\n\n/** Each class on, or off, or flipped when `on` is not given. */\nexport function toggleClass(node, names, state) {\n split(names).forEach(function(name) {\n if (state === undefined) {\n node.classList.toggle(name);\n } else {\n node.classList.toggle(name, !!state);\n }\n });\n return node;\n}\n\nexport function hasClass(node, name) {\n return !!node && node.nodeType === 1 && node.classList.contains(name);\n}\n\nfunction split(names) {\n return String(names || '').split(/\\s+/).filter(Boolean);\n}\n\n/** Take the class attribute off when there is nothing left in it. */\nexport function dropEmptyClass(node) {\n if (!node.getAttribute('class')) { node.removeAttribute('class'); }\n return node;\n}\n\n/** Take the style attribute off when there is nothing left in it. */\nexport function dropEmptyStyle(node) {\n if (!node.getAttribute('style')) { node.removeAttribute('style'); }\n return node;\n}\n\n/** Set attributes; a null or undefined value takes the attribute off. */\nexport function attr(node, attributes) {\n Object.keys(attributes).forEach(function(name) {\n var value = attributes[name];\n\n if (value === null || value === undefined) {\n node.removeAttribute(name);\n } else {\n node.setAttribute(name, value);\n }\n });\n return node;\n}\n\n/**\n * Inline styles, by css property name. A number is pixels for the properties\n * that take a length, as jQuery had it; an empty string takes one off.\n */\nexport function css(node, styles) {\n Object.keys(styles).forEach(function(property) {\n var value = styles[property];\n\n if (typeof value === 'number' && !UNITLESS[property]) { value = value + 'px'; }\n node.style.setProperty(property, value === null || value === undefined ? '' : String(value));\n });\n return node;\n}\n\nvar UNITLESS = { opacity: true, 'z-index': true, 'flex-grow': true, 'flex-shrink': true, order: true };\n\n/** What an element of this tag shows as when nothing hides it. */\nvar defaultDisplays = {};\n\nfunction defaultDisplay(node) {\n var tag = node.nodeName;\n\n if (!defaultDisplays[tag]) {\n var probe = document.body.appendChild(document.createElement(tag));\n defaultDisplays[tag] = getComputedStyle(probe).display;\n probe.remove();\n if (defaultDisplays[tag] === 'none') { defaultDisplays[tag] = 'block'; }\n }\n return defaultDisplays[tag];\n}\n\n/** Show a node: its inline display:none off, and a display of its own if a stylesheet hides it. */\nexport function show(node) {\n node.style.removeProperty('display');\n if (getComputedStyle(node).display === 'none') { node.style.display = defaultDisplay(node); }\n return node;\n}\n\nexport function hide(node) {\n node.style.display = 'none';\n return node;\n}\n\n/** Show or hide; flip when `on` is not given. */\nexport function toggle(node, state) {\n if (state === undefined) { state = !visible(node); }\n return state ? show(node) : hide(node);\n}\n\n/** Whether a node takes up room: what jQuery's :visible meant. */\nexport function visible(node) {\n return !!(node.offsetWidth || node.offsetHeight || node.getClientRects().length);\n}\n\n/** The border box's width, fractions included. */\nexport function outerWidth(node) {\n return node.getBoundingClientRect().width;\n}\n\nexport function outerHeight(node) {\n return node.getBoundingClientRect().height;\n}\n\n/** The content box's height, which is what jQuery's height() gave. */\nexport function contentHeight(node) {\n var style = getComputedStyle(node);\n return node.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);\n}\n\n/** Where a node is on the page, not in the window. */\nexport function offset(node) {\n var box = node.getBoundingClientRect();\n return { top: box.top + window.pageYOffset, left: box.left + window.pageXOffset };\n}\n\n/** Its index among its parent's element children, those matching `selector` if given. */\nexport function indexIn(node, selector) {\n return children(node.parentElement, selector).indexOf(node);\n}\n\n/** Put `node` right after `reference`. */\nexport function insertAfter(node, reference) {\n reference.parentNode.insertBefore(node, reference.nextSibling);\n return node;\n}\n\n/** Replace `node` with what it holds. */\nexport function unwrap(node) {\n var parent = node.parentNode;\n\n while (node.firstChild) { parent.insertBefore(node.firstChild, node); }\n node.remove();\n}\n\n/** Put `node` inside `wrapper`, where `node` was. */\nexport function wrap(node, wrapper) {\n node.parentNode.insertBefore(wrapper, node);\n wrapper.appendChild(node);\n return wrapper;\n}\n\n/**\n * Listen on `root` for `types` (space separated) on descendants that match\n * `selector`, with `this` the matched element, as jQuery's delegated\n * handlers had it. Hands back the listener, for removeEventListener; a\n * signal in `options` removes it too.\n */\nexport function delegate(root, types, selector, handler, options) {\n var listener = function(event) {\n var match = closest(event.target, selector, root);\n if (match && root.contains(match)) { return handler.call(match, event); }\n return undefined;\n };\n\n split(types).forEach(function(type) { root.addEventListener(type, listener, options); });\n return listener;\n}\n\n/** Listen for several space separated event types with one handler. */\nexport function on(target, types, handler, options) {\n split(types).forEach(function(type) { target.addEventListener(type, handler, options); });\n return handler;\n}\n\nexport function off(target, types, handler, options) {\n split(types).forEach(function(type) { target.removeEventListener(type, handler, options); });\n}\n\n/** A mouse event of `type` sent to `node`, the way a pointer would. */\nexport function fire(node, type) {\n node.dispatchEvent(new MouseEvent(type, { bubbles: type !== 'mouseenter' && type !== 'mouseleave' }));\n}\n\n/* ------------------------------------------------------------------\n * Taking a node away, visibly.\n *\n * The durations are jQuery's: 400 ms unless the caller says otherwise. `done` runs once the node is out of sight, which is\n * when the caller removes it; a node that is already out of sight is done at\n * once, on the next task, as jQuery had it.\n * ------------------------------------------------------------------ */\n\nvar DEFAULT_DURATION = 400;\n\nfunction animateAway(node, frames, duration, done) {\n duration = duration === undefined ? DEFAULT_DURATION : duration;\n\n var finish = function() {\n hide(node);\n if (done) { done(); }\n };\n\n if (!visible(node) || !node.animate || duration <= 0) {\n window.setTimeout(finish, 0);\n return;\n }\n\n var animation = node.animate(frames(getComputedStyle(node)), {\n duration: duration,\n easing: 'ease-in-out',\n });\n var called = false;\n var once = function() {\n if (called) { return; }\n called = true;\n finish();\n };\n\n animation.onfinish = once;\n animation.oncancel = once;\n}\n\n/** Fold a node up to nothing, then hide it. */\nexport function slideUp(node, duration, done) {\n if (typeof duration === 'function') {\n done = duration;\n duration = undefined;\n }\n\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n return [\n {\n height: style.height,\n paddingTop: style.paddingTop,\n paddingBottom: style.paddingBottom,\n marginTop: style.marginTop,\n marginBottom: style.marginBottom,\n },\n { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px' },\n ];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n\n/** Fade a node out, then hide it. */\nexport function fadeOut(node, duration, done) {\n animateAway(node, function(style) {\n return [{ opacity: style.opacity }, { opacity: 0 }];\n }, duration, done);\n}\n\n/** Fade and fold a node, in height and, with `width`, sideways too, then hide it. */\nexport function shrinkAway(node, duration, done, width) {\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n var from = { opacity: style.opacity, height: style.height };\n var to = { opacity: 0, height: '0px' };\n\n if (width) {\n from.width = style.width;\n to.width = '0px';\n }\n return [from, to];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n", "/**\n * Copy and paste for grid-editor.\n *\n * A feature plugin: load this file after the editor and every row, column,\n * section, text block, container and element gets a copy tool, and every\n * place one of them can go gets a paste tool while there is something copied\n * that fits.\n * What it can ask the editor for is the handle its factory is called with,\n * described in docs/plugins.md.\n *\n * <script src=\"dist/grideditor.min.js\"></script>\n * <script src=\"dist/plugins/grideditor.clipboard.min.js\"></script>\n *\n * What is copied is kept in localStorage, so it survives a reload and can be\n * pasted in another tab or another editor of the same site. A browser that\n * will not give the page its storage keeps it in memory, for this page only.\n *\n * A paste is an add like any other - before-add-* and after-add-*, with\n * `source: 'paste'` - so a host can turn one away.\n */\nimport { GridEditor } from '../grideditor.js';\nimport * as dom from '../dom.js';\n\nObject.assign(GridEditor.locales.en, {\n 'tool.copy': 'Copy',\n 'tool.paste': 'Paste',\n 'clipboard.paste_row': 'Paste row',\n 'clipboard.paste_section': 'Paste section',\n});\n\nvar STORAGE_ITEM = 'grideditor.clipboard';\nvar CHANGE = 'grideditor-clipboard';\nvar VERSION = 1;\n\n/** Where nothing can be stored, what was copied lives here, for this page. */\nvar memory = null;\n\n/**\n * What each drawer takes when something is pasted into it, by the kind of the\n * node the drawer belongs to. A category is what was copied: a row, a column,\n * a section, a text block, a container of any type, an element.\n */\nvar TARGETS = {\n column: ['row', 'text', 'container', 'element'],\n row: ['column'],\n section: ['row'],\n};\n\nfunction read() {\n var raw;\n\n try {\n raw = window.localStorage.getItem(STORAGE_ITEM);\n } catch (error) {\n return memory;\n }\n if (raw === null) { return memory; }\n\n try {\n var clip = JSON.parse(raw);\n return clip && clip.version === VERSION && clip.html ? clip : null;\n } catch (error) {\n return null;\n }\n}\n\nfunction write(clip) {\n try {\n window.localStorage.setItem(STORAGE_ITEM, JSON.stringify(clip));\n memory = null;\n } catch (error) {\n memory = clip;\n }\n\n announce();\n}\n\n/** Every editor on the page looks at the clipboard again. */\nfunction announce() {\n document.dispatchEvent(new CustomEvent(CHANGE));\n}\n\n// Another tab copied something: every editor here looks again\nwindow.addEventListener('storage', function(e) {\n if (e.key === STORAGE_ITEM) { announce(); }\n});\n\nGridEditor.features.clipboard = function(ge) {\n\n var shown = null; // What the toolbar's paste buttons stand for\n var listening = false; // Whether refresh is on the document's change event\n\n function used(name) {\n return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;\n }\n\n /** A category this editor can take: the plugin that makes it is here. */\n function available(clip) {\n if (clip.category === 'container') {\n return !!GridEditor.containers[clip.kind] && used(clip.kind);\n }\n if (clip.category === 'section') { return !!GridEditor.features.sections && used('sections'); }\n if (clip.category === 'element') { return !!GridEditor.features.elements && used('elements'); }\n\n return true;\n }\n\n function fits(clip, categories) {\n return !!clip && categories.indexOf(clip.category) !== -1 && available(clip);\n }\n\n function categoryOf(kind) {\n if (GridEditor.containers[kind]) { return 'container'; }\n\n return ['row', 'column', 'section', 'text', 'element'].indexOf(kind) !== -1 ? kind : null;\n }\n\n function copy(node, kind) {\n var html = ge.nodeHtml(node);\n\n write({ version: VERSION, category: categoryOf(kind), kind: kind, html: html });\n ge.emit('after-copy', ge.payloadFor(kind, node, { source: 'tool' }));\n\n // The canvas came back with new drawers: the one to flash is the\n // node's new copy tool\n var tool = dom.child(ge.drawerOf(node), '.ge-copy');\n if (!tool) { return; }\n\n var icon = dom.one(tool, 'i');\n dom.addClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-check2'); }\n setTimeout(function() {\n dom.removeClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-copy'); }\n }, 1200);\n }\n\n function paste(target, categories) {\n var clip = read();\n if (!fits(clip, categories)) { return; }\n\n ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: 'paste' });\n }\n\n /** Show the paste tools, and the toolbar's paste buttons, that fit what is copied. */\n function refresh() {\n var clip = read();\n\n dom.all(ge.canvas, '.ge-paste').forEach(function(tool) {\n dom.toggle(tool, fits(clip, tool.getAttribute('data-ge-paste').split(' ')));\n });\n\n ge.toolbarItems('clipboard').forEach(function(button) {\n var item = TOOLBAR[parseInt(button.getAttribute('data-ge-item'), 10)];\n dom.toggle(button, fits(clip, [item.kind]));\n });\n\n shown = clip;\n }\n\n var TOOLBAR = [\n { kind: 'row', labelKey: 'clipboard.paste_row' },\n { kind: 'section', labelKey: 'clipboard.paste_section' },\n ].map(function(item) {\n return Object.assign(item, {\n iconClass: 'bi bi-clipboard-plus',\n // On the right, as an icon: pasting is not one of the things\n // the add buttons make\n align: 'end',\n source: 'paste',\n // A toolbar built again - another plugin's buttons changed -\n // shows only what fits, as refresh would\n hidden: function() { return !fits(read(), [item.kind]); },\n // What the button showed, even if another tab has copied\n // something else since\n create: function() { return ge.freshNode(shown.html); },\n });\n });\n\n return {\n drawerTools: function(drawer, node, kind) {\n if (categoryOf(kind)) {\n ge.createTool(drawer, ge.t('tool.copy'), 'ge-copy', 'bi bi-copy', function() {\n copy(node, kind);\n });\n }\n\n var categories = TARGETS[kind];\n if (!categories) { return; }\n\n var tool = ge.createTool(drawer, ge.t('tool.paste'), 'ge-paste', 'bi bi-clipboard-plus', function() {\n paste(node, categories);\n });\n tool.setAttribute('data-ge-paste', categories.join(' '));\n dom.toggle(tool, fits(read(), categories));\n },\n\n toolbar: TOOLBAR,\n\n onInit: function() {\n refresh();\n if (!listening) {\n document.addEventListener(CHANGE, refresh);\n listening = true;\n }\n },\n\n onDeinit: function() {\n document.removeEventListener(CHANGE, refresh);\n listening = false;\n },\n\n // Nothing of its own on the canvas: the tools are in the drawers\n cleanMarkup: function() {},\n };\n};\n"],
|
|
5
|
-
"mappings": "MASA,IAAIA,EAAa,OAAO,WAExB,GAAI,CAACA,GAAc,OAAOA,EAAW,KAAQ,WACzC,MAAM,IAAI,MAAM,wHAC0B,ECCvC,SAASC,EAAIC,EAAMC,EAAU,CAChC,OAAOD,EAAO,MAAM,UAAU,MAAM,KAAKA,EAAK,iBAAiBC,CAAQ,CAAC,EAAI,CAAC,CACjF,CAGO,SAASC,EAAIF,EAAMC,EAAU,CAChC,OAAOD,EAAOA,EAAK,cAAcC,CAAQ,EAAI,IACjD,CAYO,SAASE,EAASC,EAAMC,EAAU,CACrC,OAAKD,EAEE,MAAM,UAAU,OAAO,KAAKA,EAAK,SAAU,SAASE,EAAM,CAC7D,MAAO,CAACD,GAAYC,EAAK,QAAQD,CAAQ,CAC7C,CAAC,EAJmB,CAAC,CAKzB,CAGO,SAASE,EAAMH,EAAMC,EAAU,CAClC,OAAOF,EAASC,EAAMC,CAAQ,EAAE,CAAC,GAAK,IAC1C,
|
|
6
|
-
"names": ["GridEditor", "all", "root", "selector", "one", "children", "node", "selector", "each", "child", "addClass", "node", "names", "split", "name", "removeClass", "split", "names", "defaultDisplays", "defaultDisplay", "node", "tag", "probe", "show", "hide", "toggle", "state", "visible", "GridEditor", "STORAGE_ITEM", "CHANGE", "VERSION", "memory", "TARGETS", "read", "raw", "error", "clip", "write", "announce", "e", "ge", "shown", "listening", "used", "name", "available", "fits", "categories", "categoryOf", "kind", "copy", "node", "html", "tool", "child", "icon", "one", "addClass", "removeClass", "paste", "
|
|
4
|
+
"sourcesContent": ["/**\n * What a plugin's or a locale's classic script imports in place of the\n * editor: the GridEditor the page already loaded.\n *\n * The build points `import { GridEditor } from '../grideditor.js'` here for\n * the classic scripts, so each registers on the editor the page has rather\n * than bringing its own. Loaded before the editor there is nothing to\n * register on, and saying so is better than a TypeError three calls later.\n */\nvar GridEditor = window.GridEditor;\n\nif (!GridEditor || typeof GridEditor.get !== 'function') {\n throw new Error('grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, ' +\n 'its locales and grideditor.jquery.js');\n}\n\nexport { GridEditor };\nexport default GridEditor;\n", "/**\n * The DOM, the way grid-editor uses it.\n *\n * Small helpers over the browser's own API, for the core and the shipped\n * plugins. Not a public API: a plugin of a host's own is free to write its\n * DOM code however it likes, and this file makes no promise to it.\n *\n * Stateless on purpose. The build puts a copy of this file in each plugin's\n * classic script, so anything kept here would be kept once per copy; a\n * module that needs to remember something about a node keeps its own\n * WeakMap.\n */\n\n/** Every element under `root` matching `selector`, as an array. */\nexport function all(root, selector) {\n return root ? Array.prototype.slice.call(root.querySelectorAll(selector)) : [];\n}\n\n/** The first element under `root` matching `selector`, or null. */\nexport function one(root, selector) {\n return root ? root.querySelector(selector) : null;\n}\n\n/** `root` itself when it matches, then every element under it that does. */\nexport function selfAndAll(root, selector) {\n if (!root) { return []; }\n\n var found = all(root, selector);\n if (root.nodeType === 1 && root.matches(selector)) { found.unshift(root); }\n return found;\n}\n\n/** The element children of `node`, those matching `selector` if one is given. */\nexport function children(node, selector) {\n if (!node) { return []; }\n\n return Array.prototype.filter.call(node.children, function(each) {\n return !selector || each.matches(selector);\n });\n}\n\n/** The first element child matching `selector`, or null. */\nexport function child(node, selector) {\n return children(node, selector)[0] || null;\n}\n\n/** The nearest ancestor-or-self matching `selector` below `stopAt`, or null. */\nexport function closest(node, selector, stopAt) {\n var found = node && node.nodeType === 1 ? node.closest(selector) : null;\n\n if (found && stopAt && found !== stopAt && !stopAt.contains(found)) { return null; }\n return found;\n}\n\n/** Whether `node` is an element matching `selector`. */\nexport function is(node, selector) {\n return !!node && node.nodeType === 1 && node.matches(selector);\n}\n\n/** Whether `node` is in the document. */\nexport function attached(node) {\n return !!node && document.documentElement.contains(node);\n}\n\n/** The element siblings after `node` that match `selector`. */\nexport function nextAll(node, selector) {\n var found = [];\n\n for (var next = node.nextElementSibling; next; next = next.nextElementSibling) {\n if (!selector || next.matches(selector)) { found.push(next); }\n }\n return found;\n}\n\n/** The ancestors of `node` up to, and not including, `stop`, nearest first. */\nexport function parentsUntil(node, stop) {\n var found = [];\n\n for (var parent = node.parentElement; parent && parent !== stop; parent = parent.parentElement) {\n found.push(parent);\n }\n return found;\n}\n\n/**\n * The nodes the html parses into, detached. Parsed the way innerHTML parses,\n * so a <script> in it never runs, here or once it is in the page.\n */\nexport function parse(html) {\n var template = document.createElement('template');\n template.innerHTML = html;\n return Array.prototype.slice.call(template.content.childNodes);\n}\n\n/** The one element the html is, detached. */\nexport function create(html) {\n var nodes = parse(html.trim());\n\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].nodeType === 1) { return nodes[i]; }\n }\n return null;\n}\n\n/** An element of `tag`, with `attributes` and `text`. */\nexport function element(tag, attributes, text) {\n var node = document.createElement(tag);\n\n Object.keys(attributes || {}).forEach(function(name) {\n var value = attributes[name];\n if (value !== null && value !== undefined && value !== false) { node.setAttribute(name, value); }\n });\n if (text !== undefined && text !== null) { node.textContent = text; }\n\n return node;\n}\n\n/** Replace what `node` holds with `html`. A <script> in it does not run. */\nexport function setHtml(node, html) {\n node.innerHTML = html === undefined || html === null ? '' : String(html);\n return node;\n}\n\n/** Add one or several space separated classes. */\nexport function addClass(node, names) {\n split(names).forEach(function(name) { node.classList.add(name); });\n return node;\n}\n\nexport function removeClass(node, names) {\n split(names).forEach(function(name) { node.classList.remove(name); });\n return node;\n}\n\n/** Each class on, or off, or flipped when `on` is not given. */\nexport function toggleClass(node, names, state) {\n split(names).forEach(function(name) {\n if (state === undefined) {\n node.classList.toggle(name);\n } else {\n node.classList.toggle(name, !!state);\n }\n });\n return node;\n}\n\nexport function hasClass(node, name) {\n return !!node && node.nodeType === 1 && node.classList.contains(name);\n}\n\nfunction split(names) {\n return String(names || '').split(/\\s+/).filter(Boolean);\n}\n\n/** Take the class attribute off when there is nothing left in it. */\nexport function dropEmptyClass(node) {\n if (!node.getAttribute('class')) { node.removeAttribute('class'); }\n return node;\n}\n\n/** Take the style attribute off when there is nothing left in it. */\nexport function dropEmptyStyle(node) {\n if (!node.getAttribute('style')) { node.removeAttribute('style'); }\n return node;\n}\n\n/** Set attributes; a null or undefined value takes the attribute off. */\nexport function attr(node, attributes) {\n Object.keys(attributes).forEach(function(name) {\n var value = attributes[name];\n\n if (value === null || value === undefined) {\n node.removeAttribute(name);\n } else {\n node.setAttribute(name, value);\n }\n });\n return node;\n}\n\n/**\n * Inline styles, by css property name. A number is pixels for the properties\n * that take a length, as jQuery had it; an empty string takes one off.\n */\nexport function css(node, styles) {\n Object.keys(styles).forEach(function(property) {\n var value = styles[property];\n\n if (typeof value === 'number' && !UNITLESS[property]) { value = value + 'px'; }\n node.style.setProperty(property, value === null || value === undefined ? '' : String(value));\n });\n return node;\n}\n\nvar UNITLESS = { opacity: true, 'z-index': true, 'flex-grow': true, 'flex-shrink': true, order: true };\n\n/** What an element of this tag shows as when nothing hides it. */\nvar defaultDisplays = {};\n\nfunction defaultDisplay(node) {\n var tag = node.nodeName;\n\n if (!defaultDisplays[tag]) {\n var probe = document.body.appendChild(document.createElement(tag));\n defaultDisplays[tag] = getComputedStyle(probe).display;\n probe.remove();\n if (defaultDisplays[tag] === 'none') { defaultDisplays[tag] = 'block'; }\n }\n return defaultDisplays[tag];\n}\n\n/** Show a node: its inline display:none off, and a display of its own if a stylesheet hides it. */\nexport function show(node) {\n node.style.removeProperty('display');\n if (getComputedStyle(node).display === 'none') { node.style.display = defaultDisplay(node); }\n return node;\n}\n\nexport function hide(node) {\n node.style.display = 'none';\n return node;\n}\n\n/** Show or hide; flip when `on` is not given. */\nexport function toggle(node, state) {\n if (state === undefined) { state = !visible(node); }\n return state ? show(node) : hide(node);\n}\n\n/** Whether a node takes up room: what jQuery's :visible meant. */\nexport function visible(node) {\n return !!(node.offsetWidth || node.offsetHeight || node.getClientRects().length);\n}\n\n/** The border box's width, fractions included. */\nexport function outerWidth(node) {\n return node.getBoundingClientRect().width;\n}\n\nexport function outerHeight(node) {\n return node.getBoundingClientRect().height;\n}\n\n/** The content box's height, which is what jQuery's height() gave. */\nexport function contentHeight(node) {\n var style = getComputedStyle(node);\n return node.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);\n}\n\n/** Where a node is on the page, not in the window. */\nexport function offset(node) {\n var box = node.getBoundingClientRect();\n return { top: box.top + window.pageYOffset, left: box.left + window.pageXOffset };\n}\n\n/** Its index among its parent's element children, those matching `selector` if given. */\nexport function indexIn(node, selector) {\n return children(node.parentElement, selector).indexOf(node);\n}\n\n/** Put `node` right after `reference`. */\nexport function insertAfter(node, reference) {\n reference.parentNode.insertBefore(node, reference.nextSibling);\n return node;\n}\n\n/** Replace `node` with what it holds. */\nexport function unwrap(node) {\n var parent = node.parentNode;\n\n while (node.firstChild) { parent.insertBefore(node.firstChild, node); }\n node.remove();\n}\n\n/** Put `node` inside `wrapper`, where `node` was. */\nexport function wrap(node, wrapper) {\n node.parentNode.insertBefore(wrapper, node);\n wrapper.appendChild(node);\n return wrapper;\n}\n\n/**\n * Listen on `root` for `types` (space separated) on descendants that match\n * `selector`, with `this` the matched element, as jQuery's delegated\n * handlers had it. Hands back the listener, for removeEventListener; a\n * signal in `options` removes it too.\n */\nexport function delegate(root, types, selector, handler, options) {\n var listener = function(event) {\n var match = closest(event.target, selector, root);\n if (match && root.contains(match)) { return handler.call(match, event); }\n return undefined;\n };\n\n split(types).forEach(function(type) { root.addEventListener(type, listener, options); });\n return listener;\n}\n\n/** Listen for several space separated event types with one handler. */\nexport function on(target, types, handler, options) {\n split(types).forEach(function(type) { target.addEventListener(type, handler, options); });\n return handler;\n}\n\nexport function off(target, types, handler, options) {\n split(types).forEach(function(type) { target.removeEventListener(type, handler, options); });\n}\n\n/** A mouse event of `type` sent to `node`, the way a pointer would. */\nexport function fire(node, type) {\n node.dispatchEvent(new MouseEvent(type, { bubbles: type !== 'mouseenter' && type !== 'mouseleave' }));\n}\n\n/* ------------------------------------------------------------------\n * Taking a node away, visibly.\n *\n * The durations are jQuery's: 400 ms unless the caller says otherwise. `done` runs once the node is out of sight, which is\n * when the caller removes it; a node that is already out of sight is done at\n * once, on the next task, as jQuery had it.\n * ------------------------------------------------------------------ */\n\nvar DEFAULT_DURATION = 400;\n\nfunction animateAway(node, frames, duration, done) {\n duration = duration === undefined ? DEFAULT_DURATION : duration;\n\n var finish = function() {\n hide(node);\n if (done) { done(); }\n };\n\n if (!visible(node) || !node.animate || duration <= 0) {\n window.setTimeout(finish, 0);\n return;\n }\n\n var animation = node.animate(frames(getComputedStyle(node)), {\n duration: duration,\n easing: 'ease-in-out',\n });\n var called = false;\n var once = function() {\n if (called) { return; }\n called = true;\n finish();\n };\n\n animation.onfinish = once;\n animation.oncancel = once;\n}\n\n/** Fold a node up to nothing, then hide it. */\nexport function slideUp(node, duration, done) {\n if (typeof duration === 'function') {\n done = duration;\n duration = undefined;\n }\n\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n return [\n {\n height: style.height,\n paddingTop: style.paddingTop,\n paddingBottom: style.paddingBottom,\n marginTop: style.marginTop,\n marginBottom: style.marginBottom,\n },\n { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px' },\n ];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n\n/** Fade a node out, then hide it. */\nexport function fadeOut(node, duration, done) {\n animateAway(node, function(style) {\n return [{ opacity: style.opacity }, { opacity: 0 }];\n }, duration, done);\n}\n\n/** Fade and fold a node, in height and, with `width`, sideways too, then hide it. */\nexport function shrinkAway(node, duration, done, width) {\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n var from = { opacity: style.opacity, height: style.height };\n var to = { opacity: 0, height: '0px' };\n\n if (width) {\n from.width = style.width;\n to.width = '0px';\n }\n return [from, to];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n", "/**\n * Copy and paste for grid-editor.\n *\n * A feature plugin: load this file after the editor and every row, column,\n * section, text block, container and element gets a copy tool, and every\n * place one of them can go gets a paste tool while there is something copied\n * that fits.\n * What it can ask the editor for is the handle its factory is called with,\n * described in docs/plugins.md.\n *\n * <script src=\"dist/grideditor.min.js\"></script>\n * <script src=\"dist/plugins/grideditor.clipboard.min.js\"></script>\n *\n * What is copied is kept in localStorage, so it survives a reload and can be\n * pasted in another tab or another editor of the same site. A browser that\n * will not give the page its storage keeps it in memory, for this page only.\n *\n * A paste is an add like any other - before-add-* and after-add-*, with\n * `source: 'paste'` - so a host can turn one away.\n *\n * With active_target (which the sidebar turns on), Ctrl+C copies the node\n * selected, or the target, and Ctrl+V pastes into the target, or the node\n * selected. `clipboard: { shortcuts: false }` turns them off. They go through\n * the same store as the tools, never the system clipboard.\n */\nimport { GridEditor } from '../grideditor.js';\nimport * as dom from '../dom.js';\n\nObject.assign(GridEditor.locales.en, {\n 'tool.copy': 'Copy',\n 'tool.paste': 'Paste',\n 'clipboard.paste_row': 'Paste row',\n 'clipboard.paste_section': 'Paste section',\n});\n\nvar STORAGE_ITEM = 'grideditor.clipboard';\nvar CHANGE = 'grideditor-clipboard';\nvar VERSION = 1;\n\n/** Where nothing can be stored, what was copied lives here, for this page. */\nvar memory = null;\n\n/**\n * What each drawer takes when something is pasted into it, by the kind of the\n * node the drawer belongs to. A category is what was copied: a row, a column,\n * a section, a text block, a container of any type, an element.\n */\nvar TARGETS = {\n column: ['row', 'text', 'container', 'element'],\n row: ['column'],\n section: ['row'],\n};\n\nfunction read() {\n var raw;\n\n try {\n raw = window.localStorage.getItem(STORAGE_ITEM);\n } catch (error) {\n return memory;\n }\n if (raw === null) { return memory; }\n\n try {\n var clip = JSON.parse(raw);\n return clip && clip.version === VERSION && clip.html ? clip : null;\n } catch (error) {\n return null;\n }\n}\n\nfunction write(clip) {\n try {\n window.localStorage.setItem(STORAGE_ITEM, JSON.stringify(clip));\n memory = null;\n } catch (error) {\n memory = clip;\n }\n\n announce();\n}\n\n/** Every editor on the page looks at the clipboard again. */\nfunction announce() {\n document.dispatchEvent(new CustomEvent(CHANGE));\n}\n\n// Another tab copied something: every editor here looks again\nwindow.addEventListener('storage', function(e) {\n if (e.key === STORAGE_ITEM) { announce(); }\n});\n\n/** The editors listening for the shortcuts, and the one used last, which a key press goes to. */\nvar listeningEditors = [];\nvar lastEditor = null;\n\nfunction track(e) {\n lastEditor = listeningEditors.filter(function(ge) { return ge.contains(e.target); })[0] || null;\n}\n\n// Captured: a control that stops the event still counts as using its editor\ndocument.addEventListener('pointerdown', track, true);\ndocument.addEventListener('focusin', track, true);\n\n/** 'c', 'v' or null, from the letter typed or, on a layout without Latin letters, the key's place. */\nfunction shortcutKey(e) {\n var key = (e.key || '').toLowerCase();\n if (!/^[a-z]$/.test(key)) { key = { KeyC: 'c', KeyV: 'v' }[e.code] || ''; }\n\n return key === 'c' || key === 'v' ? key : null;\n}\n\nfunction typing() {\n var focus = document.activeElement;\n return !!focus && (dom.is(focus, 'input, textarea, select') || focus.isContentEditable);\n}\n\nfunction textSelected() {\n var selection = window.getSelection ? window.getSelection() : null;\n return !!selection && !selection.isCollapsed && selection.toString() !== '';\n}\n\nGridEditor.features.clipboard = function(ge) {\n\n var shown = null; // What the toolbar's paste buttons stand for\n var listening = false; // Whether refresh is on the document's change event\n var shortcuts = shortcutsSetting();\n\n function used(name) {\n return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;\n }\n\n /** A category this editor can take: the plugin that makes it is here. */\n function available(clip) {\n if (clip.category === 'container') {\n return !!GridEditor.containers[clip.kind] && used(clip.kind);\n }\n if (clip.category === 'section') { return !!GridEditor.features.sections && used('sections'); }\n if (clip.category === 'element') { return !!GridEditor.features.elements && used('elements'); }\n\n return true;\n }\n\n function fits(clip, categories) {\n return !!clip && categories.indexOf(clip.category) !== -1 && available(clip);\n }\n\n function categoryOf(kind) {\n if (GridEditor.containers[kind]) { return 'container'; }\n\n return ['row', 'column', 'section', 'text', 'element'].indexOf(kind) !== -1 ? kind : null;\n }\n\n /** On, unless the host said otherwise: with no target there is nothing for a key to act on. */\n function shortcutsSetting() {\n var wanted = (ge.settings.clipboard || {}).shortcuts;\n var target = !!ge.settings.active_target;\n\n if (wanted !== undefined && typeof wanted !== 'boolean') {\n ge.warn('clipboard.shortcuts is true or false: ' + (target ? 'on' : 'off') + ', as with active_target, is used');\n wanted = undefined;\n }\n if (wanted === undefined) { return target; }\n if (wanted && !target) {\n ge.warn('clipboard.shortcuts needs active_target, or settings_panel \"sidebar\": without it, Ctrl+C and Ctrl+V do nothing');\n return false;\n }\n\n return wanted;\n }\n\n function copy(node, kind, source) {\n var html = ge.nodeHtml(node);\n\n write({ version: VERSION, category: categoryOf(kind), kind: kind, html: html });\n ge.emit('after-copy', ge.payloadFor(kind, node, { source: source || 'tool' }));\n\n // The canvas came back with new drawers: the one to flash is the\n // node's new copy tool\n var tool = dom.child(ge.drawerOf(node), '.ge-copy');\n if (!tool) { return; }\n\n var icon = dom.one(tool, 'i');\n dom.addClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-check2'); }\n setTimeout(function() {\n dom.removeClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-copy'); }\n }, 1200);\n }\n\n function paste(target, categories) {\n var clip = read();\n if (!fits(clip, categories)) { return; }\n\n ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: 'paste' });\n }\n\n /**\n * Ctrl+V: into the target as the toolbar's paste button would - a\n * section, which only the canvas takes, after the block the target is in\n * - and what that button has none for as the target's paste tool would.\n * With no target, as the selected node's paste tool. What does not fit\n * is not pasted anywhere else.\n */\n function pasteByShortcut() {\n var clip = read();\n var target = ge.getActiveTarget();\n var node = target || ge.getSelected();\n if (!clip || !node) { return false; }\n\n var placement = { appendTo: node, source: 'paste' };\n if (target && clip.category === 'section') {\n if (!available(clip)) { return false; }\n var top = target;\n while (top.parentElement && top.parentElement !== ge.canvas) { top = top.parentElement; }\n placement = { insertAfter: top, source: 'paste' };\n } else if (!fits(clip, TARGETS[ge.kindOf(node)] || [])) {\n return false;\n }\n\n var made = ge.place(ge.freshNode(clip.html), clip.kind, placement);\n if (made && made.scrollIntoView) { made.scrollIntoView({ behavior: 'smooth' }); }\n\n return true;\n }\n\n function copyByShortcut() {\n if (textSelected()) { return false; }\n\n var node = ge.getSelected() || ge.getActiveTarget();\n var kind = node && ge.kindOf(node);\n if (!kind || !categoryOf(kind)) { return false; }\n\n copy(node, kind, 'shortcut');\n return true;\n }\n\n function onKeydown(e) {\n if (lastEditor !== ge || e.repeat || e.shiftKey || e.altKey || !(e.ctrlKey || e.metaKey)) { return; }\n var key = shortcutKey(e);\n if (!key || !dom.hasClass(ge.canvas, 'ge-editing') || typing() || ge.panelOpen()) { return; }\n\n // Left alone, it is the browser's: a copy of the text, a paste in a field\n if (key === 'c' ? copyByShortcut() : pasteByShortcut()) { e.preventDefault(); }\n }\n\n function listenForShortcuts(on) {\n var at = listeningEditors.indexOf(ge);\n\n if (on && at === -1) {\n listeningEditors.push(ge);\n document.addEventListener('keydown', onKeydown);\n } else if (!on && at !== -1) {\n listeningEditors.splice(at, 1);\n document.removeEventListener('keydown', onKeydown);\n }\n }\n\n /** Show the paste tools, and the toolbar's paste buttons, that fit what is copied. */\n function refresh() {\n var clip = read();\n\n dom.all(ge.canvas, '.ge-paste').forEach(function(tool) {\n dom.toggle(tool, fits(clip, tool.getAttribute('data-ge-paste').split(' ')));\n });\n\n ge.toolbarItems('clipboard').forEach(function(button) {\n var item = TOOLBAR[parseInt(button.getAttribute('data-ge-item'), 10)];\n dom.toggle(button, fits(clip, [item.kind]));\n });\n\n shown = clip;\n }\n\n var TOOLBAR = [\n { kind: 'row', labelKey: 'clipboard.paste_row' },\n { kind: 'section', labelKey: 'clipboard.paste_section' },\n ].map(function(item) {\n return Object.assign(item, {\n iconClass: 'bi bi-clipboard-plus',\n // On the right, as an icon: pasting is not one of the things\n // the add buttons make\n align: 'end',\n source: 'paste',\n // A toolbar built again - another plugin's buttons changed -\n // shows only what fits, as refresh would\n hidden: function() { return !fits(read(), [item.kind]); },\n // What the button showed, even if another tab has copied\n // something else since\n create: function() { return ge.freshNode(shown.html); },\n });\n });\n\n return {\n drawerTools: function(drawer, node, kind) {\n if (categoryOf(kind)) {\n ge.createTool(drawer, ge.t('tool.copy'), 'ge-copy', 'bi bi-copy', function() {\n copy(node, kind);\n });\n }\n\n var categories = TARGETS[kind];\n if (!categories) { return; }\n\n var tool = ge.createTool(drawer, ge.t('tool.paste'), 'ge-paste', 'bi bi-clipboard-plus', function() {\n paste(node, categories);\n });\n tool.setAttribute('data-ge-paste', categories.join(' '));\n dom.toggle(tool, fits(read(), categories));\n },\n\n toolbar: TOOLBAR,\n\n onInit: function() {\n refresh();\n if (!listening) {\n document.addEventListener(CHANGE, refresh);\n listening = true;\n }\n listenForShortcuts(shortcuts);\n },\n\n onDeinit: function() {\n document.removeEventListener(CHANGE, refresh);\n listening = false;\n // A copy takes the canvas out of editing and back: the editor\n // stays the one used last, as it is still\n listenForShortcuts(false);\n },\n\n onDestroy: function() {\n if (lastEditor === ge) { lastEditor = null; }\n },\n\n // Nothing of its own on the canvas: the tools are in the drawers\n cleanMarkup: function() {},\n };\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["GridEditor", "all", "root", "selector", "one", "children", "node", "selector", "each", "child", "is", "node", "selector", "addClass", "node", "names", "split", "name", "removeClass", "hasClass", "node", "name", "split", "names", "defaultDisplays", "defaultDisplay", "node", "tag", "probe", "show", "hide", "toggle", "state", "visible", "GridEditor", "STORAGE_ITEM", "CHANGE", "VERSION", "memory", "TARGETS", "read", "raw", "error", "clip", "write", "announce", "e", "listeningEditors", "lastEditor", "track", "ge", "shortcutKey", "key", "typing", "focus", "is", "textSelected", "selection", "shown", "listening", "shortcuts", "shortcutsSetting", "used", "name", "available", "fits", "categories", "categoryOf", "kind", "wanted", "target", "copy", "node", "source", "html", "tool", "child", "icon", "one", "addClass", "removeClass", "paste", "pasteByShortcut", "placement", "top", "made", "copyByShortcut", "onKeydown", "hasClass", "listenForShortcuts", "on", "at", "refresh", "all", "toggle", "button", "item", "TOOLBAR", "drawer"]
|
|
7
7
|
}
|
package/docs/events.md
CHANGED
|
@@ -93,7 +93,7 @@ about every insertion can bind `grideditor:before-add` and switch on
|
|
|
93
93
|
| `grideditor:view-change` | — | no | after the view changes, only if it actually changed |
|
|
94
94
|
| `grideditor:target-change` | — | no | with `active_target`, after the column or section the toolbar adds to changes, only if it actually changed |
|
|
95
95
|
| `grideditor:selection-change` | — | no | with `settings_panel: 'sidebar'`, after the node whose settings the sidebar shows changes, only if it actually changed |
|
|
96
|
-
| `grideditor:after-copy` | — | no | with the clipboard plugin, after a node is copied |
|
|
96
|
+
| `grideditor:after-copy` | — | no | with the clipboard plugin, after a node is copied: `source: 'tool'` from its Copy tool, `'shortcut'` from Ctrl+C |
|
|
97
97
|
| `grideditor:after-autosave` | — | no | with the autosave plugin, after a draft is written |
|
|
98
98
|
| `grideditor:after-restore-draft` | — | no | with the autosave plugin, after the user chose to restore a draft and the canvas is it |
|
|
99
99
|
| `grideditor:autosave-error` | — | no | with the autosave plugin, when the storage refused a draft |
|
package/docs/plugins.md
CHANGED
|
@@ -171,6 +171,10 @@ element, and where there may be several, an array.
|
|
|
171
171
|
| `ge.setHtml(html)` | The canvas made of other markup, and edited again: the source view closed if it is open, `deinit`, the markup in, `init`. No add or delete events: it is replaced as a whole. A `<script>` in it does not run in the editor |
|
|
172
172
|
| `ge.confirm(message, options, answer)` | A question in the editor's confirm modal, the one deleting asks in. `options`: `title`, `ok`, `cancel` - the texts, the locale's by default - and `danger`, a red ok button. `answer(true)` for ok, `answer(false)` for cancel, the close button or Escape, `answer(null)` when the modal is taken away unanswered: `setLocale`, `destroy`, or another question. The browser's `confirm` without Bootstrap's javascript |
|
|
173
173
|
| `ge.refreshToolbar()` | The toolbar built again, from what the feature plugins' `toolbar`s hold now: for a plugin whose buttons come and go. The category shown with `toolbar_groups`, the preview and the source view stay as they are. Does nothing before the toolbar is first built, and after `destroy` |
|
|
174
|
+
| `ge.getSelected()` | The node the sidebar shows, as the public method gives it: `null` without the `'sidebar'` panel |
|
|
175
|
+
| `ge.getActiveTarget()` | The column or section the toolbar adds to, as the public method gives it: `null` without `active_target` |
|
|
176
|
+
| `ge.panelOpen()` | Whether a settings panel is open in a popover, an offcanvas or a modal, or one is unfolded inline in a drawer, or the editor's dialog or delete confirmation is up. The sidebar does not count: it is always there |
|
|
177
|
+
| `ge.contains(node)` | Whether a node is this editor's: in its canvas, its toolbar, its settings panels (the sidebar too, when it has it), its dialog or its confirmation |
|
|
174
178
|
| `ge.toolbarItems(name)` | An array of the toolbar buttons the feature plugin `name` declared, for showing and hiding them |
|
|
175
179
|
| `ge.bareStyle(node, family, property)` | A css property's value on the node with none of the family's classes: what a preview shows when no class applies and that is not a constant |
|
|
176
180
|
| `ge.hostStyle(node, property?)` | `{ value, priority }` of one property of the node's own style, as the host wrote it: never a breakpoint preview's. With no property, the whole of it as css text |
|
package/package.json
CHANGED
package/src/js/grideditor.js
CHANGED
|
@@ -2622,6 +2622,27 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2622
2622
|
? dom.all(mainControls, '[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
|
|
2623
2623
|
: [];
|
|
2624
2624
|
},
|
|
2625
|
+
getSelected: function() { return panelMode() === 'sidebar' ? selected : null; },
|
|
2626
|
+
getActiveTarget: function() { return settings.active_target ? activeTarget : null; },
|
|
2627
|
+
// What keeps Escape from the selection and the target, and an
|
|
2628
|
+
// inline panel unfolded in a drawer, which Escape leaves alone
|
|
2629
|
+
panelOpen: function() {
|
|
2630
|
+
if (openSettingsState || dialogState) { return true; }
|
|
2631
|
+
if (confirmDialog && dom.hasClass(confirmDialog, 'show')) { return true; }
|
|
2632
|
+
|
|
2633
|
+
return panelMode() === 'inline' && dom.all(canvas, '.ge-tools-drawer > .ge-details').some(dom.visible);
|
|
2634
|
+
},
|
|
2635
|
+
// The toolbar and the settings panels are outside the canvas
|
|
2636
|
+
contains: function(node) {
|
|
2637
|
+
if (!node || node.nodeType !== 1) { return false; }
|
|
2638
|
+
if (canvas.contains(node) || (mainControls && mainControls.contains(node))) { return true; }
|
|
2639
|
+
if (dialogPanel && dialogPanel.contains(node)) { return true; }
|
|
2640
|
+
if (confirmDialog && confirmDialog.contains(node)) { return true; }
|
|
2641
|
+
|
|
2642
|
+
return Object.keys(settingsPanels).some(function(mode) {
|
|
2643
|
+
return settingsPanels[mode].contains(node);
|
|
2644
|
+
});
|
|
2645
|
+
},
|
|
2625
2646
|
};
|
|
2626
2647
|
}
|
|
2627
2648
|
|