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 ''.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 = '' - return '
{button}{content}
'.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 = ' ' - label_class = add_css_class(label_class, 'control-label') - html = field - if field_class: - html = '
{html}
'.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 '
{content}
'.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 = [ - ('', ''), - ('', ''), - ] - 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 = '
' - div2 = '
' - html = html.replace('', '' + div2) - return '
' + html + '
' - - def fix_clearable_file_input(self, html): - """ - Fix a clearable file input - TODO: This needs improvement - - Currently Django returns - Currently: - dummy.txt - -
- Change: - - - - """ - # TODO This needs improvement - return '
' + \ - '
' + html + '
' - - 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 = \ - '
{content}
'.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 = '{addon}'.format( - addon=self.addon_before) if self.addon_before else '' - after = '{addon}'.format( - addon=self.addon_after) if self.addon_after else '' - html = \ - '
' + \ - '{before}{html}{after}
'.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 += '{help}'.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 = '
{html}
'.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 ' ' - 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.+)["\']$') - - -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 @@ - -{% load bootstrap3 %} - - - - - - - {% block bootstrap3_title %}django-bootstrap3 template title{% endblock %} - {% bootstrap_css %} - - - - {% if 'javascript_in_head'|bootstrap_setting %}{% bootstrap_javascript jquery=True %}{% endif %} - {% block bootstrap3_extra_head %}{% endblock %} - - - -{% 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 %} - - - 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 @@ - 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 %} -
- - {{ message|safe }} -
-{% 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:"?" %} - -
    - -
  • - « -
  • - - {% if pages_back %} -
  • - -
  • - {% endif %} - - {% for p in pages_shown %} - - {{ p }} - - {% endfor %} - - {% if pages_forward %} -
  • - -
  • - {% endif %} - -
  • - » -
  • - -
- -{% 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 += ''.format(url=url) - url = bootstrap_javascript_url() - if url: - javascript += ''.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='my_help_text') - 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('
beforeafter
', 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('my_help_text', 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(), '') - res = render_template( - '{% bootstrap_icon "star" title="alpha centauri" %}') - self.assertEqual( - res.strip(), - '') - - def test_alert(self): - res = render_template( - '{% bootstrap_alert "content" alert_type="danger" %}') - self.assertEqual( - res.strip(), - '
' + - 'content
' - ) - - -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 = """ -
- - hello -
-""" - self.assertEqual( - re.sub(pattern, '', res), - re.sub(pattern, '', expected) - ) - - messages = [FakeMessage("hello", "error")] - res = render_template( - '{% bootstrap_messages messages %}', messages=messages) - expected = """ -
- - hello -
- """ - self.assertEqual( - re.sub(pattern, '', res), - re.sub(pattern, '', expected) - ) - - messages = [FakeMessage("hello", None)] - res = render_template( - '{% bootstrap_messages messages %}', messages=messages) - expected = """ -
- - hello -
- """ - - 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(), '') - res = render_template( - "{% bootstrap_button 'button' size='lg' href='#' %}") - self.assertIn( - res.strip(), - 'buttonbutton') 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 += '' - 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