diff --git a/mo/web/__init__.py b/mo/web/__init__.py
index 782dba3ebd9d1dc16d5b6fda3839e897eca294c3..05998d0ffb1467c60ddba3bd76c96e1894886ef9 100644
--- a/mo/web/__init__.py
+++ b/mo/web/__init__.py
@@ -116,6 +116,7 @@ app.assets.add_assets([
     'mo.css',
     'js/news-reloader.js',
     'js/osmo.js',
+    'js/autocomplete.js'
 ])
 
 
diff --git a/static/js/autocomplete.js b/static/js/autocomplete.js
new file mode 100644
index 0000000000000000000000000000000000000000..7148dc8813b6e08b97796eb5f3cbcac3c5e7e664
--- /dev/null
+++ b/static/js/autocomplete.js
@@ -0,0 +1,111 @@
+// Adapted from https://www.w3schools.com/howto/howto_js_autocomplete.asp
+function autocomplete(inp, arr, callback=null, max=10) {
+	var currentFocus;
+	var listCount;
+
+	function do_autocomplete(e) {
+		var a, b, i, val = this.value;
+		closeAllLists();
+
+		currentFocus = -1;
+		a = document.createElement("DIV");
+		a.setAttribute("id", this.id + "autocomplete-list");
+		a.setAttribute("class", "autocomplete-items");
+		this.parentNode.appendChild(a);
+
+		listCount = 0;
+		for (i = 0; i < arr.length; i++) {
+			var key, text;
+			if (Array.isArray(arr[i])) {
+				key = arr[i][0]; text = arr[i][1];
+			} else {
+				key = arr[i]; text = arr[i];
+			}
+
+			/*check if the item starts with the same letters as the text field value:*/
+			//if (text.substr(0, val.length).toUpperCase() == val.toUpperCase()) {
+			index = text.toUpperCase().indexOf(val.toUpperCase());
+			if (index != -1) {
+				listCount++;
+				b = document.createElement("DIV");
+				b.innerHTML = text.substr(0, index) + "<strong>" + text.substr(index, val.length) + "</strong>" + text.substr(index + val.length)
+				b.innerHTML += "<input type='hidden' value='" + key + "'>";
+				b.innerHTML += "<input type='hidden' value='" + text + "'>";
+				b.addEventListener("click", function(e) {
+					key = this.getElementsByTagName("input")[0].value;
+					text = this.getElementsByTagName("input")[1].value;
+					inp.value = text;
+					if (callback) {
+						callback(key);
+					}
+					closeAllLists();
+				});
+				a.appendChild(b);
+
+			}
+			if (listCount == max) break;
+		}
+	};
+
+	inp.addEventListener("input", do_autocomplete);
+	inp.addEventListener("focus", do_autocomplete);
+
+	inp.addEventListener("keydown", function(e) {
+		var x = document.getElementById(this.id + "autocomplete-list");
+		if (x) x = x.getElementsByTagName("div");
+		if (e.keyCode == 40) { // arrow down
+			currentFocus++;
+			addActive(x);
+		} else if (e.keyCode == 38) { // arrow up
+			currentFocus--;
+			addActive(x);
+		} else if (e.keyCode == 13) { // enter
+			e.preventDefault(); // do not submit the form
+			if (currentFocus == -1 && listCount > 0) {
+				currentFocus++;
+			}
+			if (currentFocus > -1) {
+				if (x) x[currentFocus].click(); // simulate a click on the "active" item
+			}
+
+		} else if (e.keyCode == 27) { // escape
+			inp.blur();
+			closeAllLists();
+		}
+		e.stopPropagation();
+	});
+
+	function addActive(x) {
+		if (!x) return false;
+		removeActive(x);
+		if (currentFocus >= x.length) currentFocus = 0;
+		if (currentFocus < 0) currentFocus = (x.length - 1);
+		x[currentFocus].classList.add("autocomplete-active");
+	}
+	function removeActive(x) {
+		for (var i = 0; i < x.length; i++) {
+			x[i].classList.remove("autocomplete-active");
+		}
+	}
+	function closeAllLists(elmnt) {
+		/* close all autocomplete lists in the document, except the one passed as an argument: */
+		var x = document.getElementsByClassName("autocomplete-items");
+		for (var i = 0; i < x.length; i++) {
+			if (elmnt != x[i] && elmnt != inp && (!elmnt || elmnt.parentNode != x[i].parentNode)) {
+				x[i].parentNode.removeChild(x[i]);
+			}
+		}
+	}
+	// close all autocomplete lists when clicked elsewhere
+	document.addEventListener("click", function (e) {
+		closeAllLists(e.target);
+	});
+}
+
+
+function closeAllAutocomplete() {
+	var x = document.getElementsByClassName("autocomplete-items");
+	for (var i = 0; i < x.length; i++) {
+		x[i].parentNode.removeChild(x[i]);
+	}
+}
diff --git a/static/mo.css b/static/mo.css
index 7dcf41627fcf49589e0437c14bdfd8833da9d03f..188831d0cb8bdb02ccd0de25948156085c73e55f 100644
--- a/static/mo.css
+++ b/static/mo.css
@@ -408,3 +408,35 @@ div.message .msg-date {
 	font-style: italic;
 	color: #777;
 }
+
+/* Autocomplete for inputs */
+.autocomplete {
+	position: relative;
+	display: inline-block;
+}
+
+.autocomplete-items {
+	position: absolute;
+	border: 1px solid #d4d4d4;
+	border-bottom: none;
+	border-top: none;
+	z-index: 99;
+	/* position the autocomplete items to be the same width as the container */
+	top: 100%;
+	left: 0;
+	right: 0;
+	margin: 0px 15px;
+}
+.autocomplete-items div {
+	padding: 5px;
+	cursor: pointer;
+	background-color: #fff;
+	border-bottom: 1px solid #d4d4d4;
+}
+.autocomplete-items div:hover {
+	background-color: #e9e9e9;
+}
+.autocomplete-active {
+	background-color: DodgerBlue !important;
+	color: #ffffff;
+}