/*! * ui-select * http://github.com/angular-ui/ui-select * Version: 0.11.1 - 2015-03-09T14:30:26.109Z * License: MIT */ (function () { "use strict"; var KEY = { TAB: 9, ENTER: 13, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, SHIFT: 16, CTRL: 17, ALT: 18, PAGE_UP: 33, PAGE_DOWN: 34, HOME: 36, END: 35, BACKSPACE: 8, DELETE: 46, COMMAND: 91, MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'" }, isControl: function (e) { var k = e.which; switch (k) { case KEY.COMMAND: case KEY.SHIFT: case KEY.CTRL: case KEY.ALT: return true; } if (e.metaKey) return true; return false; }, isFunctionKey: function (k) { k = k.which ? k.which : k; return k >= 112 && k <= 123; }, isVerticalMovement: function (k){ return ~[KEY.UP, KEY.DOWN].indexOf(k); }, isHorizontalMovement: function (k){ return ~[KEY.LEFT,KEY.RIGHT,KEY.BACKSPACE,KEY.DELETE].indexOf(k); } }; /** * Add querySelectorAll() to jqLite. * * jqLite find() is limited to lookups by tag name. * TODO This will change with future versions of AngularJS, to be removed when this happens * * See jqLite.find - why not use querySelectorAll? https://github.com/angular/angular.js/issues/3586 * See feat(jqLite): use querySelectorAll instead of getElementsByTagName in jqLite.find https://github.com/angular/angular.js/pull/3598 */ if (angular.element.prototype.querySelectorAll === undefined) { angular.element.prototype.querySelectorAll = function(selector) { return angular.element(this[0].querySelectorAll(selector)); }; } /** * Add closest() to jqLite. */ if (angular.element.prototype.closest === undefined) { angular.element.prototype.closest = function( selector) { var elem = this[0]; var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector; while (elem) { if (matchesSelector.bind(elem)(selector)) { return elem; } else { elem = elem.parentElement; } } return false; }; } var latestId = 0; var uis = angular.module('ui.select', []) .constant('uiSelectConfig', { theme: 'bootstrap', searchEnabled: true, sortable: false, placeholder: '', // Empty by default, like HTML tag "); 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); } else { focusser.attr("tabindex", value); } //Remove the tabindex on the parent so that it is not focusable element.removeAttr("tabindex"); }); } $compile(focusser)(scope); $select.focusser = focusser; if (!$select.multiple){ element.append(focusser); focusser.bind("focus", function(){ scope.$evalAsync(function(){ $select.focus = true; }); }); focusser.bind("blur", function(){ scope.$evalAsync(function(){ $select.focus = false; }); }); focusser.bind("keydown", function(e){ if (e.which === KEY.BACKSPACE) { e.preventDefault(); e.stopPropagation(); $select.select(undefined); scope.$apply(); return; } if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { return; } if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ e.preventDefault(); e.stopPropagation(); $select.activate(); } scope.$digest(); }); focusser.bind("keyup input", function(e){ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { return; } $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input focusser.val(''); scope.$digest(); }); } scope.$watch('searchEnabled', function() { var searchEnabled = scope.$eval(attrs.searchEnabled); $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; }); 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; if ($select.multiple) { // As the search input field may now become visible, it may be necessary to recompute its size $select.sizeSearchInput(); } }); 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}; } else { $select.tagging = {isActivated: false, fct: undefined}; } }); attrs.$observe('taggingLabel', function() { if(attrs.tagging !== undefined ) { // check eval for FALSE, in this case, we disable the labels // associated with tagging if ( attrs.taggingLabel === 'false' ) { $select.taggingLabel = false; } else { $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; } } }); attrs.$observe('taggingTokens', function() { if (attrs.tagging !== undefined) { var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; $select.taggingTokens = {isActivated: true, tokens: tokens }; } }); //Automatically gets focus when loaded if (angular.isDefined(attrs.autofocus)){ $timeout(function(){ $select.setFocus(); }); } //Gets focus based on scope event name (e.g. focus-on='SomeEventName') if (angular.isDefined(attrs.focusOn)){ scope.$on(attrs.focusOn, function() { $timeout(function(){ $select.setFocus(); }); }); } 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 } else { $select.firstPass = false; } }); focusser.prop('disabled', true); //Focusser isn't needed if multiple }else{ scope.$watch('$select.selected', function(newValue) { if (ngModel.$viewValue !== newValue) { ngModel.$setViewValue(newValue); } }); } 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 = []; } else { throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); } } } $select.selected = ngModel.$viewValue; }; function onDocumentClick(e) { if (!$select.open) return; //Skip it if dropdown is close var contains = false; if (window.jQuery) { // Firefox 3.6 does not support element.contains() // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains contains = window.jQuery.contains(element[0], e.target); } else { contains = element[0].contains(e.target); } if (!contains && !$select.clickTriggeredSelect) { //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; } // See Click everywhere but here event http://stackoverflow.com/questions/12931369 $document.on('click', onDocumentClick); scope.$on('$destroy', function() { $document.off('click', onDocumentClick); }); // Move transcluded elements to their correct position in main template transcludeFn(scope, function(clone) { // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html // One day jqLite will be replaced by jQuery and we will be able to write: // var transcludedElement = clone.filter('.my-class') // instead of creating a hackish DOM element: var transcluded = angular.element('
').append(clone); var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes if (transcludedMatch.length !== 1) { throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); } element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes if (transcludedChoices.length !== 1) { throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); } element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); }); } }; }]); 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; }); $select.allowClear = (angular.isDefined(attrs.allowClear)) ? (attrs.allowClear === '') ? true : (attrs.allowClear.toLowerCase() === 'true') : false; if($select.multiple){ $select.sizeSearchInput(); } } }; }]); // Make multiple matches sortable uis.directive('uiSelectSort', ['$timeout', 'uiSelectConfig', 'uiSelectMinErr', function($timeout, uiSelectConfig, uiSelectMinErr) { return { require: '^uiSelect', link: function(scope, element, attrs, $select) { if (scope[attrs.uiSelectSort] === null) { throw uiSelectMinErr('sort', "Expected a list to sort"); } var options = angular.extend({ axis: 'horizontal' }, scope.$eval(attrs.uiSelectSortOptions)); var axis = options.axis, draggingClassName = 'dragging', droppingClassName = 'dropping', droppingBeforeClassName = 'dropping-before', droppingAfterClassName = 'dropping-after'; scope.$watch(function(){ return $select.sortable; }, function(n){ if (n) { element.attr('draggable', true); } else { element.removeAttr('draggable'); } }); element.on('dragstart', function(e) { element.addClass(draggingClassName); (e.dataTransfer || e.originalEvent.dataTransfer).setData('text/plain', scope.$index); }); element.on('dragend', function() { element.removeClass(draggingClassName); }); var move = function(from, to) { /*jshint validthis: true */ this.splice(to, 0, this.splice(from, 1)[0]); }; var dragOverHandler = function(e) { e.preventDefault(); var offset = axis === 'vertical' ? e.offsetY || e.layerY || (e.originalEvent ? e.originalEvent.offsetY : 0) : e.offsetX || e.layerX || (e.originalEvent ? e.originalEvent.offsetX : 0); if (offset < (this[axis === 'vertical' ? 'offsetHeight' : 'offsetWidth'] / 2)) { element.removeClass(droppingAfterClassName); element.addClass(droppingBeforeClassName); } else { element.removeClass(droppingBeforeClassName); element.addClass(droppingAfterClassName); } }; var dropTimeout; var dropHandler = function(e) { e.preventDefault(); var droppedItemIndex = parseInt((e.dataTransfer || e.originalEvent.dataTransfer).getData('text/plain'), 10); // prevent event firing multiple times in firefox $timeout.cancel(dropTimeout); dropTimeout = $timeout(function() { _dropHandler(droppedItemIndex); }, 20); }; var _dropHandler = function(droppedItemIndex) { var theList = scope.$eval(attrs.uiSelectSort), itemToMove = theList[droppedItemIndex], newIndex = null; if (element.hasClass(droppingBeforeClassName)) { if (droppedItemIndex < scope.$index) { newIndex = scope.$index - 1; } else { newIndex = scope.$index; } } else { if (droppedItemIndex < scope.$index) { newIndex = scope.$index; } else { newIndex = scope.$index + 1; } } move.apply(theList, [droppedItemIndex, newIndex]); scope.$apply(function() { scope.$emit('uiSelectSort:change', { array: theList, item: itemToMove, from: droppedItemIndex, to: newIndex }); }); element.removeClass(droppingClassName); element.removeClass(droppingBeforeClassName); element.removeClass(droppingAfterClassName); element.off('drop', dropHandler); }; element.on('dragenter', function() { if (element.hasClass(draggingClassName)) { return; } element.addClass(droppingClassName); element.on('dragover', dragOverHandler); element.on('drop', dropHandler); }); element.on('dragleave', function() { element.removeClass(droppingClassName); element.removeClass(droppingBeforeClassName); element.removeClass(droppingAfterClassName); element.off('dragover', dragOverHandler); element.off('drop', dropHandler); }); } }; }]); }()); angular.module("ui.select").run(["$templateCache", function($templateCache) {$templateCache.put("bootstrap/choices.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.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/select.tpl.html","
    "); $templateCache.put("selectize/choices.tpl.html","
    "); $templateCache.put("selectize/match.tpl.html","
    "); $templateCache.put("selectize/select.tpl.html","
    ");}]);