Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
bootstrap-multiselect
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
bootstrap-multiselect
Commits
8603bd73
You need to sign in or sign up before continuing.
Commit
8603bd73
authored
Jul 19, 2016
by
Daniel Fuzari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Teste cache component
parent
0fe46d3e
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
0 additions
and
1630 deletions
+0
-1630
bootstrap-multiselect.js
dist/js/bootstrap-multiselect.js
+0
-1630
No files found.
dist/js/bootstrap-multiselect.js
View file @
8603bd73
/**
* Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect)
*
* Apache License, Version 2.0:
* Copyright (c) 2012 - 2015 David Stutz
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* BSD 3-Clause License:
* Copyright (c) 2012 - 2015 David Stutz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of David Stutz nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
!
function
(
$
)
{
"
use strict
"
;
// jshint ;_;
if
(
typeof
ko
!==
'
undefined
'
&&
ko
.
bindingHandlers
&&
!
ko
.
bindingHandlers
.
multiselect
)
{
ko
.
bindingHandlers
.
multiselect
=
{
after
:
[
'
options
'
,
'
value
'
,
'
selectedOptions
'
,
'
enable
'
,
'
disable
'
],
init
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
var
$element
=
$
(
element
);
var
config
=
ko
.
toJS
(
valueAccessor
());
$element
.
multiselect
(
config
);
if
(
allBindings
.
has
(
'
options
'
))
{
var
options
=
allBindings
.
get
(
'
options
'
);
if
(
ko
.
isObservable
(
options
))
{
ko
.
computed
({
read
:
function
()
{
options
();
setTimeout
(
function
()
{
var
ms
=
$element
.
data
(
'
multiselect
'
);
if
(
ms
)
ms
.
updateOriginalOptions
();
//Not sure how beneficial this is.
$element
.
multiselect
(
'
rebuild
'
);
},
1
);
},
disposeWhenNodeIsRemoved
:
element
});
}
}
//value and selectedOptions are two-way, so these will be triggered even by our own actions.
//It needs some way to tell if they are triggered because of us or because of outside change.
//It doesn't loop but it's a waste of processing.
if
(
allBindings
.
has
(
'
value
'
))
{
var
value
=
allBindings
.
get
(
'
value
'
);
if
(
ko
.
isObservable
(
value
))
{
ko
.
computed
({
read
:
function
()
{
value
();
setTimeout
(
function
()
{
$element
.
multiselect
(
'
refresh
'
);
},
1
);
},
disposeWhenNodeIsRemoved
:
element
}).
extend
({
rateLimit
:
100
,
notifyWhenChangesStop
:
true
});
}
}
//Switched from arrayChange subscription to general subscription using 'refresh'.
//Not sure performance is any better using 'select' and 'deselect'.
if
(
allBindings
.
has
(
'
selectedOptions
'
))
{
var
selectedOptions
=
allBindings
.
get
(
'
selectedOptions
'
);
if
(
ko
.
isObservable
(
selectedOptions
))
{
ko
.
computed
({
read
:
function
()
{
selectedOptions
();
setTimeout
(
function
()
{
$element
.
multiselect
(
'
refresh
'
);
},
1
);
},
disposeWhenNodeIsRemoved
:
element
}).
extend
({
rateLimit
:
100
,
notifyWhenChangesStop
:
true
});
}
}
var
setEnabled
=
function
(
enable
)
{
setTimeout
(
function
()
{
if
(
enable
)
$element
.
multiselect
(
'
enable
'
);
else
$element
.
multiselect
(
'
disable
'
);
});
};
if
(
allBindings
.
has
(
'
enable
'
))
{
var
enable
=
allBindings
.
get
(
'
enable
'
);
if
(
ko
.
isObservable
(
enable
))
{
ko
.
computed
({
read
:
function
()
{
setEnabled
(
enable
());
},
disposeWhenNodeIsRemoved
:
element
}).
extend
({
rateLimit
:
100
,
notifyWhenChangesStop
:
true
});
}
else
{
setEnabled
(
enable
);
}
}
if
(
allBindings
.
has
(
'
disable
'
))
{
var
disable
=
allBindings
.
get
(
'
disable
'
);
if
(
ko
.
isObservable
(
disable
))
{
ko
.
computed
({
read
:
function
()
{
setEnabled
(
!
disable
());
},
disposeWhenNodeIsRemoved
:
element
}).
extend
({
rateLimit
:
100
,
notifyWhenChangesStop
:
true
});
}
else
{
setEnabled
(
!
disable
);
}
}
ko
.
utils
.
domNodeDisposal
.
addDisposeCallback
(
element
,
function
()
{
$element
.
multiselect
(
'
destroy
'
);
});
},
update
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
var
$element
=
$
(
element
);
var
config
=
ko
.
toJS
(
valueAccessor
());
$element
.
multiselect
(
'
setOptions
'
,
config
);
$element
.
multiselect
(
'
rebuild
'
);
}
};
}
function
forEach
(
array
,
callback
)
{
for
(
var
index
=
0
;
index
<
array
.
length
;
++
index
)
{
callback
(
array
[
index
],
index
);
}
}
/**
* Constructor to create a new multiselect using the given select.
*
* @param {jQuery} select
* @param {Object} options
* @returns {Multiselect}
*/
function
Multiselect
(
select
,
options
)
{
this
.
$select
=
$
(
select
);
// Placeholder via data attributes
if
(
this
.
$select
.
attr
(
"
data-placeholder
"
))
{
options
.
nonSelectedText
=
this
.
$select
.
data
(
"
placeholder
"
);
}
this
.
options
=
this
.
mergeOptions
(
$
.
extend
({},
options
,
this
.
$select
.
data
()));
// Initialization.
// We have to clone to create a new reference.
this
.
originalOptions
=
this
.
$select
.
clone
()[
0
].
options
;
this
.
query
=
''
;
this
.
searchTimeout
=
null
;
this
.
lastToggledInput
=
null
;
this
.
options
.
multiple
=
this
.
$select
.
attr
(
'
multiple
'
)
===
"
multiple
"
;
this
.
options
.
onChange
=
$
.
proxy
(
this
.
options
.
onChange
,
this
);
this
.
options
.
onDropdownShow
=
$
.
proxy
(
this
.
options
.
onDropdownShow
,
this
);
this
.
options
.
onDropdownHide
=
$
.
proxy
(
this
.
options
.
onDropdownHide
,
this
);
this
.
options
.
onDropdownShown
=
$
.
proxy
(
this
.
options
.
onDropdownShown
,
this
);
this
.
options
.
onDropdownHidden
=
$
.
proxy
(
this
.
options
.
onDropdownHidden
,
this
);
this
.
options
.
onInitialized
=
$
.
proxy
(
this
.
options
.
onInitialized
,
this
);
// Build select all if enabled.
this
.
buildContainer
();
this
.
buildButton
();
this
.
buildDropdown
();
this
.
buildSelectAll
();
this
.
buildDropdownOptions
();
this
.
buildFilter
();
this
.
updateButtonText
();
this
.
updateSelectAll
(
true
);
if
(
this
.
options
.
disableIfEmpty
&&
$
(
'
option
'
,
this
.
$select
).
length
<=
0
)
{
this
.
disable
();
}
this
.
$select
.
hide
().
after
(
this
.
$container
);
this
.
options
.
onInitialized
(
this
.
$select
,
this
.
$container
);
}
Multiselect
.
prototype
=
{
defaults
:
{
/**
* Default text function will either print 'Todos' in case no
* option is selected or a list of the selected options up to a length
* of 3 selected options.
*
* @param {jQuery} options
* @param {jQuery} select
* @returns {String}
*/
buttonText
:
function
(
options
,
select
)
{
if
(
this
.
disabledText
.
length
>
0
&&
(
this
.
disableIfEmpty
||
select
.
prop
(
'
disabled
'
))
&&
options
.
length
==
0
)
{
return
this
.
disabledText
;
}
else
if
(
options
.
length
===
0
)
{
return
this
.
nonSelectedText
;
}
else
if
(
this
.
allSelectedText
&&
options
.
length
===
$
(
'
option
'
,
$
(
select
)).
length
&&
$
(
'
option
'
,
$
(
select
)).
length
!==
1
&&
this
.
multiple
)
{
if
(
this
.
selectAllNumber
)
{
return
this
.
allSelectedText
+
'
(
'
+
options
.
length
+
'
)
'
;
}
else
{
return
this
.
allSelectedText
;
}
}
else
if
(
options
.
length
>
this
.
numberDisplayed
)
{
return
options
.
length
+
'
'
+
this
.
nSelectedText
;
}
else
{
var
selected
=
''
;
var
delimiter
=
this
.
delimiterText
;
options
.
each
(
function
()
{
var
label
=
(
$
(
this
).
attr
(
'
label
'
)
!==
undefined
)
?
$
(
this
).
attr
(
'
label
'
)
:
$
(
this
).
text
();
selected
+=
label
+
delimiter
;
});
return
selected
.
substr
(
0
,
selected
.
length
-
2
);
}
},
/**
* Updates the title of the button similar to the buttonText function.
*
* @param {jQuery} options
* @param {jQuery} select
* @returns {@exp;selected@call;substr}
*/
buttonTitle
:
function
(
options
,
select
)
{
if
(
options
.
length
===
0
)
{
return
this
.
nonSelectedText
;
}
else
{
var
selected
=
''
;
var
delimiter
=
this
.
delimiterText
;
options
.
each
(
function
()
{
var
label
=
(
$
(
this
).
attr
(
'
label
'
)
!==
undefined
)
?
$
(
this
).
attr
(
'
label
'
)
:
$
(
this
).
text
();
selected
+=
label
+
delimiter
;
});
return
selected
.
substr
(
0
,
selected
.
length
-
2
);
}
},
/**
* Create a label.
*
* @param {jQuery} element
* @returns {String}
*/
optionLabel
:
function
(
element
){
return
$
(
element
).
attr
(
'
label
'
)
||
$
(
element
).
text
();
},
/**
* Create a class.
*
* @param {jQuery} element
* @returns {String}
*/
optionClass
:
function
(
element
)
{
return
$
(
element
).
attr
(
'
class
'
)
||
''
;
},
/**
* Triggered on change of the multiselect.
*
* Not triggered when selecting/deselecting options manually.
*
* @param {jQuery} option
* @param {Boolean} checked
*/
onChange
:
function
(
option
,
checked
)
{
},
/**
* Triggered when the dropdown is shown.
*
* @param {jQuery} event
*/
onDropdownShow
:
function
(
event
)
{
},
/**
* Triggered when the dropdown is hidden.
*
* @param {jQuery} event
*/
onDropdownHide
:
function
(
event
)
{
},
/**
* Triggered after the dropdown is shown.
*
* @param {jQuery} event
*/
onDropdownShown
:
function
(
event
)
{
},
/**
* Triggered after the dropdown is hidden.
*
* @param {jQuery} event
*/
onDropdownHidden
:
function
(
event
)
{
},
/**
* Triggered on select all.
*/
onSelectAll
:
function
(
checked
)
{
},
/**
* Triggered after initializing.
*
* @param {jQuery} $select
* @param {jQuery} $container
*/
onInitialized
:
function
(
$select
,
$container
)
{
},
enableHTML
:
false
,
buttonClass
:
'
btn btn-default
'
,
inheritClass
:
false
,
buttonWidth
:
'
auto
'
,
buttonContainer
:
'
<div class="btn-group" />
'
,
dropRight
:
false
,
dropUp
:
false
,
selectedClass
:
'
active
'
,
// Maximum height of the dropdown menu.
// If maximum height is exceeded a scrollbar will be displayed.
maxHeight
:
false
,
checkboxName
:
false
,
includeSelectAllOption
:
false
,
includeSelectAllIfMoreThan
:
0
,
selectAllText
:
'
Selecionar todos
'
,
selectAllValue
:
'
multiselect-all
'
,
selectAllName
:
false
,
selectAllNumber
:
true
,
selectAllJustVisible
:
true
,
enableFiltering
:
false
,
enableCaseInsensitiveFiltering
:
false
,
enableFullValueFiltering
:
false
,
enableClickableOptGroups
:
false
,
enableCollapsibelOptGroups
:
false
,
filterPlaceholder
:
'
Pesquisar
'
,
// possible options: 'text', 'value', 'both'
filterBehavior
:
'
text
'
,
includeFilterClearBtn
:
true
,
preventInputChangeEvent
:
false
,
nonSelectedText
:
'
Todos
'
,
nSelectedText
:
'
selecionado(s)
'
,
allSelectedText
:
'
Todos selecionados
'
,
numberDisplayed
:
3
,
disableIfEmpty
:
false
,
disabledText
:
''
,
delimiterText
:
'
,
'
,
templates
:
{
button
:
'
<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>
'
,
ul
:
'
<ul class="multiselect-container dropdown-menu"></ul>
'
,
filter
:
'
<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>
'
,
filterClearBtn
:
'
<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>
'
,
li
:
'
<li><a tabindex="0"><label></label></a></li>
'
,
divider
:
'
<li class="multiselect-item divider"></li>
'
,
liGroup
:
'
<li class="multiselect-item multiselect-group"><label></label></li>
'
}
},
constructor
:
Multiselect
,
/**
* Builds the container of the multiselect.
*/
buildContainer
:
function
()
{
this
.
$container
=
$
(
this
.
options
.
buttonContainer
);
this
.
$container
.
on
(
'
show.bs.dropdown
'
,
this
.
options
.
onDropdownShow
);
this
.
$container
.
on
(
'
hide.bs.dropdown
'
,
this
.
options
.
onDropdownHide
);
this
.
$container
.
on
(
'
shown.bs.dropdown
'
,
this
.
options
.
onDropdownShown
);
this
.
$container
.
on
(
'
hidden.bs.dropdown
'
,
this
.
options
.
onDropdownHidden
);
},
/**
* Builds the button of the multiselect.
*/
buildButton
:
function
()
{
this
.
$button
=
$
(
this
.
options
.
templates
.
button
).
addClass
(
this
.
options
.
buttonClass
);
if
(
this
.
$select
.
attr
(
'
class
'
)
&&
this
.
options
.
inheritClass
)
{
this
.
$button
.
addClass
(
this
.
$select
.
attr
(
'
class
'
));
}
// Adopt active state.
if
(
this
.
$select
.
prop
(
'
disabled
'
))
{
this
.
disable
();
}
else
{
this
.
enable
();
}
// Manually add button width if set.
if
(
this
.
options
.
buttonWidth
&&
this
.
options
.
buttonWidth
!==
'
auto
'
)
{
this
.
$button
.
css
({
'
width
'
:
this
.
options
.
buttonWidth
,
'
overflow
'
:
'
hidden
'
,
'
text-overflow
'
:
'
ellipsis
'
});
this
.
$container
.
css
({
'
width
'
:
this
.
options
.
buttonWidth
});
}
// Keep the tab index from the select.
var
tabindex
=
this
.
$select
.
attr
(
'
tabindex
'
);
if
(
tabindex
)
{
this
.
$button
.
attr
(
'
tabindex
'
,
tabindex
);
}
this
.
$container
.
prepend
(
this
.
$button
);
},
/**
* Builds the ul representing the dropdown menu.
*/
buildDropdown
:
function
()
{
// Build ul.
this
.
$ul
=
$
(
this
.
options
.
templates
.
ul
);
if
(
this
.
options
.
dropRight
)
{
this
.
$ul
.
addClass
(
'
pull-right
'
);
}
// Set max height of dropdown menu to activate auto scrollbar.
if
(
this
.
options
.
maxHeight
)
{
// TODO: Add a class for this option to move the css declarations.
this
.
$ul
.
css
({
'
max-height
'
:
this
.
options
.
maxHeight
+
'
px
'
,
'
overflow-y
'
:
'
auto
'
,
'
overflow-x
'
:
'
hidden
'
});
}
if
(
this
.
options
.
dropUp
)
{
var
height
=
Math
.
min
(
this
.
options
.
maxHeight
,
$
(
'
option[data-role!="divider"]
'
,
this
.
$select
).
length
*
26
+
$
(
'
option[data-role="divider"]
'
,
this
.
$select
).
length
*
19
+
(
this
.
options
.
includeSelectAllOption
?
26
:
0
)
+
(
this
.
options
.
enableFiltering
||
this
.
options
.
enableCaseInsensitiveFiltering
?
44
:
0
));
var
moveCalc
=
height
+
34
;
this
.
$ul
.
css
({
'
max-height
'
:
height
+
'
px
'
,
'
overflow-y
'
:
'
auto
'
,
'
overflow-x
'
:
'
hidden
'
,
'
margin-top
'
:
"
-
"
+
moveCalc
+
'
px
'
});
}
this
.
$container
.
append
(
this
.
$ul
);
},
/**
* Build the dropdown options and binds all nessecary events.
*
* Uses createDivider and createOptionValue to create the necessary options.
*/
buildDropdownOptions
:
function
()
{
this
.
$select
.
children
().
each
(
$
.
proxy
(
function
(
index
,
element
)
{
var
$element
=
$
(
element
);
// Support optgroups and options without a group simultaneously.
var
tag
=
$element
.
prop
(
'
tagName
'
)
.
toLowerCase
();
if
(
$element
.
prop
(
'
value
'
)
===
this
.
options
.
selectAllValue
)
{
return
;
}
if
(
tag
===
'
optgroup
'
)
{
this
.
createOptgroup
(
element
);
}
else
if
(
tag
===
'
option
'
)
{
if
(
$element
.
data
(
'
role
'
)
===
'
divider
'
)
{
this
.
createDivider
();
}
else
{
this
.
createOptionValue
(
element
);
}
}
// Other illegal tags will be ignored.
},
this
));
// Bind the change event on the dropdown elements.
$
(
'
li input
'
,
this
.
$ul
).
on
(
'
change
'
,
$
.
proxy
(
function
(
event
)
{
var
$target
=
$
(
event
.
target
);
var
checked
=
$target
.
prop
(
'
checked
'
)
||
false
;
var
isSelectAllOption
=
$target
.
val
()
===
this
.
options
.
selectAllValue
;
// Apply or unapply the configured selected class.
if
(
this
.
options
.
selectedClass
)
{
if
(
checked
)
{
$target
.
closest
(
'
li
'
)
.
addClass
(
this
.
options
.
selectedClass
);
}
else
{
$target
.
closest
(
'
li
'
)
.
removeClass
(
this
.
options
.
selectedClass
);
}
}
// Get the corresponding option.
var
value
=
$target
.
val
();
var
$option
=
this
.
getOptionByValue
(
value
);
var
$optionsNotThis
=
$
(
'
option
'
,
this
.
$select
).
not
(
$option
);
var
$checkboxesNotThis
=
$
(
'
input
'
,
this
.
$container
).
not
(
$target
);
if
(
isSelectAllOption
)
{
if
(
checked
)
{
this
.
selectAll
(
this
.
options
.
selectAllJustVisible
);
}
else
{
this
.
deselectAll
(
this
.
options
.
selectAllJustVisible
);
}
}
else
{
if
(
checked
)
{
$option
.
prop
(
'
selected
'
,
true
);
if
(
this
.
options
.
multiple
)
{
// Simply select additional option.
$option
.
prop
(
'
selected
'
,
true
);
}
else
{
// Unselect all other options and corresponding checkboxes.
if
(
this
.
options
.
selectedClass
)
{
$
(
$checkboxesNotThis
).
closest
(
'
li
'
).
removeClass
(
this
.
options
.
selectedClass
);
}
$
(
$checkboxesNotThis
).
prop
(
'
checked
'
,
false
);
$optionsNotThis
.
prop
(
'
selected
'
,
false
);
// It's a single selection, so close.
this
.
$button
.
click
();
}
if
(
this
.
options
.
selectedClass
===
"
active
"
)
{
$optionsNotThis
.
closest
(
"
a
"
).
css
(
"
outline
"
,
""
);
}
}
else
{
// Unselect option.
$option
.
prop
(
'
selected
'
,
false
);
}
// To prevent select all from firing onChange: #575
this
.
options
.
onChange
(
$option
,
checked
);
}
this
.
$select
.
change
();
this
.
updateButtonText
();
this
.
updateSelectAll
();
if
(
this
.
options
.
preventInputChangeEvent
)
{
return
false
;
}
},
this
));
$
(
'
li a
'
,
this
.
$ul
).
on
(
'
mousedown
'
,
function
(
e
)
{
if
(
e
.
shiftKey
)
{
// Prevent selecting text by Shift+click
return
false
;
}
});
$
(
'
li a
'
,
this
.
$ul
).
on
(
'
touchstart click
'
,
$
.
proxy
(
function
(
event
)
{
event
.
stopPropagation
();
var
$target
=
$
(
event
.
target
);
if
(
event
.
shiftKey
&&
this
.
options
.
multiple
)
{
if
(
$target
.
is
(
"
label
"
)){
// Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
event
.
preventDefault
();
$target
=
$target
.
find
(
"
input
"
);
$target
.
prop
(
"
checked
"
,
!
$target
.
prop
(
"
checked
"
));
}
var
checked
=
$target
.
prop
(
'
checked
'
)
||
false
;
if
(
this
.
lastToggledInput
!==
null
&&
this
.
lastToggledInput
!==
$target
)
{
// Make sure we actually have a range
var
from
=
$target
.
closest
(
"
li
"
).
index
();
var
to
=
this
.
lastToggledInput
.
closest
(
"
li
"
).
index
();
if
(
from
>
to
)
{
// Swap the indices
var
tmp
=
to
;
to
=
from
;
from
=
tmp
;
}
// Make sure we grab all elements since slice excludes the last index
++
to
;
// Change the checkboxes and underlying options
var
range
=
this
.
$ul
.
find
(
"
li
"
).
slice
(
from
,
to
).
find
(
"
input
"
);
range
.
prop
(
'
checked
'
,
checked
);
if
(
this
.
options
.
selectedClass
)
{
range
.
closest
(
'
li
'
)
.
toggleClass
(
this
.
options
.
selectedClass
,
checked
);
}
for
(
var
i
=
0
,
j
=
range
.
length
;
i
<
j
;
i
++
)
{
var
$checkbox
=
$
(
range
[
i
]);
var
$option
=
this
.
getOptionByValue
(
$checkbox
.
val
());
$option
.
prop
(
'
selected
'
,
checked
);
}
}
// Trigger the select "change" event
$target
.
trigger
(
"
change
"
);
}
// Remembers last clicked option
if
(
$target
.
is
(
"
input
"
)
&&
!
$target
.
closest
(
"
li
"
).
is
(
"
.multiselect-item
"
)){
this
.
lastToggledInput
=
$target
;
}
$target
.
blur
();
},
this
));
// Keyboard support.
this
.
$container
.
off
(
'
keydown.multiselect
'
).
on
(
'
keydown.multiselect
'
,
$
.
proxy
(
function
(
event
)
{
if
(
$
(
'
input[type="text"]
'
,
this
.
$container
).
is
(
'
:focus
'
))
{
return
;
}
if
(
event
.
keyCode
===
9
&&
this
.
$container
.
hasClass
(
'
open
'
))
{
this
.
$button
.
click
();
}
else
{
var
$items
=
$
(
this
.
$container
).
find
(
"
li:not(.divider):not(.disabled) a
"
).
filter
(
"
:visible
"
);
if
(
!
$items
.
length
)
{
return
;
}
var
index
=
$items
.
index
(
$items
.
filter
(
'
:focus
'
));
// Navigation up.
if
(
event
.
keyCode
===
38
&&
index
>
0
)
{
index
--
;
}
// Navigate down.
else
if
(
event
.
keyCode
===
40
&&
index
<
$items
.
length
-
1
)
{
index
++
;
}
else
if
(
!~
index
)
{
index
=
0
;
}
var
$current
=
$items
.
eq
(
index
);
$current
.
focus
();
if
(
event
.
keyCode
===
32
||
event
.
keyCode
===
13
)
{
var
$checkbox
=
$current
.
find
(
'
input
'
);
$checkbox
.
prop
(
"
checked
"
,
!
$checkbox
.
prop
(
"
checked
"
));
$checkbox
.
change
();
}
event
.
stopPropagation
();
event
.
preventDefault
();
}
},
this
));
if
(
this
.
options
.
enableClickableOptGroups
&&
this
.
options
.
multiple
)
{
$
(
'
li.multiselect-group
'
,
this
.
$ul
).
on
(
'
click
'
,
$
.
proxy
(
function
(
event
)
{
event
.
stopPropagation
();
console
.
log
(
'
test
'
);
var
group
=
$
(
event
.
target
).
parent
();
// Search all option in optgroup
var
$options
=
group
.
nextUntil
(
'
li.multiselect-group
'
);
var
$visibleOptions
=
$options
.
filter
(
"
:visible:not(.disabled)
"
);
// check or uncheck items
var
allChecked
=
true
;
var
optionInputs
=
$visibleOptions
.
find
(
'
input
'
);
var
values
=
[];
optionInputs
.
each
(
function
()
{
allChecked
=
allChecked
&&
$
(
this
).
prop
(
'
checked
'
);
values
.
push
(
$
(
this
).
val
());
});
if
(
!
allChecked
)
{
this
.
select
(
values
,
false
);
}
else
{
this
.
deselect
(
values
,
false
);
}
this
.
options
.
onChange
(
optionInputs
,
!
allChecked
);
},
this
));
}
if
(
this
.
options
.
enableCollapsibleOptGroups
&&
this
.
options
.
multiple
)
{
$
(
"
li.multiselect-group input
"
,
this
.
$ul
).
off
();
$
(
"
li.multiselect-group
"
,
this
.
$ul
).
siblings
().
not
(
"
li.multiselect-group, li.multiselect-all
"
,
this
.
$ul
).
each
(
function
()
{
$
(
this
).
toggleClass
(
'
hidden
'
,
true
);
});
$
(
"
li.multiselect-group
"
,
this
.
$ul
).
on
(
"
click
"
,
$
.
proxy
(
function
(
group
)
{
group
.
stopPropagation
();
},
this
));
$
(
"
li.multiselect-group > a > b
"
,
this
.
$ul
).
on
(
"
click
"
,
$
.
proxy
(
function
(
t
)
{
t
.
stopPropagation
();
var
n
=
$
(
t
.
target
).
closest
(
'
li
'
);
var
r
=
n
.
nextUntil
(
"
li.multiselect-group
"
);
var
i
=
true
;
r
.
each
(
function
()
{
i
=
i
&&
$
(
this
).
hasClass
(
'
hidden
'
);
});
r
.
toggleClass
(
'
hidden
'
,
!
i
);
},
this
));
$
(
"
li.multiselect-group > a > input
"
,
this
.
$ul
).
on
(
"
change
"
,
$
.
proxy
(
function
(
t
)
{
t
.
stopPropagation
();
var
n
=
$
(
t
.
target
).
closest
(
'
li
'
);
var
r
=
n
.
nextUntil
(
"
li.multiselect-group
"
,
'
:not(.disabled)
'
);
var
s
=
r
.
find
(
"
input
"
);
var
i
=
true
;
s
.
each
(
function
()
{
i
=
i
&&
$
(
this
).
prop
(
"
checked
"
);
});
s
.
prop
(
"
checked
"
,
!
i
).
trigger
(
"
change
"
);
},
this
));
// Set the initial selection state of the groups.
$
(
'
li.multiselect-group
'
,
this
.
$ul
).
each
(
function
()
{
var
r
=
$
(
this
).
nextUntil
(
"
li.multiselect-group
"
,
'
:not(.disabled)
'
);
var
s
=
r
.
find
(
"
input
"
);
var
i
=
true
;
s
.
each
(
function
()
{
i
=
i
&&
$
(
this
).
prop
(
"
checked
"
);
});
$
(
this
).
find
(
'
input
'
).
prop
(
"
checked
"
,
i
);
});
// Update the group checkbox based on new selections among the
// corresponding children.
$
(
"
li input
"
,
this
.
$ul
).
on
(
"
change
"
,
$
.
proxy
(
function
(
t
)
{
t
.
stopPropagation
();
var
n
=
$
(
t
.
target
).
closest
(
'
li
'
);
var
r1
=
n
.
prevUntil
(
"
li.multiselect-group
"
,
'
:not(.disabled)
'
);
var
r2
=
n
.
nextUntil
(
"
li.multiselect-group
"
,
'
:not(.disabled)
'
);
var
s1
=
r1
.
find
(
"
input
"
);
var
s2
=
r2
.
find
(
"
input
"
);
var
i
=
$
(
t
.
target
).
prop
(
'
checked
'
);
s1
.
each
(
function
()
{
i
=
i
&&
$
(
this
).
prop
(
"
checked
"
);
});
s2
.
each
(
function
()
{
i
=
i
&&
$
(
this
).
prop
(
"
checked
"
);
});
n
.
prevAll
(
'
.multiselect-group
'
).
find
(
'
input
'
).
prop
(
'
checked
'
,
i
);
},
this
));
$
(
"
li.multiselect-all
"
,
this
.
$ul
).
css
(
'
background
'
,
'
#f3f3f3
'
).
css
(
'
border-bottom
'
,
'
1px solid #eaeaea
'
);
$
(
"
li.multiselect-group > a, li.multiselect-all > a > label.checkbox
"
,
this
.
$ul
).
css
(
'
padding
'
,
'
3px 20px 3px 35px
'
);
$
(
"
li.multiselect-group > a > input
"
,
this
.
$ul
).
css
(
'
margin
'
,
'
4px 0px 5px -20px
'
);
}
},
/**
* Create an option using the given select option.
*
* @param {jQuery} element
*/
createOptionValue
:
function
(
element
)
{
var
$element
=
$
(
element
);
if
(
$element
.
is
(
'
:selected
'
))
{
$element
.
prop
(
'
selected
'
,
true
);
}
// Support the label attribute on options.
var
label
=
this
.
options
.
optionLabel
(
element
);
var
classes
=
this
.
options
.
optionClass
(
element
);
var
value
=
$element
.
val
();
var
inputType
=
this
.
options
.
multiple
?
"
checkbox
"
:
"
radio
"
;
var
$li
=
$
(
this
.
options
.
templates
.
li
);
var
$label
=
$
(
'
label
'
,
$li
);
$label
.
addClass
(
inputType
);
$li
.
addClass
(
classes
);
if
(
this
.
options
.
enableHTML
)
{
$label
.
html
(
"
"
+
label
);
}
else
{
$label
.
text
(
"
"
+
label
);
}
var
$checkbox
=
$
(
'
<input/>
'
).
attr
(
'
type
'
,
inputType
);
if
(
this
.
options
.
checkboxName
)
{
$checkbox
.
attr
(
'
name
'
,
this
.
options
.
checkboxName
);
}
$label
.
prepend
(
$checkbox
);
var
selected
=
$element
.
prop
(
'
selected
'
)
||
false
;
$checkbox
.
val
(
value
);
if
(
value
===
this
.
options
.
selectAllValue
)
{
$li
.
addClass
(
"
multiselect-item multiselect-all
"
);
$checkbox
.
parent
().
parent
()
.
addClass
(
'
multiselect-all
'
);
}
$label
.
attr
(
'
title
'
,
$element
.
attr
(
'
title
'
));
this
.
$ul
.
append
(
$li
);
if
(
$element
.
is
(
'
:disabled
'
))
{
$checkbox
.
attr
(
'
disabled
'
,
'
disabled
'
)
.
prop
(
'
disabled
'
,
true
)
.
closest
(
'
a
'
)
.
attr
(
"
tabindex
"
,
"
-1
"
)
.
closest
(
'
li
'
)
.
addClass
(
'
disabled
'
);
}
$checkbox
.
prop
(
'
checked
'
,
selected
);
if
(
selected
&&
this
.
options
.
selectedClass
)
{
$checkbox
.
closest
(
'
li
'
)
.
addClass
(
this
.
options
.
selectedClass
);
}
},
/**
* Creates a divider using the given select option.
*
* @param {jQuery} element
*/
createDivider
:
function
(
element
)
{
var
$divider
=
$
(
this
.
options
.
templates
.
divider
);
this
.
$ul
.
append
(
$divider
);
},
/**
* Creates an optgroup.
*
* @param {jQuery} group
*/
createOptgroup
:
function
(
group
)
{
if
(
this
.
options
.
enableCollapsibleOptGroups
&&
this
.
options
.
multiple
)
{
var
label
=
$
(
group
).
attr
(
"
label
"
);
var
value
=
$
(
group
).
attr
(
"
value
"
);
var
r
=
$
(
'
<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><input type="checkbox" value="
'
+
value
+
'
"/><b>
'
+
label
+
'
<b class="caret"></b></b></a></li>
'
);
if
(
this
.
options
.
enableClickableOptGroups
)
{
r
.
addClass
(
"
multiselect-group-clickable
"
)
}
this
.
$ul
.
append
(
r
);
if
(
$
(
group
).
is
(
"
:disabled
"
))
{
r
.
addClass
(
"
disabled
"
)
}
$
(
"
option
"
,
group
).
each
(
$
.
proxy
(
function
(
$
,
group
)
{
this
.
createOptionValue
(
group
)
},
this
))
}
else
{
var
groupName
=
$
(
group
).
prop
(
'
label
'
);
// Add a header for the group.
var
$li
=
$
(
this
.
options
.
templates
.
liGroup
);
if
(
this
.
options
.
enableHTML
)
{
$
(
'
label
'
,
$li
).
html
(
groupName
);
}
else
{
$
(
'
label
'
,
$li
).
text
(
groupName
);
}
if
(
this
.
options
.
enableClickableOptGroups
)
{
$li
.
addClass
(
'
multiselect-group-clickable
'
);
}
this
.
$ul
.
append
(
$li
);
if
(
$
(
group
).
is
(
'
:disabled
'
))
{
$li
.
addClass
(
'
disabled
'
);
}
// Add the options of the group.
$
(
'
option
'
,
group
).
each
(
$
.
proxy
(
function
(
index
,
element
)
{
this
.
createOptionValue
(
element
);
},
this
));
}
},
/**
* Build the select all.
*
* Checks if a select all has already been created.
*/
buildSelectAll
:
function
()
{
if
(
typeof
this
.
options
.
selectAllValue
===
'
number
'
)
{
this
.
options
.
selectAllValue
=
this
.
options
.
selectAllValue
.
toString
();
}
var
alreadyHasSelectAll
=
this
.
hasSelectAll
();
if
(
!
alreadyHasSelectAll
&&
this
.
options
.
includeSelectAllOption
&&
this
.
options
.
multiple
&&
$
(
'
option
'
,
this
.
$select
).
length
>
this
.
options
.
includeSelectAllIfMoreThan
)
{
// Check whether to add a divider after the select all.
if
(
this
.
options
.
includeSelectAllDivider
)
{
this
.
$ul
.
prepend
(
$
(
this
.
options
.
templates
.
divider
));
}
var
$li
=
$
(
this
.
options
.
templates
.
li
);
$
(
'
label
'
,
$li
).
addClass
(
"
checkbox
"
);
if
(
this
.
options
.
enableHTML
)
{
$
(
'
label
'
,
$li
).
html
(
"
"
+
this
.
options
.
selectAllText
);
}
else
{
$
(
'
label
'
,
$li
).
text
(
"
"
+
this
.
options
.
selectAllText
);
}
if
(
this
.
options
.
selectAllName
)
{
$
(
'
label
'
,
$li
).
prepend
(
'
<input type="checkbox" name="
'
+
this
.
options
.
selectAllName
+
'
" />
'
);
}
else
{
$
(
'
label
'
,
$li
).
prepend
(
'
<input type="checkbox" />
'
);
}
var
$checkbox
=
$
(
'
input
'
,
$li
);
$checkbox
.
val
(
this
.
options
.
selectAllValue
);
$li
.
addClass
(
"
multiselect-item multiselect-all
"
);
$checkbox
.
parent
().
parent
()
.
addClass
(
'
multiselect-all
'
);
this
.
$ul
.
prepend
(
$li
);
$checkbox
.
prop
(
'
checked
'
,
false
);
}
},
/**
* Builds the filter.
*/
buildFilter
:
function
()
{
// Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
if
(
this
.
options
.
enableFiltering
||
this
.
options
.
enableCaseInsensitiveFiltering
)
{
var
enableFilterLength
=
Math
.
max
(
this
.
options
.
enableFiltering
,
this
.
options
.
enableCaseInsensitiveFiltering
);
if
(
this
.
$select
.
find
(
'
option
'
).
length
>=
enableFilterLength
)
{
this
.
$filter
=
$
(
this
.
options
.
templates
.
filter
);
$
(
'
input
'
,
this
.
$filter
).
attr
(
'
placeholder
'
,
this
.
options
.
filterPlaceholder
);
// Adds optional filter clear button
if
(
this
.
options
.
includeFilterClearBtn
){
var
clearBtn
=
$
(
this
.
options
.
templates
.
filterClearBtn
);
clearBtn
.
on
(
'
click
'
,
$
.
proxy
(
function
(
event
){
clearTimeout
(
this
.
searchTimeout
);
this
.
$filter
.
find
(
'
.multiselect-search
'
).
val
(
''
);
$
(
'
li
'
,
this
.
$ul
).
show
().
removeClass
(
"
filter-hidden
"
);
this
.
updateSelectAll
();
},
this
));
this
.
$filter
.
find
(
'
.input-group
'
).
append
(
clearBtn
);
}
this
.
$ul
.
prepend
(
this
.
$filter
);
this
.
$filter
.
val
(
this
.
query
).
on
(
'
click
'
,
function
(
event
)
{
event
.
stopPropagation
();
}).
on
(
'
input keydown
'
,
$
.
proxy
(
function
(
event
)
{
// Cancel enter key default behaviour
if
(
event
.
which
===
13
)
{
event
.
preventDefault
();
}
// This is useful to catch "keydown" events after the browser has updated the control.
clearTimeout
(
this
.
searchTimeout
);
this
.
searchTimeout
=
this
.
asyncFunction
(
$
.
proxy
(
function
()
{
if
(
this
.
query
!==
event
.
target
.
value
)
{
this
.
query
=
event
.
target
.
value
;
var
currentGroup
,
currentGroupVisible
;
$
.
each
(
$
(
'
li
'
,
this
.
$ul
),
$
.
proxy
(
function
(
index
,
element
)
{
var
value
=
$
(
'
input
'
,
element
).
length
>
0
?
$
(
'
input
'
,
element
).
val
()
:
""
;
var
text
=
$
(
'
label
'
,
element
).
text
();
var
filterCandidate
=
''
;
if
((
this
.
options
.
filterBehavior
===
'
text
'
))
{
filterCandidate
=
text
;
}
else
if
((
this
.
options
.
filterBehavior
===
'
value
'
))
{
filterCandidate
=
value
;
}
else
if
(
this
.
options
.
filterBehavior
===
'
both
'
)
{
filterCandidate
=
text
+
'
\n
'
+
value
;
}
if
(
value
!==
this
.
options
.
selectAllValue
&&
text
)
{
// By default lets assume that element is not
// interesting for this search.
var
showElement
=
false
;
if
(
this
.
options
.
enableCaseInsensitiveFiltering
)
{
filterCandidate
=
filterCandidate
.
toLowerCase
();
this
.
query
=
this
.
query
.
toLowerCase
();
}
if
(
this
.
options
.
enableFullValueFiltering
&&
this
.
options
.
filterBehavior
!==
'
both
'
)
{
var
valueToMatch
=
filterCandidate
.
trim
().
substring
(
0
,
this
.
query
.
length
);
if
(
this
.
query
.
indexOf
(
valueToMatch
)
>
-
1
)
{
showElement
=
true
;
}
}
else
if
(
filterCandidate
.
indexOf
(
this
.
query
)
>
-
1
)
{
showElement
=
true
;
}
// Toggle current element (group or group item) according to showElement boolean.
$
(
element
).
toggle
(
showElement
).
toggleClass
(
'
filter-hidden
'
,
!
showElement
);
// Differentiate groups and group items.
if
(
$
(
element
).
hasClass
(
'
multiselect-group
'
))
{
// Remember group status.
currentGroup
=
element
;
currentGroupVisible
=
showElement
;
}
else
{
// Show group name when at least one of its items is visible.
if
(
showElement
)
{
$
(
currentGroup
).
show
().
removeClass
(
'
filter-hidden
'
);
}
// Show all group items when group name satisfies filter.
if
(
!
showElement
&&
currentGroupVisible
)
{
$
(
element
).
show
().
removeClass
(
'
filter-hidden
'
);
}
}
}
},
this
));
}
this
.
updateSelectAll
();
},
this
),
300
,
this
);
},
this
));
}
}
},
/**
* Unbinds the whole plugin.
*/
destroy
:
function
()
{
this
.
$container
.
remove
();
this
.
$select
.
show
();
this
.
$select
.
data
(
'
multiselect
'
,
null
);
},
/**
* Refreshs the multiselect based on the selected options of the select.
*/
refresh
:
function
()
{
var
inputs
=
$
.
map
(
$
(
'
li input
'
,
this
.
$ul
),
$
);
$
(
'
option
'
,
this
.
$select
).
each
(
$
.
proxy
(
function
(
index
,
element
)
{
var
$elem
=
$
(
element
);
var
value
=
$elem
.
val
();
var
$input
;
for
(
var
i
=
inputs
.
length
;
0
<
i
--
;
/**/
)
{
if
(
value
!==
(
$input
=
inputs
[
i
]).
val
())
continue
;
// wrong li
if
(
$elem
.
is
(
'
:selected
'
))
{
$input
.
prop
(
'
checked
'
,
true
);
if
(
this
.
options
.
selectedClass
)
{
$input
.
closest
(
'
li
'
)
.
addClass
(
this
.
options
.
selectedClass
);
}
}
else
{
$input
.
prop
(
'
checked
'
,
false
);
if
(
this
.
options
.
selectedClass
)
{
$input
.
closest
(
'
li
'
)
.
removeClass
(
this
.
options
.
selectedClass
);
}
}
if
(
$elem
.
is
(
"
:disabled
"
))
{
$input
.
attr
(
'
disabled
'
,
'
disabled
'
)
.
prop
(
'
disabled
'
,
true
)
.
closest
(
'
li
'
)
.
addClass
(
'
disabled
'
);
}
else
{
$input
.
prop
(
'
disabled
'
,
false
)
.
closest
(
'
li
'
)
.
removeClass
(
'
disabled
'
);
}
break
;
// assumes unique values
}
},
this
));
this
.
updateButtonText
();
this
.
updateSelectAll
();
},
/**
* Select all options of the given values.
*
* If triggerOnChange is set to true, the on change event is triggered if
* and only if one value is passed.
*
* @param {Array} selectValues
* @param {Boolean} triggerOnChange
*/
select
:
function
(
selectValues
,
triggerOnChange
)
{
if
(
!
$
.
isArray
(
selectValues
))
{
selectValues
=
[
selectValues
];
}
for
(
var
i
=
0
;
i
<
selectValues
.
length
;
i
++
)
{
var
value
=
selectValues
[
i
];
if
(
value
===
null
||
value
===
undefined
)
{
continue
;
}
var
$option
=
this
.
getOptionByValue
(
value
);
var
$checkbox
=
this
.
getInputByValue
(
value
);
if
(
$option
===
undefined
||
$checkbox
===
undefined
)
{
continue
;
}
if
(
!
this
.
options
.
multiple
)
{
this
.
deselectAll
(
false
);
}
if
(
this
.
options
.
selectedClass
)
{
$checkbox
.
closest
(
'
li
'
)
.
addClass
(
this
.
options
.
selectedClass
);
}
$checkbox
.
prop
(
'
checked
'
,
true
);
$option
.
prop
(
'
selected
'
,
true
);
if
(
triggerOnChange
)
{
this
.
options
.
onChange
(
$option
,
true
);
}
}
this
.
updateButtonText
();
this
.
updateSelectAll
();
},
/**
* Clears all selected items.
*/
clearSelection
:
function
()
{
this
.
deselectAll
(
false
);
this
.
updateButtonText
();
this
.
updateSelectAll
();
},
/**
* Deselects all options of the given values.
*
* If triggerOnChange is set to true, the on change event is triggered, if
* and only if one value is passed.
*
* @param {Array} deselectValues
* @param {Boolean} triggerOnChange
*/
deselect
:
function
(
deselectValues
,
triggerOnChange
)
{
if
(
!
$
.
isArray
(
deselectValues
))
{
deselectValues
=
[
deselectValues
];
}
for
(
var
i
=
0
;
i
<
deselectValues
.
length
;
i
++
)
{
var
value
=
deselectValues
[
i
];
if
(
value
===
null
||
value
===
undefined
)
{
continue
;
}
var
$option
=
this
.
getOptionByValue
(
value
);
var
$checkbox
=
this
.
getInputByValue
(
value
);
if
(
$option
===
undefined
||
$checkbox
===
undefined
)
{
continue
;
}
if
(
this
.
options
.
selectedClass
)
{
$checkbox
.
closest
(
'
li
'
)
.
removeClass
(
this
.
options
.
selectedClass
);
}
$checkbox
.
prop
(
'
checked
'
,
false
);
$option
.
prop
(
'
selected
'
,
false
);
if
(
triggerOnChange
)
{
this
.
options
.
onChange
(
$option
,
false
);
}
}
this
.
updateButtonText
();
this
.
updateSelectAll
();
},
/**
* Selects all enabled & visible options.
*
* If justVisible is true or not specified, only visible options are selected.
*
* @param {Boolean} justVisible
* @param {Boolean} triggerOnSelectAll
*/
selectAll
:
function
(
justVisible
,
triggerOnSelectAll
)
{
justVisible
=
(
this
.
options
.
enableCollapsibleOptGroups
&&
this
.
options
.
multiple
)
?
false
:
justVisible
;
var
justVisible
=
typeof
justVisible
===
'
undefined
'
?
true
:
justVisible
;
var
allCheckboxes
=
$
(
"
li input[type='checkbox']:enabled
"
,
this
.
$ul
);
var
visibleCheckboxes
=
allCheckboxes
.
filter
(
"
:visible
"
);
var
allCheckboxesCount
=
allCheckboxes
.
length
;
var
visibleCheckboxesCount
=
visibleCheckboxes
.
length
;
if
(
justVisible
)
{
visibleCheckboxes
.
prop
(
'
checked
'
,
true
);
$
(
"
li:not(.divider):not(.disabled)
"
,
this
.
$ul
).
filter
(
"
:visible
"
).
addClass
(
this
.
options
.
selectedClass
);
}
else
{
allCheckboxes
.
prop
(
'
checked
'
,
true
);
$
(
"
li:not(.divider):not(.disabled)
"
,
this
.
$ul
).
addClass
(
this
.
options
.
selectedClass
);
}
if
(
allCheckboxesCount
===
visibleCheckboxesCount
||
justVisible
===
false
)
{
$
(
"
option:not([data-role='divider']):enabled
"
,
this
.
$select
).
prop
(
'
selected
'
,
true
);
}
else
{
var
values
=
visibleCheckboxes
.
map
(
function
()
{
return
$
(
this
).
val
();
}).
get
();
$
(
"
option:enabled
"
,
this
.
$select
).
filter
(
function
(
index
)
{
return
$
.
inArray
(
$
(
this
).
val
(),
values
)
!==
-
1
;
}).
prop
(
'
selected
'
,
true
);
}
if
(
triggerOnSelectAll
)
{
this
.
options
.
onSelectAll
();
}
},
/**
* Deselects all options.
*
* If justVisible is true or not specified, only visible options are deselected.
*
* @param {Boolean} justVisible
*/
deselectAll
:
function
(
justVisible
)
{
justVisible
=
(
this
.
options
.
enableCollapsibleOptGroups
&&
this
.
options
.
multiple
)
?
false
:
justVisible
;
justVisible
=
typeof
justVisible
===
'
undefined
'
?
true
:
justVisible
;
if
(
justVisible
)
{
var
visibleCheckboxes
=
$
(
"
li input[type='checkbox']:not(:disabled)
"
,
this
.
$ul
).
filter
(
"
:visible
"
);
visibleCheckboxes
.
prop
(
'
checked
'
,
false
);
var
values
=
visibleCheckboxes
.
map
(
function
()
{
return
$
(
this
).
val
();
}).
get
();
$
(
"
option:enabled
"
,
this
.
$select
).
filter
(
function
(
index
)
{
return
$
.
inArray
(
$
(
this
).
val
(),
values
)
!==
-
1
;
}).
prop
(
'
selected
'
,
false
);
if
(
this
.
options
.
selectedClass
)
{
$
(
"
li:not(.divider):not(.disabled)
"
,
this
.
$ul
).
filter
(
"
:visible
"
).
removeClass
(
this
.
options
.
selectedClass
);
}
}
else
{
$
(
"
li input[type='checkbox']:enabled
"
,
this
.
$ul
).
prop
(
'
checked
'
,
false
);
$
(
"
option:enabled
"
,
this
.
$select
).
prop
(
'
selected
'
,
false
);
if
(
this
.
options
.
selectedClass
)
{
$
(
"
li:not(.divider):not(.disabled)
"
,
this
.
$ul
).
removeClass
(
this
.
options
.
selectedClass
);
}
}
},
/**
* Rebuild the plugin.
*
* Rebuilds the dropdown, the filter and the select all option.
*/
rebuild
:
function
()
{
this
.
$ul
.
html
(
''
);
// Important to distinguish between radios and checkboxes.
this
.
options
.
multiple
=
this
.
$select
.
attr
(
'
multiple
'
)
===
"
multiple
"
;
this
.
buildSelectAll
();
this
.
buildDropdownOptions
();
this
.
buildFilter
();
this
.
updateButtonText
();
this
.
updateSelectAll
(
true
);
if
(
this
.
options
.
disableIfEmpty
&&
$
(
'
option
'
,
this
.
$select
).
length
<=
0
)
{
this
.
disable
();
}
else
{
this
.
enable
();
}
if
(
this
.
options
.
dropRight
)
{
this
.
$ul
.
addClass
(
'
pull-right
'
);
}
},
/**
* The provided data will be used to build the dropdown.
*/
dataprovider
:
function
(
dataprovider
)
{
var
groupCounter
=
0
;
var
$select
=
this
.
$select
.
empty
();
$
.
each
(
dataprovider
,
function
(
index
,
option
)
{
var
$tag
;
if
(
$
.
isArray
(
option
.
children
))
{
// create optiongroup tag
groupCounter
++
;
$tag
=
$
(
'
<optgroup/>
'
).
attr
({
label
:
option
.
label
||
'
Group
'
+
groupCounter
,
disabled
:
!!
option
.
disabled
});
forEach
(
option
.
children
,
function
(
subOption
)
{
// add children option tags
$tag
.
append
(
$
(
'
<option/>
'
).
attr
({
value
:
subOption
.
value
,
label
:
subOption
.
label
||
subOption
.
value
,
title
:
subOption
.
title
,
selected
:
!!
subOption
.
selected
,
disabled
:
!!
subOption
.
disabled
}));
});
}
else
{
$tag
=
$
(
'
<option/>
'
).
attr
({
value
:
option
.
value
,
label
:
option
.
label
||
option
.
value
,
title
:
option
.
title
,
class
:
option
.
class
,
selected
:
!!
option
.
selected
,
disabled
:
!!
option
.
disabled
});
$tag
.
text
(
option
.
label
||
option
.
value
);
}
$select
.
append
(
$tag
);
});
this
.
rebuild
();
},
/**
* Enable the multiselect.
*/
enable
:
function
()
{
this
.
$select
.
prop
(
'
disabled
'
,
false
);
this
.
$button
.
prop
(
'
disabled
'
,
false
)
.
removeClass
(
'
disabled
'
);
},
/**
* Disable the multiselect.
*/
disable
:
function
()
{
this
.
$select
.
prop
(
'
disabled
'
,
true
);
this
.
$button
.
prop
(
'
disabled
'
,
true
)
.
addClass
(
'
disabled
'
);
},
/**
* Set the options.
*
* @param {Array} options
*/
setOptions
:
function
(
options
)
{
this
.
options
=
this
.
mergeOptions
(
options
);
},
/**
* Merges the given options with the default options.
*
* @param {Array} options
* @returns {Array}
*/
mergeOptions
:
function
(
options
)
{
return
$
.
extend
(
true
,
{},
this
.
defaults
,
this
.
options
,
options
);
},
/**
* Checks whether a select all checkbox is present.
*
* @returns {Boolean}
*/
hasSelectAll
:
function
()
{
return
$
(
'
li.multiselect-all
'
,
this
.
$ul
).
length
>
0
;
},
/**
* Updates the select all checkbox based on the currently displayed and selected checkboxes.
*/
updateSelectAll
:
function
(
notTriggerOnSelectAll
)
{
if
(
this
.
hasSelectAll
())
{
var
allBoxes
=
$
(
"
li:not(.multiselect-item):not(.filter-hidden) input:enabled
"
,
this
.
$ul
);
var
allBoxesLength
=
allBoxes
.
length
;
var
checkedBoxesLength
=
allBoxes
.
filter
(
"
:checked
"
).
length
;
var
selectAllLi
=
$
(
"
li.multiselect-all
"
,
this
.
$ul
);
var
selectAllInput
=
selectAllLi
.
find
(
"
input
"
);
if
(
checkedBoxesLength
>
0
&&
checkedBoxesLength
===
allBoxesLength
)
{
selectAllInput
.
prop
(
"
checked
"
,
true
);
selectAllLi
.
addClass
(
this
.
options
.
selectedClass
);
this
.
options
.
onSelectAll
(
true
);
}
else
{
selectAllInput
.
prop
(
"
checked
"
,
false
);
selectAllLi
.
removeClass
(
this
.
options
.
selectedClass
);
if
(
checkedBoxesLength
===
0
)
{
if
(
!
notTriggerOnSelectAll
)
{
this
.
options
.
onSelectAll
(
false
);
}
}
}
}
},
/**
* Update the button text and its title based on the currently selected options.
*/
updateButtonText
:
function
()
{
var
options
=
this
.
getSelected
();
// First update the displayed button text.
if
(
this
.
options
.
enableHTML
)
{
$
(
'
.multiselect .multiselect-selected-text
'
,
this
.
$container
).
html
(
this
.
options
.
buttonText
(
options
,
this
.
$select
));
}
else
{
$
(
'
.multiselect .multiselect-selected-text
'
,
this
.
$container
).
text
(
this
.
options
.
buttonText
(
options
,
this
.
$select
));
}
// Now update the title attribute of the button.
$
(
'
.multiselect
'
,
this
.
$container
).
attr
(
'
title
'
,
this
.
options
.
buttonTitle
(
options
,
this
.
$select
));
},
/**
* Get all selected options.
*
* @returns {jQUery}
*/
getSelected
:
function
()
{
return
$
(
'
option
'
,
this
.
$select
).
filter
(
"
:selected
"
);
},
/**
* Gets a select option by its value.
*
* @param {String} value
* @returns {jQuery}
*/
getOptionByValue
:
function
(
value
)
{
var
options
=
$
(
'
option
'
,
this
.
$select
);
var
valueToCompare
=
value
.
toString
();
for
(
var
i
=
0
;
i
<
options
.
length
;
i
=
i
+
1
)
{
var
option
=
options
[
i
];
if
(
option
.
value
===
valueToCompare
)
{
return
$
(
option
);
}
}
},
/**
* Get the input (radio/checkbox) by its value.
*
* @param {String} value
* @returns {jQuery}
*/
getInputByValue
:
function
(
value
)
{
var
checkboxes
=
$
(
'
li input
'
,
this
.
$ul
);
var
valueToCompare
=
value
.
toString
();
for
(
var
i
=
0
;
i
<
checkboxes
.
length
;
i
=
i
+
1
)
{
var
checkbox
=
checkboxes
[
i
];
if
(
checkbox
.
value
===
valueToCompare
)
{
return
$
(
checkbox
);
}
}
},
/**
* Used for knockout integration.
*/
updateOriginalOptions
:
function
()
{
this
.
originalOptions
=
this
.
$select
.
clone
()[
0
].
options
;
},
asyncFunction
:
function
(
callback
,
timeout
,
self
)
{
var
args
=
Array
.
prototype
.
slice
.
call
(
arguments
,
3
);
return
setTimeout
(
function
()
{
callback
.
apply
(
self
||
window
,
args
);
},
timeout
);
},
setAllSelectedText
:
function
(
allSelectedText
)
{
this
.
options
.
allSelectedText
=
allSelectedText
;
this
.
updateButtonText
();
}
};
$
.
fn
.
multiselect
=
function
(
option
,
parameter
,
extraOptions
)
{
return
this
.
each
(
function
()
{
var
data
=
$
(
this
).
data
(
'
multiselect
'
);
var
options
=
typeof
option
===
'
object
'
&&
option
;
// Initialize the multiselect.
if
(
!
data
)
{
data
=
new
Multiselect
(
this
,
options
);
$
(
this
).
data
(
'
multiselect
'
,
data
);
}
// Call multiselect method.
if
(
typeof
option
===
'
string
'
)
{
data
[
option
](
parameter
,
extraOptions
);
if
(
option
===
'
destroy
'
)
{
$
(
this
).
data
(
'
multiselect
'
,
false
);
}
}
});
};
$
.
fn
.
multiselect
.
Constructor
=
Multiselect
;
$
(
function
()
{
$
(
"
select[data-role=multiselect]
"
).
multiselect
();
});
}(
window
.
jQuery
);
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment