You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by sh...@apache.org on 2016/11/02 08:38:38 UTC
[16/32] kylin git commit: KYLIN 1820 Column autocomplete should
remove the user input in model designer
KYLIN 1820 Column autocomplete should remove the user input in model designer
Signed-off-by: Jason <ji...@163.com>
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/5218c9f6
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/5218c9f6
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/5218c9f6
Branch: refs/heads/v1.6.0-rc1-hbase1.x
Commit: 5218c9f6bc6e27e86f29d3b75e5c6aaa73a9cb98
Parents: 647d51e
Author: chenzhx <34...@qq.com>
Authored: Wed Oct 26 11:35:15 2016 +0800
Committer: Jason <ji...@163.com>
Committed: Mon Oct 31 16:59:15 2016 +0800
----------------------------------------------------------------------
webapp/app/js/directives/select.js | 939 ++++++++++++++++++++++++--------
1 file changed, 703 insertions(+), 236 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kylin/blob/5218c9f6/webapp/app/js/directives/select.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/directives/select.js b/webapp/app/js/directives/select.js
index c8cf6a2..7327af9 100644
--- a/webapp/app/js/directives/select.js
+++ b/webapp/app/js/directives/select.js
@@ -1,14 +1,13 @@
/*!
* ui-select
* http://github.com/angular-ui/ui-select
- * Version: 0.13.2 - 2015-10-09T15:34:24.040Z
+ * Version: 0.19.5 - 2016-10-24T23:13:59.434Z
* License: MIT
*/
(function () {
"use strict";
-
var KEY = {
TAB: 9,
ENTER: 13,
@@ -42,7 +41,7 @@ var KEY = {
return true;
}
- if (e.metaKey) return true;
+ if (e.metaKey || e.ctrlKey || e.altKey) return true;
return false;
},
@@ -55,6 +54,13 @@ var KEY = {
},
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;
}
};
@@ -103,11 +109,16 @@ var uis = angular.module('ui.select', [])
placeholder: '', // Empty by default, like HTML tag <select>
refreshDelay: 1000, // In milliseconds
closeOnSelect: true,
+ skipFocusser: false,
dropdownPosition: 'auto',
+ removeSelected: true,
+ resetSearchInput: true,
generateId: function() {
return latestId++;
},
- appendToBody: false
+ appendToBody: false,
+ spinnerEnabled: false,
+ spinnerClass: 'glyphicon-refresh ui-select-spin'
})
// See Rename minErr and make it accessible from outside https://github.com/angular/angular.js/issues/6913
@@ -139,11 +150,11 @@ var uis = angular.module('ui.select', [])
*/
.filter('highlight', function() {
function escapeRegexp(queryToEscape) {
- return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
+ 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;
+ return query && matchItem ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<span class="ui-select-highlight">$&</span>') : matchItem;
};
})
@@ -169,8 +180,8 @@ var uis = angular.module('ui.select', [])
}]);
uis.directive('uiSelectChoices',
- ['uiSelectConfig', 'uisRepeatParser', 'uiSelectMinErr', '$compile',
- function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile) {
+ ['uiSelectConfig', 'uisRepeatParser', 'uiSelectMinErr', '$compile', '$window',
+ function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile, $window) {
return {
restrict: 'EA',
@@ -178,6 +189,9 @@ uis.directive('uiSelectChoices',
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';
@@ -187,44 +201,59 @@ uis.directive('uiSelectChoices',
if (!tAttrs.repeat) throw uiSelectMinErr('repeat', "Expected 'repeat' expression.");
- return function link(scope, element, attrs, $select, transcludeFn) {
+ // var repeat = RepeatParser.parse(attrs.repeat);
+ var groupByExp = tAttrs.groupBy;
+ var groupFilterExp = tAttrs.groupFilter;
- // var repeat = RepeatParser.parse(attrs.repeat);
- var groupByExp = attrs.groupBy;
- var groupFilterExp = attrs.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());
+ }
- $select.parseRepeatAttr(attrs.repeat, groupByExp, groupFilterExp); //Result ready at $select.parserResult
+ var parserResult = RepeatParser.parse(tAttrs.repeat);
- $select.disableChoiceExpression = attrs.uiDisableChoice;
- $select.onHighlightCallback = attrs.onHighlight;
+ 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);
+ }
- $select.dropdownPosition = attrs.position ? attrs.position.toLowerCase() : uiSelectConfig.dropdownPosition;
+ choices.attr('ng-repeat', parserResult.repeatExpression(groupByExp))
+ .attr('ng-if', '$select.open'); //Prevent unnecessary watches when dropdown is closed
- if(groupByExp) {
- var groups = element.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 choices = element.querySelectorAll('.ui-select-choices-row');
- if (choices.length !== 1) {
- throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
- }
+ 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
- choices.attr('ng-repeat', $select.parserResult.repeatExpression(groupByExp))
- .attr('ng-if', '$select.open') //Prevent unnecessary watches when dropdown is closed
- .attr('ng-click', '$select.select(' + $select.parserResult.itemName + ',false,$event)');
+ // 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)');
- var rowsInner = element.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
+ return function link(scope, element, attrs, $select) {
- $compile(element, transcludeFn)(scope); //Passing current transcludeFn to be able to append elements correctly from uisTranscludeAppend
+
+ $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;
- $select.refresh(attrs.refresh);
+ if (!attrs.minimumInputLength || $select.search.length >= attrs.minimumInputLength) {
+ $select.refresh(attrs.refresh);
+ } else {
+ $select.items = [];
+ }
});
attrs.$observe('refreshDelay', function() {
@@ -232,6 +261,14 @@ uis.directive('uiSelectChoices',
var refreshDelay = scope.$eval(attrs.refreshDelay);
$select.refreshDelay = refreshDelay !== undefined ? refreshDelay : uiSelectConfig.refreshDelay;
});
+
+ scope.$watch('$select.open', function(open) {
+ if (open) {
+ tElement.attr('role', 'listbox');
+ } else {
+ tElement.removeAttr('role');
+ }
+ });
};
}
};
@@ -244,8 +281,8 @@ uis.directive('uiSelectChoices',
* 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', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig', '$parse',
- function($scope, $element, $timeout, $filter, RepeatParser, uiSelectMinErr, uiSelectConfig, $parse) {
+ ['$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;
@@ -255,9 +292,15 @@ uis.controller('uiSelectCtrl',
ctrl.searchEnabled = uiSelectConfig.searchEnabled;
ctrl.sortable = uiSelectConfig.sortable;
ctrl.refreshDelay = uiSelectConfig.refreshDelay;
+ ctrl.paste = uiSelectConfig.paste;
+ ctrl.resetSearchInput = uiSelectConfig.resetSearchInput;
+ ctrl.refreshing = false;
+ ctrl.spinnerEnabled = uiSelectConfig.spinnerEnabled;
+ ctrl.spinnerClass = uiSelectConfig.spinnerClass;
- ctrl.removeSelected = false; //If selected item(s) should be removed from dropdown list
+ 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
@@ -271,7 +314,6 @@ uis.controller('uiSelectCtrl',
ctrl.dropdownPosition = 'auto';
ctrl.focusser = undefined; //Reference to input element used to handle focus events
- ctrl.resetSearchInput = true;
ctrl.multiple = undefined; // Initialized inside uiSelect directive link function
ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelectChoices directive link function
ctrl.tagging = {isActivated: false, fct: undefined};
@@ -279,6 +321,17 @@ uis.controller('uiSelectCtrl',
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) {
@@ -286,16 +339,36 @@ uis.controller('uiSelectCtrl',
}
ctrl.isEmpty = function() {
- return angular.isUndefined(ctrl.selected) || ctrl.selected === null || ctrl.selected === '';
+ 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.resetSearchInput === undefined && uiSelectConfig.resetSearchInput)) {
+ if (ctrl.resetSearchInput) {
ctrl.search = EMPTY_SEARCH;
//reset activeIndex
if (ctrl.selected && ctrl.items.length && !ctrl.multiple) {
- ctrl.activeIndex = ctrl.items.indexOf(ctrl.selected);
+ ctrl.activeIndex = _findIndex(ctrl.items, function(item){
+ return angular.equals(this, item);
+ }, ctrl.selected);
}
}
}
@@ -329,15 +402,48 @@ uis.controller('uiSelectCtrl',
ctrl.activeIndex = 0;
}
- // Give it time to appear before focus
- $timeout(function() {
- ctrl.search = initSearchValue || ctrl.search;
- ctrl.searchInput[0].focus();
- if(!ctrl.tagging.isActivated && ctrl.items.length > 1) {
- _ensureHighlightVisible();
+ 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) {
@@ -412,17 +518,23 @@ uis.controller('uiSelectCtrl',
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.removeSelected) {
+ if (ctrl.isEmpty() || (angular.isArray(selectedItems) && !selectedItems.length) || !ctrl.multiple || !ctrl.removeSelected) {
ctrl.setItemsFn(data);
}else{
- if ( data !== undefined ) {
- var filteredItems = data.filter(function(i) {return selectedItems && selectedItems.indexOf(i) < 0;});
+ 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
@@ -439,7 +551,11 @@ uis.controller('uiSelectCtrl',
//Remove already selected items (ex: while searching)
//TODO Should add a test
ctrl.refreshItems(items);
- ctrl.ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters
+
+ //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
+ }
}
}
});
@@ -455,7 +571,6 @@ uis.controller('uiSelectCtrl',
*/
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
@@ -463,8 +578,13 @@ uis.controller('uiSelectCtrl',
$timeout.cancel(_refreshDelayPromise);
}
_refreshDelayPromise = $timeout(function() {
- $scope.$eval(refreshAttr);
- }, ctrl.refreshDelay);
+ var refreshPromise = $scope.$eval(refreshAttr);
+ if (refreshPromise && angular.isFunction(refreshPromise.then) && !ctrl.refreshing) {
+ ctrl.refreshing = true;
+ refreshPromise.then(function() {
+ ctrl.refreshing = false;
+ });
+ }}, ctrl.refreshDelay);
}
};
@@ -473,9 +593,9 @@ uis.controller('uiSelectCtrl',
return false;
}
var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
- var isActive = itemIndex === ctrl.activeIndex;
+ var isActive = itemIndex == ctrl.activeIndex;
- if ( !isActive || ( itemIndex < 0 && ctrl.taggingLabel !== false ) ||( itemIndex < 0 && ctrl.taggingLabel === false) ) {
+ if ( !isActive || itemIndex < 0 ) {
return false;
}
@@ -486,18 +606,49 @@ uis.controller('uiSelectCtrl',
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 itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
+ var item = itemScope[ctrl.itemProperty];
+ var itemIndex = ctrl.items.indexOf(item);
var isDisabled = false;
- var item;
- if (itemIndex >= 0 && !angular.isUndefined(ctrl.disableChoiceExpression)) {
- item = ctrl.items[itemIndex];
- isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression)); // force the boolean value
- item._uiSelectChoiceDisabled = isDisabled; // store this for later reference
+ 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;
@@ -506,16 +657,23 @@ uis.controller('uiSelectCtrl',
// When the user selects an item with ENTER or clicks the dropdown
ctrl.select = function(item, skipFocusser, $event) {
- if (item === undefined || !item._uiSelectChoiceDisabled) {
+ if (item === undefined || !_isItemDisabled(item)) {
- if ( ! ctrl.items && ! ctrl.search ) return;
+ if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return;
- if (!item || !item._uiSelectChoiceDisabled) {
- if(ctrl.tagging.isActivated) {
- // if taggingLabel is disabled, we pull from ctrl.search val
+ 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' || $event.type === 'touchend') && 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 ) {
- item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
+ if (item === undefined) {
+ item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
+ }
if (!item || angular.equals( ctrl.items[0], item ) ) {
return;
}
@@ -534,7 +692,7 @@ uis.controller('uiSelectCtrl',
// 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(ctrl.search);
+ item = ctrl.tagging.fct(item);
if (!item) return;
// if item type is 'string', apply the tagging label
} else if ( typeof item === 'string' ) {
@@ -544,12 +702,12 @@ uis.controller('uiSelectCtrl',
}
}
// search ctrl.selected for dupes potentially caused by tagging and return early if found
- if ( ctrl.selected && angular.isArray(ctrl.selected) && ctrl.selected.filter( function (selection) { return angular.equals(selection, item); }).length > 0 ) {
+ if (_isItemSelected(item)) {
ctrl.close(skipFocusser);
return;
}
}
-
+ _resetSearchInput();
$scope.$broadcast('uis:select', item);
var locals = {};
@@ -565,9 +723,6 @@ uis.controller('uiSelectCtrl',
if (ctrl.closeOnSelect) {
ctrl.close(skipFocusser);
}
- if ($event && $event.type === 'click') {
- ctrl.clickTriggeredSelect = true;
- }
}
}
};
@@ -576,9 +731,8 @@ uis.controller('uiSelectCtrl',
ctrl.close = function(skipFocusser) {
if (!ctrl.open) return;
if (ctrl.ngModel && ctrl.ngModel.$setTouched) ctrl.ngModel.$setTouched();
- _resetSearchInput();
ctrl.open = false;
-
+ _resetSearchInput();
$scope.$broadcast('uis:close', skipFocusser);
};
@@ -606,18 +760,56 @@ uis.controller('uiSelectCtrl',
}
};
- ctrl.isLocked = function(itemScope, itemIndex) {
- var isLocked, item = ctrl.selected[itemIndex];
+ // 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 && !angular.isUndefined(ctrl.lockChoiceExpression)) {
- isLocked = !!(itemScope.$eval(ctrl.lockChoiceExpression)); // force the boolean value
- item._uiSelectChoiceLocked = isLocked; // store this for later reference
+ 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],
@@ -639,12 +831,18 @@ uis.controller('uiSelectCtrl',
ctrl.searchInput.css('width', '10px');
$timeout(function() { //Give tags time to render correctly
if (sizeWatch === null && !updateIfVisible(calculateContainerWidth())) {
- sizeWatch = $scope.$watch(calculateContainerWidth, function(containerWidth) {
- if (updateIfVisible(containerWidth)) {
- sizeWatch();
- sizeWatch = null;
+ sizeWatch = $scope.$watch(function() {
+ if (!updaterScheduled) {
+ updaterScheduled = true;
+ $scope.$$postDigest(function() {
+ updaterScheduled = false;
+ if (updateIfVisible(calculateContainerWidth())) {
+ sizeWatch();
+ sizeWatch = null;
+ }
+ });
}
- });
+ }, angular.noop);
}
});
};
@@ -665,7 +863,7 @@ uis.controller('uiSelectCtrl',
break;
case KEY.ENTER:
if(ctrl.open && (ctrl.tagging.isActivated || ctrl.activeIndex >= 0)){
- ctrl.select(ctrl.items[ctrl.activeIndex]); // Make sure at least one dropdown item is highlighted before adding if not in tagging mode
+ 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
}
@@ -684,17 +882,20 @@ uis.controller('uiSelectCtrl',
var key = e.which;
- // if(~[KEY.ESC,KEY.TAB].indexOf(key)){
- // //TODO: SEGURO?
- // ctrl.close();
- // }
+ if (~[KEY.ENTER,KEY.ESC].indexOf(key)){
+ e.preventDefault();
+ e.stopPropagation();
+ }
$scope.$apply(function() {
var tagged = false;
if (ctrl.items.length > 0 || ctrl.tagging.isActivated) {
- _handleDropDownSelection(key);
+ 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] ) {
@@ -730,18 +931,45 @@ uis.controller('uiSelectCtrl',
});
- // If tagging try to split by tokens and add items
ctrl.searchInput.on('paste', function (e) {
- var data = e.originalEvent.clipboardData.getData('text/plain');
- if (data && data.length > 0 && ctrl.taggingTokens.isActivated && ctrl.tagging.fct) {
- var items = data.split(ctrl.taggingTokens.tokens[0]); // split by first token only
- if (items && items.length > 0) {
+ 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(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();
}
@@ -780,10 +1008,28 @@ uis.controller('uiSelectCtrl',
}
}
+ 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);
});
+ $scope.$watch('$select.activeIndex', function(activeIndex) {
+ if (activeIndex)
+ $element.find('input').attr(
+ 'aria-activedescendant',
+ 'ui-select-choices-row-' + ctrl.generatedId + '-' + activeIndex);
+ });
+
+ $scope.$watch('$select.open', function(open) {
+ if (!open)
+ $element.find('input').removeAttr('aria-activedescendant');
+ });
}]);
uis.directive('uiSelect',
@@ -805,6 +1051,14 @@ uis.directive('uiSelect',
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');
@@ -832,12 +1086,14 @@ uis.directive('uiSelect',
}
}();
+ 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);
- //Limit the number of selections allowed
- $select.limit = (angular.isDefined(attrs.limit)) ? parseInt(attrs.limit, 10) : undefined;
-
//Set reference to ngModel from uiSelectCtrl
$select.ngModel = ngModel;
@@ -852,9 +1108,8 @@ uis.directive('uiSelect',
});
}
- scope.$watch('searchEnabled', function() {
- var searchEnabled = scope.$eval(attrs.searchEnabled);
- $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled;
+ scope.$watch(function () { return scope.$eval(attrs.searchEnabled); }, function(newVal) {
+ $select.searchEnabled = newVal !== undefined ? newVal : uiSelectConfig.searchEnabled;
});
scope.$watch('sortable', function() {
@@ -862,6 +1117,16 @@ uis.directive('uiSelect',
$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;
@@ -873,6 +1138,10 @@ uis.directive('uiSelect',
$select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true;
});
+ attrs.$observe('paste', function() {
+ $select.paste = scope.$eval(attrs.paste);
+ });
+
attrs.$observe('tagging', function() {
if(attrs.tagging !== undefined)
{
@@ -908,6 +1177,17 @@ uis.directive('uiSelect',
}
});
+ attrs.$observe('spinnerEnabled', function() {
+ // $eval() is needed otherwise we get a string instead of a boolean
+ var spinnerEnabled = scope.$eval(attrs.spinnerEnabled);
+ $select.spinnerEnabled = spinnerEnabled !== undefined ? spinnerEnabled : uiSelectConfig.spinnerEnabled;
+ });
+
+ attrs.$observe('spinnerClass', function() {
+ var spinnerClass = attrs.spinnerClass;
+ $select.spinnerClass = spinnerClass !== undefined ? attrs.spinnerClass : uiSelectConfig.spinnerClass;
+ });
+
//Automatically gets focus when loaded
if (angular.isDefined(attrs.autofocus)){
$timeout(function(){
@@ -938,11 +1218,16 @@ uis.directive('uiSelect',
}
if (!contains && !$select.clickTriggeredSelect) {
- //Will lose focus only with certain targets
- var focusableControls = ['input','button','textarea'];
- var targetController = angular.element(e.target).controller('uiSelect'); //To check if target is other ui-select
- var 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
+ 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();
}
@@ -980,6 +1265,13 @@ uis.directive('uiSelect',
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
@@ -1041,6 +1333,9 @@ uis.directive('uiSelect',
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.
@@ -1079,57 +1374,75 @@ uis.directive('uiSelect',
};
- scope.calculateDropdownPos = function(){
-
- if ($select.open) {
- dropdown = angular.element(element).querySelectorAll('.ui-select-dropdown');
- if (dropdown.length === 0) {
- return;
- }
+ 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);
- // Hide the dropdown so there is no flicker until $timeout is done executing.
- dropdown[0].style.opacity = 0;
+ var offset = uisOffset(element);
+ var offsetDropdown = uisOffset(dropdown);
- // Delay positioning the dropdown until all choices have been added so its height is correct.
- $timeout(function(){
+ //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).
- if ($select.dropdownPosition === 'up'){
- //Go UP
- setDropdownPosUp(offset, offsetDropdown);
+ // 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);
+ }
+ }
- }else{ //AUTO
+ // Display the dropdown once it has been positioned.
+ dropdown[0].style.opacity = 1;
+ });
+ };
- element.removeClass(directionUpClassName);
+ var opened = false;
- var offset = uisOffset(element);
- var offsetDropdown = uisOffset(dropdown);
+ scope.calculateDropdownPos = function() {
+ if ($select.open) {
+ dropdown = angular.element(element).querySelectorAll('.ui-select-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).
+ if (dropdown.length === 0) {
+ return;
+ }
- // 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);
- }
+ // 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;
- // Display the dropdown once it has been positioned.
- dropdown[0].style.opacity = 1;
- });
+ $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;
- }
+ if (dropdown === null || dropdown.length === 0) {
+ return;
+ }
- // Reset the position of the dropdown.
- dropdown[0].style.position = '';
- dropdown[0].style.top = '';
- element.removeClass(directionUpClassName);
+ // Reset the position of the dropdown.
+ dropdown[0].style.opacity = 0;
+ dropdown[0].style.position = '';
+ dropdown[0].style.top = '';
+ element.removeClass(directionUpClassName);
}
};
};
@@ -1144,9 +1457,14 @@ uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) {
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 = tElement.parent().attr('theme') || uiSelectConfig.theme;
- var multi = tElement.parent().attr('multiple');
+ 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) {
@@ -1168,6 +1486,17 @@ uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) {
}
};
+
+ 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) {
@@ -1181,6 +1510,9 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
$select = $scope.$select,
ngModel;
+ if (angular.isUndefined($select.selected))
+ $select.selected = [];
+
//Wait for link fn to inject it
$scope.$evalAsync(function(){ ngModel = $scope.ngModel; });
@@ -1195,17 +1527,21 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
//Remove already selected items
//e.g. When user clicks on a selection, the selected array changes and
//the dropdown should remove that item
- $select.refreshItems();
- $select.sizeSearchInput();
+ if($select.refreshItems){
+ $select.refreshItems();
+ }
+ if($select.sizeSearchInput){
+ $select.sizeSearchInput();
+ }
};
// Remove item from multiple select
ctrl.removeChoice = function(index){
- var removedChoice = $select.selected[index];
+ // if the choice is locked, don't remove it
+ if($select.isLocked(null, index)) return false;
- // if the choice is locked, can't remove it
- if(removedChoice._uiSelectChoiceLocked) return;
+ var removedChoice = $select.selected[index];
var locals = {};
locals[$select.parserResult.itemName] = removedChoice;
@@ -1224,6 +1560,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
ctrl.updateModel();
+ return true;
};
ctrl.getPlaceholder = function(){
@@ -1245,11 +1582,15 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
//$select.selected = raw selected objects (ignoring any property binding)
$select.multiple = true;
- $select.removeSelected = 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 = {},
@@ -1266,7 +1607,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
// From model --> view
ngModel.$formatters.unshift(function (inputValue) {
- var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search
+ var data = $select.parserResult && $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search
locals = {},
result;
if (!data) return inputValue;
@@ -1277,10 +1618,13 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
locals[$select.parserResult.itemName] = list[p];
result = $select.parserResult.modelMapper(scope, locals);
if($select.parserResult.trackByExp){
- var matches = /\.(.+)/.exec($select.parserResult.trackByExp);
- if(matches.length>0 && result[matches[1]] == value[matches[1]]){
- resultMultiple.unshift(list[p]);
- return true;
+ 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.toUpperCase(),value.toUpperCase())){
@@ -1307,7 +1651,10 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
//Watch for external model changes
scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) {
if (oldValue != newValue){
- ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters
+ //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();
}
});
@@ -1317,12 +1664,13 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
if(!angular.isArray(ngModel.$viewValue)){
// Have tolerance for null or undefined values
if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){
- $select.selected = [];
+ 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
};
@@ -1400,11 +1748,16 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
case KEY.BACKSPACE:
// Remove selected item and select previous/first
if(~$selectMultiple.activeMatchIndex){
- $selectMultiple.removeChoice(curr);
- return prev;
+ if($selectMultiple.removeChoice(curr)) {
+ return prev;
+ } else {
+ return curr;
+ }
+
+ } else {
+ // If nothing yet selected, select last item
+ return last;
}
- // Select last item
- else return last;
break;
case KEY.DELETE:
// Remove selected item and select next item
@@ -1465,12 +1818,22 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
stashArr = stashArr.slice(1,stashArr.length);
}
newItem = $select.tagging.fct($select.search);
- newItem.isTag = true;
- // verify the the tag doesn't match the value of an existing item
- if ( stashArr.filter( function (origItem) { return angular.equals( origItem, $select.tagging.fct($select.search) ); } ).length > 0 ) {
+ // 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;
}
- newItem.isTag = true;
+ 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
@@ -1519,12 +1882,23 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
items = items.slice(dupeIndex+1,items.length-1);
} else {
items = [];
- items.push(newItem);
+ 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});
+ }
+ }
});
}
});
@@ -1555,9 +1929,11 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
// handle the object tagging implementation
} else {
var mockObj = tempArr[i];
- mockObj.isTag = true;
+ if (angular.isObject(mockObj)) {
+ mockObj.isTag = true;
+ }
if ( angular.equals(mockObj, needle) ) {
- dupeIndex = i;
+ dupeIndex = i;
}
}
}
@@ -1575,6 +1951,24 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
};
}]);
+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',
@@ -1595,14 +1989,14 @@ uis.directive('uiSelectSingle', ['$timeout','$compile', function($timeout, $comp
//From model --> view
ngModel.$formatters.unshift(function (inputValue) {
- var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search
+ 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;
+ return result === inputValue;
};
//If possible pass same object stored in $select.selected
if ($select.selected && checkFnSingle($select.selected)) {
@@ -1699,44 +2093,48 @@ uis.directive('uiSelectSingle', ['$timeout','$compile', function($timeout, $comp
}
};
}]);
+
// Make multiple matches sortable
uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', function($timeout, uiSelectConfig, uiSelectMinErr) {
return {
- require: '^uiSelect',
- link: function(scope, element, attrs, $select) {
+ require: ['^^uiSelect', '^ngModel'],
+ link: function(scope, element, attrs, ctrls) {
if (scope[attrs.uiSelectSort] === null) {
- throw uiSelectMinErr('sort', "Expected a list to sort");
+ 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,
- draggingClassName = 'dragging',
- droppingClassName = 'dropping',
- droppingBeforeClassName = 'dropping-before',
- droppingAfterClassName = 'dropping-after';
+ 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(n){
- if (n) {
+ }, function(newValue){
+ if (newValue) {
element.attr('draggable', true);
} else {
element.removeAttr('draggable');
}
});
- element.on('dragstart', function(e) {
+ element.on('dragstart', function(event) {
element.addClass(draggingClassName);
- (e.dataTransfer || e.originalEvent.dataTransfer).setData('text/plain', scope.$index);
+ (event.dataTransfer || event.originalEvent.dataTransfer).setData('text', scope.$index.toString());
});
element.on('dragend', function() {
- element.removeClass(draggingClassName);
+ removeClass(draggingClassName);
});
var move = function(from, to) {
@@ -1744,27 +2142,33 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
this.splice(to, 0, this.splice(from, 1)[0]);
};
- var dragOverHandler = function(e) {
- e.preventDefault();
+ var removeClass = function(className) {
+ angular.forEach($select.$element.querySelectorAll('.' + className), function(el){
+ angular.element(el).removeClass(className);
+ });
+ };
- var offset = axis === 'vertical' ? e.offsetY || e.layerY || (e.originalEvent ? e.originalEvent.offsetY : 0) : e.offsetX || e.layerX || (e.originalEvent ? e.originalEvent.offsetX : 0);
+ 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)) {
- element.removeClass(droppingAfterClassName);
+ removeClass(droppingAfterClassName);
element.addClass(droppingBeforeClassName);
} else {
- element.removeClass(droppingBeforeClassName);
+ removeClass(droppingBeforeClassName);
element.addClass(droppingAfterClassName);
}
};
var dropTimeout;
- var dropHandler = function(e) {
- e.preventDefault();
+ var dropHandler = function(event) {
+ event.preventDefault();
- var droppedItemIndex = parseInt((e.dataTransfer || e.originalEvent.dataTransfer).getData('text/plain'), 10);
+ var droppedItemIndex = parseInt((event.dataTransfer || event.originalEvent.dataTransfer).getData('text'), 10);
// prevent event firing multiple times in firefox
$timeout.cancel(dropTimeout);
@@ -1774,9 +2178,9 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
};
var _dropHandler = function(droppedItemIndex) {
- var theList = scope.$eval(attrs.uiSelectSort),
- itemToMove = theList[droppedItemIndex],
- newIndex = null;
+ var theList = scope.$eval(attrs.uiSelectSort);
+ var itemToMove = theList[droppedItemIndex];
+ var newIndex = null;
if (element.hasClass(droppingBeforeClassName)) {
if (droppedItemIndex < scope.$index) {
@@ -1794,6 +2198,8 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
move.apply(theList, [droppedItemIndex, newIndex]);
+ $ngModel.$setViewValue(Date.now());
+
scope.$apply(function() {
scope.$emit('uiSelectSort:change', {
array: theList,
@@ -1803,9 +2209,9 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
});
});
- element.removeClass(droppingClassName);
- element.removeClass(droppingBeforeClassName);
- element.removeClass(droppingAfterClassName);
+ removeClass(droppingClassName);
+ removeClass(droppingBeforeClassName);
+ removeClass(droppingAfterClassName);
element.off('drop', dropHandler);
};
@@ -1821,13 +2227,14 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
element.on('drop', dropHandler);
});
- element.on('dragleave', function(e) {
- if (e.target != element) {
+ element.on('dragleave', function(event) {
+ if (event.target != element) {
return;
}
- element.removeClass(droppingClassName);
- element.removeClass(droppingBeforeClassName);
- element.removeClass(droppingAfterClassName);
+
+ removeClass(droppingClassName);
+ removeClass(droppingBeforeClassName);
+ removeClass(droppingAfterClassName);
element.off('dragover', dragOverHandler);
element.off('drop', dropHandler);
@@ -1837,6 +2244,51 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f
}]);
/**
+ * 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
@@ -1860,38 +2312,48 @@ uis.service('uisRepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinE
var match;
- var isObjectCollection = /\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)/.test(expression);
+ //var isObjectCollection = /\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)/.test(expression);
// If an array is used as collection
// if (isObjectCollection){
- //00000000000000000000000000000111111111000000000000000222222222222220033333333333333333333330000444444444444444444000000000000000556666660000077777777777755000000000000000000000088888880000000
- match = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(([\w\.]+)?\s*(|\s*[\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
+ // 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 Collection expresion (only used when using an array collection)
- // 6 Object that will be converted to Array when using (key,value) syntax
- // 7 Filters that will be applied to #6 when using (key,value) syntax
- // 8 Track by
+ // 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);
}
- if (!match[6] && isObjectCollection) {
- throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ as (_key_, _item_) in _ObjCollection_ [ 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(!match[3] ? match[5] : match[6]),
- sourceName: match[6],
- filters: match[7],
- trackByExp: match[8],
+ 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');
@@ -1905,22 +2367,27 @@ uis.service('uisRepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinE
};
self.getGroupNgRepeatExpression = function() {
- return '$group in $select.groups';
+ 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.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 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\"><a href=\"javascript:void(0)\" class=\"ui-select-choices-row-inner\"></a></div></li></ul>");
-$templateCache.put("bootstrap/match-multiple.tpl.html","<span class=\"ui-select-match\"><span ng-repeat=\"$item in $select.selected\"><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\" 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()\" 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 gl
yphicon-remove\" aria-hidden=\"true\"></i></a></span></div>");
-$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=\"text\" autocomplete=\"false\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" class=\"ui-select-search input-xs\" placeholder=\"{{$selectMultiple.getPlaceholder()}}\" ng-disabled=\"$select.disabled\" ng-hide=\"$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>");
-$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=\"text\" autocomplete=\"false\" 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\" placeholder=\"{{$select.placeholder}}\" ng-model=\"$select.search\" ng-show=\"$select.searchEnabled && $select.open\"><div class=\"ui-select-choices\"></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.open || $select.isEmpty())\" class=\"ui-select-match\" ng-transclude=\"\"></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.activate()\"><div class=\"ui-select-match\"></div><input type=\"text\" autocomplete=\"false\" tabindex=\"-1\" class=\"ui-select-search ui-select-toggle\" ng-click=\"$select.toggle($event)\" placeholder=\"{{$select.placeholder}}\" ng-model=\"$select.search\" ng-hide=\"!$select.searchEnabled || ($select.selected && !$select.open)\" ng-disabled=\"$select.disabled\" aria-label=\"{{ $select.baseTitle }}\"></div><div class=\"ui-select-choices\"></div></div>");
-$templateCache.put("select2/choices.tpl.html","<ul 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\" 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\" 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>");
+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\" 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-expanded=\"{{$select.open}}\" aria-label=\"{{$select.baseTitle}}\" ng-class=\"{\'spinner\': $select.refreshing}\" 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><span ng-show=\"$select.open && $select.refreshing && $select.spinnerEnabled\" class=\"ui-select-refreshing {{$select.spinnerClass}}\"></span> <input type=\"search\" autocomplete=\"off\" tabindex=\"-1\" aria-expanded=\"true\" aria-label=\"{{ $select.baseTitle }}\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" 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("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 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/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=\"text\" autocomplete=\"false\" 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=\"retur
n false;\"></li></ul><div class=\"ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active\" ng-class=\"{\'select2-display-none\': !$select.open}\"><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=\"select2-search\" ng-show=\"$select.searchEnabled\"><input type=\"text\" autocomplete=\"false\" autocorrect=\"false\" 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></div>");}]);
+$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=\"retur
n 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\" ng-class=\"{\'select2-active\': $select.refreshing}\" role=\"combobox\" aria-expanded=\"true\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" aria-label=\"{{ $select.baseTitle }}\" class=\"ui-select-sear
ch select2-input\" ng-model=\"$select.search\"></div><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></div></div></div>");
+$templateCache.put("selectize/choices.tpl.html","<div ng-show=\"$select.open\" class=\"ui-select-choices ui-select-dropdown selectize-dropdown\" ng-class=\"{\'single\': !$select.multiple, \'multi\': $select.multiple}\"><div class=\"ui-select-choices-content selectize-dropdown-content\"><div class=\"ui-select-choices-group optgroup\"><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-multiple.tpl.html","<div class=\"ui-select-match\" data-value=\"\" ng-repeat=\"$item in $select.selected track by $index\" ng-click=\"$selectMultiple.activeMatchIndex = $index;\" ng-class=\"{\'active\':$selectMultiple.activeMatchIndex === $index}\" ui-select-sort=\"$select.selected\"><span class=\"ui-select-match-item\" ng-class=\"{\'select-locked\':$select.isLocked(this, $index)}\"><span uis-transclude-append=\"\"></span> <span class=\"remove ui-select-match-close\" ng-hide=\"$select.disabled\" ng-click=\"$selectMultiple.removeChoice($index)\">×</span></span></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-multiple.tpl.html","<div class=\"ui-select-container selectize-control multi plugin-remove_button\" 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\" ng-class=\"{\'ui-select-search-hidden\':!$select.searchEnabled}\" placeholder=\"{{$selectMultiple.getPlaceholder()}}\" ng-model=\"$select.search\" ng-disabled=\"$select.disabled\" aria-expanded=\"{{$select.open}}\" aria-label=\"{{ $select.baseTitle }}\" ondrop=\"return false;\"></div><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></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>");}]);