diff --git a/passbook/admin/templates/generic/form.html b/passbook/admin/templates/generic/form.html
index 40a2b7568..4de06b338 100644
--- a/passbook/admin/templates/generic/form.html
+++ b/passbook/admin/templates/generic/form.html
@@ -3,6 +3,11 @@
{% load i18n %}
{% load utils %}
+{% block head %}
+{{ block.super }}
+{{ form.media.css }}
+{% endblock %}
+
{% block content %}
{% block above_form %}
@@ -16,3 +21,8 @@
{% endblock %}
+
+{% block scripts %}
+{{ block.super }}
+{{ form.media.js }}
+{% endblock %}
diff --git a/passbook/core/forms/factors.py b/passbook/core/forms/factors.py
index 30a587546..11d0061c6 100644
--- a/passbook/core/forms/factors.py
+++ b/passbook/core/forms/factors.py
@@ -2,6 +2,7 @@
from django import forms
from passbook.core.models import DummyFactor, PasswordFactor
+from passbook.lib.fields import DynamicArrayField
GENERAL_FIELDS = ['name', 'slug', 'order', 'policies', 'enabled']
@@ -16,6 +17,9 @@ class PasswordFactorForm(forms.ModelForm):
'name': forms.TextInput(),
'order': forms.NumberInput(),
}
+ field_classes = {
+ 'backends': DynamicArrayField
+ }
class DummyFactorForm(forms.ModelForm):
"""Form to create/edit Dummy Factor"""
diff --git a/passbook/core/static/css/passbook.css b/passbook/core/static/css/passbook.css
new file mode 100644
index 000000000..86711d8b1
--- /dev/null
+++ b/passbook/core/static/css/passbook.css
@@ -0,0 +1,23 @@
+.dynamic-array-widget .array-item {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.dynamic-array-widget .remove_sign {
+ width: 10px;
+ height: 2px;
+ background: #a41515;
+ border-radius: 1px;
+}
+
+.dynamic-array-widget .remove {
+ height: 15px;
+ display: flex;
+ align-items: center;
+ margin-left: 5px;
+}
+
+.dynamic-array-widget .remove:hover {
+ cursor: pointer;
+}
diff --git a/passbook/core/static/js/passbook.js b/passbook/core/static/js/passbook.js
index a52cfe21e..cb89bbd27 100644
--- a/passbook/core/static/js/passbook.js
+++ b/passbook/core/static/js/passbook.js
@@ -16,3 +16,33 @@ const typeHandler = function (e) {
$source.on('input', typeHandler) // register for oninput
$source.on('propertychange', typeHandler) // for IE8
+
+window.addEventListener('load', function () {
+
+ function addRemoveEventListener(widgetElement) {
+ widgetElement.querySelectorAll('.array-remove').forEach(function (element) {
+ element.addEventListener('click', function () {
+ this.parentNode.parentNode.remove();
+ });
+ });
+ }
+
+ document.querySelectorAll('.dynamic-array-widget').forEach(function (widgetElement) {
+
+ addRemoveEventListener(widgetElement);
+
+ widgetElement.querySelector('.add-array-item').addEventListener('click', function () {
+ var first = widgetElement.querySelector('.array-item');
+ var newElement = first.cloneNode(true);
+ var id_parts = newElement.querySelector('input').getAttribute('id').split('_');
+ var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1);
+ newElement.querySelector('input').setAttribute('id', id);
+ newElement.querySelector('input').value = '';
+
+ addRemoveEventListener(newElement);
+ first.parentElement.insertBefore(newElement, first.parentNode.lastChild);
+ });
+
+ });
+
+});
diff --git a/passbook/core/templates/base/skeleton.html b/passbook/core/templates/base/skeleton.html
index 69a60d503..dad228996 100644
--- a/passbook/core/templates/base/skeleton.html
+++ b/passbook/core/templates/base/skeleton.html
@@ -16,6 +16,7 @@
+