Razique Mahroua
2019-11-28 1759c24ad2d2b35ec5c756e3dd3a60185fe944b7
commit | author | age
1759c2 1 /**
RM 2  * angular-strap
3  * @version v2.0.3 - 2014-05-30
4  * @link http://mgcrea.github.io/angular-strap
5  * @author Olivier Louvignes (olivier@mg-crea.com)
6  * @license MIT License, http://www.opensource.org/licenses/MIT
7  */
8 'use strict';
9 angular.module('mgcrea.ngStrap.typeahead', [
10   'mgcrea.ngStrap.tooltip',
11   'mgcrea.ngStrap.helpers.parseOptions'
12 ]).provider('$typeahead', function () {
13   var defaults = this.defaults = {
14       animation: 'am-fade',
15       prefixClass: 'typeahead',
16       placement: 'bottom-left',
17       template: 'typeahead/typeahead.tpl.html',
18       trigger: 'focus',
19       container: false,
20       keyboard: true,
21       html: false,
22       delay: 0,
23       minLength: 1,
24       filter: 'filter',
25       limit: 6
26     };
27   this.$get = [
28     '$window',
29     '$rootScope',
30     '$tooltip',
31     function ($window, $rootScope, $tooltip) {
32       var bodyEl = angular.element($window.document.body);
33       function TypeaheadFactory(element, controller, config) {
34         var $typeahead = {};
35         // Common vars
36         var options = angular.extend({}, defaults, config);
37         $typeahead = $tooltip(element, options);
38         var parentScope = config.scope;
39         var scope = $typeahead.$scope;
40         scope.$resetMatches = function () {
41           scope.$matches = [];
42           scope.$activeIndex = 0;
43         };
44         scope.$resetMatches();
45         scope.$activate = function (index) {
46           scope.$$postDigest(function () {
47             $typeahead.activate(index);
48           });
49         };
50         scope.$select = function (index, evt) {
51           scope.$$postDigest(function () {
52             $typeahead.select(index);
53           });
54         };
55         scope.$isVisible = function () {
56           return $typeahead.$isVisible();
57         };
58         // Public methods
59         $typeahead.update = function (matches) {
60           scope.$matches = matches;
61           if (scope.$activeIndex >= matches.length) {
62             scope.$activeIndex = 0;
63           }
64         };
65         $typeahead.activate = function (index) {
66           scope.$activeIndex = index;
67         };
68         $typeahead.select = function (index) {
69           var value = scope.$matches[index].value;
70           controller.$setViewValue(value);
71           controller.$render();
72           scope.$resetMatches();
73           if (parentScope)
74             parentScope.$digest();
75           // Emit event
76           scope.$emit('$typeahead.select', value, index);
77         };
78         // Protected methods
79         $typeahead.$isVisible = function () {
80           if (!options.minLength || !controller) {
81             return !!scope.$matches.length;
82           }
83           // minLength support
84           return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
85         };
86         $typeahead.$getIndex = function (value) {
87           var l = scope.$matches.length, i = l;
88           if (!l)
89             return;
90           for (i = l; i--;) {
91             if (scope.$matches[i].value === value)
92               break;
93           }
94           if (i < 0)
95             return;
96           return i;
97         };
98         $typeahead.$onMouseDown = function (evt) {
99           // Prevent blur on mousedown
100           evt.preventDefault();
101           evt.stopPropagation();
102         };
103         $typeahead.$onKeyDown = function (evt) {
104           if (!/(38|40|13)/.test(evt.keyCode))
105             return;
106           evt.preventDefault();
107           evt.stopPropagation();
108           // Select with enter
109           if (evt.keyCode === 13 && scope.$matches.length) {
110             $typeahead.select(scope.$activeIndex);
111           }  // Navigate with keyboard
112           else if (evt.keyCode === 38 && scope.$activeIndex > 0)
113             scope.$activeIndex--;
114           else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1)
115             scope.$activeIndex++;
116           else if (angular.isUndefined(scope.$activeIndex))
117             scope.$activeIndex = 0;
118           scope.$digest();
119         };
120         // Overrides
121         var show = $typeahead.show;
122         $typeahead.show = function () {
123           show();
124           setTimeout(function () {
125             $typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
126             if (options.keyboard) {
127               element.on('keydown', $typeahead.$onKeyDown);
128             }
129           });
130         };
131         var hide = $typeahead.hide;
132         $typeahead.hide = function () {
133           $typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
134           if (options.keyboard) {
135             element.off('keydown', $typeahead.$onKeyDown);
136           }
137           hide();
138         };
139         return $typeahead;
140       }
141       TypeaheadFactory.defaults = defaults;
142       return TypeaheadFactory;
143     }
144   ];
145 }).directive('bsTypeahead', [
146   '$window',
147   '$parse',
148   '$q',
149   '$typeahead',
150   '$parseOptions',
151   function ($window, $parse, $q, $typeahead, $parseOptions) {
152     var defaults = $typeahead.defaults;
153     return {
154       restrict: 'EAC',
155       require: 'ngModel',
156       link: function postLink(scope, element, attr, controller) {
157         // Directive options
158         var options = { scope: scope };
159         angular.forEach([
160           'placement',
161           'container',
162           'delay',
163           'trigger',
164           'keyboard',
165           'html',
166           'animation',
167           'template',
168           'filter',
169           'limit',
170           'minLength'
171         ], function (key) {
172           if (angular.isDefined(attr[key]))
173             options[key] = attr[key];
174         });
175         // Build proper ngOptions
176         var filter = options.filter || defaults.filter;
177         var limit = options.limit || defaults.limit;
178         var ngOptions = attr.ngOptions;
179         if (filter)
180           ngOptions += ' | ' + filter + ':$viewValue';
181         if (limit)
182           ngOptions += ' | limitTo:' + limit;
183         var parsedOptions = $parseOptions(ngOptions);
184         // Initialize typeahead
185         var typeahead = $typeahead(element, controller, options);
186         // Watch model for changes
187         scope.$watch(attr.ngModel, function (newValue, oldValue) {
188           // console.warn('$watch', element.attr('ng-model'), newValue);
189           scope.$modelValue = newValue;
190           // Publish modelValue on scope for custom templates
191           parsedOptions.valuesFn(scope, controller).then(function (values) {
192             if (values.length > limit)
193               values = values.slice(0, limit);
194             // Do not re-queue an update if a correct value has been selected
195             if (values.length === 1 && values[0].value === newValue)
196               return;
197             typeahead.update(values);
198             // Queue a new rendering that will leverage collection loading
199             controller.$render();
200           });
201         });
202         // Model rendering in view
203         controller.$render = function () {
204           // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
205           if (controller.$isEmpty(controller.$viewValue))
206             return element.val('');
207           var index = typeahead.$getIndex(controller.$modelValue);
208           var selected = angular.isDefined(index) ? typeahead.$scope.$matches[index].label : controller.$viewValue;
209           selected = angular.isObject(selected) ? selected.label : selected;
210           element.val(selected.replace(/<(?:.|\n)*?>/gm, '').trim());
211         };
212         // Garbage collection
213         scope.$on('$destroy', function () {
214           typeahead.destroy();
215           options = null;
216           typeahead = null;
217         });
218       }
219     };
220   }
221 ]);