From 0ac10b09d57ee568e2891167f6bc49303f010608 Mon Sep 17 00:00:00 2001 From: Wladimir Coka Date: Mon, 16 Mar 2015 23:09:13 -0500 Subject: [PATCH] chore(build): v0.11.2 --- bower.json | 2 +- dist/select.css | 14 +- dist/select.js | 1527 ++++++++++++++++++++++++------------------- dist/select.min.css | 4 +- dist/select.min.js | 5 +- package.json | 2 +- 6 files changed, 867 insertions(+), 687 deletions(-) diff --git a/bower.json b/bower.json index e2d222a4a..beb6f1047 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ui-select", - "version": "0.11.1", + "version": "0.11.2", "homepage": "https://github.com/angular-ui/ui-select", "authors": [ "AngularUI" diff --git a/dist/select.css b/dist/select.css index 26498e0f4..d397c9750 100644 --- a/dist/select.css +++ b/dist/select.css @@ -1,7 +1,7 @@ /*! * ui-select * http://github.com/angular-ui/ui-select - * Version: 0.11.1 - 2015-03-09T14:30:26.112Z + * Version: 0.11.2 - 2015-03-17T04:08:46.478Z * License: MIT */ @@ -44,6 +44,10 @@ display:none; } +body > .select2-container.open { + z-index: 9999; /* The z-index Select2 applies to the select2-drop */ +} + /* Selectize theme */ /* Helper class to show styles when focus */ @@ -104,9 +108,9 @@ border-bottom-right-radius: 0; } -.ui-select-bootstrap > .ui-select-match { +.ui-select-bootstrap > .ui-select-match > .btn{ /* Instead of center because of .btn */ - text-align: left; + text-align: left !important; } .ui-select-bootstrap > .ui-select-match > .caret { @@ -124,6 +128,10 @@ 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; diff --git a/dist/select.js b/dist/select.js index 027e14489..aa0e5ae57 100644 --- a/dist/select.js +++ b/dist/select.js @@ -1,7 +1,7 @@ /*! * ui-select * http://github.com/angular-ui/ui-select - * Version: 0.11.1 - 2015-03-09T14:30:26.109Z + * Version: 0.11.2 - 2015-03-17T04:08:46.474Z * License: MIT */ @@ -105,7 +105,8 @@ var uis = angular.module('ui.select', []) closeOnSelect: true, generateId: function() { return latestId++; - } + }, + appendToBody: false }) // See Rename minErr and make it accessible from outside https://github.com/angular/angular.js/issues/6913 @@ -143,62 +144,31 @@ var uis = angular.module('ui.select', []) return function(matchItem, query) { return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; }; -}); - +}) /** - * Parses "repeat" attribute. + * A read-only equivalent of jQuery's offset function: http://api.jquery.com/offset/ * - * 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 + * 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) { -uis.service('RepeatParser', ['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 = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?([\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); - - if (!match) { - throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", - expression); - } - + return function(element) { + var boundingClientRect = element[0].getBoundingClientRect(); return { - itemName: match[2], // (lhs) Left-hand side, - source: $parse(match[3]), - trackByExp: match[4], - modelMapper: $parse(match[1] || match[2]) + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), + left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) }; - - }; - - self.getGroupNgRepeatExpression = function() { - return '$group in $select.groups'; - }; - - self.getNgRepeatExpression = function(itemName, source, trackByExp, grouped) { - var expression = itemName + ' in ' + (grouped ? '$group.items' : source); - if (trackByExp) { - expression += ' track by ' + trackByExp; - } - return expression; }; }]); uis.directive('uiSelectChoices', - ['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', '$compile', + ['uiSelectConfig', 'uisRepeatParser', 'uiSelectMinErr', '$compile', function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile) { return { @@ -271,7 +241,7 @@ 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', 'RepeatParser', 'uiSelectMinErr', 'uiSelectConfig', + ['$scope', '$element', '$timeout', '$filter', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig', function($scope, $element, $timeout, $filter, RepeatParser, uiSelectMinErr, uiSelectConfig) { var ctrl = this; @@ -279,37 +249,41 @@ uis.controller('uiSelectCtrl', var EMPTY_SEARCH = ''; ctrl.placeholder = uiSelectConfig.placeholder; + ctrl.searchEnabled = uiSelectConfig.searchEnabled; + ctrl.sortable = uiSelectConfig.sortable; + ctrl.refreshDelay = uiSelectConfig.refreshDelay; + + ctrl.removeSelected = false; //If selected item(s) should be removed from dropdown list + ctrl.closeOnSelect = true; //Initialized inside uiSelect directive link function ctrl.search = EMPTY_SEARCH; - ctrl.activeIndex = 0; - ctrl.activeMatchIndex = -1; - ctrl.items = []; - ctrl.selected = undefined; + + ctrl.activeIndex = 0; //Dropdown of choices + ctrl.items = []; //All available choices + ctrl.open = false; ctrl.focus = false; - ctrl.focusser = undefined; //Reference to input element used to handle focus events ctrl.disabled = false; - ctrl.searchEnabled = uiSelectConfig.searchEnabled; - ctrl.sortable = uiSelectConfig.sortable; + ctrl.selected = undefined; + + 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.refreshDelay = uiSelectConfig.refreshDelay; 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.closeOnSelect = true; // Initialized inside uiSelect directive link function ctrl.clickTriggeredSelect = false; ctrl.$filter = $filter; + 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 === ''; }; - var _searchInput = $element.querySelectorAll('input.ui-select-search'); - if (_searchInput.length !== 1) { - throw uiSelectMinErr('searchInput', "Expected 1 input.ui-select-search but got '{0}'.", _searchInput.length); - } - // 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)) { @@ -325,9 +299,10 @@ uis.controller('uiSelectCtrl', ctrl.activate = function(initSearchValue, avoidReset) { if (!ctrl.disabled && !ctrl.open) { if(!avoidReset) _resetSearchInput(); - ctrl.focusser.prop('disabled', true); //Will reactivate it on .close() + + $scope.$broadcast('uis:activate'); + ctrl.open = true; - ctrl.activeMatchIndex = -1; ctrl.activeIndex = ctrl.activeIndex >= ctrl.items.length ? 0 : ctrl.activeIndex; @@ -340,7 +315,7 @@ uis.controller('uiSelectCtrl', // Give it time to appear before focus $timeout(function() { ctrl.search = initSearchValue || ctrl.search; - _searchInput[0].focus(); + ctrl.searchInput[0].focus(); }); } }; @@ -375,16 +350,29 @@ uis.controller('uiSelectCtrl', ctrl.items = items; } - var setItemsFn = groupByExp ? updateGroups : setPlainItems; + ctrl.setItemsFn = groupByExp ? updateGroups : setPlainItems; ctrl.parserResult = RepeatParser.parse(repeatAttr); ctrl.isGrouped = !!groupByExp; ctrl.itemProperty = ctrl.parserResult.itemName; + ctrl.refreshItems = function (data){ + data = data || ctrl.parserResult.source($scope); + var selectedItems = ctrl.selected; + //TODO should implement for single mode removeSelected + if ((angular.isArray(selectedItems) && !selectedItems.length) || !ctrl.removeSelected) { + ctrl.setItemsFn(data); + }else{ + if ( data !== undefined ) { + var filteredItems = data.filter(function(i) {return selectedItems.indexOf(i) < 0;}); + ctrl.setItemsFn(filteredItems); + } + } + }; + // 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 @@ -394,36 +382,14 @@ uis.controller('uiSelectCtrl', if (!angular.isArray(items)) { throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items); } else { - if (ctrl.multiple){ - //Remove already selected items (ex: while searching) - var filteredItems = items.filter(function(i) {return ctrl.selected.indexOf(i) < 0;}); - setItemsFn(filteredItems); - }else{ - setItemsFn(items); - } + //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 - } } - }); - if (ctrl.multiple){ - //Remove already selected items - $scope.$watchCollection('$select.selected', function(selectedItems){ - var data = ctrl.parserResult.source($scope); - if (!selectedItems.length) { - setItemsFn(data); - }else{ - if ( data !== undefined ) { - var filteredItems = data.filter(function(i) {return selectedItems.indexOf(i) < 0;}); - setItemsFn(filteredItems); - } - } - ctrl.sizeSearchInput(); - }); - } - }; var _refreshDelayPromise; @@ -534,16 +500,11 @@ uis.controller('uiSelectCtrl', } } + $scope.$broadcast('uis:select', item); + var locals = {}; locals[ctrl.parserResult.itemName] = item; - if(ctrl.multiple) { - ctrl.selected.push(item); - ctrl.sizeSearchInput(); - } else { - ctrl.selected = item; - } - $timeout(function(){ ctrl.onSelectCallback($scope, { $item: item, @@ -551,7 +512,7 @@ uis.controller('uiSelectCtrl', }); }); - if (!ctrl.multiple || ctrl.closeOnSelect) { + if (ctrl.closeOnSelect) { ctrl.close(skipFocusser); } if ($event && $event.type === 'click') { @@ -567,17 +528,13 @@ uis.controller('uiSelectCtrl', if (ctrl.ngModel && ctrl.ngModel.$setTouched) ctrl.ngModel.$setTouched(); _resetSearchInput(); ctrl.open = false; - if (!ctrl.multiple){ - $timeout(function(){ - ctrl.focusser.prop('disabled', false); - if (!skipFocusser) ctrl.focusser[0].focus(); - },0,false); - } + + $scope.$broadcast('uis:close', skipFocusser); + }; ctrl.setFocus = function(){ - if (!ctrl.focus && !ctrl.multiple) ctrl.focusser[0].focus(); - if (!ctrl.focus && ctrl.multiple) _searchInput[0].focus(); + if (!ctrl.focus) ctrl.focusInput[0].focus(); }; ctrl.clear = function($event) { @@ -608,39 +565,11 @@ uis.controller('uiSelectCtrl', return isLocked; }; - // Remove item from multiple select - ctrl.removeChoice = function(index){ - var removedChoice = ctrl.selected[index]; - - // if the choice is locked, can't remove it - if(removedChoice._uiSelectChoiceLocked) return; - - var locals = {}; - locals[ctrl.parserResult.itemName] = removedChoice; - - ctrl.selected.splice(index, 1); - ctrl.activeMatchIndex = -1; - ctrl.sizeSearchInput(); - - // Give some time for scope propagation. - $timeout(function(){ - ctrl.onRemoveCallback($scope, { - $item: removedChoice, - $model: ctrl.parserResult.modelMapper($scope, locals) - }); - }); - }; - - ctrl.getPlaceholder = function(){ - //Refactor single? - if(ctrl.multiple && ctrl.selected.length) return; - return ctrl.placeholder; - }; - var sizeWatch = null; ctrl.sizeSearchInput = function() { - var input = _searchInput[0], - container = _searchInput.parent().parent()[0], + + 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; @@ -651,11 +580,11 @@ uis.controller('uiSelectCtrl', } var inputWidth = containerWidth - input.offsetLeft - 10; if (inputWidth < 50) inputWidth = containerWidth; - _searchInput.css('width', inputWidth+'px'); + ctrl.searchInput.css('width', inputWidth+'px'); return true; }; - _searchInput.css('width', '10px'); + ctrl.searchInput.css('width', '10px'); $timeout(function() { //Give tags time to render correctly if (sizeWatch === null && !updateIfVisible(calculateContainerWidth())) { sizeWatch = $scope.$watch(calculateContainerWidth, function(containerWidth) { @@ -698,68 +627,8 @@ uis.controller('uiSelectCtrl', return processed; } - // Handles selected options in "multiple" mode - function _handleMatchSelection(key){ - var caretPosition = _getCaretPosition(_searchInput[0]), - length = ctrl.selected.length, - // none = -1, - first = 0, - last = length-1, - curr = ctrl.activeMatchIndex, - next = ctrl.activeMatchIndex+1, - prev = ctrl.activeMatchIndex-1, - newIndex = curr; - - if(caretPosition > 0 || (ctrl.search.length && key == KEY.RIGHT)) return false; - - ctrl.close(); - - function getNewActiveMatchIndex(){ - switch(key){ - case KEY.LEFT: - // Select previous/first item - if(~ctrl.activeMatchIndex) return prev; - // Select last item - else return last; - break; - case KEY.RIGHT: - // Open drop-down - if(!~ctrl.activeMatchIndex || curr === last){ - ctrl.activate(); - return false; - } - // Select next/last item - else return next; - break; - case KEY.BACKSPACE: - // Remove selected item and select previous/first - if(~ctrl.activeMatchIndex){ - ctrl.removeChoice(curr); - return prev; - } - // Select last item - else return last; - break; - case KEY.DELETE: - // Remove selected item and select next item - if(~ctrl.activeMatchIndex){ - ctrl.removeChoice(ctrl.activeMatchIndex); - return curr; - } - else return false; - } - } - - newIndex = getNewActiveMatchIndex(); - - if(!ctrl.selected.length || newIndex === false) ctrl.activeMatchIndex = -1; - else ctrl.activeMatchIndex = Math.min(last,Math.max(first,newIndex)); - - return true; - } - // Bind to keyboard shortcuts - _searchInput.on('keydown', function(e) { + ctrl.searchInput.on('keydown', function(e) { var key = e.which; @@ -769,15 +638,11 @@ uis.controller('uiSelectCtrl', // } $scope.$apply(function() { - var processed = false; - var tagged = false; - if(ctrl.multiple && KEY.isHorizontalMovement(key)){ - processed = _handleMatchSelection(key); - } + var tagged = false; - if (!processed && (ctrl.items.length > 0 || ctrl.tagging.isActivated)) { - processed = _handleDropDownSelection(key); + if (ctrl.items.length > 0 || ctrl.tagging.isActivated) { + _handleDropDownSelection(key); if ( ctrl.taggingTokens.isActivated ) { for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) { if ( ctrl.taggingTokens.tokens[i] === KEY.MAP[e.keyCode] ) { @@ -789,7 +654,7 @@ uis.controller('uiSelectCtrl', } if ( tagged ) { $timeout(function() { - _searchInput.triggerHandler('tagged'); + ctrl.searchInput.triggerHandler('tagged'); var newItem = ctrl.search.replace(KEY.MAP[e.keyCode],'').trim(); if ( ctrl.tagging.fct ) { newItem = ctrl.tagging.fct( newItem ); @@ -800,12 +665,6 @@ uis.controller('uiSelectCtrl', } } - if (processed && key != KEY.TAB) { - //TODO Check si el tab selecciona aun correctamente - //Crear test - e.preventDefault(); - e.stopPropagation(); - } }); if(KEY.isVerticalMovement(key) && ctrl.items.length > 0){ @@ -815,7 +674,7 @@ uis.controller('uiSelectCtrl', }); // If tagging try to split by tokens and add items - _searchInput.on('paste', function (e) { + 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 @@ -832,166 +691,12 @@ uis.controller('uiSelectCtrl', } }); - _searchInput.on('keyup', function(e) { - if ( ! KEY.isVerticalMovement(e.which) ) { - $scope.$evalAsync( function () { - ctrl.activeIndex = ctrl.taggingLabel === false ? -1 : 0; - }); - } - // Push a "create new" item into array if there is a search string - if ( ctrl.tagging.isActivated && ctrl.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 - ctrl.activeIndex = ctrl.taggingLabel === false ? -1 : 0; - // taggingLabel === false bypasses all of this - if (ctrl.taggingLabel === false) return; - - var items = angular.copy( ctrl.items ); - var stashArr = angular.copy( ctrl.items ); - var newItem; - var item; - var hasTag = false; - var dupeIndex = -1; - var tagItems; - var tagItem; - - // case for object tagging via transform `ctrl.tagging.fct` function - if ( ctrl.tagging.fct !== undefined) { - tagItems = ctrl.$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 = ctrl.tagging.fct(ctrl.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, ctrl.tagging.fct(ctrl.search) ); } ).length > 0 ) { - return; - } - newItem.isTag = true; - // handle newItem string and stripping dupes in tagging string context - } else { - // find any tagging items already in the ctrl.items array and store them - tagItems = ctrl.$filter('filter')(items,function (item) { - return item.match(ctrl.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 = ctrl.search+' '+ctrl.taggingLabel; - if ( _findApproxDupe(ctrl.selected, ctrl.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(ctrl.selected)) ) { - // if there is a tag from prev iteration, strip it / queue the change - // and return early - if ( hasTag ) { - items = stashArr; - $scope.$evalAsync( function () { - ctrl.activeIndex = 0; - ctrl.items = items; - }); - } - return; - } - if ( _findCaseInsensitiveDupe(stashArr) ) { - // if there is a tag from prev iteration, strip it - if ( hasTag ) { - ctrl.items = stashArr.slice(1,stashArr.length); - } - return; - } - } - if ( hasTag ) dupeIndex = _findApproxDupe(ctrl.selected, newItem); - // dupe found, shave the first item - if ( dupeIndex > -1 ) { - items = items.slice(dupeIndex+1,items.length-1); - } else { - items = []; - items.push(newItem); - items = items.concat(stashArr); - } - $scope.$evalAsync( function () { - ctrl.activeIndex = 0; - ctrl.items = items; - }); - } - }); - - _searchInput.on('tagged', function() { + ctrl.searchInput.on('tagged', function() { $timeout(function() { _resetSearchInput(); }); }); - _searchInput.on('blur', function() { - $timeout(function() { - ctrl.activeMatchIndex = -1; - }); - }); - - function _findCaseInsensitiveDupe(arr) { - if ( arr === undefined || ctrl.search === undefined ) { - return false; - } - var hasDupe = arr.filter( function (origItem) { - if ( ctrl.search.toUpperCase() === undefined || origItem === undefined ) { - return false; - } - return origItem.toUpperCase() === ctrl.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 ").removeAttr('multiple'); + else + tElement.append(""); - $select.generatedId = uiSelectConfig.generateId(); - $select.baseTitle = attrs.title || 'Select box'; - $select.focusserTitle = $select.baseTitle + ' focus'; - $select.focusserId = 'focusser-' + $select.generatedId; + return function(scope, element, attrs, ctrls, transcludeFn) { - $select.multiple = angular.isDefined(attrs.multiple) && ( - attrs.multiple === '' || - attrs.multiple.toLowerCase() === 'multiple' || - attrs.multiple.toLowerCase() === 'true' - ); + var $select = ctrls[0]; + var ngModel = ctrls[1]; - $select.closeOnSelect = function() { - if (angular.isDefined(attrs.closeOnSelect)) { - return $parse(attrs.closeOnSelect)(); - } else { - return uiSelectConfig.closeOnSelect; + $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; + } + }(); + + $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"); + }); } - }(); - $select.onSelectCallback = $parse(attrs.onSelect); - $select.onRemoveCallback = $parse(attrs.onRemove); + scope.$watch('searchEnabled', function() { + var searchEnabled = scope.$eval(attrs.searchEnabled); + $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; + }); - //From view --> model - ngModel.$parsers.unshift(function (inputValue) { - var locals = {}, - result; - if ($select.multiple){ - var 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); + scope.$watch('sortable', function() { + var sortable = scope.$eval(attrs.sortable); + $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; + }); + + 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('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}; } - return resultMultiple; - }else{ - locals = {}; - locals[$select.parserResult.itemName] = inputValue; - result = $select.parserResult.modelMapper(scope, locals); - return result; - } - }); + else + { + $select.tagging = {isActivated: false, fct: undefined}; + } + }); - //From model --> view - ngModel.$formatters.unshift(function (inputValue) { - var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search - locals = {}, - result; - if (data){ - if ($select.multiple){ - var resultMultiple = []; - var checkFnMultiple = function(list, value){ - //if the list is empty add the value to the list - if (!list || !list.length){ - resultMultiple.unshift(value); - return true; - } - 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 matches = /\.(.+)/.exec($select.parserResult.trackByExp); - if(matches.length>0 && result[matches[1]] == value[matches[1]]){ - resultMultiple.unshift(list[p]); - return true; - } - } - if (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--) { - if (!checkFnMultiple($select.selected, inputValue[k])){ - checkFnMultiple(data, inputValue[k]); - } - } - return resultMultiple; - }else{ - 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; + 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; } - for (var i = data.length - 1; i >= 0; i--) { - if (checkFnSingle(data[i])) return data[i]; + 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(); + }); } - return inputValue; - }); - //Set reference to ngModel from uiSelectCtrl - $select.ngModel = ngModel; + //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(); + }); + }); + } - $select.choiceGrouped = function(group){ - return $select.isGrouped && group && group.name; - }; + function onDocumentClick(e) { + if (!$select.open) return; //Skip it if dropdown is close - //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 - var focusser = angular.element(""); + var contains = false; - if(attrs.tabindex){ - //tabindex might be an expression, wait until it contains the actual value before we set the focusser tabindex - attrs.$observe('tabindex', function(value) { - //If we are using multiple, add tabindex to the search input - if($select.multiple){ - searchInput.attr("tabindex", value); + 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 { - focusser.attr("tabindex", value); + contains = element[0].contains(e.target); } - //Remove the tabindex on the parent so that it is not focusable - element.removeAttr("tabindex"); - }); - } - $compile(focusser)(scope); - $select.focusser = focusser; + if (!contains && !$select.clickTriggeredSelect) { + //Will lose focus only with certain targets + var focusableControls = ['input','button','textarea']; + var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select + var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $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 + $select.close(skipFocusser); + scope.$digest(); + } + $select.clickTriggeredSelect = false; + } - if (!$select.multiple){ + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 + $document.on('click', onDocumentClick); - element.append(focusser); - focusser.bind("focus", function(){ - scope.$evalAsync(function(){ - $select.focus = true; - }); - }); - focusser.bind("blur", function(){ - scope.$evalAsync(function(){ - $select.focus = false; - }); + scope.$on('$destroy', function() { + $document.off('click', onDocumentClick); }); - focusser.bind("keydown", function(e){ - if (e.which === KEY.BACKSPACE) { - e.preventDefault(); - e.stopPropagation(); - $select.select(undefined); - scope.$apply(); - return; - } + // 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 - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; - } + // 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('
').append(clone); - if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ - e.preventDefault(); - e.stopPropagation(); - $select.activate(); + 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); - scope.$digest(); + 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); }); - 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; - } + // 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(); + } + }); - $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input - focusser.val(''); - scope.$digest(); + // 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('
'); + placeholder[0].style.width = offset.width + 'px'; + placeholder[0].style.height = offset.height + 'px'; + element.after(placeholder); - scope.$watch('searchEnabled', function() { - var searchEnabled = scope.$eval(attrs.searchEnabled); - $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; - }); + // 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; - scope.$watch('sortable', function() { - var sortable = scope.$eval(attrs.sortable); - $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; - }); + // Now move the actual dropdown element to the end of the body + $document.find('body').append(element); - 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; - if ($select.multiple) { - // As the search input field may now become visible, it may be necessary to recompute its size - $select.sizeSearchInput(); + element[0].style.position = 'absolute'; + element[0].style.left = offset.left + 'px'; + element[0].style.top = offset.top + 'px'; + element[0].style.width = offset.width + 'px'; } - }); - 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; - }); + function resetDropdown() { + if (placeholder === null) { + // The dropdown has not actually been display yet, so there's nothing to reset + return; + } - 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}; + // 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; } - else - { - $select.tagging = {isActivated: false, fct: undefined}; + }; + } + }; +}]); + +uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) { + return { + restrict: 'EA', + require: '^uiSelect', + replace: true, + transclude: true, + templateUrl: function(tElement) { + // Gets theme attribute from parent (ui-select) + var theme = tElement.parent().attr('theme') || uiSelectConfig.theme; + var multi = tElement.parent().attr('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(); + } + + } + }; +}]); + +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; + + //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 + $select.refreshItems(); + $select.sizeSearchInput(); + }; + + // Remove item from multiple select + ctrl.removeChoice = function(index){ + + var removedChoice = $select.selected[index]; + + // if the choice is locked, can't remove it + if(removedChoice._uiSelectChoiceLocked) return; + + 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(); + + }; + + ctrl.getPlaceholder = function(){ + //Refactor single? + if($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; + $select.removeSelected = true; + + //Input that will handle focus + $select.focusInput = $select.searchInput; + + //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; }); - 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; + // From model --> view + ngModel.$formatters.unshift(function (inputValue) { + var data = $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 matches = /\.(.+)/.exec($select.parserResult.trackByExp); + if(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; + } } - else - { - $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; + 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){ + ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters + $selectMultiple.refreshComponent(); + } }); - attrs.$observe('taggingTokens', function() { - if (attrs.tagging !== undefined) { - var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; - $select.taggingTokens = {isActivated: true, tokens: tokens }; + 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){ + $select.selected = []; + } else { + throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); + } } + $select.selected = ngModel.$viewValue; + scope.$evalAsync(); //To force $digest + }; + + scope.$on('uis:select', function (event, item) { + $select.selected.push(item); + $selectMultiple.updateModel(); }); - //Automatically gets focus when loaded - if (angular.isDefined(attrs.autofocus)){ - $timeout(function(){ - $select.setFocus(); + 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){ + $selectMultiple.removeChoice(curr); + return prev; + } + // Select last item + else 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; + } + } - //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(); - }); - }); + newIndex = getNewActiveMatchIndex(); + + if(!$select.selected.length || newIndex === false) $selectMultiple.activeMatchIndex = -1; + else $selectMultiple.activeMatchIndex = Math.min(last,Math.max(first,newIndex)); + + return true; } - if ($select.multiple){ - 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 - }); - $select.firstPass = true; // so the form doesn't get dirty as soon as it loads - scope.$watchCollection('$select.selected', function() { - if (!$select.firstPass) { - ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes + $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); + 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 ) { + return; + } + newItem.isTag = true; + // handle newItem string and stripping dupes in tagging string context } else { - $select.firstPass = false; + // 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; + } } - }); - focusser.prop('disabled', true); //Focusser isn't needed if multiple - }else{ - scope.$watch('$select.selected', function(newValue) { - if (ngModel.$viewValue !== newValue) { - ngModel.$setViewValue(newValue); + 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 = []; + items.push(newItem); + items = items.concat(stashArr); } - }); - } + scope.$evalAsync( function () { + $select.activeIndex = 0; + $select.items = items; + }); + } + }); + 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; - ngModel.$render = function() { - if($select.multiple){ - // 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){ - $select.selected = []; + return hasDupe; + } + function _findApproxDupe(haystack, needle) { + var dupeIndex = -1; + if(angular.isArray(haystack)) { + var tempArr = angular.copy(haystack); + for (var i = 0; i 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.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; + }); - if (!contains && !$select.clickTriggeredSelect) { - //Will lose focus only with certain targets - var focusableControls = ['input','button','textarea']; - var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select - var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $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 - $select.close(skipFocusser); - scope.$digest(); + //Update viewValue if model change + scope.$watch('$select.selected', function(newValue) { + if (ngModel.$viewValue !== newValue) { + ngModel.$setViewValue(newValue); } - $select.clickTriggeredSelect = false; - } + }); + + ngModel.$render = function() { + $select.selected = ngModel.$viewValue; + }; - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 - $document.on('click', onDocumentClick); + scope.$on('uis:select', function (event, item) { + $select.selected = item; + }); - scope.$on('$destroy', function() { - $document.off('click', onDocumentClick); + scope.$on('uis:close', function (event, skipFocusser) { + $timeout(function(){ + $select.focusser.prop('disabled', false); + if (!skipFocusser) $select.focusser[0].focus(); + },0,false); }); - // 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 + 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(""); + $compile(focusser)(scope); + $select.focusser = focusser; + + //Input that will handle focus + $select.focusInput = focusser; - // 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('
').append(clone); + 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){ - 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); + if (e.which === KEY.BACKSPACE) { + e.preventDefault(); + e.stopPropagation(); + $select.select(undefined); + scope.$apply(); + return; } - 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); + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; } - element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); - }); - } - }; -}]); -uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) { - return { - restrict: 'EA', - require: '^uiSelect', - replace: true, - transclude: true, - templateUrl: function(tElement) { - // Gets theme attribute from parent (ui-select) - var theme = tElement.parent().attr('theme') || uiSelectConfig.theme; - var multi = tElement.parent().attr('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; + 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(); }); - $select.allowClear = (angular.isDefined(attrs.allowClear)) ? (attrs.allowClear === '') ? true : (attrs.allowClear.toLowerCase() === 'true') : false; + 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(); + + }); - if($select.multiple){ - $select.sizeSearchInput(); - } } }; }]); - // Make multiple matches sortable uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', function($timeout, uiSelectConfig, uiSelectMinErr) { return { @@ -1547,7 +1664,10 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f element.on('drop', dropHandler); }); - element.on('dragleave', function() { + element.on('dragleave', function(e) { + if (e.target != element) { + return; + } element.removeClass(droppingClassName); element.removeClass(droppingBeforeClassName); element.removeClass(droppingAfterClassName); @@ -1559,16 +1679,67 @@ uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', f }; }]); +/** + * 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 = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?([\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); + + if (!match) { + throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", + expression); + } + + return { + itemName: match[2], // (lhs) Left-hand side, + source: $parse(match[3]), + trackByExp: match[4], + modelMapper: $parse(match[1] || match[2]) + }; + + }; + + self.getGroupNgRepeatExpression = function() { + return '$group in $select.groups'; + }; + + self.getNgRepeatExpression = function(itemName, source, trackByExp, grouped) { + var expression = itemName + ' in ' + (grouped ? '$group.items' : source); + if (trackByExp) { + expression += ' track by ' + trackByExp; + } + return expression; + }; +}]); + }()); angular.module("ui.select").run(["$templateCache", function($templateCache) {$templateCache.put("bootstrap/choices.tpl.html","
    0\">
  • 0\">
