angular-autocomplete.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* --- Made by justgoscha and licensed under MIT license --- */
  2. define(["libs/angular"], function(){
  3. var app = angular.module('autocomplete', []);
  4. app.directive('autocomplete', function() {
  5. var index = -1;
  6. return {
  7. restrict: 'E',
  8. scope: {
  9. searchParam: '=ngModel',
  10. suggestions: '=data',
  11. onType: '=onType',
  12. onSelect: '=onSelect'
  13. },
  14. controller: ['$scope', function($scope){
  15. // the index of the suggestions that's currently selected
  16. $scope.selectedIndex = -1;
  17. // set new index
  18. $scope.setIndex = function(i){
  19. $scope.selectedIndex = parseInt(i);
  20. };
  21. this.setIndex = function(i){
  22. $scope.setIndex(i);
  23. $scope.$apply();
  24. };
  25. $scope.getIndex = function(i){
  26. return $scope.selectedIndex;
  27. };
  28. // watches if the parameter filter should be changed
  29. var watching = true;
  30. // autocompleting drop down on/off
  31. $scope.completing = false;
  32. // starts autocompleting on typing in something
  33. $scope.$watch('searchParam', function(newValue, oldValue){
  34. if (oldValue === newValue) {
  35. return;
  36. }
  37. if(watching && $scope.searchParam) {
  38. $scope.completing = true;
  39. $scope.searchFilter = $scope.searchParam;
  40. $scope.selectedIndex = -1;
  41. }
  42. // function thats passed to on-type attribute gets executed
  43. if($scope.onType)
  44. $scope.onType($scope.searchParam);
  45. });
  46. // for hovering over suggestions
  47. this.preSelect = function(suggestion){
  48. watching = false;
  49. // this line determines if it is shown
  50. // in the input field before it's selected:
  51. //$scope.searchParam = suggestion;
  52. $scope.$apply();
  53. watching = true;
  54. };
  55. $scope.preSelect = this.preSelect;
  56. this.preSelectOff = function(){
  57. watching = true;
  58. };
  59. $scope.preSelectOff = this.preSelectOff;
  60. // selecting a suggestion with RIGHT ARROW or ENTER
  61. $scope.select = function(suggestion){
  62. if(suggestion){
  63. $scope.searchParam = suggestion;
  64. $scope.searchFilter = suggestion;
  65. if($scope.onSelect)
  66. $scope.onSelect(suggestion);
  67. }
  68. watching = false;
  69. $scope.completing = false;
  70. setTimeout(function(){watching = true;},1000);
  71. $scope.setIndex(-1);
  72. };
  73. }],
  74. link: function(scope, element, attrs){
  75. var attr = '';
  76. // Default atts
  77. scope.attrs = {
  78. "placeholder": "start typing...",
  79. "class": "",
  80. "id": "",
  81. "inputclass": "",
  82. "inputid": ""
  83. };
  84. for (var a in attrs) {
  85. attr = a.replace('attr', '').toLowerCase();
  86. // add attribute overriding defaults
  87. // and preventing duplication
  88. if (a.indexOf('attr') === 0) {
  89. scope.attrs[attr] = attrs[a];
  90. }
  91. }
  92. if (attrs.clickActivation) {
  93. element[0].onclick = function(e){
  94. if(!scope.searchParam){
  95. scope.completing = true;
  96. scope.$apply();
  97. }
  98. };
  99. }
  100. var key = {left: 37, up: 38, right: 39, down: 40 , enter: 13, esc: 27};
  101. document.addEventListener("keydown", function(e){
  102. var keycode = e.keyCode || e.which;
  103. switch (keycode){
  104. case key.esc:
  105. // disable suggestions on escape
  106. scope.select();
  107. scope.setIndex(-1);
  108. scope.$apply();
  109. e.preventDefault();
  110. }
  111. }, true);
  112. document.addEventListener("blur", function(e){
  113. // disable suggestions on blur
  114. // we do a timeout to prevent hiding it before a click event is registered
  115. setTimeout(function() {
  116. scope.select();
  117. scope.setIndex(-1);
  118. scope.$apply();
  119. }, 200);
  120. }, true);
  121. element[0].addEventListener("keydown",function (e){
  122. var keycode = e.keyCode || e.which;
  123. var l = angular.element(this).find('li').length;
  124. // implementation of the up and down movement in the list of suggestions
  125. switch (keycode){
  126. case key.up:
  127. index = scope.getIndex()-1;
  128. if(index<-1){
  129. index = l-1;
  130. } else if (index >= l ){
  131. index = -1;
  132. scope.setIndex(index);
  133. scope.preSelectOff();
  134. break;
  135. }
  136. scope.setIndex(index);
  137. if(index!==-1)
  138. scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
  139. scope.$apply();
  140. break;
  141. case key.down:
  142. index = scope.getIndex()+1;
  143. if(index<-1){
  144. index = l-1;
  145. } else if (index >= l ){
  146. index = -1;
  147. scope.setIndex(index);
  148. scope.preSelectOff();
  149. scope.$apply();
  150. break;
  151. }
  152. scope.setIndex(index);
  153. if(index!==-1)
  154. scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
  155. break;
  156. case key.left:
  157. break;
  158. case key.right:
  159. case key.enter:
  160. index = scope.getIndex();
  161. // scope.preSelectOff();
  162. if(index !== -1)
  163. scope.select(angular.element(angular.element(this).find('li')[index]).text());
  164. scope.setIndex(-1);
  165. scope.$apply();
  166. break;
  167. case key.esc:
  168. // disable suggestions on escape
  169. scope.select();
  170. scope.setIndex(-1);
  171. scope.$apply();
  172. e.preventDefault();
  173. break;
  174. default:
  175. return;
  176. }
  177. if(scope.getIndex()!==-1 || keycode == key.enter)
  178. e.preventDefault();
  179. });
  180. },
  181. templateUrl: 'js/libs/angular-autocomplete/ac_template.html'
  182. };
  183. });
  184. app.filter('highlight', ['$sce', function ($sce) {
  185. return function (input, searchParam) {
  186. if (typeof input === 'function') return '';
  187. if (searchParam) {
  188. var words = '(' +
  189. searchParam.split(/\ /).join(' |') + '|' +
  190. searchParam.split(/\ /).join('|') +
  191. ')',
  192. exp = new RegExp(words, 'gi');
  193. if (words.length) {
  194. input = input.replace(exp, "<span class=\"highlight\">$1</span>");
  195. }
  196. }
  197. return $sce.trustAsHtml(input);
  198. };
  199. }]);
  200. app.directive('suggestion', function(){
  201. return {
  202. restrict: 'A',
  203. require: '^autocomplete', // ^look for controller on parents element
  204. link: function(scope, element, attrs, autoCtrl){
  205. element.bind('mouseenter', function() {
  206. autoCtrl.preSelect(attrs.val);
  207. autoCtrl.setIndex(attrs.index);
  208. });
  209. element.bind('mouseleave', function() {
  210. autoCtrl.preSelectOff();
  211. });
  212. }
  213. };
  214. });
  215. })