diff --git a/plinth/modules/diagnostics/templates/diagnostics.html b/plinth/modules/diagnostics/templates/diagnostics.html
index 2fe323ab4..324dbf26c 100644
--- a/plinth/modules/diagnostics/templates/diagnostics.html
+++ b/plinth/modules/diagnostics/templates/diagnostics.html
@@ -81,7 +81,6 @@
{% block page_js %}
{% if is_running %}
-
{% endif %}
{% endblock %}
diff --git a/plinth/modules/dynamicdns/static/dynamicdns.js b/plinth/modules/dynamicdns/static/dynamicdns.js
new file mode 100644
index 000000000..fe9f9e550
--- /dev/null
+++ b/plinth/modules/dynamicdns/static/dynamicdns.js
@@ -0,0 +1,141 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+ var SELFHOST = 'https://carol.selfhost.de/update?username=&' +
+ 'password=&myip=';
+ var NOIP = 'http://dynupdate.no-ip.com/nic/update?hostname=' +
+ '&myip=';
+ var FREEDNS = 'https://freedns.afraid.org/dynamic/update.php?' +
+ '_YOURAPIKEYHERE_';
+
+ // Hide all form fields
+ $('.form-group').hide();
+ // Show the enable checkbox
+ $('#id_enabled').closest('.form-group').show();
+ if ($('#id_enabled').prop('checked')) {
+ // Show all form fields
+ show_all();
+ // Set the selectbox to the last configured value
+ select_service();
+ }
+
+ $('#id_enabled').change(function() {
+ if ($('#id_enabled').prop('checked')) {
+ show_all();
+ if ($("#id_service_type option:selected").text() == "GnuDIP") {
+ set_gnudip_mode();
+ } else {
+ set_update_url_mode();
+ }
+ } else {
+ $('.form-group').hide();
+ $('#id_enabled').closest('.form-group').show();
+ }
+ });
+
+ $('#id_service_type').change(function() {
+ var service_type = $("#id_service_type option:selected").text();
+ if (service_type == "GnuDIP") {
+ set_gnudip_mode();
+ } else {
+ set_update_url_mode();
+ if (service_type == "noip.com") {
+ $('#id_dynamicdns_update_url').val(NOIP);
+ $('#id_use_http_basic_auth').prop('checked', true);
+ } else {
+ $('#id_use_http_basic_auth').prop('checked', false);
+ }
+ if (service_type == "selfhost.bz") {
+ $('#id_dynamicdns_update_url').val(SELFHOST);
+ }
+ if (service_type == "freedns.afraid.org") {
+ $('#id_dynamicdns_update_url').val(FREEDNS);
+ }
+ if (service_type == "other update URL") {
+ $('#id_dynamicdns_update_url').val('');
+ }
+ }
+ });
+
+ $('#id_showpw').change(function() {
+ // Changing type attribute from password to text is prevented by most
+ // browsers make a new form field works for me
+ if ($('#id_showpw').prop('checked')) {
+ $('#id_dynamicdns_secret').replaceWith(
+ $('#id_dynamicdns_secret').clone().attr(
+ 'type', 'text'));
+ } else {
+ $('#id_dynamicdns_secret').replaceWith(
+ $('#id_dynamicdns_secret').clone().attr(
+ 'type', 'password'));
+ }
+ });
+
+ function select_service() {
+ var update_url = $("#id_dynamicdns_update_url").val();
+ if ($("#id_dynamicdns_server").val().length == 0) {
+ set_update_url_mode();
+ if (update_url == NOIP) {
+ $("#id_service_type").val("noip");
+ } else if (update_url == SELFHOST) {
+ $("#id_service_type").val("selfhost");
+ } else if (update_url == FREEDNS) {
+ $("#id_service_type").val("freedns");
+ } else {
+ $("#id_service_type").val("other");
+ }
+ } else {
+ $("#id_service_type").val("GnuDIP");
+ set_gnudip_mode();
+ }
+ }
+
+ function set_gnudip_mode() {
+ $('#id_dynamicdns_update_url').closest('.form-group').hide();
+ $('#id_disable_SSL_cert_check').closest('.form-group').hide();
+ $('#id_use_http_basic_auth').closest('.form-group').hide();
+ $('#id_dynamicdns_server').closest('.form-group').show();
+ }
+
+ function set_update_url_mode() {
+ $('#id_dynamicdns_update_url').closest('.form-group').show();
+ $('#id_disable_SSL_cert_check').closest('.form-group').show();
+ $('#id_use_http_basic_auth').closest('.form-group').show();
+ $('#id_dynamicdns_server').closest('.form-group').hide();
+ }
+
+ function show_all() {
+ $('#id_enabled').closest('.form-group').show();
+ $('#id_service_type').closest('.form-group').show();
+ $('#id_dynamicdns_server').closest('.form-group').show();
+ $('#id_dynamicdns_update_url').closest('.form-group').show();
+ $('#id_disable_SSL_cert_check').closest('.form-group').show();
+ $('#id_use_http_basic_auth').closest('.form-group').show();
+ $('#id_dynamicdns_domain').closest('.form-group').show();
+ $('#id_dynamicdns_user').closest('.form-group').show();
+ $('#id_dynamicdns_secret').closest('.form-group').show();
+ $('#id_showpw').closest('.form-group').show();
+ $('#id_dynamicdns_ipurl').closest('.form-group').show();
+ }
+})(jQuery);
diff --git a/plinth/modules/dynamicdns/templates/dynamicdns_configure.html b/plinth/modules/dynamicdns/templates/dynamicdns_configure.html
index f08cd3cf7..b56af59ac 100644
--- a/plinth/modules/dynamicdns/templates/dynamicdns_configure.html
+++ b/plinth/modules/dynamicdns/templates/dynamicdns_configure.html
@@ -20,6 +20,7 @@
{% load bootstrap %}
{% load i18n %}
+{% load static %}
{% block content %}
@@ -43,126 +44,5 @@
{% endblock %}
{% block page_js %}
-
+
{% endblock %}
diff --git a/plinth/modules/help/static/help.js b/plinth/modules/help/static/help.js
new file mode 100644
index 000000000..5f1158646
--- /dev/null
+++ b/plinth/modules/help/static/help.js
@@ -0,0 +1,27 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+ let downloadManualButton = $('a[href="/plinth/help/manual/download/"]');
+ downloadManualButton.attr("data-turbolinks", "false");
+})(jQuery);
diff --git a/plinth/modules/help/templates/help_index.html b/plinth/modules/help/templates/help_index.html
index 8df3c1bd5..761bb5ebc 100644
--- a/plinth/modules/help/templates/help_index.html
+++ b/plinth/modules/help/templates/help_index.html
@@ -96,10 +96,5 @@
{% endblock %}
{% block page_js %}
-
+
{% endblock %}
diff --git a/plinth/modules/mediawiki/static/mediawiki.js b/plinth/modules/mediawiki/static/mediawiki.js
new file mode 100644
index 000000000..e71874a06
--- /dev/null
+++ b/plinth/modules/mediawiki/static/mediawiki.js
@@ -0,0 +1,38 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+ $('#id_enable_public_registrations').click(function() {
+ var checkedState = $(this).prop("checked");
+ if (checkedState) {
+ $('#id_enable_private_mode').prop('checked', false);
+ }
+ });
+
+ $('#id_enable_private_mode').click(function() {
+ var checkedState = $(this).prop("checked");
+ if (checkedState) {
+ $('#id_enable_public_registrations').prop('checked', false);
+ }
+ });
+})(jQuery);
diff --git a/plinth/modules/mediawiki/templates/mediawiki.html b/plinth/modules/mediawiki/templates/mediawiki.html
index f6d90eecc..66c041a61 100644
--- a/plinth/modules/mediawiki/templates/mediawiki.html
+++ b/plinth/modules/mediawiki/templates/mediawiki.html
@@ -19,23 +19,8 @@
{% endcomment %}
{% load i18n %}
+{% load static %}
{% block page_js %}
-
+
{% endblock %}
diff --git a/plinth/modules/monkeysphere/templates/monkeysphere.html b/plinth/modules/monkeysphere/templates/monkeysphere.html
index 3e8ea1700..9e593c8e2 100644
--- a/plinth/modules/monkeysphere/templates/monkeysphere.html
+++ b/plinth/modules/monkeysphere/templates/monkeysphere.html
@@ -173,7 +173,6 @@
{% block page_js %}
{% if running %}
-
{% endif %}
{% endblock %}
diff --git a/plinth/modules/networks/static/networks.js b/plinth/modules/networks/static/networks.js
new file mode 100644
index 000000000..5ff22d32c
--- /dev/null
+++ b/plinth/modules/networks/static/networks.js
@@ -0,0 +1,97 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+
+ function ip_required(required, ip_version, fields) {
+ var prefix = '#id_' + ip_version + '_';
+ for (var i = 0; i < fields.length; i++) {
+ $(prefix + fields[i]).prop("required", required);
+ }
+ }
+
+ function ip_readonly(readonly, ip_version, fields) {
+ var prefix = '#id_' + ip_version + '_';
+ for (var i = 0; i < fields.length; i++) {
+ $(prefix + fields[i]).prop("readOnly", readonly);
+ if (readonly) {
+ $(prefix + fields[i]).val("");
+ $(prefix + fields[i]).prop("required", false);
+ }
+ }
+ }
+
+ function on_ipv4_method_change() {
+ if ($("#id_ipv4_method").prop("value") == "manual") {
+ ip_required(true, 'ipv4', ['address']);
+ ip_readonly(false, 'ipv4', ['address', 'netmask', 'gateway',
+ 'dns', 'second_dns'
+ ]);
+ } else if ($("#id_ipv4_method").prop("value") == "shared") {
+ ip_required(false, 'ipv4', ['address']);
+ ip_readonly(false, 'ipv4', ['address', 'netmask']);
+ ip_readonly(true, 'ipv4', ['gateway', 'dns', 'second_dns']);
+ } else if ($("#id_ipv4_method").prop("value") == "auto") {
+ ip_readonly(true, 'ipv4', ['address', 'netmask', 'gateway']);
+ ip_readonly(false, 'ipv4', ['dns', 'second_dns']);
+ } else {
+ ip_readonly(true, 'ipv4', ['address', 'netmask', 'gateway',
+ 'dns', 'second_dns'
+ ]);
+ }
+ }
+
+ function on_ipv6_method_change() {
+ if ($("#id_ipv6_method").prop("value") == "manual") {
+ ip_required(true, 'ipv6', ['address', 'prefix']);
+ ip_readonly(false, 'ipv6', ['address', 'prefix', 'gateway',
+ 'dns', 'second_dns'
+ ]);
+ } else if ($("#id_ipv6_method").prop("value") == "auto" ||
+ $("#id_ipv6_method").prop("value") == "dhcp") {
+ ip_readonly(true, 'ipv6', ['address', 'prefix', 'gateway']);
+ ip_readonly(false, 'ipv6', ['dns', 'second_dns']);
+ } else {
+ ip_readonly(true, 'ipv6', ['address', 'prefix', 'gateway',
+ 'dns', 'second_dns'
+ ]);
+ }
+ }
+
+ $("#id_name").focus();
+
+ $("#id_ipv4_method").change(on_ipv4_method_change).change();
+ $("#id_ipv6_method").change(on_ipv6_method_change).change();
+
+ $('#id_show_password').change(function() {
+ // Changing type attribute from password to text is prevented by
+ // most browsers. Making a new form field works.
+ new_type = 'password';
+ if ($('#id_show_password').prop('checked'))
+ new_type = 'text';
+
+ $('#id_password').replaceWith(
+ $('#id_password').clone().attr('type', new_type));
+ });
+
+})(jQuery);
diff --git a/plinth/modules/networks/templates/connections_edit.html b/plinth/modules/networks/templates/connections_edit.html
index 356522023..d677933eb 100644
--- a/plinth/modules/networks/templates/connections_edit.html
+++ b/plinth/modules/networks/templates/connections_edit.html
@@ -20,6 +20,7 @@
{% load bootstrap %}
{% load i18n %}
+{% load static %}
{% block content %}
@@ -36,79 +37,6 @@
{% block page_js %}
-
+
{% endblock %}
diff --git a/plinth/modules/openvpn/templates/openvpn.html b/plinth/modules/openvpn/templates/openvpn.html
index 6c24c9a4a..f929a8f06 100644
--- a/plinth/modules/openvpn/templates/openvpn.html
+++ b/plinth/modules/openvpn/templates/openvpn.html
@@ -135,6 +135,5 @@
{% block page_js %}
{% if status.setup_running %}
-
{% endif %}
{% endblock %}
diff --git a/plinth/modules/pagekite/static/pagekite.js b/plinth/modules/pagekite/static/pagekite.js
new file mode 100644
index 000000000..5cba2f6b7
--- /dev/null
+++ b/plinth/modules/pagekite/static/pagekite.js
@@ -0,0 +1,34 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+
+ $('#id_pagekite-enabled').change(function() {
+ if ($('#id_pagekite-enabled').prop('checked')) {
+ $('#pagekite-post-enabled-form').show('fast');
+ } else {
+ $('#pagekite-post-enabled-form').hide('fast');
+ }
+ }).change();
+
+})(jQuery);
diff --git a/plinth/modules/pagekite/templates/pagekite_configure.html b/plinth/modules/pagekite/templates/pagekite_configure.html
index b9f3a04ba..cc0aa6957 100644
--- a/plinth/modules/pagekite/templates/pagekite_configure.html
+++ b/plinth/modules/pagekite/templates/pagekite_configure.html
@@ -20,6 +20,7 @@
{% load bootstrap %}
{% load i18n %}
+{% load static %}
{% block content %}
@@ -45,19 +46,5 @@
{% block page_js %}
-
+
{% endblock %}
diff --git a/plinth/modules/snapshot/static/snapshot.js b/plinth/modules/snapshot/static/snapshot.js
new file mode 100644
index 000000000..9536815ca
--- /dev/null
+++ b/plinth/modules/snapshot/static/snapshot.js
@@ -0,0 +1,32 @@
+/**
+ * @licstart The following is the entire license notice for the JavaScript
+ * code in this page.
+ *
+ * This file is part of FreedomBox.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * @licend The above is the entire license notice for the JavaScript code
+ * in this page.
+ */
+
+(function($) {
+ $("#select-all").click(function() {
+ var checkedState = this.checked;
+ checkboxes = document.getElementsByName('snapshot_list');
+ jQuery.each(checkboxes, function(i, checkbox) {
+ checkbox.checked = checkedState;
+ });
+ });
+})(jQuery);
diff --git a/plinth/modules/snapshot/templates/snapshot_manage.html b/plinth/modules/snapshot/templates/snapshot_manage.html
index a3c594dfd..a8a24d0ba 100644
--- a/plinth/modules/snapshot/templates/snapshot_manage.html
+++ b/plinth/modules/snapshot/templates/snapshot_manage.html
@@ -20,6 +20,7 @@
{% load bootstrap %}
{% load i18n %}
+{% load static %}
{% block configuration %}