Version Description
- Updated Bootstrap Multiselect to version 1.1.1, fixing issues with the accessibility of backend multiselect dropdowns for blind users.
- New: Inputs with errors will now add the aria-describedby attribute during JavaScript validation for more accessible errors.
- New: Form errors will now always include the role="alert" attribute for more accessible errors. New fields will now also include role="alert" in custom field HTML.
- New: Added a new frm_entries_column_value filter hook.
Download this release
Release Info
Developer | formidableforms |
Plugin | Formidable Forms – Form Builder for WordPress |
Version | 5.1 |
Comparing to | |
See all releases |
Code changes from version 5.0.17 to 5.1
- classes/controllers/FrmAppController.php +1 -1
- classes/controllers/FrmFieldsController.php +2 -0
- classes/controllers/FrmFormsController.php +1 -1
- classes/helpers/FrmAppHelper.php +4 -2
- classes/helpers/FrmEntriesListHelper.php +10 -0
- classes/models/FrmAntiSpam.php +11 -11
- classes/models/FrmFieldFormHtml.php +30 -0
- classes/models/fields/FrmFieldType.php +1 -1
- classes/views/frm-fields/front-end/combo-field/combo-field.php +1 -1
- classes/views/frm-forms/form.php +0 -1
- classes/views/frm-forms/multiselect-accessibility.php +1 -6
- classes/views/frm-forms/settings.php +0 -3
- classes/views/frm-settings/permissions.php +1 -4
- css/frm_admin.css +12 -45
- formidable.php +1 -1
- js/bootstrap-multiselect.js +585 -335
- js/formidable.js +3 -1
- js/formidable.min.js +25 -24
- js/formidable_admin.js +35 -6
- languages/formidable.pot +10 -22
- readme.txt +8 -11
classes/controllers/FrmAppController.php
CHANGED
@@ -460,7 +460,7 @@ class FrmAppController {
|
|
460 |
|
461 |
// load multselect js
|
462 |
$depends_on = array( 'jquery', 'bootstrap_tooltip' );
|
463 |
-
wp_register_script( 'bootstrap-multiselect', FrmAppHelper::plugin_url() . '/js/bootstrap-multiselect.js', $depends_on, '
|
464 |
|
465 |
$page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
|
466 |
$post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title' );
|
460 |
|
461 |
// load multselect js
|
462 |
$depends_on = array( 'jquery', 'bootstrap_tooltip' );
|
463 |
+
wp_register_script( 'bootstrap-multiselect', FrmAppHelper::plugin_url() . '/js/bootstrap-multiselect.js', $depends_on, '1.1.1', true );
|
464 |
|
465 |
$page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
|
466 |
$post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title' );
|
classes/controllers/FrmFieldsController.php
CHANGED
@@ -743,7 +743,9 @@ class FrmFieldsController {
|
|
743 |
$error_body = substr( $custom_html, $start + 10, $end - $start - 10 );
|
744 |
$default_html = array(
|
745 |
'<div class="frm_error" id="frm_error_field_[key]">[error]</div>',
|
|
|
746 |
'<div class="frm_error">[error]</div>',
|
|
|
747 |
);
|
748 |
|
749 |
if ( in_array( $error_body, $default_html, true ) ) {
|
743 |
$error_body = substr( $custom_html, $start + 10, $end - $start - 10 );
|
744 |
$default_html = array(
|
745 |
'<div class="frm_error" id="frm_error_field_[key]">[error]</div>',
|
746 |
+
'<div class="frm_error" role="alert" id="frm_error_field_[key]">[error]</div>',
|
747 |
'<div class="frm_error">[error]</div>',
|
748 |
+
'<div class="frm_error" role="alert">[error]</div>',
|
749 |
);
|
750 |
|
751 |
if ( in_array( $error_body, $default_html, true ) ) {
|
classes/controllers/FrmFormsController.php
CHANGED
@@ -685,7 +685,7 @@ class FrmFormsController {
|
|
685 |
/**
|
686 |
* Check the page being loaded, determine if this is a page that should include the form popup.
|
687 |
*
|
688 |
-
* @since
|
689 |
*
|
690 |
* @return bool
|
691 |
*/
|
685 |
/**
|
686 |
* Check the page being loaded, determine if this is a page that should include the form popup.
|
687 |
*
|
688 |
+
* @since 5.0.14
|
689 |
*
|
690 |
* @return bool
|
691 |
*/
|
classes/helpers/FrmAppHelper.php
CHANGED
@@ -11,7 +11,7 @@ class FrmAppHelper {
|
|
11 |
/**
|
12 |
* @since 2.0
|
13 |
*/
|
14 |
-
public static $plug_version = '5.
|
15 |
|
16 |
/**
|
17 |
* @since 1.07.02
|
@@ -2934,9 +2934,11 @@ class FrmAppHelper {
|
|
2934 |
|
2935 |
/**
|
2936 |
* Output HTML containing reference text for accessibility
|
|
|
|
|
2937 |
*/
|
2938 |
public static function multiselect_accessibility() {
|
2939 |
-
|
2940 |
}
|
2941 |
|
2942 |
public static function get_menu_icon_class() {
|
11 |
/**
|
12 |
* @since 2.0
|
13 |
*/
|
14 |
+
public static $plug_version = '5.1';
|
15 |
|
16 |
/**
|
17 |
* @since 1.07.02
|
2934 |
|
2935 |
/**
|
2936 |
* Output HTML containing reference text for accessibility
|
2937 |
+
*
|
2938 |
+
* @deprecated 5.1
|
2939 |
*/
|
2940 |
public static function multiselect_accessibility() {
|
2941 |
+
_deprecated_function( __METHOD__, '5.1' );
|
2942 |
}
|
2943 |
|
2944 |
public static function get_menu_icon_class() {
|
classes/helpers/FrmEntriesListHelper.php
CHANGED
@@ -291,6 +291,16 @@ class FrmEntriesListHelper extends FrmListHelper {
|
|
291 |
if ( $val === false ) {
|
292 |
$this->get_column_value( $item, $val );
|
293 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
}
|
295 |
|
296 |
return $val;
|
291 |
if ( $val === false ) {
|
292 |
$this->get_column_value( $item, $val );
|
293 |
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Allows changing entries list column value.
|
297 |
+
*
|
298 |
+
* @since 5.1
|
299 |
+
*
|
300 |
+
* @param mixed $val Column value.
|
301 |
+
* @param array $args Contains `item` and `col_name`.
|
302 |
+
*/
|
303 |
+
$val = apply_filters( 'frm_entries_column_value', $val, compact( 'item', 'col_name' ) );
|
304 |
}
|
305 |
|
306 |
return $val;
|
classes/models/FrmAntiSpam.php
CHANGED
@@ -8,7 +8,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
8 |
*
|
9 |
* This token class generates tokens that are used in our Anti-Spam checking.
|
10 |
*
|
11 |
-
* @since
|
12 |
*/
|
13 |
class FrmAntiSpam extends FrmValidate {
|
14 |
|
@@ -32,7 +32,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
32 |
/**
|
33 |
* Initialise the actions for the Anti-spam.
|
34 |
*
|
35 |
-
* @since
|
36 |
*/
|
37 |
public function init() {
|
38 |
add_filter( 'frm_form_attributes', array( $this, 'add_token_to_form' ), 10, 1 );
|
@@ -42,7 +42,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
42 |
/**
|
43 |
* Return a valid token.
|
44 |
*
|
45 |
-
* @since
|
46 |
*
|
47 |
* @param mixed $current True to use current time, otherwise a timestamp string.
|
48 |
*
|
@@ -91,7 +91,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
91 |
* 'frm_form_token_check_before_today'
|
92 |
* 'frm_form_token_check_after_today'
|
93 |
*
|
94 |
-
* @since
|
95 |
*
|
96 |
* @return array Array of all valid tokens to check against.
|
97 |
*/
|
@@ -143,7 +143,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
143 |
* and frm_token_validity_in_days to extend the validation period).
|
144 |
* By default tokens are valid for day.
|
145 |
*
|
146 |
-
* @since
|
147 |
*
|
148 |
* @param string $token Token to validate.
|
149 |
*
|
@@ -157,7 +157,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
157 |
/**
|
158 |
* Add the token field to the form.
|
159 |
*
|
160 |
-
* @since
|
161 |
*
|
162 |
* @param string $attributes
|
163 |
*
|
@@ -188,7 +188,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
188 |
/**
|
189 |
* Validate Anti-spam if enabled.
|
190 |
*
|
191 |
-
* @since
|
192 |
*
|
193 |
* @return bool|string True or a string with the error.
|
194 |
*/
|
@@ -235,7 +235,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
235 |
/**
|
236 |
* Helper to run our filter on all the responses for the antispam checks.
|
237 |
*
|
238 |
-
* @since
|
239 |
*
|
240 |
* @param bool|string $is_valid Is valid entry or not.
|
241 |
*
|
@@ -248,7 +248,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
248 |
/**
|
249 |
* Helper to get the missing token message.
|
250 |
*
|
251 |
-
* @since
|
252 |
*
|
253 |
* @return string missing token message.
|
254 |
*/
|
@@ -259,7 +259,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
259 |
/**
|
260 |
* Helper to get the invalid token message.
|
261 |
*
|
262 |
-
* @since
|
263 |
*
|
264 |
* @return string Invalid token message.
|
265 |
*/
|
@@ -270,7 +270,7 @@ class FrmAntiSpam extends FrmValidate {
|
|
270 |
/**
|
271 |
* If a user is a super admin, add a support link to the message.
|
272 |
*
|
273 |
-
* @since
|
274 |
*
|
275 |
* @return string Support text if super admin, empty string if not.
|
276 |
*/
|
8 |
*
|
9 |
* This token class generates tokens that are used in our Anti-Spam checking.
|
10 |
*
|
11 |
+
* @since 4.11
|
12 |
*/
|
13 |
class FrmAntiSpam extends FrmValidate {
|
14 |
|
32 |
/**
|
33 |
* Initialise the actions for the Anti-spam.
|
34 |
*
|
35 |
+
* @since 4.11
|
36 |
*/
|
37 |
public function init() {
|
38 |
add_filter( 'frm_form_attributes', array( $this, 'add_token_to_form' ), 10, 1 );
|
42 |
/**
|
43 |
* Return a valid token.
|
44 |
*
|
45 |
+
* @since 4.11
|
46 |
*
|
47 |
* @param mixed $current True to use current time, otherwise a timestamp string.
|
48 |
*
|
91 |
* 'frm_form_token_check_before_today'
|
92 |
* 'frm_form_token_check_after_today'
|
93 |
*
|
94 |
+
* @since 4.11
|
95 |
*
|
96 |
* @return array Array of all valid tokens to check against.
|
97 |
*/
|
143 |
* and frm_token_validity_in_days to extend the validation period).
|
144 |
* By default tokens are valid for day.
|
145 |
*
|
146 |
+
* @since 4.11
|
147 |
*
|
148 |
* @param string $token Token to validate.
|
149 |
*
|
157 |
/**
|
158 |
* Add the token field to the form.
|
159 |
*
|
160 |
+
* @since 4.11
|
161 |
*
|
162 |
* @param string $attributes
|
163 |
*
|
188 |
/**
|
189 |
* Validate Anti-spam if enabled.
|
190 |
*
|
191 |
+
* @since 4.11
|
192 |
*
|
193 |
* @return bool|string True or a string with the error.
|
194 |
*/
|
235 |
/**
|
236 |
* Helper to run our filter on all the responses for the antispam checks.
|
237 |
*
|
238 |
+
* @since 4.11
|
239 |
*
|
240 |
* @param bool|string $is_valid Is valid entry or not.
|
241 |
*
|
248 |
/**
|
249 |
* Helper to get the missing token message.
|
250 |
*
|
251 |
+
* @since 4.11
|
252 |
*
|
253 |
* @return string missing token message.
|
254 |
*/
|
259 |
/**
|
260 |
* Helper to get the invalid token message.
|
261 |
*
|
262 |
+
* @since 4.11
|
263 |
*
|
264 |
* @return string Invalid token message.
|
265 |
*/
|
270 |
/**
|
271 |
* If a user is a super admin, add a support link to the message.
|
272 |
*
|
273 |
+
* @since 4.11
|
274 |
*
|
275 |
* @return string Support text if super admin, empty string if not.
|
276 |
*/
|
classes/models/FrmFieldFormHtml.php
CHANGED
@@ -230,9 +230,39 @@ class FrmFieldFormHtml {
|
|
230 |
private function replace_error_shortcode() {
|
231 |
$this->maybe_add_error_id();
|
232 |
$error = isset( $this->pass_args['errors'][ 'field' . $this->field_id ] ) ? $this->pass_args['errors'][ 'field' . $this->field_id ] : false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
FrmShortcodeHelper::remove_inline_conditions( ! empty( $error ), 'error', $error, $this->html );
|
234 |
}
|
235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
/**
|
237 |
* Add an ID to the error message for aria-describedby.
|
238 |
* This ID was added to the HTML in v3.06.02.
|
230 |
private function replace_error_shortcode() {
|
231 |
$this->maybe_add_error_id();
|
232 |
$error = isset( $this->pass_args['errors'][ 'field' . $this->field_id ] ) ? $this->pass_args['errors'][ 'field' . $this->field_id ] : false;
|
233 |
+
|
234 |
+
if ( ! empty( $error ) && false === strpos( $this->html, 'role="alert"' ) ) {
|
235 |
+
$error_body = self::get_error_body( $this->html );
|
236 |
+
if ( is_string( $error_body ) && false === strpos( $error_body, 'role=' ) ) {
|
237 |
+
$new_error_body = preg_replace( '/class="frm_error/', 'role="alert" class="frm_error', $error_body, 1 );
|
238 |
+
$this->html = str_replace( '[if error]' . $error_body . '[/if error]', '[if error]' . $new_error_body . '[/if error]', $this->html );
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
FrmShortcodeHelper::remove_inline_conditions( ! empty( $error ), 'error', $error, $this->html );
|
243 |
}
|
244 |
|
245 |
+
/**
|
246 |
+
* Pull the HTML between [if error] and [/if error] shortcodes.
|
247 |
+
*
|
248 |
+
* @param string $html
|
249 |
+
* @return string|false
|
250 |
+
*/
|
251 |
+
private static function get_error_body( $html ) {
|
252 |
+
$start = strpos( $html, '[if error]' );
|
253 |
+
if ( false === $start ) {
|
254 |
+
return false;
|
255 |
+
}
|
256 |
+
|
257 |
+
$end = strpos( $html, '[/if error]', $start );
|
258 |
+
if ( false === $end ) {
|
259 |
+
return false;
|
260 |
+
}
|
261 |
+
|
262 |
+
$error_body = substr( $html, $start + 10, $end - $start - 10 );
|
263 |
+
return $error_body;
|
264 |
+
}
|
265 |
+
|
266 |
/**
|
267 |
* Add an ID to the error message for aria-describedby.
|
268 |
* This ID was added to the HTML in v3.06.02.
|
classes/models/fields/FrmFieldType.php
CHANGED
@@ -177,7 +177,7 @@ abstract class FrmFieldType {
|
|
177 |
</$label>
|
178 |
$input
|
179 |
[if description]<div class="frm_description" id="frm_desc_field_[key]">[description]</div>[/if description]
|
180 |
-
[if error]<div class="frm_error" id="frm_error_field_[key]">[error]</div>[/if error]
|
181 |
</div>
|
182 |
DEFAULT_HTML;
|
183 |
|
177 |
</$label>
|
178 |
$input
|
179 |
[if description]<div class="frm_description" id="frm_desc_field_[key]">[description]</div>[/if description]
|
180 |
+
[if error]<div class="frm_error" role="alert" id="frm_error_field_[key]">[error]</div>[/if error]
|
181 |
</div>
|
182 |
DEFAULT_HTML;
|
183 |
|
classes/views/frm-fields/front-end/combo-field/combo-field.php
CHANGED
@@ -80,7 +80,7 @@ $inputs_attrs = $this->get_inputs_container_attrs();
|
|
80 |
// Don't show individual field errors when there is a combo field error.
|
81 |
if ( ! empty( $errors ) && isset( $errors[ 'field' . $field_id . '-' . $name ] ) && ! isset( $errors[ 'field' . $field_id ] ) ) {
|
82 |
?>
|
83 |
-
<div class="frm_error"><?php echo esc_html( $errors[ 'field' . $field_id . '-' . $name ] ); ?></div>
|
84 |
<?php } ?>
|
85 |
</div>
|
86 |
<?php } ?>
|
80 |
// Don't show individual field errors when there is a combo field error.
|
81 |
if ( ! empty( $errors ) && isset( $errors[ 'field' . $field_id . '-' . $name ] ) && ! isset( $errors[ 'field' . $field_id ] ) ) {
|
82 |
?>
|
83 |
+
<div class="frm_error" role="alert"><?php echo esc_html( $errors[ 'field' . $field_id . '-' . $name ] ); ?></div>
|
84 |
<?php } ?>
|
85 |
</div>
|
86 |
<?php } ?>
|
classes/views/frm-forms/form.php
CHANGED
@@ -64,4 +64,3 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
64 |
</div>
|
65 |
<?php
|
66 |
FrmFieldsHelper::bulk_options_overlay();
|
67 |
-
FrmAppHelper::multiselect_accessibility();
|
64 |
</div>
|
65 |
<?php
|
66 |
FrmFieldsHelper::bulk_options_overlay();
|
|
classes/views/frm-forms/multiselect-accessibility.php
CHANGED
@@ -2,9 +2,4 @@
|
|
2 |
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
die( 'You are not allowed to call this page directly.' );
|
4 |
}
|
5 |
-
|
6 |
-
<div class="hidden">
|
7 |
-
<div id="frm_press_space_checked"><?php esc_html_e( 'Checked. To uncheck this option, press Space or Enter', 'formidable' ); ?></div>
|
8 |
-
<div id="frm_press_space_unchecked"><?php esc_html_e( 'Unchecked. To check this option, press Space or Enter', 'formidable' ); ?></div>
|
9 |
-
<div id="frm_multiselect_button"><?php esc_html_e( 'You are on a Custom List of Checkboxes. To open, press Enter. Use Up and Down arrow keys to switch between options', 'formidable' ); ?></div>
|
10 |
-
</div>
|
2 |
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
die( 'You are not allowed to call this page directly.' );
|
4 |
}
|
5 |
+
_deprecated_file( esc_html( basename( __FILE__ ) ), 'x.x' );
|
|
|
|
|
|
|
|
|
|
classes/views/frm-forms/settings.php
CHANGED
@@ -64,6 +64,3 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
64 |
</div>
|
65 |
</form>
|
66 |
</div>
|
67 |
-
|
68 |
-
<?php
|
69 |
-
FrmAppHelper::multiselect_accessibility();
|
64 |
</div>
|
65 |
</form>
|
66 |
</div>
|
|
|
|
|
|
classes/views/frm-settings/permissions.php
CHANGED
@@ -14,12 +14,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
14 |
?>
|
15 |
<tr>
|
16 |
<td class="frm_left_label">
|
17 |
-
<label id="for_<?php echo esc_attr( str_replace( '[]', '', $role_field_name ) ); ?>"><?php echo esc_html( $frm_role_description ); ?></label>
|
18 |
</td>
|
19 |
<td><?php FrmAppHelper::wp_roles_dropdown( $role_field_name, $frm_settings->$frm_role, 'multiple' ); ?></td>
|
20 |
</tr>
|
21 |
<?php } ?>
|
22 |
</table>
|
23 |
-
|
24 |
-
<?php
|
25 |
-
FrmAppHelper::multiselect_accessibility();
|
14 |
?>
|
15 |
<tr>
|
16 |
<td class="frm_left_label">
|
17 |
+
<label id="for_<?php echo esc_attr( str_replace( '[]', '', $role_field_name ) ); ?>" for="<?php echo esc_attr( $role_field_name ); ?>"><?php echo esc_html( $frm_role_description ); ?></label>
|
18 |
</td>
|
19 |
<td><?php FrmAppHelper::wp_roles_dropdown( $role_field_name, $frm_settings->$frm_role, 'multiple' ); ?></td>
|
20 |
</tr>
|
21 |
<?php } ?>
|
22 |
</table>
|
|
|
|
|
|
css/frm_admin.css
CHANGED
@@ -2883,51 +2883,29 @@ a.frm_option_icon:hover::before {
|
|
2883 |
font-family: "s11-fp" !important;
|
2884 |
}
|
2885 |
|
2886 |
-
.multiselect-container.frm-dropdown-menu input[type=radio] {
|
2887 |
-
display: none;
|
2888 |
-
}
|
2889 |
-
|
2890 |
.multiselect-container {
|
2891 |
position: absolute;
|
2892 |
list-style-type: none;
|
2893 |
margin: 0;
|
2894 |
-
padding: 0
|
2895 |
-
}
|
2896 |
-
|
2897 |
-
.multiselect-container .input-group {
|
2898 |
-
margin: 5px
|
2899 |
-
}
|
2900 |
-
|
2901 |
-
.multiselect-container > li {
|
2902 |
padding: 0;
|
2903 |
-
|
2904 |
-
|
2905 |
-
|
2906 |
-
.multiselect-container > li > a.multiselect-all label {
|
2907 |
-
font-weight: 700
|
2908 |
-
}
|
2909 |
-
|
2910 |
-
.multiselect-container > li > label.multiselect-group {
|
2911 |
-
margin: 0;
|
2912 |
-
padding: 3px 20px;
|
2913 |
-
height: 100%;
|
2914 |
-
font-weight: 700
|
2915 |
}
|
2916 |
|
2917 |
-
.
|
2918 |
-
|
|
|
|
|
|
|
|
|
|
|
2919 |
}
|
2920 |
|
2921 |
-
.multiselect-container
|
2922 |
-
margin:
|
2923 |
-
padding: 3px 25px;
|
2924 |
-
height: 100%;
|
2925 |
-
cursor: pointer;
|
2926 |
-
font-weight: 400;
|
2927 |
-
display: block;
|
2928 |
}
|
2929 |
|
2930 |
-
.accordion-container .multiselect-container
|
2931 |
padding: 3px 19px 3px 7px;
|
2932 |
}
|
2933 |
|
@@ -2936,17 +2914,6 @@ a.frm_option_icon:hover::before {
|
|
2936 |
border-bottom-left-radius: 4px
|
2937 |
}
|
2938 |
|
2939 |
-
.form-inline .multiselect-container label.checkbox,
|
2940 |
-
.form-inline .multiselect-container label.radio {
|
2941 |
-
padding: 3px 20px;
|
2942 |
-
}
|
2943 |
-
|
2944 |
-
.form-inline .multiselect-container li a label.checkbox input[type=checkbox],
|
2945 |
-
.form-inline .multiselect-container li a label.radio input[type=radio] {
|
2946 |
-
margin-left: -20px;
|
2947 |
-
margin-right: 0;
|
2948 |
-
}
|
2949 |
-
|
2950 |
.frm-btn-group.btn-group, .frm-btn-group.btn-group-vertical {
|
2951 |
display: block;
|
2952 |
vertical-align: middle;
|
2883 |
font-family: "s11-fp" !important;
|
2884 |
}
|
2885 |
|
|
|
|
|
|
|
|
|
2886 |
.multiselect-container {
|
2887 |
position: absolute;
|
2888 |
list-style-type: none;
|
2889 |
margin: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2890 |
padding: 0;
|
2891 |
+
width: 100%;
|
2892 |
+
max-width: 250px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2893 |
}
|
2894 |
|
2895 |
+
.multiselect-container button.multiselect-option {
|
2896 |
+
display: block;
|
2897 |
+
width: 100%;
|
2898 |
+
text-align: left;
|
2899 |
+
background: none;
|
2900 |
+
border: none;
|
2901 |
+
padding: 5px 0 5px 15px;
|
2902 |
}
|
2903 |
|
2904 |
+
.multiselect-container button.multiselect-option label {
|
2905 |
+
margin-left: 5px;
|
|
|
|
|
|
|
|
|
|
|
2906 |
}
|
2907 |
|
2908 |
+
.accordion-container .multiselect-container label {
|
2909 |
padding: 3px 19px 3px 7px;
|
2910 |
}
|
2911 |
|
2914 |
border-bottom-left-radius: 4px
|
2915 |
}
|
2916 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2917 |
.frm-btn-group.btn-group, .frm-btn-group.btn-group-vertical {
|
2918 |
display: block;
|
2919 |
vertical-align: middle;
|
formidable.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
/*
|
3 |
Plugin Name: Formidable Forms
|
4 |
Description: Quickly and easily create drag-and-drop forms
|
5 |
-
Version: 5.
|
6 |
Plugin URI: https://formidableforms.com/
|
7 |
Author URI: https://formidableforms.com/
|
8 |
Author: Strategy11
|
2 |
/*
|
3 |
Plugin Name: Formidable Forms
|
4 |
Description: Quickly and easily create drag-and-drop forms
|
5 |
+
Version: 5.1
|
6 |
Plugin URI: https://formidableforms.com/
|
7 |
Author URI: https://formidableforms.com/
|
8 |
Author: Strategy11
|
js/bootstrap-multiselect.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
* Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/)
|
3 |
*
|
4 |
* Apache License, Version 2.0:
|
5 |
-
* Copyright (c) 2012 -
|
6 |
*
|
7 |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8 |
* use this file except in compliance with the License. You may obtain a
|
@@ -15,7 +15,7 @@
|
|
15 |
* under the License.
|
16 |
*
|
17 |
* BSD 3-Clause License:
|
18 |
-
* Copyright (c) 2012 -
|
19 |
* All rights reserved.
|
20 |
*
|
21 |
* Redistribution and use in source and binary forms, with or without
|
@@ -41,14 +41,25 @@
|
|
41 |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
42 |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
43 |
*/
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
"use strict";// jshint ;_;
|
46 |
|
47 |
if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
|
48 |
ko.bindingHandlers.multiselect = {
|
49 |
after: ['options', 'value', 'selectedOptions', 'enable', 'disable'],
|
50 |
|
51 |
-
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
|
52 |
var $element = $(element);
|
53 |
var config = ko.toJS(valueAccessor());
|
54 |
|
@@ -58,9 +69,9 @@
|
|
58 |
var options = allBindings.get('options');
|
59 |
if (ko.isObservable(options)) {
|
60 |
ko.computed({
|
61 |
-
read: function() {
|
62 |
options();
|
63 |
-
setTimeout(function() {
|
64 |
var ms = $element.data('multiselect');
|
65 |
if (ms)
|
66 |
ms.updateOriginalOptions();//Not sure how beneficial this is.
|
@@ -79,9 +90,9 @@
|
|
79 |
var value = allBindings.get('value');
|
80 |
if (ko.isObservable(value)) {
|
81 |
ko.computed({
|
82 |
-
read: function() {
|
83 |
value();
|
84 |
-
setTimeout(function() {
|
85 |
$element.multiselect('refresh');
|
86 |
}, 1);
|
87 |
},
|
@@ -96,9 +107,9 @@
|
|
96 |
var selectedOptions = allBindings.get('selectedOptions');
|
97 |
if (ko.isObservable(selectedOptions)) {
|
98 |
ko.computed({
|
99 |
-
read: function() {
|
100 |
selectedOptions();
|
101 |
-
setTimeout(function() {
|
102 |
$element.multiselect('refresh');
|
103 |
}, 1);
|
104 |
},
|
@@ -144,12 +155,12 @@
|
|
144 |
}
|
145 |
}
|
146 |
|
147 |
-
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
|
148 |
$element.multiselect('destroy');
|
149 |
});
|
150 |
},
|
151 |
|
152 |
-
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
|
153 |
var $element = $(element);
|
154 |
var config = ko.toJS(valueAccessor());
|
155 |
|
@@ -208,6 +219,7 @@
|
|
208 |
this.buildSelectAll();
|
209 |
this.buildDropdownOptions();
|
210 |
this.buildFilter();
|
|
|
211 |
|
212 |
this.updateButtonText();
|
213 |
this.updateSelectAll(true);
|
@@ -217,11 +229,17 @@
|
|
217 |
}
|
218 |
|
219 |
this.options.wasDisabled = this.$select.prop('disabled');
|
220 |
-
if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
|
221 |
-
this.disable();
|
222 |
}
|
223 |
|
224 |
this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container);
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
this.options.onInitialized(this.$select, this.$container);
|
226 |
}
|
227 |
|
@@ -237,35 +255,33 @@
|
|
237 |
* @param {jQuery} select
|
238 |
* @returns {String}
|
239 |
*/
|
240 |
-
buttonText: function(
|
241 |
-
if (this.disabledText.length > 0
|
242 |
-
&& (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) {
|
243 |
-
|
244 |
return this.disabledText;
|
245 |
}
|
246 |
-
else if (
|
247 |
return this.nonSelectedText;
|
248 |
}
|
249 |
else if (this.allSelectedText
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
|
254 |
if (this.selectAllNumber) {
|
255 |
-
return this.allSelectedText + ' (' +
|
256 |
}
|
257 |
else {
|
258 |
return this.allSelectedText;
|
259 |
}
|
260 |
}
|
261 |
-
else if (this.numberDisplayed != 0 &&
|
262 |
-
return
|
263 |
}
|
264 |
else {
|
265 |
var selected = '';
|
266 |
var delimiter = this.delimiterText;
|
267 |
|
268 |
-
|
269 |
var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
|
270 |
selected += label + delimiter;
|
271 |
});
|
@@ -280,7 +296,7 @@
|
|
280 |
* @param {jQuery} select
|
281 |
* @returns {@exp;selected@call;substr}
|
282 |
*/
|
283 |
-
buttonTitle: function(options, select) {
|
284 |
if (options.length === 0) {
|
285 |
return this.nonSelectedText;
|
286 |
}
|
@@ -295,7 +311,7 @@
|
|
295 |
return selected.substr(0, selected.length - this.delimiterText.length);
|
296 |
}
|
297 |
},
|
298 |
-
checkboxName: function(option) {
|
299 |
return false; // no checkbox name
|
300 |
},
|
301 |
/**
|
@@ -304,7 +320,7 @@
|
|
304 |
* @param {jQuery} element
|
305 |
* @returns {String}
|
306 |
*/
|
307 |
-
optionLabel: function(element){
|
308 |
return $(element).attr('label') || $(element).text();
|
309 |
},
|
310 |
/**
|
@@ -313,7 +329,7 @@
|
|
313 |
* @param {jQuery} element
|
314 |
* @returns {String}
|
315 |
*/
|
316 |
-
optionClass: function(element) {
|
317 |
return $(element).attr('class') || '';
|
318 |
},
|
319 |
/**
|
@@ -324,7 +340,7 @@
|
|
324 |
* @param {jQuery} option
|
325 |
* @param {Boolean} checked
|
326 |
*/
|
327 |
-
onChange
|
328 |
|
329 |
},
|
330 |
/**
|
@@ -332,7 +348,7 @@
|
|
332 |
*
|
333 |
* @param {jQuery} event
|
334 |
*/
|
335 |
-
onDropdownShow: function(event) {
|
336 |
|
337 |
},
|
338 |
/**
|
@@ -340,7 +356,7 @@
|
|
340 |
*
|
341 |
* @param {jQuery} event
|
342 |
*/
|
343 |
-
onDropdownHide: function(event) {
|
344 |
|
345 |
},
|
346 |
/**
|
@@ -348,7 +364,7 @@
|
|
348 |
*
|
349 |
* @param {jQuery} event
|
350 |
*/
|
351 |
-
onDropdownShown: function(event) {
|
352 |
|
353 |
},
|
354 |
/**
|
@@ -356,19 +372,19 @@
|
|
356 |
*
|
357 |
* @param {jQuery} event
|
358 |
*/
|
359 |
-
onDropdownHidden: function(event) {
|
360 |
|
361 |
},
|
362 |
/**
|
363 |
* Triggered on select all.
|
364 |
*/
|
365 |
-
onSelectAll: function() {
|
366 |
|
367 |
},
|
368 |
/**
|
369 |
* Triggered on deselect all.
|
370 |
*/
|
371 |
-
onDeselectAll: function() {
|
372 |
|
373 |
},
|
374 |
/**
|
@@ -377,7 +393,7 @@
|
|
377 |
* @param {jQuery} $select
|
378 |
* @param {jQuery} $container
|
379 |
*/
|
380 |
-
onInitialized: function($select, $container) {
|
381 |
|
382 |
},
|
383 |
/**
|
@@ -385,11 +401,11 @@
|
|
385 |
*
|
386 |
* @param {jQuery} $filter
|
387 |
*/
|
388 |
-
onFiltering: function($filter) {
|
389 |
|
390 |
},
|
391 |
enableHTML: false,
|
392 |
-
buttonClass: '
|
393 |
inheritClass: false,
|
394 |
buttonWidth: 'auto',
|
395 |
buttonContainer: '<div class="btn-group" />',
|
@@ -420,6 +436,7 @@
|
|
420 |
nonSelectedText: 'None selected',
|
421 |
nSelectedText: 'selected',
|
422 |
allSelectedText: 'All selected',
|
|
|
423 |
numberDisplayed: 3,
|
424 |
disableIfEmpty: false,
|
425 |
disabledText: '',
|
@@ -427,15 +444,21 @@
|
|
427 |
includeResetOption: false,
|
428 |
includeResetDivider: false,
|
429 |
resetText: 'Reset',
|
|
|
|
|
|
|
|
|
|
|
430 |
templates: {
|
431 |
-
button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span
|
432 |
-
|
433 |
-
filter: '<
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
|
|
439 |
}
|
440 |
},
|
441 |
|
@@ -444,9 +467,18 @@
|
|
444 |
/**
|
445 |
* Builds the container of the multiselect.
|
446 |
*/
|
447 |
-
buildContainer: function() {
|
448 |
this.$container = $(this.options.buttonContainer);
|
449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
|
451 |
this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
|
452 |
this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
|
@@ -455,7 +487,7 @@
|
|
455 |
/**
|
456 |
* Builds the button of the multiselect.
|
457 |
*/
|
458 |
-
buildButton: function() {
|
459 |
this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
|
460 |
if (this.$select.attr('class') && this.options.inheritClass) {
|
461 |
this.$button.addClass(this.$select.attr('class'));
|
@@ -471,15 +503,27 @@
|
|
471 |
// Manually add button width if set.
|
472 |
if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
|
473 |
this.$button.css({
|
474 |
-
'width'
|
475 |
-
'overflow' : 'hidden',
|
476 |
-
'text-overflow' : 'ellipsis'
|
477 |
});
|
478 |
this.$container.css({
|
479 |
'width': this.options.buttonWidth
|
480 |
});
|
481 |
}
|
482 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
// Keep the tab index from the select.
|
484 |
var tabindex = this.$select.attr('tabindex');
|
485 |
if (tabindex) {
|
@@ -490,41 +534,59 @@
|
|
490 |
},
|
491 |
|
492 |
/**
|
493 |
-
* Builds the
|
494 |
*/
|
495 |
-
buildDropdown: function() {
|
496 |
|
497 |
-
// Build
|
498 |
-
this.$
|
499 |
|
500 |
if (this.options.dropRight) {
|
501 |
-
this.$
|
|
|
|
|
|
|
502 |
}
|
503 |
|
504 |
// Set max height of dropdown menu to activate auto scrollbar.
|
505 |
if (this.options.maxHeight) {
|
506 |
// TODO: Add a class for this option to move the css declarations.
|
507 |
-
this.$
|
508 |
'max-height': this.options.maxHeight + 'px',
|
509 |
'overflow-y': 'auto',
|
510 |
'overflow-x': 'hidden'
|
511 |
});
|
512 |
}
|
513 |
|
514 |
-
if (this.options.
|
|
|
|
|
515 |
|
516 |
-
|
517 |
-
|
|
|
518 |
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
}
|
526 |
|
527 |
-
this.$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
528 |
},
|
529 |
|
530 |
/**
|
@@ -532,9 +594,9 @@
|
|
532 |
*
|
533 |
* Uses createDivider and createOptionValue to create the necessary options.
|
534 |
*/
|
535 |
-
buildDropdownOptions: function() {
|
536 |
|
537 |
-
this.$select.children().each($.proxy(function(index, element) {
|
538 |
|
539 |
var $element = $(element);
|
540 |
// Support optgroups and options without a group simultaneously.
|
@@ -554,7 +616,7 @@
|
|
554 |
this.createDivider();
|
555 |
}
|
556 |
else {
|
557 |
-
this.createOptionValue(element);
|
558 |
}
|
559 |
|
560 |
}
|
@@ -563,8 +625,8 @@
|
|
563 |
}, this));
|
564 |
|
565 |
// Bind the change event on the dropdown elements.
|
566 |
-
$(this.$
|
567 |
-
$(this.$
|
568 |
var $target = $(event.target);
|
569 |
|
570 |
var checked = $target.prop('checked') || false;
|
@@ -573,11 +635,11 @@
|
|
573 |
// Apply or unapply the configured selected class.
|
574 |
if (this.options.selectedClass) {
|
575 |
if (checked) {
|
576 |
-
$target.closest('
|
577 |
.addClass(this.options.selectedClass);
|
578 |
}
|
579 |
else {
|
580 |
-
$target.closest('
|
581 |
.removeClass(this.options.selectedClass);
|
582 |
}
|
583 |
}
|
@@ -609,7 +671,7 @@
|
|
609 |
else {
|
610 |
// Unselect all other options and corresponding checkboxes.
|
611 |
if (this.options.selectedClass) {
|
612 |
-
$($checkboxesNotThis).closest('
|
613 |
}
|
614 |
|
615 |
$($checkboxesNotThis).prop('checked', false);
|
@@ -620,7 +682,7 @@
|
|
620 |
}
|
621 |
|
622 |
if (this.options.selectedClass === "active") {
|
623 |
-
$optionsNotThis.closest("
|
624 |
}
|
625 |
}
|
626 |
else {
|
@@ -642,34 +704,36 @@
|
|
642 |
this.$select.change();
|
643 |
this.updateButtonText();
|
644 |
|
645 |
-
if(this.options.preventInputChangeEvent) {
|
646 |
return false;
|
647 |
}
|
648 |
}, this));
|
649 |
|
650 |
-
$('
|
|
|
651 |
if (e.shiftKey) {
|
652 |
// Prevent selecting text by Shift+click
|
653 |
return false;
|
654 |
}
|
655 |
});
|
656 |
|
657 |
-
$(this.$
|
|
|
658 |
event.stopPropagation();
|
659 |
|
660 |
var $target = $(event.target);
|
661 |
|
662 |
if (event.shiftKey && this.options.multiple) {
|
663 |
-
if(
|
664 |
event.preventDefault();
|
665 |
-
$target = $target.find("input");
|
666 |
$target.prop("checked", !$target.prop("checked"));
|
667 |
}
|
668 |
var checked = $target.prop('checked') || false;
|
669 |
|
670 |
if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range
|
671 |
-
var from = this.$
|
672 |
-
var to = this.$
|
673 |
|
674 |
if (from > to) { // Swap the indices
|
675 |
var tmp = to;
|
@@ -681,12 +745,12 @@
|
|
681 |
++to;
|
682 |
|
683 |
// Change the checkboxes and underlying options
|
684 |
-
var range = this.$
|
685 |
|
686 |
range.prop('checked', checked);
|
687 |
|
688 |
if (this.options.selectedClass) {
|
689 |
-
range.closest('
|
690 |
.toggleClass(this.options.selectedClass, checked);
|
691 |
}
|
692 |
|
@@ -702,100 +766,124 @@
|
|
702 |
// Trigger the select "change" event
|
703 |
$target.trigger("change");
|
704 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
705 |
|
706 |
// Remembers last clicked option
|
707 |
-
|
|
|
708 |
this.lastToggledInput = $target;
|
709 |
}
|
|
|
|
|
|
|
710 |
|
711 |
-
$target.
|
712 |
}, this));
|
713 |
|
714 |
-
//
|
715 |
-
this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
|
720 |
-
|
|
|
721 |
this.$button.click();
|
722 |
}
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
}
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
|
|
735 |
}
|
736 |
-
|
737 |
-
|
738 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
739 |
}
|
740 |
-
else if (
|
741 |
-
|
|
|
|
|
742 |
}
|
743 |
-
|
744 |
-
var $current = $items.eq(index);
|
745 |
-
$current.trigger( 'focus' );
|
746 |
-
|
747 |
-
if (event.keyCode === 32 || event.keyCode === 13) {
|
748 |
-
var $checkbox = $current.find('input');
|
749 |
-
|
750 |
-
$checkbox.prop("checked", !$checkbox.prop("checked"));
|
751 |
-
$checkbox.change();
|
752 |
-
}
|
753 |
-
|
754 |
-
event.stopPropagation();
|
755 |
-
event.preventDefault();
|
756 |
}
|
757 |
}, this));
|
758 |
|
759 |
if (this.options.enableClickableOptGroups && this.options.multiple) {
|
760 |
-
$("
|
|
|
761 |
event.stopPropagation();
|
762 |
|
763 |
var $target = $(event.target);
|
764 |
var checked = $target.prop('checked') || false;
|
765 |
|
766 |
-
var $
|
767 |
-
var $group = $
|
768 |
.not('.multiselect-filter-hidden')
|
769 |
.not('.disabled');
|
770 |
|
771 |
var $inputs = $group.find("input");
|
772 |
|
773 |
-
var values = [];
|
774 |
var $options = [];
|
775 |
|
776 |
if (this.options.selectedClass) {
|
777 |
if (checked) {
|
778 |
-
$
|
779 |
}
|
780 |
else {
|
781 |
-
$
|
782 |
}
|
783 |
}
|
784 |
|
785 |
-
$.each($inputs, $.proxy(function(index, input) {
|
786 |
-
var
|
|
|
787 |
var $option = this.getOptionByValue(value);
|
788 |
|
789 |
if (checked) {
|
790 |
-
$
|
791 |
-
$
|
792 |
.addClass(this.options.selectedClass);
|
793 |
|
794 |
$option.prop('selected', true);
|
795 |
}
|
796 |
else {
|
797 |
-
$
|
798 |
-
$
|
799 |
.removeClass(this.options.selectedClass);
|
800 |
|
801 |
$option.prop('selected', false);
|
@@ -815,13 +903,14 @@
|
|
815 |
}
|
816 |
|
817 |
if (this.options.enableCollapsibleOptGroups && this.options.multiple) {
|
818 |
-
$("
|
819 |
-
|
820 |
-
var $
|
821 |
-
|
|
|
822 |
|
823 |
var visible = true;
|
824 |
-
$inputs.each(function() {
|
825 |
visible = visible && !$(this).hasClass('multiselect-collapsible-hidden');
|
826 |
});
|
827 |
|
@@ -834,11 +923,45 @@
|
|
834 |
.removeClass('multiselect-collapsible-hidden');
|
835 |
}
|
836 |
}, this));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
837 |
|
838 |
-
|
839 |
-
|
840 |
-
|
|
|
|
|
|
|
841 |
}
|
|
|
|
|
|
|
|
|
|
|
842 |
},
|
843 |
|
844 |
/**
|
@@ -846,7 +969,7 @@
|
|
846 |
*
|
847 |
* @param {jQuery} element
|
848 |
*/
|
849 |
-
createOptionValue: function(element) {
|
850 |
var $element = $(element);
|
851 |
if ($element.is(':selected')) {
|
852 |
$element.prop('selected', true);
|
@@ -857,61 +980,46 @@
|
|
857 |
var classes = this.options.optionClass(element);
|
858 |
var value = $element.val();
|
859 |
var inputType = this.options.multiple ? "checkbox" : "radio";
|
|
|
860 |
|
861 |
-
var $
|
862 |
-
|
863 |
-
$label.addClass(inputType);
|
864 |
-
$label.attr("title", label);
|
865 |
-
$li.addClass(classes);
|
866 |
|
867 |
-
|
868 |
-
|
869 |
-
$li.addClass("multiselect-collapsible-hidden");
|
870 |
-
$li.hide();
|
871 |
}
|
872 |
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
$label.text(" " + label);
|
878 |
}
|
879 |
|
880 |
-
var $checkbox = $('<input/>').attr('type', inputType);
|
881 |
-
|
882 |
var name = this.options.checkboxName($element);
|
883 |
-
|
884 |
-
$checkbox.attr('name', name);
|
885 |
-
}
|
886 |
-
|
887 |
-
$label.prepend($checkbox);
|
888 |
|
889 |
var selected = $element.prop('selected') || false;
|
890 |
-
$checkbox.val(value);
|
891 |
|
892 |
if (value === this.options.selectAllValue) {
|
893 |
-
$
|
|
|
894 |
$checkbox.parent().parent()
|
895 |
.addClass('multiselect-all');
|
896 |
}
|
897 |
|
898 |
-
|
899 |
-
|
900 |
-
this.$ul.append($li);
|
901 |
|
902 |
if ($element.is(':disabled')) {
|
903 |
$checkbox.attr('disabled', 'disabled')
|
904 |
.prop('disabled', true)
|
905 |
-
.closest('
|
906 |
-
.attr("tabindex", "-1")
|
907 |
-
.closest('li')
|
908 |
.addClass('disabled');
|
909 |
}
|
910 |
|
911 |
$checkbox.prop('checked', selected);
|
912 |
|
913 |
if (selected && this.options.selectedClass) {
|
914 |
-
$checkbox.closest('
|
915 |
.addClass(this.options.selectedClass);
|
916 |
}
|
917 |
},
|
@@ -921,9 +1029,9 @@
|
|
921 |
*
|
922 |
* @param {jQuery} element
|
923 |
*/
|
924 |
-
createDivider: function(element) {
|
925 |
var $divider = $(this.options.templates.divider);
|
926 |
-
this.$
|
927 |
},
|
928 |
|
929 |
/**
|
@@ -931,66 +1039,74 @@
|
|
931 |
*
|
932 |
* @param {jQuery} group
|
933 |
*/
|
934 |
-
createOptgroup: function(group) {
|
935 |
-
var
|
936 |
-
var
|
937 |
-
var
|
|
|
938 |
|
939 |
-
var
|
940 |
-
$li.addClass(classes);
|
941 |
|
942 |
-
if (this.options.
|
943 |
-
$
|
|
|
944 |
}
|
945 |
else {
|
946 |
-
|
|
|
|
|
|
|
|
|
|
|
947 |
}
|
948 |
|
949 |
-
|
950 |
-
|
951 |
-
}
|
952 |
|
953 |
-
if (this.options.
|
954 |
-
$('
|
|
|
955 |
}
|
956 |
|
957 |
-
if ($
|
958 |
-
$
|
959 |
}
|
960 |
|
961 |
-
this.$
|
962 |
|
963 |
-
$("option", group).each($.proxy(function($, group) {
|
964 |
-
this.createOptionValue(group);
|
965 |
-
}, this))
|
966 |
},
|
967 |
|
968 |
/**
|
969 |
* Build the reset.
|
970 |
*
|
971 |
*/
|
972 |
-
buildReset: function() {
|
973 |
if (this.options.includeResetOption) {
|
974 |
|
975 |
// Check whether to add a divider after the reset.
|
976 |
if (this.options.includeResetDivider) {
|
977 |
-
|
|
|
|
|
978 |
}
|
979 |
|
980 |
var $resetButton = $(this.options.templates.resetButton);
|
981 |
|
982 |
if (this.options.enableHTML) {
|
983 |
-
$('
|
984 |
}
|
985 |
else {
|
986 |
-
$('
|
987 |
}
|
988 |
|
989 |
-
$('
|
990 |
this.clearSelection();
|
991 |
}, this));
|
992 |
|
993 |
-
this.$
|
994 |
}
|
995 |
},
|
996 |
|
@@ -999,7 +1115,7 @@
|
|
999 |
*
|
1000 |
* Checks if a select all has already been created.
|
1001 |
*/
|
1002 |
-
buildSelectAll: function() {
|
1003 |
if (typeof this.options.selectAllValue === 'number') {
|
1004 |
this.options.selectAllValue = this.options.selectAllValue.toString();
|
1005 |
}
|
@@ -1007,38 +1123,21 @@
|
|
1007 |
var alreadyHasSelectAll = this.hasSelectAll();
|
1008 |
|
1009 |
if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
|
1010 |
-
|
1011 |
|
1012 |
// Check whether to add a divider after the select all.
|
1013 |
if (this.options.includeSelectAllDivider) {
|
1014 |
-
this.$
|
1015 |
}
|
1016 |
|
1017 |
-
var $
|
1018 |
-
$(
|
1019 |
-
|
1020 |
-
if (this.options.enableHTML) {
|
1021 |
-
$('label', $li).html(" " + this.options.selectAllText);
|
1022 |
-
}
|
1023 |
-
else {
|
1024 |
-
$('label', $li).text(" " + this.options.selectAllText);
|
1025 |
-
}
|
1026 |
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
else {
|
1031 |
-
$('label', $li).prepend('<input type="checkbox" />');
|
1032 |
-
}
|
1033 |
|
1034 |
-
|
1035 |
-
$checkbox.val(this.options.selectAllValue);
|
1036 |
-
|
1037 |
-
$li.addClass("multiselect-item multiselect-all");
|
1038 |
-
$checkbox.parent().parent()
|
1039 |
-
.addClass('multiselect-all');
|
1040 |
-
|
1041 |
-
this.$ul.prepend($li);
|
1042 |
|
1043 |
$checkbox.prop('checked', false);
|
1044 |
}
|
@@ -1047,7 +1146,7 @@
|
|
1047 |
/**
|
1048 |
* Builds the filter.
|
1049 |
*/
|
1050 |
-
buildFilter: function() {
|
1051 |
|
1052 |
// Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
|
1053 |
if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
|
@@ -1058,15 +1157,25 @@
|
|
1058 |
this.$filter = $(this.options.templates.filter);
|
1059 |
$('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
|
1060 |
|
1061 |
-
//
|
1062 |
-
if(this.options.includeFilterClearBtn) {
|
1063 |
-
|
1064 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1065 |
clearTimeout(this.searchTimeout);
|
1066 |
|
1067 |
this.query = '';
|
1068 |
this.$filter.find('.multiselect-search').val('');
|
1069 |
-
$('
|
1070 |
|
1071 |
this.updateSelectAll();
|
1072 |
|
@@ -1075,31 +1184,39 @@
|
|
1075 |
}
|
1076 |
|
1077 |
}, this));
|
1078 |
-
this.$filter.find('.input-group').append(clearBtn);
|
1079 |
}
|
1080 |
|
1081 |
-
this.$
|
1082 |
|
1083 |
-
this.$filter.val(this.query).on('click', function(event) {
|
1084 |
event.stopPropagation();
|
1085 |
-
}).on('input keydown', $.proxy(function(event) {
|
1086 |
// Cancel enter key default behaviour
|
1087 |
if (event.which === 13) {
|
1088 |
-
|
1089 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1090 |
|
1091 |
// This is useful to catch "keydown" events after the browser has updated the control.
|
1092 |
clearTimeout(this.searchTimeout);
|
1093 |
|
1094 |
-
this.searchTimeout = this.asyncFunction($.proxy(function() {
|
1095 |
|
1096 |
if (this.query !== event.target.value) {
|
1097 |
this.query = event.target.value;
|
1098 |
|
1099 |
var currentGroup, currentGroupVisible;
|
1100 |
-
$.each($('
|
1101 |
var value = $('input', element).length > 0 ? $('input', element).val() : "";
|
1102 |
-
var text = $('label', element).text();
|
1103 |
|
1104 |
var filterCandidate = '';
|
1105 |
if ((this.options.filterBehavior === 'text')) {
|
@@ -1134,13 +1251,13 @@
|
|
1134 |
}
|
1135 |
|
1136 |
// Toggle current element (group or group item) according to showElement boolean.
|
1137 |
-
if(!showElement){
|
1138 |
-
|
1139 |
-
|
1140 |
}
|
1141 |
-
if(showElement){
|
1142 |
-
|
1143 |
-
|
1144 |
}
|
1145 |
|
1146 |
// Differentiate groups and group items.
|
@@ -1172,6 +1289,8 @@
|
|
1172 |
this.updateOptGroups();
|
1173 |
}
|
1174 |
|
|
|
|
|
1175 |
this.options.onFiltering(event.target);
|
1176 |
|
1177 |
}, this), 300, this);
|
@@ -1180,11 +1299,67 @@
|
|
1180 |
}
|
1181 |
},
|
1182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1183 |
/**
|
1184 |
* Unbinds the whole plugin.
|
1185 |
*/
|
1186 |
-
destroy: function() {
|
1187 |
this.$container.remove();
|
|
|
1188 |
this.$select.show();
|
1189 |
|
1190 |
// reset original state
|
@@ -1198,8 +1373,8 @@
|
|
1198 |
*/
|
1199 |
refresh: function () {
|
1200 |
var inputs = {};
|
1201 |
-
$('
|
1202 |
-
|
1203 |
});
|
1204 |
|
1205 |
$('option', this.$select).each($.proxy(function (index, element) {
|
@@ -1210,7 +1385,7 @@
|
|
1210 |
$input.prop('checked', true);
|
1211 |
|
1212 |
if (this.options.selectedClass) {
|
1213 |
-
$input.closest('
|
1214 |
.addClass(this.options.selectedClass);
|
1215 |
}
|
1216 |
}
|
@@ -1218,7 +1393,7 @@
|
|
1218 |
$input.prop('checked', false);
|
1219 |
|
1220 |
if (this.options.selectedClass) {
|
1221 |
-
$input.closest('
|
1222 |
.removeClass(this.options.selectedClass);
|
1223 |
}
|
1224 |
}
|
@@ -1226,12 +1401,12 @@
|
|
1226 |
if ($elem.is(":disabled")) {
|
1227 |
$input.attr('disabled', 'disabled')
|
1228 |
.prop('disabled', true)
|
1229 |
-
.closest('
|
1230 |
.addClass('disabled');
|
1231 |
}
|
1232 |
else {
|
1233 |
$input.prop('disabled', false)
|
1234 |
-
.closest('
|
1235 |
.removeClass('disabled');
|
1236 |
}
|
1237 |
}, this));
|
@@ -1253,8 +1428,8 @@
|
|
1253 |
* @param {Array} selectValues
|
1254 |
* @param {Boolean} triggerOnChange
|
1255 |
*/
|
1256 |
-
select: function(selectValues, triggerOnChange) {
|
1257 |
-
if(!$.isArray(selectValues)) {
|
1258 |
selectValues = [selectValues];
|
1259 |
}
|
1260 |
|
@@ -1268,22 +1443,27 @@
|
|
1268 |
var $option = this.getOptionByValue(value);
|
1269 |
var $checkbox = this.getInputByValue(value);
|
1270 |
|
1271 |
-
if($option === undefined || $checkbox === undefined) {
|
1272 |
continue;
|
1273 |
}
|
1274 |
|
1275 |
-
if (!this.options.multiple) {
|
1276 |
-
this.deselectAll(false);
|
1277 |
-
}
|
1278 |
-
|
1279 |
if (this.options.selectedClass) {
|
1280 |
-
$checkbox.closest('
|
1281 |
.addClass(this.options.selectedClass);
|
1282 |
}
|
1283 |
|
1284 |
$checkbox.prop('checked', true);
|
1285 |
$option.prop('selected', true);
|
1286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1287 |
if (triggerOnChange) {
|
1288 |
this.options.onChange($option, true);
|
1289 |
}
|
@@ -1319,8 +1499,13 @@
|
|
1319 |
* @param {Array} deselectValues
|
1320 |
* @param {Boolean} triggerOnChange
|
1321 |
*/
|
1322 |
-
deselect: function(deselectValues, triggerOnChange) {
|
1323 |
-
if(
|
|
|
|
|
|
|
|
|
|
|
1324 |
deselectValues = [deselectValues];
|
1325 |
}
|
1326 |
|
@@ -1334,12 +1519,12 @@
|
|
1334 |
var $option = this.getOptionByValue(value);
|
1335 |
var $checkbox = this.getInputByValue(value);
|
1336 |
|
1337 |
-
if($option === undefined || $checkbox === undefined) {
|
1338 |
continue;
|
1339 |
}
|
1340 |
|
1341 |
if (this.options.selectedClass) {
|
1342 |
-
$checkbox.closest('
|
1343 |
.removeClass(this.options.selectedClass);
|
1344 |
}
|
1345 |
|
@@ -1368,40 +1553,55 @@
|
|
1368 |
* @param {Boolean} triggerOnSelectAll
|
1369 |
*/
|
1370 |
selectAll: function (justVisible, triggerOnSelectAll) {
|
|
|
|
|
|
|
|
|
1371 |
|
|
|
|
|
1372 |
var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
|
1373 |
-
var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul);
|
1374 |
-
var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible');
|
1375 |
|
1376 |
-
if(justVisible) {
|
1377 |
-
$(
|
1378 |
-
|
|
|
1379 |
|
1380 |
-
$('input:enabled'
|
1381 |
var value = $(element).val();
|
1382 |
var option = this.getOptionByValue(value);
|
|
|
|
|
|
|
1383 |
$(option).prop('selected', true);
|
1384 |
}, this));
|
1385 |
}
|
1386 |
else {
|
1387 |
-
$(
|
1388 |
-
|
|
|
1389 |
|
1390 |
-
$('input:enabled'
|
1391 |
var value = $(element).val();
|
1392 |
var option = this.getOptionByValue(value);
|
|
|
|
|
|
|
1393 |
$(option).prop('selected', true);
|
1394 |
}, this));
|
1395 |
}
|
1396 |
|
1397 |
-
$('
|
1398 |
|
1399 |
if (this.options.enableClickableOptGroups && this.options.multiple) {
|
1400 |
this.updateOptGroups();
|
1401 |
}
|
1402 |
|
|
|
|
|
|
|
1403 |
if (triggerOnSelectAll) {
|
1404 |
-
this.options.onSelectAll();
|
1405 |
}
|
1406 |
},
|
1407 |
|
@@ -1413,40 +1613,55 @@
|
|
1413 |
* @param {Boolean} justVisible
|
1414 |
*/
|
1415 |
deselectAll: function (justVisible, triggerOnDeselectAll) {
|
|
|
|
|
|
|
|
|
1416 |
|
|
|
|
|
1417 |
var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
|
1418 |
-
var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul);
|
1419 |
-
var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible');
|
1420 |
|
1421 |
-
if(justVisible) {
|
1422 |
-
$(
|
1423 |
-
|
|
|
1424 |
|
1425 |
-
$('input[type="checkbox"]:enabled'
|
1426 |
var value = $(element).val();
|
1427 |
var option = this.getOptionByValue(value);
|
|
|
|
|
|
|
1428 |
$(option).prop('selected', false);
|
1429 |
}, this));
|
1430 |
}
|
1431 |
else {
|
1432 |
-
$(
|
1433 |
-
|
|
|
1434 |
|
1435 |
-
$('input[type="checkbox"]:enabled'
|
1436 |
var value = $(element).val();
|
1437 |
var option = this.getOptionByValue(value);
|
|
|
|
|
|
|
1438 |
$(option).prop('selected', false);
|
1439 |
}, this));
|
1440 |
}
|
1441 |
|
1442 |
-
$('
|
1443 |
|
1444 |
if (this.options.enableClickableOptGroups && this.options.multiple) {
|
1445 |
this.updateOptGroups();
|
1446 |
}
|
1447 |
|
|
|
|
|
|
|
1448 |
if (triggerOnDeselectAll) {
|
1449 |
-
this.options.onDeselectAll();
|
1450 |
}
|
1451 |
},
|
1452 |
|
@@ -1455,8 +1670,8 @@
|
|
1455 |
*
|
1456 |
* Rebuilds the dropdown, the filter and the select all option.
|
1457 |
*/
|
1458 |
-
rebuild: function() {
|
1459 |
-
this.$
|
1460 |
|
1461 |
// Important to distinguish between radios and checkboxes.
|
1462 |
this.options.multiple = this.$select.attr('multiple') === "multiple";
|
@@ -1464,6 +1679,7 @@
|
|
1464 |
this.buildSelectAll();
|
1465 |
this.buildDropdownOptions();
|
1466 |
this.buildFilter();
|
|
|
1467 |
|
1468 |
this.updateButtonText();
|
1469 |
this.updateSelectAll(true);
|
@@ -1472,22 +1688,33 @@
|
|
1472 |
this.updateOptGroups();
|
1473 |
}
|
1474 |
|
1475 |
-
if (this.options.disableIfEmpty
|
1476 |
-
this.
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
|
|
|
|
|
|
|
|
1480 |
}
|
1481 |
|
1482 |
if (this.options.dropRight) {
|
1483 |
-
this.$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1484 |
}
|
1485 |
},
|
1486 |
|
1487 |
/**
|
1488 |
* The provided data will be used to build the dropdown.
|
1489 |
*/
|
1490 |
-
dataprovider: function(dataprovider) {
|
1491 |
|
1492 |
var groupCounter = 0;
|
1493 |
var $select = this.$select.empty();
|
@@ -1504,7 +1731,7 @@
|
|
1504 |
value: option.value
|
1505 |
});
|
1506 |
|
1507 |
-
forEach(option.children, function(subOption) { // add children option tags
|
1508 |
var attributes = {
|
1509 |
value: subOption.value,
|
1510 |
label: subOption.label || subOption.value,
|
@@ -1514,10 +1741,10 @@
|
|
1514 |
};
|
1515 |
|
1516 |
//Loop through attributes object and add key-value for each attribute
|
1517 |
-
|
1518 |
attributes['data-' + key] = subOption.attributes[key];
|
1519 |
-
|
1520 |
-
|
1521 |
$tag.append($('<option/>').attr(attributes));
|
1522 |
});
|
1523 |
}
|
@@ -1533,7 +1760,7 @@
|
|
1533 |
};
|
1534 |
//Loop through attributes object and add key-value for each attribute
|
1535 |
for (var key in option.attributes) {
|
1536 |
-
|
1537 |
}
|
1538 |
//Append original attributes + new data attributes to option
|
1539 |
$tag = $('<option/>').attr(attributes);
|
@@ -1550,19 +1777,30 @@
|
|
1550 |
/**
|
1551 |
* Enable the multiselect.
|
1552 |
*/
|
1553 |
-
enable: function() {
|
1554 |
this.$select.prop('disabled', false);
|
1555 |
this.$button.prop('disabled', false)
|
1556 |
.removeClass('disabled');
|
|
|
|
|
1557 |
},
|
1558 |
|
1559 |
/**
|
1560 |
* Disable the multiselect.
|
1561 |
*/
|
1562 |
-
disable: function() {
|
1563 |
this.$select.prop('disabled', true);
|
1564 |
this.$button.prop('disabled', true)
|
1565 |
.addClass('disabled');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1566 |
},
|
1567 |
|
1568 |
/**
|
@@ -1570,7 +1808,7 @@
|
|
1570 |
*
|
1571 |
* @param {Array} options
|
1572 |
*/
|
1573 |
-
setOptions: function(options) {
|
1574 |
this.options = this.mergeOptions(options);
|
1575 |
},
|
1576 |
|
@@ -1580,7 +1818,7 @@
|
|
1580 |
* @param {Array} options
|
1581 |
* @returns {Array}
|
1582 |
*/
|
1583 |
-
mergeOptions: function(options) {
|
1584 |
return $.extend(true, {}, this.defaults, this.options, options);
|
1585 |
},
|
1586 |
|
@@ -1589,24 +1827,24 @@
|
|
1589 |
*
|
1590 |
* @returns {Boolean}
|
1591 |
*/
|
1592 |
-
hasSelectAll: function() {
|
1593 |
-
return $('
|
1594 |
},
|
1595 |
|
1596 |
/**
|
1597 |
* Update opt groups.
|
1598 |
*/
|
1599 |
-
updateOptGroups: function() {
|
1600 |
-
var $groups = $('
|
1601 |
var selectedClass = this.options.selectedClass;
|
1602 |
|
1603 |
-
$groups.each(function() {
|
1604 |
-
var $options = $(this).nextUntil('
|
1605 |
.not('.multiselect-filter-hidden')
|
1606 |
.not('.disabled');
|
1607 |
|
1608 |
var checked = true;
|
1609 |
-
$options.each(function() {
|
1610 |
var $input = $('input', this);
|
1611 |
|
1612 |
if (!$input.prop('checked')) {
|
@@ -1630,21 +1868,21 @@
|
|
1630 |
/**
|
1631 |
* Updates the select all checkbox based on the currently displayed and selected checkboxes.
|
1632 |
*/
|
1633 |
-
updateSelectAll: function(notTriggerOnSelectAll) {
|
1634 |
if (this.hasSelectAll()) {
|
1635 |
-
var allBoxes = $("
|
1636 |
var allBoxesLength = allBoxes.length;
|
1637 |
var checkedBoxesLength = allBoxes.filter(":checked").length;
|
1638 |
-
var
|
1639 |
-
var selectAllInput =
|
1640 |
|
1641 |
if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
|
1642 |
selectAllInput.prop("checked", true);
|
1643 |
-
|
1644 |
}
|
1645 |
else {
|
1646 |
selectAllInput.prop("checked", false);
|
1647 |
-
|
1648 |
}
|
1649 |
}
|
1650 |
},
|
@@ -1652,7 +1890,7 @@
|
|
1652 |
/**
|
1653 |
* Update the button text and its title based on the currently selected options.
|
1654 |
*/
|
1655 |
-
updateButtonText: function() {
|
1656 |
var options = this.getSelected();
|
1657 |
|
1658 |
// First update the displayed button text.
|
@@ -1665,6 +1903,7 @@
|
|
1665 |
|
1666 |
// Now update the title attribute of the button.
|
1667 |
$('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
|
|
|
1668 |
},
|
1669 |
|
1670 |
/**
|
@@ -1672,7 +1911,7 @@
|
|
1672 |
*
|
1673 |
* @returns {jQUery}
|
1674 |
*/
|
1675 |
-
getSelected: function() {
|
1676 |
return $('option', this.$select).filter(":selected");
|
1677 |
},
|
1678 |
|
@@ -1703,7 +1942,7 @@
|
|
1703 |
*/
|
1704 |
getInputByValue: function (value) {
|
1705 |
|
1706 |
-
var checkboxes = $('
|
1707 |
var valueToCompare = value.toString();
|
1708 |
|
1709 |
for (var i = 0; i < checkboxes.length; i = i + 1) {
|
@@ -1717,25 +1956,36 @@
|
|
1717 |
/**
|
1718 |
* Used for knockout integration.
|
1719 |
*/
|
1720 |
-
updateOriginalOptions: function() {
|
1721 |
this.originalOptions = this.$select.clone()[0].options;
|
1722 |
},
|
1723 |
|
1724 |
-
asyncFunction: function(callback, timeout, self) {
|
1725 |
var args = Array.prototype.slice.call(arguments, 3);
|
1726 |
-
return setTimeout(function() {
|
1727 |
callback.apply(self || window, args);
|
1728 |
}, timeout);
|
1729 |
},
|
1730 |
|
1731 |
-
setAllSelectedText: function(allSelectedText) {
|
1732 |
this.options.allSelectedText = allSelectedText;
|
1733 |
this.updateButtonText();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1734 |
}
|
1735 |
};
|
1736 |
|
1737 |
-
$.fn.multiselect = function(option, parameter, extraOptions) {
|
1738 |
-
return this.each(function() {
|
1739 |
var data = $(this).data('multiselect');
|
1740 |
var options = typeof option === 'object' && option;
|
1741 |
|
@@ -1758,8 +2008,8 @@
|
|
1758 |
|
1759 |
$.fn.multiselect.Constructor = Multiselect;
|
1760 |
|
1761 |
-
$(function() {
|
1762 |
$("select[data-role=multiselect]").multiselect();
|
1763 |
});
|
1764 |
|
1765 |
-
}
|
2 |
* Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/)
|
3 |
*
|
4 |
* Apache License, Version 2.0:
|
5 |
+
* Copyright (c) 2012 - 2021 David Stutz
|
6 |
*
|
7 |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8 |
* use this file except in compliance with the License. You may obtain a
|
15 |
* under the License.
|
16 |
*
|
17 |
* BSD 3-Clause License:
|
18 |
+
* Copyright (c) 2012 - 2021 David Stutz
|
19 |
* All rights reserved.
|
20 |
*
|
21 |
* Redistribution and use in source and binary forms, with or without
|
41 |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
42 |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
43 |
*/
|
44 |
+
(function (root, factory) {
|
45 |
+
// check to see if 'knockout' AMD module is specified if using requirejs
|
46 |
+
if (typeof define === 'function' && define.amd &&
|
47 |
+
typeof require === 'function' && typeof require.specified === 'function' && require.specified('knockout')) {
|
48 |
+
|
49 |
+
// AMD. Register as an anonymous module.
|
50 |
+
define(['jquery', 'knockout'], factory);
|
51 |
+
} else {
|
52 |
+
// Browser globals
|
53 |
+
factory(root.jQuery, root.ko);
|
54 |
+
}
|
55 |
+
})(this, function ($, ko) {
|
56 |
"use strict";// jshint ;_;
|
57 |
|
58 |
if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
|
59 |
ko.bindingHandlers.multiselect = {
|
60 |
after: ['options', 'value', 'selectedOptions', 'enable', 'disable'],
|
61 |
|
62 |
+
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
|
63 |
var $element = $(element);
|
64 |
var config = ko.toJS(valueAccessor());
|
65 |
|
69 |
var options = allBindings.get('options');
|
70 |
if (ko.isObservable(options)) {
|
71 |
ko.computed({
|
72 |
+
read: function () {
|
73 |
options();
|
74 |
+
setTimeout(function () {
|
75 |
var ms = $element.data('multiselect');
|
76 |
if (ms)
|
77 |
ms.updateOriginalOptions();//Not sure how beneficial this is.
|
90 |
var value = allBindings.get('value');
|
91 |
if (ko.isObservable(value)) {
|
92 |
ko.computed({
|
93 |
+
read: function () {
|
94 |
value();
|
95 |
+
setTimeout(function () {
|
96 |
$element.multiselect('refresh');
|
97 |
}, 1);
|
98 |
},
|
107 |
var selectedOptions = allBindings.get('selectedOptions');
|
108 |
if (ko.isObservable(selectedOptions)) {
|
109 |
ko.computed({
|
110 |
+
read: function () {
|
111 |
selectedOptions();
|
112 |
+
setTimeout(function () {
|
113 |
$element.multiselect('refresh');
|
114 |
}, 1);
|
115 |
},
|
155 |
}
|
156 |
}
|
157 |
|
158 |
+
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
|
159 |
$element.multiselect('destroy');
|
160 |
});
|
161 |
},
|
162 |
|
163 |
+
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
|
164 |
var $element = $(element);
|
165 |
var config = ko.toJS(valueAccessor());
|
166 |
|
219 |
this.buildSelectAll();
|
220 |
this.buildDropdownOptions();
|
221 |
this.buildFilter();
|
222 |
+
this.buildButtons();
|
223 |
|
224 |
this.updateButtonText();
|
225 |
this.updateSelectAll(true);
|
229 |
}
|
230 |
|
231 |
this.options.wasDisabled = this.$select.prop('disabled');
|
232 |
+
if (this.options.disableIfEmpty && $('option', this.$select).length <= 0 && !this.options.wasDisabled) {
|
233 |
+
this.disable(true);
|
234 |
}
|
235 |
|
236 |
this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container);
|
237 |
+
this.$select.prop('tabindex', '-1');
|
238 |
+
|
239 |
+
if (this.options.widthSynchronizationMode !== 'never') {
|
240 |
+
this.synchronizeButtonAndPopupWidth();
|
241 |
+
}
|
242 |
+
|
243 |
this.options.onInitialized(this.$select, this.$container);
|
244 |
}
|
245 |
|
255 |
* @param {jQuery} select
|
256 |
* @returns {String}
|
257 |
*/
|
258 |
+
buttonText: function (selectedOptions, select) {
|
259 |
+
if (this.disabledText.length > 0 && select.prop('disabled')) {
|
|
|
|
|
260 |
return this.disabledText;
|
261 |
}
|
262 |
+
else if (selectedOptions.length === 0) {
|
263 |
return this.nonSelectedText;
|
264 |
}
|
265 |
else if (this.allSelectedText
|
266 |
+
&& selectedOptions.length === $('option', $(select)).length
|
267 |
+
&& $('option', $(select)).length !== 1
|
268 |
+
&& this.multiple) {
|
269 |
|
270 |
if (this.selectAllNumber) {
|
271 |
+
return this.allSelectedText + ' (' + selectedOptions.length + ')';
|
272 |
}
|
273 |
else {
|
274 |
return this.allSelectedText;
|
275 |
}
|
276 |
}
|
277 |
+
else if (this.numberDisplayed != 0 && selectedOptions.length > this.numberDisplayed) {
|
278 |
+
return selectedOptions.length + ' ' + this.nSelectedText;
|
279 |
}
|
280 |
else {
|
281 |
var selected = '';
|
282 |
var delimiter = this.delimiterText;
|
283 |
|
284 |
+
selectedOptions.each(function () {
|
285 |
var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
|
286 |
selected += label + delimiter;
|
287 |
});
|
296 |
* @param {jQuery} select
|
297 |
* @returns {@exp;selected@call;substr}
|
298 |
*/
|
299 |
+
buttonTitle: function (options, select) {
|
300 |
if (options.length === 0) {
|
301 |
return this.nonSelectedText;
|
302 |
}
|
311 |
return selected.substr(0, selected.length - this.delimiterText.length);
|
312 |
}
|
313 |
},
|
314 |
+
checkboxName: function (option) {
|
315 |
return false; // no checkbox name
|
316 |
},
|
317 |
/**
|
320 |
* @param {jQuery} element
|
321 |
* @returns {String}
|
322 |
*/
|
323 |
+
optionLabel: function (element) {
|
324 |
return $(element).attr('label') || $(element).text();
|
325 |
},
|
326 |
/**
|
329 |
* @param {jQuery} element
|
330 |
* @returns {String}
|
331 |
*/
|
332 |
+
optionClass: function (element) {
|
333 |
return $(element).attr('class') || '';
|
334 |
},
|
335 |
/**
|
340 |
* @param {jQuery} option
|
341 |
* @param {Boolean} checked
|
342 |
*/
|
343 |
+
onChange: function (option, checked) {
|
344 |
|
345 |
},
|
346 |
/**
|
348 |
*
|
349 |
* @param {jQuery} event
|
350 |
*/
|
351 |
+
onDropdownShow: function (event) {
|
352 |
|
353 |
},
|
354 |
/**
|
356 |
*
|
357 |
* @param {jQuery} event
|
358 |
*/
|
359 |
+
onDropdownHide: function (event) {
|
360 |
|
361 |
},
|
362 |
/**
|
364 |
*
|
365 |
* @param {jQuery} event
|
366 |
*/
|
367 |
+
onDropdownShown: function (event) {
|
368 |
|
369 |
},
|
370 |
/**
|
372 |
*
|
373 |
* @param {jQuery} event
|
374 |
*/
|
375 |
+
onDropdownHidden: function (event) {
|
376 |
|
377 |
},
|
378 |
/**
|
379 |
* Triggered on select all.
|
380 |
*/
|
381 |
+
onSelectAll: function () {
|
382 |
|
383 |
},
|
384 |
/**
|
385 |
* Triggered on deselect all.
|
386 |
*/
|
387 |
+
onDeselectAll: function () {
|
388 |
|
389 |
},
|
390 |
/**
|
393 |
* @param {jQuery} $select
|
394 |
* @param {jQuery} $container
|
395 |
*/
|
396 |
+
onInitialized: function ($select, $container) {
|
397 |
|
398 |
},
|
399 |
/**
|
401 |
*
|
402 |
* @param {jQuery} $filter
|
403 |
*/
|
404 |
+
onFiltering: function ($filter) {
|
405 |
|
406 |
},
|
407 |
enableHTML: false,
|
408 |
+
buttonClass: 'custom-select',
|
409 |
inheritClass: false,
|
410 |
buttonWidth: 'auto',
|
411 |
buttonContainer: '<div class="btn-group" />',
|
436 |
nonSelectedText: 'None selected',
|
437 |
nSelectedText: 'selected',
|
438 |
allSelectedText: 'All selected',
|
439 |
+
resetButtonText: 'Reset',
|
440 |
numberDisplayed: 3,
|
441 |
disableIfEmpty: false,
|
442 |
disabledText: '',
|
444 |
includeResetOption: false,
|
445 |
includeResetDivider: false,
|
446 |
resetText: 'Reset',
|
447 |
+
indentGroupOptions: true,
|
448 |
+
// possible options: 'never', 'always', 'ifPopupIsSmaller', 'ifPopupIsWider'
|
449 |
+
widthSynchronizationMode: 'never',
|
450 |
+
buttonTextAlignment: 'center',
|
451 |
+
enableResetButton: false,
|
452 |
templates: {
|
453 |
+
button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span></button>',
|
454 |
+
popupContainer: '<div class="multiselect-container dropdown-menu"></div>',
|
455 |
+
filter: '<div class="multiselect-filter d-flex align-items-center"><i class="fas fa-sm fa-search text-muted"></i><input type="search" class="multiselect-search form-control" /></div>',
|
456 |
+
buttonGroup: '<div class="multiselect-buttons btn-group" style="display:flex;"></div>',
|
457 |
+
buttonGroupReset: '<button type="button" class="multiselect-reset btn btn-secondary btn-block"></button>',
|
458 |
+
option: '<button type="button" class="multiselect-option dropdown-item"></button>',
|
459 |
+
divider: '<div class="dropdown-divider"></div>',
|
460 |
+
optionGroup: '<button type="button" class="multiselect-group dropdown-item"></button>',
|
461 |
+
resetButton: '<div class="multiselect-reset text-center p-2"><button type="button" class="btn btn-sm btn-block btn-outline-secondary"></button></div>'
|
462 |
}
|
463 |
},
|
464 |
|
467 |
/**
|
468 |
* Builds the container of the multiselect.
|
469 |
*/
|
470 |
+
buildContainer: function () {
|
471 |
this.$container = $(this.options.buttonContainer);
|
472 |
+
if (this.options.widthSynchronizationMode !== 'never') {
|
473 |
+
this.$container.on('show.bs.dropdown', $.proxy(function () {
|
474 |
+
// the width needs to be synchronized again in case the width of the button changed in between
|
475 |
+
this.synchronizeButtonAndPopupWidth();
|
476 |
+
this.options.onDropdownShow();
|
477 |
+
}, this));
|
478 |
+
}
|
479 |
+
else {
|
480 |
+
this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
|
481 |
+
}
|
482 |
this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
|
483 |
this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
|
484 |
this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
|
487 |
/**
|
488 |
* Builds the button of the multiselect.
|
489 |
*/
|
490 |
+
buildButton: function () {
|
491 |
this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
|
492 |
if (this.$select.attr('class') && this.options.inheritClass) {
|
493 |
this.$button.addClass(this.$select.attr('class'));
|
503 |
// Manually add button width if set.
|
504 |
if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
|
505 |
this.$button.css({
|
506 |
+
'width': '100%' //this.options.buttonWidth,
|
|
|
|
|
507 |
});
|
508 |
this.$container.css({
|
509 |
'width': this.options.buttonWidth
|
510 |
});
|
511 |
}
|
512 |
|
513 |
+
if (this.options.buttonTextAlignment) {
|
514 |
+
switch (this.options.buttonTextAlignment) {
|
515 |
+
case 'left':
|
516 |
+
this.$button.addClass('text-left');
|
517 |
+
break;
|
518 |
+
case 'center':
|
519 |
+
this.$button.addClass('text-center');
|
520 |
+
break;
|
521 |
+
case 'right':
|
522 |
+
this.$button.addClass('text-right');
|
523 |
+
break;
|
524 |
+
}
|
525 |
+
}
|
526 |
+
|
527 |
// Keep the tab index from the select.
|
528 |
var tabindex = this.$select.attr('tabindex');
|
529 |
if (tabindex) {
|
534 |
},
|
535 |
|
536 |
/**
|
537 |
+
* Builds the popup container representing the dropdown menu.
|
538 |
*/
|
539 |
+
buildDropdown: function () {
|
540 |
|
541 |
+
// Build popup container.
|
542 |
+
this.$popupContainer = $(this.options.templates.popupContainer);
|
543 |
|
544 |
if (this.options.dropRight) {
|
545 |
+
this.$container.addClass('dropright');
|
546 |
+
}
|
547 |
+
else if (this.options.dropUp) {
|
548 |
+
this.$container.addClass("dropup");
|
549 |
}
|
550 |
|
551 |
// Set max height of dropdown menu to activate auto scrollbar.
|
552 |
if (this.options.maxHeight) {
|
553 |
// TODO: Add a class for this option to move the css declarations.
|
554 |
+
this.$popupContainer.css({
|
555 |
'max-height': this.options.maxHeight + 'px',
|
556 |
'overflow-y': 'auto',
|
557 |
'overflow-x': 'hidden'
|
558 |
});
|
559 |
}
|
560 |
|
561 |
+
if (this.options.widthSynchronizationMode !== 'never') {
|
562 |
+
this.$popupContainer.css('overflow-x', 'hidden');
|
563 |
+
}
|
564 |
|
565 |
+
this.$popupContainer.on("touchstart click", function (e) {
|
566 |
+
e.stopPropagation();
|
567 |
+
});
|
568 |
|
569 |
+
this.$container.append(this.$popupContainer);
|
570 |
+
},
|
571 |
+
|
572 |
+
synchronizeButtonAndPopupWidth: function () {
|
573 |
+
if (!this.$popupContainer || this.options.widthSynchronizationMode === 'never') {
|
574 |
+
return;
|
575 |
}
|
576 |
|
577 |
+
var buttonWidth = this.$button.outerWidth();
|
578 |
+
switch (this.options.widthSynchronizationMode) {
|
579 |
+
case 'always':
|
580 |
+
this.$popupContainer.css('min-width', buttonWidth);
|
581 |
+
this.$popupContainer.css('max-width', buttonWidth);
|
582 |
+
break;
|
583 |
+
case 'ifPopupIsSmaller':
|
584 |
+
this.$popupContainer.css('min-width', buttonWidth);
|
585 |
+
break;
|
586 |
+
case 'ifPopupIsWider':
|
587 |
+
this.$popupContainer.css('max-width', buttonWidth);
|
588 |
+
break;
|
589 |
+
}
|
590 |
},
|
591 |
|
592 |
/**
|
594 |
*
|
595 |
* Uses createDivider and createOptionValue to create the necessary options.
|
596 |
*/
|
597 |
+
buildDropdownOptions: function () {
|
598 |
|
599 |
+
this.$select.children().each($.proxy(function (index, element) {
|
600 |
|
601 |
var $element = $(element);
|
602 |
// Support optgroups and options without a group simultaneously.
|
616 |
this.createDivider();
|
617 |
}
|
618 |
else {
|
619 |
+
this.createOptionValue(element, false);
|
620 |
}
|
621 |
|
622 |
}
|
625 |
}, this));
|
626 |
|
627 |
// Bind the change event on the dropdown elements.
|
628 |
+
$(this.$popupContainer).off('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]');
|
629 |
+
$(this.$popupContainer).on('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]', $.proxy(function (event) {
|
630 |
var $target = $(event.target);
|
631 |
|
632 |
var checked = $target.prop('checked') || false;
|
635 |
// Apply or unapply the configured selected class.
|
636 |
if (this.options.selectedClass) {
|
637 |
if (checked) {
|
638 |
+
$target.closest('.multiselect-option')
|
639 |
.addClass(this.options.selectedClass);
|
640 |
}
|
641 |
else {
|
642 |
+
$target.closest('.multiselect-option')
|
643 |
.removeClass(this.options.selectedClass);
|
644 |
}
|
645 |
}
|
671 |
else {
|
672 |
// Unselect all other options and corresponding checkboxes.
|
673 |
if (this.options.selectedClass) {
|
674 |
+
$($checkboxesNotThis).closest('.dropdown-item').removeClass(this.options.selectedClass);
|
675 |
}
|
676 |
|
677 |
$($checkboxesNotThis).prop('checked', false);
|
682 |
}
|
683 |
|
684 |
if (this.options.selectedClass === "active") {
|
685 |
+
$optionsNotThis.closest(".dropdown-item").css("outline", "");
|
686 |
}
|
687 |
}
|
688 |
else {
|
704 |
this.$select.change();
|
705 |
this.updateButtonText();
|
706 |
|
707 |
+
if (this.options.preventInputChangeEvent) {
|
708 |
return false;
|
709 |
}
|
710 |
}, this));
|
711 |
|
712 |
+
$('.multiselect-option', this.$popupContainer).off('mousedown');
|
713 |
+
$('.multiselect-option', this.$popupContainer).on('mousedown', function (e) {
|
714 |
if (e.shiftKey) {
|
715 |
// Prevent selecting text by Shift+click
|
716 |
return false;
|
717 |
}
|
718 |
});
|
719 |
|
720 |
+
$(this.$popupContainer).off('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group');
|
721 |
+
$(this.$popupContainer).on('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group', $.proxy(function (event) {
|
722 |
event.stopPropagation();
|
723 |
|
724 |
var $target = $(event.target);
|
725 |
|
726 |
if (event.shiftKey && this.options.multiple) {
|
727 |
+
if (!$target.is("input")) { // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
|
728 |
event.preventDefault();
|
729 |
+
$target = $target.closest(".multiselect-option").find("input");
|
730 |
$target.prop("checked", !$target.prop("checked"));
|
731 |
}
|
732 |
var checked = $target.prop('checked') || false;
|
733 |
|
734 |
if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range
|
735 |
+
var from = this.$popupContainer.find(".multiselect-option:visible").index($target.closest(".multiselect-option"));
|
736 |
+
var to = this.$popupContainer.find(".multiselect-option:visible").index(this.lastToggledInput.closest(".multiselect-option"));
|
737 |
|
738 |
if (from > to) { // Swap the indices
|
739 |
var tmp = to;
|
745 |
++to;
|
746 |
|
747 |
// Change the checkboxes and underlying options
|
748 |
+
var range = this.$popupContainer.find(".multiselect-option:not(.multiselect-filter-hidden)").slice(from, to).find("input");
|
749 |
|
750 |
range.prop('checked', checked);
|
751 |
|
752 |
if (this.options.selectedClass) {
|
753 |
+
range.closest('.multiselect-option')
|
754 |
.toggleClass(this.options.selectedClass, checked);
|
755 |
}
|
756 |
|
766 |
// Trigger the select "change" event
|
767 |
$target.trigger("change");
|
768 |
}
|
769 |
+
else if (!$target.is('input')) {
|
770 |
+
var $checkbox = $target.closest('.multiselect-option, .multiselect-all').find('.form-check-input');
|
771 |
+
if ($checkbox.length > 0) {
|
772 |
+
if (this.options.multiple || !$checkbox.prop('checked')) {
|
773 |
+
$checkbox.prop('checked', !$checkbox.prop('checked'));
|
774 |
+
$checkbox.change();
|
775 |
+
}
|
776 |
+
}
|
777 |
+
else if (this.options.enableClickableOptGroups && this.options.multiple && !$target.hasClass("caret-container")) {
|
778 |
+
var groupItem = $target;
|
779 |
+
if (!groupItem.hasClass("multiselect-group")) {
|
780 |
+
groupItem = $target.closest('.multiselect-group');
|
781 |
+
}
|
782 |
+
$checkbox = groupItem.find(".form-check-input");
|
783 |
+
if ($checkbox.length > 0) {
|
784 |
+
$checkbox.prop('checked', !$checkbox.prop('checked'));
|
785 |
+
$checkbox.change();
|
786 |
+
}
|
787 |
+
}
|
788 |
+
|
789 |
+
event.preventDefault();
|
790 |
+
}
|
791 |
|
792 |
// Remembers last clicked option
|
793 |
+
var $input = $target.closest(".multiselect-option").find("input[type='checkbox'], input[type='radio']");
|
794 |
+
if ($input.length > 0) {
|
795 |
this.lastToggledInput = $target;
|
796 |
}
|
797 |
+
else {
|
798 |
+
this.lastToggledInput = null;
|
799 |
+
}
|
800 |
|
801 |
+
$target.blur();
|
802 |
}, this));
|
803 |
|
804 |
+
//Keyboard support.
|
805 |
+
this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function (event) {
|
806 |
+
var $items = $(this.$container).find(".multiselect-option:not(.disabled), .multiselect-group:not(.disabled), .multiselect-all").filter(":visible");
|
807 |
+
var index = $items.index($items.filter(':focus'));
|
808 |
+
var $search = $('.multiselect-search', this.$container);
|
809 |
|
810 |
+
// keyCode 9 == Tab
|
811 |
+
if (event.keyCode === 9 && this.$container.hasClass('show')) {
|
812 |
this.$button.click();
|
813 |
}
|
814 |
+
// keyCode 13 = Enter
|
815 |
+
else if (event.keyCode == 13) {
|
816 |
+
var $current = $items.eq(index);
|
817 |
+
setTimeout(function () {
|
818 |
+
$current.focus();
|
819 |
+
}, 1);
|
820 |
+
}
|
821 |
+
// keyCode 38 = Arrow Up
|
822 |
+
else if (event.keyCode == 38) {
|
823 |
+
if (index == 0 && !$search.is(':focus')) {
|
824 |
+
setTimeout(function () {
|
825 |
+
$search.focus();
|
826 |
+
}, 1);
|
827 |
}
|
828 |
+
}
|
829 |
+
// keyCode 40 = Arrow Down
|
830 |
+
else if (event.keyCode == 40) {
|
831 |
+
if ($search.is(':focus')) {
|
832 |
+
var $first = $items.eq(0);
|
833 |
+
setTimeout(function () {
|
834 |
+
$search.blur();
|
835 |
+
$first.focus();
|
836 |
+
}, 1);
|
837 |
}
|
838 |
+
else if (index == -1) {
|
839 |
+
setTimeout(function () {
|
840 |
+
$search.focus();
|
841 |
+
}, 1);
|
842 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
843 |
}
|
844 |
}, this));
|
845 |
|
846 |
if (this.options.enableClickableOptGroups && this.options.multiple) {
|
847 |
+
$(".multiselect-group input", this.$popupContainer).off("change");
|
848 |
+
$(".multiselect-group input", this.$popupContainer).on("change", $.proxy(function (event) {
|
849 |
event.stopPropagation();
|
850 |
|
851 |
var $target = $(event.target);
|
852 |
var checked = $target.prop('checked') || false;
|
853 |
|
854 |
+
var $item = $(event.target).closest('.dropdown-item');
|
855 |
+
var $group = $item.nextUntil(".multiselect-group")
|
856 |
.not('.multiselect-filter-hidden')
|
857 |
.not('.disabled');
|
858 |
|
859 |
var $inputs = $group.find("input");
|
860 |
|
|
|
861 |
var $options = [];
|
862 |
|
863 |
if (this.options.selectedClass) {
|
864 |
if (checked) {
|
865 |
+
$item.addClass(this.options.selectedClass);
|
866 |
}
|
867 |
else {
|
868 |
+
$item.removeClass(this.options.selectedClass);
|
869 |
}
|
870 |
}
|
871 |
|
872 |
+
$.each($inputs, $.proxy(function (index, input) {
|
873 |
+
var $input = $(input);
|
874 |
+
var value = $input.val();
|
875 |
var $option = this.getOptionByValue(value);
|
876 |
|
877 |
if (checked) {
|
878 |
+
$input.prop('checked', true);
|
879 |
+
$input.closest('.dropdown-item')
|
880 |
.addClass(this.options.selectedClass);
|
881 |
|
882 |
$option.prop('selected', true);
|
883 |
}
|
884 |
else {
|
885 |
+
$input.prop('checked', false);
|
886 |
+
$input.closest('.dropdown-item')
|
887 |
.removeClass(this.options.selectedClass);
|
888 |
|
889 |
$option.prop('selected', false);
|
903 |
}
|
904 |
|
905 |
if (this.options.enableCollapsibleOptGroups && this.options.multiple) {
|
906 |
+
$(".multiselect-group .caret-container", this.$popupContainer).off("click");
|
907 |
+
$(".multiselect-group .caret-container", this.$popupContainer).on("click", $.proxy(function (event) {
|
908 |
+
var $group = $(event.target).closest('.multiselect-group');
|
909 |
+
var $inputs = $group.nextUntil(".multiselect-group")
|
910 |
+
.not('.multiselect-filter-hidden');
|
911 |
|
912 |
var visible = true;
|
913 |
+
$inputs.each(function () {
|
914 |
visible = visible && !$(this).hasClass('multiselect-collapsible-hidden');
|
915 |
});
|
916 |
|
923 |
.removeClass('multiselect-collapsible-hidden');
|
924 |
}
|
925 |
}, this));
|
926 |
+
}
|
927 |
+
},
|
928 |
+
|
929 |
+
/**
|
930 |
+
* Create a checkbox container with input and label based on given values
|
931 |
+
* @param {JQuery} $item
|
932 |
+
* @param {String} label
|
933 |
+
* @param {String} name
|
934 |
+
* @param {String} value
|
935 |
+
* @param {String} inputType
|
936 |
+
* @returns {JQuery}
|
937 |
+
*/
|
938 |
+
createCheckbox: function ($item, labelContent, name, value, title, inputType) {
|
939 |
+
var $wrapper = $('<span />');
|
940 |
+
$wrapper.addClass("form-check");
|
941 |
+
|
942 |
+
if (this.options.enableHTML && $(labelContent).length > 0) {
|
943 |
+
var $checkboxLabel = $('<label class="form-check-label" />');
|
944 |
+
$checkboxLabel.html(labelContent);
|
945 |
+
$wrapper.append($checkboxLabel);
|
946 |
+
}
|
947 |
+
else {
|
948 |
+
var $checkboxLabel = $('<label class="form-check-label" />');
|
949 |
+
$checkboxLabel.text(labelContent);
|
950 |
+
$wrapper.append($checkboxLabel);
|
951 |
+
}
|
952 |
|
953 |
+
var $checkbox = $('<input class="form-check-input"/>').attr('type', inputType);
|
954 |
+
$checkbox.val(value);
|
955 |
+
$wrapper.prepend($checkbox);
|
956 |
+
|
957 |
+
if (name) {
|
958 |
+
$checkbox.attr('name', name);
|
959 |
}
|
960 |
+
|
961 |
+
$item.prepend($wrapper);
|
962 |
+
$item.attr("title", title || labelContent);
|
963 |
+
|
964 |
+
return $checkbox;
|
965 |
},
|
966 |
|
967 |
/**
|
969 |
*
|
970 |
* @param {jQuery} element
|
971 |
*/
|
972 |
+
createOptionValue: function (element, isGroupOption) {
|
973 |
var $element = $(element);
|
974 |
if ($element.is(':selected')) {
|
975 |
$element.prop('selected', true);
|
980 |
var classes = this.options.optionClass(element);
|
981 |
var value = $element.val();
|
982 |
var inputType = this.options.multiple ? "checkbox" : "radio";
|
983 |
+
var title = $element.attr('title');
|
984 |
|
985 |
+
var $option = $(this.options.templates.option);
|
986 |
+
$option.addClass(classes);
|
|
|
|
|
|
|
987 |
|
988 |
+
if (isGroupOption && this.options.indentGroupOptions) {
|
989 |
+
$option.addClass("multiselect-group-option-indented")
|
|
|
|
|
990 |
}
|
991 |
|
992 |
+
// Hide all children items when collapseOptGroupsByDefault is true
|
993 |
+
if (this.options.collapseOptGroupsByDefault && $(element).parent().prop("tagName").toLowerCase() === "optgroup") {
|
994 |
+
$option.addClass("multiselect-collapsible-hidden");
|
995 |
+
$option.hide();
|
|
|
996 |
}
|
997 |
|
|
|
|
|
998 |
var name = this.options.checkboxName($element);
|
999 |
+
var $checkbox = this.createCheckbox($option, label, name, value, title, inputType);
|
|
|
|
|
|
|
|
|
1000 |
|
1001 |
var selected = $element.prop('selected') || false;
|
|
|
1002 |
|