PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
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
← All changes | libraries/wordpress/application.php +891 -0 1.6.3 → 1.8.16 View file →
@@ -1,0 +1,891 @@
1 +<?php
2 +/**
3 + * @package VikBooking - Libraries
4 + * @subpackage wordpress
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2018 E4J s.r.l. All Rights Reserved.
7 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 + * @link https://vikwp.com
9 + */
10 +
11 +// No direct access
12 +defined('ABSPATH') or die('No script kiddies please!');
13 +
14 +// do nothing if the class already exists
15 +if (!class_exists('VikApplication'))
16 +{
17 + VikBookingLoader::import('wordpress.version');
18 +
19 + /**
20 + * Helper class to adapt the application to the requirements
21 + * of the installed Wordpress version.
22 + *
23 + * @since 1.0
24 + */
25 + class VikApplication
26 + {
27 + /**
28 + * The instance to handle the singleton.
29 + *
30 + * @var self
31 + */
32 + protected static $instance = null;
33 +
34 + /**
35 + * Class constructor.
36 + *
37 + * @param integer $id The identifier of the wordpress version (@unused).
38 + */
39 + public function __construct($id = null)
40 + {
41 +
42 + }
43 +
44 + /**
45 + * Used to keep a single instance of the object.
46 + *
47 + * @return self The class singleton.
48 + */
49 + public static function getInstance()
50 + {
51 + if (static::$instance === null)
52 + {
53 + static::$instance = new static();
54 + }
55 +
56 + return static::$instance;
57 + }
58 +
59 + /**
60 + * Backward compatibility for admin list <table> class.
61 + *
62 + * @return string The class selector to use.
63 + */
64 + public function getAdminTableClass()
65 + {
66 + return 'wp-list-table widefat fixed striped';
67 + }
68 +
69 + /**
70 + * Backward compatibility for admin list <table> head opening.
71 + *
72 + * @return string The <thead> tag to use.
73 + */
74 + public function openTableHead()
75 + {
76 + return '<thead>';
77 + }
78 +
79 + /**
80 + * Backward compatibility for admin list <table> head closing.
81 + *
82 + * @return string The </thead> tag to use.
83 + */
84 + public function closeTableHead()
85 + {
86 + return '</thead>';
87 + }
88 +
89 + /**
90 + * Backward compatibility for admin list <th> class.
91 + *
92 + * @param string $h_align The additional class to use for horizontal alignment.
93 + * Accepted rules should be: left, center or right.
94 + *
95 + * @return string The class selector to use.
96 + */
97 + public function getAdminThClass($h_align = 'center')
98 + {
99 + return 'manage-column ' . $h_align;
100 + }
101 +
102 + /**
103 + * Backward compatibility for admin list checkAll JS event.
104 + *
105 + * @param integer $count The total count of rows in the table.
106 + *
107 + * @return string The check all checkbox input to use.
108 + */
109 + public function getAdminToggle($count)
110 + {
111 + return '<input type="checkbox" onclick="Joomla.checkAll(this)" value="" name="checkall-toggle" />';
112 + }
113 +
114 + /**
115 + * Backward compatibility for admin list isChecked JS event.
116 + *
117 + * @return string The JS function to use.
118 + */
119 + public function checkboxOnClick()
120 + {
121 + return 'Joomla.isChecked(this.checked);';
122 + }
123 +
124 + /**
125 + * Includes a script framework.
126 + *
127 + * @param string $fw The framework name.
128 + *
129 + * @return void
130 + */
131 + public function loadFramework($fw)
132 + {
133 + JHtml::fetch($fw);
134 + }
135 +
136 + /**
137 + * Includes a script URI.
138 + *
139 + * @param string $uri The script URI.
140 + *
141 + * @return void
142 + */
143 + public function addScript($uri)
144 + {
145 + JHtml::fetch('script', $uri);
146 + }
147 +
148 + /**
149 + * Helper method to send e-mails.
150 + *
151 + * @param string $from_address The e-mail address of the sender.
152 + * @param string $from_name The name of the sender.
153 + * @param string $to The e-mail address of the receiver.
154 + * @param string $reply_address The reply to e-mail address.
155 + * @param string $subject The subject of the e-mail.
156 + * @param string $hmess The body of the e-mail (HTML is supported).
157 + * @param array $attachments The list of the attachments to include.
158 + * @param boolean $is_html True to support HTML body, otherwise false for plain text.
159 + * @param string $encoding The encoding to use.
160 + *
161 + * @return boolean True if the e-mail was sent successfully, otherwise false.
162 + */
163 + public function sendMail($from_address, $from_name, $to, $reply_address, $subject, $hmess, $attachments = null, $is_html = true, $encoding = 'base64')
164 + {
165 + // $subject = '=?UTF-8?B?' . base64_encode($subject) . '?=';
166 +
167 + if ($is_html)
168 + {
169 + $hmess = "<html>\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"></head>\n<body>{$hmess}</body>\n</html>";
170 + }
171 +
172 + $mailer = JFactory::getMailer();
173 + $sender = array($from_address, $from_name);
174 + $mailer->setSender($sender);
175 + $mailer->addRecipient($to);
176 + $mailer->addReplyTo($reply_address);
177 + $mailer->setSubject($subject);
178 + $mailer->setBody($hmess);
179 + $mailer->isHTML($is_html);
180 + $mailer->Encoding = $encoding;
181 +
182 + if ($attachments !== null)
183 + {
184 + if (!is_array($attachments))
185 + {
186 + $attachments = array($attachments);
187 + }
188 +
189 + foreach ($attachments as $attach)
190 + {
191 + if (!empty($attach) && file_exists($attach))
192 + {
193 + $mailer->addAttachment($attach);
194 + }
195 + }
196 + }
197 +
198 + return $mailer->Send();
199 + }
200 +
201 + /**
202 + * Backward compatibility for punycode conversion.
203 + *
204 + * @param string $mail The e-mail to convert in punycode.
205 + *
206 + * @return string The punycode conversion of the e-mail.
207 + */
208 + public function emailToPunycode($email = '')
209 + {
210 + return $email;
211 + }
212 +
213 + /**
214 + * Helper method to build a tiny YES/NO radio button.
215 + *
216 + * @param string $name The name of the input.
217 + * @param object $elem_1 The first input object.
218 + * @param object $elem_2 The second input object.
219 + * @param boolean wrapped True if the input is wrapped in a control class, otherwise false..
220 + *
221 + * @return string The html to display.
222 + */
223 + public function radioYesNo($name, $elem_1, $elem_2, $wrapped = true, $layout = null)
224 + {
225 + /**
226 + * @todo
227 + */
228 +
229 + return '';
230 + }
231 +
232 + /**
233 + * Backward compatibility for fieldset opening.
234 + *
235 + * @param string $legend The title of the fieldset.
236 + * @param string $class The class attribute for the fieldset.
237 + * @param string $id The ID attribute for the fieldset.
238 + *
239 + * @return string The html to display.
240 + */
241 + public function openFieldset($legend, $class = '', $id = '')
242 + {
243 + $data = array();
244 + $data['name'] = $legend;
245 + $data['class'] = $class;
246 + $data['id'] = $id;
247 +
248 + return JHtml::fetch('layoutfile', 'html.form.fieldset.open')->render($data);
249 + }
250 +
251 + /**
252 + * Backward compatibility for fieldset closing.
253 + *
254 + * @return string The html to display.
255 + */
256 + public function closeFieldset()
257 + {
258 + return JHtml::fetch('layoutfile', 'html.form.fieldset.close')->render();
259 + }
260 +
261 + /**
262 + * Backward compatibility for empty fieldset opening.
263 + *
264 + * @param string $class An additional class to use for the fieldset.
265 + * @param string $id The ID attribute for the fieldset.
266 + *
267 + * @return string The html to display.
268 + */
269 + public function openEmptyFieldset($class = '', $id = '')
270 + {
271 + return $this->openFieldset('', $class, $id);
272 + }
273 +
274 + /**
275 + * Backward compatibility for empty fieldset opening.
276 + *
277 + * @return string The html to display.
278 + */
279 + public function closeEmptyFieldset()
280 + {
281 + return $this->closeFieldset();
282 + }
283 +
284 + /**
285 + * Backward compatibility for control opening.
286 + *
287 + * @param string $label The label of the control field.
288 + * @param string $class The class of the control field.
289 + * @param array $attr The additional attributes to add.
290 + *
291 + * @return string The html to display.
292 + */
293 + public function openControl($label, $class = '', $attr = array())
294 + {
295 + $data = array();
296 +
297 + foreach ($attr as $k => $v)
298 + {
299 + $data[$k] = $v;
300 + }
301 +
302 + $data['label'] = $label;
303 + $data['class'] = $class;
304 +
305 + return JHtml::fetch('layoutfile', 'html.form.control.open')->render($data);
306 + }
307 +
308 + /**
309 + * Backward compatibility for control closing.
310 + *
311 + * @return string The html to display.
312 + */
313 + public function closeControl()
314 + {
315 + return JHtml::fetch('layoutfile', 'html.form.control.close')->render();
316 + }
317 +
318 + /**
319 + * Returns the codemirror editor in 3.x, otherwise a simple textarea.
320 + *
321 + * @param string $name The name of the textarea.
322 + * @param string $value The value of the textarea.
323 + *
324 + * @return string The html to display.
325 + */
326 + public function getCodeMirror($name, $value)
327 + {
328 + return '<textarea name="' . $name . '" style="width: 100%;height: 520px;">' . $value . '</textarea>';
329 + }
330 +
331 + /**
332 + * Backward compatibility for Bootstrap tabset opening.
333 + *
334 + * @param string $group The group of the tabset.
335 + * @param string $attr The attributes to use.
336 + *
337 + * @return string The html to display.
338 + */
339 + public function bootStartTabSet($group, $attr = array())
340 + {
341 + /**
342 + * @todo
343 + */
344 +
345 + return '';
346 + }
347 +
348 + /**
349 + * Backward compatibility for Bootstrap tabset closing.
350 + *
351 + * @return string The html to display.
352 + */
353 + public function bootEndTabSet()
354 + {
355 + /**
356 + * @todo
357 + */
358 +
359 + return '';
360 + }
361 +
362 + /**
363 + * Backward compatibility for Bootstrap add tab.
364 + *
365 + * @param string $group The tabset parent group.
366 + * @param string $id The id of the tab.
367 + * @param string $label The title of the tab.
368 + *
369 + * @return string The html to display.
370 + */
371 + public function bootAddTab($group, $id, $label)
372 + {
373 + /**
374 + * @todo
375 + */
376 +
377 + return '';
378 + }
379 +
380 + /**
381 + * Backward compatibility for Bootstrap end tab.
382 + *
383 + * @return string The html to display.
384 + */
385 + public function bootEndTab()
386 + {
387 + /**
388 + * @todo
389 + */
390 +
391 + return '';
392 + }
393 +
394 + /**
395 + * Backward compatibility for Bootstrap open modal JS event.
396 + *
397 + * @param string $onclose The javascript function to call on close event.
398 + *
399 + * @return string The javascript function.
400 + */
401 + public function bootOpenModalJS($onclose = '')
402 + {
403 + /**
404 + * @todo
405 + */
406 +
407 + return '';
408 + }
409 +
410 + /**
411 + * Backward compatibility for Bootstrap dismiss modal JS event.
412 + *
413 + * @param string $selector The selector to identify the modal box.
414 + *
415 + * @return string The javascript function.
416 + */
417 + public function bootDismissModalJS($selector)
418 + {
419 + /**
420 + * @todo
421 + */
422 +
423 + return '';
424 + }
425 +
426 + /**
427 + * Returns the HTML used to render a Bootstrap modal.
428 + *
429 + * @param string $id The modal ID.
430 + * @param string $title The modal title.
431 + * @param string $body The modal body (if static HTML).
432 + * @param mixed $options An array of attributes or the inline style string.
433 + *
434 + * @return string The modal HTML.
435 + */
436 + public function getJModalHtml($id, $title, $body = '', $options = null)
437 + {
438 + if (is_array($options))
439 + {
440 + $width = isset($options['width']) ? abs($options['width']) : 90;
441 + $height = isset($options['height']) ? abs($options['height']) : 80;
442 + $left = isset($options['left']) ? abs($options['left']) : $width / 2;
443 +
444 + $style = "width:$width%;height:$height%;margin-left:-$left%;";
445 +
446 + if (isset($options['top']))
447 + {
448 + if ($options['top'] === true)
449 + {
450 + $top = (100 - $height) / 2;
451 + }
452 + else
453 + {
454 + $top = $options['top'];
455 + }
456 +
457 + $style .= "top:$top%;";
458 + }
459 + }
460 + else if (is_string($options))
461 + {
462 + $style = $options;
463 + }
464 + else
465 + {
466 + $style = "width:90%;height:80%;margin-left:-45%";
467 + }
468 +
469 + $options = array();
470 + $options['id'] = $id;
471 + $options['title'] = $title;
472 + $options['body'] = $body;
473 + $options['style'] = $style;
474 +
475 + $layout = new JLayoutFile('html.plugins.modal', null, array('component' => 'com_vikbooking'));
476 +
477 + return $layout->render($options);
478 + }
479 +
480 + /**
481 + * Adds javascript support for Bootstrap popovers.
482 + *
483 + * @param string $selector Selector for the popover.
484 + * @param array $options An array of options for the popover.
485 + * Options for the popover can be:
486 + * animation boolean apply a css fade transition to the popover
487 + * html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert
488 + * content into the dom.
489 + * placement string|function how to position the popover - top | bottom | left | right
490 + * selector string If a selector is provided, popover objects will be delegated to the specified targets.
491 + * trigger string how popover is triggered - hover | focus | manual
492 + * title string|function default title value if `title` tag isn't present
493 + * content string|function default content value if `data-content` attribute isn't present
494 + * delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type
495 + * If a number is supplied, delay is applied to both hide/show
496 + * Object structure is: delay: { show: 500, hide: 100 }
497 + * container string|boolean Appends the popover to a specific element: { container: 'body' }
498 + */
499 + public function attachPopover($selector = '.wpPopover', array $options = array())
500 + {
501 + static $loaded = array();
502 +
503 + $sign = serialize(array($selector, $options));
504 +
505 + if (!isset($loaded[$sign]))
506 + {
507 + $options['sanitize'] = false;
508 + $data = $options ? json_encode($options) : '{}';
509 + JFactory::getDocument()->addScriptDeclaration(
510 +<<<JS
511 +jQuery(function() {
512 + jQuery('$selector').popover($data);
513 +});
514 +JS
515 + );
516 +
517 + $loaded[$sign] = 1;
518 + }
519 + }
520 +
521 + /**
522 + * Creates a standard tag and attach a popover event.
523 + * NOTE. FontAwesome framework MUST be loaded in order to work.
524 + *
525 + * @param array $options An array of options for the popover.
526 + *
527 + * @return string The popover HTML.
528 + *
529 + * @uses _popover()
530 + * @see attachPopover() for further details about options keys.
531 + */
532 + public function createPopover(array $options = array())
533 + {
534 + $icon = isset($options['icon_class']) ? $options['icon_class'] : 'fas fa-question-circle';
535 +
536 + $icon = isset($options['icon']) ? 'fas fa-'.$options['icon'] : $icon;
537 +
538 + $template = "<i class=\"{$icon} wp-quest-popover\" {popover}></i>";
539 +
540 + return $this->_popover($template, $options);
541 + }
542 +
543 + /**
544 + * Creates a text span and attach a popover event.
545 + *
546 + * @param array $options An array of options for the popover.
547 + *
548 + * @return string The popover HTML.
549 + *
550 + * @uses _popover()
551 + * @see attachPopover() for further details about options keys.
552 + */
553 + public function textPopover(array $options = array())
554 + {
555 + $title = isset($options['title']) ? $options['title'] : '[MISSING TITLE]';
556 + $template = "<span class=\"inline-popover wp-quest-popover\" {popover}>{$title}</span>";
557 +
558 + return $this->_popover($template, $options);
559 + }
560 +
561 + /**
562 + * Creates a popover using the provided template.
563 + *
564 + * @param string $template The popover template.
565 + * @param array $options An array of options for the popover.
566 + *
567 + * @return string The popover HTML.
568 + *
569 + * @see attachPopover() for further details about options keys.
570 + */
571 + protected function _popover($template, array $options)
572 + {
573 + $layout = new JLayoutFile('html.plugins.popover', null, array('component' => 'com_vikbooking'));
574 +
575 + $options['html'] = true;
576 + $options['title'] = isset($options['title']) ? $options['title'] : '';
577 + $options['content'] = isset($options['content']) ? $options['content'] : '';
578 + $options['trigger'] = isset($options['trigger']) ? $options['trigger'] : 'hover focus';
579 + $options['placement'] = isset($options['placement']) ? $options['placement'] : 'right';
580 + $options['template'] = isset($options['template']) ? $options['template'] : $layout->render();
581 +
582 + // attach an empty array option so that the data will be recovered
583 + // directly from the tag during the runtime
584 + $this->attachPopover(".wp-quest-popover", array());
585 +
586 + $attr = '';
587 + foreach ($options as $k => $v)
588 + {
589 + $attr .= "data-{$k}=\"" . str_replace('"', '&quot;', $v) . "\" ";
590 + }
591 +
592 + return str_replace('{popover}', $attr, $template);
593 + }
594 +
595 + /**
596 + * Return the WP date format specs.
597 + *
598 + * @param string $format The format to use.
599 + *
600 + * @return string The adapted date format.
601 + */
602 + public function jdateFormat($format = null)
603 + {
604 + // strip % from date format string, which was required in Joomla
605 + return str_replace('%', '', $format);
606 + }
607 +
608 + /**
609 + * Provides support to handle the wordpress calendar across different frameworks.
610 + *
611 + * @param string $value The date to fill.
612 + * @param string $name The input name.
613 + * @param string $id The input id attribute.
614 + * @param string $format The date format.
615 + * @param array $attributes Some attributes to use.
616 + *
617 + * @return string The calendar field.
618 + */
619 + public function calendar($value, $name, $id = null, $format = null, array $attributes = array())
620 + {
621 + $format = $this->jdateFormat($format);
622 +
623 + JHtml::fetch('behavior.calendar');
624 +
625 + return JHtml::fetch('calendar', $value, $name, $id, $format, $attributes);
626 + }
627 +
628 + /**
629 + * Returns a masked e-mail address. The e-mail are masked using
630 + * a technique to encode the bytes in hexadecimal representation.
631 + * The chunk of the masked e-mail will be also encoded to be HTML readable.
632 + *
633 + * @param string $email The e-mail to mask.
634 + * @param boolean $reverse True to reverse the e-mail address.
635 + * Only if the e-mail is not contained into an attribute.
636 + *
637 + * @return string The masked e-mail address.
638 + */
639 + public function maskMail($email, $reverse = false)
640 + {
641 + if ($reverse)
642 + {
643 + // reverse the e-mail address
644 + $email = strrev($email);
645 + }
646 +
647 + // converts the e-mail address from bin to hex
648 + $email = bin2hex($email);
649 + // append ;&#x sequence after every chunk of the masked e-mail
650 + $email = chunk_split($email, 2, ";&#x");
651 + // prepend &#x sequence before the address and trim the ending sequence
652 + $email = "&#x" . substr($email, 0, -3);
653 +
654 + return $email;
655 + }
656 +
657 + /**
658 + * Returns a safemail tag to avoid the bots spoof a plain address.
659 + *
660 + * @param string $email The e-mail address to mask.
661 + * @param boolean $mail_to True if the address should be wrapped
662 + * within a "mailto" link.
663 + *
664 + * @return string The HTML tag containing the masked address.
665 + *
666 + * @uses maskMail()
667 + */
668 + public function safeMailTag($email, $mail_to = false)
669 + {
670 + // include the CSS declaration to reverse the text contained in the <safemail> tags
671 + JFactory::getDocument()->addStyleDeclaration('safemail {direction: rtl;unicode-bidi: bidi-override;}');
672 +
673 + // mask the reversed e-mail address
674 + $masked = $this->maskMail($email, true);
675 +
676 + // include the address into a custom <safemail> tag
677 + $tag = "<safemail>$masked</safemail>";
678 +
679 + if ($mail_to)
680 + {
681 + // mask the address for mailto command (do not use reverse)
682 + $mailto = $this->maskMail($email);
683 +
684 + // wrap the safemail tag within a mailto link
685 + $tag = "<a href=\"mailto:$mailto\" class=\"mailto\">$tag</a>";
686 + }
687 +
688 + return $tag;
689 + }
690 +
691 + /**
692 + * Returns the list of all the installed languages.
693 + *
694 + * @return array The installed languages.
695 + */
696 + public function getKnownLanguages()
697 + {
698 + /**
699 + * Use JLanguage::getKnownLanguages() native method.
700 + *
701 + * @since 1.1.4
702 + */
703 + return JLanguage::getKnownLanguages();
704 + }
705 +
706 + /**
707 + * Returns the HTML code to display a toggle button in iOS style through a checkbox element.
708 + *
709 + * @param string $name the checkbox element name.
710 + * @param string $label_yes label for the checked status.
711 + * @param string $label_no label for the un-checked status.
712 + * @param string $cur_value the default checkbox value state.
713 + * @param string $yes_value the checkbox value when checked.
714 + * @param string $no_value the checkbox value when un-checked (unsupported).
715 + * @param string $onclick optional onclick attribute to fire a JS callback.
716 + * @param array $class_list optional list of additional CSS classes to add ('blue', 'orange', 'red', 'gold', 'purple').
717 + *
718 + *
719 + * @return string The HTML code to render the radio buttons.
720 + *
721 + * @since 1.16.9 (J) - 1.6.9 (WP) introduced argument $class_list.
722 + */
723 + public function printYesNoButtons($name, $label_yes, $label_no, $cur_value = '1', $yes_value = '1', $no_value = '0', $onclick = '', $class_list = [])
724 + {
725 + $html = '';
726 +
727 + /**
728 + * Normalize ID attribute for checkbox.
729 + *
730 + * @since 1.8.8
731 + */
732 + $id_yes = preg_replace("/[^a-z0-9_]+/", '-', $name . '-on');
733 +
734 + /**
735 + * New toggle button in iOS style.
736 + *
737 + * @since 1.3.0
738 + */
739 + $show_labels = ($label_yes != JText::translate('JYES') && $label_yes != JText::translate('VBYES'));
740 + $html = '<span class="vik-iostoggle-wrap vbo-iostoggle-wrap' . ($class_list ? ' ' . implode(' ', $class_list) : '') . '">
741 + <input type="checkbox" name="' . $name . '" class="vik-iostoggle-elem vbo-iostoggle-elem" id="' . $id_yes . '" value="' . $yes_value . '"' . (!empty($onclick) ? ' onclick="' . $onclick . '"' : '') . ($cur_value === $yes_value ? ' checked' : '').'>
742 + <label for="' . $id_yes . '">' . ($show_labels ? '<span class="vik-iostoggle-lbls vbo-iostoggle-lbls" data-on="' . addslashes($label_yes) . '" data-off="' . addslashes($label_no) . '"></span>' : '') . '</label>
743 + </span>';
744 +
745 + return $html;
746 + }
747 +
748 + /**
749 + * Returns the HTML code for displaying an input field for a phone number.
750 + * Adds to the documents the necessary style, script and JS code.
751 + *
752 + * @param array $attrs array of attributes for the input field.
753 + * @param array $opts array of options for the input field.
754 + * @param bool $load_assets whether to load the CSS/JS assets.
755 + *
756 + * @return string the plain HTML code to be printed for the input field.
757 + *
758 + * @since 1.3.0
759 + * @since 1.6.0 added argument $load_assets
760 + */
761 + public function printPhoneInputField($attrs = array(), $opts = array(), $load_assets = true)
762 + {
763 + if (!empty($attrs['id'])) {
764 + $selector = $attrs['id'];
765 + } elseif (!empty($attrs['name'])) {
766 + $selector = $attrs['name'];
767 + } else {
768 + $selector = time() . rand();
769 + }
770 +
771 + // input default's attributes
772 + $default_attrs = array(
773 + 'type' => 'tel',
774 + 'name' => (!empty($attrs['name']) ? $attrs['name'] : $selector),
775 + 'id' => $selector,
776 + 'value' => '',
777 + 'size' => '30',
778 + );
779 +
780 + // merge arguments attributes with the default ones
781 + $final_attrs = array_merge($default_attrs, $attrs);
782 +
783 + // format attributes
784 + $attrs_cont = array();
785 + foreach ($final_attrs as $k => $v) {
786 + array_push($attrs_cont, "{$k}=\"{$v}\"");
787 + }
788 +
789 + /**
790 + * Get the preferred countries with correct ordering.
791 + *
792 + * @wponly if multiple plugins are installed, we may have this method defined by another plugin.
793 + *
794 + * @since 1.3.11
795 + */
796 + $preferred_countries = array();
797 + $plugin_name = str_replace('com_', '', JFactory::getApplication()->input->getString('option', ''));
798 + if (!empty($plugin_name) && class_exists($plugin_name) && method_exists($plugin_name, 'preferredCountriesOrdering')) {
799 + $preferred_countries = $plugin_name::preferredCountriesOrdering();
800 + } elseif (class_exists('VikBooking')) {
801 + $preferred_countries = VikBooking::preferredCountriesOrdering();
802 + }
803 + //
804 +
805 + // build default config object properties
806 + $default_opts = array(
807 + 'nationalMode' => true,
808 + 'preferredCountries' => $preferred_countries,
809 + 'formatOnDisplay' => true,
810 + 'utilsScript' => VBO_SITE_URI . 'resources/intlTelInput_utils.js',
811 + /**
812 + * This option is not a valid property of intlTelInput plugin.
813 + * If set to true, when the input gets blurred, the full phone
814 + * number inclusive of prefix will be immediately replaced and
815 + * set into the selector input field. Defaults to false when
816 + * the form containing the input field gets submitted. It should
817 + * be set to true when the form does not get submitted.
818 + */
819 + 'fullNumberOnBlur' => false,
820 + /**
821 + * Allow to dispatch an event to transport the selected country data.
822 + *
823 + * @since 1.18.8 (J) - 1.8.8 (WP)
824 + */
825 + 'countryDataEvent' => null,
826 + );
827 +
828 + // merge config object with user's specified properties
829 + $final_opts = array_merge($default_opts, $opts);
830 + $data = json_encode($final_opts);
831 +
832 + // apply set full number on blur
833 + $full_number_on_blur = (int) $final_opts['fullNumberOnBlur'];
834 +
835 + // check if an event should be dispatched with the selected country data
836 + $countryDataEvent = $final_opts['countryDataEvent'] ? json_encode($final_opts['countryDataEvent']) : 0;
837 +
838 + $document = JFactory::getDocument();
839 +
840 + if ($load_assets) {
841 + $document->addStyleSheet(VBO_SITE_URI . 'resources/intlTelInput.css');
842 + $document->addScript(VBO_SITE_URI . 'resources/intlTelInput.js');
843 + }
844 +
845 + $render_script = <<<JS
846 +jQuery(function() {
847 + jQuery('#$selector').intlTelInput($data);
848 + jQuery('#$selector').on('blur', function() {
849 + // set or format phone number on blur
850 + var cur_phone = jQuery('#$selector').intlTelInput('getNumber');
851 + if (!cur_phone || !cur_phone.length) {
852 + return;
853 + }
854 + if ($full_number_on_blur) {
855 + jQuery('#$selector').val(cur_phone);
856 + } else {
857 + jQuery('#$selector').intlTelInput('setNumber', cur_phone);
858 + }
859 + if ($countryDataEvent && typeof VBOCore !== 'undefined') {
860 + let countryData = jQuery('#$selector').intlTelInput('getSelectedCountryData');
861 + if (countryData && countryData?.iso2) {
862 + VBOCore.emitEvent($countryDataEvent, countryData);
863 + }
864 + }
865 + });
866 + jQuery('#$selector').closest('form').on('submit', function() {
867 + // always make sure the input field contains the complete phone number
868 + jQuery('#$selector').val(jQuery('#$selector').intlTelInput('getNumber'));
869 + });
870 + jQuery('#$selector').on('vboupdatephonenumber', function(e, country) {
871 + if (country && country.length == 2 && !jQuery('#$selector').val().length) {
872 + jQuery('#$selector').intlTelInput('setCountry', country.toLowerCase());
873 + }
874 + // make sure the input field contains the complete phone number
875 + jQuery('#$selector').val(jQuery('#$selector').intlTelInput('getNumber'));
876 + });
877 +});
878 +JS;
879 +
880 + $node_elem = '<input ' . implode(' ', $attrs_cont) . ' />';
881 +
882 + if ($load_assets) {
883 + $document->addScriptDeclaration($render_script);
884 + } else {
885 + $node_elem .= "<script>{$render_script}</script>";
886 + }
887 +
888 + return $node_elem;
889 + }
890 + }
891 +}