Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
angular-ui-select
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
angular-ui-select
Commits
ac289204
Commit
ac289204
authored
Aug 03, 2018
by
bingchuan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[dev]version 0.19.3
parents
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
2708 additions
and
0 deletions
+2708
-0
bower.json
bower.json
+31
-0
select.css
dist/select.css
+321
-0
select.js
dist/select.js
+2356
-0
No files found.
bower.json
0 → 100644
View file @
ac289204
{
"name"
:
"angular-ui-select"
,
"homepage"
:
"https://github.com/angular-ui/ui-select"
,
"authors"
:
[
"AngularUI"
],
"description"
:
"AngularJS ui-select"
,
"main"
:
[
"dist/select.js"
,
"dist/select.css"
],
"license"
:
"MIT"
,
"ignore"
:
[
"**/.*"
,
"node_modules"
,
"bower_components"
,
"src"
,
"test"
,
"gulpfile.js"
,
"karma.conf.js"
,
"examples"
],
"dependencies"
:
{
"angular"
:
">=1.2.18"
},
"devDependencies"
:
{
"jquery"
:
"~1.11"
,
"angular-sanitize"
:
">=1.2.18"
,
"angular-mocks"
:
">=1.2.18"
}
}
dist/select.css
0 → 100644
View file @
ac289204
/*!
* ui-select
* http://github.com/angular-ui/ui-select
* Version: 0.19.2 - 2016-08-16T18:29:49.598Z
* License: MIT
*/
/* Style when highlighting a search. */
.ui-select-highlight
{
font-weight
:
bold
;
}
.ui-select-offscreen
{
clip
:
rect
(
0
0
0
0
)
!important
;
width
:
1px
!important
;
height
:
1px
!important
;
border
:
0
!important
;
margin
:
0
!important
;
padding
:
0
!important
;
overflow
:
hidden
!important
;
position
:
absolute
!important
;
outline
:
0
!important
;
left
:
0px
!important
;
top
:
0px
!important
;
}
.ui-select-choices-row
:hover
{
background-color
:
#f5f5f5
;
}
/* Select2 theme */
/* Mark invalid Select2 */
.ng-dirty.ng-invalid
>
a
.select2-choice
{
border-color
:
#D44950
;
}
.select2-result-single
{
padding-left
:
0
;
}
.select2-locked
>
.select2-search-choice-close
{
display
:
none
;
}
.select-locked
>
.ui-select-match-close
{
display
:
none
;
}
body
>
.select2-container.open
{
z-index
:
9999
;
/* The z-index Select2 applies to the select2-drop */
}
/* Handle up direction Select2 */
.ui-select-container
[
theme
=
"select2"
]
.direction-up
.ui-select-match
,
.ui-select-container.select2.direction-up
.ui-select-match
{
border-radius
:
4px
;
/* FIXME hardcoded value :-/ */
border-top-left-radius
:
0
;
border-top-right-radius
:
0
;
}
.ui-select-container
[
theme
=
"select2"
]
.direction-up
.ui-select-dropdown
,
.ui-select-container.select2.direction-up
.ui-select-dropdown
{
border-radius
:
4px
;
/* FIXME hardcoded value :-/ */
border-bottom-left-radius
:
0
;
border-bottom-right-radius
:
0
;
border-top-width
:
1px
;
/* FIXME hardcoded value :-/ */
border-top-style
:
solid
;
box-shadow
:
0
-4px
8px
rgba
(
0
,
0
,
0
,
0.25
);
margin-top
:
-4px
;
/* FIXME hardcoded value :-/ */
}
.ui-select-container
[
theme
=
"select2"
]
.direction-up
.ui-select-dropdown
.select2-search
,
.ui-select-container.select2.direction-up
.ui-select-dropdown
.select2-search
{
margin-top
:
4px
;
/* FIXME hardcoded value :-/ */
}
.ui-select-container
[
theme
=
"select2"
]
.direction-up.select2-dropdown-open
.ui-select-match
,
.ui-select-container.select2.direction-up.select2-dropdown-open
.ui-select-match
{
border-bottom-color
:
#5897fb
;
}
.ui-select-container
[
theme
=
"select2"
]
.ui-select-dropdown
.ui-select-search-hidden
,
.ui-select-container
[
theme
=
"select2"
]
.ui-select-dropdown
.ui-select-search-hidden
input
{
opacity
:
0
;
height
:
0
;
min-height
:
0
;
padding
:
0
;
margin
:
0
;
border
:
0
;
}
/* Selectize theme */
/* Helper class to show styles when focus */
.selectize-input.selectize-focus
{
border-color
:
#007FBB
!important
;
}
/* Fix input width for Selectize theme */
.selectize-control
>
.selectize-input
>
input
{
width
:
100%
;
}
/* Fix dropdown width for Selectize theme */
.selectize-control
>
.selectize-dropdown
{
width
:
100%
;
}
/* Mark invalid Selectize */
.ng-dirty.ng-invalid
>
div
.selectize-input
{
border-color
:
#D44950
;
}
/* Handle up direction Selectize */
.ui-select-container
[
theme
=
"selectize"
]
.direction-up
.ui-select-dropdown
{
box-shadow
:
0
-4px
8px
rgba
(
0
,
0
,
0
,
0.25
);
margin-top
:
-2px
;
/* FIXME hardcoded value :-/ */
}
.ui-select-container
[
theme
=
"selectize"
]
input
.ui-select-search-hidden
{
opacity
:
0
;
height
:
0
;
min-height
:
0
;
padding
:
0
;
margin
:
0
;
border
:
0
;
width
:
0
;
}
/* Bootstrap theme */
/* Helper class to show styles when focus */
.btn-default-focus
{
color
:
#333
;
background-color
:
#EBEBEB
;
border-color
:
#ADADAD
;
text-decoration
:
none
;
outline
:
5px
auto
-webkit-focus-ring-color
;
outline-offset
:
-2px
;
box-shadow
:
inset
0
1px
1px
rgba
(
0
,
0
,
0
,
0.075
),
0
0
8px
rgba
(
102
,
175
,
233
,
0.6
);
}
.ui-select-bootstrap
.ui-select-toggle
{
position
:
relative
;
}
.ui-select-bootstrap
.ui-select-toggle
>
.caret
{
position
:
absolute
;
height
:
10px
;
top
:
50%
;
right
:
10px
;
margin-top
:
-2px
;
}
/* Fix Bootstrap dropdown position when inside a input-group */
.input-group
>
.ui-select-bootstrap.dropdown
{
/* Instead of relative */
position
:
static
;
}
.input-group
>
.ui-select-bootstrap
>
input
.ui-select-search.form-control
{
border-radius
:
4px
;
/* FIXME hardcoded value :-/ */
border-top-right-radius
:
0
;
border-bottom-right-radius
:
0
;
}
.input-group
>
.ui-select-bootstrap
>
input
.ui-select-search.form-control.direction-up
{
border-radius
:
4px
!important
;
/* FIXME hardcoded value :-/ */
border-top-right-radius
:
0
!important
;
border-bottom-right-radius
:
0
!important
;
}
.ui-select-bootstrap
.ui-select-search-hidden
{
opacity
:
0
;
height
:
0
;
min-height
:
0
;
padding
:
0
;
margin
:
0
;
border
:
0
;
}
.ui-select-bootstrap
>
.ui-select-match
>
.btn
{
/* Instead of center because of .btn */
text-align
:
left
!important
;
}
.ui-select-bootstrap
>
.ui-select-match
>
.caret
{
position
:
absolute
;
top
:
45%
;
right
:
15px
;
}
/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */
.ui-select-bootstrap
>
.ui-select-choices
,
.ui-select-bootstrap
>
.ui-select-no-choice
{
width
:
100%
;
height
:
auto
;
max-height
:
200px
;
overflow-x
:
hidden
;
margin-top
:
-1px
;
}
body
>
.ui-select-bootstrap.open
{
z-index
:
1000
;
/* Standard Bootstrap dropdown z-index */
}
.ui-select-multiple.ui-select-bootstrap
{
height
:
auto
;
padding
:
3px
3px
0
3px
;
}
.ui-select-multiple.ui-select-bootstrap
input
.ui-select-search
{
background-color
:
transparent
!important
;
/* To prevent double background when disabled */
border
:
none
;
outline
:
none
;
height
:
1.666666em
;
margin-bottom
:
3px
;
}
.ui-select-multiple.ui-select-bootstrap
.ui-select-match
.close
{
font-size
:
1.6em
;
line-height
:
0.75
;
}
.ui-select-multiple.ui-select-bootstrap
.ui-select-match-item
{
outline
:
0
;
margin
:
0
3px
3px
0
;
}
.ui-select-multiple
.ui-select-match-item
{
position
:
relative
;
}
.ui-select-multiple
.ui-select-match-item.dropping
.ui-select-match-close
{
pointer-events
:
none
;
}
.ui-select-multiple
:hover
.ui-select-match-item.dropping-before
:before
{
content
:
""
;
position
:
absolute
;
top
:
0
;
right
:
100%
;
height
:
100%
;
margin-right
:
2px
;
border-left
:
1px
solid
#428bca
;
}
.ui-select-multiple
:hover
.ui-select-match-item.dropping-after
:after
{
content
:
""
;
position
:
absolute
;
top
:
0
;
left
:
100%
;
height
:
100%
;
margin-left
:
2px
;
border-right
:
1px
solid
#428bca
;
}
.ui-select-bootstrap
.ui-select-choices-row
>
span
{
cursor
:
pointer
;
display
:
block
;
padding
:
3px
20px
;
clear
:
both
;
font-weight
:
400
;
line-height
:
1.42857143
;
color
:
#333
;
white-space
:
nowrap
;
}
.ui-select-bootstrap
.ui-select-choices-row
>
span
:hover
,
.ui-select-bootstrap
.ui-select-choices-row
>
span
:focus
{
text-decoration
:
none
;
color
:
#262626
;
background-color
:
#f5f5f5
;
}
.ui-select-bootstrap
.ui-select-choices-row.active
>
span
{
color
:
#fff
;
text-decoration
:
none
;
outline
:
0
;
background-color
:
#428bca
;
}
.ui-select-bootstrap
.ui-select-choices-row.disabled
>
span
,
.ui-select-bootstrap
.ui-select-choices-row.active.disabled
>
span
{
color
:
#777
;
cursor
:
not-allowed
;
background-color
:
#fff
;
}
/* fix hide/show angular animation */
.ui-select-match.ng-hide-add
,
.ui-select-search.ng-hide-add
{
display
:
none
!important
;
}
/* Mark invalid Bootstrap */
.ui-select-bootstrap.ng-dirty.ng-invalid
>
button
.btn.ui-select-match
{
border-color
:
#D44950
;
}
/* Handle up direction Bootstrap */
.ui-select-container
[
theme
=
"bootstrap"
]
.direction-up
.ui-select-dropdown
{
box-shadow
:
0
-4px
8px
rgba
(
0
,
0
,
0
,
0.25
);
}
.ui-select-bootstrap
.ui-select-match-text
{
width
:
100%
;
padding-right
:
1em
;
}
.ui-select-bootstrap
.ui-select-match-text
span
{
display
:
inline-block
;
width
:
100%
;
overflow
:
hidden
;
}
.ui-select-bootstrap
.ui-select-toggle
>
a
.btn
{
position
:
absolute
;
height
:
10px
;
right
:
10px
;
margin-top
:
-2px
;
}
\ No newline at end of file
dist/select.js
0 → 100644
View file @
ac289204
/*!
* ui-select
* http://github.com/angular-ui/ui-select
* Version: 0.19.2 - 2016-08-16T18:29:49.496Z
* License: MIT
*/
(
function
()
{
"use strict"
;
var
KEY
=
{
TAB
:
9
,
ENTER
:
13
,
ESC
:
27
,
SPACE
:
32
,
LEFT
:
37
,
UP
:
38
,
RIGHT
:
39
,
DOWN
:
40
,
SHIFT
:
16
,
CTRL
:
17
,
ALT
:
18
,
PAGE_UP
:
33
,
PAGE_DOWN
:
34
,
HOME
:
36
,
END
:
35
,
BACKSPACE
:
8
,
DELETE
:
46
,
COMMAND
:
91
,
MAP
:
{
91
:
"COMMAND"
,
8
:
"BACKSPACE"
,
9
:
"TAB"
,
13
:
"ENTER"
,
16
:
"SHIFT"
,
17
:
"CTRL"
,
18
:
"ALT"
,
19
:
"PAUSEBREAK"
,
20
:
"CAPSLOCK"
,
27
:
"ESC"
,
32
:
"SPACE"
,
33
:
"PAGE_UP"
,
34
:
"PAGE_DOWN"
,
35
:
"END"
,
36
:
"HOME"
,
37
:
"LEFT"
,
38
:
"UP"
,
39
:
"RIGHT"
,
40
:
"DOWN"
,
43
:
"+"
,
44
:
"PRINTSCREEN"
,
45
:
"INSERT"
,
46
:
"DELETE"
,
48
:
"0"
,
49
:
"1"
,
50
:
"2"
,
51
:
"3"
,
52
:
"4"
,
53
:
"5"
,
54
:
"6"
,
55
:
"7"
,
56
:
"8"
,
57
:
"9"
,
59
:
";"
,
61
:
"="
,
65
:
"A"
,
66
:
"B"
,
67
:
"C"
,
68
:
"D"
,
69
:
"E"
,
70
:
"F"
,
71
:
"G"
,
72
:
"H"
,
73
:
"I"
,
74
:
"J"
,
75
:
"K"
,
76
:
"L"
,
77
:
"M"
,
78
:
"N"
,
79
:
"O"
,
80
:
"P"
,
81
:
"Q"
,
82
:
"R"
,
83
:
"S"
,
84
:
"T"
,
85
:
"U"
,
86
:
"V"
,
87
:
"W"
,
88
:
"X"
,
89
:
"Y"
,
90
:
"Z"
,
96
:
"0"
,
97
:
"1"
,
98
:
"2"
,
99
:
"3"
,
100
:
"4"
,
101
:
"5"
,
102
:
"6"
,
103
:
"7"
,
104
:
"8"
,
105
:
"9"
,
106
:
"*"
,
107
:
"+"
,
109
:
"-"
,
110
:
"."
,
111
:
"/"
,
112
:
"F1"
,
113
:
"F2"
,
114
:
"F3"
,
115
:
"F4"
,
116
:
"F5"
,
117
:
"F6"
,
118
:
"F7"
,
119
:
"F8"
,
120
:
"F9"
,
121
:
"F10"
,
122
:
"F11"
,
123
:
"F12"
,
144
:
"NUMLOCK"
,
145
:
"SCROLLLOCK"
,
186
:
";"
,
187
:
"="
,
188
:
","
,
189
:
"-"
,
190
:
"."
,
191
:
"/"
,
192
:
"`"
,
219
:
"["
,
220
:
"
\
\"
, 221 : "
]
" , 222 : "
'"
},
isControl: function (e) {
var k = e.which;
switch (k) {
case KEY.COMMAND:
case KEY.SHIFT:
case KEY.CTRL:
case KEY.ALT:
return true;
}
if (e.metaKey || e.ctrlKey || e.altKey) return true;
return false;
},
isFunctionKey: function (k) {
k = k.which ? k.which : k;
return k >= 112 && k <= 123;
},
isVerticalMovement: function (k){
return ~[KEY.UP, KEY.DOWN].indexOf(k);
},
isHorizontalMovement: function (k){
return ~[KEY.LEFT,KEY.RIGHT,KEY.BACKSPACE,KEY.DELETE].indexOf(k);
},
toSeparator: function (k) {
var sep = {ENTER:"
\
n",TAB:"
\
t",SPACE:" "}[k];
if (sep) return sep;
// return undefined for special keys other than enter, tab or space.
// no way to use them to cut strings.
return KEY[k] ? undefined : k;
}
};
/**
* Add querySelectorAll() to jqLite.
*
* jqLite find() is limited to lookups by tag name.
* TODO This will change with future versions of AngularJS, to be removed when this happens
*
* See jqLite.find - why not use querySelectorAll? https://github.com/angular/angular.js/issues/3586
* See feat(jqLite): use querySelectorAll instead of getElementsByTagName in jqLite.find https://github.com/angular/angular.js/pull/3598
*/
if (angular.element.prototype.querySelectorAll === undefined) {
angular.element.prototype.querySelectorAll = function(selector) {
return angular.element(this[0].querySelectorAll(selector));
};
}
/**
* Add closest() to jqLite.
*/
if (angular.element.prototype.closest === undefined) {
angular.element.prototype.closest = function( selector) {
var elem = this[0];
var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector;
while (elem) {
if (matchesSelector.bind(elem)(selector)) {
return elem;
} else {
elem = elem.parentElement;
}
}
return false;
};
}
var latestId = 0;
var uis = angular.module('
ui
.
select
', [])
.constant('
uiSelectConfig
', {
theme: '
bootstrap
',
searchEnabled: true,
sortable: false,
placeholder: '', // Empty by default, like HTML tag <select>
refreshDelay: 1000, // In milliseconds
closeOnSelect: true,
skipFocusser: false,
dropdownPosition: '
auto
',
removeSelected: true,
resetSearchInput: false,
generateId: function() {
return latestId++;
},
appendToBody: false
})
// See Rename minErr and make it accessible from outside https://github.com/angular/angular.js/issues/6913
.service('
uiSelectMinErr
', function() {
var minErr = angular.$$minErr('
ui
.
select
');
return function() {
var error = minErr.apply(this, arguments);
var message = error.message.replace(new RegExp('
\
nhttp
:
//errors.angularjs.org/.*'), '');
return
new
Error
(
message
);
};
})
// Recreates old behavior of ng-transclude. Used internally.
.
directive
(
'uisTranscludeAppend'
,
function
()
{
return
{
link
:
function
(
scope
,
element
,
attrs
,
ctrl
,
transclude
)
{
transclude
(
scope
,
function
(
clone
)
{
element
.
append
(
clone
);
});
}
};
})
/**
* Highlights text that matches $select.search.
*
* Taken from AngularUI Bootstrap Typeahead
* See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L340
*/
.
filter
(
'highlight'
,
function
()
{
function
escapeRegexp
(
queryToEscape
)
{
return
(
''
+
queryToEscape
).
replace
(
/
([
.?*+^$[
\]\\
(){}|-
])
/g
,
'
\\
$1'
);
}
return
function
(
matchItem
,
query
)
{
return
query
&&
matchItem
?
(
''
+
matchItem
).
replace
(
new
RegExp
(
escapeRegexp
(
query
),
'gi'
),
'<span class="ui-select-highlight">$&</span>'
)
:
matchItem
;
};
})
/**
* A read-only equivalent of jQuery's offset function: http://api.jquery.com/offset/
*
* Taken from AngularUI Bootstrap Position:
* See https://github.com/angular-ui/bootstrap/blob/master/src/position/position.js#L70
*/
.
factory
(
'uisOffset'
,
[
'$document'
,
'$window'
,
function
(
$document
,
$window
)
{
return
function
(
element
)
{
var
boundingClientRect
=
element
[
0
].
getBoundingClientRect
();
return
{
width
:
boundingClientRect
.
width
||
element
.
prop
(
'offsetWidth'
),
height
:
boundingClientRect
.
height
||
element
.
prop
(
'offsetHeight'
),
top
:
0
,
left
:
boundingClientRect
.
left
+
(
$window
.
pageXOffset
||
$document
[
0
].
documentElement
.
scrollLeft
)
};
};
}]);
uis
.
directive
(
'uiSelectChoices'
,
[
'uiSelectConfig'
,
'uisRepeatParser'
,
'uiSelectMinErr'
,
'$compile'
,
'$window'
,
function
(
uiSelectConfig
,
RepeatParser
,
uiSelectMinErr
,
$compile
,
$window
)
{
return
{
restrict
:
'EA'
,
require
:
'^uiSelect'
,
replace
:
true
,
transclude
:
true
,
templateUrl
:
function
(
tElement
)
{
// Needed so the uiSelect can detect the transcluded content
tElement
.
addClass
(
'ui-select-choices'
);
// Gets theme attribute from parent (ui-select)
var
theme
=
tElement
.
parent
().
attr
(
'theme'
)
||
uiSelectConfig
.
theme
;
return
theme
+
'/choices.tpl.html'
;
},
compile
:
function
(
tElement
,
tAttrs
)
{
if
(
!
tAttrs
.
repeat
)
throw
uiSelectMinErr
(
'repeat'
,
"Expected 'repeat' expression."
);
// var repeat = RepeatParser.parse(attrs.repeat);
var
groupByExp
=
tAttrs
.
groupBy
;
var
groupFilterExp
=
tAttrs
.
groupFilter
;
if
(
groupByExp
)
{
var
groups
=
tElement
.
querySelectorAll
(
'.ui-select-choices-group'
);
if
(
groups
.
length
!==
1
)
throw
uiSelectMinErr
(
'rows'
,
"Expected 1 .ui-select-choices-group but got '{0}'."
,
groups
.
length
);
groups
.
attr
(
'ng-repeat'
,
RepeatParser
.
getGroupNgRepeatExpression
());
}
var
parserResult
=
RepeatParser
.
parse
(
tAttrs
.
repeat
);
var
choices
=
tElement
.
querySelectorAll
(
'.ui-select-choices-row'
);
if
(
choices
.
length
!==
1
)
{
throw
uiSelectMinErr
(
'rows'
,
"Expected 1 .ui-select-choices-row but got '{0}'."
,
choices
.
length
);
}
choices
.
attr
(
'ng-repeat'
,
parserResult
.
repeatExpression
(
groupByExp
))
.
attr
(
'ng-if'
,
'$select.open'
);
//Prevent unnecessary watches when dropdown is closed
var
rowsInner
=
tElement
.
querySelectorAll
(
'.ui-select-choices-row-inner'
);
if
(
rowsInner
.
length
!==
1
)
{
throw
uiSelectMinErr
(
'rows'
,
"Expected 1 .ui-select-choices-row-inner but got '{0}'."
,
rowsInner
.
length
);
}
rowsInner
.
attr
(
'uis-transclude-append'
,
''
);
//Adding uisTranscludeAppend directive to row element after choices element has ngRepeat
// If IE8 then need to target rowsInner to apply the ng-click attr as choices will not capture the event.
var
clickTarget
=
$window
.
document
.
addEventListener
?
choices
:
rowsInner
;
clickTarget
.
attr
(
'ng-click'
,
'$select.select('
+
parserResult
.
itemName
+
',$select.skipFocusser,$event)'
);
return
function
link
(
scope
,
element
,
attrs
,
$select
)
{
$select
.
parseRepeatAttr
(
attrs
.
repeat
,
groupByExp
,
groupFilterExp
);
//Result ready at $select.parserResult
$select
.
disableChoiceExpression
=
attrs
.
uiDisableChoice
;
$select
.
onHighlightCallback
=
attrs
.
onHighlight
;
$select
.
dropdownPosition
=
attrs
.
position
?
attrs
.
position
.
toLowerCase
()
:
uiSelectConfig
.
dropdownPosition
;
scope
.
$on
(
'$destroy'
,
function
()
{
choices
.
remove
();
});
scope
.
$watch
(
'$select.search'
,
function
(
newValue
)
{
if
(
newValue
&&
!
$select
.
open
&&
$select
.
multiple
)
$select
.
activate
(
false
,
true
);
$select
.
activeIndex
=
$select
.
tagging
.
isActivated
?
-
1
:
0
;
if
(
!
attrs
.
minimumInputLength
||
$select
.
search
.
length
>=
attrs
.
minimumInputLength
)
{
$select
.
refresh
(
attrs
.
refresh
);
}
else
{
$select
.
items
=
[];
}
});
attrs
.
$observe
(
'refreshDelay'
,
function
()
{
// $eval() is needed otherwise we get a string instead of a number
var
refreshDelay
=
scope
.
$eval
(
attrs
.
refreshDelay
);
$select
.
refreshDelay
=
refreshDelay
!==
undefined
?
refreshDelay
:
uiSelectConfig
.
refreshDelay
;
});
};
}
};
}]);
/**
* Contains ui-select "intelligence".
*
* The goal is to limit dependency on the DOM whenever possible and
* put as much logic in the controller (instead of the link functions) as possible so it can be easily tested.
*/
uis
.
controller
(
'uiSelectCtrl'
,
[
'$scope'
,
'$element'
,
'$timeout'
,
'$filter'
,
'$$uisDebounce'
,
'uisRepeatParser'
,
'uiSelectMinErr'
,
'uiSelectConfig'
,
'$parse'
,
'$injector'
,
'$window'
,
function
(
$scope
,
$element
,
$timeout
,
$filter
,
$$uisDebounce
,
RepeatParser
,
uiSelectMinErr
,
uiSelectConfig
,
$parse
,
$injector
,
$window
)
{
var
ctrl
=
this
;
var
EMPTY_SEARCH
=
''
;
ctrl
.
placeholder
=
uiSelectConfig
.
placeholder
;
ctrl
.
searchEnabled
=
uiSelectConfig
.
searchEnabled
;
ctrl
.
sortable
=
uiSelectConfig
.
sortable
;
ctrl
.
refreshDelay
=
uiSelectConfig
.
refreshDelay
;
ctrl
.
paste
=
uiSelectConfig
.
paste
;
ctrl
.
resetSearchInput
=
uiSelectConfig
.
resetSearchInput
;
ctrl
.
removeSelected
=
uiSelectConfig
.
removeSelected
;
//If selected item(s) should be removed from dropdown list
ctrl
.
closeOnSelect
=
true
;
//Initialized inside uiSelect directive link function
ctrl
.
skipFocusser
=
false
;
//Set to true to avoid returning focus to ctrl when item is selected
ctrl
.
search
=
EMPTY_SEARCH
;
ctrl
.
activeIndex
=
0
;
//Dropdown of choices
ctrl
.
items
=
[];
//All available choices
ctrl
.
open
=
false
;
ctrl
.
focus
=
false
;
ctrl
.
disabled
=
false
;
ctrl
.
selected
=
undefined
;
ctrl
.
dropdownPosition
=
'auto'
;
ctrl
.
focusser
=
undefined
;
//Reference to input element used to handle focus events
ctrl
.
multiple
=
undefined
;
// Initialized inside uiSelect directive link function
ctrl
.
disableChoiceExpression
=
undefined
;
// Initialized inside uiSelectChoices directive link function
ctrl
.
tagging
=
{
isActivated
:
false
,
fct
:
undefined
};
ctrl
.
taggingTokens
=
{
isActivated
:
false
,
tokens
:
undefined
};
ctrl
.
lockChoiceExpression
=
undefined
;
// Initialized inside uiSelectMatch directive link function
ctrl
.
clickTriggeredSelect
=
false
;
ctrl
.
$filter
=
$filter
;
ctrl
.
$element
=
$element
;
// Use $injector to check for $animate and store a reference to it
ctrl
.
$animate
=
(
function
()
{
try
{
return
$injector
.
get
(
'$animate'
);
}
catch
(
err
)
{
// $animate does not exist
return
null
;
}
})();
ctrl
.
searchInput
=
$element
.
querySelectorAll
(
'input.ui-select-search'
);
if
(
ctrl
.
searchInput
.
length
!==
1
)
{
throw
uiSelectMinErr
(
'searchInput'
,
"Expected 1 input.ui-select-search but got '{0}'."
,
ctrl
.
searchInput
.
length
);
}
ctrl
.
isEmpty
=
function
()
{
return
angular
.
isUndefined
(
ctrl
.
selected
)
||
ctrl
.
selected
===
null
||
ctrl
.
selected
===
''
||
(
ctrl
.
multiple
&&
ctrl
.
selected
.
length
===
0
);
};
function
_findIndex
(
collection
,
predicate
,
thisArg
){
if
(
collection
.
findIndex
){
return
collection
.
findIndex
(
predicate
,
thisArg
);
}
else
{
var
list
=
Object
(
collection
);
var
length
=
list
.
length
>>>
0
;
var
value
;
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
value
=
list
[
i
];
if
(
predicate
.
call
(
thisArg
,
value
,
i
,
list
))
{
return
i
;
}
}
return
-
1
;
}
}
// Most of the time the user does not want to empty the search input when in typeahead mode
function
_resetSearchInput
()
{
if
(
ctrl
.
resetSearchInput
)
{
ctrl
.
search
=
EMPTY_SEARCH
;
//reset activeIndex
if
(
ctrl
.
selected
&&
ctrl
.
items
.
length
&&
!
ctrl
.
multiple
)
{
ctrl
.
activeIndex
=
_findIndex
(
ctrl
.
items
,
function
(
item
){
return
angular
.
equals
(
this
,
item
);
},
ctrl
.
selected
);
}
}
}
function
_groupsFilter
(
groups
,
groupNames
)
{
var
i
,
j
,
result
=
[];
for
(
i
=
0
;
i
<
groupNames
.
length
;
i
++
){
for
(
j
=
0
;
j
<
groups
.
length
;
j
++
){
if
(
groups
[
j
].
name
==
[
groupNames
[
i
]]){
result
.
push
(
groups
[
j
]);
}
}
}
return
result
;
}
// When the user clicks on ui-select, displays the dropdown list
ctrl
.
activate
=
function
(
initSearchValue
,
avoidReset
)
{
if
(
!
ctrl
.
disabled
&&
!
ctrl
.
open
)
{
if
(
!
avoidReset
)
_resetSearchInput
();
$scope
.
$broadcast
(
'uis:activate'
);
ctrl
.
open
=
true
;
ctrl
.
activeIndex
=
ctrl
.
activeIndex
>=
ctrl
.
items
.
length
?
0
:
ctrl
.
activeIndex
;
// ensure that the index is set to zero for tagging variants
// that where first option is auto-selected
if
(
ctrl
.
activeIndex
===
-
1
&&
ctrl
.
taggingLabel
!==
false
)
{
ctrl
.
activeIndex
=
0
;
}
var
container
=
$element
.
querySelectorAll
(
'.ui-select-choices-content'
);
var
searchInput
=
$element
.
querySelectorAll
(
'.ui-select-search'
);
if
(
ctrl
.
$animate
&&
ctrl
.
$animate
.
on
&&
ctrl
.
$animate
.
enabled
(
container
[
0
]))
{
var
animateHandler
=
function
(
elem
,
phase
)
{
if
(
phase
===
'start'
&&
ctrl
.
items
.
length
===
0
)
{
// Only focus input after the animation has finished
ctrl
.
$animate
.
off
(
'removeClass'
,
searchInput
[
0
],
animateHandler
);
$timeout
(
function
()
{
ctrl
.
focusSearchInput
(
initSearchValue
);
});
}
else
if
(
phase
===
'close'
)
{
// Only focus input after the animation has finished
ctrl
.
$animate
.
off
(
'enter'
,
container
[
0
],
animateHandler
);
$timeout
(
function
()
{
ctrl
.
focusSearchInput
(
initSearchValue
);
});
}
};
if
(
ctrl
.
items
.
length
>
0
)
{
ctrl
.
$animate
.
on
(
'enter'
,
container
[
0
],
animateHandler
);
}
else
{
ctrl
.
$animate
.
on
(
'removeClass'
,
searchInput
[
0
],
animateHandler
);
}
}
else
{
$timeout
(
function
()
{
ctrl
.
focusSearchInput
(
initSearchValue
);
if
(
!
ctrl
.
tagging
.
isActivated
&&
ctrl
.
items
.
length
>
1
)
{
_ensureHighlightVisible
();
}
});
}
}
else
if
(
ctrl
.
open
&&
!
ctrl
.
searchEnabled
)
{
// Close the selection if we don't have search enabled, and we click on the select again
ctrl
.
close
();
}
};
ctrl
.
focusSearchInput
=
function
(
initSearchValue
)
{
ctrl
.
search
=
initSearchValue
||
ctrl
.
search
;
ctrl
.
searchInput
[
0
].
focus
();
};
ctrl
.
findGroupByName
=
function
(
name
)
{
return
ctrl
.
groups
&&
ctrl
.
groups
.
filter
(
function
(
group
)
{
return
group
.
name
===
name
;
})[
0
];
};
ctrl
.
parseRepeatAttr
=
function
(
repeatAttr
,
groupByExp
,
groupFilterExp
)
{
function
updateGroups
(
items
)
{
var
groupFn
=
$scope
.
$eval
(
groupByExp
);
ctrl
.
groups
=
[];
angular
.
forEach
(
items
,
function
(
item
)
{
var
groupName
=
angular
.
isFunction
(
groupFn
)
?
groupFn
(
item
)
:
item
[
groupFn
];
var
group
=
ctrl
.
findGroupByName
(
groupName
);
if
(
group
)
{
group
.
items
.
push
(
item
);
}
else
{
ctrl
.
groups
.
push
({
name
:
groupName
,
items
:
[
item
]});
}
});
if
(
groupFilterExp
){
var
groupFilterFn
=
$scope
.
$eval
(
groupFilterExp
);
if
(
angular
.
isFunction
(
groupFilterFn
)){
ctrl
.
groups
=
groupFilterFn
(
ctrl
.
groups
);
}
else
if
(
angular
.
isArray
(
groupFilterFn
)){
ctrl
.
groups
=
_groupsFilter
(
ctrl
.
groups
,
groupFilterFn
);
}
}
ctrl
.
items
=
[];
ctrl
.
groups
.
forEach
(
function
(
group
)
{
ctrl
.
items
=
ctrl
.
items
.
concat
(
group
.
items
);
});
}
function
setPlainItems
(
items
)
{
ctrl
.
items
=
items
;
}
ctrl
.
setItemsFn
=
groupByExp
?
updateGroups
:
setPlainItems
;
ctrl
.
parserResult
=
RepeatParser
.
parse
(
repeatAttr
);
ctrl
.
isGrouped
=
!!
groupByExp
;
ctrl
.
itemProperty
=
ctrl
.
parserResult
.
itemName
;
//If collection is an Object, convert it to Array
var
originalSource
=
ctrl
.
parserResult
.
source
;
//When an object is used as source, we better create an array and use it as 'source'
var
createArrayFromObject
=
function
(){
var
origSrc
=
originalSource
(
$scope
);
$scope
.
$uisSource
=
Object
.
keys
(
origSrc
).
map
(
function
(
v
){
var
result
=
{};
result
[
ctrl
.
parserResult
.
keyName
]
=
v
;
result
.
value
=
origSrc
[
v
];
return
result
;
});
};
if
(
ctrl
.
parserResult
.
keyName
){
// Check for (key,value) syntax
createArrayFromObject
();
ctrl
.
parserResult
.
source
=
$parse
(
'$uisSource'
+
ctrl
.
parserResult
.
filters
);
$scope
.
$watch
(
originalSource
,
function
(
newVal
,
oldVal
){
if
(
newVal
!==
oldVal
)
createArrayFromObject
();
},
true
);
}
ctrl
.
refreshItems
=
function
(
data
){
data
=
data
||
ctrl
.
parserResult
.
source
(
$scope
);
var
selectedItems
=
ctrl
.
selected
;
//TODO should implement for single mode removeSelected
if
(
ctrl
.
isEmpty
()
||
(
angular
.
isArray
(
selectedItems
)
&&
!
selectedItems
.
length
)
||
!
ctrl
.
multiple
||
!
ctrl
.
removeSelected
)
{
ctrl
.
setItemsFn
(
data
);
}
else
{
if
(
data
!==
undefined
&&
data
!==
null
)
{
var
filteredItems
=
data
.
filter
(
function
(
i
)
{
return
angular
.
isArray
(
selectedItems
)
?
selectedItems
.
every
(
function
(
selectedItem
)
{
return
!
angular
.
equals
(
i
,
selectedItem
);
})
:
!
angular
.
equals
(
i
,
selectedItems
);
});
ctrl
.
setItemsFn
(
filteredItems
);
}
}
if
(
ctrl
.
dropdownPosition
===
'auto'
||
ctrl
.
dropdownPosition
===
'up'
){
$scope
.
calculateDropdownPos
();
}
$scope
.
$broadcast
(
'uis:refresh'
);
};
// See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L259
$scope
.
$watchCollection
(
ctrl
.
parserResult
.
source
,
function
(
items
)
{
if
(
items
===
undefined
||
items
===
null
)
{
// If the user specifies undefined or null => reset the collection
// Special case: items can be undefined if the user did not initialized the collection on the scope
// i.e $scope.addresses = [] is missing
ctrl
.
items
=
[];
}
else
{
if
(
!
angular
.
isArray
(
items
))
{
throw
uiSelectMinErr
(
'items'
,
"Expected an array but got '{0}'."
,
items
);
}
else
{
//Remove already selected items (ex: while searching)
//TODO Should add a test
ctrl
.
refreshItems
(
items
);
//update the view value with fresh data from items, if there is a valid model value
if
(
angular
.
isDefined
(
ctrl
.
ngModel
.
$modelValue
))
{
ctrl
.
ngModel
.
$modelValue
=
null
;
//Force scope model value and ngModel value to be out of sync to re-run formatters
}
}
}
});
};
var
_refreshDelayPromise
;
/**
* Typeahead mode: lets the user refresh the collection using his own function.
*
* See Expose $select.search for external / remote filtering https://github.com/angular-ui/ui-select/pull/31
*/
ctrl
.
refresh
=
function
(
refreshAttr
)
{
if
(
refreshAttr
!==
undefined
)
{
// Debounce
// See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L155
// FYI AngularStrap typeahead does not have debouncing: https://github.com/mgcrea/angular-strap/blob/v2.0.0-rc.4/src/typeahead/typeahead.js#L177
if
(
_refreshDelayPromise
)
{
$timeout
.
cancel
(
_refreshDelayPromise
);
}
_refreshDelayPromise
=
$timeout
(
function
()
{
$scope
.
$eval
(
refreshAttr
);
},
ctrl
.
refreshDelay
);
}
};
ctrl
.
isActive
=
function
(
itemScope
)
{
if
(
!
ctrl
.
open
)
{
return
false
;
}
var
itemIndex
=
ctrl
.
items
.
indexOf
(
itemScope
[
ctrl
.
itemProperty
]);
var
isActive
=
itemIndex
==
ctrl
.
activeIndex
;
if
(
!
isActive
||
itemIndex
<
0
)
{
return
false
;
}
if
(
isActive
&&
!
angular
.
isUndefined
(
ctrl
.
onHighlightCallback
))
{
itemScope
.
$eval
(
ctrl
.
onHighlightCallback
);
}
return
isActive
;
};
var
_isItemSelected
=
function
(
item
)
{
return
(
ctrl
.
selected
&&
angular
.
isArray
(
ctrl
.
selected
)
&&
ctrl
.
selected
.
filter
(
function
(
selection
)
{
return
angular
.
equals
(
selection
,
item
);
}).
length
>
0
);
};
var
disabledItems
=
[];
function
_updateItemDisabled
(
item
,
isDisabled
)
{
var
disabledItemIndex
=
disabledItems
.
indexOf
(
item
);
if
(
isDisabled
&&
disabledItemIndex
===
-
1
)
{
disabledItems
.
push
(
item
);
}
if
(
!
isDisabled
&&
disabledItemIndex
>
-
1
)
{
disabledItems
.
splice
(
disabledItemIndex
,
1
);
}
}
function
_isItemDisabled
(
item
)
{
return
disabledItems
.
indexOf
(
item
)
>
-
1
;
}
ctrl
.
isDisabled
=
function
(
itemScope
)
{
if
(
!
ctrl
.
open
)
return
;
var
item
=
itemScope
[
ctrl
.
itemProperty
];
var
itemIndex
=
ctrl
.
items
.
indexOf
(
item
);
var
isDisabled
=
false
;
if
(
itemIndex
>=
0
&&
(
angular
.
isDefined
(
ctrl
.
disableChoiceExpression
)
||
ctrl
.
multiple
))
{
if
(
item
.
isTag
)
return
false
;
if
(
ctrl
.
multiple
)
{
isDisabled
=
_isItemSelected
(
item
);
}
if
(
!
isDisabled
&&
angular
.
isDefined
(
ctrl
.
disableChoiceExpression
))
{
isDisabled
=
!!
(
itemScope
.
$eval
(
ctrl
.
disableChoiceExpression
));
}
_updateItemDisabled
(
item
,
isDisabled
);
}
return
isDisabled
;
};
// When the user selects an item with ENTER or clicks the dropdown
ctrl
.
select
=
function
(
item
,
skipFocusser
,
$event
)
{
if
(
item
===
undefined
||
!
_isItemDisabled
(
item
))
{
if
(
!
ctrl
.
items
&&
!
ctrl
.
search
&&
!
ctrl
.
tagging
.
isActivated
)
return
;
if
(
!
item
||
!
_isItemDisabled
(
item
))
{
// if click is made on existing item, prevent from tagging, ctrl.search does not matter
ctrl
.
clickTriggeredSelect
=
false
;
if
(
$event
&&
$event
.
type
===
'click'
&&
item
)
ctrl
.
clickTriggeredSelect
=
true
;
if
(
ctrl
.
tagging
.
isActivated
&&
ctrl
.
clickTriggeredSelect
===
false
)
{
// if taggingLabel is disabled and item is undefined we pull from ctrl.search
if
(
ctrl
.
taggingLabel
===
false
)
{
if
(
ctrl
.
activeIndex
<
0
)
{
if
(
item
===
undefined
)
{
item
=
ctrl
.
tagging
.
fct
!==
undefined
?
ctrl
.
tagging
.
fct
(
ctrl
.
search
)
:
ctrl
.
search
;
}
if
(
!
item
||
angular
.
equals
(
ctrl
.
items
[
0
],
item
)
)
{
return
;
}
}
else
{
// keyboard nav happened first, user selected from dropdown
item
=
ctrl
.
items
[
ctrl
.
activeIndex
];
}
}
else
{
// tagging always operates at index zero, taggingLabel === false pushes
// the ctrl.search value without having it injected
if
(
ctrl
.
activeIndex
===
0
)
{
// ctrl.tagging pushes items to ctrl.items, so we only have empty val
// for `item` if it is a detected duplicate
if
(
item
===
undefined
)
return
;
// create new item on the fly if we don't already have one;
// use tagging function if we have one
if
(
ctrl
.
tagging
.
fct
!==
undefined
&&
typeof
item
===
'string'
)
{
item
=
ctrl
.
tagging
.
fct
(
item
);
if
(
!
item
)
return
;
// if item type is 'string', apply the tagging label
}
else
if
(
typeof
item
===
'string'
)
{
// trim the trailing space
item
=
item
.
replace
(
ctrl
.
taggingLabel
,
''
).
trim
();
}
}
}
// search ctrl.selected for dupes potentially caused by tagging and return early if found
if
(
_isItemSelected
(
item
))
{
ctrl
.
close
(
skipFocusser
);
return
;
}
}
_resetSearchInput
();
$scope
.
$broadcast
(
'uis:select'
,
item
);
var
locals
=
{};
locals
[
ctrl
.
parserResult
.
itemName
]
=
item
;
$timeout
(
function
(){
ctrl
.
onSelectCallback
(
$scope
,
{
$item
:
item
,
$model
:
ctrl
.
parserResult
.
modelMapper
(
$scope
,
locals
)
});
});
if
(
ctrl
.
closeOnSelect
)
{
ctrl
.
close
(
skipFocusser
);
}
}
}
};
// Closes the dropdown
ctrl
.
close
=
function
(
skipFocusser
)
{
if
(
!
ctrl
.
open
)
return
;
if
(
ctrl
.
ngModel
&&
ctrl
.
ngModel
.
$setTouched
)
ctrl
.
ngModel
.
$setTouched
();
ctrl
.
open
=
false
;
_resetSearchInput
();
$scope
.
$broadcast
(
'uis:close'
,
skipFocusser
);
};
ctrl
.
setFocus
=
function
(){
if
(
!
ctrl
.
focus
)
ctrl
.
focusInput
[
0
].
focus
();
};
ctrl
.
clear
=
function
(
$event
)
{
ctrl
.
select
(
undefined
);
$event
.
stopPropagation
();
$timeout
(
function
()
{
ctrl
.
focusser
[
0
].
focus
();
},
0
,
false
);
};
// Toggle dropdown
ctrl
.
toggle
=
function
(
e
)
{
if
(
ctrl
.
open
)
{
ctrl
.
close
();
e
.
preventDefault
();
e
.
stopPropagation
();
}
else
{
ctrl
.
activate
();
}
};
// Set default function for locked choices - avoids unnecessary
// logic if functionality is not being used
ctrl
.
isLocked
=
function
()
{
return
false
;
};
$scope
.
$watch
(
function
()
{
return
angular
.
isDefined
(
ctrl
.
lockChoiceExpression
)
&&
ctrl
.
lockChoiceExpression
!==
""
;
},
_initaliseLockedChoices
);
function
_initaliseLockedChoices
(
doInitalise
)
{
if
(
!
doInitalise
)
return
;
var
lockedItems
=
[];
function
_updateItemLocked
(
item
,
isLocked
)
{
var
lockedItemIndex
=
lockedItems
.
indexOf
(
item
);
if
(
isLocked
&&
lockedItemIndex
===
-
1
)
{
lockedItems
.
push
(
item
);
}
if
(
!
isLocked
&&
lockedItemIndex
>
-
1
)
{
lockedItems
.
splice
(
lockedItemIndex
,
0
);
}
}
function
_isItemlocked
(
item
)
{
return
lockedItems
.
indexOf
(
item
)
>
-
1
;
}
ctrl
.
isLocked
=
function
(
itemScope
,
itemIndex
)
{
var
isLocked
=
false
,
item
=
ctrl
.
selected
[
itemIndex
];
if
(
item
)
{
if
(
itemScope
)
{
isLocked
=
!!
(
itemScope
.
$eval
(
ctrl
.
lockChoiceExpression
));
_updateItemLocked
(
item
,
isLocked
);
}
else
{
isLocked
=
_isItemlocked
(
item
);
}
}
return
isLocked
;
};
}
var
sizeWatch
=
null
;
var
updaterScheduled
=
false
;
ctrl
.
sizeSearchInput
=
function
()
{
var
input
=
ctrl
.
searchInput
[
0
],
container
=
ctrl
.
searchInput
.
parent
().
parent
()[
0
],
calculateContainerWidth
=
function
()
{
// Return the container width only if the search input is visible
return
container
.
clientWidth
*
!!
input
.
offsetParent
;
},
updateIfVisible
=
function
(
containerWidth
)
{
if
(
containerWidth
===
0
)
{
return
false
;
}
var
inputWidth
=
containerWidth
-
input
.
offsetLeft
-
10
;
if
(
inputWidth
<
50
)
inputWidth
=
containerWidth
;
ctrl
.
searchInput
.
css
(
'width'
,
inputWidth
+
'px'
);
return
true
;
};
ctrl
.
searchInput
.
css
(
'width'
,
'10px'
);
$timeout
(
function
()
{
//Give tags time to render correctly
if
(
sizeWatch
===
null
&&
!
updateIfVisible
(
calculateContainerWidth
()))
{
sizeWatch
=
$scope
.
$watch
(
function
()
{
if
(
!
updaterScheduled
)
{
updaterScheduled
=
true
;
$scope
.
$$postDigest
(
function
()
{
updaterScheduled
=
false
;
if
(
updateIfVisible
(
calculateContainerWidth
()))
{
sizeWatch
();
sizeWatch
=
null
;
}
});
}
},
angular
.
noop
);
}
});
};
function
_handleDropDownSelection
(
key
)
{
var
processed
=
true
;
switch
(
key
)
{
case
KEY
.
DOWN
:
if
(
!
ctrl
.
open
&&
ctrl
.
multiple
)
ctrl
.
activate
(
false
,
true
);
//In case its the search input in 'multiple' mode
else
if
(
ctrl
.
activeIndex
<
ctrl
.
items
.
length
-
1
)
{
ctrl
.
activeIndex
++
;
}
break
;
case
KEY
.
UP
:
if
(
!
ctrl
.
open
&&
ctrl
.
multiple
)
ctrl
.
activate
(
false
,
true
);
//In case its the search input in 'multiple' mode
else
if
(
ctrl
.
activeIndex
>
0
||
(
ctrl
.
search
.
length
===
0
&&
ctrl
.
tagging
.
isActivated
&&
ctrl
.
activeIndex
>
-
1
))
{
ctrl
.
activeIndex
--
;
}
break
;
case
KEY
.
TAB
:
if
(
!
ctrl
.
multiple
||
ctrl
.
open
)
ctrl
.
select
(
ctrl
.
items
[
ctrl
.
activeIndex
],
true
);
break
;
case
KEY
.
ENTER
:
if
(
ctrl
.
open
&&
(
ctrl
.
tagging
.
isActivated
||
ctrl
.
activeIndex
>=
0
)){
ctrl
.
select
(
ctrl
.
items
[
ctrl
.
activeIndex
],
ctrl
.
skipFocusser
);
// Make sure at least one dropdown item is highlighted before adding if not in tagging mode
}
else
{
ctrl
.
activate
(
false
,
true
);
//In case its the search input in 'multiple' mode
}
break
;
case
KEY
.
ESC
:
ctrl
.
close
();
break
;
default
:
processed
=
false
;
}
return
processed
;
}
// Bind to keyboard shortcuts
ctrl
.
searchInput
.
on
(
'keydown'
,
function
(
e
)
{
var
key
=
e
.
which
;
if
(
~
[
KEY
.
ENTER
,
KEY
.
ESC
].
indexOf
(
key
)){
e
.
preventDefault
();
e
.
stopPropagation
();
}
// if(~[KEY.ESC,KEY.TAB].indexOf(key)){
// //TODO: SEGURO?
// ctrl.close();
// }
$scope
.
$apply
(
function
()
{
var
tagged
=
false
;
if
(
ctrl
.
items
.
length
>
0
||
ctrl
.
tagging
.
isActivated
)
{
if
(
!
_handleDropDownSelection
(
key
)
&&
!
ctrl
.
searchEnabled
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
}
if
(
ctrl
.
taggingTokens
.
isActivated
)
{
for
(
var
i
=
0
;
i
<
ctrl
.
taggingTokens
.
tokens
.
length
;
i
++
)
{
if
(
ctrl
.
taggingTokens
.
tokens
[
i
]
===
KEY
.
MAP
[
e
.
keyCode
]
)
{
// make sure there is a new value to push via tagging
if
(
ctrl
.
search
.
length
>
0
)
{
tagged
=
true
;
}
}
}
if
(
tagged
)
{
$timeout
(
function
()
{
ctrl
.
searchInput
.
triggerHandler
(
'tagged'
);
var
newItem
=
ctrl
.
search
.
replace
(
KEY
.
MAP
[
e
.
keyCode
],
''
).
trim
();
if
(
ctrl
.
tagging
.
fct
)
{
newItem
=
ctrl
.
tagging
.
fct
(
newItem
);
}
if
(
newItem
)
ctrl
.
select
(
newItem
,
true
);
});
}
}
}
});
if
(
KEY
.
isVerticalMovement
(
key
)
&&
ctrl
.
items
.
length
>
0
){
_ensureHighlightVisible
();
}
if
(
key
===
KEY
.
ENTER
||
key
===
KEY
.
ESC
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
}
});
ctrl
.
searchInput
.
on
(
'paste'
,
function
(
e
)
{
var
data
;
if
(
window
.
clipboardData
&&
window
.
clipboardData
.
getData
)
{
// IE
data
=
window
.
clipboardData
.
getData
(
'Text'
);
}
else
{
data
=
(
e
.
originalEvent
||
e
).
clipboardData
.
getData
(
'text/plain'
);
}
// Prepend the current input field text to the paste buffer.
data
=
ctrl
.
search
+
data
;
if
(
data
&&
data
.
length
>
0
)
{
// If tagging try to split by tokens and add items
if
(
ctrl
.
taggingTokens
.
isActivated
)
{
var
items
=
[];
for
(
var
i
=
0
;
i
<
ctrl
.
taggingTokens
.
tokens
.
length
;
i
++
)
{
// split by first token that is contained in data
var
separator
=
KEY
.
toSeparator
(
ctrl
.
taggingTokens
.
tokens
[
i
])
||
ctrl
.
taggingTokens
.
tokens
[
i
];
if
(
data
.
indexOf
(
separator
)
>
-
1
)
{
items
=
data
.
split
(
separator
);
break
;
// only split by one token
}
}
if
(
items
.
length
===
0
)
{
items
=
[
data
];
}
var
oldsearch
=
ctrl
.
search
;
angular
.
forEach
(
items
,
function
(
item
)
{
var
newItem
=
ctrl
.
tagging
.
fct
?
ctrl
.
tagging
.
fct
(
item
)
:
item
;
if
(
newItem
)
{
ctrl
.
select
(
newItem
,
true
);
}
});
ctrl
.
search
=
oldsearch
||
EMPTY_SEARCH
;
e
.
preventDefault
();
e
.
stopPropagation
();
}
else
if
(
ctrl
.
paste
)
{
ctrl
.
paste
(
data
);
ctrl
.
search
=
EMPTY_SEARCH
;
e
.
preventDefault
();
e
.
stopPropagation
();
}
}
});
ctrl
.
searchInput
.
on
(
'tagged'
,
function
()
{
$timeout
(
function
()
{
_resetSearchInput
();
});
});
// See https://github.com/ivaynberg/select2/blob/3.4.6/select2.js#L1431
function
_ensureHighlightVisible
()
{
var
container
=
$element
.
querySelectorAll
(
'.ui-select-choices-content'
);
var
choices
=
container
.
querySelectorAll
(
'.ui-select-choices-row'
);
if
(
choices
.
length
<
1
)
{
throw
uiSelectMinErr
(
'choices'
,
"Expected multiple .ui-select-choices-row but got '{0}'."
,
choices
.
length
);
}
if
(
ctrl
.
activeIndex
<
0
)
{
return
;
}
var
highlighted
=
choices
[
ctrl
.
activeIndex
];
var
posY
=
highlighted
.
offsetTop
+
highlighted
.
clientHeight
-
container
[
0
].
scrollTop
;
var
height
=
container
[
0
].
offsetHeight
;
if
(
posY
>
height
)
{
container
[
0
].
scrollTop
+=
posY
-
height
;
}
else
if
(
posY
<
highlighted
.
clientHeight
)
{
if
(
ctrl
.
isGrouped
&&
ctrl
.
activeIndex
===
0
)
container
[
0
].
scrollTop
=
0
;
//To make group header visible when going all the way up
else
container
[
0
].
scrollTop
-=
highlighted
.
clientHeight
-
posY
;
}
}
var
onResize
=
$$uisDebounce
(
function
()
{
ctrl
.
sizeSearchInput
();
},
50
);
angular
.
element
(
$window
).
bind
(
'resize'
,
onResize
);
$scope
.
$on
(
'$destroy'
,
function
()
{
ctrl
.
searchInput
.
off
(
'keyup keydown tagged blur paste'
);
angular
.
element
(
$window
).
off
(
'resize'
,
onResize
);
});
}]);
uis
.
directive
(
'uiSelect'
,
[
'$document'
,
'uiSelectConfig'
,
'uiSelectMinErr'
,
'uisOffset'
,
'$compile'
,
'$parse'
,
'$timeout'
,
function
(
$document
,
uiSelectConfig
,
uiSelectMinErr
,
uisOffset
,
$compile
,
$parse
,
$timeout
)
{
return
{
restrict
:
'EA'
,
templateUrl
:
function
(
tElement
,
tAttrs
)
{
var
theme
=
tAttrs
.
theme
||
uiSelectConfig
.
theme
;
return
theme
+
(
angular
.
isDefined
(
tAttrs
.
multiple
)
?
'/select-multiple.tpl.html'
:
'/select.tpl.html'
);
},
replace
:
true
,
transclude
:
true
,
require
:
[
'uiSelect'
,
'^ngModel'
],
scope
:
true
,
controller
:
'uiSelectCtrl'
,
controllerAs
:
'$select'
,
compile
:
function
(
tElement
,
tAttrs
)
{
// Allow setting ngClass on uiSelect
var
match
=
/{
(
.*
)
}
\s
*{
(
.*
)
}/
.
exec
(
tAttrs
.
ngClass
);
if
(
match
)
{
var
combined
=
'{'
+
match
[
1
]
+
', '
+
match
[
2
]
+
'}'
;
tAttrs
.
ngClass
=
combined
;
tElement
.
attr
(
'ng-class'
,
combined
);
}
//Multiple or Single depending if multiple attribute presence
if
(
angular
.
isDefined
(
tAttrs
.
multiple
))
tElement
.
append
(
'<ui-select-multiple/>'
).
removeAttr
(
'multiple'
);
else
tElement
.
append
(
'<ui-select-single/>'
);
if
(
tAttrs
.
inputId
)
tElement
.
querySelectorAll
(
'input.ui-select-search'
)[
0
].
id
=
tAttrs
.
inputId
;
return
function
(
scope
,
element
,
attrs
,
ctrls
,
transcludeFn
)
{
var
$select
=
ctrls
[
0
];
var
ngModel
=
ctrls
[
1
];
$select
.
name
=
attrs
.
name
;
$select
.
generatedId
=
uiSelectConfig
.
generateId
();
$select
.
baseTitle
=
attrs
.
title
||
'Select box'
;
$select
.
focusserTitle
=
$select
.
baseTitle
+
' focus'
;
$select
.
focusserId
=
'focusser-'
+
$select
.
generatedId
;
$select
.
closeOnSelect
=
function
()
{
if
(
angular
.
isDefined
(
attrs
.
closeOnSelect
))
{
return
$parse
(
attrs
.
closeOnSelect
)();
}
else
{
return
uiSelectConfig
.
closeOnSelect
;
}
}();
scope
.
$watch
(
'skipFocusser'
,
function
()
{
var
skipFocusser
=
scope
.
$eval
(
attrs
.
skipFocusser
);
$select
.
skipFocusser
=
skipFocusser
!==
undefined
?
skipFocusser
:
uiSelectConfig
.
skipFocusser
;
});
$select
.
onSelectCallback
=
$parse
(
attrs
.
onSelect
);
$select
.
onRemoveCallback
=
$parse
(
attrs
.
onRemove
);
//Set reference to ngModel from uiSelectCtrl
$select
.
ngModel
=
ngModel
;
$select
.
choiceGrouped
=
function
(
group
){
return
$select
.
isGrouped
&&
group
&&
group
.
name
;
};
if
(
attrs
.
tabindex
){
attrs
.
$observe
(
'tabindex'
,
function
(
value
)
{
$select
.
focusInput
.
attr
(
'tabindex'
,
value
);
element
.
removeAttr
(
'tabindex'
);
});
}
scope
.
$watch
(
function
()
{
return
scope
.
$eval
(
attrs
.
searchEnabled
);
},
function
(
newVal
)
{
$select
.
searchEnabled
=
newVal
!==
undefined
?
newVal
:
uiSelectConfig
.
searchEnabled
;
});
scope
.
$watch
(
'sortable'
,
function
()
{
var
sortable
=
scope
.
$eval
(
attrs
.
sortable
);
$select
.
sortable
=
sortable
!==
undefined
?
sortable
:
uiSelectConfig
.
sortable
;
});
attrs
.
$observe
(
'limit'
,
function
()
{
//Limit the number of selections allowed
$select
.
limit
=
(
angular
.
isDefined
(
attrs
.
limit
))
?
parseInt
(
attrs
.
limit
,
10
)
:
undefined
;
});
scope
.
$watch
(
'removeSelected'
,
function
()
{
var
removeSelected
=
scope
.
$eval
(
attrs
.
removeSelected
);
$select
.
removeSelected
=
removeSelected
!==
undefined
?
removeSelected
:
uiSelectConfig
.
removeSelected
;
});
attrs
.
$observe
(
'disabled'
,
function
()
{
// No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
$select
.
disabled
=
attrs
.
disabled
!==
undefined
?
attrs
.
disabled
:
false
;
});
attrs
.
$observe
(
'resetSearchInput'
,
function
()
{
// $eval() is needed otherwise we get a string instead of a boolean
var
resetSearchInput
=
scope
.
$eval
(
attrs
.
resetSearchInput
);
$select
.
resetSearchInput
=
resetSearchInput
!==
undefined
?
resetSearchInput
:
true
;
});
attrs
.
$observe
(
'paste'
,
function
()
{
$select
.
paste
=
scope
.
$eval
(
attrs
.
paste
);
});
attrs
.
$observe
(
'tagging'
,
function
()
{
if
(
attrs
.
tagging
!==
undefined
)
{
// $eval() is needed otherwise we get a string instead of a boolean
var
taggingEval
=
scope
.
$eval
(
attrs
.
tagging
);
$select
.
tagging
=
{
isActivated
:
true
,
fct
:
taggingEval
!==
true
?
taggingEval
:
undefined
};
}
else
{
$select
.
tagging
=
{
isActivated
:
false
,
fct
:
undefined
};
}
});
attrs
.
$observe
(
'taggingLabel'
,
function
()
{
if
(
attrs
.
tagging
!==
undefined
)
{
// check eval for FALSE, in this case, we disable the labels
// associated with tagging
if
(
attrs
.
taggingLabel
===
'false'
)
{
$select
.
taggingLabel
=
false
;
}
else
{
$select
.
taggingLabel
=
attrs
.
taggingLabel
!==
undefined
?
attrs
.
taggingLabel
:
'(new)'
;
}
}
});
attrs
.
$observe
(
'taggingTokens'
,
function
()
{
if
(
attrs
.
tagging
!==
undefined
)
{
var
tokens
=
attrs
.
taggingTokens
!==
undefined
?
attrs
.
taggingTokens
.
split
(
'|'
)
:
[
','
,
'ENTER'
];
$select
.
taggingTokens
=
{
isActivated
:
true
,
tokens
:
tokens
};
}
});
//Automatically gets focus when loaded
if
(
angular
.
isDefined
(
attrs
.
autofocus
)){
$timeout
(
function
(){
$select
.
setFocus
();
});
}
//Gets focus based on scope event name (e.g. focus-on='SomeEventName')
if
(
angular
.
isDefined
(
attrs
.
focusOn
)){
scope
.
$on
(
attrs
.
focusOn
,
function
()
{
$timeout
(
function
(){
$select
.
setFocus
();
});
});
}
function
onDocumentClick
(
e
)
{
if
(
!
$select
.
open
)
return
;
//Skip it if dropdown is close
var
contains
=
false
;
if
(
window
.
jQuery
)
{
// Firefox 3.6 does not support element.contains()
// See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains
contains
=
window
.
jQuery
.
contains
(
element
[
0
],
e
.
target
);
}
else
{
contains
=
element
[
0
].
contains
(
e
.
target
);
}
if
(
!
contains
&&
!
$select
.
clickTriggeredSelect
)
{
var
skipFocusser
;
if
(
!
$select
.
skipFocusser
)
{
//Will lose focus only with certain targets
var
focusableControls
=
[
'input'
,
'button'
,
'textarea'
,
'select'
];
var
targetController
=
angular
.
element
(
e
.
target
).
controller
(
'uiSelect'
);
//To check if target is other ui-select
skipFocusser
=
targetController
&&
targetController
!==
$select
;
//To check if target is other ui-select
if
(
!
skipFocusser
)
skipFocusser
=
~
focusableControls
.
indexOf
(
e
.
target
.
tagName
.
toLowerCase
());
//Check if target is input, button or textarea
}
else
{
skipFocusser
=
true
;
}
$select
.
close
(
skipFocusser
);
scope
.
$digest
();
}
$select
.
clickTriggeredSelect
=
false
;
}
// See Click everywhere but here event http://stackoverflow.com/questions/12931369
$document
.
on
(
'click'
,
onDocumentClick
);
scope
.
$on
(
'$destroy'
,
function
()
{
$document
.
off
(
'click'
,
onDocumentClick
);
});
// Move transcluded elements to their correct position in main template
transcludeFn
(
scope
,
function
(
clone
)
{
// See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html
// One day jqLite will be replaced by jQuery and we will be able to write:
// var transcludedElement = clone.filter('.my-class')
// instead of creating a hackish DOM element:
var
transcluded
=
angular
.
element
(
'<div>'
).
append
(
clone
);
var
transcludedMatch
=
transcluded
.
querySelectorAll
(
'.ui-select-match'
);
transcludedMatch
.
removeAttr
(
'ui-select-match'
);
//To avoid loop in case directive as attr
transcludedMatch
.
removeAttr
(
'data-ui-select-match'
);
// Properly handle HTML5 data-attributes
if
(
transcludedMatch
.
length
!==
1
)
{
throw
uiSelectMinErr
(
'transcluded'
,
"Expected 1 .ui-select-match but got '{0}'."
,
transcludedMatch
.
length
);
}
element
.
querySelectorAll
(
'.ui-select-match'
).
replaceWith
(
transcludedMatch
);
var
transcludedChoices
=
transcluded
.
querySelectorAll
(
'.ui-select-choices'
);
transcludedChoices
.
removeAttr
(
'ui-select-choices'
);
//To avoid loop in case directive as attr
transcludedChoices
.
removeAttr
(
'data-ui-select-choices'
);
// Properly handle HTML5 data-attributes
if
(
transcludedChoices
.
length
!==
1
)
{
throw
uiSelectMinErr
(
'transcluded'
,
"Expected 1 .ui-select-choices but got '{0}'."
,
transcludedChoices
.
length
);
}
element
.
querySelectorAll
(
'.ui-select-choices'
).
replaceWith
(
transcludedChoices
);
var
transcludedNoChoice
=
transcluded
.
querySelectorAll
(
'.ui-select-no-choice'
);
transcludedNoChoice
.
removeAttr
(
'ui-select-no-choice'
);
//To avoid loop in case directive as attr
transcludedNoChoice
.
removeAttr
(
'data-ui-select-no-choice'
);
// Properly handle HTML5 data-attributes
if
(
transcludedNoChoice
.
length
==
1
)
{
element
.
querySelectorAll
(
'.ui-select-no-choice'
).
replaceWith
(
transcludedNoChoice
);
}
});
// Support for appending the select field to the body when its open
var
appendToBody
=
scope
.
$eval
(
attrs
.
appendToBody
);
if
(
appendToBody
!==
undefined
?
appendToBody
:
uiSelectConfig
.
appendToBody
)
{
scope
.
$watch
(
'$select.open'
,
function
(
isOpen
)
{
if
(
isOpen
)
{
positionDropdown
();
}
else
{
resetDropdown
();
}
});
// Move the dropdown back to its original location when the scope is destroyed. Otherwise
// it might stick around when the user routes away or the select field is otherwise removed
scope
.
$on
(
'$destroy'
,
function
()
{
resetDropdown
();
});
}
// Hold on to a reference to the .ui-select-container element for appendToBody support
var
placeholder
=
null
,
originalWidth
=
''
;
function
positionDropdown
()
{
// Remember the absolute position of the element
var
offset
=
uisOffset
(
element
);
// Clone the element into a placeholder element to take its original place in the DOM
placeholder
=
angular
.
element
(
'<div class="ui-select-placeholder"></div>'
);
placeholder
[
0
].
style
.
width
=
offset
.
width
+
'px'
;
placeholder
[
0
].
style
.
height
=
offset
.
height
+
'px'
;
element
.
after
(
placeholder
);
// Remember the original value of the element width inline style, so it can be restored
// when the dropdown is closed
originalWidth
=
element
[
0
].
style
.
width
;
// Now move the actual dropdown element to the end of the body
$document
.
find
(
'body'
).
append
(
element
);
element
[
0
].
style
.
position
=
'absolute'
;
element
[
0
].
style
.
left
=
offset
.
left
+
'px'
;
element
[
0
].
style
.
top
=
0
+
'px'
;
element
[
0
].
style
.
width
=
offset
.
width
+
'px'
;
}
function
resetDropdown
()
{
if
(
placeholder
===
null
)
{
// The dropdown has not actually been display yet, so there's nothing to reset
return
;
}
// Move the dropdown element back to its original location in the DOM
placeholder
.
replaceWith
(
element
);
placeholder
=
null
;
element
[
0
].
style
.
position
=
''
;
element
[
0
].
style
.
left
=
''
;
element
[
0
].
style
.
top
=
''
;
element
[
0
].
style
.
width
=
originalWidth
;
// Set focus back on to the moved element
$select
.
setFocus
();
}
// Hold on to a reference to the .ui-select-dropdown element for direction support.
var
dropdown
=
null
,
directionUpClassName
=
'direction-up'
;
// Support changing the direction of the dropdown if there isn't enough space to render it.
scope
.
$watch
(
'$select.open'
,
function
()
{
if
(
$select
.
dropdownPosition
===
'auto'
||
$select
.
dropdownPosition
===
'up'
){
scope
.
calculateDropdownPos
();
}
});
var
setDropdownPosUp
=
function
(
offset
,
offsetDropdown
){
offset
=
offset
||
uisOffset
(
element
);
offsetDropdown
=
offsetDropdown
||
uisOffset
(
dropdown
);
dropdown
[
0
].
style
.
position
=
'absolute'
;
dropdown
[
0
].
style
.
top
=
0
+
'px'
;
element
.
addClass
(
directionUpClassName
);
};
var
setDropdownPosDown
=
function
(
offset
,
offsetDropdown
){
element
.
removeClass
(
directionUpClassName
);
offset
=
offset
||
uisOffset
(
element
);
offsetDropdown
=
offsetDropdown
||
uisOffset
(
dropdown
);
dropdown
[
0
].
style
.
position
=
''
;
dropdown
[
0
].
style
.
top
=
''
;
};
var
calculateDropdownPosAfterAnimation
=
function
()
{
// Delay positioning the dropdown until all choices have been added so its height is correct.
$timeout
(
function
()
{
if
(
$select
.
dropdownPosition
===
'up'
)
{
//Go UP
setDropdownPosUp
();
}
else
{
//AUTO
element
.
removeClass
(
directionUpClassName
);
var
offset
=
uisOffset
(
element
);
var
offsetDropdown
=
uisOffset
(
dropdown
);
//https://code.google.com/p/chromium/issues/detail?id=342307#c4
var
scrollTop
=
$document
[
0
].
documentElement
.
scrollTop
||
$document
[
0
].
body
.
scrollTop
;
//To make it cross browser (blink, webkit, IE, Firefox).
// Determine if the direction of the dropdown needs to be changed.
if
(
offset
.
top
+
offset
.
height
+
offsetDropdown
.
height
>
scrollTop
+
$document
[
0
].
documentElement
.
clientHeight
)
{
//Go UP
setDropdownPosUp
(
offset
,
offsetDropdown
);
}
else
{
//Go DOWN
setDropdownPosDown
(
offset
,
offsetDropdown
);
}
}
// Display the dropdown once it has been positioned.
dropdown
[
0
].
style
.
opacity
=
1
;
});
};
var
opened
=
false
;
scope
.
calculateDropdownPos
=
function
()
{
if
(
$select
.
open
)
{
dropdown
=
angular
.
element
(
element
).
querySelectorAll
(
'.ui-select-dropdown'
);
if
(
dropdown
.
length
===
0
)
{
return
;
}
// Hide the dropdown so there is no flicker until $timeout is done executing.
if
(
$select
.
search
===
''
&&
!
opened
)
{
dropdown
[
0
].
style
.
opacity
=
0
;
opened
=
true
;
}
if
(
!
uisOffset
(
dropdown
).
height
&&
$select
.
$animate
&&
$select
.
$animate
.
on
&&
$select
.
$animate
.
enabled
(
dropdown
))
{
var
needsCalculated
=
true
;
$select
.
$animate
.
on
(
'enter'
,
dropdown
,
function
(
elem
,
phase
)
{
if
(
phase
===
'close'
&&
needsCalculated
)
{
calculateDropdownPosAfterAnimation
();
needsCalculated
=
false
;
}
});
}
else
{
calculateDropdownPosAfterAnimation
();
}
}
else
{
if
(
dropdown
===
null
||
dropdown
.
length
===
0
)
{
return
;
}
// Reset the position of the dropdown.
dropdown
[
0
].
style
.
opacity
=
0
;
dropdown
[
0
].
style
.
position
=
''
;
dropdown
[
0
].
style
.
top
=
''
;
element
.
removeClass
(
directionUpClassName
);
}
};
};
}
};
}]);
uis
.
directive
(
'uiSelectMatch'
,
[
'uiSelectConfig'
,
function
(
uiSelectConfig
)
{
return
{
restrict
:
'EA'
,
require
:
'^uiSelect'
,
replace
:
true
,
transclude
:
true
,
templateUrl
:
function
(
tElement
)
{
// Needed so the uiSelect can detect the transcluded content
tElement
.
addClass
(
'ui-select-match'
);
var
parent
=
tElement
.
parent
();
// Gets theme attribute from parent (ui-select)
var
theme
=
getAttribute
(
parent
,
'theme'
)
||
uiSelectConfig
.
theme
;
var
multi
=
angular
.
isDefined
(
getAttribute
(
parent
,
'multiple'
));
return
theme
+
(
multi
?
'/match-multiple.tpl.html'
:
'/match.tpl.html'
);
},
link
:
function
(
scope
,
element
,
attrs
,
$select
)
{
$select
.
lockChoiceExpression
=
attrs
.
uiLockChoice
;
attrs
.
$observe
(
'placeholder'
,
function
(
placeholder
)
{
$select
.
placeholder
=
placeholder
!==
undefined
?
placeholder
:
uiSelectConfig
.
placeholder
;
});
function
setAllowClear
(
allow
)
{
$select
.
allowClear
=
(
angular
.
isDefined
(
allow
))
?
(
allow
===
''
)
?
true
:
(
allow
.
toLowerCase
()
===
'true'
)
:
false
;
}
attrs
.
$observe
(
'allowClear'
,
setAllowClear
);
setAllowClear
(
attrs
.
allowClear
);
if
(
$select
.
multiple
){
$select
.
sizeSearchInput
();
}
}
};
function
getAttribute
(
elem
,
attribute
)
{
if
(
elem
[
0
].
hasAttribute
(
attribute
))
return
elem
.
attr
(
attribute
);
if
(
elem
[
0
].
hasAttribute
(
'data-'
+
attribute
))
return
elem
.
attr
(
'data-'
+
attribute
);
if
(
elem
[
0
].
hasAttribute
(
'x-'
+
attribute
))
return
elem
.
attr
(
'x-'
+
attribute
);
}
}]);
uis
.
directive
(
'uiSelectMultiple'
,
[
'uiSelectMinErr'
,
'$timeout'
,
function
(
uiSelectMinErr
,
$timeout
)
{
return
{
restrict
:
'EA'
,
require
:
[
'^uiSelect'
,
'^ngModel'
],
controller
:
[
'$scope'
,
'$timeout'
,
function
(
$scope
,
$timeout
){
var
ctrl
=
this
,
$select
=
$scope
.
$select
,
ngModel
;
if
(
angular
.
isUndefined
(
$select
.
selected
))
$select
.
selected
=
[];
//Wait for link fn to inject it
$scope
.
$evalAsync
(
function
(){
ngModel
=
$scope
.
ngModel
;
});
ctrl
.
activeMatchIndex
=
-
1
;
ctrl
.
updateModel
=
function
(){
ngModel
.
$setViewValue
(
Date
.
now
());
//Set timestamp as a unique string to force changes
ctrl
.
refreshComponent
();
};
ctrl
.
refreshComponent
=
function
(){
//Remove already selected items
//e.g. When user clicks on a selection, the selected array changes and
//the dropdown should remove that item
if
(
$select
.
refreshItems
){
$select
.
refreshItems
();
}
if
(
$select
.
sizeSearchInput
){
$select
.
sizeSearchInput
();
}
};
// Remove item from multiple select
ctrl
.
removeChoice
=
function
(
index
){
// if the choice is locked, don't remove it
if
(
$select
.
isLocked
(
null
,
index
))
return
false
;
var
removedChoice
=
$select
.
selected
[
index
];
var
locals
=
{};
locals
[
$select
.
parserResult
.
itemName
]
=
removedChoice
;
$select
.
selected
.
splice
(
index
,
1
);
ctrl
.
activeMatchIndex
=
-
1
;
$select
.
sizeSearchInput
();
// Give some time for scope propagation.
$timeout
(
function
(){
$select
.
onRemoveCallback
(
$scope
,
{
$item
:
removedChoice
,
$model
:
$select
.
parserResult
.
modelMapper
(
$scope
,
locals
)
});
});
ctrl
.
updateModel
();
return
true
;
};
ctrl
.
getPlaceholder
=
function
(){
//Refactor single?
if
(
$select
.
selected
&&
$select
.
selected
.
length
)
return
;
return
$select
.
placeholder
;
};
}],
controllerAs
:
'$selectMultiple'
,
link
:
function
(
scope
,
element
,
attrs
,
ctrls
)
{
var
$select
=
ctrls
[
0
];
var
ngModel
=
scope
.
ngModel
=
ctrls
[
1
];
var
$selectMultiple
=
scope
.
$selectMultiple
;
//$select.selected = raw selected objects (ignoring any property binding)
$select
.
multiple
=
true
;
//Input that will handle focus
$select
.
focusInput
=
$select
.
searchInput
;
//Properly check for empty if set to multiple
ngModel
.
$isEmpty
=
function
(
value
)
{
return
!
value
||
value
.
length
===
0
;
};
//From view --> model
ngModel
.
$parsers
.
unshift
(
function
()
{
var
locals
=
{},
result
,
resultMultiple
=
[];
for
(
var
j
=
$select
.
selected
.
length
-
1
;
j
>=
0
;
j
--
)
{
locals
=
{};
locals
[
$select
.
parserResult
.
itemName
]
=
$select
.
selected
[
j
];
result
=
$select
.
parserResult
.
modelMapper
(
scope
,
locals
);
resultMultiple
.
unshift
(
result
);
}
return
resultMultiple
;
});
// From model --> view
ngModel
.
$formatters
.
unshift
(
function
(
inputValue
)
{
var
data
=
$select
.
parserResult
&&
$select
.
parserResult
.
source
(
scope
,
{
$select
:
{
search
:
''
}}),
//Overwrite $search
locals
=
{},
result
;
if
(
!
data
)
return
inputValue
;
var
resultMultiple
=
[];
var
checkFnMultiple
=
function
(
list
,
value
){
if
(
!
list
||
!
list
.
length
)
return
;
for
(
var
p
=
list
.
length
-
1
;
p
>=
0
;
p
--
)
{
locals
[
$select
.
parserResult
.
itemName
]
=
list
[
p
];
result
=
$select
.
parserResult
.
modelMapper
(
scope
,
locals
);
if
(
$select
.
parserResult
.
trackByExp
){
var
propsItemNameMatches
=
/
(\w
*
)\.
/
.
exec
(
$select
.
parserResult
.
trackByExp
);
var
matches
=
/
\.([^\s]
+
)
/
.
exec
(
$select
.
parserResult
.
trackByExp
);
if
(
propsItemNameMatches
&&
propsItemNameMatches
.
length
>
0
&&
propsItemNameMatches
[
1
]
==
$select
.
parserResult
.
itemName
){
if
(
matches
&&
matches
.
length
>
0
&&
result
[
matches
[
1
]]
==
value
[
matches
[
1
]]){
resultMultiple
.
unshift
(
list
[
p
]);
return
true
;
}
}
}
if
(
angular
.
equals
(
result
,
value
)){
resultMultiple
.
unshift
(
list
[
p
]);
return
true
;
}
}
return
false
;
};
if
(
!
inputValue
)
return
resultMultiple
;
//If ngModel was undefined
for
(
var
k
=
inputValue
.
length
-
1
;
k
>=
0
;
k
--
)
{
//Check model array of currently selected items
if
(
!
checkFnMultiple
(
$select
.
selected
,
inputValue
[
k
])){
//Check model array of all items available
if
(
!
checkFnMultiple
(
data
,
inputValue
[
k
])){
//If not found on previous lists, just add it directly to resultMultiple
resultMultiple
.
unshift
(
inputValue
[
k
]);
}
}
}
return
resultMultiple
;
});
//Watch for external model changes
scope
.
$watchCollection
(
function
(){
return
ngModel
.
$modelValue
;
},
function
(
newValue
,
oldValue
)
{
if
(
oldValue
!=
newValue
){
//update the view value with fresh data from items, if there is a valid model value
if
(
angular
.
isDefined
(
ngModel
.
$modelValue
))
{
ngModel
.
$modelValue
=
null
;
//Force scope model value and ngModel value to be out of sync to re-run formatters
}
$selectMultiple
.
refreshComponent
();
}
});
ngModel
.
$render
=
function
()
{
// Make sure that model value is array
if
(
!
angular
.
isArray
(
ngModel
.
$viewValue
)){
// Have tolerance for null or undefined values
if
(
angular
.
isUndefined
(
ngModel
.
$viewValue
)
||
ngModel
.
$viewValue
===
null
){
ngModel
.
$viewValue
=
[];
}
else
{
throw
uiSelectMinErr
(
'multiarr'
,
"Expected model value to be array but got '{0}'"
,
ngModel
.
$viewValue
);
}
}
$select
.
selected
=
ngModel
.
$viewValue
;
$selectMultiple
.
refreshComponent
();
scope
.
$evalAsync
();
//To force $digest
};
scope
.
$on
(
'uis:select'
,
function
(
event
,
item
)
{
if
(
$select
.
selected
.
length
>=
$select
.
limit
)
{
return
;
}
$select
.
selected
.
push
(
item
);
$selectMultiple
.
updateModel
();
});
scope
.
$on
(
'uis:activate'
,
function
()
{
$selectMultiple
.
activeMatchIndex
=
-
1
;
});
scope
.
$watch
(
'$select.disabled'
,
function
(
newValue
,
oldValue
)
{
// As the search input field may now become visible, it may be necessary to recompute its size
if
(
oldValue
&&
!
newValue
)
$select
.
sizeSearchInput
();
});
$select
.
searchInput
.
on
(
'keydown'
,
function
(
e
)
{
var
key
=
e
.
which
;
scope
.
$apply
(
function
()
{
var
processed
=
false
;
// var tagged = false; //Checkme
if
(
KEY
.
isHorizontalMovement
(
key
)){
processed
=
_handleMatchSelection
(
key
);
}
if
(
processed
&&
key
!=
KEY
.
TAB
)
{
//TODO Check si el tab selecciona aun correctamente
//Crear test
e
.
preventDefault
();
e
.
stopPropagation
();
}
});
});
function
_getCaretPosition
(
el
)
{
if
(
angular
.
isNumber
(
el
.
selectionStart
))
return
el
.
selectionStart
;
// selectionStart is not supported in IE8 and we don't want hacky workarounds so we compromise
else
return
el
.
value
.
length
;
}
// Handles selected options in "multiple" mode
function
_handleMatchSelection
(
key
){
var
caretPosition
=
_getCaretPosition
(
$select
.
searchInput
[
0
]),
length
=
$select
.
selected
.
length
,
// none = -1,
first
=
0
,
last
=
length
-
1
,
curr
=
$selectMultiple
.
activeMatchIndex
,
next
=
$selectMultiple
.
activeMatchIndex
+
1
,
prev
=
$selectMultiple
.
activeMatchIndex
-
1
,
newIndex
=
curr
;
if
(
caretPosition
>
0
||
(
$select
.
search
.
length
&&
key
==
KEY
.
RIGHT
))
return
false
;
$select
.
close
();
function
getNewActiveMatchIndex
(){
switch
(
key
){
case
KEY
.
LEFT
:
// Select previous/first item
if
(
~
$selectMultiple
.
activeMatchIndex
)
return
prev
;
// Select last item
else
return
last
;
break
;
case
KEY
.
RIGHT
:
// Open drop-down
if
(
!~
$selectMultiple
.
activeMatchIndex
||
curr
===
last
){
$select
.
activate
();
return
false
;
}
// Select next/last item
else
return
next
;
break
;
case
KEY
.
BACKSPACE
:
// Remove selected item and select previous/first
if
(
~
$selectMultiple
.
activeMatchIndex
){
if
(
$selectMultiple
.
removeChoice
(
curr
))
{
return
prev
;
}
else
{
return
curr
;
}
}
else
{
// If nothing yet selected, select last item
return
last
;
}
break
;
case
KEY
.
DELETE
:
// Remove selected item and select next item
if
(
~
$selectMultiple
.
activeMatchIndex
){
$selectMultiple
.
removeChoice
(
$selectMultiple
.
activeMatchIndex
);
return
curr
;
}
else
return
false
;
}
}
newIndex
=
getNewActiveMatchIndex
();
if
(
!
$select
.
selected
.
length
||
newIndex
===
false
)
$selectMultiple
.
activeMatchIndex
=
-
1
;
else
$selectMultiple
.
activeMatchIndex
=
Math
.
min
(
last
,
Math
.
max
(
first
,
newIndex
));
return
true
;
}
$select
.
searchInput
.
on
(
'keyup'
,
function
(
e
)
{
if
(
!
KEY
.
isVerticalMovement
(
e
.
which
)
)
{
scope
.
$evalAsync
(
function
()
{
$select
.
activeIndex
=
$select
.
taggingLabel
===
false
?
-
1
:
0
;
});
}
// Push a "create new" item into array if there is a search string
if
(
$select
.
tagging
.
isActivated
&&
$select
.
search
.
length
>
0
)
{
// return early with these keys
if
(
e
.
which
===
KEY
.
TAB
||
KEY
.
isControl
(
e
)
||
KEY
.
isFunctionKey
(
e
)
||
e
.
which
===
KEY
.
ESC
||
KEY
.
isVerticalMovement
(
e
.
which
)
)
{
return
;
}
// always reset the activeIndex to the first item when tagging
$select
.
activeIndex
=
$select
.
taggingLabel
===
false
?
-
1
:
0
;
// taggingLabel === false bypasses all of this
if
(
$select
.
taggingLabel
===
false
)
return
;
var
items
=
angular
.
copy
(
$select
.
items
);
var
stashArr
=
angular
.
copy
(
$select
.
items
);
var
newItem
;
var
item
;
var
hasTag
=
false
;
var
dupeIndex
=
-
1
;
var
tagItems
;
var
tagItem
;
// case for object tagging via transform `$select.tagging.fct` function
if
(
$select
.
tagging
.
fct
!==
undefined
)
{
tagItems
=
$select
.
$filter
(
'filter'
)(
items
,{
'isTag'
:
true
});
if
(
tagItems
.
length
>
0
)
{
tagItem
=
tagItems
[
0
];
}
// remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous
if
(
items
.
length
>
0
&&
tagItem
)
{
hasTag
=
true
;
items
=
items
.
slice
(
1
,
items
.
length
);
stashArr
=
stashArr
.
slice
(
1
,
stashArr
.
length
);
}
newItem
=
$select
.
tagging
.
fct
(
$select
.
search
);
// verify the new tag doesn't match the value of a possible selection choice or an already selected item.
if
(
stashArr
.
some
(
function
(
origItem
)
{
return
angular
.
equals
(
origItem
,
newItem
);
})
||
$select
.
selected
.
some
(
function
(
origItem
)
{
return
angular
.
equals
(
origItem
,
newItem
);
})
)
{
scope
.
$evalAsync
(
function
()
{
$select
.
activeIndex
=
0
;
$select
.
items
=
items
;
});
return
;
}
if
(
newItem
)
newItem
.
isTag
=
true
;
// handle newItem string and stripping dupes in tagging string context
}
else
{
// find any tagging items already in the $select.items array and store them
tagItems
=
$select
.
$filter
(
'filter'
)(
items
,
function
(
item
)
{
return
item
.
match
(
$select
.
taggingLabel
);
});
if
(
tagItems
.
length
>
0
)
{
tagItem
=
tagItems
[
0
];
}
item
=
items
[
0
];
// remove existing tag item if found (should only ever be one tag item)
if
(
item
!==
undefined
&&
items
.
length
>
0
&&
tagItem
)
{
hasTag
=
true
;
items
=
items
.
slice
(
1
,
items
.
length
);
stashArr
=
stashArr
.
slice
(
1
,
stashArr
.
length
);
}
newItem
=
$select
.
search
+
' '
+
$select
.
taggingLabel
;
if
(
_findApproxDupe
(
$select
.
selected
,
$select
.
search
)
>
-
1
)
{
return
;
}
// verify the the tag doesn't match the value of an existing item from
// the searched data set or the items already selected
if
(
_findCaseInsensitiveDupe
(
stashArr
.
concat
(
$select
.
selected
))
)
{
// if there is a tag from prev iteration, strip it / queue the change
// and return early
if
(
hasTag
)
{
items
=
stashArr
;
scope
.
$evalAsync
(
function
()
{
$select
.
activeIndex
=
0
;
$select
.
items
=
items
;
});
}
return
;
}
if
(
_findCaseInsensitiveDupe
(
stashArr
)
)
{
// if there is a tag from prev iteration, strip it
if
(
hasTag
)
{
$select
.
items
=
stashArr
.
slice
(
1
,
stashArr
.
length
);
}
return
;
}
}
if
(
hasTag
)
dupeIndex
=
_findApproxDupe
(
$select
.
selected
,
newItem
);
// dupe found, shave the first item
if
(
dupeIndex
>
-
1
)
{
items
=
items
.
slice
(
dupeIndex
+
1
,
items
.
length
-
1
);
}
else
{
items
=
[];
if
(
newItem
)
items
.
push
(
newItem
);
items
=
items
.
concat
(
stashArr
);
}
scope
.
$evalAsync
(
function
()
{
$select
.
activeIndex
=
0
;
$select
.
items
=
items
;
if
(
$select
.
isGrouped
)
{
// update item references in groups, so that indexOf will work after angular.copy
var
itemsWithoutTag
=
newItem
?
items
.
slice
(
1
)
:
items
;
$select
.
setItemsFn
(
itemsWithoutTag
);
if
(
newItem
)
{
// add tag item as a new group
$select
.
items
.
unshift
(
newItem
);
$select
.
groups
.
unshift
({
name
:
''
,
items
:
[
newItem
],
tagging
:
true
});
}
}
});
}
});
function
_findCaseInsensitiveDupe
(
arr
)
{
if
(
arr
===
undefined
||
$select
.
search
===
undefined
)
{
return
false
;
}
var
hasDupe
=
arr
.
filter
(
function
(
origItem
)
{
if
(
$select
.
search
.
toUpperCase
()
===
undefined
||
origItem
===
undefined
)
{
return
false
;
}
return
origItem
.
toUpperCase
()
===
$select
.
search
.
toUpperCase
();
}).
length
>
0
;
return
hasDupe
;
}
function
_findApproxDupe
(
haystack
,
needle
)
{
var
dupeIndex
=
-
1
;
if
(
angular
.
isArray
(
haystack
))
{
var
tempArr
=
angular
.
copy
(
haystack
);
for
(
var
i
=
0
;
i
<
tempArr
.
length
;
i
++
)
{
// handle the simple string version of tagging
if
(
$select
.
tagging
.
fct
===
undefined
)
{
// search the array for the match
if
(
tempArr
[
i
]
+
' '
+
$select
.
taggingLabel
===
needle
)
{
dupeIndex
=
i
;
}
// handle the object tagging implementation
}
else
{
var
mockObj
=
tempArr
[
i
];
if
(
angular
.
isObject
(
mockObj
))
{
mockObj
.
isTag
=
true
;
}
if
(
angular
.
equals
(
mockObj
,
needle
)
)
{
dupeIndex
=
i
;
}
}
}
}
return
dupeIndex
;
}
$select
.
searchInput
.
on
(
'blur'
,
function
()
{
$timeout
(
function
()
{
$selectMultiple
.
activeMatchIndex
=
-
1
;
});
});
}
};
}]);
uis
.
directive
(
'uiSelectNoChoice'
,
[
'uiSelectConfig'
,
function
(
uiSelectConfig
)
{
return
{
restrict
:
'EA'
,
require
:
'^uiSelect'
,
replace
:
true
,
transclude
:
true
,
templateUrl
:
function
(
tElement
)
{
// Needed so the uiSelect can detect the transcluded content
tElement
.
addClass
(
'ui-select-no-choice'
);
// Gets theme attribute from parent (ui-select)
var
theme
=
tElement
.
parent
().
attr
(
'theme'
)
||
uiSelectConfig
.
theme
;
return
theme
+
'/no-choice.tpl.html'
;
}
};
}]);
uis
.
directive
(
'uiSelectSingle'
,
[
'$timeout'
,
'$compile'
,
function
(
$timeout
,
$compile
)
{
return
{
restrict
:
'EA'
,
require
:
[
'^uiSelect'
,
'^ngModel'
],
link
:
function
(
scope
,
element
,
attrs
,
ctrls
)
{
var
$select
=
ctrls
[
0
];
var
ngModel
=
ctrls
[
1
];
//From view --> model
ngModel
.
$parsers
.
unshift
(
function
(
inputValue
)
{
var
locals
=
{},
result
;
locals
[
$select
.
parserResult
.
itemName
]
=
inputValue
;
result
=
$select
.
parserResult
.
modelMapper
(
scope
,
locals
);
return
result
;
});
//From model --> view
ngModel
.
$formatters
.
unshift
(
function
(
inputValue
)
{
var
data
=
$select
.
parserResult
&&
$select
.
parserResult
.
source
(
scope
,
{
$select
:
{
search
:
''
}}),
//Overwrite $search
locals
=
{},
result
;
if
(
data
){
var
checkFnSingle
=
function
(
d
){
locals
[
$select
.
parserResult
.
itemName
]
=
d
;
result
=
$select
.
parserResult
.
modelMapper
(
scope
,
locals
);
return
result
===
inputValue
;
};
//If possible pass same object stored in $select.selected
if
(
$select
.
selected
&&
checkFnSingle
(
$select
.
selected
))
{
return
$select
.
selected
;
}
for
(
var
i
=
data
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
checkFnSingle
(
data
[
i
]))
return
data
[
i
];
}
}
return
inputValue
;
});
//Update viewValue if model change
scope
.
$watch
(
'$select.selected'
,
function
(
newValue
)
{
if
(
ngModel
.
$viewValue
!==
newValue
)
{
ngModel
.
$setViewValue
(
newValue
);
}
});
ngModel
.
$render
=
function
()
{
$select
.
selected
=
ngModel
.
$viewValue
;
};
scope
.
$on
(
'uis:select'
,
function
(
event
,
item
)
{
$select
.
selected
=
item
;
});
scope
.
$on
(
'uis:close'
,
function
(
event
,
skipFocusser
)
{
$timeout
(
function
(){
$select
.
focusser
.
prop
(
'disabled'
,
false
);
if
(
!
skipFocusser
)
$select
.
focusser
[
0
].
focus
();
},
0
,
false
);
});
scope
.
$on
(
'uis:activate'
,
function
()
{
focusser
.
prop
(
'disabled'
,
true
);
//Will reactivate it on .close()
});
//Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954
var
focusser
=
angular
.
element
(
"<input ng-disabled='$select.disabled' class='ui-select-focusser ui-select-offscreen' type='text' id='{{ $select.focusserId }}' aria-label='{{ $select.focusserTitle }}' aria-haspopup='true' role='button' />"
);
$compile
(
focusser
)(
scope
);
$select
.
focusser
=
focusser
;
//Input that will handle focus
$select
.
focusInput
=
focusser
;
element
.
parent
().
append
(
focusser
);
focusser
.
bind
(
"focus"
,
function
(){
scope
.
$evalAsync
(
function
(){
$select
.
focus
=
true
;
});
});
focusser
.
bind
(
"blur"
,
function
(){
scope
.
$evalAsync
(
function
(){
$select
.
focus
=
false
;
});
});
focusser
.
bind
(
"keydown"
,
function
(
e
){
if
(
e
.
which
===
KEY
.
BACKSPACE
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
$select
.
select
(
undefined
);
scope
.
$apply
();
return
;
}
if
(
e
.
which
===
KEY
.
TAB
||
KEY
.
isControl
(
e
)
||
KEY
.
isFunctionKey
(
e
)
||
e
.
which
===
KEY
.
ESC
)
{
return
;
}
if
(
e
.
which
==
KEY
.
DOWN
||
e
.
which
==
KEY
.
UP
||
e
.
which
==
KEY
.
ENTER
||
e
.
which
==
KEY
.
SPACE
){
e
.
preventDefault
();
e
.
stopPropagation
();
$select
.
activate
();
}
scope
.
$digest
();
});
focusser
.
bind
(
"keyup input"
,
function
(
e
){
if
(
e
.
which
===
KEY
.
TAB
||
KEY
.
isControl
(
e
)
||
KEY
.
isFunctionKey
(
e
)
||
e
.
which
===
KEY
.
ESC
||
e
.
which
==
KEY
.
ENTER
||
e
.
which
===
KEY
.
BACKSPACE
)
{
return
;
}
$select
.
activate
(
focusser
.
val
());
//User pressed some regular key, so we pass it to the search input
focusser
.
val
(
''
);
scope
.
$digest
();
});
}
};
}]);
// Make multiple matches sortable
uis
.
directive
(
'uiSelectSort'
,
[
'$timeout'
,
'uiSelectConfig'
,
'uiSelectMinErr'
,
function
(
$timeout
,
uiSelectConfig
,
uiSelectMinErr
)
{
return
{
require
:
[
'^^uiSelect'
,
'^ngModel'
],
link
:
function
(
scope
,
element
,
attrs
,
ctrls
)
{
if
(
scope
[
attrs
.
uiSelectSort
]
===
null
)
{
throw
uiSelectMinErr
(
'sort'
,
'Expected a list to sort'
);
}
var
$select
=
ctrls
[
0
];
var
$ngModel
=
ctrls
[
1
];
var
options
=
angular
.
extend
({
axis
:
'horizontal'
},
scope
.
$eval
(
attrs
.
uiSelectSortOptions
));
var
axis
=
options
.
axis
;
var
draggingClassName
=
'dragging'
;
var
droppingClassName
=
'dropping'
;
var
droppingBeforeClassName
=
'dropping-before'
;
var
droppingAfterClassName
=
'dropping-after'
;
scope
.
$watch
(
function
(){
return
$select
.
sortable
;
},
function
(
newValue
){
if
(
newValue
)
{
element
.
attr
(
'draggable'
,
true
);
}
else
{
element
.
removeAttr
(
'draggable'
);
}
});
element
.
on
(
'dragstart'
,
function
(
event
)
{
element
.
addClass
(
draggingClassName
);
(
event
.
dataTransfer
||
event
.
originalEvent
.
dataTransfer
).
setData
(
'text'
,
scope
.
$index
.
toString
());
});
element
.
on
(
'dragend'
,
function
()
{
removeClass
(
draggingClassName
);
});
var
move
=
function
(
from
,
to
)
{
/*jshint validthis: true */
this
.
splice
(
to
,
0
,
this
.
splice
(
from
,
1
)[
0
]);
};
var
removeClass
=
function
(
className
)
{
angular
.
forEach
(
$select
.
$element
.
querySelectorAll
(
'.'
+
className
),
function
(
el
){
angular
.
element
(
el
).
removeClass
(
className
);
});
};
var
dragOverHandler
=
function
(
event
)
{
event
.
preventDefault
();
var
offset
=
axis
===
'vertical'
?
event
.
offsetY
||
event
.
layerY
||
(
event
.
originalEvent
?
event
.
originalEvent
.
offsetY
:
0
)
:
event
.
offsetX
||
event
.
layerX
||
(
event
.
originalEvent
?
event
.
originalEvent
.
offsetX
:
0
);
if
(
offset
<
(
this
[
axis
===
'vertical'
?
'offsetHeight'
:
'offsetWidth'
]
/
2
))
{
removeClass
(
droppingAfterClassName
);
element
.
addClass
(
droppingBeforeClassName
);
}
else
{
removeClass
(
droppingBeforeClassName
);
element
.
addClass
(
droppingAfterClassName
);
}
};
var
dropTimeout
;
var
dropHandler
=
function
(
event
)
{
event
.
preventDefault
();
var
droppedItemIndex
=
parseInt
((
event
.
dataTransfer
||
event
.
originalEvent
.
dataTransfer
).
getData
(
'text'
),
10
);
// prevent event firing multiple times in firefox
$timeout
.
cancel
(
dropTimeout
);
dropTimeout
=
$timeout
(
function
()
{
_dropHandler
(
droppedItemIndex
);
},
20
);
};
var
_dropHandler
=
function
(
droppedItemIndex
)
{
var
theList
=
scope
.
$eval
(
attrs
.
uiSelectSort
);
var
itemToMove
=
theList
[
droppedItemIndex
];
var
newIndex
=
null
;
if
(
element
.
hasClass
(
droppingBeforeClassName
))
{
if
(
droppedItemIndex
<
scope
.
$index
)
{
newIndex
=
scope
.
$index
-
1
;
}
else
{
newIndex
=
scope
.
$index
;
}
}
else
{
if
(
droppedItemIndex
<
scope
.
$index
)
{
newIndex
=
scope
.
$index
;
}
else
{
newIndex
=
scope
.
$index
+
1
;
}
}
move
.
apply
(
theList
,
[
droppedItemIndex
,
newIndex
]);
$ngModel
.
$setViewValue
(
Date
.
now
());
scope
.
$apply
(
function
()
{
scope
.
$emit
(
'uiSelectSort:change'
,
{
array
:
theList
,
item
:
itemToMove
,
from
:
droppedItemIndex
,
to
:
newIndex
});
});
removeClass
(
droppingClassName
);
removeClass
(
droppingBeforeClassName
);
removeClass
(
droppingAfterClassName
);
element
.
off
(
'drop'
,
dropHandler
);
};
element
.
on
(
'dragenter'
,
function
()
{
if
(
element
.
hasClass
(
draggingClassName
))
{
return
;
}
element
.
addClass
(
droppingClassName
);
element
.
on
(
'dragover'
,
dragOverHandler
);
element
.
on
(
'drop'
,
dropHandler
);
});
element
.
on
(
'dragleave'
,
function
(
event
)
{
if
(
event
.
target
!=
element
)
{
return
;
}
removeClass
(
droppingClassName
);
removeClass
(
droppingBeforeClassName
);
removeClass
(
droppingAfterClassName
);
element
.
off
(
'dragover'
,
dragOverHandler
);
element
.
off
(
'drop'
,
dropHandler
);
});
}
};
}]);
/**
* Debounces functions
*
* Taken from UI Bootstrap $$debounce source code
* See https://github.com/angular-ui/bootstrap/blob/master/src/debounce/debounce.js
*
*/
uis
.
factory
(
'$$uisDebounce'
,
[
'$timeout'
,
function
(
$timeout
)
{
return
function
(
callback
,
debounceTime
)
{
var
timeoutPromise
;
return
function
()
{
var
self
=
this
;
var
args
=
Array
.
prototype
.
slice
.
call
(
arguments
);
if
(
timeoutPromise
)
{
$timeout
.
cancel
(
timeoutPromise
);
}
timeoutPromise
=
$timeout
(
function
()
{
callback
.
apply
(
self
,
args
);
},
debounceTime
);
};
};
}]);
uis
.
directive
(
'uisOpenClose'
,
[
'$parse'
,
'$timeout'
,
function
(
$parse
,
$timeout
)
{
return
{
restrict
:
'A'
,
require
:
'uiSelect'
,
link
:
function
(
scope
,
element
,
attrs
,
$select
)
{
$select
.
onOpenCloseCallback
=
$parse
(
attrs
.
uisOpenClose
);
scope
.
$watch
(
'$select.open'
,
function
(
isOpen
,
previousState
)
{
if
(
isOpen
!==
previousState
)
{
$timeout
(
function
()
{
$select
.
onOpenCloseCallback
(
scope
,
{
isOpen
:
isOpen
});
});
}
});
}
};
}]);
/**
* Parses "repeat" attribute.
*
* Taken from AngularJS ngRepeat source code
* See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L211
*
* Original discussion about parsing "repeat" attribute instead of fully relying on ng-repeat:
* https://github.com/angular-ui/ui-select/commit/5dd63ad#commitcomment-5504697
*/
uis
.
service
(
'uisRepeatParser'
,
[
'uiSelectMinErr'
,
'$parse'
,
function
(
uiSelectMinErr
,
$parse
)
{
var
self
=
this
;
/**
* Example:
* expression = "address in addresses | filter: {street: $select.search} track by $index"
* itemName = "address",
* source = "addresses | filter: {street: $select.search}",
* trackByExp = "$index",
*/
self
.
parse
=
function
(
expression
)
{
var
match
;
//var isObjectCollection = /\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)/.test(expression);
// If an array is used as collection
// if (isObjectCollection){
// 000000000000000000000000000000111111111000000000000000222222222222220033333333333333333333330000444444444444444444000000000000000055555555555000000000000000000000066666666600000000
match
=
expression
.
match
(
/^
\s
*
(?:([\s\S]
+
?)\s
+as
\s
+
)?(?:([\$\w][\$\w]
*
)
|
(?:\(\s
*
([\$\w][\$\w]
*
)\s
*,
\s
*
([\$\w][\$\w]
*
)\s
*
\)))\s
+in
\s
+
(\s
*
[\s\S]
+
?)?(?:\s
+track
\s
+by
\s
+
([\s\S]
+
?))?\s
*$/
);
// 1 Alias
// 2 Item
// 3 Key on (key,value)
// 4 Value on (key,value)
// 5 Source expression (including filters)
// 6 Track by
if
(
!
match
)
{
throw
uiSelectMinErr
(
'iexp'
,
"Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'."
,
expression
);
}
var
source
=
match
[
5
],
filters
=
''
;
// When using (key,value) ui-select requires filters to be extracted, since the object
// is converted to an array for $select.items
// (in which case the filters need to be reapplied)
if
(
match
[
3
])
{
// Remove any enclosing parenthesis
source
=
match
[
5
].
replace
(
/
(
^
\()
|
(\)
$
)
/g
,
''
);
// match all after | but not after ||
var
filterMatch
=
match
[
5
].
match
(
/^
\s
*
(?:[\s\S]
+
?)(?:[^\|]
|
\|\|)
+
([\s\S]
*
)\s
*$/
);
if
(
filterMatch
&&
filterMatch
[
1
].
trim
())
{
filters
=
filterMatch
[
1
];
source
=
source
.
replace
(
filters
,
''
);
}
}
return
{
itemName
:
match
[
4
]
||
match
[
2
],
// (lhs) Left-hand side,
keyName
:
match
[
3
],
//for (key, value) syntax
source
:
$parse
(
source
),
filters
:
filters
,
trackByExp
:
match
[
6
],
modelMapper
:
$parse
(
match
[
1
]
||
match
[
4
]
||
match
[
2
]),
repeatExpression
:
function
(
grouped
)
{
var
expression
=
this
.
itemName
+
' in '
+
(
grouped
?
'$group.items'
:
'$select.items'
);
if
(
this
.
trackByExp
)
{
expression
+=
' track by '
+
this
.
trackByExp
;
}
return
expression
;
}
};
};
self
.
getGroupNgRepeatExpression
=
function
()
{
return
'$group in $select.groups track by $group.name'
;
};
}]);
}());
angular
.
module
(
"ui.select"
).
run
([
"$templateCache"
,
function
(
$templateCache
)
{
$templateCache
.
put
(
"bootstrap/choices.tpl.html"
,
"<ul class=
\"
ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu
\"
role=
\"
listbox
\"
ng-show=
\"
$select.open && $select.items.length > 0
\"
><li class=
\"
ui-select-choices-group
\"
id=
\"
ui-select-choices-{{ $select.generatedId }}
\"
><div class=
\"
divider
\"
ng-show=
\"
$select.isGrouped && $index > 0
\"
></div><div ng-show=
\"
$select.isGrouped
\"
class=
\"
ui-select-choices-group-label dropdown-header
\"
ng-bind=
\"
$group.name
\"
></div><div ng-attr-id=
\"
ui-select-choices-row-{{ $select.generatedId }}-{{$index}}
\"
class=
\"
ui-select-choices-row
\"
ng-class=
\"
{active: $select.isActive(this), disabled: $select.isDisabled(this)}
\"
role=
\"
option
\"
><span class=
\"
ui-select-choices-row-inner
\"
></span></div></li></ul>"
);
$templateCache
.
put
(
"bootstrap/match-multiple.tpl.html"
,
"<span class=
\"
ui-select-match
\"
><span ng-repeat=
\"
$item in $select.selected track by $index
\"
><span class=
\"
ui-select-match-item btn btn-default btn-xs
\"
tabindex=
\"
-1
\"
type=
\"
button
\"
ng-disabled=
\"
$select.disabled
\"
ng-click=
\"
$selectMultiple.activeMatchIndex = $index;
\"
ng-class=
\"
{
\
'btn-primary
\
':$selectMultiple.activeMatchIndex === $index,
\
'select-locked
\
':$select.isLocked(this, $index)}
\"
ui-select-sort=
\"
$select.selected
\"
><span class=
\"
close ui-select-match-close
\"
ng-hide=
\"
$select.disabled
\"
ng-click=
\"
$selectMultiple.removeChoice($index)
\"
> ×</span> <span uis-transclude-append=
\"\"
></span></span></span></span>"
);
$templateCache
.
put
(
"bootstrap/match.tpl.html"
,
"<div class=
\"
ui-select-match
\"
ng-hide=
\"
$select.open && $select.searchEnabled
\"
ng-disabled=
\"
$select.disabled
\"
ng-class=
\"
{
\
'btn-default-focus
\
':$select.focus}
\"
><span tabindex=
\"
-1
\"
class=
\"
btn btn-default form-control ui-select-toggle
\"
aria-label=
\"
{{ $select.baseTitle }} activate
\"
ng-disabled=
\"
$select.disabled
\"
ng-click=
\"
$select.activate()
\"
style=
\"
outline: 0;
\"
><span ng-show=
\"
$select.isEmpty()
\"
class=
\"
ui-select-placeholder text-muted
\"
>{{$select.placeholder}}</span> <span ng-hide=
\"
$select.isEmpty()
\"
class=
\"
ui-select-match-text pull-left
\"
ng-class=
\"
{
\
'ui-select-allow-clear
\
': $select.allowClear && !$select.isEmpty()}
\"
ng-transclude=
\"\"
></span> <i class=
\"
caret pull-right
\"
ng-click=
\"
$select.toggle($event)
\"
></i> <a ng-show=
\"
$select.allowClear && !$select.isEmpty() && ($select.disabled !== true)
\"
aria-label=
\"
{{ $select.baseTitle }} clear
\"
style=
\"
margin-right: 10px
\"
ng-click=
\"
$select.clear($event)
\"
class=
\"
btn btn-xs btn-link pull-right
\"
><i class=
\"
glyphicon glyphicon-remove
\"
aria-hidden=
\"
true
\"
></i></a></span></div>"
);
$templateCache
.
put
(
"bootstrap/no-choice.tpl.html"
,
"<ul class=
\"
ui-select-no-choice dropdown-menu
\"
ng-show=
\"
$select.items.length == 0
\"
><li ng-transclude=
\"\"
></li></ul>"
);
$templateCache
.
put
(
"bootstrap/select-multiple.tpl.html"
,
"<div class=
\"
ui-select-container ui-select-multiple ui-select-bootstrap dropdown form-control
\"
ng-class=
\"
{open: $select.open}
\"
><div><div class=
\"
ui-select-match
\"
></div><input type=
\"
search
\"
autocomplete=
\"
off
\"
autocorrect=
\"
off
\"
autocapitalize=
\"
off
\"
spellcheck=
\"
false
\"
class=
\"
ui-select-search input-xs
\"
placeholder=
\"
{{$selectMultiple.getPlaceholder()}}
\"
ng-disabled=
\"
$select.disabled
\"
ng-click=
\"
$select.activate()
\"
ng-model=
\"
$select.search
\"
role=
\"
combobox
\"
aria-label=
\"
{{ $select.baseTitle }}
\"
ondrop=
\"
return false;
\"
></div><div class=
\"
ui-select-choices
\"
></div><div class=
\"
ui-select-no-choice
\"
></div></div>"
);
$templateCache
.
put
(
"bootstrap/select.tpl.html"
,
"<div class=
\"
ui-select-container ui-select-bootstrap dropdown
\"
ng-class=
\"
{open: $select.open}
\"
><div class=
\"
ui-select-match
\"
></div><input type=
\"
search
\"
autocomplete=
\"
off
\"
tabindex=
\"
-1
\"
aria-expanded=
\"
true
\"
aria-label=
\"
{{ $select.baseTitle }}
\"
aria-owns=
\"
ui-select-choices-{{ $select.generatedId }}
\"
aria-activedescendant=
\"
ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}
\"
class=
\"
form-control ui-select-search
\"
ng-class=
\"
{
\
'ui-select-search-hidden
\
' : !$select.searchEnabled }
\"
placeholder=
\"
{{$select.placeholder}}
\"
ng-model=
\"
$select.search
\"
ng-show=
\"
$select.open
\"
><div class=
\"
ui-select-choices
\"
></div><div class=
\"
ui-select-no-choice
\"
></div></div>"
);
$templateCache
.
put
(
"selectize/choices.tpl.html"
,
"<div ng-show=
\"
$select.open
\"
class=
\"
ui-select-choices ui-select-dropdown selectize-dropdown single
\"
><div class=
\"
ui-select-choices-content selectize-dropdown-content
\"
><div class=
\"
ui-select-choices-group optgroup
\"
role=
\"
listbox
\"
><div ng-show=
\"
$select.isGrouped
\"
class=
\"
ui-select-choices-group-label optgroup-header
\"
ng-bind=
\"
$group.name
\"
></div><div role=
\"
option
\"
class=
\"
ui-select-choices-row
\"
ng-class=
\"
{active: $select.isActive(this), disabled: $select.isDisabled(this)}
\"
><div class=
\"
option ui-select-choices-row-inner
\"
data-selectable=
\"\"
></div></div></div></div></div>"
);
$templateCache
.
put
(
"selectize/match.tpl.html"
,
"<div ng-hide=
\"
$select.searchEnabled && ($select.open || $select.isEmpty())
\"
class=
\"
ui-select-match
\"
><span ng-show=
\"
!$select.searchEnabled && ($select.isEmpty() || $select.open)
\"
class=
\"
ui-select-placeholder text-muted
\"
>{{$select.placeholder}}</span> <span ng-hide=
\"
$select.isEmpty() || $select.open
\"
ng-transclude=
\"\"
></span></div>"
);
$templateCache
.
put
(
"selectize/no-choice.tpl.html"
,
"<div class=
\"
ui-select-no-choice selectize-dropdown
\"
ng-show=
\"
$select.items.length == 0
\"
><div class=
\"
selectize-dropdown-content
\"
><div data-selectable=
\"\"
ng-transclude=
\"\"
></div></div></div>"
);
$templateCache
.
put
(
"selectize/select.tpl.html"
,
"<div class=
\"
ui-select-container selectize-control single
\"
ng-class=
\"
{
\
'open
\
': $select.open}
\"
><div class=
\"
selectize-input
\"
ng-class=
\"
{
\
'focus
\
': $select.open,
\
'disabled
\
': $select.disabled,
\
'selectize-focus
\
' : $select.focus}
\"
ng-click=
\"
$select.open && !$select.searchEnabled ? $select.toggle($event) : $select.activate()
\"
><div class=
\"
ui-select-match
\"
></div><input type=
\"
search
\"
autocomplete=
\"
off
\"
tabindex=
\"
-1
\"
class=
\"
ui-select-search ui-select-toggle
\"
ng-class=
\"
{
\
'ui-select-search-hidden
\
':!$select.searchEnabled}
\"
ng-click=
\"
$select.toggle($event)
\"
placeholder=
\"
{{$select.placeholder}}
\"
ng-model=
\"
$select.search
\"
ng-hide=
\"
!$select.isEmpty() && !$select.open
\"
ng-disabled=
\"
$select.disabled
\"
aria-label=
\"
{{ $select.baseTitle }}
\"
></div><div class=
\"
ui-select-choices
\"
></div><div class=
\"
ui-select-no-choice
\"
></div></div>"
);
$templateCache
.
put
(
"select2/choices.tpl.html"
,
"<ul tabindex=
\"
-1
\"
class=
\"
ui-select-choices ui-select-choices-content select2-results
\"
><li class=
\"
ui-select-choices-group
\"
ng-class=
\"
{
\
'select2-result-with-children
\
': $select.choiceGrouped($group) }
\"
><div ng-show=
\"
$select.choiceGrouped($group)
\"
class=
\"
ui-select-choices-group-label select2-result-label
\"
ng-bind=
\"
$group.name
\"
></div><ul role=
\"
listbox
\"
id=
\"
ui-select-choices-{{ $select.generatedId }}
\"
ng-class=
\"
{
\
'select2-result-sub
\
': $select.choiceGrouped($group),
\
'select2-result-single
\
': !$select.choiceGrouped($group) }
\"
><li role=
\"
option
\"
ng-attr-id=
\"
ui-select-choices-row-{{ $select.generatedId }}-{{$index}}
\"
class=
\"
ui-select-choices-row
\"
ng-class=
\"
{
\
'select2-highlighted
\
': $select.isActive(this),
\
'select2-disabled
\
': $select.isDisabled(this)}
\"
><div class=
\"
select2-result-label ui-select-choices-row-inner
\"
></div></li></ul></li></ul>"
);
$templateCache
.
put
(
"select2/match-multiple.tpl.html"
,
"<span class=
\"
ui-select-match
\"
><li class=
\"
ui-select-match-item select2-search-choice
\"
ng-repeat=
\"
$item in $select.selected track by $index
\"
ng-class=
\"
{
\
'select2-search-choice-focus
\
':$selectMultiple.activeMatchIndex === $index,
\
'select2-locked
\
':$select.isLocked(this, $index)}
\"
ui-select-sort=
\"
$select.selected
\"
><span uis-transclude-append=
\"\"
></span> <a href=
\"
javascript:;
\"
class=
\"
ui-select-match-close select2-search-choice-close
\"
ng-click=
\"
$selectMultiple.removeChoice($index)
\"
tabindex=
\"
-1
\"
></a></li></span>"
);
$templateCache
.
put
(
"select2/match.tpl.html"
,
"<a class=
\"
select2-choice ui-select-match
\"
ng-class=
\"
{
\
'select2-default
\
': $select.isEmpty()}
\"
ng-click=
\"
$select.toggle($event)
\"
aria-label=
\"
{{ $select.baseTitle }} select
\"
><span ng-show=
\"
$select.isEmpty()
\"
class=
\"
select2-chosen
\"
>{{$select.placeholder}}</span> <span ng-hide=
\"
$select.isEmpty()
\"
class=
\"
select2-chosen
\"
ng-transclude=
\"\"
></span> <abbr ng-if=
\"
$select.allowClear && !$select.isEmpty()
\"
class=
\"
select2-search-choice-close
\"
ng-click=
\"
$select.clear($event)
\"
></abbr> <span class=
\"
select2-arrow ui-select-toggle
\"
><b></b></span></a>"
);
$templateCache
.
put
(
"select2/no-choice.tpl.html"
,
"<div class=
\"
ui-select-no-choice dropdown
\"
ng-show=
\"
$select.items.length == 0
\"
><div class=
\"
dropdown-content
\"
><div data-selectable=
\"\"
ng-transclude=
\"\"
></div></div></div>"
);
$templateCache
.
put
(
"select2/select-multiple.tpl.html"
,
"<div class=
\"
ui-select-container ui-select-multiple select2 select2-container select2-container-multi
\"
ng-class=
\"
{
\
'select2-container-active select2-dropdown-open open
\
': $select.open,
\
'select2-container-disabled
\
': $select.disabled}
\"
><ul class=
\"
select2-choices
\"
><span class=
\"
ui-select-match
\"
></span><li class=
\"
select2-search-field
\"
><input type=
\"
search
\"
autocomplete=
\"
off
\"
autocorrect=
\"
off
\"
autocapitalize=
\"
off
\"
spellcheck=
\"
false
\"
role=
\"
combobox
\"
aria-expanded=
\"
true
\"
aria-owns=
\"
ui-select-choices-{{ $select.generatedId }}
\"
aria-label=
\"
{{ $select.baseTitle }}
\"
aria-activedescendant=
\"
ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}
\"
class=
\"
select2-input ui-select-search
\"
placeholder=
\"
{{$selectMultiple.getPlaceholder()}}
\"
ng-disabled=
\"
$select.disabled
\"
ng-hide=
\"
$select.disabled
\"
ng-model=
\"
$select.search
\"
ng-click=
\"
$select.activate()
\"
style=
\"
width: 34px;
\"
ondrop=
\"
return false;
\"
></li></ul><div class=
\"
ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active
\"
ng-class=
\"
{
\
'select2-display-none
\
': !$select.open || $select.items.length === 0}
\"
><div class=
\"
ui-select-choices
\"
></div></div></div>"
);
$templateCache
.
put
(
"select2/select.tpl.html"
,
"<div class=
\"
ui-select-container select2 select2-container
\"
ng-class=
\"
{
\
'select2-container-active select2-dropdown-open open
\
': $select.open,
\
'select2-container-disabled
\
': $select.disabled,
\
'select2-container-active
\
': $select.focus,
\
'select2-allowclear
\
': $select.allowClear && !$select.isEmpty()}
\"
><div class=
\"
ui-select-match
\"
></div><div class=
\"
ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active
\"
ng-class=
\"
{
\
'select2-display-none
\
': !$select.open}
\"
><div class=
\"
search-container
\"
ng-class=
\"
{
\
'ui-select-search-hidden
\
':!$select.searchEnabled,
\
'select2-search
\
':$select.searchEnabled}
\"
><input type=
\"
search
\"
autocomplete=
\"
off
\"
autocorrect=
\"
off
\"
autocapitalize=
\"
off
\"
spellcheck=
\"
false
\"
role=
\"
combobox
\"
aria-expanded=
\"
true
\"
aria-owns=
\"
ui-select-choices-{{ $select.generatedId }}
\"
aria-label=
\"
{{ $select.baseTitle }}
\"
aria-activedescendant=
\"
ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}
\"
class=
\"
ui-select-search select2-input
\"
ng-model=
\"
$select.search
\"
></div><div class=
\"
ui-select-choices
\"
></div><div class=
\"
ui-select-no-choice
\"
></div></div></div>"
);}]);
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