From 0301056c9f72e1da9baf2b176868e75a54e96087 Mon Sep 17 00:00:00 2001
From: Henrik Levkowetz <henrik@levkowetz.com>
Date: Fri, 3 Apr 2015 17:36:00 +0000
Subject: [PATCH] Added django-bootstrap3 to requirements.txt; removed local
 copy of django-bootstrap3.  - Legacy-Id: 9372

---
 bootstrap3/__init__.py                        |   3 -
 bootstrap3/bootstrap.py                       | 106 ---
 bootstrap3/components.py                      |  37 -
 bootstrap3/exceptions.py                      |  16 -
 bootstrap3/forms.py                           | 173 -----
 bootstrap3/models.py                          |   3 -
 bootstrap3/renderers.py                       | 485 -------------
 bootstrap3/templates.py                       |  56 --
 .../templates/bootstrap3/bootstrap3.html      |  27 -
 .../field_help_text_and_errors.html           |   1 -
 .../templates/bootstrap3/form_errors.html     |   6 -
 bootstrap3/templates/bootstrap3/messages.html |   6 -
 .../templates/bootstrap3/pagination.html      |  33 -
 bootstrap3/templatetags/__init__.py           |   1 -
 bootstrap3/templatetags/bootstrap3.py         | 637 ------------------
 bootstrap3/tests.py                           | 447 ------------
 bootstrap3/text.py                            |  26 -
 bootstrap3/utils.py                           |  65 --
 requirements.txt                              |   1 +
 19 files changed, 1 insertion(+), 2128 deletions(-)
 delete mode 100644 bootstrap3/__init__.py
 delete mode 100644 bootstrap3/bootstrap.py
 delete mode 100644 bootstrap3/components.py
 delete mode 100644 bootstrap3/exceptions.py
 delete mode 100644 bootstrap3/forms.py
 delete mode 100644 bootstrap3/models.py
 delete mode 100644 bootstrap3/renderers.py
 delete mode 100644 bootstrap3/templates.py
 delete mode 100644 bootstrap3/templates/bootstrap3/bootstrap3.html
 delete mode 100644 bootstrap3/templates/bootstrap3/field_help_text_and_errors.html
 delete mode 100644 bootstrap3/templates/bootstrap3/form_errors.html
 delete mode 100644 bootstrap3/templates/bootstrap3/messages.html
 delete mode 100644 bootstrap3/templates/bootstrap3/pagination.html
 delete mode 100644 bootstrap3/templatetags/__init__.py
 delete mode 100644 bootstrap3/templatetags/bootstrap3.py
 delete mode 100644 bootstrap3/tests.py
 delete mode 100644 bootstrap3/text.py
 delete mode 100644 bootstrap3/utils.py

