# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import json
import datetime

from django import template
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape as esc
from django.utils.timesince import timesince
from bob.forms.dependency import DependencyForm


register = template.Library()


@register.simple_tag
def bob_icon(name, is_white=False):
    """
    Display a bootstrap icon.

    :param name: The name of the icon to display.
    :param is_white: Whether the icon should be white (for dark background).
    """

    white = ' icon-white' if is_white else ''
    return mark_safe('<i class="icon-%s%s"></i>' % esc(name, white))


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
              position='', title_url="/"):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {
        'static': 'navbar-static-top',
        'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom',
    }
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')

    return {
        'items': items,
        'selected': selected,
        'title': title,
        'search': search,
        'position': position,
        'white': bool(white),
        'title_url': title_url,
        'class': ' '.join(klass),
    }


@register.inclusion_tag('bob/dropdown_items.html')
def dropdown_items(items, white=False):
    """
    Render dropdown items.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param title: The title to show in the menu bar.
    :param white: If True, the menu bar will be white.
    """
    return {
        'items': items.subitems,
        'white': bool(white),
    }


@register.simple_tag
def render_cell(column, row):
    """Render the cell for a given column and row."""
    return column.render_cell(row)


@register.inclusion_tag('bob/tab_menu.html')
def tab_menu(items, selected, side=None):
    """
    Show a menu in form of tabs.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param side: The direction of tabs, may be on of ``"left"``, ``"right"``,
        ``"top"`` or ``"bottom"``. Defaults to ``"top"``.
    """

    return {
        'items': items,
        'selected': selected,
        'side': side,
    }


@register.inclusion_tag('bob/sidebar_menu.html')
def sidebar_menu(items, selected):
    """
    Show menu in a sidebar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    """

    return {
        'items': items,
        'selected': selected,
    }


@register.inclusion_tag('bob/sidebar_menu_subitems.html')
def sidebar_menu_subitems(item, selected):
    """
    Show subitems of a menu in a sidebar.
    """

    return {
        'item': item,
        'selected': selected,
    }


@register.inclusion_tag('bob/pagination.html')
def pagination(page, show_all=False, show_csv=False,
               fugue_icons=False, url_query=None, neighbors=1,
               query_variable_name='page', export_variable_name='export'):
    """
    Display pagination for a list of items.

    :param page: Django's paginator page to display.
    :param show_all: Whether to show a link for disabling pagination.
    :param show_csv: Whether to show a link to CSV download.
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.
    :param url_query: The query parameters to add to all page links.
    :param neighbors: How many neighboring pages to show in paginator.
    """

    if not page:
        return {
            'show_all': show_all,
            'show_csv': show_csv,
            'fugue_icons': fugue_icons,
            'url_query': url_query,
            'export_variable_name': export_variable_name,
        }
    paginator = page.paginator
    page_no = page.number
    pages = paginator.page_range[
        max(0, page_no - 1 - neighbors):
        min(paginator.num_pages, page_no + neighbors)
    ]

    if 1 not in pages:
        pages.insert(0, 1)
        pages.insert(1, '...')
    if paginator.num_pages not in pages:
        pages.append('...')
        pages.append(paginator.num_pages)
    urls = []
    for item in pages:
        if item == '...':
            urls.append(changed_url(url_query, query_variable_name, page_no))
        else:
            urls.append(changed_url(url_query, query_variable_name, item))
    url_pages = zip(pages, urls)
    return {
        'paginator': paginator,
        'page_no': page_no,
        'page': page,
        'pages': pages,
        'show_all': show_all,
        'show_csv': show_csv,
        'fugue_icons': fugue_icons,
        'url_query': url_query,
        'url_previous_page': changed_url(
            url_query,
            query_variable_name,
            page_no - 1
        ),
        'url_next_page': changed_url(
            url_query,
            query_variable_name,
            page_no + 1
        ),
        'url_pages': url_pages,
        'url_all': changed_url(url_query, query_variable_name, 0),
        'export_variable_name': export_variable_name,
    }


