vendor: 27,993 bytes, lines 1-775
1/*! 2 * ui-select 3 * http://github.com/angular-ui/ui-select 4 * Version: 0.19.3 - 2016-08-17T06:16:41.345Z 5 * License: MIT 6 */ 7 8 9(function () { 10"use strict"; 11var KEY = { 12 TAB: 9, 13 ENTER: 13, 14 ESC: 27, 15 SPACE: 32, 16 LEFT: 37, 17 UP: 38, 18 RIGHT: 39, 19 DOWN: 40, 20 SHIFT: 16, 21 CTRL: 17, 22 ALT: 18, 23 PAGE_UP: 33, 24 PAGE_DOWN: 34, 25 HOME: 36, 26 END: 35, 27 BACKSPACE: 8, 28 DELETE: 46, 29 COMMAND: 91, 30 31 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 : "'" 32 }, 33 34 isControl: function (e) { 35 var k = e.which; 36 switch (k) { 37 case KEY.COMMAND: 38 case KEY.SHIFT: 39 case KEY.CTRL: 40 case KEY.ALT: 41 return true; 42 } 43 44 if (e.metaKey || e.ctrlKey || e.altKey) return true; 45 46 return false; 47 }, 48 isFunctionKey: function (k) { 49 k = k.which ? k.which : k; 50 return k >= 112 && k <= 123; 51 }, 52 isVerticalMovement: function (k){ 53 return ~[KEY.UP, KEY.DOWN].indexOf(k); 54 }, 55 isHorizontalMovement: function (k){ 56 return ~[KEY.LEFT,KEY.RIGHT,KEY.BACKSPACE,KEY.DELETE].indexOf(k); 57 }, 58 toSeparator: function (k) { 59 var sep = {ENTER:"\n",TAB:"\t",SPACE:" "}[k]; 60 if (sep) return sep; 61 // return undefined for special keys other than enter, tab or space. 62 // no way to use them to cut strings. 63 return KEY[k] ? undefined : k; 64 } 65 }; 66 67/** 68 * Add querySelectorAll() to jqLite. 69 * 70 * jqLite find() is limited to lookups by tag name. 71 * TODO This will change with future versions of AngularJS, to be removed when this happens 72 * 73 * See jqLite.find - why not use querySelectorAll? https://github.com/angular/angular.js/issues/3586 74 * See feat(jqLite): use querySelectorAll instead of getElementsByTagName in jqLite.find https://github.com/angular/angular.js/pull/3598 75 */ 76if (angular.element.prototype.querySelectorAll === undefined) { 77 angular.element.prototype.querySelectorAll = function(selector) { 78 return angular.element(this[0].querySelectorAll(selector)); 79 }; 80} 81 82/** 83 * Add closest() to jqLite. 84 */ 85if (angular.element.prototype.closest === undefined) { 86 angular.element.prototype.closest = function( selector) { 87 var elem = this[0]; 88 var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector; 89 90 while (elem) { 91 if (matchesSelector.bind(elem)(selector)) { 92 return elem; 93 } else { 94 elem = elem.parentElement; 95 } 96 } 97 return false; 98 }; 99} 100 101var latestId = 0; 102 103var uis = angular.module('ui.select', []) 104 105.constant('uiSelectConfig', { 106 theme: 'bootstrap', 107 searchEnabled: true, 108 sortable: false, 109 placeholder: '', // Empty by default, like HTML tag <select> 110 refreshDelay: 1000, // In milliseconds 111 closeOnSelect: true, 112 skipFocusser: false, 113 dropdownPosition: 'auto', 114 removeSelected: true, 115 resetSearchInput: true, 116 generateId: function() { 117 return latestId++; 118 }, 119 appendToBody: false 120}) 121 122// See Rename minErr and make it accessible from outside https://github.com/angular/angular.js/issues/6913 123.service('uiSelectMinErr', function() { 124 var minErr = angular.$$minErr('ui.select'); 125 return function() { 126 var error = minErr.apply(this, arguments); 127 var message = error.message.replace(new RegExp('\nhttp://errors.angularjs.org/.*'), ''); 128 return new Error(message); 129 }; 130}) 131 132// Recreates old behavior of ng-transclude. Used internally. 133.directive('uisTranscludeAppend', function () { 134 return { 135 link: function (scope, element, attrs, ctrl, transclude) { 136 transclude(scope, function (clone) { 137 element.append(clone); 138 }); 139 } 140 }; 141}) 142 143/** 144 * Highlights text that matches $select.search. 145 * 146 * Taken from AngularUI Bootstrap Typeahead 147 * See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L340 148 */ 149.filter('highlight', function() { 150 function escapeRegexp(queryToEscape) { 151 return ('' + queryToEscape).replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); 152 } 153 154 return function(matchItem, query) { 155 return query && matchItem ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<span class="ui-select-highlight">$&</span>') : matchItem; 156 }; 157}) 158 159/** 160 * A read-only equivalent of jQuery's offset function: http://api.jquery.com/offset/ 161 * 162 * Taken from AngularUI Bootstrap Position: 163 * See https://github.com/angular-ui/bootstrap/blob/master/src/position/position.js#L70 164 */ 165.factory('uisOffset', 166 ['$document', '$window', 167 function ($document, $window) { 168 169 return function(element) { 170 var boundingClientRect = element[0].getBoundingClientRect(); 171 return { 172 width: boundingClientRect.width || element.prop('offsetWidth'), 173 height: boundingClientRect.height || element.prop('offsetHeight'), 174 top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), 175 left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) 176 }; 177 }; 178}]); 179 180uis.directive('uiSelectChoices', 181 ['uiSelectConfig', 'uisRepeatParser', 'uiSelectMinErr', '$compile', '$window', 182 function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile, $window) { 183 184 return { 185 restrict: 'EA', 186 require: '^uiSelect', 187 replace: true, 188 transclude: true, 189 templateUrl: function(tElement) { 190 // Needed so the uiSelect can detect the transcluded content 191 tElement.addClass('ui-select-choices'); 192 193 // Gets theme attribute from parent (ui-select) 194 var theme = tElement.parent().attr('theme') || uiSelectConfig.theme; 195 return theme + '/choices.tpl.html'; 196 }, 197 198 compile: function(tElement, tAttrs) { 199 200 if (!tAttrs.repeat) throw uiSelectMinErr('repeat', "Expected 'repeat' expression."); 201 202 // var repeat = RepeatParser.parse(attrs.repeat); 203 var groupByExp = tAttrs.groupBy; 204 var groupFilterExp = tAttrs.groupFilter; 205 206 if (groupByExp) { 207 var groups = tElement.querySelectorAll('.ui-select-choices-group'); 208 if (groups.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-group but got '{0}'.", groups.length); 209 groups.attr('ng-repeat', RepeatParser.getGroupNgRepeatExpression()); 210 } 211 212 var parserResult = RepeatParser.parse(tAttrs.repeat); 213 214 var choices = tElement.querySelectorAll('.ui-select-choices-row'); 215 if (choices.length !== 1) { 216 throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length); 217 } 218 219 choices.attr('ng-repeat', parserResult.repeatExpression(groupByExp)) 220 .attr('ng-if', '$select.open'); //Prevent unnecessary watches when dropdown is closed 221 222 223 var rowsInner = tElement.querySelectorAll('.ui-select-choices-row-inner'); 224 if (rowsInner.length !== 1) { 225 throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row-inner but got '{0}'.", rowsInner.length); 226 } 227 rowsInner.attr('uis-transclude-append', ''); //Adding uisTranscludeAppend directive to row element after choices element has ngRepeat 228 229 // If IE8 then need to target rowsInner to apply the ng-click attr as choices will not capture the event. 230 var clickTarget = $window.document.addEventListener ? choices : rowsInner; 231 clickTarget.attr('ng-click', '$select.select(' + parserResult.itemName + ',$select.skipFocusser,$event)'); 232 233 return function link(scope, element, attrs, $select) { 234 235 236 $select.parseRepeatAttr(attrs.repeat, groupByExp, groupFilterExp); //Result ready at $select.parserResult 237 238 $select.disableChoiceExpression = attrs.uiDisableChoice; 239 $select.onHighlightCallback = attrs.onHighlight; 240 241 $select.dropdownPosition = attrs.position ? attrs.position.toLowerCase() : uiSelectConfig.dropdownPosition; 242 243 scope.$on('$destroy', function() { 244 choices.remove(); 245 }); 246 247 scope.$watch('$select.search', function(newValue) { 248 if(newValue && !$select.open && $select.multiple) $select.activate(false, true); 249 $select.activeIndex = $select.tagging.isActivated ? -1 : 0; 250 if (!attrs.minimumInputLength || $select.search.length >= attrs.minimumInputLength) { 251 $select.refresh(attrs.refresh); 252 } else { 253 $select.items = []; 254 } 255 }); 256 257 attrs.$observe('refreshDelay', function() { 258 // $eval() is needed otherwise we get a string instead of a number 259 var refreshDelay = scope.$eval(attrs.refreshDelay); 260 $select.refreshDelay = refreshDelay !== undefined ? refreshDelay : uiSelectConfig.refreshDelay; 261 }); 262 }; 263 } 264 }; 265}]); 266 267/** 268 * Contains ui-select "intelligence". 269 * 270 * The goal is to limit dependency on the DOM whenever possible and 271 * put as much logic in the controller (instead of the link functions) as possible so it can be easily tested. 272 */ 273uis.controller('uiSelectCtrl', 274 ['$scope', '$element', '$timeout', '$filter', '$$uisDebounce', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig', '$parse', '$injector', '$window', 275 function($scope, $element, $timeout, $filter, $$uisDebounce, RepeatParser, uiSelectMinErr, uiSelectConfig, $parse, $injector, $window) { 276 277 var ctrl = this; 278 279 var EMPTY_SEARCH = ''; 280 281 ctrl.placeholder = uiSelectConfig.placeholder; 282 ctrl.searchEnabled = uiSelectConfig.searchEnabled; 283 ctrl.sortable = uiSelectConfig.sortable; 284 ctrl.refreshDelay = uiSelectConfig.refreshDelay; 285 ctrl.paste = uiSelectConfig.paste; 286 ctrl.resetSearchInput = uiSelectConfig.resetSearchInput; 287 288 ctrl.removeSelected = uiSelectConfig.removeSelected; //If selected item(s) should be removed from dropdown list 289 ctrl.closeOnSelect = true; //Initialized inside uiSelect directive link function 290 ctrl.skipFocusser = false; //Set to true to avoid returning focus to ctrl when item is selected 291 ctrl.search = EMPTY_SEARCH; 292 293 ctrl.activeIndex = 0; //Dropdown of choices 294 ctrl.items = []; //All available choices 295 296 ctrl.open = false; 297 ctrl.focus = false; 298 ctrl.disabled = false; 299 ctrl.selected = undefined; 300 301 ctrl.dropdownPosition = 'auto'; 302 303 ctrl.focusser = undefined; //Reference to input element used to handle focus events 304 ctrl.multiple = undefined; // Initialized inside uiSelect directive link function 305 ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelectChoices directive link function 306 ctrl.tagging = {isActivated: false, fct: undefined}; 307 ctrl.taggingTokens = {isActivated: false, tokens: undefined}; 308 ctrl.lockChoiceExpression = undefined; // Initialized inside uiSelectMatch directive link function 309 ctrl.clickTriggeredSelect = false; 310 ctrl.$filter = $filter; 311 ctrl.$element = $element; 312 313 // Use $injector to check for $animate and store a reference to it 314 ctrl.$animate = (function () { 315 try { 316 return $injector.get('$animate'); 317 } catch (err) { 318 // $animate does not exist 319 return null; 320 } 321 })(); 322 323 ctrl.searchInput = $element.querySelectorAll('input.ui-select-search'); 324 if (ctrl.searchInput.length !== 1) { 325 throw uiSelectMinErr('searchInput', "Expected 1 input.ui-select-search but got '{0}'.", ctrl.searchInput.length); 326 } 327 328 ctrl.isEmpty = function() { 329 return angular.isUndefined(ctrl.selected) || ctrl.selected === null || ctrl.selected === '' || (ctrl.multiple && ctrl.selected.length === 0); 330 }; 331 332 function _findIndex(collection, predicate, thisArg){ 333 if (collection.findIndex){ 334 return collection.findIndex(predicate, thisArg); 335 } else { 336 var list = Object(collection); 337 var length = list.length >>> 0; 338 var value; 339 340 for (var i = 0; i < length; i++) { 341 value = list[i]; 342 if (predicate.call(thisArg, value, i, list)) { 343 return i; 344 } 345 } 346 return -1; 347 } 348 } 349 350 // Most of the time the user does not want to empty the search input when in typeahead mode 351 function _resetSearchInput() { 352 if (ctrl.resetSearchInput) { 353 ctrl.search = EMPTY_SEARCH; 354 //reset activeIndex 355 if (ctrl.selected && ctrl.items.length && !ctrl.multiple) { 356 ctrl.activeIndex = _findIndex(ctrl.items, function(item){ 357 return angular.equals(this, item); 358 }, ctrl.selected); 359 } 360 } 361 } 362 363 function _groupsFilter(groups, groupNames) { 364 var i, j, result = []; 365 for(i = 0; i < groupNames.length ;i++){ 366 for(j = 0; j < groups.length ;j++){ 367 if(groups[j].name == [groupNames[i]]){ 368 result.push(groups[j]); 369 } 370 } 371 } 372 return result; 373 } 374 375 // When the user clicks on ui-select, displays the dropdown list 376 ctrl.activate = function(initSearchValue, avoidReset) { 377 if (!ctrl.disabled && !ctrl.open) { 378 if(!avoidReset) _resetSearchInput(); 379 380 $scope.$broadcast('uis:activate'); 381 382 ctrl.open = true; 383 384 ctrl.activeIndex = ctrl.activeIndex >= ctrl.items.length ? 0 : ctrl.activeIndex; 385 386 // ensure that the index is set to zero for tagging variants 387 // that where first option is auto-selected 388 if ( ctrl.activeIndex === -1 && ctrl.taggingLabel !== false ) { 389 ctrl.activeIndex = 0; 390 } 391 392 var container = $element.querySelectorAll('.ui-select-choices-content'); 393 var searchInput = $element.querySelectorAll('.ui-select-search'); 394 if (ctrl.$animate && ctrl.$animate.on && ctrl.$animate.enabled(container[0])) { 395 var animateHandler = function(elem, phase) { 396 if (phase === 'start' && ctrl.items.length === 0) { 397 // Only focus input after the animation has finished 398 ctrl.$animate.off('removeClass', searchInput[0], animateHandler); 399 $timeout(function () { 400 ctrl.focusSearchInput(initSearchValue); 401 }); 402 } else if (phase === 'close') { 403 // Only focus input after the animation has finished 404 ctrl.$animate.off('enter', container[0], animateHandler); 405 $timeout(function () { 406 ctrl.focusSearchInput(initSearchValue); 407 }); 408 } 409 }; 410 411 if (ctrl.items.length > 0) { 412 ctrl.$animate.on('enter', container[0], animateHandler); 413 } else { 414 ctrl.$animate.on('removeClass', searchInput[0], animateHandler); 415 } 416 } else { 417 $timeout(function () { 418 ctrl.focusSearchInput(initSearchValue); 419 if(!ctrl.tagging.isActivated && ctrl.items.length > 1) { 420 _ensureHighlightVisible(); 421 } 422 }); 423 } 424 } 425 else if (ctrl.open && !ctrl.searchEnabled) { 426 // Close the selection if we don't have search enabled, and we click on the select again 427 ctrl.close(); 428 } 429 }; 430 431 ctrl.focusSearchInput = function (initSearchValue) { 432 ctrl.search = initSearchValue || ctrl.search; 433 ctrl.searchInput[0].focus(); 434 }; 435 436 ctrl.findGroupByName = function(name) { 437 return ctrl.groups && ctrl.groups.filter(function(group) { 438 return group.name === name; 439 })[0]; 440 }; 441 442 ctrl.parseRepeatAttr = function(repeatAttr, groupByExp, groupFilterExp) { 443 function updateGroups(items) { 444 var groupFn = $scope.$eval(groupByExp); 445 ctrl.groups = []; 446 angular.forEach(items, function(item) { 447 var groupName = angular.isFunction(groupFn) ? groupFn(item) : item[groupFn]; 448 var group = ctrl.findGroupByName(groupName); 449 if(group) { 450 group.items.push(item); 451 } 452 else { 453 ctrl.groups.push({name: groupName, items: [item]}); 454 } 455 }); 456 if(groupFilterExp){ 457 var groupFilterFn = $scope.$eval(groupFilterExp); 458 if( angular.isFunction(groupFilterFn)){ 459 ctrl.groups = groupFilterFn(ctrl.groups); 460 } else if(angular.isArray(groupFilterFn)){ 461 ctrl.groups = _groupsFilter(ctrl.groups, groupFilterFn); 462 } 463 } 464 ctrl.items = []; 465 ctrl.groups.forEach(function(group) { 466 ctrl.items = ctrl.items.concat(group.items); 467 }); 468 } 469 470 function setPlainItems(items) { 471 ctrl.items = items; 472 } 473 474 ctrl.setItemsFn = groupByExp ? updateGroups : setPlainItems; 475 476 ctrl.parserResult = RepeatParser.parse(repeatAttr); 477 478 ctrl.isGrouped = !!groupByExp; 479 ctrl.itemProperty = ctrl.parserResult.itemName; 480 481 //If collection is an Object, convert it to Array 482 483 var originalSource = ctrl.parserResult.source; 484 485 //When an object is used as source, we better create an array and use it as 'source' 486 var createArrayFromObject = function(){ 487 var origSrc = originalSource($scope); 488 $scope.$uisSource = Object.keys(origSrc).map(function(v){ 489 var result = {}; 490 result[ctrl.parserResult.keyName] = v; 491 result.value = origSrc[v]; 492 return result; 493 }); 494 }; 495 496 if (ctrl.parserResult.keyName){ // Check for (key,value) syntax 497 createArrayFromObject(); 498 ctrl.parserResult.source = $parse('$uisSource' + ctrl.parserResult.filters); 499 $scope.$watch(originalSource, function(newVal, oldVal){ 500 if (newVal !== oldVal) createArrayFromObject(); 501 }, true); 502 } 503 504 ctrl.refreshItems = function (data){ 505 data = data || ctrl.parserResult.source($scope); 506 var selectedItems = ctrl.selected; 507 //TODO should implement for single mode removeSelected 508 if (ctrl.isEmpty() || (angular.isArray(selectedItems) && !selectedItems.length) || !ctrl.multiple || !ctrl.removeSelected) { 509 ctrl.setItemsFn(data); 510 }else{ 511 if ( data !== undefined && data !== null ) { 512 var filteredItems = data.filter(function(i) { 513 return angular.isArray(selectedItems) ? selectedItems.every(function(selectedItem) { 514 return !angular.equals(i, selectedItem); 515 }) : !angular.equals(i, selectedItems); 516 }); 517 ctrl.setItemsFn(filteredItems); 518 } 519 } 520 if (ctrl.dropdownPosition === 'auto' || ctrl.dropdownPosition === 'up'){ 521 $scope.calculateDropdownPos(); 522 } 523 524 $scope.$broadcast('uis:refresh'); 525 }; 526 527 // See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L259 528 $scope.$watchCollection(ctrl.parserResult.source, function(items) { 529 if (items === undefined || items === null) { 530 // If the user specifies undefined or null => reset the collection 531 // Special case: items can be undefined if the user did not initialized the collection on the scope 532 // i.e $scope.addresses = [] is missing 533 ctrl.items = []; 534 } else { 535 if (!angular.isArray(items)) { 536 throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items); 537 } else { 538 //Remove already selected items (ex: while searching) 539 //TODO Should add a test 540 ctrl.refreshItems(items); 541 542 //update the view value with fresh data from items, if there is a valid model value 543 if(angular.isDefined(ctrl.ngModel.$modelValue)) { 544 ctrl.ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters 545 } 546 } 547 } 548 }); 549 550 }; 551 552 var _refreshDelayPromise; 553 554 /** 555 * Typeahead mode: lets the user refresh the collection using his own function. 556 * 557 * See Expose $select.search for external / remote filtering https://github.com/angular-ui/ui-select/pull/31 558 */ 559 ctrl.refresh = function(refreshAttr) { 560 if (refreshAttr !== undefined) { 561 562 // Debounce 563 // See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L155 564 // FYI AngularStrap typeahead does not have debouncing: https://github.com/mgcrea/angular-strap/blob/v2.0.0-rc.4/src/typeahead/typeahead.js#L177 565 if (_refreshDelayPromise) { 566 $timeout.cancel(_refreshDelayPromise); 567 } 568 _refreshDelayPromise = $timeout(function() { 569 $scope.$eval(refreshAttr); 570 }, ctrl.refreshDelay); 571 } 572 }; 573 574 ctrl.isActive = function(itemScope) { 575 if ( !ctrl.open ) { 576 return false; 577 } 578 var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]); 579 var isActive = itemIndex == ctrl.activeIndex; 580 581 if ( !isActive || itemIndex < 0 ) { 582 return false; 583 } 584 585 if (isActive && !angular.isUndefined(ctrl.onHighlightCallback)) { 586 itemScope.$eval(ctrl.onHighlightCallback); 587 } 588 589 return isActive; 590 }; 591 592 var _isItemSelected = function (item) { 593 return (ctrl.selected && angular.isArray(ctrl.selected) && 594 ctrl.selected.filter(function (selection) { return angular.equals(selection, item); }).length > 0); 595 }; 596 597 var disabledItems = []; 598 599 function _updateItemDisabled(item, isDisabled) { 600 var disabledItemIndex = disabledItems.indexOf(item); 601 if (isDisabled && disabledItemIndex === -1) { 602 disabledItems.push(item); 603 } 604 605 if (!isDisabled && disabledItemIndex > -1) { 606 disabledItems.splice(disabledItemIndex, 1); 607 } 608 } 609 610 function _isItemDisabled(item) { 611 return disabledItems.indexOf(item) > -1; 612 } 613 614 ctrl.isDisabled = function(itemScope) { 615 616 if (!ctrl.open) return; 617 618 var item = itemScope[ctrl.itemProperty]; 619 var itemIndex = ctrl.items.indexOf(item); 620 var isDisabled = false; 621 622 if (itemIndex >= 0 && (angular.isDefined(ctrl.disableChoiceExpression) || ctrl.multiple)) { 623 624 if (item.isTag) return false; 625 626 if (ctrl.multiple) { 627 isDisabled = _isItemSelected(item); 628 } 629 630 if (!isDisabled && angular.isDefined(ctrl.disableChoiceExpression)) { 631 isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression)); 632 } 633 634 _updateItemDisabled(item, isDisabled); 635 } 636 637 return isDisabled; 638 }; 639 640 641 // When the user selects an item with ENTER or clicks the dropdown 642 ctrl.select = function(item, skipFocusser, $event) { 643 if (item === undefined || !_isItemDisabled(item)) { 644 645 if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return; 646 647 if (!item || !_isItemDisabled(item)) { 648 // if click is made on existing item, prevent from tagging, ctrl.search does not matter 649 ctrl.clickTriggeredSelect = false; 650 if($event && $event.type === 'click' && item) 651 ctrl.clickTriggeredSelect = true; 652 653 if(ctrl.tagging.isActivated && ctrl.clickTriggeredSelect === false) { 654 // if taggingLabel is disabled and item is undefined we pull from ctrl.search 655 if ( ctrl.taggingLabel === false ) { 656 if ( ctrl.activeIndex < 0 ) { 657 if (item === undefined) { 658 item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search; 659 } 660 if (!item || angular.equals( ctrl.items[0], item ) ) { 661 return; 662 } 663 } else { 664 // keyboard nav happened first, user selected from dropdown 665 item = ctrl.items[ctrl.activeIndex]; 666 } 667 } else { 668 // tagging always operates at index zero, taggingLabel === false pushes 669 // the ctrl.search value without having it injected 670 if ( ctrl.activeIndex === 0 ) { 671 // ctrl.tagging pushes items to ctrl.items, so we only have empty val 672 // for `item` if it is a detected duplicate 673 if ( item === undefined ) return; 674 675 // create new item on the fly if we don't already have one; 676 // use tagging function if we have one 677 if ( ctrl.tagging.fct !== undefined && typeof item === 'string' ) { 678 item = ctrl.tagging.fct(item); 679 if (!item) return; 680 // if item type is 'string', apply the tagging label 681 } else if ( typeof item === 'string' ) { 682 // trim the trailing space 683 item = item.replace(ctrl.taggingLabel,'').trim(); 684 } 685 } 686 } 687 // search ctrl.selected for dupes potentially caused by tagging and return early if found 688 if (_isItemSelected(item)) { 689 ctrl.close(skipFocusser); 690 return; 691 } 692 } 693 _resetSearchInput(); 694 $scope.$broadcast('uis:select', item); 695 696 var locals = {}; 697 locals[ctrl.parserResult.itemName] = item; 698 699 $timeout(function(){ 700 ctrl.onSelectCallback($scope, { 701 $item: item, 702 $model: ctrl.parserResult.modelMapper($scope, locals) 703 }); 704 }); 705 706 if (ctrl.closeOnSelect) { 707 ctrl.close(skipFocusser); 708 } 709 } 710 } 711 }; 712 713 // Closes the dropdown 714 ctrl.close = function(skipFocusser) { 715 if (!ctrl.open) return; 716 if (ctrl.ngModel && ctrl.ngModel.$setTouched) ctrl.ngModel.$setTouched(); 717 ctrl.open = false; 718 _resetSearchInput(); 719 $scope.$broadcast('uis:close', skipFocusser); 720 721 }; 722 723 ctrl.setFocus = function(){ 724 if (!ctrl.focus) ctrl.focusInput[0].focus(); 725 }; 726 727 ctrl.clear = function($event) { 728 ctrl.select(undefined); 729 $event.stopPropagation(); 730 $timeout(function() { 731 ctrl.focusser[0].focus(); 732 }, 0, false); 733 }; 734 735 // Toggle dropdown 736 ctrl.toggle = function(e) { 737 if (ctrl.open) { 738 ctrl.close(); 739 e.preventDefault(); 740 e.stopPropagation(); 741 } else { 742 ctrl.activate(); 743 } 744 }; 745 746 // Set default function for locked choices - avoids unnecessary 747 // logic if functionality is not being used 748 ctrl.isLocked = function () { 749 return false; 750 }; 751 752 $scope.$watch(function () { 753 return angular.isDefined(ctrl.lockChoiceExpression) && ctrl.lockChoiceExpression !== ""; 754 }, _initaliseLockedChoices); 755 756 function _initaliseLockedChoices(doInitalise) { 757 if(!doInitalise) return; 758 759 var lockedItems = []; 760 761 function _updateItemLocked(item, isLocked) { 762 var lockedItemIndex = lockedItems.indexOf(item); 763 if (isLocked && lockedItemIndex === -1) { 764 lockedItems.push(item); 765 } 766 767 if (!isLocked && lockedItemIndex > -1) { 768 lockedItems.splice(lockedItemIndex, 0); 769 } 770 } 771 772 function _isItemlocked(item) { 773 return lockedItems.indexOf(item) > -1; 774 } 775
776 ctrl.isLocked = function (itemScope, itemIndex) { 777 var isLocked = false, 778 item = ctrl.selected[itemIndex]; 779 780 if(item) { 781 if (itemScope) { 782 isLocked = !!(itemScope.$eval(ctrl.lockChoiceExpression)); 783 _updateItemLocked(item, isLocked); 784 } else { 785 isLocked = _isItemlocked(item); 786 } 787 } 788 789 return isLocked; 790 }; 791 } 792 793 794 var sizeWatch = null; 795 var updaterScheduled = false; 796 ctrl.sizeSearchInput = function() { 797 798 var input = ctrl.searchInput[0], 799 container = ctrl.searchInput.parent().parent()[0], 800 calculateContainerWidth = function() { 801 // Return the container width only if the search input is visible 802 return container.clientWidth * !!input.offsetParent; 803 }, 804 updateIfVisible = function(containerWidth) { 805 if (containerWidth === 0) { 806 return false; 807 } 808 var inputWidth = containerWidth - input.offsetLeft - 10; 809 if (inputWidth < 50) inputWidth = containerWidth; 810 ctrl.searchInput.css('width', inputWidth+'px'); 811 return true; 812 }; 813 814 ctrl.searchInput.css('width', '10px'); 815 $timeout(function() { //Give tags time to render correctly 816 if (sizeWatch === null && !updateIfVisible(calculateContainerWidth())) { 817 sizeWatch = $scope.$watch(function() { 818 if (!updaterScheduled) { 819 updaterScheduled = true; 820 $scope.$$postDigest(function() { 821 updaterScheduled = false; 822 if (updateIfVisible(calculateContainerWidth())) { 823 sizeWatch(); 824 sizeWatch = null; 825 } 826 }); 827 } 828 }, angular.noop); 829 } 830 }); 831 }; 832 833 function _handleDropDownSelection(key) { 834 var processed = true; 835 switch (key) { 836 case KEY.DOWN: 837 if (!ctrl.open && ctrl.multiple) ctrl.activate(false, true); //In case its the search input in 'multiple' mode 838 else if (ctrl.activeIndex < ctrl.items.length - 1) { ctrl.activeIndex++; } 839 break; 840 case KEY.UP: 841 if (!ctrl.open && ctrl.multiple) ctrl.activate(false, true); //In case its the search input in 'multiple' mode 842 else if (ctrl.activeIndex > 0 || (ctrl.search.length === 0 && ctrl.tagging.isActivated && ctrl.activeIndex > -1)) { ctrl.activeIndex--; } 843 break; 844 case KEY.TAB: 845 if (!ctrl.multiple || ctrl.open) ctrl.select(ctrl.items[ctrl.activeIndex], true); 846 break; 847 case KEY.ENTER: 848 if(ctrl.open && (ctrl.tagging.isActivated || ctrl.activeIndex >= 0)){ 849 ctrl.select(ctrl.items[ctrl.activeIndex], ctrl.skipFocusser); // Make sure at least one dropdown item is highlighted before adding if not in tagging mode 850 } else { 851 ctrl.activate(false, true); //In case its the search input in 'multiple' mode 852 } 853 break; 854 case KEY.ESC: 855 ctrl.close(); 856 break; 857 default: 858 processed = false; 859 } 860 return processed; 861 } 862 863 // Bind to keyboard shortcuts 864 ctrl.searchInput.on('keydown', function(e) { 865 866 var key = e.which; 867 868 if (~[KEY.ENTER,KEY.ESC].indexOf(key)){ 869 e.preventDefault(); 870 e.stopPropagation(); 871 } 872 873 // if(~[KEY.ESC,KEY.TAB].indexOf(key)){ 874 // //TODO: SEGURO? 875 // ctrl.close(); 876 // } 877 878 $scope.$apply(function() { 879 880 var tagged = false; 881 882 if (ctrl.items.length > 0 || ctrl.tagging.isActivated) { 883 if(!_handleDropDownSelection(key) && !ctrl.searchEnabled) { 884 e.preventDefault(); 885 e.stopPropagation(); 886 } 887 if ( ctrl.taggingTokens.isActivated ) { 888 for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) { 889 if ( ctrl.taggingTokens.tokens[i] === KEY.MAP[e.keyCode] ) { 890 // make sure there is a new value to push via tagging 891 if ( ctrl.search.length > 0 ) { 892 tagged = true; 893 } 894 } 895 } 896 if ( tagged ) { 897 $timeout(function() { 898 ctrl.searchInput.triggerHandler('tagged'); 899 var newItem = ctrl.search.replace(KEY.MAP[e.keyCode],'').trim(); 900 if ( ctrl.tagging.fct ) { 901 newItem = ctrl.tagging.fct( newItem ); 902 } 903 if (newItem) ctrl.select(newItem, true); 904 }); 905 } 906 } 907 } 908 909 });
vendor: 53,826 bytes, lines 910-2342
910 911 if(KEY.isVerticalMovement(key) && ctrl.items.length > 0){ 912 _ensureHighlightVisible(); 913 } 914 915 if (key === KEY.ENTER || key === KEY.ESC) { 916 e.preventDefault(); 917 e.stopPropagation(); 918 } 919 920 }); 921 922 ctrl.searchInput.on('paste', function (e) { 923 var data; 924 925 if (window.clipboardData && window.clipboardData.getData) { // IE 926 data = window.clipboardData.getData('Text'); 927 } else { 928 data = (e.originalEvent || e).clipboardData.getData('text/plain'); 929 } 930 931 // Prepend the current input field text to the paste buffer. 932 data = ctrl.search + data; 933 934 if (data && data.length > 0) { 935 // If tagging try to split by tokens and add items 936 if (ctrl.taggingTokens.isActivated) { 937 var items = []; 938 for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) { // split by first token that is contained in data 939 var separator = KEY.toSeparator(ctrl.taggingTokens.tokens[i]) || ctrl.taggingTokens.tokens[i]; 940 if (data.indexOf(separator) > -1) { 941 items = data.split(separator); 942 break; // only split by one token 943 } 944 } 945 if (items.length === 0) { 946 items = [data]; 947 } 948 var oldsearch = ctrl.search; 949 angular.forEach(items, function (item) { 950 var newItem = ctrl.tagging.fct ? ctrl.tagging.fct(item) : item; 951 if (newItem) { 952 ctrl.select(newItem, true); 953 } 954 }); 955 ctrl.search = oldsearch || EMPTY_SEARCH; 956 e.preventDefault(); 957 e.stopPropagation(); 958 } else if (ctrl.paste) { 959 ctrl.paste(data); 960 ctrl.search = EMPTY_SEARCH; 961 e.preventDefault(); 962 e.stopPropagation(); 963 } 964 } 965 }); 966 967 ctrl.searchInput.on('tagged', function() { 968 $timeout(function() { 969 _resetSearchInput(); 970 }); 971 }); 972 973 // See https://github.com/ivaynberg/select2/blob/3.4.6/select2.js#L1431 974 function _ensureHighlightVisible() { 975 var container = $element.querySelectorAll('.ui-select-choices-content'); 976 var choices = container.querySelectorAll('.ui-select-choices-row'); 977 if (choices.length < 1) { 978 throw uiSelectMinErr('choices', "Expected multiple .ui-select-choices-row but got '{0}'.", choices.length); 979 } 980 981 if (ctrl.activeIndex < 0) { 982 return; 983 } 984 985 var highlighted = choices[ctrl.activeIndex]; 986 var posY = highlighted.offsetTop + highlighted.clientHeight - container[0].scrollTop; 987 var height = container[0].offsetHeight; 988 989 if (posY > height) { 990 container[0].scrollTop += posY - height; 991 } else if (posY < highlighted.clientHeight) { 992 if (ctrl.isGrouped && ctrl.activeIndex === 0) 993 container[0].scrollTop = 0; //To make group header visible when going all the way up 994 else 995 container[0].scrollTop -= highlighted.clientHeight - posY; 996 } 997 } 998 999 var onResize = $$uisDebounce(function() { 1000 ctrl.sizeSearchInput(); 1001 }, 50); 1002 1003 angular.element($window).bind('resize', onResize); 1004 1005 $scope.$on('$destroy', function() { 1006 ctrl.searchInput.off('keyup keydown tagged blur paste'); 1007 angular.element($window).off('resize', onResize); 1008 }); 1009}]); 1010 1011uis.directive('uiSelect', 1012 ['$document', 'uiSelectConfig', 'uiSelectMinErr', 'uisOffset', '$compile', '$parse', '$timeout', 1013 function($document, uiSelectConfig, uiSelectMinErr, uisOffset, $compile, $parse, $timeout) { 1014 1015 return { 1016 restrict: 'EA', 1017 templateUrl: function(tElement, tAttrs) { 1018 var theme = tAttrs.theme || uiSelectConfig.theme; 1019 return theme + (angular.isDefined(tAttrs.multiple) ? '/select-multiple.tpl.html' : '/select.tpl.html'); 1020 }, 1021 replace: true, 1022 transclude: true, 1023 require: ['uiSelect', '^ngModel'], 1024 scope: true, 1025 1026 controller: 'uiSelectCtrl', 1027 controllerAs: '$select', 1028 compile: function(tElement, tAttrs) { 1029 1030 // Allow setting ngClass on uiSelect 1031 var match = /{(.*)}\s*{(.*)}/.exec(tAttrs.ngClass); 1032 if(match) { 1033 var combined = '{'+ match[1] +', '+ match[2] +'}'; 1034 tAttrs.ngClass = combined; 1035 tElement.attr('ng-class', combined); 1036 } 1037 1038 //Multiple or Single depending if multiple attribute presence 1039 if (angular.isDefined(tAttrs.multiple)) 1040 tElement.append('<ui-select-multiple/>').removeAttr('multiple'); 1041 else 1042 tElement.append('<ui-select-single/>'); 1043 1044 if (tAttrs.inputId) 1045 tElement.querySelectorAll('input.ui-select-search')[0].id = tAttrs.inputId; 1046 1047 return function(scope, element, attrs, ctrls, transcludeFn) { 1048 1049 var $select = ctrls[0]; 1050 var ngModel = ctrls[1]; 1051 1052 $select.generatedId = uiSelectConfig.generateId(); 1053 $select.baseTitle = attrs.title || 'Select box'; 1054 $select.focusserTitle = $select.baseTitle + ' focus'; 1055 $select.focusserId = 'focusser-' + $select.generatedId; 1056 1057 $select.closeOnSelect = function() { 1058 if (angular.isDefined(attrs.closeOnSelect)) { 1059 return $parse(attrs.closeOnSelect)(); 1060 } else { 1061 return uiSelectConfig.closeOnSelect; 1062 } 1063 }(); 1064 1065 scope.$watch('skipFocusser', function() { 1066 var skipFocusser = scope.$eval(attrs.skipFocusser); 1067 $select.skipFocusser = skipFocusser !== undefined ? skipFocusser : uiSelectConfig.skipFocusser; 1068 }); 1069 1070 $select.onSelectCallback = $parse(attrs.onSelect); 1071 $select.onRemoveCallback = $parse(attrs.onRemove); 1072 1073 //Set reference to ngModel from uiSelectCtrl 1074 $select.ngModel = ngModel; 1075 1076 $select.choiceGrouped = function(group){ 1077 return $select.isGrouped && group && group.name; 1078 }; 1079 1080 if(attrs.tabindex){ 1081 attrs.$observe('tabindex', function(value) { 1082 $select.focusInput.attr('tabindex', value); 1083 element.removeAttr('tabindex'); 1084 }); 1085 } 1086 1087 scope.$watch(function () { return scope.$eval(attrs.searchEnabled); }, function(newVal) { 1088 $select.searchEnabled = newVal !== undefined ? newVal : uiSelectConfig.searchEnabled; 1089 }); 1090 1091 scope.$watch('sortable', function() { 1092 var sortable = scope.$eval(attrs.sortable); 1093 $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; 1094 }); 1095 1096 attrs.$observe('limit', function() { 1097 //Limit the number of selections allowed 1098 $select.limit = (angular.isDefined(attrs.limit)) ? parseInt(attrs.limit, 10) : undefined; 1099 }); 1100 1101 scope.$watch('removeSelected', function() { 1102 var removeSelected = scope.$eval(attrs.removeSelected); 1103 $select.removeSelected = removeSelected !== undefined ? removeSelected : uiSelectConfig.removeSelected; 1104 }); 1105 1106 attrs.$observe('disabled', function() { 1107 // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string 1108 $select.disabled = attrs.disabled !== undefined ? attrs.disabled : false; 1109 }); 1110 1111 attrs.$observe('resetSearchInput', function() { 1112 // $eval() is needed otherwise we get a string instead of a boolean 1113 var resetSearchInput = scope.$eval(attrs.resetSearchInput); 1114 $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; 1115 }); 1116 1117 attrs.$observe('paste', function() { 1118 $select.paste = scope.$eval(attrs.paste); 1119 }); 1120 1121 attrs.$observe('tagging', function() { 1122 if(attrs.tagging !== undefined) 1123 { 1124 // $eval() is needed otherwise we get a string instead of a boolean 1125 var taggingEval = scope.$eval(attrs.tagging); 1126 $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; 1127 } 1128 else 1129 { 1130 $select.tagging = {isActivated: false, fct: undefined}; 1131 } 1132 }); 1133 1134 attrs.$observe('taggingLabel', function() { 1135 if(attrs.tagging !== undefined ) 1136 { 1137 // check eval for FALSE, in this case, we disable the labels 1138 // associated with tagging 1139 if ( attrs.taggingLabel === 'false' ) { 1140 $select.taggingLabel = false; 1141 } 1142 else 1143 { 1144 $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; 1145 } 1146 } 1147 }); 1148 1149 attrs.$observe('taggingTokens', function() { 1150 if (attrs.tagging !== undefined) { 1151 var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; 1152 $select.taggingTokens = {isActivated: true, tokens: tokens }; 1153 } 1154 }); 1155 1156 //Automatically gets focus when loaded 1157 if (angular.isDefined(attrs.autofocus)){ 1158 $timeout(function(){ 1159 $select.setFocus(); 1160 }); 1161 } 1162 1163 //Gets focus based on scope event name (e.g. focus-on='SomeEventName') 1164 if (angular.isDefined(attrs.focusOn)){ 1165 scope.$on(attrs.focusOn, function() { 1166 $timeout(function(){ 1167 $select.setFocus(); 1168 }); 1169 }); 1170 } 1171 1172 function onDocumentClick(e) { 1173 if (!$select.open) return; //Skip it if dropdown is close 1174 1175 var contains = false; 1176 1177 if (window.jQuery) { 1178 // Firefox 3.6 does not support element.contains() 1179 // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains 1180 contains = window.jQuery.contains(element[0], e.target); 1181 } else { 1182 contains = element[0].contains(e.target); 1183 } 1184 1185 if (!contains && !$select.clickTriggeredSelect) { 1186 var skipFocusser; 1187 if (!$select.skipFocusser) { 1188 //Will lose focus only with certain targets 1189 var focusableControls = ['input','button','textarea','select']; 1190 var targetController = angular.element(e.target).controller('uiSelect'); //To check if target is other ui-select 1191 skipFocusser = targetController && targetController !== $select; //To check if target is other ui-select 1192 if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea 1193 } else { 1194 skipFocusser = true; 1195 } 1196 $select.close(skipFocusser); 1197 scope.$digest(); 1198 } 1199 $select.clickTriggeredSelect = false; 1200 } 1201 1202 // See Click everywhere but here event http://stackoverflow.com/questions/12931369 1203 $document.on('click', onDocumentClick); 1204 1205 scope.$on('$destroy', function() { 1206 $document.off('click', onDocumentClick); 1207 }); 1208 1209 // Move transcluded elements to their correct position in main template 1210 transcludeFn(scope, function(clone) { 1211 // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html 1212 1213 // One day jqLite will be replaced by jQuery and we will be able to write: 1214 // var transcludedElement = clone.filter('.my-class') 1215 // instead of creating a hackish DOM element: 1216 var transcluded = angular.element('<div>').append(clone); 1217 1218 var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); 1219 transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr 1220 transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes 1221 if (transcludedMatch.length !== 1) { 1222 throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); 1223 } 1224 element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); 1225 1226 var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); 1227 transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr 1228 transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes 1229 if (transcludedChoices.length !== 1) { 1230 throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); 1231 } 1232 element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); 1233 1234 var transcludedNoChoice = transcluded.querySelectorAll('.ui-select-no-choice'); 1235 transcludedNoChoice.removeAttr('ui-select-no-choice'); //To avoid loop in case directive as attr 1236 transcludedNoChoice.removeAttr('data-ui-select-no-choice'); // Properly handle HTML5 data-attributes 1237 if (transcludedNoChoice.length == 1) { 1238 element.querySelectorAll('.ui-select-no-choice').replaceWith(transcludedNoChoice); 1239 } 1240 }); 1241 1242 // Support for appending the select field to the body when its open 1243 var appendToBody = scope.$eval(attrs.appendToBody); 1244 if (appendToBody !== undefined ? appendToBody : uiSelectConfig.appendToBody) { 1245 scope.$watch('$select.open', function(isOpen) { 1246 if (isOpen) { 1247 positionDropdown(); 1248 } else { 1249 resetDropdown(); 1250 } 1251 }); 1252 1253 // Move the dropdown back to its original location when the scope is destroyed. Otherwise 1254 // it might stick around when the user routes away or the select field is otherwise removed 1255 scope.$on('$destroy', function() { 1256 resetDropdown(); 1257 }); 1258 } 1259 1260 // Hold on to a reference to the .ui-select-container element for appendToBody support 1261 var placeholder = null, 1262 originalWidth = ''; 1263 1264 function positionDropdown() { 1265 // Remember the absolute position of the element 1266 var offset = uisOffset(element); 1267 1268 // Clone the element into a placeholder element to take its original place in the DOM 1269 placeholder = angular.element('<div class="ui-select-placeholder"></div>'); 1270 placeholder[0].style.width = offset.width + 'px'; 1271 placeholder[0].style.height = offset.height + 'px'; 1272 element.after(placeholder); 1273 1274 // Remember the original value of the element width inline style, so it can be restored 1275 // when the dropdown is closed 1276 originalWidth = element[0].style.width; 1277 1278 // Now move the actual dropdown element to the end of the body 1279 $document.find('body').append(element); 1280 1281 element[0].style.position = 'absolute'; 1282 element[0].style.left = offset.left + 'px'; 1283 element[0].style.top = offset.top + 'px'; 1284 element[0].style.width = offset.width + 'px'; 1285 } 1286 1287 function resetDropdown() { 1288 if (placeholder === null) { 1289 // The dropdown has not actually been display yet, so there's nothing to reset 1290 return; 1291 } 1292 1293 // Move the dropdown element back to its original location in the DOM 1294 placeholder.replaceWith(element); 1295 placeholder = null; 1296 1297 element[0].style.position = ''; 1298 element[0].style.left = ''; 1299 element[0].style.top = ''; 1300 element[0].style.width = originalWidth; 1301 1302 // Set focus back on to the moved element 1303 $select.setFocus(); 1304 } 1305 1306 // Hold on to a reference to the .ui-select-dropdown element for direction support. 1307 var dropdown = null, 1308 directionUpClassName = 'direction-up'; 1309 1310 // Support changing the direction of the dropdown if there isn't enough space to render it. 1311 scope.$watch('$select.open', function() { 1312 1313 if ($select.dropdownPosition === 'auto' || $select.dropdownPosition === 'up'){ 1314 scope.calculateDropdownPos(); 1315 } 1316 1317 }); 1318 1319 var setDropdownPosUp = function(offset, offsetDropdown){ 1320 1321 offset = offset || uisOffset(element); 1322 offsetDropdown = offsetDropdown || uisOffset(dropdown); 1323 1324 dropdown[0].style.position = 'absolute'; 1325 dropdown[0].style.top = (offsetDropdown.height * -1) + 'px'; 1326 element.addClass(directionUpClassName); 1327 1328 }; 1329 1330 var setDropdownPosDown = function(offset, offsetDropdown){ 1331 1332 element.removeClass(directionUpClassName); 1333 1334 offset = offset || uisOffset(element); 1335 offsetDropdown = offsetDropdown || uisOffset(dropdown); 1336 1337 dropdown[0].style.position = ''; 1338 dropdown[0].style.top = ''; 1339 1340 }; 1341 1342 var calculateDropdownPosAfterAnimation = function() { 1343 // Delay positioning the dropdown until all choices have been added so its height is correct. 1344 $timeout(function() { 1345 if ($select.dropdownPosition === 'up') { 1346 //Go UP 1347 setDropdownPosUp(); 1348 } else { 1349 //AUTO 1350 element.removeClass(directionUpClassName); 1351 1352 var offset = uisOffset(element); 1353 var offsetDropdown = uisOffset(dropdown); 1354 1355 //https://code.google.com/p/chromium/issues/detail?id=342307#c4 1356 var scrollTop = $document[0].documentElement.scrollTop || $document[0].body.scrollTop; //To make it cross browser (blink, webkit, IE, Firefox). 1357 1358 // Determine if the direction of the dropdown needs to be changed. 1359 if (offset.top + offset.height + offsetDropdown.height > scrollTop + $document[0].documentElement.clientHeight) { 1360 //Go UP 1361 setDropdownPosUp(offset, offsetDropdown); 1362 }else{ 1363 //Go DOWN 1364 setDropdownPosDown(offset, offsetDropdown); 1365 } 1366 } 1367 1368 // Display the dropdown once it has been positioned. 1369 dropdown[0].style.opacity = 1; 1370 }); 1371 }; 1372 1373 var opened = false; 1374 1375 scope.calculateDropdownPos = function() { 1376 if ($select.open) { 1377 dropdown = angular.element(element).querySelectorAll('.ui-select-dropdown'); 1378 1379 if (dropdown.length === 0) { 1380 return; 1381 } 1382 1383 // Hide the dropdown so there is no flicker until $timeout is done executing. 1384 if ($select.search === '' && !opened) { 1385 dropdown[0].style.opacity = 0; 1386 opened = true; 1387 } 1388 1389 if (!uisOffset(dropdown).height && $select.$animate && $select.$animate.on && $select.$animate.enabled(dropdown)) { 1390 var needsCalculated = true; 1391 1392 $select.$animate.on('enter', dropdown, function (elem, phase) { 1393 if (phase === 'close' && needsCalculated) { 1394 calculateDropdownPosAfterAnimation(); 1395 needsCalculated = false; 1396 } 1397 }); 1398 } else { 1399 calculateDropdownPosAfterAnimation(); 1400 } 1401 } else { 1402 if (dropdown === null || dropdown.length === 0) { 1403 return; 1404 } 1405 1406 // Reset the position of the dropdown. 1407 dropdown[0].style.opacity = 0; 1408 dropdown[0].style.position = ''; 1409 dropdown[0].style.top = ''; 1410 element.removeClass(directionUpClassName); 1411 } 1412 }; 1413 }; 1414 } 1415 }; 1416}]); 1417 1418uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) { 1419 return { 1420 restrict: 'EA', 1421 require: '^uiSelect', 1422 replace: true, 1423 transclude: true, 1424 templateUrl: function(tElement) { 1425 // Needed so the uiSelect can detect the transcluded content 1426 tElement.addClass('ui-select-match'); 1427 1428 var parent = tElement.parent(); 1429 // Gets theme attribute from parent (ui-select) 1430 var theme = getAttribute(parent, 'theme') || uiSelectConfig.theme; 1431 var multi = angular.isDefined(getAttribute(parent, 'multiple')); 1432 1433 return theme + (multi ? '/match-multiple.tpl.html' : '/match.tpl.html'); 1434 }, 1435 link: function(scope, element, attrs, $select) { 1436 $select.lockChoiceExpression = attrs.uiLockChoice; 1437 attrs.$observe('placeholder', function(placeholder) { 1438 $select.placeholder = placeholder !== undefined ? placeholder : uiSelectConfig.placeholder; 1439 }); 1440 1441 function setAllowClear(allow) { 1442 $select.allowClear = (angular.isDefined(allow)) ? (allow === '') ? true : (allow.toLowerCase() === 'true') : false; 1443 } 1444 1445 attrs.$observe('allowClear', setAllowClear); 1446 setAllowClear(attrs.allowClear); 1447 1448 if($select.multiple){ 1449 $select.sizeSearchInput(); 1450 } 1451 1452 } 1453 }; 1454 1455 function getAttribute(elem, attribute) { 1456 if (elem[0].hasAttribute(attribute)) 1457 return elem.attr(attribute); 1458 1459 if (elem[0].hasAttribute('data-' + attribute)) 1460 return elem.attr('data-' + attribute); 1461 1462 if (elem[0].hasAttribute('x-' + attribute)) 1463 return elem.attr('x-' + attribute); 1464 } 1465}]); 1466 1467uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelectMinErr, $timeout) { 1468 return { 1469 restrict: 'EA', 1470 require: ['^uiSelect', '^ngModel'], 1471 1472 controller: ['$scope','$timeout', function($scope, $timeout){ 1473 1474 var ctrl = this, 1475 $select = $scope.$select, 1476 ngModel; 1477 1478 if (angular.isUndefined($select.selected)) 1479 $select.selected = []; 1480 1481 //Wait for link fn to inject it 1482 $scope.$evalAsync(function(){ ngModel = $scope.ngModel; }); 1483 1484 ctrl.activeMatchIndex = -1; 1485 1486 ctrl.updateModel = function(){ 1487 ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes 1488 ctrl.refreshComponent(); 1489 }; 1490 1491 ctrl.refreshComponent = function(){ 1492 //Remove already selected items 1493 //e.g. When user clicks on a selection, the selected array changes and 1494 //the dropdown should remove that item 1495 if($select.refreshItems){ 1496 $select.refreshItems(); 1497 } 1498 if($select.sizeSearchInput){ 1499 $select.sizeSearchInput(); 1500 } 1501 }; 1502 1503 // Remove item from multiple select 1504 ctrl.removeChoice = function(index){ 1505 1506 // if the choice is locked, don't remove it 1507 if($select.isLocked(null, index)) return false; 1508 1509 var removedChoice = $select.selected[index]; 1510 1511 var locals = {}; 1512 locals[$select.parserResult.itemName] = removedChoice; 1513 1514 $select.selected.splice(index, 1); 1515 ctrl.activeMatchIndex = -1; 1516 $select.sizeSearchInput(); 1517 1518 // Give some time for scope propagation. 1519 $timeout(function(){ 1520 $select.onRemoveCallback($scope, { 1521 $item: removedChoice, 1522 $model: $select.parserResult.modelMapper($scope, locals) 1523 }); 1524 }); 1525 1526 ctrl.updateModel(); 1527 1528 return true; 1529 }; 1530 1531 ctrl.getPlaceholder = function(){ 1532 //Refactor single? 1533 if($select.selected && $select.selected.length) return; 1534 return $select.placeholder; 1535 }; 1536 1537 1538 }], 1539 controllerAs: '$selectMultiple', 1540 1541 link: function(scope, element, attrs, ctrls) { 1542 1543 var $select = ctrls[0]; 1544 var ngModel = scope.ngModel = ctrls[1]; 1545 var $selectMultiple = scope.$selectMultiple; 1546 1547 //$select.selected = raw selected objects (ignoring any property binding) 1548 1549 $select.multiple = true; 1550 1551 //Input that will handle focus 1552 $select.focusInput = $select.searchInput; 1553 1554 //Properly check for empty if set to multiple 1555 ngModel.$isEmpty = function(value) { 1556 return !value || value.length === 0; 1557 }; 1558 1559 //From view --> model 1560 ngModel.$parsers.unshift(function () { 1561 var locals = {}, 1562 result, 1563 resultMultiple = []; 1564 for (var j = $select.selected.length - 1; j >= 0; j--) { 1565 locals = {}; 1566 locals[$select.parserResult.itemName] = $select.selected[j]; 1567 result = $select.parserResult.modelMapper(scope, locals); 1568 resultMultiple.unshift(result); 1569 } 1570 return resultMultiple; 1571 }); 1572 1573 // From model --> view 1574 ngModel.$formatters.unshift(function (inputValue) { 1575 var data = $select.parserResult && $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search 1576 locals = {}, 1577 result; 1578 if (!data) return inputValue; 1579 var resultMultiple = []; 1580 var checkFnMultiple = function(list, value){ 1581 if (!list || !list.length) return; 1582 for (var p = list.length - 1; p >= 0; p--) { 1583 locals[$select.parserResult.itemName] = list[p]; 1584 result = $select.parserResult.modelMapper(scope, locals); 1585 if($select.parserResult.trackByExp){ 1586 var propsItemNameMatches = /(\w*)\./.exec($select.parserResult.trackByExp); 1587 var matches = /\.([^\s]+)/.exec($select.parserResult.trackByExp); 1588 if(propsItemNameMatches && propsItemNameMatches.length > 0 && propsItemNameMatches[1] == $select.parserResult.itemName){ 1589 if(matches && matches.length>0 && result[matches[1]] == value[matches[1]]){ 1590 resultMultiple.unshift(list[p]); 1591 return true; 1592 } 1593 } 1594 } 1595 if (angular.equals(result,value)){ 1596 resultMultiple.unshift(list[p]); 1597 return true; 1598 } 1599 } 1600 return false; 1601 }; 1602 if (!inputValue) return resultMultiple; //If ngModel was undefined 1603 for (var k = inputValue.length - 1; k >= 0; k--) { 1604 //Check model array of currently selected items 1605 if (!checkFnMultiple($select.selected, inputValue[k])){ 1606 //Check model array of all items available 1607 if (!checkFnMultiple(data, inputValue[k])){ 1608 //If not found on previous lists, just add it directly to resultMultiple 1609 resultMultiple.unshift(inputValue[k]); 1610 } 1611 } 1612 } 1613 return resultMultiple; 1614 }); 1615 1616 //Watch for external model changes 1617 scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) { 1618 if (oldValue != newValue){ 1619 //update the view value with fresh data from items, if there is a valid model value 1620 if(angular.isDefined(ngModel.$modelValue)) { 1621 ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters 1622 } 1623 $selectMultiple.refreshComponent(); 1624 } 1625 }); 1626 1627 ngModel.$render = function() { 1628 // Make sure that model value is array 1629 if(!angular.isArray(ngModel.$viewValue)){ 1630 // Have tolerance for null or undefined values 1631 if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){ 1632 ngModel.$viewValue = []; 1633 } else { 1634 throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); 1635 } 1636 } 1637 $select.selected = ngModel.$viewValue; 1638 $selectMultiple.refreshComponent(); 1639 scope.$evalAsync(); //To force $digest 1640 }; 1641 1642 scope.$on('uis:select', function (event, item) { 1643 if($select.selected.length >= $select.limit) { 1644 return; 1645 } 1646 $select.selected.push(item); 1647 $selectMultiple.updateModel(); 1648 }); 1649 1650 scope.$on('uis:activate', function () { 1651 $selectMultiple.activeMatchIndex = -1; 1652 }); 1653 1654 scope.$watch('$select.disabled', function(newValue, oldValue) { 1655 // As the search input field may now become visible, it may be necessary to recompute its size 1656 if (oldValue && !newValue) $select.sizeSearchInput(); 1657 }); 1658 1659 $select.searchInput.on('keydown', function(e) { 1660 var key = e.which; 1661 scope.$apply(function() { 1662 var processed = false; 1663 // var tagged = false; //Checkme 1664 if(KEY.isHorizontalMovement(key)){ 1665 processed = _handleMatchSelection(key); 1666 } 1667 if (processed && key != KEY.TAB) { 1668 //TODO Check si el tab selecciona aun correctamente 1669 //Crear test 1670 e.preventDefault(); 1671 e.stopPropagation(); 1672 } 1673 }); 1674 }); 1675 function _getCaretPosition(el) { 1676 if(angular.isNumber(el.selectionStart)) return el.selectionStart; 1677 // selectionStart is not supported in IE8 and we don't want hacky workarounds so we compromise 1678 else return el.value.length; 1679 } 1680 // Handles selected options in "multiple" mode 1681 function _handleMatchSelection(key){ 1682 var caretPosition = _getCaretPosition($select.searchInput[0]), 1683 length = $select.selected.length, 1684 // none = -1, 1685 first = 0, 1686 last = length-1, 1687 curr = $selectMultiple.activeMatchIndex, 1688 next = $selectMultiple.activeMatchIndex+1, 1689 prev = $selectMultiple.activeMatchIndex-1, 1690 newIndex = curr; 1691 1692 if(caretPosition > 0 || ($select.search.length && key == KEY.RIGHT)) return false; 1693 1694 $select.close(); 1695 1696 function getNewActiveMatchIndex(){ 1697 switch(key){ 1698 case KEY.LEFT: 1699 // Select previous/first item 1700 if(~$selectMultiple.activeMatchIndex) return prev; 1701 // Select last item 1702 else return last; 1703 break; 1704 case KEY.RIGHT: 1705 // Open drop-down 1706 if(!~$selectMultiple.activeMatchIndex || curr === last){ 1707 $select.activate(); 1708 return false; 1709 } 1710 // Select next/last item 1711 else return next; 1712 break; 1713 case KEY.BACKSPACE: 1714 // Remove selected item and select previous/first 1715 if(~$selectMultiple.activeMatchIndex){ 1716 if($selectMultiple.removeChoice(curr)) { 1717 return prev; 1718 } else { 1719 return curr; 1720 } 1721 1722 } else { 1723 // If nothing yet selected, select last item 1724 return last; 1725 } 1726 break; 1727 case KEY.DELETE: 1728 // Remove selected item and select next item 1729 if(~$selectMultiple.activeMatchIndex){ 1730 $selectMultiple.removeChoice($selectMultiple.activeMatchIndex); 1731 return curr; 1732 } 1733 else return false; 1734 } 1735 } 1736 1737 newIndex = getNewActiveMatchIndex(); 1738 1739 if(!$select.selected.length || newIndex === false) $selectMultiple.activeMatchIndex = -1; 1740 else $selectMultiple.activeMatchIndex = Math.min(last,Math.max(first,newIndex)); 1741 1742 return true; 1743 } 1744 1745 $select.searchInput.on('keyup', function(e) { 1746 1747 if ( ! KEY.isVerticalMovement(e.which) ) { 1748 scope.$evalAsync( function () { 1749 $select.activeIndex = $select.taggingLabel === false ? -1 : 0; 1750 }); 1751 } 1752 // Push a "create new" item into array if there is a search string 1753 if ( $select.tagging.isActivated && $select.search.length > 0 ) { 1754 1755 // return early with these keys 1756 if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || KEY.isVerticalMovement(e.which) ) { 1757 return; 1758 } 1759 // always reset the activeIndex to the first item when tagging 1760 $select.activeIndex = $select.taggingLabel === false ? -1 : 0; 1761 // taggingLabel === false bypasses all of this 1762 if ($select.taggingLabel === false) return; 1763 1764 var items = angular.copy( $select.items ); 1765 var stashArr = angular.copy( $select.items ); 1766 var newItem; 1767 var item; 1768 var hasTag = false; 1769 var dupeIndex = -1; 1770 var tagItems; 1771 var tagItem; 1772 1773 // case for object tagging via transform `$select.tagging.fct` function 1774 if ( $select.tagging.fct !== undefined) { 1775 tagItems = $select.$filter('filter')(items,{'isTag': true}); 1776 if ( tagItems.length > 0 ) { 1777 tagItem = tagItems[0]; 1778 } 1779 // remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous 1780 if ( items.length > 0 && tagItem ) { 1781 hasTag = true; 1782 items = items.slice(1,items.length); 1783 stashArr = stashArr.slice(1,stashArr.length); 1784 } 1785 newItem = $select.tagging.fct($select.search); 1786 // verify the new tag doesn't match the value of a possible selection choice or an already selected item. 1787 if ( 1788 stashArr.some(function (origItem) { 1789 return angular.equals(origItem, newItem); 1790 }) || 1791 $select.selected.some(function (origItem) { 1792 return angular.equals(origItem, newItem); 1793 }) 1794 ) { 1795 scope.$evalAsync(function () { 1796 $select.activeIndex = 0; 1797 $select.items = items; 1798 }); 1799 return; 1800 } 1801 if (newItem) newItem.isTag = true; 1802 // handle newItem string and stripping dupes in tagging string context 1803 } else { 1804 // find any tagging items already in the $select.items array and store them 1805 tagItems = $select.$filter('filter')(items,function (item) { 1806 return item.match($select.taggingLabel); 1807 }); 1808 if ( tagItems.length > 0 ) { 1809 tagItem = tagItems[0]; 1810 } 1811 item = items[0]; 1812 // remove existing tag item if found (should only ever be one tag item) 1813 if ( item !== undefined && items.length > 0 && tagItem ) { 1814 hasTag = true; 1815 items = items.slice(1,items.length); 1816 stashArr = stashArr.slice(1,stashArr.length); 1817 } 1818 newItem = $select.search+' '+$select.taggingLabel; 1819 if ( _findApproxDupe($select.selected, $select.search) > -1 ) { 1820 return; 1821 } 1822 // verify the the tag doesn't match the value of an existing item from 1823 // the searched data set or the items already selected 1824 if ( _findCaseInsensitiveDupe(stashArr.concat($select.selected)) ) { 1825 // if there is a tag from prev iteration, strip it / queue the change 1826 // and return early 1827 if ( hasTag ) { 1828 items = stashArr; 1829 scope.$evalAsync( function () { 1830 $select.activeIndex = 0; 1831 $select.items = items; 1832 }); 1833 } 1834 return; 1835 } 1836 if ( _findCaseInsensitiveDupe(stashArr) ) { 1837 // if there is a tag from prev iteration, strip it 1838 if ( hasTag ) { 1839 $select.items = stashArr.slice(1,stashArr.length); 1840 } 1841 return; 1842 } 1843 } 1844 if ( hasTag ) dupeIndex = _findApproxDupe($select.selected, newItem); 1845 // dupe found, shave the first item 1846 if ( dupeIndex > -1 ) { 1847 items = items.slice(dupeIndex+1,items.length-1); 1848 } else { 1849 items = []; 1850 if (newItem) items.push(newItem); 1851 items = items.concat(stashArr); 1852 } 1853 scope.$evalAsync( function () { 1854 $select.activeIndex = 0; 1855 $select.items = items; 1856 1857 if ($select.isGrouped) { 1858 // update item references in groups, so that indexOf will work after angular.copy 1859 var itemsWithoutTag = newItem ? items.slice(1) : items; 1860 $select.setItemsFn(itemsWithoutTag); 1861 if (newItem) { 1862 // add tag item as a new group 1863 $select.items.unshift(newItem); 1864 $select.groups.unshift({name: '', items: [newItem], tagging: true}); 1865 } 1866 } 1867 }); 1868 } 1869 }); 1870 function _findCaseInsensitiveDupe(arr) { 1871 if ( arr === undefined || $select.search === undefined ) { 1872 return false; 1873 } 1874 var hasDupe = arr.filter( function (origItem) { 1875 if ( $select.search.toUpperCase() === undefined || origItem === undefined ) { 1876 return false; 1877 } 1878 return origItem.toUpperCase() === $select.search.toUpperCase(); 1879 }).length > 0; 1880 1881 return hasDupe; 1882 } 1883 function _findApproxDupe(haystack, needle) { 1884 var dupeIndex = -1; 1885 if(angular.isArray(haystack)) { 1886 var tempArr = angular.copy(haystack); 1887 for (var i = 0; i <tempArr.length; i++) { 1888 // handle the simple string version of tagging 1889 if ( $select.tagging.fct === undefined ) { 1890 // search the array for the match 1891 if ( tempArr[i]+' '+$select.taggingLabel === needle ) { 1892 dupeIndex = i; 1893 } 1894 // handle the object tagging implementation 1895 } else { 1896 var mockObj = tempArr[i]; 1897 if (angular.isObject(mockObj)) { 1898 mockObj.isTag = true; 1899 } 1900 if ( angular.equals(mockObj, needle) ) { 1901 dupeIndex = i; 1902 } 1903 } 1904 } 1905 } 1906 return dupeIndex; 1907 } 1908 1909 $select.searchInput.on('blur', function() { 1910 $timeout(function() { 1911 $selectMultiple.activeMatchIndex = -1; 1912 }); 1913 }); 1914 1915 } 1916 }; 1917}]); 1918 1919uis.directive('uiSelectNoChoice', 1920 ['uiSelectConfig', function (uiSelectConfig) { 1921 return { 1922 restrict: 'EA', 1923 require: '^uiSelect', 1924 replace: true, 1925 transclude: true, 1926 templateUrl: function (tElement) { 1927 // Needed so the uiSelect can detect the transcluded content 1928 tElement.addClass('ui-select-no-choice'); 1929 1930 // Gets theme attribute from parent (ui-select) 1931 var theme = tElement.parent().attr('theme') || uiSelectConfig.theme; 1932 return theme + '/no-choice.tpl.html'; 1933 } 1934 }; 1935 }]); 1936 1937uis.directive('uiSelectSingle', ['$timeout','$compile', function($timeout, $compile) { 1938 return { 1939 restrict: 'EA', 1940 require: ['^uiSelect', '^ngModel'], 1941 link: function(scope, element, attrs, ctrls) { 1942 1943 var $select = ctrls[0]; 1944 var ngModel = ctrls[1]; 1945 1946 //From view --> model 1947 ngModel.$parsers.unshift(function (inputValue) { 1948 var locals = {}, 1949 result; 1950 locals[$select.parserResult.itemName] = inputValue; 1951 result = $select.parserResult.modelMapper(scope, locals); 1952 return result; 1953 }); 1954 1955 //From model --> view 1956 ngModel.$formatters.unshift(function (inputValue) { 1957 var data = $select.parserResult && $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search 1958 locals = {}, 1959 result; 1960 if (data){ 1961 var checkFnSingle = function(d){ 1962 locals[$select.parserResult.itemName] = d; 1963 result = $select.parserResult.modelMapper(scope, locals); 1964 return result === inputValue; 1965 }; 1966 //If possible pass same object stored in $select.selected 1967 if ($select.selected && checkFnSingle($select.selected)) { 1968 return $select.selected; 1969 } 1970 for (var i = data.length - 1; i >= 0; i--) { 1971 if (checkFnSingle(data[i])) return data[i]; 1972 } 1973 } 1974 return inputValue; 1975 }); 1976 1977 //Update viewValue if model change 1978 scope.$watch('$select.selected', function(newValue) { 1979 if (ngModel.$viewValue !== newValue) { 1980 ngModel.$setViewValue(newValue); 1981 } 1982 }); 1983 1984 ngModel.$render = function() { 1985 $select.selected = ngModel.$viewValue; 1986 }; 1987 1988 scope.$on('uis:select', function (event, item) { 1989 $select.selected = item; 1990 }); 1991 1992 scope.$on('uis:close', function (event, skipFocusser) { 1993 $timeout(function(){ 1994 $select.focusser.prop('disabled', false); 1995 if (!skipFocusser) $select.focusser[0].focus(); 1996 },0,false); 1997 }); 1998 1999 scope.$on('uis:activate', function () { 2000 focusser.prop('disabled', true); //Will reactivate it on .close() 2001 }); 2002 2003 //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 2004 var focusser = angular.element("<input ng-disabled='$select.disabled' class='ui-select-focusser ui-select-offscreen' type='text' id='{{ $select.focusserId }}' aria-label='{{ $select.focusserTitle }}' aria-haspopup='true' role='button' />"); 2005 $compile(focusser)(scope); 2006 $select.focusser = focusser; 2007 2008 //Input that will handle focus 2009 $select.focusInput = focusser; 2010 2011 element.parent().append(focusser); 2012 focusser.bind("focus", function(){ 2013 scope.$evalAsync(function(){ 2014 $select.focus = true; 2015 }); 2016 }); 2017 focusser.bind("blur", function(){ 2018 scope.$evalAsync(function(){ 2019 $select.focus = false; 2020 }); 2021 }); 2022 focusser.bind("keydown", function(e){ 2023 2024 if (e.which === KEY.BACKSPACE) { 2025 e.preventDefault(); 2026 e.stopPropagation(); 2027 $select.select(undefined); 2028 scope.$apply(); 2029 return; 2030 } 2031 2032 if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { 2033 return; 2034 } 2035 2036 if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ 2037 e.preventDefault(); 2038 e.stopPropagation(); 2039 $select.activate(); 2040 } 2041 2042 scope.$digest(); 2043 }); 2044 2045 focusser.bind("keyup input", function(e){ 2046 2047 if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { 2048 return; 2049 } 2050 2051 $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input 2052 focusser.val(''); 2053 scope.$digest(); 2054 2055 }); 2056 2057 2058 } 2059 }; 2060}]); 2061 2062// Make multiple matches sortable 2063uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', function($timeout, uiSelectConfig, uiSelectMinErr) { 2064 return { 2065 require: ['^^uiSelect', '^ngModel'], 2066 link: function(scope, element, attrs, ctrls) { 2067 if (scope[attrs.uiSelectSort] === null) { 2068 throw uiSelectMinErr('sort', 'Expected a list to sort'); 2069 } 2070 2071 var $select = ctrls[0]; 2072 var $ngModel = ctrls[1]; 2073 2074 var options = angular.extend({ 2075 axis: 'horizontal' 2076 }, 2077 scope.$eval(attrs.uiSelectSortOptions)); 2078 2079 var axis = options.axis; 2080 var draggingClassName = 'dragging'; 2081 var droppingClassName = 'dropping'; 2082 var droppingBeforeClassName = 'dropping-before'; 2083 var droppingAfterClassName = 'dropping-after'; 2084 2085 scope.$watch(function(){ 2086 return $select.sortable; 2087 }, function(newValue){ 2088 if (newValue) { 2089 element.attr('draggable', true); 2090 } else { 2091 element.removeAttr('draggable'); 2092 } 2093 }); 2094 2095 element.on('dragstart', function(event) { 2096 element.addClass(draggingClassName); 2097 2098 (event.dataTransfer || event.originalEvent.dataTransfer).setData('text', scope.$index.toString()); 2099 }); 2100 2101 element.on('dragend', function() { 2102 removeClass(draggingClassName); 2103 }); 2104 2105 var move = function(from, to) { 2106 /*jshint validthis: true */ 2107 this.splice(to, 0, this.splice(from, 1)[0]); 2108 }; 2109 2110 var removeClass = function(className) { 2111 angular.forEach($select.$element.querySelectorAll('.' + className), function(el){ 2112 angular.element(el).removeClass(className); 2113 }); 2114 }; 2115 2116 var dragOverHandler = function(event) { 2117 event.preventDefault(); 2118 2119 var offset = axis === 'vertical' ? event.offsetY || event.layerY || (event.originalEvent ? event.originalEvent.offsetY : 0) : event.offsetX || event.layerX || (event.originalEvent ? event.originalEvent.offsetX : 0); 2120 2121 if (offset < (this[axis === 'vertical' ? 'offsetHeight' : 'offsetWidth'] / 2)) { 2122 removeClass(droppingAfterClassName); 2123 element.addClass(droppingBeforeClassName); 2124 2125 } else { 2126 removeClass(droppingBeforeClassName); 2127 element.addClass(droppingAfterClassName); 2128 } 2129 }; 2130 2131 var dropTimeout; 2132 2133 var dropHandler = function(event) { 2134 event.preventDefault(); 2135 2136 var droppedItemIndex = parseInt((event.dataTransfer || event.originalEvent.dataTransfer).getData('text'), 10); 2137 2138 // prevent event firing multiple times in firefox 2139 $timeout.cancel(dropTimeout); 2140 dropTimeout = $timeout(function() { 2141 _dropHandler(droppedItemIndex); 2142 }, 20); 2143 }; 2144 2145 var _dropHandler = function(droppedItemIndex) { 2146 var theList = scope.$eval(attrs.uiSelectSort); 2147 var itemToMove = theList[droppedItemIndex]; 2148 var newIndex = null; 2149 2150 if (element.hasClass(droppingBeforeClassName)) { 2151 if (droppedItemIndex < scope.$index) { 2152 newIndex = scope.$index - 1; 2153 } else { 2154 newIndex = scope.$index; 2155 } 2156 } else { 2157 if (droppedItemIndex < scope.$index) { 2158 newIndex = scope.$index; 2159 } else { 2160 newIndex = scope.$index + 1; 2161 } 2162 } 2163 2164 move.apply(theList, [droppedItemIndex, newIndex]); 2165 2166 $ngModel.$setViewValue(Date.now()); 2167 2168 scope.$apply(function() { 2169 scope.$emit('uiSelectSort:change', { 2170 array: theList, 2171 item: itemToMove, 2172 from: droppedItemIndex, 2173 to: newIndex 2174 }); 2175 }); 2176 2177 removeClass(droppingClassName); 2178 removeClass(droppingBeforeClassName); 2179 removeClass(droppingAfterClassName); 2180 2181 element.off('drop', dropHandler); 2182 }; 2183 2184 element.on('dragenter', function() { 2185 if (element.hasClass(draggingClassName)) { 2186 return; 2187 } 2188 2189 element.addClass(droppingClassName); 2190 2191 element.on('dragover', dragOverHandler); 2192 element.on('drop', dropHandler); 2193 }); 2194 2195 element.on('dragleave', function(event) { 2196 if (event.target != element) { 2197 return; 2198 } 2199 2200 removeClass(droppingClassName); 2201 removeClass(droppingBeforeClassName); 2202 removeClass(droppingAfterClassName); 2203 2204 element.off('dragover', dragOverHandler); 2205 element.off('drop', dropHandler); 2206 }); 2207 } 2208 }; 2209}]); 2210 2211/** 2212 * Debounces functions 2213 * 2214 * Taken from UI Bootstrap $$debounce source code 2215 * See https://github.com/angular-ui/bootstrap/blob/master/src/debounce/debounce.js 2216 * 2217 */ 2218uis.factory('$$uisDebounce', ['$timeout', function($timeout) { 2219 return function(callback, debounceTime) { 2220 var timeoutPromise; 2221 2222 return function() { 2223 var self = this; 2224 var args = Array.prototype.slice.call(arguments); 2225 if (timeoutPromise) { 2226 $timeout.cancel(timeoutPromise); 2227 } 2228 2229 timeoutPromise = $timeout(function() { 2230 callback.apply(self, args); 2231 }, debounceTime); 2232 }; 2233 }; 2234}]); 2235 2236uis.directive('uisOpenClose', ['$parse', '$timeout', function ($parse, $timeout) { 2237 return { 2238 restrict: 'A', 2239 require: 'uiSelect', 2240 link: function (scope, element, attrs, $select) { 2241 $select.onOpenCloseCallback = $parse(attrs.uisOpenClose); 2242 2243 scope.$watch('$select.open', function (isOpen, previousState) { 2244 if (isOpen !== previousState) { 2245 $timeout(function () { 2246 $select.onOpenCloseCallback(scope, { 2247 isOpen: isOpen 2248 }); 2249 }); 2250 } 2251 }); 2252 } 2253 }; 2254}]); 2255 2256/** 2257 * Parses "repeat" attribute. 2258 * 2259 * Taken from AngularJS ngRepeat source code 2260 * See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L211 2261 * 2262 * Original discussion about parsing "repeat" attribute instead of fully relying on ng-repeat: 2263 * https://github.com/angular-ui/ui-select/commit/5dd63ad#commitcomment-5504697 2264 */ 2265 2266uis.service('uisRepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinErr, $parse) { 2267 var self = this; 2268 2269 /** 2270 * Example: 2271 * expression = "address in addresses | filter: {street: $select.search} track by $index" 2272 * itemName = "address", 2273 * source = "addresses | filter: {street: $select.search}", 2274 * trackByExp = "$index", 2275 */ 2276 self.parse = function(expression) { 2277 2278 2279 var match; 2280 //var isObjectCollection = /\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)/.test(expression); 2281 // If an array is used as collection 2282 2283 // if (isObjectCollection){ 2284 // 000000000000000000000000000000111111111000000000000000222222222222220033333333333333333333330000444444444444444444000000000000000055555555555000000000000000000000066666666600000000 2285 match = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(\s*[\s\S]+?)?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); 2286 2287 // 1 Alias 2288 // 2 Item 2289 // 3 Key on (key,value) 2290 // 4 Value on (key,value) 2291 // 5 Source expression (including filters) 2292 // 6 Track by 2293 2294 if (!match) { 2295 throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", 2296 expression); 2297 } 2298 2299 var source = match[5], 2300 filters = ''; 2301 2302 // When using (key,value) ui-select requires filters to be extracted, since the object 2303 // is converted to an array for $select.items 2304 // (in which case the filters need to be reapplied) 2305 if (match[3]) { 2306 // Remove any enclosing parenthesis 2307 source = match[5].replace(/(^\()|(\)$)/g, ''); 2308 // match all after | but not after || 2309 var filterMatch = match[5].match(/^\s*(?:[\s\S]+?)(?:[^\|]|\|\|)+([\s\S]*)\s*$/); 2310 if(filterMatch && filterMatch[1].trim()) { 2311 filters = filterMatch[1]; 2312 source = source.replace(filters, ''); 2313 } 2314 } 2315 2316 return { 2317 itemName: match[4] || match[2], // (lhs) Left-hand side, 2318 keyName: match[3], //for (key, value) syntax 2319 source: $parse(source), 2320 filters: filters, 2321 trackByExp: match[6], 2322 modelMapper: $parse(match[1] || match[4] || match[2]), 2323 repeatExpression: function (grouped) { 2324 var expression = this.itemName + ' in ' + (grouped ? '$group.items' : '$select.items'); 2325 if (this.trackByExp) { 2326 expression += ' track by ' + this.trackByExp; 2327 } 2328 return expression; 2329 } 2330 }; 2331 2332 }; 2333 2334 self.getGroupNgRepeatExpression = function() { 2335 return '$group in $select.groups track by $group.name'; 2336 }; 2337 2338}]); 2339 2340}()); 2341angular.module("ui.select").run(["$templateCache", function($templateCache) {$templateCache.put("bootstrap/choices.tpl.html","<ul class=\"ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu\" role=\"listbox\" ng-show=\"$select.open && $select.items.length > 0\"><li class=\"ui-select-choices-group\" id=\"ui-select-choices-{{ $select.generatedId }}\"><div class=\"divider\" ng-show=\"$select.isGrouped && $index > 0\"></div><div ng-show=\"$select.isGrouped\" class=\"ui-select-choices-group-label dropdown-header\" ng-bind=\"$group.name\"></div><div ng-attr-id=\"ui-select-choices-row-{{ $select.generatedId }}-{{$index}}\" class=\"ui-select-choices-row\" ng-class=\"{active: $select.isActive(this), disabled: $select.isDisabled(this)}\" role=\"option\"><span class=\"ui-select-choices-row-inner\"></span></div></li></ul>"); 2342$templateCache.put("bootstrap/match-multiple.tpl.html","<span class=\"ui-select-match\"><span ng-repeat=\"$item in $select.selected track by $index\"><span class=\"ui-select-match-item btn btn-default btn-xs\" tabindex=\"-1\" type=\"button\" ng-disabled=\"$select.disabled\" ng-click=\"$selectMultiple.activeMatchIndex = $index;\" ng-class=\"{\'btn-primary\':$selectMultiple.activeMatchIndex === $index, \'select-locked\':$select.isLocked(this, $index)}\" ui-select-sort=\"$select.selected\"><span class=\"close ui-select-match-close\" ng-hide=\"$select.disabled\" ng-click=\"$selectMultiple.removeChoice($index)\"> ×</span>
2342 <span uis-transclude-append=\"\"></span></span></span></span>"); 2343$templateCache.put("bootstrap/match.tpl.html","<div class=\"ui-select-match\" ng-hide=\"$select.open && $select.searchEnabled\" ng-disabled=\"$select.disabled\" ng-class=\"{\'btn-default-focus\':$select.focus}\"><span tabindex=\"-1\" class=\"btn btn-default form-control ui-select-toggle\" aria-label=\"{{ $select.baseTitle }} activate\" ng-disabled=\"$select.disabled\" ng-click=\"$select.activate()\" style=\"outline: 0;\"><span ng-show=\"$select.isEmpty()\" class=\"ui-select-placeholder text-muted\">{{$select.placeholder}}</span> <span ng-hide=\"$select.isEmpty()\" class=\"ui-select-match-text pull-left\" ng-class=\"{\'ui-select-allow-clear\': $select.allowClear && !$select.isEmpty()}\" ng-transclude=\"\"></span> <i class=\"caret pull-right\" ng-click=\"$select.toggle($event)\"></i> <a ng-show=\"$select.allowClear && !$select.isEmpty() && ($select.disabled !== true)\" aria-label=\"{{ $select.baseTitle }} clear\" style=\"margin-right: 10px\" ng-click=\"$select.clear($event)\" class=\"btn btn-xs btn-link pull-right\"><i class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\"></i></a></span></div>"); 2344$templateCache.put("bootstrap/no-choice.tpl.html","<ul class=\"ui-select-no-choice dropdown-menu\" ng-show=\"$select.items.length == 0\"><li ng-transclude=\"\"></li></ul>"); 2345$templateCache.put("bootstrap/select-multiple.tpl.html","<div class=\"ui-select-container ui-select-multiple ui-select-bootstrap dropdown form-control\" ng-class=\"{open: $select.open}\"><div><div class=\"ui-select-match\"></div><input type=\"search\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" class=\"ui-select-search input-xs\" placeholder=\"{{$selectMultiple.getPlaceholder()}}\" ng-disabled=\"$select.disabled\" ng-click=\"$select.activate()\" ng-model=\"$select.search\" role=\"combobox\" aria-label=\"{{ $select.baseTitle }}\" ondrop=\"return false;\"></div><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></div></div>"); 2346$templateCache.put("bootstrap/select.tpl.html","<div class=\"ui-select-container ui-select-bootstrap dropdown\" ng-class=\"{open: $select.open}\"><div class=\"ui-select-match\"></div><input type=\"search\" autocomplete=\"off\" tabindex=\"-1\" aria-expanded=\"true\" aria-label=\"{{ $select.baseTitle }}\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" aria-activedescendant=\"ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}\" class=\"form-control ui-select-search\" ng-class=\"{ \'ui-select-search-hidden\' : !$select.searchEnabled }\" placeholder=\"{{$select.placeholder}}\" ng-model=\"$select.search\" ng-show=\"$select.open\"><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></div></div>"); 2347$templateCache.put("select2/choices.tpl.html","<ul tabindex=\"-1\" class=\"ui-select-choices ui-select-choices-content select2-results\"><li class=\"ui-select-choices-group\" ng-class=\"{\'select2-result-with-children\': $select.choiceGrouped($group) }\"><div ng-show=\"$select.choiceGrouped($group)\" class=\"ui-select-choices-group-label select2-result-label\" ng-bind=\"$group.name\"></div><ul role=\"listbox\" id=\"ui-select-choices-{{ $select.generatedId }}\" ng-class=\"{\'select2-result-sub\': $select.choiceGrouped($group), \'select2-result-single\': !$select.choiceGrouped($group) }\"><li role=\"option\" ng-attr-id=\"ui-select-choices-row-{{ $select.generatedId }}-{{$index}}\" class=\"ui-select-choices-row\" ng-class=\"{\'select2-highlighted\': $select.isActive(this), \'select2-disabled\': $select.isDisabled(this)}\"><div class=\"select2-result-label ui-select-choices-row-inner\"></div></li></ul></li></ul>"); 2348$templateCache.put("select2/match-multiple.tpl.html","<span class=\"ui-select-match\"><li class=\"ui-select-match-item select2-search-choice\" ng-repeat=\"$item in $select.selected track by $index\" ng-class=\"{\'select2-search-choice-focus\':$selectMultiple.activeMatchIndex === $index, \'select2-locked\':$select.isLocked(this, $index)}\" ui-select-sort=\"$select.selected\">
2348<span uis-transclude-append=\"\"></span> <a href=\"javascript:;\" class=\"ui-select-match-close select2-search-choice-close\" ng-click=\"$selectMultiple.removeChoice($index)\" tabindex=\"-1\"></a></li></span>"); 2349$templateCache.put("select2/match.tpl.html","<a class=\"select2-choice ui-select-match\" ng-class=\"{\'select2-default\': $select.isEmpty()}\" ng-click=\"$select.toggle($event)\" aria-label=\"{{ $select.baseTitle }} select\"><span ng-show=\"$select.isEmpty()\" class=\"select2-chosen\">{{$select.placeholder}}</span> <span ng-hide=\"$select.isEmpty()\" class=\"select2-chosen\" ng-transclude=\"\"></span> <abbr ng-if=\"$select.allowClear && !$select.isEmpty()\" class=\"select2-search-choice-close\" ng-click=\"$select.clear($event)\"></abbr> <span class=\"select2-arrow ui-select-toggle\"><b></b></span></a>"); 2350$templateCache.put("select2/no-choice.tpl.html","<div class=\"ui-select-no-choice dropdown\" ng-show=\"$select.items.length == 0\"><div class=\"dropdown-content\"><div data-selectable=\"\" ng-transclude=\"\"></div></div></div>"); 2351$templateCache.put("select2/select-multiple.tpl.html","<div class=\"ui-select-container ui-select-multiple select2 select2-container select2-container-multi\" ng-class=\"{\'select2-container-active select2-dropdown-open open\': $select.open, \'select2-container-disabled\': $select.disabled}\"><ul class=\"select2-choices\"><span class=\"ui-select-match\"></span><li class=\"select2-search-field\"><input type=\"search\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" role=\"combobox\" aria-expanded=\"true\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" aria-label=\"{{ $select.baseTitle }}\" aria-activedescendant=\"ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}\" class=\"select2-input ui-select-search\" placeholder=\"{{$selectMultiple.getPlaceholder()}}\" ng-disabled=\"$select.disabled\" ng-hide=\"$select.disabled\" ng-model=\"$select.search\" ng-click=\"$select.activate()\" style=\"width: 34px;\" ondrop=\"return false;\"></li></ul><div class=\"ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active\" ng-class=\"{\'select2-display-none\': !$select.open || $select.items.length === 0}\"><div class=\"ui-select-choices\"></div></div></div>"); 2352$templateCache.put("select2/select.tpl.html","<div class=\"ui-select-container select2 select2-container\" ng-class=\"{\'select2-container-active select2-dropdown-open open\': $select.open, \'select2-container-disabled\': $select.disabled, \'select2-container-active\': $select.focus, \'select2-allowclear\': $select.allowClear && !$select.isEmpty()}\"><div class=\"ui-select-match\"></div><div class=\"ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active\" ng-class=\"{\'select2-display-none\': !$select.open}\"><div class=\"search-container\" ng-class=\"{\'ui-select-search-hidden\':!$select.searchEnabled, \'select2-search\':$select.searchEnabled}\"><input type=\"search\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" role=\"combobox\" aria-expanded=\"true\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" aria-label=\"{{ $select.baseTitle }}\" aria-activedescendant=\"ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}\" class=\"ui-select-search select2-input\" ng-model=\"$select.search\"></div><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></div></div></div>"); 2353$templateCache.put("selectize/choices.tpl.html","<div ng-show=\"$select.open\" class=\"ui-select-choices ui-select-dropdown selectize-dropdown single\"><div class=\"ui-select-choices-content selectize-dropdown-content\"><div class=\"ui-select-choices-group optgroup\" role=\"listbox\"><div ng-show=\"$select.isGrouped\" class=\"ui-select-choices-group-label optgroup-header\" ng-bind=\"$group.name\"></div><div role=\"option\" class=\"ui-select-choices-row\" ng-class=\"{active: $select.isActive(this), disabled: $select.isDisabled(this)}\"><div class=\"option ui-select-choices-row-inner\" data-selectable=\"\"></div></div></div></div></div>"); 2354$templateCache.put("selectize/match.tpl.html","<div ng-hide=\"$select.searchEnabled && ($select.open || $select.isEmpty())\" class=\"ui-select-match\"><span ng-show=\"!$select.searchEnabled && ($select.isEmpty() || $select.open)\" class=\"ui-select-placeholder text-muted\">{{$select.placeholder}}</span> <span ng-hide=\"$select.isEmpty() || $select.open\" ng-transclude=\"\"></span></div>"); 2355$templateCache.put("selectize/no-choice.tpl.html","<div class=\"ui-select-no-choice selectize-dropdown\" ng-show=\"$select.items.length == 0\"><div class=\"selectize-dropdown-content\"><div data-selectable=\"\" ng-transclude=\"\"></div></div></div>"); 2356$templateCache.put("selectize/select.tpl.html","<div class=\"ui-select-container selectize-control single\" ng-class=\"{\'open\': $select.open}\"><div class=\"selectize-input\" ng-class=\"{\'focus\': $select.open, \'disabled\': $select.disabled, \'selectize-focus\' : $select.focus}\" ng-click=\"$select.open && !$select.searchEnabled ? $select.toggle($event) : $select.activate()\"><div class=\"ui-select-match\"></div><input type=\"search\" autocomplete=\"off\" tabindex=\"-1\" class=\"ui-select-search ui-select-toggle\" ng-class=\"{\'ui-select-search-hidden\':!$select.searchEnabled}\" ng-click=\"$select.toggle($event)\" placeholder=\"{{$select.placeholder}}
2356\" ng-model=\"$select.search\" ng-hide=\"!$select.isEmpty() && !$select.open\" ng-disabled=\"$select.disabled\" aria-label=\"{{ $select.baseTitle }}\"></div><div class=\"ui-select-choices\"></div><div class=\"ui-select-no-choice\"></div></div>");}]); 2357 2358
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.