addStyleSheet(VBO_ADMIN_URI . 'helpers/' . 'wp.css');
}
}
/**
* Includes a script URI.
*
* @param string $uri The script URI.
*
* @return void
*/
public function addScript($uri)
{
JHtml::fetch('script', $uri);
}
/**
* Sets additional commands for any methods. Like raise an error if the recipient email address is empty.
* Returns this object for chainability.
*/
public function setCommand($key, $value)
{
if (!empty($key)) {
$this->commands[$key] = $value;
}
return $this;
}
public function sendMail($from_address, $from_name, $to, $reply_address, $subject, $hmess, $is_html = true, $encoding = 'base64', $attachment = null)
{
if (!is_array($to) && strpos($to, ',') !== false) {
$all_recipients = explode(',', $to);
foreach ($all_recipients as $k => $v) {
if (empty($v)) {
unset($all_recipients[$k]);
}
}
if (count($all_recipients) > 0) {
$to = $all_recipients;
}
}
if (empty($to)) {
//Prevent Joomla Exceptions that would stop the script execution
if (isset($this->commands['print_errors'])) {
VikError::raiseWarning('', 'The recipient email address is empty. Email message could not be sent. Please check your configuration.');
}
return false;
}
if ($from_name == $from_address) {
$mainframe = JFactory::getApplication();
$attempt_fromn = $mainframe->get('fromname', '');
if (!empty($attempt_fromn)) {
$from_name = $attempt_fromn;
}
}
/**
* Conditional text rules may set extra recipients or attachments.
*
* @since 1.16.0 (J) - 1.6.0 (WP)
*/
$extra_admin_recipients = VikBooking::addAdminEmailRecipient(null);
$bcc_addresses = VikBooking::addAdminEmailRecipient(null, $bcc = true);
$extra_attachments = VikBooking::addEmailAttachment(null);
if ($extra_admin_recipients) {
// cast a possible string to array
$to = (array) $to;
// merge additional recipients
$to = array_merge($to, $extra_admin_recipients);
}
if ($extra_attachments) {
$attachment = $attachment ? (array) $attachment : [];
$attachment = array_merge($attachment, $extra_attachments);
}
/**
* We let the internal library process the email sending depending on the platform.
* This will allow us to perform the required manipulation of the content, if needed.
*
* @since 1.15.2 (J) - 1.5.5 (WP)
*/
$mail_data = new VBOMailWrapper([
'sender' => [$from_address, $from_name],
'recipient' => $to,
'bcc' => $bcc_addresses,
'reply' => $reply_address,
'subject' => $subject,
'content' => $hmess,
'attachments' => $attachment,
]);
// unset queues for the next email sending operation
VikBooking::addAdminEmailRecipient(null, false, $reset = true);
VikBooking::addEmailAttachment(null, $reset = true);
// dispatch the email sending command
return VBOFactory::getPlatform()->getMailer()->send($mail_data);
}
/**
* @param $arr_values array
* @param $current_key string
* @param $empty_value string (J3.x only)
* @param $default
* @param $input_name string
* @param $record_id = '' string
*/
public function getDropDown($arr_values, $current_key, $empty_value, $default, $input_name, $record_id = '')
{
$dropdown = '';
$dropdown .= ''."\n";
return $dropdown;
}
public function loadSelect2()
{
//load JS + CSS
$document = JFactory::getDocument();
$document->addStyleSheet(VBO_ADMIN_URI.'resources/select2.min.css');
$this->addScript(VBO_ADMIN_URI.'resources/select2.min.js');
}
/**
* Returns the HTML code to render a regular dropdown
* menu styled through the jQuery plugin Select2.
*
* @param $arr_values array
* @param $current_key string
* @param $input_name string
* @param $placeholder string used when the select has no selected option (it's empty)
* @param $empty_name [string] the name of the option to set an empty value to the field ()
* @param $empty_val [string] the value of the option to set an empty value to the field ()
* @param $onchange [string] javascript code for the onchange attribute
* @param $idattr [string] the identifier attribute of the select
*
* @return string
*/
public function getNiceSelect($arr_values, $current_key, $input_name, $placeholder, $empty_name = '', $empty_val = '', $onchange = 'document.adminForm.submit();', $idattr = '')
{
//load JS + CSS
$this->loadSelect2();
//attribute
$idattr = empty($idattr) ? rand(1, 999) : $idattr;
//select
$dropdown = ''."\n";
//js code
$dropdown .= ''."\n";
return $dropdown;
}
/**
* Adds the script declaration to render the Bootstrap JModal window.
* The suffix can be passed to generate other JS functions.
* Optionally pass JavaScript code for the 'show' and 'hide' events.
* For compatibility with the Joomla framework, this method should be
* echoed although it does not return anything on WordPress.
*
* @param $suffix string
* @param $hide_js string
* @param $show_js string
*
* @return void should still be echoed for compatibility with J.
*/
public function getJmodalScript($suffix = '', $hide_js = '', $show_js = '')
{
static $loaded = [];
$doc = JFactory::getDocument();
if (!isset($loaded[$suffix]))
{
$doc->addScriptDeclaration(
<<addStyleSheet(VBO_SITE_URI.'resources/jquery-ui.min.css');
JHtml::fetch('jquery.framework', true, true);
$this->addScript(VBO_SITE_URI.'resources/jquery-ui.min.js');
$vbo_df = VikBooking::getDateFormat();
$juidf = $vbo_df == "%d/%m/%Y" ? 'dd/mm/yy' : ($vbo_df == "%m/%d/%Y" ? 'mm/dd/yy' : 'yy/mm/dd');
$is_rtl_lan = false;
$is_rtl_str = 'false';
$day_names_min_len = 2;
$now_lang = JFactory::getLanguage();
if (method_exists($now_lang, 'isRtl')) {
$is_rtl_lan = $now_lang->isRtl();
$is_rtl_str = $is_rtl_lan ? 'true' : $is_rtl_str;
if ($is_rtl_lan) {
// for most RTL languages, 2 chars for the week-days would not make sense
$day_names_min_len = 3;
}
}
$ldecl = '
jQuery(function($){'."\n".'
$.datepicker.regional["vikbooking"] = {'."\n".'
closeText: "'.JText::translate('VBJQCALDONE').'",'."\n".'
prevText: "'.JText::translate('VBJQCALPREV').'",'."\n".'
nextText: "'.JText::translate('VBJQCALNEXT').'",'."\n".'
currentText: "'.JText::translate('VBJQCALTODAY').'",'."\n".'
monthNames: ["'.JText::translate('VBMONTHONE').'","'.JText::translate('VBMONTHTWO').'","'.JText::translate('VBMONTHTHREE').'","'.JText::translate('VBMONTHFOUR').'","'.JText::translate('VBMONTHFIVE').'","'.JText::translate('VBMONTHSIX').'","'.JText::translate('VBMONTHSEVEN').'","'.JText::translate('VBMONTHEIGHT').'","'.JText::translate('VBMONTHNINE').'","'.JText::translate('VBMONTHTEN').'","'.JText::translate('VBMONTHELEVEN').'","'.JText::translate('VBMONTHTWELVE').'"],'."\n".'
monthNamesShort: ["'.$this->safeSubstr(JText::translate('VBMONTHONE')).'","'.$this->safeSubstr(JText::translate('VBMONTHTWO')).'","'.$this->safeSubstr(JText::translate('VBMONTHTHREE')).'","'.$this->safeSubstr(JText::translate('VBMONTHFOUR')).'","'.$this->safeSubstr(JText::translate('VBMONTHFIVE')).'","'.$this->safeSubstr(JText::translate('VBMONTHSIX')).'","'.$this->safeSubstr(JText::translate('VBMONTHSEVEN')).'","'.$this->safeSubstr(JText::translate('VBMONTHEIGHT')).'","'.$this->safeSubstr(JText::translate('VBMONTHNINE')).'","'.$this->safeSubstr(JText::translate('VBMONTHTEN')).'","'.$this->safeSubstr(JText::translate('VBMONTHELEVEN')).'","'.$this->safeSubstr(JText::translate('VBMONTHTWELVE')).'"],'."\n".'
dayNames: ["'.JText::translate('VBWEEKDAYZERO').'", "'.JText::translate('VBWEEKDAYONE').'", "'.JText::translate('VBWEEKDAYTWO').'", "'.JText::translate('VBWEEKDAYTHREE').'", "'.JText::translate('VBWEEKDAYFOUR').'", "'.JText::translate('VBWEEKDAYFIVE').'", "'.JText::translate('VBWEEKDAYSIX').'"],'."\n".'
dayNamesShort: ["'.$this->safeSubstr(JText::translate('VBWEEKDAYZERO')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYONE')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYTWO')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYTHREE')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYFOUR')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYFIVE')).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYSIX')).'"],'."\n".'
dayNamesMin: ["'.$this->safeSubstr(JText::translate('VBWEEKDAYZERO'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYONE'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYTWO'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYTHREE'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYFOUR'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYFIVE'), $day_names_min_len).'", "'.$this->safeSubstr(JText::translate('VBWEEKDAYSIX'), $day_names_min_len).'"],'."\n".'
weekHeader: "'.JText::translate('VBJQCALWKHEADER').'",'."\n".'
dateFormat: "'.$juidf.'",'."\n".'
firstDay: '.VikBooking::getFirstWeekDay().','."\n".'
isRTL: ' . $is_rtl_str . ','."\n".'
showMonthAfterYear: false,'."\n".'
yearSuffix: ""'."\n".'
};'."\n".'
$.datepicker.setDefaults($.datepicker.regional["vikbooking"]);'."\n".'
});';
/**
* Trigger event to allow third party plugins to overwrite the JS declaration for the datepicker.
*
* @since 1.16.0 (J) - 1.6.0 (WP)
*/
VBOFactory::getPlatform()->getDispatcher()->trigger('onBeforeDeclareDatepickerRegionalVikBooking', [$is_rtl_lan, $now_lang->getTag(), &$ldecl]);
// add script declaration
$document->addScriptDeclaration($ldecl);
// cache loaded flag
$datepicker_loaded = 1;
}
/**
* Loads the CMS's native datepicker calendar.
*
* @since 1.10
*/
public function getCalendar($val, $name, $id = null, $df = null, array $attributes = array())
{
if ($df === null)
{
$df = VikBooking::getDateFormat();
}
return parent::calendar($val, $name, $id, $df, $attributes);
}
/**
* Returns a masked e-mail address. The e-mail are masked using
* a technique to encode the bytes in hexadecimal representation.
* The chunk of the masked e-mail will be also encoded to be HTML readable.
*
* @param string $email The e-mail to mask.
* @param boolean $reverse True to reverse the e-mail address.
* Only if the e-mail is not contained into an attribute.
*
* @return string The masked e-mail address.
*/
public function maskMail($email, $reverse = false)
{
if ($reverse)
{
// reverse the e-mail address
$email = strrev($email);
}
// converts the e-mail address from bin to hex
$email = bin2hex($email);
// append ; sequence after every chunk of the masked e-mail
$email = chunk_split($email, 2, ";");
// prepend sequence before the address and trim the ending sequence
$email = "" . substr($email, 0, -3);
return $email;
}
/**
* Returns a safemail tag to avoid the bots spoof a plain address.
*
* @param string $email The e-mail address to mask.
* @param boolean $mail_to True if the address should be wrapped
* within a "mailto" link.
*
* @return string The HTML tag containing the masked address.
*
* @uses maskMail()
*/
public function safeMailTag($email, $mail_to = false)
{
// include the CSS declaration to reverse the text contained in the tags
JFactory::getDocument()->addStyleDeclaration('safemail {direction: rtl;unicode-bidi: bidi-override;}');
// mask the reversed e-mail address
$masked = $this->maskMail($email, true);
// include the address into a custom tag
$tag = "$masked";
if ($mail_to)
{
// mask the address for mailto command (do not use reverse)
$mailto = $this->maskMail($email);
// wrap the safemail tag within a mailto link
$tag = "$tag";
}
return $tag;
}
/**
* Loads and echoes the script necessary to render the Fancybox
* plugin for jQuery to open images or iframes within a modal box.
* This resolves conflicts with some Bootstrap or Joomla (4) versions
* that do not support the old-native CSS class .modal with "behavior.modal".
* Mainly made to open pictures in a modal box, so the default "type" is set to "image".
* By passing a custom $opts string, the "type" property could be set to "iframe", but
* in this case it's better to use the other method of this class (Jmodal).
* The base jQuery library should be already loaded when using this method.
*
* @param string $selector The jQuery selector to trigger Fancybox.
* @param string $opts The options object for the Fancybox setup.
* @param boolean $reloadfunc If true, an additional function is included in the script
* to apply again Fancybox to newly added images to the DOM (via Ajax).
*
* @return void
*
* @uses addScript()
*/
public function prepareModalBox($selector = '.vbomodal', $opts = '', $reloadfunc = false)
{
if (empty($opts)) {
$opts = '{
"helpers": {
"overlay": {
"locked": false
}
},
"width": "70%",
"height": "75%",
"autoScale": true,
"transitionIn": "none",
"transitionOut": "none",
"padding": 0,
"type": "image"
}';
}
$document = JFactory::getDocument();
$document->addStyleSheet(VBO_SITE_URI.'resources/jquery.fancybox.css');
$this->addScript(VBO_SITE_URI.'resources/jquery.fancybox.js');
$reloadjs = '
function reloadFancybox() {
jQuery("'.$selector.'").fancybox('.$opts.');
}
';
$js = '
';
echo $js;
}
/**
* Method used to handle the reCAPTCHA events.
*
* @param string $event The reCAPTCHA event to trigger.
* Here's the list of the accepted events:
* - display Returns the HTML used to
* display the reCAPTCHA input.
* - check Validates the POST data to make sure
* the reCAPTCHA input was checked.
* @param array $options A configuration array.
*
* @return mixed The event response.
*
* @since 1.2.3
* @wponly the Joomla integration differs
*/
public function reCaptcha($event = 'display', array $options = array())
{
$response = null;
// an optional configuration array (just leave empty)
$options = array();
// trigger reCAPTCHA display event to fill $response var
do_action_ref_array('vik_recaptcha_' . $event, array(&$response, $options));
// display reCAPTCHA by echoing it (empty in case reCAPTCHA is not available)
return $response;
}
/**
* Checks if the com_user captcha is configured.
* In case the parameter is set to global, the default one
* will be retrieved.
*
* @param string $plugin The plugin name to check ('recaptcha' by default).
*
* @return boolean True if configured, otherwise false.
*
* @since 1.2.3
* @wponly the Joomla integration differs
*/
public function isCaptcha($plugin = 'recaptcha')
{
return apply_filters('vik_' . $plugin . '_on', false);
}
/**
* Checks if the global captcha is configured.
*
* @param string $plugin The plugin name to check ('recaptcha' by default).
*
* @return boolean True if configured, otherwise false.
*
* @since 1.2.3
*/
public function isGlobalCaptcha($plugin = 'recaptcha')
{
return $this->isCaptcha($plugin);
}
/**
* Method used to obtain a WordPress media form field.
*
* @return string The media in HTML.
*
* @since 1.3.0
*/
public function getMediaField($name, $value = null, array $data = array())
{
// check if WordPress is installed
if (VBOPlatformDetection::isWordPress())
{
add_action('admin_enqueue_scripts', function() {
wp_enqueue_media();
});
// import form field class
JLoader::import('adapter.form.field');
// create XML field manifest
$xml = "";
// instantiate field
$field = JFormField::getInstance(simplexml_load_string($xml));
// overwrite name and value within data
$data['name'] = $name;
$data['value'] = $value;
// inject display data within field instance
foreach ($data as $k => $v)
{
$field->bind($v, $k);
}
// render field
return $field->render();
}
// fallback to Joomla
// init media field
$field = new JFormFieldMedia(null, $value);
// setup an empty form as placeholder
$field->setForm(new JForm('vikbooking.media'));
// force field attributes
$data['name'] = $name;
$data['value'] = $value;
if (empty($data['previewWidth']))
{
// there is no preview width, set a defualt value
// to make the image visible within the popover
$data['previewWidth'] = 480;
}
// render the field
return $field->render('joomla.form.field.media', $data);
}
/**
* Displays a multi-state toggle switch element with unlimited buttons.
* Custom values, contents, labels, attributes and JS events can be attached
* to each button. VCM will use this same method.
*
* @param string $name the input name equal for all radio buttons.
* @param string $value the current input field value to be pre-selected.
* @param array $values list of radio buttons with each value.
* @param array $labels list of contents for each button trigger.
* @param array $attrs list of associative array attributes for each button.
* @param array $wrap list of associative array attributes for the wrapper.
*
* @return string the necessary HTML to render the multi-state toggle switch.
*
* @since 1.15.0 (J) - 1.5.0 (WP)
*/
public function multiStateToggleSwitchField($name, $value, $values = array(), $labels = array(), $attrs = array(), $wrap = array())
{
static $tooltip_js_declared = null;
// whether tooltip for titles is needed
$needs_tooltip = false;
// HTML container
$multi_state_switch = '';
if (!is_array($values) || !count($values)) {
// values must be set or we don't know what buttons to display
return $multi_state_switch;
}
// build default classes for the tri-state toggle switch (with 3 buttons)
$def_tristate_cls = array(
'vik-multiswitch-radiobtn-on',
'vik-multiswitch-radiobtn-def',
'vik-multiswitch-radiobtn-off',
);
// start wrapper
$multi_state_switch .= "\n" . '
' . "\n";
foreach ($values as $btn_k => $btn_val) {
// build default classes for button label
$btn_classes = array('vik-multiswitch-radiobtn');
if (isset($def_tristate_cls[$btn_k])) {
// push default class for a 3-state toggle switch
array_push($btn_classes, $def_tristate_cls[$btn_k]);
}
// check if additional custom classes have been defined for this button
if (isset($attrs[$btn_k]) && isset($attrs[$btn_k]['label_class']) && !empty($attrs[$btn_k]['label_class'])) {
if (is_array($attrs[$btn_k]['label_class'])) {
// list of additional classes for this button
$btn_classes = array_merge($btn_classes, $attrs[$btn_k]['label_class']);
} elseif (is_string($attrs[$btn_k]['label_class'])) {
// multiple classes should be space-separated
array_push($btn_classes, $attrs[$btn_k]['label_class']);
}
}
// check title as first thing, even though this is passed along with the labels
$label_title = '';
if (isset($labels[$btn_k]) && !is_scalar($labels[$btn_k]) && isset($labels[$btn_k]['title'])) {
$needs_tooltip = true;
$label_title = ' title="' . addslashes(htmlentities($labels[$btn_k]['title'])) . '"';
}
// start button label
$multi_state_switch .= "\t" . '' . "\n";
}
// end wrapper
$multi_state_switch .= '
' . "\n";
// check tooltip JS rendering
if (!$tooltip_js_declared && $needs_tooltip) {
// turn static flag on
$tooltip_js_declared = 1;
// add script declaration for JS rendering of tooltips
$doc = JFactory::getDocument();
$doc->addScriptDeclaration(
<<getVisualEditorFonts();
$font_shortfam = $this->getVisualEditorFonts(true);
$js_font_names = json_encode($font_shortfam);
$css_font_decl = '';
foreach ($font_families as $k => $font_name) {
$font_val = $font_shortfam[$k];
$css_font_decl .= '.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="' . $font_val . '"]::before,';
$css_font_decl .= '.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="' . $font_val . '"]::before {' . "\n";
$css_font_decl .= 'content: "' . $font_name . '";' . "\n";
$css_font_decl .= 'font-family: "' . $font_name . '";' . "\n";
$css_font_decl .= '}' . "\n";
}
// append inline CSS styles to document
$doc->addStyleDeclaration('
.ql-picker.ql-specialtags .ql-picker-label {
padding-right: 18px;
}
.ql-picker.ql-specialtags .ql-picker-label:before {
content: "' . htmlspecialchars(JText::translate('VBO_CONDTEXT_TKN')) . '";
}
' . $css_font_decl);
// append theme CSS to document
$doc->addStyleSheet(VBO_ADMIN_URI . 'resources/quill/quill.snow.css');
// append JS assets to document
$this->addScript(VBO_ADMIN_URI . 'resources/quill/quill.js');
$this->addScript(VBO_ADMIN_URI . 'resources/quill/quill-image-resize.min.js');
$this->addScript(VBO_ADMIN_URI . 'resources/quill/vik-content-builder.js');
// icon for mail wrapper
$mail_wrapper_icn = '';
// icon for mail preview
$mail_preview_icn = '';
// icon for property logo (home icon)
$mail_homelogo_icn = '';
// append JS script declaration to document
$doc->addScriptDeclaration(
<< $aval) {
if ($aname == 'name') {
// skip reserved attribute name
continue;
}
$ta_attributes[] = $aname . '="' . JHtml::fetch('esc_attr', $aval) . '"';
}
// labels for modes and editor
$text_html_lbl = JText::translate('VBO_MODE_TEXTHTML');
$visual_mode_lbl = JText::translate('VBO_MODE_VISUAL');
JText::script('VBO_CONT_WRAPPER');
JText::script('VBO_CONT_WRAPPER_HELP');
// allowed modes to display the visual editor
$allowed_modes = array(
'text' => $text_html_lbl,
'visual' => $visual_mode_lbl,
'modal-visual' => $visual_mode_lbl,
);
// define the default buttons to display for mode switching
$modes = array(
'text' => $text_html_lbl,
'modal-visual' => $visual_mode_lbl,
);
// overwrite modes to display
if (isset($opts['modes']) && is_array($opts['modes']) && count($opts['modes'])) {
// check if the given array is associative, hence inclusive of texts
if (array_keys($opts['modes']) != range(0, (count($opts['modes']) - 1))) {
// replace modes
$modes = $opts['modes'];
} else {
// only the allowed keys must have been passed
$new_modes = array();
foreach ($opts['modes'] as $mode_type) {
if (!isset($allowed_modes[$mode_type])) {
continue;
}
$new_modes[$mode_type] = $allowed_modes[$mode_type];
}
$modes = count($new_modes) ? $new_modes : $modes;
}
}
// overwrite default mode
if (isset($opts['def_mode']) && isset($modes[$opts['def_mode']]) && count($modes) > 1) {
$def_mode_val = $modes[$opts['def_mode']];
unset($modes[$opts['def_mode']]);
// sort modes accordingly
$modes = array_merge(array($opts['def_mode'] => $def_mode_val), $modes);
// reset the array pointer
reset($modes);
}
// set default mode
$default_mode = key($modes);
// build visual editor JS options
$js_editor_opts = [
'snippetsyntax' => true,
'modules' => [
'toolbar' => [
'container' => [
[
[
'font' => $this->getVisualEditorFonts(true)
],
],
[
[
'header' => [1, 2, 3, 4, 5, 6, false]
]
],
[
'bold',
'italic',
'underline',
'strike',
'blockquote',
],
[
['align' => []],
['indent' => '-1'],
['indent' => '+1'],
],
[
[
'color' => []
],
[
'background' => []
],
],
[
['list' => 'ordered'],
['list' => 'bullet'],
],
[
'link',
'image',
'homelogo',
],
[
'mailwrapper',
'preview',
],
],
],
'imageResize' => [
'displaySize' => true,
],
],
'theme' => 'snow',
];
// build the list of special tags to be added to the editor
$special_tags_btns = array();
foreach ($btns as $tag_val) {
$special_tags_btns[] = $tag_val;
}
if (count($special_tags_btns)) {
// add custom buttons to the editor to manage special tags
$js_editor_opts['modules']['toolbar']['container'][] = array(
array('specialtags' => $special_tags_btns)
);
// append CSS inline styles
$editor .= '' . "\n";
}
// attept to pretty print a JSON encoded string for the editor options
$editor_opts_str = defined('JSON_PRETTY_PRINT') ? json_encode($js_editor_opts, JSON_PRETTY_PRINT) : json_encode($js_editor_opts);
// safe default value for editor (HTML tags should not be converted to entities)
$safe_value = preg_replace("/(<\/ ?textarea>)+/i", '', $value);
// the HTML to render the visual editor
$html_visual_editor = '';
// build the actual HTML content
$editor .= '
' . "\n";
if (count($modes) > 1) {
// display buttons to switch mode only if more than one mode available
$editor .= "\t" . '