PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
2.0.14 2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / assets / js / template-collections.js

template-collections.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.14, at assets/js/template-collections.js

142 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Immutable editing of converted carousel collections. Existing templates need no migration. */
2 (function (scope) {
3 'use strict';
4 function copy(value) { return JSON.parse(JSON.stringify(value)); }
5 function classes(node) { return String((node.attrs || {}).class || '').split(/\s+/); }
6 function references(node) {
7 var found = [];
8 function scan(value) {
9 if (!value || typeof value !== 'object') return;
10 if (typeof value.field === 'string' && found.indexOf(value.field) < 0) found.push(value.field);
11 Object.keys(value).forEach(function (key) { if (key !== 'field') scan(value[key]); });
12 }
13 scan(node); return found;
14 }
15 function label(field, context) {
16 if (field.type === 'image') return /avatar|author|testimonial/.test(context) ? 'Portrait' : 'Image';
17 if (field.type === 'url') return 'Link URL';
18 var tokens = context.split(/\s+/).reverse();
19 for (var i = 0; i < tokens.length; i++) {
20 var token = tokens[i];
21 if (/extra-label|timeline-label/.test(token)) return 'Date / label';
22 if (/subtitle|position|job/.test(token)) return 'Subtitle';
23 if (/testimonial.*(?:title|name)|author-name|person-name/.test(token)) return 'Name';
24 if (/testimonial.*description|quote/.test(token)) return 'Quote';
25 if (/description|excerpt/.test(token)) return 'Description';
26 if (/title|heading/.test(token)) return 'Title';
27 if (/caption/.test(token)) return 'Caption';
28 }
29 return field.type === 'number' ? 'Value' : field.type === 'boolean' ? 'Enabled' : 'Text';
30 }
31 function fieldsFor(node, fields) {
32 var map = {}, contexts = {};
33 (fields || []).forEach(function (f) { map[f.id] = f; });
34 function walk(n, context) {
35 context = context + ' ' + classes(n).join(' ');
36 if (n.field) contexts[n.field] = context;
37 references(n.attrs || {}).forEach(function (id) { if (!contexts[id]) contexts[id] = context; });
38 (n.children || []).forEach(function (c) { walk(c, context); });
39 }
40 walk(node, '');
41 return references(node).map(function (id) {
42 return map[id] ? Object.assign({}, map[id], { label: label(map[id], contexts[id] || '') }) : null;
43 }).filter(Boolean);
44 }
45 function groups(attributes) {
46 var result = [];
47 function walk(nodes, path) {
48 (nodes || []).forEach(function (node, i) {
49 var here = path.concat(i);
50 if (classes(node).indexOf('swiper-wrapper') >= 0) {
51 var items = [];
52 (node.children || []).forEach(function (child, at) {
53 if (classes(child).indexOf('swiper-slide') < 0 || classes(child).indexOf('swiper-slide-duplicate') >= 0) return;
54 var fields = fieldsFor(child, attributes.fields);
55 var title = fields.find(function (f) { return /Title|Name|Date/.test(f.label) && f.value; });
56 items.push({at: at, node: child, fields: fields,
57 key: (child.attrs || {})['data-bkbg-item-id'] || references(child).join('-') || String(at),
58 title: title ? String(title.value).replace(/<[^>]*>/g, '').replace(/&[^;]+;/g, ' ').trim().slice(0,60) : ''});
59 });
60 result.push({path: here, items: items});
61 } else walk(node.children, here);
62 });
63 }
64 walk(attributes.tree, []); return result;
65 }
66 function atPath(tree, path) {
67 var node, nodes = tree;
68 path.forEach(function (index) { node = nodes[index]; nodes = node.children; });
69 return node;
70 }
71 function change(attributes, path, operation, index, token) {
72 var tree = copy(attributes.tree || []), fields = copy(attributes.fields || []);
73 var track = atPath(tree, path), items = (track.children || []).filter(function (n) {
74 return classes(n).indexOf('swiper-slide') >= 0 && classes(n).indexOf('swiper-slide-duplicate') < 0;
75 });
76 var templates = copy(!Array.isArray(attributes.collectionTemplates) && attributes.collectionTemplates || {}), templateKey = path.join('-');
77 var template = templates[templateKey];
78 var item = items[index] || (operation === 'add' && template ? template.node : null);
79 if (!item) return null;
80 var at = track.children.indexOf(item);
81 if (operation === 'remove') {
82 if (items.length === 1) templates[templateKey] = {node:copy(item),fields:fields.filter(function(f){return references(item).indexOf(f.id)>=0;})};
83 var removed = references(item); track.children.splice(at,1);
84 var retained = references(tree);
85 fields = fields.filter(function (f) { return removed.indexOf(f.id) < 0 || retained.indexOf(f.id) >= 0; });
86 } else if (operation === 'up' || operation === 'down') {
87 var target = index + (operation === 'up' ? -1 : 1);
88 if (!items[target]) return null;
89 var otherAt = track.children.indexOf(items[target]);
90 track.children[at] = items[target]; track.children[otherAt] = item;
91 } else if (operation === 'add' || operation === 'duplicate') {
92 token = token || Date.now().toString(36) + Math.random().toString(36).slice(2,8);
93 var clone = copy(item), ids = {}, refs = references(item);
94 var existing = new Set(fields.map(function (f) { return f.id; }));
95 refs.forEach(function (id, i) {
96 var original = fields.find(function (f) { return f.id === id; }) || (template && template.fields.find(function(f){return f.id === id;}));
97 if (!original) return;
98 var next = 'item-' + token + '-' + i;
99 while (existing.has(next)) next += '-copy';
100 existing.add(next); ids[id] = next;
101 var field = copy(original); field.id = next;
102 if (operation === 'add') {
103 if (field.type === 'image') { field.image = Object.assign({}, field.image, {src:'',alt:'',id:0}); delete field.image.srcset; delete field.image.sizes; field.value = ''; }
104 else if (field.type === 'url') field.value = '';
105 else if (field.type === 'text' || field.type === 'richtext') field.value = '';
106 }
107 fields.push(field);
108 });
109 var elementIds = {};
110 function scanIds(n) {
111 if (typeof (n.attrs || {}).id === 'string') elementIds[n.attrs.id] = n.attrs.id + '-item-' + token;
112 (n.children || []).forEach(scanIds);
113 }
114 scanIds(clone);
115 function remap(value, name) {
116 if (value && typeof value === 'object') {
117 if (ids[value.field]) value.field = ids[value.field];
118 Object.keys(value).forEach(function (k) { value[k] = remap(value[k], k); });
119 } else if (typeof value === 'string') {
120 if (name === 'id' && elementIds[value]) return elementIds[value];
121 if (['href','xlink:href','fill','stroke','clip-path','mask','for','aria-controls','aria-labelledby','aria-describedby'].indexOf(name) >= 0) {
122 Object.keys(elementIds).forEach(function (id) {
123 value = value.replace('url(#'+id+')','url(#'+elementIds[id]+')');
124 if (value === '#'+id) value = '#'+elementIds[id];
125 if (['for','aria-controls','aria-labelledby','aria-describedby'].indexOf(name)>=0) value=value.split(/\s+/).map(function(v){return v===id?elementIds[id]:v;}).join(' ');
126 });
127 }
128 }
129 return value;
130 }
131 remap(clone,''); clone.attrs = clone.attrs || {}; clone.attrs['data-bkbg-item-id'] = token;
132 // Preserve source style classes, while stripping runtime state from copied items.
133 clone.attrs.class = classes(clone).filter(function (c) { return !/^swiper-slide-(active|prev|next|duplicate)$/.test(c); }).join(' ');
134 track.children.splice(operation === 'add' ? track.children.length : at+1,0,clone);
135 } else return null;
136 return {tree:tree,fields:fields,collectionTemplates:templates};
137 }
138 var api = {groups:groups,change:change,references:references};
139 if (typeof module !== 'undefined' && module.exports) module.exports = api;
140 else scope.bkbgTemplateCollections = api;
141 })(typeof window !== 'undefined' ? window : this);
142