diff --git a/video_metadata/static/file_listing.js b/video_metadata/static/file_listing.js
new file mode 100644
index 0000000..598a235
--- /dev/null
+++ b/video_metadata/static/file_listing.js
@@ -0,0 +1,58 @@
+function logError(s) {
+ console.log(s);
+ let error_box = document.querySelector('#js_errors');
+ let p = document.createElement('p');
+ p.appendChild(document.createTextNode(s));
+ error_box.appendChild(p);
+ error_box.style.display = 'block';
+}
+
+function fetchJSON(url, responseFunc, event) {
+ fetch(url)
+ .then(function(response) {
+ console.log('Response.url: ', response.url)
+ if (!response.ok)
+ throw Error(response.statusText);
+
+ return response.json();
+ })
+ .then(function(json) {
+ responseFunc(json, event);
+ })
+ .catch(function(error) {
+ logError('fetch error: ' + error);
+ return null;
+ });
+}
+
+function parseResponse(json, event) {
+ if (json.errors.length > 0) {
+ for (let err of json.errors)
+ logError(err);
+ } else {
+ for (let path of json.paths) {
+ console.log(event);
+ }
+ }
+}
+
+window.onload = function() {
+ document.querySelector('#noscript').style.display = 'none';
+ let error_box = document.querySelector('#js_errors');
+ error_box.style.display = 'none';
+
+ if (!('fetch' in window)) {
+ logError('Unsupported Browser, No fetch API');
+ return;
+ }
+
+ // replace top level links with event listeners that fetch json directory listings
+ for (let link of document.querySelectorAll('#videolist > li > a')) {
+ let href = link.getAttribute('href')
+
+ link.addEventListener('click', function(e) {
+ e.preventDefault();
+ fetchJSON(window.location + href, parseResponse, e);
+ });
+ }
+}
diff --git a/video_metadata/templates/base.html b/video_metadata/templates/base.html
index dce219e..1a9ecb3 100644
--- a/video_metadata/templates/base.html
+++ b/video_metadata/templates/base.html
@@ -3,6 +3,7 @@
{% block title %}{% endblock %} - pvdb
+ {% block extra_js %}{% endblock %}