def changed_url(query, name, value):
    if not query:
        return '%s=%s' % (name, value)
    query = query.copy()
    if value is not None and value not in ('1', 1):
        query[name] = value
    else:
        try:
            del query[name]
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def bob_export(query, export):
    """Modify the query string of an URL to change the ``export`` argument."""

    if not query:
        return 'export=%s' % export
    query = query.copy()
    if export:
        query['export'] = export
    else:
        try:
            del query['export']
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action="", method="POST", fugue_icons=False,
         css_class="form-horizontal", title="", submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {
        'form': form,
        'action': action,
        'title': title,
        'method': method,
        'fugue_icons': fugue_icons,
        'css_class': css_class,
        'submit_label': submit_label,
    }


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception(
            "{}.Meta.fieldset attribute is UNDEFINED or EMPTY".format(
                repr(form_instance)
            )
        )
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
                 sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {
        'columns': new_columns,
        'sort': sort,
        'url_query': url_query,
        'fugue_icons': fugue_icons,
        'sort_variable_name': sort_variable_name,
    }


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


@register.simple_tag
def bob_export_url(query, value, export_variable_name='export'):
    """Modify the query string of an URL to change the ``export_variable_name``
    argument.
    """
    if not query:
        return '%s=%s' % (export_variable_name, value)
    query = query.copy()
    if value:
        query[export_variable_name] = value
    else:
        try:
            del query[export_variable_name]
        except KeyError:
            pass
    return query.urlencode()


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""

    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(
        esc(json.dumps(form.get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


@register.filter
def get_item(obj, key):
    return obj[key]

f1ef92521dd62e7ce56efecab0ba6662a11d069a
<mask token>


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
    position='', title_url='/'):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {'static': 'navbar-static-top', 'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom'}
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')
    return {'items': items, 'selected': selected, 'title': title, 'search':
        search, 'position': position, 'white': bool(white), 'title_url':
        title_url, 'class': ' '.join(klass)}


<mask token>


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action='', method='POST', fugue_icons=False, css_class=
    'form-horizontal', title='', submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {'form': form, 'action': action, 'title': title, 'method':
        method, 'fugue_icons': fugue_icons, 'css_class': css_class,
        'submit_label': submit_label}


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception('{}.Meta.fieldset attribute is UNDEFINED or EMPTY'.
            format(repr(form_instance)))
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
    sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {'columns': new_columns, 'sort': sort, 'url_query': url_query,
        'fugue_icons': fugue_icons, 'sort_variable_name': sort_variable_name}


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


<mask token>


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""
    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(esc(json.dumps(form.
        get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


<mask token>

<mask token>


@register.simple_tag
def bob_icon(name, is_white=False):
    """
    Display a bootstrap icon.

    :param name: The name of the icon to display.
    :param is_white: Whether the icon should be white (for dark background).
    """
    white = ' icon-white' if is_white else ''
    return mark_safe('<i class="icon-%s%s"></i>' % esc(name, white))


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
    position='', title_url='/'):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {'static': 'navbar-static-top', 'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom'}
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')
    return {'items': items, 'selected': selected, 'title': title, 'search':
        search, 'position': position, 'white': bool(white), 'title_url':
        title_url, 'class': ' '.join(klass)}


<mask token>


@register.simple_tag
def render_cell(column, row):
    """Render the cell for a given column and row."""
    return column.render_cell(row)


<mask token>


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action='', method='POST', fugue_icons=False, css_class=
    'form-horizontal', title='', submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {'form': form, 'action': action, 'title': title, 'method':
        method, 'fugue_icons': fugue_icons, 'css_class': css_class,
        'submit_label': submit_label}


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception('{}.Meta.fieldset attribute is UNDEFINED or EMPTY'.
            format(repr(form_instance)))
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
    sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {'columns': new_columns, 'sort': sort, 'url_query': url_query,
        'fugue_icons': fugue_icons, 'sort_variable_name': sort_variable_name}


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


<mask token>


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""
    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(esc(json.dumps(form.
        get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


@register.filter
def get_item(obj, key):
    return obj[key]

<mask token>


@register.simple_tag
def bob_icon(name, is_white=False):
    """
    Display a bootstrap icon.

    :param name: The name of the icon to display.
    :param is_white: Whether the icon should be white (for dark background).
    """
    white = ' icon-white' if is_white else ''
    return mark_safe('<i class="icon-%s%s"></i>' % esc(name, white))


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
    position='', title_url='/'):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {'static': 'navbar-static-top', 'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom'}
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')
    return {'items': items, 'selected': selected, 'title': title, 'search':
        search, 'position': position, 'white': bool(white), 'title_url':
        title_url, 'class': ' '.join(klass)}


<mask token>


@register.simple_tag
def render_cell(column, row):
    """Render the cell for a given column and row."""
    return column.render_cell(row)


<mask token>


@register.inclusion_tag('bob/sidebar_menu.html')
def sidebar_menu(items, selected):
    """
    Show menu in a sidebar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    """
    return {'items': items, 'selected': selected}


@register.inclusion_tag('bob/sidebar_menu_subitems.html')
def sidebar_menu_subitems(item, selected):
    """
    Show subitems of a menu in a sidebar.
    """
    return {'item': item, 'selected': selected}


@register.inclusion_tag('bob/pagination.html')
def pagination(page, show_all=False, show_csv=False, fugue_icons=False,
    url_query=None, neighbors=1, query_variable_name='page',
    export_variable_name='export'):
    """
    Display pagination for a list of items.

    :param page: Django's paginator page to display.
    :param show_all: Whether to show a link for disabling pagination.
    :param show_csv: Whether to show a link to CSV download.
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.
    :param url_query: The query parameters to add to all page links.
    :param neighbors: How many neighboring pages to show in paginator.
    """
    if not page:
        return {'show_all': show_all, 'show_csv': show_csv, 'fugue_icons':
            fugue_icons, 'url_query': url_query, 'export_variable_name':
            export_variable_name}
    paginator = page.paginator
    page_no = page.number
    pages = paginator.page_range[max(0, page_no - 1 - neighbors):min(
        paginator.num_pages, page_no + neighbors)]
    if 1 not in pages:
        pages.insert(0, 1)
        pages.insert(1, '...')
    if paginator.num_pages not in pages:
        pages.append('...')
        pages.append(paginator.num_pages)
    urls = []
    for item in pages:
        if item == '...':
            urls.append(changed_url(url_query, query_variable_name, page_no))
        else:
            urls.append(changed_url(url_query, query_variable_name, item))
    url_pages = zip(pages, urls)
    return {'paginator': paginator, 'page_no': page_no, 'page': page,
        'pages': pages, 'show_all': show_all, 'show_csv': show_csv,
        'fugue_icons': fugue_icons, 'url_query': url_query,
        'url_previous_page': changed_url(url_query, query_variable_name, 
        page_no - 1), 'url_next_page': changed_url(url_query,
        query_variable_name, page_no + 1), 'url_pages': url_pages,
        'url_all': changed_url(url_query, query_variable_name, 0),
        'export_variable_name': export_variable_name}


def changed_url(query, name, value):
    if not query:
        return '%s=%s' % (name, value)
    query = query.copy()
    if value is not None and value not in ('1', 1):
        query[name] = value
    else:
        try:
            del query[name]
        except KeyError:
            pass
    return query.urlencode()


<mask token>


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action='', method='POST', fugue_icons=False, css_class=
    'form-horizontal', title='', submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {'form': form, 'action': action, 'title': title, 'method':
        method, 'fugue_icons': fugue_icons, 'css_class': css_class,
        'submit_label': submit_label}


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception('{}.Meta.fieldset attribute is UNDEFINED or EMPTY'.
            format(repr(form_instance)))
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
    sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {'columns': new_columns, 'sort': sort, 'url_query': url_query,
        'fugue_icons': fugue_icons, 'sort_variable_name': sort_variable_name}


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


<mask token>


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""
    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(esc(json.dumps(form.
        get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


@register.filter
def get_item(obj, key):
    return obj[key]

<mask token>
register = template.Library()


@register.simple_tag
def bob_icon(name, is_white=False):
    """
    Display a bootstrap icon.

    :param name: The name of the icon to display.
    :param is_white: Whether the icon should be white (for dark background).
    """
    white = ' icon-white' if is_white else ''
    return mark_safe('<i class="icon-%s%s"></i>' % esc(name, white))


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
    position='', title_url='/'):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {'static': 'navbar-static-top', 'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom'}
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')
    return {'items': items, 'selected': selected, 'title': title, 'search':
        search, 'position': position, 'white': bool(white), 'title_url':
        title_url, 'class': ' '.join(klass)}


@register.inclusion_tag('bob/dropdown_items.html')
def dropdown_items(items, white=False):
    """
    Render dropdown items.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param title: The title to show in the menu bar.
    :param white: If True, the menu bar will be white.
    """
    return {'items': items.subitems, 'white': bool(white)}


@register.simple_tag
def render_cell(column, row):
    """Render the cell for a given column and row."""
    return column.render_cell(row)


@register.inclusion_tag('bob/tab_menu.html')
def tab_menu(items, selected, side=None):
    """
    Show a menu in form of tabs.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param side: The direction of tabs, may be on of ``"left"``, ``"right"``,
        ``"top"`` or ``"bottom"``. Defaults to ``"top"``.
    """
    return {'items': items, 'selected': selected, 'side': side}


@register.inclusion_tag('bob/sidebar_menu.html')
def sidebar_menu(items, selected):
    """
    Show menu in a sidebar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    """
    return {'items': items, 'selected': selected}


@register.inclusion_tag('bob/sidebar_menu_subitems.html')
def sidebar_menu_subitems(item, selected):
    """
    Show subitems of a menu in a sidebar.
    """
    return {'item': item, 'selected': selected}


@register.inclusion_tag('bob/pagination.html')
def pagination(page, show_all=False, show_csv=False, fugue_icons=False,
    url_query=None, neighbors=1, query_variable_name='page',
    export_variable_name='export'):
    """
    Display pagination for a list of items.

    :param page: Django's paginator page to display.
    :param show_all: Whether to show a link for disabling pagination.
    :param show_csv: Whether to show a link to CSV download.
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.
    :param url_query: The query parameters to add to all page links.
    :param neighbors: How many neighboring pages to show in paginator.
    """
    if not page:
        return {'show_all': show_all, 'show_csv': show_csv, 'fugue_icons':
            fugue_icons, 'url_query': url_query, 'export_variable_name':
            export_variable_name}
    paginator = page.paginator
    page_no = page.number
    pages = paginator.page_range[max(0, page_no - 1 - neighbors):min(
        paginator.num_pages, page_no + neighbors)]
    if 1 not in pages:
        pages.insert(0, 1)
        pages.insert(1, '...')
    if paginator.num_pages not in pages:
        pages.append('...')
        pages.append(paginator.num_pages)
    urls = []
    for item in pages:
        if item == '...':
            urls.append(changed_url(url_query, query_variable_name, page_no))
        else:
            urls.append(changed_url(url_query, query_variable_name, item))
    url_pages = zip(pages, urls)
    return {'paginator': paginator, 'page_no': page_no, 'page': page,
        'pages': pages, 'show_all': show_all, 'show_csv': show_csv,
        'fugue_icons': fugue_icons, 'url_query': url_query,
        'url_previous_page': changed_url(url_query, query_variable_name, 
        page_no - 1), 'url_next_page': changed_url(url_query,
        query_variable_name, page_no + 1), 'url_pages': url_pages,
        'url_all': changed_url(url_query, query_variable_name, 0),
        'export_variable_name': export_variable_name}


def changed_url(query, name, value):
    if not query:
        return '%s=%s' % (name, value)
    query = query.copy()
    if value is not None and value not in ('1', 1):
        query[name] = value
    else:
        try:
            del query[name]
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def bob_export(query, export):
    """Modify the query string of an URL to change the ``export`` argument."""
    if not query:
        return 'export=%s' % export
    query = query.copy()
    if export:
        query['export'] = export
    else:
        try:
            del query['export']
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action='', method='POST', fugue_icons=False, css_class=
    'form-horizontal', title='', submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {'form': form, 'action': action, 'title': title, 'method':
        method, 'fugue_icons': fugue_icons, 'css_class': css_class,
        'submit_label': submit_label}


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception('{}.Meta.fieldset attribute is UNDEFINED or EMPTY'.
            format(repr(form_instance)))
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
    sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {'columns': new_columns, 'sort': sort, 'url_query': url_query,
        'fugue_icons': fugue_icons, 'sort_variable_name': sort_variable_name}


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


@register.simple_tag
def bob_export_url(query, value, export_variable_name='export'):
    """Modify the query string of an URL to change the ``export_variable_name``
    argument.
    """
    if not query:
        return '%s=%s' % (export_variable_name, value)
    query = query.copy()
    if value:
        query[export_variable_name] = value
    else:
        try:
            del query[export_variable_name]
        except KeyError:
            pass
    return query.urlencode()


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""
    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(esc(json.dumps(form.
        get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


@register.filter
def get_item(obj, key):
    return obj[key]

# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import json
import datetime

from django import template
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape as esc
from django.utils.timesince import timesince
from bob.forms.dependency import DependencyForm


register = template.Library()


@register.simple_tag
def bob_icon(name, is_white=False):
    """
    Display a bootstrap icon.

    :param name: The name of the icon to display.
    :param is_white: Whether the icon should be white (for dark background).
    """

    white = ' icon-white' if is_white else ''
    return mark_safe('<i class="icon-%s%s"></i>' % esc(name, white))


@register.inclusion_tag('bob/main_menu.html')
def main_menu(items, selected, title=None, search=None, white=False,
              position='', title_url="/"):
    """
    Show main menu bar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param title: The title to show in the menu bar.
    :param search: The URL for the search form.
    :param white: If True, the menu bar will be white.
    :param position: Empty, or one of ``'fixed'``, ``'static'``, ``'bottom'``.
    """
    positions = {
        'static': 'navbar-static-top',
        'fixed': 'navbar-fixed-top',
        'bottom': 'navbar-fixed-bottom',
    }
    klass = ['navbar', positions.get(position, '')]
    if not white:
        klass.append('navbar-inverse')

    return {
        'items': items,
        'selected': selected,
        'title': title,
        'search': search,
        'position': position,
        'white': bool(white),
        'title_url': title_url,
        'class': ' '.join(klass),
    }


@register.inclusion_tag('bob/dropdown_items.html')
def dropdown_items(items, white=False):
    """
    Render dropdown items.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param title: The title to show in the menu bar.
    :param white: If True, the menu bar will be white.
    """
    return {
        'items': items.subitems,
        'white': bool(white),
    }


@register.simple_tag
def render_cell(column, row):
    """Render the cell for a given column and row."""
    return column.render_cell(row)


@register.inclusion_tag('bob/tab_menu.html')
def tab_menu(items, selected, side=None):
    """
    Show a menu in form of tabs.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    :param side: The direction of tabs, may be on of ``"left"``, ``"right"``,
        ``"top"`` or ``"bottom"``. Defaults to ``"top"``.
    """

    return {
        'items': items,
        'selected': selected,
        'side': side,
    }


@register.inclusion_tag('bob/sidebar_menu.html')
def sidebar_menu(items, selected):
    """
    Show menu in a sidebar.

    :param items: The list of :class:`bob.menu.MenuItem` instances to show.
    :param selected: The :data:`name` of the currently selected item.
    """

    return {
        'items': items,
        'selected': selected,
    }


@register.inclusion_tag('bob/sidebar_menu_subitems.html')
def sidebar_menu_subitems(item, selected):
    """
    Show subitems of a menu in a sidebar.
    """

    return {
        'item': item,
        'selected': selected,
    }


@register.inclusion_tag('bob/pagination.html')
def pagination(page, show_all=False, show_csv=False,
               fugue_icons=False, url_query=None, neighbors=1,
               query_variable_name='page', export_variable_name='export'):
    """
    Display pagination for a list of items.

    :param page: Django's paginator page to display.
    :param show_all: Whether to show a link for disabling pagination.
    :param show_csv: Whether to show a link to CSV download.
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.
    :param url_query: The query parameters to add to all page links.
    :param neighbors: How many neighboring pages to show in paginator.
    """

    if not page:
        return {
            'show_all': show_all,
            'show_csv': show_csv,
            'fugue_icons': fugue_icons,
            'url_query': url_query,
            'export_variable_name': export_variable_name,
        }
    paginator = page.paginator
    page_no = page.number
    pages = paginator.page_range[
        max(0, page_no - 1 - neighbors):
        min(paginator.num_pages, page_no + neighbors)
    ]

    if 1 not in pages:
        pages.insert(0, 1)
        pages.insert(1, '...')
    if paginator.num_pages not in pages:
        pages.append('...')
        pages.append(paginator.num_pages)
    urls = []
    for item in pages:
        if item == '...':
            urls.append(changed_url(url_query, query_variable_name, page_no))
        else:
            urls.append(changed_url(url_query, query_variable_name, item))
    url_pages = zip(pages, urls)
    return {
        'paginator': paginator,
        'page_no': page_no,
        'page': page,
        'pages': pages,
        'show_all': show_all,
        'show_csv': show_csv,
        'fugue_icons': fugue_icons,
        'url_query': url_query,
        'url_previous_page': changed_url(
            url_query,
            query_variable_name,
            page_no - 1
        ),
        'url_next_page': changed_url(
            url_query,
            query_variable_name,
            page_no + 1
        ),
        'url_pages': url_pages,
        'url_all': changed_url(url_query, query_variable_name, 0),
        'export_variable_name': export_variable_name,
    }


def changed_url(query, name, value):
    if not query:
        return '%s=%s' % (name, value)
    query = query.copy()
    if value is not None and value not in ('1', 1):
        query[name] = value
    else:
        try:
            del query[name]
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def bob_export(query, export):
    """Modify the query string of an URL to change the ``export`` argument."""

    if not query:
        return 'export=%s' % export
    query = query.copy()
    if export:
        query['export'] = export
    else:
        try:
            del query['export']
        except KeyError:
            pass
    return query.urlencode()


@register.filter
def timesince_limited(d):
    """
    Display time between given date and now in a human-readable form if the
    time span is less than a day, otherwise display the date normally.

    :param d: The date to display.
    """
    today = datetime.datetime.now()
    delta = datetime.timedelta
    interval = today - d
    if today.strftime('%Y-%m-%d') == d.strftime('%Y-%m-%d'):
        if interval < delta(days=0, hours=1):
            return timesince(d) + ' ago '
        else:
            return d.strftime('%H:%M')
    else:
        return d


@register.inclusion_tag('bob/form.html')
def form(form, action="", method="POST", fugue_icons=False,
         css_class="form-horizontal", title="", submit_label='Save'):
    """
    Render a form.

    :param form: The form to render.
    :param action: The submit URL.
    :param method: The submit method, either ``"GET"`` or ``"POST"``.
    :param fugue_icons: Whether to use Fugue or Bootstrap icon.
    :param css_class: The CSS class to use for the ``<form>`` tag.
    :param title: Form title.
    :param submit_label: Submit button label.
    """
    return {
        'form': form,
        'action': action,
        'title': title,
        'method': method,
        'fugue_icons': fugue_icons,
        'css_class': css_class,
        'submit_label': submit_label,
    }


@register.inclusion_tag('bob/form_as_fieldsets.html')
def form_as_fieldsets(form_instance, *args, **kwargs):
    if not getattr(form_instance.Meta, 'fieldset', None):
        raise Exception(
            "{}.Meta.fieldset attribute is UNDEFINED or EMPTY".format(
                repr(form_instance)
            )
        )
    return form(form_instance, *args, **kwargs)


@register.inclusion_tag('bob/form.html')
def form_horizontal(*args, **kwargs):
    return form(*args, **kwargs)


@register.inclusion_tag('bob/table_header.html')
def table_header(columns=None, url_query=None, sort=None, fugue_icons=False,
                 sort_variable_name='sort'):
    """
    Render a table header with sorted column options

    :param columns: a list of objects of
                    type :py:class:bob.data_table.DataTableColumn
    :param url_query: The query parameters to add to all page links
    :param sort: means that the column is now sorted
    :param fugue_icons: Whether to use Fugue icons or Bootstrap icons.

    show_conditions field on column item - func and args which determines
    whether the column is to be displayed.
    """
    new_columns = []
    for column in columns:
        if isinstance(column.show_conditions, tuple):
            func, arg = column.show_conditions
            if func(arg):
                new_columns.append(column)
        else:
            new_columns.append(column)
    return {
        'columns': new_columns,
        'sort': sort,
        'url_query': url_query,
        'fugue_icons': fugue_icons,
        'sort_variable_name': sort_variable_name,
    }


@register.simple_tag
def bob_sort_url(query, field, sort_variable_name, type):
    """Modify the query string of an URL to change the ``sort_variable_name``
    argument.
    """
    query = query.copy()
    if type == 'desc':
        query[sort_variable_name] = '-' + field
    elif type == 'asc':
        query[sort_variable_name] = field
    return query.urlencode()


@register.simple_tag
def bob_export_url(query, value, export_variable_name='export'):
    """Modify the query string of an URL to change the ``export_variable_name``
    argument.
    """
    if not query:
        return '%s=%s' % (export_variable_name, value)
    query = query.copy()
    if value:
        query[export_variable_name] = value
    else:
        try:
            del query[export_variable_name]
        except KeyError:
            pass
    return query.urlencode()


@register.simple_tag
def dependency_data(form):
    """Render the data-bob-dependencies tag if this is a DependencyForm"""

    if not isinstance(form, DependencyForm):
        return ''
    return 'data-bob-dependencies="{0}"'.format(
        esc(json.dumps(form.get_dependencies_for_js())))


@register.inclusion_tag('bob/field_wrapper.html')
def field_wrapper(field):
    """Render the full control-group tag of a field."""
    return {'field': field}


@register.filter
def get_item(obj, key):
    return obj[key]
