PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.2.8
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.2.8
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / assets / js / modules / address-autocomplete / admin / preview.js

preview.js in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 2.2.8, at assets/js/modules/address-autocomplete/admin/preview.js

430 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2
3 function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
4 function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
5 function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
6 function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
7 (function ($) {
8 'use strict';
9
10 var addressAutocompletePreview = {
11 // Cache DOM elements
12 $apiKeyInput: null,
13 $addressInput: null,
14 // Store autocomplete instance
15 placeAutocompleteElement: null,
16 // Store bootstrap script reference
17 bootstrapScript: null,
18 // Configuration
19 config: {
20 debug: false,
21 apiKeyLength: 39,
22 retryAttempts: 3,
23 retryDelay: 1000,
24 scriptLoadTimeout: 5000
25 },
26 /**
27 * Initialize the preview autocomplete
28 */
29 init: function init() {
30 var _this = this;
31 // Cache DOM elements
32 this.$apiKeyInput = $('.merchant-module-page-setting-field[data-id="api_key"] input');
33 this.$addressInput = $('#merchant-address-autocomplete');
34
35 // Validate required elements exist
36 if (!this.$apiKeyInput.length || !this.$addressInput.length) {
37 this.handleError('Required DOM elements not found', 'warning');
38 return;
39 }
40
41 // Trigger activation on page load
42 this.activateAutocompleteIfNeeded();
43
44 // Handle changes to the API key input with debouncing
45 var debounceTimer;
46 this.$apiKeyInput.on('input', function () {
47 clearTimeout(debounceTimer);
48 debounceTimer = setTimeout(function () {
49 _this.activateAutocompleteIfNeeded();
50 }, 500);
51 });
52 this.log('Preview autocomplete initialized');
53 },
54 /**
55 * Check if the API key is valid and activate Autocomplete
56 */
57 activateAutocompleteIfNeeded: function activateAutocompleteIfNeeded() {
58 var apiKey = this.$apiKeyInput.val();
59 if (apiKey && this.validateApiKey(apiKey)) {
60 this.loadGoogleMapsScript(apiKey);
61 } else {
62 this.deactivateAutocomplete();
63 }
64 },
65 /**
66 * Validate API key format
67 * @param {string} apiKey - The API key to validate
68 * @returns {boolean}
69 */
70 validateApiKey: function validateApiKey(apiKey) {
71 if (typeof apiKey !== 'string') {
72 return false;
73 }
74
75 // Basic length check
76 if (apiKey.length !== this.config.apiKeyLength) {
77 this.log("Invalid API key length: ".concat(apiKey.length, ", expected: ").concat(this.config.apiKeyLength), 'warning');
78 return false;
79 }
80
81 // Check for valid characters (alphanumeric, hyphens, underscores)
82 var validPattern = /^[A-Za-z0-9_-]+$/;
83 if (!validPattern.test(apiKey)) {
84 this.log('API key contains invalid characters', 'warning');
85 return false;
86 }
87 return true;
88 },
89 /**
90 * Load the Google Maps script using the inline bootstrap loader
91 * @param {string} apiKey - The Google Maps API key
92 */
93 loadGoogleMapsScript: function loadGoogleMapsScript(apiKey) {
94 // Deactivate any existing instance first
95 this.deactivateAutocomplete();
96 try {
97 var _window$google;
98 // Check if Google Maps is already loaded
99 if ((_window$google = window.google) !== null && _window$google !== void 0 && (_window$google = _window$google.maps) !== null && _window$google !== void 0 && _window$google.importLibrary) {
100 this.log('Google Maps already loaded, initializing autocomplete');
101 this.initializeAutocomplete();
102 return;
103 }
104
105 // Inject the inline bootstrap loader
106 this.bootstrapScript = document.createElement('script');
107 this.bootstrapScript.textContent = this.getBootstrapLoaderScript(apiKey);
108 this.bootstrapScript.setAttribute('data-merchant-maps-loader', 'true');
109 document.head.appendChild(this.bootstrapScript);
110
111 // Initialize autocomplete with timeout protection
112 this.initializeAutocompleteWithTimeout();
113 this.log('Google Maps script injected');
114 } catch (error) {
115 this.handleError('Failed to load Google Maps script', 'error', error);
116 this.deactivateAutocomplete();
117 }
118 },
119 /**
120 * Get the Google Maps bootstrap loader script
121 * @param {string} apiKey - The API key
122 * @returns {string} Bootstrap loader script
123 */
124 getBootstrapLoaderScript: function getBootstrapLoaderScript(apiKey) {
125 // Sanitize API key
126 var sanitizedKey = this.sanitizeValue(apiKey);
127 return "\n\t\t\t\t(g=>{var h,a,k,p=\"The Google Maps JavaScript API\",c=\"google\",l=\"importLibrary\",q=\"__ib__\",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement(\"script\"));e.set(\"libraries\",[...r]+\"\");for(k in g)e.set(k.replace(/[A-Z]/g,t=>\"_\"+t[0].toLowerCase()),g[k]);e.set(\"callback\",c+\".maps.\"+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+\" could not load.\"));a.nonce=m.querySelector(\"script[nonce]\")?.nonce||\"\";m.head.append(a)}));d[l]?console.warn(p+\" only loads once. Ignoring:\",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({\n\t\t\t\t\tkey: \"".concat(sanitizedKey, "\",\n\t\t\t\t\tv: \"weekly\"\n\t\t\t\t});\n\t\t\t");
128 },
129 /**
130 * Initialize autocomplete with timeout protection
131 */
132 initializeAutocompleteWithTimeout: function initializeAutocompleteWithTimeout() {
133 var _this2 = this;
134 var timeout = setTimeout(function () {
135 _this2.handleError('Google Maps script load timeout', 'error');
136 _this2.deactivateAutocomplete();
137 }, this.config.scriptLoadTimeout);
138 this.initializeAutocomplete().then(function () {
139 clearTimeout(timeout);
140 }).catch(function (error) {
141 clearTimeout(timeout);
142 _this2.handleError('Failed to initialize autocomplete', 'error', error);
143 _this2.deactivateAutocomplete();
144 });
145 },
146 /**
147 * Initialize the PlaceAutocompleteElement
148 * @returns {Promise<void>}
149 */
150 initializeAutocomplete: function () {
151 var _initializeAutocomplete = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
152 var _this3 = this;
153 var attempt,
154 _yield$google$maps$im,
155 PlaceAutocompleteElement,
156 _args2 = arguments,
157 _t;
158 return _regenerator().w(function (_context2) {
159 while (1) switch (_context2.p = _context2.n) {
160 case 0:
161 attempt = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : 1;
162 _context2.p = 1;
163 _context2.n = 2;
164 return this.waitForGoogleMaps();
165 case 2:
166 _context2.n = 3;
167 return google.maps.importLibrary('places');
168 case 3:
169 _yield$google$maps$im = _context2.v;
170 PlaceAutocompleteElement = _yield$google$maps$im.PlaceAutocompleteElement;
171 // Create the new PlaceAutocompleteElement
172 this.placeAutocompleteElement = new PlaceAutocompleteElement();
173
174 // Copy styling from original input
175 if (this.$addressInput.attr('class')) {
176 this.placeAutocompleteElement.className = this.$addressInput.attr('class');
177 }
178
179 // Hide the original input field
180 this.$addressInput.hide();
181
182 // Insert the autocomplete element after the hidden input
183 this.$addressInput.after(this.placeAutocompleteElement);
184
185 // Listen for place selection
186 this.placeAutocompleteElement.addEventListener('gmp-select', /*#__PURE__*/function () {
187 var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(event) {
188 return _regenerator().w(function (_context) {
189 while (1) switch (_context.n) {
190 case 0:
191 _context.n = 1;
192 return _this3.handlePlaceSelection(event.placePrediction);
193 case 1:
194 return _context.a(2);
195 }
196 }, _callee);
197 }));
198 return function (_x) {
199 return _ref.apply(this, arguments);
200 };
201 }());
202 this.log('Autocomplete element initialized successfully');
203 _context2.n = 7;
204 break;
205 case 4:
206 _context2.p = 4;
207 _t = _context2.v;
208 if (!(attempt < this.config.retryAttempts)) {
209 _context2.n = 6;
210 break;
211 }
212 this.log("Retry attempt ".concat(attempt + 1, " to initialize autocomplete"));
213 _context2.n = 5;
214 return this.delay(this.config.retryDelay);
215 case 5:
216 return _context2.a(2, this.initializeAutocomplete(attempt + 1));
217 case 6:
218 throw _t;
219 case 7:
220 return _context2.a(2);
221 }
222 }, _callee2, this, [[1, 4]]);
223 }));
224 function initializeAutocomplete() {
225 return _initializeAutocomplete.apply(this, arguments);
226 }
227 return initializeAutocomplete;
228 }(),
229 /**
230 * Wait for Google Maps to be available
231 * @returns {Promise<void>}
232 */
233 waitForGoogleMaps: function waitForGoogleMaps() {
234 return new Promise(function (resolve, reject) {
235 var maxAttempts = 50;
236 var attempts = 0;
237 var _checkGoogleMaps = function checkGoogleMaps() {
238 var _window$google2;
239 if ((_window$google2 = window.google) !== null && _window$google2 !== void 0 && (_window$google2 = _window$google2.maps) !== null && _window$google2 !== void 0 && _window$google2.importLibrary) {
240 resolve();
241 } else if (attempts >= maxAttempts) {
242 reject(new Error('Google Maps failed to load'));
243 } else {
244 attempts++;
245 setTimeout(_checkGoogleMaps, 100);
246 }
247 };
248 _checkGoogleMaps();
249 });
250 },
251 /**
252 * Handle place selection from autocomplete
253 * @param {Object} placePrediction - Place prediction object
254 */
255 handlePlaceSelection: function () {
256 var _handlePlaceSelection = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(placePrediction) {
257 var place, formattedAddress, _t2;
258 return _regenerator().w(function (_context3) {
259 while (1) switch (_context3.p = _context3.n) {
260 case 0:
261 _context3.p = 0;
262 // Disable input during processing
263 this.setLoadingState(true);
264 place = placePrediction.toPlace(); // Fetch the fields you need
265 _context3.n = 1;
266 return place.fetchFields({
267 fields: ['displayName', 'formattedAddress', 'location', 'addressComponents']
268 });
269 case 1:
270 // Sanitize and update the hidden input
271 formattedAddress = this.sanitizeValue(place.formattedAddress || '');
272 this.$addressInput.val(formattedAddress);
273
274 // Trigger change event
275 this.$addressInput.trigger('change');
276 this.log('Selected place:', place.toJSON());
277 _context3.n = 3;
278 break;
279 case 2:
280 _context3.p = 2;
281 _t2 = _context3.v;
282 this.handleError('Error fetching place details', 'error', _t2);
283 case 3:
284 _context3.p = 3;
285 this.setLoadingState(false);
286 return _context3.f(3);
287 case 4:
288 return _context3.a(2);
289 }
290 }, _callee3, this, [[0, 2, 3, 4]]);
291 }));
292 function handlePlaceSelection(_x2) {
293 return _handlePlaceSelection.apply(this, arguments);
294 }
295 return handlePlaceSelection;
296 }(),
297 /**
298 * Set loading state
299 * @param {boolean} isLoading - Loading state
300 */
301 setLoadingState: function setLoadingState(isLoading) {
302 if (this.placeAutocompleteElement) {
303 if (isLoading) {
304 this.placeAutocompleteElement.classList.add('loading');
305 } else {
306 this.placeAutocompleteElement.classList.remove('loading');
307 }
308 }
309 },
310 /**
311 * Sanitize input value to prevent XSS
312 * @param {string} value - Value to sanitize
313 * @returns {string} Sanitized value
314 */
315 sanitizeValue: function sanitizeValue(value) {
316 if (typeof value !== 'string') {
317 return '';
318 }
319 return $('<div>').text(value).html();
320 },
321 /**
322 * Deactivate and clean up autocomplete
323 */
324 deactivateAutocomplete: function deactivateAutocomplete() {
325 // Remove the PlaceAutocompleteElement
326 if (this.placeAutocompleteElement) {
327 try {
328 this.placeAutocompleteElement.remove();
329 } catch (error) {
330 this.log('Error removing autocomplete element', 'warning');
331 }
332 this.placeAutocompleteElement = null;
333 }
334
335 // Clean up Google Maps scripts
336 this.cleanupGoogleMapsScripts();
337
338 // Reset address field
339 this.resetAddressField();
340 this.log('Autocomplete deactivated');
341 },
342 /**
343 * Clean up Google Maps scripts from DOM
344 */
345 cleanupGoogleMapsScripts: function cleanupGoogleMapsScripts() {
346 try {
347 var _window$google3;
348 // Remove Google Maps from window
349 if ((_window$google3 = window.google) !== null && _window$google3 !== void 0 && _window$google3.maps) {
350 delete window.google;
351 }
352
353 // Remove all Google Maps related scripts
354 document.querySelectorAll('script').forEach(function (script) {
355 if (script.src.includes('googleapis.com/maps') || script.src.includes('maps.gstatic.com') || script.textContent.includes('importLibrary') || script.getAttribute('data-merchant-maps-loader')) {
356 script.remove();
357 }
358 });
359 this.bootstrapScript = null;
360 this.log('Google Maps scripts cleaned up');
361 } catch (error) {
362 this.handleError('Error cleaning up scripts', 'warning', error);
363 }
364 },
365 /**
366 * Reset the address input field to default state
367 */
368 resetAddressField: function resetAddressField() {
369 if (this.$addressInput && this.$addressInput.length) {
370 this.$addressInput.val('');
371 this.$addressInput.show();
372 this.$addressInput.removeClass('loading');
373 }
374 },
375 /**
376 * Handle errors with different severity levels
377 * @param {string} message - Error message
378 * @param {string} severity - Error severity (critical, error, warning)
379 * @param {Error} error - Original error object
380 */
381 handleError: function handleError(message) {
382 var severity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'error';
383 var error = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
384 var fullMessage = error ? "".concat(message, ": ").concat(error.message) : message;
385 if (severity === 'critical' || severity === 'error') {
386 console.error("[AddressAutocompletePreview] ".concat(fullMessage), error);
387 } else {
388 console.warn("[AddressAutocompletePreview] ".concat(fullMessage));
389 }
390 this.log(fullMessage, severity);
391 },
392 /**
393 * Debug logging
394 * @param {*} message - Log message
395 * @param {string} level - Log level
396 */
397 log: function log(message) {
398 var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'log';
399 if (this.config.debug && typeof console !== 'undefined') {
400 var timestamp = new Date().toISOString();
401 console[level]("[AddressAutocompletePreview ".concat(timestamp, "]"), message);
402 }
403 },
404 /**
405 * Delay helper for retry logic
406 * @param {number} ms - Milliseconds to delay
407 * @returns {Promise}
408 */
409 delay: function delay(ms) {
410 return new Promise(function (resolve) {
411 return setTimeout(resolve, ms);
412 });
413 }
414 };
415
416 // Initialize when DOM is ready
417 $(document).ready(function () {
418 addressAutocompletePreview.init();
419 });
420
421 // Cleanup on page unload
422 $(window).on('beforeunload', function () {
423 addressAutocompletePreview.deactivateAutocomplete();
424 });
425
426 // Expose to global scope for debugging (optional)
427 if (addressAutocompletePreview.config.debug) {
428 window.addressAutocompletePreview = addressAutocompletePreview;
429 }
430 })(jQuery);