Commit fba873d5 authored by Sergey Markelov's avatar Sergey Markelov

Merge remote-tracking branch 'upstream/master'

parents d3dddbf6 19d6dbd7
...@@ -5,6 +5,7 @@ from wtforms import form, fields, validators ...@@ -5,6 +5,7 @@ from wtforms import form, fields, validators
from flask.ext import admin, login from flask.ext import admin, login
from flask.ext.admin.contrib import sqla from flask.ext.admin.contrib import sqla
from flask.ext.admin import helpers, expose from flask.ext.admin import helpers, expose
from werkzeug.security import generate_password_hash, check_password_hash
# Create Flask application # Create Flask application
...@@ -59,7 +60,10 @@ class LoginForm(form.Form): ...@@ -59,7 +60,10 @@ class LoginForm(form.Form):
if user is None: if user is None:
raise validators.ValidationError('Invalid user') raise validators.ValidationError('Invalid user')
if user.password != self.password.data: # we're comparing the plaintext pw with the the hash from the db
if not check_password_hash(user.password, self.password.data):
# to compare plain text passwords use
# if user.password != self.password.data:
raise validators.ValidationError('Invalid password') raise validators.ValidationError('Invalid password')
def get_user(self): def get_user(self):
...@@ -125,6 +129,9 @@ class MyAdminIndexView(admin.AdminIndexView): ...@@ -125,6 +129,9 @@ class MyAdminIndexView(admin.AdminIndexView):
user = User() user = User()
form.populate_obj(user) form.populate_obj(user)
# we hash the users password to avoid saving it as plaintext in the db,
# remove to use plain text:
user.password = generate_password_hash(form.password.data)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
...@@ -188,7 +195,9 @@ def build_sample_db(): ...@@ -188,7 +195,9 @@ def build_sample_db():
user.last_name = last_names[i] user.last_name = last_names[i]
user.login = user.first_name.lower() user.login = user.first_name.lower()
user.email = user.login + "@example.com" user.email = user.login + "@example.com"
user.password = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10)) user.password = generate_password_hash(''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10)))
# passwords are hashed, to use plaintext passwords use:
# user.password = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10))
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
Authentication Authentication
</p> </p>
<p> <p>
This example shows how you can use Flask-Login for authentication. It is only intended as a basic demonstration, so please don't freak out when you see passwords being stored as plain text. This example shows how you can use Flask-Login for authentication. It is only intended as a basic demonstration.
</p> </p>
{% else %} {% else %}
<form method="POST" action=""> <form method="POST" action="">
......
__version__ = '1.0.8' __version__ = '1.0.9.dev0'
__author__ = 'Serge S. Koval' __author__ = 'Serge S. Koval'
__email__ = 'serge.koval+github@gmail.com' __email__ = 'serge.koval+github@gmail.com'
......
...@@ -148,7 +148,8 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -148,7 +148,8 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
return args return args
def __init__(self, name=None, category=None, endpoint=None, url=None, def __init__(self, name=None, category=None, endpoint=None, url=None,
static_folder=None, static_url_path=None): static_folder=None, static_url_path=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -168,9 +169,16 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -168,9 +169,16 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
and '/admin/' prefix won't be applied. and '/admin/' prefix won't be applied.
:param static_url_path: :param static_url_path:
Static URL Path. If provided, this specifies the path to the static url directory. Static URL Path. If provided, this specifies the path to the static url directory.
:param debug: :param menu_class_name:
Optional debug flag. If set to `True`, will rethrow exceptions in some cases, so Werkzeug Optional class name for the menu item.
debugger can catch them. :param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self.name = name self.name = name
self.category = category self.category = category
...@@ -178,6 +186,11 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -178,6 +186,11 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
self.url = url self.url = url
self.static_folder = static_folder self.static_folder = static_folder
self.static_url_path = static_url_path self.static_url_path = static_url_path
self.menu = None
self.menu_class_name = menu_class_name
self.menu_icon_type = menu_icon_type
self.menu_icon_value = menu_icon_value
# Initialized from create_blueprint # Initialized from create_blueprint
self.admin = None self.admin = None
...@@ -483,6 +496,9 @@ class Admin(object): ...@@ -483,6 +496,9 @@ class Admin(object):
def _add_view_to_menu(self, view): def _add_view_to_menu(self, view):
self._add_menu_item(MenuView(view.name, view), view.category) self._add_menu_item(MenuView(view.name, view), view.category)
def get_category_menu_item(self, name):
return self._menu_categories.get(name)
def init_app(self, app): def init_app(self, app):
""" """
Register all views with the Flask application. Register all views with the Flask application.
......
# bootstrap glyph icon
ICON_TYPE_GLYPH = 'glyph'
# image relative to Flask static folder
ICON_TYPE_IMAGE = 'image'
# external image
ICON_TYPE_IMAGE_URL = 'image-url'
...@@ -199,7 +199,8 @@ class ModelView(BaseModelView): ...@@ -199,7 +199,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, name=None, def __init__(self, model, name=None,
category=None, endpoint=None, url=None): category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor Constructor
...@@ -213,10 +214,24 @@ class ModelView(BaseModelView): ...@@ -213,10 +214,24 @@ class ModelView(BaseModelView):
Endpoint Endpoint
:param url: :param url:
Custom URL Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self._search_fields = [] self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
...@@ -493,12 +508,11 @@ class ModelView(BaseModelView): ...@@ -493,12 +508,11 @@ class ModelView(BaseModelView):
model.save() model.save()
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to create model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to create model')
return False return False
else: else:
self.after_model_change(form, model, True) self.after_model_change(form, model, True)
...@@ -520,12 +534,11 @@ class ModelView(BaseModelView): ...@@ -520,12 +534,11 @@ class ModelView(BaseModelView):
model.save() model.save()
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to update model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to update model')
return False return False
else: else:
self.after_model_change(form, model, False) self.after_model_change(form, model, False)
...@@ -545,12 +558,11 @@ class ModelView(BaseModelView): ...@@ -545,12 +558,11 @@ class ModelView(BaseModelView):
return True return True
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to delete model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to delete model')
return False return False
# FileField access API # FileField access API
...@@ -600,7 +612,5 @@ class ModelView(BaseModelView): ...@@ -600,7 +612,5 @@ class ModelView(BaseModelView):
count=count)) count=count))
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to delete models. %(error)s', error=str(ex)),
'error')
flash(gettext('Failed to delete models. %(error)s', error=str(ex)),
'error')
...@@ -132,10 +132,14 @@ class ModelView(BaseModelView): ...@@ -132,10 +132,14 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, name=None, def __init__(self, model, name=None,
category=None, endpoint=None, url=None): category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
self._search_fields = [] self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
...@@ -350,10 +354,9 @@ class ModelView(BaseModelView): ...@@ -350,10 +354,9 @@ class ModelView(BaseModelView):
save_inline(form, model) save_inline(form, model)
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
return False return False
else: else:
self.after_model_change(form, model, True) self.after_model_change(form, model, True)
...@@ -370,10 +373,9 @@ class ModelView(BaseModelView): ...@@ -370,10 +373,9 @@ class ModelView(BaseModelView):
save_inline(form, model) save_inline(form, model)
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
return False return False
else: else:
self.after_model_change(form, model, False) self.after_model_change(form, model, False)
...@@ -387,10 +389,9 @@ class ModelView(BaseModelView): ...@@ -387,10 +389,9 @@ class ModelView(BaseModelView):
return True return True
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
return False return False
# Default model actions # Default model actions
...@@ -425,6 +426,4 @@ class ModelView(BaseModelView): ...@@ -425,6 +426,4 @@ class ModelView(BaseModelView):
count=count)) count=count))
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to delete models. %(error)s', error=str(ex)), 'error')
flash(gettext('Failed to delete models. %(error)s', error=str(ex)), 'error')
...@@ -39,7 +39,8 @@ class ModelView(BaseModelView): ...@@ -39,7 +39,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, coll, def __init__(self, coll,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor Constructor
...@@ -53,6 +54,16 @@ class ModelView(BaseModelView): ...@@ -53,6 +54,16 @@ class ModelView(BaseModelView):
Endpoint Endpoint
:param url: :param url:
Custom URL Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self._search_fields = [] self._search_fields = []
...@@ -62,7 +73,10 @@ class ModelView(BaseModelView): ...@@ -62,7 +73,10 @@ class ModelView(BaseModelView):
if endpoint is None: if endpoint is None:
endpoint = ('%sview' % coll.name).lower() endpoint = ('%sview' % coll.name).lower()
super(ModelView, self).__init__(None, name, category, endpoint, url) super(ModelView, self).__init__(None, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.coll = coll self.coll = coll
......
...@@ -242,7 +242,8 @@ class ModelView(BaseModelView): ...@@ -242,7 +242,8 @@ class ModelView(BaseModelView):
""" """
def __init__(self, model, session, def __init__(self, model, session,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -258,6 +259,16 @@ class ModelView(BaseModelView): ...@@ -258,6 +259,16 @@ class ModelView(BaseModelView):
Endpoint name. If not set, defaults to the model name Endpoint name. If not set, defaults to the model name
:param url: :param url:
Base URL. If not set, defaults to '/admin/' + endpoint Base URL. If not set, defaults to '/admin/' + endpoint
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
self.session = session self.session = session
...@@ -271,7 +282,10 @@ class ModelView(BaseModelView): ...@@ -271,7 +282,10 @@ class ModelView(BaseModelView):
if self.form_choices is None: if self.form_choices is None:
self.form_choices = {} self.form_choices = {}
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
# Primary key # Primary key
self._primary_key = self.scaffold_pk() self._primary_key = self.scaffold_pk()
...@@ -831,11 +845,11 @@ class ModelView(BaseModelView): ...@@ -831,11 +845,11 @@ class ModelView(BaseModelView):
self.session.commit() self.session.commit()
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
self.session.rollback() self.session.rollback()
return False return False
else: else:
self.after_model_change(form, model, True) self.after_model_change(form, model, True)
...@@ -857,10 +871,9 @@ class ModelView(BaseModelView): ...@@ -857,10 +871,9 @@ class ModelView(BaseModelView):
self.session.commit() self.session.commit()
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
self.session.rollback() self.session.rollback()
return False return False
...@@ -884,11 +897,11 @@ class ModelView(BaseModelView): ...@@ -884,11 +897,11 @@ class ModelView(BaseModelView):
return True return True
except Exception as ex: except Exception as ex:
if not self.handle_view_exception(ex): if not self.handle_view_exception(ex):
raise flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
self.session.rollback() self.session.rollback()
return False return False
# Default model actions # Default model actions
......
...@@ -5,6 +5,12 @@ from flask.ext.admin import helpers as h ...@@ -5,6 +5,12 @@ from flask.ext.admin import helpers as h
__all__ = ['Select2Widget', 'DatePickerWidget', 'DateTimePickerWidget', 'RenderTemplateWidget', 'Select2TagsWidget', ] __all__ = ['Select2Widget', 'DatePickerWidget', 'DateTimePickerWidget', 'RenderTemplateWidget', 'Select2TagsWidget', ]
def _is_bootstrap3():
view = h.get_current_view()
return view and view.admin.template_mode == 'bootstrap3'
class Select2Widget(widgets.Select): class Select2Widget(widgets.Select):
""" """
`Select2 <https://github.com/ivaynberg/select2>`_ styled select widget. `Select2 <https://github.com/ivaynberg/select2>`_ styled select widget.
...@@ -14,7 +20,7 @@ class Select2Widget(widgets.Select): ...@@ -14,7 +20,7 @@ class Select2Widget(widgets.Select):
""" """
def __call__(self, field, **kwargs): def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'select2') kwargs.setdefault('data-role', u'select2')
allow_blank = getattr(field, 'allow_blank', False) allow_blank = getattr(field, 'allow_blank', False)
if allow_blank and not self.multiple: if allow_blank and not self.multiple:
kwargs['data-allow-blank'] = u'1' kwargs['data-allow-blank'] = u'1'
...@@ -41,7 +47,12 @@ class DatePickerWidget(widgets.TextInput): ...@@ -41,7 +47,12 @@ class DatePickerWidget(widgets.TextInput):
""" """
def __call__(self, field, **kwargs): def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datepicker') kwargs.setdefault('data-role', u'datepicker')
kwargs.setdefault('data-date-format', u'yyyy-mm-dd')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'YYYY-MM-DD')
else:
kwargs.setdefault('data-date-format', u'yyyy-mm-dd')
kwargs.setdefault('data-date-autoclose', u'true') kwargs.setdefault('data-date-autoclose', u'true')
self.date_format = kwargs['data-date-format'] self.date_format = kwargs['data-date-format']
return super(DatePickerWidget, self).__call__(field, **kwargs) return super(DatePickerWidget, self).__call__(field, **kwargs)
...@@ -55,10 +66,15 @@ class DateTimePickerWidget(widgets.TextInput): ...@@ -55,10 +66,15 @@ class DateTimePickerWidget(widgets.TextInput):
""" """
def __call__(self, field, **kwargs): def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datetimepicker') kwargs.setdefault('data-role', u'datetimepicker')
kwargs.setdefault('data-date-format', u'yyyy-mm-dd hh:ii:ss')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'YYYY-MM-DD hh:mm:ss')
else:
kwargs.setdefault('data-date-format', u'yyyy-mm-dd hh:ii:ss')
kwargs.setdefault('data-date-today-btn', u'linked')
kwargs.setdefault('data-date-today-highlight', u'true')
kwargs.setdefault('data-date-autoclose', u'true') kwargs.setdefault('data-date-autoclose', u'true')
kwargs.setdefault('data-date-today-btn', u'linked')
kwargs.setdefault('data-date-today-highlight', u'true')
return super(DateTimePickerWidget, self).__call__(field, **kwargs) return super(DateTimePickerWidget, self).__call__(field, **kwargs)
...@@ -70,7 +86,12 @@ class TimePickerWidget(widgets.TextInput): ...@@ -70,7 +86,12 @@ class TimePickerWidget(widgets.TextInput):
""" """
def __call__(self, field, **kwargs): def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'timepicker') kwargs.setdefault('data-role', u'timepicker')
kwargs.setdefault('data-date-format', u'hh:ii:ss')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'hh:mm:ss')
else:
kwargs.setdefault('data-date-format', u'hh:ii:ss')
kwargs.setdefault('data-date-autoclose', u'true') kwargs.setdefault('data-date-autoclose', u'true')
return super(TimePickerWidget, self).__call__(field, **kwargs) return super(TimePickerWidget, self).__call__(field, **kwargs)
......
...@@ -5,11 +5,18 @@ class BaseMenu(object): ...@@ -5,11 +5,18 @@ class BaseMenu(object):
""" """
Base menu item Base menu item
""" """
def __init__(self, name): def __init__(self, name, class_name=None, icon_type=None, icon_value=None):
self.name = name self.name = name
self.class_name = class_name
self.icon_type = icon_type
self.icon_value = icon_value
self.parent = None
self._children = [] self._children = []
def add_child(self, menu): def add_child(self, menu):
# TODO: Check if menu item is already assigned to some parent
menu.parent = self
self._children.append(menu) self._children.append(menu)
def get_url(self): def get_url(self):
...@@ -25,6 +32,15 @@ class BaseMenu(object): ...@@ -25,6 +32,15 @@ class BaseMenu(object):
return False return False
def get_class_name(self):
return self.class_name
def get_icon_type(self):
return self.icon_type
def get_icon_value(self):
return self.icon_value
def is_visible(self): def is_visible(self):
return True return True
...@@ -65,11 +81,16 @@ class MenuView(BaseMenu): ...@@ -65,11 +81,16 @@ class MenuView(BaseMenu):
Admin view menu item Admin view menu item
""" """
def __init__(self, name, view=None): def __init__(self, name, view=None):
super(MenuView, self).__init__(name) super(MenuView, self).__init__(name,
class_name=view.menu_class_name,
icon_type=view.menu_icon_type,
icon_value=view.menu_icon_value)
self._view = view self._view = view
self._cached_url = None self._cached_url = None
view.menu = self
def get_url(self): def get_url(self):
if self._view is None: if self._view is None:
return None return None
...@@ -103,8 +124,8 @@ class MenuLink(BaseMenu): ...@@ -103,8 +124,8 @@ class MenuLink(BaseMenu):
""" """
Link item Link item
""" """
def __init__(self, name, url=None, endpoint=None, category=None): def __init__(self, name, url=None, endpoint=None, category=None, class_name=None, icon_type=None, icon_value=None):
super(MenuLink, self).__init__(name) super(MenuLink, self).__init__(name, class_name, icon_type, icon_value)
self.category = category self.category = category
......
...@@ -365,7 +365,7 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -365,7 +365,7 @@ class BaseModelView(BaseView, ActionsMixin):
'style': 'color: black' 'style': 'color: black'
} }
} }
Note, changing the format of a DateTimeField will require changes to both form_widget_args and form_args:: Note, changing the format of a DateTimeField will require changes to both form_widget_args and form_args::
form_args = dict( form_args = dict(
...@@ -484,7 +484,8 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -484,7 +484,8 @@ class BaseModelView(BaseView, ActionsMixin):
""" """
def __init__(self, model, def __init__(self, model,
name=None, category=None, endpoint=None, url=None): name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
""" """
Constructor. Constructor.
...@@ -500,9 +501,16 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -500,9 +501,16 @@ class BaseModelView(BaseView, ActionsMixin):
'userview' 'userview'
:param url: :param url:
Base URL. If not provided, will use endpoint as a URL. Base URL. If not provided, will use endpoint as a URL.
:param debug: :param menu_class_name:
Enable debugging mode. Won't catch exceptions on model Optional class name for the menu item.
save failures. :param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
""" """
# If name not provided, it is model name # If name not provided, it is model name
...@@ -513,7 +521,10 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -513,7 +521,10 @@ class BaseModelView(BaseView, ActionsMixin):
if endpoint is None: if endpoint is None:
endpoint = model.__name__.lower() endpoint = model.__name__.lower()
super(BaseModelView, self).__init__(name, category, endpoint, url) super(BaseModelView, self).__init__(name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.model = model self.model = model
...@@ -890,9 +901,9 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -890,9 +901,9 @@ class BaseModelView(BaseView, ActionsMixin):
# Exception handler # Exception handler
def handle_view_exception(self, exc): def handle_view_exception(self, exc):
if self._debug: if self._debug:
return False raise
return True return False
# Model event handlers # Model event handlers
def on_model_change(self, form, model, is_created): def on_model_change(self, form, model, is_created):
...@@ -954,6 +965,29 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -954,6 +965,29 @@ class BaseModelView(BaseView, ActionsMixin):
By default do nothing. By default do nothing.
""" """
pass pass
def on_form_prefill (self, form, id):
"""
Perform additional actions to pre-fill the edit form.
Called from edit_view, if the current action is rendering
the form rather than receiving client side input, after
default pre-filling has been performed.
By default does nothing.
You only need to override this if you have added custom
fields that depend on the database contents in a way that
Flask-admin can't figure out by itself. Fields that were
added by name of a normal column or relationship should
work out of the box.
:param form:
Form instance
:param id:
id of the object that is going to be edited
"""
pass
def create_model(self, form): def create_model(self, form):
""" """
...@@ -1296,6 +1330,9 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -1296,6 +1330,9 @@ class BaseModelView(BaseView, ActionsMixin):
else: else:
return redirect(return_url) return redirect(return_url)
if request.method == 'GET':
self.on_form_prefill(form, id)
form_opts = FormOpts(widget_args=self.form_widget_args, form_opts = FormOpts(widget_args=self.form_widget_args,
form_rules=self._form_edit_rules) form_rules=self._form_edit_rules)
......
...@@ -20,6 +20,7 @@ form.icon button { ...@@ -20,6 +20,7 @@ form.icon button {
a.icon, button span.glyphicon { a.icon, button span.glyphicon {
text-decoration: none; text-decoration: none;
margin-left: 10px; margin-left: 10px;
color: #333;
} }
/* Model search form */ /* Model search form */
......
...@@ -32,6 +32,7 @@ var AdminFilters = function(element, filtersElement, filterGroups) { ...@@ -32,6 +32,7 @@ var AdminFilters = function(element, filtersElement, filterGroups) {
if($('.filters tr').length == 0) { if($('.filters tr').length == 0) {
$('button', $root).hide(); $('button', $root).hide();
$('a[class=btn]', $root).hide(); $('a[class=btn]', $root).hide();
$('.filters tbody').remove();
} else { } else {
$('button', $root).show(); $('button', $root).show();
} }
...@@ -92,6 +93,9 @@ var AdminFilters = function(element, filtersElement, filterGroups) { ...@@ -92,6 +93,9 @@ var AdminFilters = function(element, filtersElement, filterGroups) {
if (filter.type) { if (filter.type) {
$field.attr('data-role', filter.type); $field.attr('data-role', filter.type);
if (filter.type == "datepicker") {
$field.attr('data-date-format', "YYYY-MM-DD");
}
faForm.applyStyle($field, filter.type); faForm.applyStyle($field, filter.type);
} }
} }
......
...@@ -109,17 +109,25 @@ ...@@ -109,17 +109,25 @@
return true; return true;
case 'datepicker': case 'datepicker':
$el.datetimepicker({ $el.datetimepicker({
minView: 'month' // TODO: Have separate converters for bs2 and bs3
// Bootstrap 2 option
minView: 'month',
// Bootstrap 3 option
pickTime: false
}); });
return true; return true;
case 'datetimepicker': case 'datetimepicker':
$el.datetimepicker(); $el.datetimepicker({
});
return true; return true;
case 'timepicker': case 'timepicker':
$el.datetimepicker({ $el.datetimepicker({
// Bootstrap 2 option
startView: 'day', startView: 'day',
maxView: 'day', maxView: 'day',
formatViewType: 'time' formatViewType: 'time',
// Bootstrap 3 option
pickDate: false
}); });
return true; return true;
} }
......
/*!
* Datetimepicker for Bootstrap v3
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:99999!important;border-radius:4px}.bootstrap-datetimepicker-widget.timepicker-sbs{width:600px}.bootstrap-datetimepicker-widget.bottom:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);position:absolute;top:-7px;left:7px}.bootstrap-datetimepicker-widget.bottom:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:8px}.bootstrap-datetimepicker-widget.top:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.top:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;position:absolute;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget .dow{width:14.2857%}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:100%;font-weight:bold;font-size:1.2em}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;width:20px;height:20px;border-radius:4px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#999}.bootstrap-datetimepicker-widget td.today{position:relative}.bootstrap-datetimepicker-widget td.today:before{content:'';display:inline-block;border-left:7px solid transparent;border-bottom:7px solid #428bca;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:block;width:47px;height:54px;line-height:54px;float:left;margin:2px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td span.old{color:#999}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget th.switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px}.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important}
\ No newline at end of file
/*
Version 3.0.0
=========================================================
bootstrap-datetimepicker.js
https://github.com/Eonasdan/bootstrap-datetimepicker
=========================================================
The MIT License (MIT)
Copyright (c) 2014 Jonathan Peterson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function(n){if(typeof define=="function"&&define.amd)define(["jquery","moment"],n);else if(jQuery)if(moment)n(jQuery,moment);else throw"bootstrap-datetimepicker requires moment.js to be loaded first";else throw"bootstrap-datetimepicker requires jQuery to be loaded first";})(function(n,t){if(typeof t=="undefined"){alert("momentjs is requried");throw new Error("momentjs is required");}var u=0,r=t,f=function(t,f){var st={pickDate:!0,pickTime:!0,useMinutes:!0,useSeconds:!1,useCurrent:!0,minuteStepping:1,minDate:new r({y:1900}),maxDate:(new r).add(100,"y"),showToday:!0,collapse:!0,language:"en",defaultDate:"",disabledDates:!1,enabledDates:!1,icons:{},useStrict:!1,direction:"auto",sideBySide:!1,daysOfWeekDisabled:!1},ht={time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down"},e=this,ct=function(){var i=!1,o,h,s;if(e.options=n.extend({},st,f),e.options.icons=n.extend({},ht,e.options.icons),e.element=n(t),lt(),!(e.options.pickTime||e.options.pickDate))throw new Error("Must choose at least one picker");if(e.id=u++,r.lang(e.options.language),e.date=r(),e.unset=!1,e.isInput=e.element.is("input"),e.component=!1,e.element.hasClass("input-group")&&(e.component=e.element.find(".datepickerbutton").size()==0?e.element.find("[class^='input-group-']"):e.element.find(".datepickerbutton")),e.format=e.options.format,o=r()._lang._longDateFormat,e.format||(e.format=e.options.pickDate?o.L:"",e.options.pickDate&&e.options.pickTime&&(e.format+=" "),e.format+=e.options.pickTime?o.LT:"",e.options.useSeconds&&(~o.LT.indexOf(" A")?e.format=e.format.split(" A")[0]+":ss A":e.format+=":ss")),e.use24hours=e.format.toLowerCase().indexOf("a")<1,e.component&&(i=e.component.find("span")),e.options.pickTime&&i&&i.addClass(e.options.icons.time),e.options.pickDate&&i&&(i.removeClass(e.options.icons.time),i.addClass(e.options.icons.date)),e.widget=n(ni()).appendTo("body"),e.options.useSeconds&&!e.use24hours&&e.widget.width(300),e.minViewMode=e.options.minViewMode||0,typeof e.minViewMode=="string")switch(e.minViewMode){case"months":e.minViewMode=1;break;case"years":e.minViewMode=2;break;default:e.minViewMode=0}if(e.viewMode=e.options.viewMode||0,typeof e.viewMode=="string")switch(e.viewMode){case"months":e.viewMode=1;break;case"years":e.viewMode=2;break;default:e.viewMode=0}e.options.disabledDates=d(e.options.disabledDates);e.options.enabledDates=d(e.options.enabledDates);e.startViewMode=e.viewMode;e.setMinDate(e.options.minDate);e.setMaxDate(e.options.maxDate);at();vt();yt();pt();wt();l();b();ut();e.options.defaultDate!==""&&p().val()==""&&e.setValue(e.options.defaultDate);e.options.minuteStepping!==1&&(h=e.date.minutes(),s=e.options.minuteStepping,e.date.minutes(Math.round(h/s)*s%60).seconds(0))},p=function(){return e.isInput?e.element:dateStr=e.element.find("input")},lt=function(){var n;n=e.element.is("input")?e.element.data():e.element.data();n.dateFormat!==undefined&&(e.options.format=n.dateFormat);n.datePickdate!==undefined&&(e.options.pickDate=n.datePickdate);n.datePicktime!==undefined&&(e.options.pickTime=n.datePicktime);n.dateUseminutes!==undefined&&(e.options.useMinutes=n.dateUseminutes);n.dateUseseconds!==undefined&&(e.options.useSeconds=n.dateUseseconds);n.dateUsecurrent!==undefined&&(e.options.useCurrent=n.dateUsecurrent);n.dateMinutestepping!==undefined&&(e.options.minuteStepping=n.dateMinutestepping);n.dateMindate!==undefined&&(e.options.minDate=n.dateMindate);n.dateMaxdate!==undefined&&(e.options.maxDate=n.dateMaxdate);n.dateShowtoday!==undefined&&(e.options.showToday=n.dateShowtoday);n.dateCollapse!==undefined&&(e.options.collapse=n.dateCollapse);n.dateLanguage!==undefined&&(e.options.language=n.dateLanguage);n.dateDefaultdate!==undefined&&(e.options.defaultDate=n.dateDefaultdate);n.dateDisableddates!==undefined&&(e.options.disabledDates=n.dateDisableddates);n.dateEnableddates!==undefined&&(e.options.enabledDates=n.dateEnableddates);n.dateIcons!==undefined&&(e.options.icons=n.dateIcons);n.dateUsestrict!==undefined&&(e.options.useStrict=n.dateUsestrict);n.dateDirection!==undefined&&(e.options.direction=n.dateDirection);n.dateSidebyside!==undefined&&(e.options.sideBySide=n.dateSidebyside)},it=function(){var u="absolute",t=e.component?e.component.offset():e.element.offset(),i=n(window),r;e.width=e.component?e.component.outerWidth():e.element.outerWidth();t.top=t.top+e.element.outerHeight();e.options.direction==="up"?r="top":e.options.direction==="bottom"?r="bottom":e.options.direction==="auto"&&(r=t.top+e.widget.height()>i.height()+i.scrollTop()&&e.widget.height()+e.element.outerHeight()<t.top?"top":"bottom");r==="top"?(t.top-=e.widget.height()+e.element.outerHeight()+15,e.widget.addClass("top").removeClass("bottom")):(t.top+=1,e.widget.addClass("bottom").removeClass("top"));e.options.width!==undefined&&e.widget.width(e.options.width);e.options.orientation==="left"&&(e.widget.addClass("left-oriented"),t.left=t.left-e.widget.width()+20);gt()&&(u="fixed",t.top-=i.scrollTop(),t.left-=i.scrollLeft());i.width()<t.left+e.widget.outerWidth()?(t.right=i.width()-t.left-e.width,t.left="auto",e.widget.addClass("pull-right")):(t.right="auto",e.widget.removeClass("pull-right"));e.widget.css({position:u,top:t.top,left:t.left,right:t.right})},c=function(n,t){r(e.date).isSame(r(n))||(e.element.trigger({type:"dp.change",date:r(e.date),oldDate:r(n)}),t!=="change"&&e.element.change())},g=function(n){e.element.trigger({type:"dp.error",date:r(n)})},l=function(n){r.lang(e.options.language);var t=n;t||(t=p().val(),t&&(e.date=r(t,e.format,e.options.useStrict)),e.date||(e.date=r()));e.viewDate=r(e.date).startOf("month");y();nt()},at=function(){r.lang(e.options.language);var i=n("<tr>"),u=r.weekdaysMin(),t;if(r()._lang._week.dow==0)for(t=0;t<7;t++)i.append('<th class="dow">'+u[t]+"<\/th>");else for(t=1;t<8;t++)t==7?i.append('<th class="dow">'+u[0]+"<\/th>"):i.append('<th class="dow">'+u[t]+"<\/th>");e.widget.find(".datepicker-days thead").append(i)},vt=function(){r.lang(e.options.language);for(var n="",t=0,i=r.monthsShort();t<12;)n+='<span class="month">'+i[t++]+"<\/span>";e.widget.find(".datepicker-months td").append(n)},y=function(){r.lang(e.options.language);var t=e.viewDate.year(),h=e.viewDate.month(),o=e.options.minDate.year(),y=e.options.minDate.month(),s=e.options.maxDate.year(),p=e.options.maxDate.month(),i,w,c=[],v,f,u,b,d,l,a=r.months();for(e.widget.find(".datepicker-days").find(".disabled").removeClass("disabled"),e.widget.find(".datepicker-months").find(".disabled").removeClass("disabled"),e.widget.find(".datepicker-years").find(".disabled").removeClass("disabled"),e.widget.find(".datepicker-days th:eq(1)").text(a[h]+" "+t),i=r(e.viewDate).subtract("months",1),b=i.daysInMonth(),i.date(b).startOf("week"),(t==o&&h<=y||t<o)&&e.widget.find(".datepicker-days th:eq(0)").addClass("disabled"),(t==s&&h>=p||t>s)&&e.widget.find(".datepicker-days th:eq(2)").addClass("disabled"),w=r(i).add(42,"d");i.isBefore(w);){if(i.weekday()===r().startOf("week").weekday()&&(v=n("<tr>"),c.push(v)),f="",i.year()<t||i.year()==t&&i.month()<h?f+=" old":(i.year()>t||i.year()==t&&i.month()>h)&&(f+=" new"),i.isSame(r({y:e.date.year(),M:e.date.month(),d:e.date.date()}))&&(f+=" active"),(k(i)||!ot(i))&&(f+=" disabled"),e.options.showToday===!0&&i.isSame(r(),"day")&&(f+=" today"),e.options.daysOfWeekDisabled)for(u in e.options.daysOfWeekDisabled)if(i.day()==e.options.daysOfWeekDisabled[u]){f+=" disabled";break}v.append('<td class="day'+f+'">'+i.date()+"<\/td>");i.add(1,"d")}for(e.widget.find(".datepicker-days tbody").empty().append(c),l=e.date.year(),a=e.widget.find(".datepicker-months").find("th:eq(1)").text(t).end().find("span").removeClass("active"),l===t&&a.eq(e.date.month()).addClass("active"),l-1<o&&e.widget.find(".datepicker-months th:eq(0)").addClass("disabled"),l+1>s&&e.widget.find(".datepicker-months th:eq(2)").addClass("disabled"),u=0;u<12;u++)t==o&&y>u||t<o?n(a[u]).addClass("disabled"):(t==s&&p<u||t>s)&&n(a[u]).addClass("disabled");for(c="",t=parseInt(t/10,10)*10,d=e.widget.find(".datepicker-years").find("th:eq(1)").text(t+"-"+(t+9)).end().find("td"),e.widget.find(".datepicker-years").find("th").removeClass("disabled"),o>t&&e.widget.find(".datepicker-years").find("th:eq(0)").addClass("disabled"),s<t+9&&e.widget.find(".datepicker-years").find("th:eq(2)").addClass("disabled"),t-=1,u=-1;u<11;u++)c+='<span class="year'+(u===-1||u===10?" old":"")+(l===t?" active":"")+(t<o||t>s?" disabled":"")+'">'+t+"<\/span>",t+=1;d.html(c)},yt=function(){r.lang(e.options.language);var f=e.widget.find(".timepicker .timepicker-hours table"),n="",t,i,u;if(f.parent().hide(),e.use24hours)for(t=0,i=0;i<6;i+=1){for(n+="<tr>",u=0;u<4;u+=1)n+='<td class="hour">'+s(t.toString())+"<\/td>",t++;n+="<\/tr>"}else for(t=1,i=0;i<3;i+=1){for(n+="<tr>",u=0;u<4;u+=1)n+='<td class="hour">'+s(t.toString())+"<\/td>",t++;n+="<\/tr>"}f.html(n)},pt=function(){var f=e.widget.find(".timepicker .timepicker-minutes table"),n="",i=0,r,u,t=e.options.minuteStepping;for(f.parent().hide(),(t=1)&&(t=5),r=0;r<Math.ceil(15/t);r++){for(n+="<tr>",u=0;u<4;u+=1)i<60?(n+='<td class="minute">'+s(i.toString())+"<\/td>",i+=t):n+="<td><\/td>";n+="<\/tr>"}f.html(n)},wt=function(){var r=e.widget.find(".timepicker .timepicker-seconds table"),n="",u=0,t,i;for(r.parent().hide(),t=0;t<3;t++){for(n+="<tr>",i=0;i<4;i+=1)n+='<td class="second">'+s(u.toString())+"<\/td>",u+=5;n+="<\/tr>"}r.html(n)},nt=function(){if(e.date){var t=e.widget.find(".timepicker span[data-time-component]"),n=e.date.hours(),i="AM";e.use24hours||(n>=12&&(i="PM"),n===0?n=12:n!=12&&(n=n%12),e.widget.find(".timepicker [data-action=togglePeriod]").text(i));t.filter("[data-time-component=hours]").text(s(n));t.filter("[data-time-component=minutes]").text(s(e.date.minutes()));t.filter("[data-time-component=seconds]").text(s(e.date.second()))}},bt=function(t){t.stopPropagation();t.preventDefault();e.unset=!1;var i=n(t.target).closest("span, td, th"),u,f,s,h,l=r(e.date);if(i.length===1&&!i.is(".disabled"))switch(i[0].nodeName.toLowerCase()){case"th":switch(i[0].className){case"switch":b(1);break;case"prev":case"next":s=o.modes[e.viewMode].navStep;i[0].className==="prev"&&(s=s*-1);e.viewDate.add(s,o.modes[e.viewMode].navFnc);y()}break;case"span":i.is(".month")?(u=i.parent().find("span").index(i),e.viewDate.month(u)):(f=parseInt(i.text(),10)||0,e.viewDate.year(f));e.viewMode===e.minViewMode&&(e.date=r({y:e.viewDate.year(),M:e.viewDate.month(),d:e.viewDate.date(),h:e.date.hours(),m:e.date.minutes(),s:e.date.seconds()}),c(l,t.type),a());b(-1);y();break;case"td":i.is(".day")&&(h=parseInt(i.text(),10)||1,u=e.viewDate.month(),f=e.viewDate.year(),i.is(".old")?u===0?(u=11,f-=1):u-=1:i.is(".new")&&(u==11?(u=0,f+=1):u+=1),e.date=r({y:f,M:u,d:h,h:e.date.hours(),m:e.date.minutes(),s:e.date.seconds()}),e.viewDate=r({y:f,M:u,d:Math.min(28,h)}),y(),a(),c(l,t.type))}},w={incrementHours:function(){v("add","hours",1)},incrementMinutes:function(){v("add","minutes",e.options.minuteStepping)},incrementSeconds:function(){v("add","seconds",1)},decrementHours:function(){v("subtract","hours",1)},decrementMinutes:function(){v("subtract","minutes",e.options.minuteStepping)},decrementSeconds:function(){v("subtract","seconds",1)},togglePeriod:function(){var n=e.date.hours();n>=12?n-=12:n+=12;e.date.hours(n)},showPicker:function(){e.widget.find(".timepicker > div:not(.timepicker-picker)").hide();e.widget.find(".timepicker .timepicker-picker").show()},showHours:function(){e.widget.find(".timepicker .timepicker-picker").hide();e.widget.find(".timepicker .timepicker-hours").show()},showMinutes:function(){e.widget.find(".timepicker .timepicker-picker").hide();e.widget.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){e.widget.find(".timepicker .timepicker-picker").hide();e.widget.find(".timepicker .timepicker-seconds").show()},selectHour:function(t){var r=e.widget.find(".timepicker [data-action=togglePeriod]").text(),i=parseInt(n(t.target).text(),10);r=="PM"&&(i+=12);e.date.hours(i);w.showPicker.call(e)},selectMinute:function(t){e.date.minutes(parseInt(n(t.target).text(),10));w.showPicker.call(e)},selectSecond:function(t){e.date.seconds(parseInt(n(t.target).text(),10));w.showPicker.call(e)}},kt=function(t){var i=r(e.date),u=n(t.currentTarget).data("action"),f=w[u].apply(e,arguments);return tt(t),e.date||(e.date=r({y:1970})),a(),nt(),c(i,t.type),f},tt=function(n){n.stopPropagation();n.preventDefault()},rt=function(t){r.lang(e.options.language);var f=n(t.target),u=r(e.date),i=r(f.val(),e.format,e.options.useStrict);i.isValid()&&!k(i)&&ot(i)?(l(),e.setValue(i),c(u,t.type),a()):(e.viewDate=u,c(u,t.type),g(i),e.unset=!0)},b=function(n){n&&(e.viewMode=Math.max(e.minViewMode,Math.min(2,e.viewMode+n)));var t=o.modes[e.viewMode].clsName;e.widget.find(".datepicker > div").hide().filter(".datepicker-"+o.modes[e.viewMode].clsName).show()},ut=function(){var i,r,t,f,u;e.widget.on("click",".datepicker *",n.proxy(bt,this));e.widget.on("click","[data-action]",n.proxy(kt,this));e.widget.on("mousedown",n.proxy(tt,this));if(e.options.pickDate&&e.options.pickTime)e.widget.on("click.togglePicker",".accordion-toggle",function(o){if(o.stopPropagation(),i=n(this),r=i.closest("ul"),t=r.find(".in"),f=r.find(".collapse:not(.in)"),t&&t.length){if(u=t.data("collapse"),u&&u.date-transitioning)return;t.collapse("hide");f.collapse("show");i.find("span").toggleClass(e.options.icons.time+" "+e.options.icons.date);e.element.find(".input-group-addon span").toggleClass(e.options.icons.time+" "+e.options.icons.date)}});if(e.isInput)e.element.on({focus:n.proxy(e.show,this),change:n.proxy(rt,this),blur:n.proxy(e.hide,this)});else{e.element.on({change:n.proxy(rt,this)},"input");if(e.component)e.component.on("click",n.proxy(e.show,this));else e.element.on("click",n.proxy(e.show,this))}},dt=function(){n(window).on("resize.datetimepicker"+e.id,n.proxy(it,this));if(!e.isInput)n(document).on("mousedown.datetimepicker"+e.id,n.proxy(e.hide,this))},ft=function(){e.widget.off("click",".datepicker *",e.click);e.widget.off("click","[data-action]");e.widget.off("mousedown",e.stopEvent);e.options.pickDate&&e.options.pickTime&&e.widget.off("click.togglePicker");e.isInput?e.element.off({focus:e.show,change:e.change}):(e.element.off({change:e.change},"input"),e.component?e.component.off("click",e.show):e.element.off("click",e.show))},et=function(){n(window).off("resize.datetimepicker"+e.id);e.isInput||n(document).off("mousedown.datetimepicker"+e.id)},gt=function(){if(e.element){for(var i=e.element.parents(),r=!1,t=0;t<i.length;t++)if(n(i[t]).css("position")=="fixed"){r=!0;break}return r}return!1},a=function(){r.lang(e.options.language);var n="";e.unset||(n=r(e.date).format(e.format));p().val(n);e.element.data("date",n);e.options.pickTime||e.hide()},v=function(n,t,i){r.lang(e.options.language);var u;if(n=="add"?(u=r(e.date),u.hours()==23&&u.add(i,t),u.add(i,t)):u=r(e.date).subtract(i,t),k(r(u.subtract(i,t)))||k(u)){g(u.format(e.format));return}n=="add"?e.date.add(i,t):e.date.subtract(i,t);e.unset=!1},k=function(n){return(r.lang(e.options.language),n.isAfter(e.options.maxDate)||n.isBefore(e.options.minDate))?!0:e.options.disabledDates===!1?!1:e.options.disabledDates[r(n).format("YYYY-MM-DD")]===!0},ot=function(n){return(r.lang(e.options.language),e.options.enabledDates===!1)?!0:e.options.enabledDates[r(n).format("YYYY-MM-DD")]===!0},d=function(n){var t={},u=0;for(i=0;i<n.length;i++)dDate=r(n[i]),dDate.isValid()&&(t[dDate.format("YYYY-MM-DD")]=!0,u++);return u>0?t:!1},s=function(n){return n=n.toString(),n.length>=2?n:"0"+n},ni=function(){if(e.options.pickDate&&e.options.pickTime){var n="";return n='<div class="bootstrap-datetimepicker-widget'+(e.options.sideBySide?" timepicker-sbs":"")+' dropdown-menu" style="z-index:9999 !important;">',n+=e.options.sideBySide?'<div class="row"><div class="col-sm-6 datepicker">'+o.template+'<\/div><div class="col-sm-6 timepicker">'+h.getTemplate()+"<\/div><\/div>":'<ul class="list-unstyled"><li'+(e.options.collapse?' class="collapse in"':"")+'><div class="datepicker">'+o.template+'<\/div><\/li><li class="picker-switch accordion-toggle"><a class="btn" style="width:100%"><span class="'+e.options.icons.time+'"><\/span><\/a><\/li><li'+(e.options.collapse?' class="collapse"':"")+'><div class="timepicker">'+h.getTemplate()+"<\/div><\/li><\/ul>",n+"<\/div>"}return e.options.pickTime?'<div class="bootstrap-datetimepicker-widget dropdown-menu"><div class="timepicker">'+h.getTemplate()+"<\/div><\/div>":'<div class="bootstrap-datetimepicker-widget dropdown-menu"><div class="datepicker">'+o.template+"<\/div><\/div>"},o={modes:[{clsName:"days",navFnc:"month",navStep:1},{clsName:"months",navFnc:"year",navStep:1},{clsName:"years",navFnc:"year",navStep:10}],headTemplate:'<thead><tr><th class="prev">&lsaquo;<\/th><th colspan="5" class="switch"><\/th><th class="next">&rsaquo;<\/th><\/tr><\/thead>',contTemplate:'<tbody><tr><td colspan="7"><\/td><\/tr><\/tbody>'},h={hourTemplate:'<span data-action="showHours" data-time-component="hours" class="timepicker-hour"><\/span>',minuteTemplate:'<span data-action="showMinutes" data-time-component="minutes" class="timepicker-minute"><\/span>',secondTemplate:'<span data-action="showSeconds" data-time-component="seconds" class="timepicker-second"><\/span>'};o.template='<div class="datepicker-days"><table class="table-condensed">'+o.headTemplate+'<tbody><\/tbody><\/table><\/div><div class="datepicker-months"><table class="table-condensed">'+o.headTemplate+o.contTemplate+'<\/table><\/div><div class="datepicker-years"><table class="table-condensed">'+o.headTemplate+o.contTemplate+"<\/table><\/div>";h.getTemplate=function(){return'<div class="timepicker-picker"><table class="table-condensed"><tr><td><a href="#" class="btn" data-action="incrementHours"><span class="'+e.options.icons.up+'"><\/span><\/a><\/td><td class="separator"><\/td><td>'+(e.options.useMinutes?'<a href="#" class="btn" data-action="incrementMinutes"><span class="'+e.options.icons.up+'"><\/span><\/a>':"")+"<\/td>"+(e.options.useSeconds?'<td class="separator"><\/td><td><a href="#" class="btn" data-action="incrementSeconds"><span class="'+e.options.icons.up+'"><\/span><\/a><\/td>':"")+(e.use24hours?"":'<td class="separator"><\/td>')+"<\/tr><tr><td>"+h.hourTemplate+'<\/td> <td class="separator">:<\/td><td>'+(e.options.useMinutes?h.minuteTemplate:'<span class="timepicker-minute">00<\/span>')+"<\/td> "+(e.options.useSeconds?'<td class="separator">:<\/td><td>'+h.secondTemplate+"<\/td>":"")+(e.use24hours?"":'<td class="separator"><\/td><td><button type="button" class="btn btn-primary" data-action="togglePeriod"><\/button><\/td>')+'<\/tr><tr><td><a href="#" class="btn" data-action="decrementHours"><span class="'+e.options.icons.down+'"><\/span><\/a><\/td><td class="separator"><\/td><td>'+(e.options.useMinutes?'<a href="#" class="btn" data-action="decrementMinutes"><span class="'+e.options.icons.down+'"><\/span><\/a>':"")+"<\/td>"+(e.options.useSeconds?'<td class="separator"><\/td><td><a href="#" class="btn" data-action="decrementSeconds"><span class="'+e.options.icons.down+'"><\/span><\/a><\/td>':"")+(e.use24hours?"":'<td class="separator"><\/td>')+'<\/tr><\/table><\/div><div class="timepicker-hours" data-action="selectHour"><table class="table-condensed"><\/table><\/div><div class="timepicker-minutes" data-action="selectMinute"><table class="table-condensed"><\/table><\/div>'+(e.options.useSeconds?'<div class="timepicker-seconds" data-action="selectSecond"><table class="table-condensed"><\/table><\/div>':"")};e.destroy=function(){ft();et();e.widget.remove();e.element.removeData("DateTimePicker");e.component&&e.component.removeData("DateTimePicker")};e.show=function(n){e.options.useCurrent===!0&&p().val()==""&&e.setValue(r().format(e.format));e.widget.show();e.height=e.component?e.component.outerHeight():e.element.outerHeight();it();e.element.trigger({type:"dp.show",date:r(e.date)});dt();n&&tt(n)};e.disable=function(){var n=e.element.find("input");n.prop("disabled")||(n.prop("disabled",!0),ft())};e.enable=function(){var n=e.element.find("input");n.prop("disabled")&&(n.prop("disabled",!1),ut())};e.hide=function(t){if(!t||!n(t.target).is(e.element.attr("id"))){for(var f=e.widget.find(".collapse"),u,i=0;i<f.length;i++)if(u=f.eq(i).data("collapse"),u&&u.date-transitioning)return;e.widget.hide();e.viewMode=e.startViewMode;b();e.element.trigger({type:"dp.hide",date:r(e.date)});et()}};e.setValue=function(n){r.lang(e.options.language);n?e.unset=!1:(e.unset=!0,a());r.isMoment(n)||(n=r(n));n.isValid()?(e.date=n,a(),e.viewDate=r({y:e.date.year(),M:e.date.month()}),y(),nt()):g(n)};e.getDate=function(){return e.unset?null:e.date};e.setDate=function(n){var t=r(e.date);n?e.setValue(n):e.setValue(null);c(t,"function")};e.setDisabledDates=function(n){e.options.disabledDates=d(n);e.viewDate&&l()};e.setEnabledDates=function(n){e.options.enabledDates=d(n);e.viewDate&&l()};e.setMaxDate=function(n){n!=undefined&&(e.options.maxDate=r(n),e.viewDate&&l())};e.setMinDate=function(n){n!=undefined&&(e.options.minDate=r(n),e.viewDate&&l())};ct()};n.fn.datetimepicker=function(t){return this.each(function(){var i=n(this),r=i.data("DateTimePicker");r||i.data("DateTimePicker",new f(this,t))})}});
\ No newline at end of file
//! moment.js
//! version : 2.7.0
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
(function(a){function b(a,b,c){switch(arguments.length){case 2:return null!=a?a:b;case 3:return null!=a?a:null!=b?b:c;default:throw new Error("Implement me")}}function c(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(a,b){function c(){mb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}var d=!0;return j(function(){return d&&(c(),d=!1),b.apply(this,arguments)},b)}function e(a,b){return function(c){return m(a.call(this,c),b)}}function f(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function g(){}function h(a){z(a),j(this,a)}function i(a){var b=s(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._bubble()}function j(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function k(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&Ab.hasOwnProperty(b)&&(c[b]=a[b]);return c}function l(a){return 0>a?Math.ceil(a):Math.floor(a)}function m(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.length<b;)d="0"+d;return(e?c?"+":"":"-")+d}function n(a,b,c,d){var e=b._milliseconds,f=b._days,g=b._months;d=null==d?!0:d,e&&a._d.setTime(+a._d+e*c),f&&hb(a,"Date",gb(a,"Date")+f*c),g&&fb(a,gb(a,"Month")+g*c),d&&mb.updateOffset(a,f||g)}function o(a){return"[object Array]"===Object.prototype.toString.call(a)}function p(a){return"[object Date]"===Object.prototype.toString.call(a)||a instanceof Date}function q(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function r(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=bc[a]||cc[b]||b}return a}function s(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=r(c),b&&(d[b]=a[c]));return d}function t(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}mb[b]=function(e,f){var g,h,i=mb.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=mb().utc().set(d,a);return i.call(mb.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function v(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function w(a,b,c){return bb(mb([a,11,31+b-c]),b,c).week}function x(a){return y(a)?366:365}function y(a){return a%4===0&&a%100!==0||a%400===0}function z(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[tb]<0||a._a[tb]>11?tb:a._a[ub]<1||a._a[ub]>v(a._a[sb],a._a[tb])?ub:a._a[vb]<0||a._a[vb]>23?vb:a._a[wb]<0||a._a[wb]>59?wb:a._a[xb]<0||a._a[xb]>59?xb:a._a[yb]<0||a._a[yb]>999?yb:-1,a._pf._overflowDayOfYear&&(sb>b||b>ub)&&(b=ub),a._pf.overflow=b)}function A(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function B(a){return a?a.toLowerCase().replace("_","-"):a}function C(a,b){return b._isUTC?mb(a).zone(b._offset||0):mb(a).local()}function D(a,b){return b.abbr=a,zb[a]||(zb[a]=new g),zb[a].set(b),zb[a]}function E(a){delete zb[a]}function F(a){var b,c,d,e,f=0,g=function(a){if(!zb[a]&&Bb)try{require("./lang/"+a)}catch(b){}return zb[a]};if(!a)return mb.fn._lang;if(!o(a)){if(c=g(a))return c;a=[a]}for(;f<a.length;){for(e=B(a[f]).split("-"),b=e.length,d=B(a[f+1]),d=d?d.split("-"):null;b>0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&q(e,d,!0)>=b-1)break;b--}f++}return mb.fn._lang}function G(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function H(a){var b,c,d=a.match(Fb);for(b=0,c=d.length;c>b;b++)d[b]=hc[d[b]]?hc[d[b]]:G(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function I(a,b){return a.isValid()?(b=J(b,a.lang()),dc[b]||(dc[b]=H(b)),dc[b](a)):a.lang().invalidDate()}function J(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Gb.lastIndex=0;d>=0&&Gb.test(a);)a=a.replace(Gb,c),Gb.lastIndex=0,d-=1;return a}function K(a,b){var c,d=b._strict;switch(a){case"Q":return Rb;case"DDDD":return Tb;case"YYYY":case"GGGG":case"gggg":return d?Ub:Jb;case"Y":case"G":case"g":return Wb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Vb:Kb;case"S":if(d)return Rb;case"SS":if(d)return Sb;case"SSS":if(d)return Tb;case"DDD":return Ib;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Mb;case"a":case"A":return F(b._l)._meridiemParse;case"X":return Pb;case"Z":case"ZZ":return Nb;case"T":return Ob;case"SSSS":return Lb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Sb:Hb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Hb;case"Do":return Qb;default:return c=new RegExp(T(S(a.replace("\\","")),"i"))}}function L(a){a=a||"";var b=a.match(Nb)||[],c=b[b.length-1]||[],d=(c+"").match(_b)||["-",0,0],e=+(60*d[1])+u(d[2]);return"+"===d[0]?-e:e}function M(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[tb]=3*(u(b)-1));break;case"M":case"MM":null!=b&&(e[tb]=u(b)-1);break;case"MMM":case"MMMM":d=F(c._l).monthsParse(b),null!=d?e[tb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[ub]=u(b));break;case"Do":null!=b&&(e[ub]=u(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=u(b));break;case"YY":e[sb]=mb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[sb]=u(b);break;case"a":case"A":c._isPm=F(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[vb]=u(b);break;case"m":case"mm":e[wb]=u(b);break;case"s":case"ss":e[xb]=u(b);break;case"S":case"SS":case"SSS":case"SSSS":e[yb]=u(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=L(b);break;case"dd":case"ddd":case"dddd":d=F(c._l).weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=u(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=mb.parseTwoDigitYear(b)}}function N(a){var c,d,e,f,g,h,i,j;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[sb],bb(mb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(j=F(a._l),g=j._week.dow,h=j._week.doy,d=b(c.gg,a._a[sb],bb(mb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=cb(d,e,f,h,g),a._a[sb]=i.year,a._dayOfYear=i.dayOfYear}function O(a){var c,d,e,f,g=[];if(!a._d){for(e=Q(a),a._w&&null==a._a[ub]&&null==a._a[tb]&&N(a),a._dayOfYear&&(f=b(a._a[sb],e[sb]),a._dayOfYear>x(f)&&(a._pf._overflowDayOfYear=!0),d=Z(f,0,a._dayOfYear),a._a[tb]=d.getUTCMonth(),a._a[ub]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];a._d=(a._useUTC?Z:Y).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function P(a){var b;a._d||(b=s(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],O(a))}function Q(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function R(a){if(a._f===mb.ISO_8601)return void V(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=F(a._l),h=""+a._i,i=h.length,j=0;for(d=J(a._f,g).match(Fb)||[],b=0;b<d.length;b++)e=d[b],c=(h.match(K(e,a))||[])[0],c&&(f=h.substr(0,h.indexOf(c)),f.length>0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),hc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),M(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[vb]<12&&(a._a[vb]+=12),a._isPm===!1&&12===a._a[vb]&&(a._a[vb]=0),O(a),z(a)}function S(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function T(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function U(a){var b,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;f<a._f.length;f++)g=0,b=j({},a),b._pf=c(),b._f=a._f[f],R(b),A(b)&&(g+=b._pf.charsLeftOver,g+=10*b._pf.unusedTokens.length,b._pf.score=g,(null==e||e>g)&&(e=g,d=b));j(a,d||b)}function V(a){var b,c,d=a._i,e=Xb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Zb.length;c>b;b++)if(Zb[b][1].exec(d)){a._f=Zb[b][0]+(e[6]||" ");break}for(b=0,c=$b.length;c>b;b++)if($b[b][1].exec(d)){a._f+=$b[b][0];break}d.match(Nb)&&(a._f+="Z"),R(a)}else a._isValid=!1}function W(a){V(a),a._isValid===!1&&(delete a._isValid,mb.createFromInputFallback(a))}function X(b){var c=b._i,d=Cb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?W(b):o(c)?(b._a=c.slice(0),O(b)):p(c)?b._d=new Date(+c):"object"==typeof c?P(b):"number"==typeof c?b._d=new Date(c):mb.createFromInputFallback(b)}function Y(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function Z(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function $(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function _(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function ab(a,b,c){var d=rb(Math.abs(a)/1e3),e=rb(d/60),f=rb(e/60),g=rb(f/24),h=rb(g/365),i=d<ec.s&&["s",d]||1===e&&["m"]||e<ec.m&&["mm",e]||1===f&&["h"]||f<ec.h&&["hh",f]||1===g&&["d"]||g<=ec.dd&&["dd",g]||g<=ec.dm&&["M"]||g<ec.dy&&["MM",rb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,_.apply({},i)}function bb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=mb(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function cb(a,b,c,d,e){var f,g,h=Z(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:x(a-1)+g}}function db(b){var c=b._i,d=b._f;return null===c||d===a&&""===c?mb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=F().preparse(c)),mb.isMoment(c)?(b=k(c),b._d=new Date(+c._d)):d?o(d)?U(b):R(b):X(b),new h(b))}function eb(a,b){var c,d;if(1===b.length&&o(b[0])&&(b=b[0]),!b.length)return mb();for(c=b[0],d=1;d<b.length;++d)b[d][a](c)&&(c=b[d]);return c}function fb(a,b){var c;return"string"==typeof b&&(b=a.lang().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),v(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function gb(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function hb(a,b,c){return"Month"===b?fb(a,c):a._d["set"+(a._isUTC?"UTC":"")+b](c)}function ib(a,b){return function(c){return null!=c?(hb(this,a,c),mb.updateOffset(this,b),this):gb(this,a)}}function jb(a){mb.duration.fn[a]=function(){return this._data[a]}}function kb(a,b){mb.duration.fn["as"+a]=function(){return+this/b}}function lb(a){"undefined"==typeof ender&&(nb=qb.moment,qb.moment=a?d("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.",mb):mb)}for(var mb,nb,ob,pb="2.7.0",qb="undefined"!=typeof global?global:this,rb=Math.round,sb=0,tb=1,ub=2,vb=3,wb=4,xb=5,yb=6,zb={},Ab={_isAMomentObject:null,_i:null,_f:null,_l:null,_strict:null,_tzm:null,_isUTC:null,_offset:null,_pf:null,_lang:null},Bb="undefined"!=typeof module&&module.exports,Cb=/^\/?Date\((\-?\d+)/i,Db=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,Eb=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,Fb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,Gb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,Hb=/\d\d?/,Ib=/\d{1,3}/,Jb=/\d{1,4}/,Kb=/[+\-]?\d{1,6}/,Lb=/\d+/,Mb=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Nb=/Z|[\+\-]\d\d:?\d\d/gi,Ob=/T/i,Pb=/[\+\-]?\d+(\.\d{1,3})?/,Qb=/\d{1,2}/,Rb=/\d/,Sb=/\d\d/,Tb=/\d{3}/,Ub=/\d{4}/,Vb=/[+-]?\d{6}/,Wb=/[+-]?\d+/,Xb=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Yb="YYYY-MM-DDTHH:mm:ssZ",Zb=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],$b=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],_b=/([\+\-]|\d\d)/gi,ac=("Date|Hours|Minutes|Seconds|Milliseconds".split("|"),{Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6}),bc={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",Q:"quarter",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},cc={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},dc={},ec={s:45,m:45,h:22,dd:25,dm:45,dy:345},fc="DDD w W M D d".split(" "),gc="M D H h m s w W".split(" "),hc={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return m(this.year()%100,2)},YYYY:function(){return m(this.year(),4)},YYYYY:function(){return m(this.year(),5)},YYYYYY:function(){var a=this.year(),b=a>=0?"+":"-";return b+m(Math.abs(a),6)},gg:function(){return m(this.weekYear()%100,2)},gggg:function(){return m(this.weekYear(),4)},ggggg:function(){return m(this.weekYear(),5)},GG:function(){return m(this.isoWeekYear()%100,2)},GGGG:function(){return m(this.isoWeekYear(),4)},GGGGG:function(){return m(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return u(this.milliseconds()/100)},SS:function(){return m(u(this.milliseconds()/10),2)},SSS:function(){return m(this.milliseconds(),3)},SSSS:function(){return m(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+":"+m(u(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+m(u(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},ic=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];fc.length;)ob=fc.pop(),hc[ob+"o"]=f(hc[ob],ob);for(;gc.length;)ob=gc.pop(),hc[ob+ob]=e(hc[ob],2);for(hc.DDDD=e(hc.DDD,3),j(g.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=mb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=mb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return bb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),mb=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=c(),db(g)},mb.suppressDeprecationWarnings=!1,mb.createFromInputFallback=d("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),mb.min=function(){var a=[].slice.call(arguments,0);return eb("isBefore",a)},mb.max=function(){var a=[].slice.call(arguments,0);return eb("isAfter",a)},mb.utc=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=d,g._strict=f,g._pf=c(),db(g).utc()},mb.unix=function(a){return mb(1e3*a)},mb.duration=function(a,b){var c,d,e,f=a,g=null;return mb.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=Db.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:u(g[ub])*c,h:u(g[vb])*c,m:u(g[wb])*c,s:u(g[xb])*c,ms:u(g[yb])*c}):(g=Eb.exec(a))&&(c="-"===g[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(g[2]),M:e(g[3]),d:e(g[4]),h:e(g[5]),m:e(g[6]),s:e(g[7]),w:e(g[8])}),d=new i(f),mb.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},mb.version=pb,mb.defaultFormat=Yb,mb.ISO_8601=function(){},mb.momentProperties=Ab,mb.updateOffset=function(){},mb.relativeTimeThreshold=function(b,c){return ec[b]===a?!1:(ec[b]=c,!0)},mb.lang=function(a,b){var c;return a?(b?D(B(a),b):null===b?(E(a),a="en"):zb[a]||F(a),c=mb.duration.fn._lang=mb.fn._lang=F(a),c._abbr):mb.fn._lang._abbr},mb.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),F(a)},mb.isMoment=function(a){return a instanceof h||null!=a&&a.hasOwnProperty("_isAMomentObject")},mb.isDuration=function(a){return a instanceof i},ob=ic.length-1;ob>=0;--ob)t(ic[ob]);mb.normalizeUnits=function(a){return r(a)},mb.invalid=function(a){var b=mb.utc(0/0);return null!=a?j(b._pf,a):b._pf.userInvalidated=!0,b},mb.parseZone=function(){return mb.apply(null,arguments).parseZone()},mb.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)},j(mb.fn=h.prototype,{clone:function(){return mb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=mb(this).utc();return 0<a.year()&&a.year()<=9999?I(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):I(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var a=this;return[a.year(),a.month(),a.date(),a.hours(),a.minutes(),a.seconds(),a.milliseconds()]},isValid:function(){return A(this)},isDSTShifted:function(){return this._a?this.isValid()&&q(this._a,(this._isUTC?mb.utc(this._a):mb(this._a)).toArray())>0:!1},parsingFlags:function(){return j({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=I(this,a||mb.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,-1),this},diff:function(a,b,c){var d,e,f=C(a,this),g=6e4*(this.zone()-f.zone());return b=r(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-mb(this).startOf("month")-(f-mb(f).startOf("month")))/d,e-=6e4*(this.zone()-mb(this).startOf("month").zone()-(f.zone()-mb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:l(e)},from:function(a,b){return mb.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(mb(),a)},calendar:function(a){var b=a||mb(),c=C(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){return y(this.year())},isDST:function(){return this.zone()<this.clone().month(0).zone()||this.zone()<this.clone().month(5).zone()},day:function(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=$(a,this.lang()),this.add({d:a-b})):b},month:ib("Month",!0),startOf:function(a){switch(a=r(a)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===a?this.weekday(0):"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(a){return a=r(a),this.startOf(a).add("isoWeek"===a?"week":a,1).subtract("ms",1)},isAfter:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)>+mb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+mb(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+C(a,this).startOf(b)},min:d("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),this>a?this:a}),max:d("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c=this._offset||0;return null==a?this._isUTC?c:this._d.getTimezoneOffset():("string"==typeof a&&(a=L(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,c!==a&&(!b||this._changeInProgress?n(this,mb.duration(c-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,mb.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?mb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return v(this.year(),this.month())},dayOfYear:function(a){var b=rb((mb(this).startOf("day")-mb(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=bb(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=bb(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=bb(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return w(this.year(),1,4)},weeksInYear:function(){var a=this._lang._week;return w(this.year(),a.dow,a.doy)},get:function(a){return a=r(a),this[a]()},set:function(a,b){return a=r(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=F(b),this)}}),mb.fn.millisecond=mb.fn.milliseconds=ib("Milliseconds",!1),mb.fn.second=mb.fn.seconds=ib("Seconds",!1),mb.fn.minute=mb.fn.minutes=ib("Minutes",!1),mb.fn.hour=mb.fn.hours=ib("Hours",!0),mb.fn.date=ib("Date",!0),mb.fn.dates=d("dates accessor is deprecated. Use date instead.",ib("Date",!0)),mb.fn.year=ib("FullYear",!0),mb.fn.years=d("years accessor is deprecated. Use year instead.",ib("FullYear",!0)),mb.fn.days=mb.fn.day,mb.fn.months=mb.fn.month,mb.fn.weeks=mb.fn.week,mb.fn.isoWeeks=mb.fn.isoWeek,mb.fn.quarters=mb.fn.quarter,mb.fn.toJSON=mb.fn.toISOString,j(mb.duration.fn=i.prototype,{_bubble:function(){var a,b,c,d,e=this._milliseconds,f=this._days,g=this._months,h=this._data;h.milliseconds=e%1e3,a=l(e/1e3),h.seconds=a%60,b=l(a/60),h.minutes=b%60,c=l(b/60),h.hours=c%24,f+=l(c/24),h.days=f%30,g+=l(f/30),h.months=g%12,d=l(g/12),h.years=d},weeks:function(){return l(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)},humanize:function(a){var b=+this,c=ab(b,!a,this.lang());return a&&(c=this.lang().pastFuture(b,c)),this.lang().postformat(c)},add:function(a,b){var c=mb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=mb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=r(a),this[a.toLowerCase()+"s"]()},as:function(a){return a=r(a),this["as"+a.charAt(0).toUpperCase()+a.slice(1)+"s"]()},lang:mb.fn.lang,toIsoString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}});for(ob in ac)ac.hasOwnProperty(ob)&&(kb(ob,ac[ob]),jb(ob.toLowerCase()));kb("Weeks",6048e5),mb.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},mb.lang("en",{ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),Bb?module.exports=mb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(qb.moment=nb),mb}),lb(!0)):lb()}).call(this);
\ No newline at end of file
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
{% block head_css %} {% block head_css %}
<link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap.css') }}" rel="stylesheet"> <link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap-responsive.css') }}" rel="stylesheet"> <link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap-responsive.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='admin/css/bootstrap2/admin.css') }}" rel="stylesheet"> <link href="{{ admin_static.url(filename='admin/css/bootstrap2/admin.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block head %} {% block head %}
...@@ -56,7 +55,8 @@ ...@@ -56,7 +55,8 @@
{% block tail_js %} {% block tail_js %}
<script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='bootstrap/bootstrap2/js/bootstrap.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='bootstrap/bootstrap2/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='select2/select2.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='vendor/moment-2.7.0.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
{% endblock %} {% endblock %}
{% block tail %} {% block tail %}
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="{{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %} {% macro menu() %}
{% for item in admin_view.admin.menu() %} {% for item in admin_view.admin.menu() %}
{% if item.is_category() %} {% if item.is_category() %}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{% if children %} {% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %} {% set class_name = item.get_class_name() %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a> {% if item.is_active(admin_view) %}
<li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b>
</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% for child in children %} {% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = child.get_class_name() %}
<a href="{{ child.get_url() }}">{{ child.name }}</a> {% if child.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">{{ menu_icon(item) }}{{ child.name }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
...@@ -16,8 +42,13 @@ ...@@ -16,8 +42,13 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = item.get_class_name() %}
<a href="{{ item.get_url() }}">{{ item.name }}</a> {% if item.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
...@@ -28,7 +59,7 @@ ...@@ -28,7 +59,7 @@
{% for item in admin_view.admin.menu_links() %} {% for item in admin_view.admin.menu_links() %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
<li> <li>
<a href="{{ item.get_url() }}">{{ item.name }}</a> <a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
......
{% import 'admin/static.html' as admin_static with context %}
{# ---------------------- Pager -------------------------- #} {# ---------------------- Pager -------------------------- #}
{% macro pager(page, pages, generator) -%} {% macro pager(page, pages, generator) -%}
{% if pages > 1 %} {% if pages > 1 %}
...@@ -75,7 +77,7 @@ ...@@ -75,7 +77,7 @@
{%- endmacro %} {%- endmacro %}
{# ---------------------- Forms -------------------------- #} {# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}) %} {% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %} {% set direct_error = h.is_field_error(field.errors) %}
<div class="control-group{{ ' error' if direct_error else '' }}"> <div class="control-group{{ ' error' if direct_error else '' }}">
<div class="control-label"> <div class="control-label">
...@@ -102,6 +104,9 @@ ...@@ -102,6 +104,9 @@
</ul> </ul>
{% endif %} {% endif %}
</div> </div>
{% if caller %}
{{ caller(form, field, direct_error, kwargs) }}
{% endif %}
</div> </div>
{% endmacro %} {% endmacro %}
...@@ -165,3 +170,13 @@ ...@@ -165,3 +170,13 @@
{{ render_form_buttons(cancel_url, extra) }} {{ render_form_buttons(cancel_url, extra) }}
{% endcall %} {% endcall %}
{% endmacro %} {% endmacro %}
{% macro form_css() %}
<link href="{{ admin_static.url(filename='vendor/select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/datetimepicker-bs2/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endmacro %}
{% macro form_js() %}
<script src="{{ admin_static.url(filename='vendor/datetimepicker-bs2/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endmacro %}
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %} {% macro extra() %}
<input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" /> <input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" />
...@@ -8,8 +7,7 @@ ...@@ -8,8 +7,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -30,6 +28,5 @@ ...@@ -30,6 +28,5 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script> {{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endblock %} {% endblock %}
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %} {% macro extra() %}
<input name="_continue_editing" type="submit" class="btn btn-large" value="{{ _gettext('Save and Continue') }}" /> <input name="_continue_editing" type="submit" class="btn btn-large" value="{{ _gettext('Save and Continue') }}" />
...@@ -8,8 +7,7 @@ ...@@ -8,8 +7,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -21,6 +19,5 @@ ...@@ -21,6 +19,5 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script> {{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endblock %} {% endblock %}
...@@ -6,8 +6,7 @@ ...@@ -6,8 +6,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -103,7 +102,7 @@ ...@@ -103,7 +102,7 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{%- if admin_view.can_edit -%} {%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="Edit record"> <a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</a> </a>
{%- endif -%} {%- endif -%}
...@@ -112,7 +111,7 @@ ...@@ -112,7 +111,7 @@
{% if csrf_token %} {% if csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %} {% endif %}
<button onclick="return confirm('{{ _gettext('You sure you want to delete this item?') }}');" title="Delete record"> <button onclick="return confirm('{{ _gettext('You sure you want to delete this item?') }}');" title="{{ _gettext('Delete record') }}">
<i class="icon-trash"></i> <i class="icon-trash"></i>
</button> </button>
</form> </form>
...@@ -145,8 +144,7 @@ ...@@ -145,8 +144,7 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script> {{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script> <script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
{{ actionlib.script(_gettext('Please select at least one model.'), {{ actionlib.script(_gettext('Please select at least one model.'),
......
...@@ -66,7 +66,8 @@ ...@@ -66,7 +66,8 @@
{% block tail_js %} {% block tail_js %}
<script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='bootstrap/bootstrap3/js/bootstrap.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='bootstrap/bootstrap3/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='select2/select2.min.js') }}" type="text/javascript"></script> <script src="{{ admin_static.url(filename='vendor/moment-2.7.0.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
{% endblock %} {% endblock %}
{% block tail %} {% block tail %}
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="glyphicon {{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %} {% macro menu() %}
{% for item in admin_view.admin.menu() %} {% for item in admin_view.admin.menu() %}
{% if item.is_category() %} {% if item.is_category() %}
{% set children = item.get_children() %} {% set children = item.get_children() %}
{% if children %} {% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %} {% if item.is_active(admin_view) %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a> <li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% for child in children %} {% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = child.get_class_name() %}
<a href="{{ child.get_url() }}">{{ child.name }}</a> {% if child.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">{{ menu_icon(item) }}{{ child.name }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
...@@ -16,8 +39,13 @@ ...@@ -16,8 +39,13 @@
{% endif %} {% endif %}
{% else %} {% else %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %} {% set class_name = item.get_class_name() %}
<a href="{{ item.get_url() }}">{{ item.name }}</a> {% if item.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
...@@ -28,7 +56,7 @@ ...@@ -28,7 +56,7 @@
{% for item in admin_view.admin.menu_links() %} {% for item in admin_view.admin.menu_links() %}
{% if item.is_accessible() and item.is_visible() %} {% if item.is_accessible() and item.is_visible() %}
<li> <li>
<a href="{{ item.get_url() }}">{{ item.name }}</a> <a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li> </li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
......
{% import 'admin/static.html' as admin_static with context %}
{# ---------------------- Pager -------------------------- #} {# ---------------------- Pager -------------------------- #}
{% macro pager(page, pages, generator) -%} {% macro pager(page, pages, generator) -%}
{% if pages > 1 %} {% if pages > 1 %}
...@@ -73,7 +75,7 @@ ...@@ -73,7 +75,7 @@
{%- endmacro %} {%- endmacro %}
{# ---------------------- Forms -------------------------- #} {# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}) %} {% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %} {% set direct_error = h.is_field_error(field.errors) %}
<div class="form-group{{ ' error' if direct_error else '' }}"> <div class="form-group{{ ' error' if direct_error else '' }}">
<label for="{{ field.id }}" class="col-md-2 control-label">{{ field.label.text }} <label for="{{ field.id }}" class="col-md-2 control-label">{{ field.label.text }}
...@@ -83,7 +85,7 @@ ...@@ -83,7 +85,7 @@
&nbsp; &nbsp;
{%- endif %} {%- endif %}
</label> </label>
<div class="{{ kwargs.get('column_class', 'col-md-4') }}"> <div class="{{ kwargs.get('column_class', 'col-md-10') }}">
{% set _dummy = kwargs.setdefault('class', 'form-control') %} {% set _dummy = kwargs.setdefault('class', 'form-control') %}
{{ field(**kwargs)|safe }} {{ field(**kwargs)|safe }}
</div> </div>
...@@ -97,6 +99,9 @@ ...@@ -97,6 +99,9 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% if caller %}
{{ caller(form, field, direct_error, kwargs) }}
{% endif %}
</div> </div>
{% endmacro %} {% endmacro %}
...@@ -158,3 +163,13 @@ ...@@ -158,3 +163,13 @@
{{ render_form_buttons(cancel_url, extra) }} {{ render_form_buttons(cancel_url, extra) }}
{% endcall %} {% endcall %}
{% endmacro %} {% endmacro %}
{% macro form_css() %}
<link href="{{ admin_static.url(filename='vendor/select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/datetimepicker-bs3/bootstrap-datetimepicker.min.css') }}" rel="stylesheet">
{% endmacro %}
{% macro form_js() %}
<script src="{{ admin_static.url(filename='vendor/datetimepicker-bs3/bootstrap-datetimepicker.min.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endmacro %}
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %} {% macro extra() %}
<input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" /> <input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" />
...@@ -8,8 +7,7 @@ ...@@ -8,8 +7,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -32,6 +30,5 @@ ...@@ -32,6 +30,5 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script> {{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endblock %} {% endblock %}
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %} {% macro extra() %}
<input name="_continue_editing" type="submit" class="btn" value="{{ _gettext('Save and Continue') }}" /> <input name="_continue_editing" type="submit" class="btn" value="{{ _gettext('Save and Continue') }}" />
...@@ -8,8 +7,7 @@ ...@@ -8,8 +7,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -21,6 +19,5 @@ ...@@ -21,6 +19,5 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script> {{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endblock %} {% endblock %}
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
{%- endfor %} {%- endfor %}
</select> </select>
{%- else -%} {%- else -%}
<input name="flt{{n}}_{{ filter_arg }}" type="text" value="{{ value or '' }}" class="filter-val form-control"{% if filter.data_type %} data-role="{{ filter.data_type }}"{% endif %}></input> <input name="flt{{n}}_{{ filter_arg }}" type="text" value="{{ value or '' }}" class="filter-val form-control"{% if filter.data_type %} data-role="{{ filter.data_type }}"{% endif %} {% if filter.data_type == "datepicker" %}data-date-format="YYYY-MM-DD"{% endif %}></input>
{%- endif -%} {%- endif -%}
</td> </td>
</tr> </tr>
......
...@@ -6,8 +6,7 @@ ...@@ -6,8 +6,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet"> {{ lib.form_css() }}
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
...@@ -103,7 +102,7 @@ ...@@ -103,7 +102,7 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{%- if admin_view.can_edit -%} {%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="Edit record"> <a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<span class="glyphicon glyphicon-pencil"></span> <span class="glyphicon glyphicon-pencil"></span>
</a> </a>
{%- endif -%} {%- endif -%}
...@@ -145,9 +144,8 @@ ...@@ -145,9 +144,8 @@
{% block tail %} {% block tail %}
{{ super() }} {{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script> <script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
{{ lib.form_js() }}
{{ actionlib.script(_gettext('Please select at least one model.'), {{ actionlib.script(_gettext('Please select at least one model.'),
actions, actions,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment