'select', // Type
* 'name' => 'Search By', // Displayed title for item
* 'id' => 'searchby', // Item id and name
* 'width' => '120px', // (Optional) Item width
* 'size' => '15', // (Optional) Maximum number of items to show in the menu (default 15)
* 'value' => $vars['searchby'], // (Optional) Current value(-s) for item
* 'values' => array('mac' => 'MAC Address',
* 'ip' => 'IP Address')); // Array with option items
* - array for 'multiselect' item type (array keys same as above)
* $search[] = array('type' => 'multiselect',
* 'name' => 'Priorities',
* 'id' => 'priority',
* 'width' => '150px',
* 'subtext' => TRUE, // (Optional) Display items value right of the item name
* 'encode' => FALSE, // (Optional) Use var_encode for values, use when values contains commas or empty string
* 'value' => $vars['priority'],
* 'values' => $priorities);
* - array for 'text' or 'input' item type
* $search[] = array('type' => 'text',
* 'name' => 'Address',
* 'id' => 'address',
* 'width' => '120px',
* 'placeholder' => FALSE, // (Optional) Display item name as pleseholder or left relatively input
* 'value' => $vars['address']);
* - array for 'datetime' item type
* $search[] = array('type' => 'datetime',
* 'id' => 'timestamp',
* 'presets' => TRUE, // (optional) Show select field with timerange presets
* 'min' => dbFetchCell('SELECT MIN(`timestamp`) FROM `syslog`'), // (optional) Minimum allowed date/time
* 'max' => dbFetchCell('SELECT MAX(`timestamp`) FROM `syslog`'), // (optional) Maximum allowed date/time
* 'from' => $vars['timestamp_from'], // (optional) Current 'from' value
* 'to' => $vars['timestamp_to']); // (optional) Current 'to' value
* - array for 'sort' item pseudo type
* $search[] = array('type' => 'sort',
* 'value' => $vars['sort'],
* 'values' => $sorts);
* - array for 'newline' item pseudo type
* $search[] = array('type' => 'newline',
* 'hr' => FALSE); // (optional) show or not horizontal line
* print_search($search, 'Title here', 'search', url);
*
* @param array $data
* @param string|null $title
* @param string $button
* @param string|null $url
*
* @return void
*/
function print_search($data, $title = NULL, $button = 'search', $url = NULL)
{
// Cache permissions to session var
permissions_cache_session();
//r($_SESSION['cache']);
$submit_by_key = FALSE;
$string_items = '';
foreach ($data as $item) {
if ($url && isset($item['id'])) {
// Remove old vars from url
$url = preg_replace('/' . $item['id'] . '=[^\/]+\/?/', '', $url);
}
if ($item['type'] == 'sort') {
$sort = $item;
continue;
} elseif (isset($item['submit_by_key']) && $item['submit_by_key']) {
$submit_by_key = TRUE;
}
$string_items .= generate_form_element($item);
}
$form_id = 'search-' . random_string('4');
if ($submit_by_key) {
$action = '';
if ($url) {
$action .= 'this.form.prop(\'action\', form_to_path(\'' . $form_id . '\'));';
}
register_html_resource('script', '$(function(){$(\'form#' . $form_id . '\').each(function(){$(this).find(\'input\').keypress(function(e){if(e.which==10||e.which==13){' . $action . 'this.form.submit();}});});});');
}
// Form header
$string = PHP_EOL . '' . PHP_EOL;
$string .= '
' . PHP_EOL;
$string .= '' . PHP_EOL . PHP_EOL;
// Print search form
echo($string);
}
// Just callback functions to print_form with $return flag
function generate_form($data)
{
return print_form($data, TRUE);
}
function generate_form_box($data)
{
return print_form_box($data, TRUE);
}
/**
* Calculate and store default grid sizes for form rows based on the given data.
*
* @param array $data Input data containing form row and element information
*/
function form_grid_calculate(&$data)
{
// Calculate grid sizes for rows
foreach ($data['row'] as $k => $row) {
$row_count = safe_count($row);
// Default (auto) grid size for elements
$row_grid = (int)(12 / $row_count);
$grid_count = 0; // Count for custom passed grid sizes
foreach ($row as $id => $element) {
if (isset($element['div_class']) && preg_match('/col-(?:lg|md|sm)-(\d+)/', $element['div_class'], $matches)) {
// Class with col size passed
$grid_count += (int)$matches[1];
} elseif (isset($element['grid'])) {
// Grid size passed
if ($element['grid'] > 0 && $element['grid'] <= 12) {
$grid_count += (int)$element['grid'];
} else {
// Incorrect size
unset($row[$k]['grid']);
}
}
}
$row_grid = 12 - $grid_count; // Free grid size after custom grid
$row_grid = (int)($row_grid / $row_count); // Default (auto) grid size for elements
if ($grid_count > 2 && $row_grid < 1) {
$row_grid = 1;
} // minimum 1 for auto if custom grid passed
elseif ($row_grid < 2) {
$row_grid = 2;
} // minimum 2 for auto
$data['grid'][$k] = $row_grid; // Store default grid size for row
}
}
/**
* Pretty form generator
*
* Form options:
* id - form id, default is auto generated
* type - rows (multiple elements with small amount of rows), horizontal (mostly single element per row), simple (raw form without any grid/divs)
* brand - only for rows, adds "other" form title (I think not work and obsolete)
* title - displayed form title (only for rows and horizontal)
* icon - adds icon to title
* class - adds div with class (default box box-solid) in horizontal
* space - adds style for base div in rows type and horizontal with box box-solid class (padding: xx) and horizontal type with box class (padding-top: xx)
* style - adds style for base form element, default (margin-bottom:0;)
* url - form action url, if url set and submit element with id "search" used (or submit_by_key), than form send params as GET query
* submit_by_key - send form query by press enter key in text/input forms
* fieldset - horizontal specific, array with fieldset names and descriptions, in form element should be add fieldset option with same key name
*
* Elements options see in generate_form_element() description
*
* @param array $data Form options and form elements
* @param bool $return If used and set to TRUE, print_form() will return form html instead of outputting it.
*
* @return NULL
*/
function print_form($data, $return = FALSE)
{
// Just return if safety requirements are not fulfilled
if (isset($data['userlevel']) && $data['userlevel'] > $_SESSION['userlevel']) {
return;
}
// Return if the user doesn't have write permissions to the relevant entity
if (isset($data['entity_write_permit']) &&
!is_entity_write_permitted($data['entity_write_permit']['entity_id'], $data['entity_write_permit']['entity_type'])) {
return;
}
// Time our form filling.
$form_start = microtime(TRUE);
$form_id = (isset($data['id']) ? $data['id'] : 'form-' . random_string());
$form_class = 'form form-inline'; // default for rows and simple
if (isset($data['style'])) {
$form_style = ' style="' . $data['style'] . '"';
} else {
$form_style = ' style="margin-bottom: 0px;"';
}
$base_class = array_key_exists('class', $data) ? $data['class'] : OBS_CLASS_BOX;
$base_space = $data['space'] ?: '5px';
$used_vars = [];
// Cache permissions to session var
permissions_cache_session();
if ($data['submit_by_key']) {
$action = '';
if ($data['url']) {
$action .= 'this.form.prop("action", form_to_path("' . $form_id . '"));';
}
register_html_resource('script', '
$(document).ready(function() {
$("form#' . $form_id . '").on("keypress", "input", function(e) {
if (e.which === 10 || e.which === 13) {
' . $action . '
this.form.submit();
}
});
});
');
}
// Form elements
if ($data['type'] === 'rows') {
// Rows form, see example in html/pages/devices.inc.php
$div_padding = 'padding: ' . $base_space . ' !important;';
if (str_contains_array($base_class, 'box')) {
$base_space = ($data['space'] ? $data['space'] : '10px');
// Box horizontal style
$box_args = [
'id' => 'box-' . $form_id,
'header-border' => TRUE,
'body-style' => $div_padding
];
if (isset($data['title'])) {
$box_args['title'] = $data['title'];
}
$div_begin = generate_box_open($box_args);
$div_end = generate_box_close();
unset($box_args);
} else {
$div_begin = '
' . PHP_EOL;
} else {
// Do not add control group for submit/hidden
$fieldset[$row_group] .= $row_elements;
}
//$row_style = 'style="margin-top: '.$base_space.';"'; // Add space between rows
}
foreach ($data['fieldset'] as $row_group => $entry) {
if (isset($fieldset[$row_group])) {
if (!is_array($entry)) {
$entry = ['title' => $entry];
}
$fieldset_begin = '';
$fieldset_end = '';
// Additional div class if set
if (isset($entry['class'])) {
$fieldset_begin = '
' . PHP_EOL . $fieldset_begin;
$fieldset_end .= '
' . PHP_EOL;
}
$row_elements = $fieldset_begin . '
' . PHP_EOL;
$fieldset[$row_group] = $row_elements . $fieldset_end;
}
}
// Final combining elements
$string_elements = implode('', $fieldset);
} else {
// Simple form, without any divs, see example in html/pages/user_edit.inc.php
$div_begin = '';
$div_end = '';
$string_elements = '';
foreach ($data['row'] as $k => $row) {
foreach ($row as $id => $element) {
$used_vars[] = $id;
$element['id'] = $id;
if ($id === 'search') {
// Add form_id here, for generate onclick action in submit button
if ($data['url']) {
$element['form_id'] = $form_id;
}
} else {
// all other cases add form_id
$element['form_id'] = $form_id;
}
$string_elements .= generate_form_element($element);
}
$string_elements .= PHP_EOL;
}
}
// Add CSRF Token
if (!in_array('requesttoken', $used_vars) && isset($_SESSION['requesttoken'])) {
$string_elements .= generate_form_element(['type' => 'hidden',
'id' => 'requesttoken',
'value' => $_SESSION['requesttoken']]) . PHP_EOL;
$used_vars[] = 'requesttoken';
}
// Always clean pagination from form action url
$used_vars[] = 'pageno';
$used_vars[] = 'pagination';
$used_vars[] = 'pagesize';
// Remove old vars from url
if ($data['url']) {
foreach ($used_vars as $var) {
$data['url'] = preg_replace('/' . $var . '=[^\/]+\/?/', '', $data['url']);
}
}
// Form header
if (isset($data['right']) && $data['right']) {
$form_class .= ' pull-right';
}
// auto add some common html attribs
$form_attribs = ['class' => $form_class];
foreach (['onchange', 'oninput', 'onclick', 'ondblclick', 'onfocus', 'onsubmit'] as $attrib) {
if (isset($data[$attrib])) {
$form_attribs[$attrib] = $data[$attrib];
}
}
$string = PHP_EOL . "" . PHP_EOL;
$string .= $div_begin;
$string .= '' . PHP_EOL;
$string .= $div_end;
$string .= "" . PHP_EOL;
if ($return) {
// Save generation time for profiling
$GLOBALS['form_time'] += utime() - $form_start;
// Return form as string
return $string;
}
// Print form
echo($string);
// Save generation time for profiling (after echo)
$GLOBALS['form_time'] += utime() - $form_start;
}
// Box specific form (mostly same as in print_form, but support only box style and fieldset options)
// FIXME should likely not be in this file? As it's used throughout the software now...
function print_form_box($data, $return = FALSE)
{
// Just return if safety requirements are not fulfilled
if (isset($data['userlevel']) && $data['userlevel'] > $_SESSION['userlevel']) {
return;
}
// Return if the user doesn't have write permissions to the relevant entity
if (isset($data['entity_write_permit']) &&
!is_entity_write_permitted($data['entity_write_permit']['entity_id'], $data['entity_write_permit']['entity_type'])) {
return;
}
// Time our form filling.
$form_start = microtime(TRUE);
$form_id = (isset($data['id']) ? $data['id'] : 'form-' . random_string());
$form_class = 'form form-horizontal';
if (isset($data['style'])) {
$form_style = ' style="' . $data['style'] . '"';
} else {
$form_style = ' style="margin-bottom:0px;"';
}
$base_class = (array_key_exists('class', $data) ? $data['class'] : OBS_CLASS_BOX);
$base_space = ($data['space'] ? $data['space'] : '15px');
$used_vars = [];
// Cache permissions to session var
permissions_cache_session();
//r($_SESSION['cache']);
if ($data['submit_by_key']) {
$action = '';
if ($data['url']) {
$action .= 'this.form.prop(\'action\', form_to_path(\'' . $form_id . '\'));';
}
register_html_resource('script', '$(function(){$(\'form#' . $form_id . '\').each(function(){$(this).find(\'input\').keypress(function(e){if(e.which==10||e.which==13){' . $action . 'this.form.submit();}});});});');
}
$header = '';
if (isset($data['title'])) {
$header .= '
' . escape_html($data['title']) . '
' . PHP_EOL;
}
// Form elements
$div_begin = '
' . PHP_EOL;
$div_end = '
' . PHP_EOL;
if ($data['type'] === 'horizontal') {
$row_style = '';
$fieldset = [];
foreach ($data['row'] as $k => $row) {
$first_key = key($row);
$row_group = $k;
$row_elements = '';
$row_label = '';
$row_control_group = FALSE;
$i = 0;
foreach ($row as $id => $element) {
$used_vars[] = $id;
$element['id'] = $id;
if ($element['fieldset']) {
$row_group = $element['fieldset']; // Add this element to group
}
// Additional element options for horizontal specific form
$div_style = '';
switch ($element['type']) {
case 'hidden':
break;
case 'submit':
$div_class = 'form-actions';
break;
case 'text':
case 'input':
case 'password':
case 'textarea':
default:
$row_control_group = TRUE;
// In horizontal, first element name always placed at left
if (!isset($element['placeholder'])) {
$element['placeholder'] = TRUE;
}
// offset == FALSE disable label width and align class control-label
if (!isset($element['offset'])) {
if (isset($data['fieldset'][$element['fieldset']]['offset'])) {
// Copy from fieldset
$element['offset'] = $data['fieldset'][$element['fieldset']]['offset'];
} elseif (($element['type'] === 'raw' || $element['type'] === 'html') && $first_key === $id) {
// When raw/html element first, disable offset
$element['offset'] = FALSE;
} else {
// Default
$element['offset'] = TRUE;
}
}
if ($i < 1) {
// Add label for first element in row
if ($element['name']) {
$row_label = ' ' . PHP_EOL;
}
$row_control_id = $element['id'] . '_div';
if ($element['type'] === 'datetime') {
$element['name'] = '';
}
}
// nextrow class element to new line (after label)
$div_class = ($element['offset']) ? 'controls' : 'nextrow';
break;
}
if (!isset($element['div_class'])) {
$element['div_class'] = $div_class;
}
if ($element['div_class'] === 'form-actions') {
// Remove margins only for form-actions elements
$div_style = 'margin: 0px;';
}
//if ($element['right'])
//{
// $element['div_class'] .= ' pull-right';
//}
if (isset($element['div_style'])) {
$div_style .= ' ' . $element['div_style'];
}
if ($id === 'search') {
// Add form_id here, for generate onclick action in submit button
if ($data['url']) {
$element['form_id'] = $form_id;
}
} else {
// all other cases add form_id
$element['form_id'] = $form_id;
}
$row_elements .= generate_form_element($element);
$i++;
}
if ($element['div_class']) {
// no additional divs if empty div class (hidden element for example)
$row_begin = $row_label . PHP_EOL . '
' . PHP_EOL;
}
if (isset($entry['tooltip'])) {
$box_args['style'] = $entry['style'];
}
$fieldset_begin = generate_box_open($box_args);
$fieldset_end = generate_box_close();
// Additional div class if set
if (isset($entry['class'])) {
$fieldset_begin = '
' . PHP_EOL . $fieldset_begin;
$fieldset_end .= '
' . PHP_EOL;
}
$row_elements = $fieldset_begin . '
' . PHP_EOL;
$fieldset[$group] = $row_elements . $fieldset_end;
}
}
// Combine fieldsets into common rows
foreach ($divs as $entry) {
$row_elements = $div_begin;
foreach ($entry as $i => $group) {
$row_elements .= $fieldset[$group];
if ($i > 0) {
// unset all fieldsets except first one for replace later
unset($fieldset[$group]);
}
}
$row_elements .= $div_end;
// now replace first fieldset in group
$fieldset[array_shift($entry)] = $row_elements;
}
// Final combining elements
$string_elements = implode('', $fieldset);
}
// Add CSRF Token
if (!in_array('requesttoken', $used_vars) && isset($_SESSION['requesttoken'])) {
$string_elements .= generate_form_element(['type' => 'hidden',
'id' => 'requesttoken',
'value' => $_SESSION['requesttoken']]) . PHP_EOL;
$used_vars[] = 'requesttoken';
}
// Remove old vars from url
if ($data['url']) {
foreach ($used_vars as $var) {
$data['url'] = preg_replace('/' . $var . '=[^\/]+\/?/', '', $data['url']);
}
}
// Form header
$string = PHP_EOL . "" . PHP_EOL;
$string .= $header;
$string .= '' . PHP_EOL;
$string .= $fieldset_tooltip;
$string .= "" . PHP_EOL;
if ($return) {
// Save generation time for profiling
$GLOBALS['form_time'] += utime() - $form_start;
// Return form as string
return $string;
}
// Print form
echo($string);
// Save generation time for profiling (after echo)
$GLOBALS['form_time'] += utime() - $form_start;
}
/**
* Generates form elements. The main use for print_search() and print_form(), see examples of this functions.
*
* Common options (can be in any(mostly) element type):
* (string) id - element identificator
* (array) attribs - any custom element attrib (where key is attrib name, value - attrib value)
* (bool) offset - for horizontal forms enable (default) or disable element offset (shift to the right on 180px)
* Options tree:
* textarea -\
* (string)id, (string)name, (bool)readonly, (bool)disabled, (string)width, (string)class,
* (int)rows, (int)cols,
* (string)value, (bool,string)placeholder, (bool)ajax, (array)ajax_vars
* text, input, password -\
* (string)id, (string)name, (bool)readonly, (bool)disabled, (string)width, (string)class,
* (string)value, (bool,string)placeholder, (bool)ajax, (array)ajax_vars,
* (bool)show_password
* hidden -\
* (string)id, (string)value
* select, multiselect -\
* (string)id, (string)name, (bool)readonly, (bool)disabled, (string)onchange, (string)width,
* (string)title, (int)size, (bool)right, (bool)live-search, (bool)encode, (bool)subtext
* (string)value, (array)values, (string)icon,
* values items can be arrays, ie:
* value => array('name' => string, 'group' => string, 'icon' => string, 'class' => string, 'style' => string)
* datetime -\
* (string)id, (string)name, (bool)readonly, (bool)disabled,
* (string|FALSE)from, (string|FALSE)to, (bool)presets, (string)min, (string)max
* (string)value (use it for single input)
* checkbox, switch, toggle -\
* (string)id, (string)name, (bool)readonly, (bool)disabled, (string)onchange,
* [switch only]: (bool)revert, (int)width, (string)size, (string)off-color, (string)on-color, (string)off-text, (string)on-text
* [toggle only]: (string)view, (string)size, (string)palette, (string)group, (string)label,
* (string)icon-check, (string)label-check, (string)icon-uncheck, (string)label-uncheck
* (string)value, (string)placeholder, (string)title
* submit -\
* (string)id, (string)name, (bool)readonly, (bool)disabled,
* (string)class, (bool)right, (string)tooltip,
* (string)value, (string)form_id, (string)icon
* html, raw -\
* (string)id, (bool)offset,
* (string)html, (string)value
* newline -\
* (string)id,
* (bool)hr
*
* @param array $item Options for current form element
* @param string $type Type of form element, also can passed as $item['type']
*
* @return string Generated form element
*/
function generate_form_element($item, $type = '')
{
// Check community edition
if (isset($item['community']) && !$item['community'] && OBSERVIUM_EDITION === 'community') {
return '';
}
// Check and initialize 'readonly' and 'disabled'
$item['readonly'] = isset($item['readonly']) ? $item['readonly'] : FALSE;
$item['disabled'] = isset($item['disabled']) ? $item['disabled'] : FALSE;
$value_isset = isset($item['value']);
if (!$value_isset) {
$item['value'] = '';
}
if (is_array($item['value'])) {
// Passed from URI comma values always converted to array, re-implode it
$item['value_escaped'] = escape_html(implode(',', $item['value']));
} else {
$item['value_escaped'] = escape_html($item['value']);
}
if (!isset($item['type'])) {
$item['type'] = $type;
}
$string = '';
$element_tooltip = '';
$element_data = '';
// auto add some common html attribs
foreach (['onchange', 'oninput', 'onclick', 'ondblclick', 'onfocus', 'onsubmit'] as $attrib) {
if (isset($item[$attrib])) {
$item['attribs'][$attrib] = $item[$attrib];
}
}
if (isset($item['attribs']) && is_array($item['attribs'])) {
// Custom html attributes
if (isset($item['attribs']['data-toggle'])) {
// Enable item specific JS/CSS/Script
switch ($item['attribs']['data-toggle']) {
case 'confirm':
case 'confirmation':
if ($item['attribs']['data-toggle'] === 'confirmation') {
$item['attribs']['data-toggle'] = 'confirm';
}
// popConfirm
//register_html_resource('js', 'jquery.popconfirm.js');
//register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").popConfirm();');
// Bootstrap-Confirmation
register_html_resource('js', 'bootstrap-confirmation.js');
//register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] .
// '\']").confirmation({rootSelector: \'[data-toggle=' . $item['attribs']['data-toggle'] . ']\',});');
//$script_options = [ 'rootSelector: \'[data-toggle=' . $item['attribs']['data-toggle'] . ']\'' ];
if (!isset($item['attribs']['data-btn-ok-label'])) {
// default "Yes"
//$item['attribs']['data-btn-ok-label'] = 'Yes';
}
if (!isset($item['attribs']['data-btn-ok-class'])) {
// default "btn btn-xs btn-primary"
}
if (!isset($item['attribs']['data-btn-ok-icon'])) {
// default "glyphicon glyphicon-ok"
//$item['attribs']['data-btn-ok-icon'] = 'Yes';
}
if (!isset($item['attribs']['data-btn-cancel-label'])) {
// default "No"
//$item['attribs']['data-btn-cancel-label'] = 'Cheese';
}
if (!isset($item['attribs']['data-btn-cancel-class'])) {
// default "btn btn-xs btn-default"
//$item['attribs']['data-btn-cancel-class'] = 'btn-small btn-warning';
}
if (!isset($item['attribs']['data-btn-cancel-icon'])) {
// default "glyphicon glyphicon-remove"
//$item['attribs']['data-btn-cancel-icon'] = 'icon-sort';
}
// migrate from popConfirm
if (!isset($item['attribs']['data-title'])) {
$item['attribs']['data-title'] = 'Confirmation';
}
if (!isset($item['attribs']['data-singleton'])) {
$item['attribs']['data-singleton'] = 'true';
}
if (!isset($item['attribs']['data-popout'])) {
$item['attribs']['data-popout'] = 'true';
}
if (isset($item['attribs']['data-confirm-placement'])) {
$item['attribs']['data-placement'] = $item['attribs']['data-confirm-placement'];
unset($item['attribs']['data-confirm-placement']);
}
if (isset($item['attribs']['data-confirm-content'])) {
$item['attribs']['data-content'] = $item['attribs']['data-confirm-content'];
unset($item['attribs']['data-confirm-content']);
}
//register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").confirmation({' . implode(', ', $script_options) . '});');
break;
case 'switch':
// Bootstrap Toggle
//$element_data .= ' data-selector="bootstrap-toggle"';
$item['attribs']['data-selector'] = "bootstrap-toggle";
register_html_resource('js', 'bootstrap-toggle.min.js');
//register_html_resource('js', 'bootstrap-toggle.js'); /// DEVEL
register_html_resource('css', 'bootstrap-toggle.min.css');
register_html_resource('script', '$("input[data-selector=\'bootstrap-toggle\']").bootstrapToggle();');
/* bootstrapSwitch (deprecated)
register_html_resource('js', 'bootstrap-switch.min.js');
//register_html_resource('css', 'bootstrap-switch.css');
register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").bootstrapSwitch();');
*/
break;
case 'toggle':
if (str_contains($item['class'], 'tiny-toggle')) {
// TinyToggle
$script = '';
if ($item['onchange']) {
// Here toggle specific onchange behavior
$script .= 'onChange: function(obj, value) { ' . $item['onchange'] . ' },';
// Set custom element selector
$selector = 'tiny-toggle-' . md5($item['onchange']);
//$element_data .= ' data-selector="'.$selector.'"';
$item['attribs']['data-selector'] = $selector;
register_html_resource('script', '$("input[data-selector=\'' . $selector . '\'].tiny-toggle").tinyToggle({' . $script . '});');
unset($item['onchange']);
} else {
//$element_data .= ' data-selector="tiny-toggle"';
$item['attribs']['data-selector'] = "tiny-toggle";
//register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").tinyToggle({'.$script.'});'); // this selector intersects with bootstrap toggle
register_html_resource('script', '$("input[data-selector=\'tiny-toggle\'].tiny-toggle").tinyToggle();');
}
register_html_resource('js', 'tiny-toggle.min.js');
register_html_resource('css', 'tiny-toggle.min.css');
} else {
// Bootstrap Toggle
//$element_data .= ' data-selector="bootstrap-toggle"';
$item['attribs']['data-selector'] = "bootstrap-toggle";
register_html_resource('js', 'bootstrap-toggle.min.js');
//register_html_resource('js', 'bootstrap-toggle.js'); /// DEVEL
register_html_resource('css', 'bootstrap-toggle.min.css');
register_html_resource('script', '$("input[data-selector=\'bootstrap-toggle\']").bootstrapToggle();');
}
//r($item);
break;
}
}
$element_data .= ' ' . generate_html_attribs($item['attribs']);
}
switch ($item['type']) {
case 'hidden':
if (!$item['readonly'] && !$item['disabled']) // If item readonly or disabled, just skip item
{
$string .= ' ' . PHP_EOL;
}
break;
case 'password':
case 'textarea':
case 'text':
case 'input':
$item_class = '';
$value_hidden = FALSE;
if ($item['type'] !== 'textarea') {
$item_begin = ' 7)) {
$item['value_escaped'] = '••••••••';
$value_hidden = TRUE;
}
// add icon for show/hide password
if ($item['show_password']) {
$item_begin .= ' data-toggle="password" ';
register_html_resource('js', 'bootstrap-show-password.min.js');
$GLOBALS['cache_html']['javascript'][] = "$('[data-toggle=\"password\"]').password();";
}
//elseif (!$value_hidden && $autocomplete_off) {}
}
// Disable Autocomplete if required
if ($autocomplete_off) {
$browser = detect_browser();
//r($browser);
// Autofill off is not simple!
//https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion#The_autocomplete_attribute_and_login_fields
//https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands
//https://stackoverflow.com/questions/41945535/html-disable-password-manager
if ($item['type'] === 'password' && !$value_hidden) {
$autocomplete_value = 'new-password';
$item_begin = '' .
'' .
'' .
$item_begin;
} else {
$autocomplete_value = 'off';
}
$item_begin .= ' autocomplete="' . $autocomplete_value . '" ';
if ($browser['browser'] === 'Safari') {
// Safari issue: https://stackoverflow.com/questions/22661977/disabling-safari-autofill-on-usernames-and-passwords
//$item_begin .= ' autocomplete="off" readonly onfocus="if (this.hasAttribute(\'readonly\')) {this.removeAttribute(\'readonly\'); this.blur(); this.focus();}" ';
//$item_begin .= ' autocomplete="false" ';
// This disable safari autocomplete button
register_html_resource('style', <<';
$item_class .= 'input';
} else {
$item_begin = ' ';
$item_class .= 'form-control';
}
$item_begin .= $element_data; // Add custom data- attribs
if ($item['disabled']) {
$item_end = ' disabled="1"' . $item_end;
} elseif ($item['readonly']) {
$item_end = ' readonly="1"' . $item_end;
}
if (isset($item['placeholder']) && $item['placeholder'] !== FALSE) {
if ($item['placeholder'] === TRUE) {
$item['placeholder'] = $item['name'];
}
$string .= PHP_EOL;
$string .= $item_begin . 'placeholder="' . $item['placeholder'] . '" ';
$item['placeholder'] = TRUE; // Set to true for check at end
} elseif ($item['type'] === 'text') {
$string .= $item_begin;
} else {
$string .= '