overleaf/services/web/public/js/libs/angular-autocomplete/angular-autocomplete.js
2014-06-16 16:26:34 +01:00

264 lines
6.8 KiB
JavaScript

/* --- Made by justgoscha and licensed under MIT license --- */
define(["libs/angular"], function(){
var app = angular.module('autocomplete', []);
app.directive('autocomplete', function() {
var index = -1;
return {
restrict: 'E',
scope: {
searchParam: '=ngModel',
suggestions: '=data',
onType: '=onType',
onSelect: '=onSelect'
},
controller: ['$scope', function($scope){
// the index of the suggestions that's currently selected
$scope.selectedIndex = -1;
// set new index
$scope.setIndex = function(i){
$scope.selectedIndex = parseInt(i);
};
this.setIndex = function(i){
$scope.setIndex(i);
$scope.$apply();
};
$scope.getIndex = function(i){
return $scope.selectedIndex;
};
// watches if the parameter filter should be changed
var watching = true;
// autocompleting drop down on/off
$scope.completing = false;
// starts autocompleting on typing in something
$scope.$watch('searchParam', function(newValue, oldValue){
if (oldValue === newValue) {
return;
}
if(watching && $scope.searchParam) {
$scope.completing = true;
$scope.searchFilter = $scope.searchParam;
$scope.selectedIndex = -1;
}
// function thats passed to on-type attribute gets executed
if($scope.onType)
$scope.onType($scope.searchParam);
});
// for hovering over suggestions
this.preSelect = function(suggestion){
watching = false;
// this line determines if it is shown
// in the input field before it's selected:
//$scope.searchParam = suggestion;
$scope.$apply();
watching = true;
};
$scope.preSelect = this.preSelect;
this.preSelectOff = function(){
watching = true;
};
$scope.preSelectOff = this.preSelectOff;
// selecting a suggestion with RIGHT ARROW or ENTER
$scope.select = function(suggestion){
if(suggestion){
$scope.searchParam = suggestion;
$scope.searchFilter = suggestion;
if($scope.onSelect)
$scope.onSelect(suggestion);
}
watching = false;
$scope.completing = false;
setTimeout(function(){watching = true;},1000);
$scope.setIndex(-1);
};
}],
link: function(scope, element, attrs){
var attr = '';
// Default atts
scope.attrs = {
"placeholder": "start typing...",
"class": "",
"id": "",
"inputclass": "",
"inputid": ""
};
for (var a in attrs) {
attr = a.replace('attr', '').toLowerCase();
// add attribute overriding defaults
// and preventing duplication
if (a.indexOf('attr') === 0) {
scope.attrs[attr] = attrs[a];
}
}
if (attrs.clickActivation) {
element[0].onclick = function(e){
if(!scope.searchParam){
scope.completing = true;
scope.$apply();
}
};
}
var key = {left: 37, up: 38, right: 39, down: 40 , enter: 13, esc: 27};
document.addEventListener("keydown", function(e){
var keycode = e.keyCode || e.which;
switch (keycode){
case key.esc:
// disable suggestions on escape
scope.select();
scope.setIndex(-1);
scope.$apply();
e.preventDefault();
}
}, true);
document.addEventListener("blur", function(e){
// disable suggestions on blur
// we do a timeout to prevent hiding it before a click event is registered
setTimeout(function() {
scope.select();
scope.setIndex(-1);
scope.$apply();
}, 200);
}, true);
element[0].addEventListener("keydown",function (e){
var keycode = e.keyCode || e.which;
var l = angular.element(this).find('li').length;
// implementation of the up and down movement in the list of suggestions
switch (keycode){
case key.up:
index = scope.getIndex()-1;
if(index<-1){
index = l-1;
} else if (index >= l ){
index = -1;
scope.setIndex(index);
scope.preSelectOff();
break;
}
scope.setIndex(index);
if(index!==-1)
scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
scope.$apply();
break;
case key.down:
index = scope.getIndex()+1;
if(index<-1){
index = l-1;
} else if (index >= l ){
index = -1;
scope.setIndex(index);
scope.preSelectOff();
scope.$apply();
break;
}
scope.setIndex(index);
if(index!==-1)
scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());
break;
case key.left:
break;
case key.right:
case key.enter:
index = scope.getIndex();
// scope.preSelectOff();
if(index !== -1)
scope.select(angular.element(angular.element(this).find('li')[index]).text());
scope.setIndex(-1);
scope.$apply();
break;
case key.esc:
// disable suggestions on escape
scope.select();
scope.setIndex(-1);
scope.$apply();
e.preventDefault();
break;
default:
return;
}
if(scope.getIndex()!==-1 || keycode == key.enter)
e.preventDefault();
});
},
templateUrl: 'js/libs/angular-autocomplete/ac_template.html'
};
});
app.filter('highlight', ['$sce', function ($sce) {
return function (input, searchParam) {
if (typeof input === 'function') return '';
if (searchParam) {
var words = '(' +
searchParam.split(/\ /).join(' |') + '|' +
searchParam.split(/\ /).join('|') +
')',
exp = new RegExp(words, 'gi');
if (words.length) {
input = input.replace(exp, "<span class=\"highlight\">$1</span>");
}
}
return $sce.trustAsHtml(input);
};
}]);
app.directive('suggestion', function(){
return {
restrict: 'A',
require: '^autocomplete', // ^look for controller on parents element
link: function(scope, element, attrs, autoCtrl){
element.bind('mouseenter', function() {
autoCtrl.preSelect(attrs.val);
autoCtrl.setIndex(attrs.index);
});
element.bind('mouseleave', function() {
autoCtrl.preSelectOff();
});
}
};
});
})