@themarioga/grid-editor 8.9.0 → 8.10.1

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.
@@ -8,16 +8,6 @@ function all(root, selector) {
8
8
  function one(root, selector) {
9
9
  return root ? root.querySelector(selector) : null;
10
10
  }
11
- function selfAndAll(root, selector) {
12
- if (!root) {
13
- return [];
14
- }
15
- var found = all(root, selector);
16
- if (root.nodeType === 1 && root.matches(selector)) {
17
- found.unshift(root);
18
- }
19
- return found;
20
- }
21
11
  function children(node, selector) {
22
12
  if (!node) {
23
13
  return [];
@@ -29,20 +19,6 @@ function children(node, selector) {
29
19
  function child(node, selector) {
30
20
  return children(node, selector)[0] || null;
31
21
  }
32
- function parse(html) {
33
- var template = document.createElement("template");
34
- template.innerHTML = html;
35
- return Array.prototype.slice.call(template.content.childNodes);
36
- }
37
- function create(html) {
38
- var nodes = parse(html.trim());
39
- for (var i = 0; i < nodes.length; i++) {
40
- if (nodes[i].nodeType === 1) {
41
- return nodes[i];
42
- }
43
- }
44
- return null;
45
- }
46
22
  function addClass(node, names) {
47
23
  split(names).forEach(function(name) {
48
24
  node.classList.add(name);
@@ -108,18 +84,6 @@ var TARGETS = {
108
84
  row: ["column"],
109
85
  section: ["row"]
110
86
  };
111
- var REFERENCES = [
112
- "data-bs-target",
113
- "data-bs-parent",
114
- "href",
115
- "aria-controls",
116
- "aria-labelledby",
117
- "aria-describedby",
118
- "for",
119
- "data-ge-popup-id",
120
- "data-ge-popup-target"
121
- ];
122
- var GENERATED_ID = /^ge-([a-z][a-z-]*?)-\d+-[a-z0-9]+$/;
123
87
  function read() {
124
88
  var raw;
125
89
  try {
@@ -206,64 +170,7 @@ GridEditor.features.clipboard = function(ge) {
206
170
  if (!fits(clip, categories)) {
207
171
  return;
208
172
  }
209
- ge.place(fresh(clip.html), clip.kind, { appendTo: target, source: "paste" });
210
- }
211
- function fresh(html) {
212
- var node = create(html);
213
- var renamed = {};
214
- var taken = function(id) {
215
- return !!document.getElementById(id) || Object.keys(renamed).some(function(old) {
216
- return renamed[old] === id;
217
- });
218
- };
219
- selfAndAll(node, "[id]").forEach(function(element) {
220
- var id = element.id;
221
- if (!document.getElementById(id)) {
222
- return;
223
- }
224
- var generated = GENERATED_ID.exec(id);
225
- var next;
226
- if (generated) {
227
- do {
228
- next = ge.containerId(generated[1]);
229
- } while (taken(next));
230
- } else {
231
- var n = 2;
232
- while (taken(id + "-" + n)) {
233
- n++;
234
- }
235
- next = id + "-" + n;
236
- }
237
- renamed[id] = next;
238
- element.id = next;
239
- });
240
- if (!Object.keys(renamed).length) {
241
- return node;
242
- }
243
- selfAndAll(node, "*").forEach(function(element) {
244
- REFERENCES.forEach(function(name) {
245
- var value = element.getAttribute(name);
246
- if (value === null) {
247
- return;
248
- }
249
- if (name === "href" && value.charAt(0) !== "#") {
250
- return;
251
- }
252
- var rewritten = value.split(/(\s+)/).map(function(token) {
253
- if (renamed[token]) {
254
- return renamed[token];
255
- }
256
- if (token.charAt(0) === "#" && renamed[token.slice(1)]) {
257
- return "#" + renamed[token.slice(1)];
258
- }
259
- return token;
260
- }).join("");
261
- if (rewritten !== value) {
262
- element.setAttribute(name, rewritten);
263
- }
264
- });
265
- });
266
- return node;
173
+ ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: "paste" });
267
174
  }
268
175
  function refresh() {
269
176
  var clip = read();
@@ -286,10 +193,15 @@ GridEditor.features.clipboard = function(ge) {
286
193
  // the add buttons make
287
194
  align: "end",
288
195
  source: "paste",
196
+ // A toolbar built again - another plugin's buttons changed -
197
+ // shows only what fits, as refresh would
198
+ hidden: function() {
199
+ return !fits(read(), [item.kind]);
200
+ },
289
201
  // What the button showed, even if another tab has copied
290
202
  // something else since
291
203
  create: function() {
292
- return fresh(shown.html);
204
+ return ge.freshNode(shown.html);
293
205
  }
294
206
  });
295
207
  });
@@ -12,16 +12,6 @@
12
12
  function one(root, selector) {
13
13
  return root ? root.querySelector(selector) : null;
14
14
  }
15
- function selfAndAll(root, selector) {
16
- if (!root) {
17
- return [];
18
- }
19
- var found = all(root, selector);
20
- if (root.nodeType === 1 && root.matches(selector)) {
21
- found.unshift(root);
22
- }
23
- return found;
24
- }
25
15
  function children(node, selector) {
26
16
  if (!node) {
27
17
  return [];
@@ -33,20 +23,6 @@
33
23
  function child(node, selector) {
34
24
  return children(node, selector)[0] || null;
35
25
  }
36
- function parse(html) {
37
- var template = document.createElement("template");
38
- template.innerHTML = html;
39
- return Array.prototype.slice.call(template.content.childNodes);
40
- }
41
- function create(html) {
42
- var nodes = parse(html.trim());
43
- for (var i = 0; i < nodes.length; i++) {
44
- if (nodes[i].nodeType === 1) {
45
- return nodes[i];
46
- }
47
- }
48
- return null;
49
- }
50
26
  function addClass(node, names) {
51
27
  split(names).forEach(function(name) {
52
28
  node.classList.add(name);
@@ -112,18 +88,6 @@
112
88
  row: ["column"],
113
89
  section: ["row"]
114
90
  };
115
- var REFERENCES = [
116
- "data-bs-target",
117
- "data-bs-parent",
118
- "href",
119
- "aria-controls",
120
- "aria-labelledby",
121
- "aria-describedby",
122
- "for",
123
- "data-ge-popup-id",
124
- "data-ge-popup-target"
125
- ];
126
- var GENERATED_ID = /^ge-([a-z][a-z-]*?)-\d+-[a-z0-9]+$/;
127
91
  function read() {
128
92
  var raw;
129
93
  try {
@@ -210,64 +174,7 @@
210
174
  if (!fits(clip, categories)) {
211
175
  return;
212
176
  }
213
- ge.place(fresh(clip.html), clip.kind, { appendTo: target, source: "paste" });
214
- }
215
- function fresh(html) {
216
- var node = create(html);
217
- var renamed = {};
218
- var taken = function(id) {
219
- return !!document.getElementById(id) || Object.keys(renamed).some(function(old) {
220
- return renamed[old] === id;
221
- });
222
- };
223
- selfAndAll(node, "[id]").forEach(function(element) {
224
- var id = element.id;
225
- if (!document.getElementById(id)) {
226
- return;
227
- }
228
- var generated = GENERATED_ID.exec(id);
229
- var next;
230
- if (generated) {
231
- do {
232
- next = ge.containerId(generated[1]);
233
- } while (taken(next));
234
- } else {
235
- var n = 2;
236
- while (taken(id + "-" + n)) {
237
- n++;
238
- }
239
- next = id + "-" + n;
240
- }
241
- renamed[id] = next;
242
- element.id = next;
243
- });
244
- if (!Object.keys(renamed).length) {
245
- return node;
246
- }
247
- selfAndAll(node, "*").forEach(function(element) {
248
- REFERENCES.forEach(function(name) {
249
- var value = element.getAttribute(name);
250
- if (value === null) {
251
- return;
252
- }
253
- if (name === "href" && value.charAt(0) !== "#") {
254
- return;
255
- }
256
- var rewritten = value.split(/(\s+)/).map(function(token) {
257
- if (renamed[token]) {
258
- return renamed[token];
259
- }
260
- if (token.charAt(0) === "#" && renamed[token.slice(1)]) {
261
- return "#" + renamed[token.slice(1)];
262
- }
263
- return token;
264
- }).join("");
265
- if (rewritten !== value) {
266
- element.setAttribute(name, rewritten);
267
- }
268
- });
269
- });
270
- return node;
177
+ ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: "paste" });
271
178
  }
272
179
  function refresh() {
273
180
  var clip = read();
@@ -290,10 +197,15 @@
290
197
  // the add buttons make
291
198
  align: "end",
292
199
  source: "paste",
200
+ // A toolbar built again - another plugin's buttons changed -
201
+ // shows only what fits, as refresh would
202
+ hidden: function() {
203
+ return !fits(read(), [item.kind]);
204
+ },
293
205
  // What the button showed, even if another tab has copied
294
206
  // something else since
295
207
  create: function() {
296
- return fresh(shown.html);
208
+ return ge.freshNode(shown.html);
297
209
  }
298
210
  });
299
211
  });
@@ -1 +1 @@
1
- (()=>{var l=window.GridEditor;if(!l||typeof l.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 x(t,e){return t?Array.prototype.slice.call(t.querySelectorAll(e)):[]}function S(t,e){return t?t.querySelector(e):null}function b(t,e){if(!t)return[];var n=x(t,e);return t.nodeType===1&&t.matches(e)&&n.unshift(t),n}function P(t,e){return t?Array.prototype.filter.call(t.children,function(n){return!e||n.matches(e)}):[]}function B(t,e){return P(t,e)[0]||null}function _(t){var e=document.createElement("template");return e.innerHTML=t,Array.prototype.slice.call(e.content.childNodes)}function L(t){for(var e=_(t.trim()),n=0;n<e.length;n++)if(e[n].nodeType===1)return e[n];return null}function I(t,e){return N(e).forEach(function(n){t.classList.add(n)}),t}function j(t,e){return N(e).forEach(function(n){t.classList.remove(n)}),t}function N(t){return String(t||"").split(/\s+/).filter(Boolean)}var d={};function F(t){var e=t.nodeName;if(!d[e]){var n=document.body.appendChild(document.createElement(e));d[e]=getComputedStyle(n).display,n.remove(),d[e]==="none"&&(d[e]="block")}return d[e]}function M(t){return t.style.removeProperty("display"),getComputedStyle(t).display==="none"&&(t.style.display=F(t)),t}function U(t){return t.style.display="none",t}function g(t,e){return e===void 0&&(e=!z(t)),e?M(t):U(t)}function z(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}Object.assign(l.locales.en,{"tool.copy":"Copy","tool.paste":"Paste","clipboard.paste_row":"Paste row","clipboard.paste_section":"Paste section"});var A="grideditor.clipboard",w="grideditor-clipboard",R=1,v=null,J={column:["row","text","container","element"],row:["column"],section:["row"]},K=["data-bs-target","data-bs-parent","href","aria-controls","aria-labelledby","aria-describedby","for","data-ge-popup-id","data-ge-popup-target"],W=/^ge-([a-z][a-z-]*?)-\d+-[a-z0-9]+$/;function E(){var t;try{t=window.localStorage.getItem(A)}catch(n){return v}if(t===null)return v;try{var e=JSON.parse(t);return e&&e.version===R&&e.html?e:null}catch(n){return null}}function V(t){try{window.localStorage.setItem(A,JSON.stringify(t)),v=null}catch(e){v=t}H()}function H(){document.dispatchEvent(new CustomEvent(w))}window.addEventListener("storage",function(t){t.key===A&&H()});l.features.clipboard=function(t){var e=null,n=!1;function h(r){return!t.settings.plugins||t.settings.plugins.indexOf(r)!==-1}function k(r){return r.category==="container"?!!l.containers[r.kind]&&h(r.kind):r.category==="section"?!!l.features.sections&&h("sections"):r.category==="element"?!!l.features.elements&&h("elements"):!0}function m(r,i){return!!r&&i.indexOf(r.category)!==-1&&k(r)}function T(r){return l.containers[r]?"container":["row","column","section","text","element"].indexOf(r)!==-1?r:null}function D(r,i){var o=t.nodeHtml(r);V({version:R,category:T(i),kind:i,html:o}),t.emit("after-copy",t.payloadFor(i,r,{source:"tool"}));var u=B(t.drawerOf(r),".ge-copy");if(u){var a=S(u,"i");I(u,"ge-copied"),a&&a.setAttribute("class","bi bi-check2"),setTimeout(function(){j(u,"ge-copied"),a&&a.setAttribute("class","bi bi-copy")},1200)}}function G(r,i){var o=E();m(o,i)&&t.place(C(o.html),o.kind,{appendTo:r,source:"paste"})}function C(r){var i=L(r),o={},u=function(a){return!!document.getElementById(a)||Object.keys(o).some(function(c){return o[c]===a})};return b(i,"[id]").forEach(function(a){var c=a.id;if(document.getElementById(c)){var p=W.exec(c),s;if(p)do s=t.containerId(p[1]);while(u(s));else{for(var f=2;u(c+"-"+f);)f++;s=c+"-"+f}o[c]=s,a.id=s}}),Object.keys(o).length&&b(i,"*").forEach(function(a){K.forEach(function(c){var p=a.getAttribute(c);if(p!==null&&!(c==="href"&&p.charAt(0)!=="#")){var s=p.split(/(\s+)/).map(function(f){return o[f]?o[f]:f.charAt(0)==="#"&&o[f.slice(1)]?"#"+o[f.slice(1)]:f}).join("");s!==p&&a.setAttribute(c,s)}})}),i}function y(){var r=E();x(t.canvas,".ge-paste").forEach(function(i){g(i,m(r,i.getAttribute("data-ge-paste").split(" ")))}),t.toolbarItems("clipboard").forEach(function(i){var o=O[parseInt(i.getAttribute("data-ge-item"),10)];g(i,m(r,[o.kind]))}),e=r}var O=[{kind:"row",labelKey:"clipboard.paste_row"},{kind:"section",labelKey:"clipboard.paste_section"}].map(function(r){return Object.assign(r,{iconClass:"bi bi-clipboard-plus",align:"end",source:"paste",create:function(){return C(e.html)}})});return{drawerTools:function(r,i,o){T(o)&&t.createTool(r,t.t("tool.copy"),"ge-copy","bi bi-copy",function(){D(i,o)});var u=J[o];if(u){var a=t.createTool(r,t.t("tool.paste"),"ge-paste","bi bi-clipboard-plus",function(){G(i,u)});a.setAttribute("data-ge-paste",u.join(" ")),g(a,m(E(),u))}},toolbar:O,onInit:function(){y(),n||(document.addEventListener(w,y),n=!0)},onDeinit:function(){document.removeEventListener(w,y),n=!1},cleanMarkup:function(){}}};})();
1
+ (()=>{var u=window.GridEditor;if(!u||typeof u.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 b(t,e){return t?Array.prototype.slice.call(t.querySelectorAll(e)):[]}function w(t,e){return t?t.querySelector(e):null}function k(t,e){return t?Array.prototype.filter.call(t.children,function(i){return!e||i.matches(e)}):[]}function E(t,e){return k(t,e)[0]||null}function T(t,e){return C(e).forEach(function(i){t.classList.add(i)}),t}function A(t,e){return C(e).forEach(function(i){t.classList.remove(i)}),t}function C(t){return String(t||"").split(/\s+/).filter(Boolean)}var f={};function j(t){var e=t.nodeName;if(!f[e]){var i=document.body.appendChild(document.createElement(e));f[e]=getComputedStyle(i).display,i.remove(),f[e]==="none"&&(f[e]="block")}return f[e]}function I(t){return t.style.removeProperty("display"),getComputedStyle(t).display==="none"&&(t.style.display=j(t)),t}function H(t){return t.style.display="none",t}function s(t,e){return e===void 0&&(e=!R(t)),e?I(t):H(t)}function R(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}Object.assign(u.locales.en,{"tool.copy":"Copy","tool.paste":"Paste","clipboard.paste_row":"Paste row","clipboard.paste_section":"Paste section"});var h="grideditor.clipboard",v="grideditor-clipboard",S=1,d=null,G={column:["row","text","container","element"],row:["column"],section:["row"]};function p(){var t;try{t=window.localStorage.getItem(h)}catch(i){return d}if(t===null)return d;try{var e=JSON.parse(t);return e&&e.version===S&&e.html?e:null}catch(i){return null}}function _(t){try{window.localStorage.setItem(h,JSON.stringify(t)),d=null}catch(e){d=t}O()}function O(){document.dispatchEvent(new CustomEvent(v))}window.addEventListener("storage",function(t){t.key===h&&O()});u.features.clipboard=function(t){var e=null,i=!1;function m(n){return!t.settings.plugins||t.settings.plugins.indexOf(n)!==-1}function L(n){return n.category==="container"?!!u.containers[n.kind]&&m(n.kind):n.category==="section"?!!u.features.sections&&m("sections"):n.category==="element"?!!u.features.elements&&m("elements"):!0}function l(n,r){return!!n&&r.indexOf(n.category)!==-1&&L(n)}function y(n){return u.containers[n]?"container":["row","column","section","text","element"].indexOf(n)!==-1?n:null}function B(n,r){var o=t.nodeHtml(n);_({version:S,category:y(r),kind:r,html:o}),t.emit("after-copy",t.payloadFor(r,n,{source:"tool"}));var a=E(t.drawerOf(n),".ge-copy");if(a){var c=w(a,"i");T(a,"ge-copied"),c&&c.setAttribute("class","bi bi-check2"),setTimeout(function(){A(a,"ge-copied"),c&&c.setAttribute("class","bi bi-copy")},1200)}}function N(n,r){var o=p();l(o,r)&&t.place(t.freshNode(o.html),o.kind,{appendTo:n,source:"paste"})}function g(){var n=p();b(t.canvas,".ge-paste").forEach(function(r){s(r,l(n,r.getAttribute("data-ge-paste").split(" ")))}),t.toolbarItems("clipboard").forEach(function(r){var o=x[parseInt(r.getAttribute("data-ge-item"),10)];s(r,l(n,[o.kind]))}),e=n}var x=[{kind:"row",labelKey:"clipboard.paste_row"},{kind:"section",labelKey:"clipboard.paste_section"}].map(function(n){return Object.assign(n,{iconClass:"bi bi-clipboard-plus",align:"end",source:"paste",hidden:function(){return!l(p(),[n.kind])},create:function(){return t.freshNode(e.html)}})});return{drawerTools:function(n,r,o){y(o)&&t.createTool(n,t.t("tool.copy"),"ge-copy","bi bi-copy",function(){B(r,o)});var a=G[o];if(a){var c=t.createTool(n,t.t("tool.paste"),"ge-paste","bi bi-clipboard-plus",function(){N(r,a)});c.setAttribute("data-ge-paste",a.join(" ")),s(c,l(p(),a))}},toolbar:x,onInit:function(){g(),i||(document.addEventListener(v,g),i=!0)},onDeinit:function(){document.removeEventListener(v,g),i=!1},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\n/**\n * The attributes that point at an id, as the id itself or as a #selector.\n * A pasted copy that has to take new ids takes them here too, so its tabs,\n * its accordion and its popups still point at their own panes.\n */\nvar REFERENCES = ['data-bs-target', 'data-bs-parent', 'href', 'aria-controls', 'aria-labelledby',\n 'aria-describedby', 'for', 'data-ge-popup-id', 'data-ge-popup-target'];\n\n/** An id grid-editor generated: ge-{type}-{counter}-{random}. */\nvar GENERATED_ID = /^ge-([a-z][a-z-]*?)-\\d+-[a-z0-9]+$/;\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(fresh(clip.html), clip.kind, { appendTo: target, source: 'paste' });\n }\n\n /**\n * The copied markup as a node, with new ids where the page already has\n * them: two copies of a tab strip pointing at one set of panes is a tab\n * strip that opens the other copy's tabs. An id the page does not have\n * is kept, so pasting into another page changes nothing.\n *\n * Parsed the way innerHTML parses, so a <script> in what was copied is\n * markup, pasted like the rest, and does not run in the editor.\n */\n function fresh(html) {\n var node = dom.create(html);\n var renamed = {};\n var taken = function(id) {\n return !!document.getElementById(id) || Object.keys(renamed).some(function(old) {\n return renamed[old] === id;\n });\n };\n\n dom.selfAndAll(node, '[id]').forEach(function(element) {\n var id = element.id;\n if (!document.getElementById(id)) { return; }\n\n var generated = GENERATED_ID.exec(id);\n var next;\n\n if (generated) {\n do { next = ge.containerId(generated[1]); } while (taken(next));\n } else {\n var n = 2;\n while (taken(id + '-' + n)) { n++; }\n next = id + '-' + n;\n }\n\n renamed[id] = next;\n element.id = next;\n });\n\n if (!Object.keys(renamed).length) { return node; }\n\n dom.selfAndAll(node, '*').forEach(function(element) {\n REFERENCES.forEach(function(name) {\n var value = element.getAttribute(name);\n if (value === null) { return; }\n if (name === 'href' && value.charAt(0) !== '#') { return; }\n\n var rewritten = value.split(/(\\s+)/).map(function(token) {\n if (renamed[token]) { return renamed[token]; }\n if (token.charAt(0) === '#' && renamed[token.slice(1)]) { return '#' + renamed[token.slice(1)]; }\n\n return token;\n }).join('');\n\n if (rewritten !== value) { element.setAttribute(name, rewritten); }\n });\n });\n\n return node;\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 // What the button showed, even if another tab has copied\n // something else since\n create: function() { return fresh(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,CAGO,SAASE,EAAWH,EAAMC,EAAU,CACvC,GAAI,CAACD,EAAQ,MAAO,CAAC,EAErB,IAAII,EAAQL,EAAIC,EAAMC,CAAQ,EAC9B,OAAID,EAAK,WAAa,GAAKA,EAAK,QAAQC,CAAQ,GAAKG,EAAM,QAAQJ,CAAI,EAChEI,CACX,CAGO,SAASC,EAASC,EAAML,EAAU,CACrC,OAAKK,EAEE,MAAM,UAAU,OAAO,KAAKA,EAAK,SAAU,SAASC,EAAM,CAC7D,MAAO,CAACN,GAAYM,EAAK,QAAQN,CAAQ,CAC7C,CAAC,EAJmB,CAAC,CAKzB,CAGO,SAASO,EAAMF,EAAML,EAAU,CAClC,OAAOI,EAASC,EAAML,CAAQ,EAAE,CAAC,GAAK,IAC1C,CA4CO,SAASQ,EAAMC,EAAM,CACxB,IAAIC,EAAW,SAAS,cAAc,UAAU,EAChD,OAAAA,EAAS,UAAYD,EACd,MAAM,UAAU,MAAM,KAAKC,EAAS,QAAQ,UAAU,CACjE,CAGO,SAASC,EAAOF,EAAM,CAGzB,QAFIG,EAAQJ,EAAMC,EAAK,KAAK,CAAC,EAEpBI,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAC9B,GAAID,EAAMC,CAAC,EAAE,WAAa,EAAK,OAAOD,EAAMC,CAAC,EAEjD,OAAO,IACX,CAsBO,SAASC,EAASC,EAAMC,EAAO,CAClC,OAAAC,EAAMD,CAAK,EAAE,QAAQ,SAASE,EAAM,CAAEH,EAAK,UAAU,IAAIG,CAAI,CAAG,CAAC,EAC1DH,CACX,CAEO,SAASI,EAAYJ,EAAMC,EAAO,CACrC,OAAAC,EAAMD,CAAK,EAAE,QAAQ,SAASE,EAAM,CAAEH,EAAK,UAAU,OAAOG,CAAI,CAAG,CAAC,EAC7DH,CACX,CAkBA,SAASK,EAAMC,EAAO,CAClB,OAAO,OAAOA,GAAS,EAAE,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO,CAC1D,CA6CA,IAAIC,EAAkB,CAAC,EAEvB,SAASC,EAAeC,EAAM,CAC1B,IAAIC,EAAMD,EAAK,SAEf,GAAI,CAACF,EAAgBG,CAAG,EAAG,CACvB,IAAIC,EAAQ,SAAS,KAAK,YAAY,SAAS,cAAcD,CAAG,CAAC,EACjEH,EAAgBG,CAAG,EAAI,iBAAiBC,CAAK,EAAE,QAC/CA,EAAM,OAAO,EACTJ,EAAgBG,CAAG,IAAM,SAAUH,EAAgBG,CAAG,EAAI,QAClE,CACA,OAAOH,EAAgBG,CAAG,CAC9B,CAGO,SAASE,EAAKH,EAAM,CACvB,OAAAA,EAAK,MAAM,eAAe,SAAS,EAC/B,iBAAiBA,CAAI,EAAE,UAAY,SAAUA,EAAK,MAAM,QAAUD,EAAeC,CAAI,GAClFA,CACX,CAEO,SAASI,EAAKJ,EAAM,CACvB,OAAAA,EAAK,MAAM,QAAU,OACdA,CACX,CAGO,SAASK,EAAOL,EAAMM,EAAO,CAChC,OAAIA,IAAU,SAAaA,EAAQ,CAACC,EAAQP,CAAI,GACzCM,EAAQH,EAAKH,CAAI,EAAII,EAAKJ,CAAI,CACzC,CAGO,SAASO,EAAQP,EAAM,CAC1B,MAAO,CAAC,EAAEA,EAAK,aAAeA,EAAK,cAAgBA,EAAK,eAAe,EAAE,OAC7E,CCjNA,OAAO,OAAOQ,EAAW,QAAQ,GAAI,CACjC,YAAa,OACb,aAAc,QACd,sBAAuB,YACvB,0BAA2B,eAC/B,CAAC,EAED,IAAIC,EAAe,uBACfC,EAAS,uBACTC,EAAU,EAGVC,EAAS,KAOTC,EAAU,CACV,OAAQ,CAAC,MAAO,OAAQ,YAAa,SAAS,EAC9C,IAAK,CAAC,QAAQ,EACd,QAAS,CAAC,KAAK,CACnB,EAOIC,EAAa,CAAC,iBAAkB,iBAAkB,OAAQ,gBAAiB,kBAC3E,mBAAoB,MAAO,mBAAoB,sBAAsB,EAGrEC,EAAe,qCAEnB,SAASC,GAAO,CACZ,IAAIC,EAEJ,GAAI,CACAA,EAAM,OAAO,aAAa,QAAQR,CAAY,CAClD,OAASS,EAAO,CACZ,OAAON,CACX,CACA,GAAIK,IAAQ,KAAQ,OAAOL,EAE3B,GAAI,CACA,IAAIO,EAAO,KAAK,MAAMF,CAAG,EACzB,OAAOE,GAAQA,EAAK,UAAYR,GAAWQ,EAAK,KAAOA,EAAO,IAClE,OAASD,EAAO,CACZ,OAAO,IACX,CACJ,CAEA,SAASE,EAAMD,EAAM,CACjB,GAAI,CACA,OAAO,aAAa,QAAQV,EAAc,KAAK,UAAUU,CAAI,CAAC,EAC9DP,EAAS,IACb,OAASM,EAAO,CACZN,EAASO,CACb,CAEAE,EAAS,CACb,CAGA,SAASA,GAAW,CAChB,SAAS,cAAc,IAAI,YAAYX,CAAM,CAAC,CAClD,CAGA,OAAO,iBAAiB,UAAW,SAASY,EAAG,CACvCA,EAAE,MAAQb,GAAgBY,EAAS,CAC3C,CAAC,EAEDb,EAAW,SAAS,UAAY,SAASe,EAAI,CAEzC,IAAIC,EAAQ,KACRC,EAAY,GAEhB,SAASC,EAAKC,EAAM,CAChB,MAAO,CAACJ,EAAG,SAAS,SAAWA,EAAG,SAAS,QAAQ,QAAQI,CAAI,IAAM,EACzE,CAGA,SAASC,EAAUT,EAAM,CACrB,OAAIA,EAAK,WAAa,YACX,CAAC,CAACX,EAAW,WAAWW,EAAK,IAAI,GAAKO,EAAKP,EAAK,IAAI,EAE3DA,EAAK,WAAa,UAAoB,CAAC,CAACX,EAAW,SAAS,UAAYkB,EAAK,UAAU,EACvFP,EAAK,WAAa,UAAoB,CAAC,CAACX,EAAW,SAAS,UAAYkB,EAAK,UAAU,EAEpF,EACX,CAEA,SAASG,EAAKV,EAAMW,EAAY,CAC5B,MAAO,CAAC,CAACX,GAAQW,EAAW,QAAQX,EAAK,QAAQ,IAAM,IAAMS,EAAUT,CAAI,CAC/E,CAEA,SAASY,EAAWC,EAAM,CACtB,OAAIxB,EAAW,WAAWwB,CAAI,EAAY,YAEnC,CAAC,MAAO,SAAU,UAAW,OAAQ,SAAS,EAAE,QAAQA,CAAI,IAAM,GAAKA,EAAO,IACzF,CAEA,SAASC,EAAKC,EAAMF,EAAM,CACtB,IAAIG,EAAOZ,EAAG,SAASW,CAAI,EAE3Bd,EAAM,CAAE,QAAST,EAAS,SAAUoB,EAAWC,CAAI,EAAG,KAAMA,EAAM,KAAMG,CAAK,CAAC,EAC9EZ,EAAG,KAAK,aAAcA,EAAG,WAAWS,EAAME,EAAM,CAAE,OAAQ,MAAO,CAAC,CAAC,EAInE,IAAIE,EAAWC,EAAMd,EAAG,SAASW,CAAI,EAAG,UAAU,EAClD,GAAKE,EAEL,KAAIE,EAAWC,EAAIH,EAAM,GAAG,EACxBI,EAASJ,EAAM,WAAW,EAC1BE,GAAQA,EAAK,aAAa,QAAS,cAAc,EACrD,WAAW,UAAW,CACdG,EAAYL,EAAM,WAAW,EAC7BE,GAAQA,EAAK,aAAa,QAAS,YAAY,CACvD,EAAG,IAAI,EACX,CAEA,SAASI,EAAMC,EAAQb,EAAY,CAC/B,IAAIX,EAAOH,EAAK,EACXa,EAAKV,EAAMW,CAAU,GAE1BP,EAAG,MAAMqB,EAAMzB,EAAK,IAAI,EAAGA,EAAK,KAAM,CAAE,SAAUwB,EAAQ,OAAQ,OAAQ,CAAC,CAC/E,CAWA,SAASC,EAAMT,EAAM,CACjB,IAAID,EAAWW,EAAOV,CAAI,EACtBW,EAAU,CAAC,EACXC,EAAQ,SAASC,EAAI,CACrB,MAAO,CAAC,CAAC,SAAS,eAAeA,CAAE,GAAK,OAAO,KAAKF,CAAO,EAAE,KAAK,SAASG,EAAK,CAC5E,OAAOH,EAAQG,CAAG,IAAMD,CAC5B,CAAC,CACL,EAqBA,OAnBIE,EAAWhB,EAAM,MAAM,EAAE,QAAQ,SAASiB,EAAS,CACnD,IAAIH,EAAKG,EAAQ,GACjB,GAAK,SAAS,eAAeH,CAAE,EAE/B,KAAII,EAAYrC,EAAa,KAAKiC,CAAE,EAChCK,EAEJ,GAAID,EACA,GAAKC,EAAO9B,EAAG,YAAY6B,EAAU,CAAC,CAAC,QAAYL,EAAMM,CAAI,OAC1D,CAEH,QADIC,EAAI,EACDP,EAAMC,EAAK,IAAMM,CAAC,GAAKA,IAC9BD,EAAOL,EAAK,IAAMM,CACtB,CAEAR,EAAQE,CAAE,EAAIK,EACdF,EAAQ,GAAKE,EACjB,CAAC,EAEI,OAAO,KAAKP,CAAO,EAAE,QAEtBI,EAAWhB,EAAM,GAAG,EAAE,QAAQ,SAASiB,EAAS,CAChDrC,EAAW,QAAQ,SAASa,EAAM,CAC9B,IAAI4B,EAAQJ,EAAQ,aAAaxB,CAAI,EACrC,GAAI4B,IAAU,MACV,EAAA5B,IAAS,QAAU4B,EAAM,OAAO,CAAC,IAAM,KAE3C,KAAIC,EAAYD,EAAM,MAAM,OAAO,EAAE,IAAI,SAASE,EAAO,CACrD,OAAIX,EAAQW,CAAK,EAAYX,EAAQW,CAAK,EACtCA,EAAM,OAAO,CAAC,IAAM,KAAOX,EAAQW,EAAM,MAAM,CAAC,CAAC,EAAY,IAAMX,EAAQW,EAAM,MAAM,CAAC,CAAC,EAEtFA,CACX,CAAC,EAAE,KAAK,EAAE,EAEND,IAAcD,GAASJ,EAAQ,aAAaxB,EAAM6B,CAAS,EACnE,CAAC,CACL,CAAC,EAEMtB,CACX,CAGA,SAASwB,GAAU,CACf,IAAIvC,EAAOH,EAAK,EAEZ2C,EAAIpC,EAAG,OAAQ,WAAW,EAAE,QAAQ,SAASa,EAAM,CAC/CwB,EAAOxB,EAAMP,EAAKV,EAAMiB,EAAK,aAAa,eAAe,EAAE,MAAM,GAAG,CAAC,CAAC,CAC9E,CAAC,EAEDb,EAAG,aAAa,WAAW,EAAE,QAAQ,SAASsC,EAAQ,CAClD,IAAIC,EAAOC,EAAQ,SAASF,EAAO,aAAa,cAAc,EAAG,EAAE,CAAC,EAChED,EAAOC,EAAQhC,EAAKV,EAAM,CAAC2C,EAAK,IAAI,CAAC,CAAC,CAC9C,CAAC,EAEDtC,EAAQL,CACZ,CAEA,IAAI4C,EAAU,CACV,CAAE,KAAM,MAAO,SAAU,qBAAsB,EAC/C,CAAE,KAAM,UAAW,SAAU,yBAA0B,CAC3D,EAAE,IAAI,SAASD,EAAM,CACjB,OAAO,OAAO,OAAOA,EAAM,CACvB,UAAW,uBAGX,MAAO,MACP,OAAQ,QAGR,OAAQ,UAAW,CAAE,OAAOlB,EAAMpB,EAAM,IAAI,CAAG,CACnD,CAAC,CACL,CAAC,EAED,MAAO,CACH,YAAa,SAASwC,EAAQ9B,EAAMF,EAAM,CAClCD,EAAWC,CAAI,GACfT,EAAG,WAAWyC,EAAQzC,EAAG,EAAE,WAAW,EAAG,UAAW,aAAc,UAAW,CACzEU,EAAKC,EAAMF,CAAI,CACnB,CAAC,EAGL,IAAIF,EAAajB,EAAQmB,CAAI,EAC7B,GAAKF,EAEL,KAAIM,EAAOb,EAAG,WAAWyC,EAAQzC,EAAG,EAAE,YAAY,EAAG,WAAY,uBAAwB,UAAW,CAChGmB,EAAMR,EAAMJ,CAAU,CAC1B,CAAC,EACDM,EAAK,aAAa,gBAAiBN,EAAW,KAAK,GAAG,CAAC,EACnD8B,EAAOxB,EAAMP,EAAKb,EAAK,EAAGc,CAAU,CAAC,EAC7C,EAEA,QAASiC,EAET,OAAQ,UAAW,CACfL,EAAQ,EACHjC,IACD,SAAS,iBAAiBf,EAAQgD,CAAO,EACzCjC,EAAY,GAEpB,EAEA,SAAU,UAAW,CACjB,SAAS,oBAAoBf,EAAQgD,CAAO,EAC5CjC,EAAY,EAChB,EAGA,YAAa,UAAW,CAAC,CAC7B,CACJ",
6
- "names": ["GridEditor", "all", "root", "selector", "one", "selfAndAll", "found", "children", "node", "each", "child", "parse", "html", "template", "create", "nodes", "i", "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", "REFERENCES", "GENERATED_ID", "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", "target", "fresh", "create", "renamed", "taken", "id", "old", "selfAndAll", "element", "generated", "next", "n", "value", "rewritten", "token", "refresh", "all", "toggle", "button", "item", "TOOLBAR", "drawer"]
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": "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",
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", "target", "refresh", "all", "toggle", "button", "item", "TOOLBAR", "drawer"]
7
7
  }
package/docs/plugins.md CHANGED
@@ -137,6 +137,7 @@ element, and where there may be several, an array.
137
137
  | `ge.t(key, params)` | A string from the locale |
138
138
  | `ge.warn(message)` | A console warning, prefixed like the editor's own |
139
139
  | `ge.containerId(type)` | A generated id, stable across a reset, for Bootstrap's toggles |
140
+ | `ge.freshNode(html)` | The markup as a detached node, with new ids for those the page already has and the attributes that point at them - `data-bs-target`, `data-bs-parent`, `href="#…"`, `aria-controls`, `aria-labelledby`, `aria-describedby`, `for`, `data-ge-popup-id`, `data-ge-popup-target` - following. An id grid-editor generated gets a new one from `containerId`, any other a `-2`, `-3`… suffix. What the clipboard pastes, and what a host inserting saved markup should hand `ge.place`. Place each copy before making the next: it checks the page as it is |
140
141
  | `ge.defaultRegion()` | A row with one full width column: what an empty pane starts as |
141
142
  | `ge.createTool(drawer, title, className, iconClass, handlers)` | A tool in a drawer, which it returns. `handlers` is a click handler or `{ eventName: handler }`; each gets the DOM event, with `this` the tool |
142
143
  | `ge.createMoveTool(drawer)` | The drag handle, unless `drag_handle` says the whole drawer is one; the tool, or null |
@@ -376,7 +377,10 @@ rather than remembering anything about it.
376
377
  preview buttons, always as an icon - `bi bi-plus` when it has none.
377
378
  `label`, a string or a function called whenever the toolbar is built, stands
378
379
  in for `labelKey` when the label takes parameters; `className` goes on the
379
- button. With `inColumn: true` what it makes belongs in a column, as a
380
+ button. `hidden`, a boolean or a function called whenever the toolbar is
381
+ built, builds the button hidden: a plugin that shows and hides its buttons
382
+ gives it, so a toolbar built again - `ge.refreshToolbar()`, `setLocale` -
383
+ shows them as they are. With `inColumn: true` what it makes belongs in a column, as a
380
384
  container or a text does: a click puts it in a row and a column of its own
381
385
  at the end of the canvas, and a drop anywhere but in a column does the
382
386
  same where it was dropped.
package/package.json CHANGED
@@ -32,7 +32,7 @@
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "version": "8.9.0",
35
+ "version": "8.10.1",
36
36
  "main": "dist/grideditor.js",
37
37
  "module": "dist/grideditor.esm.js",
38
38
  "types": "dist/grideditor.d.ts",
@@ -48,6 +48,17 @@ var METHODS = {
48
48
  */
49
49
  var PLACEMENTS = ['appendTo', 'prependTo', 'insertAfter', 'insertBefore'];
50
50
 
51
+ /**
52
+ * The attributes that point at an id, as the id itself or as a #selector.
53
+ * A copy that has to take new ids (see freshNode) takes them here too, so
54
+ * its tabs, its accordion and its popups still point at their own panes.
55
+ */
56
+ var REFERENCES = ['data-bs-target', 'data-bs-parent', 'href', 'aria-controls', 'aria-labelledby',
57
+ 'aria-describedby', 'for', 'data-ge-popup-id', 'data-ge-popup-target'];
58
+
59
+ /** An id grid-editor generated: ge-{type}-{counter}-{random}. */
60
+ var GENERATED_ID = /^ge-([a-z][a-z-]*?)-\d+-[a-z0-9]+$/;
61
+
51
62
  /**
52
63
  * Bootstrap 5's breakpoints, smallest first, which is the order the cascade
53
64
  * runs in: a size written for a tier applies to every wider tier that does not
@@ -1594,6 +1605,9 @@ function build(instance, baseElem, optionsOrMethod) {
1594
1605
  var button = labelButton(dom.element('a', { 'class': 'btn btn-sm ' + colour + ' ge-add-container ge-add-feature' }), label, iconClass);
1595
1606
 
1596
1607
  dom.addClass(button, item.className || '');
1608
+ // Hidden as it is now: a plugin that shows and hides its buttons
1609
+ // only hears of what changes, not of the toolbar being built again
1610
+ if (typeof item.hidden === 'function' ? item.hidden() : item.hidden) { dom.hide(button); }
1597
1611
  dom.attr(button, {
1598
1612
  'data-ge-toolbar': 'feature',
1599
1613
  'data-ge-feature': name,
@@ -2540,6 +2554,7 @@ function build(instance, baseElem, optionsOrMethod) {
2540
2554
  t: t,
2541
2555
  warn: warn,
2542
2556
  containerId: containerId,
2557
+ freshNode: freshNode,
2543
2558
  defaultRegion: defaultRegion,
2544
2559
  createTool: createTool,
2545
2560
  createMoveTool: createMoveTool,
@@ -3266,6 +3281,66 @@ function build(instance, baseElem, optionsOrMethod) {
3266
3281
  Math.random().toString(36).slice(2, 6);
3267
3282
  }
3268
3283
 
3284
+ /**
3285
+ * Markup as a node, with new ids where the page already has them: two
3286
+ * copies of a tab strip pointing at one set of panes is a tab strip that
3287
+ * opens the other copy's tabs. An id the page does not have is kept, so
3288
+ * markup that goes into another page changes nothing. The clipboard
3289
+ * pastes through it, and a host inserting saved markup should too.
3290
+ *
3291
+ * Parsed the way innerHTML parses, so a <script> in the markup is markup,
3292
+ * placed like the rest, and does not run in the editor.
3293
+ */
3294
+ function freshNode(html) {
3295
+ var node = dom.create(html);
3296
+ var renamed = {};
3297
+ var taken = function(id) {
3298
+ return !!document.getElementById(id) || Object.keys(renamed).some(function(old) {
3299
+ return renamed[old] === id;
3300
+ });
3301
+ };
3302
+
3303
+ dom.selfAndAll(node, '[id]').forEach(function(element) {
3304
+ var id = element.id;
3305
+ if (!document.getElementById(id)) { return; }
3306
+
3307
+ var generated = GENERATED_ID.exec(id);
3308
+ var next;
3309
+
3310
+ if (generated) {
3311
+ do { next = containerId(generated[1]); } while (taken(next));
3312
+ } else {
3313
+ var n = 2;
3314
+ while (taken(id + '-' + n)) { n++; }
3315
+ next = id + '-' + n;
3316
+ }
3317
+
3318
+ renamed[id] = next;
3319
+ element.id = next;
3320
+ });
3321
+
3322
+ if (!Object.keys(renamed).length) { return node; }
3323
+
3324
+ dom.selfAndAll(node, '*').forEach(function(element) {
3325
+ REFERENCES.forEach(function(name) {
3326
+ var value = element.getAttribute(name);
3327
+ if (value === null) { return; }
3328
+ if (name === 'href' && value.charAt(0) !== '#') { return; }
3329
+
3330
+ var rewritten = value.split(/(\s+)/).map(function(token) {
3331
+ if (renamed[token]) { return renamed[token]; }
3332
+ if (token.charAt(0) === '#' && renamed[token.slice(1)]) { return '#' + renamed[token.slice(1)]; }
3333
+
3334
+ return token;
3335
+ }).join('');
3336
+
3337
+ if (rewritten !== value) { element.setAttribute(name, rewritten); }
3338
+ });
3339
+ });
3340
+
3341
+ return node;
3342
+ }
3343
+
3269
3344
  /** A pane's starting content: one full width column, ready to edit. */
3270
3345
  function defaultRegion() {
3271
3346
  var row = createRow();