diff --git a/bootstrap3/__init__.py b/bootstrap3/__init__.py
deleted file mode 100644
index c2f508106..000000000
--- a/bootstrap3/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-__version__ = '5.1.1'
diff --git a/bootstrap3/bootstrap.py b/bootstrap3/bootstrap.py
deleted file mode 100644
index cae8e496a..000000000
--- a/bootstrap3/bootstrap.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.utils.importlib import import_module
-
-
-# Default settings
-BOOTSTRAP3_DEFAULTS = {
-    'jquery_url': '//code.jquery.com/jquery.min.js',
-    'base_url': '//netdna.bootstrapcdn.com/bootstrap/3.3.2/',
-    'css_url': None,
-    'theme_url': None,
-    'javascript_url': None,
-    'javascript_in_head': False,
-    'include_jquery': False,
-    'horizontal_label_class': 'col-md-2',
-    'horizontal_field_class': 'col-md-4',
-    'set_required': True,
-    'set_placeholder': True,
-    'required_css_class': '',
-    'error_css_class': 'has-error',
-    'success_css_class': 'has-success',
-    'formset_renderers': {
-        'default': 'bootstrap3.renderers.FormsetRenderer',
-    },
-    'form_renderers': {
-        'default': 'bootstrap3.renderers.FormRenderer',
-    },
-    'field_renderers': {
-        'default': 'bootstrap3.renderers.FieldRenderer',
-        'inline': 'bootstrap3.renderers.InlineFieldRenderer',
-    },
-}
-
-# Start with a copy of default settings
-BOOTSTRAP3 = BOOTSTRAP3_DEFAULTS.copy()
-
-# Override with user settings from settings.py
-BOOTSTRAP3.update(getattr(settings, 'BOOTSTRAP3', {}))
-
-
-def get_bootstrap_setting(setting, default=None):
-    """
-    Read a setting
-    """
-    return BOOTSTRAP3.get(setting, default)
-
-
-def bootstrap_url(postfix):
-    """
-    Prefix a relative url with the bootstrap base url
-    """
-    return get_bootstrap_setting('base_url') + postfix
-
-
-def jquery_url():
-    """
-    Return the full url to jQuery file to use
-    """
-    return get_bootstrap_setting('jquery_url')
-
-
-def javascript_url():
-    """
-    Return the full url to the Bootstrap JavaScript file
-    """
-    return get_bootstrap_setting('javascript_url') or \
-        bootstrap_url('js/bootstrap.min.js')
-
-
-def css_url():
-    """
-    Return the full url to the Bootstrap CSS file
-    """
-    return get_bootstrap_setting('css_url') or \
-        bootstrap_url('css/bootstrap.min.css')
-
-
-def theme_url():
-    """
-    Return the full url to the theme CSS file
-    """
-    return get_bootstrap_setting('theme_url')
-
-
-def get_renderer(renderers, **kwargs):
-    layout = kwargs.get('layout', '')
-    path = renderers.get(layout, renderers['default'])
-    mod, cls = path.rsplit(".", 1)
-    return getattr(import_module(mod), cls)
-
-
-def get_formset_renderer(**kwargs):
-    renderers = get_bootstrap_setting('formset_renderers')
-    return get_renderer(renderers, **kwargs)
-
-
-def get_form_renderer(**kwargs):
-    renderers = get_bootstrap_setting('form_renderers')
-    return get_renderer(renderers, **kwargs)
-
-
-def get_field_renderer(**kwargs):
-    renderers = get_bootstrap_setting('field_renderers')
-    return get_renderer(renderers, **kwargs)
diff --git a/bootstrap3/components.py b/bootstrap3/components.py
deleted file mode 100644
index 403203b0a..000000000
--- a/bootstrap3/components.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.forms.widgets import flatatt
-
-from .text import text_value
-
-
-def render_icon(icon, title=''):
-    """
-    Render a Bootstrap glyphicon icon
-    """
-    attrs = {
-        'class': 'glyphicon glyphicon-{icon}'.format(icon=icon),
-    }
-    if title:
-        attrs['title'] = title
-    return '<span{attrs}></span>'.format(attrs=flatatt(attrs))
-
-
-def render_alert(content, alert_type=None, dismissable=True):
-    """
-    Render a Bootstrap alert
-    """
-    button = ''
-    if not alert_type:
-        alert_type = 'info'
-    css_classes = ['alert', 'alert-' + text_value(alert_type)]
-    if dismissable:
-        css_classes.append('alert-dismissable')
-        button = '<button type="button" class="close" ' + \
-                 'data-dismiss="alert" aria-hidden="true">&times;</button>'
-    return '<div class="{css_classes}">{button}{content}</div>'.format(
-        css_classes=' '.join(css_classes),
-        button=button,
-        content=text_value(content),
-    )
diff --git a/bootstrap3/exceptions.py b/bootstrap3/exceptions.py
deleted file mode 100644
index 0dcf9acb6..000000000
--- a/bootstrap3/exceptions.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-
-class BootstrapException(Exception):
-    """
-    Any exception from this package
-    """
-    pass
-
-
-class BootstrapError(BootstrapException):
-    """
-    Any exception that is an error
-    """
-    pass
diff --git a/bootstrap3/forms.py b/bootstrap3/forms.py
deleted file mode 100644
index a9c78f95d..000000000
--- a/bootstrap3/forms.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.contrib.admin.widgets import AdminFileWidget
-from django.forms import (
-    HiddenInput, FileInput, CheckboxSelectMultiple, Textarea, TextInput
-)
-
-from .bootstrap import (
-    get_bootstrap_setting, get_form_renderer, get_field_renderer,
-    get_formset_renderer
-)
-from .text import text_concat, text_value
-from .exceptions import BootstrapError
-from .utils import add_css_class, render_tag
-from .components import render_icon
-
-
-FORM_GROUP_CLASS = 'form-group'
-
-
-def render_formset(formset, **kwargs):
-    """
-    Render a formset to a Bootstrap layout
-    """
-    renderer_cls = get_formset_renderer(**kwargs)
-    return renderer_cls(formset, **kwargs).render()
-
-
-def render_formset_errors(form, **kwargs):
-    """
-    Render formset errors to a Bootstrap layout
-    """
-    renderer_cls = get_formset_renderer(**kwargs)
-    return renderer_cls(form, **kwargs).render_errors()
-
-
-def render_form(form, **kwargs):
-    """
-    Render a formset to a Bootstrap layout
-    """
-    renderer_cls = get_form_renderer(**kwargs)
-    return renderer_cls(form, **kwargs).render()
-
-
-def render_form_errors(form, type='all', **kwargs):
-    """
-    Render form errors to a Bootstrap layout
-    """
-    renderer_cls = get_form_renderer(**kwargs)
-    return renderer_cls(form, **kwargs).render_errors(type)
-
-
-def render_field(field, **kwargs):
-    """
-    Render a formset to a Bootstrap layout
-    """
-    renderer_cls = get_field_renderer(**kwargs)
-    return renderer_cls(field, **kwargs).render()
-
-
-def render_label(content, label_for=None, label_class=None, label_title=''):
-    """
-    Render a label with content
-    """
-    attrs = {}
-    if label_for:
-        attrs['for'] = label_for
-    if label_class:
-        attrs['class'] = label_class
-    if label_title:
-        attrs['title'] = label_title
-    return render_tag('label', attrs=attrs, content=content)
-
-
-def render_button(
-        content, button_type=None, icon=None, button_class='', size='',
-        href=''):
-    """
-    Render a button with content
-    """
-    attrs = {}
-    classes = add_css_class('btn', button_class)
-    size = text_value(size).lower().strip()
-    if size == 'xs':
-        classes = add_css_class(classes, 'btn-xs')
-    elif size == 'sm' or size == 'small':
-        classes = add_css_class(classes, 'btn-sm')
-    elif size == 'lg' or size == 'large':
-        classes = add_css_class(classes, 'btn-lg')
-    elif size == 'md' or size == 'medium':
-        pass
-    elif size:
-        raise BootstrapError(
-            'Parameter "size" should be "xs", "sm", "lg" or ' +
-            'empty ("{}" given).'.format(size))
-    if button_type:
-        if button_type == 'submit':
-            classes = add_css_class(classes, 'btn-primary')
-        elif button_type not in ('reset', 'button', 'link'):
-            raise BootstrapError(
-                'Parameter "button_type" should be "submit", "reset", ' +
-                '"button", "link" or empty  ("{}" given).'.format(button_type))
-        attrs['type'] = button_type
-    attrs['class'] = classes
-    icon_content = render_icon(icon) if icon else ''
-    if href:
-        attrs['href'] = href
-        tag = 'a'
-    else:
-        tag = 'button'
-    return render_tag(
-        tag, attrs=attrs, content=text_concat(
-            icon_content, content, separator=' '))
-
-
-def render_field_and_label(
-        field, label, field_class='', label_for=None, label_class='',
-        layout='', **kwargs):
-    """
-    Render a field with its label
-    """
-    if layout == 'horizontal':
-        if not label_class:
-            label_class = get_bootstrap_setting('horizontal_label_class')
-        if not field_class:
-            field_class = get_bootstrap_setting('horizontal_field_class')
-        if not label:
-            label = '&#160;'
-        label_class = add_css_class(label_class, 'control-label')
-    html = field
-    if field_class:
-        html = '<div class="{klass}">{html}</div>'.format(
-            klass=field_class, html=html)
-    if label:
-        html = render_label(
-            label, label_for=label_for, label_class=label_class) + html
-    return html
-
-
-def render_form_group(content, css_class=FORM_GROUP_CLASS):
-    """
-    Render a Bootstrap form group
-    """
-    return '<div class="{klass}">{content}</div>'.format(
-        klass=css_class,
-        content=content,
-    )
-
-
-def is_widget_required_attribute(widget):
-    """
-    Is this widget required?
-    """
-    if not get_bootstrap_setting('set_required'):
-        return False
-    if not widget.is_required:
-        return False
-    if isinstance(
-            widget, (
-                AdminFileWidget, HiddenInput, FileInput,
-                CheckboxSelectMultiple)):
-        return False
-    return True
-
-
-def is_widget_with_placeholder(widget):
-    """
-    Is this a widget that should have a placeholder?
-    Only text, search, url, tel, e-mail, password, number have placeholders
-    These are all derived form TextInput, except for Textarea
-    """
-    return isinstance(widget, (TextInput, Textarea))
diff --git a/bootstrap3/models.py b/bootstrap3/models.py
deleted file mode 100644
index 60b94b00d..000000000
--- a/bootstrap3/models.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Empty models.py, required file for Django tests
diff --git a/bootstrap3/renderers.py b/bootstrap3/renderers.py
deleted file mode 100644
index f383844c9..000000000
--- a/bootstrap3/renderers.py
+++ /dev/null
@@ -1,485 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-from django.contrib.auth.forms import ReadOnlyPasswordHashWidget
-
-from django.forms import (
-    TextInput, DateInput, FileInput, CheckboxInput,
-    ClearableFileInput, Select, RadioSelect, CheckboxSelectMultiple
-)
-from django.forms.extras import SelectDateWidget
-from django.forms.forms import BaseForm, BoundField
-from django.forms.formsets import BaseFormSet
-from django.utils.html import conditional_escape, strip_tags
-from django.template import Context
-from django.template.loader import get_template
-from django.utils.safestring import mark_safe
-
-from .bootstrap import get_bootstrap_setting
-from .text import text_value
-from .exceptions import BootstrapError
-from .utils import add_css_class
-from .forms import (
-    render_form, render_field, render_label, render_form_group,
-    is_widget_with_placeholder, is_widget_required_attribute, FORM_GROUP_CLASS
-)
-
-
-class BaseRenderer(object):
-    def __init__(self, *args, **kwargs):
-        self.layout = kwargs.get('layout', '')
-        self.form_group_class = kwargs.get(
-            'form_group_class', FORM_GROUP_CLASS)
-        self.field_class = kwargs.get('field_class', '')
-        self.label_class = kwargs.get('label_class', '')
-        self.show_help = kwargs.get('show_help', True)
-        self.show_label = kwargs.get('show_label', True)
-        self.exclude = kwargs.get('exclude', '')
-        self.set_required = kwargs.get('set_required', True)
-        self.size = self.parse_size(kwargs.get('size', ''))
-        self.horizontal_label_class = kwargs.get(
-            'horizontal_label_class',
-            get_bootstrap_setting('horizontal_label_class')
-        )
-        self.horizontal_field_class = kwargs.get(
-            'horizontal_field_class',
-            get_bootstrap_setting('horizontal_field_class')
-        )
-
-    def parse_size(self, size):
-        size = text_value(size).lower().strip()
-        if size in ('sm', 'small'):
-            return 'small'
-        if size in ('lg', 'large'):
-            return 'large'
-        if size in ('md', 'medium', ''):
-            return 'medium'
-        raise BootstrapError('Invalid value "%s" for parameter "size" (expected "sm", "md", "lg" or "").' % size)
-
-    def get_size_class(self, prefix='input'):
-        if self.size == 'small':
-            return prefix + '-sm'
-        if self.size == 'large':
-            return prefix + '-lg'
-        return ''
-
-
-class FormsetRenderer(BaseRenderer):
-    """
-    Default formset renderer
-    """
-
-    def __init__(self, formset, *args, **kwargs):
-        if not isinstance(formset, BaseFormSet):
-            raise BootstrapError(
-                'Parameter "formset" should contain a valid Django Formset.')
-        self.formset = formset
-        super(FormsetRenderer, self).__init__(*args, **kwargs)
-
-    def render_management_form(self):
-        return text_value(self.formset.management_form)
-
-    def render_form(self, form, **kwargs):
-        return render_form(form, **kwargs)
-
-    def render_forms(self):
-        rendered_forms = []
-        for form in self.formset.forms:
-            rendered_forms.append(self.render_form(
-                form,
-                layout=self.layout,
-                form_group_class=self.form_group_class,
-                field_class=self.field_class,
-                label_class=self.label_class,
-                show_help=self.show_help,
-                exclude=self.exclude,
-                set_required=self.set_required,
-                size=self.size,
-                horizontal_label_class=self.horizontal_label_class,
-                horizontal_field_class=self.horizontal_field_class,
-            ))
-        return '\n'.join(rendered_forms)
-
-    def get_formset_errors(self):
-        return self.formset.non_form_errors()
-
-    def render_errors(self):
-        formset_errors = self.get_formset_errors()
-        if formset_errors:
-            return get_template(
-                'bootstrap3/form_errors.html').render(
-                    Context({
-                        'errors': formset_errors,
-                        'form': self.formset,
-                        'layout': self.layout,
-                    })
-                )
-        return ''
-
-    def render(self):
-        return self.render_errors() + self.render_management_form() + \
-            self.render_forms()
-
-
-class FormRenderer(BaseRenderer):
-    """
-    Default form renderer
-    """
-
-    def __init__(self, form, *args, **kwargs):
-        if not isinstance(form, BaseForm):
-            raise BootstrapError(
-                'Parameter "form" should contain a valid Django Form.')
-        self.form = form
-        super(FormRenderer, self).__init__(*args, **kwargs)
-        # Handle form.empty_permitted
-        if self.form.empty_permitted:
-            self.set_required = False
-
-    def render_fields(self):
-        rendered_fields = []
-        for field in self.form:
-            rendered_fields.append(render_field(
-                field,
-                layout=self.layout,
-                form_group_class=self.form_group_class,
-                field_class=self.field_class,
-                label_class=self.label_class,
-                show_help=self.show_help,
-                exclude=self.exclude,
-                set_required=self.set_required,
-                size=self.size,
-                horizontal_label_class=self.horizontal_label_class,
-                horizontal_field_class=self.horizontal_field_class,
-            ))
-        return '\n'.join(rendered_fields)
-
-    def get_fields_errors(self):
-        form_errors = []
-        for field in self.form:
-            if field.is_hidden and field.errors:
-                form_errors += field.errors
-        return form_errors
-
-    def render_errors(self, type='all'):
-        form_errors = None
-        if type == 'all':
-            form_errors = self.get_fields_errors() + \
-                self.form.non_field_errors()
-        elif type == 'fields':
-            form_errors = self.get_fields_errors()
-        elif type == 'non_fields':
-            form_errors = self.form.non_field_errors()
-
-        if form_errors:
-            return get_template(
-                'bootstrap3/form_errors.html').render(
-                    Context({
-                        'errors': form_errors,
-                        'form': self.form,
-                        'layout': self.layout,
-                    })
-                )
-        return ''
-
-    def render(self):
-        return self.render_errors() + self.render_fields()
-
-
-class FieldRenderer(BaseRenderer):
-    """
-    Default field renderer
-    """
-
-    def __init__(self, field, *args, **kwargs):
-        if not isinstance(field, BoundField):
-            raise BootstrapError(
-                'Parameter "field" should contain a valid Django BoundField.')
-        self.field = field
-        super(FieldRenderer, self).__init__(*args, **kwargs)
-
-        self.widget = field.field.widget
-        self.initial_attrs = self.widget.attrs.copy()
-        self.field_help = text_value(mark_safe(field.help_text)) \
-            if self.show_help and field.help_text else ''
-        self.field_errors = [conditional_escape(text_value(error))
-                             for error in field.errors]
-
-        if get_bootstrap_setting('set_placeholder'):
-            self.placeholder = field.label
-        else:
-            self.placeholder = ''
-
-        self.addon_before = kwargs.get('addon_before', self.initial_attrs.pop('addon_before', ''))
-        self.addon_after = kwargs.get('addon_after', self.initial_attrs.pop('addon_after', ''))
-
-        # These are set in Django or in the global BOOTSTRAP3 settings, and
-        # they can be overwritten in the template
-        error_css_class = kwargs.get('error_css_class', '')
-        required_css_class = kwargs.get('required_css_class', '')
-        bound_css_class = kwargs.get('bound_css_class', '')
-        if error_css_class:
-            self.error_css_class = error_css_class
-        else:
-            self.error_css_class = getattr(
-                field.form, 'error_css_class',
-                get_bootstrap_setting('error_css_class'))
-        if required_css_class:
-            self.required_css_class = required_css_class
-        else:
-            self.required_css_class = getattr(
-                field.form, 'required_css_class',
-                get_bootstrap_setting('required_css_class'))
-        if bound_css_class:
-            self.success_css_class = bound_css_class
-        else:
-            self.success_css_class = getattr(
-                field.form, 'bound_css_class',
-                get_bootstrap_setting('success_css_class'))
-
-        # Handle form.empty_permitted
-        if self.field.form.empty_permitted:
-            self.set_required = False
-            self.required_css_class = ''
-
-    def restore_widget_attrs(self):
-        self.widget.attrs = self.initial_attrs
-
-    def add_class_attrs(self):
-        classes = self.widget.attrs.get('class', '')
-        if isinstance(self.widget, ReadOnlyPasswordHashWidget):
-            classes = add_css_class(
-                classes, 'form-control-static', prepend=True)
-        elif not isinstance(self.widget, (CheckboxInput,
-                                          RadioSelect,
-                                          CheckboxSelectMultiple,
-                                          FileInput)):
-            classes = add_css_class(classes, 'form-control', prepend=True)
-            # For these widget types, add the size class here
-            classes = add_css_class(classes, self.get_size_class())
-        self.widget.attrs['class'] = classes
-
-    def add_placeholder_attrs(self):
-        placeholder = self.widget.attrs.get('placeholder', self.placeholder)
-        if placeholder and is_widget_with_placeholder(self.widget):
-            self.widget.attrs['placeholder'] = placeholder
-
-    def add_help_attrs(self):
-        if not isinstance(self.widget, CheckboxInput):
-            self.widget.attrs['title'] = self.widget.attrs.get(
-                'title', strip_tags(self.field_help))
-
-    def add_required_attrs(self):
-        if self.set_required and is_widget_required_attribute(self.widget):
-            self.widget.attrs['required'] = 'required'
-
-    def add_widget_attrs(self):
-        self.add_class_attrs()
-        self.add_placeholder_attrs()
-        self.add_help_attrs()
-        self.add_required_attrs()
-
-    def list_to_class(self, html, klass):
-        classes = add_css_class(klass, self.get_size_class())
-        mapping = [
-            ('<ul', '<div'),
-            ('</ul>', '</div>'),
-            ('<li', '<div class="{klass}"'.format(klass=classes)),
-            ('</li>', '</div>'),
-        ]
-        for k, v in mapping:
-            html = html.replace(k, v)
-        return html
-
-    def put_inside_label(self, html):
-        content = '{field} {label}'.format(field=html, label=self.field.label)
-        return render_label(
-            content=content, label_for=self.field.id_for_label,
-            label_title=strip_tags(self.field_help))
-
-    def fix_date_select_input(self, html):
-        div1 = '<div class="col-xs-4">'
-        div2 = '</div>'
-        html = html.replace('<select', div1 + '<select')
-        html = html.replace('</select>', '</select>' + div2)
-        return '<div class="row bootstrap3-multi-input">' + html + '</div>'
-
-    def fix_clearable_file_input(self, html):
-        """
-        Fix a clearable file input
-        TODO: This needs improvement
-
-        Currently Django returns
-        Currently:
-        <a href="dummy.txt">dummy.txt</a>
-        <input id="file4-clear_id" name="file4-clear" type="checkbox" />
-        <label for="file4-clear_id">Clear</label><br />
-        Change: <input id="id_file4" name="file4" type="file" />
-        <span class=help-block></span>
-        </div>
-
-        """
-        # TODO This needs improvement
-        return '<div class="row bootstrap3-multi-input">' + \
-            '<div class="col-xs-12">' + html + '</div></div>'
-
-    def post_widget_render(self, html):
-        if isinstance(self.widget, RadioSelect):
-            html = self.list_to_class(html, 'radio')
-        elif isinstance(self.widget, CheckboxSelectMultiple):
-            html = self.list_to_class(html, 'checkbox')
-        elif isinstance(self.widget, SelectDateWidget):
-            html = self.fix_date_select_input(html)
-        elif isinstance(self.widget, ClearableFileInput):
-            html = self.fix_clearable_file_input(html)
-        elif isinstance(self.widget, CheckboxInput):
-            html = self.put_inside_label(html)
-        return html
-
-    def wrap_widget(self, html):
-        if isinstance(self.widget, CheckboxInput):
-            checkbox_class = add_css_class('checkbox', self.get_size_class())
-            html = \
-                '<div class="{klass}">{content}</div>'.format(
-                    klass=checkbox_class, content=html
-                )
-        return html
-
-    def make_input_group(self, html):
-        if (
-                (self.addon_before or self.addon_after) and
-                isinstance(self.widget, (TextInput, DateInput, Select))
-        ):
-            before = '<span class="input-group-addon">{addon}</span>'.format(
-                addon=self.addon_before) if self.addon_before else ''
-            after = '<span class="input-group-addon">{addon}</span>'.format(
-                addon=self.addon_after) if self.addon_after else ''
-            html = \
-                '<div class="input-group">' + \
-                '{before}{html}{after}</div>'.format(
-                    before=before,
-                    after=after,
-                    html=html
-                )
-        return html
-
-    def append_to_field(self, html):
-        help_text_and_errors = [self.field_help] + self.field_errors \
-            if self.field_help else self.field_errors
-        if help_text_and_errors:
-            help_html = get_template(
-                'bootstrap3/field_help_text_and_errors.html'
-            ).render(Context({
-                'field': self.field,
-                'help_text_and_errors': help_text_and_errors,
-                'layout': self.layout,
-            }))
-            html += '<span class="help-block">{help}</span>'.format(
-                help=help_html)
-        return html
-
-    def get_field_class(self):
-        field_class = self.field_class
-        if not field_class and self.layout == 'horizontal':
-            field_class = self.horizontal_field_class
-        return field_class
-
-    def wrap_field(self, html):
-        field_class = self.get_field_class()
-        if field_class:
-            html = '<div class="{klass}">{html}</div>'.format(
-                klass=field_class, html=html)
-        return html
-
-    def get_label_class(self):
-        label_class = self.label_class
-        if not label_class and self.layout == 'horizontal':
-            label_class = self.horizontal_label_class
-        label_class = text_value(label_class)
-        if not self.show_label:
-            label_class = add_css_class(label_class, 'sr-only')
-        return add_css_class(label_class, 'control-label')
-
-    def get_label(self):
-        if isinstance(self.widget, CheckboxInput):
-            label = None
-        else:
-            label = self.field.label
-        if self.layout == 'horizontal' and not label:
-            return '&#160;'
-        return label
-
-    def add_label(self, html):
-        label = self.get_label()
-        if label:
-            html = render_label(
-                label,
-                label_for=self.field.id_for_label,
-                label_class=self.get_label_class()) + html
-        return html
-
-    def get_form_group_class(self):
-        form_group_class = self.form_group_class
-        if self.field.errors and self.error_css_class:
-            form_group_class = add_css_class(
-                form_group_class, self.error_css_class)
-        if self.field.field.required and self.required_css_class:
-            form_group_class = add_css_class(
-                form_group_class, self.required_css_class)
-        if self.field_errors:
-            form_group_class = add_css_class(form_group_class, 'has-error')
-        elif self.field.form.is_bound:
-            form_group_class = add_css_class(
-                form_group_class, self.success_css_class)
-        if self.layout == 'horizontal':
-            form_group_class = add_css_class(
-                form_group_class, self.get_size_class(prefix='form-group'))
-        return form_group_class
-
-    def wrap_label_and_field(self, html):
-        return render_form_group(html, self.get_form_group_class())
-
-    def render(self):
-        # See if we're not excluded
-        if self.field.name in self.exclude.replace(' ', '').split(','):
-            return ''
-        # Hidden input requires no special treatment
-        if self.field.is_hidden:
-            return text_value(self.field)
-        # Render the widget
-        self.add_widget_attrs()
-        html = self.field.as_widget(attrs=self.widget.attrs)
-        self.restore_widget_attrs()
-        # Start post render
-        html = self.post_widget_render(html)
-        html = self.wrap_widget(html)
-        html = self.make_input_group(html)
-        html = self.append_to_field(html)
-        html = self.wrap_field(html)
-        html = self.add_label(html)
-        html = self.wrap_label_and_field(html)
-        return html
-
-
-class InlineFieldRenderer(FieldRenderer):
-    """
-    Inline field renderer
-    """
-
-    def add_error_attrs(self):
-        field_title = self.widget.attrs.get('title', '')
-        field_title += ' ' + ' '.join(
-            [strip_tags(e) for e in self.field_errors])
-        self.widget.attrs['title'] = field_title.strip()
-
-    def add_widget_attrs(self):
-        super(InlineFieldRenderer, self).add_widget_attrs()
-        self.add_error_attrs()
-
-    def append_to_field(self, html):
-        return html
-
-    def get_field_class(self):
-        return self.field_class
-
-    def get_label_class(self):
-        return add_css_class(self.label_class, 'sr-only')
diff --git a/bootstrap3/templates.py b/bootstrap3/templates.py
deleted file mode 100644
index a6485e7ba..000000000
--- a/bootstrap3/templates.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import re
-
-from django.template import Variable, VariableDoesNotExist
-from django.template.base import (
-    FilterExpression, kwarg_re, TemplateSyntaxError
-)
-
-# Extra features for template file handling
-
-QUOTED_STRING = re.compile(r'^["\'](?P<noquotes>.+)["\']$')
-
-
-def handle_var(value, context):
-    # Resolve FilterExpression and Variable immediately
-    if isinstance(value, FilterExpression) or isinstance(value, Variable):
-        return value.resolve(context)
-    # Return quoted strings unquotes, from djangosnippets.org/snippets/886
-    stringval = QUOTED_STRING.search(value)
-    if stringval:
-        return stringval.group('noquotes')
-    # Resolve variable or return string value
-    try:
-        return Variable(value).resolve(context)
-    except VariableDoesNotExist:
-        return value
-
-
-def parse_token_contents(parser, token):
-    bits = token.split_contents()
-    tag = bits.pop(0)
-    args = []
-    kwargs = {}
-    asvar = None
-    if len(bits) >= 2 and bits[-2] == 'as':
-        asvar = bits[-1]
-        bits = bits[:-2]
-    if len(bits):
-        for bit in bits:
-            match = kwarg_re.match(bit)
-            if not match:
-                raise TemplateSyntaxError(
-                    'Malformed arguments to tag "{}"'.format(tag))
-            name, value = match.groups()
-            if name:
-                kwargs[name] = parser.compile_filter(value)
-            else:
-                args.append(parser.compile_filter(value))
-    return {
-        'tag': tag,
-        'args': args,
-        'kwargs': kwargs,
-        'asvar': asvar,
-    }
diff --git a/bootstrap3/templates/bootstrap3/bootstrap3.html b/bootstrap3/templates/bootstrap3/bootstrap3.html
deleted file mode 100644
index f68228ad2..000000000
--- a/bootstrap3/templates/bootstrap3/bootstrap3.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-{% load bootstrap3 %}
-<html{% if request.LANGUAGE_CODE %} lang="{{ request.LANGUAGE_CODE }}"{% endif %}>
-
-<head>
-    <meta charset="utf-8">
-    <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge" /><![endif]-->
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>{% block bootstrap3_title %}django-bootstrap3 template title{% endblock %}</title>
-    {% bootstrap_css %}
-    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
-    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
-    <!--[if lt IE 9]>
-    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
-    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
-    <![endif]-->
-    {% if 'javascript_in_head'|bootstrap_setting %}{% bootstrap_javascript jquery=True %}{% endif %}
-    {% block bootstrap3_extra_head %}{% endblock %}
-</head>
-
-<body>
-{% block bootstrap3_content %}django-bootstrap3 template content{% endblock %}
-{% if not 'javascript_in_head'|bootstrap_setting %}{% bootstrap_javascript jquery=True %}{% endif %}
-{% block bootstrap3_extra_script %}{% endblock %}
-</body>
-
-</html>
diff --git a/bootstrap3/templates/bootstrap3/field_help_text_and_errors.html b/bootstrap3/templates/bootstrap3/field_help_text_and_errors.html
deleted file mode 100644
index 5330389ad..000000000
--- a/bootstrap3/templates/bootstrap3/field_help_text_and_errors.html
+++ /dev/null
@@ -1 +0,0 @@
-{{ help_text_and_errors|join:' ' }}
diff --git a/bootstrap3/templates/bootstrap3/form_errors.html b/bootstrap3/templates/bootstrap3/form_errors.html
deleted file mode 100644
index 6d9e70025..000000000
--- a/bootstrap3/templates/bootstrap3/form_errors.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<div class="alert alert-danger alert-dismissable alert-link">
-    <button class="close" type="button" data-dismiss="alert" aria-hidden="true">&#215;</button>
-    {% for error in errors %}
-        {{ error }}{% if not forloop.last %}<br>{% endif %}
-    {% endfor %}
-</div>
diff --git a/bootstrap3/templates/bootstrap3/messages.html b/bootstrap3/templates/bootstrap3/messages.html
deleted file mode 100644
index 6192177aa..000000000
--- a/bootstrap3/templates/bootstrap3/messages.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% for message in messages %}
-    <div class="alert{% if message.tags %} alert-{% if message.tags == 'error' %}danger{% else %}{{ message.tags }}{% endif %}{% endif %} alert-dismissable">
-        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&#215;</button>
-        {{ message|safe }}
-    </div>
-{% endfor %}
diff --git a/bootstrap3/templates/bootstrap3/pagination.html b/bootstrap3/templates/bootstrap3/pagination.html
deleted file mode 100644
index 8949d1d8e..000000000
--- a/bootstrap3/templates/bootstrap3/pagination.html
+++ /dev/null
@@ -1,33 +0,0 @@
-{% with bootstrap_pagination_url=bootstrap_pagination_url|default:"?" %}
-
-    <ul class="{{ pagination_css_classes }}">
-
-        <li class="prev{% if current_page == 1 %} disabled{% endif %}">
-            <a href="{% if current_page == 1 %}#{% else %}{{ bootstrap_pagination_url }}{{ parameter_name }}=1{% endif %}">&laquo;</a>
-        </li>
-
-        {% if pages_back %}
-            <li>
-                <a href="{{ bootstrap_pagination_url }}{{ parameter_name }}={{ pages_back }}">&hellip;</a>
-            </li>
-        {% endif %}
-
-        {% for p in pages_shown %}
-            <li{% if current_page == p %} class="active"{% endif %}>
-                <a href="{% if current_page == p %}#{% else %}{{ bootstrap_pagination_url }}{{ parameter_name }}={{ p }}{% endif %}">{{ p }}</a>
-            </li>
-        {% endfor %}
-
-        {% if pages_forward %}
-            <li>
-                <a href="{{ bootstrap_pagination_url }}{{ parameter_name }}={{ pages_forward }}">&hellip;</a>
-            </li>
-        {% endif %}
-
-        <li class="last{% if current_page == num_pages %} disabled{% endif %}">
-            <a href="{% if current_page == num_pages %}#{% else %}{{ bootstrap_pagination_url }}{{ parameter_name }}={{ num_pages }}{% endif %}">&raquo;</a>
-        </li>
-
-    </ul>
-
-{% endwith %}
diff --git a/bootstrap3/templatetags/__init__.py b/bootstrap3/templatetags/__init__.py
deleted file mode 100644
index 40a96afc6..000000000
--- a/bootstrap3/templatetags/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# -*- coding: utf-8 -*-
diff --git a/bootstrap3/templatetags/bootstrap3.py b/bootstrap3/templatetags/bootstrap3.py
deleted file mode 100644
index 2febb0233..000000000
--- a/bootstrap3/templatetags/bootstrap3.py
+++ /dev/null
@@ -1,637 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import re
-
-from math import floor
-
-from django import template
-from django.template.loader import get_template
-
-from ..bootstrap import (
-    css_url, javascript_url, jquery_url, theme_url, get_bootstrap_setting
-)
-from ..utils import render_link_tag
-from ..forms import (
-    render_button, render_field, render_field_and_label, render_form,
-    render_form_group, render_formset,
-    render_label, render_form_errors, render_formset_errors
-)
-from ..components import render_icon, render_alert
-from ..templates import handle_var, parse_token_contents
-from ..text import force_text
-
-
-register = template.Library()
-
-
-@register.filter
-def bootstrap_setting(value):
-    """
-    A simple way to read bootstrap settings in a template.
-    Please consider this filter private for now, do not use it in your own
-    templates.
-    """
-    return get_bootstrap_setting(value)
-
-
-@register.simple_tag
-def bootstrap_jquery_url():
-    """
-    **Tag name**::
-
-        bootstrap_jquery_url
-
-    Return the full url to jQuery file to use
-
-    Default value: ``//code.jquery.com/jquery.min.js``
-
-    This value is configurable, see Settings section
-
-    **usage**::
-
-        {% bootstrap_jquery_url %}
-
-    **example**::
-
-        {% bootstrap_jquery_url %}
-    """
-    return jquery_url()
-
-
-@register.simple_tag
-def bootstrap_javascript_url():
-    """
-    Return the full url to the Bootstrap JavaScript library
-
-    Default value: ``None``
-
-    This value is configurable, see Settings section
-
-    **Tag name**::
-
-        bootstrap_javascript_url
-
-    **usage**::
-
-        {% bootstrap_javascript_url %}
-
-    **example**::
-
-        {% bootstrap_javascript_url %}
-    """
-    return javascript_url()
-
-
-@register.simple_tag
-def bootstrap_css_url():
-    """
-    Return the full url to the Bootstrap CSS library
-
-    Default value: ``None``
-
-    This value is configurable, see Settings section
-
-    **Tag name**::
-
-        bootstrap_css_url
-
-    **usage**::
-
-        {% bootstrap_css_url %}
-
-    **example**::
-
-        {% bootstrap_css_url %}
-    """
-    return css_url()
-
-
-@register.simple_tag
-def bootstrap_theme_url():
-    """
-    Return the full url to a Bootstrap theme CSS library
-
-    Default value: ``None``
-
-    This value is configurable, see Settings section
-
-    **Tag name**::
-
-        bootstrap_css_url
-
-    **usage**::
-
-        {% bootstrap_css_url %}
-
-    **example**::
-
-        {% bootstrap_css_url %}
-    """
-    return theme_url()
-
-
-@register.simple_tag
-def bootstrap_css():
-    """
-    Return HTML for Bootstrap CSS
-    Adjust url in settings. If no url is returned, we don't want this statement
-    to return any HTML.
-    This is intended behavior.
-
-    Default value: ``FIXTHIS``
-
-    This value is configurable, see Settings section
-
-    **Tag name**::
-
-        bootstrap_css
-
-    **usage**::
-
-        {% bootstrap_css %}
-
-    **example**::
-
-        {% bootstrap_css %}
-    """
-    urls = [url for url in [bootstrap_css_url(), bootstrap_theme_url()] if url]
-    return ''.join([render_link_tag(url) for url in urls])
-
-
-@register.simple_tag
-def bootstrap_javascript(jquery=None):
-    """
-    Return HTML for Bootstrap JavaScript.
-
-    Adjust url in settings. If no url is returned, we don't want this
-    statement to return any HTML.
-    This is intended behavior.
-
-    Default value: ``None``
-
-    This value is configurable, see Settings section
-
-    **Tag name**::
-
-        bootstrap_javascript
-
-    **Parameters**:
-
-        :jquery: Truthy to include jQuery as well as Bootstrap
-
-    **usage**::
-
-        {% bootstrap_javascript %}
-
-    **example**::
-
-        {% bootstrap_javascript jquery=1 %}
-    """
-
-    javascript = ''
-    # See if we have to include jQuery
-    if jquery is None:
-        jquery = get_bootstrap_setting('include_jquery', False)
-    # NOTE: No async on scripts, not mature enough. See issue #52 and #56
-    if jquery:
-        url = bootstrap_jquery_url()
-        if url:
-            javascript += '<script src="{url}"></script>'.format(url=url)
-    url = bootstrap_javascript_url()
-    if url:
-        javascript += '<script src="{url}"></script>'.format(url=url)
-    return javascript
-
-
-@register.simple_tag
-def bootstrap_formset(*args, **kwargs):
-    """
-    Render a formset
-
-
-    **Tag name**::
-
-        bootstrap_formset
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_formset formset %}
-
-    **example**::
-
-        {% bootstrap_formset formset layout='horizontal' %}
-
-    """
-    return render_formset(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_formset_errors(*args, **kwargs):
-    """
-    Render form errors
-
-    **Tag name**::
-
-        bootstrap_form_errors
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_form_errors form %}
-
-    **example**::
-
-        {% bootstrap_form_errors form layout='inline' %}
-    """
-    return render_formset_errors(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_form(*args, **kwargs):
-    """
-    Render a form
-
-    **Tag name**::
-
-        bootstrap_form
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_form form %}
-
-    **example**::
-
-        {% bootstrap_form form layout='inline' %}
-    """
-    return render_form(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_form_errors(*args, **kwargs):
-    """
-    Render form errors
-
-    **Tag name**::
-
-        bootstrap_form_errors
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_form_errors form %}
-
-    **example**::
-
-        {% bootstrap_form_errors form layout='inline' %}
-    """
-    return render_form_errors(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_field(*args, **kwargs):
-    """
-    Render a field
-
-    **Tag name**::
-
-        bootstrap_field
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_field form_field %}
-
-    **example**::
-
-        {% bootstrap_field form_field %}
-    """
-    return render_field(*args, **kwargs)
-
-
-@register.simple_tag()
-def bootstrap_label(*args, **kwargs):
-    """
-    Render a label
-
-    **Tag name**::
-
-        bootstrap_label
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_label FIXTHIS %}
-
-    **example**::
-
-        {% bootstrap_label FIXTHIS %}
-    """
-    return render_label(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_button(*args, **kwargs):
-    """
-    Render a button
-
-    **Tag name**::
-
-        bootstrap_button
-
-    **Parameters**:
-
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_button FIXTHIS %}
-
-    **example**::
-
-        {% bootstrap_button FIXTHIS %}
-    """
-    return render_button(*args, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_icon(icon, **kwargs):
-    """
-    Render an icon
-
-    **Tag name**::
-
-        bootstrap_icon
-
-    **Parameters**:
-
-        :icon: icon name
-
-    **usage**::
-
-        {% bootstrap_icon "icon_name" %}
-
-    **example**::
-
-        {% bootstrap_icon "star" %}
-
-    """
-    return render_icon(icon, **kwargs)
-
-
-@register.simple_tag
-def bootstrap_alert(content, alert_type='info', dismissable=True):
-    """
-    Render an alert
-
-    **Tag name**::
-
-        bootstrap_alert
-
-    **Parameters**:
-
-        :content: HTML content of alert
-        :alert_type: one of 'info', 'warning', 'danger' or 'success'
-        :dismissable: boolean, is alert dismissable
-
-    **usage**::
-
-        {% bootstrap_alert "my_content" %}
-
-    **example**::
-
-        {% bootstrap_alert "Something went wrong" alert_type='error' %}
-
-    """
-    return render_alert(content, alert_type, dismissable)
-
-
-@register.tag('buttons')
-def bootstrap_buttons(parser, token):
-    """
-    Render buttons for form
-
-    **Tag name**::
-
-        bootstrap_buttons
-
-    **Parameters**:
-
-        :parser:
-        :token:
-
-    **usage**::
-
-        {% bootstrap_buttons FIXTHIS %}
-
-    **example**::
-
-        {% bootstrap_buttons FIXTHIS %}
-    """
-    kwargs = parse_token_contents(parser, token)
-    kwargs['nodelist'] = parser.parse(('endbuttons', ))
-    parser.delete_first_token()
-    return ButtonsNode(**kwargs)
-
-
-class ButtonsNode(template.Node):
-    def __init__(self, nodelist, args, kwargs, asvar, **kwargs2):
-        self.nodelist = nodelist
-        self.args = args
-        self.kwargs = kwargs
-        self.asvar = asvar
-
-    def render(self, context):
-        output_kwargs = {}
-        for key in self.kwargs:
-            output_kwargs[key] = handle_var(self.kwargs[key], context)
-        buttons = []
-        submit = output_kwargs.get('submit', None)
-        reset = output_kwargs.get('reset', None)
-        if submit:
-            buttons.append(bootstrap_button(submit, 'submit'))
-        if reset:
-            buttons.append(bootstrap_button(reset, 'reset'))
-        buttons = ' '.join(buttons) + self.nodelist.render(context)
-        output_kwargs.update({
-            'label': None,
-            'field': buttons,
-        })
-        output = render_form_group(render_field_and_label(**output_kwargs))
-        if self.asvar:
-            context[self.asvar] = output
-            return ''
-        else:
-            return output
-
-
-@register.simple_tag(takes_context=True)
-def bootstrap_messages(context, *args, **kwargs):
-    """
-    Show django.contrib.messages Messages in Bootstrap alert containers.
-
-    In order to make the alerts dismissable (with the close button),
-    we have to set the jquery parameter too when using the
-    bootstrap_javascript tag.
-
-    **Tag name**::
-
-        bootstrap_messages
-
-    **Parameters**:
-
-        :context:
-        :args:
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_messages FIXTHIS %}
-
-    **example**::
-
-        {% bootstrap_javascript jquery=1 %}
-        {% bootstrap_messages FIXTHIS %}
-
-    """
-    return get_template('bootstrap3/messages.html').render(context)
-
-
-@register.inclusion_tag('bootstrap3/pagination.html')
-def bootstrap_pagination(page, **kwargs):
-    """
-    Render pagination for a page
-
-    **Tag name**::
-
-        bootstrap_pagination
-
-    **Parameters**:
-
-        :page:
-        :parameter_name: Name of paging URL parameter (default: "page")
-        :kwargs:
-
-    **usage**::
-
-        {% bootstrap_pagination FIXTHIS %}
-
-    **example**::
-
-        {% bootstrap_pagination FIXTHIS %}
-    """
-
-    pagination_kwargs = kwargs.copy()
-    pagination_kwargs['page'] = page
-    return get_pagination_context(**pagination_kwargs)
-
-
-def get_pagination_context(page, pages_to_show=11,
-                           url=None, size=None, extra=None,
-                           parameter_name='page'):
-    """
-    Generate Bootstrap pagination context from a page object
-    """
-    pages_to_show = int(pages_to_show)
-    if pages_to_show < 1:
-        raise ValueError("Pagination pages_to_show should be a positive " +
-                         "integer, you specified {pages}".format(
-                             pages=pages_to_show))
-    num_pages = page.paginator.num_pages
-    current_page = page.number
-    half_page_num = int(floor(pages_to_show / 2)) - 1
-    if half_page_num < 0:
-        half_page_num = 0
-    first_page = current_page - half_page_num
-    if first_page <= 1:
-        first_page = 1
-    if first_page > 1:
-        pages_back = first_page - half_page_num
-        if pages_back < 1:
-            pages_back = 1
-    else:
-        pages_back = None
-    last_page = first_page + pages_to_show - 1
-    if pages_back is None:
-        last_page += 1
-    if last_page > num_pages:
-        last_page = num_pages
-    if last_page < num_pages:
-        pages_forward = last_page + half_page_num
-        if pages_forward > num_pages:
-            pages_forward = num_pages
-    else:
-        pages_forward = None
-        if first_page > 1:
-            first_page -= 1
-        if pages_back is not None and pages_back > 1:
-            pages_back -= 1
-        else:
-            pages_back = None
-    pages_shown = []
-    for i in range(first_page, last_page + 1):
-        pages_shown.append(i)
-        # Append proper character to url
-    if url:
-        # Remove existing page GET parameters
-        url = force_text(url)
-        url = re.sub(r'\?{0}\=[^\&]+'.format(parameter_name), '?', url)
-        url = re.sub(r'\&{0}\=[^\&]+'.format(parameter_name), '', url)
-        # Append proper separator
-        if '?' in url:
-            url += '&'
-        else:
-            url += '?'
-            # Append extra string to url
-    if extra:
-        if not url:
-            url = '?'
-        url += force_text(extra) + '&'
-    if url:
-        url = url.replace('?&', '?')
-    # Set CSS classes, see http://getbootstrap.com/components/#pagination
-    pagination_css_classes = ['pagination']
-    if size == 'small':
-        pagination_css_classes.append('pagination-sm')
-    elif size == 'large':
-        pagination_css_classes.append('pagination-lg')
-        # Build context object
-    return {
-        'bootstrap_pagination_url': url,
-        'num_pages': num_pages,
-        'current_page': current_page,
-        'first_page': first_page,
-        'last_page': last_page,
-        'pages_shown': pages_shown,
-        'pages_back': pages_back,
-        'pages_forward': pages_forward,
-        'pagination_css_classes': ' '.join(pagination_css_classes),
-        'parameter_name': parameter_name,
-    }
diff --git a/bootstrap3/tests.py b/bootstrap3/tests.py
deleted file mode 100644
index cf2345fd1..000000000
--- a/bootstrap3/tests.py
+++ /dev/null
@@ -1,447 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import re
-
-from django.test import TestCase
-
-from django import forms
-from django.template import Template, Context
-
-from .text import text_value, text_concat
-from .exceptions import BootstrapError
-from .utils import add_css_class
-
-
-RADIO_CHOICES = (
-    ('1', 'Radio 1'),
-    ('2', 'Radio 2'),
-)
-
-MEDIA_CHOICES = (
-    ('Audio', (
-        ('vinyl', 'Vinyl'),
-        ('cd', 'CD'),
-    )
-    ),
-    ('Video', (
-        ('vhs', 'VHS Tape'),
-        ('dvd', 'DVD'),
-    )
-    ),
-    ('unknown', 'Unknown'),
-)
-
-
-class TestForm(forms.Form):
-    """
-    Form with a variety of widgets to test bootstrap3 rendering.
-    """
-    date = forms.DateField(required=False)
-    subject = forms.CharField(
-        max_length=100,
-        help_text='my_help_text',
-        required=True,
-        widget=forms.TextInput(attrs={'placeholder': 'placeholdertest'}),
-    )
-    message = forms.CharField(required=False, help_text='<i>my_help_text</i>')
-    sender = forms.EmailField(label='Sender © unicode')
-    secret = forms.CharField(initial=42, widget=forms.HiddenInput)
-    cc_myself = forms.BooleanField(
-        required=False, help_text='You will get a copy in your mailbox.')
-    select1 = forms.ChoiceField(choices=RADIO_CHOICES)
-    select2 = forms.MultipleChoiceField(
-        choices=RADIO_CHOICES,
-        help_text='Check as many as you like.',
-    )
-    select3 = forms.ChoiceField(choices=MEDIA_CHOICES)
-    select4 = forms.MultipleChoiceField(
-        choices=MEDIA_CHOICES,
-        help_text='Check as many as you like.',
-    )
-    category1 = forms.ChoiceField(
-        choices=RADIO_CHOICES, widget=forms.RadioSelect)
-    category2 = forms.MultipleChoiceField(
-        choices=RADIO_CHOICES,
-        widget=forms.CheckboxSelectMultiple,
-        help_text='Check as many as you like.',
-    )
-    category3 = forms.ChoiceField(
-        widget=forms.RadioSelect, choices=MEDIA_CHOICES)
-    category4 = forms.MultipleChoiceField(
-        choices=MEDIA_CHOICES,
-        widget=forms.CheckboxSelectMultiple,
-        help_text='Check as many as you like.',
-    )
-    addon = forms.CharField(
-        widget=forms.TextInput(attrs={'addon_before': 'before', 'addon_after': 'after'}),
-    )
-
-    required_css_class = 'bootstrap3-req'
-
-    def clean(self):
-        cleaned_data = super(TestForm, self).clean()
-        raise forms.ValidationError(
-            "This error was added to show the non field errors styling.")
-        return cleaned_data
-
-
-class TestFormWithoutRequiredClass(TestForm):
-    required_css_class = ''
-
-
-def render_template(text, **context_args):
-    """
-    Create a template ``text`` that first loads bootstrap3.
-    """
-    template = Template("{% load bootstrap3 %}" + text)
-    if 'form' not in context_args:
-        context_args['form'] = TestForm()
-    return template.render(Context(context_args))
-
-
-def render_formset(formset=None, **context_args):
-    """
-    Create a template that renders a formset
-    """
-    context_args['formset'] = formset
-    return render_template('{% bootstrap_formset formset %}', **context_args)
-
-
-def render_form(form=None, **context_args):
-    """
-    Create a template that renders a form
-    """
-    if form:
-        context_args['form'] = form
-    return render_template('{% bootstrap_form form %}', **context_args)
-
-
-def render_form_field(field, **context_args):
-    """
-    Create a template that renders a field
-    """
-    form_field = 'form.%s' % field
-    return render_template(
-        '{% bootstrap_field ' + form_field + ' %}', **context_args)
-
-
-def render_field(field, **context_args):
-    """
-    Create a template that renders a field
-    """
-    context_args['field'] = field
-    return render_template('{% bootstrap_field field %}', **context_args)
-
-
-class SettingsTest(TestCase):
-    def test_settings(self):
-        from .bootstrap import BOOTSTRAP3
-        self.assertTrue(BOOTSTRAP3)
-
-    def test_settings_filter(self):
-        res = render_template(
-            '{% load bootstrap3 %}' +
-            '{{ "required_css_class"|bootstrap_setting }}')
-        self.assertEqual(res.strip(), 'bootstrap3-req')
-        res = render_template(
-            '{% load bootstrap3 %}' +
-            '{% if "javascript_in_head"|bootstrap_setting %}' +
-            'head{% else %}body{% endif %}'
-        )
-        self.assertEqual(res.strip(), 'head')
-
-    def test_required_class(self):
-        form = TestForm()
-        res = render_template('{% bootstrap_form form %}', form=form)
-        self.assertIn('bootstrap3-req', res)
-
-    def test_error_class(self):
-        form = TestForm({})
-        res = render_template('{% bootstrap_form form %}', form=form)
-        self.assertIn('bootstrap3-err', res)
-
-    def test_bound_class(self):
-        form = TestForm({'sender': 'sender'})
-        res = render_template('{% bootstrap_form form %}', form=form)
-        self.assertIn('bootstrap3-bound', res)
-
-
-class TemplateTest(TestCase):
-    def test_empty_template(self):
-        res = render_template('')
-        self.assertEqual(res.strip(), '')
-
-    def test_text_template(self):
-        res = render_template('some text')
-        self.assertEqual(res.strip(), 'some text')
-
-    def test_bootstrap_template(self):
-        template = Template((
-            '{% extends "bootstrap3/bootstrap3.html" %}' +
-            '{% block bootstrap3_content %}' +
-            'test_bootstrap3_content' +
-            '{% endblock %}'
-        ))
-        res = template.render(Context({}))
-        self.assertIn('test_bootstrap3_content', res)
-
-    def test_javascript_without_jquery(self):
-        res = render_template('{% bootstrap_javascript jquery=0 %}')
-        self.assertIn('bootstrap', res)
-        self.assertNotIn('jquery', res)
-
-    def test_javascript_with_jquery(self):
-        res = render_template('{% bootstrap_javascript jquery=1 %}')
-        self.assertIn('bootstrap', res)
-        self.assertIn('jquery', res)
-
-
-class FormSetTest(TestCase):
-    def test_illegal_formset(self):
-        with self.assertRaises(BootstrapError):
-            render_formset(formset='illegal')
-
-
-class FormTest(TestCase):
-    def test_illegal_form(self):
-        with self.assertRaises(BootstrapError):
-            render_form(form='illegal')
-
-    def test_field_names(self):
-        form = TestForm()
-        res = render_form(form)
-        for field in form:
-            self.assertIn('name="%s"' % field.name, res)
-
-    def test_field_addons(self):
-        form = TestForm()
-        res = render_form(form)
-        self.assertIn('<div class="input-group"><span class="input-group-addon">before</span><input', res)
-        self.assertIn('/><span class="input-group-addon">after</span></div>', res)
-
-    def test_exclude(self):
-        form = TestForm()
-        res = render_template(
-            '{% bootstrap_form form exclude="cc_myself" %}', form=form)
-        self.assertNotIn('cc_myself', res)
-
-    def test_layout_horizontal(self):
-        form = TestForm()
-        res = render_template(
-            '{% bootstrap_form form layout="horizontal" %}', form=form)
-        self.assertIn('col-md-2', res)
-        self.assertIn('col-md-4', res)
-        res = render_template(
-            '{% bootstrap_form form layout="horizontal" ' +
-            'horizontal_label_class="hlabel" ' +
-            'horizontal_field_class="hfield" %}',
-            form=form
-        )
-        self.assertIn('hlabel', res)
-        self.assertIn('hfield', res)
-
-    def test_buttons_tag(self):
-        form = TestForm()
-        res = render_template(
-            '{% buttons layout="horizontal" %}{% endbuttons %}', form=form)
-        self.assertIn('col-md-2', res)
-        self.assertIn('col-md-4', res)
-
-
-class FieldTest(TestCase):
-    def test_illegal_field(self):
-        with self.assertRaises(BootstrapError):
-            render_field(field='illegal')
-
-    def test_show_help(self):
-        res = render_form_field('subject')
-        self.assertIn('my_help_text', res)
-        self.assertNotIn('<i>my_help_text</i>', res)
-        res = render_template('{% bootstrap_field form.subject show_help=0 %}')
-        self.assertNotIn('my_help_text', res)
-
-    def test_subject(self):
-        res = render_form_field('subject')
-        self.assertIn('type="text"', res)
-        self.assertIn('placeholder="placeholdertest"', res)
-
-    def test_required_field(self):
-        required_field = render_form_field('subject')
-        self.assertIn('required', required_field)
-        self.assertIn('bootstrap3-req', required_field)
-        not_required_field = render_form_field('message')
-        self.assertNotIn('required', not_required_field)
-        # Required field with required=0
-        form_field = 'form.subject'
-        rendered = render_template(
-            '{% bootstrap_field ' + form_field + ' set_required=0 %}')
-        self.assertNotIn('required', rendered)
-        # Required settings in field
-        form_field = 'form.subject'
-        rendered = render_template(
-            '{% bootstrap_field ' +
-            form_field +
-            ' required_css_class="test-required" %}')
-        self.assertIn('test-required', rendered)
-
-    def test_empty_permitted(self):
-        form = TestForm()
-        res = render_form_field('subject', form=form)
-        self.assertIn('required', res)
-        form.empty_permitted = True
-        res = render_form_field('subject', form=form)
-        self.assertNotIn('required', res)
-
-    def test_input_group(self):
-        res = render_template(
-            '{% bootstrap_field form.subject addon_before="$" ' +
-            'addon_after=".00" %}'
-        )
-        self.assertIn('class="input-group"', res)
-        self.assertIn('class="input-group-addon">$', res)
-        self.assertIn('class="input-group-addon">.00', res)
-
-    def test_size(self):
-        def _test_size(param, klass):
-            res = render_template(
-                '{% bootstrap_field form.subject size="' + param + '" %}')
-            self.assertIn(klass, res)
-
-        def _test_size_medium(param):
-            res = render_template(
-                '{% bootstrap_field form.subject size="' + param + '" %}')
-            self.assertNotIn('input-lg', res)
-            self.assertNotIn('input-sm', res)
-            self.assertNotIn('input-md', res)
-        _test_size('sm', 'input-sm')
-        _test_size('small', 'input-sm')
-        _test_size('lg', 'input-lg')
-        _test_size('large', 'input-lg')
-        _test_size_medium('md')
-        _test_size_medium('medium')
-        _test_size_medium('')
-
-
-class ComponentsTest(TestCase):
-    def test_icon(self):
-        res = render_template('{% bootstrap_icon "star" %}')
-        self.assertEqual(
-            res.strip(), '<span class="glyphicon glyphicon-star"></span>')
-        res = render_template(
-            '{% bootstrap_icon "star" title="alpha centauri" %}')
-        self.assertEqual(
-            res.strip(),
-            '<span class="glyphicon glyphicon-star" ' +
-            'title="alpha centauri"></span>')
-
-    def test_alert(self):
-        res = render_template(
-            '{% bootstrap_alert "content" alert_type="danger" %}')
-        self.assertEqual(
-            res.strip(),
-            '<div class="alert alert-danger alert-dismissable">' +
-            '<button type="button" class="close" data-dismiss="alert" ' +
-            'aria-hidden="true">' +
-            '&times;</button>content</div>'
-        )
-
-
-class MessagesTest(TestCase):
-    def test_messages(self):
-        class FakeMessage(object):
-            """
-            Follows the `django.contrib.messages.storage.base.Message` API.
-            """
-
-            def __init__(self, message, tags):
-                self.tags = tags
-                self.message = message
-
-            def __str__(self):
-                return self.message
-
-        pattern = re.compile(r'\s+')
-        messages = [FakeMessage("hello", "warning")]
-        res = render_template(
-            '{% bootstrap_messages messages %}', messages=messages)
-        expected = """
-    <div class="alert alert-warning alert-dismissable">
-        <button type="button" class="close" data-dismiss="alert"
-            aria-hidden="true">&#215;</button>
-        hello
-    </div>
-"""
-        self.assertEqual(
-            re.sub(pattern, '', res),
-            re.sub(pattern, '', expected)
-        )
-
-        messages = [FakeMessage("hello", "error")]
-        res = render_template(
-            '{% bootstrap_messages messages %}', messages=messages)
-        expected = """
-    <div class="alert alert-danger alert-dismissable">
-        <button type="button" class="close" data-dismiss="alert"
-            aria-hidden="true">&#215;</button>
-        hello
-    </div>
-        """
-        self.assertEqual(
-            re.sub(pattern, '', res),
-            re.sub(pattern, '', expected)
-        )
-
-        messages = [FakeMessage("hello", None)]
-        res = render_template(
-            '{% bootstrap_messages messages %}', messages=messages)
-        expected = """
-    <div class="alert alert-dismissable">
-        <button type="button" class="close" data-dismiss="alert"
-            aria-hidden="true">&#215;</button>
-        hello
-    </div>
-        """
-
-        self.assertEqual(
-            re.sub(pattern, '', res),
-            re.sub(pattern, '', expected)
-        )
-
-
-class TextTest(TestCase):
-    def test_add_css_class(self):
-        css_classes = "one two"
-        css_class = "three four"
-        classes = add_css_class(css_classes, css_class)
-        self.assertEqual(classes, "one two three four")
-
-        classes = add_css_class(css_classes, css_class, prepend=True)
-        self.assertEqual(classes, "three four one two")
-
-
-class HtmlTest(TestCase):
-    def test_text_value(self):
-        self.assertEqual(text_value(''), "")
-        self.assertEqual(text_value(' '), " ")
-        self.assertEqual(text_value(None), "")
-        self.assertEqual(text_value(1), "1")
-
-    def test_text_concat(self):
-        self.assertEqual(text_concat(1, 2), "12")
-        self.assertEqual(text_concat(1, 2, separator='='), "1=2")
-        self.assertEqual(text_concat(None, 2, separator='='), "2")
-
-
-class ButtonTest(TestCase):
-    def test_button(self):
-        res = render_template(
-            "{% bootstrap_button 'button' size='lg' %}")
-        self.assertEqual(
-            res.strip(), '<button class="btn btn-lg">button</button>')
-        res = render_template(
-            "{% bootstrap_button 'button' size='lg' href='#' %}")
-        self.assertIn(
-            res.strip(),
-            '<a class="btn btn-lg" href="#">button</a><a href="#" ' +
-            'class="btn btn-lg">button</a>')
diff --git a/bootstrap3/text.py b/bootstrap3/text.py
deleted file mode 100644
index d4fcd4dd5..000000000
--- a/bootstrap3/text.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-
-try:
-    from django.utils.encoding import force_text
-except ImportError:
-    from django.utils.encoding import force_unicode as force_text
-
-
-def text_value(value):
-    """
-    Force a value to text, render None as an empty string
-    """
-    if value is None:
-        return ''
-    return force_text(value)
-
-
-def text_concat(*args, **kwargs):
-    """
-    Concatenate several values as a text string with an optional separator
-    """
-    separator = text_value(kwargs.get('separator', ''))
-    values = filter(None, [text_value(v) for v in args])
-    return separator.join(values)
diff --git a/bootstrap3/utils.py b/bootstrap3/utils.py
deleted file mode 100644
index 8410d63fb..000000000
--- a/bootstrap3/utils.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.forms.widgets import flatatt
-
-from .text import text_value
-
-
-# Handle HTML and CSS manipulation
-
-
-def split_css_classes(css_classes):
-    """
-    Turn string into a list of CSS classes
-    """
-    classes_list = text_value(css_classes).split(' ')
-    return [c for c in classes_list if c]
-
-
-def add_css_class(css_classes, css_class, prepend=False):
-    """
-    Add a CSS class to a string of CSS classes
-    """
-    classes_list = split_css_classes(css_classes)
-    classes_to_add = [c for c in split_css_classes(css_class)
-                      if c not in classes_list]
-    if prepend:
-        classes_list = classes_to_add + classes_list
-    else:
-        classes_list += classes_to_add
-    return ' '.join(classes_list)
-
-
-def remove_css_class(css_classes, css_class):
-    """
-    Remove a CSS class from a string of CSS classes
-    """
-    remove = set(split_css_classes(css_class))
-    classes_list = [c for c in split_css_classes(css_classes)
-                    if c not in remove]
-    return ' '.join(classes_list)
-
-
-def render_link_tag(url, rel='stylesheet', media='all'):
-    """
-    Build a link tag
-    """
-    return render_tag(
-        'link',
-        attrs={'href': url, 'rel': rel, 'media': media},
-        close=False)
-
-
-def render_tag(tag, attrs=None, content=None, close=True):
-    """
-    Render a HTML tag
-    """
-    builder = '<{tag}{attrs}>{content}'
-    if content or close:
-        builder += '</{tag}>'
-    return builder.format(
-        tag=tag,
-        attrs=flatatt(attrs) if attrs else '',
-        content=text_value(content),
-    )
diff --git a/requirements.txt b/requirements.txt
index e99397316..e30147344 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,6 +3,7 @@ coverage>=3.7.1
 cssselect>=0.6.1
 decorator>=3.4.0
 defusedxml>=0.4.1
+django-bootstrap3>=5.1.1
 docutils>=0.12
 lxml>=3.4.0
 mimeparse>=0.1.3