PluginProbe
Sharing Image / 2.0.12
Sharing Image v2.0.12
3.10 trunk 2.0 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0 3.1 3.2 3.3 All 29 releases
sharing-image / assets / scripts / widget.js

widget.js in Sharing Image 2.0.12, at assets/scripts/widget.js

3,600 lines 88.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (() => { // webpackBootstrap
2 /******/ "use strict";
3 var __webpack_exports__ = {};
4
5 ;// CONCATENATED MODULE: ./src/scripts/builders/element.js
6 /**
7 * Helper to create new DOM element.
8 *
9 * @param {string} tag Element tagname.
10 * @param {Object} args List of element options.
11 */
12 function buildElement(tag, args) {
13 const element = document.createElement(tag); // Set class list
14
15 if (args.hasOwnProperty('classes')) {
16 args.classes.forEach(cl => {
17 element.classList.add(cl);
18 });
19 } // Set textContent
20
21
22 if (args.hasOwnProperty('text')) {
23 element.textContent = args.text;
24 } // Set innerHTML
25
26
27 if (args.hasOwnProperty('html')) {
28 element.innerHTML = args.html;
29 } // Set attributes
30
31
32 if (args.hasOwnProperty('attributes')) {
33 for (const key in args.attributes) {
34 const value = args.attributes[key];
35
36 if (undefined === value) {
37 continue;
38 }
39
40 element.setAttribute(key, value);
41 }
42 } // Set data attributes
43
44
45 if (args.hasOwnProperty('dataset')) {
46 for (const key in args.dataset) {
47 element.setAttribute('data-' + key, args.dataset[key]);
48 }
49 } // Append child
50
51
52 if (args.hasOwnProperty('append')) {
53 args.append.appendChild(element);
54 } // Prepend child
55
56
57 if (args.hasOwnProperty('prepend')) {
58 args.prepend.insertBefore(element, args.prepend.firstChild);
59 }
60
61 return element;
62 }
63
64 /* harmony default export */ const builders_element = (buildElement);
65 ;// CONCATENATED MODULE: ./src/scripts/builders/input.js
66
67
68 /**
69 * Helper to create input field.
70 *
71 * @param {Object} args List of control options.
72 * @param {HTMLElement} parent Parent HTML element to append this field.
73 */
74
75 function buildInput(args, parent) {
76 const field = builders_element('div', {
77 classes: args.classes || [],
78 append: parent
79 });
80
81 if (args.hasOwnProperty('label')) {
82 const label = builders_element('h4', {
83 text: args.label
84 });
85
86 if (null !== args.label) {
87 field.appendChild(label);
88 }
89 }
90
91 const input = builders_element('input', {
92 attributes: {
93 type: 'text'
94 },
95 dataset: args.dataset || {},
96 append: field
97 }); // Set attributes.
98
99 if (args.hasOwnProperty('attributes')) {
100 for (const key in args.attributes) {
101 const value = args.attributes[key];
102
103 if (undefined === value) {
104 continue;
105 }
106
107 input.setAttribute(key, value);
108 }
109 }
110
111 if ('range' === input.type) {
112 const counter = builders.element('em', {
113 text: input.value,
114 append: field
115 });
116 input.addEventListener('change', () => {
117 counter.textContent = input.value;
118 });
119 input.addEventListener('input', () => {
120 counter.textContent = input.value;
121 });
122 }
123
124 return input;
125 }
126
127 /* harmony default export */ const input = (buildInput);
128 ;// CONCATENATED MODULE: ./src/scripts/builders/checkbox.js
129
130 /**
131 * Helper to create radio field.
132 *
133 * @param {Object} args List of control options.
134 * @param {HTMLElement} parent Parent HTML element to append this field.
135 */
136
137 function buildCheckbox(args, parent) {
138 const field = builders_element('label', {
139 classes: args.classes || [],
140 append: parent
141 });
142 const checkbox = builders_element('input', {
143 attributes: {
144 type: 'checkbox'
145 },
146 dataset: args.dataset || {},
147 append: field
148 }); // Set attributes
149
150 if (args.hasOwnProperty('attributes')) {
151 for (const key in args.attributes) {
152 const value = args.attributes[key];
153
154 if (undefined === value) {
155 continue;
156 }
157
158 checkbox.setAttribute(key, value);
159 }
160 }
161
162 if (args.hasOwnProperty('checked')) {
163 const checked = args.checked;
164
165 if (checked && checked === checkbox.value) {
166 checkbox.setAttribute('checked', 'checked');
167 }
168 }
169
170 if (args.hasOwnProperty('label')) {
171 const label = builders_element('span', {
172 text: args.label
173 });
174
175 if (null !== args.label) {
176 field.appendChild(label);
177 }
178 }
179
180 return checkbox;
181 }
182
183 /* harmony default export */ const builders_checkbox = (buildCheckbox);
184 ;// CONCATENATED MODULE: ./src/scripts/builders/radio.js
185
186 /**
187 * Helper to create radio field.
188 *
189 * @param {Object} args List of control options.
190 * @param {HTMLElement} parent Parent HTML element to append this field.
191 */
192
193 function buildRadio(args, parent) {
194 const field = builders_element('label', {
195 classes: args.classes || [],
196 append: parent
197 });
198 const radio = builders_element('input', {
199 attributes: {
200 type: 'radio'
201 },
202 dataset: args.dataset || {},
203 append: field
204 }); // Set attributes
205
206 if (args.hasOwnProperty('attributes')) {
207 for (const key in args.attributes) {
208 const value = args.attributes[key];
209
210 if (undefined === value) {
211 continue;
212 }
213
214 radio.setAttribute(key, value);
215 }
216 }
217
218 if (args.hasOwnProperty('checked')) {
219 const checked = args.checked;
220
221 if (checked && checked === radio.value) {
222 radio.setAttribute('checked', 'checked');
223 }
224 }
225
226 if (args.hasOwnProperty('label')) {
227 const label = builders_element('span', {
228 text: args.label
229 });
230
231 if (null !== args.label) {
232 field.appendChild(label);
233 }
234 }
235
236 if (args.hasOwnProperty('help')) {
237 builders_element('small', {
238 text: args.help,
239 append: field
240 });
241 }
242
243 return radio;
244 }
245
246 /* harmony default export */ const builders_radio = (buildRadio);
247 ;// CONCATENATED MODULE: ./src/scripts/builders/select.js
248
249 /**
250 * Helper to create select field.
251 *
252 * @param {Object} args List of control options.
253 * @param {HTMLElement} parent Parent HTML element to append this field.
254 */
255
256 function buildSelect(args, parent) {
257 const field = builders_element('div', {
258 classes: args.classes || [],
259 append: parent
260 });
261
262 if (args.hasOwnProperty('label')) {
263 const label = builders_element('h4', {
264 text: args.label
265 });
266
267 if (null !== args.label) {
268 field.appendChild(label);
269 }
270 }
271
272 const select = builders_element('select', {
273 dataset: args.dataset || {},
274 append: field
275 }); // Set attributes
276
277 if (args.hasOwnProperty('attributes')) {
278 for (const key in args.attributes) {
279 const value = args.attributes[key];
280
281 if (undefined === value) {
282 continue;
283 }
284
285 select.setAttribute(key, value);
286 }
287 }
288
289 const options = args.options || {};
290
291 for (const key in options) {
292 const option = builders_element('option', {
293 text: options[key],
294 attributes: {
295 value: key
296 },
297 append: select
298 });
299
300 if (args.hasOwnProperty('selected')) {
301 const selected = args.selected;
302
303 if (selected && selected === option.value) {
304 option.setAttribute('selected', 'selected');
305 }
306 }
307 }
308
309 return select;
310 }
311
312 /* harmony default export */ const builders_select = (buildSelect);
313 ;// CONCATENATED MODULE: ./src/scripts/builders/textarea.js
314
315 /**
316 * Helper to create input field.
317 *
318 * @param {Object} args List of control options.
319 * @param {HTMLElement} parent Parent HTML element to append this field.
320 */
321
322 function buildTextarea(args, parent) {
323 const field = builders_element('div', {
324 classes: args.classes || [],
325 append: parent
326 });
327
328 if (args.hasOwnProperty('label')) {
329 const label = builders_element('h4', {
330 text: args.label
331 });
332
333 if (null !== args.label) {
334 field.appendChild(label);
335 }
336 }
337
338 const textarea = builders_element('textarea', {
339 dataset: args.dataset || {},
340 append: field
341 }); // Set attributes
342
343 if (args.hasOwnProperty('attributes')) {
344 for (const key in args.attributes) {
345 const value = args.attributes[key];
346
347 if (undefined === value) {
348 continue;
349 }
350
351 textarea.setAttribute(key, value);
352 }
353 } // Set content
354
355
356 if (args.hasOwnProperty('content')) {
357 const content = args.content;
358
359 if (undefined !== content) {
360 textarea.innerHTML = content;
361 }
362 }
363
364 return textarea;
365 }
366
367 /* harmony default export */ const builders_textarea = (buildTextarea);
368 ;// CONCATENATED MODULE: ./src/scripts/builders/control.js
369
370
371
372
373
374
375 /**
376 * Helper to create control.
377 *
378 * @param {Object} args List of control options.
379 */
380
381 function buildControl(args) {
382 const control = builders_element('div', {
383 classes: args.classes || []
384 });
385
386 if (args.hasOwnProperty('append')) {
387 args.append.appendChild(control);
388 }
389
390 if (args.hasOwnProperty('prepend')) {
391 args.prepend.insertBefore(control, args.prepend.firstChild);
392 }
393
394 if (args.hasOwnProperty('label')) {
395 builders_element('h3', {
396 text: args.label,
397 append: control
398 });
399 }
400
401 if (args.hasOwnProperty('description')) {
402 builders_element('p', {
403 text: args.description,
404 append: control
405 });
406 }
407
408 if (args.hasOwnProperty('fields')) {
409 args.fields.forEach(field => {
410 switch (field.group) {
411 case 'input':
412 input(field, control);
413 break;
414
415 case 'textarea':
416 builders_textarea(field, control);
417 break;
418
419 case 'radio':
420 builders_radio(field, control);
421 break;
422
423 case 'select':
424 builders_select(field, control);
425 break;
426
427 case 'checkbox':
428 builders_checkbox(field, control);
429 break;
430 }
431 });
432 }
433
434 if (args.hasOwnProperty('help')) {
435 builders_element('small', {
436 text: args.help,
437 append: control
438 });
439 }
440
441 return control;
442 }
443
444 /* harmony default export */ const control = (buildControl);
445 ;// CONCATENATED MODULE: ./src/scripts/builders/layer.js
446
447 /**
448 * Helper to create layer.
449 *
450 * @param {Object} args List of layer options.
451 */
452
453 function buildLayer(args) {
454 const layer = builders_element('div', {
455 classes: args.classes || []
456 });
457
458 if (args.hasOwnProperty('append')) {
459 args.append.appendChild(layer);
460 }
461
462 if (args.hasOwnProperty('prepend')) {
463 args.prepend.insertBefore(layer, args.prepend.firstChild);
464 }
465
466 if (!args.hasOwnProperty('label')) {
467 args.label = '';
468 }
469
470 builders_element('h2', {
471 text: args.label,
472 append: layer
473 });
474
475 if (args.hasOwnProperty('description')) {
476 builders_element('h5', {
477 text: args.description,
478 append: layer
479 });
480 }
481
482 return layer;
483 }
484
485 /* harmony default export */ const builders_layer = (buildLayer);
486 ;// CONCATENATED MODULE: ./src/scripts/helpers/param.js
487 /**
488 * Get current location search parameter.
489 *
490 * @param {string} key URL parameter key.
491 */
492 function getSearchParam(key) {
493 const params = new URL(document.location.href);
494 return params.searchParams.get(key);
495 }
496
497 /* harmony default export */ const param = (getSearchParam);
498 ;// CONCATENATED MODULE: ./src/scripts/helpers/attachment.js
499 /**
500 * Upload media frame.
501 *
502 * @param {string} header Frame header text.
503 * @param {Function} callback Callback function.
504 */
505 function uploadMedia(header, callback) {
506 const frame = wp.media({
507 title: header,
508 multiple: false
509 });
510 frame.on('select', () => {
511 const selection = frame.state().get('selection').first().toJSON();
512
513 if (selection.id) {
514 callback(selection.id);
515 }
516 });
517 frame.open();
518 }
519
520 /* harmony default export */ const attachment = (uploadMedia);
521 ;// CONCATENATED MODULE: ./src/scripts/helpers/defaults.js
522 /**
523 * Append empty default properties to object if not exist.
524 *
525 * @param {Object} object Source object.
526 * @param {Array} defaults Required defaults properties.
527 */
528 function intersectDefaults(object, defaults) {
529 defaults.forEach(item => {
530 if (undefined === object[item]) {
531 object[item] = {};
532 }
533 });
534 return object;
535 }
536
537 /* harmony default export */ const defaults = (intersectDefaults);
538 ;// CONCATENATED MODULE: ./src/scripts/helpers/index.js
539
540
541
542 const Helper = {
543 param: param,
544 attachment: attachment,
545 defaults: defaults
546 };
547 /* harmony default export */ const helpers = (Helper);
548 ;// CONCATENATED MODULE: ./src/scripts/builders/media.js
549
550
551
552 /**
553 * Helper to create media block.
554 *
555 * @param {Object} args List of media options.
556 */
557
558 function buildMedia(args) {
559 const media = control({
560 classes: args.classes || []
561 });
562
563 if (args.hasOwnProperty('append')) {
564 args.append.appendChild(media);
565 }
566
567 if (args.hasOwnProperty('prepend')) {
568 args.prepend.insertBefore(media, args.prepend.firstChild);
569 } // Labels are required.
570
571
572 args.labels = args.labels || {};
573 const attachment = builders_element('input', {
574 attributes: {
575 type: 'hidden',
576 name: args.name
577 },
578 append: media
579 });
580 const upload = builders_element('button', {
581 classes: ['button'],
582 text: args.labels.button,
583 attributes: {
584 type: 'button'
585 },
586 append: media
587 });
588 const details = builders_element('a', {
589 classes: ['hidden'],
590 text: args.labels.details,
591 attributes: {
592 target: '_blank'
593 }
594 });
595
596 if (args.hasOwnProperty('link')) {
597 media.appendChild(details);
598 } // Helper function to update attachment value.
599
600
601 const setAttachment = id => {
602 attachment.setAttribute('value', id);
603 attachment.dispatchEvent(new Event('change', {
604 bubbles: true
605 }));
606 let link = null;
607
608 if (args.hasOwnProperty('link')) {
609 link = new URL(args.link);
610 link.searchParams.set('item', id);
611 details.setAttribute('href', link.href);
612 }
613
614 if (args.remove) {
615 upload.textContent = args.labels.remove;
616 }
617
618 details.classList.remove('hidden');
619 }; // Helper function to remove attachment value.
620
621
622 const removeAttachment = () => {
623 attachment.setAttribute('value', '');
624 attachment.dispatchEvent(new Event('change', {
625 bubbles: true
626 })); // Set default button title.
627
628 upload.textContent = args.labels.button;
629 details.classList.add('hidden');
630 }; // Update fields if this layer has attachment.
631
632
633 if (args.value) {
634 setAttachment(args.value);
635 }
636
637 upload.addEventListener('click', () => {
638 if (args.remove && attachment.value) {
639 return removeAttachment();
640 }
641
642 helpers.attachment(args.labels.heading, id => {
643 setAttachment(id);
644 });
645 });
646 return media;
647 }
648
649 /* harmony default export */ const media = (buildMedia);
650 ;// CONCATENATED MODULE: ./src/scripts/builders/index.js
651
652
653
654
655
656
657
658
659
660 const Build = {
661 element: builders_element,
662 control: control,
663 layer: builders_layer,
664 checkbox: builders_checkbox,
665 media: media,
666 input: input,
667 textarea: builders_textarea,
668 radio: builders_radio,
669 select: builders_select
670 };
671 /* harmony default export */ const builders = (Build);
672 ;// CONCATENATED MODULE: ./src/scripts/sections/catalog.js
673
674 const {
675 __
676 } = wp.i18n; // Store global scriot object for settings page.
677
678 let params = null;
679 /**
680 * Create template card in catalog.
681 *
682 * @param {HTMLElement} catalog Catalog HTML element.
683 * @param {number} index Current card index.
684 * @param {Object} option List of template options.
685 */
686
687 function createCard(catalog, index, option) {
688 const card = builders.element('div', {
689 classes: ['sharing-image-catalog-card'],
690 append: catalog
691 });
692 const preview = builders.element('figure', {
693 classes: ['sharing-image-catalog-preview'],
694 append: card
695 });
696
697 if (option.preview) {
698 builders.element('img', {
699 attributes: {
700 src: option.preview,
701 alt: ''
702 },
703 append: preview
704 });
705 }
706
707 const footer = builders.element('footer', {
708 classes: ['sharing-image-catalog-footer'],
709 append: card
710 });
711 builders.element('h2', {
712 text: option.title || __('Untitled', 'sharing-image'),
713 append: footer
714 });
715 const link = new URL(document.location.href);
716 link.searchParams.set('template', index);
717 builders.element('a', {
718 classes: ['button'],
719 text: __('Edit template', 'sharing-image'),
720 attributes: {
721 href: link.href
722 },
723 append: footer
724 });
725 }
726 /**
727 * Create new template button in catalog.
728 *
729 * @param {HTMLElement} catalog Catalog HTML element.
730 * @param {number} index New card index.
731 */
732
733
734 function createNewButton(catalog, index) {
735 const link = new URL(document.location.href);
736 link.searchParams.set('template', index);
737 const button = builders.element('a', {
738 classes: ['sharing-image-catalog-new'],
739 attributes: {
740 href: link.href
741 },
742 append: catalog
743 });
744 const title = builders.element('h2', {
745 append: button
746 });
747 builders.element('strong', {
748 text: __('Add new template', 'sharing-image'),
749 append: title
750 }); // Restrict new template creation for not Premium users.
751
752 if (params.templates.length === 0) {
753 return;
754 }
755
756 const license = params.license || {};
757
758 if (license.premium || license.develop) {
759 return;
760 }
761
762 builders.element('span', {
763 text: __('(Availible for Premium only)', 'sharing-image'),
764 append: title
765 });
766
767 if (params.links.premium) {
768 button.href = params.links.premium;
769 }
770 }
771 /**
772 * Create templates catalog from options.
773 *
774 * @param {HTMLElement} content Settings content element.
775 * @param {Object} settings Global settings field.
776 */
777
778
779 function createCatalog(content, settings) {
780 params = settings;
781 const catalog = builders.element('div', {
782 classes: ['sharing-image-catalog'],
783 append: content
784 });
785 let index = 1;
786 settings.templates.forEach(template => {
787 createCard(catalog, index++, template);
788 });
789 createNewButton(catalog, index);
790 }
791
792 /* harmony default export */ const catalog = (createCatalog);
793 ;// CONCATENATED MODULE: ./src/scripts/sections/editor.js
794 /**
795 * Editor settings.
796 */
797
798 /* global ajaxurl:true */
799
800 const {
801 __: editor_
802 } = wp.i18n; // Store global scriot object for settings page.
803
804 let editor_params = null; // Preview element.
805
806 let preview = null; // Root editor element.
807
808 let editor = null;
809 /**
810 * Show template warning message.
811 *
812 * @param {string} message Warning message.
813 */
814
815 function showTemplateError(message) {
816 const viewport = preview.parentNode; // Try to find warning element.
817
818 const warning = viewport.querySelector('.sharing-image-editor-warning');
819
820 if (null === warning) {
821 return;
822 }
823
824 warning.classList.add('warning-visible');
825 warning.textContent = message || editor_('Unknown generation error', 'sharing-image');
826 }
827 /**
828 * Remove warning message block.
829 */
830
831
832 function hideTemplateError() {
833 const viewport = preview.parentNode; // Try to find warning element.
834
835 const warning = viewport.querySelector('.sharing-image-editor-warning');
836
837 if (null === warning) {
838 return;
839 }
840
841 warning.classList.remove('warning-visible');
842 }
843 /**
844 * Geneate template using editor data.
845 */
846
847
848 function generateTemplate() {
849 preview.classList.add('preview-loader');
850 const request = new XMLHttpRequest();
851 request.open('POST', ajaxurl);
852 request.responseType = 'blob'; // Create data bundle using form data.
853
854 const bundle = new window.FormData(editor);
855 bundle.set('action', 'sharing_image_show');
856 hideTemplateError(); // Set blob for success response.
857
858 request.addEventListener('readystatechange', () => {
859 if (request.readyState === 2) {
860 request.responseType = 'json';
861
862 if (request.status === 200) {
863 request.responseType = 'blob';
864 }
865 }
866 });
867 request.addEventListener('load', () => {
868 const response = request.response || {}; // Hide preview loader on request complete.
869
870 preview.classList.remove('preview-blank', 'preview-loader');
871
872 if (200 !== request.status) {
873 return showTemplateError(response.data);
874 }
875
876 let image = preview.querySelector('img');
877
878 if (null === image) {
879 image = builders.element('img', {
880 append: preview
881 });
882 } // Set new blob image source.
883
884
885 image.src = window.URL.createObjectURL(response);
886 });
887 request.addEventListener('error', () => {
888 showTemplateError(); // Hide preview loader on request complete.
889
890 preview.classList.remove('preview-blank', 'preview-loader');
891 });
892 request.send(bundle);
893 }
894 /**
895 * Save template while editor submiting.
896 */
897
898
899 function saveTemplate() {
900 const request = new XMLHttpRequest();
901 request.open('POST', ajaxurl);
902 request.responseType = 'json';
903 preview.classList.add('preview-loader'); // Create data bundle using editor data.
904
905 const bundle = new window.FormData(editor);
906 bundle.set('action', 'sharing_image_save');
907 request.addEventListener('load', () => {
908 const response = request.response || {};
909
910 if (!response.data) {
911 return showTemplateError();
912 }
913
914 if (!response.success) {
915 // Hide preview loader on request complete.
916 preview.classList.remove('preview-loader');
917 return showTemplateError(response.data);
918 }
919
920 const input = preview.querySelector('input');
921
922 if (null !== input) {
923 input.value = response.data;
924 }
925
926 editor.submit();
927 });
928 request.addEventListener('error', () => {
929 // Hide preview loader on request complete.
930 preview.classList.remove('preview-loader');
931 showTemplateError();
932 });
933 request.send(bundle);
934 }
935 /**
936 * Update form fields name attributes for layers
937 *
938 * @param {HTMLElement} designer Layouts designer element.
939 */
940
941
942 function reorderLayers(designer) {
943 const layers = designer.children;
944
945 for (let index = 0; index < layers.length; index++) {
946 const fields = layers[index].querySelectorAll('[name]');
947 fields.forEach(field => {
948 let name = field.getAttribute('name'); // Try to find layer index.
949
950 const match = name.match(/(.+?\[layers\])\[(\d+)\](\[.+?\])$/);
951
952 if (null !== match) {
953 name = match[1] + `[${index}]` + match[3];
954 }
955
956 field.name = name;
957 });
958 }
959 }
960 /**
961 * Update template background settings with custom logic.
962 *
963 * @param {HTMLElement} fieldset Fieldset HTML element.
964 * @param {Object} data Current template data.
965 */
966
967
968 function createPermanentAttachment(fieldset, data) {
969 data.background = data.background || null; // Create background settings control.
970
971 const control = builders.control({
972 classes: ['sharing-image-editor-control', 'control-reduced'],
973 label: editor_('Template background settings', 'sharing-image'),
974 fields: [{
975 group: 'radio',
976 classes: ['sharing-image-editor-control-radio'],
977 attributes: {
978 name: editor_params.name + '[background]',
979 value: 'blank'
980 },
981 label: editor_('Do not use background image', 'sharing-image'),
982 checked: data.background
983 }, {
984 group: 'radio',
985 classes: ['sharing-image-editor-control-radio'],
986 attributes: {
987 name: editor_params.name + '[background]',
988 value: 'dynamic'
989 },
990 label: editor_('Select for each post separately', 'sharing-image'),
991 help: editor_('Post thumbnail will be used if autogenerate', 'sharing-image'),
992 checked: data.background
993 }, {
994 group: 'radio',
995 classes: ['sharing-image-editor-control-radio'],
996 attributes: {
997 name: editor_params.name + '[background]',
998 value: 'permanent'
999 },
1000 label: editor_('Upload permanent background', 'sharing-image'),
1001 checked: data.background
1002 }],
1003 append: fieldset
1004 });
1005 const media = builders.media({
1006 name: editor_params.name + '[attachment]',
1007 classes: ['sharing-image-editor-control', 'control-media'],
1008 value: data.attachment,
1009 link: editor_params.links.uploads,
1010 labels: {
1011 button: editor_('Upload image', 'sharing-image'),
1012 heading: editor_('Select background image', 'sharing-image'),
1013 details: editor_('Attachment details', 'sharing-image')
1014 },
1015 append: fieldset
1016 });
1017 const upload = media.querySelector('button');
1018 upload.disabled = true;
1019 builders.control({
1020 classes: ['sharing-image-editor-control'],
1021 label: editor_('Fill color', 'sharing-image'),
1022 fields: [{
1023 group: 'input',
1024 classes: ['sharing-image-editor-control-color'],
1025 attributes: {
1026 name: editor_params.name + '[fill]',
1027 type: 'color',
1028 value: data.fill
1029 }
1030 }],
1031 append: fieldset
1032 });
1033 control.querySelectorAll('input').forEach(radio => {
1034 if ('radio' !== radio.type) {
1035 return;
1036 } // Show upload button for checked permanent radio.
1037
1038
1039 if (radio.checked && 'permanent' === radio.value) {
1040 upload.disabled = false;
1041 }
1042
1043 radio.addEventListener('change', () => {
1044 upload.disabled = true;
1045
1046 if ('permanent' === radio.value) {
1047 upload.disabled = false;
1048 }
1049 });
1050 });
1051 }
1052 /**
1053 * Text layer dynamic/static fields manager.
1054 *
1055 * @param {HTMLElement} layer Current layer element.
1056 * @param {string} name Fields name attribute prefix.
1057 * @param {Object} data Layer data object.
1058 */
1059
1060
1061 function createDynamicFields(layer, name, data) {
1062 const control = builders.control({
1063 classes: ['sharing-image-editor-control'],
1064 append: layer
1065 });
1066 const checkbox = builders.checkbox({
1067 classes: ['sharing-image-editor-control-checkbox'],
1068 attributes: {
1069 name: name + '[dynamic]',
1070 value: 'dynamic'
1071 },
1072 label: editor_('Dynamic field. Filled in the post editing screen.', 'sharing-image'),
1073 checked: data.dynamic
1074 }, control);
1075 const fields = [];
1076 fields[fields.length] = builders.control({
1077 classes: ['sharing-image-editor-control', 'control-extend', 'control-hidden'],
1078 help: editor_('Displayed only in the metabox.', 'sharing-image'),
1079 fields: [{
1080 group: 'input',
1081 classes: ['sharing-image-editor-control-input'],
1082 attributes: {
1083 name: name + '[title]',
1084 value: data.title
1085 },
1086 label: editor_('Field name', 'sharing-image')
1087 }],
1088 append: layer
1089 });
1090 fields[fields.length] = builders.control({
1091 classes: ['sharing-image-editor-control', 'control-extend', 'control-hidden'],
1092 help: editor_('This field is used for example only, to see how the editor will look.', 'sharing-image'),
1093 fields: [{
1094 group: 'textarea',
1095 classes: ['sharing-image-editor-control-textarea'],
1096 content: data.sample || 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
1097 attributes: {
1098 name: name + '[sample]',
1099 rows: 2
1100 },
1101 label: editor_('Text sample', 'sharing-image')
1102 }],
1103 append: layer
1104 });
1105 fields[fields.length] = builders.control({
1106 classes: ['sharing-image-editor-control', 'control-hidden'],
1107 label: editor_('Preset text field', 'sharing-image'),
1108 fields: [{
1109 group: 'radio',
1110 classes: ['sharing-image-editor-control-radio'],
1111 attributes: {
1112 name: name + '[preset]',
1113 value: 'none'
1114 },
1115 dataset: {
1116 persistent: true
1117 },
1118 label: editor_('Fill in manually', 'sharing-image'),
1119 checked: data.preset || 'none'
1120 }, {
1121 group: 'radio',
1122 classes: ['sharing-image-editor-control-radio'],
1123 attributes: {
1124 name: name + '[preset]',
1125 value: 'title'
1126 },
1127 dataset: {
1128 persistent: true
1129 },
1130 label: editor_('Take from post title', 'sharing-image'),
1131 checked: data.preset || 'none'
1132 }, {
1133 group: 'radio',
1134 classes: ['sharing-image-editor-control-radio'],
1135 attributes: {
1136 name: name + '[preset]',
1137 value: 'excerpt'
1138 },
1139 dataset: {
1140 persistent: true
1141 },
1142 label: editor_('Use post excerpt text', 'sharing-image'),
1143 checked: data.preset || 'none'
1144 }],
1145 append: layer
1146 });
1147 fields[fields.length] = builders.control({
1148 classes: ['sharing-image-editor-control', 'control-extend'],
1149 help: editor_('You can use non-breaking spaces to manage your string position.', 'sharing-image'),
1150 fields: [{
1151 group: 'textarea',
1152 classes: ['sharing-image-editor-control-textarea'],
1153 content: data.content,
1154 attributes: {
1155 name: name + '[content]',
1156 rows: 2
1157 },
1158 label: editor_('Content', 'sharing-image')
1159 }],
1160 append: layer
1161 }); // Helper function to toggle contols visibility.
1162
1163 const toggleClasses = () => {
1164 fields.forEach(field => {
1165 field.classList.toggle('control-hidden');
1166 });
1167 };
1168
1169 if (checkbox.checked) {
1170 toggleClasses();
1171 }
1172
1173 checkbox.addEventListener('change', () => {
1174 toggleClasses();
1175 });
1176 }
1177 /**
1178 * Text layer more options fields manager.
1179 *
1180 * @param {HTMLElement} layer Current layer element.
1181 * @param {string} name Fields name attribute prefix.
1182 * @param {Object} data Layer data object.
1183 */
1184
1185
1186 function createMoreFields(layer, name, data) {
1187 const fields = [];
1188 fields[fields.length] = createFontField(layer, name, data);
1189 fields[fields.length] = builders.control({
1190 classes: ['sharing-image-editor-control', 'control-hidden'],
1191 fields: [{
1192 group: 'input',
1193 classes: ['sharing-image-editor-control-color'],
1194 attributes: {
1195 type: 'color',
1196 name: name + '[color]',
1197 value: data.color || '#ffffff'
1198 },
1199 label: editor_('Text color', 'sharing-image')
1200 }],
1201 append: layer
1202 });
1203 fields[fields.length] = builders.control({
1204 classes: ['sharing-image-editor-control', 'control-series', 'control-hidden'],
1205 fields: [{
1206 group: 'select',
1207 classes: ['sharing-image-editor-control-select'],
1208 options: {
1209 left: editor_('Left', 'sharing-image'),
1210 center: editor_('Center', 'sharing-image'),
1211 right: editor_('Right', 'sharing-image')
1212 },
1213 attributes: {
1214 name: name + '[horizontal]'
1215 },
1216 label: editor_('Horizontal alignment', 'sharing-image'),
1217 selected: data.horizontal
1218 }, {
1219 group: 'select',
1220 classes: ['sharing-image-editor-control-select'],
1221 options: {
1222 top: editor_('Top', 'sharing-image'),
1223 center: editor_('Center', 'sharing-image'),
1224 bottom: editor_('Bottom', 'sharing-image')
1225 },
1226 attributes: {
1227 name: name + '[vertical]'
1228 },
1229 label: editor_('Vertical alignment', 'sharing-image'),
1230 selected: data.vertical
1231 }],
1232 append: layer
1233 });
1234 const control = builders.control({
1235 classes: ['sharing-image-editor-control'],
1236 append: layer
1237 });
1238 const button = builders.element('button', {
1239 classes: ['sharing-image-editor-more'],
1240 text: editor_('More options', 'sharing-image'),
1241 attributes: {
1242 type: 'button'
1243 },
1244 append: control
1245 });
1246 button.addEventListener('click', () => {
1247 fields.forEach(field => {
1248 field.classList.remove('control-hidden');
1249 }); // Remove button on expand.
1250
1251 layer.removeChild(control);
1252 }); // Open more fields for existing layers.
1253
1254 if (Object.keys(data).length > 0) {
1255 button.click();
1256 }
1257 }
1258 /**
1259 * Create font field in text layer.
1260 *
1261 * @param {HTMLElement} layer Current layer element.
1262 * @param {string} name Fields name attribute prefix.
1263 * @param {Object} data Layer data object.
1264 */
1265
1266
1267 function createFontField(layer, name, data) {
1268 const control = builders.control({
1269 classes: ['sharing-image-editor-control', 'control-upload', 'control-hidden'],
1270 append: layer
1271 });
1272 const select = builders.select({
1273 classes: ['sharing-image-editor-control-select'],
1274 options: editor_params.fonts,
1275 attributes: {
1276 name: name + '[fontname]'
1277 },
1278 label: editor_('Font family', 'sharing-image'),
1279 selected: data.fontname
1280 }, control);
1281 const media = builders.media({
1282 name: name + '[fontfile]',
1283 classes: ['sharing-image-editor-control-media'],
1284 value: data.fontfile,
1285 link: editor_params.links.uploads,
1286 labels: {
1287 button: editor_('Upload custom font', 'sharing-image'),
1288 heading: editor_('Upload custom font', 'sharing-image'),
1289 details: editor_('Font attachment', 'sharing-image'),
1290 remove: editor_('Remove font', 'sharing-image')
1291 },
1292 remove: true,
1293 append: control
1294 });
1295 builders.element('small', {
1296 text: editor_('Custom font can only be in .ttf format.'),
1297 append: control
1298 });
1299
1300 if (data.fontfile) {
1301 select.disabled = true;
1302 } // Find media attachment input.
1303
1304
1305 const input = media.querySelector('input');
1306 input.addEventListener('change', () => {
1307 select.disabled = false;
1308
1309 if (input.value) {
1310 select.disabled = true;
1311 }
1312 });
1313 return control;
1314 }
1315 /**
1316 * Rectangle layer outline option.
1317 *
1318 * @param {HTMLElement} layer Current layer element.
1319 * @param {string} name Fields name attribute prefix.
1320 * @param {Object} data Layer data object.
1321 */
1322
1323
1324 function createRectangleOutline(layer, name, data) {
1325 const control = builders.control({
1326 classes: ['sharing-image-editor-control'],
1327 append: layer
1328 });
1329 const checkbox = builders.checkbox({
1330 classes: ['sharing-image-editor-control-checkbox'],
1331 attributes: {
1332 name: name + '[outline]',
1333 value: 'outline'
1334 },
1335 label: editor_('Outline rectangle.', 'sharing-image'),
1336 checked: data.outline
1337 }, control);
1338 const range = builders.control({
1339 classes: ['sharing-image-editor-control', 'control-hidden'],
1340 fields: [{
1341 group: 'input',
1342 classes: ['sharing-image-editor-control-range'],
1343 attributes: {
1344 type: 'range',
1345 name: name + '[thickness]',
1346 min: 0,
1347 max: 50,
1348 step: 1,
1349 value: data.thickness || '0'
1350 },
1351 label: editor_('Border width', 'sharing-image')
1352 }],
1353 append: layer
1354 });
1355
1356 if (data.outline) {
1357 range.classList.remove('control-hidden');
1358 }
1359
1360 checkbox.addEventListener('change', () => {
1361 range.classList.add('control-hidden');
1362
1363 if (checkbox.checked) {
1364 range.classList.remove('control-hidden');
1365 }
1366 });
1367 }
1368 /**
1369 * Create catalog button in footer.
1370 *
1371 * @param {HTMLElement} footer Footer HTML element.
1372 */
1373
1374
1375 function createCatalogButton(footer) {
1376 const link = new URL(document.location.href);
1377 link.searchParams.delete('template');
1378 builders.element('a', {
1379 classes: ['button'],
1380 text: editor_('← Back to Catalog', 'sharing-image'),
1381 attributes: {
1382 href: link.href
1383 },
1384 append: footer
1385 });
1386 }
1387 /**
1388 * Create template deletion button in footer.
1389 *
1390 * @param {HTMLElement} footer Footer HTML element.
1391 */
1392
1393
1394 function createDeleteButton(footer) {
1395 const href = new URL(document.location.href); // Get template index from current link.
1396
1397 const index = href.searchParams.get('template'); // Set template index to delete link.
1398
1399 const link = new URL(editor.getAttribute('action'));
1400 link.searchParams.set('action', 'sharing_image_delete');
1401 link.searchParams.set('template', index);
1402 link.searchParams.set('nonce', editor_params.nonce);
1403 builders.element('a', {
1404 classes: ['sharing-image-editor-delete'],
1405 text: editor_('Delete template', 'sharing-image'),
1406 attributes: {
1407 href: link.href
1408 },
1409 append: footer
1410 });
1411 }
1412 /**
1413 * Create preview element.
1414 *
1415 * @param {HTMLElement} viewport Monitor viewport element.
1416 * @param {Object} data Template data object.
1417 */
1418
1419
1420 function createPreview(viewport, data) {
1421 preview = builders.element('div', {
1422 classes: ['sharing-image-editor-preview', 'preview-blank'],
1423 append: viewport
1424 });
1425
1426 if (data.preview) {
1427 builders.element('img', {
1428 attributes: {
1429 src: data.preview,
1430 alt: ''
1431 },
1432 append: preview
1433 });
1434 preview.classList.remove('preview-blank');
1435 }
1436
1437 builders.element('span', {
1438 classes: ['sharing-image-editor-loader'],
1439 append: preview
1440 });
1441 builders.element('input', {
1442 attributes: {
1443 type: 'hidden',
1444 name: editor_params.name + '[preview]',
1445 value: data.preview
1446 },
1447 append: preview
1448 });
1449 return preview;
1450 }
1451 /**
1452 *
1453 * @param {*} designer
1454 * @param {*} layer
1455 */
1456
1457
1458 function createCollapseButton(designer, layer) {
1459 const label = layer.querySelector('h2');
1460 const button = builders.element('button', {
1461 classes: ['sharing-image-editor-collapse'],
1462 attributes: {
1463 type: 'button',
1464 title: editor_('Collapse layer', 'sharing-image')
1465 },
1466 append: label
1467 });
1468 button.addEventListener('click', e => {
1469 e.preventDefault(); // Set default button title.
1470
1471 button.setAttribute('title', editor_('Collapse layer', 'sharing-image'));
1472 layer.classList.toggle('layer-collapsed'); // Check if new class is collapsed.
1473
1474 const collapsed = layer.classList.contains('layer-collapsed');
1475
1476 if (collapsed) {
1477 button.setAttribute('title', editor_('Expand layer', 'sharing-image'));
1478 }
1479 });
1480 }
1481 /**
1482 * Create button inside layer box to change order.
1483 *
1484 * @param {HTMLElement} designer Layers designer HTML element.
1485 * @param {HTMLElement} layer Current layer HTML emelemt.
1486 */
1487
1488
1489 function createOrderLayersButton(designer, layer) {
1490 const button = builders.element('button', {
1491 classes: ['sharing-image-editor-order'],
1492 attributes: {
1493 type: 'button',
1494 title: editor_('Raise higher', 'sharing-image')
1495 },
1496 append: layer
1497 });
1498 button.addEventListener('click', () => {
1499 if (layer.previousSibling) {
1500 designer.insertBefore(layer, layer.previousSibling);
1501 } // Update fields name attributes.
1502
1503
1504 reorderLayers(designer);
1505
1506 if (editor.classList.contains('editor-suspend')) {
1507 return;
1508 }
1509
1510 generateTemplate();
1511 });
1512 }
1513 /**
1514 * Create button to delete layer.
1515 *
1516 * @param {HTMLElement} designer Layers designer HTML element.
1517 * @param {HTMLElement} layer Current layer HTML emelemt.
1518 */
1519
1520
1521 function createDeleteLayerButton(designer, layer) {
1522 const control = builders.control({
1523 classes: ['sharing-image-editor-control', 'control-footer'],
1524 append: layer
1525 });
1526 const button = builders.element('button', {
1527 classes: ['sharing-image-editor-delete'],
1528 text: editor_('Delete layer', 'sharing-image'),
1529 attributes: {
1530 type: 'button'
1531 },
1532 append: control
1533 });
1534 button.addEventListener('click', () => {
1535 designer.removeChild(layer); // Update fields name attributes.
1536
1537 reorderLayers(designer);
1538
1539 if (editor.classList.contains('editor-suspend')) {
1540 return;
1541 }
1542
1543 generateTemplate();
1544 });
1545 }
1546 /**
1547 * Create image layer.
1548 *
1549 * @param {number} index Current layer index.
1550 * @param {Object} data Current template layer data.
1551 */
1552
1553
1554 function createLayerImage(index, data) {
1555 const description = [];
1556 description.push(editor_('Use jpg, gif or png image formats.', 'sharing-image'));
1557 description.push(editor_('Leave width and height fields blank to use the original image size.', 'sharing-image'));
1558 description.push(editor_('Sizes are calculated proportionally if not filled.', 'sharing-image'));
1559 const layer = builders.layer({
1560 classes: ['sharing-image-editor-layer', 'layer-image'],
1561 label: editor_('Image', 'sharing-image'),
1562 description: description.join(' ')
1563 }); // Form fields name for this layer.
1564
1565 const name = editor_params.name + `[layers][${index}]`;
1566 builders.element('input', {
1567 attributes: {
1568 type: 'hidden',
1569 name: name + '[type]',
1570 value: 'image'
1571 },
1572 append: layer
1573 });
1574 builders.media({
1575 name: name + '[attachment]',
1576 classes: ['sharing-image-editor-control', 'control-media'],
1577 value: data.attachment,
1578 link: editor_params.links.uploads,
1579 labels: {
1580 button: editor_('Upload image', 'sharing-image'),
1581 heading: editor_('Select layer image', 'sharing-image'),
1582 details: editor_('Attachment details', 'sharing-image')
1583 },
1584 append: layer
1585 });
1586 builders.control({
1587 classes: ['sharing-image-editor-control', 'control-sizes'],
1588 fields: [{
1589 group: 'input',
1590 classes: ['sharing-image-editor-control-input'],
1591 attributes: {
1592 name: name + '[x]',
1593 value: data.x,
1594 placeholder: '10'
1595 },
1596 label: editor_('X', 'sharing-image')
1597 }, {
1598 group: 'input',
1599 classes: ['sharing-image-editor-control-input'],
1600 attributes: {
1601 name: name + '[y]',
1602 value: data.y,
1603 placeholder: '10'
1604 },
1605 label: editor_('Y', 'sharing-image')
1606 }, {
1607 group: 'input',
1608 classes: ['sharing-image-editor-control-input'],
1609 attributes: {
1610 name: name + '[width]',
1611 value: data.width
1612 },
1613 label: editor_('Width', 'sharing-image')
1614 }, {
1615 group: 'input',
1616 classes: ['sharing-image-editor-control-input'],
1617 attributes: {
1618 name: name + '[height]',
1619 value: data.height
1620 },
1621 label: editor_('Height', 'sharing-image')
1622 }],
1623 append: layer
1624 });
1625 return layer;
1626 }
1627 /**
1628 * Create text layer.
1629 *
1630 * @param {number} index Current layer index.
1631 * @param {Object} data Current template data.
1632 */
1633
1634
1635 function createLayerText(index, data) {
1636 const description = [];
1637 description.push(editor_('Write a text to the current image.', 'sharing-image'));
1638 description.push(editor_('If the font does not fit within your limits, its size will decrease.', 'sharing-image'));
1639 description.push(editor_('Avoid using large font sizes for long text – this affects performance.', 'sharing-image'));
1640 const layer = builders.layer({
1641 classes: ['sharing-image-editor-layer', 'layer-text'],
1642 label: editor_('Text', 'sharing-image'),
1643 description: description.join(' ')
1644 }); // Form fields name for this layer.
1645
1646 const name = editor_params.name + `[layers][${index}]`;
1647 builders.element('input', {
1648 attributes: {
1649 type: 'hidden',
1650 name: name + '[type]',
1651 value: 'text'
1652 },
1653 append: layer
1654 });
1655 builders.control({
1656 classes: ['sharing-image-editor-control', 'control-sizes'],
1657 fields: [{
1658 group: 'input',
1659 classes: ['sharing-image-editor-control-input'],
1660 attributes: {
1661 type: 'text',
1662 name: name + '[x]',
1663 value: data.x,
1664 placeholder: '10'
1665 },
1666 label: editor_('X', 'sharing-image')
1667 }, {
1668 group: 'input',
1669 classes: ['sharing-image-editor-control-input'],
1670 attributes: {
1671 type: 'text',
1672 name: name + '[y]',
1673 value: data.y,
1674 placeholder: '10'
1675 },
1676 label: editor_('Y', 'sharing-image')
1677 }, {
1678 group: 'input',
1679 classes: ['sharing-image-editor-control-input'],
1680 attributes: {
1681 type: 'text',
1682 name: name + '[width]',
1683 value: data.width,
1684 placeholder: '1000'
1685 },
1686 label: editor_('Width', 'sharing-image')
1687 }, {
1688 group: 'input',
1689 classes: ['sharing-image-editor-control-input'],
1690 attributes: {
1691 type: 'text',
1692 name: name + '[height]',
1693 value: data.height
1694 },
1695 label: editor_('Height', 'sharing-image')
1696 }],
1697 append: layer
1698 }); // Create static/dynamic text fields.
1699
1700 createDynamicFields(layer, name, data); // Create more options.
1701
1702 createMoreFields(layer, name, data);
1703 builders.control({
1704 classes: ['sharing-image-editor-control', 'control-series'],
1705 fields: [{
1706 group: 'input',
1707 classes: ['sharing-image-editor-control-range'],
1708 attributes: {
1709 type: 'range',
1710 name: name + '[fontsize]',
1711 min: 10,
1712 max: 200,
1713 step: 1,
1714 value: data.fontsize || '48'
1715 },
1716 label: editor_('Font size', 'sharing-image')
1717 }, {
1718 group: 'input',
1719 classes: ['sharing-image-editor-control-range'],
1720 attributes: {
1721 type: 'range',
1722 name: name + '[lineheight]',
1723 min: 0,
1724 max: 4,
1725 step: 0.125,
1726 value: data.lineheight || '1.5'
1727 },
1728 label: editor_('Line height', 'sharing-image')
1729 }],
1730 append: layer
1731 });
1732 return layer;
1733 }
1734 /**
1735 * Create filter layer.
1736 *
1737 * @param {number} index Current layer index.
1738 * @param {Object} data Current template data.
1739 */
1740
1741
1742 function createLayerFilter(index, data) {
1743 const description = [];
1744 description.push(editor_('Filters are applied one after another to the entire editor image.', 'sharing-image'));
1745 description.push(editor_('If you want to control their order, create multiple layers.', 'sharing-image'));
1746 const layer = builders.layer({
1747 classes: ['sharing-image-editor-layer', 'layer-text'],
1748 label: editor_('Filter', 'sharing-image'),
1749 description: description.join(' ')
1750 }); // Form fields name for this layer.
1751
1752 const name = editor_params.name + `[layers][${index}]`;
1753 builders.element('input', {
1754 attributes: {
1755 type: 'hidden',
1756 name: name + '[type]',
1757 value: 'filter'
1758 },
1759 append: layer
1760 });
1761 builders.control({
1762 classes: ['sharing-image-editor-control'],
1763 fields: [{
1764 group: 'checkbox',
1765 classes: ['sharing-image-editor-control-checkbox'],
1766 attributes: {
1767 name: name + '[grayscale]',
1768 value: 'grayscale'
1769 },
1770 label: editor_('Turns image into a grayscale version', 'sharing-image'),
1771 checked: data.grayscale
1772 }],
1773 append: layer
1774 });
1775 builders.control({
1776 classes: ['sharing-image-editor-control'],
1777 fields: [{
1778 group: 'checkbox',
1779 classes: ['sharing-image-editor-control-checkbox'],
1780 attributes: {
1781 name: name + '[blur]',
1782 value: 'blur'
1783 },
1784 label: editor_('Blur image by Gaussian effect', 'sharing-image'),
1785 checked: data.blur
1786 }],
1787 append: layer
1788 });
1789 builders.control({
1790 classes: ['sharing-image-editor-control'],
1791 fields: [{
1792 group: 'input',
1793 classes: ['sharing-image-editor-control-range'],
1794 attributes: {
1795 type: 'range',
1796 name: name + '[contrast]',
1797 min: -50,
1798 max: 50,
1799 step: 5,
1800 value: data.contrast || '0'
1801 },
1802 label: editor_('Contrast', 'sharing-image')
1803 }],
1804 append: layer
1805 });
1806 builders.control({
1807 classes: ['sharing-image-editor-control'],
1808 fields: [{
1809 group: 'input',
1810 classes: ['sharing-image-editor-control-range'],
1811 attributes: {
1812 type: 'range',
1813 name: name + '[brightness]',
1814 min: -50,
1815 max: 50,
1816 step: 5,
1817 value: data.brightness || '0'
1818 },
1819 label: editor_('Brightness', 'sharing-image')
1820 }],
1821 append: layer
1822 });
1823 builders.control({
1824 classes: ['sharing-image-editor-control'],
1825 fields: [{
1826 group: 'input',
1827 classes: ['sharing-image-editor-control-range'],
1828 attributes: {
1829 type: 'range',
1830 name: name + '[blackout]',
1831 min: 0,
1832 max: 100,
1833 step: 5,
1834 value: data.blackout || '0'
1835 },
1836 label: editor_('Blackout', 'sharing-image')
1837 }],
1838 append: layer
1839 });
1840 return layer;
1841 }
1842 /**
1843 * Create rectangle layer.
1844 *
1845 * @param {number} index Current layer index.
1846 * @param {Object} data Current template data.
1847 */
1848
1849
1850 function createLayerRectangle(index, data) {
1851 const description = [];
1852 description.push(editor_('Draw a colored rectangle on current image.', 'sharing-image'));
1853 description.push(editor_('You can get filled or outlined figure with custom color and opacity.', 'sharing-image'));
1854 description.push(editor_('Use small height to draw the line.', 'sharing-image'));
1855 const layer = builders.layer({
1856 classes: ['sharing-image-editor-layer', 'layer-text'],
1857 label: editor_('Rectangle', 'sharing-image'),
1858 description: description.join(' ')
1859 }); // Form fields name for this layer.
1860
1861 const name = editor_params.name + `[layers][${index}]`;
1862 builders.element('input', {
1863 attributes: {
1864 type: 'hidden',
1865 name: name + '[type]',
1866 value: 'rectangle'
1867 },
1868 append: layer
1869 });
1870 builders.control({
1871 classes: ['sharing-image-editor-control'],
1872 fields: [{
1873 group: 'input',
1874 classes: ['sharing-image-editor-control-color'],
1875 attributes: {
1876 type: 'color',
1877 name: name + '[color]',
1878 value: data.color || '#ffffff'
1879 },
1880 label: editor_('Rectangle color', 'sharing-image')
1881 }],
1882 append: layer
1883 });
1884 builders.control({
1885 classes: ['sharing-image-editor-control', 'control-sizes'],
1886 fields: [{
1887 group: 'input',
1888 classes: ['sharing-image-editor-control-input'],
1889 attributes: {
1890 type: 'text',
1891 name: name + '[x]' || 0,
1892 value: data.x
1893 },
1894 label: editor_('X', 'sharing-image')
1895 }, {
1896 group: 'input',
1897 classes: ['sharing-image-editor-control-input'],
1898 attributes: {
1899 type: 'text',
1900 name: name + '[y]' || 0,
1901 value: data.y
1902 },
1903 label: editor_('Y', 'sharing-image')
1904 }, {
1905 group: 'input',
1906 classes: ['sharing-image-editor-control-input'],
1907 attributes: {
1908 type: 'text',
1909 name: name + '[width]',
1910 value: data.width
1911 },
1912 label: editor_('Width', 'sharing-image')
1913 }, {
1914 group: 'input',
1915 classes: ['sharing-image-editor-control-input'],
1916 attributes: {
1917 type: 'text',
1918 name: name + '[height]',
1919 value: data.height
1920 },
1921 label: editor_('Height', 'sharing-image')
1922 }],
1923 append: layer
1924 });
1925 createRectangleOutline(layer, name, data);
1926 builders.control({
1927 classes: ['sharing-image-editor-control'],
1928 fields: [{
1929 group: 'input',
1930 classes: ['sharing-image-editor-control-range'],
1931 attributes: {
1932 type: 'range',
1933 name: name + '[opacity]',
1934 min: 0,
1935 max: 100,
1936 step: 5,
1937 value: data.opacity || '0'
1938 },
1939 label: editor_('Opacity', 'sharing-image')
1940 }],
1941 append: layer
1942 });
1943 return layer;
1944 }
1945 /**
1946 * Create new layer.
1947 *
1948 * @param {HTMLElement} designer Designer HTML element.
1949 * @param {string} type New layer type.
1950 * @param {number} index Layer index.
1951 * @param {Object} data New layer data.
1952 */
1953
1954
1955 function createLayer(designer, type, index) {
1956 let data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1957 let layer = null;
1958
1959 switch (type) {
1960 case 'image':
1961 layer = createLayerImage(index, data);
1962 break;
1963
1964 case 'text':
1965 layer = createLayerText(index, data);
1966 break;
1967
1968 case 'filter':
1969 layer = createLayerFilter(index, data);
1970 break;
1971
1972 case 'rectangle':
1973 layer = createLayerRectangle(index, data);
1974 break;
1975 }
1976
1977 if (null === layer) {
1978 return;
1979 }
1980
1981 designer.insertBefore(layer, designer.firstChild); // Delete this layer button.
1982
1983 createDeleteLayerButton(designer, layer); // Create collapse button.
1984
1985 createCollapseButton(designer, layer); // Reorder layers button.
1986
1987 createOrderLayersButton(designer, layer);
1988 }
1989 /**
1990 * Create layers designer control.
1991 *
1992 * @param {HTMLElement} fieldset Fieldset HTML element.
1993 * @param {Object} data Current template data.
1994 */
1995
1996
1997 function createDesigner(fieldset, data) {
1998 const control = builders.control({
1999 classes: ['sharing-image-editor-control', 'control-select', 'control-compact'],
2000 fields: [{
2001 group: 'select',
2002 classes: ['sharing-image-editor-control-select'],
2003 options: {
2004 text: editor_('Text', 'sharing-image'),
2005 image: editor_('Image', 'sharing-image'),
2006 filter: editor_('Filter', 'sharing-image'),
2007 rectangle: editor_('Rectangle', 'sharing-image')
2008 }
2009 }],
2010 append: fieldset
2011 });
2012 const button = builders.element('button', {
2013 classes: ['button'],
2014 text: editor_('Add new', 'sharing-image'),
2015 attributes: {
2016 type: 'button'
2017 },
2018 append: control
2019 });
2020 const designer = builders.element('div', {
2021 classes: ['sharing-image-editor-designer'],
2022 append: fieldset
2023 }); // Set default layers set.
2024
2025 let layers = data.layers || [];
2026 layers = layers.reverse();
2027 layers.forEach((layer, index) => {
2028 if (layer.hasOwnProperty('type')) {
2029 createLayer(designer, layer.type, index++, layer);
2030 }
2031 });
2032 button.addEventListener('click', () => {
2033 const select = control.querySelector('select');
2034
2035 if (null === select) {
2036 return;
2037 }
2038
2039 createLayer(designer, select.value, designer.children.length);
2040 });
2041 }
2042 /**
2043 * Create common settings on template editor screen.
2044 *
2045 * @param {Object} data Current template data.
2046 */
2047
2048
2049 function createFieldset(data) {
2050 const fieldset = builders.element('div', {
2051 classes: ['sharing-image-editor-fieldset'],
2052 append: editor
2053 }); // Create template title control.
2054
2055 builders.control({
2056 classes: ['sharing-image-editor-control', 'control-compact', 'control-extend'],
2057 help: editor_('Used only in the admin panel', 'sharing-image'),
2058 fields: [{
2059 group: 'input',
2060 classes: ['sharing-image-editor-control-input'],
2061 attributes: {
2062 name: editor_params.name + '[title]',
2063 value: data.title
2064 },
2065 dataset: {
2066 persistent: true
2067 },
2068 label: editor_('Template title', 'sharing-image')
2069 }],
2070 append: fieldset
2071 }); // Create background settings with custom logic.
2072
2073 createPermanentAttachment(fieldset, data); // Create width/height settings control.
2074
2075 builders.control({
2076 classes: ['sharing-image-editor-control', 'control-compact', 'control-sizes'],
2077 fields: [{
2078 group: 'input',
2079 classes: ['sharing-image-editor-control-input'],
2080 attributes: {
2081 name: editor_params.name + '[width]',
2082 value: data.width || '1200',
2083 placeholder: '1200'
2084 },
2085 label: editor_('Editor width', 'sharing-image')
2086 }, {
2087 group: 'input',
2088 classes: ['sharing-image-editor-control-input'],
2089 attributes: {
2090 name: editor_params.name + '[height]',
2091 value: data.height || '630',
2092 placeholder: '630'
2093 },
2094 label: editor_('Editor height', 'sharing-image')
2095 }],
2096 append: fieldset
2097 });
2098 const description = [];
2099 description.push(editor_('You can add multiple layers on your editor.', 'sharing-image'));
2100 description.push(editor_('Note that the stacking order of the layers is important.', 'sharing-image'));
2101 description.push(editor_('You can change the order using the arrows in the corner of each box.', 'sharing-image'));
2102 builders.control({
2103 classes: ['sharing-image-editor-control', 'control-reduced'],
2104 label: editor_('Add layers', 'sharing-image'),
2105 description: description.join(' '),
2106 append: fieldset
2107 }); // Create layers designer block.
2108
2109 createDesigner(fieldset, data);
2110 const footer = builders.control({
2111 classes: ['sharing-image-editor-control', 'control-footer'],
2112 append: fieldset
2113 }); // Create back to catalog button.
2114
2115 createCatalogButton(footer); // Create template deletion button.
2116
2117 createDeleteButton(footer);
2118 fieldset.addEventListener('change', e => {
2119 if (editor.classList.contains('editor-suspend')) {
2120 return;
2121 }
2122
2123 const target = e.target; // Skip fields that don't affect the poster.
2124
2125 if (target.hasAttribute('data-persistent')) {
2126 return;
2127 }
2128
2129 if (!target.hasAttribute('name')) {
2130 return;
2131 }
2132
2133 generateTemplate();
2134 });
2135 }
2136 /**
2137 * Create button to submit editor form.
2138 *
2139 * @param {HTMLElement} manager Manager element.
2140 */
2141
2142
2143 function createSubmitButton(manager) {
2144 builders.element('button', {
2145 text: editor_('Save changes', 'sharing-image'),
2146 classes: ['button', 'button-primary'],
2147 attributes: {
2148 type: 'submit'
2149 },
2150 append: manager
2151 });
2152 }
2153 /**
2154 * Create button to generate new template manually.
2155 *
2156 * @param {HTMLElement} manager Manager element.
2157 */
2158
2159
2160 function createGenerateButton(manager) {
2161 const button = builders.element('button', {
2162 text: editor_('Generate preview', 'sharing-image'),
2163 classes: ['button'],
2164 attributes: {
2165 type: 'button'
2166 },
2167 append: manager
2168 });
2169 button.addEventListener('click', () => {
2170 generateTemplate();
2171 });
2172 }
2173 /**
2174 * Create disable live-reloading checkbox.
2175 *
2176 * @param {HTMLElement} manager Manager element.
2177 * @param {Object} data Template data.
2178 */
2179
2180
2181 function createSuspendCheckbox(manager, data) {
2182 const checkbox = builders.checkbox({
2183 classes: ['sharing-image-editor-suspend'],
2184 attributes: {
2185 name: editor_params.name + '[suspend]',
2186 value: 'suspend'
2187 },
2188 label: editor_('Disable live-reload', 'sharing-image'),
2189 checked: data.suspend
2190 }, manager);
2191
2192 if (data.suspend) {
2193 editor.classList.add('editor-suspend');
2194 }
2195
2196 checkbox.addEventListener('change', () => {
2197 editor.classList.remove('editor-suspend');
2198
2199 if (checkbox.checked) {
2200 editor.classList.add('editor-suspend');
2201 }
2202 });
2203 }
2204 /**
2205 * Create template settings preview.
2206 *
2207 * @param {Object} data Current template data.
2208 */
2209
2210
2211 function createMonitor(data) {
2212 const monitor = builders.element('div', {
2213 classes: ['sharing-image-editor-monitor'],
2214 append: editor
2215 });
2216 const viewport = builders.element('div', {
2217 classes: ['sharing-image-editor-viewport'],
2218 append: monitor
2219 });
2220 createPreview(viewport, data);
2221 builders.element('div', {
2222 classes: ['sharing-image-editor-warning'],
2223 append: viewport
2224 });
2225 const manager = builders.element('div', {
2226 classes: ['sharing-image-editor-manager'],
2227 append: viewport
2228 }); // Create live-reload manager checkbox.
2229
2230 createSuspendCheckbox(manager, data); // Create submit form button.
2231
2232 createSubmitButton(manager); // Create template generator button.
2233
2234 createGenerateButton(manager);
2235 }
2236 /**
2237 * Create form hidden settings fields.
2238 *
2239 * @param {HTMLElement} content Settings content element.
2240 * @param {number} index Current option index.
2241 */
2242
2243
2244 function prepareEditor(content, index) {
2245 editor_params.name = 'sharing_image_editor';
2246 const form = builders.element('form', {
2247 classes: ['sharing-image-editor'],
2248 attributes: {
2249 action: editor_params.links.action,
2250 method: 'POST'
2251 },
2252 append: content
2253 });
2254 builders.element('input', {
2255 attributes: {
2256 type: 'hidden',
2257 name: 'action',
2258 value: editor_params.name
2259 },
2260 append: form
2261 });
2262 builders.element('input', {
2263 attributes: {
2264 type: 'hidden',
2265 name: 'sharing_image_index',
2266 value: index
2267 },
2268 append: form
2269 });
2270 builders.element('input', {
2271 attributes: {
2272 type: 'hidden',
2273 name: 'sharing_image_nonce',
2274 value: editor_params.nonce
2275 },
2276 append: form
2277 });
2278 form.addEventListener('submit', e => {
2279 e.preventDefault();
2280 saveTemplate();
2281 });
2282 return form;
2283 }
2284 /**
2285 * Create template editor page.
2286 *
2287 * @param {HTMLElement} content Settings content element.
2288 * @param {Object} settings Global settings object.
2289 * @param {number} index Current option index.
2290 * @param {Object} data Template data.
2291 */
2292
2293
2294 function createEditor(content, settings, index) {
2295 let data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
2296 editor_params = settings; // Prepare form with hidden fields and events.
2297
2298 editor = prepareEditor(content, index); // Create monitor section part.
2299
2300 createMonitor(data); // Create fieldset section part.
2301
2302 createFieldset(data);
2303 }
2304
2305 /* harmony default export */ const sections_editor = (createEditor);
2306 ;// CONCATENATED MODULE: ./src/scripts/sections/config.js
2307 /**
2308 * Config settings tab.
2309 */
2310
2311 const {
2312 __: config_
2313 } = wp.i18n; // Store global scriot object for settings page.
2314
2315 let config_params = null;
2316 /**
2317 * Create default poster option.
2318 *
2319 * @param {HTMLElement} options Options form element.
2320 * @param {Object} data Config data object.
2321 */
2322
2323 function createDefaultOptions(options, data) {
2324 const control = builders.control({
2325 classes: ['sharing-image-config-control'],
2326 label: config_('Default poster', 'sharing-image'),
2327 append: options
2328 });
2329 builders.media({
2330 name: config_params.name + '[default]',
2331 classes: ['sharing-image-config-control-media'],
2332 label: config_('Default poster', 'sharing-image'),
2333 value: data.default,
2334 link: config_params.links.uploads,
2335 labels: {
2336 button: config_('Upload image', 'sharing-image'),
2337 heading: config_('Select default poster', 'sharing-image'),
2338 details: config_('Attachment details', 'sharing-image'),
2339 remove: config_('Remove image', 'sharing-image')
2340 },
2341 remove: true,
2342 append: control
2343 });
2344 const description = [];
2345 description.push(config_('The default poster is used on pages where there is no generated.', 'sharing-image'));
2346 description.push(config_('Best image size: 1200×630 pixels.', 'sharing-image'));
2347 builders.element('small', {
2348 text: description.join(' '),
2349 append: control
2350 });
2351 }
2352 /**
2353 * Create uploads directory option.
2354 *
2355 * @param {HTMLElement} options Options form element.
2356 * @param {Object} data Config data object.
2357 */
2358
2359
2360 function createUploadsOptions(options, data) {
2361 const control = builders.control({
2362 classes: ['sharing-image-config-control'],
2363 label: config_('Upload directory', 'sharing-image'),
2364 append: options
2365 });
2366 const fieldset = builders.element('div', {
2367 classes: ['sharing-image-config-control-fieldset'],
2368 append: control
2369 });
2370 builders.radio({
2371 classes: ['sharing-image-config-control-radio'],
2372 attributes: {
2373 name: config_params.name + '[uploads]',
2374 value: 'default'
2375 },
2376 label: config_('Use default uploads directory', 'sharing-image'),
2377 checked: data.uploads || 'default'
2378 }, fieldset);
2379 builders.radio({
2380 classes: ['sharing-image-config-control-radio'],
2381 attributes: {
2382 name: config_params.name + '[uploads]',
2383 value: 'custom'
2384 },
2385 label: config_('Choose custom storage for posters', 'sharing-image'),
2386 checked: data.uploads || 'default'
2387 }, fieldset);
2388 const input = builders.input({
2389 classes: ['sharing-image-config-control-input'],
2390 attributes: {
2391 name: config_params.name + '[storage]',
2392 value: data.storage || config_params.links.storage,
2393 disabled: 'disabled'
2394 }
2395 }, control);
2396 builders.element('small', {
2397 text: config_('Use relative path from site root. Directory should be writeable.', 'sharing-image'),
2398 append: control
2399 });
2400 control.querySelectorAll('input').forEach(radio => {
2401 if ('radio' !== radio.type) {
2402 return;
2403 } // Show storage input for checked custom radio.
2404
2405
2406 if (radio.checked && 'custom' === radio.value) {
2407 input.disabled = false;
2408 }
2409
2410 radio.addEventListener('change', () => {
2411 input.disabled = true;
2412
2413 if ('custom' === radio.value) {
2414 input.disabled = false;
2415 }
2416 });
2417 });
2418 }
2419 /**
2420 * Create format and quality poster options.
2421 *
2422 * @param {HTMLElement} options Options form element.
2423 * @param {Object} data Config data object.
2424 */
2425
2426
2427 function createImageOptions(options, data) {
2428 const control = builders.control({
2429 classes: ['sharing-image-config-control', 'control-extra'],
2430 label: config_('Poster image format', 'sharing-image'),
2431 help: config_('The higher the value, the less compression. Availible for JPEG only.', 'sharing-image'),
2432 fields: [{
2433 group: 'select',
2434 classes: ['sharing-image-config-control-select'],
2435 options: {
2436 jpg: config_('JPEG', 'sharing-image'),
2437 png: config_('PNG', 'sharing-image')
2438 },
2439 attributes: {
2440 name: config_params.name + '[format]'
2441 },
2442 selected: data.format || 'jpg'
2443 }, {
2444 group: 'input',
2445 classes: ['sharing-image-config-control-range'],
2446 attributes: {
2447 type: 'range',
2448 name: config_params.name + '[quality]',
2449 min: 10,
2450 max: 100,
2451 step: 5,
2452 value: data.quality || '90',
2453 disabled: 'disabled'
2454 },
2455 label: config_('Image quality', 'sharing-image')
2456 }],
2457 append: options
2458 }); // Find control format select.
2459
2460 const format = control.querySelector('select'); // Find control quiality input.
2461
2462 const quality = control.querySelector('input');
2463
2464 if ('jpg' === format.value) {
2465 quality.disabled = false;
2466 }
2467
2468 format.addEventListener('change', () => {
2469 quality.disabled = true;
2470
2471 if ('jpg' === format.value) {
2472 quality.disabled = false;
2473 }
2474 });
2475 }
2476 /**
2477 * Create autogenerate poster options.
2478 *
2479 * @param {HTMLElement} options Options form element.
2480 * @param {Object} data Config data object.
2481 * @param {Array} templates List of templates.
2482 */
2483
2484
2485 function createAutogenerateOptions(options, data, templates) {
2486 const fields = {}; // Add the option for disabling feature.
2487
2488 fields.manual = config_('Disable auto generation', 'sharing-image');
2489 templates.forEach((template, i) => {
2490 fields[i] = template.title || config_('Untitled', 'sharing-image');
2491 });
2492 let selected = data.autogenerate;
2493
2494 if (typeof selected === 'undefined') {
2495 selected = 'manual';
2496 }
2497
2498 builders.control({
2499 classes: ['sharing-image-config-control'],
2500 label: config_('Auto generate poster', 'sharing-image'),
2501 help: config_('This template will be applied automatically on post save.', 'sharing-image'),
2502 fields: [{
2503 group: 'select',
2504 classes: ['sharing-image-config-control-select'],
2505 options: fields,
2506 attributes: {
2507 name: config_params.name + '[autogenerate]'
2508 },
2509 selected: String(selected)
2510 }],
2511 append: options
2512 });
2513 }
2514 /**
2515 * Create required form meta fields.
2516 *
2517 * @param {HTMLElement} options Options form element.
2518 */
2519
2520
2521 function createMetaFields(options) {
2522 builders.element('input', {
2523 attributes: {
2524 type: 'hidden',
2525 name: 'action',
2526 value: config_params.name
2527 },
2528 append: options
2529 });
2530 builders.element('input', {
2531 attributes: {
2532 type: 'hidden',
2533 name: 'sharing_image_nonce',
2534 value: config_params.nonce
2535 },
2536 append: options
2537 });
2538 builders.element('button', {
2539 text: config_('Save changes', 'sharing-image'),
2540 classes: ['button', 'button-primary'],
2541 attributes: {
2542 type: 'submit'
2543 },
2544 append: options
2545 });
2546 }
2547 /**
2548 * Create templates catalog from options.
2549 *
2550 * @param {HTMLElement} content Settings content element.
2551 * @param {Object} settings Global settings field.
2552 */
2553
2554
2555 function createConfig(content, settings) {
2556 config_params = settings; // Set params name for template form fields.
2557
2558 config_params.name = 'sharing_image_config'; // Find config element
2559
2560 const config = content.querySelector('.sharing-image-config');
2561
2562 if (null === config) {
2563 return;
2564 }
2565
2566 const options = builders.element('form', {
2567 classes: ['sharing-image-config-options'],
2568 attributes: {
2569 action: config_params.links.action,
2570 method: 'POST'
2571 },
2572 append: config
2573 });
2574 const data = config_params.config || {};
2575 const templates = config_params.templates || []; // Poster image options.
2576
2577 createImageOptions(options, data); // Autogenerate poster.
2578
2579 createAutogenerateOptions(options, data, templates); // Uploads directory options.
2580
2581 createUploadsOptions(options, data); // Default poster.
2582
2583 createDefaultOptions(options, data); // Create required form fields
2584
2585 createMetaFields(options);
2586 }
2587
2588 /* harmony default export */ const config = (createConfig);
2589 ;// CONCATENATED MODULE: ./src/scripts/sections/premium.js
2590 /**
2591 * Premium settings tab.
2592 */
2593
2594 /* global ajaxurl:true */
2595
2596 const {
2597 __: premium_
2598 } = wp.i18n; // Store global scriot object for settings page.
2599
2600 let premium_params = null; // Premium HTML emelent.
2601
2602 let premium = null;
2603 /**
2604 * Parse error code from settings or AJAX response.
2605 *
2606 * @param {string} code Error code from settings or AJAX response.
2607 * @param {string} title Prepended error title. Optional.
2608 */
2609
2610 function parseErrorCode(code, title) {
2611 const message = [];
2612
2613 if (undefined === title) {
2614 title = premium_('Verification failed.', 'sharing-image');
2615 }
2616
2617 message.push(title);
2618
2619 switch (code) {
2620 case 'LIMIT_EXCEEDED':
2621 message.push(premium_('The number of valid licenses for this key has been exceeded.', 'sharing-image'));
2622 break;
2623
2624 case 'KEY_NOT_FOUND':
2625 message.push(premium_('Premium key is invalid or expired.', 'sharing-image'));
2626 break;
2627
2628 case 'SERVER_ERROR':
2629 message.push(premium_('Unable to get a response from the verification server.', 'sharing-image'));
2630 break;
2631 }
2632
2633 return message.join(' ');
2634 }
2635 /**
2636 * Show premium warning message.
2637 *
2638 * @param {string} message Warning message.
2639 */
2640
2641
2642 function showPremiumError(message) {
2643 // Try to find warning element.
2644 const warning = premium.querySelector('.sharing-image-premium-warning');
2645
2646 if (null === warning) {
2647 return;
2648 }
2649
2650 warning.classList.add('warning-visible');
2651 warning.textContent = message || premium_('Unknown request error', 'sharing-image');
2652 }
2653 /**
2654 * Remove warning message block.
2655 */
2656
2657
2658 function hidePremiumError() {
2659 // Try to find warning element.
2660 const warning = premium.querySelector('.sharing-image-premium-warning');
2661
2662 if (null === warning) {
2663 return;
2664 }
2665
2666 warning.classList.remove('warning-visible');
2667 }
2668 /**
2669 * Revoke Premium key.
2670 *
2671 * @param {HTMLElement} access Access form element.
2672 */
2673
2674
2675 function revokePremium(access) {
2676 access.classList.add('access-loader');
2677 const request = new XMLHttpRequest();
2678 request.open('POST', ajaxurl);
2679 request.responseType = 'json'; // Create data bundle using form data.
2680
2681 const bundle = new window.FormData(access);
2682 bundle.set('action', 'sharing_image_revoke');
2683 hidePremiumError();
2684 request.addEventListener('load', () => {
2685 const response = request.response || {}; // Hide form loader class.
2686
2687 access.classList.remove('access-loader');
2688
2689 if (!response.data) {
2690 return showPremiumError();
2691 }
2692
2693 if (!response.success) {
2694 return showPremiumError(response.data);
2695 }
2696
2697 premium_params.license = response.data; // Refresh premium fields.
2698
2699 preparePremiumFields();
2700 });
2701 request.addEventListener('error', () => {
2702 showPremiumError(); // Hide form loader class.
2703
2704 access.classList.remove('access-loader');
2705 });
2706 request.send(bundle);
2707 }
2708 /**
2709 * Verify Premium key.
2710 *
2711 * @param {HTMLElement} access Access form element.
2712 */
2713
2714
2715 function verifyPremium(access) {
2716 access.classList.add('access-loader');
2717 const request = new XMLHttpRequest();
2718 request.open('POST', ajaxurl);
2719 request.responseType = 'json'; // Create data bundle using form data.
2720
2721 const bundle = new window.FormData(access);
2722 bundle.set('action', 'sharing_image_verify');
2723 hidePremiumError();
2724 request.addEventListener('load', () => {
2725 const response = request.response || {}; // Hide form loader class.
2726
2727 access.classList.remove('access-loader');
2728
2729 if (!response.data) {
2730 return showPremiumError();
2731 }
2732
2733 if (!response.success) {
2734 return showPremiumError(parseErrorCode(response.code, response.data));
2735 }
2736
2737 premium_params.license = response.data; // Refresh premium fields.
2738
2739 preparePremiumFields();
2740 });
2741 request.addEventListener('error', () => {
2742 showPremiumError(); // Hide form loader class.
2743
2744 access.classList.remove('access-loader');
2745 });
2746 request.send(bundle);
2747 }
2748 /**
2749 * Show verify form if stil not premium.
2750 *
2751 * @param {HTMLElement} access Access HTML element.
2752 * @param {Object} license License data.
2753 */
2754
2755
2756 function showVerifyForm(access, license) {
2757 if (license.error) {
2758 showPremiumError(parseErrorCode(license.error));
2759 }
2760
2761 builders.element('strong', {
2762 text: premium_('Do you already have a key? Enter it here', 'sharing-image'),
2763 append: access
2764 });
2765 const verify = builders.element('div', {
2766 classes: ['sharing-image-premium-verify'],
2767 append: access
2768 });
2769 builders.element('input', {
2770 label: premium_('Your Premium key', 'sharing-image'),
2771 attributes: {
2772 type: 'text',
2773 name: 'sharing_image_key',
2774 value: license.key
2775 },
2776 append: verify
2777 });
2778 builders.element('button', {
2779 classes: ['button'],
2780 text: premium_('Submit', 'sharing-image'),
2781 attributes: {
2782 type: 'submit'
2783 },
2784 append: verify
2785 });
2786 builders.element('span', {
2787 classes: ['spinner'],
2788 append: verify
2789 });
2790 access.addEventListener('submit', e => {
2791 e.preventDefault();
2792 verifyPremium(access);
2793 });
2794 }
2795 /**
2796 * Show alert for develop license mode.
2797 */
2798
2799
2800 function showDevelopAlert() {
2801 showPremiumError(premium_('Using plugin with a development license is prohibited in production.', 'sharing-image'));
2802 }
2803 /**
2804 * Show revoke Premium button.
2805 *
2806 * @param {HTMLElement} access Access HTML element.
2807 */
2808
2809
2810 function showRevokeButton(access) {
2811 const revoke = builders.element('div', {
2812 classes: ['sharing-image-premium-revoke'],
2813 append: access
2814 });
2815 const description = [];
2816 description.push(premium_('Disabling premium mode will not remove the license for this domain.', 'sharing-image'));
2817 description.push(premium_('Your current key will also be saved in the plugin settings.', 'sharing-image'));
2818 description.push(premium_('Use key management tool to delete the license for the site.', 'sharing-image'));
2819 builders.element('p', {
2820 text: description.join(' '),
2821 append: revoke
2822 });
2823 builders.element('button', {
2824 classes: ['button'],
2825 text: premium_('Disable Premium'),
2826 attributes: {
2827 type: 'submit'
2828 },
2829 append: revoke
2830 });
2831 builders.element('span', {
2832 classes: ['spinner'],
2833 append: revoke
2834 });
2835 access.addEventListener('submit', e => {
2836 e.preventDefault();
2837 revokePremium(access);
2838 });
2839 }
2840 /**
2841 * Show permit information.
2842 *
2843 * @param {HTMLElement} access Access HTML element.
2844 * @param {string} key License key from settings.
2845 */
2846
2847
2848 function showLicenseInfo(access, key) {
2849 const permit = builders.element('div', {
2850 classes: ['sharing-image-premium-permit'],
2851 append: access
2852 });
2853 const button = builders.element('button', {
2854 classes: ['sharing-image-premium-show', 'button'],
2855 text: premium_('Show License key'),
2856 attributes: {
2857 type: 'button'
2858 },
2859 append: permit
2860 });
2861 button.addEventListener('click', () => {
2862 permit.classList.toggle('permit-visible');
2863 });
2864 builders.element('strong', {
2865 text: key,
2866 append: permit
2867 });
2868 return permit;
2869 }
2870 /**
2871 * Show fields if user has the license.
2872 *
2873 * @param {HTMLElement} access Access HTML element.
2874 * @param {Object} license License data.
2875 */
2876
2877
2878 function showPremiumData(access, license) {
2879 premium.classList.add('premium-enabled');
2880
2881 if (license.develop) {
2882 return showDevelopAlert();
2883 }
2884
2885 if (license.key) {
2886 showLicenseInfo(access, license.key);
2887 }
2888
2889 showRevokeButton(access);
2890 }
2891 /**
2892 * Set premium fields according settings.
2893 */
2894
2895
2896 function preparePremiumFields() {
2897 let access = premium.querySelector('.sharing-image-premium-access');
2898
2899 if (null !== access) {
2900 premium.removeChild(access);
2901 }
2902
2903 access = builders.element('form', {
2904 classes: ['sharing-image-premium-access'],
2905 attributes: {
2906 action: '',
2907 method: 'POST'
2908 },
2909 append: premium
2910 });
2911 premium.classList.remove('premium-enabled');
2912 builders.element('input', {
2913 attributes: {
2914 type: 'hidden',
2915 name: 'sharing_image_nonce',
2916 value: premium_params.nonce
2917 },
2918 append: access
2919 });
2920 const license = premium_params.license || {}; // Show fields if user has the license.
2921
2922 if (license.premium || license.develop) {
2923 return showPremiumData(access, license);
2924 }
2925
2926 return showVerifyForm(access, license);
2927 }
2928 /**
2929 * Create templates catalog from options.
2930 *
2931 * @param {HTMLElement} content Settings content element.
2932 * @param {Object} settings Global settings field.
2933 */
2934
2935
2936 function createPremium(content, settings) {
2937 premium_params = settings; // Find premium element
2938
2939 premium = content.querySelector('.sharing-image-premium');
2940
2941 if (null === premium) {
2942 return;
2943 }
2944
2945 builders.element('div', {
2946 classes: ['sharing-image-premium-warning'],
2947 append: premium
2948 });
2949 preparePremiumFields();
2950 }
2951
2952 /* harmony default export */ const sections_premium = (createPremium);
2953 ;// CONCATENATED MODULE: ./src/scripts/sections/picker.js
2954 /**
2955 * Metabox handler.
2956 */
2957
2958 /* global ajaxurl:true */
2959
2960 const {
2961 __: picker_
2962 } = wp.i18n; // Store global script object for metabox.
2963
2964 let picker_params = null; // Poster HTML element.
2965
2966 let poster = null; // Is gutenberg editor used.
2967
2968 let gutenberg = false;
2969 /**
2970 * Show picker warning message.
2971 *
2972 * @param {string} message Warning message.
2973 */
2974
2975 function showPickerError(message) {
2976 const picker = poster.parentNode; // Try to find warning element.
2977
2978 const warning = picker.querySelector('.sharing-image-picker-warning');
2979
2980 if (null === warning) {
2981 return;
2982 }
2983
2984 warning.classList.add('warning-visible');
2985 warning.textContent = message || picker_('Unknown generation error', 'sharing-image');
2986 }
2987 /**
2988 * Remove warning message block.
2989 */
2990
2991
2992 function hidePickerError() {
2993 const picker = poster.parentNode; // Try to find warning element.
2994
2995 const warning = picker.querySelector('.sharing-image-picker-warning');
2996
2997 if (null === warning) {
2998 return;
2999 }
3000
3001 warning.classList.remove('warning-visible');
3002 }
3003 /**
3004 * Handle poster generation action.
3005 *
3006 * @param {HTMLElement} picker Picker element.
3007 */
3008
3009
3010 function generatePoster(picker) {
3011 const request = new XMLHttpRequest();
3012 request.open('POST', ajaxurl);
3013 request.responseType = 'json';
3014 poster.classList.add('poster-loader'); // Create data form data bundle.
3015
3016 const bundle = new window.FormData();
3017 bundle.set('action', 'sharing_image_generate');
3018 picker.querySelectorAll('[name]').forEach(field => {
3019 bundle.append(field.name, field.value);
3020 });
3021 hidePickerError();
3022 request.addEventListener('load', () => {
3023 const response = request.response || {}; // Hide preview loader on request complete.
3024
3025 poster.classList.remove('poster-loader');
3026
3027 if (!response.data) {
3028 return showPickerError();
3029 }
3030
3031 if (!response.success) {
3032 return showPickerError(response.data);
3033 }
3034
3035 for (const key in response.data) {
3036 // Find all poster input fields and set response data value.
3037 poster.querySelectorAll('input').forEach(input => {
3038 const name = picker_params.name + '[' + key + ']';
3039
3040 if (name === input.name) {
3041 input.value = response.data[key];
3042 }
3043 });
3044 }
3045
3046 let image = poster.querySelector('img');
3047
3048 if (null === image) {
3049 image = builders.element('img', {
3050 append: poster
3051 });
3052 }
3053
3054 image.src = response.data.poster; // Show the poster.
3055
3056 poster.classList.add('poster-visible');
3057 });
3058 request.addEventListener('error', () => {
3059 showPickerError(); // Hide preview loader on request complete.
3060
3061 poster.classList.remove('poster-loader');
3062 });
3063 request.send(bundle);
3064 }
3065 /**
3066 * Create designer template selector.
3067 *
3068 * @param {HTMLElement} picker Picker element.
3069 * @param {HTMLElement} designer Designer element.
3070 * @param {Object} selected Seleted template.
3071 */
3072
3073
3074 function createTemplate(picker, designer, selected) {
3075 const fields = {};
3076 picker_params.templates.forEach((template, i) => {
3077 fields[i] = template.title || picker_('Untitled', 'sharing-image');
3078 });
3079 const template = builders.select({
3080 classes: ['sharing-image-picker-template'],
3081 options: fields,
3082 attributes: {
3083 name: picker_params.name + '[template]'
3084 },
3085 selected: String(selected)
3086 }, picker);
3087 template.addEventListener('change', () => {
3088 const fieldset = designer.childNodes;
3089
3090 for (let i = 0; i < fieldset.length; i++) {
3091 fieldset[i].classList.remove('fieldset-visible');
3092
3093 if (i === parseInt(template.value)) {
3094 fieldset[i].classList.add('fieldset-visible');
3095 }
3096 }
3097 });
3098 return template;
3099 }
3100 /**
3101 * Prefill caption fields for classic editor.
3102 *
3103 * @param {HTMLElement} textarea Caption textarea field.
3104 * @param {string} preset Preset field.
3105 */
3106
3107
3108 function fillClassicEditorPreset(textarea, preset) {
3109 const source = document.getElementById(preset);
3110
3111 if (null === source) {
3112 return;
3113 }
3114
3115 const updateCaption = () => {
3116 textarea.value = source.value;
3117 };
3118
3119 source.addEventListener('change', updateCaption); // Stop textarea update after first user input.
3120
3121 textarea.addEventListener('change', () => {
3122 source.removeEventListener('change', updateCaption);
3123 });
3124 updateCaption();
3125 }
3126 /**
3127 * Prefill caption fields for block editor.
3128 *
3129 * @param {HTMLElement} textarea Caption textarea field.
3130 * @param {string} preset Preset field.
3131 */
3132
3133
3134 function fillBlockEditorPreset(textarea, preset) {
3135 const getAttribute = () => {
3136 return wp.data.select('core/editor').getEditedPostAttribute(preset);
3137 };
3138
3139 let attribute = getAttribute();
3140 wp.data.subscribe(() => {
3141 const updated = getAttribute();
3142
3143 if (attribute !== updated) {
3144 textarea.textContent = updated;
3145 }
3146
3147 attribute = updated;
3148 });
3149 }
3150 /**
3151 * Try to prefill caption field.
3152 *
3153 * @param {HTMLElement} textarea Caption textarea field.
3154 * @param {string} preset Preset field.
3155 */
3156
3157
3158 function fillCaptionPreset(textarea, preset) {
3159 if (gutenberg) {
3160 return fillBlockEditorPreset(textarea, preset);
3161 }
3162
3163 fillClassicEditorPreset(textarea, preset);
3164 }
3165 /**
3166 * Create designer attachment field for dynamic background.
3167 *
3168 * @param {HTMLElement} fieldset Fieldset element.
3169 * @param {Object} template Template data.
3170 * @param {Array} values Template fieldset values.
3171 * @param {string} name Field name attribute.
3172 */
3173
3174
3175 function createDesignerAttachment(fieldset, template, values, name) {
3176 if ('dynamic' !== template.background) {
3177 return;
3178 }
3179
3180 builders.media({
3181 name: name + '[attachment]',
3182 classes: ['sharing-image-picker-media'],
3183 value: values.attachment,
3184 link: picker_params.links.uploads,
3185 labels: {
3186 button: picker_('Upload background', 'sharing-image'),
3187 heading: picker_('Select background image', 'sharing-image'),
3188 details: picker_('Attachment', 'sharing-image')
3189 },
3190 append: fieldset
3191 });
3192 }
3193 /**
3194 * Create designer captions for text layers.
3195 *
3196 * @param {HTMLElement} fieldset Fieldset element.
3197 * @param {Object} template Template data.
3198 * @param {Array} values Template fieldset values.
3199 * @param {string} name Field name attribute.
3200 */
3201
3202
3203 function createDesignerCaptions(fieldset, template, values, name) {
3204 const captions = values.captions || []; // Set default layers list.
3205
3206 template.layers = template.layers || [];
3207 template.layers.forEach((layer, n) => {
3208 if ('text' !== layer.type || !layer.dynamic) {
3209 return;
3210 }
3211
3212 const textarea = builders.textarea({
3213 classes: ['sharing-image-picker-caption'],
3214 label: layer.title || null,
3215 attributes: {
3216 name: name + `[captions][${n}]`
3217 }
3218 }, fieldset);
3219
3220 if (!captions[n]) {
3221 fillCaptionPreset(textarea, layer.preset);
3222 }
3223
3224 textarea.textContent = captions[n];
3225 });
3226 }
3227 /**
3228 * Create fields designer.
3229 *
3230 * @param {HTMLElement} picker Picker element.
3231 * @param {Object} data Picker data object.
3232 */
3233
3234
3235 function picker_createDesigner(picker, data) {
3236 const designer = builders.element('div', {
3237 classes: ['sharing-image-picker-designer']
3238 });
3239 let selected = data.template || 0; // Reset selected template if index undefined.
3240
3241 if (!picker_params.templates[selected]) {
3242 selected = 0;
3243 } // Create designer fields
3244
3245
3246 picker_params.templates.forEach((template, i) => {
3247 // Set default layers list.
3248 template.layers = template.layers || [];
3249 const fieldset = builders.element('div', {
3250 classes: ['sharing-image-picker-fieldset'],
3251 append: designer
3252 });
3253
3254 if (i === parseInt(selected)) {
3255 fieldset.classList.add('fieldset-visible');
3256 }
3257
3258 let values = {};
3259
3260 if (data.fieldset && data.fieldset[i]) {
3261 values = data.fieldset[i];
3262 }
3263
3264 const name = picker_params.name + `[fieldset][${i}]`;
3265 builders.element('input', {
3266 attributes: {
3267 type: 'hidden',
3268 name: name
3269 },
3270 append: fieldset
3271 }); // Create attachment field.
3272
3273 createDesignerAttachment(fieldset, template, values, name); // Create all caption fields.
3274
3275 createDesignerCaptions(fieldset, template, values, name);
3276 }); // Create template selector.
3277
3278 if (picker_params.templates.length > 1) {
3279 createTemplate(picker, designer, selected);
3280 }
3281
3282 picker.appendChild(designer);
3283 }
3284 /**
3285 * Create button to generate new metabox poster.
3286 *
3287 * @param {HTMLElement} picker Picker element.
3288 * @param {HTMLElement} manager Manager element.
3289 */
3290
3291
3292 function picker_createGenerateButton(picker, manager) {
3293 const button = builders.element('button', {
3294 classes: ['sharing-image-picker-generate', 'button'],
3295 text: picker_('Generate', 'sharing-image'),
3296 attributes: {
3297 type: 'button'
3298 },
3299 append: manager
3300 });
3301 button.addEventListener('click', () => {
3302 generatePoster(picker);
3303 });
3304 }
3305 /**
3306 * Create button to delete current metabox poster.
3307 *
3308 * @param {HTMLElement} manager Manager element.
3309 */
3310
3311
3312 function picker_createDeleteButton(manager) {
3313 const button = builders.element('button', {
3314 classes: ['sharing-image-picker-delete', 'button', 'button-delete'],
3315 text: picker_('Remove', 'sharing-image'),
3316 attributes: {
3317 type: 'button'
3318 },
3319 append: manager
3320 });
3321 button.addEventListener('click', () => {
3322 const image = poster.querySelector('img');
3323
3324 if (null !== image) {
3325 poster.removeChild(image);
3326 }
3327
3328 poster.querySelectorAll('input').forEach(input => {
3329 input.value = '';
3330 });
3331 poster.classList.remove('poster-visible');
3332 });
3333 }
3334 /**
3335 * Create picker manager.
3336 *
3337 * @param {HTMLElement} picker Picker element.
3338 */
3339
3340
3341 function createManager(picker) {
3342 const manager = builders.element('div', {
3343 classes: ['sharing-image-picker-manager'],
3344 append: picker
3345 }); // Create poster generation button.
3346
3347 picker_createGenerateButton(picker, manager); // Create poster removing button.
3348
3349 picker_createDeleteButton(manager);
3350 builders.element('span', {
3351 classes: ['sharing-image-picker-spinner', 'spinner'],
3352 append: manager
3353 });
3354 }
3355 /**
3356 * Create poster block.
3357 *
3358 * @param {HTMLElement} picker Picker element.
3359 * @param {Object} data Picker data object.
3360 */
3361
3362
3363 function createPoster(picker, data) {
3364 poster = builders.element('div', {
3365 classes: ['sharing-image-picker-poster'],
3366 append: picker
3367 });
3368
3369 if (data.poster) {
3370 builders.element('img', {
3371 attributes: {
3372 src: data.poster,
3373 alt: ''
3374 },
3375 append: poster
3376 });
3377 poster.classList.add('poster-visible');
3378 }
3379
3380 builders.element('input', {
3381 attributes: {
3382 type: 'hidden',
3383 name: picker_params.name + '[poster]',
3384 value: data.poster
3385 },
3386 append: poster
3387 });
3388 builders.element('input', {
3389 attributes: {
3390 type: 'hidden',
3391 name: picker_params.name + '[width]',
3392 value: data.width
3393 },
3394 append: poster
3395 });
3396 builders.element('input', {
3397 attributes: {
3398 type: 'hidden',
3399 name: picker_params.name + '[height]',
3400 value: data.height
3401 },
3402 append: poster
3403 });
3404 return poster;
3405 }
3406 /**
3407 * Check that the poster sizes are set or show an error message.
3408 *
3409 * @param {Object} data Picker data object.
3410 */
3411
3412
3413 function showSizesWarning(data) {
3414 if (!data.poster) {
3415 return;
3416 }
3417
3418 if (!data.width || !data.height) {
3419 showPickerError(picker_('Image sizes are not set. Regenerate the poster.', 'sharing-image'));
3420 }
3421 }
3422 /**
3423 * Get new config with AJAX call and reinit metabox.
3424 *
3425 * @param {HTMLElement} widget Widget element.
3426 */
3427
3428
3429 const rebuildPicker = widget => {
3430 const request = new XMLHttpRequest();
3431 request.open('POST', ajaxurl);
3432 request.responseType = 'json';
3433 poster.classList.add('poster-loader'); // We need post ID for this request.
3434
3435 const postId = wp.data.select('core/editor').getCurrentPostId(); // Create data form data bundle.
3436
3437 const bundle = new window.FormData();
3438 bundle.set('action', 'sharing_image_rebuild');
3439 bundle.set('post', postId); // Find picker child.
3440
3441 const picker = widget.querySelector('.sharing-image-picker');
3442 picker.querySelectorAll('[name]').forEach(field => {
3443 if ('sharing_image_nonce' === field.name) {
3444 bundle.append(field.name, field.value);
3445 }
3446 });
3447 hidePickerError();
3448 request.addEventListener('load', () => {
3449 const response = request.response || {}; // Hide preview loader on request complete.
3450
3451 poster.classList.remove('poster-loader');
3452
3453 if (!response.data) {
3454 return showPickerError();
3455 }
3456
3457 if (!response.success) {
3458 return showPickerError(response.data);
3459 }
3460
3461 buildPicker(widget, response.data);
3462 });
3463 request.addEventListener('error', () => {
3464 showPickerError(); // Hide preview loader on request complete.
3465
3466 poster.classList.remove('poster-loader');
3467 });
3468 request.send(bundle);
3469 };
3470 /**
3471 * Wait Gutenberg post saving and reinit tasks list.
3472 *
3473 * @param {HTMLElement} widget Widget element.
3474 */
3475
3476
3477 const subscribeOnSaving = widget => {
3478 let wasSavingPost = wp.data.select('core/edit-post').isSavingMetaBoxes();
3479 wp.data.subscribe(() => {
3480 const isSavingPost = wp.data.select('core/edit-post').isSavingMetaBoxes();
3481
3482 if (wasSavingPost && !isSavingPost) {
3483 rebuildPicker(widget);
3484 }
3485
3486 wasSavingPost = isSavingPost;
3487 });
3488 };
3489 /**
3490 * Build metabox fields.
3491 *
3492 * @param {HTMLElement} widget Widget element.
3493 * @param {Object} settings Global settings object.
3494 */
3495
3496
3497 function buildPicker(widget, settings) {
3498 picker_params = settings; // Set params name for template form fields.
3499
3500 picker_params.name = 'sharing_image_picker';
3501
3502 while (widget.firstChild) {
3503 widget.removeChild(widget.lastChild);
3504 }
3505
3506 if ('taxonomy' === picker_params.context) {
3507 const title = builders.element('div', {
3508 classes: ['sharing-image-title'],
3509 append: widget
3510 });
3511 builders.element('strong', {
3512 text: picker_('Sharing Image', 'sharing-image'),
3513 append: title
3514 });
3515 }
3516
3517 const picker = builders.element('div', {
3518 classes: ['sharing-image-picker'],
3519 append: widget
3520 });
3521 const data = picker_params.meta || {}; // Create poster block.
3522
3523 createPoster(picker, data); // Create fields designer.
3524
3525 picker_createDesigner(picker, data);
3526 builders.element('div', {
3527 classes: ['sharing-image-picker-warning'],
3528 append: picker
3529 }); // Show unset poster sizes warning.
3530
3531 showSizesWarning(data); // Create metabox manager block.
3532
3533 createManager(picker);
3534 builders.element('input', {
3535 attributes: {
3536 type: 'hidden',
3537 name: 'sharing_image_nonce',
3538 value: picker_params.nonce
3539 },
3540 append: picker
3541 });
3542 }
3543 /**
3544 * Create metabox generator picker and subscribe to events.
3545 *
3546 * @param {HTMLElement} widget Widget element.
3547 * @param {Object} settings Global settings object.
3548 */
3549
3550
3551 function createPicker(widget, settings) {
3552 buildPicker(widget, settings);
3553 gutenberg = wp.data && wp.data.select('core/editor');
3554
3555 if (gutenberg) {
3556 subscribeOnSaving(widget);
3557 }
3558 }
3559
3560 /* harmony default export */ const picker = (createPicker);
3561 ;// CONCATENATED MODULE: ./src/scripts/sections/index.js
3562
3563
3564
3565
3566
3567 const Section = {
3568 catalog: catalog,
3569 editor: sections_editor,
3570 config: config,
3571 premium: sections_premium,
3572 picker: picker
3573 };
3574 /* harmony default export */ const sections = (Section);
3575 ;// CONCATENATED MODULE: ./src/scripts/widget.js
3576
3577 /**
3578 * Init metabox handler.
3579 */
3580
3581 (function () {
3582 if (typeof 'undefined' === wp) {
3583 return;
3584 }
3585
3586 const object = window.sharingImageWidget || {}; // Set default templates empty list.
3587
3588 object.templates = object.templates || []; // Find metabox element.
3589
3590 const widgets = document.querySelectorAll('.sharing-image-widget');
3591 widgets.forEach(widget => {
3592 if (object.context) {
3593 widget.classList.add(`widget-${object.context}`);
3594 }
3595
3596 sections.picker(widget, object);
3597 });
3598 })();
3599 /******/ })()
3600 ;