PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.11
VikBooking Hotel Booking Engine & PMS v1.8.11
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
vikbooking / admin / resources / quill / vik-content-builder.js

vik-content-builder.js in VikBooking Hotel Booking Engine & PMS 1.8.11, at admin/resources/quill/vik-content-builder.js

252 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * @package VikBooking
3 * @subpackage vik-content-builder
4 * @author Alessio Gaggii - E4J srl
5 * @copyright Copyright (C) 2025 E4J srl. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE
7 * @link https://vikwp.com - https://e4j.com - https://e4jconnect.com
8 */
9
10 /**
11 * Class handler to store the editor instances and switch building modes.
12 *
13 * AJAX requests may load this asset multiple times, so we declare an anonymous class, but
14 * we assign it to a variable using "var" because it is not block-scoped like "let" or "const".
15 */
16 var VikContentBuilder = class {
17
18 /** @var array */
19 static editors = new Array;
20
21 /**
22 * Push the instance of an editor to the pool.
23 *
24 * @param object editor The editor instance.
25 *
26 * @return void
27 */
28 static pushEditor(editor) {
29 this.editors.push(editor);
30 }
31
32 /**
33 * Switch between building modes.
34 *
35 * @param object elem DOM element object clicked.
36 * @param string mode Optional mode to switch to.
37 *
38 * @return void
39 */
40 static switchMode(elem, mode) {
41 // determine who triggered the mode switch
42 let btn = jQuery(elem);
43 if (mode) {
44 btn = btn.closest('.vik-contentbuilder-wrapper').find('.vik-contentbuilder-switcher-btn[data-switch="' + mode + '"]');
45 }
46
47 // determine the new building mode to activate
48 let switch_to = btn.attr("data-switch");
49
50 // toggle active mode button
51 btn.parent('.vik-contentbuilder-switcher').find('.vik-contentbuilder-switcher-btn').removeClass('vik-contentbuilder-switcher-btn-active');
52 btn.addClass('vik-contentbuilder-switcher-btn-active');
53
54 // hide any mode content except the wanted one
55 let ed_conts = btn.closest('.vik-contentbuilder-wrapper');
56 ed_conts.find('.vik-contentbuilder-inner [data-switch]').not('[data-switch="' + switch_to + '"]').hide();
57
58 // target building mode content
59 let target = ed_conts.find('.vik-contentbuilder-inner [data-switch="' + switch_to + '"]');
60
61 // determine how to display the requested building mode
62 if (switch_to.indexOf('modal') >= 0) {
63 // modal window
64 let container_id = target.attr('data-container');
65 if (!container_id) {
66 container_id = switch_to;
67 }
68
69 // get the proper visual editor container to move and display
70 let editor_container = ed_conts.find('.vik-contentbuilder-inner [data-switch="' + container_id + '"]').find('.vik-contentbuilder-editor-wrap');
71 if (editor_container.length) {
72 // check if we also have an inline visual editor mode
73 let has_inline_ve = ed_conts.find('.vik-contentbuilder-inner [data-switch="visual"]').length;
74
75 // display the modal window with the visual editor
76 let modal_body = VBOCore.displayModal({
77 suffix: 'contentbuilder-' + switch_to,
78 extra_class: 'vbo-modal-rounded vbo-modal-large vbo-modal-nofooter vik-contentbuilder-modal-content',
79 lock_scroll: true,
80 draggable: false,
81 onDismiss: () => {
82 // put back the editor container element on its original position
83 editor_container.appendTo(ed_conts.find('.vik-contentbuilder-inner [data-switch="' + container_id + '"]'));
84 // switch to text mode or inline visual mode
85 VikContentBuilder.switchMode(btn, (has_inline_ve ? 'visual' : 'text'));
86 },
87 });
88
89 // move the editor container element to the body of the modal window
90 editor_container.appendTo(modal_body);
91 } else {
92 // unexpcted markup, fallback to displaying the target
93 target.show();
94 }
95 } else {
96 // show document target element
97 target.show();
98 }
99
100 // handle text mode special tag buttons
101 let text_mode_tags = jQuery('.vik-contentbuilder-textmode-sptags');
102 if (text_mode_tags.length && switch_to.indexOf('text') >= 0) {
103 text_mode_tags.show();
104 } else if (text_mode_tags.length && switch_to.indexOf('visual') >= 0) {
105 text_mode_tags.hide();
106 }
107 }
108 }
109
110 /**
111 * Class handler for uploading an image file and display it through Quill.
112 *
113 * AJAX requests may load this asset multiple times, so we declare an anonymous class, but
114 * we assign it to a variable using "var" because it is not block-scoped like "let" or "const".
115 */
116 var VikContentBuilderImageHandler = class {
117
118 constructor(editor, endpoint) {
119 this.editor = editor;
120 this.endpoint = endpoint || null;
121 }
122
123 setEndpoint(endpoint) {
124 if (endpoint && endpoint.length) {
125 this.endpoint = endpoint;
126 }
127 return this;
128 }
129
130 present() {
131 var input = document.createElement('input');
132 input.setAttribute('type', 'file');
133 input.onchange = () => {
134 var file = input.files[0];
135 var rgxi = /^image\//;
136 if (!file || !file.name || !rgxi.test(file.type)) {
137 alert('Only images are allowed.');
138 return;
139 }
140 this.upload(file);
141 };
142 input.click();
143 }
144
145 upload(file) {
146 if (!file instanceof File) {
147 console.error('Invalid file');
148 return false;
149 }
150
151 if (!this.endpoint) {
152 console.error('Missing endpoint');
153 return false;
154 }
155
156 // build form data
157 var formd = new FormData();
158 formd.append('file', file);
159 formd.append('type', 'image');
160
161 // start progress
162 this.startProgress();
163
164 // perform the AJAX upload
165 var that = this;
166 var jqxhr = jQuery.ajax({
167 xhr: function() {
168 var xhrobj = jQuery.ajaxSettings.xhr();
169 if (xhrobj.upload) {
170 xhrobj.upload.addEventListener('progress', function(event) {
171 var percent = 0;
172 var position = event.loaded || event.position;
173 var total = event.total;
174 if (event.lengthComputable) {
175 percent = Math.ceil(position / total * 100);
176 }
177 // update progress
178 that.setProgress(percent);
179 }, false);
180 }
181 return xhrobj;
182 },
183 url: that.endpoint,
184 type: 'POST',
185 contentType: false,
186 processData: false,
187 cache: false,
188 data: formd,
189 success: function(resp) {
190 try {
191 resp = JSON.parse(resp);
192 if (resp.status == 1) {
193 that.complete(resp);
194 } else {
195 throw resp.error ? resp.error : 'An error occurred! Please try again.';
196 }
197 } catch (err) {
198 that.stopProgress();
199 console.warn(err, resp);
200 alert(err);
201 }
202 },
203 error: function(err) {
204 that.stopProgress();
205 console.error(err.responseText);
206 alert('An error occurred. Please try again.');
207 },
208 });
209 }
210
211 complete(resp) {
212 this.stopProgress();
213 this.editor.insertEmbed(this.editor.getSelection().index, 'image', resp.url);
214 }
215
216 startProgress() {
217 // access the toolbar module
218 var toolbar = null;
219 try {
220 toolbar = this.editor.getModule('toolbar').container;
221 } catch(e) {
222 // do nothing
223 }
224 if (!toolbar) {
225 return false;
226 }
227
228 // create progress element
229 this.progress_el = document.createElement("PROGRESS");
230 this.progress_el.setAttribute('max', 100);
231 this.progress_el.setAttribute('value', 0);
232
233 // append progress to toolbar
234 toolbar.appendChild(this.progress_el);
235 }
236
237 setProgress(value) {
238 if (!this.progress_el) {
239 return false;
240 }
241 this.progress_el.setAttribute('value', value);
242 }
243
244 stopProgress() {
245 if (!this.progress_el) {
246 return false;
247 }
248 this.progress_el.remove();
249 this.progress_el = null;
250 }
251 }
252