User Role Editor - Version 4.38

Version Description

Download this release

Release Info

Developer shinephp
Plugin Icon 128x128 User Role Editor
Version 4.38
Comparing to
See all releases

Code changes from version 4.37 to 4.38

changelog.txt CHANGED
@@ -1,6 +1,11 @@
1
  CHANGES LOG (full version).
2
  ===========================
3
 
 
 
 
 
 
4
  = [4.37] 01.11.2017 =
5
  * New: New option was added. It's possible to select permanent quant of capabilities columns at the "Settings->User Role Editor->General" tab.
6
  * Update: User capabilities are shown for custom post types which use the WordPress built-in 'post' or 'page' capability type. (0/0) was shown earlier instead of the quantity of user capabilities really used.
1
  CHANGES LOG (full version).
2
  ===========================
3
 
4
+ = [4.38] 27.11.2017 =
5
+ * Security: XSS vulnerability was fixed at URE's options page. Bug was discovered and fixed at tab index value numeric type checking. Tab index value is additionally escaped before output also.
6
+ * Security: Deprecated code for debug output to the .log file in case of database query error was removed.
7
+ * Security: Multiple select jQuery plugin (https://github.com/wenzhixin/multiple-select/) was updated to the latest available version 1.2.1, which fixed XSS vulnerability, existed in earlier versions.
8
+
9
  = [4.37] 01.11.2017 =
10
  * New: New option was added. It's possible to select permanent quant of capabilities columns at the "Settings->User Role Editor->General" tab.
11
  * Update: User capabilities are shown for custom post types which use the WordPress built-in 'post' or 'page' capability type. (0/0) was shown earlier instead of the quantity of user capabilities really used.
css/multiple-select.css CHANGED
@@ -3,161 +3,189 @@
3
  */
4
 
5
  .ms-parent {
6
- display: inline-block;
7
- position: relative;
8
- vertical-align: middle;
9
  }
10
 
11
  .ms-choice {
12
- display: block;
13
- height: 26px;
14
- padding: 0;
15
- overflow: hidden;
16
- position: relative;
17
- cursor: pointer;
18
- border: 1px solid #aaa;
19
- white-space: nowrap;
20
- line-height: 26px;
21
- color: #444;
22
- text-decoration: none;
23
- -webkit-border-radius: 4px;
24
- -moz-border-radius: 4px;
25
- border-radius: 4px;
26
- background-color: #fff;
 
27
  }
28
 
29
  .ms-choice.disabled {
30
- background-color: #f4f4f4;
31
- background-image: none;
32
- border: 1px solid #ddd;
33
- cursor: default;
34
  }
35
 
36
  .ms-choice > span {
37
- white-space: nowrap;
38
- overflow: hidden;
39
- text-overflow: ellipsis;
40
- display: block;
41
- float: left;
42
- padding-left: 8px;
 
 
 
43
  }
44
 
45
  .ms-choice > span.placeholder {
46
- color: #999;
47
  }
48
 
49
  .ms-choice > div {
50
- float: right;
51
- width: 20px;
52
- height: 25px;
53
- background: url('multiple-select.png') right top no-repeat;
 
 
54
  }
55
 
56
  .ms-choice > div.open {
57
- background: url('multiple-select.png') left top no-repeat;
58
  }
59
 
60
  .ms-drop {
61
- overflow: hidden;
62
- display: none;
63
- margin-top: -1px;
64
- padding: 0;
65
- position: absolute;
66
- z-index: 1000;
67
- top: 100%;
68
- background: #fff;
69
- color: #000;
70
- border: 1px solid #aaa;
71
- -webkit-border-radius: 4px;
72
- -moz-border-radius: 4px;
73
- border-radius: 4px;
74
- -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
75
- -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
76
- box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
79
-
80
  .ms-search {
81
- display: inline-block;
82
- margin: 0;
83
- min-height: 26px;
84
- padding: 4px;
85
- position: relative;
86
- white-space: nowrap;
87
- width: 100%;
88
- z-index: 10000;
89
  }
90
 
91
  .ms-search input {
92
- width: 100%;
93
- height: auto !important;
94
- min-height: 24px;
95
- padding: 0 20px 0 5px;
96
- margin: 0;
97
- outline: 0;
98
- font-family: sans-serif;
99
- font-size: 1em;
100
- border: 1px solid #aaa;
101
- -webkit-border-radius: 0;
102
- -moz-border-radius: 0;
103
- border-radius: 0;
104
- -webkit-box-shadow: none;
105
- -moz-box-shadow: none;
106
- box-shadow: none;
107
- background: #fff url('multiple-select.png') no-repeat 100% -22px;
108
- background: url('multiple-select.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
109
- background: url('multiple-select.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
110
- background: url('multiple-select.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
111
- background: url('multiple-select.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
112
- background: url('multiple-select.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
113
- background: url('multiple-select.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
114
  }
115
 
116
  .ms-search, .ms-search input {
117
- -webkit-box-sizing: border-box;
118
- -khtml-box-sizing: border-box;
119
- -moz-box-sizing: border-box;
120
- -ms-box-sizing: border-box;
121
- box-sizing: border-box;
122
  }
123
 
124
  .ms-drop ul {
125
- overflow: auto;
126
- margin: 0;
127
- padding: 5px 8px;
128
  }
129
 
130
  .ms-drop ul > li {
131
- list-style: none;
132
- display: list-item;
133
- background-image: none;
134
- position: static;
135
  }
136
 
137
  .ms-drop ul > li .disabled {
138
- opacity: .35;
139
- filter: Alpha(Opacity=35);
140
  }
141
 
142
  .ms-drop ul > li.multiple {
143
- display: block;
144
- float: left;
145
  }
146
 
147
  .ms-drop ul > li.group {
148
- clear: both;
149
  }
150
 
151
  .ms-drop ul > li.multiple label {
152
- white-space: nowrap;
153
- overflow: hidden;
154
- text-overflow: ellipsis;
 
 
 
 
 
 
 
 
155
  }
156
 
157
  .ms-drop ul > li label.optgroup {
158
- font-weight: bold;
159
  }
160
 
161
  .ms-drop input[type="checkbox"] {
162
- vertical-align: middle;
 
 
 
 
163
  }
3
  */
4
 
5
  .ms-parent {
6
+ display: inline-block;
7
+ position: relative;
8
+ vertical-align: middle;
9
  }
10
 
11
  .ms-choice {
12
+ display: block;
13
+ width: 100%;
14
+ height: 26px;
15
+ padding: 0;
16
+ overflow: hidden;
17
+ cursor: pointer;
18
+ border: 1px solid #aaa;
19
+ text-align: left;
20
+ white-space: nowrap;
21
+ line-height: 26px;
22
+ color: #444;
23
+ text-decoration: none;
24
+ -webkit-border-radius: 4px;
25
+ -moz-border-radius: 4px;
26
+ border-radius: 4px;
27
+ background-color: #fff;
28
  }
29
 
30
  .ms-choice.disabled {
31
+ background-color: #f4f4f4;
32
+ background-image: none;
33
+ border: 1px solid #ddd;
34
+ cursor: default;
35
  }
36
 
37
  .ms-choice > span {
38
+ position: absolute;
39
+ top: 0;
40
+ left: 0;
41
+ right: 20px;
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ display: block;
46
+ padding-left: 8px;
47
  }
48
 
49
  .ms-choice > span.placeholder {
50
+ color: #999;
51
  }
52
 
53
  .ms-choice > div {
54
+ position: absolute;
55
+ top: 0;
56
+ right: 0;
57
+ width: 20px;
58
+ height: 25px;
59
+ background: url('multiple-select.png') left top no-repeat;
60
  }
61
 
62
  .ms-choice > div.open {
63
+ background: url('multiple-select.png') right top no-repeat;
64
  }
65
 
66
  .ms-drop {
67
+ width: 100%;
68
+ overflow: hidden;
69
+ display: none;
70
+ margin-top: -1px;
71
+ padding: 0;
72
+ position: absolute;
73
+ z-index: 1000;
74
+ background: #fff;
75
+ color: #000;
76
+ border: 1px solid #aaa;
77
+ -webkit-border-radius: 4px;
78
+ -moz-border-radius: 4px;
79
+ border-radius: 4px;
80
+ }
81
+
82
+ .ms-drop.bottom {
83
+ top: 100%;
84
+ -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
85
+ -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
86
+ box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
87
+ }
88
+
89
+ .ms-drop.top {
90
+ bottom: 100%;
91
+ -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
92
+ -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
93
+ box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
94
  }
95
 
 
96
  .ms-search {
97
+ display: inline-block;
98
+ margin: 0;
99
+ min-height: 26px;
100
+ padding: 4px;
101
+ position: relative;
102
+ white-space: nowrap;
103
+ width: 100%;
104
+ z-index: 10000;
105
  }
106
 
107
  .ms-search input {
108
+ width: 100%;
109
+ height: auto !important;
110
+ min-height: 24px;
111
+ padding: 0 20px 0 5px;
112
+ margin: 0;
113
+ outline: 0;
114
+ font-family: sans-serif;
115
+ font-size: 1em;
116
+ border: 1px solid #aaa;
117
+ -webkit-border-radius: 0;
118
+ -moz-border-radius: 0;
119
+ border-radius: 0;
120
+ -webkit-box-shadow: none;
121
+ -moz-box-shadow: none;
122
+ box-shadow: none;
123
+ background: #fff url('multiple-select.png') no-repeat 100% -22px;
124
+ background: url('multiple-select.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
125
+ background: url('multiple-select.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
126
+ background: url('multiple-select.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
127
+ background: url('multiple-select.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
128
+ background: url('multiple-select.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
129
+ background: url('multiple-select.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
130
  }
131
 
132
  .ms-search, .ms-search input {
133
+ -webkit-box-sizing: border-box;
134
+ -khtml-box-sizing: border-box;
135
+ -moz-box-sizing: border-box;
136
+ -ms-box-sizing: border-box;
137
+ box-sizing: border-box;
138
  }
139
 
140
  .ms-drop ul {
141
+ overflow: auto;
142
+ margin: 0;
143
+ padding: 5px 8px;
144
  }
145
 
146
  .ms-drop ul > li {
147
+ list-style: none;
148
+ display: list-item;
149
+ background-image: none;
150
+ position: static;
151
  }
152
 
153
  .ms-drop ul > li .disabled {
154
+ opacity: .35;
155
+ filter: Alpha(Opacity=35);
156
  }
157
 
158
  .ms-drop ul > li.multiple {
159
+ display: block;
160
+ float: left;
161
  }
162
 
163
  .ms-drop ul > li.group {
164
+ clear: both;
165
  }
166
 
167
  .ms-drop ul > li.multiple label {
168
+ width: 100%;
169
+ display: block;
170
+ white-space: nowrap;
171
+ overflow: hidden;
172
+ text-overflow: ellipsis;
173
+ }
174
+
175
+ .ms-drop ul > li label {
176
+ font-weight: normal;
177
+ display: block;
178
+ white-space: nowrap;
179
  }
180
 
181
  .ms-drop ul > li label.optgroup {
182
+ font-weight: bold;
183
  }
184
 
185
  .ms-drop input[type="checkbox"] {
186
+ vertical-align: middle;
187
+ }
188
+
189
+ .ms-drop .ms-no-results {
190
+ display: none;
191
  }
css/multiple-select.png CHANGED
Binary file
includes/classes/ure-lib.php CHANGED
@@ -655,7 +655,6 @@ class URE_Lib extends URE_Base_Lib {
655
  limit 0, 1";
656
  $option_value = $wpdb->get_var($query);
657
  if ($wpdb->last_error) {
658
- $this->log_event($wpdb->last_error, true);
659
  return $error_message;
660
  }
661
  if ($option_value) {
@@ -665,7 +664,6 @@ class URE_Lib extends URE_Base_Lib {
665
  limit 1";
666
  $record = $wpdb->query($query);
667
  if ($wpdb->last_error) {
668
- $this->log_event($wpdb->last_error, true);
669
  return $error_message;
670
  }
671
  $wp_roles = new WP_Roles();
@@ -712,14 +710,13 @@ class URE_Lib extends URE_Base_Lib {
712
  limit 0, 1";
713
  $option_id = $wpdb->get_var($query);
714
  if ($wpdb->last_error) {
715
- $this->log_event($wpdb->last_error, true);
716
  return false;
717
  }
718
  if (!$option_id) {
719
  $roles_option_name = $wpdb->prefix.'user_roles';
720
  $query = "select option_value
721
  from $wpdb->options
722
- where option_name like '$roles_option_name' limit 0,1";
723
  $serialized_roles = $wpdb->get_var($query);
724
  // create user roles record backup
725
  $query = "insert into $wpdb->options
@@ -727,7 +724,6 @@ class URE_Lib extends URE_Base_Lib {
727
  values ('$backup_option_name', '$serialized_roles', 'no')";
728
  $record = $wpdb->query($query);
729
  if ($wpdb->last_error) {
730
- $this->log_event($wpdb->last_error, true);
731
  return false;
732
  }
733
  }
@@ -1324,7 +1320,6 @@ class URE_Lib extends URE_Base_Lib {
1324
  limit 1";
1325
  $wpdb->query($query);
1326
  if ($wpdb->last_error) {
1327
- $this->log_event($wpdb->last_error, true);
1328
  return false;
1329
  }
1330
  // @TODO: save role additional options
@@ -1422,33 +1417,7 @@ class URE_Lib extends URE_Base_Lib {
1422
 
1423
  return true;
1424
  }
1425
- // end of update_roles()
1426
-
1427
-
1428
- /**
1429
- * Write message to the log file
1430
- *
1431
- * @global type $wp_version
1432
- * @param string $message
1433
- * @param boolean $show_message
1434
- */
1435
- protected function log_event($message, $show_message = false) {
1436
- global $wp_version, $wpdb;
1437
-
1438
- $file_name = URE_PLUGIN_DIR . 'user-role-editor.log';
1439
- $fh = fopen($file_name, 'a');
1440
- $cr = "\n";
1441
- $s = $cr . date("d-m-Y H:i:s") . $cr .
1442
- 'WordPress version: ' . $wp_version . ', PHP version: ' . phpversion() . ', MySQL version: ' . $wpdb->db_version() . $cr;
1443
- fwrite($fh, $s);
1444
- fwrite($fh, $message . $cr);
1445
- fclose($fh);
1446
-
1447
- if ($show_message) {
1448
- $this->show_message('Error! ' . esc_html__('Error is occur. Please check the log file.', 'user-role-editor'));
1449
- }
1450
- }
1451
- // end of log_event()
1452
 
1453
 
1454
  /**
655
  limit 0, 1";
656
  $option_value = $wpdb->get_var($query);
657
  if ($wpdb->last_error) {
 
658
  return $error_message;
659
  }
660
  if ($option_value) {
664
  limit 1";
665
  $record = $wpdb->query($query);
666
  if ($wpdb->last_error) {
 
667
  return $error_message;
668
  }
669
  $wp_roles = new WP_Roles();
710
  limit 0, 1";
711
  $option_id = $wpdb->get_var($query);
712
  if ($wpdb->last_error) {
 
713
  return false;
714
  }
715
  if (!$option_id) {
716
  $roles_option_name = $wpdb->prefix.'user_roles';
717
  $query = "select option_value
718
  from $wpdb->options
719
+ where option_name='$roles_option_name' limit 0,1";
720
  $serialized_roles = $wpdb->get_var($query);
721
  // create user roles record backup
722
  $query = "insert into $wpdb->options
724
  values ('$backup_option_name', '$serialized_roles', 'no')";
725
  $record = $wpdb->query($query);
726
  if ($wpdb->last_error) {
 
727
  return false;
728
  }
729
  }
1320
  limit 1";
1321
  $wpdb->query($query);
1322
  if ($wpdb->last_error) {
 
1323
  return false;
1324
  }
1325
  // @TODO: save role additional options
1417
 
1418
  return true;
1419
  }
1420
+ // end of update_roles()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1421
 
1422
 
1423
  /**
includes/classes/user-other-roles.php CHANGED
@@ -76,7 +76,7 @@ class URE_User_Other_Roles {
76
  $select_primary_role = apply_filters('ure_users_select_primary_role', true);
77
 
78
  wp_enqueue_script('jquery-ui-dialog', '', array('jquery-ui-core', 'jquery-ui-button', 'jquery'));
79
- wp_register_script('ure-jquery-multiple-select', plugins_url('/js/jquery.multiple.select.js', URE_PLUGIN_FULL_PATH));
80
  wp_enqueue_script('ure-jquery-multiple-select');
81
  wp_register_script('ure-user-profile-other-roles', plugins_url('/js/user-profile-other-roles.js', URE_PLUGIN_FULL_PATH));
82
  wp_enqueue_script('ure-user-profile-other-roles');
76
  $select_primary_role = apply_filters('ure_users_select_primary_role', true);
77
 
78
  wp_enqueue_script('jquery-ui-dialog', '', array('jquery-ui-core', 'jquery-ui-button', 'jquery'));
79
+ wp_register_script('ure-jquery-multiple-select', plugins_url('/js/multiple-select.js', URE_PLUGIN_FULL_PATH));
80
  wp_enqueue_script('ure-jquery-multiple-select');
81
  wp_register_script('ure-user-profile-other-roles', plugins_url('/js/user-profile-other-roles.js', URE_PLUGIN_FULL_PATH));
82
  wp_enqueue_script('ure-user-profile-other-roles');
includes/classes/user-role-editor.php CHANGED
@@ -779,7 +779,7 @@ class User_Role_Editor {
779
  $view = new URE_Role_View();
780
  $view->role_default_prepare_html(0);
781
 
782
- $ure_tab_idx = $this->lib->get_request_var('ure_tab_idx', 'int');
783
 
784
  do_action('ure_settings_load');
785
 
779
  $view = new URE_Role_View();
780
  $view->role_default_prepare_html(0);
781
 
782
+ $ure_tab_idx = (int) $this->lib->get_request_var('ure_tab_idx', 'post', 'int');
783
 
784
  do_action('ure_settings_load');
785
 
includes/settings-template.php CHANGED
@@ -247,7 +247,8 @@ if (!$multisite) {
247
  jQuery(function() {
248
  jQuery('#ure_tabs').tabs();
249
  <?php
250
- if ($ure_tab_idx>0) {
 
251
  ?>
252
  jQuery("#ure_tabs").tabs("option", "active", <?php echo $ure_tab_idx; ?>);
253
  <?php
247
  jQuery(function() {
248
  jQuery('#ure_tabs').tabs();
249
  <?php
250
+ $ure_tab_idx = (int) $ure_tab_idx;
251
+ if ($ure_tab_idx>0 && $ure_tab_idx<=count($tabs_index)) {
252
  ?>
253
  jQuery("#ure_tabs").tabs("option", "active", <?php echo $ure_tab_idx; ?>);
254
  <?php
js/jquery.multiple.select.js DELETED
@@ -1,332 +0,0 @@
1
- /**
2
- * @author zhixin wen <wenzhixin2010@gmail.com>
3
- * @version 1.0.4
4
- *
5
- * http://wenzhixin.net.cn/p/multiple-select/
6
- */
7
-
8
- (function($) {
9
-
10
- 'use strict';
11
-
12
- function MultipleSelect($el, options) {
13
- var that = this,
14
- elWidth = $el.width();
15
-
16
- this.$el = $el.hide();
17
- this.options = options;
18
-
19
- this.$parent = $('<div class="ms-parent"></div>');
20
- this.$choice = $('<div class="ms-choice"><span class="placeholder">' +
21
- options.placeholder + '</span><div></div></div>');
22
- this.$drop = $('<div class="ms-drop"></div>');
23
- this.$el.after(this.$parent);
24
- this.$parent.append(this.$choice);
25
- this.$parent.append(this.$drop);
26
-
27
- if (this.$el.prop('disabled')) {
28
- this.$choice.addClass('disabled');
29
- }
30
- this.$choice.css('width', elWidth + 'px')
31
- .find('span').css('width', (elWidth - 28) + 'px');
32
- this.$drop.css({
33
- width: (options.width || elWidth) + 'px'
34
- });
35
-
36
- $('body').click(function(e) {
37
- if ($(e.target)[0] === that.$choice[0] ||
38
- $(e.target).parents('.ms-choice')[0] === that.$choice[0]) {
39
- return;
40
- }
41
- if (($(e.target)[0] === that.$drop[0] ||
42
- $(e.target).parents('.ms-drop')[0] !== that.$drop[0]) &&
43
- that.options.isopen) {
44
- that.close();
45
- }
46
- });
47
-
48
- if (this.options.isopen) {
49
- this.open();
50
- }
51
- }
52
-
53
- MultipleSelect.prototype = {
54
- constructor : MultipleSelect,
55
-
56
- init: function() {
57
- var that = this,
58
- html = [];
59
- if (this.options.filter) {
60
- html.push(
61
- '<div class="ms-search">',
62
- '<input type="text" autocomplete="off" autocorrect="off" autocapitilize="off" spellcheck="false">',
63
- '</div>'
64
- );
65
- }
66
- html.push('<ul>');
67
- if (this.options.selectAll) {
68
- html.push(
69
- '<li>',
70
- '<label>',
71
- '<input type="checkbox" name="selectAll" /> ',
72
- '[' + this.options.selectAllText + ']',
73
- '</label>',
74
- '</li>'
75
- );
76
- }
77
- $.each(this.$el.children(), function(i, elm) {
78
- html.push(that.optionToHtml(i, elm));
79
- });
80
- html.push('</ul>');
81
- this.$drop.html(html.join(''));
82
- this.$drop.find('ul').css('max-height', this.options.maxHeight + 'px');
83
- this.$drop.find('.multiple').css('width', this.options.multipleWidth + 'px');
84
-
85
- this.$searchInput = this.$drop.find('.ms-search input');
86
- this.$selectAll = this.$drop.find('input[name="selectAll"]');
87
- this.$selectGroups = this.$drop.find('label.optgroup');
88
- this.$selectItems = this.$drop.find('input[name="selectItem"]:enabled');
89
- this.$disableItems = this.$drop.find('input[name="selectItem"]:disabled');
90
- this.events();
91
- this.update();
92
- },
93
-
94
- optionToHtml: function(i, elm, group, groupDisabled) {
95
- var that = this,
96
- $elm = $(elm),
97
- html = [],
98
- multiple = this.options.multiple;
99
- if ($elm.is('option')) {
100
- var value = $elm.val(),
101
- text = $elm.text(),
102
- selected = $elm.prop('selected'),
103
- disabled = groupDisabled || $elm.prop('disabled');
104
- html.push(
105
- '<li' + (multiple ? ' class="multiple"' : '') + '>',
106
- '<label' + (disabled ? ' class="disabled"' : '') + '>',
107
- '<input type="checkbox" name="selectItem" value="' + value + '"' +
108
- (selected ? ' checked="checked"' : '') +
109
- (disabled ? ' disabled="disabled"' : '') +
110
- (group ? ' data-group="' + group + '"' : '') +
111
- '/> ',
112
- text,
113
- '</label>',
114
- '</li>'
115
- );
116
- } else if (!group && $elm.is('optgroup')) {
117
- var _group = 'group_' + i,
118
- label = $elm.attr('label'),
119
- disabled = $elm.prop('disabled');
120
- html.push(
121
- '<li class="group">',
122
- '<label class="optgroup' + (disabled ? ' disabled' : '') + '" data-group="' + _group + '">',
123
- label,
124
- '</label>',
125
- '</li>');
126
- $.each($elm.children(), function(i, elm) {
127
- html.push(that.optionToHtml(i, elm, _group, disabled));
128
- });
129
- }
130
- return html.join('');
131
- },
132
-
133
- events: function() {
134
- var that = this;
135
- this.$choice.off('click').on('click', function() {
136
- that[that.options.isopen ? 'close' : 'open']();
137
- });
138
- this.$searchInput.off('keyup').on('keyup', function() {
139
- that.filter();
140
- });
141
- this.$selectAll.off('click').on('click', function() {
142
- var checked = $(this).prop('checked'),
143
- $items = that.$selectItems.filter(':visible');
144
- if ($items.length === that.$selectItems.length) {
145
- that[checked ? 'checkAll' : 'uncheckAll']();
146
- } else { // when the filter option is true
147
- $items.prop('checked', checked);
148
- that.update();
149
- }
150
- });
151
- this.$selectGroups.off('click').on('click', function() {
152
- var group = $(this).attr('data-group'),
153
- $items = that.$selectItems.filter(':visible'),
154
- $children = $items.filter('[data-group="' + group + '"]'),
155
- checked = $children.length !== $children.filter(':checked').length;
156
- $children.prop('checked', checked);
157
- that.updateSelectAll();
158
- that.update();
159
- that.options.onOptgroupClick({
160
- label: $(this).text(),
161
- checked: checked,
162
- children: $children.get()
163
- });
164
- });
165
- this.$selectItems.off('click').on('click', function() {
166
- that.updateSelectAll();
167
- that.update();
168
- that.options.onClick({
169
- label: $(this).parent().text(),
170
- value: $(this).val(),
171
- checked: $(this).prop('checked')
172
- });
173
- });
174
- },
175
-
176
- open: function() {
177
- if (this.$choice.hasClass('disabled')) {
178
- return;
179
- }
180
- this.options.isopen = true;
181
- this.$choice.find('>div').addClass('open');
182
- this.$drop.show();
183
- if (this.options.filter) {
184
- this.$searchInput.val('');
185
- this.filter();
186
- }
187
- this.options.onOpen();
188
- },
189
-
190
- close: function() {
191
- this.options.isopen = false;
192
- this.$choice.find('>div').removeClass('open');
193
- this.$drop.hide();
194
- this.options.onClose();
195
- },
196
-
197
- update: function() {
198
- var selects = this.getSelects('text'),
199
- $span = this.$choice.find('>span');
200
- if (selects.length) {
201
- $span.removeClass('placeholder').html(selects.join(','));
202
- } else {
203
- $span.addClass('placeholder').html(this.options.placeholder);
204
- }
205
- // set selects to select
206
- this.$el.val(this.getSelects());
207
- },
208
-
209
- updateSelectAll: function() {
210
- var $items = this.$selectItems.filter(':visible');
211
- this.$selectAll.prop('checked', $items.length &&
212
- $items.length === $items.filter(':checked').length);
213
- },
214
-
215
- //value or text, default: 'value'
216
- getSelects: function(type) {
217
- var values = [];
218
- this.$drop.find('input[name="selectItem"]:checked').each(function() {
219
- values.push(type === 'text' ? $(this).parent().text() : $(this).val());
220
- });
221
- return values;
222
- },
223
-
224
- setSelects: function(values) {
225
- var that = this;
226
- this.$selectItems.prop('checked', false);
227
- $.each(values, function(i, value) {
228
- that.$selectItems.filter('[value="' + value + '"]').prop('checked', true);
229
- });
230
- this.$selectAll.prop('checked', this.$selectItems.length ===
231
- this.$selectItems.filter(':checked').length);
232
- this.update();
233
- },
234
-
235
- enable: function() {
236
- this.$choice.removeClass('disabled');
237
- },
238
-
239
- disable: function() {
240
- this.$choice.addClass('disabled');
241
- },
242
-
243
- checkAll: function() {
244
- this.$selectItems.prop('checked', true);
245
- this.$selectAll.prop('checked', true);
246
- this.update();
247
- this.options.onCheckAll();
248
- },
249
-
250
- uncheckAll: function() {
251
- this.$selectItems.prop('checked', false);
252
- this.$selectAll.prop('checked', false);
253
- this.update();
254
- this.options.onUncheckAll();
255
- },
256
-
257
- refresh: function() {
258
- this.init();
259
- },
260
-
261
- filter: function() {
262
- var that = this,
263
- text = $.trim(this.$searchInput.val()).toLowerCase();
264
- if (text.length === 0) {
265
- this.$selectItems.parent().show();
266
- this.$disableItems.parent().show();
267
- this.$selectGroups.show();
268
- } else {
269
- this.$selectItems.each(function() {
270
- var $parent = $(this).parent();
271
- $parent[$parent.text().toLowerCase().indexOf(text) < 0 ? 'hide' : 'show']();
272
- });
273
- this.$disableItems.parent().hide();
274
- this.$selectGroups.each(function() {
275
- var group = $(this).attr('data-group'),
276
- $items = that.$selectItems.filter(':visible');
277
- $(this)[$items.filter('[data-group="' + group + '"]').length === 0 ? 'hide' : 'show']();
278
- });
279
- }
280
- this.updateSelectAll();
281
- }
282
- };
283
-
284
- $.fn.multipleSelect = function() {
285
- var option = arguments[0],
286
- args = arguments,
287
-
288
- value,
289
- allowedMethods = ['getSelects', 'setSelects', 'enable', 'disable', 'checkAll', 'uncheckAll', 'refresh'];
290
-
291
- this.each(function() {
292
- var $this = $(this),
293
- data = $this.data('multipleSelect'),
294
- options = $.extend({}, $.fn.multipleSelect.defaults, typeof option === 'object' && option);
295
-
296
- if (!data) {
297
- data = new MultipleSelect($this, options);
298
- $this.data('multipleSelect', data);
299
- }
300
-
301
- if (typeof option === 'string') {
302
- if ($.inArray(option, allowedMethods) < 0) {
303
- throw "Unknown method: " + option;
304
- }
305
- value = data[option](args[1]);
306
- } else {
307
- data.init();
308
- }
309
- });
310
-
311
- return value ? value : this;
312
- };
313
-
314
- $.fn.multipleSelect.defaults = {
315
- isopen: false,
316
- placeholder: '',
317
- selectAll: true,
318
- selectAllText: 'Select all',
319
- multiple: false,
320
- multipleWidth: 80,
321
- filter: false,
322
- width: undefined,
323
- maxHeight: 250,
324
-
325
- onOpen: function() {return false;},
326
- onClose: function() {return false;},
327
- onCheckAll: function() {return false;},
328
- onUncheckAll: function() {return false;},
329
- onOptgroupClick: function() {return false;},
330
- onClick: function() {return false;}
331
- };
332
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/multiple-select.js ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author zhixin wen <wenzhixin2010@gmail.com>
3
+ * @version 1.2.1
4
+ *
5
+ * http://wenzhixin.net.cn/p/multiple-select/
6
+ */
7
+
8
+ (function ($) {
9
+
10
+ 'use strict';
11
+
12
+ // it only does '%s', and return '' when arguments are undefined
13
+ var sprintf = function (str) {
14
+ var args = arguments,
15
+ flag = true,
16
+ i = 1;
17
+
18
+ str = str.replace(/%s/g, function () {
19
+ var arg = args[i++];
20
+
21
+ if (typeof arg === 'undefined') {
22
+ flag = false;
23
+ return '';
24
+ }
25
+ return arg;
26
+ });
27
+ return flag ? str : '';
28
+ };
29
+
30
+ var removeDiacritics = function (str) {
31
+ var defaultDiacriticsRemovalMap = [
32
+ {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},
33
+ {'base':'AA','letters':/[\uA732]/g},
34
+ {'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g},
35
+ {'base':'AO','letters':/[\uA734]/g},
36
+ {'base':'AU','letters':/[\uA736]/g},
37
+ {'base':'AV','letters':/[\uA738\uA73A]/g},
38
+ {'base':'AY','letters':/[\uA73C]/g},
39
+ {'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g},
40
+ {'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g},
41
+ {'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g},
42
+ {'base':'DZ','letters':/[\u01F1\u01C4]/g},
43
+ {'base':'Dz','letters':/[\u01F2\u01C5]/g},
44
+ {'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g},
45
+ {'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g},
46
+ {'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g},
47
+ {'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g},
48
+ {'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g},
49
+ {'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g},
50
+ {'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g},
51
+ {'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g},
52
+ {'base':'LJ','letters':/[\u01C7]/g},
53
+ {'base':'Lj','letters':/[\u01C8]/g},
54
+ {'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g},
55
+ {'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g},
56
+ {'base':'NJ','letters':/[\u01CA]/g},
57
+ {'base':'Nj','letters':/[\u01CB]/g},
58
+ {'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g},
59
+ {'base':'OI','letters':/[\u01A2]/g},
60
+ {'base':'OO','letters':/[\uA74E]/g},
61
+ {'base':'OU','letters':/[\u0222]/g},
62
+ {'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g},
63
+ {'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g},
64
+ {'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g},
65
+ {'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g},
66
+ {'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g},
67
+ {'base':'TZ','letters':/[\uA728]/g},
68
+ {'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g},
69
+ {'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g},
70
+ {'base':'VY','letters':/[\uA760]/g},
71
+ {'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g},
72
+ {'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g},
73
+ {'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g},
74
+ {'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g},
75
+ {'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g},
76
+ {'base':'aa','letters':/[\uA733]/g},
77
+ {'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g},
78
+ {'base':'ao','letters':/[\uA735]/g},
79
+ {'base':'au','letters':/[\uA737]/g},
80
+ {'base':'av','letters':/[\uA739\uA73B]/g},
81
+ {'base':'ay','letters':/[\uA73D]/g},
82
+ {'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g},
83
+ {'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g},
84
+ {'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g},
85
+ {'base':'dz','letters':/[\u01F3\u01C6]/g},
86
+ {'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g},
87
+ {'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g},
88
+ {'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g},
89
+ {'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g},
90
+ {'base':'hv','letters':/[\u0195]/g},
91
+ {'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g},
92
+ {'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g},
93
+ {'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g},
94
+ {'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g},
95
+ {'base':'lj','letters':/[\u01C9]/g},
96
+ {'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g},
97
+ {'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g},
98
+ {'base':'nj','letters':/[\u01CC]/g},
99
+ {'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g},
100
+ {'base':'oi','letters':/[\u01A3]/g},
101
+ {'base':'ou','letters':/[\u0223]/g},
102
+ {'base':'oo','letters':/[\uA74F]/g},
103
+ {'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g},
104
+ {'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g},
105
+ {'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g},
106
+ {'base':'s','letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g},
107
+ {'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g},
108
+ {'base':'tz','letters':/[\uA729]/g},
109
+ {'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g},
110
+ {'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g},
111
+ {'base':'vy','letters':/[\uA761]/g},
112
+ {'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g},
113
+ {'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g},
114
+ {'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g},
115
+ {'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g}
116
+ ];
117
+
118
+ for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
119
+ str = str.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base);
120
+ }
121
+
122
+ return str;
123
+
124
+ };
125
+
126
+ function MultipleSelect($el, options) {
127
+ var that = this,
128
+ name = $el.attr('name') || options.name || '';
129
+
130
+ this.options = options;
131
+
132
+ // hide select element
133
+ this.$el = $el.hide();
134
+
135
+ // label element
136
+ this.$label = this.$el.closest('label');
137
+ if (this.$label.length === 0 && this.$el.attr('id')) {
138
+ this.$label = $(sprintf('label[for="%s"]', this.$el.attr('id').replace(/:/g, '\\:')));
139
+ }
140
+
141
+ // restore class and title from select element
142
+ this.$parent = $(sprintf(
143
+ '<div class="ms-parent %s" %s/>',
144
+ $el.attr('class') || '',
145
+ sprintf('title="%s"', $el.attr('title'))));
146
+
147
+ // add placeholder to choice button
148
+ this.$choice = $(sprintf([
149
+ '<button type="button" class="ms-choice">',
150
+ '<span class="placeholder">%s</span>',
151
+ '<div></div>',
152
+ '</button>'
153
+ ].join(''),
154
+ this.options.placeholder));
155
+
156
+ // default position is bottom
157
+ this.$drop = $(sprintf('<div class="ms-drop %s"%s></div>',
158
+ this.options.position,
159
+ sprintf(' style="width: %s"', this.options.dropWidth)));
160
+
161
+ this.$el.after(this.$parent);
162
+ this.$parent.append(this.$choice);
163
+ this.$parent.append(this.$drop);
164
+
165
+ if (this.$el.prop('disabled')) {
166
+ this.$choice.addClass('disabled');
167
+ }
168
+ this.$parent.css('width',
169
+ this.options.width ||
170
+ this.$el.css('width') ||
171
+ this.$el.outerWidth() + 20);
172
+
173
+ this.selectAllName = 'data-name="selectAll' + name + '"';
174
+ this.selectGroupName = 'data-name="selectGroup' + name + '"';
175
+ this.selectItemName = 'data-name="selectItem' + name + '"';
176
+
177
+ if (!this.options.keepOpen) {
178
+ $(document).click(function (e) {
179
+ if ($(e.target)[0] === that.$choice[0] ||
180
+ $(e.target).parents('.ms-choice')[0] === that.$choice[0]) {
181
+ return;
182
+ }
183
+ if (($(e.target)[0] === that.$drop[0] ||
184
+ $(e.target).parents('.ms-drop')[0] !== that.$drop[0] && e.target !== $el[0]) &&
185
+ that.options.isOpen) {
186
+ that.close();
187
+ }
188
+ });
189
+ }
190
+ }
191
+
192
+ MultipleSelect.prototype = {
193
+ constructor: MultipleSelect,
194
+
195
+ init: function () {
196
+ var that = this,
197
+ $ul = $('<ul></ul>');
198
+
199
+ this.$drop.html('');
200
+
201
+ if (this.options.filter) {
202
+ this.$drop.append([
203
+ '<div class="ms-search">',
204
+ '<input type="text" autocomplete="off" autocorrect="off" autocapitilize="off" spellcheck="false">',
205
+ '</div>'].join('')
206
+ );
207
+ }
208
+
209
+ if (this.options.selectAll && !this.options.single) {
210
+ $ul.append([
211
+ '<li class="ms-select-all">',
212
+ '<label>',
213
+ sprintf('<input type="checkbox" %s /> ', this.selectAllName),
214
+ this.options.selectAllDelimiter[0],
215
+ this.options.selectAllText,
216
+ this.options.selectAllDelimiter[1],
217
+ '</label>',
218
+ '</li>'
219
+ ].join(''));
220
+ }
221
+
222
+ $.each(this.$el.children(), function (i, elm) {
223
+ $ul.append(that.optionToHtml(i, elm));
224
+ });
225
+ $ul.append(sprintf('<li class="ms-no-results">%s</li>', this.options.noMatchesFound));
226
+ this.$drop.append($ul);
227
+
228
+ this.$drop.find('ul').css('max-height', this.options.maxHeight + 'px');
229
+ this.$drop.find('.multiple').css('width', this.options.multipleWidth + 'px');
230
+
231
+ this.$searchInput = this.$drop.find('.ms-search input');
232
+ this.$selectAll = this.$drop.find('input[' + this.selectAllName + ']');
233
+ this.$selectGroups = this.$drop.find('input[' + this.selectGroupName + ']');
234
+ this.$selectItems = this.$drop.find('input[' + this.selectItemName + ']:enabled');
235
+ this.$disableItems = this.$drop.find('input[' + this.selectItemName + ']:disabled');
236
+ this.$noResults = this.$drop.find('.ms-no-results');
237
+
238
+ this.events();
239
+ this.updateSelectAll(true);
240
+ this.update(true);
241
+
242
+ if (this.options.isOpen) {
243
+ this.open();
244
+ }
245
+ },
246
+
247
+ optionToHtml: function (i, elm, group, groupDisabled) {
248
+ var that = this,
249
+ $elm = $(elm),
250
+ classes = $elm.attr('class') || '',
251
+ title = sprintf('title="%s"', $elm.attr('title')),
252
+ multiple = this.options.multiple ? 'multiple' : '',
253
+ disabled,
254
+ type = this.options.single ? 'radio' : 'checkbox';
255
+
256
+ if ($elm.is('option')) {
257
+ var value = $elm.val(),
258
+ text = that.options.textTemplate($elm),
259
+ selected = $elm.prop('selected'),
260
+ style = sprintf('style="%s"', this.options.styler(value)),
261
+ $el;
262
+
263
+ disabled = groupDisabled || $elm.prop('disabled');
264
+
265
+ $el = $([
266
+ sprintf('<li class="%s %s" %s %s>', multiple, classes, title, style),
267
+ sprintf('<label class="%s">', disabled ? 'disabled' : ''),
268
+ sprintf('<input type="%s" %s%s%s%s>',
269
+ type, this.selectItemName,
270
+ selected ? ' checked="checked"' : '',
271
+ disabled ? ' disabled="disabled"' : '',
272
+ sprintf(' data-group="%s"', group)),
273
+ sprintf('<span>%s</span>', text),
274
+ '</label>',
275
+ '</li>'
276
+ ].join(''));
277
+ $el.find('input').val(value);
278
+ return $el;
279
+ }
280
+ if ($elm.is('optgroup')) {
281
+ var label = that.options.labelTemplate($elm),
282
+ $group = $('<div/>');
283
+
284
+ group = 'group_' + i;
285
+ disabled = $elm.prop('disabled');
286
+
287
+ $group.append([
288
+ '<li class="group">',
289
+ sprintf('<label class="optgroup %s" data-group="%s">', disabled ? 'disabled' : '', group),
290
+ this.options.hideOptgroupCheckboxes || this.options.single ? '' :
291
+ sprintf('<input type="checkbox" %s %s>',
292
+ this.selectGroupName, disabled ? 'disabled="disabled"' : ''),
293
+ label,
294
+ '</label>',
295
+ '</li>'
296
+ ].join(''));
297
+
298
+ $.each($elm.children(), function (i, elm) {
299
+ $group.append(that.optionToHtml(i, elm, group, disabled));
300
+ });
301
+ return $group.html();
302
+ }
303
+ },
304
+
305
+ events: function () {
306
+ var that = this,
307
+ toggleOpen = function (e) {
308
+ e.preventDefault();
309
+ that[that.options.isOpen ? 'close' : 'open']();
310
+ };
311
+
312
+ if (this.$label) {
313
+ this.$label.off('click').on('click', function (e) {
314
+ if (e.target.nodeName.toLowerCase() !== 'label' || e.target !== this) {
315
+ return;
316
+ }
317
+ toggleOpen(e);
318
+ if (!that.options.filter || !that.options.isOpen) {
319
+ that.focus();
320
+ }
321
+ e.stopPropagation(); // Causes lost focus otherwise
322
+ });
323
+ }
324
+
325
+ this.$choice.off('click').on('click', toggleOpen)
326
+ .off('focus').on('focus', this.options.onFocus)
327
+ .off('blur').on('blur', this.options.onBlur);
328
+
329
+ this.$parent.off('keydown').on('keydown', function (e) {
330
+ switch (e.which) {
331
+ case 27: // esc key
332
+ that.close();
333
+ that.$choice.focus();
334
+ break;
335
+ }
336
+ });
337
+
338
+ this.$searchInput.off('keydown').on('keydown',function (e) {
339
+ // Ensure shift-tab causes lost focus from filter as with clicking away
340
+ if (e.keyCode === 9 && e.shiftKey) {
341
+ that.close();
342
+ }
343
+ }).off('keyup').on('keyup', function (e) {
344
+ // enter or space
345
+ // Avoid selecting/deselecting if no choices made
346
+ if (that.options.filterAcceptOnEnter && (e.which === 13 || e.which == 32) && that.$searchInput.val()) {
347
+ that.$selectAll.click();
348
+ that.close();
349
+ that.focus();
350
+ return;
351
+ }
352
+ that.filter();
353
+ });
354
+
355
+ this.$selectAll.off('click').on('click', function () {
356
+ var checked = $(this).prop('checked'),
357
+ $items = that.$selectItems.filter(':visible');
358
+
359
+ if ($items.length === that.$selectItems.length) {
360
+ that[checked ? 'checkAll' : 'uncheckAll']();
361
+ } else { // when the filter option is true
362
+ that.$selectGroups.prop('checked', checked);
363
+ $items.prop('checked', checked);
364
+ that.options[checked ? 'onCheckAll' : 'onUncheckAll']();
365
+ that.update();
366
+ }
367
+ });
368
+ this.$selectGroups.off('click').on('click', function () {
369
+ var group = $(this).parent().attr('data-group'),
370
+ $items = that.$selectItems.filter(':visible'),
371
+ $children = $items.filter(sprintf('[data-group="%s"]', group)),
372
+ checked = $children.length !== $children.filter(':checked').length;
373
+
374
+ $children.prop('checked', checked);
375
+ that.updateSelectAll();
376
+ that.update();
377
+ that.options.onOptgroupClick({
378
+ label: $(this).parent().text(),
379
+ checked: checked,
380
+ children: $children.get(),
381
+ instance: that
382
+ });
383
+ });
384
+ this.$selectItems.off('click').on('click', function () {
385
+ that.updateSelectAll();
386
+ that.update();
387
+ that.updateOptGroupSelect();
388
+ that.options.onClick({
389
+ label: $(this).parent().text(),
390
+ value: $(this).val(),
391
+ checked: $(this).prop('checked'),
392
+ instance: that
393
+ });
394
+
395
+ if (that.options.single && that.options.isOpen && !that.options.keepOpen) {
396
+ that.close();
397
+ }
398
+
399
+ if (that.options.single) {
400
+ var clickedVal = $(this).val();
401
+ that.$selectItems.filter(function() {
402
+ return $(this).val() !== clickedVal;
403
+ }).each(function() {
404
+ $(this).prop('checked', false);
405
+ });
406
+ that.update();
407
+ }
408
+ });
409
+ },
410
+
411
+ open: function () {
412
+ if (this.$choice.hasClass('disabled')) {
413
+ return;
414
+ }
415
+ this.options.isOpen = true;
416
+ this.$choice.find('>div').addClass('open');
417
+ this.$drop[this.animateMethod('show')]();
418
+
419
+ // fix filter bug: no results show
420
+ this.$selectAll.parent().show();
421
+ this.$noResults.hide();
422
+
423
+ // Fix #77: 'All selected' when no options
424
+ if (!this.$el.children().length) {
425
+ this.$selectAll.parent().hide();
426
+ this.$noResults.show();
427
+ }
428
+
429
+ if (this.options.container) {
430
+ var offset = this.$drop.offset();
431
+ this.$drop.appendTo($(this.options.container));
432
+ this.$drop.offset({
433
+ top: offset.top,
434
+ left: offset.left
435
+ });
436
+ }
437
+
438
+ if (this.options.filter) {
439
+ this.$searchInput.val('');
440
+ this.$searchInput.focus();
441
+ this.filter();
442
+ }
443
+ this.options.onOpen();
444
+ },
445
+
446
+ close: function () {
447
+ this.options.isOpen = false;
448
+ this.$choice.find('>div').removeClass('open');
449
+ this.$drop[this.animateMethod('hide')]();
450
+ if (this.options.container) {
451
+ this.$parent.append(this.$drop);
452
+ this.$drop.css({
453
+ 'top': 'auto',
454
+ 'left': 'auto'
455
+ });
456
+ }
457
+ this.options.onClose();
458
+ },
459
+
460
+ animateMethod: function (method) {
461
+ var methods = {
462
+ show: {
463
+ fade: 'fadeIn',
464
+ slide: 'slideDown'
465
+ },
466
+ hide: {
467
+ fade: 'fadeOut',
468
+ slide: 'slideUp'
469
+ }
470
+ };
471
+
472
+ return methods[method][this.options.animate] || method;
473
+ },
474
+
475
+ update: function (isInit) {
476
+ var selects = this.options.displayValues ? this.getSelects() : this.getSelects('text'),
477
+ $span = this.$choice.find('>span'),
478
+ sl = selects.length;
479
+
480
+ if (sl === 0) {
481
+ $span.addClass('placeholder').html(this.options.placeholder);
482
+ } else if (this.options.allSelected && sl === this.$selectItems.length + this.$disableItems.length) {
483
+ $span.removeClass('placeholder').html(this.options.allSelected);
484
+ } else if (this.options.ellipsis && sl > this.options.minimumCountSelected) {
485
+ $span.removeClass('placeholder').text(selects.slice(0, this.options.minimumCountSelected)
486
+ .join(this.options.delimiter) + '...');
487
+ } else if (this.options.countSelected && sl > this.options.minimumCountSelected) {
488
+ $span.removeClass('placeholder').html(this.options.countSelected
489
+ .replace('#', selects.length)
490
+ .replace('%', this.$selectItems.length + this.$disableItems.length));
491
+ } else {
492
+ $span.removeClass('placeholder').text(selects.join(this.options.delimiter));
493
+ }
494
+
495
+ if (this.options.addTitle) {
496
+ $span.prop('title', this.getSelects('text'));
497
+ }
498
+
499
+ // set selects to select
500
+ this.$el.val(this.getSelects()).trigger('change');
501
+
502
+ // add selected class to selected li
503
+ this.$drop.find('li').removeClass('selected');
504
+ this.$drop.find('input:checked').each(function () {
505
+ $(this).parents('li').first().addClass('selected');
506
+ });
507
+
508
+ // trigger <select> change event
509
+ if (!isInit) {
510
+ this.$el.trigger('change');
511
+ }
512
+ },
513
+
514
+ updateSelectAll: function (isInit) {
515
+ var $items = this.$selectItems;
516
+
517
+ if (!isInit) {
518
+ $items = $items.filter(':visible');
519
+ }
520
+ this.$selectAll.prop('checked', $items.length &&
521
+ $items.length === $items.filter(':checked').length);
522
+ if (!isInit && this.$selectAll.prop('checked')) {
523
+ this.options.onCheckAll();
524
+ }
525
+ },
526
+
527
+ updateOptGroupSelect: function () {
528
+ var $items = this.$selectItems.filter(':visible');
529
+ $.each(this.$selectGroups, function (i, val) {
530
+ var group = $(val).parent().attr('data-group'),
531
+ $children = $items.filter(sprintf('[data-group="%s"]', group));
532
+ $(val).prop('checked', $children.length &&
533
+ $children.length === $children.filter(':checked').length);
534
+ });
535
+ },
536
+
537
+ //value or text, default: 'value'
538
+ getSelects: function (type) {
539
+ var that = this,
540
+ texts = [],
541
+ values = [];
542
+ this.$drop.find(sprintf('input[%s]:checked', this.selectItemName)).each(function () {
543
+ texts.push($(this).parents('li').first().text());
544
+ values.push($(this).val());
545
+ });
546
+
547
+ if (type === 'text' && this.$selectGroups.length) {
548
+ texts = [];
549
+ this.$selectGroups.each(function () {
550
+ var html = [],
551
+ text = $.trim($(this).parent().text()),
552
+ group = $(this).parent().data('group'),
553
+ $children = that.$drop.find(sprintf('[%s][data-group="%s"]', that.selectItemName, group)),
554
+ $selected = $children.filter(':checked');
555
+
556
+ if (!$selected.length) {
557
+ return;
558
+ }
559
+
560
+ html.push('[');
561
+ html.push(text);
562
+ if ($children.length > $selected.length) {
563
+ var list = [];
564
+ $selected.each(function () {
565
+ list.push($(this).parent().text());
566
+ });
567
+ html.push(': ' + list.join(', '));
568
+ }
569
+ html.push(']');
570
+ texts.push(html.join(''));
571
+ });
572
+ }
573
+ return type === 'text' ? texts : values;
574
+ },
575
+
576
+ setSelects: function (values) {
577
+ var that = this;
578
+ this.$selectItems.prop('checked', false);
579
+ this.$disableItems.prop('checked', false);
580
+ $.each(values, function (i, value) {
581
+ that.$selectItems.filter(sprintf('[value="%s"]', value)).prop('checked', true);
582
+ that.$disableItems.filter(sprintf('[value="%s"]', value)).prop('checked', true);
583
+ });
584
+ this.$selectAll.prop('checked', this.$selectItems.length ===
585
+ this.$selectItems.filter(':checked').length + this.$disableItems.filter(':checked').length);
586
+
587
+ $.each(that.$selectGroups, function (i, val) {
588
+ var group = $(val).parent().attr('data-group'),
589
+ $children = that.$selectItems.filter('[data-group="' + group + '"]');
590
+ $(val).prop('checked', $children.length &&
591
+ $children.length === $children.filter(':checked').length);
592
+ });
593
+
594
+ this.update();
595
+ },
596
+
597
+ enable: function () {
598
+ this.$choice.removeClass('disabled');
599
+ },
600
+
601
+ disable: function () {
602
+ this.$choice.addClass('disabled');
603
+ },
604
+
605
+ checkAll: function () {
606
+ this.$selectItems.prop('checked', true);
607
+ this.$selectGroups.prop('checked', true);
608
+ this.$selectAll.prop('checked', true);
609
+ this.update();
610
+ this.options.onCheckAll();
611
+ },
612
+
613
+ uncheckAll: function () {
614
+ this.$selectItems.prop('checked', false);
615
+ this.$selectGroups.prop('checked', false);
616
+ this.$selectAll.prop('checked', false);
617
+ this.update();
618
+ this.options.onUncheckAll();
619
+ },
620
+
621
+ focus: function () {
622
+ this.$choice.focus();
623
+ this.options.onFocus();
624
+ },
625
+
626
+ blur: function () {
627
+ this.$choice.blur();
628
+ this.options.onBlur();
629
+ },
630
+
631
+ refresh: function () {
632
+ this.init();
633
+ },
634
+
635
+ filter: function () {
636
+ var that = this,
637
+ text = $.trim(this.$searchInput.val()).toLowerCase();
638
+
639
+ if (text.length === 0) {
640
+ this.$selectAll.parent().show();
641
+ this.$selectItems.parent().show();
642
+ this.$disableItems.parent().show();
643
+ this.$selectGroups.parent().show();
644
+ this.$noResults.hide();
645
+ } else {
646
+ this.$selectItems.each(function () {
647
+ var $parent = $(this).parent();
648
+ $parent[removeDiacritics($parent.text().toLowerCase()).indexOf(removeDiacritics(text)) < 0 ? 'hide' : 'show']();
649
+ });
650
+ this.$disableItems.parent().hide();
651
+ this.$selectGroups.each(function () {
652
+ var $parent = $(this).parent();
653
+ var group = $parent.attr('data-group'),
654
+ $items = that.$selectItems.filter(':visible');
655
+ $parent[$items.filter(sprintf('[data-group="%s"]', group)).length ? 'show' : 'hide']();
656
+ });
657
+
658
+ //Check if no matches found
659
+ if (this.$selectItems.parent().filter(':visible').length) {
660
+ this.$selectAll.parent().show();
661
+ this.$noResults.hide();
662
+ } else {
663
+ this.$selectAll.parent().hide();
664
+ this.$noResults.show();
665
+ }
666
+ }
667
+ this.updateOptGroupSelect();
668
+ this.updateSelectAll();
669
+ this.options.onFilter(text);
670
+ }
671
+ };
672
+
673
+ $.fn.multipleSelect = function () {
674
+ var option = arguments[0],
675
+ args = arguments,
676
+
677
+ value,
678
+ allowedMethods = [
679
+ 'getSelects', 'setSelects',
680
+ 'enable', 'disable',
681
+ 'open', 'close',
682
+ 'checkAll', 'uncheckAll',
683
+ 'focus', 'blur',
684
+ 'refresh', 'close'
685
+ ];
686
+
687
+ this.each(function () {
688
+ var $this = $(this),
689
+ data = $this.data('multipleSelect'),
690
+ options = $.extend({}, $.fn.multipleSelect.defaults,
691
+ $this.data(), typeof option === 'object' && option);
692
+
693
+ if (!data) {
694
+ data = new MultipleSelect($this, options);
695
+ $this.data('multipleSelect', data);
696
+ }
697
+
698
+ if (typeof option === 'string') {
699
+ if ($.inArray(option, allowedMethods) < 0) {
700
+ throw 'Unknown method: ' + option;
701
+ }
702
+ value = data[option](args[1]);
703
+ } else {
704
+ data.init();
705
+ if (args[1]) {
706
+ value = data[args[1]].apply(data, [].slice.call(args, 2));
707
+ }
708
+ }
709
+ });
710
+
711
+ return typeof value !== 'undefined' ? value : this;
712
+ };
713
+
714
+ $.fn.multipleSelect.defaults = {
715
+ name: '',
716
+ isOpen: false,
717
+ placeholder: '',
718
+ selectAll: true,
719
+ selectAllDelimiter: ['[', ']'],
720
+ minimumCountSelected: 3,
721
+ ellipsis: false,
722
+ multiple: false,
723
+ multipleWidth: 80,
724
+ single: false,
725
+ filter: false,
726
+ width: undefined,
727
+ dropWidth: undefined,
728
+ maxHeight: 250,
729
+ container: null,
730
+ position: 'bottom',
731
+ keepOpen: false,
732
+ animate: 'none', // 'none', 'fade', 'slide'
733
+ displayValues: false,
734
+ delimiter: ', ',
735
+ addTitle: false,
736
+ filterAcceptOnEnter: false,
737
+ hideOptgroupCheckboxes: false,
738
+
739
+ selectAllText: 'Select all',
740
+ allSelected: 'All selected',
741
+ countSelected: '# of % selected',
742
+ noMatchesFound: 'No matches found',
743
+
744
+ styler: function () {
745
+ return false;
746
+ },
747
+ textTemplate: function ($elm) {
748
+ return $elm.html();
749
+ },
750
+ labelTemplate: function ($elm) {
751
+ return $elm.attr('label');
752
+ },
753
+
754
+ onOpen: function () {
755
+ return false;
756
+ },
757
+ onClose: function () {
758
+ return false;
759
+ },
760
+ onCheckAll: function () {
761
+ return false;
762
+ },
763
+ onUncheckAll: function () {
764
+ return false;
765
+ },
766
+ onFocus: function () {
767
+ return false;
768
+ },
769
+ onBlur: function () {
770
+ return false;
771
+ },
772
+ onOptgroupClick: function () {
773
+ return false;
774
+ },
775
+ onClick: function () {
776
+ return false;
777
+ },
778
+ onFilter: function () {
779
+ return false;
780
+ }
781
+ };
782
+ })(jQuery);
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=vladi
4
  Tags: user, role, editor, security, access, permission, capability
5
  Requires at least: 4.0
6
  Tested up to: 4.9
7
- Stable tag: 4.37
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -79,6 +79,11 @@ https://translate.wordpress.org/projects/wp-plugins/user-role-editor/
79
 
80
  == Changelog =
81
 
 
 
 
 
 
82
  = [4.37] 01.11.2017 =
83
  * New: New option was added to the "Settings->User Role Editor->General" tab. It's possible to set a default value for a quant of columns at capabilities section of the main User Role Editor page.
84
  * Update: User capabilities are shown for custom post types which use the WordPress built-in 'post' or 'page' capability type. (0/0) was shown earlier instead of the quantity of user capabilities really used.
@@ -160,3 +165,9 @@ For full list of changes applied to User Role Editor plugin look changelog.txt f
160
  You can find more information about "User Role Editor" plugin at [this page](http://www.shinephp.com/user-role-editor-wordpress-plugin/)
161
 
162
  I am ready to answer on your questions about plugin usage. Use [plugin page comments](http://www.shinephp.com/user-role-editor-wordpress-plugin/) for that.
 
 
 
 
 
 
4
  Tags: user, role, editor, security, access, permission, capability
5
  Requires at least: 4.0
6
  Tested up to: 4.9
7
+ Stable tag: 4.38
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
79
 
80
  == Changelog =
81
 
82
+ = [4.38] 27.11.2017 =
83
+ * Security: XSS vulnerability was fixed at URE's options page. Bug was discovered and fixed at tab index value numeric type checking. Tab index value is additionally escaped before output also.
84
+ * Security: Deprecated code for debug output to the .log file in case of database query error was removed.
85
+ * Security: Multiple select jQuery plugin (https://github.com/wenzhixin/multiple-select/) was updated to the latest available version 1.2.1, which fixed XSS vulnerability, existed in earlier versions.
86
+
87
  = [4.37] 01.11.2017 =
88
  * New: New option was added to the "Settings->User Role Editor->General" tab. It's possible to set a default value for a quant of columns at capabilities section of the main User Role Editor page.
89
  * Update: User capabilities are shown for custom post types which use the WordPress built-in 'post' or 'page' capability type. (0/0) was shown earlier instead of the quantity of user capabilities really used.
165
  You can find more information about "User Role Editor" plugin at [this page](http://www.shinephp.com/user-role-editor-wordpress-plugin/)
166
 
167
  I am ready to answer on your questions about plugin usage. Use [plugin page comments](http://www.shinephp.com/user-role-editor-wordpress-plugin/) for that.
168
+
169
+ == Upgrade Notice ==
170
+ = [4.38] 27.11.2017 =
171
+ * Security: XSS vulnerability was fixed at URE's options page. Bug was discovered and fixed at tab index value numeric type checking. Tab index value is additionally escaped before output also.
172
+ * Security: Deprecated code for debug output to the .log file in case of database query error was removed.
173
+ * Security: Multiple select jQuery plugin (https://github.com/wenzhixin/multiple-select/) was updated to the latest available version 1.2.1, which fixed XSS vulnerability, existed in earlier versions.
user-role-editor.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: User Role Editor
4
  Plugin URI: https://www.role-editor.com
5
  Description: Change/add/delete WordPress user roles and capabilities.
6
- Version: 4.37
7
  Author: Vladimir Garagulya
8
  Author URI: https://www.role-editor.com
9
  Text Domain: user-role-editor
@@ -23,7 +23,7 @@ if (defined('URE_PLUGIN_URL')) {
23
  wp_die('It seems that other version of User Role Editor is active. Please deactivate it before use this version');
24
  }
25
 
26
- define('URE_VERSION', '4.37');
27
  define('URE_PLUGIN_URL', plugin_dir_url(__FILE__));
28
  define('URE_PLUGIN_DIR', plugin_dir_path(__FILE__));
29
  define('URE_PLUGIN_BASE_NAME', plugin_basename(__FILE__));
3
  Plugin Name: User Role Editor
4
  Plugin URI: https://www.role-editor.com
5
  Description: Change/add/delete WordPress user roles and capabilities.
6
+ Version: 4.38
7
  Author: Vladimir Garagulya
8
  Author URI: https://www.role-editor.com
9
  Text Domain: user-role-editor
23
  wp_die('It seems that other version of User Role Editor is active. Please deactivate it before use this version');
24
  }
25
 
26
+ define('URE_VERSION', '4.38');
27
  define('URE_PLUGIN_URL', plugin_dir_url(__FILE__));
28
  define('URE_PLUGIN_DIR', plugin_dir_path(__FILE__));
29
  define('URE_PLUGIN_BASE_NAME', plugin_basename(__FILE__));