Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
S
selectize
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Custom Issue Tracker
Custom Issue Tracker
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
angularjs
selectize
Commits
0c35bfed
Commit
0c35bfed
authored
Aug 03, 2018
by
bingchuan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[dev] version 0.12.0
parents
Pipeline
#74
failed with stages
Changes
4
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
3818 additions
and
0 deletions
+3818
-0
bower.json
bower.json
+40
-0
selectize.bootstrap3.css
dist/css/selectize.bootstrap3.css
+401
-0
selectize.css
dist/css/selectize.css
+317
-0
selectize.js
dist/js/selectize.js
+3060
-0
No files found.
bower.json
0 → 100644
View file @
0c35bfed
{
"name"
:
"selectize"
,
"keywords"
:
[
"select"
,
"ui"
,
"form"
,
"input"
,
"control"
,
"autocomplete"
,
"tagging"
,
"tag"
],
"description"
:
"Selectize is a jQuery-based custom <select> UI control. Useful for tagging, contact lists, country selectors, etc."
,
"version"
:
"0.12.0"
,
"license"
:
"Apache License, Version 2.0"
,
"readmeFilename"
:
"README.md"
,
"repository"
:
{
"type"
:
"git"
,
"url"
:
"git://github.com/brianreavis/selectize.js.git"
},
"main"
:
[
"dist/css/selectize.css"
,
"dist/js/selectize.js"
],
"ignore"
:
[
"Makefile"
,
"Gruntfile.js"
,
"examples"
,
"node_modules"
,
"bower_components"
,
"docs"
,
"src"
,
"test"
,
".travis.yml"
,
"testem.json"
,
"selectize.jquery.json"
,
"*.sh"
,
"package.json"
],
"dependencies"
:
{
"jquery"
:
">=1.7.0"
,
"sifter"
:
"0.4.x"
,
"microplugin"
:
"0.0.x"
},
"devDependencies"
:
{
"bootstrap2"
:
"bootstrap#2"
,
"bootstrap3"
:
"bootstrap#3.2"
}
}
dist/css/selectize.bootstrap3.css
0 → 100644
View file @
0c35bfed
/**
* selectize.bootstrap3.css (v0.12.0) - Bootstrap 3 Theme
* Copyright (c) 2013–2015 Brian Reavis & contributors
*
* 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.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi
>
.selectize-input
>
div
.ui-sortable-placeholder
{
visibility
:
visible
!important
;
background
:
#f2f2f2
!important
;
background
:
rgba
(
0
,
0
,
0
,
0.06
)
!important
;
border
:
0
none
!important
;
-webkit-box-shadow
:
inset
0
0
12px
4px
#ffffff
;
box-shadow
:
inset
0
0
12px
4px
#ffffff
;
}
.selectize-control.plugin-drag_drop
.ui-sortable-placeholder
::after
{
content
:
'!'
;
visibility
:
hidden
;
}
.selectize-control.plugin-drag_drop
.ui-sortable-helper
{
-webkit-box-shadow
:
0
2px
5px
rgba
(
0
,
0
,
0
,
0.2
);
box-shadow
:
0
2px
5px
rgba
(
0
,
0
,
0
,
0.2
);
}
.selectize-dropdown-header
{
position
:
relative
;
padding
:
3px
12px
;
border-bottom
:
1px
solid
#d0d0d0
;
background
:
#f8f8f8
;
-webkit-border-radius
:
4px
4px
0
0
;
-moz-border-radius
:
4px
4px
0
0
;
border-radius
:
4px
4px
0
0
;
}
.selectize-dropdown-header-close
{
position
:
absolute
;
right
:
12px
;
top
:
50%
;
color
:
#333333
;
opacity
:
0.4
;
margin-top
:
-12px
;
line-height
:
20px
;
font-size
:
20px
!important
;
}
.selectize-dropdown-header-close
:hover
{
color
:
#000000
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
{
border-right
:
1px
solid
#f2f2f2
;
border-top
:
0
none
;
float
:
left
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
:last-child
{
border-right
:
0
none
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
:before
{
display
:
none
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup-header
{
border-top
:
0
none
;
}
.selectize-control.plugin-remove_button
[
data-value
]
{
position
:
relative
;
padding-right
:
24px
!important
;
}
.selectize-control.plugin-remove_button
[
data-value
]
.remove
{
z-index
:
1
;
/* fixes ie bug (see #392) */
position
:
absolute
;
top
:
0
;
right
:
0
;
bottom
:
0
;
width
:
17px
;
text-align
:
center
;
font-weight
:
bold
;
font-size
:
12px
;
color
:
inherit
;
text-decoration
:
none
;
vertical-align
:
middle
;
display
:
inline-block
;
padding
:
1px
0
0
0
;
border-left
:
1px
solid
rgba
(
0
,
0
,
0
,
0
);
-webkit-border-radius
:
0
2px
2px
0
;
-moz-border-radius
:
0
2px
2px
0
;
border-radius
:
0
2px
2px
0
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
}
.selectize-control.plugin-remove_button
[
data-value
]
.remove
:hover
{
background
:
rgba
(
0
,
0
,
0
,
0.05
);
}
.selectize-control.plugin-remove_button
[
data-value
]
.active
.remove
{
border-left-color
:
rgba
(
0
,
0
,
0
,
0
);
}
.selectize-control.plugin-remove_button
.disabled
[
data-value
]
.remove
:hover
{
background
:
none
;
}
.selectize-control.plugin-remove_button
.disabled
[
data-value
]
.remove
{
border-left-color
:
rgba
(
77
,
77
,
77
,
0
);
}
.selectize-control
{
position
:
relative
;
}
.selectize-dropdown
,
.selectize-input
,
.selectize-input
input
{
color
:
#333333
;
font-family
:
inherit
;
font-size
:
inherit
;
line-height
:
20px
;
-webkit-font-smoothing
:
inherit
;
}
.selectize-input
,
.selectize-control.single
.selectize-input.input-active
{
background
:
#ffffff
;
cursor
:
text
;
display
:
inline-block
;
}
.selectize-input
{
border
:
1px
solid
#cccccc
;
padding
:
6px
12px
;
display
:
inline-block
;
width
:
100%
;
overflow
:
hidden
;
position
:
relative
;
z-index
:
1
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-box-shadow
:
none
;
box-shadow
:
none
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.selectize-control.multi
.selectize-input.has-items
{
padding
:
5px
12px
2px
;
}
.selectize-input.full
{
background-color
:
#ffffff
;
}
.selectize-input.disabled
,
.selectize-input.disabled
*
{
cursor
:
default
!important
;
}
.selectize-input.focus
{
-webkit-box-shadow
:
inset
0
1px
2px
rgba
(
0
,
0
,
0
,
0.15
);
box-shadow
:
inset
0
1px
2px
rgba
(
0
,
0
,
0
,
0.15
);
}
.selectize-input.dropdown-active
{
-webkit-border-radius
:
4px
4px
0
0
;
-moz-border-radius
:
4px
4px
0
0
;
border-radius
:
4px
4px
0
0
;
}
.selectize-input
>
*
{
vertical-align
:
baseline
;
display
:
-moz-inline-stack
;
display
:
inline-block
;
zoom
:
1
;
*
display
:
inline
;
}
.selectize-control.multi
.selectize-input
>
div
{
cursor
:
pointer
;
margin
:
0
3px
3px
0
;
padding
:
1px
3px
;
background
:
#efefef
;
color
:
#333333
;
border
:
0
solid
rgba
(
0
,
0
,
0
,
0
);
}
.selectize-control.multi
.selectize-input
>
div
.active
{
background
:
#428bca
;
color
:
#ffffff
;
border
:
0
solid
rgba
(
0
,
0
,
0
,
0
);
}
.selectize-control.multi
.selectize-input.disabled
>
div
,
.selectize-control.multi
.selectize-input.disabled
>
div
.active
{
color
:
#808080
;
background
:
#ffffff
;
border
:
0
solid
rgba
(
77
,
77
,
77
,
0
);
}
.selectize-input
>
input
{
display
:
inline-block
!important
;
padding
:
0
!important
;
min-height
:
0
!important
;
max-height
:
none
!important
;
max-width
:
100%
!important
;
margin
:
0
!important
;
text-indent
:
0
!important
;
border
:
0
none
!important
;
background
:
none
!important
;
line-height
:
inherit
!important
;
-webkit-user-select
:
auto
!important
;
-webkit-box-shadow
:
none
!important
;
box-shadow
:
none
!important
;
}
.selectize-input
>
input
::-ms-clear
{
display
:
none
;
}
.selectize-input
>
input
:focus
{
outline
:
none
!important
;
}
.selectize-input
::after
{
content
:
' '
;
display
:
block
;
clear
:
left
;
}
.selectize-input.dropdown-active
::before
{
content
:
' '
;
display
:
block
;
position
:
absolute
;
background
:
#ffffff
;
height
:
1px
;
bottom
:
0
;
left
:
0
;
right
:
0
;
}
.selectize-dropdown
{
position
:
absolute
;
z-index
:
10
;
border
:
1px
solid
#d0d0d0
;
background
:
#ffffff
;
margin
:
-1px
0
0
0
;
border-top
:
0
none
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
-webkit-border-radius
:
0
0
4px
4px
;
-moz-border-radius
:
0
0
4px
4px
;
border-radius
:
0
0
4px
4px
;
}
.selectize-dropdown
[
data-selectable
]
{
cursor
:
pointer
;
overflow
:
hidden
;
}
.selectize-dropdown
[
data-selectable
]
.highlight
{
background
:
rgba
(
255
,
237
,
40
,
0.4
);
-webkit-border-radius
:
1px
;
-moz-border-radius
:
1px
;
border-radius
:
1px
;
}
.selectize-dropdown
[
data-selectable
],
.selectize-dropdown
.optgroup-header
{
padding
:
3px
12px
;
}
.selectize-dropdown
.optgroup
:first-child
.optgroup-header
{
border-top
:
0
none
;
}
.selectize-dropdown
.optgroup-header
{
color
:
#777777
;
background
:
#ffffff
;
cursor
:
default
;
}
.selectize-dropdown
.active
{
background-color
:
#f5f5f5
;
color
:
#262626
;
}
.selectize-dropdown
.active.create
{
color
:
#262626
;
}
.selectize-dropdown
.create
{
color
:
rgba
(
51
,
51
,
51
,
0.5
);
}
.selectize-dropdown-content
{
overflow-y
:
auto
;
overflow-x
:
hidden
;
max-height
:
200px
;
}
.selectize-control.single
.selectize-input
,
.selectize-control.single
.selectize-input
input
{
cursor
:
pointer
;
}
.selectize-control.single
.selectize-input.input-active
,
.selectize-control.single
.selectize-input.input-active
input
{
cursor
:
text
;
}
.selectize-control.single
.selectize-input
:after
{
content
:
' '
;
display
:
block
;
position
:
absolute
;
top
:
50%
;
right
:
17px
;
margin-top
:
-3px
;
width
:
0
;
height
:
0
;
border-style
:
solid
;
border-width
:
5px
5px
0
5px
;
border-color
:
#333333
transparent
transparent
transparent
;
}
.selectize-control.single
.selectize-input.dropdown-active
:after
{
margin-top
:
-4px
;
border-width
:
0
5px
5px
5px
;
border-color
:
transparent
transparent
#333333
transparent
;
}
.selectize-control.rtl.single
.selectize-input
:after
{
left
:
17px
;
right
:
auto
;
}
.selectize-control.rtl
.selectize-input
>
input
{
margin
:
0
4px
0
-2px
!important
;
}
.selectize-control
.selectize-input.disabled
{
opacity
:
0.5
;
background-color
:
#ffffff
;
}
.selectize-dropdown
,
.selectize-dropdown.form-control
{
height
:
auto
;
padding
:
0
;
margin
:
2px
0
0
0
;
z-index
:
1000
;
background
:
#ffffff
;
border
:
1px
solid
#cccccc
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.15
);
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
-webkit-box-shadow
:
0
6px
12px
rgba
(
0
,
0
,
0
,
0.175
);
box-shadow
:
0
6px
12px
rgba
(
0
,
0
,
0
,
0.175
);
}
.selectize-dropdown
.optgroup-header
{
font-size
:
12px
;
line-height
:
1.42857143
;
}
.selectize-dropdown
.optgroup
:first-child:before
{
display
:
none
;
}
.selectize-dropdown
.optgroup
:before
{
content
:
' '
;
display
:
block
;
height
:
1px
;
margin
:
9px
0
;
overflow
:
hidden
;
background-color
:
#e5e5e5
;
margin-left
:
-12px
;
margin-right
:
-12px
;
}
.selectize-dropdown-content
{
padding
:
5px
0
;
}
.selectize-dropdown-header
{
padding
:
6px
12px
;
}
.selectize-input
{
min-height
:
34px
;
}
.selectize-input.dropdown-active
{
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.selectize-input.dropdown-active
::before
{
display
:
none
;
}
.selectize-input.focus
{
border-color
:
#66afe9
;
outline
:
0
;
-webkit-box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
.075
),
0
0
8px
rgba
(
102
,
175
,
233
,
0.6
);
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
.075
),
0
0
8px
rgba
(
102
,
175
,
233
,
0.6
);
}
.has-error
.selectize-input
{
border-color
:
#a94442
;
-webkit-box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
);
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
);
}
.has-error
.selectize-input
:focus
{
border-color
:
#843534
;
-webkit-box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
),
0
0
6px
#ce8483
;
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
),
0
0
6px
#ce8483
;
}
.selectize-control.multi
.selectize-input.has-items
{
padding-left
:
9px
;
padding-right
:
9px
;
}
.selectize-control.multi
.selectize-input
>
div
{
-webkit-border-radius
:
3px
;
-moz-border-radius
:
3px
;
border-radius
:
3px
;
}
.form-control.selectize-control
{
padding
:
0
;
height
:
auto
;
border
:
none
;
background
:
none
;
-webkit-box-shadow
:
none
;
box-shadow
:
none
;
-webkit-border-radius
:
0
;
-moz-border-radius
:
0
;
border-radius
:
0
;
}
dist/css/selectize.css
0 → 100644
View file @
0c35bfed
/**
* selectize.css (v0.12.0)
* Copyright (c) 2013–2015 Brian Reavis & contributors
*
* 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.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi
>
.selectize-input
>
div
.ui-sortable-placeholder
{
visibility
:
visible
!important
;
background
:
#f2f2f2
!important
;
background
:
rgba
(
0
,
0
,
0
,
0.06
)
!important
;
border
:
0
none
!important
;
-webkit-box-shadow
:
inset
0
0
12px
4px
#ffffff
;
box-shadow
:
inset
0
0
12px
4px
#ffffff
;
}
.selectize-control.plugin-drag_drop
.ui-sortable-placeholder
::after
{
content
:
'!'
;
visibility
:
hidden
;
}
.selectize-control.plugin-drag_drop
.ui-sortable-helper
{
-webkit-box-shadow
:
0
2px
5px
rgba
(
0
,
0
,
0
,
0.2
);
box-shadow
:
0
2px
5px
rgba
(
0
,
0
,
0
,
0.2
);
}
.selectize-dropdown-header
{
position
:
relative
;
padding
:
5px
8px
;
border-bottom
:
1px
solid
#d0d0d0
;
background
:
#f8f8f8
;
-webkit-border-radius
:
3px
3px
0
0
;
-moz-border-radius
:
3px
3px
0
0
;
border-radius
:
3px
3px
0
0
;
}
.selectize-dropdown-header-close
{
position
:
absolute
;
right
:
8px
;
top
:
50%
;
color
:
#303030
;
opacity
:
0.4
;
margin-top
:
-12px
;
line-height
:
20px
;
font-size
:
20px
!important
;
}
.selectize-dropdown-header-close
:hover
{
color
:
#000000
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
{
border-right
:
1px
solid
#f2f2f2
;
border-top
:
0
none
;
float
:
left
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
:last-child
{
border-right
:
0
none
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup
:before
{
display
:
none
;
}
.selectize-dropdown.plugin-optgroup_columns
.optgroup-header
{
border-top
:
0
none
;
}
.selectize-control.plugin-remove_button
[
data-value
]
{
position
:
relative
;
padding-right
:
24px
!important
;
}
.selectize-control.plugin-remove_button
[
data-value
]
.remove
{
z-index
:
1
;
/* fixes ie bug (see #392) */
position
:
absolute
;
top
:
0
;
right
:
0
;
bottom
:
0
;
width
:
17px
;
text-align
:
center
;
font-weight
:
bold
;
font-size
:
12px
;
color
:
inherit
;
text-decoration
:
none
;
vertical-align
:
middle
;
display
:
inline-block
;
padding
:
2px
0
0
0
;
border-left
:
1px
solid
#d0d0d0
;
-webkit-border-radius
:
0
2px
2px
0
;
-moz-border-radius
:
0
2px
2px
0
;
border-radius
:
0
2px
2px
0
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
}
.selectize-control.plugin-remove_button
[
data-value
]
.remove
:hover
{
background
:
rgba
(
0
,
0
,
0
,
0.05
);
}
.selectize-control.plugin-remove_button
[
data-value
]
.active
.remove
{
border-left-color
:
#cacaca
;
}
.selectize-control.plugin-remove_button
.disabled
[
data-value
]
.remove
:hover
{
background
:
none
;
}
.selectize-control.plugin-remove_button
.disabled
[
data-value
]
.remove
{
border-left-color
:
#ffffff
;
}
.selectize-control
{
position
:
relative
;
}
.selectize-dropdown
,
.selectize-input
,
.selectize-input
input
{
color
:
#303030
;
font-family
:
inherit
;
font-size
:
13px
;
line-height
:
18px
;
-webkit-font-smoothing
:
inherit
;
}
.selectize-input
,
.selectize-control.single
.selectize-input.input-active
{
background
:
#ffffff
;
cursor
:
text
;
display
:
inline-block
;
}
.selectize-input
{
border
:
1px
solid
#d0d0d0
;
padding
:
8px
8px
;
display
:
inline-block
;
width
:
100%
;
overflow
:
hidden
;
position
:
relative
;
z-index
:
1
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.1
);
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.1
);
-webkit-border-radius
:
3px
;
-moz-border-radius
:
3px
;
border-radius
:
3px
;
}
.selectize-control.multi
.selectize-input.has-items
{
padding
:
6px
8px
3px
;
}
.selectize-input.full
{
background-color
:
#ffffff
;
}
.selectize-input.disabled
,
.selectize-input.disabled
*
{
cursor
:
default
!important
;
}
.selectize-input.focus
{
-webkit-box-shadow
:
inset
0
1px
2px
rgba
(
0
,
0
,
0
,
0.15
);
box-shadow
:
inset
0
1px
2px
rgba
(
0
,
0
,
0
,
0.15
);
}
.selectize-input.dropdown-active
{
-webkit-border-radius
:
3px
3px
0
0
;
-moz-border-radius
:
3px
3px
0
0
;
border-radius
:
3px
3px
0
0
;
}
.selectize-input
>
*
{
vertical-align
:
baseline
;
display
:
-moz-inline-stack
;
display
:
inline-block
;
zoom
:
1
;
*
display
:
inline
;
}
.selectize-control.multi
.selectize-input
>
div
{
cursor
:
pointer
;
margin
:
0
3px
3px
0
;
padding
:
2px
6px
;
background
:
#f2f2f2
;
color
:
#303030
;
border
:
0
solid
#d0d0d0
;
}
.selectize-control.multi
.selectize-input
>
div
.active
{
background
:
#e8e8e8
;
color
:
#303030
;
border
:
0
solid
#cacaca
;
}
.selectize-control.multi
.selectize-input.disabled
>
div
,
.selectize-control.multi
.selectize-input.disabled
>
div
.active
{
color
:
#7d7d7d
;
background
:
#ffffff
;
border
:
0
solid
#ffffff
;
}
.selectize-input
>
input
{
display
:
inline-block
!important
;
padding
:
0
!important
;
min-height
:
0
!important
;
max-height
:
none
!important
;
max-width
:
100%
!important
;
margin
:
0
2px
0
0
!important
;
text-indent
:
0
!important
;
border
:
0
none
!important
;
background
:
none
!important
;
line-height
:
inherit
!important
;
-webkit-user-select
:
auto
!important
;
-webkit-box-shadow
:
none
!important
;
box-shadow
:
none
!important
;
}
.selectize-input
>
input
::-ms-clear
{
display
:
none
;
}
.selectize-input
>
input
:focus
{
outline
:
none
!important
;
}
.selectize-input
::after
{
content
:
' '
;
display
:
block
;
clear
:
left
;
}
.selectize-input.dropdown-active
::before
{
content
:
' '
;
display
:
block
;
position
:
absolute
;
background
:
#f0f0f0
;
height
:
1px
;
bottom
:
0
;
left
:
0
;
right
:
0
;
}
.selectize-dropdown
{
position
:
absolute
;
z-index
:
10
;
border
:
1px
solid
#d0d0d0
;
background
:
#ffffff
;
margin
:
-1px
0
0
0
;
border-top
:
0
none
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
box-sizing
:
border-box
;
-webkit-box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.1
);
-webkit-border-radius
:
0
0
3px
3px
;
-moz-border-radius
:
0
0
3px
3px
;
border-radius
:
0
0
3px
3px
;
}
.selectize-dropdown
[
data-selectable
]
{
cursor
:
pointer
;
overflow
:
hidden
;
}
.selectize-dropdown
[
data-selectable
]
.highlight
{
background
:
rgba
(
125
,
168
,
208
,
0.2
);
-webkit-border-radius
:
1px
;
-moz-border-radius
:
1px
;
border-radius
:
1px
;
}
.selectize-dropdown
[
data-selectable
],
.selectize-dropdown
.optgroup-header
{
padding
:
5px
8px
;
}
.selectize-dropdown
.optgroup
:first-child
.optgroup-header
{
border-top
:
0
none
;
}
.selectize-dropdown
.optgroup-header
{
color
:
#303030
;
background
:
#ffffff
;
cursor
:
default
;
}
.selectize-dropdown
.active
{
background-color
:
#f5fafd
;
color
:
#495c68
;
}
.selectize-dropdown
.active.create
{
color
:
#495c68
;
}
.selectize-dropdown
.create
{
color
:
rgba
(
48
,
48
,
48
,
0.5
);
}
.selectize-dropdown-content
{
overflow-y
:
auto
;
overflow-x
:
hidden
;
max-height
:
200px
;
}
.selectize-control.single
.selectize-input
,
.selectize-control.single
.selectize-input
input
{
cursor
:
pointer
;
}
.selectize-control.single
.selectize-input.input-active
,
.selectize-control.single
.selectize-input.input-active
input
{
cursor
:
text
;
}
.selectize-control.single
.selectize-input
:after
{
content
:
' '
;
display
:
block
;
position
:
absolute
;
top
:
50%
;
right
:
15px
;
margin-top
:
-3px
;
width
:
0
;
height
:
0
;
border-style
:
solid
;
border-width
:
5px
5px
0
5px
;
border-color
:
#808080
transparent
transparent
transparent
;
}
.selectize-control.single
.selectize-input.dropdown-active
:after
{
margin-top
:
-4px
;
border-width
:
0
5px
5px
5px
;
border-color
:
transparent
transparent
#808080
transparent
;
}
.selectize-control.rtl.single
.selectize-input
:after
{
left
:
15px
;
right
:
auto
;
}
.selectize-control.rtl
.selectize-input
>
input
{
margin
:
0
4px
0
-2px
!important
;
}
.selectize-control
.selectize-input.disabled
{
opacity
:
0.5
;
background-color
:
#fafafa
;
}
dist/js/selectize.js
0 → 100644
View file @
0c35bfed
/**
* selectize.js (v0.12.0)
* Copyright (c) 2013–2015 Brian Reavis & contributors
*
* 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.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
/*jshint curly:false */
/*jshint browser:true */
(
function
(
root
,
factory
)
{
if
(
typeof
define
===
'function'
&&
define
.
amd
)
{
define
([
'jquery'
,
'sifter'
,
'microplugin'
],
factory
);
}
else
if
(
typeof
exports
===
'object'
)
{
module
.
exports
=
factory
(
require
(
'jquery'
),
require
(
'sifter'
),
require
(
'microplugin'
));
}
else
{
root
.
Selectize
=
factory
(
root
.
jQuery
,
root
.
Sifter
,
root
.
MicroPlugin
);
}
}(
this
,
function
(
$
,
Sifter
,
MicroPlugin
)
{
'use strict'
;
var
highlight
=
function
(
$element
,
pattern
)
{
if
(
typeof
pattern
===
'string'
&&
!
pattern
.
length
)
return
;
var
regex
=
(
typeof
pattern
===
'string'
)
?
new
RegExp
(
pattern
,
'i'
)
:
pattern
;
var
highlight
=
function
(
node
)
{
var
skip
=
0
;
if
(
node
.
nodeType
===
3
)
{
var
pos
=
node
.
data
.
search
(
regex
);
if
(
pos
>=
0
&&
node
.
data
.
length
>
0
)
{
var
match
=
node
.
data
.
match
(
regex
);
var
spannode
=
document
.
createElement
(
'span'
);
spannode
.
className
=
'highlight'
;
var
middlebit
=
node
.
splitText
(
pos
);
var
endbit
=
middlebit
.
splitText
(
match
[
0
].
length
);
var
middleclone
=
middlebit
.
cloneNode
(
true
);
spannode
.
appendChild
(
middleclone
);
middlebit
.
parentNode
.
replaceChild
(
spannode
,
middlebit
);
skip
=
1
;
}
}
else
if
(
node
.
nodeType
===
1
&&
node
.
childNodes
&&
!
/
(
script|style
)
/i
.
test
(
node
.
tagName
))
{
for
(
var
i
=
0
;
i
<
node
.
childNodes
.
length
;
++
i
)
{
i
+=
highlight
(
node
.
childNodes
[
i
]);
}
}
return
skip
;
};
return
$element
.
each
(
function
()
{
highlight
(
this
);
});
};
var
MicroEvent
=
function
()
{};
MicroEvent
.
prototype
=
{
on
:
function
(
event
,
fct
){
this
.
_events
=
this
.
_events
||
{};
this
.
_events
[
event
]
=
this
.
_events
[
event
]
||
[];
this
.
_events
[
event
].
push
(
fct
);
},
off
:
function
(
event
,
fct
){
var
n
=
arguments
.
length
;
if
(
n
===
0
)
return
delete
this
.
_events
;
if
(
n
===
1
)
return
delete
this
.
_events
[
event
];
this
.
_events
=
this
.
_events
||
{};
if
(
event
in
this
.
_events
===
false
)
return
;
this
.
_events
[
event
].
splice
(
this
.
_events
[
event
].
indexOf
(
fct
),
1
);
},
trigger
:
function
(
event
/* , args... */
){
this
.
_events
=
this
.
_events
||
{};
if
(
event
in
this
.
_events
===
false
)
return
;
for
(
var
i
=
0
;
i
<
this
.
_events
[
event
].
length
;
i
++
){
this
.
_events
[
event
][
i
].
apply
(
this
,
Array
.
prototype
.
slice
.
call
(
arguments
,
1
));
}
}
};
/**
* Mixin will delegate all MicroEvent.js function in the destination object.
*
* - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
*
* @param {object} the object which will support MicroEvent
*/
MicroEvent
.
mixin
=
function
(
destObject
){
var
props
=
[
'on'
,
'off'
,
'trigger'
];
for
(
var
i
=
0
;
i
<
props
.
length
;
i
++
){
destObject
.
prototype
[
props
[
i
]]
=
MicroEvent
.
prototype
[
props
[
i
]];
}
};
var
IS_MAC
=
/Mac/
.
test
(
navigator
.
userAgent
);
var
KEY_A
=
65
;
var
KEY_COMMA
=
188
;
var
KEY_RETURN
=
13
;
var
KEY_ESC
=
27
;
var
KEY_LEFT
=
37
;
var
KEY_UP
=
38
;
var
KEY_P
=
80
;
var
KEY_RIGHT
=
39
;
var
KEY_DOWN
=
40
;
var
KEY_N
=
78
;
var
KEY_BACKSPACE
=
8
;
var
KEY_DELETE
=
46
;
var
KEY_SHIFT
=
16
;
var
KEY_CMD
=
IS_MAC
?
91
:
17
;
var
KEY_CTRL
=
IS_MAC
?
18
:
17
;
var
KEY_TAB
=
9
;
var
TAG_SELECT
=
1
;
var
TAG_INPUT
=
2
;
// for now, android support in general is too spotty to support validity
var
SUPPORTS_VALIDITY_API
=
!
/android/i
.
test
(
window
.
navigator
.
userAgent
)
&&
!!
document
.
createElement
(
'form'
).
validity
;
var
isset
=
function
(
object
)
{
return
typeof
object
!==
'undefined'
;
};
/**
* Converts a scalar to its best string representation
* for hash keys and HTML attribute values.
*
* Transformations:
* 'str' -> 'str'
* null -> ''
* undefined -> ''
* true -> '1'
* false -> '0'
* 0 -> '0'
* 1 -> '1'
*
* @param {string} value
* @returns {string|null}
*/
var
hash_key
=
function
(
value
)
{
if
(
typeof
value
===
'undefined'
||
value
===
null
)
return
null
;
if
(
typeof
value
===
'boolean'
)
return
value
?
'1'
:
'0'
;
return
value
+
''
;
};
/**
* Escapes a string for use within HTML.
*
* @param {string} str
* @returns {string}
*/
var
escape_html
=
function
(
str
)
{
return
(
str
+
''
)
.
replace
(
/&/g
,
'&'
)
.
replace
(
/</g
,
'<'
)
.
replace
(
/>/g
,
'>'
)
.
replace
(
/"/g
,
'"'
);
};
/**
* Escapes "$" characters in replacement strings.
*
* @param {string} str
* @returns {string}
*/
var
escape_replace
=
function
(
str
)
{
return
(
str
+
''
).
replace
(
/
\$
/g
,
'$$$$'
);
};
var
hook
=
{};
/**
* Wraps `method` on `self` so that `fn`
* is invoked before the original method.
*
* @param {object} self
* @param {string} method
* @param {function} fn
*/
hook
.
before
=
function
(
self
,
method
,
fn
)
{
var
original
=
self
[
method
];
self
[
method
]
=
function
()
{
fn
.
apply
(
self
,
arguments
);
return
original
.
apply
(
self
,
arguments
);
};
};
/**
* Wraps `method` on `self` so that `fn`
* is invoked after the original method.
*
* @param {object} self
* @param {string} method
* @param {function} fn
*/
hook
.
after
=
function
(
self
,
method
,
fn
)
{
var
original
=
self
[
method
];
self
[
method
]
=
function
()
{
var
result
=
original
.
apply
(
self
,
arguments
);
fn
.
apply
(
self
,
arguments
);
return
result
;
};
};
/**
* Wraps `fn` so that it can only be invoked once.
*
* @param {function} fn
* @returns {function}
*/
var
once
=
function
(
fn
)
{
var
called
=
false
;
return
function
()
{
if
(
called
)
return
;
called
=
true
;
fn
.
apply
(
this
,
arguments
);
};
};
/**
* Wraps `fn` so that it can only be called once
* every `delay` milliseconds (invoked on the falling edge).
*
* @param {function} fn
* @param {int} delay
* @returns {function}
*/
var
debounce
=
function
(
fn
,
delay
)
{
var
timeout
;
return
function
()
{
var
self
=
this
;
var
args
=
arguments
;
window
.
clearTimeout
(
timeout
);
timeout
=
window
.
setTimeout
(
function
()
{
fn
.
apply
(
self
,
args
);
},
delay
);
};
};
/**
* Debounce all fired events types listed in `types`
* while executing the provided `fn`.
*
* @param {object} self
* @param {array} types
* @param {function} fn
*/
var
debounce_events
=
function
(
self
,
types
,
fn
)
{
var
type
;
var
trigger
=
self
.
trigger
;
var
event_args
=
{};
// override trigger method
self
.
trigger
=
function
()
{
var
type
=
arguments
[
0
];
if
(
types
.
indexOf
(
type
)
!==
-
1
)
{
event_args
[
type
]
=
arguments
;
}
else
{
return
trigger
.
apply
(
self
,
arguments
);
}
};
// invoke provided function
fn
.
apply
(
self
,
[]);
self
.
trigger
=
trigger
;
// trigger queued events
for
(
type
in
event_args
)
{
if
(
event_args
.
hasOwnProperty
(
type
))
{
trigger
.
apply
(
self
,
event_args
[
type
]);
}
}
};
/**
* A workaround for http://bugs.jquery.com/ticket/6696
*
* @param {object} $parent - Parent element to listen on.
* @param {string} event - Event name.
* @param {string} selector - Descendant selector to filter by.
* @param {function} fn - Event handler.
*/
var
watchChildEvent
=
function
(
$parent
,
event
,
selector
,
fn
)
{
$parent
.
on
(
event
,
selector
,
function
(
e
)
{
var
child
=
e
.
target
;
while
(
child
&&
child
.
parentNode
!==
$parent
[
0
])
{
child
=
child
.
parentNode
;
}
e
.
currentTarget
=
child
;
return
fn
.
apply
(
this
,
[
e
]);
});
};
/**
* Determines the current selection within a text input control.
* Returns an object containing:
* - start
* - length
*
* @param {object} input
* @returns {object}
*/
var
getSelection
=
function
(
input
)
{
var
result
=
{};
if
(
'selectionStart'
in
input
)
{
result
.
start
=
input
.
selectionStart
;
result
.
length
=
input
.
selectionEnd
-
result
.
start
;
}
else
if
(
document
.
selection
)
{
input
.
focus
();
var
sel
=
document
.
selection
.
createRange
();
var
selLen
=
document
.
selection
.
createRange
().
text
.
length
;
sel
.
moveStart
(
'character'
,
-
input
.
value
.
length
);
result
.
start
=
sel
.
text
.
length
-
selLen
;
result
.
length
=
selLen
;
}
return
result
;
};
/**
* Copies CSS properties from one element to another.
*
* @param {object} $from
* @param {object} $to
* @param {array} properties
*/
var
transferStyles
=
function
(
$from
,
$to
,
properties
)
{
var
i
,
n
,
styles
=
{};
if
(
properties
)
{
for
(
i
=
0
,
n
=
properties
.
length
;
i
<
n
;
i
++
)
{
styles
[
properties
[
i
]]
=
$from
.
css
(
properties
[
i
]);
}
}
else
{
styles
=
$from
.
css
();
}
$to
.
css
(
styles
);
};
/**
* Measures the width of a string within a
* parent element (in pixels).
*
* @param {string} str
* @param {object} $parent
* @returns {int}
*/
var
measureString
=
function
(
str
,
$parent
)
{
if
(
!
str
)
{
return
0
;
}
var
$test
=
$
(
'<test>'
).
css
({
position
:
'absolute'
,
top
:
-
99999
,
left
:
-
99999
,
width
:
'auto'
,
padding
:
0
,
whiteSpace
:
'pre'
}).
text
(
str
).
appendTo
(
'body'
);
transferStyles
(
$parent
,
$test
,
[
'letterSpacing'
,
'fontSize'
,
'fontFamily'
,
'fontWeight'
,
'textTransform'
]);
var
width
=
$test
.
width
();
$test
.
remove
();
return
width
;
};
/**
* Sets up an input to grow horizontally as the user
* types. If the value is changed manually, you can
* trigger the "update" handler to resize:
*
* $input.trigger('update');
*
* @param {object} $input
*/
var
autoGrow
=
function
(
$input
)
{
var
currentWidth
=
null
;
var
update
=
function
(
e
,
options
)
{
var
value
,
keyCode
,
printable
,
placeholder
,
width
;
var
shift
,
character
,
selection
;
e
=
e
||
window
.
event
||
{};
options
=
options
||
{};
if
(
e
.
metaKey
||
e
.
altKey
)
return
;
if
(
!
options
.
force
&&
$input
.
data
(
'grow'
)
===
false
)
return
;
value
=
$input
.
val
();
if
(
e
.
type
&&
e
.
type
.
toLowerCase
()
===
'keydown'
)
{
keyCode
=
e
.
keyCode
;
printable
=
(
(
keyCode
>=
97
&&
keyCode
<=
122
)
||
// a-z
(
keyCode
>=
65
&&
keyCode
<=
90
)
||
// A-Z
(
keyCode
>=
48
&&
keyCode
<=
57
)
||
// 0-9
keyCode
===
32
// space
);
if
(
keyCode
===
KEY_DELETE
||
keyCode
===
KEY_BACKSPACE
)
{
selection
=
getSelection
(
$input
[
0
]);
if
(
selection
.
length
)
{
value
=
value
.
substring
(
0
,
selection
.
start
)
+
value
.
substring
(
selection
.
start
+
selection
.
length
);
}
else
if
(
keyCode
===
KEY_BACKSPACE
&&
selection
.
start
)
{
value
=
value
.
substring
(
0
,
selection
.
start
-
1
)
+
value
.
substring
(
selection
.
start
+
1
);
}
else
if
(
keyCode
===
KEY_DELETE
&&
typeof
selection
.
start
!==
'undefined'
)
{
value
=
value
.
substring
(
0
,
selection
.
start
)
+
value
.
substring
(
selection
.
start
+
1
);
}
}
else
if
(
printable
)
{
shift
=
e
.
shiftKey
;
character
=
String
.
fromCharCode
(
e
.
keyCode
);
if
(
shift
)
character
=
character
.
toUpperCase
();
else
character
=
character
.
toLowerCase
();
value
+=
character
;
}
}
placeholder
=
$input
.
attr
(
'placeholder'
);
if
(
!
value
&&
placeholder
)
{
value
=
placeholder
;
}
width
=
measureString
(
value
,
$input
)
+
4
;
if
(
width
!==
currentWidth
)
{
currentWidth
=
width
;
$input
.
width
(
width
);
$input
.
triggerHandler
(
'resize'
);
}
};
$input
.
on
(
'keydown keyup update blur'
,
update
);
update
();
};
var
Selectize
=
function
(
$input
,
settings
)
{
var
key
,
i
,
n
,
dir
,
input
,
self
=
this
;
input
=
$input
[
0
];
input
.
selectize
=
self
;
// detect rtl environment
var
computedStyle
=
window
.
getComputedStyle
&&
window
.
getComputedStyle
(
input
,
null
);
dir
=
computedStyle
?
computedStyle
.
getPropertyValue
(
'direction'
)
:
input
.
currentStyle
&&
input
.
currentStyle
.
direction
;
dir
=
dir
||
$input
.
parents
(
'[dir]:first'
).
attr
(
'dir'
)
||
''
;
// setup default state
$
.
extend
(
self
,
{
order
:
0
,
settings
:
settings
,
$input
:
$input
,
tabIndex
:
$input
.
attr
(
'tabindex'
)
||
''
,
tagType
:
input
.
tagName
.
toLowerCase
()
===
'select'
?
TAG_SELECT
:
TAG_INPUT
,
rtl
:
/rtl/i
.
test
(
dir
),
eventNS
:
'.selectize'
+
(
++
Selectize
.
count
),
highlightedValue
:
null
,
isOpen
:
false
,
isDisabled
:
false
,
isRequired
:
$input
.
is
(
'[required]'
),
isInvalid
:
false
,
isLocked
:
false
,
isFocused
:
false
,
isInputHidden
:
false
,
isSetup
:
false
,
isShiftDown
:
false
,
isCmdDown
:
false
,
isCtrlDown
:
false
,
ignoreFocus
:
false
,
ignoreBlur
:
false
,
ignoreHover
:
false
,
hasOptions
:
false
,
currentResults
:
null
,
lastValue
:
''
,
caretPos
:
0
,
loading
:
0
,
loadedSearches
:
{},
$activeOption
:
null
,
$activeItems
:
[],
optgroups
:
{},
options
:
{},
userOptions
:
{},
items
:
[],
renderCache
:
{},
onSearchChange
:
settings
.
loadThrottle
===
null
?
self
.
onSearchChange
:
debounce
(
self
.
onSearchChange
,
settings
.
loadThrottle
)
});
// search system
self
.
sifter
=
new
Sifter
(
this
.
options
,
{
diacritics
:
settings
.
diacritics
});
// build options table
if
(
self
.
settings
.
options
)
{
for
(
i
=
0
,
n
=
self
.
settings
.
options
.
length
;
i
<
n
;
i
++
)
{
self
.
registerOption
(
self
.
settings
.
options
[
i
]);
}
delete
self
.
settings
.
options
;
}
// build optgroup table
if
(
self
.
settings
.
optgroups
)
{
for
(
i
=
0
,
n
=
self
.
settings
.
optgroups
.
length
;
i
<
n
;
i
++
)
{
self
.
registerOptionGroup
(
self
.
settings
.
optgroups
[
i
]);
}
delete
self
.
settings
.
optgroups
;
}
// option-dependent defaults
self
.
settings
.
mode
=
self
.
settings
.
mode
||
(
self
.
settings
.
maxItems
===
1
?
'single'
:
'multi'
);
if
(
typeof
self
.
settings
.
hideSelected
!==
'boolean'
)
{
self
.
settings
.
hideSelected
=
self
.
settings
.
mode
===
'multi'
;
}
self
.
initializePlugins
(
self
.
settings
.
plugins
);
self
.
setupCallbacks
();
self
.
setupTemplates
();
self
.
setup
();
};
// mixins
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MicroEvent
.
mixin
(
Selectize
);
MicroPlugin
.
mixin
(
Selectize
);
// methods
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
$
.
extend
(
Selectize
.
prototype
,
{
/**
* Creates all elements and sets up event bindings.
*/
setup
:
function
()
{
var
self
=
this
;
var
settings
=
self
.
settings
;
var
eventNS
=
self
.
eventNS
;
var
$window
=
$
(
window
);
var
$document
=
$
(
document
);
var
$input
=
self
.
$input
;
var
$wrapper
;
var
$control
;
var
$control_input
;
var
$dropdown
;
var
$dropdown_content
;
var
$dropdown_parent
;
var
inputMode
;
var
timeout_blur
;
var
timeout_focus
;
var
classes
;
var
classes_plugins
;
inputMode
=
self
.
settings
.
mode
;
classes
=
$input
.
attr
(
'class'
)
||
''
;
$wrapper
=
$
(
'<div>'
).
addClass
(
settings
.
wrapperClass
).
addClass
(
classes
).
addClass
(
inputMode
);
$control
=
$
(
'<div>'
).
addClass
(
settings
.
inputClass
).
addClass
(
'items'
).
appendTo
(
$wrapper
);
$control_input
=
$
(
'<input type="text" autocomplete="off" />'
).
appendTo
(
$control
).
attr
(
'tabindex'
,
$input
.
is
(
':disabled'
)
?
'-1'
:
self
.
tabIndex
);
$dropdown_parent
=
$
(
settings
.
dropdownParent
||
$wrapper
);
$dropdown
=
$
(
'<div>'
).
addClass
(
settings
.
dropdownClass
).
addClass
(
inputMode
).
hide
().
appendTo
(
$dropdown_parent
);
$dropdown_content
=
$
(
'<div>'
).
addClass
(
settings
.
dropdownContentClass
).
appendTo
(
$dropdown
);
if
(
self
.
settings
.
copyClassesToDropdown
)
{
$dropdown
.
addClass
(
classes
);
}
$wrapper
.
css
({
width
:
$input
[
0
].
style
.
width
});
if
(
self
.
plugins
.
names
.
length
)
{
classes_plugins
=
'plugin-'
+
self
.
plugins
.
names
.
join
(
' plugin-'
);
$wrapper
.
addClass
(
classes_plugins
);
$dropdown
.
addClass
(
classes_plugins
);
}
if
((
settings
.
maxItems
===
null
||
settings
.
maxItems
>
1
)
&&
self
.
tagType
===
TAG_SELECT
)
{
$input
.
attr
(
'multiple'
,
'multiple'
);
}
if
(
self
.
settings
.
placeholder
)
{
$control_input
.
attr
(
'placeholder'
,
settings
.
placeholder
);
}
// if splitOn was not passed in, construct it from the delimiter to allow pasting universally
if
(
!
self
.
settings
.
splitOn
&&
self
.
settings
.
delimiter
)
{
var
delimiterEscaped
=
self
.
settings
.
delimiter
.
replace
(
/
[
-
\/\\
^$*+?.()|[
\]
{}
]
/g
,
'
\\
$&'
);
self
.
settings
.
splitOn
=
new
RegExp
(
'
\\
s*'
+
delimiterEscaped
+
'+
\\
s*'
);
}
if
(
$input
.
attr
(
'autocorrect'
))
{
$control_input
.
attr
(
'autocorrect'
,
$input
.
attr
(
'autocorrect'
));
}
if
(
$input
.
attr
(
'autocapitalize'
))
{
$control_input
.
attr
(
'autocapitalize'
,
$input
.
attr
(
'autocapitalize'
));
}
self
.
$wrapper
=
$wrapper
;
self
.
$control
=
$control
;
self
.
$control_input
=
$control_input
;
self
.
$dropdown
=
$dropdown
;
self
.
$dropdown_content
=
$dropdown_content
;
$dropdown
.
on
(
'mouseenter'
,
'[data-selectable]'
,
function
()
{
return
self
.
onOptionHover
.
apply
(
self
,
arguments
);
});
$dropdown
.
on
(
'mousedown click'
,
'[data-selectable]'
,
function
()
{
return
self
.
onOptionSelect
.
apply
(
self
,
arguments
);
});
watchChildEvent
(
$control
,
'mousedown'
,
'*:not(input)'
,
function
()
{
return
self
.
onItemSelect
.
apply
(
self
,
arguments
);
});
autoGrow
(
$control_input
);
$control
.
on
({
mousedown
:
function
()
{
return
self
.
onMouseDown
.
apply
(
self
,
arguments
);
},
click
:
function
()
{
return
self
.
onClick
.
apply
(
self
,
arguments
);
}
});
$control_input
.
on
({
mousedown
:
function
(
e
)
{
e
.
stopPropagation
();
},
keydown
:
function
()
{
return
self
.
onKeyDown
.
apply
(
self
,
arguments
);
},
keyup
:
function
()
{
return
self
.
onKeyUp
.
apply
(
self
,
arguments
);
},
keypress
:
function
()
{
return
self
.
onKeyPress
.
apply
(
self
,
arguments
);
},
resize
:
function
()
{
self
.
positionDropdown
.
apply
(
self
,
[]);
},
blur
:
function
()
{
return
self
.
onBlur
.
apply
(
self
,
arguments
);
},
focus
:
function
()
{
self
.
ignoreBlur
=
false
;
return
self
.
onFocus
.
apply
(
self
,
arguments
);
},
paste
:
function
()
{
return
self
.
onPaste
.
apply
(
self
,
arguments
);
}
});
$document
.
on
(
'keydown'
+
eventNS
,
function
(
e
)
{
self
.
isCmdDown
=
e
[
IS_MAC
?
'metaKey'
:
'ctrlKey'
];
self
.
isCtrlDown
=
e
[
IS_MAC
?
'altKey'
:
'ctrlKey'
];
self
.
isShiftDown
=
e
.
shiftKey
;
});
$document
.
on
(
'keyup'
+
eventNS
,
function
(
e
)
{
if
(
e
.
keyCode
===
KEY_CTRL
)
self
.
isCtrlDown
=
false
;
if
(
e
.
keyCode
===
KEY_SHIFT
)
self
.
isShiftDown
=
false
;
if
(
e
.
keyCode
===
KEY_CMD
)
self
.
isCmdDown
=
false
;
});
$document
.
on
(
'mousedown'
+
eventNS
,
function
(
e
)
{
if
(
self
.
isFocused
)
{
// prevent events on the dropdown scrollbar from causing the control to blur
if
(
e
.
target
===
self
.
$dropdown
[
0
]
||
e
.
target
.
parentNode
===
self
.
$dropdown
[
0
])
{
return
false
;
}
// blur on click outside
if
(
!
self
.
$control
.
has
(
e
.
target
).
length
&&
e
.
target
!==
self
.
$control
[
0
])
{
self
.
blur
(
e
.
target
);
}
}
});
$window
.
on
([
'scroll'
+
eventNS
,
'resize'
+
eventNS
].
join
(
' '
),
function
()
{
if
(
self
.
isOpen
)
{
self
.
positionDropdown
.
apply
(
self
,
arguments
);
}
});
$window
.
on
(
'mousemove'
+
eventNS
,
function
()
{
self
.
ignoreHover
=
false
;
});
// store original children and tab index so that they can be
// restored when the destroy() method is called.
this
.
revertSettings
=
{
$children
:
$input
.
children
().
detach
(),
tabindex
:
$input
.
attr
(
'tabindex'
)
};
$input
.
attr
(
'tabindex'
,
-
1
).
hide
().
after
(
self
.
$wrapper
);
if
(
$
.
isArray
(
settings
.
items
))
{
self
.
setValue
(
settings
.
items
);
delete
settings
.
items
;
}
// feature detect for the validation API
if
(
SUPPORTS_VALIDITY_API
)
{
$input
.
on
(
'invalid'
+
eventNS
,
function
(
e
)
{
e
.
preventDefault
();
self
.
isInvalid
=
true
;
self
.
refreshState
();
});
}
self
.
updateOriginalInput
();
self
.
refreshItems
();
self
.
refreshState
();
self
.
updatePlaceholder
();
self
.
isSetup
=
true
;
if
(
$input
.
is
(
':disabled'
))
{
self
.
disable
();
}
self
.
on
(
'change'
,
this
.
onChange
);
$input
.
data
(
'selectize'
,
self
);
$input
.
addClass
(
'selectized'
);
self
.
trigger
(
'initialize'
);
// preload options
if
(
settings
.
preload
===
true
)
{
self
.
onSearchChange
(
''
);
}
},
/**
* Sets up default rendering functions.
*/
setupTemplates
:
function
()
{
var
self
=
this
;
var
field_label
=
self
.
settings
.
labelField
;
var
field_optgroup
=
self
.
settings
.
optgroupLabelField
;
var
templates
=
{
'optgroup'
:
function
(
data
)
{
return
'<div class="optgroup">'
+
data
.
html
+
'</div>'
;
},
'optgroup_header'
:
function
(
data
,
escape
)
{
return
'<div class="optgroup-header">'
+
escape
(
data
[
field_optgroup
])
+
'</div>'
;
},
'option'
:
function
(
data
,
escape
)
{
return
'<div class="option">'
+
escape
(
data
[
field_label
])
+
'</div>'
;
},
'item'
:
function
(
data
,
escape
)
{
return
'<div class="item">'
+
escape
(
data
[
field_label
])
+
'</div>'
;
},
'option_create'
:
function
(
data
,
escape
)
{
return
'<div class="create">Add <strong>'
+
escape
(
data
.
input
)
+
'</strong>…</div>'
;
}
};
self
.
settings
.
render
=
$
.
extend
({},
templates
,
self
.
settings
.
render
);
},
/**
* Maps fired events to callbacks provided
* in the settings used when creating the control.
*/
setupCallbacks
:
function
()
{
var
key
,
fn
,
callbacks
=
{
'initialize'
:
'onInitialize'
,
'change'
:
'onChange'
,
'item_add'
:
'onItemAdd'
,
'item_remove'
:
'onItemRemove'
,
'clear'
:
'onClear'
,
'option_add'
:
'onOptionAdd'
,
'option_remove'
:
'onOptionRemove'
,
'option_clear'
:
'onOptionClear'
,
'optgroup_add'
:
'onOptionGroupAdd'
,
'optgroup_remove'
:
'onOptionGroupRemove'
,
'optgroup_clear'
:
'onOptionGroupClear'
,
'dropdown_open'
:
'onDropdownOpen'
,
'dropdown_close'
:
'onDropdownClose'
,
'type'
:
'onType'
,
'load'
:
'onLoad'
,
'focus'
:
'onFocus'
,
'blur'
:
'onBlur'
};
for
(
key
in
callbacks
)
{
if
(
callbacks
.
hasOwnProperty
(
key
))
{
fn
=
this
.
settings
[
callbacks
[
key
]];
if
(
fn
)
this
.
on
(
key
,
fn
);
}
}
},
/**
* Triggered when the main control element
* has a click event.
*
* @param {object} e
* @return {boolean}
*/
onClick
:
function
(
e
)
{
var
self
=
this
;
// necessary for mobile webkit devices (manual focus triggering
// is ignored unless invoked within a click event)
if
(
!
self
.
isFocused
)
{
self
.
focus
();
e
.
preventDefault
();
}
},
/**
* Triggered when the main control element
* has a mouse down event.
*
* @param {object} e
* @return {boolean}
*/
onMouseDown
:
function
(
e
)
{
var
self
=
this
;
var
defaultPrevented
=
e
.
isDefaultPrevented
();
var
$target
=
$
(
e
.
target
);
if
(
self
.
isFocused
)
{
// retain focus by preventing native handling. if the
// event target is the input it should not be modified.
// otherwise, text selection within the input won't work.
if
(
e
.
target
!==
self
.
$control_input
[
0
])
{
if
(
self
.
settings
.
mode
===
'single'
)
{
// toggle dropdown
self
.
isOpen
?
self
.
close
()
:
self
.
open
();
}
else
if
(
!
defaultPrevented
)
{
self
.
setActiveItem
(
null
);
}
return
false
;
}
}
else
{
// give control focus
if
(
!
defaultPrevented
)
{
window
.
setTimeout
(
function
()
{
self
.
focus
();
},
0
);
}
}
},
/**
* Triggered when the value of the control has been changed.
* This should propagate the event to the original DOM
* input / select element.
*/
onChange
:
function
()
{
this
.
$input
.
trigger
(
'change'
);
},
/**
* Triggered on <input> paste.
*
* @param {object} e
* @returns {boolean}
*/
onPaste
:
function
(
e
)
{
var
self
=
this
;
if
(
self
.
isFull
()
||
self
.
isInputHidden
||
self
.
isLocked
)
{
e
.
preventDefault
();
}
else
{
// If a regex or string is included, this will split the pasted
// input and create Items for each separate value
if
(
self
.
settings
.
splitOn
)
{
setTimeout
(
function
()
{
var
splitInput
=
$
.
trim
(
self
.
$control_input
.
val
()
||
''
).
split
(
self
.
settings
.
splitOn
);
for
(
var
i
=
0
,
n
=
splitInput
.
length
;
i
<
n
;
i
++
)
{
self
.
createItem
(
splitInput
[
i
]);
}
},
0
);
}
}
},
/**
* Triggered on <input> keypress.
*
* @param {object} e
* @returns {boolean}
*/
onKeyPress
:
function
(
e
)
{
if
(
this
.
isLocked
)
return
e
&&
e
.
preventDefault
();
var
character
=
String
.
fromCharCode
(
e
.
keyCode
||
e
.
which
);
if
(
this
.
settings
.
create
&&
this
.
settings
.
mode
===
'multi'
&&
character
===
this
.
settings
.
delimiter
)
{
this
.
createItem
();
e
.
preventDefault
();
return
false
;
}
},
/**
* Triggered on <input> keydown.
*
* @param {object} e
* @returns {boolean}
*/
onKeyDown
:
function
(
e
)
{
var
isInput
=
e
.
target
===
this
.
$control_input
[
0
];
var
self
=
this
;
if
(
self
.
isLocked
)
{
if
(
e
.
keyCode
!==
KEY_TAB
)
{
e
.
preventDefault
();
}
return
;
}
switch
(
e
.
keyCode
)
{
case
KEY_A
:
if
(
self
.
isCmdDown
)
{
self
.
selectAll
();
return
;
}
break
;
case
KEY_ESC
:
if
(
self
.
isOpen
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
self
.
close
();
}
return
;
case
KEY_N
:
if
(
!
e
.
ctrlKey
||
e
.
altKey
)
break
;
case
KEY_DOWN
:
if
(
!
self
.
isOpen
&&
self
.
hasOptions
)
{
self
.
open
();
}
else
if
(
self
.
$activeOption
)
{
self
.
ignoreHover
=
true
;
var
$next
=
self
.
getAdjacentOption
(
self
.
$activeOption
,
1
);
if
(
$next
.
length
)
self
.
setActiveOption
(
$next
,
true
,
true
);
}
e
.
preventDefault
();
return
;
case
KEY_P
:
if
(
!
e
.
ctrlKey
||
e
.
altKey
)
break
;
case
KEY_UP
:
if
(
self
.
$activeOption
)
{
self
.
ignoreHover
=
true
;
var
$prev
=
self
.
getAdjacentOption
(
self
.
$activeOption
,
-
1
);
if
(
$prev
.
length
)
self
.
setActiveOption
(
$prev
,
true
,
true
);
}
e
.
preventDefault
();
return
;
case
KEY_RETURN
:
if
(
self
.
isOpen
&&
self
.
$activeOption
)
{
self
.
onOptionSelect
({
currentTarget
:
self
.
$activeOption
});
e
.
preventDefault
();
}
return
;
case
KEY_LEFT
:
self
.
advanceSelection
(
-
1
,
e
);
return
;
case
KEY_RIGHT
:
self
.
advanceSelection
(
1
,
e
);
return
;
case
KEY_TAB
:
if
(
self
.
settings
.
selectOnTab
&&
self
.
isOpen
&&
self
.
$activeOption
)
{
self
.
onOptionSelect
({
currentTarget
:
self
.
$activeOption
});
// Default behaviour is to jump to the next field, we only want this
// if the current field doesn't accept any more entries
if
(
!
self
.
isFull
())
{
e
.
preventDefault
();
}
}
if
(
self
.
settings
.
create
&&
self
.
createItem
())
{
e
.
preventDefault
();
}
return
;
case
KEY_BACKSPACE
:
case
KEY_DELETE
:
self
.
deleteSelection
(
e
);
return
;
}
if
((
self
.
isFull
()
||
self
.
isInputHidden
)
&&
!
(
IS_MAC
?
e
.
metaKey
:
e
.
ctrlKey
))
{
e
.
preventDefault
();
return
;
}
},
/**
* Triggered on <input> keyup.
*
* @param {object} e
* @returns {boolean}
*/
onKeyUp
:
function
(
e
)
{
var
self
=
this
;
if
(
self
.
isLocked
)
return
e
&&
e
.
preventDefault
();
var
value
=
self
.
$control_input
.
val
()
||
''
;
if
(
self
.
lastValue
!==
value
)
{
self
.
lastValue
=
value
;
self
.
onSearchChange
(
value
);
self
.
refreshOptions
();
self
.
trigger
(
'type'
,
value
);
}
},
/**
* Invokes the user-provide option provider / loader.
*
* Note: this function is debounced in the Selectize
* constructor (by `settings.loadDelay` milliseconds)
*
* @param {string} value
*/
onSearchChange
:
function
(
value
)
{
var
self
=
this
;
var
fn
=
self
.
settings
.
load
;
if
(
!
fn
)
return
;
if
(
self
.
loadedSearches
.
hasOwnProperty
(
value
))
return
;
self
.
loadedSearches
[
value
]
=
true
;
self
.
load
(
function
(
callback
)
{
fn
.
apply
(
self
,
[
value
,
callback
]);
});
},
/**
* Triggered on <input> focus.
*
* @param {object} e (optional)
* @returns {boolean}
*/
onFocus
:
function
(
e
)
{
var
self
=
this
;
var
wasFocused
=
self
.
isFocused
;
if
(
self
.
isDisabled
)
{
self
.
blur
();
e
&&
e
.
preventDefault
();
return
false
;
}
if
(
self
.
ignoreFocus
)
return
;
self
.
isFocused
=
true
;
if
(
self
.
settings
.
preload
===
'focus'
)
self
.
onSearchChange
(
''
);
if
(
!
wasFocused
)
self
.
trigger
(
'focus'
);
if
(
!
self
.
$activeItems
.
length
)
{
self
.
showInput
();
self
.
setActiveItem
(
null
);
self
.
refreshOptions
(
!!
self
.
settings
.
openOnFocus
);
}
self
.
refreshState
();
},
/**
* Triggered on <input> blur.
*
* @param {object} e
* @param {Element} dest
*/
onBlur
:
function
(
e
,
dest
)
{
var
self
=
this
;
if
(
!
self
.
isFocused
)
return
;
self
.
isFocused
=
false
;
if
(
self
.
ignoreFocus
)
{
return
;
}
else
if
(
!
self
.
ignoreBlur
&&
document
.
activeElement
===
self
.
$dropdown_content
[
0
])
{
// necessary to prevent IE closing the dropdown when the scrollbar is clicked
self
.
ignoreBlur
=
true
;
self
.
onFocus
(
e
);
return
;
}
var
deactivate
=
function
()
{
self
.
close
();
self
.
setTextboxValue
(
''
);
self
.
setActiveItem
(
null
);
self
.
setActiveOption
(
null
);
self
.
setCaret
(
self
.
items
.
length
);
self
.
refreshState
();
// IE11 bug: element still marked as active
(
dest
||
document
.
body
).
focus
();
self
.
ignoreFocus
=
false
;
self
.
trigger
(
'blur'
);
};
self
.
ignoreFocus
=
true
;
if
(
self
.
settings
.
create
&&
self
.
settings
.
createOnBlur
)
{
self
.
createItem
(
null
,
false
,
deactivate
);
}
else
{
deactivate
();
}
},
/**
* Triggered when the user rolls over
* an option in the autocomplete dropdown menu.
*
* @param {object} e
* @returns {boolean}
*/
onOptionHover
:
function
(
e
)
{
if
(
this
.
ignoreHover
)
return
;
this
.
setActiveOption
(
e
.
currentTarget
,
false
);
},
/**
* Triggered when the user clicks on an option
* in the autocomplete dropdown menu.
*
* @param {object} e
* @returns {boolean}
*/
onOptionSelect
:
function
(
e
)
{
var
value
,
$target
,
$option
,
self
=
this
;
if
(
e
.
preventDefault
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
}
$target
=
$
(
e
.
currentTarget
);
if
(
$target
.
hasClass
(
'create'
))
{
self
.
createItem
(
null
,
function
()
{
if
(
self
.
settings
.
closeAfterSelect
)
{
self
.
close
();
}
});
}
else
{
value
=
$target
.
attr
(
'data-value'
);
if
(
typeof
value
!==
'undefined'
)
{
self
.
lastQuery
=
null
;
self
.
setTextboxValue
(
''
);
self
.
addItem
(
value
);
if
(
self
.
settings
.
closeAfterSelect
)
{
self
.
close
();
}
else
if
(
!
self
.
settings
.
hideSelected
&&
e
.
type
&&
/mouse/
.
test
(
e
.
type
))
{
self
.
setActiveOption
(
self
.
getOption
(
value
));
}
}
}
},
/**
* Triggered when the user clicks on an item
* that has been selected.
*
* @param {object} e
* @returns {boolean}
*/
onItemSelect
:
function
(
e
)
{
var
self
=
this
;
if
(
self
.
isLocked
)
return
;
if
(
self
.
settings
.
mode
===
'multi'
)
{
e
.
preventDefault
();
self
.
setActiveItem
(
e
.
currentTarget
,
e
);
}
},
/**
* Invokes the provided method that provides
* results to a callback---which are then added
* as options to the control.
*
* @param {function} fn
*/
load
:
function
(
fn
)
{
var
self
=
this
;
var
$wrapper
=
self
.
$wrapper
.
addClass
(
self
.
settings
.
loadingClass
);
self
.
loading
++
;
fn
.
apply
(
self
,
[
function
(
results
)
{
self
.
loading
=
Math
.
max
(
self
.
loading
-
1
,
0
);
if
(
results
&&
results
.
length
)
{
self
.
addOption
(
results
);
self
.
refreshOptions
(
self
.
isFocused
&&
!
self
.
isInputHidden
);
}
if
(
!
self
.
loading
)
{
$wrapper
.
removeClass
(
self
.
settings
.
loadingClass
);
}
self
.
trigger
(
'load'
,
results
);
}]);
},
/**
* Sets the input field of the control to the specified value.
*
* @param {string} value
*/
setTextboxValue
:
function
(
value
)
{
var
$input
=
this
.
$control_input
;
var
changed
=
$input
.
val
()
!==
value
;
if
(
changed
)
{
$input
.
val
(
value
).
triggerHandler
(
'update'
);
this
.
lastValue
=
value
;
}
},
/**
* Returns the value of the control. If multiple items
* can be selected (e.g. <select multiple>), this returns
* an array. If only one item can be selected, this
* returns a string.
*
* @returns {mixed}
*/
getValue
:
function
()
{
if
(
this
.
tagType
===
TAG_SELECT
&&
this
.
$input
.
attr
(
'multiple'
))
{
return
this
.
items
;
}
else
{
return
this
.
items
.
join
(
this
.
settings
.
delimiter
);
}
},
/**
* Resets the selected items to the given value.
*
* @param {mixed} value
*/
setValue
:
function
(
value
,
silent
)
{
var
events
=
silent
?
[]
:
[
'change'
];
debounce_events
(
this
,
events
,
function
()
{
this
.
clear
();
this
.
addItems
(
value
,
silent
);
});
},
/**
* Sets the selected item.
*
* @param {object} $item
* @param {object} e (optional)
*/
setActiveItem
:
function
(
$item
,
e
)
{
var
self
=
this
;
var
eventName
;
var
i
,
idx
,
begin
,
end
,
item
,
swap
;
var
$last
;
if
(
self
.
settings
.
mode
===
'single'
)
return
;
$item
=
$
(
$item
);
// clear the active selection
if
(
!
$item
.
length
)
{
$
(
self
.
$activeItems
).
removeClass
(
'active'
);
self
.
$activeItems
=
[];
if
(
self
.
isFocused
)
{
self
.
showInput
();
}
return
;
}
// modify selection
eventName
=
e
&&
e
.
type
.
toLowerCase
();
if
(
eventName
===
'mousedown'
&&
self
.
isShiftDown
&&
self
.
$activeItems
.
length
)
{
$last
=
self
.
$control
.
children
(
'.active:last'
);
begin
=
Array
.
prototype
.
indexOf
.
apply
(
self
.
$control
[
0
].
childNodes
,
[
$last
[
0
]]);
end
=
Array
.
prototype
.
indexOf
.
apply
(
self
.
$control
[
0
].
childNodes
,
[
$item
[
0
]]);
if
(
begin
>
end
)
{
swap
=
begin
;
begin
=
end
;
end
=
swap
;
}
for
(
i
=
begin
;
i
<=
end
;
i
++
)
{
item
=
self
.
$control
[
0
].
childNodes
[
i
];
if
(
self
.
$activeItems
.
indexOf
(
item
)
===
-
1
)
{
$
(
item
).
addClass
(
'active'
);
self
.
$activeItems
.
push
(
item
);
}
}
e
.
preventDefault
();
}
else
if
((
eventName
===
'mousedown'
&&
self
.
isCtrlDown
)
||
(
eventName
===
'keydown'
&&
this
.
isShiftDown
))
{
if
(
$item
.
hasClass
(
'active'
))
{
idx
=
self
.
$activeItems
.
indexOf
(
$item
[
0
]);
self
.
$activeItems
.
splice
(
idx
,
1
);
$item
.
removeClass
(
'active'
);
}
else
{
self
.
$activeItems
.
push
(
$item
.
addClass
(
'active'
)[
0
]);
}
}
else
{
$
(
self
.
$activeItems
).
removeClass
(
'active'
);
self
.
$activeItems
=
[
$item
.
addClass
(
'active'
)[
0
]];
}
// ensure control has focus
self
.
hideInput
();
if
(
!
this
.
isFocused
)
{
self
.
focus
();
}
},
/**
* Sets the selected item in the dropdown menu
* of available options.
*
* @param {object} $object
* @param {boolean} scroll
* @param {boolean} animate
*/
setActiveOption
:
function
(
$option
,
scroll
,
animate
)
{
var
height_menu
,
height_item
,
y
;
var
scroll_top
,
scroll_bottom
;
var
self
=
this
;
if
(
self
.
$activeOption
)
self
.
$activeOption
.
removeClass
(
'active'
);
self
.
$activeOption
=
null
;
$option
=
$
(
$option
);
if
(
!
$option
.
length
)
return
;
self
.
$activeOption
=
$option
.
addClass
(
'active'
);
if
(
scroll
||
!
isset
(
scroll
))
{
height_menu
=
self
.
$dropdown_content
.
height
();
height_item
=
self
.
$activeOption
.
outerHeight
(
true
);
scroll
=
self
.
$dropdown_content
.
scrollTop
()
||
0
;
y
=
self
.
$activeOption
.
offset
().
top
-
self
.
$dropdown_content
.
offset
().
top
+
scroll
;
scroll_top
=
y
;
scroll_bottom
=
y
-
height_menu
+
height_item
;
if
(
y
+
height_item
>
height_menu
+
scroll
)
{
self
.
$dropdown_content
.
stop
().
animate
({
scrollTop
:
scroll_bottom
},
animate
?
self
.
settings
.
scrollDuration
:
0
);
}
else
if
(
y
<
scroll
)
{
self
.
$dropdown_content
.
stop
().
animate
({
scrollTop
:
scroll_top
},
animate
?
self
.
settings
.
scrollDuration
:
0
);
}
}
},
/**
* Selects all items (CTRL + A).
*/
selectAll
:
function
()
{
var
self
=
this
;
if
(
self
.
settings
.
mode
===
'single'
)
return
;
self
.
$activeItems
=
Array
.
prototype
.
slice
.
apply
(
self
.
$control
.
children
(
':not(input)'
).
addClass
(
'active'
));
if
(
self
.
$activeItems
.
length
)
{
self
.
hideInput
();
self
.
close
();
}
self
.
focus
();
},
/**
* Hides the input element out of view, while
* retaining its focus.
*/
hideInput
:
function
()
{
var
self
=
this
;
self
.
setTextboxValue
(
''
);
self
.
$control_input
.
css
({
opacity
:
0
,
position
:
'absolute'
,
left
:
self
.
rtl
?
10000
:
-
10000
});
self
.
isInputHidden
=
true
;
},
/**
* Restores input visibility.
*/
showInput
:
function
()
{
this
.
$control_input
.
css
({
opacity
:
1
,
position
:
'relative'
,
left
:
0
});
this
.
isInputHidden
=
false
;
},
/**
* Gives the control focus.
*/
focus
:
function
()
{
var
self
=
this
;
if
(
self
.
isDisabled
)
return
;
self
.
ignoreFocus
=
true
;
self
.
$control_input
[
0
].
focus
();
window
.
setTimeout
(
function
()
{
self
.
ignoreFocus
=
false
;
self
.
onFocus
();
},
0
);
},
/**
* Forces the control out of focus.
*
* @param {Element} dest
*/
blur
:
function
(
dest
)
{
this
.
$control_input
[
0
].
blur
();
this
.
onBlur
(
null
,
dest
);
},
/**
* Returns a function that scores an object
* to show how good of a match it is to the
* provided query.
*
* @param {string} query
* @param {object} options
* @return {function}
*/
getScoreFunction
:
function
(
query
)
{
return
this
.
sifter
.
getScoreFunction
(
query
,
this
.
getSearchOptions
());
},
/**
* Returns search options for sifter (the system
* for scoring and sorting results).
*
* @see https://github.com/brianreavis/sifter.js
* @return {object}
*/
getSearchOptions
:
function
()
{
var
settings
=
this
.
settings
;
var
sort
=
settings
.
sortField
;
if
(
typeof
sort
===
'string'
)
{
sort
=
[{
field
:
sort
}];
}
return
{
fields
:
settings
.
searchField
,
conjunction
:
settings
.
searchConjunction
,
sort
:
sort
};
},
/**
* Searches through available options and returns
* a sorted array of matches.
*
* Returns an object containing:
*
* - query {string}
* - tokens {array}
* - total {int}
* - items {array}
*
* @param {string} query
* @returns {object}
*/
search
:
function
(
query
)
{
var
i
,
value
,
score
,
result
,
calculateScore
;
var
self
=
this
;
var
settings
=
self
.
settings
;
var
options
=
this
.
getSearchOptions
();
// validate user-provided result scoring function
if
(
settings
.
score
)
{
calculateScore
=
self
.
settings
.
score
.
apply
(
this
,
[
query
]);
if
(
typeof
calculateScore
!==
'function'
)
{
throw
new
Error
(
'Selectize "score" setting must be a function that returns a function'
);
}
}
// perform search
if
(
query
!==
self
.
lastQuery
)
{
self
.
lastQuery
=
query
;
result
=
self
.
sifter
.
search
(
query
,
$
.
extend
(
options
,
{
score
:
calculateScore
}));
self
.
currentResults
=
result
;
}
else
{
result
=
$
.
extend
(
true
,
{},
self
.
currentResults
);
}
// filter out selected items
if
(
settings
.
hideSelected
)
{
for
(
i
=
result
.
items
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
self
.
items
.
indexOf
(
hash_key
(
result
.
items
[
i
].
id
))
!==
-
1
)
{
result
.
items
.
splice
(
i
,
1
);
}
}
}
return
result
;
},
/**
* Refreshes the list of available options shown
* in the autocomplete dropdown menu.
*
* @param {boolean} triggerDropdown
*/
refreshOptions
:
function
(
triggerDropdown
)
{
var
i
,
j
,
k
,
n
,
groups
,
groups_order
,
option
,
option_html
,
optgroup
,
optgroups
,
html
,
html_children
,
has_create_option
;
var
$active
,
$active_before
,
$create
;
if
(
typeof
triggerDropdown
===
'undefined'
)
{
triggerDropdown
=
true
;
}
var
self
=
this
;
var
query
=
$
.
trim
(
self
.
$control_input
.
val
());
var
results
=
self
.
search
(
query
);
var
$dropdown_content
=
self
.
$dropdown_content
;
var
active_before
=
self
.
$activeOption
&&
hash_key
(
self
.
$activeOption
.
attr
(
'data-value'
));
// build markup
n
=
results
.
items
.
length
;
if
(
typeof
self
.
settings
.
maxOptions
===
'number'
)
{
n
=
Math
.
min
(
n
,
self
.
settings
.
maxOptions
);
}
// render and group available options individually
groups
=
{};
groups_order
=
[];
for
(
i
=
0
;
i
<
n
;
i
++
)
{
option
=
self
.
options
[
results
.
items
[
i
].
id
];
option_html
=
self
.
render
(
'option'
,
option
);
optgroup
=
option
[
self
.
settings
.
optgroupField
]
||
''
;
optgroups
=
$
.
isArray
(
optgroup
)
?
optgroup
:
[
optgroup
];
for
(
j
=
0
,
k
=
optgroups
&&
optgroups
.
length
;
j
<
k
;
j
++
)
{
optgroup
=
optgroups
[
j
];
if
(
!
self
.
optgroups
.
hasOwnProperty
(
optgroup
))
{
optgroup
=
''
;
}
if
(
!
groups
.
hasOwnProperty
(
optgroup
))
{
groups
[
optgroup
]
=
[];
groups_order
.
push
(
optgroup
);
}
groups
[
optgroup
].
push
(
option_html
);
}
}
// sort optgroups
if
(
this
.
settings
.
lockOptgroupOrder
)
{
groups_order
.
sort
(
function
(
a
,
b
)
{
var
a_order
=
self
.
optgroups
[
a
].
$order
||
0
;
var
b_order
=
self
.
optgroups
[
b
].
$order
||
0
;
return
a_order
-
b_order
;
});
}
// render optgroup headers & join groups
html
=
[];
for
(
i
=
0
,
n
=
groups_order
.
length
;
i
<
n
;
i
++
)
{
optgroup
=
groups_order
[
i
];
if
(
self
.
optgroups
.
hasOwnProperty
(
optgroup
)
&&
groups
[
optgroup
].
length
)
{
// render the optgroup header and options within it,
// then pass it to the wrapper template
html_children
=
self
.
render
(
'optgroup_header'
,
self
.
optgroups
[
optgroup
])
||
''
;
html_children
+=
groups
[
optgroup
].
join
(
''
);
html
.
push
(
self
.
render
(
'optgroup'
,
$
.
extend
({},
self
.
optgroups
[
optgroup
],
{
html
:
html_children
})));
}
else
{
html
.
push
(
groups
[
optgroup
].
join
(
''
));
}
}
$dropdown_content
.
html
(
html
.
join
(
''
));
// highlight matching terms inline
if
(
self
.
settings
.
highlight
&&
results
.
query
.
length
&&
results
.
tokens
.
length
)
{
for
(
i
=
0
,
n
=
results
.
tokens
.
length
;
i
<
n
;
i
++
)
{
highlight
(
$dropdown_content
,
results
.
tokens
[
i
].
regex
);
}
}
// add "selected" class to selected options
if
(
!
self
.
settings
.
hideSelected
)
{
for
(
i
=
0
,
n
=
self
.
items
.
length
;
i
<
n
;
i
++
)
{
self
.
getOption
(
self
.
items
[
i
]).
addClass
(
'selected'
);
}
}
// add create option
has_create_option
=
self
.
canCreate
(
query
);
if
(
has_create_option
)
{
$dropdown_content
.
prepend
(
self
.
render
(
'option_create'
,
{
input
:
query
}));
$create
=
$
(
$dropdown_content
[
0
].
childNodes
[
0
]);
}
// activate
self
.
hasOptions
=
results
.
items
.
length
>
0
||
has_create_option
;
if
(
self
.
hasOptions
)
{
if
(
results
.
items
.
length
>
0
)
{
$active_before
=
active_before
&&
self
.
getOption
(
active_before
);
if
(
$active_before
&&
$active_before
.
length
)
{
$active
=
$active_before
;
}
else
if
(
self
.
settings
.
mode
===
'single'
&&
self
.
items
.
length
)
{
$active
=
self
.
getOption
(
self
.
items
[
0
]);
}
if
(
!
$active
||
!
$active
.
length
)
{
if
(
$create
&&
!
self
.
settings
.
addPrecedence
)
{
$active
=
self
.
getAdjacentOption
(
$create
,
1
);
}
else
{
$active
=
$dropdown_content
.
find
(
'[data-selectable]:first'
);
}
}
}
else
{
$active
=
$create
;
}
self
.
setActiveOption
(
$active
);
if
(
triggerDropdown
&&
!
self
.
isOpen
)
{
self
.
open
();
}
}
else
{
self
.
setActiveOption
(
null
);
if
(
triggerDropdown
&&
self
.
isOpen
)
{
self
.
close
();
}
}
},
/**
* Adds an available option. If it already exists,
* nothing will happen. Note: this does not refresh
* the options list dropdown (use `refreshOptions`
* for that).
*
* Usage:
*
* this.addOption(data)
*
* @param {object|array} data
*/
addOption
:
function
(
data
)
{
var
i
,
n
,
value
,
self
=
this
;
if
(
$
.
isArray
(
data
))
{
for
(
i
=
0
,
n
=
data
.
length
;
i
<
n
;
i
++
)
{
self
.
addOption
(
data
[
i
]);
}
return
;
}
if
(
value
=
self
.
registerOption
(
data
))
{
self
.
userOptions
[
value
]
=
true
;
self
.
lastQuery
=
null
;
self
.
trigger
(
'option_add'
,
value
,
data
);
}
},
/**
* Registers an option to the pool of options.
*
* @param {object} data
* @return {boolean|string}
*/
registerOption
:
function
(
data
)
{
var
key
=
hash_key
(
data
[
this
.
settings
.
valueField
]);
if
(
!
key
||
this
.
options
.
hasOwnProperty
(
key
))
return
false
;
data
.
$order
=
data
.
$order
||
++
this
.
order
;
this
.
options
[
key
]
=
data
;
return
key
;
},
/**
* Registers an option group to the pool of option groups.
*
* @param {object} data
* @return {boolean|string}
*/
registerOptionGroup
:
function
(
data
)
{
var
key
=
hash_key
(
data
[
this
.
settings
.
optgroupValueField
]);
if
(
!
key
)
return
false
;
data
.
$order
=
data
.
$order
||
++
this
.
order
;
this
.
optgroups
[
key
]
=
data
;
return
key
;
},
/**
* Registers a new optgroup for options
* to be bucketed into.
*
* @param {string} id
* @param {object} data
*/
addOptionGroup
:
function
(
id
,
data
)
{
data
[
this
.
settings
.
optgroupValueField
]
=
id
;
if
(
id
=
this
.
registerOptionGroup
(
data
))
{
this
.
trigger
(
'optgroup_add'
,
id
,
data
);
}
},
/**
* Removes an existing option group.
*
* @param {string} id
*/
removeOptionGroup
:
function
(
id
)
{
if
(
this
.
optgroups
.
hasOwnProperty
(
id
))
{
delete
this
.
optgroups
[
id
];
this
.
renderCache
=
{};
this
.
trigger
(
'optgroup_remove'
,
id
);
}
},
/**
* Clears all existing option groups.
*/
clearOptionGroups
:
function
()
{
this
.
optgroups
=
{};
this
.
renderCache
=
{};
this
.
trigger
(
'optgroup_clear'
);
},
/**
* Updates an option available for selection. If
* it is visible in the selected items or options
* dropdown, it will be re-rendered automatically.
*
* @param {string} value
* @param {object} data
*/
updateOption
:
function
(
value
,
data
)
{
var
self
=
this
;
var
$item
,
$item_new
;
var
value_new
,
index_item
,
cache_items
,
cache_options
,
order_old
;
value
=
hash_key
(
value
);
value_new
=
hash_key
(
data
[
self
.
settings
.
valueField
]);
// sanity checks
if
(
value
===
null
)
return
;
if
(
!
self
.
options
.
hasOwnProperty
(
value
))
return
;
if
(
typeof
value_new
!==
'string'
)
throw
new
Error
(
'Value must be set in option data'
);
order_old
=
self
.
options
[
value
].
$order
;
// update references
if
(
value_new
!==
value
)
{
delete
self
.
options
[
value
];
index_item
=
self
.
items
.
indexOf
(
value
);
if
(
index_item
!==
-
1
)
{
self
.
items
.
splice
(
index_item
,
1
,
value_new
);
}
}
data
.
$order
=
data
.
$order
||
order_old
;
self
.
options
[
value_new
]
=
data
;
// invalidate render cache
cache_items
=
self
.
renderCache
[
'item'
];
cache_options
=
self
.
renderCache
[
'option'
];
if
(
cache_items
)
{
delete
cache_items
[
value
];
delete
cache_items
[
value_new
];
}
if
(
cache_options
)
{
delete
cache_options
[
value
];
delete
cache_options
[
value_new
];
}
// update the item if it's selected
if
(
self
.
items
.
indexOf
(
value_new
)
!==
-
1
)
{
$item
=
self
.
getItem
(
value
);
$item_new
=
$
(
self
.
render
(
'item'
,
data
));
if
(
$item
.
hasClass
(
'active'
))
$item_new
.
addClass
(
'active'
);
$item
.
replaceWith
(
$item_new
);
}
// invalidate last query because we might have updated the sortField
self
.
lastQuery
=
null
;
// update dropdown contents
if
(
self
.
isOpen
)
{
self
.
refreshOptions
(
false
);
}
},
/**
* Removes a single option.
*
* @param {string} value
* @param {boolean} silent
*/
removeOption
:
function
(
value
,
silent
)
{
var
self
=
this
;
value
=
hash_key
(
value
);
var
cache_items
=
self
.
renderCache
[
'item'
];
var
cache_options
=
self
.
renderCache
[
'option'
];
if
(
cache_items
)
delete
cache_items
[
value
];
if
(
cache_options
)
delete
cache_options
[
value
];
delete
self
.
userOptions
[
value
];
delete
self
.
options
[
value
];
self
.
lastQuery
=
null
;
self
.
trigger
(
'option_remove'
,
value
);
self
.
removeItem
(
value
,
silent
);
},
/**
* Clears all options.
*/
clearOptions
:
function
()
{
var
self
=
this
;
self
.
loadedSearches
=
{};
self
.
userOptions
=
{};
self
.
renderCache
=
{};
self
.
options
=
self
.
sifter
.
items
=
{};
self
.
lastQuery
=
null
;
self
.
trigger
(
'option_clear'
);
self
.
clear
();
},
/**
* Returns the jQuery element of the option
* matching the given value.
*
* @param {string} value
* @returns {object}
*/
getOption
:
function
(
value
)
{
return
this
.
getElementWithValue
(
value
,
this
.
$dropdown_content
.
find
(
'[data-selectable]'
));
},
/**
* Returns the jQuery element of the next or
* previous selectable option.
*
* @param {object} $option
* @param {int} direction can be 1 for next or -1 for previous
* @return {object}
*/
getAdjacentOption
:
function
(
$option
,
direction
)
{
var
$options
=
this
.
$dropdown
.
find
(
'[data-selectable]'
);
var
index
=
$options
.
index
(
$option
)
+
direction
;
return
index
>=
0
&&
index
<
$options
.
length
?
$options
.
eq
(
index
)
:
$
();
},
/**
* Finds the first element with a "data-value" attribute
* that matches the given value.
*
* @param {mixed} value
* @param {object} $els
* @return {object}
*/
getElementWithValue
:
function
(
value
,
$els
)
{
value
=
hash_key
(
value
);
if
(
typeof
value
!==
'undefined'
&&
value
!==
null
)
{
for
(
var
i
=
0
,
n
=
$els
.
length
;
i
<
n
;
i
++
)
{
if
(
$els
[
i
].
getAttribute
(
'data-value'
)
===
value
)
{
return
$
(
$els
[
i
]);
}
}
}
return
$
();
},
/**
* Returns the jQuery element of the item
* matching the given value.
*
* @param {string} value
* @returns {object}
*/
getItem
:
function
(
value
)
{
return
this
.
getElementWithValue
(
value
,
this
.
$control
.
children
());
},
/**
* "Selects" multiple items at once. Adds them to the list
* at the current caret position.
*
* @param {string} value
* @param {boolean} silent
*/
addItems
:
function
(
values
,
silent
)
{
var
items
=
$
.
isArray
(
values
)
?
values
:
[
values
];
for
(
var
i
=
0
,
n
=
items
.
length
;
i
<
n
;
i
++
)
{
this
.
isPending
=
(
i
<
n
-
1
);
this
.
addItem
(
items
[
i
],
silent
);
}
},
/**
* "Selects" an item. Adds it to the list
* at the current caret position.
*
* @param {string} value
* @param {boolean} silent
*/
addItem
:
function
(
value
,
silent
)
{
var
events
=
silent
?
[]
:
[
'change'
];
debounce_events
(
this
,
events
,
function
()
{
var
$item
,
$option
,
$options
;
var
self
=
this
;
var
inputMode
=
self
.
settings
.
mode
;
var
i
,
active
,
value_next
,
wasFull
;
value
=
hash_key
(
value
);
if
(
self
.
items
.
indexOf
(
value
)
!==
-
1
)
{
if
(
inputMode
===
'single'
)
self
.
close
();
return
;
}
if
(
!
self
.
options
.
hasOwnProperty
(
value
))
return
;
if
(
inputMode
===
'single'
)
self
.
clear
();
if
(
inputMode
===
'multi'
&&
self
.
isFull
())
return
;
$item
=
$
(
self
.
render
(
'item'
,
self
.
options
[
value
]));
wasFull
=
self
.
isFull
();
self
.
items
.
splice
(
self
.
caretPos
,
0
,
value
);
self
.
insertAtCaret
(
$item
);
if
(
!
self
.
isPending
||
(
!
wasFull
&&
self
.
isFull
()))
{
self
.
refreshState
();
}
if
(
self
.
isSetup
)
{
$options
=
self
.
$dropdown_content
.
find
(
'[data-selectable]'
);
// update menu / remove the option (if this is not one item being added as part of series)
if
(
!
self
.
isPending
)
{
$option
=
self
.
getOption
(
value
);
value_next
=
self
.
getAdjacentOption
(
$option
,
1
).
attr
(
'data-value'
);
self
.
refreshOptions
(
self
.
isFocused
&&
inputMode
!==
'single'
);
if
(
value_next
)
{
self
.
setActiveOption
(
self
.
getOption
(
value_next
));
}
}
// hide the menu if the maximum number of items have been selected or no options are left
if
(
!
$options
.
length
||
self
.
isFull
())
{
self
.
close
();
}
else
{
self
.
positionDropdown
();
}
self
.
updatePlaceholder
();
self
.
trigger
(
'item_add'
,
value
,
$item
);
self
.
updateOriginalInput
({
silent
:
silent
});
}
});
},
/**
* Removes the selected item matching
* the provided value.
*
* @param {string} value
*/
removeItem
:
function
(
value
,
silent
)
{
var
self
=
this
;
var
$item
,
i
,
idx
;
$item
=
(
typeof
value
===
'object'
)
?
value
:
self
.
getItem
(
value
);
value
=
hash_key
(
$item
.
attr
(
'data-value'
));
i
=
self
.
items
.
indexOf
(
value
);
if
(
i
!==
-
1
)
{
$item
.
remove
();
if
(
$item
.
hasClass
(
'active'
))
{
idx
=
self
.
$activeItems
.
indexOf
(
$item
[
0
]);
self
.
$activeItems
.
splice
(
idx
,
1
);
}
self
.
items
.
splice
(
i
,
1
);
self
.
lastQuery
=
null
;
if
(
!
self
.
settings
.
persist
&&
self
.
userOptions
.
hasOwnProperty
(
value
))
{
self
.
removeOption
(
value
,
silent
);
}
if
(
i
<
self
.
caretPos
)
{
self
.
setCaret
(
self
.
caretPos
-
1
);
}
self
.
refreshState
();
self
.
updatePlaceholder
();
self
.
updateOriginalInput
({
silent
:
silent
});
self
.
positionDropdown
();
self
.
trigger
(
'item_remove'
,
value
,
$item
);
}
},
/**
* Invokes the `create` method provided in the
* selectize options that should provide the data
* for the new item, given the user input.
*
* Once this completes, it will be added
* to the item list.
*
* @param {string} value
* @param {boolean} [triggerDropdown]
* @param {function} [callback]
* @return {boolean}
*/
createItem
:
function
(
input
,
triggerDropdown
)
{
var
self
=
this
;
var
caret
=
self
.
caretPos
;
input
=
input
||
$
.
trim
(
self
.
$control_input
.
val
()
||
''
);
var
callback
=
arguments
[
arguments
.
length
-
1
];
if
(
typeof
callback
!==
'function'
)
callback
=
function
()
{};
if
(
typeof
triggerDropdown
!==
'boolean'
)
{
triggerDropdown
=
true
;
}
if
(
!
self
.
canCreate
(
input
))
{
callback
();
return
false
;
}
self
.
lock
();
var
setup
=
(
typeof
self
.
settings
.
create
===
'function'
)
?
this
.
settings
.
create
:
function
(
input
)
{
var
data
=
{};
data
[
self
.
settings
.
labelField
]
=
input
;
data
[
self
.
settings
.
valueField
]
=
input
;
return
data
;
};
var
create
=
once
(
function
(
data
)
{
self
.
unlock
();
if
(
!
data
||
typeof
data
!==
'object'
)
return
callback
();
var
value
=
hash_key
(
data
[
self
.
settings
.
valueField
]);
if
(
typeof
value
!==
'string'
)
return
callback
();
self
.
setTextboxValue
(
''
);
self
.
addOption
(
data
);
self
.
setCaret
(
caret
);
self
.
addItem
(
value
);
self
.
refreshOptions
(
triggerDropdown
&&
self
.
settings
.
mode
!==
'single'
);
callback
(
data
);
});
var
output
=
setup
.
apply
(
this
,
[
input
,
create
]);
if
(
typeof
output
!==
'undefined'
)
{
create
(
output
);
}
return
true
;
},
/**
* Re-renders the selected item lists.
*/
refreshItems
:
function
()
{
this
.
lastQuery
=
null
;
if
(
this
.
isSetup
)
{
this
.
addItem
(
this
.
items
);
}
this
.
refreshState
();
this
.
updateOriginalInput
();
},
/**
* Updates all state-dependent attributes
* and CSS classes.
*/
refreshState
:
function
()
{
var
invalid
,
self
=
this
;
if
(
self
.
isRequired
)
{
if
(
self
.
items
.
length
)
self
.
isInvalid
=
false
;
self
.
$control_input
.
prop
(
'required'
,
invalid
);
}
self
.
refreshClasses
();
},
/**
* Updates all state-dependent CSS classes.
*/
refreshClasses
:
function
()
{
var
self
=
this
;
var
isFull
=
self
.
isFull
();
var
isLocked
=
self
.
isLocked
;
self
.
$wrapper
.
toggleClass
(
'rtl'
,
self
.
rtl
);
self
.
$control
.
toggleClass
(
'focus'
,
self
.
isFocused
)
.
toggleClass
(
'disabled'
,
self
.
isDisabled
)
.
toggleClass
(
'required'
,
self
.
isRequired
)
.
toggleClass
(
'invalid'
,
self
.
isInvalid
)
.
toggleClass
(
'locked'
,
isLocked
)
.
toggleClass
(
'full'
,
isFull
).
toggleClass
(
'not-full'
,
!
isFull
)
.
toggleClass
(
'input-active'
,
self
.
isFocused
&&
!
self
.
isInputHidden
)
.
toggleClass
(
'dropdown-active'
,
self
.
isOpen
)
.
toggleClass
(
'has-options'
,
!
$
.
isEmptyObject
(
self
.
options
))
.
toggleClass
(
'has-items'
,
self
.
items
.
length
>
0
);
self
.
$control_input
.
data
(
'grow'
,
!
isFull
&&
!
isLocked
);
},
/**
* Determines whether or not more items can be added
* to the control without exceeding the user-defined maximum.
*
* @returns {boolean}
*/
isFull
:
function
()
{
return
this
.
settings
.
maxItems
!==
null
&&
this
.
items
.
length
>=
this
.
settings
.
maxItems
;
},
/**
* Refreshes the original <select> or <input>
* element to reflect the current state.
*/
updateOriginalInput
:
function
(
opts
)
{
var
i
,
n
,
options
,
label
,
self
=
this
;
opts
=
opts
||
{};
if
(
self
.
tagType
===
TAG_SELECT
)
{
options
=
[];
for
(
i
=
0
,
n
=
self
.
items
.
length
;
i
<
n
;
i
++
)
{
label
=
self
.
options
[
self
.
items
[
i
]][
self
.
settings
.
labelField
]
||
''
;
options
.
push
(
'<option value="'
+
escape_html
(
self
.
items
[
i
])
+
'" selected="selected">'
+
escape_html
(
label
)
+
'</option>'
);
}
if
(
!
options
.
length
&&
!
this
.
$input
.
attr
(
'multiple'
))
{
options
.
push
(
'<option value="" selected="selected"></option>'
);
}
self
.
$input
.
html
(
options
.
join
(
''
));
}
else
{
self
.
$input
.
val
(
self
.
getValue
());
self
.
$input
.
attr
(
'value'
,
self
.
$input
.
val
());
}
if
(
self
.
isSetup
)
{
if
(
!
opts
.
silent
)
{
self
.
trigger
(
'change'
,
self
.
$input
.
val
());
}
}
},
/**
* Shows/hide the input placeholder depending
* on if there items in the list already.
*/
updatePlaceholder
:
function
()
{
if
(
!
this
.
settings
.
placeholder
)
return
;
var
$input
=
this
.
$control_input
;
if
(
this
.
items
.
length
)
{
$input
.
removeAttr
(
'placeholder'
);
}
else
{
$input
.
attr
(
'placeholder'
,
this
.
settings
.
placeholder
);
}
$input
.
triggerHandler
(
'update'
,
{
force
:
true
});
},
/**
* Shows the autocomplete dropdown containing
* the available options.
*/
open
:
function
()
{
var
self
=
this
;
if
(
self
.
isLocked
||
self
.
isOpen
||
(
self
.
settings
.
mode
===
'multi'
&&
self
.
isFull
()))
return
;
self
.
focus
();
self
.
isOpen
=
true
;
self
.
refreshState
();
self
.
$dropdown
.
css
({
visibility
:
'hidden'
,
display
:
'block'
});
self
.
positionDropdown
();
self
.
$dropdown
.
css
({
visibility
:
'visible'
});
self
.
trigger
(
'dropdown_open'
,
self
.
$dropdown
);
},
/**
* Closes the autocomplete dropdown menu.
*/
close
:
function
()
{
var
self
=
this
;
var
trigger
=
self
.
isOpen
;
if
(
self
.
settings
.
mode
===
'single'
&&
self
.
items
.
length
)
{
self
.
hideInput
();
}
self
.
isOpen
=
false
;
self
.
$dropdown
.
hide
();
self
.
setActiveOption
(
null
);
self
.
refreshState
();
if
(
trigger
)
self
.
trigger
(
'dropdown_close'
,
self
.
$dropdown
);
},
/**
* Calculates and applies the appropriate
* position of the dropdown.
*/
positionDropdown
:
function
()
{
var
$control
=
this
.
$control
;
var
offset
=
this
.
settings
.
dropdownParent
===
'body'
?
$control
.
offset
()
:
$control
.
position
();
offset
.
top
+=
$control
.
outerHeight
(
true
);
this
.
$dropdown
.
css
({
width
:
$control
.
outerWidth
(),
top
:
offset
.
top
,
left
:
offset
.
left
});
},
/**
* Resets / clears all selected items
* from the control.
*
* @param {boolean} silent
*/
clear
:
function
(
silent
)
{
var
self
=
this
;
if
(
!
self
.
items
.
length
)
return
;
self
.
$control
.
children
(
':not(input)'
).
remove
();
self
.
items
=
[];
self
.
lastQuery
=
null
;
self
.
setCaret
(
0
);
self
.
setActiveItem
(
null
);
self
.
updatePlaceholder
();
self
.
updateOriginalInput
({
silent
:
silent
});
self
.
refreshState
();
self
.
showInput
();
self
.
trigger
(
'clear'
);
},
/**
* A helper method for inserting an element
* at the current caret position.
*
* @param {object} $el
*/
insertAtCaret
:
function
(
$el
)
{
var
caret
=
Math
.
min
(
this
.
caretPos
,
this
.
items
.
length
);
if
(
caret
===
0
)
{
this
.
$control
.
prepend
(
$el
);
}
else
{
$
(
this
.
$control
[
0
].
childNodes
[
caret
]).
before
(
$el
);
}
this
.
setCaret
(
caret
+
1
);
},
/**
* Removes the current selected item(s).
*
* @param {object} e (optional)
* @returns {boolean}
*/
deleteSelection
:
function
(
e
)
{
var
i
,
n
,
direction
,
selection
,
values
,
caret
,
option_select
,
$option_select
,
$tail
;
var
self
=
this
;
direction
=
(
e
&&
e
.
keyCode
===
KEY_BACKSPACE
)
?
-
1
:
1
;
selection
=
getSelection
(
self
.
$control_input
[
0
]);
if
(
self
.
$activeOption
&&
!
self
.
settings
.
hideSelected
)
{
option_select
=
self
.
getAdjacentOption
(
self
.
$activeOption
,
-
1
).
attr
(
'data-value'
);
}
// determine items that will be removed
values
=
[];
if
(
self
.
$activeItems
.
length
)
{
$tail
=
self
.
$control
.
children
(
'.active:'
+
(
direction
>
0
?
'last'
:
'first'
));
caret
=
self
.
$control
.
children
(
':not(input)'
).
index
(
$tail
);
if
(
direction
>
0
)
{
caret
++
;
}
for
(
i
=
0
,
n
=
self
.
$activeItems
.
length
;
i
<
n
;
i
++
)
{
values
.
push
(
$
(
self
.
$activeItems
[
i
]).
attr
(
'data-value'
));
}
if
(
e
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
}
}
else
if
((
self
.
isFocused
||
self
.
settings
.
mode
===
'single'
)
&&
self
.
items
.
length
)
{
if
(
direction
<
0
&&
selection
.
start
===
0
&&
selection
.
length
===
0
)
{
values
.
push
(
self
.
items
[
self
.
caretPos
-
1
]);
}
else
if
(
direction
>
0
&&
selection
.
start
===
self
.
$control_input
.
val
().
length
)
{
values
.
push
(
self
.
items
[
self
.
caretPos
]);
}
}
// allow the callback to abort
if
(
!
values
.
length
||
(
typeof
self
.
settings
.
onDelete
===
'function'
&&
self
.
settings
.
onDelete
.
apply
(
self
,
[
values
])
===
false
))
{
return
false
;
}
// perform removal
if
(
typeof
caret
!==
'undefined'
)
{
self
.
setCaret
(
caret
);
}
while
(
values
.
length
)
{
self
.
removeItem
(
values
.
pop
());
}
self
.
showInput
();
self
.
positionDropdown
();
self
.
refreshOptions
(
true
);
// select previous option
if
(
option_select
)
{
$option_select
=
self
.
getOption
(
option_select
);
if
(
$option_select
.
length
)
{
self
.
setActiveOption
(
$option_select
);
}
}
return
true
;
},
/**
* Selects the previous / next item (depending
* on the `direction` argument).
*
* > 0 - right
* < 0 - left
*
* @param {int} direction
* @param {object} e (optional)
*/
advanceSelection
:
function
(
direction
,
e
)
{
var
tail
,
selection
,
idx
,
valueLength
,
cursorAtEdge
,
$tail
;
var
self
=
this
;
if
(
direction
===
0
)
return
;
if
(
self
.
rtl
)
direction
*=
-
1
;
tail
=
direction
>
0
?
'last'
:
'first'
;
selection
=
getSelection
(
self
.
$control_input
[
0
]);
if
(
self
.
isFocused
&&
!
self
.
isInputHidden
)
{
valueLength
=
self
.
$control_input
.
val
().
length
;
cursorAtEdge
=
direction
<
0
?
selection
.
start
===
0
&&
selection
.
length
===
0
:
selection
.
start
===
valueLength
;
if
(
cursorAtEdge
&&
!
valueLength
)
{
self
.
advanceCaret
(
direction
,
e
);
}
}
else
{
$tail
=
self
.
$control
.
children
(
'.active:'
+
tail
);
if
(
$tail
.
length
)
{
idx
=
self
.
$control
.
children
(
':not(input)'
).
index
(
$tail
);
self
.
setActiveItem
(
null
);
self
.
setCaret
(
direction
>
0
?
idx
+
1
:
idx
);
}
}
},
/**
* Moves the caret left / right.
*
* @param {int} direction
* @param {object} e (optional)
*/
advanceCaret
:
function
(
direction
,
e
)
{
var
self
=
this
,
fn
,
$adj
;
if
(
direction
===
0
)
return
;
fn
=
direction
>
0
?
'next'
:
'prev'
;
if
(
self
.
isShiftDown
)
{
$adj
=
self
.
$control_input
[
fn
]();
if
(
$adj
.
length
)
{
self
.
hideInput
();
self
.
setActiveItem
(
$adj
);
e
&&
e
.
preventDefault
();
}
}
else
{
self
.
setCaret
(
self
.
caretPos
+
direction
);
}
},
/**
* Moves the caret to the specified index.
*
* @param {int} i
*/
setCaret
:
function
(
i
)
{
var
self
=
this
;
if
(
self
.
settings
.
mode
===
'single'
)
{
i
=
self
.
items
.
length
;
}
else
{
i
=
Math
.
max
(
0
,
Math
.
min
(
self
.
items
.
length
,
i
));
}
if
(
!
self
.
isPending
)
{
// the input must be moved by leaving it in place and moving the
// siblings, due to the fact that focus cannot be restored once lost
// on mobile webkit devices
var
j
,
n
,
fn
,
$children
,
$child
;
$children
=
self
.
$control
.
children
(
':not(input)'
);
for
(
j
=
0
,
n
=
$children
.
length
;
j
<
n
;
j
++
)
{
$child
=
$
(
$children
[
j
]).
detach
();
if
(
j
<
i
)
{
self
.
$control_input
.
before
(
$child
);
}
else
{
self
.
$control
.
append
(
$child
);
}
}
}
self
.
caretPos
=
i
;
},
/**
* Disables user input on the control. Used while
* items are being asynchronously created.
*/
lock
:
function
()
{
this
.
close
();
this
.
isLocked
=
true
;
this
.
refreshState
();
},
/**
* Re-enables user input on the control.
*/
unlock
:
function
()
{
this
.
isLocked
=
false
;
this
.
refreshState
();
},
/**
* Disables user input on the control completely.
* While disabled, it cannot receive focus.
*/
disable
:
function
()
{
var
self
=
this
;
self
.
$input
.
prop
(
'disabled'
,
true
);
self
.
$control_input
.
prop
(
'disabled'
,
true
).
prop
(
'tabindex'
,
-
1
);
self
.
isDisabled
=
true
;
self
.
lock
();
},
/**
* Enables the control so that it can respond
* to focus and user input.
*/
enable
:
function
()
{
var
self
=
this
;
self
.
$input
.
prop
(
'disabled'
,
false
);
self
.
$control_input
.
prop
(
'disabled'
,
false
).
prop
(
'tabindex'
,
self
.
tabIndex
);
self
.
isDisabled
=
false
;
self
.
unlock
();
},
/**
* Completely destroys the control and
* unbinds all event listeners so that it can
* be garbage collected.
*/
destroy
:
function
()
{
var
self
=
this
;
var
eventNS
=
self
.
eventNS
;
var
revertSettings
=
self
.
revertSettings
;
self
.
trigger
(
'destroy'
);
self
.
off
();
self
.
$wrapper
.
remove
();
self
.
$dropdown
.
remove
();
self
.
$input
.
html
(
''
)
.
append
(
revertSettings
.
$children
)
.
removeAttr
(
'tabindex'
)
.
removeClass
(
'selectized'
)
.
attr
({
tabindex
:
revertSettings
.
tabindex
})
.
show
();
self
.
$control_input
.
removeData
(
'grow'
);
self
.
$input
.
removeData
(
'selectize'
);
$
(
window
).
off
(
eventNS
);
$
(
document
).
off
(
eventNS
);
$
(
document
.
body
).
off
(
eventNS
);
delete
self
.
$input
[
0
].
selectize
;
},
/**
* A helper method for rendering "item" and
* "option" templates, given the data.
*
* @param {string} templateName
* @param {object} data
* @returns {string}
*/
render
:
function
(
templateName
,
data
)
{
var
value
,
id
,
label
;
var
html
=
''
;
var
cache
=
false
;
var
self
=
this
;
var
regex_tag
=
/^
[\t
\r\n]
*<
([
a-z
][
a-z0-9
\-
_
]
*
(?:\:[
a-z
][
a-z0-9
\-
_
]
*
)?)
/i
;
if
(
templateName
===
'option'
||
templateName
===
'item'
)
{
value
=
hash_key
(
data
[
self
.
settings
.
valueField
]);
cache
=
!!
value
;
}
// pull markup from cache if it exists
if
(
cache
)
{
if
(
!
isset
(
self
.
renderCache
[
templateName
]))
{
self
.
renderCache
[
templateName
]
=
{};
}
if
(
self
.
renderCache
[
templateName
].
hasOwnProperty
(
value
))
{
return
self
.
renderCache
[
templateName
][
value
];
}
}
// render markup
html
=
self
.
settings
.
render
[
templateName
].
apply
(
this
,
[
data
,
escape_html
]);
// add mandatory attributes
if
(
templateName
===
'option'
||
templateName
===
'option_create'
)
{
html
=
html
.
replace
(
regex_tag
,
'<$1 data-selectable'
);
}
if
(
templateName
===
'optgroup'
)
{
id
=
data
[
self
.
settings
.
optgroupValueField
]
||
''
;
html
=
html
.
replace
(
regex_tag
,
'<$1 data-group="'
+
escape_replace
(
escape_html
(
id
))
+
'"'
);
}
if
(
templateName
===
'option'
||
templateName
===
'item'
)
{
html
=
html
.
replace
(
regex_tag
,
'<$1 data-value="'
+
escape_replace
(
escape_html
(
value
||
''
))
+
'"'
);
}
// update cache
if
(
cache
)
{
self
.
renderCache
[
templateName
][
value
]
=
html
;
}
return
html
;
},
/**
* Clears the render cache for a template. If
* no template is given, clears all render
* caches.
*
* @param {string} templateName
*/
clearCache
:
function
(
templateName
)
{
var
self
=
this
;
if
(
typeof
templateName
===
'undefined'
)
{
self
.
renderCache
=
{};
}
else
{
delete
self
.
renderCache
[
templateName
];
}
},
/**
* Determines whether or not to display the
* create item prompt, given a user input.
*
* @param {string} input
* @return {boolean}
*/
canCreate
:
function
(
input
)
{
var
self
=
this
;
if
(
!
self
.
settings
.
create
)
return
false
;
var
filter
=
self
.
settings
.
createFilter
;
return
input
.
length
&&
(
typeof
filter
!==
'function'
||
filter
.
apply
(
self
,
[
input
]))
&&
(
typeof
filter
!==
'string'
||
new
RegExp
(
filter
).
test
(
input
))
&&
(
!
(
filter
instanceof
RegExp
)
||
filter
.
test
(
input
));
}
});
Selectize
.
count
=
0
;
Selectize
.
defaults
=
{
options
:
[],
optgroups
:
[],
plugins
:
[],
delimiter
:
','
,
splitOn
:
null
,
// regexp or string for splitting up values from a paste command
persist
:
true
,
diacritics
:
true
,
create
:
false
,
createOnBlur
:
false
,
createFilter
:
null
,
highlight
:
true
,
openOnFocus
:
true
,
maxOptions
:
1000
,
maxItems
:
null
,
hideSelected
:
null
,
addPrecedence
:
false
,
selectOnTab
:
false
,
preload
:
false
,
allowEmptyOption
:
false
,
closeAfterSelect
:
false
,
scrollDuration
:
60
,
loadThrottle
:
300
,
loadingClass
:
'loading'
,
dataAttr
:
'data-data'
,
optgroupField
:
'optgroup'
,
valueField
:
'value'
,
labelField
:
'text'
,
optgroupLabelField
:
'label'
,
optgroupValueField
:
'value'
,
lockOptgroupOrder
:
false
,
sortField
:
'$order'
,
searchField
:
[
'text'
],
searchConjunction
:
'and'
,
mode
:
null
,
wrapperClass
:
'selectize-control'
,
inputClass
:
'selectize-input'
,
dropdownClass
:
'selectize-dropdown'
,
dropdownContentClass
:
'selectize-dropdown-content'
,
dropdownParent
:
null
,
copyClassesToDropdown
:
true
,
/*
load : null, // function(query, callback) { ... }
score : null, // function(search) { ... }
onInitialize : null, // function() { ... }
onChange : null, // function(value) { ... }
onItemAdd : null, // function(value, $item) { ... }
onItemRemove : null, // function(value) { ... }
onClear : null, // function() { ... }
onOptionAdd : null, // function(value, data) { ... }
onOptionRemove : null, // function(value) { ... }
onOptionClear : null, // function() { ... }
onOptionGroupAdd : null, // function(id, data) { ... }
onOptionGroupRemove : null, // function(id) { ... }
onOptionGroupClear : null, // function() { ... }
onDropdownOpen : null, // function($dropdown) { ... }
onDropdownClose : null, // function($dropdown) { ... }
onType : null, // function(str) { ... }
onDelete : null, // function(values) { ... }
*/
render
:
{
/*
item: null,
optgroup: null,
optgroup_header: null,
option: null,
option_create: null
*/
}
};
$
.
fn
.
selectize
=
function
(
settings_user
)
{
var
defaults
=
$
.
fn
.
selectize
.
defaults
;
var
settings
=
$
.
extend
({},
defaults
,
settings_user
);
var
attr_data
=
settings
.
dataAttr
;
var
field_label
=
settings
.
labelField
;
var
field_value
=
settings
.
valueField
;
var
field_optgroup
=
settings
.
optgroupField
;
var
field_optgroup_label
=
settings
.
optgroupLabelField
;
var
field_optgroup_value
=
settings
.
optgroupValueField
;
var
optionsMap
=
{};
/**
* Initializes selectize from a <input type="text"> element.
*
* @param {object} $input
* @param {object} settings_element
*/
var
init_textbox
=
function
(
$input
,
settings_element
)
{
var
i
,
n
,
values
,
option
;
var
data_raw
=
$input
.
attr
(
attr_data
);
if
(
!
data_raw
)
{
var
value
=
$
.
trim
(
$input
.
val
()
||
''
);
if
(
!
settings
.
allowEmptyOption
&&
!
value
.
length
)
return
;
values
=
value
.
split
(
settings
.
delimiter
);
for
(
i
=
0
,
n
=
values
.
length
;
i
<
n
;
i
++
)
{
option
=
{};
option
[
field_label
]
=
values
[
i
];
option
[
field_value
]
=
values
[
i
];
settings_element
.
options
.
push
(
option
);
}
settings_element
.
items
=
values
;
}
else
{
settings_element
.
options
=
JSON
.
parse
(
data_raw
);
for
(
i
=
0
,
n
=
settings_element
.
options
.
length
;
i
<
n
;
i
++
)
{
settings_element
.
items
.
push
(
settings_element
.
options
[
i
][
field_value
]);
}
}
};
/**
* Initializes selectize from a <select> element.
*
* @param {object} $input
* @param {object} settings_element
*/
var
init_select
=
function
(
$input
,
settings_element
)
{
var
i
,
n
,
tagName
,
$children
,
order
=
0
;
var
options
=
settings_element
.
options
;
var
readData
=
function
(
$el
)
{
var
data
=
attr_data
&&
$el
.
attr
(
attr_data
);
if
(
typeof
data
===
'string'
&&
data
.
length
)
{
return
JSON
.
parse
(
data
);
}
return
null
;
};
var
addOption
=
function
(
$option
,
group
)
{
$option
=
$
(
$option
);
var
value
=
hash_key
(
$option
.
attr
(
'value'
));
if
(
!
value
&&
!
settings
.
allowEmptyOption
)
return
;
// if the option already exists, it's probably been
// duplicated in another optgroup. in this case, push
// the current group to the "optgroup" property on the
// existing option so that it's rendered in both places.
if
(
optionsMap
.
hasOwnProperty
(
value
))
{
if
(
group
)
{
var
arr
=
optionsMap
[
value
][
field_optgroup
];
if
(
!
arr
)
{
optionsMap
[
value
][
field_optgroup
]
=
group
;
}
else
if
(
!
$
.
isArray
(
arr
))
{
optionsMap
[
value
][
field_optgroup
]
=
[
arr
,
group
];
}
else
{
arr
.
push
(
group
);
}
}
return
;
}
var
option
=
readData
(
$option
)
||
{};
option
[
field_label
]
=
option
[
field_label
]
||
$option
.
text
();
option
[
field_value
]
=
option
[
field_value
]
||
value
;
option
[
field_optgroup
]
=
option
[
field_optgroup
]
||
group
;
optionsMap
[
value
]
=
option
;
options
.
push
(
option
);
if
(
$option
.
is
(
':selected'
))
{
settings_element
.
items
.
push
(
value
);
}
};
var
addGroup
=
function
(
$optgroup
)
{
var
i
,
n
,
id
,
optgroup
,
$options
;
$optgroup
=
$
(
$optgroup
);
id
=
$optgroup
.
attr
(
'label'
);
if
(
id
)
{
optgroup
=
readData
(
$optgroup
)
||
{};
optgroup
[
field_optgroup_label
]
=
id
;
optgroup
[
field_optgroup_value
]
=
id
;
settings_element
.
optgroups
.
push
(
optgroup
);
}
$options
=
$
(
'option'
,
$optgroup
);
for
(
i
=
0
,
n
=
$options
.
length
;
i
<
n
;
i
++
)
{
addOption
(
$options
[
i
],
id
);
}
};
settings_element
.
maxItems
=
$input
.
attr
(
'multiple'
)
?
null
:
1
;
$children
=
$input
.
children
();
for
(
i
=
0
,
n
=
$children
.
length
;
i
<
n
;
i
++
)
{
tagName
=
$children
[
i
].
tagName
.
toLowerCase
();
if
(
tagName
===
'optgroup'
)
{
addGroup
(
$children
[
i
]);
}
else
if
(
tagName
===
'option'
)
{
addOption
(
$children
[
i
]);
}
}
};
return
this
.
each
(
function
()
{
if
(
this
.
selectize
)
return
;
var
instance
;
var
$input
=
$
(
this
);
var
tag_name
=
this
.
tagName
.
toLowerCase
();
var
placeholder
=
$input
.
attr
(
'placeholder'
)
||
$input
.
attr
(
'data-placeholder'
);
if
(
!
placeholder
&&
!
settings
.
allowEmptyOption
)
{
placeholder
=
$input
.
children
(
'option[value=""]'
).
text
();
}
var
settings_element
=
{
'placeholder'
:
placeholder
,
'options'
:
[],
'optgroups'
:
[],
'items'
:
[]
};
if
(
tag_name
===
'select'
)
{
init_select
(
$input
,
settings_element
);
}
else
{
init_textbox
(
$input
,
settings_element
);
}
instance
=
new
Selectize
(
$input
,
$
.
extend
(
true
,
{},
defaults
,
settings_element
,
settings_user
));
});
};
$
.
fn
.
selectize
.
defaults
=
Selectize
.
defaults
;
$
.
fn
.
selectize
.
support
=
{
validity
:
SUPPORTS_VALIDITY_API
};
Selectize
.
define
(
'drag_drop'
,
function
(
options
)
{
if
(
!
$
.
fn
.
sortable
)
throw
new
Error
(
'The "drag_drop" plugin requires jQuery UI "sortable".'
);
if
(
this
.
settings
.
mode
!==
'multi'
)
return
;
var
self
=
this
;
self
.
lock
=
(
function
()
{
var
original
=
self
.
lock
;
return
function
()
{
var
sortable
=
self
.
$control
.
data
(
'sortable'
);
if
(
sortable
)
sortable
.
disable
();
return
original
.
apply
(
self
,
arguments
);
};
})();
self
.
unlock
=
(
function
()
{
var
original
=
self
.
unlock
;
return
function
()
{
var
sortable
=
self
.
$control
.
data
(
'sortable'
);
if
(
sortable
)
sortable
.
enable
();
return
original
.
apply
(
self
,
arguments
);
};
})();
self
.
setup
=
(
function
()
{
var
original
=
self
.
setup
;
return
function
()
{
original
.
apply
(
this
,
arguments
);
var
$control
=
self
.
$control
.
sortable
({
items
:
'[data-value]'
,
forcePlaceholderSize
:
true
,
disabled
:
self
.
isLocked
,
start
:
function
(
e
,
ui
)
{
ui
.
placeholder
.
css
(
'width'
,
ui
.
helper
.
css
(
'width'
));
$control
.
css
({
overflow
:
'visible'
});
},
stop
:
function
()
{
$control
.
css
({
overflow
:
'hidden'
});
var
active
=
self
.
$activeItems
?
self
.
$activeItems
.
slice
()
:
null
;
var
values
=
[];
$control
.
children
(
'[data-value]'
).
each
(
function
()
{
values
.
push
(
$
(
this
).
attr
(
'data-value'
));
});
self
.
setValue
(
values
);
self
.
setActiveItem
(
active
);
}
});
};
})();
});
Selectize
.
define
(
'dropdown_header'
,
function
(
options
)
{
var
self
=
this
;
options
=
$
.
extend
({
title
:
'Untitled'
,
headerClass
:
'selectize-dropdown-header'
,
titleRowClass
:
'selectize-dropdown-header-title'
,
labelClass
:
'selectize-dropdown-header-label'
,
closeClass
:
'selectize-dropdown-header-close'
,
html
:
function
(
data
)
{
return
(
'<div class="'
+
data
.
headerClass
+
'">'
+
'<div class="'
+
data
.
titleRowClass
+
'">'
+
'<span class="'
+
data
.
labelClass
+
'">'
+
data
.
title
+
'</span>'
+
'<a href="javascript:void(0)" class="'
+
data
.
closeClass
+
'">×</a>'
+
'</div>'
+
'</div>'
);
}
},
options
);
self
.
setup
=
(
function
()
{
var
original
=
self
.
setup
;
return
function
()
{
original
.
apply
(
self
,
arguments
);
self
.
$dropdown_header
=
$
(
options
.
html
(
options
));
self
.
$dropdown
.
prepend
(
self
.
$dropdown_header
);
};
})();
});
Selectize
.
define
(
'optgroup_columns'
,
function
(
options
)
{
var
self
=
this
;
options
=
$
.
extend
({
equalizeWidth
:
true
,
equalizeHeight
:
true
},
options
);
this
.
getAdjacentOption
=
function
(
$option
,
direction
)
{
var
$options
=
$option
.
closest
(
'[data-group]'
).
find
(
'[data-selectable]'
);
var
index
=
$options
.
index
(
$option
)
+
direction
;
return
index
>=
0
&&
index
<
$options
.
length
?
$options
.
eq
(
index
)
:
$
();
};
this
.
onKeyDown
=
(
function
()
{
var
original
=
self
.
onKeyDown
;
return
function
(
e
)
{
var
index
,
$option
,
$options
,
$optgroup
;
if
(
this
.
isOpen
&&
(
e
.
keyCode
===
KEY_LEFT
||
e
.
keyCode
===
KEY_RIGHT
))
{
self
.
ignoreHover
=
true
;
$optgroup
=
this
.
$activeOption
.
closest
(
'[data-group]'
);
index
=
$optgroup
.
find
(
'[data-selectable]'
).
index
(
this
.
$activeOption
);
if
(
e
.
keyCode
===
KEY_LEFT
)
{
$optgroup
=
$optgroup
.
prev
(
'[data-group]'
);
}
else
{
$optgroup
=
$optgroup
.
next
(
'[data-group]'
);
}
$options
=
$optgroup
.
find
(
'[data-selectable]'
);
$option
=
$options
.
eq
(
Math
.
min
(
$options
.
length
-
1
,
index
));
if
(
$option
.
length
)
{
this
.
setActiveOption
(
$option
);
}
return
;
}
return
original
.
apply
(
this
,
arguments
);
};
})();
var
getScrollbarWidth
=
function
()
{
var
div
;
var
width
=
getScrollbarWidth
.
width
;
var
doc
=
document
;
if
(
typeof
width
===
'undefined'
)
{
div
=
doc
.
createElement
(
'div'
);
div
.
innerHTML
=
'<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>'
;
div
=
div
.
firstChild
;
doc
.
body
.
appendChild
(
div
);
width
=
getScrollbarWidth
.
width
=
div
.
offsetWidth
-
div
.
clientWidth
;
doc
.
body
.
removeChild
(
div
);
}
return
width
;
};
var
equalizeSizes
=
function
()
{
var
i
,
n
,
height_max
,
width
,
width_last
,
width_parent
,
$optgroups
;
$optgroups
=
$
(
'[data-group]'
,
self
.
$dropdown_content
);
n
=
$optgroups
.
length
;
if
(
!
n
||
!
self
.
$dropdown_content
.
width
())
return
;
if
(
options
.
equalizeHeight
)
{
height_max
=
0
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
height_max
=
Math
.
max
(
height_max
,
$optgroups
.
eq
(
i
).
height
());
}
$optgroups
.
css
({
height
:
height_max
});
}
if
(
options
.
equalizeWidth
)
{
width_parent
=
self
.
$dropdown_content
.
innerWidth
()
-
getScrollbarWidth
();
width
=
Math
.
round
(
width_parent
/
n
);
$optgroups
.
css
({
width
:
width
});
if
(
n
>
1
)
{
width_last
=
width_parent
-
width
*
(
n
-
1
);
$optgroups
.
eq
(
n
-
1
).
css
({
width
:
width_last
});
}
}
};
if
(
options
.
equalizeHeight
||
options
.
equalizeWidth
)
{
hook
.
after
(
this
,
'positionDropdown'
,
equalizeSizes
);
hook
.
after
(
this
,
'refreshOptions'
,
equalizeSizes
);
}
});
Selectize
.
define
(
'remove_button'
,
function
(
options
)
{
if
(
this
.
settings
.
mode
===
'single'
)
return
;
options
=
$
.
extend
({
label
:
'×'
,
title
:
'Remove'
,
className
:
'remove'
,
append
:
true
},
options
);
var
self
=
this
;
var
html
=
'<a href="javascript:void(0)" class="'
+
options
.
className
+
'" tabindex="-1" title="'
+
escape_html
(
options
.
title
)
+
'">'
+
options
.
label
+
'</a>'
;
/**
* Appends an element as a child (with raw HTML).
*
* @param {string} html_container
* @param {string} html_element
* @return {string}
*/
var
append
=
function
(
html_container
,
html_element
)
{
var
pos
=
html_container
.
search
(
/
(
<
\/[^
>
]
+>
\s
*
)
$/
);
return
html_container
.
substring
(
0
,
pos
)
+
html_element
+
html_container
.
substring
(
pos
);
};
this
.
setup
=
(
function
()
{
var
original
=
self
.
setup
;
return
function
()
{
// override the item rendering method to add the button to each
if
(
options
.
append
)
{
var
render_item
=
self
.
settings
.
render
.
item
;
self
.
settings
.
render
.
item
=
function
(
data
)
{
return
append
(
render_item
.
apply
(
this
,
arguments
),
html
);
};
}
original
.
apply
(
this
,
arguments
);
// add event listener
this
.
$control
.
on
(
'click'
,
'.'
+
options
.
className
,
function
(
e
)
{
e
.
preventDefault
();
if
(
self
.
isLocked
)
return
;
var
$item
=
$
(
e
.
currentTarget
).
parent
();
self
.
setActiveItem
(
$item
);
if
(
self
.
deleteSelection
())
{
self
.
setCaret
(
self
.
items
.
length
);
}
});
};
})();
});
Selectize
.
define
(
'restore_on_backspace'
,
function
(
options
)
{
var
self
=
this
;
options
.
text
=
options
.
text
||
function
(
option
)
{
return
option
[
this
.
settings
.
labelField
];
};
this
.
onKeyDown
=
(
function
()
{
var
original
=
self
.
onKeyDown
;
return
function
(
e
)
{
var
index
,
option
;
if
(
e
.
keyCode
===
KEY_BACKSPACE
&&
this
.
$control_input
.
val
()
===
''
&&
!
this
.
$activeItems
.
length
)
{
index
=
this
.
caretPos
-
1
;
if
(
index
>=
0
&&
index
<
this
.
items
.
length
)
{
option
=
this
.
options
[
this
.
items
[
index
]];
if
(
this
.
deleteSelection
(
e
))
{
this
.
setTextboxValue
(
options
.
text
.
apply
(
this
,
[
option
]));
this
.
refreshOptions
(
true
);
}
e
.
preventDefault
();
return
;
}
}
return
original
.
apply
(
this
,
arguments
);
};
})();
});
return
Selectize
;
}));
\ 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