"); -$templateCache.put("bootstrap/match-multiple.tpl.html"," × "); +$templateCache.put("bootstrap/match-multiple.tpl.html"," × "); $templateCache.put("bootstrap/match.tpl.html","
{{$select.placeholder}}
"); -$templateCache.put("bootstrap/select-multiple.tpl.html","
"); +$templateCache.put("bootstrap/select-multiple.tpl.html","
"); $templateCache.put("bootstrap/select.tpl.html","
"); $templateCache.put("select2/choices.tpl.html","
"); -$templateCache.put("select2/match-multiple.tpl.html","
  • "); -$templateCache.put("select2/match.tpl.html","{{$select.placeholder}} "); -$templateCache.put("select2/select-multiple.tpl.html","
    "); +$templateCache.put("select2/match-multiple.tpl.html","
  • "); +$templateCache.put("select2/match.tpl.html","{{$select.placeholder}} "); +$templateCache.put("select2/select-multiple.tpl.html","
    "); $templateCache.put("select2/select.tpl.html","
    "); $templateCache.put("selectize/choices.tpl.html","
    "); $templateCache.put("selectize/match.tpl.html","
    "); diff --git a/dist/select.min.css b/dist/select.min.css index 522304493..0e4604733 100644 --- a/dist/select.min.css +++ b/dist/select.min.css @@ -1,6 +1,6 @@ /*! * ui-select * http://github.com/angular-ui/ui-select - * Version: 0.11.1 - 2015-03-09T14:30:26.112Z + * Version: 0.11.2 - 2015-03-17T04:08:46.478Z * License: MIT - */.ui-select-highlight{font-weight:700}.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:0!important;top:0!important}.ng-dirty.ng-invalid>a.select2-choice{border-color:#D44950}.select2-result-single{padding-left:0}.select-locked>.ui-select-match-close,.select2-locked>.select2-search-choice-close{display:none}.selectize-input.selectize-focus{border-color:#007FBB!important}.selectize-control>.selectize-dropdown,.selectize-control>.selectize-input>input{width:100%}.ng-dirty.ng-invalid>div.selectize-input{border-color:#D44950}.btn-default-focus{color:#333;background-color:#EBEBEB;border-color:#ADADAD;text-decoration:none;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.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}.input-group>.ui-select-bootstrap.dropdown{position:static}.input-group>.ui-select-bootstrap>input.ui-select-search.form-control{border-radius:4px 0 0 4px}.ui-select-bootstrap>.ui-select-match{text-align:left}.ui-select-bootstrap>.ui-select-match>.caret{position:absolute;top:45%;right:15px}.ui-select-bootstrap>.ui-select-choices{width:100%;height:auto;max-height:200px;overflow-x:hidden;margin-top:-1px}.ui-select-multiple.ui-select-bootstrap{height:auto;padding:3px 3px 0}.ui-select-multiple.ui-select-bootstrap input.ui-select-search{background-color:transparent!important;border:none;outline:0;height:1.666666em;margin-bottom:3px}.ui-select-multiple.ui-select-bootstrap .ui-select-match .close{font-size:1.6em;line-height:.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-before:before{content:"";position:absolute;top:0;right:100%;height:100%;margin-right:2px;border-left:1px solid #428bca}.ui-select-multiple .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>a{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>a:focus,.ui-select-bootstrap .ui-select-choices-row>a:hover{text-decoration:none;color:#262626;background-color:#f5f5f5}.ui-select-bootstrap .ui-select-choices-row.active>a{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.ui-select-bootstrap .ui-select-choices-row.active.disabled>a,.ui-select-bootstrap .ui-select-choices-row.disabled>a{color:#777;cursor:not-allowed;background-color:#fff}.ui-select-match.ng-hide-add,.ui-select-search.ng-hide-add{display:none!important}.ui-select-bootstrap.ng-dirty.ng-invalid>button.btn.ui-select-match{border-color:#D44950} \ No newline at end of file + */.ui-select-highlight{font-weight:700}.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:0!important;top:0!important}.ng-dirty.ng-invalid>a.select2-choice{border-color:#D44950}.select2-result-single{padding-left:0}.select-locked>.ui-select-match-close,.select2-locked>.select2-search-choice-close{display:none}body>.select2-container.open{z-index:9999}.selectize-input.selectize-focus{border-color:#007FBB!important}.selectize-control>.selectize-dropdown,.selectize-control>.selectize-input>input{width:100%}.ng-dirty.ng-invalid>div.selectize-input{border-color:#D44950}.btn-default-focus{color:#333;background-color:#EBEBEB;border-color:#ADADAD;text-decoration:none;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.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}.input-group>.ui-select-bootstrap.dropdown{position:static}.input-group>.ui-select-bootstrap>input.ui-select-search.form-control{border-radius:4px 0 0 4px}.ui-select-bootstrap>.ui-select-match>.btn{text-align:left!important}.ui-select-bootstrap>.ui-select-match>.caret{position:absolute;top:45%;right:15px}.ui-select-bootstrap>.ui-select-choices{width:100%;height:auto;max-height:200px;overflow-x:hidden;margin-top:-1px}body>.ui-select-bootstrap.open{z-index:1000}.ui-select-multiple.ui-select-bootstrap{height:auto;padding:3px 3px 0}.ui-select-multiple.ui-select-bootstrap input.ui-select-search{background-color:transparent!important;border:none;outline:0;height:1.666666em;margin-bottom:3px}.ui-select-multiple.ui-select-bootstrap .ui-select-match .close{font-size:1.6em;line-height:.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-before:before{content:"";position:absolute;top:0;right:100%;height:100%;margin-right:2px;border-left:1px solid #428bca}.ui-select-multiple .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>a{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>a:focus,.ui-select-bootstrap .ui-select-choices-row>a:hover{text-decoration:none;color:#262626;background-color:#f5f5f5}.ui-select-bootstrap .ui-select-choices-row.active>a{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.ui-select-bootstrap .ui-select-choices-row.active.disabled>a,.ui-select-bootstrap .ui-select-choices-row.disabled>a{color:#777;cursor:not-allowed;background-color:#fff}.ui-select-match.ng-hide-add,.ui-select-search.ng-hide-add{display:none!important}.ui-select-bootstrap.ng-dirty.ng-invalid>button.btn.ui-select-match{border-color:#D44950} \ No newline at end of file diff --git a/dist/select.min.js b/dist/select.min.js index e0d9a6725..7e50d794d 100644 --- a/dist/select.min.js +++ b/dist/select.min.js @@ -1,7 +1,8 @@ /*! * ui-select * http://github.com/angular-ui/ui-select - * Version: 0.11.1 - 2015-03-09T14:30:26.109Z + * Version: 0.11.2 - 2015-03-17T04:08:46.474Z * License: MIT */ -!function(){"use strict";var e={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(t){var c=t.which;switch(c){case e.COMMAND:case e.SHIFT:case e.CTRL:case e.ALT:return!0}return t.metaKey?!0:!1},isFunctionKey:function(e){return e=e.which?e.which:e,e>=112&&123>=e},isVerticalMovement:function(t){return~[e.UP,e.DOWN].indexOf(t)},isHorizontalMovement:function(t){return~[e.LEFT,e.RIGHT,e.BACKSPACE,e.DELETE].indexOf(t)}};void 0===angular.element.prototype.querySelectorAll&&(angular.element.prototype.querySelectorAll=function(e){return angular.element(this[0].querySelectorAll(e))}),void 0===angular.element.prototype.closest&&(angular.element.prototype.closest=function(e){for(var t=this[0],c=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.msMatchesSelector;t;){if(c.bind(t)(e))return t;t=t.parentElement}return!1});var t=0,c=angular.module("ui.select",[]).constant("uiSelectConfig",{theme:"bootstrap",searchEnabled:!0,sortable:!1,placeholder:"",refreshDelay:1e3,closeOnSelect:!0,generateId:function(){return t++}}).service("uiSelectMinErr",function(){var e=angular.$$minErr("ui.select");return function(){var t=e.apply(this,arguments),c=t.message.replace(new RegExp("\nhttp://errors.angularjs.org/.*"),"");return new Error(c)}}).directive("uisTranscludeAppend",function(){return{link:function(e,t,c,i,l){l(e,function(e){t.append(e)})}}}).filter("highlight",function(){function e(e){return e.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}return function(t,c){return c&&t?t.replace(new RegExp(e(c),"gi"),'$&'):t}});c.service("RepeatParser",["uiSelectMinErr","$parse",function(e,t){var c=this;c.parse=function(c){var i=c.match(/^\s*(?:([\s\S]+?)\s+as\s+)?([\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!i)throw e("iexp","Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",c);return{itemName:i[2],source:t(i[3]),trackByExp:i[4],modelMapper:t(i[1]||i[2])}},c.getGroupNgRepeatExpression=function(){return"$group in $select.groups"},c.getNgRepeatExpression=function(e,t,c,i){var l=e+" in "+(i?"$group.items":t);return c&&(l+=" track by "+c),l}}]),c.directive("uiSelectChoices",["uiSelectConfig","RepeatParser","uiSelectMinErr","$compile",function(e,t,c,i){return{restrict:"EA",require:"^uiSelect",replace:!0,transclude:!0,templateUrl:function(t){var c=t.parent().attr("theme")||e.theme;return c+"/choices.tpl.html"},compile:function(l,s){if(!s.repeat)throw c("repeat","Expected 'repeat' expression.");return function(l,s,a,n,r){var o=a.groupBy;if(n.parseRepeatAttr(a.repeat,o),n.disableChoiceExpression=a.uiDisableChoice,n.onHighlightCallback=a.onHighlight,o){var u=s.querySelectorAll(".ui-select-choices-group");if(1!==u.length)throw c("rows","Expected 1 .ui-select-choices-group but got '{0}'.",u.length);u.attr("ng-repeat",t.getGroupNgRepeatExpression())}var d=s.querySelectorAll(".ui-select-choices-row");if(1!==d.length)throw c("rows","Expected 1 .ui-select-choices-row but got '{0}'.",d.length);d.attr("ng-repeat",t.getNgRepeatExpression(n.parserResult.itemName,"$select.items",n.parserResult.trackByExp,o)).attr("ng-if","$select.open").attr("ng-mouseenter","$select.setActiveItem("+n.parserResult.itemName+")").attr("ng-click","$select.select("+n.parserResult.itemName+",false,$event)");var p=s.querySelectorAll(".ui-select-choices-row-inner");if(1!==p.length)throw c("rows","Expected 1 .ui-select-choices-row-inner but got '{0}'.",p.length);p.attr("uis-transclude-append",""),i(s,r)(l),l.$watch("$select.search",function(e){e&&!n.open&&n.multiple&&n.activate(!1,!0),n.activeIndex=n.tagging.isActivated?-1:0,n.refresh(a.refresh)}),a.$observe("refreshDelay",function(){var t=l.$eval(a.refreshDelay);n.refreshDelay=void 0!==t?t:e.refreshDelay})}}}}]),c.controller("uiSelectCtrl",["$scope","$element","$timeout","$filter","RepeatParser","uiSelectMinErr","uiSelectConfig",function(t,c,i,l,s,a,n){function r(){(f.resetSearchInput||void 0===f.resetSearchInput&&n.resetSearchInput)&&(f.search=v,f.selected&&f.items.length&&!f.multiple&&(f.activeIndex=f.items.indexOf(f.selected)))}function o(t){var c=!0;switch(t){case e.DOWN:!f.open&&f.multiple?f.activate(!1,!0):f.activeIndex0||0===f.search.length&&f.tagging.isActivated&&f.activeIndex>-1)&&f.activeIndex--;break;case e.TAB:(!f.multiple||f.open)&&f.select(f.items[f.activeIndex],!0);break;case e.ENTER:f.open&&f.activeIndex>=0?f.select(f.items[f.activeIndex]):f.activate(!1,!0);break;case e.ESC:f.close();break;default:c=!1}return c}function u(t){function c(){switch(t){case e.LEFT:return~f.activeMatchIndex?o:a;case e.RIGHT:return~f.activeMatchIndex&&n!==a?r:(f.activate(),!1);case e.BACKSPACE:return~f.activeMatchIndex?(f.removeChoice(n),o):a;case e.DELETE:return~f.activeMatchIndex?(f.removeChoice(f.activeMatchIndex),n):!1}}var i=g(m[0]),l=f.selected.length,s=0,a=l-1,n=f.activeMatchIndex,r=f.activeMatchIndex+1,o=f.activeMatchIndex-1,u=n;return i>0||f.search.length&&t==e.RIGHT?!1:(f.close(),u=c(),f.activeMatchIndex=f.selected.length&&u!==!1?Math.min(a,Math.max(s,u)):-1,!0)}function d(e){if(void 0===e||void 0===f.search)return!1;var t=e.filter(function(e){return void 0===f.search.toUpperCase()||void 0===e?!1:e.toUpperCase()===f.search.toUpperCase()}).length>0;return t}function p(e,t){var c=-1;if(angular.isArray(e))for(var i=angular.copy(e),l=0;ls?e[0].scrollTop+=l-s:l=f.items.length?0:f.activeIndex,-1===f.activeIndex&&f.taggingLabel!==!1&&(f.activeIndex=0),i(function(){f.search=e||f.search,m[0].focus()}))},f.findGroupByName=function(e){return f.groups&&f.groups.filter(function(t){return t.name===e})[0]},f.parseRepeatAttr=function(e,c){function i(e){f.groups=[],angular.forEach(e,function(e){var i=t.$eval(c),l=angular.isFunction(i)?i(e):e[i],s=f.findGroupByName(l);s?s.items.push(e):f.groups.push({name:l,items:[e]})}),f.items=[],f.groups.forEach(function(e){f.items=f.items.concat(e.items)})}function l(e){f.items=e}var n=c?i:l;f.parserResult=s.parse(e),f.isGrouped=!!c,f.itemProperty=f.parserResult.itemName,t.$watchCollection(f.parserResult.source,function(e){if(void 0===e||null===e)f.items=[];else{if(!angular.isArray(e))throw a("items","Expected an array but got '{0}'.",e);if(f.multiple){var t=e.filter(function(e){return f.selected.indexOf(e)<0});n(t)}else n(e);f.ngModel.$modelValue=null}}),f.multiple&&t.$watchCollection("$select.selected",function(e){var c=f.parserResult.source(t);if(e.length){if(void 0!==c){var i=c.filter(function(t){return e.indexOf(t)<0});n(i)}}else n(c);f.sizeSearchInput()})};var $;f.refresh=function(e){void 0!==e&&($&&i.cancel($),$=i(function(){t.$eval(e)},f.refreshDelay))},f.setActiveItem=function(e){f.activeIndex=f.items.indexOf(e)},f.isActive=function(e){if(!f.open)return!1;var t=f.items.indexOf(e[f.itemProperty]),c=t===f.activeIndex;return!c||0>t&&f.taggingLabel!==!1||0>t&&f.taggingLabel===!1?!1:(c&&!angular.isUndefined(f.onHighlightCallback)&&e.$eval(f.onHighlightCallback),c)},f.isDisabled=function(e){if(f.open){var t,c=f.items.indexOf(e[f.itemProperty]),i=!1;return c>=0&&!angular.isUndefined(f.disableChoiceExpression)&&(t=f.items[c],i=!!e.$eval(f.disableChoiceExpression),t._uiSelectChoiceDisabled=i),i}},f.select=function(e,c,l){if(void 0===e||!e._uiSelectChoiceDisabled){if(!f.items&&!f.search)return;if(!e||!e._uiSelectChoiceDisabled){if(f.tagging.isActivated){if(f.taggingLabel===!1)if(f.activeIndex<0){if(e=void 0!==f.tagging.fct?f.tagging.fct(f.search):f.search,!e||angular.equals(f.items[0],e))return}else e=f.items[f.activeIndex];else if(0===f.activeIndex){if(void 0===e)return;if(void 0!==f.tagging.fct&&"string"==typeof e){if(e=f.tagging.fct(f.search),!e)return}else"string"==typeof e&&(e=e.replace(f.taggingLabel,"").trim())}if(f.selected&&angular.isArray(f.selected)&&f.selected.filter(function(t){return angular.equals(t,e)}).length>0)return f.close(c),void 0}var s={};s[f.parserResult.itemName]=e,f.multiple?(f.selected.push(e),f.sizeSearchInput()):f.selected=e,i(function(){f.onSelectCallback(t,{$item:e,$model:f.parserResult.modelMapper(t,s)})}),(!f.multiple||f.closeOnSelect)&&f.close(c),l&&"click"===l.type&&(f.clickTriggeredSelect=!0)}}},f.close=function(e){f.open&&(f.ngModel&&f.ngModel.$setTouched&&f.ngModel.$setTouched(),r(),f.open=!1,f.multiple||i(function(){f.focusser.prop("disabled",!1),e||f.focusser[0].focus()},0,!1))},f.setFocus=function(){f.focus||f.multiple||f.focusser[0].focus(),!f.focus&&f.multiple&&m[0].focus()},f.clear=function(e){f.select(void 0),e.stopPropagation(),f.focusser[0].focus()},f.toggle=function(e){f.open?(f.close(),e.preventDefault(),e.stopPropagation()):f.activate()},f.isLocked=function(e,t){var c,i=f.selected[t];return i&&!angular.isUndefined(f.lockChoiceExpression)&&(c=!!e.$eval(f.lockChoiceExpression),i._uiSelectChoiceLocked=c),c},f.removeChoice=function(e){var c=f.selected[e];if(!c._uiSelectChoiceLocked){var l={};l[f.parserResult.itemName]=c,f.selected.splice(e,1),f.activeMatchIndex=-1,f.sizeSearchInput(),i(function(){f.onRemoveCallback(t,{$item:c,$model:f.parserResult.modelMapper(t,l)})})}},f.getPlaceholder=function(){return f.multiple&&f.selected.length?void 0:f.placeholder};var b=null;f.sizeSearchInput=function(){var e=m[0],c=m.parent().parent()[0],l=function(){return c.clientWidth*!!e.offsetParent},s=function(t){if(0===t)return!1;var c=t-e.offsetLeft-10;return 50>c&&(c=t),m.css("width",c+"px"),!0};m.css("width","10px"),i(function(){null!==b||s(l())||(b=t.$watch(l,function(e){s(e)&&(b(),b=null)}))})},m.on("keydown",function(c){var l=c.which;t.$apply(function(){var t=!1,s=!1;if(f.multiple&&e.isHorizontalMovement(l)&&(t=u(l)),!t&&(f.items.length>0||f.tagging.isActivated)&&(t=o(l),f.taggingTokens.isActivated)){for(var a=0;a0&&(s=!0);s&&i(function(){m.triggerHandler("tagged");var t=f.search.replace(e.MAP[c.keyCode],"").trim();f.tagging.fct&&(t=f.tagging.fct(t)),t&&f.select(t,!0)})}t&&l!=e.TAB&&(c.preventDefault(),c.stopPropagation())}),e.isVerticalMovement(l)&&f.items.length>0&&h()}),m.on("paste",function(e){var t=e.originalEvent.clipboardData.getData("text/plain");if(t&&t.length>0&&f.taggingTokens.isActivated&&f.tagging.fct){var c=t.split(f.taggingTokens.tokens[0]);c&&c.length>0&&(angular.forEach(c,function(e){var t=f.tagging.fct(e);t&&f.select(t,!0)}),e.preventDefault(),e.stopPropagation())}}),m.on("keyup",function(c){if(e.isVerticalMovement(c.which)||t.$evalAsync(function(){f.activeIndex=f.taggingLabel===!1?-1:0}),f.tagging.isActivated&&f.search.length>0){if(c.which===e.TAB||e.isControl(c)||e.isFunctionKey(c)||c.which===e.ESC||e.isVerticalMovement(c.which))return;if(f.activeIndex=f.taggingLabel===!1?-1:0,f.taggingLabel===!1)return;var i,l,s,a,n=angular.copy(f.items),r=angular.copy(f.items),o=!1,u=-1;if(void 0!==f.tagging.fct){if(s=f.$filter("filter")(n,{isTag:!0}),s.length>0&&(a=s[0]),n.length>0&&a&&(o=!0,n=n.slice(1,n.length),r=r.slice(1,r.length)),i=f.tagging.fct(f.search),i.isTag=!0,r.filter(function(e){return angular.equals(e,f.tagging.fct(f.search))}).length>0)return;i.isTag=!0}else{if(s=f.$filter("filter")(n,function(e){return e.match(f.taggingLabel)}),s.length>0&&(a=s[0]),l=n[0],void 0!==l&&n.length>0&&a&&(o=!0,n=n.slice(1,n.length),r=r.slice(1,r.length)),i=f.search+" "+f.taggingLabel,p(f.selected,f.search)>-1)return;if(d(r.concat(f.selected)))return o&&(n=r,t.$evalAsync(function(){f.activeIndex=0,f.items=n})),void 0;if(d(r))return o&&(f.items=r.slice(1,r.length)),void 0}o&&(u=p(f.selected,i)),u>-1?n=n.slice(u+1,n.length-1):(n=[],n.push(i),n=n.concat(r)),t.$evalAsync(function(){f.activeIndex=0,f.items=n})}}),m.on("tagged",function(){i(function(){r()})}),m.on("blur",function(){i(function(){f.activeMatchIndex=-1})}),t.$on("$destroy",function(){m.off("keyup keydown tagged blur paste")})}]),c.directive("uiSelect",["$document","uiSelectConfig","uiSelectMinErr","$compile","$parse","$timeout",function(t,c,i,l,s,a){return{restrict:"EA",templateUrl:function(e,t){var i=t.theme||c.theme;return i+(angular.isDefined(t.multiple)?"/select-multiple.tpl.html":"/select.tpl.html")},replace:!0,transclude:!0,require:["uiSelect","^ngModel"],scope:!0,controller:"uiSelectCtrl",controllerAs:"$select",link:function(n,r,o,u,d){function p(e){if(g.open){var t=!1;if(t=window.jQuery?window.jQuery.contains(r[0],e.target):r[0].contains(e.target),!t&&!g.clickTriggeredSelect){var c=["input","button","textarea"],i=angular.element(e.target).scope(),l=i&&i.$select&&i.$select!==g;l||(l=~c.indexOf(e.target.tagName.toLowerCase())),g.close(l),n.$digest()}g.clickTriggeredSelect=!1}}var g=u[0],h=u[1],f=r.querySelectorAll("input.ui-select-search");g.generatedId=c.generateId(),g.baseTitle=o.title||"Select box",g.focusserTitle=g.baseTitle+" focus",g.focusserId="focusser-"+g.generatedId,g.multiple=angular.isDefined(o.multiple)&&(""===o.multiple||"multiple"===o.multiple.toLowerCase()||"true"===o.multiple.toLowerCase()),g.closeOnSelect=function(){return angular.isDefined(o.closeOnSelect)?s(o.closeOnSelect)():c.closeOnSelect}(),g.onSelectCallback=s(o.onSelect),g.onRemoveCallback=s(o.onRemove),h.$parsers.unshift(function(e){var t,c={};if(g.multiple){for(var i=[],l=g.selected.length-1;l>=0;l--)c={},c[g.parserResult.itemName]=g.selected[l],t=g.parserResult.modelMapper(n,c),i.unshift(t);return i}return c={},c[g.parserResult.itemName]=e,t=g.parserResult.modelMapper(n,c)}),h.$formatters.unshift(function(e){var t,c=g.parserResult.source(n,{$select:{search:""}}),i={};if(c){if(g.multiple){var l=[],s=function(e,c){if(!e||!e.length)return l.unshift(c),!0;for(var s=e.length-1;s>=0;s--){if(i[g.parserResult.itemName]=e[s],t=g.parserResult.modelMapper(n,i),g.parserResult.trackByExp){var a=/\.(.+)/.exec(g.parserResult.trackByExp);if(a.length>0&&t[a[1]]==c[a[1]])return l.unshift(e[s]),!0}if(t==c)return l.unshift(e[s]),!0}return!1};if(!e)return l;for(var a=e.length-1;a>=0;a--)s(g.selected,e[a])||s(c,e[a]);return l}var r=function(c){return i[g.parserResult.itemName]=c,t=g.parserResult.modelMapper(n,i),t==e};if(g.selected&&r(g.selected))return g.selected;for(var o=c.length-1;o>=0;o--)if(r(c[o]))return c[o]}return e}),g.ngModel=h,g.choiceGrouped=function(e){return g.isGrouped&&e&&e.name};var v=angular.element("");o.tabindex&&o.$observe("tabindex",function(e){g.multiple?f.attr("tabindex",e):v.attr("tabindex",e),r.removeAttr("tabindex")}),l(v)(n),g.focusser=v,g.multiple||(r.append(v),v.bind("focus",function(){n.$evalAsync(function(){g.focus=!0})}),v.bind("blur",function(){n.$evalAsync(function(){g.focus=!1})}),v.bind("keydown",function(t){return t.which===e.BACKSPACE?(t.preventDefault(),t.stopPropagation(),g.select(void 0),n.$apply(),void 0):(t.which===e.TAB||e.isControl(t)||e.isFunctionKey(t)||t.which===e.ESC||((t.which==e.DOWN||t.which==e.UP||t.which==e.ENTER||t.which==e.SPACE)&&(t.preventDefault(),t.stopPropagation(),g.activate()),n.$digest()),void 0)}),v.bind("keyup input",function(t){t.which===e.TAB||e.isControl(t)||e.isFunctionKey(t)||t.which===e.ESC||t.which==e.ENTER||t.which===e.BACKSPACE||(g.activate(v.val()),v.val(""),n.$digest())})),n.$watch("searchEnabled",function(){var e=n.$eval(o.searchEnabled);g.searchEnabled=void 0!==e?e:c.searchEnabled}),n.$watch("sortable",function(){var e=n.$eval(o.sortable);g.sortable=void 0!==e?e:c.sortable}),o.$observe("disabled",function(){g.disabled=void 0!==o.disabled?o.disabled:!1,g.multiple&&g.sizeSearchInput()}),o.$observe("resetSearchInput",function(){var e=n.$eval(o.resetSearchInput);g.resetSearchInput=void 0!==e?e:!0}),o.$observe("tagging",function(){if(void 0!==o.tagging){var e=n.$eval(o.tagging);g.tagging={isActivated:!0,fct:e!==!0?e:void 0}}else g.tagging={isActivated:!1,fct:void 0}}),o.$observe("taggingLabel",function(){void 0!==o.tagging&&(g.taggingLabel="false"===o.taggingLabel?!1:void 0!==o.taggingLabel?o.taggingLabel:"(new)")}),o.$observe("taggingTokens",function(){if(void 0!==o.tagging){var e=void 0!==o.taggingTokens?o.taggingTokens.split("|"):[",","ENTER"];g.taggingTokens={isActivated:!0,tokens:e}}}),angular.isDefined(o.autofocus)&&a(function(){g.setFocus()}),angular.isDefined(o.focusOn)&&n.$on(o.focusOn,function(){a(function(){g.setFocus()})}),g.multiple?(n.$watchCollection(function(){return h.$modelValue},function(e,t){t!=e&&(h.$modelValue=null)}),g.firstPass=!0,n.$watchCollection("$select.selected",function(){g.firstPass?g.firstPass=!1:h.$setViewValue(Date.now())}),v.prop("disabled",!0)):n.$watch("$select.selected",function(e){h.$viewValue!==e&&h.$setViewValue(e)}),h.$render=function(){if(g.multiple&&!angular.isArray(h.$viewValue)){if(!angular.isUndefined(h.$viewValue)&&null!==h.$viewValue)throw i("multiarr","Expected model value to be array but got '{0}'",h.$viewValue);g.selected=[]}g.selected=h.$viewValue},t.on("click",p),n.$on("$destroy",function(){t.off("click",p)}),d(n,function(e){var t=angular.element("
    ").append(e),c=t.querySelectorAll(".ui-select-match");if(c.removeAttr("ui-select-match"),c.removeAttr("data-ui-select-match"),1!==c.length)throw i("transcluded","Expected 1 .ui-select-match but got '{0}'.",c.length);r.querySelectorAll(".ui-select-match").replaceWith(c);var l=t.querySelectorAll(".ui-select-choices");if(l.removeAttr("ui-select-choices"),l.removeAttr("data-ui-select-choices"),1!==l.length)throw i("transcluded","Expected 1 .ui-select-choices but got '{0}'.",l.length);r.querySelectorAll(".ui-select-choices").replaceWith(l)})}}}]),c.directive("uiSelectMatch",["uiSelectConfig",function(e){return{restrict:"EA",require:"^uiSelect",replace:!0,transclude:!0,templateUrl:function(t){var c=t.parent().attr("theme")||e.theme,i=t.parent().attr("multiple");return c+(i?"/match-multiple.tpl.html":"/match.tpl.html")},link:function(t,c,i,l){l.lockChoiceExpression=i.uiLockChoice,i.$observe("placeholder",function(t){l.placeholder=void 0!==t?t:e.placeholder}),l.allowClear=angular.isDefined(i.allowClear)?""===i.allowClear?!0:"true"===i.allowClear.toLowerCase():!1,l.multiple&&l.sizeSearchInput()}}}]),c.directive("uiSelectSort",["$timeout","uiSelectConfig","uiSelectMinErr",function(e,t,c){return{require:"^uiSelect",link:function(t,i,l,s){if(null===t[l.uiSelectSort])throw c("sort","Expected a list to sort");var a=angular.extend({axis:"horizontal"},t.$eval(l.uiSelectSortOptions)),n=a.axis,r="dragging",o="dropping",u="dropping-before",d="dropping-after";t.$watch(function(){return s.sortable},function(e){e?i.attr("draggable",!0):i.removeAttr("draggable")}),i.on("dragstart",function(e){i.addClass(r),(e.dataTransfer||e.originalEvent.dataTransfer).setData("text/plain",t.$index)}),i.on("dragend",function(){i.removeClass(r)});var p,g=function(e,t){this.splice(t,0,this.splice(e,1)[0])},h=function(e){e.preventDefault();var t="vertical"===n?e.offsetY||e.layerY||(e.originalEvent?e.originalEvent.offsetY:0):e.offsetX||e.layerX||(e.originalEvent?e.originalEvent.offsetX:0);t
  • '),e.put("bootstrap/match-multiple.tpl.html",' × '),e.put("bootstrap/match.tpl.html",'
    {{$select.placeholder}}
    '),e.put("bootstrap/select-multiple.tpl.html",''),e.put("bootstrap/select.tpl.html",''),e.put("select2/choices.tpl.html",'
    '),e.put("select2/match-multiple.tpl.html",'
  • '),e.put("select2/match.tpl.html",'{{$select.placeholder}} '),e.put("select2/select-multiple.tpl.html",'
    '),e.put("select2/select.tpl.html",'
    '),e.put("selectize/choices.tpl.html",'
    '),e.put("selectize/match.tpl.html",'
    '),e.put("selectize/select.tpl.html",'
    ')}]); \ No newline at end of file +!function(){"use strict";var e={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(t){var c=t.which;switch(c){case e.COMMAND:case e.SHIFT:case e.CTRL:case e.ALT:return!0}return t.metaKey?!0:!1},isFunctionKey:function(e){return e=e.which?e.which:e,e>=112&&123>=e},isVerticalMovement:function(t){return~[e.UP,e.DOWN].indexOf(t)},isHorizontalMovement:function(t){return~[e.LEFT,e.RIGHT,e.BACKSPACE,e.DELETE].indexOf(t)}};void 0===angular.element.prototype.querySelectorAll&&(angular.element.prototype.querySelectorAll=function(e){return angular.element(this[0].querySelectorAll(e))}),void 0===angular.element.prototype.closest&&(angular.element.prototype.closest=function(e){for(var t=this[0],c=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.msMatchesSelector;t;){if(c.bind(t)(e))return t;t=t.parentElement}return!1});var t=0,c=angular.module("ui.select",[]).constant("uiSelectConfig",{theme:"bootstrap",searchEnabled:!0,sortable:!1,placeholder:"",refreshDelay:1e3,closeOnSelect:!0,generateId:function(){return t++},appendToBody:!1}).service("uiSelectMinErr",function(){var e=angular.$$minErr("ui.select");return function(){var t=e.apply(this,arguments),c=t.message.replace(new RegExp("\nhttp://errors.angularjs.org/.*"),"");return new Error(c)}}).directive("uisTranscludeAppend",function(){return{link:function(e,t,c,i,s){s(e,function(e){t.append(e)})}}}).filter("highlight",function(){function e(e){return e.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}return function(t,c){return c&&t?t.replace(new RegExp(e(c),"gi"),'$&'):t}}).factory("uisOffset",["$document","$window",function(e,t){return function(c){var i=c[0].getBoundingClientRect();return{width:i.width||c.prop("offsetWidth"),height:i.height||c.prop("offsetHeight"),top:i.top+(t.pageYOffset||e[0].documentElement.scrollTop),left:i.left+(t.pageXOffset||e[0].documentElement.scrollLeft)}}}]);c.directive("uiSelectChoices",["uiSelectConfig","uisRepeatParser","uiSelectMinErr","$compile",function(e,t,c,i){return{restrict:"EA",require:"^uiSelect",replace:!0,transclude:!0,templateUrl:function(t){var c=t.parent().attr("theme")||e.theme;return c+"/choices.tpl.html"},compile:function(s,l){if(!l.repeat)throw c("repeat","Expected 'repeat' expression.");return function(s,l,n,a,r){var o=n.groupBy;if(a.parseRepeatAttr(n.repeat,o),a.disableChoiceExpression=n.uiDisableChoice,a.onHighlightCallback=n.onHighlight,o){var u=l.querySelectorAll(".ui-select-choices-group");if(1!==u.length)throw c("rows","Expected 1 .ui-select-choices-group but got '{0}'.",u.length);u.attr("ng-repeat",t.getGroupNgRepeatExpression())}var d=l.querySelectorAll(".ui-select-choices-row");if(1!==d.length)throw c("rows","Expected 1 .ui-select-choices-row but got '{0}'.",d.length);d.attr("ng-repeat",t.getNgRepeatExpression(a.parserResult.itemName,"$select.items",a.parserResult.trackByExp,o)).attr("ng-if","$select.open").attr("ng-mouseenter","$select.setActiveItem("+a.parserResult.itemName+")").attr("ng-click","$select.select("+a.parserResult.itemName+",false,$event)");var p=l.querySelectorAll(".ui-select-choices-row-inner");if(1!==p.length)throw c("rows","Expected 1 .ui-select-choices-row-inner but got '{0}'.",p.length);p.attr("uis-transclude-append",""),i(l,r)(s),s.$watch("$select.search",function(e){e&&!a.open&&a.multiple&&a.activate(!1,!0),a.activeIndex=a.tagging.isActivated?-1:0,a.refresh(n.refresh)}),n.$observe("refreshDelay",function(){var t=s.$eval(n.refreshDelay);a.refreshDelay=void 0!==t?t:e.refreshDelay})}}}}]),c.controller("uiSelectCtrl",["$scope","$element","$timeout","$filter","uisRepeatParser","uiSelectMinErr","uiSelectConfig",function(t,c,i,s,l,n,a){function r(){(d.resetSearchInput||void 0===d.resetSearchInput&&a.resetSearchInput)&&(d.search=p,d.selected&&d.items.length&&!d.multiple&&(d.activeIndex=d.items.indexOf(d.selected)))}function o(t){var c=!0;switch(t){case e.DOWN:!d.open&&d.multiple?d.activate(!1,!0):d.activeIndex0||0===d.search.length&&d.tagging.isActivated&&d.activeIndex>-1)&&d.activeIndex--;break;case e.TAB:(!d.multiple||d.open)&&d.select(d.items[d.activeIndex],!0);break;case e.ENTER:d.open&&d.activeIndex>=0?d.select(d.items[d.activeIndex]):d.activate(!1,!0);break;case e.ESC:d.close();break;default:c=!1}return c}function u(){var e=c.querySelectorAll(".ui-select-choices-content"),t=e.querySelectorAll(".ui-select-choices-row");if(t.length<1)throw n("choices","Expected multiple .ui-select-choices-row but got '{0}'.",t.length);if(!(d.activeIndex<0)){var i=t[d.activeIndex],s=i.offsetTop+i.clientHeight-e[0].scrollTop,l=e[0].offsetHeight;s>l?e[0].scrollTop+=s-l:s=d.items.length?0:d.activeIndex,-1===d.activeIndex&&d.taggingLabel!==!1&&(d.activeIndex=0),i(function(){d.search=e||d.search,d.searchInput[0].focus()}))},d.findGroupByName=function(e){return d.groups&&d.groups.filter(function(t){return t.name===e})[0]},d.parseRepeatAttr=function(e,c){function i(e){d.groups=[],angular.forEach(e,function(e){var i=t.$eval(c),s=angular.isFunction(i)?i(e):e[i],l=d.findGroupByName(s);l?l.items.push(e):d.groups.push({name:s,items:[e]})}),d.items=[],d.groups.forEach(function(e){d.items=d.items.concat(e.items)})}function s(e){d.items=e}d.setItemsFn=c?i:s,d.parserResult=l.parse(e),d.isGrouped=!!c,d.itemProperty=d.parserResult.itemName,d.refreshItems=function(e){e=e||d.parserResult.source(t);var c=d.selected;if(angular.isArray(c)&&!c.length||!d.removeSelected)d.setItemsFn(e);else if(void 0!==e){var i=e.filter(function(e){return c.indexOf(e)<0});d.setItemsFn(i)}},t.$watchCollection(d.parserResult.source,function(e){if(void 0===e||null===e)d.items=[];else{if(!angular.isArray(e))throw n("items","Expected an array but got '{0}'.",e);d.refreshItems(e),d.ngModel.$modelValue=null}})};var h;d.refresh=function(e){void 0!==e&&(h&&i.cancel(h),h=i(function(){t.$eval(e)},d.refreshDelay))},d.setActiveItem=function(e){d.activeIndex=d.items.indexOf(e)},d.isActive=function(e){if(!d.open)return!1;var t=d.items.indexOf(e[d.itemProperty]),c=t===d.activeIndex;return!c||0>t&&d.taggingLabel!==!1||0>t&&d.taggingLabel===!1?!1:(c&&!angular.isUndefined(d.onHighlightCallback)&&e.$eval(d.onHighlightCallback),c)},d.isDisabled=function(e){if(d.open){var t,c=d.items.indexOf(e[d.itemProperty]),i=!1;return c>=0&&!angular.isUndefined(d.disableChoiceExpression)&&(t=d.items[c],i=!!e.$eval(d.disableChoiceExpression),t._uiSelectChoiceDisabled=i),i}},d.select=function(e,c,s){if(void 0===e||!e._uiSelectChoiceDisabled){if(!d.items&&!d.search)return;if(!e||!e._uiSelectChoiceDisabled){if(d.tagging.isActivated){if(d.taggingLabel===!1)if(d.activeIndex<0){if(e=void 0!==d.tagging.fct?d.tagging.fct(d.search):d.search,!e||angular.equals(d.items[0],e))return}else e=d.items[d.activeIndex];else if(0===d.activeIndex){if(void 0===e)return;if(void 0!==d.tagging.fct&&"string"==typeof e){if(e=d.tagging.fct(d.search),!e)return}else"string"==typeof e&&(e=e.replace(d.taggingLabel,"").trim())}if(d.selected&&angular.isArray(d.selected)&&d.selected.filter(function(t){return angular.equals(t,e)}).length>0)return d.close(c),void 0}t.$broadcast("uis:select",e);var l={};l[d.parserResult.itemName]=e,i(function(){d.onSelectCallback(t,{$item:e,$model:d.parserResult.modelMapper(t,l)})}),d.closeOnSelect&&d.close(c),s&&"click"===s.type&&(d.clickTriggeredSelect=!0)}}},d.close=function(e){d.open&&(d.ngModel&&d.ngModel.$setTouched&&d.ngModel.$setTouched(),r(),d.open=!1,t.$broadcast("uis:close",e))},d.setFocus=function(){d.focus||d.focusInput[0].focus()},d.clear=function(e){d.select(void 0),e.stopPropagation(),d.focusser[0].focus()},d.toggle=function(e){d.open?(d.close(),e.preventDefault(),e.stopPropagation()):d.activate()},d.isLocked=function(e,t){var c,i=d.selected[t];return i&&!angular.isUndefined(d.lockChoiceExpression)&&(c=!!e.$eval(d.lockChoiceExpression),i._uiSelectChoiceLocked=c),c};var g=null;d.sizeSearchInput=function(){var e=d.searchInput[0],c=d.searchInput.parent().parent()[0],s=function(){return c.clientWidth*!!e.offsetParent},l=function(t){if(0===t)return!1;var c=t-e.offsetLeft-10;return 50>c&&(c=t),d.searchInput.css("width",c+"px"),!0};d.searchInput.css("width","10px"),i(function(){null!==g||l(s())||(g=t.$watch(s,function(e){l(e)&&(g(),g=null)}))})},d.searchInput.on("keydown",function(c){var s=c.which;t.$apply(function(){var t=!1;if((d.items.length>0||d.tagging.isActivated)&&(o(s),d.taggingTokens.isActivated)){for(var l=0;l0&&(t=!0);t&&i(function(){d.searchInput.triggerHandler("tagged");var t=d.search.replace(e.MAP[c.keyCode],"").trim();d.tagging.fct&&(t=d.tagging.fct(t)),t&&d.select(t,!0)})}}),e.isVerticalMovement(s)&&d.items.length>0&&u()}),d.searchInput.on("paste",function(e){var t=e.originalEvent.clipboardData.getData("text/plain");if(t&&t.length>0&&d.taggingTokens.isActivated&&d.tagging.fct){var c=t.split(d.taggingTokens.tokens[0]);c&&c.length>0&&(angular.forEach(c,function(e){var t=d.tagging.fct(e);t&&d.select(t,!0)}),e.preventDefault(),e.stopPropagation())}}),d.searchInput.on("tagged",function(){i(function(){r()})}),t.$on("$destroy",function(){d.searchInput.off("keyup keydown tagged blur paste")})}]),c.directive("uiSelect",["$document","uiSelectConfig","uiSelectMinErr","uisOffset","$compile","$parse","$timeout",function(e,t,c,i,s,l,n){return{restrict:"EA",templateUrl:function(e,c){var i=c.theme||t.theme;return i+(angular.isDefined(c.multiple)?"/select-multiple.tpl.html":"/select.tpl.html")},replace:!0,transclude:!0,require:["uiSelect","^ngModel"],scope:!0,controller:"uiSelectCtrl",controllerAs:"$select",compile:function(s,a){return angular.isDefined(a.multiple)?s.append("").removeAttr("multiple"):s.append(""),function(s,a,r,o,u){function d(e){if(g.open){var t=!1;if(t=window.jQuery?window.jQuery.contains(a[0],e.target):a[0].contains(e.target),!t&&!g.clickTriggeredSelect){var c=["input","button","textarea"],i=angular.element(e.target).scope(),l=i&&i.$select&&i.$select!==g;l||(l=~c.indexOf(e.target.tagName.toLowerCase())),g.close(l),s.$digest()}g.clickTriggeredSelect=!1}}function p(){var t=i(a);m=angular.element('
    '),m[0].style.width=t.width+"px",m[0].style.height=t.height+"px",a.after(m),$=a[0].style.width,e.find("body").append(a),a[0].style.position="absolute",a[0].style.left=t.left+"px",a[0].style.top=t.top+"px",a[0].style.width=t.width+"px"}function h(){null!==m&&(m.replaceWith(a),m=null,a[0].style.position="",a[0].style.left="",a[0].style.top="",a[0].style.width=$)}var g=o[0],f=o[1];g.generatedId=t.generateId(),g.baseTitle=r.title||"Select box",g.focusserTitle=g.baseTitle+" focus",g.focusserId="focusser-"+g.generatedId,g.closeOnSelect=function(){return angular.isDefined(r.closeOnSelect)?l(r.closeOnSelect)():t.closeOnSelect}(),g.onSelectCallback=l(r.onSelect),g.onRemoveCallback=l(r.onRemove),g.ngModel=f,g.choiceGrouped=function(e){return g.isGrouped&&e&&e.name},r.tabindex&&r.$observe("tabindex",function(e){g.focusInput.attr("tabindex",e),a.removeAttr("tabindex")}),s.$watch("searchEnabled",function(){var e=s.$eval(r.searchEnabled);g.searchEnabled=void 0!==e?e:t.searchEnabled}),s.$watch("sortable",function(){var e=s.$eval(r.sortable);g.sortable=void 0!==e?e:t.sortable}),r.$observe("disabled",function(){g.disabled=void 0!==r.disabled?r.disabled:!1}),r.$observe("resetSearchInput",function(){var e=s.$eval(r.resetSearchInput);g.resetSearchInput=void 0!==e?e:!0}),r.$observe("tagging",function(){if(void 0!==r.tagging){var e=s.$eval(r.tagging);g.tagging={isActivated:!0,fct:e!==!0?e:void 0}}else g.tagging={isActivated:!1,fct:void 0}}),r.$observe("taggingLabel",function(){void 0!==r.tagging&&(g.taggingLabel="false"===r.taggingLabel?!1:void 0!==r.taggingLabel?r.taggingLabel:"(new)")}),r.$observe("taggingTokens",function(){if(void 0!==r.tagging){var e=void 0!==r.taggingTokens?r.taggingTokens.split("|"):[",","ENTER"];g.taggingTokens={isActivated:!0,tokens:e}}}),angular.isDefined(r.autofocus)&&n(function(){g.setFocus()}),angular.isDefined(r.focusOn)&&s.$on(r.focusOn,function(){n(function(){g.setFocus()})}),e.on("click",d),s.$on("$destroy",function(){e.off("click",d)}),u(s,function(e){var t=angular.element("
    ").append(e),i=t.querySelectorAll(".ui-select-match");if(i.removeAttr("ui-select-match"),i.removeAttr("data-ui-select-match"),1!==i.length)throw c("transcluded","Expected 1 .ui-select-match but got '{0}'.",i.length);a.querySelectorAll(".ui-select-match").replaceWith(i);var s=t.querySelectorAll(".ui-select-choices");if(s.removeAttr("ui-select-choices"),s.removeAttr("data-ui-select-choices"),1!==s.length)throw c("transcluded","Expected 1 .ui-select-choices but got '{0}'.",s.length);a.querySelectorAll(".ui-select-choices").replaceWith(s)});var v=s.$eval(r.appendToBody);(void 0!==v?v:t.appendToBody)&&(s.$watch("$select.open",function(e){e?p():h()}),s.$on("$destroy",function(){h()}));var m=null,$=""}}}}]),c.directive("uiSelectMatch",["uiSelectConfig",function(e){return{restrict:"EA",require:"^uiSelect",replace:!0,transclude:!0,templateUrl:function(t){var c=t.parent().attr("theme")||e.theme,i=t.parent().attr("multiple");return c+(i?"/match-multiple.tpl.html":"/match.tpl.html")},link:function(t,c,i,s){function l(e){s.allowClear=angular.isDefined(e)?""===e?!0:"true"===e.toLowerCase():!1}s.lockChoiceExpression=i.uiLockChoice,i.$observe("placeholder",function(t){s.placeholder=void 0!==t?t:e.placeholder}),i.$observe("allowClear",l),l(i.allowClear),s.multiple&&s.sizeSearchInput()}}}]),c.directive("uiSelectMultiple",["uiSelectMinErr","$timeout",function(t,c){return{restrict:"EA",require:["^uiSelect","^ngModel"],controller:["$scope","$timeout",function(e,t){var c,i=this,s=e.$select;e.$evalAsync(function(){c=e.ngModel}),i.activeMatchIndex=-1,i.updateModel=function(){c.$setViewValue(Date.now()),i.refreshComponent()},i.refreshComponent=function(){s.refreshItems(),s.sizeSearchInput()},i.removeChoice=function(c){var l=s.selected[c];if(!l._uiSelectChoiceLocked){var n={};n[s.parserResult.itemName]=l,s.selected.splice(c,1),i.activeMatchIndex=-1,s.sizeSearchInput(),t(function(){s.onRemoveCallback(e,{$item:l,$model:s.parserResult.modelMapper(e,n)})}),i.updateModel()}},i.getPlaceholder=function(){return s.selected.length?void 0:s.placeholder}}],controllerAs:"$selectMultiple",link:function(i,s,l,n){function a(e){return angular.isNumber(e.selectionStart)?e.selectionStart:e.value.length}function r(t){function c(){switch(t){case e.LEFT:return~h.activeMatchIndex?u:n;case e.RIGHT:return~h.activeMatchIndex&&r!==n?o:(d.activate(),!1);case e.BACKSPACE:return~h.activeMatchIndex?(h.removeChoice(r),u):n;case e.DELETE:return~h.activeMatchIndex?(h.removeChoice(h.activeMatchIndex),r):!1}}var i=a(d.searchInput[0]),s=d.selected.length,l=0,n=s-1,r=h.activeMatchIndex,o=h.activeMatchIndex+1,u=h.activeMatchIndex-1,p=r;return i>0||d.search.length&&t==e.RIGHT?!1:(d.close(),p=c(),h.activeMatchIndex=d.selected.length&&p!==!1?Math.min(n,Math.max(l,p)):-1,!0)}function o(e){if(void 0===e||void 0===d.search)return!1;var t=e.filter(function(e){return void 0===d.search.toUpperCase()||void 0===e?!1:e.toUpperCase()===d.search.toUpperCase()}).length>0;return t}function u(e,t){var c=-1;if(angular.isArray(e))for(var i=angular.copy(e),s=0;s=0;s--)t={},t[d.parserResult.itemName]=d.selected[s],e=d.parserResult.modelMapper(i,t),c.unshift(e);return c}),p.$formatters.unshift(function(e){var t,c=d.parserResult.source(i,{$select:{search:""}}),s={};if(!c)return e;var l=[],n=function(e,c){if(e&&e.length){for(var n=e.length-1;n>=0;n--){if(s[d.parserResult.itemName]=e[n],t=d.parserResult.modelMapper(i,s),d.parserResult.trackByExp){var a=/\.(.+)/.exec(d.parserResult.trackByExp);if(a.length>0&&t[a[1]]==c[a[1]])return l.unshift(e[n]),!0}if(angular.equals(t,c))return l.unshift(e[n]),!0}return!1}};if(!e)return l;for(var a=e.length-1;a>=0;a--)n(d.selected,e[a])||n(c,e[a])||l.unshift(e[a]);return l}),i.$watchCollection(function(){return p.$modelValue},function(e,t){t!=e&&(p.$modelValue=null,h.refreshComponent())}),p.$render=function(){if(!angular.isArray(p.$viewValue)){if(!angular.isUndefined(p.$viewValue)&&null!==p.$viewValue)throw t("multiarr","Expected model value to be array but got '{0}'",p.$viewValue);d.selected=[]}d.selected=p.$viewValue,i.$evalAsync()},i.$on("uis:select",function(e,t){d.selected.push(t),h.updateModel()}),i.$on("uis:activate",function(){h.activeMatchIndex=-1}),i.$watch("$select.disabled",function(e,t){t&&!e&&d.sizeSearchInput()}),d.searchInput.on("keydown",function(t){var c=t.which;i.$apply(function(){var i=!1;e.isHorizontalMovement(c)&&(i=r(c)),i&&c!=e.TAB&&(t.preventDefault(),t.stopPropagation())})}),d.searchInput.on("keyup",function(t){if(e.isVerticalMovement(t.which)||i.$evalAsync(function(){d.activeIndex=d.taggingLabel===!1?-1:0}),d.tagging.isActivated&&d.search.length>0){if(t.which===e.TAB||e.isControl(t)||e.isFunctionKey(t)||t.which===e.ESC||e.isVerticalMovement(t.which))return;if(d.activeIndex=d.taggingLabel===!1?-1:0,d.taggingLabel===!1)return;var c,s,l,n,a=angular.copy(d.items),r=angular.copy(d.items),p=!1,h=-1;if(void 0!==d.tagging.fct){if(l=d.$filter("filter")(a,{isTag:!0}),l.length>0&&(n=l[0]),a.length>0&&n&&(p=!0,a=a.slice(1,a.length),r=r.slice(1,r.length)),c=d.tagging.fct(d.search),c.isTag=!0,r.filter(function(e){return angular.equals(e,d.tagging.fct(d.search))}).length>0)return;c.isTag=!0}else{if(l=d.$filter("filter")(a,function(e){return e.match(d.taggingLabel)}),l.length>0&&(n=l[0]),s=a[0],void 0!==s&&a.length>0&&n&&(p=!0,a=a.slice(1,a.length),r=r.slice(1,r.length)),c=d.search+" "+d.taggingLabel,u(d.selected,d.search)>-1)return;if(o(r.concat(d.selected)))return p&&(a=r,i.$evalAsync(function(){d.activeIndex=0,d.items=a})),void 0;if(o(r))return p&&(d.items=r.slice(1,r.length)),void 0}p&&(h=u(d.selected,c)),h>-1?a=a.slice(h+1,a.length-1):(a=[],a.push(c),a=a.concat(r)),i.$evalAsync(function(){d.activeIndex=0,d.items=a})}}),d.searchInput.on("blur",function(){c(function(){h.activeMatchIndex=-1})})}}}]),c.directive("uiSelectSingle",["$timeout","$compile",function(t,c){return{restrict:"EA",require:["^uiSelect","^ngModel"],link:function(i,s,l,n){var a=n[0],r=n[1];r.$parsers.unshift(function(e){var t,c={};return c[a.parserResult.itemName]=e,t=a.parserResult.modelMapper(i,c)}),r.$formatters.unshift(function(e){var t,c=a.parserResult.source(i,{$select:{search:""}}),s={};if(c){var l=function(c){return s[a.parserResult.itemName]=c,t=a.parserResult.modelMapper(i,s),t==e};if(a.selected&&l(a.selected))return a.selected;for(var n=c.length-1;n>=0;n--)if(l(c[n]))return c[n]}return e}),i.$watch("$select.selected",function(e){r.$viewValue!==e&&r.$setViewValue(e)}),r.$render=function(){a.selected=r.$viewValue},i.$on("uis:select",function(e,t){a.selected=t}),i.$on("uis:close",function(e,c){t(function(){a.focusser.prop("disabled",!1),c||a.focusser[0].focus()},0,!1)}),i.$on("uis:activate",function(){o.prop("disabled",!0)});var o=angular.element("");c(o)(i),a.focusser=o,a.focusInput=o,s.parent().append(o),o.bind("focus",function(){i.$evalAsync(function(){a.focus=!0})}),o.bind("blur",function(){i.$evalAsync(function(){a.focus=!1})}),o.bind("keydown",function(t){return t.which===e.BACKSPACE?(t.preventDefault(),t.stopPropagation(),a.select(void 0),i.$apply(),void 0):(t.which===e.TAB||e.isControl(t)||e.isFunctionKey(t)||t.which===e.ESC||((t.which==e.DOWN||t.which==e.UP||t.which==e.ENTER||t.which==e.SPACE)&&(t.preventDefault(),t.stopPropagation(),a.activate()),i.$digest()),void 0)}),o.bind("keyup input",function(t){t.which===e.TAB||e.isControl(t)||e.isFunctionKey(t)||t.which===e.ESC||t.which==e.ENTER||t.which===e.BACKSPACE||(a.activate(o.val()),o.val(""),i.$digest())})}}}]),c.directive("uiSelectSort",["$timeout","uiSelectConfig","uiSelectMinErr",function(e,t,c){return{require:"^uiSelect",link:function(t,i,s,l){if(null===t[s.uiSelectSort])throw c("sort","Expected a list to sort");var n=angular.extend({axis:"horizontal"},t.$eval(s.uiSelectSortOptions)),a=n.axis,r="dragging",o="dropping",u="dropping-before",d="dropping-after";t.$watch(function(){return l.sortable},function(e){e?i.attr("draggable",!0):i.removeAttr("draggable")}),i.on("dragstart",function(e){i.addClass(r),(e.dataTransfer||e.originalEvent.dataTransfer).setData("text/plain",t.$index)}),i.on("dragend",function(){i.removeClass(r)});var p,h=function(e,t){this.splice(t,0,this.splice(e,1)[0])},g=function(e){e.preventDefault();var t="vertical"===a?e.offsetY||e.layerY||(e.originalEvent?e.originalEvent.offsetY:0):e.offsetX||e.layerX||(e.originalEvent?e.originalEvent.offsetX:0);t
  • '),e.put("bootstrap/match-multiple.tpl.html",' × '),e.put("bootstrap/match.tpl.html",'
    {{$select.placeholder}}
    '),e.put("bootstrap/select-multiple.tpl.html",''),e.put("bootstrap/select.tpl.html",''),e.put("select2/choices.tpl.html",'
    '),e.put("select2/match-multiple.tpl.html",'
  • '),e.put("select2/match.tpl.html",'{{$select.placeholder}} '),e.put("select2/select-multiple.tpl.html",'
    '),e.put("select2/select.tpl.html",'
    '),e.put("selectize/choices.tpl.html",'
    '),e.put("selectize/match.tpl.html",'
    '),e.put("selectize/select.tpl.html",'
    ') +}]); \ No newline at end of file diff --git a/package.json b/package.json index 7c03e5ddd..d3779da94 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "repository": { "url": "git://github.com/angular-ui/ui-select.git" }, - "version": "0.11.1", + "version": "0.11.2", "devDependencies": { "bower": "~1.3", "del": "~0.1.1",