diff --git a/apps/acls/api/login_acl.py b/apps/acls/api/login_acl.py
index c42e61a2c..85e902143 100644
--- a/apps/acls/api/login_acl.py
+++ b/apps/acls/api/login_acl.py
@@ -1,20 +1,14 @@
-from common.permissions import IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember
 from common.drf.api import JMSBulkModelViewSet
 from ..models import LoginACL
 from .. import serializers
 from ..filters import LoginAclFilter
 
-__all__ = ['LoginACLViewSet', ]
+__all__ = ['LoginACLViewSet']
 
 
 class LoginACLViewSet(JMSBulkModelViewSet):
     queryset = LoginACL.objects.all()
     filterset_class = LoginAclFilter
     search_fields = ('name',)
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.LoginACLSerializer
 
-    def get_permissions(self):
-        if self.action in ["retrieve", "list"]:
-            self.permission_classes = (IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember)
-        return super().get_permissions()
diff --git a/apps/acls/api/login_asset_acl.py b/apps/acls/api/login_asset_acl.py
index fa03851b9..1447c16d5 100644
--- a/apps/acls/api/login_asset_acl.py
+++ b/apps/acls/api/login_asset_acl.py
@@ -1,5 +1,4 @@
 from orgs.mixins.api import OrgBulkModelViewSet
-from common.permissions import IsOrgAdmin
 from .. import models, serializers
 
 
@@ -10,5 +9,4 @@ class LoginAssetACLViewSet(OrgBulkModelViewSet):
     model = models.LoginAssetACL
     filterset_fields = ('name', )
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdmin, )
     serializer_class = serializers.LoginAssetACLSerializer
diff --git a/apps/acls/api/login_asset_check.py b/apps/acls/api/login_asset_check.py
index f45f4f2ff..fc5f4157f 100644
--- a/apps/acls/api/login_asset_check.py
+++ b/apps/acls/api/login_asset_check.py
@@ -1,19 +1,23 @@
 from rest_framework.response import Response
 from rest_framework.generics import CreateAPIView
 
-from common.permissions import IsAppUser
 from common.utils import reverse, lazyproperty
 from orgs.utils import tmp_to_org
-from tickets.api import GenericTicketStatusRetrieveCloseAPI
 from ..models import LoginAssetACL
 from .. import serializers
 
-__all__ = ['LoginAssetCheckAPI', 'LoginAssetConfirmStatusAPI']
+__all__ = ['LoginAssetCheckAPI']
 
 
 class LoginAssetCheckAPI(CreateAPIView):
-    permission_classes = (IsAppUser,)
     serializer_class = serializers.LoginAssetCheckSerializer
+    model = LoginAssetACL
+    rbac_perms = {
+        'POST': 'tickets.add_superticket'
+    }
+
+    def get_queryset(self):
+        return LoginAssetACL.objects.all()
 
     def create(self, request, *args, **kwargs):
         is_need_confirm, response_data = self.check_if_need_confirm()
@@ -46,7 +50,7 @@ class LoginAssetCheckAPI(CreateAPIView):
             org_id=self.serializer.org.id
         )
         confirm_status_url = reverse(
-            view_name='api-acls:login-asset-confirm-status',
+            view_name='api-tickets:super-ticket-status',
             kwargs={'pk': str(ticket.id)}
         )
         ticket_detail_url = reverse(
@@ -71,6 +75,3 @@ class LoginAssetCheckAPI(CreateAPIView):
         serializer.is_valid(raise_exception=True)
         return serializer
 
-
-class LoginAssetConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
-    pass
diff --git a/apps/acls/apps.py b/apps/acls/apps.py
index 2456a1b4f..0a93fe495 100644
--- a/apps/acls/apps.py
+++ b/apps/acls/apps.py
@@ -1,5 +1,7 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class AclsConfig(AppConfig):
     name = 'acls'
+    verbose_name = _('Acls')
diff --git a/apps/acls/migrations/0003_auto_20211130_1037.py b/apps/acls/migrations/0003_auto_20211130_1037.py
new file mode 100644
index 000000000..d4d3524be
--- /dev/null
+++ b/apps/acls/migrations/0003_auto_20211130_1037.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.1.13 on 2021-11-30 02:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('acls', '0002_auto_20210926_1047'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='loginacl',
+            options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login acl'},
+        ),
+        migrations.AlterModelOptions(
+            name='loginassetacl',
+            options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login asset acl'},
+        ),
+    ]
diff --git a/apps/acls/urls/api_urls.py b/apps/acls/urls/api_urls.py
index 24fbc11b0..c4040ff45 100644
--- a/apps/acls/urls/api_urls.py
+++ b/apps/acls/urls/api_urls.py
@@ -12,7 +12,6 @@ router.register(r'login-asset-acls', api.LoginAssetACLViewSet, 'login-asset-acl'
 
 urlpatterns = [
     path('login-asset/check/', api.LoginAssetCheckAPI.as_view(), name='login-asset-check'),
-    path('login-asset-confirm/<uuid:pk>/status/', api.LoginAssetConfirmStatusAPI.as_view(), name='login-asset-confirm-status')
 ]
 
 urlpatterns += router.urls
diff --git a/apps/applications/api/account.py b/apps/applications/api/account.py
index 4fc06807e..1c9449fb3 100644
--- a/apps/applications/api/account.py
+++ b/apps/applications/api/account.py
@@ -6,8 +6,9 @@ from django.db.models import F, Q
 
 from common.drf.filters import BaseFilterSet
 from common.drf.api import JMSBulkModelViewSet
+from rbac.permissions import RBACPermission
 from ..models import Account
-from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
+from ..hands import NeedMFAVerify
 from .. import serializers
 
 
@@ -31,7 +32,8 @@ class AccountFilterSet(BaseFilterSet):
         username = self.get_query_param('username')
         if not username:
             return qs
-        qs = qs.filter(Q(username=username) | Q(systemuser__username=username)).distinct()
+        q = Q(username=username) | Q(systemuser__username=username)
+        qs = qs.filter(q).distinct()
         return qs
 
 
@@ -41,7 +43,6 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
     filterset_class = AccountFilterSet
     filterset_fields = ['username', 'app_display', 'type', 'category', 'app']
     serializer_class = serializers.AppAccountSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def get_queryset(self):
         queryset = Account.get_queryset()
@@ -50,5 +51,9 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
 
 class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
     serializer_class = serializers.AppAccountSecretSerializer
-    permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
+    permission_classes = [RBACPermission, NeedMFAVerify]
     http_method_names = ['get', 'options']
+    rbac_perms = {
+        'retrieve': 'applications.view_applicationaccountsecret',
+        'list': 'applications.view_applicationaccountsecret',
+    }
diff --git a/apps/applications/api/application.py b/apps/applications/api/application.py
index ef00fe8c1..6d98123bc 100644
--- a/apps/applications/api/application.py
+++ b/apps/applications/api/application.py
@@ -7,7 +7,6 @@ from rest_framework.response import Response
 
 from common.tree import TreeNodeSerializer
 from common.mixins.api import SuggestionMixin
-from ..hands import IsOrgAdminOrAppUser
 from .. import serializers
 from ..models import Application
 
@@ -22,12 +21,15 @@ class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
         'type': ['exact', 'in'],
     }
     search_fields = ('name', 'type', 'category')
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_classes = {
         'default': serializers.AppSerializer,
         'get_tree': TreeNodeSerializer,
         'suggestion': serializers.MiniAppSerializer
     }
+    rbac_perms = {
+        'get_tree': 'applications.view_application',
+        'match': 'assets.match_application'
+    }
 
     @action(methods=['GET'], detail=False, url_path='tree')
     def get_tree(self, request, *args, **kwargs):
diff --git a/apps/applications/api/remote_app.py b/apps/applications/api/remote_app.py
index c68679bab..4aea7d93b 100644
--- a/apps/applications/api/remote_app.py
+++ b/apps/applications/api/remote_app.py
@@ -2,10 +2,8 @@
 #
 
 from orgs.mixins import generics
-from ..hands import IsAppUser
 from .. import models
 from ..serializers import RemoteAppConnectionInfoSerializer
-from ..permissions import IsRemoteApp
 
 
 __all__ = [
@@ -15,5 +13,4 @@ __all__ = [
 
 class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
     model = models.Application
-    permission_classes = (IsAppUser, IsRemoteApp)
     serializer_class = RemoteAppConnectionInfoSerializer
diff --git a/apps/applications/apps.py b/apps/applications/apps.py
index 3c22ddedc..1662edcf0 100644
--- a/apps/applications/apps.py
+++ b/apps/applications/apps.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
 
+from django.utils.translation import ugettext_lazy as _
 from django.apps import AppConfig
 
 
 class ApplicationsConfig(AppConfig):
     name = 'applications'
+    verbose_name = _('Applications')
diff --git a/apps/applications/const.py b/apps/applications/const.py
index 79a76b593..76fc51e33 100644
--- a/apps/applications/const.py
+++ b/apps/applications/const.py
@@ -1,10 +1,10 @@
 #  coding: utf-8
 #
-from django.db.models import TextChoices
+from django.db import models
 from django.utils.translation import ugettext_lazy as _
 
 
-class AppCategory(TextChoices):
+class AppCategory(models.TextChoices):
     db = 'db', _('Database')
     remote_app = 'remote_app', _('Remote app')
     cloud = 'cloud', 'Cloud'
@@ -14,14 +14,15 @@ class AppCategory(TextChoices):
         return dict(cls.choices).get(category, '')
 
 
-class AppType(TextChoices):
+class AppType(models.TextChoices):
     # db category
     mysql = 'mysql', 'MySQL'
-    redis = 'redis', 'Redis'
     oracle = 'oracle', 'Oracle'
     pgsql = 'postgresql', 'PostgreSQL'
     mariadb = 'mariadb', 'MariaDB'
     sqlserver = 'sqlserver', 'SQLServer'
+    redis = 'redis', 'Redis'
+    mongodb = 'mongodb', 'MongoDB'
 
     # remote-app category
     chrome = 'chrome', 'Chrome'
@@ -36,7 +37,7 @@ class AppType(TextChoices):
     def category_types_mapper(cls):
         return {
             AppCategory.db: [
-                cls.mysql, cls.oracle, cls.redis, cls.pgsql, cls.mariadb, cls.sqlserver
+                cls.mysql, cls.oracle, cls.pgsql, cls.mariadb, cls.sqlserver, cls.redis, cls.mongodb
             ],
             AppCategory.remote_app: [cls.chrome, cls.mysql_workbench, cls.vmware_client, cls.custom],
             AppCategory.cloud: [cls.k8s]
diff --git a/apps/applications/hands.py b/apps/applications/hands.py
index 4987d5948..74d6bf9cb 100644
--- a/apps/applications/hands.py
+++ b/apps/applications/hands.py
@@ -11,5 +11,5 @@
 """
 
 
-from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser, NeedMFAVerify
+from common.permissions import NeedMFAVerify
 from users.models import User, UserGroup
diff --git a/apps/applications/migrations/0017_auto_20220217_2135.py b/apps/applications/migrations/0017_auto_20220217_2135.py
new file mode 100644
index 000000000..4807b570d
--- /dev/null
+++ b/apps/applications/migrations/0017_auto_20220217_2135.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.13 on 2022-02-17 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0016_auto_20220118_1455'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='account',
+            options={'permissions': [('view_applicationaccountsecret', 'Can view application account secret'), ('change_appplicationaccountsecret', 'Can view application account secret')], 'verbose_name': 'Application account'},
+        ),
+        migrations.AlterModelOptions(
+            name='applicationuser',
+            options={'verbose_name': 'Application user'},
+        ),
+        migrations.AlterModelOptions(
+            name='historicalaccount',
+            options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Application account'},
+        ),
+    ]
diff --git a/apps/applications/migrations/0018_auto_20220223_1539.py b/apps/applications/migrations/0018_auto_20220223_1539.py
new file mode 100644
index 000000000..637d8cb86
--- /dev/null
+++ b/apps/applications/migrations/0018_auto_20220223_1539.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2022-02-23 07:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0017_auto_20220217_2135'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='application',
+            name='type',
+            field=models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
+        ),
+    ]
diff --git a/apps/applications/migrations/0019_auto_20220310_1853.py b/apps/applications/migrations/0019_auto_20220310_1853.py
new file mode 100644
index 000000000..42a5683a0
--- /dev/null
+++ b/apps/applications/migrations/0019_auto_20220310_1853.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-10 10:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0018_auto_20220223_1539'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='application',
+            options={'ordering': ('name',), 'permissions': [('match_application', 'Can match application')], 'verbose_name': 'Application'},
+        ),
+    ]
diff --git a/apps/applications/models/account.py b/apps/applications/models/account.py
index 1b0da7979..eac7b99ef 100644
--- a/apps/applications/models/account.py
+++ b/apps/applications/models/account.py
@@ -20,8 +20,12 @@ class Account(BaseUser):
     auth_attrs = ['username', 'password', 'private_key', 'public_key']
 
     class Meta:
-        verbose_name = _('Account')
+        verbose_name = _('Application account')
         unique_together = [('username', 'app', 'systemuser')]
+        permissions = [
+            ('view_applicationaccountsecret', _('Can view application account secret')),
+            ('change_appplicationaccountsecret', _('Can view application account secret')),
+        ]
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
diff --git a/apps/applications/models/application.py b/apps/applications/models/application.py
index 725736aeb..467a5de39 100644
--- a/apps/applications/models/application.py
+++ b/apps/applications/models/application.py
@@ -219,6 +219,9 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
         verbose_name = _('Application')
         unique_together = [('org_id', 'name')]
         ordering = ('name',)
+        permissions = [
+            ('match_application', _('Can match application')),
+        ]
 
     def __str__(self):
         category_display = self.get_category_display()
@@ -265,3 +268,4 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
 class ApplicationUser(SystemUser):
     class Meta:
         proxy = True
+        verbose_name = _('Application user')
diff --git a/apps/applications/serializers/application.py b/apps/applications/serializers/application.py
index 25171f181..9b62d1dc1 100644
--- a/apps/applications/serializers/application.py
+++ b/apps/applications/serializers/application.py
@@ -119,7 +119,8 @@ class AppAccountSerializer(AppSerializerMixin, AuthSerializerMixin, BulkOrgResou
             'username': {'default': '', 'required': False},
             'password': {'write_only': True},
             'app_display': {'label': _('Application display')},
-            'systemuser_display': {'label': _('System User')}
+            'systemuser_display': {'label': _('System User')},
+            'account': {'label': _('account')}
         }
         use_model_bulk_create = True
         model_bulk_create_kwargs = {
diff --git a/apps/applications/serializers/attrs/application_type/__init__.py b/apps/applications/serializers/attrs/application_type/__init__.py
index 94a6f921d..603dd89a4 100644
--- a/apps/applications/serializers/attrs/application_type/__init__.py
+++ b/apps/applications/serializers/attrs/application_type/__init__.py
@@ -1,10 +1,11 @@
 
 from .mysql import *
-from .redis import *
 from .mariadb import *
 from .oracle import *
 from .pgsql import *
 from .sqlserver import *
+from .redis import *
+from .mongodb import *
 
 from .chrome import *
 from .mysql_workbench import *
diff --git a/apps/applications/serializers/attrs/application_type/mongodb.py b/apps/applications/serializers/attrs/application_type/mongodb.py
new file mode 100644
index 000000000..3824e9c8a
--- /dev/null
+++ b/apps/applications/serializers/attrs/application_type/mongodb.py
@@ -0,0 +1,11 @@
+from rest_framework import serializers
+from django.utils.translation import ugettext_lazy as _
+
+from ..application_category import DBSerializer
+
+__all__ = ['MongoDBSerializer']
+
+
+class MongoDBSerializer(DBSerializer):
+    port = serializers.IntegerField(default=27017, label=_('Port'), allow_null=True)
+
diff --git a/apps/applications/serializers/attrs/attrs.py b/apps/applications/serializers/attrs/attrs.py
index a1ca0a3da..1f3fb7944 100644
--- a/apps/applications/serializers/attrs/attrs.py
+++ b/apps/applications/serializers/attrs/attrs.py
@@ -25,11 +25,12 @@ category_serializer_classes_mapping = {
 type_serializer_classes_mapping = {
     # db
     const.AppType.mysql.value: application_type.MySQLSerializer,
-    const.AppType.redis.value: application_type.RedisSerializer,
     const.AppType.mariadb.value: application_type.MariaDBSerializer,
     const.AppType.oracle.value: application_type.OracleSerializer,
     const.AppType.pgsql.value: application_type.PostgreSerializer,
     const.AppType.sqlserver.value: application_type.SQLServerSerializer,
+    const.AppType.redis.value: application_type.RedisSerializer,
+    const.AppType.mongodb.value: application_type.MongoDBSerializer,
     # cloud
     const.AppType.k8s.value: application_type.K8SSerializer
 }
diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py
index 45afd4379..7a8ce9a60 100644
--- a/apps/assets/api/__init__.py
+++ b/apps/assets/api/__init__.py
@@ -10,4 +10,4 @@ from .domain import *
 from .cmd_filter import *
 from .gathered_user import *
 from .favorite_asset import *
-from .backup import *
+from .account_backup import *
diff --git a/apps/assets/api/backup.py b/apps/assets/api/account_backup.py
similarity index 81%
rename from apps/assets/api/backup.py
rename to apps/assets/api/account_backup.py
index 4c0aaf18f..bce4ce55b 100644
--- a/apps/assets/api/backup.py
+++ b/apps/assets/api/account_backup.py
@@ -1,11 +1,9 @@
 # -*- coding: utf-8 -*-
 #
-from rest_framework import status, mixins, viewsets
+from rest_framework import status, viewsets
 from rest_framework.response import Response
 
-from common.permissions import IsOrgAdmin
 from orgs.mixins.api import OrgBulkModelViewSet
-
 from .. import serializers
 from ..tasks import execute_account_backup_plan
 from ..models import (
@@ -24,17 +22,13 @@ class AccountBackupPlanViewSet(OrgBulkModelViewSet):
     ordering_fields = ('name',)
     ordering = ('name',)
     serializer_class = serializers.AccountBackupPlanSerializer
-    permission_classes = (IsOrgAdmin,)
 
 
-class AccountBackupPlanExecutionViewSet(
-    mixins.CreateModelMixin, mixins.ListModelMixin,
-    mixins.RetrieveModelMixin, viewsets.GenericViewSet
-):
+class AccountBackupPlanExecutionViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.AccountBackupPlanExecutionSerializer
     search_fields = ('trigger',)
     filterset_fields = ('trigger', 'plan_id')
-    permission_classes = (IsOrgAdmin,)
+    http_method_names = ['get', 'post', 'options']
 
     def get_queryset(self):
         queryset = AccountBackupPlanExecution.objects.all()
diff --git a/apps/assets/api/accounts.py b/apps/assets/api/accounts.py
index d4b08b380..0a3fe3dfa 100644
--- a/apps/assets/api/accounts.py
+++ b/apps/assets/api/accounts.py
@@ -1,13 +1,14 @@
 from django.db.models import F, Q
-from rest_framework.decorators import action
-from django_filters import rest_framework as filters
-from rest_framework.response import Response
 from django.shortcuts import get_object_or_404
+from django_filters import rest_framework as filters
+from rest_framework.decorators import action
+from rest_framework.response import Response
 from rest_framework.generics import CreateAPIView
 
 from orgs.mixins.api import OrgBulkModelViewSet
-from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, NeedMFAVerify
+from rbac.permissions import RBACPermission
 from common.drf.filters import BaseFilterSet
+from common.permissions import NeedMFAVerify
 from ..tasks.account_connectivity import test_accounts_connectivity_manual
 from ..models import AuthBook, Node
 from .. import serializers
@@ -62,7 +63,9 @@ class AccountViewSet(OrgBulkModelViewSet):
         'default': serializers.AccountSerializer,
         'verify_account': serializers.AssetTaskSerializer
     }
-    permission_classes = (IsOrgAdmin,)
+    rbac_perms = {
+        'verify_account': 'assets.add_authbook'
+    }
 
     def get_queryset(self):
         queryset = AuthBook.get_queryset()
@@ -82,17 +85,23 @@ class AccountSecretsViewSet(AccountViewSet):
     serializer_classes = {
         'default': serializers.AccountSecretSerializer
     }
-    permission_classes = (IsOrgAdmin, NeedMFAVerify)
     http_method_names = ['get']
+    permission_classes = [RBACPermission, NeedMFAVerify]
+    rbac_perms = {
+        'list': 'assets.view_assetaccountsecret',
+        'retrieve': 'assets.view_assetaccountsecret',
+    }
 
 
 class AccountTaskCreateAPI(CreateAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.AccountTaskSerializer
     filterset_fields = AccountViewSet.filterset_fields
     search_fields = AccountViewSet.search_fields
     filterset_class = AccountViewSet.filterset_class
 
+    def check_permissions(self, request):
+        return request.user.has_perm('assets.test_assetconnectivity')
+
     def get_accounts(self):
         queryset = AuthBook.objects.all()
         queryset = self.filter_queryset(queryset)
@@ -109,5 +118,4 @@ class AccountTaskCreateAPI(CreateAPIView):
     def get_exception_handler(self):
         def handler(e, context):
             return Response({"error": str(e)}, status=400)
-
         return handler
diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py
index e7f1598cd..1192599b9 100644
--- a/apps/assets/api/admin_user.py
+++ b/apps/assets/api/admin_user.py
@@ -2,9 +2,9 @@ from django.db.models import Count
 
 from orgs.mixins.api import OrgBulkModelViewSet
 from common.utils import get_logger
-from ..hands import IsOrgAdmin
 from ..models import SystemUser
 from .. import serializers
+from rbac.permissions import RBACPermission
 
 
 logger = get_logger(__file__)
@@ -20,7 +20,7 @@ class AdminUserViewSet(OrgBulkModelViewSet):
     filterset_fields = ("name", "username")
     search_fields = filterset_fields
     serializer_class = serializers.AdminUserSerializer
-    permission_classes = (IsOrgAdmin,)
+    permission_classes = (RBACPermission,)
     ordering_fields = ('name',)
     ordering = ('name', )
 
diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py
index 5b4ef7bb3..bec662c63 100644
--- a/apps/assets/api/asset.py
+++ b/apps/assets/api/asset.py
@@ -1,13 +1,11 @@
 # -*- coding: utf-8 -*-
 #
-from assets.api import FilterAssetByNodeMixin
 from rest_framework.viewsets import ModelViewSet
 from rest_framework.generics import RetrieveAPIView, ListAPIView
 from django.shortcuts import get_object_or_404
 from django.db.models import Q
 
 from common.utils import get_logger, get_object_or_none
-from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
 from common.mixins.api import SuggestionMixin
 from users.models import User, UserGroup
 from users.serializers import UserSerializer, UserGroupSerializer
@@ -17,6 +15,7 @@ from perms.serializers import AssetPermissionSerializer
 from perms.filters import AssetPermissionFilter
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.mixins import generics
+from assets.api import FilterAssetByNodeMixin
 from ..models import Asset, Node, Platform
 from .. import serializers
 from ..tasks import (
@@ -55,7 +54,9 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
         'default': serializers.AssetSerializer,
         'suggestion': serializers.MiniAssetSerializer
     }
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'match': 'assets.match_asset'
+    }
     extra_filter_backends = [FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend]
 
     def set_assets_node(self, assets):
@@ -76,8 +77,10 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
 
 class AssetPlatformRetrieveApi(RetrieveAPIView):
     queryset = Platform.objects.all()
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.PlatformSerializer
+    rbac_perms = {
+        'retrieve': 'assets.view_gateway'
+    }
 
     def get_object(self):
         asset_pk = self.kwargs.get('pk')
@@ -87,16 +90,10 @@ class AssetPlatformRetrieveApi(RetrieveAPIView):
 
 class AssetPlatformViewSet(ModelViewSet):
     queryset = Platform.objects.all()
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.PlatformSerializer
     filterset_fields = ['name', 'base']
     search_fields = ['name']
 
-    def get_permissions(self):
-        if self.request.method.lower() in ['get', 'options']:
-            self.permission_classes = (IsOrgAdmin,)
-        return super().get_permissions()
-
     def check_object_permissions(self, request, obj):
         if request.method.lower() in ['delete', 'put', 'patch'] and obj.internal:
             self.permission_denied(
@@ -131,7 +128,6 @@ class AssetsTaskMixin:
 class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
     model = Asset
     serializer_class = serializers.AssetTaskSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def create(self, request, *args, **kwargs):
         pk = self.kwargs.get('pk')
@@ -139,11 +135,26 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
         request.data['assets'] = [pk]
         return super().create(request, *args, **kwargs)
 
+    def check_permissions(self, request):
+        action = request.data.get('action')
+        action_perm_require = {
+            'refresh': 'assets.refresh_assethardwareinfo',
+            'push_system_user': 'assets.push_assetsystemuser',
+            'test': 'assets.test_assetconnectivity',
+            'test_system_user': 'assets.test_assetconnectivity'
+        }
+        perm_required = action_perm_require.get(action)
+        has = self.request.user.has_perm(perm_required)
+
+        if not has:
+            self.permission_denied(request)
+
     def perform_asset_task(self, serializer):
         data = serializer.validated_data
         action = data['action']
         if action not in ['push_system_user', 'test_system_user']:
             return
+
         asset = data['asset']
         system_users = data.get('system_users')
         if not system_users:
@@ -166,12 +177,23 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
 class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
     model = Asset
     serializer_class = serializers.AssetsTaskSerializer
-    permission_classes = (IsOrgAdmin,)
+
+    def check_permissions(self, request):
+        action = request.data.get('action')
+        action_perm_require = {
+            'refresh': 'assets.refresh_assethardwareinfo1',
+        }
+        perm_required = action_perm_require.get(action)
+        has = self.request.user.has_perm(perm_required)
+        if not has:
+            self.permission_denied(request)
 
 
 class AssetGatewayListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.GatewayWithAuthSerializer
+    rbac_perms = {
+        'list': 'assets.view_gateway'
+    }
 
     def get_queryset(self):
         asset_id = self.kwargs.get('pk')
@@ -183,7 +205,6 @@ class AssetGatewayListApi(generics.ListAPIView):
 
 
 class BaseAssetPermUserOrUserGroupListApi(ListAPIView):
-    permission_classes = (IsOrgAdmin,)
 
     def get_object(self):
         asset_id = self.kwargs.get('pk')
@@ -220,11 +241,13 @@ class AssetPermUserGroupListApi(BaseAssetPermUserOrUserGroupListApi):
 
 
 class BaseAssetPermUserOrUserGroupPermissionsListApiMixin(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     model = AssetPermission
     serializer_class = AssetPermissionSerializer
     filterset_class = AssetPermissionFilter
     search_fields = ('name',)
+    rbac_perms = {
+        'list': 'perms.view_assetpermission'
+    }
 
     def get_object(self):
         asset_id = self.kwargs.get('pk')
diff --git a/apps/assets/api/cmd_filter.py b/apps/assets/api/cmd_filter.py
index 1c6f19cbb..dcb2d77c9 100644
--- a/apps/assets/api/cmd_filter.py
+++ b/apps/assets/api/cmd_filter.py
@@ -8,14 +8,11 @@ from django.shortcuts import get_object_or_404
 from common.utils import reverse
 from common.utils import lazyproperty
 from orgs.mixins.api import OrgBulkModelViewSet
-from tickets.api import GenericTicketStatusRetrieveCloseAPI
-from ..hands import IsOrgAdmin, IsAppUser
 from ..models import CommandFilter, CommandFilterRule
 from .. import serializers
 
 __all__ = [
     'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI',
-    'CommandConfirmStatusAPI'
 ]
 
 
@@ -23,7 +20,6 @@ class CommandFilterViewSet(OrgBulkModelViewSet):
     model = CommandFilter
     filterset_fields = ("name",)
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.CommandFilterSerializer
 
 
@@ -31,7 +27,6 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
     model = CommandFilterRule
     filterset_fields = ('content',)
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.CommandFilterRuleSerializer
 
     def get_queryset(self):
@@ -43,8 +38,10 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
 
 
 class CommandConfirmAPI(CreateAPIView):
-    permission_classes = (IsAppUser,)
     serializer_class = serializers.CommandConfirmSerializer
+    rbac_perms = {
+        'POST': 'tickets.add_superticket'
+    }
 
     def create(self, request, *args, **kwargs):
         ticket = self.create_command_confirm_ticket()
@@ -56,14 +53,14 @@ class CommandConfirmAPI(CreateAPIView):
             run_command=self.serializer.data.get('run_command'),
             session=self.serializer.session,
             cmd_filter_rule=self.serializer.cmd_filter_rule,
-            org_id=self.serializer.org.id
+            org_id=self.serializer.org.id,
         )
         return ticket
 
     @staticmethod
     def get_response_data(ticket):
         confirm_status_url = reverse(
-            view_name='api-assets:command-confirm-status',
+            view_name='api-tickets:super-ticket-status',
             kwargs={'pk': str(ticket.id)}
         )
         ticket_detail_url = reverse(
@@ -86,6 +83,3 @@ class CommandConfirmAPI(CreateAPIView):
         serializer.is_valid(raise_exception=True)
         return serializer
 
-
-class CommandConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
-    pass
diff --git a/apps/assets/api/domain.py b/apps/assets/api/domain.py
index fe3255103..b022f4dec 100644
--- a/apps/assets/api/domain.py
+++ b/apps/assets/api/domain.py
@@ -6,7 +6,6 @@ from rest_framework.views import APIView, Response
 from rest_framework.serializers import ValidationError
 
 from common.utils import get_logger
-from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
 from orgs.mixins.api import OrgBulkModelViewSet
 from ..models import Domain, Gateway
 from .. import serializers
@@ -20,7 +19,6 @@ class DomainViewSet(OrgBulkModelViewSet):
     model = Domain
     filterset_fields = ("name", )
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.DomainSerializer
     ordering_fields = ('name',)
     ordering = ('name', )
@@ -35,13 +33,15 @@ class GatewayViewSet(OrgBulkModelViewSet):
     model = Gateway
     filterset_fields = ("domain__name", "name", "username", "ip", "domain")
     search_fields = ("domain__name", "name", "username", "ip")
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.GatewaySerializer
 
 
 class GatewayTestConnectionApi(SingleObjectMixin, APIView):
-    permission_classes = (IsOrgAdmin,)
+    queryset = Gateway.objects.all()
     object = None
+    rbac_perms = {
+        'POST': 'assets.change_gateway'
+    }
 
     def post(self, request, *args, **kwargs):
         self.object = self.get_object(Gateway.objects.all())
diff --git a/apps/assets/api/gathered_user.py b/apps/assets/api/gathered_user.py
index 959259799..22be7daf7 100644
--- a/apps/assets/api/gathered_user.py
+++ b/apps/assets/api/gathered_user.py
@@ -3,7 +3,6 @@
 
 from orgs.mixins.api import OrgModelViewSet
 from assets.models import GatheredUser
-from common.permissions import IsOrgAdmin
 
 from ..serializers import GatheredUserSerializer
 from ..filters import AssetRelatedByNodeFilterBackend
@@ -15,7 +14,6 @@ __all__ = ['GatheredUserViewSet']
 class GatheredUserViewSet(OrgModelViewSet):
     model = GatheredUser
     serializer_class = GatheredUserSerializer
-    permission_classes = [IsOrgAdmin]
     extra_filter_backends = [AssetRelatedByNodeFilterBackend]
 
     filterset_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname', 'asset_id']
diff --git a/apps/assets/api/label.py b/apps/assets/api/label.py
index 06dff8b8a..cbb7b5bb3 100644
--- a/apps/assets/api/label.py
+++ b/apps/assets/api/label.py
@@ -17,7 +17,6 @@ from django.db.models import Count
 
 from common.utils import get_logger
 from orgs.mixins.api import OrgBulkModelViewSet
-from ..hands import IsOrgAdmin
 from ..models import Label
 from .. import serializers
 
@@ -30,7 +29,6 @@ class LabelViewSet(OrgBulkModelViewSet):
     model = Label
     filterset_fields = ("name", "value")
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.LabelSerializer
 
     def list(self, request, *args, **kwargs):
diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py
index c7f32b306..4eeb722d4 100644
--- a/apps/assets/api/node.py
+++ b/apps/assets/api/node.py
@@ -20,7 +20,6 @@ from common.tree import TreeNodeSerializer
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.mixins import generics
 from orgs.utils import current_org
-from ..hands import IsOrgAdmin
 from ..models import Node
 from ..tasks import (
     update_node_assets_hardware_info_manual,
@@ -46,8 +45,11 @@ class NodeViewSet(SuggestionMixin, OrgBulkModelViewSet):
     model = Node
     filterset_fields = ('value', 'key', 'id')
     search_fields = ('value', )
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.NodeSerializer
+    rbac_perms = {
+        'match': 'assets.match_node',
+        'check_assets_amount_task': 'assets.change_node'
+    }
 
     @action(methods=[POST], detail=False, url_path='check_assets_amount_task')
     def check_assets_amount_task(self, request):
@@ -85,7 +87,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
     ]
     """
     model = Node
-    permission_classes = (IsOrgAdmin,)
     serializer_class = TreeNodeSerializer
 
     @staticmethod
@@ -100,7 +101,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
 
 
 class NodeChildrenApi(generics.ListCreateAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.NodeSerializer
     instance = None
     is_initial = False
@@ -199,7 +199,6 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
 
 
 class NodeAssetsApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.AssetSerializer
 
     def get_queryset(self):
@@ -214,7 +213,6 @@ class NodeAssetsApi(generics.ListAPIView):
 
 class NodeAddChildrenApi(generics.UpdateAPIView):
     model = Node
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.NodeAddChildrenSerializer
     instance = None
 
@@ -231,7 +229,6 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
 class NodeAddAssetsApi(generics.UpdateAPIView):
     model = Node
     serializer_class = serializers.NodeAssetsSerializer
-    permission_classes = (IsOrgAdmin,)
     instance = None
 
     def perform_update(self, serializer):
@@ -243,7 +240,6 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
 class NodeRemoveAssetsApi(generics.UpdateAPIView):
     model = Node
     serializer_class = serializers.NodeAssetsSerializer
-    permission_classes = (IsOrgAdmin,)
     instance = None
 
     def perform_update(self, serializer):
@@ -262,7 +258,6 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
 class MoveAssetsToNodeApi(generics.UpdateAPIView):
     model = Node
     serializer_class = serializers.NodeAssetsSerializer
-    permission_classes = (IsOrgAdmin,)
     instance = None
 
     def perform_update(self, serializer):
@@ -305,8 +300,6 @@ class MoveAssetsToNodeApi(generics.UpdateAPIView):
 class NodeTaskCreateApi(generics.CreateAPIView):
     model = Node
     serializer_class = serializers.NodeTaskSerializer
-    permission_classes = (IsOrgAdmin,)
-
     def get_object(self):
         node_id = self.kwargs.get('pk')
         node = get_object_or_none(self.model, id=node_id)
diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py
index 673a9f34d..f679b4e3f 100644
--- a/apps/assets/api/system_user.py
+++ b/apps/assets/api/system_user.py
@@ -1,16 +1,15 @@
 # ~*~ coding: utf-8 ~*~
 from django.shortcuts import get_object_or_404
-from django.middleware import csrf
 from rest_framework.response import Response
+from rest_framework.decorators import action
 
 from common.utils import get_logger, get_object_or_none
 from common.utils.crypto import get_aes_crypto
-from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsValidUser
+from common.permissions import IsValidUser
+from common.mixins.api import SuggestionMixin
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.mixins import generics
-from common.mixins.api import SuggestionMixin
 from orgs.utils import tmp_to_root_org
-from rest_framework.decorators import action
 from ..models import SystemUser, CommandFilterRule
 from .. import serializers
 from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
@@ -46,7 +45,11 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
     }
     ordering_fields = ('name', 'protocol', 'login_mode')
     ordering = ('name', )
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'su_from': 'assets.view_systemuser',
+        'su_to': 'assets.view_systemuser',
+        'match': 'assets.match_systemuser'
+    }
 
     @action(methods=['get'], detail=False, url_path='su-from')
     def su_from(self, request, *args, **kwargs):
@@ -80,8 +83,13 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
     Get system user auth info
     """
     model = SystemUser
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = SystemUserWithAuthInfoSerializer
+    rbac_perms = {
+        'retrieve': 'assets.view_systemusersecret',
+        'list': 'assets.view_systemusersecret',
+        'change': 'assets.change_systemuser',
+        'destroy': 'assets.change_systemuser',
+    }
 
     def destroy(self, request, *args, **kwargs):
         instance = self.get_object()
@@ -114,7 +122,7 @@ class SystemUserTempAuthInfoApi(generics.CreateAPIView):
 
         with tmp_to_root_org():
             instance = get_object_or_404(SystemUser, pk=pk)
-            instance.set_temp_auth(instance_id, self.request.user, data)
+            instance.set_temp_auth(instance_id, self.request.user.id, data)
         return Response(serializer.data, status=201)
 
 
@@ -123,7 +131,6 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
     Get system user with asset auth info
     """
     model = SystemUser
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = SystemUserWithAuthInfoSerializer
 
     def get_object(self):
@@ -140,8 +147,10 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
     Get system user with asset auth info
     """
     model = SystemUser
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = SystemUserWithAuthInfoSerializer
+    rbac_perms = {
+        'retrieve': 'assets.view_systemusersecret',
+    }
 
     def get_object(self):
         instance = super().get_object()
@@ -153,7 +162,6 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
 
 
 class SystemUserTaskApi(generics.CreateAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.SystemUserTaskSerializer
 
     def do_push(self, system_user, asset_ids=None):
@@ -175,6 +183,18 @@ class SystemUserTaskApi(generics.CreateAPIView):
         pk = self.kwargs.get('pk')
         return get_object_or_404(SystemUser, pk=pk)
 
+    def check_permissions(self, request):
+        action = request.data.get('action')
+        action_perm_require = {
+            'push': 'assets.push_assetsystemuser',
+            'test': 'assets.test_assetconnectivity'
+        }
+        perm_required = action_perm_require.get(action)
+        has = self.request.user.has_perm(perm_required)
+
+        if not has:
+            self.permission_denied(request)
+
     def perform_create(self, serializer):
         action = serializer.validated_data["action"]
         asset = serializer.validated_data.get('asset')
@@ -198,7 +218,9 @@ class SystemUserTaskApi(generics.CreateAPIView):
 
 
 class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'list': 'assets.view_commandfilterule'
+    }
 
     def get_serializer_class(self):
         from ..serializers import CommandFilterRuleSerializer
@@ -224,10 +246,12 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
 
 
 class SystemUserAssetsListView(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.AssetSimpleSerializer
     filterset_fields = ("hostname", "ip")
     search_fields = filterset_fields
+    rbac_perms = {
+        'list': 'assets.view_asset'
+    }
 
     def get_object(self):
         pk = self.kwargs.get('pk')
diff --git a/apps/assets/api/system_user_relation.py b/apps/assets/api/system_user_relation.py
index 374d45cc2..36c16a09b 100644
--- a/apps/assets/api/system_user_relation.py
+++ b/apps/assets/api/system_user_relation.py
@@ -5,7 +5,6 @@ from django.db.models import F, Value, Model
 from django.db.models.signals import m2m_changed
 from django.db.models.functions import Concat
 
-from common.permissions import IsOrgAdmin
 from common.utils import get_logger
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.utils import current_org
@@ -65,13 +64,12 @@ class RelationMixin:
 
 
 class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet):
-    pass
+    perm_model = models.SystemUser
 
 
 class SystemUserAssetRelationViewSet(BaseRelationViewSet):
     serializer_class = serializers.SystemUserAssetRelationSerializer
     model = models.SystemUser.assets.through
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'asset', 'systemuser',
     ]
@@ -97,7 +95,6 @@ class SystemUserAssetRelationViewSet(BaseRelationViewSet):
 class SystemUserNodeRelationViewSet(BaseRelationViewSet):
     serializer_class = serializers.SystemUserNodeRelationSerializer
     model = models.SystemUser.nodes.through
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'node', 'systemuser',
     ]
@@ -118,7 +115,6 @@ class SystemUserNodeRelationViewSet(BaseRelationViewSet):
 class SystemUserUserRelationViewSet(BaseRelationViewSet):
     serializer_class = serializers.SystemUserUserRelationSerializer
     model = models.SystemUser.users.through
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'user', 'systemuser',
     ]
@@ -140,4 +136,3 @@ class SystemUserUserRelationViewSet(BaseRelationViewSet):
             )
         )
         return queryset
-
diff --git a/apps/assets/apps.py b/apps/assets/apps.py
index 04ed9fded..e1bb43544 100644
--- a/apps/assets/apps.py
+++ b/apps/assets/apps.py
@@ -1,11 +1,16 @@
 from __future__ import unicode_literals
+from django.utils.translation import ugettext_lazy as _
 
 from django.apps import AppConfig
 
 
 class AssetsConfig(AppConfig):
     name = 'assets'
+    verbose_name = _('App assets')
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
 
     def ready(self):
         super().ready()
-        from . import signals_handler
+        from . import signal_handlers
diff --git a/apps/assets/hands.py b/apps/assets/hands.py
index ee13e589e..c2bf4fd20 100644
--- a/apps/assets/hands.py
+++ b/apps/assets/hands.py
@@ -11,5 +11,4 @@
 """
 
 
-from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
 from users.models import User, UserGroup
diff --git a/apps/assets/migrations/0086_auto_20220217_2135.py b/apps/assets/migrations/0086_auto_20220217_2135.py
new file mode 100644
index 000000000..fcdb9e0c8
--- /dev/null
+++ b/apps/assets/migrations/0086_auto_20220217_2135.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.13 on 2022-02-17 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('assets', '0085_commandfilterrule_ignore_case'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='asset',
+            options={'ordering': ['hostname'], 'permissions': [('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset')], 'verbose_name': 'Asset'},
+        ),
+        migrations.AlterModelOptions(
+            name='authbook',
+            options={'permissions': [('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret')], 'verbose_name': 'AuthBook'},
+        ),
+        migrations.AlterModelOptions(
+            name='label',
+            options={'verbose_name': 'Label'},
+        ),
+    ]
diff --git a/apps/assets/migrations/0087_auto_20220223_1539.py b/apps/assets/migrations/0087_auto_20220223_1539.py
new file mode 100644
index 000000000..6c3302e51
--- /dev/null
+++ b/apps/assets/migrations/0087_auto_20220223_1539.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2022-02-23 07:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('assets', '0086_auto_20220217_2135'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='systemuser',
+            name='protocol',
+            field=models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol'),
+        ),
+    ]
diff --git a/apps/assets/migrations/0088_auto_20220303_1612.py b/apps/assets/migrations/0088_auto_20220303_1612.py
new file mode 100644
index 000000000..f0b0191e1
--- /dev/null
+++ b/apps/assets/migrations/0088_auto_20220303_1612.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.14 on 2022-03-03 08:12
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('assets', '0087_auto_20220223_1539'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='asset',
+            options={'ordering': ['hostname'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset'), ('match_asset', 'Can match asset')], 'verbose_name': 'Asset'},
+        ),
+        migrations.AlterModelOptions(
+            name='node',
+            options={'ordering': ['parent_key', 'value'], 'permissions': [('match_node', 'Can match node')], 'verbose_name': 'Node'},
+        ),
+        migrations.AlterModelOptions(
+            name='systemuser',
+            options={'ordering': ['name'], 'permissions': [('match_systemuser', 'Can match system user')], 'verbose_name': 'System user'},
+        ),
+    ]
diff --git a/apps/assets/migrations/0089_auto_20220310_0616.py b/apps/assets/migrations/0089_auto_20220310_0616.py
new file mode 100644
index 000000000..342c3d887
--- /dev/null
+++ b/apps/assets/migrations/0089_auto_20220310_0616.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.1.14 on 2022-03-09 22:16
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('assets', '0088_auto_20220303_1612'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='authbook',
+            options={'permissions': [('test_authbook', 'Can test asset account connectivity'), ('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret')], 'verbose_name': 'AuthBook'},
+        ),
+        migrations.AlterModelOptions(
+            name='systemuser',
+            options={'ordering': ['name'], 'permissions': [('view_systemuserasset', 'Can view system user asset'), ('add_systemuserasset', 'Can add asset to system user'), ('remove_systemuserasset', 'Can remove system user asset'), ('match_systemuser', 'Can match system user')], 'verbose_name': 'System user'},
+        ),
+        migrations.AlterModelOptions(
+            name='asset',
+            options={'ordering': ['hostname'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset'), ('match_asset', 'Can match asset'), ('add_assettonode', 'Add asset to node'), ('move_assettonode', 'Move asset to node')], 'verbose_name': 'Asset'},
+        ),
+        migrations.AlterModelOptions(
+            name='gateway',
+            options={'permissions': [('test_gateway', 'Test gateway')], 'verbose_name': 'Gateway'},
+        ),
+    ]
diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py
index aaabab1b7..d846c6ce4 100644
--- a/apps/assets/models/asset.py
+++ b/apps/assets/models/asset.py
@@ -8,7 +8,6 @@ from functools import reduce
 from collections import OrderedDict
 
 from django.db import models
-from common.db.models import TextChoices
 from django.utils.translation import ugettext_lazy as _
 from rest_framework.exceptions import ValidationError
 
@@ -59,7 +58,7 @@ class AssetQuerySet(models.QuerySet):
 class ProtocolsMixin:
     protocols = ''
 
-    class Protocol(TextChoices):
+    class Protocol(models.TextChoices):
         ssh = 'ssh', 'SSH'
         rdp = 'rdp', 'RDP'
         telnet = 'telnet', 'Telnet'
@@ -355,3 +354,11 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
         unique_together = [('org_id', 'hostname')]
         verbose_name = _("Asset")
         ordering = ["hostname", ]
+        permissions = [
+            ('refresh_assethardwareinfo', _('Can refresh asset hardware info')),
+            ('test_assetconnectivity', _('Can test asset connectivity')),
+            ('push_assetsystemuser', _('Can push system user to asset')),
+            ('match_asset', _('Can match asset')),
+            ('add_assettonode', _('Add asset to node')),
+            ('move_assettonode', _('Move asset to node')),
+        ]
diff --git a/apps/assets/models/authbook.py b/apps/assets/models/authbook.py
index c489ab608..53766d2b7 100644
--- a/apps/assets/models/authbook.py
+++ b/apps/assets/models/authbook.py
@@ -26,6 +26,11 @@ class AuthBook(BaseUser, AbsConnectivity):
     class Meta:
         verbose_name = _('AuthBook')
         unique_together = [('username', 'asset', 'systemuser')]
+        permissions = [
+            ('test_authbook', _('Can test asset account connectivity')),
+            ('view_assetaccountsecret', _('Can view asset account secret')),
+            ('change_assetaccountsecret', _('Can change asset account secret'))
+        ]
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
diff --git a/apps/assets/models/cmd_filter.py b/apps/assets/models/cmd_filter.py
index d7eac13eb..82fd50e89 100644
--- a/apps/assets/models/cmd_filter.py
+++ b/apps/assets/models/cmd_filter.py
@@ -186,13 +186,15 @@ class CommandFilterRule(OrgModelMixin):
         return ticket
 
     @classmethod
-    def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None, asset_id=None, application_id=None):
+    def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
+                     asset_id=None, application_id=None, org_id=None):
         user_groups = []
         user = get_object_or_none(User, pk=user_id)
         if user:
             user_groups.extend(list(user.groups.all()))
         user_group = get_object_or_none(UserGroup, pk=user_group_id)
         if user_group:
+            org_id = user_group.org_id
             user_groups.append(user_group)
         system_user = get_object_or_none(SystemUser, pk=system_user_id)
         asset = get_object_or_none(Asset, pk=asset_id)
@@ -203,13 +205,18 @@ class CommandFilterRule(OrgModelMixin):
         if user_groups:
             q |= Q(user_groups__in=set(user_groups))
         if system_user:
+            org_id = system_user.org_id
             q |= Q(system_users=system_user)
         if asset:
+            org_id = asset.org_id
             q |= Q(assets=asset)
         if application:
+            org_id = application.org_id
             q |= Q(applications=application)
         if q:
             cmd_filters = CommandFilter.objects.filter(q).filter(is_active=True)
+            if org_id:
+                cmd_filters = cmd_filters.filter(org_id=org_id)
             rule_ids = cmd_filters.values_list('rules', flat=True)
             rules = cls.objects.filter(id__in=rule_ids)
         else:
diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py
index 7fcb02c3c..ba3b069f5 100644
--- a/apps/assets/models/domain.py
+++ b/apps/assets/models/domain.py
@@ -7,7 +7,6 @@ import random
 from django.core.cache import cache
 import paramiko
 from django.db import models
-from django.db.models import TextChoices
 from django.utils.translation import ugettext_lazy as _
 
 from common.utils import get_logger
@@ -55,7 +54,7 @@ class Gateway(BaseUser):
     UNCONNECTIVE_SILENCE_PERIOD_KEY_TMPL = 'asset_unconnective_gateway_silence_period_{}'
     UNCONNECTIVE_SILENCE_PERIOD_BEGIN_VALUE = 60 * 5
 
-    class Protocol(TextChoices):
+    class Protocol(models.TextChoices):
         ssh = 'ssh', 'SSH'
 
     ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
@@ -71,6 +70,9 @@ class Gateway(BaseUser):
     class Meta:
         unique_together = [('name', 'org_id')]
         verbose_name = _("Gateway")
+        permissions = [
+            ('test_gateway', _('Test gateway'))
+        ]
 
     def set_unconnective(self):
         unconnective_key = self.UNCONNECTIVE_KEY_TMPL.format(self.id)
diff --git a/apps/assets/models/label.py b/apps/assets/models/label.py
index c81726425..f7820ccb1 100644
--- a/apps/assets/models/label.py
+++ b/apps/assets/models/label.py
@@ -37,3 +37,4 @@ class Label(OrgModelMixin):
     class Meta:
         db_table = "assets_label"
         unique_together = [('name', 'value', 'org_id')]
+        verbose_name = _('Label')
diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py
index 77614276a..dcebab3eb 100644
--- a/apps/assets/models/node.py
+++ b/apps/assets/models/node.py
@@ -558,6 +558,9 @@ class Node(OrgModelMixin, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
     class Meta:
         verbose_name = _("Node")
         ordering = ['parent_key', 'value']
+        permissions = [
+            ('match_node', _('Can match node')),
+        ]
 
     def __str__(self):
         return self.full_value
diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py
index a888b404b..6ae0fa825 100644
--- a/apps/assets/models/user.py
+++ b/apps/assets/models/user.py
@@ -5,13 +5,11 @@
 import logging
 
 from django.db import models
-from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
 from django.core.validators import MinValueValidator, MaxValueValidator
 from django.core.cache import cache
 
 from common.utils import signer, get_object_or_none
-from common.db.models import TextChoices
 from .base import BaseUser
 from .asset import Asset
 from .authbook import AuthBook
@@ -24,17 +22,18 @@ logger = logging.getLogger(__name__)
 class ProtocolMixin:
     protocol: str
 
-    class Protocol(TextChoices):
+    class Protocol(models.TextChoices):
         ssh = 'ssh', 'SSH'
         rdp = 'rdp', 'RDP'
         telnet = 'telnet', 'Telnet'
         vnc = 'vnc', 'VNC'
         mysql = 'mysql', 'MySQL'
-        redis = 'redis', 'Redis'
         oracle = 'oracle', 'Oracle'
         mariadb = 'mariadb', 'MariaDB'
         postgresql = 'postgresql', 'PostgreSQL'
         sqlserver = 'sqlserver', 'SQLServer'
+        redis = 'redis', 'Redis'
+        mongodb = 'mongodb', 'MongoDB'
         k8s = 'k8s', 'K8S'
 
     SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp]
@@ -46,8 +45,9 @@ class ProtocolMixin:
         Protocol.rdp
     ]
     APPLICATION_CATEGORY_DB_PROTOCOLS = [
-        Protocol.mysql, Protocol.redis, Protocol.oracle,
-        Protocol.mariadb, Protocol.postgresql, Protocol.sqlserver
+        Protocol.mysql, Protocol.mariadb, Protocol.oracle,
+        Protocol.postgresql, Protocol.sqlserver,
+        Protocol.redis, Protocol.mongodb
     ]
     APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [
         Protocol.k8s
@@ -217,7 +217,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
         (LOGIN_MANUAL, _('Manually input'))
     )
 
-    class Type(TextChoices):
+    class Type(models.TextChoices):
         common = 'common', _('Common user')
         admin = 'admin', _('Admin user')
 
@@ -323,9 +323,15 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
         ordering = ['name']
         unique_together = [('name', 'org_id')]
         verbose_name = _("System user")
+        permissions = [
+            ('view_systemuserasset', _('Can view system user asset')),
+            ('add_systemuserasset', _('Can add asset to system user')),
+            ('remove_systemuserasset', _('Can remove system user asset')),
+            ('match_systemuser', _('Can match system user')),
+        ]
 
 
-# Todo: 准备废弃
+# Deprecated: 准备废弃
 class AdminUser(BaseUser):
     """
     A privileged user that ansible can use it to push system user and so on
diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py
index bb09e7580..fb4c12d67 100644
--- a/apps/assets/serializers/node.py
+++ b/apps/assets/serializers/node.py
@@ -5,7 +5,6 @@ from django.utils.translation import ugettext as _
 from orgs.mixins.serializers import BulkOrgResourceModelSerializer
 from ..models import Asset, Node
 
-
 __all__ = [
     'NodeSerializer', "NodeAddChildrenSerializer",
     "NodeAssetsSerializer", "NodeTaskSerializer",
@@ -45,7 +44,6 @@ class NodeSerializer(BulkOrgResourceModelSerializer):
 
     def create(self, validated_data):
         full_value = validated_data.get('full_value')
-        value = validated_data.get('value')
 
         # 直接多层级创建
         if full_value:
@@ -53,7 +51,8 @@ class NodeSerializer(BulkOrgResourceModelSerializer):
         # 根据 value 在 root 下创建
         else:
             key = Node.org_root().get_next_child_key()
-            node = Node.objects.create(key=key, value=value)
+            validated_data['key'] = key
+            node = Node.objects.create(**validated_data)
         return node
 
 
diff --git a/apps/assets/signals_handler/__init__.py b/apps/assets/signal_handlers/__init__.py
similarity index 100%
rename from apps/assets/signals_handler/__init__.py
rename to apps/assets/signal_handlers/__init__.py
diff --git a/apps/assets/signals_handler/asset.py b/apps/assets/signal_handlers/asset.py
similarity index 100%
rename from apps/assets/signals_handler/asset.py
rename to apps/assets/signal_handlers/asset.py
diff --git a/apps/assets/signals_handler/authbook.py b/apps/assets/signal_handlers/authbook.py
similarity index 100%
rename from apps/assets/signals_handler/authbook.py
rename to apps/assets/signal_handlers/authbook.py
diff --git a/apps/assets/signals_handler/common.py b/apps/assets/signal_handlers/common.py
similarity index 100%
rename from apps/assets/signals_handler/common.py
rename to apps/assets/signal_handlers/common.py
diff --git a/apps/assets/signals_handler/node_assets_amount.py b/apps/assets/signal_handlers/node_assets_amount.py
similarity index 100%
rename from apps/assets/signals_handler/node_assets_amount.py
rename to apps/assets/signal_handlers/node_assets_amount.py
diff --git a/apps/assets/signals_handler/node_assets_mapping.py b/apps/assets/signal_handlers/node_assets_mapping.py
similarity index 100%
rename from apps/assets/signals_handler/node_assets_mapping.py
rename to apps/assets/signal_handlers/node_assets_mapping.py
diff --git a/apps/assets/signals_handler/system_user.py b/apps/assets/signal_handlers/system_user.py
similarity index 100%
rename from apps/assets/signals_handler/system_user.py
rename to apps/assets/signal_handlers/system_user.py
diff --git a/apps/assets/task_handlers/backup/handlers.py b/apps/assets/task_handlers/backup/handlers.py
index d0e9fe365..a73bced59 100644
--- a/apps/assets/task_handlers/backup/handlers.py
+++ b/apps/assets/task_handlers/backup/handlers.py
@@ -88,7 +88,7 @@ class AssetAccountHandler(BaseAccountHandler):
         for k, v in df_dict.items():
             df_dict[k] = pd.DataFrame(v)
 
-        logger.info('\n\033[33m- 共收集{}条资产账号\033[0m'.format(accounts.count()))
+        logger.info('\n\033[33m- 共收集 {} 条资产账号\033[0m'.format(accounts.count()))
         return df_dict
 
 
diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py
index b92903d02..59d4ab171 100644
--- a/apps/assets/urls/api_urls.py
+++ b/apps/assets/urls/api_urls.py
@@ -26,8 +26,8 @@ router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
 router.register(r'system-users-assets-relations', api.SystemUserAssetRelationViewSet, 'system-users-assets-relation')
 router.register(r'system-users-nodes-relations', api.SystemUserNodeRelationViewSet, 'system-users-nodes-relation')
 router.register(r'system-users-users-relations', api.SystemUserUserRelationViewSet, 'system-users-users-relation')
-router.register(r'backup', api.AccountBackupPlanViewSet, 'backup')
-router.register(r'backup-execution', api.AccountBackupPlanExecutionViewSet, 'backup-execution')
+router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup')
+router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
 
 cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
 cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
@@ -68,7 +68,6 @@ urlpatterns = [
     path('gateways/<uuid:pk>/test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
 
     path('cmd-filters/command-confirm/', api.CommandConfirmAPI.as_view(), name='command-confirm'),
-    path('cmd-filters/command-confirm/<uuid:pk>/status/', api.CommandConfirmStatusAPI.as_view(), name='command-confirm-status')
 
 ]
 
diff --git a/apps/audits/api.py b/apps/audits/api.py
index 2130c8333..9a023e3dd 100644
--- a/apps/audits/api.py
+++ b/apps/audits/api.py
@@ -3,8 +3,10 @@
 from rest_framework.mixins import ListModelMixin, CreateModelMixin
 from django.db.models import F, Value
 from django.db.models.functions import Concat
+from rest_framework.permissions import IsAuthenticated
+from rest_framework import generics
 
-from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsOrgAdmin
+from common.drf.api import JMSReadOnlyModelViewSet
 from common.drf.filters import DatetimeRangeFilter
 from common.api import CommonGenericViewSet
 from orgs.mixins.api import OrgGenericViewSet, OrgBulkModelViewSet, OrgRelationMixin
@@ -20,7 +22,6 @@ class FTPLogViewSet(CreateModelMixin,
                     OrgGenericViewSet):
     model = FTPLog
     serializer_class = FTPLogSerializer
-    permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
     extra_filter_backends = [DatetimeRangeFilter]
     date_range_filter_fields = [
         ('date_start', ('date_from', 'date_to'))
@@ -30,9 +31,8 @@ class FTPLogViewSet(CreateModelMixin,
     ordering = ['-date_start']
 
 
-class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):
+class UserLoginCommonMixin:
     queryset = UserLoginLog.objects.all()
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
     serializer_class = UserLoginLogSerializer
     extra_filter_backends = [DatetimeRangeFilter]
     date_range_filter_fields = [
@@ -41,6 +41,9 @@ class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):
     filterset_fields = ['username', 'ip', 'city', 'type', 'status', 'mfa']
     search_fields = ['username', 'ip', 'city']
 
+
+class UserLoginLogViewSet(UserLoginCommonMixin, ListModelMixin, CommonGenericViewSet):
+
     @staticmethod
     def get_org_members():
         users = current_org.get_members().values_list('username', flat=True)
@@ -55,10 +58,18 @@ class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):
         return queryset
 
 
+class MyLoginLogAPIView(UserLoginCommonMixin, generics.ListAPIView):
+    permission_classes = [IsAuthenticated]
+
+    def get_queryset(self):
+        qs = super().get_queryset()
+        qs = qs.filter(username=self.request.user.username)
+        return qs
+
+
 class OperateLogViewSet(ListModelMixin, OrgGenericViewSet):
     model = OperateLog
     serializer_class = OperateLogSerializer
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
     extra_filter_backends = [DatetimeRangeFilter]
     date_range_filter_fields = [
         ('datetime', ('date_from', 'date_to'))
@@ -70,7 +81,6 @@ class OperateLogViewSet(ListModelMixin, OrgGenericViewSet):
 
 class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet):
     queryset = PasswordChangeLog.objects.all()
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
     serializer_class = PasswordChangeLogSerializer
     extra_filter_backends = [DatetimeRangeFilter]
     date_range_filter_fields = [
@@ -91,7 +101,6 @@ class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet):
 class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
     model = CommandExecution
     serializer_class = CommandExecutionSerializer
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
     extra_filter_backends = [DatetimeRangeFilter]
     date_range_filter_fields = [
         ('date_start', ('date_from', 'date_to'))
@@ -117,7 +126,6 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
 class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet):
     serializer_class = CommandExecutionHostsRelationSerializer
     m2m_field = CommandExecution.hosts.field
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
     filterset_fields = [
         'id', 'asset', 'commandexecution'
     ]
diff --git a/apps/audits/apps.py b/apps/audits/apps.py
index ba010cd6c..26d7be9ca 100644
--- a/apps/audits/apps.py
+++ b/apps/audits/apps.py
@@ -1,12 +1,14 @@
 from django.apps import AppConfig
 from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
 from django.db.models.signals import post_save
 
 
 class AuditsConfig(AppConfig):
     name = 'audits'
+    verbose_name = _('Audits')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
         if settings.SYSLOG_ENABLE:
-            post_save.connect(signals_handler.on_audits_log_create)
+            post_save.connect(signal_handlers.on_audits_log_create)
diff --git a/apps/audits/migrations/0013_auto_20211130_1037.py b/apps/audits/migrations/0013_auto_20211130_1037.py
new file mode 100644
index 000000000..15fa2a382
--- /dev/null
+++ b/apps/audits/migrations/0013_auto_20211130_1037.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.1.13 on 2021-11-30 02:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('audits', '0012_auto_20210414_1443'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='ftplog',
+            options={'verbose_name': 'File transfer log'},
+        ),
+        migrations.AlterModelOptions(
+            name='operatelog',
+            options={'verbose_name': 'Operate log'},
+        ),
+        migrations.AlterModelOptions(
+            name='passwordchangelog',
+            options={'verbose_name': 'Password change log'},
+        ),
+        migrations.AlterModelOptions(
+            name='userloginlog',
+            options={'ordering': ['-datetime', 'username'], 'verbose_name': 'User login log'},
+        ),
+    ]
diff --git a/apps/audits/models.py b/apps/audits/models.py
index f40dce16c..0cd17ede2 100644
--- a/apps/audits/models.py
+++ b/apps/audits/models.py
@@ -43,6 +43,9 @@ class FTPLog(OrgModelMixin):
     is_success = models.BooleanField(default=True, verbose_name=_("Success"))
     date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Date start'))
 
+    class Meta:
+        verbose_name = _("File transfer log")
+
 
 class OperateLog(OrgModelMixin):
     ACTION_CREATE = 'create'
@@ -73,6 +76,9 @@ class OperateLog(OrgModelMixin):
             self.org_id = Organization.ROOT_ID
         return super(OperateLog, self).save(*args, **kwargs)
 
+    class Meta:
+        verbose_name = _("Operate log")
+
 
 class PasswordChangeLog(models.Model):
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
@@ -84,6 +90,9 @@ class PasswordChangeLog(models.Model):
     def __str__(self):
         return "{} change {}'s password".format(self.change_by, self.user)
 
+    class Meta:
+        verbose_name = _('Password change log')
+
 
 class UserLoginLog(models.Model):
     LOGIN_TYPE_CHOICE = (
@@ -155,3 +164,4 @@ class UserLoginLog(models.Model):
 
     class Meta:
         ordering = ['-datetime', 'username']
+        verbose_name = _('User login log')
diff --git a/apps/audits/signals_handler.py b/apps/audits/signal_handlers.py
similarity index 98%
rename from apps/audits/signals_handler.py
rename to apps/audits/signal_handlers.py
index 0f9356066..daa3fa4bf 100644
--- a/apps/audits/signals_handler.py
+++ b/apps/audits/signal_handlers.py
@@ -102,11 +102,6 @@ def create_operate_log(action, sender, resource):
 
 
 M2M_NEED_RECORD = {
-    'OrganizationMember': (
-        _('User and Organization'),
-        _('{User} JOINED {Organization}'),
-        _('{User} LEFT {Organization}')
-    ),
     User.groups.through._meta.object_name: (
         _('User and Group'),
         _('{User} JOINED {UserGroup}'),
diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py
index 8cc822706..7301b67fb 100644
--- a/apps/audits/urls/api_urls.py
+++ b/apps/audits/urls/api_urls.py
@@ -1,7 +1,7 @@
 # ~*~ coding: utf-8 ~*~
 from __future__ import unicode_literals
 
-from django.urls.conf import re_path
+from django.urls.conf import re_path, path
 from rest_framework.routers import DefaultRouter
 
 from common import api as capi
@@ -20,6 +20,7 @@ router.register(r'command-executions-hosts-relations', api.CommandExecutionHostR
 
 
 urlpatterns = [
+    path('my-login-logs/', api.MyLoginLogAPIView.as_view(), name='my-login-log'),
 ]
 
 old_version_urlpatterns = [
diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py
index 4bf980374..6062f64ce 100644
--- a/apps/authentication/api/connection_token.py
+++ b/apps/authentication/api/connection_token.py
@@ -24,12 +24,10 @@ from applications.models import Application
 from authentication.signals import post_auth_failed
 from common.utils import get_logger, random_string
 from common.mixins.api import SerializerMixin
-from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser
 from common.utils.common import get_file_by_arch
 from orgs.mixins.api import RootOrgViewMixin
 from common.http import is_true
 from perms.models.base import Action
-from perms.utils.application.permission import validate_permission as app_validate_permission
 from perms.utils.application.permission import get_application_actions
 from perms.utils.asset.permission import get_asset_actions
 
@@ -42,6 +40,14 @@ __all__ = ['UserConnectionTokenViewSet']
 
 
 class ClientProtocolMixin:
+    """
+    下载客户端支持的连接文件,里面包含了 token,和 其他连接信息
+
+    - [x] RDP
+    - [ ] KoKo
+
+    本质上,这里还是暴露出 token 来,进行使用
+    """
     request: Request
     get_serializer: Callable
     create_token: Callable
@@ -99,7 +105,7 @@ class ClientProtocolMixin:
         width = self.request.query_params.get('width')
         full_screen = is_true(self.request.query_params.get('full_screen'))
         drives_redirect = is_true(self.request.query_params.get('drives_redirect'))
-        token = self.create_token(user, asset, application, system_user)
+        token, secret = self.create_token(user, asset, application, system_user)
 
         # 设置磁盘挂载
         if drives_redirect:
@@ -167,7 +173,7 @@ class ClientProtocolMixin:
         rst = rst.decode('ascii')
         return rst
 
-    @action(methods=['POST', 'GET'], detail=False, url_path='rdp/file', permission_classes=[IsValidUser])
+    @action(methods=['POST', 'GET'], detail=False, url_path='rdp/file')
     def get_rdp_file(self, request, *args, **kwargs):
         if self.request.method == 'GET':
             data = self.request.query_params
@@ -214,7 +220,7 @@ class ClientProtocolMixin:
         }
         return data
 
-    @action(methods=['POST', 'GET'], detail=False, url_path='client-url', permission_classes=[IsValidUser])
+    @action(methods=['POST', 'GET'], detail=False, url_path='client-url')
     def get_client_protocol_url(self, request, *args, **kwargs):
         serializer = self.get_valid_serializer()
         try:
@@ -255,6 +261,7 @@ class SecretDetailMixin:
             'asset': asset,
             'application': application,
             'gateway': gateway,
+            'domain': domain,
             'remote_app': remote_app,
         }
 
@@ -267,12 +274,19 @@ class SecretDetailMixin:
         return {
             'asset': asset,
             'application': None,
+            'domain': asset.domain,
             'gateway': gateway,
             'remote_app': None,
         }
 
-    @action(methods=['POST'], detail=False, permission_classes=[IsSuperUserOrAppUser], url_path='secret-info/detail')
+    @action(methods=['POST'], detail=False, url_path='secret-info/detail')
     def get_secret_detail(self, request, *args, **kwargs):
+        perm_required = 'authentication.view_connectiontokensecret'
+
+        # 非常重要的 api,再逻辑层再判断一下,双重保险
+        if not request.user.has_perm(perm_required):
+            raise PermissionDenied('Not allow to view secret')
+
         token = request.data.get('token', '')
         try:
             value, user, system_user, asset, app, expired_at, actions = self.valid_token(token)
@@ -288,16 +302,26 @@ class SecretDetailMixin:
             user=user, system_user=system_user,
             expired_at=expired_at, actions=actions
         )
+        cmd_filter_kwargs = {
+            'system_user_id': system_user.id,
+            'user_id': user.id,
+        }
         if asset:
             asset_detail = self._get_asset_secret_detail(asset)
             system_user.load_asset_more_auth(asset.id, user.username, user.id)
             data['type'] = 'asset'
             data.update(asset_detail)
+            cmd_filter_kwargs['asset_id'] = asset.id
         else:
             app_detail = self._get_application_secret_detail(app)
             system_user.load_app_more_auth(app.id, user.username, user.id)
             data['type'] = 'application'
             data.update(app_detail)
+            cmd_filter_kwargs['application_id'] = app.id
+
+        from assets.models import CommandFilterRule
+        cmd_filter_rules = CommandFilterRule.get_queryset(**cmd_filter_kwargs)
+        data['cmd_filter_rules'] = cmd_filter_rules
 
         serializer = self.get_serializer(data)
         return Response(data=serializer.data, status=200)
@@ -307,12 +331,18 @@ class UserConnectionTokenViewSet(
     RootOrgViewMixin, SerializerMixin, ClientProtocolMixin,
     SecretDetailMixin, GenericViewSet
 ):
-    permission_classes = (IsSuperUserOrAppUser,)
     serializer_classes = {
         'default': ConnectionTokenSerializer,
         'get_secret_detail': ConnectionTokenSecretSerializer,
     }
     CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
+    rbac_perms = {
+        'GET': 'authentication.view_connectiontoken',
+        'create': 'authentication.add_connectiontoken',
+        'get_secret_detail': 'authentication.view_connectiontokensecret',
+        'get_rdp_file': 'authentication.add_connectiontoken',
+        'get_client_protocol_url': 'authentication.add_connectiontoken',
+    }
 
     @staticmethod
     def check_resource_permission(user, asset, application, system_user):
@@ -330,8 +360,10 @@ class UserConnectionTokenViewSet(
         return True
 
     def create_token(self, user, asset, application, system_user, ttl=5 * 60):
-        if not self.request.user.is_superuser and user != self.request.user:
-            raise PermissionDenied('Only super user can create user token')
+        # 再次强调一下权限
+        perm_required = 'authentication.add_superconnectiontoken'
+        if user != self.request.user and not self.request.user.has_perm(perm_required):
+            raise PermissionDenied('Only can create user token')
         self.check_resource_permission(user, asset, application, system_user)
         token = random_string(36)
         secret = random_string(16)
@@ -361,15 +393,20 @@ class UserConnectionTokenViewSet(
 
         key = self.CACHE_KEY_PREFIX.format(token)
         cache.set(key, value, timeout=ttl)
-        return token
+        return token, secret
 
     def create(self, request, *args, **kwargs):
         serializer = self.get_serializer(data=request.data)
         serializer.is_valid(raise_exception=True)
 
         asset, application, system_user, user = self.get_request_resource(serializer)
-        token = self.create_token(user, asset, application, system_user)
-        return Response({"token": token}, status=201)
+        token, secret = self.create_token(user, asset, application, system_user)
+        tp = 'app' if application else 'asset'
+        data = {
+            "id": token, 'secret': secret,
+            'type': tp, 'protocol': system_user.protocol
+        }
+        return Response(data, status=201)
 
     def valid_token(self, token):
         from users.models import User
@@ -403,14 +440,6 @@ class UserConnectionTokenViewSet(
             raise serializers.ValidationError('Permission expired or invalid')
         return value, user, system_user, asset, app, expired_at, actions
 
-    def get_permissions(self):
-        if self.action in ["create", "get_rdp_file"]:
-            if self.request.data.get('user', None):
-                self.permission_classes = (IsSuperUser,)
-            else:
-                self.permission_classes = (IsValidUser,)
-        return super().get_permissions()
-
     def get(self, request):
         token = request.query_params.get('token')
         key = self.CACHE_KEY_PREFIX.format(token)
diff --git a/apps/authentication/api/dingtalk.py b/apps/authentication/api/dingtalk.py
index ce1732118..da03f015b 100644
--- a/apps/authentication/api/dingtalk.py
+++ b/apps/authentication/api/dingtalk.py
@@ -5,7 +5,6 @@ from rest_framework.response import Response
 from users.permissions import IsAuthPasswdTimeValid
 from users.models import User
 from common.utils import get_logger
-from common.permissions import IsOrgAdmin
 from common.mixins.api import RoleUserMixin, RoleAdminMixin
 from authentication import errors
 
@@ -32,4 +31,4 @@ class DingTalkQRUnBindForUserApi(RoleUserMixin, DingTalkQRUnBindBase):
 
 class DingTalkQRUnBindForAdminApi(RoleAdminMixin, DingTalkQRUnBindBase):
     user_id_url_kwarg = 'user_id'
-    permission_classes = (IsOrgAdmin,)
+    
\ No newline at end of file
diff --git a/apps/authentication/api/feishu.py b/apps/authentication/api/feishu.py
index 1665d057a..aaed60db9 100644
--- a/apps/authentication/api/feishu.py
+++ b/apps/authentication/api/feishu.py
@@ -5,7 +5,6 @@ from rest_framework.response import Response
 from users.permissions import IsAuthPasswdTimeValid
 from users.models import User
 from common.utils import get_logger
-from common.permissions import IsOrgAdmin
 from common.mixins.api import RoleUserMixin, RoleAdminMixin
 from authentication import errors
 
@@ -32,7 +31,6 @@ class FeiShuQRUnBindForUserApi(RoleUserMixin, FeiShuQRUnBindBase):
 
 class FeiShuQRUnBindForAdminApi(RoleAdminMixin, FeiShuQRUnBindBase):
     user_id_url_kwarg = 'user_id'
-    permission_classes = (IsOrgAdmin,)
 
 
 class FeiShuEventSubscriptionCallback(APIView):
diff --git a/apps/authentication/api/login_confirm.py b/apps/authentication/api/login_confirm.py
index e3a1bc118..9adedb1e0 100644
--- a/apps/authentication/api/login_confirm.py
+++ b/apps/authentication/api/login_confirm.py
@@ -1,13 +1,10 @@
 # -*- coding: utf-8 -*-
 #
-from rest_framework.generics import UpdateAPIView
 from rest_framework.response import Response
 from rest_framework.views import APIView
 from rest_framework.permissions import AllowAny
-from django.shortcuts import get_object_or_404
 
 from common.utils import get_logger
-from common.permissions import IsOrgAdmin
 from .. import errors, mixins
 
 __all__ = ['TicketStatusApi']
diff --git a/apps/authentication/api/mfa.py b/apps/authentication/api/mfa.py
index 950152def..90aeb7fc4 100644
--- a/apps/authentication/api/mfa.py
+++ b/apps/authentication/api/mfa.py
@@ -39,14 +39,6 @@ class MFASendCodeApi(AuthMixin, CreateAPIView):
     username = ''
     ip = ''
 
-    def get_user_from_db(self, username):
-        try:
-            user = get_object_or_404(User, username=username)
-            return user
-        except Exception as e:
-            self.incr_mfa_failed_time(username, self.ip)
-            raise e
-
     def get_user_from_db(self, username):
         """避免暴力测试用户名"""
         ip = self.get_request_ip()
diff --git a/apps/authentication/api/sso.py b/apps/authentication/api/sso.py
index 11435edcb..11f76d633 100644
--- a/apps/authentication/api/sso.py
+++ b/apps/authentication/api/sso.py
@@ -13,7 +13,7 @@ from common.utils.timezone import utc_now
 from common.const.http import POST, GET
 from common.drf.api import JMSGenericViewSet
 from common.drf.serializers import EmptySerializer
-from common.permissions import IsSuperUser
+from common.permissions import OnlySuperUser
 from common.utils import reverse
 from users.models import User
 from ..serializers import SSOTokenSerializer
@@ -32,9 +32,8 @@ class SSOViewSet(AuthMixin, JMSGenericViewSet):
         'login_url': SSOTokenSerializer,
         'login': EmptySerializer
     }
-    permission_classes = (IsSuperUser,)
 
-    @action(methods=[POST], detail=False, permission_classes=[IsSuperUser], url_path='login-url')
+    @action(methods=[POST], detail=False, permission_classes=[OnlySuperUser], url_path='login-url')
     def login_url(self, request, *args, **kwargs):
         if not settings.AUTH_SSO:
             raise SSOAuthClosed()
diff --git a/apps/authentication/api/wecom.py b/apps/authentication/api/wecom.py
index c66da5f79..486efde21 100644
--- a/apps/authentication/api/wecom.py
+++ b/apps/authentication/api/wecom.py
@@ -5,7 +5,6 @@ from rest_framework.response import Response
 from users.permissions import IsAuthPasswdTimeValid
 from users.models import User
 from common.utils import get_logger
-from common.permissions import IsOrgAdmin
 from common.mixins.api import RoleUserMixin, RoleAdminMixin
 from authentication import errors
 
@@ -32,4 +31,4 @@ class WeComQRUnBindForUserApi(RoleUserMixin, WeComQRUnBindBase):
 
 class WeComQRUnBindForAdminApi(RoleAdminMixin, WeComQRUnBindBase):
     user_id_url_kwarg = 'user_id'
-    permission_classes = (IsOrgAdmin,)
+    
\ No newline at end of file
diff --git a/apps/authentication/apps.py b/apps/authentication/apps.py
index 5b63e5696..6516ed70e 100644
--- a/apps/authentication/apps.py
+++ b/apps/authentication/apps.py
@@ -1,11 +1,13 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class AuthenticationConfig(AppConfig):
     name = 'authentication'
+    verbose_name = _('Authentication')
 
     def ready(self):
-        from . import signals_handlers
+        from . import signal_handlers
         from . import notifications
         super().ready()
 
diff --git a/apps/authentication/backends/base.py b/apps/authentication/backends/base.py
new file mode 100644
index 000000000..12b978250
--- /dev/null
+++ b/apps/authentication/backends/base.py
@@ -0,0 +1,54 @@
+from django.contrib.auth.backends import BaseBackend
+from django.contrib.auth.backends import ModelBackend
+
+from users.models import User
+from common.utils import get_logger
+
+
+logger = get_logger(__file__)
+
+
+class JMSBaseAuthBackend:
+
+    @staticmethod
+    def is_enabled():
+        return True
+
+    def has_perm(self, user_obj, perm, obj=None):
+        return False
+
+    def user_can_authenticate(self, user):
+        """
+        Reject users with is_valid=False. Custom user models that don't have
+        that attribute are allowed.
+        """
+        is_valid = getattr(user, 'is_valid', None)
+        return is_valid or is_valid is None
+
+    # allow user to authenticate
+    def username_allow_authenticate(self, username):
+        return self.allow_authenticate(username=username)
+
+    def user_allow_authenticate(self, user):
+        return self.allow_authenticate(user=user)
+
+    def allow_authenticate(self, user=None, username=None):
+        if user:
+            allowed_backend_paths = user.get_allowed_auth_backend_paths()
+        else:
+            allowed_backend_paths = User.get_user_allowed_auth_backend_paths(username)
+        if allowed_backend_paths is None:
+            # 特殊值 None 表示没有限制
+            return True
+        backend_name = self.__class__.__name__
+        allowed_backend_names = [path.split('.')[-1] for path in allowed_backend_paths]
+        allow = backend_name in allowed_backend_names
+        if not allow:
+            info = 'User {} skip authentication backend {}, because it not in {}'
+            info = info.format(username, backend_name, ','.join(allowed_backend_names))
+            logger.debug(info)
+        return allow
+
+
+class JMSModelBackend(JMSBaseAuthBackend, ModelBackend):
+    pass
diff --git a/apps/authentication/backends/cas/__init__.py b/apps/authentication/backends/cas/__init__.py
index bbdbdb814..e12668747 100644
--- a/apps/authentication/backends/cas/__init__.py
+++ b/apps/authentication/backends/cas/__init__.py
@@ -1,3 +1,6 @@
 # -*- coding: utf-8 -*-
 #
+
+# 保证 utils 中的模块进行初始化
+from . import utils
 from .backends import *
diff --git a/apps/authentication/backends/cas/backends.py b/apps/authentication/backends/cas/backends.py
index ec56c6d4d..db137a1bd 100644
--- a/apps/authentication/backends/cas/backends.py
+++ b/apps/authentication/backends/cas/backends.py
@@ -1,11 +1,14 @@
 # -*- coding: utf-8 -*-
 #
 from django_cas_ng.backends import CASBackend as _CASBackend
+from django.conf import settings
 
+from ..base import JMSBaseAuthBackend
 
 __all__ = ['CASBackend']
 
 
-class CASBackend(_CASBackend):
-    def user_can_authenticate(self, user):
-        return True
+class CASBackend(JMSBaseAuthBackend, _CASBackend):
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_CAS
diff --git a/apps/authentication/backends/cas/utils.py b/apps/authentication/backends/cas/utils.py
new file mode 100644
index 000000000..aad1764e3
--- /dev/null
+++ b/apps/authentication/backends/cas/utils.py
@@ -0,0 +1,32 @@
+from django_cas_ng import utils
+from django_cas_ng.utils import (
+    django_settings, get_protocol,
+    urllib_parse, REDIRECT_FIELD_NAME, get_redirect_url
+)
+
+
+def get_service_url(request, redirect_to=None):
+    """
+    重写 get_service url 方法, CAS_ROOT_PROXIED_AS 为空时, 支持跳转回当前访问的域名地址
+    """
+    """Generates application django service URL for CAS"""
+    if getattr(django_settings, 'CAS_ROOT_PROXIED_AS', None):
+        service = django_settings.CAS_ROOT_PROXIED_AS + request.path
+    else:
+        protocol = get_protocol(request)
+        host = request.get_host()
+        service = urllib_parse.urlunparse(
+            (protocol, host, request.path, '', '', ''),
+        )
+    if not django_settings.CAS_STORE_NEXT:
+        if '?' in service:
+            service += '&'
+        else:
+            service += '?'
+        service += urllib_parse.urlencode({
+            REDIRECT_FIELD_NAME: redirect_to or get_redirect_url(request)
+        })
+    return service
+
+
+utils.get_service_url = get_service_url
diff --git a/apps/authentication/backends/api.py b/apps/authentication/backends/drf.py
similarity index 85%
rename from apps/authentication/backends/api.py
rename to apps/authentication/backends/drf.py
index 79d420626..595ae35b6 100644
--- a/apps/authentication/backends/api.py
+++ b/apps/authentication/backends/drf.py
@@ -8,13 +8,14 @@ from django.core.cache import cache
 from django.utils.translation import ugettext as _
 from six import text_type
 from django.contrib.auth import get_user_model
-from django.contrib.auth.backends import ModelBackend
+
 from rest_framework import HTTP_HEADER_ENCODING
 from rest_framework import authentication, exceptions
 from common.auth import signature
 
 from common.utils import get_object_or_none, make_signature, http_to_unixtime
 from ..models import AccessKey, PrivateToken
+from .base import JMSBaseAuthBackend, JMSModelBackend
 
 
 UserModel = get_user_model()
@@ -28,18 +29,6 @@ def get_request_date_header(request):
     return date
 
 
-class JMSModelBackend(ModelBackend):
-    def user_can_authenticate(self, user):
-        return True
-
-    def get_user(self, user_id):
-        try:
-            user = UserModel._default_manager.get(pk=user_id)
-        except UserModel.DoesNotExist:
-            return None
-        return user if user.is_valid else None
-
-
 class AccessKeyAuthentication(authentication.BaseAuthentication):
     """App使用Access key进行签名认证, 目前签名算法比较简单,
     app注册或者手动建立后,会生成 access_key_id 和 access_key_secret,
@@ -164,7 +153,7 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
         return self.keyword
 
 
-class PrivateTokenAuthentication(authentication.TokenAuthentication):
+class PrivateTokenAuthentication(JMSBaseAuthBackend, authentication.TokenAuthentication):
     model = PrivateToken
 
 
@@ -212,46 +201,3 @@ class SignatureAuthentication(signature.SignatureAuthentication):
         except AccessKey.DoesNotExist:
             return None, None
 
-
-class SSOAuthentication(JMSModelBackend):
-    """
-    什么也不做呀😺
-    """
-
-    def authenticate(self, request, sso_token=None, **kwargs):
-        pass
-
-
-class WeComAuthentication(JMSModelBackend):
-    """
-    什么也不做呀😺
-    """
-
-    def authenticate(self, request, **kwargs):
-        pass
-
-
-class DingTalkAuthentication(JMSModelBackend):
-    """
-    什么也不做呀😺
-    """
-
-    def authenticate(self, request, **kwargs):
-        pass
-
-
-class FeiShuAuthentication(JMSModelBackend):
-    """
-    什么也不做呀😺
-    """
-
-    def authenticate(self, request, **kwargs):
-        pass
-
-
-class AuthorizationTokenAuthentication(JMSModelBackend):
-    """
-    什么也不做呀😺
-    """
-    def authenticate(self, request, **kwargs):
-        pass
diff --git a/apps/authentication/backends/ldap.py b/apps/authentication/backends/ldap.py
index b034a62c5..1c8a80cb1 100644
--- a/apps/authentication/backends/ldap.py
+++ b/apps/authentication/backends/ldap.py
@@ -1,43 +1,38 @@
 # coding:utf-8
 #
 
-import warnings
 import ldap
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
-from django_auth_ldap.backend import _LDAPUser, LDAPBackend, LDAPSettings
+from django_auth_ldap.backend import _LDAPUser, LDAPBackend
 from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
 
 from users.utils import construct_user_email
 from common.const import LDAP_AD_ACCOUNT_DISABLE
+from .base import JMSBaseAuthBackend
 
 logger = _LDAPConfig.get_logger()
 
 
-class LDAPAuthorizationBackend(LDAPBackend):
+class LDAPAuthorizationBackend(JMSBaseAuthBackend, LDAPBackend):
     """
     Override this class to override _LDAPUser to LDAPUser
     """
     @staticmethod
-    def user_can_authenticate(user):
-        """
-        Reject users with is_active=False. Custom user models that don't have
-        that attribute are allowed.
-        """
-        is_valid = getattr(user, 'is_valid', None)
-        return is_valid or is_valid is None
+    def is_enabled():
+        return settings.AUTH_LDAP
 
     def get_or_build_user(self, username, ldap_user):
         """
-                This must return a (User, built) 2-tuple for the given LDAP user.
+        This must return a (User, built) 2-tuple for the given LDAP user.
 
-                username is the Django-friendly username of the user. ldap_user.dn is
-                the user's DN and ldap_user.attrs contains all of their LDAP
-                attributes.
+        username is the Django-friendly username of the user. ldap_user.dn is
+        the user's DN and ldap_user.attrs contains all of their LDAP
+        attributes.
 
-                The returned User object may be an unsaved model instance.
+        The returned User object may be an unsaved model instance.
 
-                """
+        """
         model = self.get_user_model()
 
         if self.settings.USER_QUERY_FIELD:
diff --git a/apps/authentication/backends/oidc/__init__.py b/apps/authentication/backends/oidc/__init__.py
index e69de29bb..a0957e5c9 100644
--- a/apps/authentication/backends/oidc/__init__.py
+++ b/apps/authentication/backends/oidc/__init__.py
@@ -0,0 +1 @@
+from .backends import *
diff --git a/apps/authentication/backends/oidc/backends.py b/apps/authentication/backends/oidc/backends.py
new file mode 100644
index 000000000..d1d7bd48c
--- /dev/null
+++ b/apps/authentication/backends/oidc/backends.py
@@ -0,0 +1,290 @@
+"""
+    OpenID Connect relying party (RP) authentication backends
+    =========================================================
+
+    This modules defines backends allowing to authenticate a user using a specific token endpoint
+    of an OpenID Connect provider (OP).
+
+"""
+
+import base64
+import requests
+from rest_framework.exceptions import ParseError
+from django.contrib.auth import get_user_model
+from django.contrib.auth.backends import ModelBackend
+from django.core.exceptions import SuspiciousOperation
+from django.db import transaction
+from django.urls import reverse
+from django.conf import settings
+
+from common.utils import get_logger
+
+from ..base import JMSBaseAuthBackend
+from .utils import validate_and_return_id_token, build_absolute_uri
+from .decorator import ssl_verification
+from .signals import (
+    openid_create_or_update_user, openid_user_login_failed, openid_user_login_success
+)
+
+logger = get_logger(__file__)
+
+__all__ = ['OIDCAuthCodeBackend', 'OIDCAuthPasswordBackend']
+
+
+class UserMixin:
+
+    @transaction.atomic
+    def get_or_create_user_from_claims(self, request, claims):
+        log_prompt = "Get or Create user from claims [ActionForUser]: {}"
+        logger.debug(log_prompt.format('start'))
+
+        sub = claims['sub']
+        name = claims.get('name', sub)
+        username = claims.get('preferred_username', sub)
+        email = claims.get('email', "{}@{}".format(username, 'jumpserver.openid'))
+        logger.debug(
+            log_prompt.format(
+                "sub: {}|name: {}|username: {}|email: {}".format(sub, name, username, email)
+            )
+        )
+
+        user, created = get_user_model().objects.get_or_create(
+            username=username, defaults={"name": name, "email": email}
+        )
+        logger.debug(log_prompt.format("user: {}|created: {}".format(user, created)))
+        logger.debug(log_prompt.format("Send signal => openid create or update user"))
+        openid_create_or_update_user.send(
+            sender=self.__class__, request=request, user=user, created=created,
+            name=name, username=username, email=email
+        )
+        return user, created
+
+
+class OIDCBaseBackend(UserMixin, JMSBaseAuthBackend, ModelBackend):
+
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_OPENID
+
+
+class OIDCAuthCodeBackend(OIDCBaseBackend):
+    """ Allows to authenticate users using an OpenID Connect Provider (OP).
+
+    This authentication backend is able to authenticate users in the case of the OpenID Connect
+    Authorization Code flow. The ``authenticate`` method provided by this backend is likely to be
+    called when the callback URL is requested by the OpenID Connect Provider (OP). Thus it will
+    call the OIDC provider again in order to request a valid token using the authorization code that
+    should be available in the request parameters associated with the callback call.
+
+    """
+
+    @ssl_verification
+    def authenticate(self, request, nonce=None, **kwargs):
+        """ Authenticates users in case of the OpenID Connect Authorization code flow. """
+        log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
+        logger.debug(log_prompt.format('start'))
+
+        # NOTE: the request object is mandatory to perform the authentication using an authorization
+        # code provided by the OIDC supplier.
+        if (nonce is None and settings.AUTH_OPENID_USE_NONCE) or request is None:
+            logger.debug(log_prompt.format('Request or nonce value is missing'))
+            return
+
+        # Fetches required GET parameters from the HTTP request object.
+        state = request.GET.get('state')
+        code = request.GET.get('code')
+
+        # Don't go further if the state value or the authorization code is not present in the GET
+        # parameters because we won't be able to get a valid token for the user in that case.
+        if (state is None and settings.AUTH_OPENID_USE_STATE) or code is None:
+            logger.debug(log_prompt.format('Authorization code or state value is missing'))
+            raise SuspiciousOperation('Authorization code or state value is missing')
+
+        # Prepares the token payload that will be used to request an authentication token to the
+        # token endpoint of the OIDC provider.
+        logger.debug(log_prompt.format('Prepares token payload'))
+        token_payload = {
+            'client_id': settings.AUTH_OPENID_CLIENT_ID,
+            'client_secret': settings.AUTH_OPENID_CLIENT_SECRET,
+            'grant_type': 'authorization_code',
+            'code': code,
+            'redirect_uri': build_absolute_uri(
+                request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
+            )
+        }
+
+        # Prepares the token headers that will be used to request an authentication token to the
+        # token endpoint of the OIDC provider.
+        logger.debug(log_prompt.format('Prepares token headers'))
+        basic_token = "{}:{}".format(settings.AUTH_OPENID_CLIENT_ID, settings.AUTH_OPENID_CLIENT_SECRET)
+        headers = {"Authorization": "Basic {}".format(base64.b64encode(basic_token.encode()).decode())}
+
+        # Calls the token endpoint.
+        logger.debug(log_prompt.format('Call the token endpoint'))
+        token_response = requests.post(
+            settings.AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT, data=token_payload, headers=headers
+        )
+        try:
+            token_response.raise_for_status()
+            token_response_data = token_response.json()
+        except Exception as e:
+            error = "Json token response error, token response " \
+                    "content is: {}, error is: {}".format(token_response.content, str(e))
+            logger.debug(log_prompt.format(error))
+            raise ParseError(error)
+
+        # Validates the token.
+        logger.debug(log_prompt.format('Validate ID Token'))
+        raw_id_token = token_response_data.get('id_token')
+        id_token = validate_and_return_id_token(raw_id_token, nonce)
+        if id_token is None:
+            logger.debug(log_prompt.format(
+                'ID Token is missing, raw id token is: {}'.format(raw_id_token))
+            )
+            return
+
+        # Retrieves the access token and refresh token.
+        access_token = token_response_data.get('access_token')
+        refresh_token = token_response_data.get('refresh_token')
+
+        # Stores the ID token, the related access token and the refresh token in the session.
+        request.session['oidc_auth_id_token'] = raw_id_token
+        request.session['oidc_auth_access_token'] = access_token
+        request.session['oidc_auth_refresh_token'] = refresh_token
+
+        # If the id_token contains userinfo scopes and claims we don't have to hit the userinfo
+        # endpoint.
+        # https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
+        if settings.AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS:
+            logger.debug(log_prompt.format('ID Token in claims'))
+            claims = id_token
+        else:
+            # Fetches the claims (user information) from the userinfo endpoint provided by the OP.
+            logger.debug(log_prompt.format('Fetches the claims from the userinfo endpoint'))
+            claims_response = requests.get(
+                settings.AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT,
+                headers={'Authorization': 'Bearer {0}'.format(access_token)}
+            )
+            try:
+                claims_response.raise_for_status()
+                claims = claims_response.json()
+            except Exception as e:
+                error = "Json claims response error, claims response " \
+                        "content is: {}, error is: {}".format(claims_response.content, str(e))
+                logger.debug(log_prompt.format(error))
+                raise ParseError(error)
+
+        logger.debug(log_prompt.format('Get or create user from claims'))
+        user, created = self.get_or_create_user_from_claims(request, claims)
+
+        logger.debug(log_prompt.format('Update or create oidc user'))
+
+        if self.user_can_authenticate(user):
+            logger.debug(log_prompt.format('OpenID user login success'))
+            logger.debug(log_prompt.format('Send signal => openid user login success'))
+            openid_user_login_success.send(sender=self.__class__, request=request, user=user)
+            return user
+        else:
+            logger.debug(log_prompt.format('OpenID user login failed'))
+            logger.debug(log_prompt.format('Send signal => openid user login failed'))
+            openid_user_login_failed.send(
+                sender=self.__class__, request=request, username=user.username,
+                reason="User is invalid"
+            )
+            return None
+
+
+class OIDCAuthPasswordBackend(OIDCBaseBackend):
+
+    @ssl_verification
+    def authenticate(self, request, username=None, password=None, **kwargs):
+        try:
+            return self._authenticate(request, username, password, **kwargs)
+        except Exception as e:
+            error = f'Authenticate exception: {e}'
+            logger.error(error, exc_info=True)
+            return
+
+    def _authenticate(self, request, username=None, password=None, **kwargs):
+        """
+        https://oauth.net/2/
+        https://aaronparecki.com/oauth-2-simplified/#password
+        """
+        log_prompt = "Process authenticate [OIDCAuthPasswordBackend]: {}"
+        logger.debug(log_prompt.format('start'))
+        request_timeout = 15
+
+        if not username or not password:
+            logger.debug(log_prompt.format('Username or password is missing'))
+            return
+
+        # Prepares the token payload that will be used to request an authentication token to the
+        # token endpoint of the OIDC provider.
+        logger.debug(log_prompt.format('Prepares token payload'))
+        token_payload = {
+            'client_id': settings.AUTH_OPENID_CLIENT_ID,
+            'client_secret': settings.AUTH_OPENID_CLIENT_SECRET,
+            'grant_type': 'password',
+            'username': username,
+            'password': password,
+        }
+
+        # Calls the token endpoint.
+        logger.debug(log_prompt.format('Call the token endpoint'))
+        token_response = requests.post(settings.AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT, data=token_payload, timeout=request_timeout)
+        try:
+            token_response.raise_for_status()
+            token_response_data = token_response.json()
+        except Exception as e:
+            error = "Json token response error, token response " \
+                    "content is: {}, error is: {}".format(token_response.content, str(e))
+            logger.debug(log_prompt.format(error))
+            logger.debug(log_prompt.format('Send signal => openid user login failed'))
+            openid_user_login_failed.send(
+                sender=self.__class__, request=request, username=username, reason=error
+            )
+            return
+
+        # Retrieves the access token
+        access_token = token_response_data.get('access_token')
+
+        # Fetches the claims (user information) from the userinfo endpoint provided by the OP.
+        logger.debug(log_prompt.format('Fetches the claims from the userinfo endpoint'))
+        claims_response = requests.get(
+            settings.AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT,
+            headers={'Authorization': 'Bearer {0}'.format(access_token)},
+            timeout=request_timeout
+        )
+        try:
+            claims_response.raise_for_status()
+            claims = claims_response.json()
+        except Exception as e:
+            error = "Json claims response error, claims response " \
+                    "content is: {}, error is: {}".format(claims_response.content, str(e))
+            logger.debug(log_prompt.format(error))
+            logger.debug(log_prompt.format('Send signal => openid user login failed'))
+            openid_user_login_failed.send(
+                sender=self.__class__, request=request, username=username, reason=error
+            )
+            return
+
+        logger.debug(log_prompt.format('Get or create user from claims'))
+        user, created = self.get_or_create_user_from_claims(request, claims)
+
+        logger.debug(log_prompt.format('Update or create oidc user'))
+
+        if self.user_can_authenticate(user):
+            logger.debug(log_prompt.format('OpenID user login success'))
+            logger.debug(log_prompt.format('Send signal => openid user login success'))
+            openid_user_login_success.send(
+                sender=self.__class__, request=request, user=user
+            )
+            return user
+        else:
+            logger.debug(log_prompt.format('OpenID user login failed'))
+            logger.debug(log_prompt.format('Send signal => openid user login failed'))
+            openid_user_login_failed.send(
+                sender=self.__class__, request=request, username=username, reason="User is invalid"
+            )
+            return None
+
diff --git a/apps/authentication/backends/oidc/decorator.py b/apps/authentication/backends/oidc/decorator.py
new file mode 100644
index 000000000..e28813de8
--- /dev/null
+++ b/apps/authentication/backends/oidc/decorator.py
@@ -0,0 +1,60 @@
+#  coding: utf-8
+#
+
+import warnings
+import contextlib
+import requests
+
+from django.conf import settings
+from urllib3.exceptions import InsecureRequestWarning
+
+from .utils import get_logger
+
+__all__ = ['ssl_verification']
+
+old_merge_environment_settings = requests.Session.merge_environment_settings
+
+
+logger = get_logger(__file__)
+
+
+@contextlib.contextmanager
+def no_ssl_verification():
+    """
+    https://stackoverflow.com/questions/15445981/
+    how-do-i-disable-the-security-certificate-check-in-python-requests
+    """
+    opened_adapters = set()
+
+    def merge_environment_settings(self, url, proxies, stream, verify, cert):
+        # Verification happens only once per connection so we need to close
+        # all the opened adapters once we're done. Otherwise, the effects of
+        # verify=False persist beyond the end of this context manager.
+        opened_adapters.add(self.get_adapter(url))
+        _settings = old_merge_environment_settings(
+            self, url, proxies, stream, verify, cert
+        )
+        _settings['verify'] = False
+        return _settings
+
+    requests.Session.merge_environment_settings = merge_environment_settings
+    try:
+        with warnings.catch_warnings():
+            warnings.simplefilter('ignore', InsecureRequestWarning)
+            yield
+    finally:
+        requests.Session.merge_environment_settings = old_merge_environment_settings
+        for adapter in opened_adapters:
+            try:
+                adapter.close()
+            except:
+                pass
+
+
+def ssl_verification(func):
+    def wrapper(*args, **kwargs):
+        if not settings.AUTH_OPENID_IGNORE_SSL_VERIFICATION:
+            return func(*args, **kwargs)
+        with no_ssl_verification():
+            return func(*args, **kwargs)
+    return wrapper
diff --git a/apps/authentication/backends/oidc/middleware.py b/apps/authentication/backends/oidc/middleware.py
index 0e58591d4..c2ad33637 100644
--- a/apps/authentication/backends/oidc/middleware.py
+++ b/apps/authentication/backends/oidc/middleware.py
@@ -1,10 +1,106 @@
-from jms_oidc_rp.middleware import OIDCRefreshIDTokenMiddleware as _OIDCRefreshIDTokenMiddleware
+import time
+import requests
+import requests.exceptions
+
 from django.core.exceptions import MiddlewareNotUsed
 from django.conf import settings
+from django.contrib import auth
+from common.utils import get_logger
+
+from .utils import validate_and_return_id_token
+from .decorator import ssl_verification
 
 
-class OIDCRefreshIDTokenMiddleware(_OIDCRefreshIDTokenMiddleware):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
+logger = get_logger(__file__)
+
+
+class OIDCRefreshIDTokenMiddleware:
+    """ Allows to periodically refresh the ID token associated with the authenticated user. """
+
+    def __init__(self, get_response):
         if not settings.AUTH_OPENID:
             raise MiddlewareNotUsed
+
+        self.get_response = get_response
+
+    def __call__(self, request):
+        # Refreshes tokens only in the applicable cases.
+        if request.method == 'GET' and not request.is_ajax() and request.user.is_authenticated and settings.AUTH_OPENID:
+            self.refresh_token(request)
+        response = self.get_response(request)
+        return response
+
+    @ssl_verification
+    def refresh_token(self, request):
+        """ Refreshes the token of the current user. """
+
+        log_prompt = "Process refresh Token: {}"
+        # logger.debug(log_prompt.format('Start'))
+
+        # NOTE: SHARE_SESSION is False means that the user does not share sessions
+        # with other applications
+        if not settings.AUTH_OPENID_SHARE_SESSION:
+            logger.debug(log_prompt.format('Not share session'))
+            return
+
+        # NOTE: no refresh token in the session means that the user wasn't authentified using the
+        # OpenID Connect provider (OP).
+        refresh_token = request.session.get('oidc_auth_refresh_token')
+        if refresh_token is None:
+            logger.debug(log_prompt.format('Refresh token is missing'))
+            return
+
+        id_token_exp_timestamp = request.session.get('oidc_auth_id_token_exp_timestamp', None)
+        now_timestamp = time.time()
+        # Returns immediately if the token is still valid.
+        if id_token_exp_timestamp is not None and id_token_exp_timestamp > now_timestamp:
+            # logger.debug(log_prompt.format('Returns immediately because token is still valid'))
+            return
+
+        # Prepares the token payload that will be used to request a new token from the token
+        # endpoint.
+        refresh_token = request.session.pop('oidc_auth_refresh_token')
+        token_payload = {
+            'client_id': settings.AUTH_OPENID_CLIENT_ID,
+            'client_secret': settings.AUTH_OPENID_CLIENT_SECRET,
+            'grant_type': 'refresh_token',
+            'refresh_token': refresh_token,
+            'scope': settings.AUTH_OPENID_SCOPES,
+        }
+
+        # Calls the token endpoint.
+        logger.debug(log_prompt.format('Calls the token endpoint'))
+        token_response = requests.post(settings.AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT, data=token_payload)
+        try:
+            token_response.raise_for_status()
+        except requests.exceptions.HTTPError as e:
+            logger.debug(log_prompt.format('Request exception http error: {}'.format(str(e))))
+            logger.debug(log_prompt.format('Logout'))
+            auth.logout(request)
+            return
+        token_response_data = token_response.json()
+
+        # Validates the token.
+        logger.debug(log_prompt.format('Validate ID Token'))
+        raw_id_token = token_response_data.get('id_token')
+        id_token = validate_and_return_id_token(raw_id_token, validate_nonce=False)
+
+        # If the token cannot be validated we have to log out the current user.
+        if id_token is None:
+            logger.debug(log_prompt.format('ID Token is None'))
+            auth.logout(request)
+            logger.debug(log_prompt.format('Logout'))
+            return
+
+        # Retrieves the access token and refresh token.
+        access_token = token_response_data.get('access_token')
+        refresh_token = token_response_data.get('refresh_token')
+
+        # Stores the ID token, the related access token and the refresh token in the session.
+        request.session['oidc_auth_id_token'] = raw_id_token
+        request.session['oidc_auth_access_token'] = access_token
+        request.session['oidc_auth_refresh_token'] = refresh_token
+
+        # Saves the new expiration timestamp.
+        request.session['oidc_auth_id_token_exp_timestamp'] = \
+            time.time() + settings.AUTH_OPENID_ID_TOKEN_MAX_AGE
diff --git a/apps/authentication/backends/oidc/signals.py b/apps/authentication/backends/oidc/signals.py
new file mode 100644
index 000000000..85d2dcd94
--- /dev/null
+++ b/apps/authentication/backends/oidc/signals.py
@@ -0,0 +1,18 @@
+"""
+    OpenID Connect relying party (RP) signals
+    =========================================
+
+    This modules defines Django signals that can be triggered during the OpenID Connect
+    authentication process.
+
+"""
+
+from django.dispatch import Signal
+
+
+openid_create_or_update_user = Signal(
+    providing_args=['request', 'user', 'created', 'name', 'username', 'email']
+)
+openid_user_login_success = Signal(providing_args=['request', 'user'])
+openid_user_login_failed = Signal(providing_args=['request', 'username', 'reason'])
+
diff --git a/apps/authentication/backends/oidc/urls.py b/apps/authentication/backends/oidc/urls.py
new file mode 100644
index 000000000..a79ebe0c2
--- /dev/null
+++ b/apps/authentication/backends/oidc/urls.py
@@ -0,0 +1,20 @@
+"""
+    OpenID Connect relying party (RP) URLs
+    ======================================
+
+    This modules defines the URLs allowing to perform OpenID Connect flows on a Relying Party (RP).
+    It defines three main endpoints: the authentication request endpoint, the authentication
+    callback endpoint and the end session endpoint.
+
+"""
+
+from django.urls import path
+
+from . import views
+
+
+urlpatterns = [
+    path('login/', views.OIDCAuthRequestView.as_view(), name='login'),
+    path('callback/', views.OIDCAuthCallbackView.as_view(), name='login-callback'),
+    path('logout/', views.OIDCEndSessionView.as_view(), name='logout'),
+]
diff --git a/apps/authentication/backends/oidc/utils.py b/apps/authentication/backends/oidc/utils.py
new file mode 100644
index 000000000..2a0f0609e
--- /dev/null
+++ b/apps/authentication/backends/oidc/utils.py
@@ -0,0 +1,126 @@
+"""
+    OpenID Connect relying party (RP) utilities
+    ===========================================
+
+    This modules defines utilities allowing to manipulate ID tokens and other common helpers.
+
+"""
+
+import datetime as dt
+from calendar import timegm
+from urllib.parse import urlparse, urljoin
+
+from django.core.exceptions import SuspiciousOperation
+from django.utils.encoding import force_bytes, smart_bytes
+from jwkest import JWKESTException
+from jwkest.jwk import KEYS
+from jwkest.jws import JWS
+from django.conf import settings
+
+from common.utils import get_logger
+
+
+logger = get_logger(__file__)
+
+
+def validate_and_return_id_token(jws, nonce=None, validate_nonce=True):
+    """ Validates the id_token according to the OpenID Connect specification. """
+    log_prompt = "Validate ID Token: {}"
+    logger.debug(log_prompt.format('Get shared key'))
+    shared_key = settings.AUTH_OPENID_CLIENT_ID \
+        if settings.AUTH_OPENID_PROVIDER_SIGNATURE_ALG == 'HS256' \
+        else settings.AUTH_OPENID_PROVIDER_SIGNATURE_KEY  # RS256
+
+    try:
+        # Decodes the JSON Web Token and raise an error if the signature is invalid.
+        logger.debug(log_prompt.format('Verify compact jwk'))
+        id_token = JWS().verify_compact(force_bytes(jws), _get_jwks_keys(shared_key))
+    except JWKESTException as e:
+        logger.debug(log_prompt.format('Verify compact jwkest exception: {}'.format(str(e))))
+        return
+
+    # Validates the claims embedded in the id_token.
+    logger.debug(log_prompt.format('Validate claims'))
+    _validate_claims(id_token, nonce=nonce, validate_nonce=validate_nonce)
+
+    return id_token
+
+
+def _get_jwks_keys(shared_key):
+    """ Returns JWKS keys used to decrypt id_token values. """
+    # The OpenID Connect Provider (OP) uses RSA keys to sign/enrypt ID tokens and generate public
+    # keys allowing to decrypt them. These public keys are exposed through the 'jwks_uri' and should
+    # be used to decrypt the JWS - JSON Web Signature.
+    log_prompt = "Get jwks keys: {}"
+    logger.debug(log_prompt.format('Start'))
+    jwks_keys = KEYS()
+    logger.debug(log_prompt.format('Load from provider jwks endpoint'))
+    jwks_keys.load_from_url(settings.AUTH_OPENID_PROVIDER_JWKS_ENDPOINT)
+    # Adds the shared key (which can correspond to the client_secret) as an oct key so it can be
+    # used for HMAC signatures.
+    logger.debug(log_prompt.format('Add key'))
+    jwks_keys.add({'key': smart_bytes(shared_key), 'kty': 'oct'})
+    logger.debug(log_prompt.format('End'))
+    return jwks_keys
+
+
+def _validate_claims(id_token, nonce=None, validate_nonce=True):
+    """ Validates the claims embedded in the JSON Web Token. """
+    log_prompt = "Validate claims: {}"
+    logger.debug(log_prompt.format('Start'))
+
+    iss_parsed_url = urlparse(id_token['iss'])
+    provider_parsed_url = urlparse(settings.AUTH_OPENID_PROVIDER_ENDPOINT)
+    if iss_parsed_url.netloc != provider_parsed_url.netloc:
+        logger.debug(log_prompt.format('Invalid issuer'))
+        raise SuspiciousOperation('Invalid issuer')
+
+    if isinstance(id_token['aud'], str):
+        id_token['aud'] = [id_token['aud']]
+
+    if settings.AUTH_OPENID_CLIENT_ID not in id_token['aud']:
+        logger.debug(log_prompt.format('Invalid audience'))
+        raise SuspiciousOperation('Invalid audience')
+
+    if len(id_token['aud']) > 1 and 'azp' not in id_token:
+        logger.debug(log_prompt.format('Incorrect id_token: azp'))
+        raise SuspiciousOperation('Incorrect id_token: azp')
+
+    if 'azp' in id_token and id_token['azp'] != settings.AUTH_OPENID_CLIENT_ID:
+        raise SuspiciousOperation('Incorrect id_token: azp')
+
+    utc_timestamp = timegm(dt.datetime.utcnow().utctimetuple())
+    if utc_timestamp > id_token['exp']:
+        logger.debug(log_prompt.format('Signature has expired'))
+        raise SuspiciousOperation('Signature has expired')
+
+    if 'nbf' in id_token and utc_timestamp < id_token['nbf']:
+        logger.debug(log_prompt.format('Incorrect id_token: nbf'))
+        raise SuspiciousOperation('Incorrect id_token: nbf')
+
+    # Verifies that the token was issued in the allowed timeframe.
+    if utc_timestamp > id_token['iat'] + settings.AUTH_OPENID_ID_TOKEN_MAX_AGE:
+        logger.debug(log_prompt.format('Incorrect id_token: iat'))
+        raise SuspiciousOperation('Incorrect id_token: iat')
+
+    # Validate the nonce to ensure the request was not modified if applicable.
+    id_token_nonce = id_token.get('nonce', None)
+    if validate_nonce and settings.AUTH_OPENID_USE_NONCE and id_token_nonce != nonce:
+        logger.debug(log_prompt.format('Incorrect id_token: nonce'))
+        raise SuspiciousOperation('Incorrect id_token: nonce')
+
+    logger.debug(log_prompt.format('End'))
+
+
+def build_absolute_uri(request, path=None):
+    """
+    Build absolute redirect uri
+    """
+    if path is None:
+        path = '/'
+
+    if settings.BASE_SITE_URL:
+        redirect_uri = urljoin(settings.BASE_SITE_URL, path)
+    else:
+        redirect_uri = request.build_absolute_uri(path)
+    return redirect_uri
diff --git a/apps/authentication/backends/oidc/views.py b/apps/authentication/backends/oidc/views.py
new file mode 100644
index 000000000..1c9442ef2
--- /dev/null
+++ b/apps/authentication/backends/oidc/views.py
@@ -0,0 +1,222 @@
+"""
+    OpenID Connect relying party (RP) views
+    =======================================
+
+    This modules defines views allowing to start the authorization and authentication process in
+    order to authenticate a specific user. The most important views are: the "login" allowing to
+    authenticate the users using the OP and get an authorizartion code, the callback view allowing
+    to retrieve a valid token for the considered user and the logout view.
+
+"""
+
+import time
+
+from django.conf import settings
+from django.contrib import auth
+from django.core.exceptions import SuspiciousOperation
+from django.http import HttpResponseRedirect, QueryDict
+from django.urls import reverse
+from django.utils.crypto import get_random_string
+from django.utils.http import is_safe_url, urlencode
+from django.views.generic import View
+
+from .utils import get_logger, build_absolute_uri
+
+
+logger = get_logger(__file__)
+
+
+class OIDCAuthRequestView(View):
+    """ Allows to start the authorization flow in order to authenticate the end-user.
+
+    This view acts as the main endpoint to trigger the authentication process involving the OIDC
+    provider (OP). It prepares an authentication request that will be sent to the authorization
+    server in order to authenticate the end-user.
+
+    """
+
+    http_method_names = ['get', ]
+
+    def get(self, request):
+        """ Processes GET requests. """
+
+        log_prompt = "Process GET requests [OIDCAuthRequestView]: {}"
+        logger.debug(log_prompt.format('Start'))
+
+        # Defines common parameters used to bootstrap the authentication request.
+        logger.debug(log_prompt.format('Construct request params'))
+        authentication_request_params = request.GET.dict()
+        authentication_request_params.update({
+            'scope': settings.AUTH_OPENID_SCOPES,
+            'response_type': 'code',
+            'client_id': settings.AUTH_OPENID_CLIENT_ID,
+            'redirect_uri': build_absolute_uri(
+                request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
+            )
+        })
+
+        # States should be used! They are recommended in order to maintain state between the
+        # authentication request and the callback.
+        if settings.AUTH_OPENID_USE_STATE:
+            logger.debug(log_prompt.format('Use state'))
+            state = get_random_string(settings.AUTH_OPENID_STATE_LENGTH)
+            authentication_request_params.update({'state': state})
+            request.session['oidc_auth_state'] = state
+
+        # Nonces should be used too! In that case the generated nonce is stored both in the
+        # authentication request parameters and in the user's session.
+        if settings.AUTH_OPENID_USE_NONCE:
+            logger.debug(log_prompt.format('Use nonce'))
+            nonce = get_random_string(settings.AUTH_OPENID_NONCE_LENGTH)
+            authentication_request_params.update({'nonce': nonce, })
+            request.session['oidc_auth_nonce'] = nonce
+
+        # Stores the "next" URL in the session if applicable.
+        logger.debug(log_prompt.format('Stores next url in the session'))
+        next_url = request.GET.get('next')
+        request.session['oidc_auth_next_url'] = next_url \
+            if is_safe_url(url=next_url, allowed_hosts=(request.get_host(), )) else None
+
+        # Redirects the user to authorization endpoint.
+        logger.debug(log_prompt.format('Construct redirect url'))
+        query = urlencode(authentication_request_params)
+        redirect_url = '{url}?{query}'.format(
+            url=settings.AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT, query=query)
+
+        logger.debug(log_prompt.format('Redirect'))
+        return HttpResponseRedirect(redirect_url)
+
+
+class OIDCAuthCallbackView(View):
+    """ Allows to complete the authentication process.
+
+    This view acts as the main endpoint to complete the authentication process involving the OIDC
+    provider (OP). It checks the request sent by the OIDC provider in order to determine whether the
+    considered was successfully authentified or not and authenticates the user at the current
+    application level if applicable.
+
+    """
+
+    http_method_names = ['get', ]
+
+    def get(self, request):
+        """ Processes GET requests. """
+        log_prompt = "Process GET requests [OIDCAuthCallbackView]: {}"
+        logger.debug(log_prompt.format('Start'))
+        callback_params = request.GET
+
+        # Retrieve the state value that was previously generated. No state means that we cannot
+        # authenticate the user (so a failure should be returned).
+        state = request.session.get('oidc_auth_state', None)
+
+        # Retrieve the nonce that was previously generated and remove it from the current session.
+        # If no nonce is available (while the USE_NONCE setting is set to True) this means that the
+        # authentication cannot be performed and so we have redirect the user to a failure URL.
+        nonce = request.session.pop('oidc_auth_nonce', None)
+
+        # NOTE: a redirect to the failure page should be return if some required GET parameters are
+        # missing or if no state can be retrieved from the current session.
+
+        if (
+            ((nonce and settings.AUTH_OPENID_USE_NONCE) or not settings.AUTH_OPENID_USE_NONCE)
+            and
+            (
+                (state and settings.AUTH_OPENID_USE_STATE and 'state' in callback_params)
+                or
+                (not settings.AUTH_OPENID_USE_STATE)
+            )
+            and
+            ('code' in callback_params)
+        ):
+            # Ensures that the passed state values is the same as the one that was previously
+            # generated when forging the authorization request. This is necessary to mitigate
+            # Cross-Site Request Forgery (CSRF, XSRF).
+            if settings.AUTH_OPENID_USE_STATE and callback_params['state'] != state:
+                logger.debug(log_prompt.format('Invalid OpenID Connect callback state value'))
+                raise SuspiciousOperation('Invalid OpenID Connect callback state value')
+
+            # Authenticates the end-user.
+            next_url = request.session.get('oidc_auth_next_url', None)
+            logger.debug(log_prompt.format('Process authenticate'))
+            user = auth.authenticate(nonce=nonce, request=request)
+            if user and user.is_valid:
+                logger.debug(log_prompt.format('Login: {}'.format(user)))
+                auth.login(self.request, user)
+                # Stores an expiration timestamp in the user's session. This value will be used if
+                # the project is configured to periodically refresh user's token.
+                self.request.session['oidc_auth_id_token_exp_timestamp'] = \
+                    time.time() + settings.AUTH_OPENID_ID_TOKEN_MAX_AGE
+                # Stores the "session_state" value that can be passed by the OpenID Connect provider
+                # in order to maintain a consistent session state across the OP and the related
+                # relying parties (RP).
+                self.request.session['oidc_auth_session_state'] = \
+                    callback_params.get('session_state', None)
+
+                logger.debug(log_prompt.format('Redirect'))
+                return HttpResponseRedirect(
+                    next_url or settings.AUTH_OPENID_AUTHENTICATION_REDIRECT_URI
+                )
+
+        if 'error' in callback_params:
+            logger.debug(
+                log_prompt.format('Error in callback params: {}'.format(callback_params['error']))
+            )
+            # If we receive an error in the callback GET parameters, this means that the
+            # authentication could not be performed at the OP level. In that case we have to logout
+            # the current user because we could've obtained this error after a prompt=none hit on
+            # OpenID Connect Provider authenticate endpoint.
+            logger.debug(log_prompt.format('Logout'))
+            auth.logout(request)
+
+        logger.debug(log_prompt.format('Redirect'))
+        return HttpResponseRedirect(settings.AUTH_OPENID_AUTHENTICATION_FAILURE_REDIRECT_URI)
+
+
+class OIDCEndSessionView(View):
+    """ Allows to end the session of any user authenticated using OpenID Connect.
+
+    This view acts as the main endpoint to end the session of an end-user that was authenticated
+    using the OIDC provider (OP). It calls the "end-session" endpoint provided by the provider if
+    applicable.
+
+    """
+
+    http_method_names = ['get', 'post', ]
+
+    def get(self, request):
+        """ Processes GET requests. """
+        log_prompt = "Process GET requests [OIDCEndSessionView]: {}"
+        logger.debug(log_prompt.format('Start'))
+        return self.post(request)
+
+    def post(self, request):
+        """ Processes POST requests. """
+        log_prompt = "Process POST requests [OIDCEndSessionView]: {}"
+        logger.debug(log_prompt.format('Start'))
+
+        logout_url = settings.LOGOUT_REDIRECT_URL or '/'
+
+        # Log out the current user.
+        if request.user.is_authenticated:
+            logger.debug(log_prompt.format('Current user is authenticated'))
+            try:
+                logout_url = self.provider_end_session_url \
+                    if settings.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT else logout_url
+            except KeyError:  # pragma: no cover
+                logout_url = logout_url
+            logger.debug(log_prompt.format('Log out the current user: {}'.format(request.user)))
+            auth.logout(request)
+
+        # Redirects the user to the appropriate URL.
+        logger.debug(log_prompt.format('Redirect'))
+        return HttpResponseRedirect(logout_url)
+
+    @property
+    def provider_end_session_url(self):
+        """ Returns the end-session URL. """
+        q = QueryDict(mutable=True)
+        q[settings.AUTH_OPENID_PROVIDER_END_SESSION_REDIRECT_URI_PARAMETER] = \
+            build_absolute_uri(self.request, path=settings.LOGOUT_REDIRECT_URL or '/')
+        q[settings.AUTH_OPENID_PROVIDER_END_SESSION_ID_TOKEN_PARAMETER] = \
+            self.request.session['oidc_auth_id_token']
+        return '{}?{}'.format(settings.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT, q.urlencode())
diff --git a/apps/authentication/backends/openid.py b/apps/authentication/backends/openid.py
deleted file mode 100644
index a82161b8e..000000000
--- a/apps/authentication/backends/openid.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""
-使用下面的工程,进行jumpserver 的 oidc 认证
-https://github.com/BaiJiangJie/jumpserver-django-oidc-rp
-"""
\ No newline at end of file
diff --git a/apps/authentication/backends/pubkey.py b/apps/authentication/backends/pubkey.py
index 3355eacaa..1494d6b2e 100644
--- a/apps/authentication/backends/pubkey.py
+++ b/apps/authentication/backends/pubkey.py
@@ -1,13 +1,20 @@
 # -*- coding: utf-8 -*-
 #
 from django.contrib.auth import get_user_model
+from django.conf import settings
+
+from .base import JMSBaseAuthBackend
 
 UserModel = get_user_model()
 
 __all__ = ['PublicKeyAuthBackend']
 
 
-class PublicKeyAuthBackend:
+class PublicKeyAuthBackend(JMSBaseAuthBackend):
+    @staticmethod
+    def is_enabled():
+        return settings.TERMINAL_PUBLIC_KEY_AUTH
+
     def authenticate(self, request, username=None, public_key=None, **kwargs):
         if not public_key:
             return None
@@ -22,15 +29,6 @@ class PublicKeyAuthBackend:
                   self.user_can_authenticate(user):
                 return user
 
-    @staticmethod
-    def user_can_authenticate(user):
-        """
-        Reject users with is_active=False. Custom user models that don't have
-        that attribute are allowed.
-        """
-        is_active = getattr(user, 'is_active', None)
-        return is_active or is_active is None
-
     def get_user(self, user_id):
         try:
             user = UserModel._default_manager.get(pk=user_id)
diff --git a/apps/authentication/backends/radius.py b/apps/authentication/backends/radius.py
index 1baf3d569..170534370 100644
--- a/apps/authentication/backends/radius.py
+++ b/apps/authentication/backends/radius.py
@@ -6,6 +6,8 @@ from django.contrib.auth import get_user_model
 from radiusauth.backends import RADIUSBackend, RADIUSRealmBackend
 from django.conf import settings
 
+from .base import JMSBaseAuthBackend
+
 
 User = get_user_model()
 
@@ -39,11 +41,17 @@ class CreateUserMixin:
             return None
 
 
-class RadiusBackend(CreateUserMixin, RADIUSBackend):
+class RadiusBaseBackend(CreateUserMixin, JMSBaseAuthBackend):
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_RADIUS
+
+
+class RadiusBackend(RadiusBaseBackend, RADIUSBackend):
     def authenticate(self, request, username='', password='', **kwargs):
         return super().authenticate(request, username=username, password=password)
 
 
-class RadiusRealmBackend(CreateUserMixin, RADIUSRealmBackend):
+class RadiusRealmBackend(RadiusBaseBackend, RADIUSRealmBackend):
     def authenticate(self, request, username='', password='', realm=None, **kwargs):
         return super().authenticate(request, username=username, password=password, realm=realm)
diff --git a/apps/authentication/backends/saml2/__init__.py b/apps/authentication/backends/saml2/__init__.py
index bbdbdb814..448096520 100644
--- a/apps/authentication/backends/saml2/__init__.py
+++ b/apps/authentication/backends/saml2/__init__.py
@@ -1,3 +1,4 @@
 # -*- coding: utf-8 -*-
 #
+
 from .backends import *
diff --git a/apps/authentication/backends/saml2/backends.py b/apps/authentication/backends/saml2/backends.py
index 8b7cfe3d0..d31692462 100644
--- a/apps/authentication/backends/saml2/backends.py
+++ b/apps/authentication/backends/saml2/backends.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
 from django.contrib.auth import get_user_model
-from django.contrib.auth.backends import ModelBackend
+from django.conf import settings
 from django.db import transaction
 
 from common.utils import get_logger
@@ -10,16 +10,17 @@ from .signals import (
     saml2_user_authenticated, saml2_user_authentication_failed,
     saml2_create_or_update_user
 )
+from ..base import JMSBaseAuthBackend
 
 __all__ = ['SAML2Backend']
 
 logger = get_logger(__file__)
 
 
-class SAML2Backend(ModelBackend):
-    def user_can_authenticate(self, user):
-        is_valid = getattr(user, 'is_valid', None)
-        return is_valid or is_valid is None
+class SAML2Backend(JMSBaseAuthBackend):
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_SAML2
 
     @transaction.atomic
     def get_or_create_from_saml_data(self, request, **saml_user_data):
diff --git a/apps/authentication/backends/sso.py b/apps/authentication/backends/sso.py
new file mode 100644
index 000000000..86d8f76e1
--- /dev/null
+++ b/apps/authentication/backends/sso.py
@@ -0,0 +1,63 @@
+from django.conf import settings
+
+from .base import JMSBaseAuthBackend
+
+
+class SSOAuthentication(JMSBaseAuthBackend):
+    """
+    什么也不做呀😺
+    """
+
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_SSO
+
+    def authenticate(self, request, sso_token=None, **kwargs):
+        pass
+
+
+class WeComAuthentication(JMSBaseAuthBackend):
+    """
+    什么也不做呀😺
+    """
+
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_WECOM
+
+    def authenticate(self, request, **kwargs):
+        pass
+
+
+class DingTalkAuthentication(JMSBaseAuthBackend):
+    """
+    什么也不做呀😺
+    """
+
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_DINGTALK
+
+    def authenticate(self, request, **kwargs):
+        pass
+
+
+class FeiShuAuthentication(JMSBaseAuthBackend):
+    """
+    什么也不做呀😺
+    """
+
+    @staticmethod
+    def is_enabled():
+        return settings.AUTH_FEISHU
+
+    def authenticate(self, request, **kwargs):
+        pass
+
+
+class AuthorizationTokenAuthentication(JMSBaseAuthBackend):
+    """
+    什么也不做呀😺
+    """
+    def authenticate(self, request, **kwargs):
+        pass
diff --git a/apps/authentication/migrations/0007_connectiontoken.py b/apps/authentication/migrations/0007_connectiontoken.py
new file mode 100644
index 000000000..86341ff5b
--- /dev/null
+++ b/apps/authentication/migrations/0007_connectiontoken.py
@@ -0,0 +1,32 @@
+# Generated by Django 3.1.12 on 2022-02-11 06:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('authentication', '0006_auto_20211227_1059'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ConnectionToken',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
+                ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
+                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
+                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
+            ],
+            options={'verbose_name': 'Connection token'},
+        ),
+        migrations.AlterModelOptions(
+            name='accesskey',
+            options={'verbose_name': 'Access key'},
+        ),
+        migrations.AlterModelOptions(
+            name='ssotoken',
+            options={'verbose_name': 'SSO token'},
+        ),
+    ]
diff --git a/apps/authentication/migrations/0008_superconnectiontoken.py b/apps/authentication/migrations/0008_superconnectiontoken.py
new file mode 100644
index 000000000..82e956a24
--- /dev/null
+++ b/apps/authentication/migrations/0008_superconnectiontoken.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.14 on 2022-03-02 11:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('authentication', '0007_connectiontoken'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='SuperConnectionToken',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Super connection token',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('authentication.connectiontoken',),
+        ),
+    ]
diff --git a/apps/authentication/migrations/0009_auto_20220310_0616.py b/apps/authentication/migrations/0009_auto_20220310_0616.py
new file mode 100644
index 000000000..652aa2ea4
--- /dev/null
+++ b/apps/authentication/migrations/0009_auto_20220310_0616.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-09 22:16
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('authentication', '0008_superconnectiontoken'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='connectiontoken',
+            options={'permissions': [('view_connectiontokensecret', 'Can view connection token secret')], 'verbose_name': 'Connection token'},
+        ),
+    ]
diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py
index c74b1a5ff..56216e91f 100644
--- a/apps/authentication/mixins.py
+++ b/apps/authentication/mixins.py
@@ -12,9 +12,10 @@ from django.contrib import auth
 from django.utils.translation import ugettext as _
 from rest_framework.request import Request
 from django.contrib.auth import (
-    BACKEND_SESSION_KEY, _get_backends,
-    PermissionDenied, user_login_failed, _clean_credentials
+    BACKEND_SESSION_KEY, load_backend,
+    PermissionDenied, user_login_failed, _clean_credentials,
 )
+from django.core.exceptions import ImproperlyConfigured
 from django.shortcuts import reverse, redirect, get_object_or_404
 
 from common.utils import get_request_ip, get_logger, bulk_get, FlashMessageUtil
@@ -29,27 +30,38 @@ from .const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
 logger = get_logger(__name__)
 
 
-def check_backend_can_auth(username, backend_path, allowed_auth_backends):
-    if allowed_auth_backends is not None and backend_path not in allowed_auth_backends:
-        logger.debug('Skip user auth backend: {}, {} not in'.format(
-            username, backend_path, ','.join(allowed_auth_backends)
-        ))
-        return False
-    return True
+def _get_backends(return_tuples=False):
+    backends = []
+    for backend_path in settings.AUTHENTICATION_BACKENDS:
+        backend = load_backend(backend_path)
+        # 检查 backend 是否启用
+        if not backend.is_enabled():
+            continue
+        backends.append((backend, backend_path) if return_tuples else backend)
+    if not backends:
+        raise ImproperlyConfigured(
+            'No authentication backends have been defined. Does '
+            'AUTHENTICATION_BACKENDS contain anything?'
+        )
+    return backends
+
+
+auth._get_backends = _get_backends
 
 
 def authenticate(request=None, **credentials):
     """
     If the given credentials are valid, return a User object.
+    之所以 hack 这个 auticate
     """
     username = credentials.get('username')
-    allowed_auth_backends = User.get_user_allowed_auth_backends(username)
 
     for backend, backend_path in _get_backends(return_tuples=True):
-        # 预先检查,不浪费认证时间
-        if not check_backend_can_auth(username, backend_path, allowed_auth_backends):
+        # 检查用户名是否允许认证 (预先检查,不浪费认证时间)
+        if not backend.username_allow_authenticate(username):
             continue
 
+        # 原生
         backend_signature = inspect.signature(backend.authenticate)
         try:
             backend_signature.bind(request, **credentials)
@@ -63,21 +75,17 @@ def authenticate(request=None, **credentials):
             break
         if user is None:
             continue
-        # 如果是 None, 证明没有检查过, 需要再次检查
-        if allowed_auth_backends is None:
-            # 有些 authentication 参数中不带 username, 之后还要再检查
-            allowed_auth_backends = user.get_allowed_auth_backends()
-            if not check_backend_can_auth(user.username, backend_path, allowed_auth_backends):
-                continue
+
+        # 检查用户是否允许认证
+        if not backend.user_allow_authenticate(user):
+            continue
 
         # Annotate the user object with the path of the backend.
         user.backend = backend_path
         return user
 
     # The credentials supplied are invalid to all backends, fire signal
-    user_login_failed.send(
-        sender=__name__, credentials=_clean_credentials(credentials), request=request
-    )
+    user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request)
 
 
 auth.authenticate = authenticate
diff --git a/apps/authentication/models.py b/apps/authentication/models.py
index 58c80ee68..1b353f737 100644
--- a/apps/authentication/models.py
+++ b/apps/authentication/models.py
@@ -29,6 +29,9 @@ class AccessKey(models.Model):
     def __str__(self):
         return str(self.id)
 
+    class Meta:
+        verbose_name = _("Access key")
+
 
 class PrivateToken(Token):
     """Inherit from auth token, otherwise migration is boring"""
@@ -45,3 +48,23 @@ class SSOToken(models.JMSBaseModel):
     authkey = models.UUIDField(primary_key=True, default=uuid.uuid4, verbose_name=_('Token'))
     expired = models.BooleanField(default=False, verbose_name=_('Expired'))
     user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name=_('User'), db_constraint=False)
+
+    class Meta:
+        verbose_name = _('SSO token')
+
+
+class ConnectionToken(models.JMSBaseModel):
+    # Todo: 未来可能放到这里,不记录到 redis 了,虽然方便,但是不易于审计
+    # Todo: add connection token 可能要授权给 普通用户, 或者放开就行
+
+    class Meta:
+        verbose_name = _('Connection token')
+        permissions = [
+            ('view_connectiontokensecret', _('Can view connection token secret'))
+        ]
+
+
+class SuperConnectionToken(ConnectionToken):
+    class Meta:
+        proxy = True
+        verbose_name = _("Super connection token")
diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py
index d405e5fb8..f6661ba38 100644
--- a/apps/authentication/serializers.py
+++ b/apps/authentication/serializers.py
@@ -5,7 +5,7 @@ from rest_framework import serializers
 
 from common.utils import get_object_or_none
 from users.models import User
-from assets.models import Asset, SystemUser, Gateway
+from assets.models import Asset, SystemUser, Gateway, Domain, CommandFilterRule
 from applications.models import Application
 from users.serializers import UserProfileSerializer
 from assets.serializers import ProtocolsField
@@ -169,7 +169,7 @@ class ConnectionTokenAssetSerializer(serializers.ModelSerializer):
 class ConnectionTokenSystemUserSerializer(serializers.ModelSerializer):
     class Meta:
         model = SystemUser
-        fields = ['id', 'name', 'username', 'password', 'private_key', 'ad_domain']
+        fields = ['id', 'name', 'username', 'password', 'private_key', 'protocol', 'ad_domain', 'org_id']
 
 
 class ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
@@ -185,9 +185,30 @@ class ConnectionTokenRemoteAppSerializer(serializers.Serializer):
 
 
 class ConnectionTokenApplicationSerializer(serializers.ModelSerializer):
+    attrs = serializers.JSONField(read_only=True)
+
     class Meta:
         model = Application
-        fields = ['id', 'name', 'category', 'type']
+        fields = ['id', 'name', 'category', 'type', 'attrs', 'org_id']
+
+
+class ConnectionTokenDomainSerializer(serializers.ModelSerializer):
+    gateways = ConnectionTokenGatewaySerializer(many=True, read_only=True)
+
+    class Meta:
+        model = Domain
+        fields = ['id', 'name', 'gateways']
+
+
+class ConnectionTokenFilterRuleSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = CommandFilterRule
+        fields = [
+            'id', 'type', 'content', 'ignore_case', 'pattern',
+            'priority', 'action',
+            'date_created',
+        ]
 
 
 class ConnectionTokenSecretSerializer(serializers.Serializer):
@@ -199,6 +220,8 @@ class ConnectionTokenSecretSerializer(serializers.Serializer):
     remote_app = ConnectionTokenRemoteAppSerializer(read_only=True)
     application = ConnectionTokenApplicationSerializer(read_only=True)
     system_user = ConnectionTokenSystemUserSerializer(read_only=True)
+    cmd_filter_rules = ConnectionTokenFilterRuleSerializer(many=True)
+    domain = ConnectionTokenDomainSerializer(read_only=True)
     gateway = ConnectionTokenGatewaySerializer(read_only=True)
     actions = ActionsField()
     expired_at = serializers.IntegerField()
diff --git a/apps/authentication/signals_handlers.py b/apps/authentication/signal_handlers.py
similarity index 87%
rename from apps/authentication/signals_handlers.py
rename to apps/authentication/signal_handlers.py
index a1063186a..5c5aa1abd 100644
--- a/apps/authentication/signals_handlers.py
+++ b/apps/authentication/signal_handlers.py
@@ -6,8 +6,9 @@ from django.core.cache import cache
 from django.dispatch import receiver
 from django_cas_ng.signals import cas_user_authenticated
 
-from jms_oidc_rp.signals import openid_user_login_failed, openid_user_login_success
-
+from authentication.backends.oidc.signals import (
+    openid_user_login_failed, openid_user_login_success
+)
 from authentication.backends.saml2.signals import (
     saml2_user_authenticated, saml2_user_authentication_failed
 )
@@ -16,6 +17,9 @@ from .signals import post_auth_success, post_auth_failed
 
 @receiver(user_logged_in)
 def on_user_auth_login_success(sender, user, request, **kwargs):
+    # 失效 perms 缓存
+    user.expire_perms_cache()
+
     # 开启了 MFA,且没有校验过, 可以全局校验, middleware 中可以全局管理 oidc 等第三方认证的 MFA
     if settings.SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY \
             and user.mfa_enabled \
@@ -24,12 +28,12 @@ def on_user_auth_login_success(sender, user, request, **kwargs):
 
     # 单点登录,超过了自动退出
     if settings.USER_LOGIN_SINGLE_MACHINE_ENABLED:
-        user_id = 'single_machine_login_' + str(user.id)
-        session_key = cache.get(user_id)
+        lock_key = 'single_machine_login_' + str(user.id)
+        session_key = cache.get(lock_key)
         if session_key and session_key != request.session.session_key:
             session = import_module(settings.SESSION_ENGINE).SessionStore(session_key)
             session.delete()
-        cache.set(user_id, request.session.session_key, None)
+        cache.set(lock_key, request.session.session_key, None)
 
 
 @receiver(openid_user_login_success)
diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html
index b74217cee..3e24aa610 100644
--- a/apps/authentication/templates/authentication/login.html
+++ b/apps/authentication/templates/authentication/login.html
@@ -115,10 +115,21 @@
         .mfa-div {
             width: 100%;
         }
+
+        .login-page-language {
+            margin-right: -11px !important;
+            padding-top: 12px !important;
+            padding-left: 0 !important;
+            padding-bottom: 8px !important;
+            color: #666 !important;
+            font-weight: 350 !important;
+            min-height: auto !important;
+        }
     </style>
 </head>
 
 <body>
+
 <div class="login-content">
     <div class="right-image-box">
         <a href="{% if not XPACK_ENABLED %}https://github.com/jumpserver/jumpserver{% endif %}">
@@ -127,6 +138,22 @@
     </div>
     <div class="left-form-box {% if not form.challenge and not form.captcha %} no-captcha-challenge {% endif %}">
         <div style="background-color: white">
+            <ul class="nav navbar-top-links navbar-right">
+                <li class="dropdown">
+                    <a class="dropdown-toggle login-page-language" data-toggle="dropdown" href="#" target="_blank">
+                        <i class="fa fa-globe fa-lg" style="margin-right: 2px"></i>
+                        {% ifequal request.COOKIES.django_language 'en' %}
+                            <span>English<b class="caret"></b></span>
+                        {% else %}
+                            <span>中文(简体)<b class="caret"></b></span>
+                        {% endifequal %}
+                    </a>
+                    <ul class="dropdown-menu profile-dropdown dropdown-menu-right">
+                        <li> <a id="switch_cn" href="{% url 'i18n-switch' lang='zh-hans' %}"> <span>中文(简体)</span> </a> </li>
+                        <li> <a id="switch_en" href="{% url 'i18n-switch' lang='en' %}"> <span>English</span> </a> </li>
+                    </ul>
+                </li>
+            </ul>
             <div class="jms-title">
                 <span style="font-size: 21px;font-weight:400;color: #151515;letter-spacing: 0;">{{ JMS_TITLE }}</span>
             </div>
diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py
index 53944f759..ce0d3c647 100644
--- a/apps/authentication/urls/view_urls.py
+++ b/apps/authentication/urls/view_urls.py
@@ -55,7 +55,7 @@ urlpatterns = [
 
     # openid
     path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')),
-    path('openid/', include(('jms_oidc_rp.urls', 'authentication'), namespace='openid')),
+    path('openid/', include(('authentication.backends.oidc.urls', 'authentication'), namespace='openid')),
     path('saml2/', include(('authentication.backends.saml2.urls', 'authentication'), namespace='saml2')),
     path('captcha/', include('captcha.urls')),
 ]
diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py
index 545da111f..ee831eb6c 100644
--- a/apps/authentication/views/login.py
+++ b/apps/authentication/views/login.py
@@ -184,9 +184,7 @@ class UserLoginView(mixins.AuthMixin, FormView):
     @staticmethod
     def get_forgot_password_url():
         forgot_password_url = reverse('authentication:forgot-password')
-        has_other_auth_backend = settings.AUTHENTICATION_BACKENDS[0] != settings.AUTH_BACKEND_MODEL
-        if has_other_auth_backend and settings.FORGOT_PASSWORD_URL:
-            forgot_password_url = settings.FORGOT_PASSWORD_URL
+        forgot_password_url = settings.FORGOT_PASSWORD_URL or forgot_password_url
         return forgot_password_url
 
     def get_context_data(self, **kwargs):
diff --git a/apps/common/apps.py b/apps/common/apps.py
index 998881866..2a5799d10 100644
--- a/apps/common/apps.py
+++ b/apps/common/apps.py
@@ -8,7 +8,7 @@ class CommonConfig(AppConfig):
     name = 'common'
 
     def ready(self):
-        from . import signals_handlers
+        from . import signal_handlers
         from .signals import django_ready
         if 'migrate' in sys.argv or 'compilemessages' in sys.argv:
             return
diff --git a/apps/common/const/signals.py b/apps/common/const/signals.py
index 5d35518ab..690a30161 100644
--- a/apps/common/const/signals.py
+++ b/apps/common/const/signals.py
@@ -2,7 +2,7 @@
 `m2m_changed`
 
 ```
-def m2m_signals_handler(action, instance, reverse, model, pk_set, using):
+def m2m_signal_handler(action, instance, reverse, model, pk_set, using):
      pass
 ```
 """
diff --git a/apps/common/management/commands/expire_caches.py b/apps/common/management/commands/expire_caches.py
index 37fe7605b..bc20a3cf5 100644
--- a/apps/common/management/commands/expire_caches.py
+++ b/apps/common/management/commands/expire_caches.py
@@ -1,6 +1,6 @@
 from django.core.management.base import BaseCommand
 
-from assets.signals_handler.node_assets_mapping import expire_node_assets_mapping_for_memory
+from assets.signal_handlers.node_assets_mapping import expire_node_assets_mapping_for_memory
 from orgs.caches import OrgResourceStatisticsCache
 from orgs.models import Organization
 
diff --git a/apps/common/management/commands/services/command.py b/apps/common/management/commands/services/command.py
index 170eb69ae..dd2cd9cdb 100644
--- a/apps/common/management/commands/services/command.py
+++ b/apps/common/management/commands/services/command.py
@@ -52,7 +52,7 @@ class Services(TextChoices):
 
     @classmethod
     def export_services_values(cls):
-        return [cls.all.value, cls.web.value, cls.task.value]
+        return [cls.all.value, cls.web.value, cls.task.value] + [s.value for s in cls.all_services()]
 
     @classmethod
     def get_service_objects(cls, service_names, **kwargs):
diff --git a/apps/common/management/commands/services/services/celery_base.py b/apps/common/management/commands/services/services/celery_base.py
index 5542fd72f..435e4ebe2 100644
--- a/apps/common/management/commands/services/services/celery_base.py
+++ b/apps/common/management/commands/services/services/celery_base.py
@@ -23,9 +23,10 @@ class CeleryBaseService(BaseService):
             server_hostname = '%h'
 
         cmd = [
-            'celery', 'worker',
-            '-P', 'threads',
+            'celery',
             '-A', 'ops',
+            'worker',
+            '-P', 'threads',
             '-l', 'INFO',
             '-c', str(self.num),
             '-Q', self.queue,
diff --git a/apps/common/management/commands/services/services/flower.py b/apps/common/management/commands/services/services/flower.py
index df2230776..402e9f37d 100644
--- a/apps/common/management/commands/services/services/flower.py
+++ b/apps/common/management/commands/services/services/flower.py
@@ -16,8 +16,9 @@ class FlowerService(BaseService):
         if os.getuid() == 0:
             os.environ.setdefault('C_FORCE_ROOT', '1')
         cmd = [
-            'celery', 'flower',
+            'celery',
             '-A', 'ops',
+            'flower',
             '-l', 'INFO',
             '--url_prefix=/core/flower',
             '--auto_refresh=False',
diff --git a/apps/common/mixins/api/action.py b/apps/common/mixins/api/action.py
index 994ade06b..96b01eb08 100644
--- a/apps/common/mixins/api/action.py
+++ b/apps/common/mixins/api/action.py
@@ -8,7 +8,6 @@ from rest_framework.decorators import action
 from rest_framework.request import Request
 
 from common.const.http import POST
-from common.permissions import IsValidUser
 
 
 __all__ = ['SuggestionMixin', 'RenderToJsonMixin']
@@ -23,8 +22,8 @@ class SuggestionMixin:
     get_serializer: Callable
     get_paginated_response: Callable
 
-    @action(methods=['get'], detail=False, permission_classes=(IsValidUser,))
-    def suggestions(self, request, *args, **kwargs):
+    @action(methods=['get'], detail=False, url_path='suggestions')
+    def match(self, request, *args, **kwargs):
         queryset = self.filter_queryset(self.get_queryset())
         queryset = queryset[:self.suggestion_limit]
         page = self.paginate_queryset(queryset)
diff --git a/apps/common/mixins/api/permission.py b/apps/common/mixins/api/permission.py
index 6ffa15e53..64efbe7f5 100644
--- a/apps/common/mixins/api/permission.py
+++ b/apps/common/mixins/api/permission.py
@@ -25,6 +25,9 @@ class RoleAdminMixin:
     @lazyproperty
     def user(self):
         user_id = self.kwargs.get(self.user_id_url_kwarg)
+        if hasattr(self, 'swagger_fake_view') and not user_id:
+            return self.request.user  # NOQA
+
         user_model = get_user_model()
         return user_model.objects.get(id=user_id)
 
diff --git a/apps/common/mixins/views.py b/apps/common/mixins/views.py
index f167d2001..4fc1dbf3c 100644
--- a/apps/common/mixins/views.py
+++ b/apps/common/mixins/views.py
@@ -2,8 +2,11 @@
 #
 from django.contrib.auth.mixins import UserPassesTestMixin
 from rest_framework import permissions
+from rest_framework.decorators import action
 from rest_framework.request import Request
+from rest_framework.response import Response
 
+from common.permissions import IsValidUser
 
 __all__ = ["PermissionsMixin"]
 
@@ -21,5 +24,3 @@ class PermissionsMixin(UserPassesTestMixin):
             if not permission_class().has_permission(self.request, self):
                 return False
         return True
-
-
diff --git a/apps/common/permissions.py b/apps/common/permissions.py
index bd9a1e030..b52c0cc5c 100644
--- a/apps/common/permissions.py
+++ b/apps/common/permissions.py
@@ -5,9 +5,6 @@ from rest_framework import permissions
 from django.conf import settings
 from common.exceptions import MFAVerifyRequired
 
-from orgs.utils import current_org
-from common.utils import is_uuid
-
 
 class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
     """Allows access to valid user, is active and not expired"""
@@ -17,80 +14,12 @@ class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
                and request.user.is_valid
 
 
-class IsAppUser(IsValidUser):
-    """Allows access only to app user """
-
+class OnlySuperUser(IsValidUser):
     def has_permission(self, request, view):
-        return super(IsAppUser, self).has_permission(request, view) \
-               and request.user.is_app
-
-
-class IsSuperUser(IsValidUser):
-    def has_permission(self, request, view):
-        return super(IsSuperUser, self).has_permission(request, view) \
+        return super().has_permission(request, view) \
                and request.user.is_superuser
 
 
-class IsSuperUserOrAppUser(IsSuperUser):
-    def has_permission(self, request, view):
-        if request.user.is_anonymous:
-            return False
-        return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
-               or request.user.is_app
-
-
-class IsSuperAuditor(IsValidUser):
-    def has_permission(self, request, view):
-        return super(IsSuperAuditor, self).has_permission(request, view) \
-               and request.user.is_super_auditor
-
-
-class IsOrgAuditor(IsValidUser):
-    def has_permission(self, request, view):
-        if not current_org:
-            return False
-        return super(IsOrgAuditor, self).has_permission(request, view) \
-               and current_org.can_audit_by(request.user)
-
-
-class IsOrgAdmin(IsValidUser):
-    """Allows access only to superuser"""
-
-    def has_permission(self, request, view):
-        if not current_org:
-            return False
-        return super(IsOrgAdmin, self).has_permission(request, view) \
-               and current_org.can_admin_by(request.user)
-
-
-class IsOrgAdminOrAppUser(IsValidUser):
-    """Allows access between superuser and app user"""
-
-    def has_permission(self, request, view):
-        if not current_org:
-            return False
-        if request.user.is_anonymous:
-            return False
-        return super(IsOrgAdminOrAppUser, self).has_permission(request, view) \
-               and (current_org.can_admin_by(request.user) or request.user.is_app)
-
-
-class IsOrgAdminOrAppUserOrUserReadonly(IsOrgAdminOrAppUser):
-    def has_permission(self, request, view):
-        if IsValidUser.has_permission(self, request, view) \
-                and request.method in permissions.SAFE_METHODS:
-            return True
-        else:
-            return IsOrgAdminOrAppUser.has_permission(self, request, view)
-
-
-class IsCurrentUserOrReadOnly(permissions.BasePermission):
-    def has_object_permission(self, request, view, obj):
-        if request.method in permissions.SAFE_METHODS:
-            return True
-        return obj == request.user
-
-
 class WithBootstrapToken(permissions.BasePermission):
     def has_permission(self, request, view):
         authorization = request.META.get('HTTP_AUTHORIZATION', '')
@@ -100,21 +29,6 @@ class WithBootstrapToken(permissions.BasePermission):
         return settings.BOOTSTRAP_TOKEN == request_bootstrap_token
 
 
-class UserCanAnyPermCurrentOrg(permissions.BasePermission):
-    def has_permission(self, request, view):
-        return current_org.can_any_by(request.user)
-
-
-class UserCanUpdatePassword(permissions.BasePermission):
-    def has_permission(self, request, view):
-        return request.user.can_update_password()
-
-
-class UserCanUpdateSSHKey(permissions.BasePermission):
-    def has_permission(self, request, view):
-        return request.user.can_update_ssh_key()
-
-
 class NeedMFAVerify(permissions.BasePermission):
     def has_permission(self, request, view):
         if not settings.SECURITY_VIEW_AUTH_NEED_MFA:
@@ -126,80 +40,7 @@ class NeedMFAVerify(permissions.BasePermission):
         raise MFAVerifyRequired()
 
 
-class CanUpdateDeleteUser(permissions.BasePermission):
-
-    @staticmethod
-    def has_delete_object_permission(request, view, obj):
-        if request.user.is_anonymous:
-            return False
-        if not request.user.can_admin_current_org:
-            return False
-        # 超级管理员 / 组织管理员
-        if str(request.user.id) == str(obj.id):
-            return False
-        # 超级管理员
-        if request.user.is_superuser:
-            if obj.is_superuser and obj.username in ['admin']:
-                return False
-            return True
-        # 组织管理员
-        if obj.is_superuser:
-            return False
-        if obj.is_super_auditor:
-            return False
-        if obj.can_admin_current_org:
-            return False
-        return True
-
-    @staticmethod
-    def has_update_object_permission(request, view, obj):
-        if request.user.is_anonymous:
-            return False
-        if not request.user.can_admin_current_org:
-            return False
-        # 超级管理员 / 组织管理员
-        if str(request.user.id) == str(obj.id):
-            return True
-        # 超级管理员
-        if request.user.is_superuser:
-            return True
-        # 组织管理员
-        if obj.is_superuser:
-            return False
-        if obj.is_super_auditor:
-            return False
-        return True
-
-    def has_object_permission(self, request, view, obj):
-        if request.user.is_anonymous:
-            return False
-        if not request.user.can_admin_current_org:
-            return False
-        if request.method in ['DELETE']:
-            return self.has_delete_object_permission(request, view, obj)
-        if request.method in ['PUT', 'PATCH']:
-            return self.has_update_object_permission(request, view, obj)
-        return True
-
-
 class IsObjectOwner(IsValidUser):
     def has_object_permission(self, request, view, obj):
         return (super().has_object_permission(request, view, obj) and
                 request.user == getattr(obj, 'user', None))
-
-
-class HasQueryParamsUserAndIsCurrentOrgMember(permissions.BasePermission):
-    def has_permission(self, request, view):
-        query_user_id = request.query_params.get('user')
-        if not query_user_id or not is_uuid(query_user_id):
-            return False
-        query_user = current_org.get_members().filter(id=query_user_id).first()
-        return bool(query_user)
-
-
-class OnlySuperUserCanList(IsValidUser):
-    def has_permission(self, request, view):
-        user = request.user
-        if view.action == 'list' and not user.is_superuser:
-            return False
-        return True
diff --git a/apps/common/signals_handlers.py b/apps/common/signal_handlers.py
similarity index 100%
rename from apps/common/signals_handlers.py
rename to apps/common/signal_handlers.py
diff --git a/apps/common/tree.py b/apps/common/tree.py
index 8d5e40e5b..d4ffa47bf 100644
--- a/apps/common/tree.py
+++ b/apps/common/tree.py
@@ -15,6 +15,7 @@ class TreeNode:
     iconSkin = ""
     parentInfo = ''
     meta = {}
+    checked = False
 
     _tree = None
 
@@ -101,4 +102,7 @@ class TreeNodeSerializer(serializers.Serializer):
     open = serializers.BooleanField(default=False)
     iconSkin = serializers.CharField(max_length=128, allow_blank=True)
     nocheck = serializers.BooleanField(default=False)
+    checked = serializers.BooleanField(default=False)
+    halfCheck = serializers.BooleanField(default=False)
+    chkDisabled = serializers.BooleanField(default=False)
     meta = serializers.JSONField()
diff --git a/apps/common/utils/connection.py b/apps/common/utils/connection.py
index a19b04f85..22625c8ad 100644
--- a/apps/common/utils/connection.py
+++ b/apps/common/utils/connection.py
@@ -55,9 +55,9 @@ class Subscription:
                         _next(item)
                 except Exception as e:
                     error(msg, item)
-                    logger.error('Subscribe handler handle msg error: ', e)
+                    logger.error('Subscribe handler handle msg error: {}'.format(e))
         except Exception as e:
-            logger.error('Consume msg error: ', e)
+            logger.error('Consume msg error: {}'.format(e))
 
         try:
             complete()
@@ -100,4 +100,4 @@ class RedisPubSub:
         self.redis.publish(self.ch, data_json)
         return True
 
-    
+
diff --git a/apps/jumpserver/api.py b/apps/jumpserver/api.py
index 3e7c4f19c..eb09f7214 100644
--- a/apps/jumpserver/api.py
+++ b/apps/jumpserver/api.py
@@ -15,8 +15,7 @@ from assets.models import Asset
 from terminal.models import Session
 from terminal.utils import ComponentsPrometheusMetricsUtil
 from orgs.utils import current_org
-from common.permissions import IsOrgAdmin, IsOrgAuditor
-from common.utils import lazyproperty, get_request_ip
+from common.utils import lazyproperty
 from orgs.caches import OrgResourceStatisticsCache
 
 
@@ -213,8 +212,10 @@ class DatesLoginMetricMixin:
 
 
 class IndexApi(DatesLoginMetricMixin, APIView):
-    permission_classes = (IsOrgAdmin | IsOrgAuditor,)
     http_method_names = ['get']
+    rbac_perms = {
+        'GET': 'rbac.view_dashboard'
+    }
 
     def get(self, request, *args, **kwargs):
         data = {}
diff --git a/apps/jumpserver/context_processor.py b/apps/jumpserver/context_processor.py
index f714759f0..54a7b856e 100644
--- a/apps/jumpserver/context_processor.py
+++ b/apps/jumpserver/context_processor.py
@@ -2,7 +2,7 @@
 #
 from django.templatetags.static import static
 from django.conf import settings
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext_lazy as _
 
 default_context = {
     'DEFAULT_PK': '00000000-0000-0000-0000-000000000000',
diff --git a/apps/jumpserver/settings/_xpack.py b/apps/jumpserver/settings/_xpack.py
index 9f4319a35..322740201 100644
--- a/apps/jumpserver/settings/_xpack.py
+++ b/apps/jumpserver/settings/_xpack.py
@@ -6,6 +6,7 @@ from .. import const
 from .base import INSTALLED_APPS, TEMPLATES
 
 XPACK_DIR = os.path.join(const.BASE_DIR, 'xpack')
+# XPACK_ENABLED = False
 XPACK_ENABLED = os.path.isdir(XPACK_DIR)
 XPACK_TEMPLATES_DIR = []
 XPACK_CONTEXT_PROCESSOR = []
diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py
index 883f63d07..c545772e1 100644
--- a/apps/jumpserver/settings/auth.py
+++ b/apps/jumpserver/settings/auth.py
@@ -74,6 +74,13 @@ AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
 AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
 AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
 AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
+# Other default
+AUTH_OPENID_STATE_LENGTH = 32
+AUTH_OPENID_NONCE_LENGTH = 32
+AUTH_OPENID_AUTHENTICATION_REDIRECT_URI = '/'
+AUTH_OPENID_AUTHENTICATION_FAILURE_REDIRECT_URI = '/'
+AUTH_OPENID_PROVIDER_END_SESSION_REDIRECT_URI_PARAMETER = 'post_logout_redirect_uri'
+AUTH_OPENID_PROVIDER_END_SESSION_ID_TOKEN_PARAMETER = 'id_token_hint'
 # ==============================================================================
 
 # Radius Auth
@@ -138,38 +145,35 @@ TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
 OTP_IN_RADIUS = CONFIG.OTP_IN_RADIUS
 
 
-AUTH_BACKEND_MODEL = 'authentication.backends.api.JMSModelBackend'
+RBAC_BACKEND = 'rbac.backends.RBACBackend'
+AUTH_BACKEND_MODEL = 'authentication.backends.base.JMSModelBackend'
 AUTH_BACKEND_PUBKEY = 'authentication.backends.pubkey.PublicKeyAuthBackend'
 AUTH_BACKEND_LDAP = 'authentication.backends.ldap.LDAPAuthorizationBackend'
-AUTH_BACKEND_OIDC_PASSWORD = 'jms_oidc_rp.backends.OIDCAuthPasswordBackend'
-AUTH_BACKEND_OIDC_CODE = 'jms_oidc_rp.backends.OIDCAuthCodeBackend'
+AUTH_BACKEND_OIDC_PASSWORD = 'authentication.backends.oidc.OIDCAuthPasswordBackend'
+AUTH_BACKEND_OIDC_CODE = 'authentication.backends.oidc.OIDCAuthCodeBackend'
 AUTH_BACKEND_RADIUS = 'authentication.backends.radius.RadiusBackend'
 AUTH_BACKEND_CAS = 'authentication.backends.cas.CASBackend'
-AUTH_BACKEND_SSO = 'authentication.backends.api.SSOAuthentication'
-AUTH_BACKEND_WECOM = 'authentication.backends.api.WeComAuthentication'
-AUTH_BACKEND_DINGTALK = 'authentication.backends.api.DingTalkAuthentication'
-AUTH_BACKEND_FEISHU = 'authentication.backends.api.FeiShuAuthentication'
-AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.api.AuthorizationTokenAuthentication'
+AUTH_BACKEND_SSO = 'authentication.backends.sso.SSOAuthentication'
+AUTH_BACKEND_WECOM = 'authentication.backends.sso.WeComAuthentication'
+AUTH_BACKEND_DINGTALK = 'authentication.backends.sso.DingTalkAuthentication'
+AUTH_BACKEND_FEISHU = 'authentication.backends.sso.FeiShuAuthentication'
+AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.sso.AuthorizationTokenAuthentication'
 AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend'
 
 
 AUTHENTICATION_BACKENDS = [
-    AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_WECOM,
-    AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU, AUTH_BACKEND_AUTH_TOKEN,
-    AUTH_BACKEND_SSO,
+    # 只做权限校验
+    RBAC_BACKEND,
+    # 密码形式
+    AUTH_BACKEND_MODEL,  AUTH_BACKEND_PUBKEY, AUTH_BACKEND_LDAP, AUTH_BACKEND_RADIUS,
+    # 跳转形式
+    AUTH_BACKEND_CAS, AUTH_BACKEND_OIDC_PASSWORD, AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_SAML2,
+    # 扫码模式
+    AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU,
+    # Token模式
+    AUTH_BACKEND_AUTH_TOKEN, AUTH_BACKEND_SSO,
 ]
 
-if AUTH_CAS:
-    AUTHENTICATION_BACKENDS.insert(0, AUTH_BACKEND_CAS)
-if AUTH_OPENID:
-    AUTHENTICATION_BACKENDS.insert(0, AUTH_BACKEND_OIDC_PASSWORD)
-    AUTHENTICATION_BACKENDS.insert(0, AUTH_BACKEND_OIDC_CODE)
-if AUTH_RADIUS:
-    AUTHENTICATION_BACKENDS.insert(0, AUTH_BACKEND_RADIUS)
-if AUTH_SAML2:
-    AUTHENTICATION_BACKENDS.insert(0, AUTH_BACKEND_SAML2)
-
-
 ONLY_ALLOW_EXIST_USER_AUTH = CONFIG.ONLY_ALLOW_EXIST_USER_AUTH
 ONLY_ALLOW_AUTH_FROM_SOURCE = CONFIG.ONLY_ALLOW_AUTH_FROM_SOURCE
 
diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py
index fd8feeaad..d81089a6d 100644
--- a/apps/jumpserver/settings/base.py
+++ b/apps/jumpserver/settings/base.py
@@ -1,4 +1,10 @@
 import os
+import platform
+
+if platform.system() == 'Darwin' and platform.machine() == 'arm64':
+    import pymysql
+    pymysql.version_info = (1, 4, 2, "final", 0)
+    pymysql.install_as_MySQLdb()
 
 from django.urls import reverse_lazy
 
@@ -49,6 +55,7 @@ INSTALLED_APPS = [
     'tickets.apps.TicketsConfig',
     'acls.apps.AclsConfig',
     'notifications.apps.NotificationsConfig',
+    'rbac.apps.RBACConfig',
     'common.apps.CommonConfig',
     'jms_oidc_rp',
     'rest_framework',
@@ -67,10 +74,9 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'django.forms',
-    'simple_history',
+    'simple_history',  # 这个要放到最后,别特么瞎改顺序
 ]
 
-
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -91,7 +97,6 @@ MIDDLEWARE = [
     'simple_history.middleware.HistoryRequestMiddleware',
 ]
 
-
 ROOT_URLCONF = 'jumpserver.urls'
 
 TEMPLATES = [
@@ -111,7 +116,6 @@ TEMPLATES = [
                 'django.template.context_processors.media',
                 'jumpserver.context_processor.jumpserver_processor',
                 'orgs.context_processor.org_processor',
-                'jms_oidc_rp.context_processors.oidc',
             ],
         },
     },
@@ -157,13 +161,14 @@ DATABASES = {
         'OPTIONS': DB_OPTIONS
     }
 }
+
+
 DB_CA_PATH = os.path.join(PROJECT_DIR, 'data', 'certs', 'db_ca.pem')
 if CONFIG.DB_ENGINE.lower() == 'mysql':
     DB_OPTIONS['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
     if os.path.isfile(DB_CA_PATH):
         DB_OPTIONS['ssl'] = {'ca': DB_CA_PATH}
 
-
 # Password validation
 # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
 #
@@ -233,7 +238,6 @@ EMAIL_RECIPIENT = CONFIG.EMAIL_RECIPIENT
 EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
 EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
 
-
 # Custom User Auth model
 AUTH_USER_MODEL = 'users.User'
 
diff --git a/apps/jumpserver/settings/libs.py b/apps/jumpserver/settings/libs.py
index 9c67a15e8..eb7b5a9eb 100644
--- a/apps/jumpserver/settings/libs.py
+++ b/apps/jumpserver/settings/libs.py
@@ -7,7 +7,7 @@ REST_FRAMEWORK = {
     # Use Django's standard `django.contrib.auth` permissions,
     # or allow read-only access for unauthenticated users.
     'DEFAULT_PERMISSION_CLASSES': (
-        'common.permissions.IsSuperUser',
+        'rbac.permissions.RBACPermission',
     ),
     'DEFAULT_RENDERER_CLASSES': (
         'rest_framework.renderers.JSONRenderer',
@@ -26,11 +26,11 @@ REST_FRAMEWORK = {
     ),
     'DEFAULT_AUTHENTICATION_CLASSES': (
         # 'rest_framework.authentication.BasicAuthentication',
-        'authentication.backends.api.AccessKeyAuthentication',
-        'authentication.backends.api.AccessTokenAuthentication',
-        'authentication.backends.api.PrivateTokenAuthentication',
-        'authentication.backends.api.SignatureAuthentication',
-        'authentication.backends.api.SessionAuthentication',
+        'authentication.backends.drf.AccessKeyAuthentication',
+        'authentication.backends.drf.AccessTokenAuthentication',
+        'authentication.backends.drf.PrivateTokenAuthentication',
+        'authentication.backends.drf.SignatureAuthentication',
+        'authentication.backends.drf.SessionAuthentication',
     ),
     'DEFAULT_FILTER_BACKENDS': (
         'django_filters.rest_framework.DjangoFilterBackend',
diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py
index ea41cc7ee..6a9ae69a9 100644
--- a/apps/jumpserver/urls.py
+++ b/apps/jumpserver/urls.py
@@ -24,6 +24,7 @@ api_v1 = [
     path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
     path('acls/', include('acls.urls.api_urls', namespace='api-acls')),
     path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')),
+    path('rbac/', include('rbac.urls.api_urls', namespace='api-rbac')),
     path('prometheus/metrics/', api.PrometheusMetricsApi.as_view()),
 ]
 
@@ -32,7 +33,8 @@ app_view_patterns = [
     path('ops/', include('ops.urls.view_urls'), name='ops'),
     path('common/', include('common.urls.view_urls'), name='common'),
     re_path(r'flower/(?P<path>.*)', views.celery_flower_view, name='flower-view'),
-    path('download/', views.ResourceDownload.as_view(), name='download')
+    path('download/', views.ResourceDownload.as_view(), name='download'),
+    path('i18n/<str:lang>/', views.I18NView.as_view(), name='i18n-switch'),
 ]
 
 if settings.XPACK_ENABLED:
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
index 8611f2cc2..a0b66d917 100644
--- a/apps/locale/zh/LC_MESSAGES/django.mo
+++ b/apps/locale/zh/LC_MESSAGES/django.mo
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:67a04954f2a6c1da8c2f40307166a87ba1088e858422378eb638a1d1aafc5a89
-size 97595
+oid sha256:a885732955761c2942989a3e93751709e2be4ec75504bd009406671b93e0bfda
+size 107544
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index a4edb3f6b..4ebc1fe65 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/zh/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: JumpServer 0.3.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-02-15 17:52+0800\n"
+"POT-Creation-Date: 2022-03-10 20:12+0800\n"
 "PO-Revision-Date: 2021-05-20 10:54+0800\n"
 "Last-Translator: ibuler <ibuler@qq.com>\n"
 "Language-Team: JumpServer team<ibuler@qq.com>\n"
@@ -17,15 +17,20 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Poedit 2.4.3\n"
 
+#: acls/apps.py:7
+msgid "Acls"
+msgstr "访问控制"
+
 #: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
-#: applications/models/application.py:202 assets/models/asset.py:139
+#: applications/models/application.py:202 assets/models/asset.py:138
 #: assets/models/base.py:175 assets/models/cluster.py:18
-#: assets/models/cmd_filter.py:27 assets/models/domain.py:24
+#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
-#: orgs/models.py:24 perms/models/base.py:83 settings/models.py:29
-#: settings/serializers/sms.py:6 terminal/models/storage.py:23
-#: terminal/models/task.py:16 terminal/models/terminal.py:100
-#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:549
+#: orgs/models.py:12 perms/models/base.py:83 rbac/models/role.py:29
+#: settings/models.py:29 settings/serializers/sms.py:6
+#: terminal/models/storage.py:23 terminal/models/task.py:16
+#: terminal/models/terminal.py:100 users/forms/profile.py:32
+#: users/models/group.py:15 users/models/user.py:574
 #: users/templates/users/_select_user_modal.html:13
 #: users/templates/users/user_asset_permission.html:37
 #: users/templates/users/user_asset_permission.html:154
@@ -46,21 +51,22 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
 
 #: acls/models/base.py:31 authentication/models.py:17
 #: authentication/templates/authentication/_access_key_modal.html:32
-#: perms/models/base.py:88 terminal/models/sharing.py:24
+#: perms/models/base.py:88 terminal/models/sharing.py:26
 #: users/templates/users/_select_user_modal.html:18
 msgid "Active"
 msgstr "激活中"
 
 #: acls/models/base.py:32 applications/models/application.py:215
-#: assets/models/asset.py:144 assets/models/asset.py:232
+#: assets/models/asset.py:143 assets/models/asset.py:231
 #: assets/models/backup.py:54 assets/models/base.py:180
 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:48
-#: assets/models/cmd_filter.py:96 assets/models/domain.py:25
-#: assets/models/domain.py:65 assets/models/group.py:23
-#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:27
-#: perms/models/base.py:93 settings/models.py:34 terminal/models/storage.py:26
-#: terminal/models/terminal.py:114 tickets/models/ticket.py:75
-#: users/models/group.py:16 users/models/user.py:585
+#: assets/models/cmd_filter.py:96 assets/models/domain.py:24
+#: assets/models/domain.py:64 assets/models/group.py:23
+#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:15
+#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:34
+#: terminal/models/storage.py:26 terminal/models/terminal.py:114
+#: tickets/models/comment.py:24 tickets/models/ticket.py:154
+#: users/models/group.py:16 users/models/user.py:611
 #: xpack/plugins/change_auth_plan/models/base.py:44
 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:113
 #: xpack/plugins/gathered_user/models.py:26
@@ -82,15 +88,14 @@ msgstr "登录复核"
 
 #: acls/models/login_acl.py:24 acls/models/login_asset_acl.py:20
 #: assets/models/cmd_filter.py:30 assets/models/label.py:15 audits/models.py:37
-#: audits/models.py:57 audits/models.py:79 audits/serializers.py:100
-#: authentication/models.py:47 orgs/models.py:19 orgs/models.py:433
-#: perms/models/base.py:84 templates/index.html:78
+#: audits/models.py:60 audits/models.py:85 audits/serializers.py:100
+#: authentication/models.py:50 orgs/models.py:196 perms/models/base.py:84
+#: rbac/builtin.py:96 rbac/models/rolebinding.py:35 templates/index.html:78
 #: terminal/backends/command/models.py:19
-#: terminal/backends/command/serializers.py:12 terminal/models/session.py:40
-#: terminal/notifications.py:90 terminal/notifications.py:138
-#: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:169
-#: users/models/user.py:756 users/models/user.py:782
-#: users/serializers/group.py:19
+#: terminal/backends/command/serializers.py:12 terminal/models/session.py:42
+#: terminal/notifications.py:88 terminal/notifications.py:136
+#: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:793
+#: users/models/user.py:824 users/serializers/group.py:19
 #: users/templates/users/user_asset_permission.html:38
 #: users/templates/users/user_asset_permission.html:64
 #: users/templates/users/user_database_app_permission.html:37
@@ -104,7 +109,7 @@ msgstr "规则"
 
 #: acls/models/login_acl.py:31 acls/models/login_asset_acl.py:26
 #: acls/serializers/login_acl.py:17 acls/serializers/login_asset_acl.py:75
-#: assets/models/cmd_filter.py:89 audits/models.py:58 audits/serializers.py:51
+#: assets/models/cmd_filter.py:89 audits/models.py:61 audits/serializers.py:51
 #: authentication/templates/authentication/_access_key_modal.html:34
 #: users/templates/users/_granted_assets.html:29
 #: users/templates/users/user_asset_permission.html:44
@@ -124,23 +129,24 @@ msgstr "登录访问控制"
 
 #: acls/models/login_asset_acl.py:21
 #: applications/serializers/application.py:122
-#: applications/serializers/application.py:166
+#: applications/serializers/application.py:167
 msgid "System User"
 msgstr "系统用户"
 
 #: acls/models/login_asset_acl.py:22
 #: applications/serializers/attrs/application_category/remote_app.py:36
-#: assets/models/asset.py:356 assets/models/authbook.py:19
+#: assets/models/asset.py:355 assets/models/authbook.py:19
 #: assets/models/backup.py:31 assets/models/cmd_filter.py:38
 #: assets/models/gathered_user.py:14 assets/serializers/system_user.py:264
-#: audits/models.py:39 perms/models/asset_permission.py:24
+#: audits/models.py:39 perms/models/asset_permission.py:23
 #: templates/index.html:82 terminal/backends/command/models.py:20
-#: terminal/backends/command/serializers.py:13 terminal/models/session.py:42
-#: terminal/notifications.py:89
+#: terminal/backends/command/serializers.py:13 terminal/models/session.py:44
+#: terminal/notifications.py:87
 #: users/templates/users/user_asset_permission.html:40
 #: users/templates/users/user_asset_permission.html:70
 #: xpack/plugins/change_auth_plan/models/asset.py:199
-#: xpack/plugins/cloud/models.py:217
+#: xpack/plugins/change_auth_plan/serializers/asset.py:180
+#: xpack/plugins/cloud/models.py:220
 msgid "Asset"
 msgstr "资产"
 
@@ -158,11 +164,11 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
 
 #: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
 #: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
-#: assets/models/gathered_user.py:15 audits/models.py:110
+#: assets/models/gathered_user.py:15 audits/models.py:119
 #: authentication/forms.py:15 authentication/forms.py:17
 #: authentication/templates/authentication/_msg_different_city.html:9
 #: authentication/templates/authentication/_msg_oauth_bind.html:9
-#: ops/models/adhoc.py:154 users/forms/profile.py:31 users/models/user.py:547
+#: ops/models/adhoc.py:159 users/forms/profile.py:31 users/models/user.py:572
 #: users/templates/users/_msg_user_created.html:12
 #: users/templates/users/_select_user_modal.html:14
 #: xpack/plugins/change_auth_plan/models/asset.py:34
@@ -182,8 +188,8 @@ msgstr ""
 
 #: acls/serializers/login_asset_acl.py:31 acls/serializers/rules/rules.py:33
 #: applications/serializers/attrs/application_type/mysql_workbench.py:17
-#: assets/models/asset.py:211 assets/models/domain.py:61
-#: assets/serializers/account.py:12
+#: assets/models/asset.py:210 assets/models/domain.py:60
+#: assets/serializers/account.py:13
 #: authentication/templates/authentication/_msg_oauth_bind.html:12
 #: authentication/templates/authentication/_msg_rest_password_success.html:8
 #: authentication/templates/authentication/_msg_rest_public_key_success.html:8
@@ -193,8 +199,8 @@ msgstr ""
 msgid "IP"
 msgstr "IP"
 
-#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:212
-#: assets/serializers/account.py:13 assets/serializers/gathered_user.py:23
+#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:211
+#: assets/serializers/account.py:14 assets/serializers/gathered_user.py:23
 #: settings/serializers/terminal.py:7
 #: users/templates/users/_granted_assets.html:25
 #: users/templates/users/user_asset_permission.html:157
@@ -207,8 +213,8 @@ msgid ""
 "options: {}"
 msgstr "格式为逗号分隔的字符串, * 表示匹配所有. 可选的协议有: {}"
 
-#: acls/serializers/login_asset_acl.py:55 assets/models/asset.py:214
-#: assets/models/domain.py:63 assets/models/user.py:235
+#: acls/serializers/login_asset_acl.py:55 assets/models/asset.py:213
+#: assets/models/domain.py:62 assets/models/user.py:235
 #: terminal/serializers/session.py:30 terminal/serializers/storage.py:69
 msgid "Protocol"
 msgstr "协议"
@@ -244,10 +250,14 @@ msgstr ""
 msgid "Time Period"
 msgstr "时段"
 
-#: applications/api/mixin.py:28 templates/_nav_user.html:10
+#: applications/api/mixin.py:28
 msgid "My applications"
 msgstr "我的应用"
 
+#: applications/apps.py:9 applications/models/application.py:60
+msgid "Applications"
+msgstr "应用管理"
+
 #: applications/const.py:8
 #: applications/serializers/attrs/application_category/db.py:14
 #: applications/serializers/attrs/application_type/mysql_workbench.py:25
@@ -259,22 +269,21 @@ msgstr "数据库"
 msgid "Remote app"
 msgstr "远程应用"
 
-#: applications/const.py:30
+#: applications/const.py:31
 msgid "Custom"
 msgstr "自定义"
 
 #: applications/models/account.py:12 applications/models/application.py:219
 #: assets/models/backup.py:32 assets/models/cmd_filter.py:45
-#: perms/models/application_permission.py:27 users/models/user.py:170
+#: perms/models/application_permission.py:28
 msgid "Application"
 msgstr "应用程序"
 
 #: applications/models/account.py:15 assets/models/authbook.py:20
 #: assets/models/cmd_filter.py:42 assets/models/user.py:325 audits/models.py:40
-#: perms/models/application_permission.py:32
-#: perms/models/asset_permission.py:26 templates/_nav.html:45
-#: terminal/backends/command/models.py:21
-#: terminal/backends/command/serializers.py:14 terminal/models/session.py:44
+#: perms/models/application_permission.py:33
+#: perms/models/asset_permission.py:25 terminal/backends/command/models.py:21
+#: terminal/backends/command/serializers.py:14 terminal/models/session.py:46
 #: users/templates/users/_granted_assets.html:27
 #: users/templates/users/user_asset_permission.html:42
 #: users/templates/users/user_asset_permission.html:76
@@ -283,6 +292,7 @@ msgstr "应用程序"
 #: users/templates/users/user_database_app_permission.html:67
 #: xpack/plugins/change_auth_plan/models/app.py:36
 #: xpack/plugins/change_auth_plan/models/app.py:147
+#: xpack/plugins/change_auth_plan/serializers/app.py:65
 msgid "System user"
 msgstr "系统用户"
 
@@ -291,18 +301,17 @@ msgstr "系统用户"
 msgid "Version"
 msgstr "版本"
 
-#: applications/models/account.py:23 xpack/plugins/cloud/models.py:82
-#: xpack/plugins/cloud/serializers/task.py:66
-msgid "Account"
-msgstr "账户"
+#: applications/models/account.py:23 rbac/ztree/tree_nodes.py:147
+msgid "Application account"
+msgstr "应用账号"
 
-#: applications/models/application.py:60 templates/_nav.html:60
-msgid "Applications"
-msgstr "应用管理"
+#: applications/models/account.py:26 applications/models/account.py:27
+msgid "Can view application account secret"
+msgstr "可以查看应用账号密码"
 
 #: applications/models/application.py:204
 #: applications/serializers/application.py:99 assets/models/label.py:21
-#: perms/models/application_permission.py:20
+#: perms/models/application_permission.py:21
 #: perms/serializers/application/user_permission.py:33
 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22
 #: xpack/plugins/change_auth_plan/models/app.py:25
@@ -312,18 +321,18 @@ msgstr "类别"
 #: applications/models/application.py:207
 #: applications/serializers/application.py:101 assets/models/backup.py:49
 #: assets/models/cmd_filter.py:82 assets/models/user.py:233
-#: perms/models/application_permission.py:23
+#: perms/models/application_permission.py:24
 #: perms/serializers/application/user_permission.py:34
-#: terminal/models/storage.py:55 terminal/models/storage.py:116
-#: tickets/models/flow.py:56 tickets/models/ticket.py:52
+#: terminal/models/storage.py:55 terminal/models/storage.py:119
+#: tickets/models/flow.py:56 tickets/models/ticket.py:131
 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:29
 #: xpack/plugins/change_auth_plan/models/app.py:28
 #: xpack/plugins/change_auth_plan/models/app.py:153
 msgid "Type"
 msgstr "类型"
 
-#: applications/models/application.py:211 assets/models/asset.py:218
-#: assets/models/domain.py:30 assets/models/domain.py:64
+#: applications/models/application.py:211 assets/models/asset.py:217
+#: assets/models/domain.py:29 assets/models/domain.py:63
 msgid "Domain"
 msgstr "网域"
 
@@ -331,6 +340,14 @@ msgstr "网域"
 msgid "Attrs"
 msgstr "属性"
 
+#: applications/models/application.py:223
+msgid "Can match application"
+msgstr "匹配应用"
+
+#: applications/models/application.py:271
+msgid "Application user"
+msgstr "应用用户"
+
 #: applications/serializers/application.py:70
 #: applications/serializers/application.py:100 assets/serializers/label.py:13
 #: perms/serializers/application/permission.py:18
@@ -348,49 +365,54 @@ msgstr "类别名称"
 msgid "Type display"
 msgstr "类型名称"
 
-#: applications/serializers/application.py:103 assets/models/asset.py:231
+#: applications/serializers/application.py:103 assets/models/asset.py:230
 #: assets/models/base.py:181 assets/models/cluster.py:26
-#: assets/models/domain.py:27 assets/models/gathered_user.py:19
+#: assets/models/domain.py:26 assets/models/gathered_user.py:19
 #: assets/models/group.py:22 assets/models/label.py:25
-#: assets/serializers/account.py:17 common/db/models.py:113
+#: assets/serializers/account.py:18 common/db/models.py:113
 #: common/mixins/models.py:50 ops/models/adhoc.py:39 ops/models/command.py:30
-#: orgs/models.py:26 orgs/models.py:435 perms/models/base.py:92
-#: users/models/group.py:18 users/models/user.py:783
+#: orgs/models.py:14 orgs/models.py:199 perms/models/base.py:92
+#: users/models/group.py:18 users/models/user.py:825
 #: xpack/plugins/cloud/models.py:122
 msgid "Date created"
 msgstr "创建日期"
 
 #: applications/serializers/application.py:104 assets/models/base.py:182
-#: assets/models/gathered_user.py:20 assets/serializers/account.py:20
+#: assets/models/gathered_user.py:20 assets/serializers/account.py:21
 #: common/db/models.py:114 common/mixins/models.py:51 ops/models/adhoc.py:40
-#: orgs/models.py:436
+#: orgs/models.py:200
 msgid "Date updated"
 msgstr "更新日期"
 
 #: applications/serializers/application.py:121
-#: applications/serializers/application.py:165
+#: applications/serializers/application.py:166
 msgid "Application display"
 msgstr "应用名称"
 
+#: applications/serializers/application.py:123
+msgid "account"
+msgstr "账号"
+
 #: applications/serializers/attrs/application_category/cloud.py:8
 #: assets/models/cluster.py:40
 msgid "Cluster"
 msgstr "集群"
 
 #: applications/serializers/attrs/application_category/db.py:11
-#: ops/models/adhoc.py:152 settings/serializers/auth/radius.py:14
+#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
 #: xpack/plugins/cloud/serializers/account_attrs.py:68
 msgid "Host"
 msgstr "主机"
 
 #: applications/serializers/attrs/application_category/db.py:12
+#: applications/serializers/attrs/application_type/mongodb.py:10
 #: applications/serializers/attrs/application_type/mysql.py:10
 #: applications/serializers/attrs/application_type/mysql_workbench.py:21
 #: applications/serializers/attrs/application_type/oracle.py:10
 #: applications/serializers/attrs/application_type/pgsql.py:10
 #: applications/serializers/attrs/application_type/redis.py:10
 #: applications/serializers/attrs/application_type/sqlserver.py:10
-#: assets/models/asset.py:215 assets/models/domain.py:62
+#: assets/models/asset.py:214 assets/models/domain.py:61
 #: settings/serializers/auth/radius.py:15
 #: xpack/plugins/cloud/serializers/account_attrs.py:69
 msgid "Port"
@@ -405,10 +427,10 @@ msgstr "应用路径"
 
 #: applications/serializers/attrs/application_category/remote_app.py:44
 #: assets/serializers/system_user.py:163
-#: xpack/plugins/change_auth_plan/serializers/asset.py:65
-#: xpack/plugins/change_auth_plan/serializers/asset.py:68
-#: xpack/plugins/change_auth_plan/serializers/asset.py:71
-#: xpack/plugins/change_auth_plan/serializers/asset.py:102
+#: xpack/plugins/change_auth_plan/serializers/asset.py:66
+#: xpack/plugins/change_auth_plan/serializers/asset.py:69
+#: xpack/plugins/change_auth_plan/serializers/asset.py:72
+#: xpack/plugins/change_auth_plan/serializers/asset.py:103
 #: xpack/plugins/cloud/serializers/account_attrs.py:52
 msgid "This field is required."
 msgstr "该字段是必填项。"
@@ -467,146 +489,186 @@ msgstr "Vmware 密码"
 msgid "Number required"
 msgstr "需要为数字"
 
-#: assets/api/node.py:60
+#: assets/api/node.py:62
 msgid "You can't update the root node name"
 msgstr "不能修改根节点名称"
 
-#: assets/api/node.py:67
+#: assets/api/node.py:69
 msgid "You can't delete the root node ({})"
 msgstr "不能删除根节点 ({})"
 
-#: assets/api/node.py:70
+#: assets/api/node.py:72
 msgid "Deletion failed and the node contains assets"
 msgstr "删除失败,节点包含资产"
 
-#: assets/models/asset.py:140
+#: assets/apps.py:9
+msgid "App assets"
+msgstr "资产管理"
+
+#: assets/models/asset.py:139
 msgid "Base"
 msgstr "基础"
 
-#: assets/models/asset.py:141
+#: assets/models/asset.py:140
 msgid "Charset"
 msgstr "编码"
 
-#: assets/models/asset.py:142 assets/serializers/asset.py:176
-#: tickets/models/ticket.py:54
+#: assets/models/asset.py:141 assets/serializers/asset.py:176
+#: tickets/models/ticket.py:133
 msgid "Meta"
 msgstr "元数据"
 
-#: assets/models/asset.py:143
+#: assets/models/asset.py:142
 msgid "Internal"
 msgstr "内部的"
 
-#: assets/models/asset.py:163 assets/models/asset.py:217
-#: assets/serializers/account.py:14 assets/serializers/asset.py:63
+#: assets/models/asset.py:162 assets/models/asset.py:216
+#: assets/serializers/account.py:15 assets/serializers/asset.py:63
 #: perms/serializers/asset/user_permission.py:43
 msgid "Platform"
 msgstr "系统平台"
 
-#: assets/models/asset.py:169
+#: assets/models/asset.py:168
 msgid "Vendor"
 msgstr "制造商"
 
-#: assets/models/asset.py:170
+#: assets/models/asset.py:169
 msgid "Model"
 msgstr "型号"
 
-#: assets/models/asset.py:171 tickets/models/ticket.py:80
+#: assets/models/asset.py:170 tickets/models/ticket.py:159
 msgid "Serial number"
 msgstr "序列号"
 
-#: assets/models/asset.py:173
+#: assets/models/asset.py:172
 msgid "CPU model"
 msgstr "CPU型号"
 
-#: assets/models/asset.py:174
+#: assets/models/asset.py:173
 msgid "CPU count"
 msgstr "CPU数量"
 
-#: assets/models/asset.py:175
+#: assets/models/asset.py:174
 msgid "CPU cores"
 msgstr "CPU核数"
 
-#: assets/models/asset.py:176
+#: assets/models/asset.py:175
 msgid "CPU vcpus"
 msgstr "CPU总数"
 
-#: assets/models/asset.py:177
+#: assets/models/asset.py:176
 msgid "Memory"
 msgstr "内存"
 
-#: assets/models/asset.py:178
+#: assets/models/asset.py:177
 msgid "Disk total"
 msgstr "硬盘大小"
 
-#: assets/models/asset.py:179
+#: assets/models/asset.py:178
 msgid "Disk info"
 msgstr "硬盘信息"
 
-#: assets/models/asset.py:181
+#: assets/models/asset.py:180
 msgid "OS"
 msgstr "操作系统"
 
-#: assets/models/asset.py:182
+#: assets/models/asset.py:181
 msgid "OS version"
 msgstr "系统版本"
 
-#: assets/models/asset.py:183
+#: assets/models/asset.py:182
 msgid "OS arch"
 msgstr "系统架构"
 
-#: assets/models/asset.py:184
+#: assets/models/asset.py:183
 msgid "Hostname raw"
 msgstr "主机名原始"
 
-#: assets/models/asset.py:216 assets/serializers/account.py:15
+#: assets/models/asset.py:215 assets/serializers/account.py:16
 #: assets/serializers/asset.py:65 perms/serializers/asset/user_permission.py:41
 #: xpack/plugins/cloud/models.py:104 xpack/plugins/cloud/serializers/task.py:42
 msgid "Protocols"
 msgstr "协议组"
 
-#: assets/models/asset.py:219 assets/models/user.py:225
-#: perms/models/asset_permission.py:25
+#: assets/models/asset.py:218 assets/models/user.py:225
+#: perms/models/asset_permission.py:24
 #: xpack/plugins/change_auth_plan/models/asset.py:43
 #: xpack/plugins/gathered_user/models.py:24
 msgid "Nodes"
 msgstr "节点"
 
-#: assets/models/asset.py:220 assets/models/cmd_filter.py:47
-#: assets/models/domain.py:66 assets/models/label.py:22
+#: assets/models/asset.py:219 assets/models/cmd_filter.py:47
+#: assets/models/domain.py:65 assets/models/label.py:22
 msgid "Is active"
 msgstr "激活"
 
-#: assets/models/asset.py:223 assets/models/cluster.py:19
-#: assets/models/user.py:222 assets/models/user.py:374 templates/_nav.html:44
+#: assets/models/asset.py:222 assets/models/cluster.py:19
+#: assets/models/user.py:222 assets/models/user.py:380
 msgid "Admin user"
 msgstr "特权用户"
 
-#: assets/models/asset.py:226
+#: assets/models/asset.py:225
 msgid "Public IP"
 msgstr "公网IP"
 
-#: assets/models/asset.py:227
+#: assets/models/asset.py:226
 msgid "Asset number"
 msgstr "资产编号"
 
-#: assets/models/asset.py:229 templates/_nav.html:46
+#: assets/models/asset.py:228
 msgid "Labels"
 msgstr "标签管理"
 
-#: assets/models/asset.py:230 assets/models/base.py:183
+#: assets/models/asset.py:229 assets/models/base.py:183
 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:52
 #: assets/models/cmd_filter.py:99 assets/models/group.py:21
-#: common/db/models.py:111 common/mixins/models.py:49 orgs/models.py:25
-#: orgs/models.py:437 perms/models/base.py:91 users/models/user.py:593
+#: common/db/models.py:111 common/mixins/models.py:49 orgs/models.py:13
+#: orgs/models.py:201 perms/models/base.py:91 users/models/user.py:619
 #: users/serializers/group.py:33
 #: xpack/plugins/change_auth_plan/models/base.py:48
 #: xpack/plugins/cloud/models.py:119 xpack/plugins/gathered_user/models.py:30
 msgid "Created by"
 msgstr "创建者"
 
+#: assets/models/asset.py:358
+msgid "Can refresh asset hardware info"
+msgstr "可以更新资产硬件信息"
+
+#: assets/models/asset.py:359
+msgid "Can test asset connectivity"
+msgstr "可以测试资产连接性"
+
+#: assets/models/asset.py:360
+msgid "Can push system user to asset"
+msgstr "可以推送系统用户到资产"
+
+#: assets/models/asset.py:361
+msgid "Can match asset"
+msgstr "可以匹配资产"
+
+#: assets/models/asset.py:362
+msgid "Add asset to node"
+msgstr "添加资产到节点"
+
+#: assets/models/asset.py:363
+msgid "Move asset to node"
+msgstr "移动资产到节点"
+
 #: assets/models/authbook.py:27
 msgid "AuthBook"
-msgstr "账号"
+msgstr "资产账号"
+
+#: assets/models/authbook.py:30
+msgid "Can test asset account connectivity"
+msgstr "可以测试资产账号连接性"
+
+#: assets/models/authbook.py:31
+msgid "Can view asset account secret"
+msgstr "可以查看资产账号密码"
+
+#: assets/models/authbook.py:32
+msgid "Can change asset account secret"
+msgstr "可以更改资产账号密码"
 
 #: assets/models/backup.py:30 perms/models/base.py:54
 #: settings/serializers/terminal.py:12
@@ -622,7 +684,7 @@ msgstr "收件人"
 
 #: assets/models/backup.py:62 assets/models/backup.py:124
 msgid "Account backup plan"
-msgstr "账户备份计划"
+msgstr "账号备份计划"
 
 #: assets/models/backup.py:100
 #: xpack/plugins/change_auth_plan/models/base.py:107
@@ -635,7 +697,7 @@ msgid "Timing trigger"
 msgstr "定时触发"
 
 #: assets/models/backup.py:105 audits/models.py:44 ops/models/command.py:31
-#: perms/models/base.py:89 terminal/models/session.py:54
+#: perms/models/base.py:89 terminal/models/session.py:56
 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:55
 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:57
 #: xpack/plugins/change_auth_plan/models/base.py:112
@@ -646,7 +708,7 @@ msgstr "开始日期"
 
 #: assets/models/backup.py:108
 #: authentication/templates/authentication/_msg_oauth_bind.html:11
-#: notifications/notifications.py:187 ops/models/adhoc.py:252
+#: notifications/notifications.py:187 ops/models/adhoc.py:258
 #: xpack/plugins/change_auth_plan/models/base.py:115
 #: xpack/plugins/change_auth_plan/models/base.py:204
 #: xpack/plugins/gathered_user/models.py:79
@@ -663,17 +725,21 @@ msgstr "账号备份快照"
 msgid "Trigger mode"
 msgstr "触发模式"
 
-#: assets/models/backup.py:119 audits/models.py:116
-#: terminal/models/sharing.py:88
+#: assets/models/backup.py:119 audits/models.py:125
+#: terminal/models/sharing.py:94
 #: xpack/plugins/change_auth_plan/models/base.py:201
+#: xpack/plugins/change_auth_plan/serializers/app.py:66
+#: xpack/plugins/change_auth_plan/serializers/asset.py:179
 #: xpack/plugins/cloud/models.py:176
 msgid "Reason"
 msgstr "原因"
 
 #: assets/models/backup.py:121 audits/serializers.py:82
-#: audits/serializers.py:97 ops/models/adhoc.py:254
+#: audits/serializers.py:97 ops/models/adhoc.py:260
 #: terminal/serializers/session.py:35
 #: xpack/plugins/change_auth_plan/models/base.py:202
+#: xpack/plugins/change_auth_plan/serializers/app.py:67
+#: xpack/plugins/change_auth_plan/serializers/asset.py:181
 msgid "Is success"
 msgstr "是否成功"
 
@@ -689,8 +755,9 @@ msgstr "未知"
 msgid "Ok"
 msgstr "成功"
 
-#: assets/models/base.py:32 audits/models.py:107
-#: xpack/plugins/change_auth_plan/task_handlers/base/manager.py:121
+#: assets/models/base.py:32 audits/models.py:116
+#: xpack/plugins/change_auth_plan/serializers/app.py:88
+#: xpack/plugins/change_auth_plan/serializers/asset.py:198
 #: xpack/plugins/cloud/const.py:29
 msgid "Failed"
 msgstr "失败"
@@ -703,9 +770,9 @@ msgstr "可连接性"
 msgid "Date verified"
 msgstr "校验日期"
 
-#: assets/models/base.py:177 audits/signals_handler.py:68
+#: assets/models/base.py:177 audits/signal_handlers.py:68
 #: authentication/forms.py:22
-#: authentication/templates/authentication/login.html:151
+#: authentication/templates/authentication/login.html:178
 #: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
 #: users/templates/users/_msg_user_created.html:13
 #: users/templates/users/user_password_update.html:43
@@ -737,7 +804,7 @@ msgstr "带宽"
 msgid "Contact"
 msgstr "联系人"
 
-#: assets/models/cluster.py:22 users/models/user.py:568
+#: assets/models/cluster.py:22 users/models/user.py:594
 msgid "Phone"
 msgstr "手机"
 
@@ -762,8 +829,8 @@ msgstr "运营商"
 msgid "Default"
 msgstr "默认"
 
-#: assets/models/cluster.py:36 assets/models/label.py:14
-#: users/models/user.py:768
+#: assets/models/cluster.py:36 assets/models/label.py:14 rbac/const.py:6
+#: users/models/user.py:810
 msgid "System"
 msgstr "系统"
 
@@ -772,8 +839,8 @@ msgid "Default Cluster"
 msgstr "默认Cluster"
 
 #: assets/models/cmd_filter.py:34 perms/models/base.py:86
-#: templates/_nav.html:21 users/models/group.py:31 users/models/user.py:555
-#: users/templates/users/_select_user_modal.html:16
+#: rbac/ztree/tree_nodes.py:84 users/models/group.py:31
+#: users/models/user.py:580 users/templates/users/_select_user_modal.html:16
 #: users/templates/users/user_asset_permission.html:39
 #: users/templates/users/user_asset_permission.html:67
 #: users/templates/users/user_database_app_permission.html:38
@@ -782,6 +849,7 @@ msgid "User group"
 msgstr "用户组"
 
 #: assets/models/cmd_filter.py:60 assets/serializers/system_user.py:54
+#: rbac/ztree/tree_nodes.py:123
 msgid "Command filter"
 msgstr "命令过滤器"
 
@@ -790,7 +858,7 @@ msgid "Regex"
 msgstr "正则表达式"
 
 #: assets/models/cmd_filter.py:68 ops/models/command.py:26
-#: terminal/backends/command/serializers.py:15 terminal/models/session.py:51
+#: terminal/backends/command/serializers.py:15 terminal/models/session.py:53
 #: terminal/templates/terminal/_msg_command_alert.html:12
 #: terminal/templates/terminal/_msg_command_execute_alert.html:10
 msgid "Command"
@@ -821,7 +889,7 @@ msgstr "每行一个命令"
 msgid "Ignore case"
 msgstr "忽略大小写"
 
-#: assets/models/cmd_filter.py:103
+#: assets/models/cmd_filter.py:103 rbac/ztree/tree_nodes.py:126
 msgid "Command filter rule"
 msgstr "命令过滤规则"
 
@@ -833,20 +901,24 @@ msgstr "生成的正则表达式有误"
 msgid "Command confirm"
 msgstr "命令复核"
 
-#: assets/models/domain.py:73
+#: assets/models/domain.py:72
 msgid "Gateway"
 msgstr "网关"
 
-#: assets/models/domain.py:128
+#: assets/models/domain.py:74
+msgid "Test gateway"
+msgstr "测试网关"
+
+#: assets/models/domain.py:130
 #, python-brace-format
 msgid "Unable to connect to port {port} on {ip}"
 msgstr "无法连接到 {ip} 上的端口 {port}"
 
-#: assets/models/domain.py:131
+#: assets/models/domain.py:133
 msgid "Authentication failed"
 msgstr "认证失败"
 
-#: assets/models/domain.py:133 assets/models/domain.py:155
+#: assets/models/domain.py:135 assets/models/domain.py:157
 msgid "Connect failed"
 msgstr "连接失败"
 
@@ -878,6 +950,10 @@ msgstr "默认资产组"
 msgid "Value"
 msgstr "值"
 
+#: assets/models/label.py:40 settings/serializers/sms.py:7
+msgid "Label"
+msgstr "标签"
+
 #: assets/models/node.py:151
 msgid "New node"
 msgstr "新节点"
@@ -886,15 +962,15 @@ msgstr "新节点"
 msgid "empty"
 msgstr "空"
 
-#: assets/models/node.py:545 perms/models/asset_permission.py:100
+#: assets/models/node.py:545 perms/models/asset_permission.py:103
 msgid "Key"
 msgstr "键"
 
-#: assets/models/node.py:547 assets/serializers/node.py:21
+#: assets/models/node.py:547 assets/serializers/node.py:20
 msgid "Full value"
 msgstr "全称"
 
-#: assets/models/node.py:550 perms/models/asset_permission.py:101
+#: assets/models/node.py:550 perms/models/asset_permission.py:104
 msgid "Parent key"
 msgstr "ssh私钥"
 
@@ -906,6 +982,10 @@ msgstr "ssh私钥"
 msgid "Node"
 msgstr "节点"
 
+#: assets/models/node.py:562
+msgid "Can match node"
+msgstr "可以匹配节点"
+
 #: assets/models/user.py:216
 msgid "Automatic managed"
 msgstr "托管密码"
@@ -923,14 +1003,12 @@ msgid "Username same with user"
 msgstr "用户名与用户相同"
 
 #: assets/models/user.py:227 assets/serializers/domain.py:29
-#: templates/_nav.html:39
 #: terminal/templates/terminal/_msg_command_execute_alert.html:16
 #: xpack/plugins/change_auth_plan/models/asset.py:39
 msgid "Assets"
 msgstr "资产"
 
-#: assets/models/user.py:231 templates/_nav.html:17
-#: users/views/profile/pubkey.py:37
+#: assets/models/user.py:231 users/apps.py:9
 msgid "Users"
 msgstr "用户管理"
 
@@ -958,7 +1036,7 @@ msgstr "认证方式"
 msgid "SFTP Root"
 msgstr "SFTP根路径"
 
-#: assets/models/user.py:241 authentication/models.py:45
+#: assets/models/user.py:241 authentication/models.py:48
 msgid "Token"
 msgstr ""
 
@@ -978,6 +1056,22 @@ msgstr "用户切换"
 msgid "Switch from"
 msgstr "切换自"
 
+#: assets/models/user.py:327
+msgid "Can view system user asset"
+msgstr "可以查看系统用户资产列表"
+
+#: assets/models/user.py:328
+msgid "Can add asset to system user"
+msgstr "可以添加资产到系统用户"
+
+#: assets/models/user.py:329
+msgid "Can remove system user asset"
+msgstr "可以移除系统用户资产"
+
+#: assets/models/user.py:330
+msgid "Can match system user"
+msgstr "可以匹配系统用户"
+
 #: assets/models/utils.py:35
 #, python-format
 msgid "%(value)s is not an even number"
@@ -1002,7 +1096,7 @@ msgstr ""
 "{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设"
 "置加密密码"
 
-#: assets/serializers/account.py:39 assets/serializers/account.py:67
+#: assets/serializers/account.py:40 assets/serializers/account.py:83
 msgid "System user display"
 msgstr "系统用户名称"
 
@@ -1072,7 +1166,6 @@ msgid "Assets amount"
 msgstr "资产数量"
 
 #: assets/serializers/domain.py:14
-#: perms/serializers/application/permission.py:46
 msgid "Applications amount"
 msgstr "应用数量"
 
@@ -1080,15 +1173,15 @@ msgstr "应用数量"
 msgid "Gateways count"
 msgstr "网关数量"
 
-#: assets/serializers/node.py:18
+#: assets/serializers/node.py:17
 msgid "value"
 msgstr "值"
 
-#: assets/serializers/node.py:32
+#: assets/serializers/node.py:31
 msgid "Can't contains: /"
 msgstr "不能包含: /"
 
-#: assets/serializers/node.py:42
+#: assets/serializers/node.py:41
 msgid "The same level node name cannot be the same"
 msgstr "同级别节点名字不能重复"
 
@@ -1097,6 +1190,7 @@ msgid "SSH key fingerprint"
 msgstr "密钥指纹"
 
 #: assets/serializers/system_user.py:30
+#: perms/serializers/application/permission.py:46
 msgid "Apps amount"
 msgstr "应用数量"
 
@@ -1280,8 +1374,13 @@ msgstr "为了安全,禁止推送用户 {}"
 msgid "No assets matched, stop task"
 msgstr "没有匹配到资产,结束任务"
 
-#: audits/models.py:27 audits/models.py:54
+#: audits/apps.py:9
+msgid "Audits"
+msgstr "日志审计"
+
+#: audits/models.py:27 audits/models.py:57
 #: authentication/templates/authentication/_access_key_modal.html:65
+#: rbac/tree.py:238 rbac/ztree/tree.py:161
 #: users/templates/users/user_asset_permission.html:128
 #: users/templates/users/user_database_app_permission.html:111
 msgid "Delete"
@@ -1311,8 +1410,8 @@ msgstr "创建目录"
 msgid "Symlink"
 msgstr "建立软链接"
 
-#: audits/models.py:38 audits/models.py:61 audits/models.py:81
-#: terminal/models/session.py:47 terminal/models/sharing.py:76
+#: audits/models.py:38 audits/models.py:64 audits/models.py:87
+#: terminal/models/session.py:49 terminal/models/sharing.py:82
 msgid "Remote addr"
 msgstr "远端地址"
 
@@ -1324,90 +1423,109 @@ msgstr "操作"
 msgid "Filename"
 msgstr "文件名"
 
-#: audits/models.py:43 audits/models.py:106 terminal/models/sharing.py:84
-#: xpack/plugins/change_auth_plan/task_handlers/base/manager.py:119
+#: audits/models.py:43 audits/models.py:115 terminal/models/sharing.py:90
+#: xpack/plugins/change_auth_plan/serializers/app.py:87
+#: xpack/plugins/change_auth_plan/serializers/asset.py:197
 msgid "Success"
 msgstr "成功"
 
-#: audits/models.py:52
+#: audits/models.py:47
+msgid "File transfer log"
+msgstr "文件管理"
+
+#: audits/models.py:55
 #: authentication/templates/authentication/_access_key_modal.html:22
+#: rbac/tree.py:235 rbac/ztree/tree.py:158
 msgid "Create"
 msgstr "创建"
 
-#: audits/models.py:53 templates/_csv_import_export.html:18
-#: templates/_csv_update_modal.html:6
+#: audits/models.py:56 rbac/tree.py:237 rbac/ztree/tree.py:160
+#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
 #: users/templates/users/user_asset_permission.html:127
 #: users/templates/users/user_database_app_permission.html:110
 msgid "Update"
 msgstr "更新"
 
-#: audits/models.py:59 audits/serializers.py:63
+#: audits/models.py:62 audits/serializers.py:63
 msgid "Resource Type"
 msgstr "资源类型"
 
-#: audits/models.py:60
+#: audits/models.py:63
 msgid "Resource"
 msgstr "资源"
 
-#: audits/models.py:62 audits/models.py:82
+#: audits/models.py:65 audits/models.py:88
 msgid "Datetime"
 msgstr "日期"
 
 #: audits/models.py:80
+msgid "Operate log"
+msgstr "操作日志"
+
+#: audits/models.py:86
 msgid "Change by"
 msgstr "修改者"
 
-#: audits/models.py:100
+#: audits/models.py:94
+msgid "Password change log"
+msgstr "改密日志"
+
+#: audits/models.py:109
 msgid "Disabled"
 msgstr "禁用"
 
-#: audits/models.py:101 settings/models.py:33
+#: audits/models.py:110 settings/models.py:33
 msgid "Enabled"
 msgstr "启用"
 
-#: audits/models.py:102
+#: audits/models.py:111
 msgid "-"
 msgstr ""
 
-#: audits/models.py:111
+#: audits/models.py:120
 msgid "Login type"
 msgstr "登录方式"
 
-#: audits/models.py:112
+#: audits/models.py:121
 #: tickets/serializers/ticket/meta/ticket_type/login_confirm.py:14
 msgid "Login ip"
 msgstr "登录IP"
 
-#: audits/models.py:113
+#: audits/models.py:122
 #: authentication/templates/authentication/_msg_different_city.html:11
 #: tickets/serializers/ticket/meta/ticket_type/login_confirm.py:17
 msgid "Login city"
 msgstr "登录城市"
 
-#: audits/models.py:114 audits/serializers.py:44
+#: audits/models.py:123 audits/serializers.py:44
 msgid "User agent"
 msgstr "用户代理"
 
-#: audits/models.py:115
+#: audits/models.py:124
 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
-#: users/forms/profile.py:64 users/models/user.py:571
-#: users/serializers/profile.py:123
+#: users/forms/profile.py:64 users/models/user.py:597
+#: users/serializers/profile.py:121
 msgid "MFA"
 msgstr "MFA"
 
-#: audits/models.py:117 tickets/models/ticket.py:61
-#: xpack/plugins/cloud/models.py:172 xpack/plugins/cloud/models.py:221
+#: audits/models.py:126 terminal/models/status.py:33
+#: tickets/models/ticket.py:140 xpack/plugins/cloud/models.py:172
+#: xpack/plugins/cloud/models.py:224
 msgid "Status"
 msgstr "状态"
 
-#: audits/models.py:118
+#: audits/models.py:127
 msgid "Date login"
 msgstr "登录日期"
 
-#: audits/models.py:119 audits/serializers.py:46
+#: audits/models.py:128 audits/serializers.py:46
 msgid "Authentication backend"
 msgstr "认证方式"
 
+#: audits/models.py:167
+msgid "User login log"
+msgstr "用户登录日志"
+
 #: audits/serializers.py:14
 msgid "Operate display"
 msgstr "操作名称"
@@ -1433,7 +1551,7 @@ msgstr "主机名称"
 msgid "Result"
 msgstr "结果"
 
-#: audits/serializers.py:98 terminal/serializers/storage.py:151
+#: audits/serializers.py:98 terminal/serializers/storage.py:161
 msgid "Hosts"
 msgstr "主机"
 
@@ -1445,277 +1563,269 @@ msgstr "运行用户"
 msgid "Run as display"
 msgstr "运行用户名称"
 
-#: audits/serializers.py:102
+#: audits/serializers.py:102 rbac/serializers/rolebinding.py:22
 msgid "User display"
 msgstr "用户名称"
 
-#: audits/signals_handler.py:67
+#: audits/signal_handlers.py:67
 msgid "SSH Key"
 msgstr "SSH 密钥"
 
-#: audits/signals_handler.py:69
+#: audits/signal_handlers.py:69
 msgid "SSO"
 msgstr ""
 
-#: audits/signals_handler.py:70
+#: audits/signal_handlers.py:70
 msgid "Auth Token"
 msgstr "认证令牌"
 
-#: audits/signals_handler.py:71 authentication/notifications.py:73
+#: audits/signal_handlers.py:71 authentication/notifications.py:73
 #: authentication/views/login.py:164 authentication/views/wecom.py:158
-#: notifications/backends/__init__.py:11 users/models/user.py:607
+#: notifications/backends/__init__.py:11 users/models/user.py:633
 msgid "WeCom"
 msgstr "企业微信"
 
-#: audits/signals_handler.py:72 authentication/views/dingtalk.py:160
+#: audits/signal_handlers.py:72 authentication/views/dingtalk.py:160
 #: authentication/views/login.py:170 notifications/backends/__init__.py:12
-#: users/models/user.py:608
+#: users/models/user.py:634
 msgid "DingTalk"
 msgstr "钉钉"
 
-#: audits/signals_handler.py:106
-msgid "User and Organization"
-msgstr "用户与组织"
-
-#: audits/signals_handler.py:107
-#, python-brace-format
-msgid "{User} JOINED {Organization}"
-msgstr "{User} 加入 {Organization}"
-
-#: audits/signals_handler.py:108
-#, python-brace-format
-msgid "{User} LEFT {Organization}"
-msgstr "{User} 离开 {Organization}"
-
-#: audits/signals_handler.py:111
+#: audits/signal_handlers.py:106
 msgid "User and Group"
 msgstr "用户与用户组"
 
-#: audits/signals_handler.py:112
+#: audits/signal_handlers.py:107
 #, python-brace-format
 msgid "{User} JOINED {UserGroup}"
 msgstr "{User} 加入 {UserGroup}"
 
-#: audits/signals_handler.py:113
+#: audits/signal_handlers.py:108
 #, python-brace-format
 msgid "{User} LEFT {UserGroup}"
 msgstr "{User} 离开 {UserGroup}"
 
-#: audits/signals_handler.py:116
+#: audits/signal_handlers.py:111
 msgid "Asset and SystemUser"
 msgstr "资产与系统用户"
 
-#: audits/signals_handler.py:117
+#: audits/signal_handlers.py:112
 #, python-brace-format
 msgid "{Asset} ADD {SystemUser}"
 msgstr "{Asset} 添加 {SystemUser}"
 
-#: audits/signals_handler.py:118
+#: audits/signal_handlers.py:113
 #, python-brace-format
 msgid "{Asset} REMOVE {SystemUser}"
 msgstr "{Asset} 移除 {SystemUser}"
 
-#: audits/signals_handler.py:121
+#: audits/signal_handlers.py:116
 msgid "Node and Asset"
 msgstr "节点与资产"
 
-#: audits/signals_handler.py:122
+#: audits/signal_handlers.py:117
 #, python-brace-format
 msgid "{Node} ADD {Asset}"
 msgstr "{Node} 添加 {Asset}"
 
-#: audits/signals_handler.py:123
+#: audits/signal_handlers.py:118
 #, python-brace-format
 msgid "{Node} REMOVE {Asset}"
 msgstr "{Node} 移除 {Asset}"
 
-#: audits/signals_handler.py:126
+#: audits/signal_handlers.py:121
 msgid "User asset permissions"
 msgstr "用户资产授权"
 
-#: audits/signals_handler.py:127
+#: audits/signal_handlers.py:122
 #, python-brace-format
 msgid "{AssetPermission} ADD {User}"
 msgstr "{AssetPermission} 添加 {User}"
 
-#: audits/signals_handler.py:128
+#: audits/signal_handlers.py:123
 #, python-brace-format
 msgid "{AssetPermission} REMOVE {User}"
 msgstr "{AssetPermission} 移除 {User}"
 
-#: audits/signals_handler.py:131
+#: audits/signal_handlers.py:126
 msgid "User group asset permissions"
 msgstr "用户组资产授权"
 
-#: audits/signals_handler.py:132
+#: audits/signal_handlers.py:127
 #, python-brace-format
 msgid "{AssetPermission} ADD {UserGroup}"
 msgstr "{AssetPermission} 添加 {UserGroup}"
 
-#: audits/signals_handler.py:133
+#: audits/signal_handlers.py:128
 #, python-brace-format
 msgid "{AssetPermission} REMOVE {UserGroup}"
 msgstr "{AssetPermission} 移除 {UserGroup}"
 
-#: audits/signals_handler.py:136 perms/models/asset_permission.py:30
-#: templates/_nav.html:78 users/templates/users/_user_detail_nav_header.html:31
+#: audits/signal_handlers.py:131 perms/models/asset_permission.py:29
+#: rbac/ztree/tree_nodes.py:27 rbac/ztree/tree_nodes.py:171
+#: users/templates/users/_user_detail_nav_header.html:31
 msgid "Asset permission"
 msgstr "资产授权"
 
-#: audits/signals_handler.py:137
+#: audits/signal_handlers.py:132
 #, python-brace-format
 msgid "{AssetPermission} ADD {Asset}"
 msgstr "{AssetPermission} 添加 {Asset}"
 
-#: audits/signals_handler.py:138
+#: audits/signal_handlers.py:133
 #, python-brace-format
 msgid "{AssetPermission} REMOVE {Asset}"
 msgstr "{AssetPermission} 移除 {Asset}"
 
-#: audits/signals_handler.py:141
+#: audits/signal_handlers.py:136
 msgid "Node permission"
 msgstr "节点授权"
 
-#: audits/signals_handler.py:142
+#: audits/signal_handlers.py:137
 #, python-brace-format
 msgid "{AssetPermission} ADD {Node}"
 msgstr "{AssetPermission} 添加 {Node}"
 
-#: audits/signals_handler.py:143
+#: audits/signal_handlers.py:138
 #, python-brace-format
 msgid "{AssetPermission} REMOVE {Node}"
 msgstr "{AssetPermission} 移除 {Node}"
 
-#: audits/signals_handler.py:146
+#: audits/signal_handlers.py:141
 msgid "Asset permission and SystemUser"
 msgstr "资产授权与系统用户"
 
-#: audits/signals_handler.py:147
+#: audits/signal_handlers.py:142
 #, python-brace-format
 msgid "{AssetPermission} ADD {SystemUser}"
 msgstr "{AssetPermission} 添加 {SystemUser}"
 
-#: audits/signals_handler.py:148
+#: audits/signal_handlers.py:143
 #, python-brace-format
 msgid "{AssetPermission} REMOVE {SystemUser}"
 msgstr "{AssetPermission} 移除 {SystemUser}"
 
-#: audits/signals_handler.py:151
+#: audits/signal_handlers.py:146
 msgid "User application permissions"
 msgstr "用户应用授权"
 
-#: audits/signals_handler.py:152
+#: audits/signal_handlers.py:147
 #, python-brace-format
 msgid "{ApplicationPermission} ADD {User}"
 msgstr "{ApplicationPermission} 添加 {User}"
 
-#: audits/signals_handler.py:153
+#: audits/signal_handlers.py:148
 #, python-brace-format
 msgid "{ApplicationPermission} REMOVE {User}"
 msgstr "{ApplicationPermission} 移除 {User}"
 
-#: audits/signals_handler.py:156
+#: audits/signal_handlers.py:151
 msgid "User group application permissions"
 msgstr "用户组应用授权"
 
-#: audits/signals_handler.py:157
+#: audits/signal_handlers.py:152
 #, python-brace-format
 msgid "{ApplicationPermission} ADD {UserGroup}"
 msgstr "{ApplicationPermission} 添加 {UserGroup}"
 
-#: audits/signals_handler.py:158
+#: audits/signal_handlers.py:153
 #, python-brace-format
 msgid "{ApplicationPermission} REMOVE {UserGroup}"
 msgstr "{ApplicationPermission} 移除 {UserGroup}"
 
-#: audits/signals_handler.py:161 perms/models/application_permission.py:37
+#: audits/signal_handlers.py:156 perms/models/application_permission.py:38
+#: rbac/ztree/tree_nodes.py:90 rbac/ztree/tree_nodes.py:174
 msgid "Application permission"
 msgstr "应用授权"
 
-#: audits/signals_handler.py:162
+#: audits/signal_handlers.py:157
 #, python-brace-format
 msgid "{ApplicationPermission} ADD {Application}"
 msgstr "{ApplicationPermission} 添加 {Application}"
 
-#: audits/signals_handler.py:163
+#: audits/signal_handlers.py:158
 #, python-brace-format
 msgid "{ApplicationPermission} REMOVE {Application}"
 msgstr "{ApplicationPermission} 移除 {Application}"
 
-#: audits/signals_handler.py:166
+#: audits/signal_handlers.py:161
 msgid "Application permission and SystemUser"
 msgstr "应用授权与系统用户"
 
-#: audits/signals_handler.py:167
+#: audits/signal_handlers.py:162
 #, python-brace-format
 msgid "{ApplicationPermission} ADD {SystemUser}"
 msgstr "{ApplicationPermission} 添加 {SystemUser}"
 
-#: audits/signals_handler.py:168
+#: audits/signal_handlers.py:163
 #, python-brace-format
 msgid "{ApplicationPermission} REMOVE {SystemUser}"
 msgstr "{ApplicationPermission} 移除 {SystemUser}"
 
-#: authentication/api/connection_token.py:285
+#: authentication/api/connection_token.py:296
 msgid "Invalid token"
 msgstr "无效的令牌"
 
-#: authentication/api/mfa.py:72
+#: authentication/api/mfa.py:64
 msgid "Current user not support mfa type: {}"
 msgstr "当前用户不支持 MFA 类型: {}"
 
-#: authentication/api/mfa.py:119
+#: authentication/api/mfa.py:111
 msgid "Code is invalid, {}"
 msgstr "验证码无效: {}"
 
-#: authentication/backends/api.py:67
+#: authentication/apps.py:7
+msgid "Authentication"
+msgstr "认证"
+
+#: authentication/backends/drf.py:56
 msgid "Invalid signature header. No credentials provided."
 msgstr ""
 
-#: authentication/backends/api.py:70
+#: authentication/backends/drf.py:59
 msgid "Invalid signature header. Signature string should not contain spaces."
 msgstr ""
 
-#: authentication/backends/api.py:77
+#: authentication/backends/drf.py:66
 msgid "Invalid signature header. Format like AccessKeyId:Signature"
 msgstr ""
 
-#: authentication/backends/api.py:81
+#: authentication/backends/drf.py:70
 msgid ""
 "Invalid signature header. Signature string should not contain invalid "
 "characters."
 msgstr ""
 
-#: authentication/backends/api.py:101 authentication/backends/api.py:117
+#: authentication/backends/drf.py:90 authentication/backends/drf.py:106
 msgid "Invalid signature."
 msgstr ""
 
-#: authentication/backends/api.py:108
+#: authentication/backends/drf.py:97
 msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT"
 msgstr ""
 
-#: authentication/backends/api.py:113
+#: authentication/backends/drf.py:102
 msgid "Expired, more than 15 minutes"
 msgstr ""
 
-#: authentication/backends/api.py:120
+#: authentication/backends/drf.py:109
 msgid "User disabled."
 msgstr "用户已禁用"
 
-#: authentication/backends/api.py:138
+#: authentication/backends/drf.py:127
 msgid "Invalid token header. No credentials provided."
 msgstr ""
 
-#: authentication/backends/api.py:141
+#: authentication/backends/drf.py:130
 msgid "Invalid token header. Sign string should not contain spaces."
 msgstr ""
 
-#: authentication/backends/api.py:148
+#: authentication/backends/drf.py:137
 msgid ""
 "Invalid token header. Sign string should not contain invalid characters."
 msgstr ""
 
-#: authentication/backends/api.py:159
+#: authentication/backends/drf.py:148
 msgid "Invalid token or cache refreshed."
 msgstr ""
 
@@ -1749,11 +1859,11 @@ msgstr "禁用或失效"
 
 #: authentication/errors.py:33
 msgid "This account is inactive."
-msgstr "此账户已禁用"
+msgstr "此账号已禁用"
 
 #: authentication/errors.py:34
 msgid "This account is expired"
-msgstr "此账户已过期"
+msgstr "此账号已过期"
 
 #: authentication/errors.py:35
 msgid "Auth backend not match"
@@ -1833,15 +1943,15 @@ msgstr "该 时间段 不被允许登录"
 msgid "SSO auth closed"
 msgstr "SSO 认证关闭了"
 
-#: authentication/errors.py:300 authentication/mixins.py:364
+#: authentication/errors.py:300 authentication/mixins.py:372
 msgid "Your password is too simple, please change it for security"
 msgstr "你的密码过于简单,为了安全,请修改"
 
-#: authentication/errors.py:309 authentication/mixins.py:371
+#: authentication/errors.py:309 authentication/mixins.py:379
 msgid "You should to change your password before login"
 msgstr "登录完成前,请先修改密码"
 
-#: authentication/errors.py:318 authentication/mixins.py:378
+#: authentication/errors.py:318 authentication/mixins.py:386
 msgid "Your password has expired, please reset before logging in"
 msgstr "您的密码已过期,先修改再登录"
 
@@ -1933,22 +2043,42 @@ msgstr "设置手机号码启用"
 msgid "Clear phone number to disable"
 msgstr "清空手机号码禁用"
 
-#: authentication/mixins.py:314
+#: authentication/mixins.py:322
 msgid "The MFA type ({}) is not enabled"
 msgstr "该 MFA ({}) 方式没有启用"
 
-#: authentication/mixins.py:354
+#: authentication/mixins.py:362
 msgid "Please change your password"
 msgstr "请修改密码"
 
-#: authentication/models.py:37
+#: authentication/models.py:33 terminal/serializers/storage.py:30
+msgid "Access key"
+msgstr "Access key"
+
+#: authentication/models.py:40
 msgid "Private Token"
 msgstr "SSH密钥"
 
-#: authentication/models.py:46
+#: authentication/models.py:49
 msgid "Expired"
 msgstr "过期时间"
 
+#: authentication/models.py:53
+msgid "SSO token"
+msgstr ""
+
+#: authentication/models.py:61
+msgid "Connection token"
+msgstr "连接令牌"
+
+#: authentication/models.py:63
+msgid "Can view connection token secret"
+msgstr "可以查看连接令牌密文"
+
+#: authentication/models.py:70
+msgid "Super connection token"
+msgstr "超级连接令牌"
+
 #: authentication/notifications.py:19
 msgid "Different city login reminder"
 msgstr "异地登录提醒"
@@ -1980,7 +2110,7 @@ msgid "Secret"
 msgstr "密钥"
 
 #: authentication/templates/authentication/_access_key_modal.html:33
-#: terminal/notifications.py:92 terminal/notifications.py:140
+#: terminal/notifications.py:90 terminal/notifications.py:138
 msgid "Date"
 msgstr "日期"
 
@@ -1990,14 +2120,14 @@ msgid "Show"
 msgstr "显示"
 
 #: authentication/templates/authentication/_access_key_modal.html:66
-#: settings/serializers/security.py:39 users/models/user.py:458
-#: users/serializers/profile.py:120 users/templates/users/mfa_setting.html:60
+#: settings/serializers/security.py:39 users/models/user.py:469
+#: users/serializers/profile.py:111 users/templates/users/mfa_setting.html:60
 #: users/templates/users/user_verify_mfa.html:36
 msgid "Disable"
 msgstr "禁用"
 
 #: authentication/templates/authentication/_access_key_modal.html:67
-#: users/models/user.py:459 users/serializers/profile.py:121
+#: users/models/user.py:470 users/serializers/profile.py:112
 #: users/templates/users/mfa_setting.html:26
 #: users/templates/users/mfa_setting.html:67
 msgid "Enable"
@@ -2127,22 +2257,22 @@ msgid ""
 "security issues"
 msgstr "如果这次公钥更新不是由你发起的,那么你的账号可能存在安全问题"
 
-#: authentication/templates/authentication/login.html:143
+#: authentication/templates/authentication/login.html:170
 msgid "Welcome back, please enter username and password to login"
 msgstr "欢迎回来,请输入用户名和密码登录"
 
-#: authentication/templates/authentication/login.html:179
+#: authentication/templates/authentication/login.html:206
 #: users/templates/users/forgot_password.html:15
 #: users/templates/users/forgot_password.html:16
 msgid "Forgot password"
 msgstr "忘记密码"
 
-#: authentication/templates/authentication/login.html:186
+#: authentication/templates/authentication/login.html:213
 #: templates/_header_bar.html:83
 msgid "Login"
 msgstr "登录"
 
-#: authentication/templates/authentication/login.html:193
+#: authentication/templates/authentication/login.html:220
 msgid "More login options"
 msgstr "更多登录方式"
 
@@ -2246,7 +2376,7 @@ msgid "The FeiShu is already bound to another user"
 msgstr "该飞书已经绑定其他用户"
 
 #: authentication/views/feishu.py:148 authentication/views/login.py:176
-#: notifications/backends/__init__.py:14 users/models/user.py:609
+#: notifications/backends/__init__.py:14 users/models/user.py:635
 msgid "FeiShu"
 msgstr "飞书"
 
@@ -2278,7 +2408,7 @@ msgstr "正在跳转到 {} 认证"
 msgid "Please enable cookies and try again."
 msgstr "设置你的浏览器支持cookie"
 
-#: authentication/views/login.py:265
+#: authentication/views/login.py:263
 msgid ""
 "Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
 "                  Don't close this page"
@@ -2286,15 +2416,15 @@ msgstr ""
 "等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
 "                  不要关闭本页面"
 
-#: authentication/views/login.py:270
+#: authentication/views/login.py:268
 msgid "No ticket found"
 msgstr "没有发现工单"
 
-#: authentication/views/login.py:304
+#: authentication/views/login.py:302
 msgid "Logout success"
 msgstr "退出登录成功"
 
-#: authentication/views/login.py:305
+#: authentication/views/login.py:303
 msgid "Logout success, return login page"
 msgstr "退出登录成功,返回到登录页面"
 
@@ -2421,7 +2551,7 @@ msgstr "编码数据为 text"
 msgid "Encrypt field using Secret Key"
 msgstr "加密的字段"
 
-#: common/mixins/api/action.py:53
+#: common/mixins/api/action.py:52
 msgid "Request file format may be wrong"
 msgstr "上传的文件格式错误 或 其它类型资源的文件"
 
@@ -2499,11 +2629,11 @@ msgstr "手机号格式不正确"
 
 #: jumpserver/conf.py:292
 msgid "Create account successfully"
-msgstr "创建账户成功"
+msgstr "创建账号成功"
 
 #: jumpserver/conf.py:294
 msgid "Your account has been created successfully"
-msgstr "你的账户已创建成功"
+msgstr "你的账号已创建成功"
 
 #: jumpserver/context_processor.py:17
 msgid "JumpServer Open Source Bastion Host"
@@ -2536,12 +2666,16 @@ msgstr ""
 "div><div>如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运</"
 "div>"
 
+#: notifications/apps.py:7
+msgid "Notifications"
+msgstr "通知"
+
 #: notifications/backends/__init__.py:10 users/forms/profile.py:101
-#: users/models/user.py:551
+#: users/models/user.py:576
 msgid "Email"
 msgstr "邮件"
 
-#: notifications/backends/__init__.py:13
+#: notifications/backends/__init__.py:13 rbac/ztree/tree_nodes.py:257
 msgid "Site message"
 msgstr "站内信"
 
@@ -2553,9 +2687,9 @@ msgstr "等待任务开始"
 msgid "Not has host {} permission"
 msgstr "没有该主机 {} 权限"
 
-#: ops/apps.py:9 ops/notifications.py:15
-msgid "Operations"
-msgstr "运维"
+#: ops/apps.py:9 ops/notifications.py:16
+msgid "App ops"
+msgstr "作业中心"
 
 #: ops/mixin.py:29 ops/mixin.py:92 ops/mixin.py:162
 #: settings/serializers/auth/ldap.py:66
@@ -2602,59 +2736,76 @@ msgstr "单位: 时"
 msgid "Callback"
 msgstr "回调"
 
-#: ops/models/adhoc.py:149
+#: ops/models/adhoc.py:135 terminal/models/task.py:26
+#: xpack/plugins/gathered_user/models.py:73
+msgid "Task"
+msgstr "任务"
+
+#: ops/models/adhoc.py:138
+msgid "Can view task monitor"
+msgstr "可以查看任务监控"
+
+#: ops/models/adhoc.py:154
 msgid "Tasks"
 msgstr "任务"
 
-#: ops/models/adhoc.py:150
+#: ops/models/adhoc.py:155
 msgid "Pattern"
 msgstr "模式"
 
-#: ops/models/adhoc.py:151
+#: ops/models/adhoc.py:156
 msgid "Options"
 msgstr "选项"
 
-#: ops/models/adhoc.py:153
+#: ops/models/adhoc.py:158
 msgid "Run as admin"
 msgstr "再次执行"
 
-#: ops/models/adhoc.py:156
+#: ops/models/adhoc.py:161
 msgid "Become"
 msgstr "Become"
 
-#: ops/models/adhoc.py:157
+#: ops/models/adhoc.py:162
 msgid "Create by"
 msgstr "创建者"
 
-#: ops/models/adhoc.py:246
+#: ops/models/adhoc.py:243
+msgid "AdHoc"
+msgstr ""
+
+#: ops/models/adhoc.py:252
 msgid "Task display"
 msgstr "任务名称"
 
-#: ops/models/adhoc.py:248
+#: ops/models/adhoc.py:254
 msgid "Host amount"
 msgstr "主机数量"
 
-#: ops/models/adhoc.py:250
+#: ops/models/adhoc.py:256
 msgid "Start time"
 msgstr "开始时间"
 
-#: ops/models/adhoc.py:251
+#: ops/models/adhoc.py:257
 msgid "End time"
 msgstr "完成时间"
 
-#: ops/models/adhoc.py:253 ops/models/command.py:29
+#: ops/models/adhoc.py:259 ops/models/command.py:29
 #: terminal/serializers/session.py:39
 msgid "Is finished"
 msgstr "是否完成"
 
-#: ops/models/adhoc.py:255
+#: ops/models/adhoc.py:261
 msgid "Adhoc raw result"
 msgstr "结果"
 
-#: ops/models/adhoc.py:256
+#: ops/models/adhoc.py:262
 msgid "Adhoc result summary"
 msgstr "汇总"
 
+#: ops/models/adhoc.py:339
+msgid "AdHoc execution"
+msgstr "命令执行"
+
 #: ops/models/command.py:32
 msgid "Date finished"
 msgstr "结束日期"
@@ -2671,30 +2822,34 @@ msgstr "命令 `{}` 不允许被执行 ......."
 msgid "Task end"
 msgstr "任务结束"
 
-#: ops/notifications.py:16
+#: ops/models/command.py:160
+msgid "Command execution"
+msgstr "命令执行"
+
+#: ops/notifications.py:17
 msgid "Server performance"
 msgstr "监控告警"
 
-#: ops/notifications.py:22
+#: ops/notifications.py:23
 msgid "Terminal health check warning"
 msgstr "终端健康状况检查警告"
 
-#: ops/notifications.py:63
+#: ops/notifications.py:68
 #, python-brace-format
 msgid "The terminal is offline: {name}"
 msgstr "终端已离线: {name}"
 
-#: ops/notifications.py:68
+#: ops/notifications.py:73
 #, python-brace-format
 msgid "Disk used more than {max_threshold}%: => {value}"
 msgstr "硬盘使用率超过 {max_threshold}%: => {value}"
 
-#: ops/notifications.py:73
+#: ops/notifications.py:78
 #, python-brace-format
 msgid "Memory used more than {max_threshold}%: => {value}"
 msgstr "内存使用率超过 {max_threshold}%: => {value}"
 
-#: ops/notifications.py:78
+#: ops/notifications.py:83
 #, python-brace-format
 msgid "CPU load more than {max_threshold}: => {value}"
 msgstr "CPU 使用率超过 {max_threshold}: => {value}"
@@ -2715,37 +2870,41 @@ msgstr "任务列表"
 msgid "Update task content: {}"
 msgstr "更新任务内容: {}"
 
-#: orgs/api.py:79
+#: orgs/api.py:68
 msgid "The current organization ({}) cannot be deleted"
 msgstr "当前组织 ({}) 不能被删除"
 
-#: orgs/api.py:87
+#: orgs/api.py:76
 msgid "The organization have resource ({}) cannot be deleted"
 msgstr "组织存在资源 ({}) 不能被删除"
 
-#: orgs/mixins/models.py:46 orgs/mixins/serializers.py:25 orgs/models.py:37
-#: orgs/models.py:432 orgs/serializers.py:106
-#: tickets/serializers/ticket/ticket.py:77
+#: orgs/apps.py:7 rbac/tree.py:105
+msgid "App organizations"
+msgstr "组织管理"
+
+#: orgs/mixins/models.py:46 orgs/mixins/serializers.py:25 orgs/models.py:27
+#: orgs/models.py:193 rbac/const.py:7 rbac/models/rolebinding.py:42
+#: rbac/serializers/rolebinding.py:40 tickets/serializers/ticket/ticket.py:77
 msgid "Organization"
 msgstr "组织"
 
-#: orgs/models.py:17 users/const.py:12
-msgid "Organization administrator"
-msgstr "组织管理员"
-
-#: orgs/models.py:18 users/const.py:13
-msgid "Organization auditor"
-msgstr "组织审计员"
-
-#: orgs/models.py:31
+#: orgs/models.py:21
 msgid "GLOBAL"
 msgstr "全局组织"
 
-#: orgs/models.py:434 users/models/user.py:559 users/serializers/user.py:37
-#: users/templates/users/_select_user_modal.html:15
+#: orgs/models.py:29
+msgid "Can view root org"
+msgstr "可以查看全局组织"
+
+#: orgs/models.py:198 rbac/models/role.py:46 rbac/models/rolebinding.py:38
+#: users/models/user.py:584 users/templates/users/_select_user_modal.html:15
 msgid "Role"
 msgstr "角色"
 
+#: perms/apps.py:9
+msgid "App permissions"
+msgstr "授权管理"
+
 #: perms/exceptions.py:9
 msgid "The administrator is modifying permissions. Please wait"
 msgstr "管理员正在修改授权,请稍等"
@@ -2754,14 +2913,58 @@ msgstr "管理员正在修改授权,请稍等"
 msgid "The authorization cannot be revoked for the time being"
 msgstr "该授权暂时不能撤销"
 
-#: perms/models/asset_permission.py:133
+#: perms/models/application_permission.py:40
+msgid "Can view application of permission to user"
+msgstr "可以查看授权给用户的应用"
+
+#: perms/models/application_permission.py:112
+msgid "Permed application"
+msgstr "授权的应用"
+
+#: perms/models/application_permission.py:117
+msgid "Can view user apps"
+msgstr "可以查看用户授权的应用"
+
+#: perms/models/application_permission.py:118
+msgid "Can view usergroup apps"
+msgstr "可以查看用户组授权的应用"
+
+#: perms/models/asset_permission.py:32
+msgid "Can view asset of permission to user"
+msgstr "可以查看授权给用户的资产"
+
+#: perms/models/asset_permission.py:33
+msgid "Can view asset of permission to user group"
+msgstr "可以查看授权给用户组的资产"
+
+#: perms/models/asset_permission.py:136
 msgid "Ungrouped"
 msgstr "未分组"
 
-#: perms/models/asset_permission.py:135
+#: perms/models/asset_permission.py:138
 msgid "Favorite"
 msgstr "收藏夹"
 
+#: perms/models/asset_permission.py:185
+msgid "Permed asset"
+msgstr "授权的资产"
+
+#: perms/models/asset_permission.py:187
+msgid "Can view my assets"
+msgstr "可以查看我的资产"
+
+#: perms/models/asset_permission.py:188
+msgid "Can connect my assets"
+msgstr "可以连接我的资产"
+
+#: perms/models/asset_permission.py:189
+msgid "Can view user assets"
+msgstr "可以查看用户授权的资产"
+
+#: perms/models/asset_permission.py:190
+msgid "Can view usergroup assets"
+msgstr "可以查看用户组授权的资产"
+
 #: perms/models/base.py:55
 msgid "Connect"
 msgstr "连接"
@@ -2793,7 +2996,7 @@ msgstr "剪贴板复制粘贴"
 #: perms/models/base.py:90
 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:58
 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:60
-#: users/models/user.py:590
+#: users/models/user.py:616
 msgid "Date expired"
 msgstr "失效日期"
 
@@ -2836,20 +3039,21 @@ msgstr "组织 ({}) 的应用授权"
 #: perms/serializers/application/permission.py:20
 #: perms/serializers/application/permission.py:41
 #: perms/serializers/asset/permission.py:19
-#: perms/serializers/asset/permission.py:45 users/serializers/user.py:79
+#: perms/serializers/asset/permission.py:45 users/serializers/user.py:139
 msgid "Is valid"
-msgstr "账户是否有效"
+msgstr "账号是否有效"
 
 #: perms/serializers/application/permission.py:21
 #: perms/serializers/application/permission.py:40
 #: perms/serializers/asset/permission.py:20
-#: perms/serializers/asset/permission.py:44 users/serializers/user.py:28
-#: users/serializers/user.py:80
+#: perms/serializers/asset/permission.py:44 users/serializers/user.py:85
+#: users/serializers/user.py:141
 msgid "Is expired"
 msgstr "已过期"
 
 #: perms/serializers/application/permission.py:43
-#: perms/serializers/asset/permission.py:47 users/serializers/group.py:34
+#: perms/serializers/asset/permission.py:47 rbac/serializers/role.py:26
+#: users/serializers/group.py:34
 msgid "Users amount"
 msgstr "用户数量"
 
@@ -2905,43 +3109,587 @@ msgstr ""
 msgid "If you have any question, please contact the administrator"
 msgstr "如果有疑问或需求,请联系系统管理员"
 
-#: settings/api/alibaba_sms.py:30 settings/api/tencent_sms.py:34
+#: rbac/api/role.py:32
+msgid "Internal role, can't be destroy"
+msgstr ""
+
+#: rbac/api/role.py:36
+msgid "The role has been bound to users, can't be destroy"
+msgstr ""
+
+#: rbac/api/role.py:43
+msgid "Internal role, can't be update"
+msgstr ""
+
+#: rbac/api/rolebinding.py:46
+msgid "{} at least one system role"
+msgstr "{} 至少有一个系统角色"
+
+#: rbac/apps.py:7
+msgid "RBAC"
+msgstr "RBAC"
+
+#: rbac/builtin.py:87
+msgid "SystemAdmin"
+msgstr "系统管理员"
+
+#: rbac/builtin.py:90
+msgid "SystemAuditor"
+msgstr "系统审计员"
+
+#: rbac/builtin.py:93
+msgid "SystemComponent"
+msgstr "系统组件"
+
+#: rbac/builtin.py:99
+msgid "OrgAdmin"
+msgstr "组织管理员"
+
+#: rbac/builtin.py:102
+msgid "OrgAuditor"
+msgstr "组织审计员"
+
+#: rbac/builtin.py:105
+msgid "OrgUser"
+msgstr "组织用户"
+
+#: rbac/models/menu.py:13
+msgid "Menu permission"
+msgstr "菜单授权"
+
+#: rbac/models/menu.py:15
+msgid "Can view console view"
+msgstr "可以显示控制台"
+
+#: rbac/models/menu.py:16
+msgid "Can view audit view"
+msgstr "可以显示审计台"
+
+#: rbac/models/menu.py:17
+msgid "Can view workspace view"
+msgstr "可以显示工作台"
+
+#: rbac/models/menu.py:18
+msgid "Can view web terminal"
+msgstr "Web终端"
+
+#: rbac/models/menu.py:19
+msgid "Can view file manager"
+msgstr "文件管理"
+
+#: rbac/models/menu.py:20
+msgid "Can view dashboard"
+msgstr "仪表盘"
+
+#: rbac/models/permission.py:22
+msgid "Permission"
+msgstr "授权"
+
+#: rbac/models/role.py:31 rbac/models/rolebinding.py:32
+msgid "Scope"
+msgstr "范围"
+
+#: rbac/models/role.py:34
+msgid "Permissions"
+msgstr "授权"
+
+#: rbac/models/role.py:36
+msgid "Built-in"
+msgstr "内置"
+
+#: rbac/models/role.py:127 rbac/ztree/tree_nodes.py:230
+msgid "System role"
+msgstr "系统角色"
+
+#: rbac/models/role.py:135 rbac/ztree/tree_nodes.py:227
+msgid "Organization role"
+msgstr "组织角色"
+
+#: rbac/models/rolebinding.py:47
+msgid "Role binding"
+msgstr "角色绑定"
+
+#: rbac/models/rolebinding.py:113
+msgid ""
+"User last role in org, can not be delete, you can remove user from org "
+"instead"
+msgstr "用户最后一个角色,不能删除,你可以将用户从组织移除"
+
+#: rbac/models/rolebinding.py:120
+msgid "Organization role binding"
+msgstr "组织角色绑定"
+
+#: rbac/models/rolebinding.py:134
+msgid "System role binding"
+msgstr "系统角色绑定"
+
+#: rbac/serializers/permission.py:26 users/serializers/profile.py:125
+msgid "Perms"
+msgstr "权限"
+
+#: rbac/serializers/role.py:11
+msgid "Scope display"
+msgstr "范围名称"
+
+#: rbac/serializers/role.py:27
+msgid "Display name"
+msgstr "显示名称"
+
+#: rbac/serializers/rolebinding.py:23
+msgid "Role display"
+msgstr "角色显示"
+
+#: rbac/serializers/rolebinding.py:56
+msgid "Has bound this role"
+msgstr "已经绑定"
+
+#: rbac/tree.py:17 rbac/tree.py:18 rbac/ztree/tree_nodes.py:6
+msgid "All permissions"
+msgstr "所有权限"
+
+#: rbac/tree.py:24 rbac/ztree/tree_nodes.py:12
+msgid "Console view"
+msgstr "控制台"
+
+#: rbac/tree.py:25 rbac/ztree/tree_nodes.py:21
+msgid "Workspace view"
+msgstr "工作台"
+
+#: rbac/tree.py:26 rbac/ztree/tree_nodes.py:24
+msgid "Audit view"
+msgstr "审计台"
+
+#: rbac/tree.py:27 rbac/ztree/tree_nodes.py:51 settings/models.py:140
+msgid "System setting"
+msgstr "系统设置"
+
+#: rbac/tree.py:28
+msgid "Other"
+msgstr "其它"
+
+#: rbac/tree.py:36
+msgid "Accounts"
+msgstr "账号管理"
+
+#: rbac/tree.py:40 rbac/ztree/tree_nodes.py:30
+msgid "Session audits"
+msgstr "会话审计"
+
+#: rbac/tree.py:50
+msgid "Cloud import"
+msgstr "云同步"
+
+#: rbac/tree.py:51
+msgid "Backup account"
+msgstr "备份账号"
+
+#: rbac/tree.py:52
+msgid "Gather account"
+msgstr "收集账号"
+
+#: rbac/tree.py:53
+msgid "App change auth"
+msgstr "应用改密"
+
+#: rbac/tree.py:54
+msgid "Asset change auth"
+msgstr "资产改密"
+
+#: rbac/tree.py:55 rbac/ztree/tree_nodes.py:198
+msgid "Terminal setting"
+msgstr "终端设置"
+
+#: rbac/tree.py:56 rbac/ztree/tree_nodes.py:42
+msgid "My assets"
+msgstr "我的资产"
+
+#: rbac/tree.py:57
+msgid "My apps"
+msgstr "我的应用"
+
+#: rbac/tree.py:106
+msgid "Ticket comment"
+msgstr "工单评论"
+
+#: rbac/tree.py:107
+msgid "Common setting"
+msgstr "一般设置"
+
+#: rbac/tree.py:236 rbac/ztree/tree.py:159
+msgid "View"
+msgstr "查看"
+
+#: rbac/ztree/tree.py:103 rbac/ztree/tree_nodes.py:96
+msgid "Detail"
+msgstr "详情"
+
+#: rbac/ztree/tree_nodes.py:9
+msgid "View menu"
+msgstr "视图菜单"
+
+#: rbac/ztree/tree_nodes.py:15
+msgid "User management"
+msgstr "用户管理"
+
+#: rbac/ztree/tree_nodes.py:18
+msgid "User list"
+msgstr "用户列表"
+
+#: rbac/ztree/tree_nodes.py:33
+msgid "Online/Offline Session record"
+msgstr "在线/离线会话记录"
+
+#: rbac/ztree/tree_nodes.py:36
+msgid "Asset management"
+msgstr "资产管理"
+
+#: rbac/ztree/tree_nodes.py:39 rbac/ztree/tree_nodes.py:117
+msgid "Asset list"
+msgstr "资产列表"
+
+#: rbac/ztree/tree_nodes.py:45
+msgid "My application"
+msgstr "我的应用"
+
+#: rbac/ztree/tree_nodes.py:48 rbac/ztree/tree_nodes.py:254
+msgid "Bulk command"
+msgstr "批量命令"
+
+#: rbac/ztree/tree_nodes.py:54
+msgid "Ticket system"
+msgstr "工单系统"
+
+#: rbac/ztree/tree_nodes.py:57 templates/_header_bar.html:12
+msgid "Help"
+msgstr "帮助"
+
+#: rbac/ztree/tree_nodes.py:60
+msgid "API permission"
+msgstr "API权限"
+
+#: rbac/ztree/tree_nodes.py:63
+msgid "Application management"
+msgstr "应用管理"
+
+#: rbac/ztree/tree_nodes.py:66
+msgid "Account management"
+msgstr "账号管理"
+
+#: rbac/ztree/tree_nodes.py:69
+msgid "Permission management"
+msgstr "权限管理"
+
+#: rbac/ztree/tree_nodes.py:72
+msgid "Access control"
+msgstr "访问控制"
+
+#: rbac/ztree/tree_nodes.py:75
+msgid "Job center"
+msgstr "作业中心"
+
+#: rbac/ztree/tree_nodes.py:78
+msgid "Session audit"
+msgstr "会话审计"
+
+#: rbac/ztree/tree_nodes.py:81
+msgid "Log audit"
+msgstr "日志审计"
+
+#: rbac/ztree/tree_nodes.py:87
+msgid "Role list"
+msgstr "角色列表"
+
+#: rbac/ztree/tree_nodes.py:93
+msgid "User login acl"
+msgstr "用户登录规则"
+
+#: rbac/ztree/tree_nodes.py:99
+msgid "Permission list"
+msgstr "权限列表"
+
+#: rbac/ztree/tree_nodes.py:102
+msgid "Node tree"
+msgstr "节点树"
+
+#: rbac/ztree/tree_nodes.py:105
+msgid "Cloud sync"
+msgstr "云同步"
+
+#: rbac/ztree/tree_nodes.py:108
+msgid "Sync instance task list"
+msgstr "同步实例任务列表"
+
+#: rbac/ztree/tree_nodes.py:111 rbac/ztree/tree_nodes.py:120
+msgid "Account list"
+msgstr "账号列表"
+
+#: rbac/ztree/tree_nodes.py:114
+msgid "Common/Admin User"
+msgstr "普通/特权用户"
+
+#: rbac/ztree/tree_nodes.py:129
+msgid "Platform list"
+msgstr "平台列表"
+
+#: rbac/ztree/tree_nodes.py:132
+msgid "Label management"
+msgstr "标签管理"
+
+#: rbac/ztree/tree_nodes.py:135
+msgid "Remote application"
+msgstr "远程应用"
+
+#: rbac/ztree/tree_nodes.py:138 rbac/ztree/tree_nodes.py:192
+msgid "Database application"
+msgstr "数据库应用"
+
+#: rbac/ztree/tree_nodes.py:141 rbac/ztree/tree_nodes.py:195
+msgid "Kubernetes"
+msgstr ""
+
+#: rbac/ztree/tree_nodes.py:144
+msgid "Asset account"
+msgstr "资产账号"
+
+#: rbac/ztree/tree_nodes.py:150 xpack/plugins/gathered_user/meta.py:11
+msgid "Gathered user"
+msgstr "收集用户"
+
+#: rbac/ztree/tree_nodes.py:153
+msgid "Gathered user list"
+msgstr "收集用户列表"
+
+#: rbac/ztree/tree_nodes.py:156
+msgid "Gathered user task list"
+msgstr "收集用户任务列表"
+
+#: rbac/ztree/tree_nodes.py:159 xpack/plugins/change_auth_plan/meta.py:9
+#: xpack/plugins/change_auth_plan/models/asset.py:123
+msgid "Change auth plan"
+msgstr "改密计划"
+
+#: rbac/ztree/tree_nodes.py:162
+#: xpack/plugins/change_auth_plan/models/asset.py:67
+msgid "Asset change auth plan"
+msgstr "资产改密计划"
+
+#: rbac/ztree/tree_nodes.py:165 xpack/plugins/change_auth_plan/models/app.py:46
+#: xpack/plugins/change_auth_plan/models/app.py:95
+msgid "Application change auth plan"
+msgstr "应用改密计划"
+
+#: rbac/ztree/tree_nodes.py:168
+msgid "Account backup"
+msgstr "账号备份"
+
+#: rbac/ztree/tree_nodes.py:177
+msgid "Asset login"
+msgstr "资产登录"
+
+#: rbac/ztree/tree_nodes.py:180
+msgid "Task list"
+msgstr "任务列表"
+
+#: rbac/ztree/tree_nodes.py:183 terminal/models/command.py:24
+msgid "Command record"
+msgstr "命令记录"
+
+#: rbac/ztree/tree_nodes.py:186
+msgid "File transfer"
+msgstr "文件传输"
+
+#: rbac/ztree/tree_nodes.py:189
+msgid "Remote App"
+msgstr "远程应用"
+
+#: rbac/ztree/tree_nodes.py:201
+msgid "Terminal management"
+msgstr "终端管理"
+
+#: rbac/ztree/tree_nodes.py:204 terminal/models/storage.py:113
+#: terminal/models/terminal.py:108
+msgid "Command storage"
+msgstr "命令存储"
+
+#: rbac/ztree/tree_nodes.py:207 terminal/models/storage.py:173
+#: terminal/models/terminal.py:109
+msgid "Replay storage"
+msgstr "录像存储"
+
+#: rbac/ztree/tree_nodes.py:210
+msgid "Organization management"
+msgstr "组织管理"
+
+#: rbac/ztree/tree_nodes.py:213 xpack/plugins/license/meta.py:11
+#: xpack/plugins/license/models.py:127
+msgid "License"
+msgstr "许可证"
+
+#: rbac/ztree/tree_nodes.py:218
+msgid "View all permission"
+msgstr "查看所有权限"
+
+#: rbac/ztree/tree_nodes.py:221
+msgid "Domain list"
+msgstr "网域列表"
+
+#: rbac/ztree/tree_nodes.py:224
+msgid "Gateway list"
+msgstr "网关列表"
+
+#: rbac/ztree/tree_nodes.py:233
+msgid "Run gather user task"
+msgstr "执行收集用户任务"
+
+#: rbac/ztree/tree_nodes.py:236
+msgid "Run asset change auth plan"
+msgstr "执行资产改密计划"
+
+#: rbac/ztree/tree_nodes.py:239
+msgid "Run application change auth plan"
+msgstr "执行应用改密计划"
+
+#: rbac/ztree/tree_nodes.py:242
+msgid "Run account backup plan"
+msgstr "执行账号备份计划"
+
+#: rbac/ztree/tree_nodes.py:245
+msgid "Run task"
+msgstr "运行任务"
+
+#: rbac/ztree/tree_nodes.py:248
+msgid "View task version"
+msgstr "查看任务版本"
+
+#: rbac/ztree/tree_nodes.py:251
+msgid "View execution history"
+msgstr "查看执行历史"
+
+#: rbac/ztree/tree_nodes.py:260
+msgid "Message subscription"
+msgstr "消息订阅"
+
+#: rbac/ztree/tree_nodes.py:263
+msgid "Component monitor"
+msgstr "组件监控"
+
+#: rbac/ztree/tree_nodes.py:266
+msgid "View my/assigned ticket"
+msgstr "查看我的/待审批工单"
+
+#: rbac/ztree/tree_nodes.py:269
+msgid "Create asset/application ticket"
+msgstr "创建资产/应用申请工单"
+
+#: rbac/ztree/tree_nodes.py:272
+msgid "Change/close ticket"
+msgstr "更新/关闭工单"
+
+#: rbac/ztree/tree_nodes.py:275
+msgid "View some of the assets searched"
+msgstr "查看搜索的部分资产"
+
+#: rbac/ztree/tree_nodes.py:282
+msgid "Overview"
+msgstr "概览"
+
+#: rbac/ztree/tree_nodes.py:287 rbac/ztree/tree_nodes.py:296
+msgid "View permission user"
+msgstr "查看授权用户"
+
+#: rbac/ztree/tree_nodes.py:290 rbac/ztree/tree_nodes.py:299
+msgid "Add user to role"
+msgstr "添加用户到角色"
+
+#: rbac/ztree/tree_nodes.py:293 rbac/ztree/tree_nodes.py:302
+msgid "Remove user from role"
+msgstr "从角色移除用户"
+
+#: rbac/ztree/tree_nodes.py:305
+msgid "Run sync instance task"
+msgstr "执行同步实例任务"
+
+#: settings/api/alibaba_sms.py:31 settings/api/tencent_sms.py:35
 msgid "test_phone is required"
 msgstr "测试手机号 该字段是必填项。"
 
-#: settings/api/alibaba_sms.py:51 settings/api/dingtalk.py:31
-#: settings/api/feishu.py:35 settings/api/tencent_sms.py:56
-#: settings/api/wecom.py:36
+#: settings/api/alibaba_sms.py:52 settings/api/dingtalk.py:28
+#: settings/api/feishu.py:36 settings/api/tencent_sms.py:57
+#: settings/api/wecom.py:37
 msgid "Test success"
 msgstr "测试成功"
 
-#: settings/api/email.py:22
+#: settings/api/email.py:20
 msgid "Test mail sent to {}, please check"
 msgstr "邮件已经发送{}, 请检查"
 
-#: settings/api/ldap.py:157
+#: settings/api/ldap.py:166
 msgid "Synchronization start, please wait."
 msgstr "同步开始,请稍等"
 
-#: settings/api/ldap.py:161
+#: settings/api/ldap.py:170
 msgid "Synchronization is running, please wait."
 msgstr "同步正在运行,请稍等"
 
-#: settings/api/ldap.py:166
+#: settings/api/ldap.py:175
 msgid "Synchronization error: {}"
 msgstr "同步错误: {}"
 
-#: settings/api/ldap.py:199
+#: settings/api/ldap.py:211
 msgid "Get ldap users is None"
 msgstr "获取 LDAP 用户为 None"
 
-#: settings/api/ldap.py:208
+#: settings/api/ldap.py:220
 msgid "Imported {} users successfully (Organization: {})"
 msgstr "成功导入 {} 个用户 ( 组织: {} )"
 
-#: settings/models.py:195 users/templates/users/reset_password.html:29
-msgid "Setting"
-msgstr "设置"
+#: settings/apps.py:7
+msgid "Settings"
+msgstr "系统设置"
+
+#: settings/models.py:142
+msgid "Can change basic setting"
+msgstr "基本设置"
+
+#: settings/models.py:143
+msgid "Can change email setting"
+msgstr "邮件设置"
+
+#: settings/models.py:144
+msgid "Can change auth setting"
+msgstr "认证设置"
+
+#: settings/models.py:145
+msgid "Can sys msg sub setting"
+msgstr "消息订阅设置"
+
+#: settings/models.py:146
+msgid "Can change sms setting"
+msgstr "短信设置"
+
+#: settings/models.py:147
+msgid "Can change security setting"
+msgstr "安全设置"
+
+#: settings/models.py:148
+msgid "Can change clean setting"
+msgstr "定期清理"
+
+#: settings/models.py:149
+msgid "Can change interface setting"
+msgstr "界面设置"
+
+#: settings/models.py:150
+msgid "Can change license setting"
+msgstr "许可证设置"
+
+#: settings/models.py:151
+msgid "Can change terminal setting"
+msgstr "终端设置"
+
+#: settings/models.py:152
+msgid "Can change other setting"
+msgstr "其它设置"
 
 #: settings/serializers/auth/base.py:10
 msgid "CAS Auth"
@@ -3713,10 +4461,6 @@ msgstr ""
 "根据登录 IP 是否所属常用登录城市进行判断,若账号在非常用城市登录,会发送异地"
 "登录提醒"
 
-#: settings/serializers/sms.py:7
-msgid "Label"
-msgstr "标签"
-
 #: settings/serializers/terminal.py:13
 msgid "Auto"
 msgstr "自动"
@@ -3767,108 +4511,108 @@ msgstr "RDP 访问地址, 如: dev.jumpserver.org:3389"
 msgid "Enable XRDP"
 msgstr "启用 XRDP 服务"
 
-#: settings/utils/ldap.py:415
+#: settings/utils/ldap.py:417
 msgid "ldap:// or ldaps:// protocol is used."
 msgstr "使用 ldap:// 或 ldaps:// 协议"
 
-#: settings/utils/ldap.py:426
+#: settings/utils/ldap.py:428
 msgid "Host or port is disconnected: {}"
 msgstr "主机或端口不可连接: {}"
 
-#: settings/utils/ldap.py:428
+#: settings/utils/ldap.py:430
 msgid "The port is not the port of the LDAP service: {}"
 msgstr "端口不是LDAP服务端口: {}"
 
-#: settings/utils/ldap.py:430
+#: settings/utils/ldap.py:432
 msgid "Please add certificate: {}"
 msgstr "请添加证书"
 
-#: settings/utils/ldap.py:434 settings/utils/ldap.py:461
-#: settings/utils/ldap.py:491 settings/utils/ldap.py:519
+#: settings/utils/ldap.py:436 settings/utils/ldap.py:463
+#: settings/utils/ldap.py:493 settings/utils/ldap.py:521
 msgid "Unknown error: {}"
 msgstr "未知错误: {}"
 
-#: settings/utils/ldap.py:448
+#: settings/utils/ldap.py:450
 msgid "Bind DN or Password incorrect"
 msgstr "绑定DN或密码错误"
 
-#: settings/utils/ldap.py:455
+#: settings/utils/ldap.py:457
 msgid "Please enter Bind DN: {}"
 msgstr "请输入绑定DN: {}"
 
-#: settings/utils/ldap.py:457
+#: settings/utils/ldap.py:459
 msgid "Please enter Password: {}"
 msgstr "请输入密码: {}"
 
-#: settings/utils/ldap.py:459
+#: settings/utils/ldap.py:461
 msgid "Please enter correct Bind DN and Password: {}"
 msgstr "请输入正确的绑定DN和密码: {}"
 
-#: settings/utils/ldap.py:477
+#: settings/utils/ldap.py:479
 msgid "Invalid User OU or User search filter: {}"
 msgstr "不合法的用户OU或用户过滤器: {}"
 
-#: settings/utils/ldap.py:508
+#: settings/utils/ldap.py:510
 msgid "LDAP User attr map not include: {}"
 msgstr "LDAP属性映射没有包含: {}"
 
-#: settings/utils/ldap.py:515
+#: settings/utils/ldap.py:517
 msgid "LDAP User attr map is not dict"
 msgstr "LDAP属性映射不合法"
 
-#: settings/utils/ldap.py:534
+#: settings/utils/ldap.py:536
 msgid "LDAP authentication is not enabled"
 msgstr "LDAP认证没有启用"
 
-#: settings/utils/ldap.py:552
+#: settings/utils/ldap.py:554
 msgid "Error (Invalid LDAP server): {}"
 msgstr "错误 (不合法的LDAP服务器地址): {}"
 
-#: settings/utils/ldap.py:554
+#: settings/utils/ldap.py:556
 msgid "Error (Invalid Bind DN): {}"
 msgstr "错误(不合法的绑定DN): {}"
 
-#: settings/utils/ldap.py:556
+#: settings/utils/ldap.py:558
 msgid "Error (Invalid LDAP User attr map): {}"
 msgstr "错误(不合法的LDAP属性映射): {}"
 
-#: settings/utils/ldap.py:558
+#: settings/utils/ldap.py:560
 msgid "Error (Invalid User OU or User search filter): {}"
 msgstr "错误(不合法的用户OU或用户过滤器): {}"
 
-#: settings/utils/ldap.py:560
+#: settings/utils/ldap.py:562
 msgid "Error (Not enabled LDAP authentication): {}"
 msgstr "错误(没有启用LDAP认证): {}"
 
-#: settings/utils/ldap.py:562
+#: settings/utils/ldap.py:564
 msgid "Error (Unknown): {}"
 msgstr "错误(未知): {}"
 
-#: settings/utils/ldap.py:565
+#: settings/utils/ldap.py:567
 msgid "Succeed: Match {} s user"
 msgstr "成功匹配 {} 个用户"
 
-#: settings/utils/ldap.py:598
+#: settings/utils/ldap.py:600
 msgid "Authentication failed (configuration incorrect): {}"
 msgstr "认证失败(配置错误): {}"
 
-#: settings/utils/ldap.py:600
+#: settings/utils/ldap.py:602
 msgid "Authentication failed (before login check failed): {}"
 msgstr "认证失败(登录前检查失败): {}"
 
-#: settings/utils/ldap.py:602
+#: settings/utils/ldap.py:604
 msgid "Authentication failed (username or password incorrect): {}"
 msgstr "认证失败 (用户名或密码不正确): {}"
 
-#: settings/utils/ldap.py:604
+#: settings/utils/ldap.py:606
 msgid "Authentication failed (Unknown): {}"
 msgstr "认证失败: (未知): {}"
 
-#: settings/utils/ldap.py:607
+#: settings/utils/ldap.py:609
 msgid "Authentication success: {}"
 msgstr "认证成功: {}"
 
-#: templates/_base_list.html:37 templates/_user_profile.html:23
+#: templates/_base_list.html:37
 msgid "Search"
 msgstr "搜索"
 
@@ -3904,10 +4648,6 @@ msgstr "下载更新的模板或使用导出的csv格式"
 msgid "Download the update template"
 msgstr "下载更新模版"
 
-#: templates/_header_bar.html:12
-msgid "Help"
-msgstr "帮助"
-
 #: templates/_header_bar.html:19
 msgid "Docs"
 msgstr "文档"
@@ -3916,8 +4656,7 @@ msgstr "文档"
 msgid "Commercial support"
 msgstr "商业支持"
 
-#: templates/_header_bar.html:70 templates/_nav.html:30
-#: templates/_nav_user.html:37 users/forms/profile.py:43
+#: templates/_header_bar.html:70 users/forms/profile.py:43
 #: users/templates/users/user_password_update.html:39
 msgid "Profile"
 msgstr "个人信息"
@@ -3945,11 +4684,11 @@ msgid ""
 "            "
 msgstr ""
 "\n"
-"                您的账户已经过期,请联系管理员。            "
+"                您的账号已经过期,请联系管理员。            "
 
 #: templates/_message.html:13
 msgid "Your account will at"
-msgstr "您的账户将于"
+msgstr "您的账号将于"
 
 #: templates/_message.html:13 templates/_message.html:30
 msgid "expired. "
@@ -4023,137 +4762,6 @@ msgstr "等待:"
 msgid "The verification code has been sent"
 msgstr "验证码已发送"
 
-#: templates/_nav.html:7
-msgid "Dashboard"
-msgstr "仪表盘"
-
-#: templates/_nav.html:20
-msgid "User list"
-msgstr "用户列表"
-
-#: templates/_nav.html:42
-msgid "Asset list"
-msgstr "资产列表"
-
-#: templates/_nav.html:43
-msgid "Domain list"
-msgstr "网域列表"
-
-#: templates/_nav.html:47
-msgid "Command filters"
-msgstr "命令过滤"
-
-#: templates/_nav.html:49
-msgid "Platform list"
-msgstr "平台列表"
-
-#: templates/_nav.html:64 templates/_nav.html:82 templates/_nav_user.html:16
-msgid "RemoteApp"
-msgstr "远程应用"
-
-#: templates/_nav.html:66 templates/_nav.html:86 templates/_nav_user.html:22
-#: users/templates/users/user_database_app_permission.html:39
-#: users/templates/users/user_database_app_permission.html:64
-msgid "DatabaseApp"
-msgstr "数据库应用"
-
-#: templates/_nav.html:75
-msgid "Perms"
-msgstr "权限管理"
-
-#: templates/_nav.html:97 terminal/notifications.py:24
-msgid "Sessions"
-msgstr "会话管理"
-
-#: templates/_nav.html:100
-msgid "Session online"
-msgstr "在线会话"
-
-#: templates/_nav.html:101
-msgid "Session offline"
-msgstr "历史会话"
-
-#: templates/_nav.html:102
-msgid "Commands"
-msgstr "命令记录"
-
-#: templates/_nav.html:105 templates/_nav_user.html:42
-msgid "Web terminal"
-msgstr "Web终端"
-
-#: templates/_nav.html:106 templates/_nav_user.html:47
-msgid "File manager"
-msgstr "文件管理"
-
-#: templates/_nav.html:110 terminal/apps.py:9
-#: terminal/serializers/session.py:38
-msgid "Terminal"
-msgstr "终端"
-
-#: templates/_nav.html:121
-msgid "Job Center"
-msgstr "作业中心"
-
-#: templates/_nav.html:124
-msgid "Task list"
-msgstr "任务列表"
-
-#: templates/_nav.html:125 templates/_nav.html:153
-msgid "Batch command"
-msgstr "批量命令"
-
-#: templates/_nav.html:127
-msgid "Task monitor"
-msgstr "任务监控"
-
-#: templates/_nav.html:137
-msgid "Tickets"
-msgstr "工单管理"
-
-#: templates/_nav.html:146
-msgid "Audits"
-msgstr "日志审计"
-
-#: templates/_nav.html:149
-msgid "Login log"
-msgstr "登录日志"
-
-#: templates/_nav.html:150
-msgid "FTP log"
-msgstr "FTP日志"
-
-#: templates/_nav.html:151
-msgid "Operate log"
-msgstr "操作日志"
-
-#: templates/_nav.html:152
-msgid "Password change log"
-msgstr "改密日志"
-
-#: templates/_nav.html:163
-msgid "XPack"
-msgstr "XPack"
-
-#: templates/_nav.html:171
-msgid "Account list"
-msgstr "账户列表"
-
-#: templates/_nav.html:172
-msgid "Sync instance"
-msgstr "同步实例"
-
-#: templates/_nav.html:187
-msgid "Settings"
-msgstr "系统设置"
-
-#: templates/_nav_user.html:4
-msgid "My assets"
-msgstr "我的资产"
-
-#: templates/_nav_user.html:31
-msgid "Command execution"
-msgstr "命令执行"
-
 #: templates/_pagination.html:59
 msgid ""
 "Displays the results of items _START_ to _END_; A total of _TOTAL_ entries"
@@ -4362,31 +4970,31 @@ msgstr "Jmservisor 是在 windows 远程应用发布服务器中用来拉起远
 msgid "Filters"
 msgstr "过滤"
 
-#: terminal/api/session.py:192
+#: terminal/api/session.py:211
 msgid "Session does not exist: {}"
 msgstr "会话不存在: {}"
 
-#: terminal/api/session.py:195
+#: terminal/api/session.py:214
 msgid "Session is finished or the protocol not supported"
 msgstr "会话已经完成或协议不支持"
 
-#: terminal/api/session.py:200
+#: terminal/api/session.py:219
 msgid "User does not exist: {}"
 msgstr "用户不存在: {}"
 
-#: terminal/api/session.py:207
+#: terminal/api/session.py:227
 msgid "User does not have permission"
 msgstr "用户没有权限"
 
-#: terminal/api/sharing.py:28
+#: terminal/api/sharing.py:30
 msgid "Secure session sharing settings is disabled"
 msgstr "未开启会话共享"
 
-#: terminal/api/storage.py:30
+#: terminal/api/storage.py:28
 msgid "Deleting the default storage is not allowed"
 msgstr "不允许删除默认存储配置"
 
-#: terminal/api/storage.py:33
+#: terminal/api/storage.py:31
 msgid "Cannot delete storage that is being used"
 msgstr "不允许删除正在使用的存储配置"
 
@@ -4398,22 +5006,26 @@ msgstr "命令存储"
 msgid "Invalid"
 msgstr "无效"
 
-#: terminal/api/storage.py:122
+#: terminal/api/storage.py:119
 msgid "Test failure: {}"
 msgstr "测试失败: {}"
 
-#: terminal/api/storage.py:125
+#: terminal/api/storage.py:122
 msgid "Test successful"
 msgstr "测试成功"
 
-#: terminal/api/storage.py:127
+#: terminal/api/storage.py:124
 msgid "Test failure: Account invalid"
-msgstr "测试失败: 账户无效"
+msgstr "测试失败: 账号无效"
 
-#: terminal/api/terminal.py:41
+#: terminal/api/terminal.py:39
 msgid "Have online sessions"
 msgstr "有在线会话"
 
+#: terminal/apps.py:9
+msgid "Terminals"
+msgstr "终端管理"
+
 #: terminal/backends/command/es.py:27
 msgid "Invalid elasticsearch config"
 msgstr "无效的 Elasticsearch 配置"
@@ -4435,8 +5047,8 @@ msgstr "输入"
 msgid "Output"
 msgstr "输出"
 
-#: terminal/backends/command/models.py:24 terminal/models/sharing.py:15
-#: terminal/models/sharing.py:58
+#: terminal/backends/command/models.py:24 terminal/models/replay.py:9
+#: terminal/models/sharing.py:17 terminal/models/sharing.py:64
 #: terminal/templates/terminal/_msg_command_alert.html:10
 msgid "Session"
 msgstr "会话"
@@ -4462,20 +5074,20 @@ msgstr "时间戳"
 msgid "Remote Address"
 msgstr "远端地址"
 
-#: terminal/const.py:32
+#: terminal/const.py:33
 msgid "Critical"
 msgstr "严重"
 
-#: terminal/const.py:33
+#: terminal/const.py:34
 msgid "High"
 msgstr "较高"
 
-#: terminal/const.py:34 users/templates/users/reset_password.html:50
+#: terminal/const.py:35 users/templates/users/reset_password.html:50
 #: users/templates/users/user_password_update.html:104
 msgid "Normal"
 msgstr "正常"
 
-#: terminal/const.py:35
+#: terminal/const.py:36
 msgid "Offline"
 msgstr "离线"
 
@@ -4487,60 +5099,100 @@ msgstr "不支持批量创建"
 msgid "Storage is invalid"
 msgstr "存储无效"
 
-#: terminal/models/session.py:46 terminal/models/sharing.py:81
+#: terminal/models/replay.py:12
+msgid "Session replay"
+msgstr "会话录像"
+
+#: terminal/models/replay.py:14
+msgid "Can upload session replay"
+msgstr "可以上传会话录像"
+
+#: terminal/models/replay.py:15
+msgid "Can download session replay"
+msgstr "可以下载会话录像"
+
+#: terminal/models/session.py:48 terminal/models/sharing.py:87
 msgid "Login from"
 msgstr "登录来源"
 
-#: terminal/models/session.py:50
+#: terminal/models/session.py:52
 msgid "Replay"
 msgstr "回放"
 
-#: terminal/models/session.py:55
+#: terminal/models/session.py:57
 msgid "Date end"
 msgstr "结束日期"
 
-#: terminal/models/sharing.py:20
+#: terminal/models/session.py:242
+msgid "Session record"
+msgstr "会话记录"
+
+#: terminal/models/session.py:244
+msgid "Can monitor session"
+msgstr "可以监控会话"
+
+#: terminal/models/session.py:245
+msgid "Can share session"
+msgstr "可以分享会话"
+
+#: terminal/models/session.py:246
+msgid "Can terminate session"
+msgstr "可以终断会话"
+
+#: terminal/models/session.py:247
+msgid "Can validate session action perm"
+msgstr "可以验证会话动作权限"
+
+#: terminal/models/sharing.py:22
 msgid "Creator"
 msgstr "创建者"
 
-#: terminal/models/sharing.py:22 terminal/models/sharing.py:60
+#: terminal/models/sharing.py:24 terminal/models/sharing.py:66
 msgid "Verify code"
 msgstr "验证码"
 
-#: terminal/models/sharing.py:27
+#: terminal/models/sharing.py:29
 msgid "Expired time (min)"
 msgstr "过期时间 (分)"
 
-#: terminal/models/sharing.py:48
-msgid "Link not active"
-msgstr "链接失效"
-
-#: terminal/models/sharing.py:50
-msgid "Link expired"
-msgstr "链接过期"
-
-#: terminal/models/sharing.py:63
+#: terminal/models/sharing.py:34 terminal/models/sharing.py:69
 msgid "Session sharing"
 msgstr "会话分享"
 
-#: terminal/models/sharing.py:67 terminal/serializers/sharing.py:49
+#: terminal/models/sharing.py:36
+msgid "Can add super session sharing"
+msgstr "可以创建超级会话分享"
+
+#: terminal/models/sharing.py:54
+msgid "Link not active"
+msgstr "链接失效"
+
+#: terminal/models/sharing.py:56
+msgid "Link expired"
+msgstr "链接过期"
+
+#: terminal/models/sharing.py:73 terminal/serializers/sharing.py:49
 msgid "Joiner"
 msgstr "加入者"
 
-#: terminal/models/sharing.py:70
+#: terminal/models/sharing.py:76
 msgid "Date joined"
 msgstr "加入日期"
 
-#: terminal/models/sharing.py:73
+#: terminal/models/sharing.py:79
 msgid "Date left"
 msgstr "结束日期"
 
-#: terminal/models/sharing.py:91
+#: terminal/models/sharing.py:97
 #: xpack/plugins/change_auth_plan/models/base.py:192
 msgid "Finished"
 msgstr "结束"
 
-#: terminal/models/sharing.py:111
+#: terminal/models/sharing.py:102
+msgid "Session join record"
+msgstr "会话加入记录"
+
+#: terminal/models/sharing.py:118
 msgid "Invalid verification code"
 msgstr "验证码不正确"
 
@@ -4596,23 +5248,23 @@ msgstr "SSH端口"
 msgid "HTTP Port"
 msgstr "HTTP端口"
 
-#: terminal/models/terminal.py:108
-msgid "Command storage"
-msgstr "命令存储"
+#: terminal/models/terminal.py:183 terminal/serializers/session.py:38
+msgid "Terminal"
+msgstr "终端"
 
-#: terminal/models/terminal.py:109
-msgid "Replay storage"
-msgstr "录像存储"
+#: terminal/notifications.py:22
+msgid "Sessions"
+msgstr "会话管理"
 
-#: terminal/notifications.py:70
+#: terminal/notifications.py:68
 msgid "Danger command alert"
 msgstr "危险命令告警"
 
-#: terminal/notifications.py:91 terminal/notifications.py:139
+#: terminal/notifications.py:89 terminal/notifications.py:137
 msgid "Level"
 msgstr "级别"
 
-#: terminal/notifications.py:109
+#: terminal/notifications.py:107
 msgid "Batch danger command alert"
 msgstr "批量危险命令告警"
 
@@ -4656,60 +5308,57 @@ msgstr "端点无效: 移除路径 `{}`"
 msgid "Bucket"
 msgstr "桶名称"
 
-#: terminal/serializers/storage.py:30
-msgid "Access key"
-msgstr "Access key"
-
-#: terminal/serializers/storage.py:34 users/models/user.py:582
+#: terminal/serializers/storage.py:34 users/models/user.py:608
 msgid "Secret key"
 msgstr "密钥"
 
 #: terminal/serializers/storage.py:39 terminal/serializers/storage.py:51
 #: terminal/serializers/storage.py:81 terminal/serializers/storage.py:91
+#: terminal/serializers/storage.py:99
 msgid "Endpoint"
 msgstr "端点"
 
-#: terminal/serializers/storage.py:66 xpack/plugins/cloud/models.py:214
+#: terminal/serializers/storage.py:66 xpack/plugins/cloud/models.py:217
 msgid "Region"
 msgstr "地域"
 
-#: terminal/serializers/storage.py:101
+#: terminal/serializers/storage.py:110
 msgid "Container name"
 msgstr "容器名称"
 
-#: terminal/serializers/storage.py:103
+#: terminal/serializers/storage.py:112
 msgid "Account name"
-msgstr "账户名称"
+msgstr "账号名称"
 
-#: terminal/serializers/storage.py:104
+#: terminal/serializers/storage.py:113
 msgid "Account key"
-msgstr "账户密钥"
+msgstr "账号密钥"
 
-#: terminal/serializers/storage.py:107
+#: terminal/serializers/storage.py:116
 msgid "Endpoint suffix"
 msgstr "端点后缀"
 
-#: terminal/serializers/storage.py:128
+#: terminal/serializers/storage.py:138
 msgid "The address format is incorrect"
 msgstr "地址格式不正确"
 
-#: terminal/serializers/storage.py:135
+#: terminal/serializers/storage.py:145
 msgid "Host invalid"
 msgstr "主机无效"
 
-#: terminal/serializers/storage.py:138
+#: terminal/serializers/storage.py:148
 msgid "Port invalid"
 msgstr "端口无效"
 
-#: terminal/serializers/storage.py:154
+#: terminal/serializers/storage.py:164
 msgid "Index"
 msgstr "索引"
 
-#: terminal/serializers/storage.py:156
+#: terminal/serializers/storage.py:166
 msgid "Doc type"
 msgstr "文档类型"
 
-#: terminal/serializers/storage.py:158
+#: terminal/serializers/storage.py:168
 msgid "Ignore Certificate Verification"
 msgstr "忽略证书认证"
 
@@ -4725,6 +5374,10 @@ msgstr "没有发现"
 msgid "view"
 msgstr "查看"
 
+#: tickets/apps.py:7
+msgid "Tickets"
+msgstr "工单管理"
+
 #: tickets/const.py:8
 msgid "General"
 msgstr "一般"
@@ -4932,7 +5585,7 @@ msgid "Body"
 msgstr "内容"
 
 #: tickets/models/flow.py:19 tickets/models/flow.py:61
-#: tickets/models/ticket.py:29
+#: tickets/models/ticket.py:30
 msgid "Approve level"
 msgstr "审批级别"
 
@@ -4952,42 +5605,58 @@ msgstr "工单批准信息"
 msgid "Ticket flow"
 msgstr "工单流程"
 
-#: tickets/models/ticket.py:42
+#: tickets/models/relation.py:10
+msgid "Ticket session relation"
+msgstr "工单会话"
+
+#: tickets/models/ticket.py:35
+msgid "Ticket step"
+msgstr "工单步骤"
+
+#: tickets/models/ticket.py:46
 msgid "Ticket assignee"
 msgstr "工单受理人"
 
-#: tickets/models/ticket.py:49
+#: tickets/models/ticket.py:128
 msgid "Title"
 msgstr "标题"
 
-#: tickets/models/ticket.py:57
+#: tickets/models/ticket.py:136
 msgid "State"
 msgstr "状态"
 
-#: tickets/models/ticket.py:65
+#: tickets/models/ticket.py:144
 msgid "Approval step"
 msgstr "审批步骤"
 
-#: tickets/models/ticket.py:70
+#: tickets/models/ticket.py:149
 msgid "Applicant"
 msgstr "申请人"
 
-#: tickets/models/ticket.py:72
+#: tickets/models/ticket.py:151
 msgid "Applicant display"
 msgstr "申请人名称"
 
-#: tickets/models/ticket.py:73
+#: tickets/models/ticket.py:152
 msgid "Process"
 msgstr "流程"
 
-#: tickets/models/ticket.py:78
+#: tickets/models/ticket.py:157
 msgid "TicketFlow"
 msgstr "工单流程"
 
-#: tickets/models/ticket.py:297
+#: tickets/models/ticket.py:163
+msgid "Ticket"
+msgstr "工单管理"
+
+#: tickets/models/ticket.py:311
 msgid "Please try again"
 msgstr "请再次尝试"
 
+#: tickets/models/ticket.py:319
+msgid "Super ticket"
+msgstr "超级工单"
+
 #: tickets/notifications.py:57
 msgid "Your has a new ticket, applicant - {}"
 msgstr "你有一个新的工单, 申请人 - {}"
@@ -5004,6 +5673,10 @@ msgstr "你的工单已被处理, 处理人 - {}"
 msgid "Ticket has processed - {} ({})"
 msgstr "你的工单已被处理, 处理人 - {} ({})"
 
+#: tickets/serializers/super_ticket.py:11
+msgid "Processor"
+msgstr "处理人"
+
 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:18
 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:19
 msgid "Apply name"
@@ -5135,18 +5808,26 @@ msgstr "当前组织已存在该类型"
 msgid "Click here to review"
 msgstr "点击查看"
 
-#: users/api/user.py:209
+#: users/api/user.py:175
 msgid "Could not reset self otp, use profile reset instead"
 msgstr "不能在该页面重置 MFA 多因子认证, 请去个人信息页面重置"
 
-#: users/const.py:10 users/models/user.py:167
+#: users/const.py:10
 msgid "System administrator"
 msgstr "系统管理员"
 
-#: users/const.py:11 users/models/user.py:168
+#: users/const.py:11
 msgid "System auditor"
 msgstr "系统审计员"
 
+#: users/const.py:12
+msgid "Organization administrator"
+msgstr "组织管理员"
+
+#: users/const.py:13
+msgid "Organization auditor"
+msgstr "组织审计员"
+
 #: users/const.py:18
 msgid "Reset link will be generated and sent to the user"
 msgstr "生成重置密码链接,通过邮件发送给用户"
@@ -5182,7 +5863,7 @@ msgid ""
 "and key sensitive information properly. (for example: setting complex "
 "password, enabling MFA)"
 msgstr ""
-"为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:"
+"为了保护您和公司的安全,请妥善保管您的账号、密码和密钥等重要敏感信息;(如:"
 "设置复杂密码,并启用 MFA 多因子认证)"
 
 #: users/forms/profile.py:76
@@ -5230,55 +5911,75 @@ msgid "Public key should not be the same as your old one."
 msgstr "不能和原来的密钥相同"
 
 #: users/forms/profile.py:149 users/serializers/profile.py:95
-#: users/serializers/profile.py:171 users/serializers/profile.py:184
+#: users/serializers/profile.py:175 users/serializers/profile.py:202
 msgid "Not a valid ssh public key"
 msgstr "SSH密钥不合法"
 
-#: users/forms/profile.py:160 users/models/user.py:579
+#: users/forms/profile.py:160 users/models/user.py:605
 #: users/templates/users/user_password_update.html:48
 msgid "Public key"
 msgstr "SSH公钥"
 
-#: users/models/user.py:460
+#: users/models/user.py:471
 msgid "Force enable"
 msgstr "强制启用"
 
-#: users/models/user.py:526
+#: users/models/user.py:538
 msgid "Local"
 msgstr "数据库"
 
-#: users/models/user.py:562
+#: users/models/user.py:586 users/serializers/user.py:140
+msgid "Is service account"
+msgstr "服务账号"
+
+#: users/models/user.py:588
 msgid "Avatar"
 msgstr "头像"
 
-#: users/models/user.py:565
+#: users/models/user.py:591
 msgid "Wechat"
 msgstr "微信"
 
-#: users/models/user.py:576
+#: users/models/user.py:602
 msgid "Private key"
 msgstr "ssh私钥"
 
-#: users/models/user.py:598
+#: users/models/user.py:624
 msgid "Source"
 msgstr "来源"
 
-#: users/models/user.py:602
+#: users/models/user.py:628
 msgid "Date password last updated"
 msgstr "最后更新密码日期"
 
-#: users/models/user.py:605
+#: users/models/user.py:631
 msgid "Need update password"
 msgstr "需要更新密码"
 
-#: users/models/user.py:764
+#: users/models/user.py:795
+msgid "Can invite user"
+msgstr "可以邀请用户"
+
+#: users/models/user.py:796
+msgid "Can remove user"
+msgstr "可以移除用户"
+
+#: users/models/user.py:797
+msgid "Can match user"
+msgstr "可以匹配用户"
+
+#: users/models/user.py:806
 msgid "Administrator"
 msgstr "管理员"
 
-#: users/models/user.py:767
+#: users/models/user.py:809
 msgid "Administrator is the super user of system"
 msgstr "Administrator是初始的超级管理员"
 
+#: users/models/user.py:834
+msgid "User password history"
+msgstr "用户密码历史"
+
 #: users/notifications.py:55
 #: users/templates/users/_msg_password_expire_reminder.html:17
 #: users/templates/users/reset_password.html:5
@@ -5314,7 +6015,7 @@ msgstr "重置 MFA"
 msgid "The old password is incorrect"
 msgstr "旧密码错误"
 
-#: users/serializers/profile.py:36 users/serializers/user.py:142
+#: users/serializers/profile.py:36 users/serializers/profile.py:189
 msgid "Password does not match security rules"
 msgstr "密码不满足安全规则"
 
@@ -5326,89 +6027,97 @@ msgstr "新密码不能是最近 {} 次的密码"
 msgid "The newly set password is inconsistent"
 msgstr "两次密码不一致"
 
-#: users/serializers/profile.py:142 users/serializers/user.py:78
+#: users/serializers/profile.py:141 users/serializers/user.py:138
 msgid "Is first login"
 msgstr "首次登录"
 
-#: users/serializers/user.py:22
+#: users/serializers/user.py:26 users/serializers/user.py:33
+msgid "System roles"
+msgstr "系统角色"
+
+#: users/serializers/user.py:31 users/serializers/user.py:34
+msgid "Org roles"
+msgstr "组织角色"
+
+#: users/serializers/user.py:77
 #: xpack/plugins/change_auth_plan/models/base.py:35
 #: xpack/plugins/change_auth_plan/serializers/base.py:22
 msgid "Password strategy"
 msgstr "密码策略"
 
-#: users/serializers/user.py:24
+#: users/serializers/user.py:79
 msgid "MFA enabled"
 msgstr "MFA"
 
-#: users/serializers/user.py:25
+#: users/serializers/user.py:80
 msgid "MFA force enabled"
 msgstr "强制 MFA"
 
-#: users/serializers/user.py:26
+#: users/serializers/user.py:82
 msgid "MFA level display"
 msgstr "MFA 等级名称"
 
-#: users/serializers/user.py:27
+#: users/serializers/user.py:84
 msgid "Login blocked"
 msgstr "登录被阻塞"
 
-#: users/serializers/user.py:29
-msgid "Can update"
-msgstr "是否可更新"
-
-#: users/serializers/user.py:30
-msgid "Can delete"
-msgstr "是否可删除"
-
-#: users/serializers/user.py:32
+#: users/serializers/user.py:87
 msgid "Can public key authentication"
 msgstr "能否公钥认证"
 
-#: users/serializers/user.py:34 users/serializers/user.py:85
-msgid "Organization role name"
-msgstr "组织角色名称"
-
-#: users/serializers/user.py:81
+#: users/serializers/user.py:142
 msgid "Avatar url"
 msgstr "头像路径"
 
-#: users/serializers/user.py:83
+#: users/serializers/user.py:144
 msgid "Groups name"
 msgstr "用户组名"
 
-#: users/serializers/user.py:84
+#: users/serializers/user.py:145
 msgid "Source name"
 msgstr "用户来源名"
 
-#: users/serializers/user.py:86
+#: users/serializers/user.py:146
+msgid "Organization role name"
+msgstr "组织角色名称"
+
+#: users/serializers/user.py:147
 msgid "Super role name"
 msgstr "超级角色名称"
 
-#: users/serializers/user.py:87
+#: users/serializers/user.py:148
 msgid "Total role name"
 msgstr "汇总角色名称"
 
-#: users/serializers/user.py:89
+#: users/serializers/user.py:150
 msgid "Is wecom bound"
 msgstr "是否绑定了企业微信"
 
-#: users/serializers/user.py:90
+#: users/serializers/user.py:151
 msgid "Is dingtalk bound"
 msgstr "是否绑定了钉钉"
 
-#: users/serializers/user.py:91
+#: users/serializers/user.py:152
 msgid "Is feishu bound"
 msgstr "是否绑定了飞书"
 
-#: users/serializers/user.py:92
+#: users/serializers/user.py:153
 msgid "Is OTP bound"
 msgstr "是否绑定了虚拟 MFA"
 
-#: users/serializers/user.py:116
-msgid "Role limit to {}"
-msgstr "角色只能为 {}"
+#: users/serializers/user.py:155
+msgid "System role name"
+msgstr "系统角色名称"
 
-#: users/serializers/user.py:228
+#: users/serializers/user.py:247
+msgid "Select users"
+msgstr "选择用户"
+
+#: users/serializers/user.py:248
+msgid "For security, only list several users"
+msgstr "为了安全,仅列出几个用户"
+
+#: users/serializers/user.py:281
 msgid "name not unique"
 msgstr "名称重复"
 
@@ -5556,6 +6265,10 @@ msgstr "您的密码必须满足:"
 msgid "Password strength"
 msgstr "密码强度:"
 
+#: users/templates/users/reset_password.html:29
+msgid "Setting"
+msgstr "设置"
+
 #: users/templates/users/reset_password.html:48
 #: users/templates/users/user_password_update.html:102
 msgid "Very weak"
@@ -5600,6 +6313,11 @@ msgstr "包含"
 msgid "Exclude"
 msgstr "不包含"
 
+#: users/templates/users/user_database_app_permission.html:39
+#: users/templates/users/user_database_app_permission.html:64
+msgid "DatabaseApp"
+msgstr "数据库应用"
+
 #: users/templates/users/user_otp_check_password.html:6
 msgid "Enable OTP"
 msgstr "启用 MFA(OTP)"
@@ -5698,10 +6416,6 @@ msgstr "MFA(OTP) 禁用成功,返回登录页面"
 msgid "Password invalid"
 msgstr "用户名或密码无效"
 
-#: users/views/profile/pubkey.py:38
-msgid "Public key update"
-msgstr "密钥更新"
-
 #: users/views/profile/reset.py:40
 msgid "Send reset password message"
 msgstr "发送重置密码邮件"
@@ -5741,34 +6455,28 @@ msgstr "* 新密码不能是最近 {} 次的密码"
 msgid "Reset password success, return to login page"
 msgstr "重置密码成功,返回到登录页面"
 
-#: xpack/plugins/change_auth_plan/api/app.py:114
-#: xpack/plugins/change_auth_plan/api/asset.py:101
+#: xpack/apps.py:8
+msgid "XPACK"
+msgstr ""
+
+#: xpack/plugins/change_auth_plan/api/app.py:109
+#: xpack/plugins/change_auth_plan/api/asset.py:95
 msgid "The parameter 'action' must be [{}]"
 msgstr "参数 'action' 必须是 [{}]"
 
-#: xpack/plugins/change_auth_plan/meta.py:9
-#: xpack/plugins/change_auth_plan/models/asset.py:67
-#: xpack/plugins/change_auth_plan/models/asset.py:123
-msgid "Change auth plan"
-msgstr "改密计划"
-
-#: xpack/plugins/change_auth_plan/models/app.py:46
-#: xpack/plugins/change_auth_plan/models/app.py:95
-msgid "Application change auth plan"
-msgstr "应用改密计划执行"
-
 #: xpack/plugins/change_auth_plan/models/app.py:99
 #: xpack/plugins/change_auth_plan/models/app.py:151
 msgid "Application change auth plan execution"
 msgstr "应用改密计划执行"
 
 #: xpack/plugins/change_auth_plan/models/app.py:144
+#: xpack/plugins/change_auth_plan/serializers/app.py:64
 msgid "App"
 msgstr "应用"
 
 #: xpack/plugins/change_auth_plan/models/app.py:156
 msgid "Application change auth plan task"
-msgstr "用用改密计划任务"
+msgstr "应用改密计划任务"
 
 #: xpack/plugins/change_auth_plan/models/app.py:180
 #: xpack/plugins/change_auth_plan/models/asset.py:263
@@ -5788,18 +6496,21 @@ msgid "Replace (The key generated by JumpServer) "
 msgstr "替换 (由 JumpServer 生成的密钥)"
 
 #: xpack/plugins/change_auth_plan/models/asset.py:49
-#: xpack/plugins/change_auth_plan/serializers/asset.py:34
+#: xpack/plugins/change_auth_plan/serializers/asset.py:35
 msgid "SSH Key strategy"
 msgstr "SSH 密钥策略"
 
 #: xpack/plugins/change_auth_plan/models/asset.py:134
+msgid "Asset change auth plan execution"
+msgstr "资产改密计划执行"
+
 #: xpack/plugins/change_auth_plan/models/asset.py:210
 msgid "Change auth plan execution"
 msgstr "改密计划执行"
 
 #: xpack/plugins/change_auth_plan/models/asset.py:217
-msgid "Change auth plan task"
-msgstr "改密计划任务"
+msgid "Asset change auth plan task"
+msgstr "资产改密计划任务"
 
 #: xpack/plugins/change_auth_plan/models/asset.py:252
 msgid "This asset does not have a privileged user set: "
@@ -5873,11 +6584,11 @@ msgstr ""
 "{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加"
 "密密码"
 
-#: xpack/plugins/change_auth_plan/serializers/asset.py:31
+#: xpack/plugins/change_auth_plan/serializers/asset.py:32
 msgid "Change Password"
 msgstr "更改密码"
 
-#: xpack/plugins/change_auth_plan/serializers/asset.py:32
+#: xpack/plugins/change_auth_plan/serializers/asset.py:33
 msgid "Change SSH Key"
 msgstr "修改 SSH Key"
 
@@ -5909,11 +6620,11 @@ msgstr "连接主机失败"
 msgid "Data could not be sent to remote"
 msgstr "无法将数据发送到远程"
 
-#: xpack/plugins/cloud/api.py:38
+#: xpack/plugins/cloud/api.py:40
 msgid "Test connection successful"
 msgstr "测试成功"
 
-#: xpack/plugins/cloud/api.py:40
+#: xpack/plugins/cloud/api.py:42
 msgid "Test connection failed: {}"
 msgstr "测试连接失败:{}"
 
@@ -6005,9 +6716,10 @@ msgstr "云管中心"
 msgid "Provider"
 msgstr "云服务商"
 
-#: xpack/plugins/cloud/models.py:39
-msgid "Cloud account"
-msgstr "云账号"
+#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:82
+#: xpack/plugins/cloud/serializers/task.py:66
+msgid "Account"
+msgstr "账号"
 
 #: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers/task.py:37
 msgid "Regions"
@@ -6041,22 +6753,30 @@ msgstr "最后同步日期"
 msgid "Sync instance task"
 msgstr "同步实例任务"
 
-#: xpack/plugins/cloud/models.py:179 xpack/plugins/cloud/models.py:224
+#: xpack/plugins/cloud/models.py:179 xpack/plugins/cloud/models.py:227
 msgid "Date sync"
 msgstr "同步日期"
 
-#: xpack/plugins/cloud/models.py:204
+#: xpack/plugins/cloud/models.py:183
+msgid "Sync instance task execution"
+msgstr "同步实例任务执行"
+
+#: xpack/plugins/cloud/models.py:207
 msgid "Sync task"
 msgstr "同步任务"
 
-#: xpack/plugins/cloud/models.py:208
+#: xpack/plugins/cloud/models.py:211
 msgid "Sync instance task history"
 msgstr "同步实例任务历史"
 
-#: xpack/plugins/cloud/models.py:211
+#: xpack/plugins/cloud/models.py:214
 msgid "Instance"
 msgstr "实例"
 
+#: xpack/plugins/cloud/models.py:231
+msgid "Sync instance detail"
+msgstr "同步实例详情"
+
 #: xpack/plugins/cloud/providers/aws_international.py:17
 msgid "China (Beijing)"
 msgstr "中国(北京)"
@@ -6256,7 +6976,7 @@ msgstr "用户域"
 
 #: xpack/plugins/cloud/serializers/account_attrs.py:113
 msgid "Service account key"
-msgstr "账户密钥"
+msgstr "服务账号密钥"
 
 #: xpack/plugins/cloud/serializers/account_attrs.py:114
 msgid "The file is in JSON format"
@@ -6290,20 +7010,12 @@ msgstr "定时执行"
 
 #: xpack/plugins/cloud/utils.py:68
 msgid "Account unavailable"
-msgstr "账户无效"
-
-#: xpack/plugins/gathered_user/meta.py:11
-msgid "Gathered user"
-msgstr "收集用户"
+msgstr "账号无效"
 
 #: xpack/plugins/gathered_user/models.py:39
 msgid "Gather user task"
 msgstr "收集用户任务"
 
-#: xpack/plugins/gathered_user/models.py:73
-msgid "Task"
-msgstr "任务"
-
 #: xpack/plugins/gathered_user/models.py:85
 msgid "gather user task execution"
 msgstr "收集用户执行"
@@ -6316,11 +7028,11 @@ msgstr "资产为空,请更改节点"
 msgid "Executed times"
 msgstr "执行次数"
 
-#: xpack/plugins/interface/api.py:43
+#: xpack/plugins/interface/api.py:46
 msgid "It is already in the default setting state!"
 msgstr "当前已经是初始化状态!"
 
-#: xpack/plugins/interface/api.py:46
+#: xpack/plugins/interface/api.py:49
 msgid "Restore default successfully."
 msgstr "恢复默认成功!"
 
@@ -6348,18 +7060,18 @@ msgstr "管理页面logo"
 msgid "Logo of logout page"
 msgstr "退出页面logo"
 
-#: xpack/plugins/license/api.py:37
+#: xpack/plugins/interface/models.py:36
+msgid "Interface setting"
+msgstr "界面设置"
+
+#: xpack/plugins/license/api.py:41
 msgid "License import successfully"
 msgstr "许可证导入成功"
 
-#: xpack/plugins/license/api.py:38
+#: xpack/plugins/license/api.py:42
 msgid "License is invalid"
 msgstr "无效的许可证"
 
-#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:127
-msgid "License"
-msgstr "许可证"
-
 #: xpack/plugins/license/models.py:71
 msgid "Standard edition"
 msgstr "标准版"
@@ -6375,3 +7087,36 @@ msgstr "旗舰版"
 #: xpack/plugins/license/models.py:77
 msgid "Community edition"
 msgstr "社区版"
+
+#~ msgid "Permed remote application"
+#~ msgstr "授权的远程应用"
+
+#~ msgid "Can view my remoteapp"
+#~ msgstr "可以查看我的应用"
+
+#~ msgid "Can connect my remoteapp"
+#~ msgstr "可以连接我的远程应用"
+
+#~ msgid "Can view my database application"
+#~ msgstr "可以查看我的数据库应用"
+
+#~ msgid "Can connect my database application"
+#~ msgstr "可以连接我的数据库应用"
+
+#~ msgid "Can view my kubernetes application"
+#~ msgstr "可以查看我的Kubernetes"
+
+#~ msgid "Can connect my kubernetes application"
+#~ msgstr "可以连接我的Kubernetes"
+
+#~ msgid "Can change terminal basic setting"
+#~ msgstr "基本设置"
+
+#~ msgid "Cloud account"
+#~ msgstr "云账号"
+
+#~ msgid "Test cloud account"
+#~ msgstr "测试云账号"
+
+#~ msgid "Can view resource statistics"
+#~ msgstr "可以查看资源统计"
diff --git a/apps/notifications/api/notifications.py b/apps/notifications/api/notifications.py
index e1ad87a43..225b8a065 100644
--- a/apps/notifications/api/notifications.py
+++ b/apps/notifications/api/notifications.py
@@ -3,7 +3,7 @@ from rest_framework.views import APIView
 from rest_framework.response import Response
 
 from common.drf.api import JMSGenericViewSet
-from common.permissions import IsObjectOwner, IsSuperUser, OnlySuperUserCanList
+from common.permissions import IsValidUser
 from notifications.notifications import system_msgs
 from notifications.models import SystemMsgSubscription, UserMsgSubscription
 from notifications.backends import BACKEND
@@ -19,14 +19,15 @@ __all__ = (
 
 
 class BackendListView(APIView):
+    permission_classes = [IsValidUser]
+
     def get(self, request):
         data = [
             {
                 'name': backend,
                 'name_display': backend.label
             }
-            for backend in BACKEND
-            if backend.is_enable
+            for backend in BACKEND if backend.is_enable
         ]
         return Response(data=data)
 
@@ -41,6 +42,9 @@ class SystemMsgSubscriptionViewSet(ListModelMixin,
         'update': SystemMsgSubscriptionSerializer,
         'partial_update': SystemMsgSubscriptionSerializer
     }
+    rbac_perms = {
+
+    }
 
     def list(self, request, *args, **kwargs):
         data = []
@@ -80,9 +84,12 @@ class UserMsgSubscriptionViewSet(ListModelMixin,
                                  UpdateModelMixin,
                                  JMSGenericViewSet):
     lookup_field = 'user_id'
-    queryset = UserMsgSubscription.objects.all()
     serializer_class = UserMsgSubscriptionSerializer
-    permission_classes = (IsObjectOwner | IsSuperUser, OnlySuperUserCanList)
+    permission_classes = (IsValidUser,)
+
+    def get_queryset(self):
+        queryset = UserMsgSubscription.objects.filter(user=self.request.user)
+        return queryset
 
 
 def get_all_test_messages(request):
@@ -121,5 +128,3 @@ def get_all_test_messages(request):
         <hr />
         """).format(msg_cls.__name__, msg_text)
     return HttpResponse(html_data + text_data)
-
-
diff --git a/apps/notifications/apps.py b/apps/notifications/apps.py
index e888822d4..306f2b55b 100644
--- a/apps/notifications/apps.py
+++ b/apps/notifications/apps.py
@@ -1,10 +1,12 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class NotificationsConfig(AppConfig):
     name = 'notifications'
+    verbose_name = _('Notifications')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
         super().ready()
diff --git a/apps/notifications/signals_handler.py b/apps/notifications/signal_handlers.py
similarity index 86%
rename from apps/notifications/signals_handler.py
rename to apps/notifications/signal_handlers.py
index 79964464a..aaf3480bf 100644
--- a/apps/notifications/signals_handler.py
+++ b/apps/notifications/signal_handlers.py
@@ -49,7 +49,7 @@ def on_site_message_create(sender, instance, created, **kwargs):
     new_site_msg_chan.publish(data)
 
 
-@receiver(post_migrate, dispatch_uid='notifications.signals_handler.create_system_messages')
+@receiver(post_migrate, dispatch_uid='notifications.signal_handlers.create_system_messages')
 def create_system_messages(app_config: AppConfig, **kwargs):
     try:
         notifications_module = import_module('.notifications', app_config.module.__package__)
@@ -85,9 +85,11 @@ def create_system_messages(app_config: AppConfig, **kwargs):
 
 @receiver(post_save, sender=User)
 def on_user_post_save(sender, instance, created, **kwargs):
-    if created:
-        receive_backends = []
-        for backend in BACKEND:
-            if backend.get_account(instance):
-                receive_backends.append(backend)
-        UserMsgSubscription.objects.create(user=instance, receive_backends=receive_backends)
+    if not created:
+        return
+    receive_backends = []
+    # Todo: IDE 识别不了 get_account
+    for backend in BACKEND:
+        if backend.get_account(instance):
+            receive_backends.append(backend)
+    UserMsgSubscription.objects.create(user=instance, receive_backends=receive_backends)
diff --git a/apps/notifications/ws.py b/apps/notifications/ws.py
index e3d48f79d..391bb659d 100644
--- a/apps/notifications/ws.py
+++ b/apps/notifications/ws.py
@@ -5,7 +5,7 @@ from channels.generic.websocket import JsonWebsocketConsumer
 from common.utils import get_logger
 from common.db.utils import safe_db_connection
 from .site_msg import SiteMessageUtil
-from .signals_handler import new_site_msg_chan
+from .signal_handlers import new_site_msg_chan
 
 logger = get_logger(__name__)
 
diff --git a/apps/ops/ansible/callback.py b/apps/ops/ansible/callback.py
index cb42350b7..3fe1933ac 100644
--- a/apps/ops/ansible/callback.py
+++ b/apps/ops/ansible/callback.py
@@ -5,7 +5,7 @@ import json
 import os
 from collections import defaultdict
 
-from ansible import constants as C
+import ansible.constants as C
 from ansible.plugins.callback import CallbackBase
 from ansible.plugins.callback.default import CallbackModule
 from ansible.plugins.callback.minimal import CallbackModule as CMDCallBackModule
diff --git a/apps/ops/api/adhoc.py b/apps/ops/api/adhoc.py
index f7b32e4fd..3bb1acb53 100644
--- a/apps/ops/api/adhoc.py
+++ b/apps/ops/api/adhoc.py
@@ -5,8 +5,6 @@ from django.shortcuts import get_object_or_404
 from rest_framework import viewsets, generics
 from rest_framework.views import Response
 
-from common.drf.api import JMSBulkModelViewSet
-from common.permissions import IsOrgAdmin
 from common.drf.serializers import CeleryTaskSerializer
 from ..models import Task, AdHoc, AdHocExecution
 from ..serializers import (
@@ -18,7 +16,6 @@ from ..serializers import (
 )
 from ..tasks import run_ansible_task
 from orgs.mixins.api import OrgBulkModelViewSet
-from orgs.utils import current_org
 
 __all__ = [
     'TaskViewSet', 'TaskRun', 'AdHocViewSet', 'AdHocRunHistoryViewSet'
@@ -30,7 +27,6 @@ class TaskViewSet(OrgBulkModelViewSet):
     filterset_fields = ("name",)
     search_fields = filterset_fields
     serializer_class = TaskSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def get_serializer_class(self):
         if self.action == 'retrieve':
@@ -46,7 +42,6 @@ class TaskViewSet(OrgBulkModelViewSet):
 class TaskRun(generics.RetrieveAPIView):
     queryset = Task.objects.all()
     serializer_class = CeleryTaskSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def retrieve(self, request, *args, **kwargs):
         task = self.get_object()
@@ -57,7 +52,6 @@ class TaskRun(generics.RetrieveAPIView):
 class AdHocViewSet(viewsets.ModelViewSet):
     queryset = AdHoc.objects.all()
     serializer_class = AdHocSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def get_serializer_class(self):
         if self.action == 'retrieve':
@@ -75,7 +69,6 @@ class AdHocViewSet(viewsets.ModelViewSet):
 class AdHocRunHistoryViewSet(viewsets.ModelViewSet):
     queryset = AdHocExecution.objects.all()
     serializer_class = AdHocExecutionSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def get_queryset(self):
         task_id = self.request.query_params.get('task')
diff --git a/apps/ops/api/celery.py b/apps/ops/api/celery.py
index 3968b39b7..cd452c471 100644
--- a/apps/ops/api/celery.py
+++ b/apps/ops/api/celery.py
@@ -10,7 +10,7 @@ from celery.result import AsyncResult
 from rest_framework import generics
 from django_celery_beat.models import PeriodicTask
 
-from common.permissions import IsValidUser, IsSuperUser
+from common.permissions import IsValidUser
 from common.api import LogTailApi
 from ..models import CeleryTask
 from ..serializers import CeleryResultSerializer, CeleryPeriodTaskSerializer
@@ -88,7 +88,6 @@ class CeleryResultApi(generics.RetrieveAPIView):
 class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):
     queryset = PeriodicTask.objects.all()
     serializer_class = CeleryPeriodTaskSerializer
-    permission_classes = (IsSuperUser,)
     http_method_names = ('get', 'head', 'options', 'patch')
 
     def get_queryset(self):
diff --git a/apps/ops/apps.py b/apps/ops/apps.py
index 43496ebf2..819a23002 100644
--- a/apps/ops/apps.py
+++ b/apps/ops/apps.py
@@ -6,13 +6,13 @@ from django.apps import AppConfig
 
 class OpsConfig(AppConfig):
     name = 'ops'
-    verbose_name = _('Operations')
+    verbose_name = _('App ops')
 
     def ready(self):
         from orgs.models import Organization
         from orgs.utils import set_current_org
         set_current_org(Organization.root())
         from .celery import signal_handler
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
         super().ready()
diff --git a/apps/ops/migrations/0021_auto_20211130_1037.py b/apps/ops/migrations/0021_auto_20211130_1037.py
new file mode 100644
index 000000000..362abe030
--- /dev/null
+++ b/apps/ops/migrations/0021_auto_20211130_1037.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.1.13 on 2021-11-30 02:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ops', '0020_adhoc_run_system_user'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='adhoc',
+            options={'get_latest_by': 'date_created', 'verbose_name': 'AdHoc'},
+        ),
+        migrations.AlterModelOptions(
+            name='adhocexecution',
+            options={'get_latest_by': 'date_start', 'verbose_name': 'AdHoc execution'},
+        ),
+        migrations.AlterModelOptions(
+            name='commandexecution',
+            options={'verbose_name': 'Command execution'},
+        ),
+        migrations.AlterModelOptions(
+            name='task',
+            options={'get_latest_by': 'date_created', 'ordering': ('-date_updated',), 'verbose_name': 'Task'},
+        ),
+        migrations.AlterModelOptions(
+            name='task',
+            options={'get_latest_by': 'date_created', 'ordering': ('-date_updated',), 'permissions': [('view_taskmonitor', 'Can view task monitor')], 'verbose_name': 'Task'},
+        ),
+    ]
diff --git a/apps/ops/models/adhoc.py b/apps/ops/models/adhoc.py
index d1bb8c7f9..1a6e02fd1 100644
--- a/apps/ops/models/adhoc.py
+++ b/apps/ops/models/adhoc.py
@@ -38,7 +38,8 @@ class Task(PeriodTaskModelMixin, OrgModelMixin):
     comment = models.TextField(blank=True, verbose_name=_("Comment"))
     date_created = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_("Date created"))
     date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
-    latest_adhoc = models.ForeignKey('ops.AdHoc', on_delete=models.SET_NULL, null=True, related_name='task_latest')
+    latest_adhoc = models.ForeignKey('ops.AdHoc', on_delete=models.SET_NULL,
+                                     null=True, related_name='task_latest')
     latest_execution = models.ForeignKey('ops.AdHocExecution', on_delete=models.SET_NULL, null=True, related_name='task_latest')
     total_run_amount = models.IntegerField(default=0)
     success_run_amount = models.IntegerField(default=0)
@@ -131,7 +132,11 @@ class Task(PeriodTaskModelMixin, OrgModelMixin):
         db_table = 'ops_task'
         unique_together = ('name', 'org_id')
         ordering = ('-date_updated',)
+        verbose_name = _("Task")
         get_latest_by = 'date_created'
+        permissions = [
+            ('view_taskmonitor', _('Can view task monitor'))
+        ]
 
 
 class AdHoc(OrgModelMixin):
@@ -235,6 +240,7 @@ class AdHoc(OrgModelMixin):
     class Meta:
         db_table = "ops_adhoc"
         get_latest_by = 'date_created'
+        verbose_name = _('AdHoc')
 
 
 class AdHocExecution(OrgModelMixin):
@@ -330,3 +336,4 @@ class AdHocExecution(OrgModelMixin):
     class Meta:
         db_table = "ops_adhoc_execution"
         get_latest_by = 'date_start'
+        verbose_name = _("AdHoc execution")
diff --git a/apps/ops/models/celery.py b/apps/ops/models/celery.py
index 51412988c..9ab5f49e1 100644
--- a/apps/ops/models/celery.py
+++ b/apps/ops/models/celery.py
@@ -2,6 +2,8 @@
 #
 import uuid
 import os
+
+from django.utils.translation import gettext_lazy as _
 from django.conf import settings
 from django.db import models
 
diff --git a/apps/ops/models/command.py b/apps/ops/models/command.py
index 81f9ce9e9..66bd8ed28 100644
--- a/apps/ops/models/command.py
+++ b/apps/ops/models/command.py
@@ -155,3 +155,6 @@ class CommandExecution(OrgModelMixin):
         self.save()
         print('-' * 10 + ' ' + ugettext('Task end') + ' ' + '-' * 10)
         return self.result
+
+    class Meta:
+        verbose_name = _("Command execution")
diff --git a/apps/ops/notifications.py b/apps/ops/notifications.py
index b34b00e1f..86c7ab188 100644
--- a/apps/ops/notifications.py
+++ b/apps/ops/notifications.py
@@ -13,7 +13,7 @@ __all__ = ('ServerPerformanceMessage', 'ServerPerformanceCheckUtil')
 
 class ServerPerformanceMessage(SystemMessage):
     category = 'Operations'
-    category_label = _('Operations')
+    category_label = _('App ops')
     message_type_label = _('Server performance')
 
     def __init__(self, terms_with_errors):
@@ -32,7 +32,11 @@ class ServerPerformanceMessage(SystemMessage):
 
     @classmethod
     def post_insert_to_db(cls, subscription: SystemMsgSubscription):
-        admins = User.objects.filter(role=User.ROLE.ADMIN)
+        from rbac.models import Role, RoleBinding
+        # Todo: 需要更改这里
+        admin_role = Role.BuiltinRole.system_admin.get_role()
+        admins_ids = RoleBinding.objects.filter(role=admin_role).values_list('user_id', flat=True)
+        admins = User.objects.filter(id__in=admins_ids)
         subscription.users.add(*admins)
         subscription.receive_backends = [BACKEND.EMAIL]
         subscription.save()
diff --git a/apps/ops/signals_handler.py b/apps/ops/signal_handlers.py
similarity index 100%
rename from apps/ops/signals_handler.py
rename to apps/ops/signal_handlers.py
diff --git a/apps/ops/views.py b/apps/ops/views.py
index 7b18b0f46..fee4a0b9f 100644
--- a/apps/ops/views.py
+++ b/apps/ops/views.py
@@ -3,15 +3,18 @@
 from django.views.generic import TemplateView
 from django.conf import settings
 
-from common.permissions import IsOrgAdmin, IsOrgAuditor
 from common.mixins.views import PermissionsMixin
+from rbac.permissions import RBACPermission
 
 __all__ = ['CeleryTaskLogView']
 
 
 class CeleryTaskLogView(PermissionsMixin, TemplateView):
     template_name = 'ops/celery_task_log.html'
-    permission_classes = [IsOrgAdmin | IsOrgAuditor]
+    permission_classes = [RBACPermission]
+    rbac_perms = {
+        'GET': 'ops.view_tasklog'
+    }
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
diff --git a/apps/orgs/api.py b/apps/orgs/api.py
index 4a5c24fdb..e1a41a29f 100644
--- a/apps/orgs/api.py
+++ b/apps/orgs/api.py
@@ -2,20 +2,14 @@
 #
 
 from django.utils.translation import ugettext as _
-from rest_framework import status
-from rest_framework.views import Response
 from rest_framework_bulk import BulkModelViewSet
 from rest_framework.generics import RetrieveAPIView
 from rest_framework.exceptions import PermissionDenied
 
-from common.permissions import IsSuperUserOrAppUser, IsValidUser, UserCanAnyPermCurrentOrg
-from common.drf.api import JMSBulkRelationModelViewSet
-from .models import Organization, ROLE
+from common.permissions import IsValidUser
+from .models import Organization
 from .serializers import (
-    OrgSerializer, OrgReadSerializer,
-    OrgRetrieveSerializer, OrgMemberSerializer,
-    OrgMemberAdminSerializer, OrgMemberUserSerializer,
-    CurrentOrgSerializer
+    OrgSerializer, CurrentOrgSerializer
 )
 from users.models import User, UserGroup
 from assets.models import (
@@ -26,8 +20,6 @@ from applications.models import Application
 from perms.models import AssetPermission, ApplicationPermission
 from orgs.utils import current_org, tmp_to_root_org
 from common.utils import get_logger
-from .filters import OrgMemberRelationFilterSet
-from .models import OrganizationMember
 
 
 logger = get_logger(__file__)
@@ -47,23 +39,20 @@ class OrgViewSet(BulkModelViewSet):
     search_fields = ('name', 'comment')
     queryset = Organization.objects.all()
     serializer_class = OrgSerializer
-    permission_classes = (IsSuperUserOrAppUser,)
     ordering_fields = ('name',)
     ordering = ('name', )
 
     def get_serializer_class(self):
         mapper = {
-            'list': OrgReadSerializer,
-            'retrieve': OrgRetrieveSerializer
+            'list': OrgSerializer,
+            'retrieve': OrgSerializer
         }
         return mapper.get(self.action, super().get_serializer_class())
 
     @tmp_to_root_org()
     def get_data_from_model(self, org, model):
         if model == User:
-            data = model.objects.filter(
-                orgs__id=org.id, m2m_org_members__role__in=[ROLE.USER, ROLE.ADMIN, ROLE.AUDITOR]
-            )
+            data = model.get_org_users(org=org)
         elif model == Node:
             # 根节点不能手动删除,所以排除检查
             data = model.objects.filter(org_id=org.id).exclude(parent_key='', key__regex=r'^[0-9]+$')
@@ -91,77 +80,9 @@ class OrgViewSet(BulkModelViewSet):
         super().perform_destroy(instance)
 
 
-class OrgMemberRelationBulkViewSet(JMSBulkRelationModelViewSet):
-    permission_classes = (IsSuperUserOrAppUser,)
-    m2m_field = Organization.members.field
-    serializer_class = OrgMemberSerializer
-    filterset_class = OrgMemberRelationFilterSet
-    search_fields = ('user__name', 'user__username', 'org__name')
-
-    def get_queryset(self):
-        queryset = super().get_queryset()
-        queryset = queryset.exclude(user__role=User.ROLE.APP)
-        return queryset
-
-    def perform_bulk_destroy(self, queryset):
-        objs = list(queryset.all().prefetch_related('user', 'org'))
-        queryset.delete()
-        self.send_m2m_changed_signal(objs, action='post_remove')
-
-
-class OrgMemberAdminRelationBulkViewSet(JMSBulkRelationModelViewSet):
-    permission_classes = (IsSuperUserOrAppUser,)
-    m2m_field = Organization.members.field
-    serializer_class = OrgMemberAdminSerializer
-    filterset_class = OrgMemberRelationFilterSet
-    search_fields = ('user__name', 'user__username', 'org__name')
-    lookup_field = 'user_id'
-
-    def get_queryset(self):
-        queryset = super().get_queryset()
-        org_id = self.kwargs.get('org_id')
-        queryset = queryset.filter(org_id=org_id, role=ROLE.ADMIN)
-        return queryset
-
-    def perform_bulk_create(self, serializer):
-        data = serializer.validated_data
-        relations = [OrganizationMember(**i) for i in data]
-        OrganizationMember.objects.bulk_create(relations, ignore_conflicts=True)
-
-    def perform_bulk_destroy(self, queryset):
-        objs = list(queryset.all().prefetch_related('user', 'org'))
-        queryset.delete()
-        self.send_m2m_changed_signal(objs, action='post_remove')
-
-
-class OrgMemberUserRelationBulkViewSet(JMSBulkRelationModelViewSet):
-    permission_classes = (IsSuperUserOrAppUser,)
-    m2m_field = Organization.members.field
-    serializer_class = OrgMemberUserSerializer
-    filterset_class = OrgMemberRelationFilterSet
-    search_fields = ('user__name', 'user__username', 'org__name')
-    lookup_field = 'user_id'
-
-    def get_queryset(self):
-        queryset = super().get_queryset()
-        org_id = self.kwargs.get('org_id')
-        queryset = queryset.filter(org_id=org_id, role=ROLE.USER)
-        return queryset
-
-    def perform_bulk_create(self, serializer):
-        data = serializer.validated_data
-        relations = [OrganizationMember(**i) for i in data]
-        OrganizationMember.objects.bulk_create(relations, ignore_conflicts=True)
-
-    def perform_bulk_destroy(self, queryset):
-        objs = list(queryset.all().prefetch_related('user', 'org'))
-        queryset.delete()
-        self.send_m2m_changed_signal(objs, action='post_remove')
-
-
 class CurrentOrgDetailApi(RetrieveAPIView):
     serializer_class = CurrentOrgSerializer
-    permission_classes = (IsValidUser, UserCanAnyPermCurrentOrg)
+    permission_classes = (IsValidUser,)
 
     def get_object(self):
         return current_org
diff --git a/apps/orgs/apps.py b/apps/orgs/apps.py
index c83d67e3c..14a48203c 100644
--- a/apps/orgs/apps.py
+++ b/apps/orgs/apps.py
@@ -1,8 +1,10 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class OrgsConfig(AppConfig):
     name = 'orgs'
+    verbose_name = _('App organizations')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
diff --git a/apps/orgs/caches.py b/apps/orgs/caches.py
index ae1a610ce..e8b0bdcba 100644
--- a/apps/orgs/caches.py
+++ b/apps/orgs/caches.py
@@ -6,11 +6,10 @@ from orgs.utils import current_org, tmp_to_org
 from common.cache import Cache, IntegerField
 from common.utils import get_logger
 from users.models import UserGroup, User
-from assets.models import Node, AdminUser, SystemUser, Domain, Gateway, Asset
+from assets.models import Node, SystemUser, Domain, Gateway, Asset
 from terminal.models import Session
 from applications.models import Application
 from perms.models import AssetPermission, ApplicationPermission
-from .models import OrganizationMember
 
 logger = get_logger(__file__)
 
@@ -84,13 +83,8 @@ class OrgResourceStatisticsCache(OrgRelatedCache):
         return SystemUser.objects.filter(type=SystemUser.Type.common).count()
 
     def compute_users_amount(self):
-        users = User.objects.exclude(role='App')
-
-        if not self.org.is_root():
-            users = users.filter(m2m_org_members__org_id=self.org.id)
-
-        users_amount = users.values('id').distinct().count()
-        return users_amount
+        amount = User.get_org_users(self.org).count()
+        return amount
 
     def compute_assets_amount(self):
         if self.org.is_root():
diff --git a/apps/orgs/context_processor.py b/apps/orgs/context_processor.py
index c78ed834a..bc1ca0ede 100644
--- a/apps/orgs/context_processor.py
+++ b/apps/orgs/context_processor.py
@@ -1,17 +1,12 @@
 # -*- coding: utf-8 -*-
 #
 
-from .utils import current_org, get_org_from_request
-from .models import Organization
+from .utils import get_org_from_request
 
 
 def org_processor(request):
     context = {
-        # 'ADMIN_ORGS': request.user.admin_orgs,
-        # 'AUDIT_ORGS': request.user.audit_orgs,
-        'ADMIN_OR_AUDIT_ORGS': Organization.get_user_admin_or_audit_orgs(request.user),
         'CURRENT_ORG': get_org_from_request(request),
-        # 'HAS_ORG_PERM': current_org.can_admin_by(request.user),
     }
     return context
 
diff --git a/apps/orgs/filters.py b/apps/orgs/filters.py
index c6c026198..ee68a0ed8 100644
--- a/apps/orgs/filters.py
+++ b/apps/orgs/filters.py
@@ -1,16 +1,6 @@
-from django_filters.rest_framework import filterset
 from django_filters.rest_framework import filters
 
-from .models import OrganizationMember
-
 
 class UUIDInFilter(filters.BaseInFilter, filters.UUIDFilter):
     pass
 
-
-class OrgMemberRelationFilterSet(filterset.FilterSet):
-    id = UUIDInFilter(field_name='id', lookup_expr='in')
-
-    class Meta:
-        model = OrganizationMember
-        fields = ('org_id', 'user_id', 'org', 'user', 'role', 'id')
diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py
index 2448fffc3..c1fd64fbe 100644
--- a/apps/orgs/middleware.py
+++ b/apps/orgs/middleware.py
@@ -2,6 +2,7 @@
 #
 
 from .utils import get_org_from_request, set_current_org
+from rbac.models import RoleBinding
 
 
 class OrgMiddleware:
@@ -14,22 +15,19 @@ class OrgMiddleware:
             return
         if not request.user.is_authenticated:
             return
-        if request.user.is_common_user:
-            return
+
         org = get_org_from_request(request)
-        if org.can_admin_by(request.user):
-            return
-        if org.can_audit_by(request.user):
-            return
-        admin_orgs = request.user.admin_orgs
-        if admin_orgs:
-            request.session['oid'] = str(admin_orgs[0].id)
-            return
-        audit_orgs = request.user.audit_orgs
-        if audit_orgs:
-            request.session['oid'] = str(audit_orgs[0].id)
+
+        search_org = None if org.is_root() else org
+        has_roles = RoleBinding.objects.filter(user=request.user, org=search_org).exists()
+        if has_roles:
             return
 
+        roles_bindings = RoleBinding.objects.filter(user=request.user).exclude(org=None)
+        if roles_bindings:
+            org_id = str(list(roles_bindings.values_list('org_id', flat=True))[0])
+            request.session['oid'] = org_id
+
     def __call__(self, request):
         self.set_permed_org_if_need(request)
         org = get_org_from_request(request)
diff --git a/apps/orgs/migrations/0010_auto_20210219_1241.py b/apps/orgs/migrations/0010_auto_20210219_1241.py
index f5694d7bc..facc6a654 100644
--- a/apps/orgs/migrations/0010_auto_20210219_1241.py
+++ b/apps/orgs/migrations/0010_auto_20210219_1241.py
@@ -44,11 +44,12 @@ def migrate_default_org_id(apps, schema_editor):
 
 
 def add_all_user_to_default_org(apps, schema_editor):
-    User = apps.get_model('users', 'User')
-    Organization = apps.get_model('orgs', 'Organization')
+    user_model = apps.get_model('users', 'User')
+    org_model = apps.get_model('orgs', 'Organization')
+    org_members_model = apps.get_model('orgs', 'OrganizationMember')
 
-    users_qs = User.objects.all()
-    default_org = Organization.objects.get(id=default_id)
+    users_qs = user_model.objects.all()
+    default_org = org_model.objects.get(id=default_id)
 
     t_start = time.time()
     count = users_qs.count()
@@ -57,7 +58,8 @@ def add_all_user_to_default_org(apps, schema_editor):
     batch_size = 1000
     for i in range(0, count, batch_size):
         users = list(users_qs[i:i + batch_size])
-        default_org.members.add(*users)
+        members = [org_members_model(user=user, org=default_org) for user in users]
+        org_members_model.objects.bulk_create(members, ignore_conflicts=True)
         print(f'Add users to default org: {i+1}-{i+len(users)}')
     interval = round((time.time() - t_start) * 1000, 2)
     print(f'done, use {interval} ms')
diff --git a/apps/orgs/migrations/0011_auto_20211223_1913.py b/apps/orgs/migrations/0011_auto_20211223_1913.py
new file mode 100644
index 000000000..5a1789ed4
--- /dev/null
+++ b/apps/orgs/migrations/0011_auto_20211223_1913.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2021-12-23 11:13
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('orgs', '0010_auto_20210219_1241'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='organizationmember',
+            name='role',
+            field=models.CharField(default='User', max_length=16, verbose_name='Role'),
+        ),
+    ]
diff --git a/apps/orgs/migrations/0012_auto_20220118_1054.py b/apps/orgs/migrations/0012_auto_20220118_1054.py
new file mode 100644
index 000000000..a7d21277c
--- /dev/null
+++ b/apps/orgs/migrations/0012_auto_20220118_1054.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.13 on 2022-01-18 02:54
+
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('rbac', '0004_auto_20211201_1901'),
+        ('orgs', '0011_auto_20211223_1913'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='organization',
+            options={'permissions': (('view_rootorg', 'Can view root org'),), 'verbose_name': 'Organization'},
+        ),
+        migrations.AlterField(
+            model_name='organization',
+            name='members',
+            field=models.ManyToManyField(related_name='orgs', through='rbac.RoleBinding', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
diff --git a/apps/orgs/models.py b/apps/orgs/models.py
index c8baec8fb..2daa2bd3f 100644
--- a/apps/orgs/models.py
+++ b/apps/orgs/models.py
@@ -1,22 +1,10 @@
 import uuid
-from functools import partial
-from itertools import chain
 
 from django.db import models
-from django.db.models import signals
-from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
 
 from common.utils import lazyproperty, settings
-from common.const import choices
 from common.tree import TreeNode
-from common.db.models import TextChoices
-
-
-class ROLE(TextChoices):
-    ADMIN = choices.ADMIN, _('Organization administrator')
-    AUDITOR = choices.AUDITOR, _("Organization auditor")
-    USER = choices.USER, _('User')
 
 
 class Organization(models.Model):
@@ -25,7 +13,9 @@ class Organization(models.Model):
     created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
     date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
     comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
-    members = models.ManyToManyField('users.User', related_name='orgs', through='orgs.OrganizationMember', through_fields=('org', 'user'))
+    members = models.ManyToManyField(
+        'users.User', related_name='orgs', through='rbac.RoleBinding', through_fields=('org', 'user')
+    )
 
     ROOT_ID = '00000000-0000-0000-0000-000000000000'
     ROOT_NAME = _('GLOBAL')
@@ -35,6 +25,9 @@ class Organization(models.Model):
 
     class Meta:
         verbose_name = _("Organization")
+        permissions = (
+            ('view_rootorg', _('Can view root org')),
+        )
 
     def __str__(self):
         return str(self.name)
@@ -79,113 +72,9 @@ class Organization(models.Model):
     def expire_orgs_mapping(cls):
         cls.orgs_mapping = None
 
-    def get_org_members_by_role(self, role):
-        from users.models import User
-        if not self.is_root():
-            return self.members.filter(m2m_org_members__role=role)
-        users = User.objects.filter(role=role)
-        return users
-
-    @property
-    def users(self):
-        return self.get_org_members_by_role(ROLE.USER)
-
-    @property
-    def admins(self):
-        return self.get_org_members_by_role(ROLE.ADMIN)
-
-    @property
-    def auditors(self):
-        return self.get_org_members_by_role(ROLE.AUDITOR)
-
     def org_id(self):
         return self.id
 
-    def get_members(self, exclude=()):
-        from users.models import User
-        if self.is_root():
-            members = User.objects.exclude(role__in=exclude)
-        else:
-            members = self.members.exclude(m2m_org_members__role__in=exclude)
-        return members.exclude(role=User.ROLE.APP).distinct()
-
-    def can_admin_by(self, user):
-        if user.is_superuser:
-            return True
-        if self.admins.filter(id=user.id).exists():
-            return True
-        return False
-
-    def can_audit_by(self, user):
-        if user.is_superuser or user.is_super_auditor:
-            return True
-        if self.can_admin_by(user):
-            return True
-        if self.auditors.filter(id=user.id).exists():
-            return True
-        return False
-
-    def can_use_by(self, user):
-        if user.is_superuser or user.is_super_auditor:
-            return True
-        if self.can_audit_by(user):
-            return True
-        if self.users.filter(id=user.id).exists():
-            return True
-        return False
-
-    def can_any_by(self, user):
-        if user.is_superuser or user.is_super_auditor:
-            return True
-        return self.members.filter(id=user.id).exists()
-
-    @classmethod
-    def get_user_orgs_by_role(cls, user, role):
-        if not isinstance(role, (tuple, list)):
-            role = (role, )
-
-        return cls.objects.filter(
-            m2m_org_members__role__in=role,
-            m2m_org_members__user_id=user.id
-        ).distinct()
-
-    @classmethod
-    def get_user_all_orgs(cls, user):
-        return cls.objects.filter(members=user).distinct()
-
-    @classmethod
-    def get_user_admin_orgs(cls, user):
-        if user.is_anonymous:
-            return cls.objects.none()
-        if user.is_superuser:
-            return [cls.root(), *cls.objects.all()]
-        return cls.get_user_orgs_by_role(user, ROLE.ADMIN)
-
-    @classmethod
-    def get_user_user_orgs(cls, user):
-        if user.is_anonymous:
-            return cls.objects.none()
-        return [
-            *cls.get_user_orgs_by_role(user, ROLE.USER),
-            cls.default()
-        ]
-
-    @classmethod
-    def get_user_audit_orgs(cls, user):
-        if user.is_anonymous:
-            return cls.objects.none()
-        if user.is_super_auditor:
-            return [cls.root(), *cls.objects.all()]
-        return cls.get_user_orgs_by_role(user, ROLE.AUDITOR)
-
-    @classmethod
-    def get_user_admin_or_audit_orgs(cls, user):
-        if user.is_anonymous:
-            return cls.objects.none()
-        if user.is_superuser or user.is_super_auditor:
-            return [cls.root(), *cls.objects.all()]
-        return cls.get_user_orgs_by_role(user, (ROLE.AUDITOR, ROLE.ADMIN))
-
     @classmethod
     def default(cls):
         defaults = dict(id=cls.DEFAULT_ID, name=cls.DEFAULT_NAME)
@@ -212,10 +101,25 @@ class Organization(models.Model):
         from .caches import OrgResourceStatisticsCache
         return OrgResourceStatisticsCache(self)
 
+    def get_members(self):
+        return self.members.all().distinct()
+
+    def add_member(self, user, role=None):
+        from rbac.builtin import BuiltinRole
+        from .utils import tmp_to_org
+        role_id = BuiltinRole.org_user.id
+        if role:
+            role_id = role.id
+        with tmp_to_org(self):
+            defaults = {
+                'user': user, 'role_id': role_id, 'org_id': self.id, 'scope': 'org'
+            }
+            self.members.through.objects.update_or_create(**defaults, defaults=defaults)
+
     def get_total_resources_amount(self):
         from django.apps import apps
         from orgs.mixins.models import OrgModelMixin
-        summary = {'users.Members': self.members.all().count()}
+        summary = {'users.Members': self.get_members().count()}
         for app_name, app_config in apps.app_configs.items():
             models_cls = app_config.get_models()
             for model in models_cls:
@@ -248,179 +152,35 @@ class Organization(models.Model):
         })
         return node
 
+    def delete_related_models(self):
+        from orgs.utils import tmp_to_root_org
+        from tickets.models import TicketFlow
+        with tmp_to_root_org():
+            TicketFlow.objects.filter(org_id=self.id).delete()
 
-def _convert_to_uuid_set(users):
-    rst = set()
-    for user in users:
-        if isinstance(user, models.Model):
-            rst.add(user.id)
-        elif not isinstance(user, uuid.UUID):
-            rst.add(uuid.UUID(user))
-    return rst
+    def delete(self, *args, **kwargs):
+        self.delete_related_models()
+        return super().delete(*args, **kwargs)
 
 
-def _none2list(*args):
-    return ([] if v is None else v for v in args)
-
-
-def _users2pks_if_need(users, admins, auditors):
-    pks = []
-    for user in chain(users, admins, auditors):
-        if hasattr(user, 'pk'):
-            pks.append(user.pk)
-        else:
-            pks.append(user)
-    return pks
-
-
-class UserRoleMapper(dict):
-    def __init__(self, container=set):
-        super().__init__()
-        self.users = container()
-        self.admins = container()
-        self.auditors = container()
-
-        self[ROLE.USER] = self.users
-        self[ROLE.ADMIN] = self.admins
-        self[ROLE.AUDITOR] = self.auditors
-
-
-class OrgMemberManager(models.Manager):
-
-    def remove_users(self, org, users):
-        from users.models import User
-        pk_set = []
-        for user in users:
-            if hasattr(user, 'pk'):
-                pk_set.append(user.pk)
-            else:
-                pk_set.append(user)
-
-        send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
-                       model=User, pk_set=pk_set, using=self.db)
-        send(action="pre_remove")
-        self.filter(org_id=org.id, user_id__in=pk_set).delete()
-        send(action="post_remove")
-
-    def remove_users_by_role(self, org, users=None, admins=None, auditors=None):
-        from users.models import User
-
-        if not any((users, admins, auditors)):
-            return
-        users, admins, auditors = _none2list(users, admins, auditors)
-
-        send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
-                       model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
-
-        send(action="pre_remove")
-        self.filter(org_id=org.id).filter(
-            Q(user__in=users, role=ROLE.USER) |
-            Q(user__in=admins, role=ROLE.ADMIN) |
-            Q(user__in=auditors, role=ROLE.AUDITOR)
-        ).delete()
-        send(action="post_remove")
-
-    def add_users_by_role(self, org, users=None, admins=None, auditors=None):
-        from users.models import User
-
-        if not any((users, admins, auditors)):
-            return
-        users, admins, auditors = _none2list(users, admins, auditors)
-
-        add_mapper = (
-            (users, ROLE.USER),
-            (admins, ROLE.ADMIN),
-            (auditors, ROLE.AUDITOR)
-        )
-
-        oms_add = []
-        for _users, _role in add_mapper:
-            for _user in _users:
-                if isinstance(_user, models.Model):
-                    _user = _user.id
-                oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role))
-
-        pk_set = _users2pks_if_need(users, admins, auditors)
-        send = partial(
-            signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
-            model=User, pk_set=pk_set, using=self.db
-        )
-
-        send(action='pre_add')
-        self.bulk_create(oms_add, ignore_conflicts=True)
-        send(action='post_add')
-
-    def _get_remove_add_set(self, new_users, old_users):
-        if new_users is None:
-            return None, None
-        new_users = _convert_to_uuid_set(new_users)
-        return (old_users - new_users), (new_users - old_users)
-
-    def set_user_roles(self, org, user, roles):
-        """
-        设置某个用户在某个组织里的角色
-        """
-        old_roles = set(self.filter(org_id=org.id, user=user).values_list('role', flat=True))
-        new_roles = set(roles)
-
-        roles_remove = old_roles - new_roles
-        roles_add = new_roles - old_roles
-
-        to_remove = UserRoleMapper()
-        to_add = UserRoleMapper()
-
-        for role in roles_remove:
-            if role in to_remove:
-                to_remove[role].add(user)
-        for role in roles_add:
-            if role in to_add:
-                to_add[role].add(user)
-
-        # 先添加再移除 (防止用户角色由组织用户->组织管理员时从组织清除用户)
-        self.add_users_by_role(
-            org,
-            to_add.users,
-            to_add.admins,
-            to_add.auditors
-        )
-
-        self.remove_users_by_role(
-            org,
-            to_remove.users,
-            to_remove.admins,
-            to_remove.auditors
-        )
-
-    def set_users_by_role(self, org, users=None, admins=None, auditors=None):
-        """
-        给组织设置带角色的用户
-        """
-
-        oms = self.filter(org_id=org.id).values_list('role', 'user_id')
-
-        old_mapper = UserRoleMapper()
-
-        for role, user_id in oms:
-            if role in old_mapper:
-                old_mapper[role].add(user_id)
-
-        users_remove, users_add = self._get_remove_add_set(users, old_mapper.users)
-        admins_remove, admins_add = self._get_remove_add_set(admins, old_mapper.admins)
-        auditors_remove, auditors_add = self._get_remove_add_set(auditors, old_mapper.auditors)
-
-        self.remove_users_by_role(
-            org,
-            users_remove,
-            admins_remove,
-            auditors_remove
-        )
-
-        self.add_users_by_role(
-            org,
-            users_add,
-            admins_add,
-            auditors_add
-        )
+# class OrgMemberManager(models.Manager):
+#     def remove_users(self, org, users):
+#         from users.models import User
+#         pk_set = []
+#         for user in users:
+#             if hasattr(user, 'pk'):
+#                 pk_set.append(user.pk)
+#             else:
+#                 pk_set.append(user)
+#
+#         send = partial(
+#             signals.m2m_changed.send, sender=self.model,
+#             instance=org, reverse=False, model=User,
+#             pk_set=pk_set, using=self.db
+#         )
+#         send(action="pre_remove")
+#         self.filter(org_id=org.id, user_id__in=pk_set).delete()
+#         send(action="post_remove")
 
 
 class OrganizationMember(models.Model):
@@ -429,18 +189,22 @@ class OrganizationMember(models.Model):
     """
 
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
-    org = models.ForeignKey(Organization, related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('Organization'))
-    user = models.ForeignKey('users.User', related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('User'))
-    role = models.CharField(max_length=16, choices=ROLE.choices, default=ROLE.USER, verbose_name=_("Role"))
+    org = models.ForeignKey(
+        Organization, related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('Organization')
+    )
+    user = models.ForeignKey(
+        'users.User', related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('User')
+    )
+    role = models.CharField(max_length=16, default='User', verbose_name=_("Role"))
     date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
     date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
     created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
 
-    objects = OrgMemberManager()
+    # objects = OrgMemberManager()
 
     class Meta:
         unique_together = [('org', 'user', 'role')]
         db_table = 'orgs_organization_members'
 
     def __str__(self):
-        return '{} is {}: {}'.format(self.user.name, self.org.name, self.role)
+        return '{} | {}'.format(self.user, self.org)
diff --git a/apps/orgs/serializers.py b/apps/orgs/serializers.py
index 24ef3e8be..0ae933044 100644
--- a/apps/orgs/serializers.py
+++ b/apps/orgs/serializers.py
@@ -1,12 +1,8 @@
-from django.db.models import F
 from rest_framework.serializers import ModelSerializer
 from rest_framework import serializers
-from django.utils.translation import ugettext_lazy as _
 
-from users.models.user import User
-from common.drf.serializers import BulkModelSerializer
-from common.db.models import concated_display as display
-from .models import Organization, OrganizationMember, ROLE
+from .utils import get_current_org
+from .models import Organization
 
 
 class ResourceStatisticsSerializer(serializers.Serializer):
@@ -25,11 +21,7 @@ class ResourceStatisticsSerializer(serializers.Serializer):
     app_perms_amount = serializers.IntegerField(required=False)
 
 
-class OrgSerializer(BulkModelSerializer):
-    users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
-    admins = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
-    auditors = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
-
+class OrgSerializer(ModelSerializer):
     resource_statistics = ResourceStatisticsSerializer(source='resource_statistics_cache', read_only=True)
 
     class Meta:
@@ -38,104 +30,26 @@ class OrgSerializer(BulkModelSerializer):
         fields_small = fields_mini + [
             'resource_statistics',
             'is_default', 'is_root',
-            'date_created',
+            'date_created', 'created_by',
             'comment', 'created_by',
         ]
 
-        fields_m2m = ['users', 'admins', 'auditors']
+        fields_m2m = []
         fields = fields_small + fields_m2m
         read_only_fields = ['created_by', 'date_created']
 
-    def create(self, validated_data):
-        members = self._pop_members(validated_data)
-        instance = Organization.objects.create(**validated_data)
-        OrganizationMember.objects.add_users_by_role(instance, *members)
-        return instance
-
-    def _pop_members(self, validated_data):
-        return (
-            validated_data.pop('users', None),
-            validated_data.pop('admins', None),
-            validated_data.pop('auditors', None)
-        )
-
-    def update(self, instance, validated_data):
-        members = self._pop_members(validated_data)
-        for attr, value in validated_data.items():
-            setattr(instance, attr, value)
-        instance.save()
-        OrganizationMember.objects.set_users_by_role(instance, *members)
-        return instance
-
-
-class OrgReadSerializer(OrgSerializer):
-    pass
-
-
-class OrgMemberSerializer(BulkModelSerializer):
-    org_display = serializers.CharField(read_only=True)
-    user_display = serializers.CharField(read_only=True)
-    role_display = serializers.CharField(source='get_role_display', read_only=True)
-
-    class Meta:
-        model = OrganizationMember
-        fields_mini = ['id']
-        fields_small = fields_mini + [
-            'role', 'role_display'
-        ]
-        fields_fk = ['org', 'user', 'org_display', 'user_display',]
-        fields = fields_small + fields_fk
-        use_model_bulk_create = True
-        model_bulk_create_kwargs = {'ignore_conflicts': True}
-
-    def get_unique_together_validators(self):
-        if self.parent:
-            return []
-        return super().get_unique_together_validators()
-
-    @classmethod
-    def setup_eager_loading(cls, queryset):
-        return queryset.annotate(
-            org_display=F('org__name'),
-            user_display=display('user__name', 'user__username')
-        ).distinct()
-
-
-class OrgMemberOldBaseSerializer(BulkModelSerializer):
-    organization = serializers.PrimaryKeyRelatedField(
-        label=_('Organization'), queryset=Organization.objects.all(), required=True, source='org'
-    )
-
-    def to_internal_value(self, data):
-        view = self.context['view']
-        org_id = view.kwargs.get('org_id')
-        if org_id:
-            data['organization'] = org_id
-        return super().to_internal_value(data)
-
-    class Meta:
-        model = OrganizationMember
-        fields = ('id', 'organization', 'user', 'role')
-
-
-class OrgMemberAdminSerializer(OrgMemberOldBaseSerializer):
-    role = serializers.HiddenField(default=ROLE.ADMIN)
-
-
-class OrgMemberUserSerializer(OrgMemberOldBaseSerializer):
-    role = serializers.HiddenField(default=ROLE.USER)
-
-
-class OrgRetrieveSerializer(OrgReadSerializer):
-    admins = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
-    auditors = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
-    users = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
-
-    class Meta(OrgReadSerializer.Meta):
-        pass
-
 
 class CurrentOrgSerializer(ModelSerializer):
     class Meta:
         model = Organization
         fields = ['id', 'name', 'is_default', 'is_root', 'comment']
+
+
+class CurrentOrgDefault:
+    requires_context = False
+
+    def __call__(self, *args):
+        return get_current_org()
+
+    def __repr__(self):
+        return '%s()' % self.__class__.__name__
diff --git a/apps/orgs/signals_handler/__init__.py b/apps/orgs/signal_handlers/__init__.py
similarity index 100%
rename from apps/orgs/signals_handler/__init__.py
rename to apps/orgs/signal_handlers/__init__.py
diff --git a/apps/orgs/signals_handler/cache.py b/apps/orgs/signal_handlers/cache.py
similarity index 82%
rename from apps/orgs/signals_handler/cache.py
rename to apps/orgs/signal_handlers/cache.py
index 734ad7af0..1d9ca3891 100644
--- a/apps/orgs/signals_handler/cache.py
+++ b/apps/orgs/signal_handlers/cache.py
@@ -1,15 +1,13 @@
-from django.db.models.signals import m2m_changed
 from django.db.models.signals import post_save, pre_delete, pre_save, post_delete
 from django.dispatch import receiver
 
-from orgs.models import Organization, OrganizationMember
+from orgs.models import Organization
 from assets.models import Node
 from perms.models import (AssetPermission, ApplicationPermission)
 from users.models import UserGroup, User
 from applications.models import Application
 from terminal.models import Session
-from assets.models import Asset, AdminUser, SystemUser, Domain, Gateway
-from common.const.signals import POST_PREFIX
+from assets.models import Asset, SystemUser, Domain, Gateway
 from orgs.caches import OrgResourceStatisticsCache
 
 
@@ -32,20 +30,20 @@ def on_user_delete_refresh_cache(sender, instance, **kwargs):
     refresh_user_amount_on_user_create_or_delete(instance.id)
 
 
-@receiver(m2m_changed, sender=OrganizationMember)
-def on_org_user_changed_refresh_cache(sender, action, instance, reverse, pk_set, **kwargs):
-    if not action.startswith(POST_PREFIX):
-        return
-
-    if reverse:
-        orgs = Organization.objects.filter(id__in=pk_set)
-    else:
-        orgs = [instance]
-
-    for org in orgs:
-        org_cache = OrgResourceStatisticsCache(org)
-        org_cache.expire('users_amount')
-    OrgResourceStatisticsCache(Organization.root()).expire('users_amount')
+# @receiver(m2m_changed, sender=OrganizationMember)
+# def on_org_user_changed_refresh_cache(sender, action, instance, reverse, pk_set, **kwargs):
+#     if not action.startswith(POST_PREFIX):
+#         return
+#
+#     if reverse:
+#         orgs = Organization.objects.filter(id__in=pk_set)
+#     else:
+#         orgs = [instance]
+#
+#     for org in orgs:
+#         org_cache = OrgResourceStatisticsCache(org)
+#         org_cache.expire('users_amount')
+#     OrgResourceStatisticsCache(Organization.root()).expire('users_amount')
 
 
 class OrgResourceStatisticsRefreshUtil:
diff --git a/apps/orgs/signals_handler/common.py b/apps/orgs/signal_handlers/common.py
similarity index 83%
rename from apps/orgs/signals_handler/common.py
rename to apps/orgs/signal_handlers/common.py
index f49b32f19..dfd29955d 100644
--- a/apps/orgs/signals_handler/common.py
+++ b/apps/orgs/signal_handlers/common.py
@@ -10,7 +10,7 @@ from django.db.models.signals import m2m_changed
 from django.db.models.signals import post_save, pre_delete
 
 from orgs.utils import tmp_to_org
-from orgs.models import Organization, OrganizationMember
+from orgs.models import Organization
 from orgs.hands import set_current_org, Node, get_current_org
 from perms.models import (AssetPermission, ApplicationPermission)
 from users.models import UserGroup, User
@@ -20,6 +20,7 @@ from common.signals import django_ready
 from common.utils import get_logger
 from common.utils.connection import RedisPubSub
 from assets.models import CommandFilterRule
+from users.signals import post_user_leave_org
 
 
 logger = get_logger(__file__)
@@ -55,6 +56,7 @@ def subscribe_orgs_mapping_expire(sender, **kwargs):
     t.start()
 
 
+# 创建对应的root
 @receiver(post_save, sender=Organization)
 def on_org_create_or_update(sender, instance, created=False, **kwargs):
     # 必须放到最开始, 因为下面调用Node.save方法时会获取当前组织的org_id(即instance.org_id), 如果不过期会找不到
@@ -72,9 +74,6 @@ def on_org_create_or_update(sender, instance, created=False, **kwargs):
 def on_org_delete(sender, instance, **kwargs):
     expire_orgs_mapping_for_memory(instance.id)
 
-
-@receiver(pre_delete, sender=Organization)
-def on_org_delete(sender, instance, **kwargs):
     # 删除该组织下所有 节点
     with tmp_to_org(instance):
         root_node = Node.org_root()
@@ -144,25 +143,6 @@ def _clear_users_from_org(org, users):
     _remove_users(CommandFilterRule, users, org, user_field_name='reviewers')
 
 
-@receiver(m2m_changed, sender=OrganizationMember)
-def on_org_user_changed(action, instance, reverse, pk_set, **kwargs):
-    if action == 'post_remove':
-        if reverse:
-            user = instance
-            org_pk_set = pk_set
-
-            orgs = Organization.objects.filter(id__in=org_pk_set)
-            for org in orgs:
-                if not org.members.filter(id=user.id).exists():
-                    _clear_users_from_org(org, user)
-        else:
-            org = instance
-            user_pk_set = pk_set
-
-            leaved_users = set(pk_set) - set(org.members.filter(id__in=user_pk_set).values_list('id', flat=True))
-            _clear_users_from_org(org, leaved_users)
-
-
 @receiver(post_save, sender=User)
 @on_transaction_commit
 def on_user_created_set_default_org(sender, instance, created, **kwargs):
@@ -170,4 +150,11 @@ def on_user_created_set_default_org(sender, instance, created, **kwargs):
         return
     if instance.orgs.count() > 0:
         return
-    Organization.default().members.add(instance)
+    with tmp_to_org(Organization.default()):
+        Organization.default().add_member(instance)
+
+
+@receiver(post_user_leave_org)
+def on_user_leave_org(sender, user=None, org=None, **kwargs):
+    logger.debug('User leave org signal recv: {} <> {}'.format(user, org))
+    _clear_users_from_org(org, [user])
diff --git a/apps/orgs/urls/api_urls.py b/apps/orgs/urls/api_urls.py
index 61fad175e..e064d226f 100644
--- a/apps/orgs/urls/api_urls.py
+++ b/apps/orgs/urls/api_urls.py
@@ -11,13 +11,6 @@ app_name = 'orgs'
 router = BulkRouter()
 
 router.register(r'orgs', api.OrgViewSet, 'org')
-router.register(r'org-member-relation', api.OrgMemberRelationBulkViewSet, 'org-member-relation')
-
-router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
-                api.OrgMemberAdminRelationBulkViewSet, 'membership-admins')
-router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
-                api.OrgMemberUserRelationBulkViewSet, 'membership-users'),
-
 
 urlpatterns = [
     path('orgs/current/', api.CurrentOrgDetailApi.as_view(), name='current-org-detail'),
diff --git a/apps/perms/api/application/application_permission_relation.py b/apps/perms/api/application/application_permission_relation.py
index 770463f7e..611d0930e 100644
--- a/apps/perms/api/application/application_permission_relation.py
+++ b/apps/perms/api/application/application_permission_relation.py
@@ -9,7 +9,6 @@ from applications.models import Application
 from orgs.mixins.api import OrgRelationMixin
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.utils import current_org
-from common.permissions import IsOrgAdmin
 from perms import serializers
 from perms import models
 
@@ -24,6 +23,8 @@ __all__ = [
 
 
 class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
+    perm_model = models.ApplicationPermission
+
     def get_queryset(self):
         queryset = super().get_queryset()
         org_id = current_org.org_id()
@@ -36,7 +37,6 @@ class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
 class ApplicationPermissionUserRelationViewSet(RelationMixin):
     serializer_class = serializers.ApplicationPermissionUserRelationSerializer
     m2m_field = models.ApplicationPermission.users.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', "user", "applicationpermission",
     ]
@@ -51,7 +51,6 @@ class ApplicationPermissionUserRelationViewSet(RelationMixin):
 class ApplicationPermissionUserGroupRelationViewSet(RelationMixin):
     serializer_class = serializers.ApplicationPermissionUserGroupRelationSerializer
     m2m_field = models.ApplicationPermission.user_groups.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', "usergroup", "applicationpermission"
     ]
@@ -66,7 +65,6 @@ class ApplicationPermissionUserGroupRelationViewSet(RelationMixin):
 class ApplicationPermissionApplicationRelationViewSet(RelationMixin):
     serializer_class = serializers.ApplicationPermissionApplicationRelationSerializer
     m2m_field = models.ApplicationPermission.applications.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'application', 'applicationpermission',
     ]
@@ -81,7 +79,6 @@ class ApplicationPermissionApplicationRelationViewSet(RelationMixin):
 class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
     serializer_class = serializers.ApplicationPermissionSystemUserRelationSerializer
     m2m_field = models.ApplicationPermission.system_users.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'systemuser', 'applicationpermission',
     ]
@@ -91,8 +88,8 @@ class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
 
     def get_queryset(self):
         queryset = super().get_queryset()
-        queryset = queryset \
-            .annotate(systemuser_display=Concat(
+        queryset = queryset.annotate(
+            systemuser_display=Concat(
                 F('systemuser__name'), Value('('), F('systemuser__username'),
                 Value(')')
             ))
@@ -100,7 +97,6 @@ class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
 
 
 class ApplicationPermissionAllApplicationListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.ApplicationPermissionAllApplicationSerializer
     only_fields = serializers.ApplicationPermissionAllApplicationSerializer.Meta.only_fields
     filterset_fields = ('name',)
@@ -109,13 +105,12 @@ class ApplicationPermissionAllApplicationListApi(generics.ListAPIView):
     def get_queryset(self):
         pk = self.kwargs.get('pk')
         perm = get_object_or_404(models.ApplicationPermission, pk=pk)
-        applications = Application.objects.filter(granted_by_permissions=perm)\
+        applications = Application.objects.filter(granted_by_permissions=perm) \
             .only(*self.only_fields).distinct()
         return applications
 
 
 class ApplicationPermissionAllUserListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.ApplicationPermissionAllUserSerializer
     only_fields = serializers.ApplicationPermissionAllUserSerializer.Meta.only_fields
     filterset_fields = ('username', 'name')
diff --git a/apps/perms/api/application/user_group_permission.py b/apps/perms/api/application/user_group_permission.py
index 34c3eefaa..e8061e9fc 100644
--- a/apps/perms/api/application/user_group_permission.py
+++ b/apps/perms/api/application/user_group_permission.py
@@ -4,7 +4,6 @@
 from django.db.models import Q
 from rest_framework.generics import ListAPIView
 
-from common.permissions import IsOrgAdminOrAppUser
 from common.mixins.api import CommonApiMixin
 from applications.models import Application
 from perms import serializers
@@ -18,14 +17,19 @@ class UserGroupGrantedApplicationsApi(CommonApiMixin, ListAPIView):
     """
     获取用户组直接授权的应用
     """
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.AppGrantedSerializer
     only_fields = serializers.AppGrantedSerializer.Meta.only_fields
     filterset_fields = ['id', 'name', 'category', 'type', 'comment']
     search_fields = ['name', 'comment']
+    rbac_perms = {
+        'list': 'perms.view_applicationpermission'
+    }
 
     def get_queryset(self):
-        user_group_id = self.kwargs.get('pk', '')
+        user_group_id = self.kwargs.get('pk')
+        if not user_group_id:
+            return Application.objects.none()
+
         queryset = Application.objects\
             .filter(Q(granted_by_permissions__user_groups__id=user_group_id))\
             .distinct().only(*self.only_fields)
diff --git a/apps/perms/api/application/user_permission/common.py b/apps/perms/api/application/user_permission/common.py
index 707e2ca72..f24718560 100644
--- a/apps/perms/api/application/user_permission/common.py
+++ b/apps/perms/api/application/user_permission/common.py
@@ -10,14 +10,13 @@ from rest_framework.generics import (
     ListAPIView, get_object_or_404
 )
 
-from orgs.utils import tmp_to_root_org
+from orgs.utils import tmp_to_root_org, get_current_org
 from applications.models import Application
 from perms.utils.application.permission import (
     get_application_system_user_ids,
     validate_permission,
 )
-from perms.api.asset.user_permission.mixin import RoleAdminMixin, RoleUserMixin
-from common.permissions import IsOrgAdminOrAppUser
+from .mixin import RoleAdminMixin, RoleUserMixin
 from perms.hands import User, SystemUser
 from perms import serializers
 
@@ -29,7 +28,7 @@ __all__ = [
 ]
 
 
-class GrantedApplicationSystemUsersMixin(ListAPIView):
+class BaseGrantedApplicationSystemUsersApi(ListAPIView):
     serializer_class = serializers.ApplicationSystemUserSerializer
     only_fields = serializers.ApplicationSystemUserSerializer.Meta.only_fields
     user: None
@@ -46,17 +45,19 @@ class GrantedApplicationSystemUsersMixin(ListAPIView):
         return system_users
 
 
-class UserGrantedApplicationSystemUsersApi(RoleAdminMixin, GrantedApplicationSystemUsersMixin):
+class UserGrantedApplicationSystemUsersApi(RoleAdminMixin, BaseGrantedApplicationSystemUsersApi):
     pass
 
 
-class MyGrantedApplicationSystemUsersApi(RoleUserMixin, GrantedApplicationSystemUsersMixin):
+class MyGrantedApplicationSystemUsersApi(RoleUserMixin, BaseGrantedApplicationSystemUsersApi):
     pass
 
 
 @method_decorator(tmp_to_root_org(), name='get')
 class ValidateUserApplicationPermissionApi(APIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'GET': 'perms.view_applicationpermission'
+    }
 
     def get(self, request, *args, **kwargs):
         user_id = request.query_params.get('user_id', '')
diff --git a/apps/perms/api/application/user_permission/mixin.py b/apps/perms/api/application/user_permission/mixin.py
new file mode 100644
index 000000000..9f2becb34
--- /dev/null
+++ b/apps/perms/api/application/user_permission/mixin.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+#
+
+from common.mixins.api import RoleAdminMixin as _RoleAdminMixin
+from common.mixins.api import RoleUserMixin as _RoleUserMixin
+from orgs.utils import tmp_to_root_org
+
+
+class RoleAdminMixin(_RoleAdminMixin):
+    rbac_perms = (
+        ('list', 'perms.view_userapp'),
+        ('retrieve', 'perms.view_userapps'),
+        ('get_tree', 'perms.view_userapps'),
+        ('GET', 'perms.view_userapps'),
+    )
+
+
+class RoleUserMixin(_RoleUserMixin):
+    rbac_perms = (
+        ('list', 'perms.view_myapps'),
+        ('retrieve', 'perms.view_myapps'),
+        ('get_tree', 'perms.view_myapps'),
+        ('GET', 'perms.view_myapps'),
+    )
+
+    def dispatch(self, *args, **kwargs):
+        with tmp_to_root_org():
+            return super().dispatch(*args, **kwargs)
\ No newline at end of file
diff --git a/apps/perms/api/asset/asset_permission.py b/apps/perms/api/asset/asset_permission.py
index 7b3be59c1..afadc456c 100644
--- a/apps/perms/api/asset/asset_permission.py
+++ b/apps/perms/api/asset/asset_permission.py
@@ -4,19 +4,15 @@ from perms.filters import AssetPermissionFilter
 from perms.models import AssetPermission
 from orgs.mixins.api import OrgBulkModelViewSet
 from perms import serializers
-from common.permissions import IsOrgAdmin
 
 
-__all__ = [
-    'AssetPermissionViewSet',
-]
+__all__ = ['AssetPermissionViewSet']
 
 
 class AssetPermissionViewSet(OrgBulkModelViewSet):
     """
     资产授权列表的增删改查api
     """
-    permission_classes = (IsOrgAdmin,)
     model = AssetPermission
     serializer_class = serializers.AssetPermissionSerializer
     filterset_class = AssetPermissionFilter
diff --git a/apps/perms/api/asset/asset_permission_relation.py b/apps/perms/api/asset/asset_permission_relation.py
index ed3c30964..b0a67f858 100644
--- a/apps/perms/api/asset/asset_permission_relation.py
+++ b/apps/perms/api/asset/asset_permission_relation.py
@@ -2,15 +2,12 @@
 #
 from rest_framework import generics
 from django.db.models import F, Value
-from django.db.models import Q
 from django.db.models.functions import Concat
 from django.shortcuts import get_object_or_404
 
-from assets.models import Node, Asset
 from orgs.mixins.api import OrgRelationMixin
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.utils import current_org
-from common.permissions import IsOrgAdmin
 from perms import serializers
 from perms import models
 from perms.utils.asset.user_permission import UserGrantedAssetsQueryUtils
@@ -24,6 +21,8 @@ __all__ = [
 
 
 class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
+    perm_model = models.AssetPermission
+
     def get_queryset(self):
         queryset = super().get_queryset()
         org_id = current_org.org_id()
@@ -36,7 +35,6 @@ class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
 class AssetPermissionUserRelationViewSet(RelationMixin):
     serializer_class = serializers.AssetPermissionUserRelationSerializer
     m2m_field = models.AssetPermission.users.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', "user", "assetpermission",
     ]
@@ -50,7 +48,6 @@ class AssetPermissionUserRelationViewSet(RelationMixin):
 
 
 class AssetPermissionAllUserListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.AssetPermissionAllUserSerializer
     filterset_fields = ("username", "name")
     search_fields = filterset_fields
@@ -67,7 +64,6 @@ class AssetPermissionAllUserListApi(generics.ListAPIView):
 class AssetPermissionUserGroupRelationViewSet(RelationMixin):
     serializer_class = serializers.AssetPermissionUserGroupRelationSerializer
     m2m_field = models.AssetPermission.user_groups.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', "usergroup", "assetpermission"
     ]
@@ -83,7 +79,6 @@ class AssetPermissionUserGroupRelationViewSet(RelationMixin):
 class AssetPermissionAssetRelationViewSet(RelationMixin):
     serializer_class = serializers.AssetPermissionAssetRelationSerializer
     m2m_field = models.AssetPermission.assets.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'asset', 'assetpermission',
     ]
@@ -97,7 +92,6 @@ class AssetPermissionAssetRelationViewSet(RelationMixin):
 
 
 class AssetPermissionAllAssetListApi(generics.ListAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.AssetPermissionAllAssetSerializer
     filterset_fields = ("hostname", "ip")
     search_fields = filterset_fields
@@ -112,7 +106,6 @@ class AssetPermissionAllAssetListApi(generics.ListAPIView):
 class AssetPermissionNodeRelationViewSet(RelationMixin):
     serializer_class = serializers.AssetPermissionNodeRelationSerializer
     m2m_field = models.AssetPermission.nodes.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'node', 'assetpermission',
     ]
@@ -128,7 +121,6 @@ class AssetPermissionNodeRelationViewSet(RelationMixin):
 class AssetPermissionSystemUserRelationViewSet(RelationMixin):
     serializer_class = serializers.AssetPermissionSystemUserRelationSerializer
     m2m_field = models.AssetPermission.system_users.field
-    permission_classes = (IsOrgAdmin,)
     filterset_fields = [
         'id', 'systemuser', 'assetpermission',
     ]
@@ -138,9 +130,8 @@ class AssetPermissionSystemUserRelationViewSet(RelationMixin):
 
     def get_queryset(self):
         queryset = super().get_queryset()
-        queryset = queryset \
-            .annotate(systemuser_display=Concat(
-                F('systemuser__name'), Value('('), F('systemuser__username'),
-                Value(')')
+        queryset = queryset.annotate(
+            systemuser_display=Concat(
+                F('systemuser__name'), Value('('), F('systemuser__username'), Value(')')
             ))
         return queryset
diff --git a/apps/perms/api/asset/user_group_permission.py b/apps/perms/api/asset/user_group_permission.py
index e8cea21b5..9b6499bb3 100644
--- a/apps/perms/api/asset/user_group_permission.py
+++ b/apps/perms/api/asset/user_group_permission.py
@@ -6,7 +6,6 @@ from django.db.models import Q
 from rest_framework.generics import ListAPIView
 from rest_framework.response import Response
 
-from common.permissions import IsOrgAdminOrAppUser
 from common.utils import lazyproperty
 from perms.models import AssetPermission
 from assets.models import Asset, Node
@@ -32,14 +31,18 @@ class UserGroupMixin:
 
 
 class UserGroupGrantedAssetsApi(ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.AssetGrantedSerializer
     only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
     filterset_fields = ['hostname', 'ip', 'id', 'comment']
     search_fields = ['hostname', 'ip', 'comment']
+    rbac_perms = {
+        'list': 'perms.view_usergroupassets',
+    }
 
     def get_queryset(self):
-        user_group_id = self.kwargs.get('pk', '')
+        user_group_id = self.kwargs.get('pk')
+        if not user_group_id:
+            return Asset.objects.none()
 
         asset_perm_ids = list(AssetPermission.objects.valid().filter(
             user_groups__id=user_group_id
@@ -65,11 +68,13 @@ class UserGroupGrantedAssetsApi(ListAPIView):
 
 
 class UserGroupGrantedNodeAssetsApi(ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.AssetGrantedSerializer
     only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
     filterset_fields = ['hostname', 'ip', 'id', 'comment']
     search_fields = ['hostname', 'ip', 'comment']
+    rbac_perms = {
+        'list': 'perms.view_usergroupassets',
+    }
 
     def get_queryset(self):
         if getattr(self, 'swagger_fake_view', False):
@@ -119,10 +124,15 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
 
 class UserGroupGrantedNodesApi(ListAPIView):
     serializer_class = serializers.NodeGrantedSerializer
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'list': 'perms.view_usergroupassets',
+    }
 
     def get_queryset(self):
-        user_group_id = self.kwargs.get('pk', '')
+        user_group_id = self.kwargs.get('pk')
+        if not user_group_id:
+            return Node.objects.none()
+
         nodes = Node.objects.filter(
             Q(granted_by_permissions__user_groups__id=user_group_id) |
             Q(assets__granted_by_permissions__user_groups__id=user_group_id)
@@ -131,7 +141,10 @@ class UserGroupGrantedNodesApi(ListAPIView):
 
 
 class UserGroupGrantedNodeChildrenAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = {
+        'list': 'perms.view_usergroupassets',
+        'GET': 'perms.view_usergroupassets',
+    }
 
     def get_children_nodes(self, parent_key):
         return Node.objects.filter(parent_key=parent_key)
diff --git a/apps/perms/api/asset/user_permission/common.py b/apps/perms/api/asset/user_permission/common.py
index e874d591f..609fb52c3 100644
--- a/apps/perms/api/asset/user_permission/common.py
+++ b/apps/perms/api/asset/user_permission/common.py
@@ -13,7 +13,7 @@ from rest_framework.generics import (
 
 from orgs.utils import tmp_to_root_org
 from perms.utils.asset.permission import get_asset_system_user_ids_with_actions_by_user, validate_permission
-from common.permissions import IsOrgAdminOrAppUser, IsOrgAdmin, IsValidUser
+from common.permissions import IsValidUser
 from common.utils import get_logger, lazyproperty
 
 from perms.hands import User, Asset, SystemUser
@@ -33,8 +33,11 @@ __all__ = [
 
 @method_decorator(tmp_to_root_org(), name='get')
 class GetUserAssetPermissionActionsApi(RetrieveAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.ActionsSerializer
+    rbac_perms = {
+        'retrieve': 'perms.view_userassets',
+        'GET': 'perms.view_userassets',
+    }
 
     def get_user(self):
         user_id = self.request.query_params.get('user_id', '')
@@ -61,10 +64,9 @@ class GetUserAssetPermissionActionsApi(RetrieveAPIView):
 
 @method_decorator(tmp_to_root_org(), name='get')
 class ValidateUserAssetPermissionApi(APIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
-
-    def get_cache_policy(self):
-        return 0
+    rbac_perms = {
+        'GET': 'perms.view_userassets'
+    }
 
     def get(self, request, *args, **kwargs):
         user_id = self.request.query_params.get('user_id', '')
@@ -97,39 +99,54 @@ class ValidateUserAssetPermissionApi(APIView):
 
 # TODO 删除
 class RefreshAssetPermissionCacheApi(RetrieveAPIView):
-    permission_classes = (IsOrgAdmin,)
-
     def retrieve(self, request, *args, **kwargs):
         return Response({'msg': True}, status=200)
 
 
 class UserGrantedAssetSystemUsersForAdminApi(ListAPIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
     serializer_class = serializers.AssetSystemUserSerializer
     only_fields = serializers.AssetSystemUserSerializer.Meta.only_fields
+    rbac_perms = {
+        'list': 'perms.view_userassets'
+    }
 
     @lazyproperty
     def user(self):
         user_id = self.kwargs.get('pk')
         return User.objects.get(id=user_id)
 
+    @lazyproperty
+    def system_users_with_actions(self):
+        asset_id = self.kwargs.get('asset_id')
+        asset = get_object_or_404(Asset, id=asset_id, is_active=True)
+        return self.get_asset_system_user_ids_with_actions(asset)
+
     def get_asset_system_user_ids_with_actions(self, asset):
         return get_asset_system_user_ids_with_actions_by_user(self.user, asset)
 
     def get_queryset(self):
-        asset_id = self.kwargs.get('asset_id')
-        asset = get_object_or_404(Asset, id=asset_id, is_active=True)
-        system_users_with_actions = self.get_asset_system_user_ids_with_actions(asset)
-        system_user_ids = system_users_with_actions.keys()
-        system_users = SystemUser.objects.filter(id__in=system_user_ids)\
+        system_user_ids = self.system_users_with_actions.keys()
+        system_users = SystemUser.objects.filter(id__in=system_user_ids) \
             .only(*self.serializer_class.Meta.only_fields) \
             .order_by('name')
-        system_users = list(system_users)
-        for system_user in system_users:
-            actions = system_users_with_actions.get(system_user.id, 0)
-            system_user.actions = actions
         return system_users
 
+    def paginate_queryset(self, queryset):
+        page = super().paginate_queryset(queryset)
+
+        if page:
+            page = self.set_systemusers_action(page)
+        else:
+            self.set_systemusers_action(queryset)
+        return page
+
+    def set_systemusers_action(self, queryset):
+        queryset_list = list(queryset)
+        for system_user in queryset_list:
+            actions = self.system_users_with_actions.get(system_user.id, 0)
+            system_user.actions = actions
+        return queryset_list
+
 
 @method_decorator(tmp_to_root_org(), name='list')
 class MyGrantedAssetSystemUsersApi(UserGrantedAssetSystemUsersForAdminApi):
@@ -142,7 +159,5 @@ class MyGrantedAssetSystemUsersApi(UserGrantedAssetSystemUsersForAdminApi):
 
 # TODO 删除
 class UserAssetPermissionsCacheApi(DestroyAPIView):
-    permission_classes = (IsOrgAdmin,)
-
     def destroy(self, request, *args, **kwargs):
         return Response(status=204)
diff --git a/apps/perms/api/asset/user_permission/mixin.py b/apps/perms/api/asset/user_permission/mixin.py
index c0f25b8a4..9514d41b0 100644
--- a/apps/perms/api/asset/user_permission/mixin.py
+++ b/apps/perms/api/asset/user_permission/mixin.py
@@ -2,7 +2,6 @@
 #
 from rest_framework.request import Request
 
-from common.permissions import IsOrgAdminOrAppUser, IsValidUser
 from common.http import is_true
 from common.mixins.api import RoleAdminMixin as _RoleAdminMixin
 from common.mixins.api import RoleUserMixin as _RoleUserMixin
@@ -22,12 +21,22 @@ class PermBaseMixin:
 
 
 class RoleAdminMixin(PermBaseMixin, _RoleAdminMixin):
-    permission_classes = (IsOrgAdminOrAppUser,)
+    rbac_perms = (
+        ('list', 'perms.view_userassets'),
+        ('retrieve', 'perms.view_userassets'),
+        ('get_tree', 'perms.view_userassets'),
+        ('GET', 'perms.view_userassets'),
+    )
 
 
 class RoleUserMixin(PermBaseMixin, _RoleUserMixin):
-    permission_classes = (IsValidUser,)
+    rbac_perms = (
+        ('list', 'perms.view_myassets'),
+        ('retrieve', 'perms.view_myassets'),
+        ('get_tree', 'perms.view_myassets'),
+        ('GET', 'perms.view_myassets'),
+    )
 
-    def get(self, request, *args, **kwargs):
+    def dispatch(self, *args, **kwargs):
         with tmp_to_root_org():
-            return super().get(request, *args, **kwargs)
+            return super().dispatch(*args, **kwargs)
diff --git a/apps/perms/api/asset/user_permission/user_permission_assets/views.py b/apps/perms/api/asset/user_permission/user_permission_assets/views.py
index 8a9690e12..64ddb0180 100644
--- a/apps/perms/api/asset/user_permission/user_permission_assets/views.py
+++ b/apps/perms/api/asset/user_permission/user_permission_assets/views.py
@@ -85,15 +85,15 @@ class MyAllAssetsAsTreeApi(UserAllGrantedAssetsQuerysetMixin,
     pass
 
 
-class UserGrantedNodeAssetsForAdminApi(UserGrantedNodeAssetsMixin,
-                                       RoleAdminMixin,
+class UserGrantedNodeAssetsForAdminApi(RoleAdminMixin,
+                                       UserGrantedNodeAssetsMixin,
                                        AssetsSerializerFormatMixin,
                                        ListAPIView):
     pass
 
 
-class MyGrantedNodeAssetsApi(UserGrantedNodeAssetsMixin,
-                             RoleUserMixin,
+class MyGrantedNodeAssetsApi(RoleUserMixin,
+                             UserGrantedNodeAssetsMixin,
                              AssetsSerializerFormatMixin,
                              ListAPIView):
     pass
diff --git a/apps/perms/api/asset/user_permission/user_permission_nodes.py b/apps/perms/api/asset/user_permission/user_permission_nodes.py
index 2a5cfacf2..558b888c2 100644
--- a/apps/perms/api/asset/user_permission/user_permission_nodes.py
+++ b/apps/perms/api/asset/user_permission/user_permission_nodes.py
@@ -113,7 +113,9 @@ class UserGrantedNodeChildrenAsTreeForAdminApi(RoleAdminMixin, UserGrantedNodeCh
 
 
 class MyGrantedNodeChildrenAsTreeApi(RoleUserMixin, UserGrantedNodeChildrenMixin, BaseNodeChildrenAsTreeApi):
-    pass
+    def get_permissions(self):
+        permissions = super().get_permissions()
+        return permissions
 
 
 class UserGrantedNodesForAdminApi(RoleAdminMixin, UserGrantedNodesMixin, BaseGrantedNodeApi):
diff --git a/apps/perms/api/base.py b/apps/perms/api/base.py
index 8c0028baf..0e285796e 100644
--- a/apps/perms/api/base.py
+++ b/apps/perms/api/base.py
@@ -1,5 +1,4 @@
 from django.db.models import Q
-from common.permissions import IsOrgAdmin
 from common.utils import get_object_or_none
 from orgs.mixins.api import OrgBulkModelViewSet
 from assets.models import SystemUser
@@ -14,7 +13,6 @@ class BasePermissionViewSet(OrgBulkModelViewSet):
         'user_id', 'username', 'system_user_id', 'system_user',
         'user_group_id', 'user_group'
     ]
-    permission_classes = (IsOrgAdmin,)
 
     def filter_valid(self, queryset):
         valid_query = self.request.query_params.get('is_valid', None)
diff --git a/apps/perms/apps.py b/apps/perms/apps.py
index d6ce1f752..432c194cd 100644
--- a/apps/perms/apps.py
+++ b/apps/perms/apps.py
@@ -1,12 +1,14 @@
 from __future__ import unicode_literals
 
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class PermsConfig(AppConfig):
     name = 'perms'
+    verbose_name = _('App permissions')
 
     def ready(self):
         super().ready()
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
diff --git a/apps/perms/migrations/0024_auto_20220217_2135.py b/apps/perms/migrations/0024_auto_20220217_2135.py
new file mode 100644
index 000000000..60e1a2ecf
--- /dev/null
+++ b/apps/perms/migrations/0024_auto_20220217_2135.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.1.13 on 2022-02-17 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('perms', '0023_auto_20220112_2035'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='applicationpermission',
+            options={'ordering': ('name',), 'verbose_name': 'Application permission'},
+        ),
+        migrations.AlterModelOptions(
+            name='assetpermission',
+            options={'ordering': ('name',), 'verbose_name': 'Asset permission'},
+        ),
+    ]
diff --git a/apps/perms/migrations/0025_auto_20220223_1539.py b/apps/perms/migrations/0025_auto_20220223_1539.py
new file mode 100644
index 000000000..47d3487c7
--- /dev/null
+++ b/apps/perms/migrations/0025_auto_20220223_1539.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2022-02-23 07:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('perms', '0024_auto_20220217_2135'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='applicationpermission',
+            name='type',
+            field=models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
+        ),
+    ]
diff --git a/apps/perms/migrations/0026_auto_20220307_1500.py b/apps/perms/migrations/0026_auto_20220307_1500.py
new file mode 100644
index 000000000..efb4a1795
--- /dev/null
+++ b/apps/perms/migrations/0026_auto_20220307_1500.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.1.14 on 2022-03-07 07:00
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0018_auto_20220223_1539'),
+        ('assets', '0088_auto_20220303_1612'),
+        ('perms', '0025_auto_20220223_1539'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PermedAsset',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Permed asset',
+                'permissions': [('view_myassets', 'Can view my assets'), ('connect_myassets', 'Can connect my assets'), ('view_userassets', 'Can view user assets'), ('view_usergroupassets', 'Can view usergroup assets')],
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('assets.asset',),
+        ),
+    ]
diff --git a/apps/perms/migrations/0027_auto_20220310_1802.py b/apps/perms/migrations/0027_auto_20220310_1802.py
new file mode 100644
index 000000000..b4446f82a
--- /dev/null
+++ b/apps/perms/migrations/0027_auto_20220310_1802.py
@@ -0,0 +1,36 @@
+# Generated by Django 3.1.14 on 2022-03-10 10:02
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0018_auto_20220223_1539'),
+        ('perms', '0026_auto_20220307_1500'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PermedApplication',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Permed application',
+                'permissions': [('view_myapps', 'Can view my apps'), ('connect_myapps', 'Can connect my apps'), ('view_userapps', 'Can view user apps'), ('view_usergroupapps', 'Can view usergroup apps')],
+                'proxy': True,
+                'default_permissions': [],
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('applications.application',),
+        ),
+        migrations.AlterModelOptions(
+            name='applicationpermission',
+            options={'ordering': ('name',), 'permissions': [('view_permuserapplication', 'Can view application of permission to user')], 'verbose_name': 'Application permission'},
+        ),
+        migrations.AlterModelOptions(
+            name='assetpermission',
+            options={'ordering': ('name',), 'permissions': [('view_permuserasset', 'Can view asset of permission to user'), ('view_permusergroupasset', 'Can view asset of permission to user group')], 'verbose_name': 'Asset permission'},
+        ),
+    ]
diff --git a/apps/perms/models/application_permission.py b/apps/perms/models/application_permission.py
index bec5f3da5..6cf5e33d7 100644
--- a/apps/perms/models/application_permission.py
+++ b/apps/perms/models/application_permission.py
@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _
 
 from common.utils import lazyproperty
 from .base import BasePermission, Action
+from applications.models import Application
 from users.models import User
 from applications.const import AppCategory, AppType
 
@@ -35,6 +36,9 @@ class ApplicationPermission(BasePermission):
     class Meta:
         unique_together = [('org_id', 'name')]
         verbose_name = _('Application permission')
+        permissions = [
+            ('view_permuserapplication', _('Can view application of permission to user'))
+        ]
         ordering = ('name',)
 
     @property
@@ -100,3 +104,16 @@ class ApplicationPermission(BasePermission):
         include_choices = cls.get_include_actions_choices(category)
         exclude_choices = set(Action.NAME_MAP.values()) - set(include_choices)
         return exclude_choices
+
+
+class PermedApplication(Application):
+    class Meta:
+        proxy = True
+        verbose_name = _('Permed application')
+        default_permissions = []
+        permissions = [
+            ('view_myapps', 'Can view my apps'),
+            ('connect_myapps', 'Can connect my apps'),
+            ('view_userapps', _('Can view user apps')),
+            ('view_usergroupapps', _('Can view usergroup apps')),
+        ]
diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py
index 7947449e7..0834e704a 100644
--- a/apps/perms/models/asset_permission.py
+++ b/apps/perms/models/asset_permission.py
@@ -1,9 +1,8 @@
 import logging
 
 from django.utils.translation import ugettext_lazy as _
-from django.db.models import F
+from django.db.models import F, TextChoices
 
-from common.db.models import TextChoices
 from orgs.mixins.models import OrgModelMixin
 from common.db import models
 from common.utils import lazyproperty
@@ -29,6 +28,10 @@ class AssetPermission(BasePermission):
         unique_together = [('org_id', 'name')]
         verbose_name = _("Asset permission")
         ordering = ('name',)
+        permissions = [
+            ('view_permuserasset', _('Can view asset of permission to user')),
+            ('view_permusergroupasset', _('Can view asset of permission to user group'))
+        ]
 
     @lazyproperty
     def users_amount(self):
@@ -174,3 +177,16 @@ class PermNode(Node):
     def save(self):
         # 这是个只读 Model
         raise NotImplementedError
+
+
+class PermedAsset(Asset):
+    class Meta:
+        proxy = True
+        verbose_name = _('Permed asset')
+        permissions = [
+            ('view_myassets', _('Can view my assets')),
+            ('connect_myassets', _('Can connect my assets')),
+            ('view_userassets', _('Can view user assets')),
+            ('view_usergroupassets', _('Can view usergroup assets')),
+        ]
+
diff --git a/apps/perms/serializers/application/permission.py b/apps/perms/serializers/application/permission.py
index f3e6443a0..c7ebdf769 100644
--- a/apps/perms/serializers/application/permission.py
+++ b/apps/perms/serializers/application/permission.py
@@ -43,7 +43,7 @@ class ApplicationPermissionSerializer(BasePermissionSerializer):
             'users_amount': {'label': _('Users amount')},
             'user_groups_amount': {'label': _('User groups amount')},
             'system_users_amount': {'label': _('System users amount')},
-            'applications_amount': {'label': _('Applications amount')},
+            'applications_amount': {'label': _('Apps amount')},
         }
 
     def _filter_actions_choices(self, choices):
diff --git a/apps/perms/signals_handler/__init__.py b/apps/perms/signal_handlers/__init__.py
similarity index 100%
rename from apps/perms/signals_handler/__init__.py
rename to apps/perms/signal_handlers/__init__.py
diff --git a/apps/perms/signals_handler/app_permission.py b/apps/perms/signal_handlers/app_permission.py
similarity index 100%
rename from apps/perms/signals_handler/app_permission.py
rename to apps/perms/signal_handlers/app_permission.py
diff --git a/apps/perms/signals_handler/asset_permission.py b/apps/perms/signal_handlers/asset_permission.py
similarity index 100%
rename from apps/perms/signals_handler/asset_permission.py
rename to apps/perms/signal_handlers/asset_permission.py
diff --git a/apps/perms/signals_handler/refresh_perms.py b/apps/perms/signal_handlers/refresh_perms.py
similarity index 100%
rename from apps/perms/signals_handler/refresh_perms.py
rename to apps/perms/signal_handlers/refresh_perms.py
diff --git a/apps/rbac/__init__.py b/apps/rbac/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/rbac/admin.py b/apps/rbac/admin.py
new file mode 100644
index 000000000..8c38f3f3d
--- /dev/null
+++ b/apps/rbac/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/apps/rbac/api/__init__.py b/apps/rbac/api/__init__.py
new file mode 100644
index 000000000..894655045
--- /dev/null
+++ b/apps/rbac/api/__init__.py
@@ -0,0 +1,4 @@
+from .permission import *
+from .role import *
+from .rolebinding import *
+
diff --git a/apps/rbac/api/permission.py b/apps/rbac/api/permission.py
new file mode 100644
index 000000000..75025ba8a
--- /dev/null
+++ b/apps/rbac/api/permission.py
@@ -0,0 +1,53 @@
+from rest_framework.response import Response
+from rest_framework.decorators import action
+from django.shortcuts import get_object_or_404
+
+from common.tree import TreeNodeSerializer
+from common.drf.api import JMSModelViewSet
+from ..models import Permission, Role
+from ..serializers import PermissionSerializer
+
+__all__ = ['PermissionViewSet']
+
+
+class PermissionViewSet(JMSModelViewSet):
+    filterset_fields = ['codename']
+    serializer_classes = {
+        'get_tree': TreeNodeSerializer,
+        'default': PermissionSerializer
+    }
+    scope = 'org'
+    check_disabled = False
+    http_method_names = ['get', 'option', 'head']
+
+    @action(methods=['GET'], detail=False, url_path='tree')
+    def get_tree(self, request, *args, **kwargs):
+        queryset = self.filter_queryset(self.get_queryset()).distinct()
+        tree_nodes = Permission.create_tree_nodes(
+            queryset, scope=self.scope, check_disabled=self.check_disabled
+        )
+        serializer = self.get_serializer(tree_nodes, many=True)
+        return Response(serializer.data)
+
+    def get_queryset(self):
+        self.scope = self.request.query_params.get('scope') or 'org'
+        role_id = self.request.query_params.get('role')
+
+        if role_id:
+            role = get_object_or_404(Role, pk=role_id)
+            self.scope = role.scope
+            queryset = role.get_permissions()
+        else:
+            queryset = Permission.get_permissions(self.scope)
+        queryset = queryset.prefetch_related('content_type')
+        return queryset
+
+
+# class UserPermsApi(ListAPIView):
+#     serializer_class = UserPermsSerializer
+#     permission_classes = (IsValidUser,)
+#
+#     def list(self, request, *args, **kwargs):
+#         perms = RoleBinding.get_user_perms(request.user)
+#         serializer = super().get_serializer(data={'perms': perms})
+#         return Res
diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py
new file mode 100644
index 000000000..a11edc409
--- /dev/null
+++ b/apps/rbac/api/role.py
@@ -0,0 +1,103 @@
+from django.db.models import Count
+from django.utils.translation import ugettext as _
+from rest_framework.exceptions import PermissionDenied
+from rest_framework.decorators import action
+
+from common.drf.api import JMSModelViewSet
+from ..serializers import RoleSerializer, RoleUserSerializer
+from ..models import Role, SystemRole, OrgRole
+from .permission import PermissionViewSet
+
+__all__ = [
+    'RoleViewSet', 'SystemRoleViewSet', 'OrgRoleViewSet',
+    'SystemRolePermissionsViewSet', 'OrgRolePermissionsViewSet',
+]
+
+
+class RoleViewSet(JMSModelViewSet):
+    queryset = Role.objects.all()
+    serializer_classes = {
+        'default': RoleSerializer,
+        'users': RoleUserSerializer,
+    }
+    filterset_fields = ['name', 'scope', 'builtin']
+    search_fields = filterset_fields
+    rbac_perms = {
+        'users': 'rbac.view_rolebinding'
+    }
+
+    def perform_destroy(self, instance):
+        from orgs.utils import tmp_to_root_org
+        if instance.builtin:
+            error = _("Internal role, can't be destroy")
+            raise PermissionDenied(error)
+        with tmp_to_root_org():
+            if instance.users.count() >= 1:
+                error = _("The role has been bound to users, can't be destroy")
+                raise PermissionDenied(error)
+        return super().perform_destroy(instance)
+
+    def perform_update(self, serializer):
+        instance = serializer.instance
+        if instance.builtin:
+            error = _("Internal role, can't be update")
+            raise PermissionDenied(error)
+        return super().perform_update(serializer)
+
+    def get_queryset(self):
+        queryset = super().get_queryset() \
+            .annotate(permissions_amount=Count('permissions'))
+        return queryset
+
+    @action(methods=['GET'], detail=True)
+    def users(self, *args, **kwargs):
+        role = self.get_object()
+        queryset = role.users
+        return self.get_paginated_response_with_query_set(queryset)
+
+
+class SystemRoleViewSet(RoleViewSet):
+    queryset = SystemRole.objects.all()
+
+
+class OrgRoleViewSet(RoleViewSet):
+    queryset = OrgRole.objects.all()
+
+
+class BaseRolePermissionsViewSet(PermissionViewSet):
+    model = None
+    role_pk = None
+    filterset_fields = []
+    http_method_names = ['get', 'option']
+    check_disabled = False
+
+    def get_queryset(self):
+        role_id = self.kwargs.get(self.role_pk)
+        if not role_id:
+            return self.model.objects.none()
+
+        role = self.model.objects.get(id=role_id)
+        self.scope = role.scope
+        self.check_disabled = role.builtin
+        queryset = role.get_permissions() \
+            .prefetch_related('content_type')
+        return queryset
+
+
+# Sub view set
+class SystemRolePermissionsViewSet(BaseRolePermissionsViewSet):
+    role_pk = 'system_role_pk'
+    model = SystemRole
+    rbac_perms = (
+        ('get_tree', 'rbac.view_systemrole'),
+    )
+
+
+# Sub view set
+class OrgRolePermissionsViewSet(BaseRolePermissionsViewSet):
+    role_pk = 'org_role_pk'
+    model = OrgRole
+    rbac_perms = (
+        ('get_tree', 'rbac.view_orgrole'),
+    )
+
diff --git a/apps/rbac/api/rolebinding.py b/apps/rbac/api/rolebinding.py
new file mode 100644
index 000000000..677ef30bf
--- /dev/null
+++ b/apps/rbac/api/rolebinding.py
@@ -0,0 +1,64 @@
+from django.utils.translation import ugettext as _
+from django.db.models import F, Value
+from django.db.models.functions import Concat
+
+from orgs.mixins.api import OrgBulkModelViewSet
+from orgs.utils import current_org
+from common.exceptions import JMSException
+from .. import serializers
+from ..models import RoleBinding, SystemRoleBinding, OrgRoleBinding
+
+__all__ = ['RoleBindingViewSet', 'SystemRoleBindingViewSet', 'OrgRoleBindingViewSet']
+
+
+class RoleBindingViewSet(OrgBulkModelViewSet):
+    model = RoleBinding
+    serializer_class = serializers.RoleBindingSerializer
+    filterset_fields = [
+        'scope', 'user', 'role', 'org',
+        'user__name', 'user__username', 'role__name'
+    ]
+    search_fields = [
+        'user__name', 'user__username', 'role__name'
+    ]
+
+    def get_queryset(self):
+        queryset = super().get_queryset() \
+            .prefetch_related('user', 'role') \
+            .annotate(
+                user_display=Concat(
+                    F('user__name'), Value('('),
+                    F('user__username'), Value(')')
+                ),
+                role_display=F('role__name')
+            )
+        return queryset
+
+
+class SystemRoleBindingViewSet(RoleBindingViewSet):
+    model = SystemRoleBinding
+    serializer_class = serializers.SystemRoleBindingSerializer
+
+    def perform_destroy(self, instance):
+        user = instance.user
+        role_qs = self.model.objects.filter(user=user)
+        if role_qs.count() == 1:
+            msg = _('{} at least one system role').format(user)
+            raise JMSException(code='system_role_delete_error', detail=msg)
+        return super().perform_destroy(instance)
+
+
+class OrgRoleBindingViewSet(RoleBindingViewSet):
+    model = OrgRoleBinding
+    serializer_class = serializers.OrgRoleBindingSerializer
+
+    def perform_bulk_create(self, serializer):
+        validated_data = serializer.validated_data
+        bindings = [
+            OrgRoleBinding(
+                role=d['role'], user=d['user'],
+                org_id=current_org.id, scope='org'
+            )
+            for d in validated_data
+        ]
+        OrgRoleBinding.objects.bulk_create(bindings, ignore_conflicts=True)
diff --git a/apps/rbac/apps.py b/apps/rbac/apps.py
new file mode 100644
index 000000000..f22c7cf0a
--- /dev/null
+++ b/apps/rbac/apps.py
@@ -0,0 +1,11 @@
+from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
+
+
+class RBACConfig(AppConfig):
+    name = 'rbac'
+    verbose_name = _('RBAC')
+
+    def ready(self):
+        from . import signal_handlers
+        super().ready()
diff --git a/apps/rbac/backends.py b/apps/rbac/backends.py
new file mode 100644
index 000000000..bc9dbb56a
--- /dev/null
+++ b/apps/rbac/backends.py
@@ -0,0 +1,30 @@
+from django.core.exceptions import PermissionDenied
+
+from authentication.backends.base import JMSBaseAuthBackend
+
+
+class RBACBackend(JMSBaseAuthBackend):
+    """ 只做权限校验 """
+    @staticmethod
+    def is_enabled():
+        return True
+
+    def authenticate(self, *args, **kwargs):
+        return None
+
+    def username_allow_authenticate(self, username):
+        return False
+
+    def has_perm(self, user_obj, perm, obj=None):
+        if not user_obj.is_active:
+            raise PermissionDenied()
+        if perm == '*':
+            return True
+        perm_set = set(i.strip() for i in perm.split('|'))
+        has_perm = bool(perm_set & set(user_obj.perms))
+        if not has_perm:
+            raise PermissionDenied()
+        return has_perm
+
+    # def has_module_perms(self, user_obj, app_label):
+    #     return True
diff --git a/apps/rbac/builtin.py b/apps/rbac/builtin.py
new file mode 100644
index 000000000..64a73d546
--- /dev/null
+++ b/apps/rbac/builtin.py
@@ -0,0 +1,143 @@
+from django.utils.translation import ugettext_noop
+
+from .const import Scope, system_exclude_permissions, org_exclude_permissions
+
+user_perms = (
+    ('rbac', 'menupermission', 'view', 'workspace'),
+    ('rbac', 'menupermission', 'view', 'webterminal'),
+    ('rbac', 'menupermission', 'view', 'filemanager'),
+    ('perms', 'permedasset', 'view,connect', 'myassets'),
+    ('perms', 'permedapplication', 'view,connect', 'myapps'),
+    ('assets', 'asset', 'match', 'asset'),
+    ('assets', 'systemuser', 'match', 'systemuser'),
+    ('assets', 'node', 'match', 'node'),
+    ('ops', 'commandexecution', 'add', 'commandexecution'),
+)
+
+auditor_perms = user_perms + (
+    ('rbac', 'menupermission', 'view', 'audit'),
+    ('rbac', 'menupermission', 'view', 'dashboard'),
+    ('audits', '*', '*', '*'),
+    ('terminal', 'commandstorage', 'view', 'commandstorage'),
+    ('terminal', 'sessionreplay', 'view,download', 'sessionreplay'),
+    ('terminal', 'session', '*', '*'),
+    ('terminal', 'command', '*', '*'),
+)
+
+
+app_exclude_perms = [
+    ('users', 'user', 'add,delete', 'user'),
+    ('orgs', 'org', 'add,delete,change', 'org'),
+    ('rbac', '*', '*', '*'),
+]
+
+need_check = [
+    *auditor_perms, *user_perms, *app_exclude_perms,
+    *system_exclude_permissions, *org_exclude_permissions
+]
+defines_errors = [d for d in need_check if len(d) != 4]
+if len(defines_errors) != 0:
+    raise ValueError('Perms define error: {}'.format(defines_errors))
+
+
+class PredefineRole:
+    id_prefix = '00000000-0000-0000-0000-00000000000'
+
+    def __init__(self, index, name, scope, perms, perms_type='include'):
+        self.id = self.id_prefix + index
+        self.name = name
+        self.scope = scope
+        self.perms = perms
+        self.perms_type = perms_type
+
+    def get_role(self):
+        from rbac.models import Role
+        return Role.objects.get(id=self.id)
+
+    def _get_defaults(self):
+        from rbac.models import Permission
+        q = Permission.get_define_permissions_q(self.perms)
+        permissions = Permission.get_permissions(self.scope)
+
+        if not q:
+            permissions = permissions.none()
+        elif self.perms_type == 'include':
+            permissions = permissions.filter(q)
+        else:
+            permissions = permissions.exclude(q)
+
+        perms = permissions.values_list('id', flat=True)
+        defaults = {
+            'id': self.id, 'name': self.name, 'scope': self.scope,
+            'builtin': True, 'permissions': perms
+        }
+        return defaults
+
+    def update_or_create_role(self):
+        from rbac.models import Role
+        defaults = self._get_defaults()
+        permissions = defaults.pop('permissions', [])
+        role, created = Role.objects.update_or_create(defaults, id=self.id)
+        role.permissions.set(permissions)
+        return role, created
+
+
+class BuiltinRole:
+    system_admin = PredefineRole(
+        '1', ugettext_noop('SystemAdmin'), Scope.system, []
+    )
+    system_auditor = PredefineRole(
+        '2', ugettext_noop('SystemAuditor'), Scope.system, auditor_perms
+    )
+    system_component = PredefineRole(
+        '4', ugettext_noop('SystemComponent'), Scope.system, app_exclude_perms, 'exclude'
+    )
+    system_user = PredefineRole(
+        '3', ugettext_noop('User'), Scope.system, []
+    )
+    org_admin = PredefineRole(
+        '5', ugettext_noop('OrgAdmin'), Scope.org, []
+    )
+    org_auditor = PredefineRole(
+        '6', ugettext_noop('OrgAuditor'), Scope.org, auditor_perms
+    )
+    org_user = PredefineRole(
+        '7', ugettext_noop('OrgUser'), Scope.org, user_perms
+    )
+
+    @classmethod
+    def get_roles(cls):
+        roles = {
+            k: v
+            for k, v in cls.__dict__.items()
+            if isinstance(v, PredefineRole)
+        }
+        return roles
+
+    @classmethod
+    def get_system_role_by_old_name(cls, name):
+        mapper = {
+            'App': cls.system_component,
+            'Admin': cls.system_admin,
+            'User': cls.system_user,
+            'Auditor': cls.system_auditor
+        }
+        return mapper[name].get_role()
+
+    @classmethod
+    def get_org_role_by_old_name(cls, name):
+        mapper = {
+            'Admin': cls.org_admin,
+            'User': cls.org_user,
+            'Auditor': cls.org_auditor,
+        }
+        return mapper[name].get_role()
+
+    @classmethod
+    def sync_to_db(cls, show_msg=False):
+        roles = cls.get_roles()
+
+        for pre_role in roles.values():
+            role, created = pre_role.update_or_create_role()
+            if show_msg:
+                print("Update builtin Role: {} - {}".format(role.name, created))
diff --git a/apps/rbac/const.py b/apps/rbac/const.py
new file mode 100644
index 000000000..c575cbfac
--- /dev/null
+++ b/apps/rbac/const.py
@@ -0,0 +1,101 @@
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class Scope(models.TextChoices):
+    system = 'system', _('System')
+    org = 'org', _('Organization')
+
+
+exclude_permissions = (
+    # ('App', 'Model', 'Action', 'Resource') Model 和 Resource 可能不同
+    # users.add_user
+    ('auth', '*', '*', '*'),
+    ('authentication', 'loginconfirmsetting', '*', '*'),
+    ('captcha', '*', '*', '*'),
+    ('contenttypes', '*', '*', '*'),
+    ('django_cas_ng', '*', '*', '*'),
+    ('django_celery_beat', '*', '*', '*'),
+    ('jms_oidc_rp', '*', '*', '*'),
+    ('admin', '*', '*', '*'),
+    ('sessions', '*', '*', '*'),
+    ('notifications', '*', '*', '*'),
+    ('common', 'setting', '*', '*'),
+
+    ('authentication', 'privatetoken', '*', '*'),
+    ('users', 'userpasswordhistory', '*', '*'),
+    ('applications', 'applicationuser', '*', '*'),
+    ('applications', 'historicalaccount', '*', '*'),
+    ('assets', 'adminuser', '*', '*'),
+    ('assets', 'assetgroup', '*', '*'),
+    ('assets', 'cluster', '*', '*'),
+    ('assets', 'favoriteasset', '*', '*'),
+    ('assets', 'historicalauthbook', '*', '*'),
+    ('assets', 'assetuser', '*', '*'),
+    ('perms', 'databaseapppermission', '*', '*'),
+    ('perms', 'k8sapppermission', '*', '*'),
+    ('perms', 'remoteapppermission', '*', '*'),
+    ('perms', 'userassetgrantedtreenoderelation', '*', '*'),
+    ('perms', 'usergrantedmappingnode', '*', '*'),
+    ('perms', 'permnode', '*', '*'),
+    ('perms', 'rebuildusertreetask', '*', '*'),
+    ('perms', 'permedasset', 'add,change,delete', 'permedasset'),
+    ('perms', 'permedapplication', 'add,change,delete', 'permedapplication'),
+    ('rbac', 'contenttype', '*', '*'),
+    ('rbac', 'permission', 'add,delete,change', 'permission'),
+    ('rbac', 'rolebinding', '*', '*'),
+    ('rbac', 'role', '*', '*'),
+    ('ops', 'adhoc', '*', '*'),
+    ('ops', 'adhocexecution', 'delete,change', '*'),
+    ('ops', 'celerytask', '*', '*'),
+    ('ops', 'task', 'add,change', 'task'),
+    ('ops', 'commandexecution', 'delete,change', 'commandexecution'),
+    ('orgs', 'organizationmember', '*', '*'),
+    ('settings', 'setting', 'add,delete', 'setting'),
+    ('audits', 'operatelog', 'add,delete,change', 'operatelog'),
+    ('audits', 'passwordchangelog', 'add,change,delete', 'passwordchangelog'),
+    ('audits', 'userloginlog', 'add,change,delete,change', 'userloginlog'),
+    ('audits', 'ftplog', 'change,delete', 'ftplog'),
+    ('tickets', 'ticket', '*', '*'),
+    ('tickets', 'comment', 'change,delete', 'comment'),
+    ('tickets', 'ticketstep', '*', '*'),
+    ('tickets', 'ticketapprovalrule', '*', '*'),
+    ('xpack', 'interface', '*', '*'),
+    ('xpack', 'license', '*', '*'),
+    ('xpack', 'syncinstancedetail', 'add,delete,change', 'syncinstancedetail'),
+    ('xpack', 'syncinstancetaskexecution', 'add,delete,change', 'syncinstancetaskexecution'),
+    ('common', 'permission', 'add,delete,view,change', 'permission'),
+    ('terminal', 'command', 'delete,change', 'command'),
+    ('terminal', 'status', 'delete,change', 'status'),
+    ('terminal', 'sessionjoinrecord', 'delete', 'sessionjoinrecord'),
+    ('terminal', 'sessionreplay', 'delete', 'sessionreplay'),
+    ('terminal', 'session', 'delete', 'session'),
+    ('terminal', 'session', 'delete,change', 'command'),
+)
+
+
+only_system_permissions = (
+    ('users', 'user', 'delete', 'user'),
+    ('rbac', 'role', 'delete,add,change', 'role'),
+    ('rbac', 'systemrole', '*', '*'),
+    ('rbac', 'rolebinding', '*', '*'),
+    ('rbac', 'systemrolebinding', '*', '*'),
+    ('rbac', 'orgrole', 'delete,add,change', '*'),
+    ('rbac', 'orgrolebinding', 'delete,add,change', '*'),
+    ('orgs', 'organization', '*', '*'),
+    ('xpack', 'license', '*', '*'),
+    ('settings', 'setting', '*', '*'),
+    ('ops', 'task', 'view', 'taskmonitor'),
+    ('terminal', 'terminal', '*', '*'),
+    ('terminal', 'commandstorage', '*', '*'),
+    ('terminal', 'replaystorage', '*', '*'),
+    ('terminal', 'status', '*', '*'),
+    ('terminal', 'task', '*', '*'),
+    ('tickets', 'ticketflow', '*', '*'),
+)
+
+only_org_permissions = (
+)
+
+system_exclude_permissions = list(exclude_permissions) + list(only_org_permissions)
+org_exclude_permissions = list(exclude_permissions) + list(only_system_permissions)
diff --git a/apps/rbac/migrations/0001_initial.py b/apps/rbac/migrations/0001_initial.py
new file mode 100644
index 000000000..5687bf574
--- /dev/null
+++ b/apps/rbac/migrations/0001_initial.py
@@ -0,0 +1,151 @@
+# Generated by Django 3.1.13 on 2021-11-19 08:29
+
+from django.conf import settings
+import django.contrib.auth.models
+import django.contrib.contenttypes.models
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('auth', '0012_alter_user_first_name_max_length'),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('orgs', '0010_auto_20210219_1241'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='MenuPermission',
+            fields=[
+                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
+            ],
+            options={
+                'verbose_name': 'Menu permission',
+                'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view')],
+                'default_permissions': [],
+            },
+        ),
+        migrations.CreateModel(
+            name='Role',
+            fields=[
+                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
+                ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
+                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
+                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
+                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=128, verbose_name='Name')),
+                ('scope', models.CharField(choices=[('system', 'System'), ('org', 'Organization')], default='system', max_length=128, verbose_name='Scope')),
+                ('builtin', models.BooleanField(default=False, verbose_name='Built-in')),
+                ('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ContentType',
+            fields=[
+            ],
+            options={
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('contenttypes.contenttype',),
+            managers=[
+                ('objects', django.contrib.contenttypes.models.ContentTypeManager()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Permission',
+            fields=[
+            ],
+            options={
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('auth.permission',),
+            managers=[
+                ('objects', django.contrib.auth.models.PermissionManager()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='RoleBinding',
+            fields=[
+                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
+                ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
+                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
+                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
+                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
+                ('scope', models.CharField(choices=[('system', 'System'), ('org', 'Organization')], default='system', max_length=128, verbose_name='Scope')),
+                ('org', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='role_bindings', to='orgs.organization', verbose_name='Organization')),
+                ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_bindings', to='rbac.role', verbose_name='Role')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_bindings', to=settings.AUTH_USER_MODEL, verbose_name='User')),
+            ],
+            options={
+                'verbose_name': 'Role binding',
+                'unique_together': {('user', 'role', 'org')},
+            },
+        ),
+        migrations.AddField(
+            model_name='role',
+            name='permissions',
+            field=models.ManyToManyField(blank=True, related_name='roles', to='rbac.Permission', verbose_name='Permissions'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='role',
+            unique_together={('name', 'scope')},
+        ),
+        migrations.CreateModel(
+            name='OrgRoleBinding',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Organization role binding',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('rbac.rolebinding',),
+        ),
+        migrations.CreateModel(
+            name='SystemRoleBinding',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'System role binding',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('rbac.rolebinding',),
+        ),
+        migrations.CreateModel(
+            name='OrgRole',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Organization role',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('rbac.role',),
+        ),
+        migrations.CreateModel(
+            name='SystemRole',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'System role',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('rbac.role',),
+        ),
+    ]
diff --git a/apps/rbac/migrations/0002_auto_20210929_1409.py b/apps/rbac/migrations/0002_auto_20210929_1409.py
new file mode 100644
index 000000000..e280d4f20
--- /dev/null
+++ b/apps/rbac/migrations/0002_auto_20210929_1409.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.1.13 on 2021-11-30 02:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rbac', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='permission',
+            options={'verbose_name': 'Permission'},
+        ),
+        migrations.AlterModelOptions(
+            name='role',
+            options={'verbose_name': 'Role'},
+        )
+    ]
diff --git a/apps/rbac/migrations/0003_auto_20211130_1037.py b/apps/rbac/migrations/0003_auto_20211130_1037.py
new file mode 100644
index 000000000..b72b56876
--- /dev/null
+++ b/apps/rbac/migrations/0003_auto_20211130_1037.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.1.13 on 2021-12-01 11:01
+
+from django.db import migrations
+from rbac.builtin import BuiltinRole
+
+
+def create_builtin_roles(apps, schema_editor):
+    BuiltinRole.sync_to_db(show_msg=True)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rbac', '0002_auto_20210929_1409'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_builtin_roles)
+    ]
diff --git a/apps/rbac/migrations/0004_auto_20211201_1901.py b/apps/rbac/migrations/0004_auto_20211201_1901.py
new file mode 100644
index 000000000..36736f254
--- /dev/null
+++ b/apps/rbac/migrations/0004_auto_20211201_1901.py
@@ -0,0 +1,50 @@
+# Generated by Django 3.1.13 on 2021-12-01 11:01
+
+from django.db import migrations
+
+from rbac.builtin import BuiltinRole
+
+
+def migrate_system_role_binding(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
+    user_model = apps.get_model('users', 'User')
+    role_binding_model = apps.get_model('rbac', 'SystemRoleBinding')
+    users = user_model.objects.using(db_alias).all()
+
+    role_bindings = []
+    for user in users:
+        role = BuiltinRole.get_system_role_by_old_name(user.role)
+        role_binding = role_binding_model(scope='system', user_id=user.id, role_id=role.id)
+        role_bindings.append(role_binding)
+    role_binding_model.objects.bulk_create(role_bindings, ignore_conflicts=True)
+
+
+def migrate_org_role_binding(apps, schema_editor):
+    db_alias = schema_editor.connection.alias
+    org_member_model = apps.get_model('orgs', 'OrganizationMember')
+    role_binding_model = apps.get_model('rbac', 'RoleBinding')
+    members = org_member_model.objects.using(db_alias).all()
+
+    role_bindings = []
+    for member in members:
+        role = BuiltinRole.get_org_role_by_old_name(member.role)
+        role_binding = role_binding_model(
+            scope='org',
+            user_id=member.user.id,
+            role_id=role.id,
+            org_id=member.org.id
+        )
+        role_bindings.append(role_binding)
+    role_binding_model.objects.bulk_create(role_bindings)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rbac', '0003_auto_20211130_1037'),
+    ]
+
+    operations = [
+        migrations.RunPython(migrate_system_role_binding),
+        migrations.RunPython(migrate_org_role_binding)
+    ]
diff --git a/apps/rbac/migrations/0005_auto_20220307_1524.py b/apps/rbac/migrations/0005_auto_20220307_1524.py
new file mode 100644
index 000000000..88b9a0f91
--- /dev/null
+++ b/apps/rbac/migrations/0005_auto_20220307_1524.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-07 07:46
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rbac', '0004_auto_20211201_1901'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='menupermission',
+            options={'default_permissions': [], 'permissions': [('view_dashboard', 'Can view resource statistics'), ('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'},
+        ),
+    ]
diff --git a/apps/rbac/migrations/0006_auto_20220310_0616.py b/apps/rbac/migrations/0006_auto_20220310_0616.py
new file mode 100644
index 000000000..aa76969bd
--- /dev/null
+++ b/apps/rbac/migrations/0006_auto_20220310_0616.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-09 22:16
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rbac', '0005_auto_20220307_1524'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='menupermission',
+            options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager'), ('view_dashboard', 'Can view dashboard')], 'verbose_name': 'Menu permission'},
+        ),
+    ]
diff --git a/apps/rbac/migrations/__init__.py b/apps/rbac/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/rbac/models/__init__.py b/apps/rbac/models/__init__.py
new file mode 100644
index 000000000..7a8b0548e
--- /dev/null
+++ b/apps/rbac/models/__init__.py
@@ -0,0 +1,4 @@
+from .permission import *
+from .rolebinding import *
+from .role import *
+from .menu import *
diff --git a/apps/rbac/models/menu.py b/apps/rbac/models/menu.py
new file mode 100644
index 000000000..b13c3d99e
--- /dev/null
+++ b/apps/rbac/models/menu.py
@@ -0,0 +1,21 @@
+import uuid
+
+from django.utils.translation import gettext_lazy as _
+from django.db import models
+
+
+class MenuPermission(models.Model):
+    """ 附加权限位类,用来定义无资源类的权限,不做实体资源 """
+    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
+
+    class Meta:
+        default_permissions = []
+        verbose_name = _('Menu permission')
+        permissions = [
+            ('view_console', _('Can view console view')),
+            ('view_audit', _('Can view audit view')),
+            ('view_workspace', _('Can view workspace view')),
+            ('view_webterminal', _('Can view web terminal')),
+            ('view_filemanager', _('Can view file manager')),
+            ('view_dashboard', _('Can view dashboard')),
+        ]
diff --git a/apps/rbac/models/permission.py b/apps/rbac/models/permission.py
new file mode 100644
index 000000000..a93c7535c
--- /dev/null
+++ b/apps/rbac/models/permission.py
@@ -0,0 +1,89 @@
+from django.db.models import Q
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.auth.models import Permission as DjangoPermission
+from django.contrib.auth.models import ContentType as DjangoContentType
+
+from .. import const
+
+Scope = const.Scope
+
+__all__ = ['Permission', 'ContentType']
+
+
+class ContentType(DjangoContentType):
+    class Meta:
+        proxy = True
+
+
+class Permission(DjangoPermission):
+    """ 权限类 """
+    class Meta:
+        proxy = True
+        verbose_name = _('Permission')
+
+    @classmethod
+    def to_perms(cls, queryset):
+        perms = queryset.values_list(
+            'content_type__app_label', 'codename'
+        )
+        perms = list(set(["%s.%s" % (ct, codename) for ct, codename in perms]))
+        return sorted(perms)
+
+    @property
+    def app_label_codename(self):
+        return '%s.%s' % (self.content_type.app_label, self.codename)
+
+    @classmethod
+    def get_define_permissions_q(cls, defines):
+        """
+        :param defines: [(app, model, codename),]
+        :return:
+        """
+        if not defines:
+            return None
+        q = Q()
+
+        for define in defines:
+            app_label, model, actions, resource, *args = list(define)
+            kwargs = {}
+            if app_label != '*':
+                kwargs['content_type__app_label'] = app_label
+            if model != '*':
+                kwargs['content_type__model'] = model
+
+            actions_list = [a.strip() for a in actions.split(',')]
+            actions_regex = '|'.join(actions_list)
+            if actions == '*' and resource == '*':
+                pass
+            elif actions == '*' and resource != '*':
+                kwargs['codename__iregex'] = r'[a-z]+_{}'.format(resource)
+            elif actions != '*' and resource == '*':
+                kwargs['codename__iregex'] = r'({})_[a-z]+'.format(actions_regex)
+            else:
+                kwargs['codename__iregex'] = r'({})_{}'.format(actions_regex, resource)
+            q |= Q(**kwargs)
+        return q
+
+    @classmethod
+    def clean_permissions(cls, permissions, scope=Scope.system):
+        if scope == Scope.org:
+            excludes = const.org_exclude_permissions
+        else:
+            excludes = const.system_exclude_permissions
+        q = cls.get_define_permissions_q(excludes)
+        if q:
+            permissions = permissions.exclude(q)
+        return permissions
+
+    @staticmethod
+    def create_tree_nodes(permissions, scope, check_disabled=False):
+        from ..tree import PermissionTreeUtil
+        util = PermissionTreeUtil(permissions, scope, check_disabled)
+        return util.create_tree_nodes()
+
+    @classmethod
+    def get_permissions(cls, scope):
+        permissions = cls.objects.all()
+        permissions = cls.clean_permissions(permissions, scope=scope)
+        return permissions
+
diff --git a/apps/rbac/models/role.py b/apps/rbac/models/role.py
new file mode 100644
index 000000000..9d539ba13
--- /dev/null
+++ b/apps/rbac/models/role.py
@@ -0,0 +1,135 @@
+from django.utils.translation import ugettext_lazy as _, gettext
+from django.db import models
+
+from common.db.models import JMSModel
+from common.utils import lazyproperty
+from .permission import Permission
+from ..builtin import BuiltinRole
+from .. import const
+
+__all__ = ['Role', 'SystemRole', 'OrgRole']
+
+
+class SystemRoleManager(models.Manager):
+    def get_queryset(self):
+        queryset = super().get_queryset()
+        return queryset.filter(scope=const.Scope.system)
+
+
+class OrgRoleManager(models.Manager):
+    def get_queryset(self):
+        queryset = super().get_queryset()
+        return queryset.filter(scope=const.Scope.org)
+
+
+class Role(JMSModel):
+    """ 定义 角色 | 角色-权限 关系 """
+    Scope = const.Scope
+
+    name = models.CharField(max_length=128, verbose_name=_('Name'))
+    scope = models.CharField(
+        max_length=128, choices=Scope.choices, default=Scope.system, verbose_name=_('Scope')
+    )
+    permissions = models.ManyToManyField(
+        'rbac.Permission', related_name='roles', blank=True, verbose_name=_('Permissions')
+    )
+    builtin = models.BooleanField(default=False, verbose_name=_('Built-in'))
+    comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
+
+    BuiltinRole = BuiltinRole
+    objects = models.Manager()
+    org_roles = OrgRoleManager()
+    system_roles = SystemRoleManager()
+
+    class Meta:
+        unique_together = [('name', 'scope')]
+        verbose_name = _('Role')
+
+    def __str__(self):
+        return '%s(%s)' % (self.name, self.get_scope_display())
+
+    def is_system_admin(self):
+        return str(self.id) == self.BuiltinRole.system_admin.id and self.builtin
+
+    def is_org_admin(self):
+        return str(self.id) == self.BuiltinRole.org_admin.id and self.builtin
+
+    def is_admin(self):
+        yes = self.is_system_admin() or self.is_org_admin()
+        return yes
+
+    @staticmethod
+    def get_scope_roles_perms(roles, scope):
+        has_admin = any([r.is_admin() for r in roles])
+        if has_admin:
+            perms = Permission.objects.all()
+        else:
+            perms = Permission.objects.filter(roles__in=roles).distinct()
+        perms = Permission.clean_permissions(perms, scope=scope)
+        return perms
+
+    @classmethod
+    def get_roles_permissions(cls, roles):
+        org_roles = [role for role in roles if role.scope == cls.Scope.org]
+        org_perms_id = cls.get_scope_roles_perms(org_roles, cls.Scope.org)\
+            .values_list('id', flat=True)
+
+        system_roles = [role for role in roles if role.scope == cls.Scope.system]
+        system_perms_id = cls.get_scope_roles_perms(system_roles, cls.Scope.system)\
+            .values_list('id', flat=True)
+        perms_id = set(org_perms_id) | set(system_perms_id)
+        permissions = Permission.objects.filter(id__in=perms_id)\
+            .prefetch_related('content_type')
+        return permissions
+
+    @classmethod
+    def get_roles_perms(cls, roles):
+        permissions = cls.get_roles_permissions(roles)
+        return Permission.to_perms(permissions)
+
+    def get_permissions(self):
+        if self.is_admin():
+            permissions = Permission.objects.all()
+        else:
+            permissions = self.permissions.all()
+        permissions = Permission.clean_permissions(permissions, self.scope)
+        return permissions
+
+    @lazyproperty
+    def users(self):
+        from .rolebinding import RoleBinding
+        return RoleBinding.get_role_users(self)
+
+    @lazyproperty
+    def users_amount(self):
+        return self.users.count()
+
+    @lazyproperty
+    def permissions_amount(self):
+        return self.permissions.count()
+
+    @classmethod
+    def create_builtin_roles(cls):
+        BuiltinRole.sync_to_db()
+
+    @property
+    def display_name(self):
+        if not self.builtin:
+            return self.name
+        return gettext(self.name)
+
+
+class SystemRole(Role):
+    objects = SystemRoleManager()
+
+    class Meta:
+        proxy = True
+        verbose_name = _('System role')
+
+
+class OrgRole(Role):
+    objects = OrgRoleManager()
+
+    class Meta:
+        proxy = True
+        verbose_name = _('Organization role')
diff --git a/apps/rbac/models/rolebinding.py b/apps/rbac/models/rolebinding.py
new file mode 100644
index 000000000..55061e230
--- /dev/null
+++ b/apps/rbac/models/rolebinding.py
@@ -0,0 +1,138 @@
+from django.utils.translation import gettext_lazy as _
+from django.db import models
+from django.db.models import Q
+from rest_framework.serializers import ValidationError
+
+from common.db.models import JMSModel
+from common.utils import lazyproperty
+from orgs.utils import current_org
+from .role import Role
+from ..const import Scope
+
+__all__ = ['RoleBinding', 'SystemRoleBinding', 'OrgRoleBinding']
+
+
+class RoleBindingManager(models.Manager):
+    def get_queryset(self):
+        queryset = super(RoleBindingManager, self).get_queryset()
+
+        if not current_org.is_root():
+            q = Q(scope=Scope.system) | Q(org_id=current_org.id, scope=Scope.org)
+        else:
+            q = Q()
+        queryset = queryset.filter(q)
+        return queryset
+
+
+class RoleBinding(JMSModel):
+    Scope = Scope
+    """ 定义 用户-角色 关系 """
+    scope = models.CharField(
+        max_length=128, choices=Scope.choices, default=Scope.system,
+        verbose_name=_('Scope')
+    )
+    user = models.ForeignKey(
+        'users.User', related_name='role_bindings', on_delete=models.CASCADE, verbose_name=_('User')
+    )
+    role = models.ForeignKey(
+        Role, related_name='role_bindings', on_delete=models.CASCADE, verbose_name=_('Role')
+    )
+    org = models.ForeignKey(
+        'orgs.Organization', related_name='role_bindings', blank=True, null=True,
+        on_delete=models.CASCADE, verbose_name=_('Organization')
+    )
+    objects = RoleBindingManager()
+
+    class Meta:
+        verbose_name = _('Role binding')
+        unique_together = [
+            ('user', 'role', 'org'),
+        ]
+
+    def __str__(self):
+        display = '{user} & {role}'.format(user=self.user, role=self.role)
+        if self.org:
+            display += ' | {org}'.format(org=self.org)
+        return display
+
+    def save(self, *args, **kwargs):
+        self.scope = self.role.scope
+        return super().save(*args, **kwargs)
+
+    @classmethod
+    def get_user_perms(cls, user):
+        roles = cls.get_user_roles(user)
+        return Role.get_roles_perms(roles)
+
+    @classmethod
+    def get_role_users(cls, role):
+        from users.models import User
+        bindings = cls.objects.filter(role=role, scope=role.scope)
+        user_ids = bindings.values_list('user', flat=True).distinct()
+        return User.objects.filter(id__in=user_ids)
+
+    @classmethod
+    def get_user_roles(cls, user):
+        bindings = cls.objects.filter(user=user)
+        roles_id = bindings.values_list('role', flat=True).distinct()
+        return Role.objects.filter(id__in=roles_id)
+
+    @lazyproperty
+    def user_display(self):
+        return self.user.name
+
+    @lazyproperty
+    def role_display(self):
+        return self.role.display_name
+
+
+class OrgRoleBindingManager(models.Manager):
+    def get_queryset(self):
+        queryset = super().get_queryset()
+        if current_org.is_root():
+            queryset = queryset.filter(scope=Scope.org)
+        else:
+            queryset = queryset.filter(org=current_org.id, scope=Scope.org)
+        return queryset
+
+
+class OrgRoleBinding(RoleBinding):
+    objects = OrgRoleBindingManager()
+
+    def save(self, *args, **kwargs):
+        self.org_id = current_org.id
+        self.scope = Scope.org
+        return super().save(*args, **kwargs)
+
+    def delete(self, **kwargs):
+        has_other_role = self.__class__.objects \
+            .filter(user=self.user, scope=self.scope) \
+            .exclude(id=self.id) \
+            .exists()
+        if not has_other_role:
+            error = _('User last role in org, can not be delete, '
+                      'you can remove user from org instead')
+            raise ValidationError({'error': error})
+        return super().delete(**kwargs)
+
+    class Meta:
+        proxy = True
+        verbose_name = _('Organization role binding')
+
+
+class SystemRoleBindingManager(models.Manager):
+    def get_queryset(self):
+        queryset = super().get_queryset().filter(scope=Scope.system)
+        return queryset
+
+
+class SystemRoleBinding(RoleBinding):
+    objects = SystemRoleBindingManager()
+
+    class Meta:
+        proxy = True
+        verbose_name = _('System role binding')
+
+    def save(self, *args, **kwargs):
+        self.scope = Scope.system
+        return super().save(*args, **kwargs)
diff --git a/apps/rbac/permissions.py b/apps/rbac/permissions.py
new file mode 100644
index 000000000..dc1260d67
--- /dev/null
+++ b/apps/rbac/permissions.py
@@ -0,0 +1,126 @@
+from rest_framework import permissions, exceptions
+
+from common.utils import get_logger
+
+logger = get_logger(__name__)
+
+
+class RBACPermission(permissions.DjangoModelPermissions):
+    default_rbac_perms_tmpl = (
+        ('list', '%(app_label)s.view_%(model_name)s'),
+        ('retrieve', '%(app_label)s.view_%(model_name)s'),
+        ('create', '%(app_label)s.add_%(model_name)s'),
+        ('update', '%(app_label)s.change_%(model_name)s'),
+        ('partial_update', '%(app_label)s.change_%(model_name)s'),
+        ('destroy', '%(app_label)s.delete_%(model_name)s'),
+        ('bulk_update', '%(app_label)s.change_%(model_name)s'),
+        ('partial_bulk_update', '%(app_label)s.change_%(model_name)s'),
+        ('bulk_destroy', '%(app_label)s.delete_%(model_name)s'),
+        ('render_to_json', '%(app_label)s.add_%(model_name)s'),
+        ('metadata', '*'),
+        ('GET', '%(app_label)s.view_%(model_name)s'),
+        ('OPTIONS', '*'),
+        ('HEAD', '%(app_label)s.view_%(model_name)s'),
+        ('POST', '%(app_label)s.add_%(model_name)s'),
+        ('PUT', '%(app_label)s.change_%(model_name)s'),
+        ('PATCH', '%(app_label)s.change_%(model_name)s'),
+        ('DELETE', '%(app_label)s.delete_%(model_name)s'),
+    )
+
+    # rbac_perms = ((), ())
+    # def get_rbac_perms():
+    #     return {}
+
+    @staticmethod
+    def format_perms(perms_tmpl, model_cls):
+        perms = set()
+        if not perms_tmpl:
+            return perms
+        if isinstance(perms_tmpl, str):
+            perms_tmpl = [perms_tmpl]
+        for perm_tmpl in perms_tmpl:
+            perm = perm_tmpl % {
+                'app_label': model_cls._meta.app_label,
+                'model_name': model_cls._meta.model_name
+            }
+            perms.add(perm)
+        return perms
+
+    @classmethod
+    def parse_action_model_perms(cls, action, model_cls):
+        perm_tmpl = dict(cls.default_rbac_perms_tmpl).get(action)
+        return cls.format_perms(perm_tmpl, model_cls)
+
+    def get_default_action_perms(self, model_cls):
+        if model_cls is None:
+            return {}
+        perms = {}
+        for action, tmpl in dict(self.default_rbac_perms_tmpl).items():
+            perms[action] = self.format_perms(tmpl, model_cls)
+        return perms
+
+    def get_rbac_perms(self, view, model_cls) -> dict:
+        if hasattr(view, 'get_rbac_perms'):
+            return dict(view.get_rbac_perms())
+        perms = self.get_default_action_perms(model_cls)
+        if hasattr(view, 'rbac_perms'):
+            perms.update(dict(view.rbac_perms))
+        return perms
+
+    def _get_action_perms(self, action, model_cls, view):
+        action_perms_map = self.get_rbac_perms(view, model_cls)
+        if action not in action_perms_map:
+            msg = 'Action not allowed: {}, only `{}` supported'.format(
+                action, ','.join(list(action_perms_map.keys()))
+            )
+            logger.error(msg)
+            raise exceptions.PermissionDenied(msg)
+        perms = action_perms_map[action]
+        return perms
+
+    def get_model_cls(self, view):
+        if hasattr(view, 'perm_model'):
+            return getattr(view, 'perm_model')
+
+        try:
+            queryset = self._queryset(view)
+            model_cls = queryset.model
+        except AssertionError:
+            model_cls = None
+        return model_cls
+
+    def get_require_perms(self, request, view):
+        """
+        获取 request, view 需要的 perms
+        :param request:
+        :param view:
+        :return:
+        """
+
+        model_cls = self.get_model_cls(view)
+        action = getattr(view, 'action', None)
+        if not action:
+            action = request.method
+        perms = self._get_action_perms(action, model_cls, view)
+        return perms
+
+    def has_permission(self, request, view):
+        # Workaround to ensure DjangoModelPermissions are not applied
+        # to the root view when using DefaultRouter.
+        if getattr(view, '_ignore_rbac_permissions', False):
+            return True
+        if not request.user:
+            return False
+        if request.user.is_anonymous and self.authenticated_users_only:
+            return False
+
+        action = getattr(view, 'action', None)
+        if action == 'metadata':
+            return True
+
+        perms = self.get_require_perms(request, view)
+        if isinstance(perms, str):
+            perms = [perms]
+        has = request.user.has_perms(perms)
+        logger.debug('View require perms: {}, result: {}'.format(perms, has))
+        return has
diff --git a/apps/rbac/serializers/__init__.py b/apps/rbac/serializers/__init__.py
new file mode 100644
index 000000000..894655045
--- /dev/null
+++ b/apps/rbac/serializers/__init__.py
@@ -0,0 +1,4 @@
+from .permission import *
+from .role import *
+from .rolebinding import *
+
diff --git a/apps/rbac/serializers/permission.py b/apps/rbac/serializers/permission.py
new file mode 100644
index 000000000..809b86636
--- /dev/null
+++ b/apps/rbac/serializers/permission.py
@@ -0,0 +1,32 @@
+from django.utils.translation import ugettext_lazy as _
+from rest_framework import serializers
+from django.contrib.auth.models import ContentType
+
+from ..models import Permission
+
+
+__all__ = ['PermissionSerializer', 'UserPermsSerializer']
+
+
+class ContentTypeSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ContentType
+        fields = ['id', 'app_label', 'model']
+
+
+class PermissionSerializer(serializers.ModelSerializer):
+    content_type = ContentTypeSerializer(read_only=True)
+
+    class Meta:
+        model = Permission
+        fields = ['id', 'name', 'content_type', 'codename']
+
+
+class UserPermsSerializer(serializers.Serializer):
+    perms = serializers.ListField(label=_('Perms'), read_only=True)
+
+    def create(self, validated_data):
+        pass
+
+    def update(self, instance, validated_data):
+        pass
diff --git a/apps/rbac/serializers/role.py b/apps/rbac/serializers/role.py
new file mode 100644
index 000000000..6070fc8dc
--- /dev/null
+++ b/apps/rbac/serializers/role.py
@@ -0,0 +1,34 @@
+from rest_framework import serializers
+from django.utils.translation import ugettext_lazy as _
+
+from users.models import User
+from ..models import Role
+
+__all__ = ['RoleSerializer', 'RoleUserSerializer']
+
+
+class RoleSerializer(serializers.ModelSerializer):
+    scope_display = serializers.ReadOnlyField(source='get_scope_display', label=_('Scope display'))
+
+    class Meta:
+        model = Role
+        fields_mini = ['id', 'name', 'display_name', 'scope']
+        read_only_fields = [
+            'users_amount', 'builtin', 'scope_display',
+            'date_created', 'date_updated',
+            'created_by', 'updated_by',
+        ]
+        fields = fields_mini + read_only_fields + [
+            'comment', 'permissions'
+        ]
+        extra_kwargs = {
+            'permissions': {'write_only': True},
+            'users_amount': {'label': _('Users amount')},
+            'display_name': {'label': _('Display name')}
+        }
+
+
+class RoleUserSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = User
+        fields = ['id', 'name', 'username']
diff --git a/apps/rbac/serializers/rolebinding.py b/apps/rbac/serializers/rolebinding.py
new file mode 100644
index 000000000..75f0ed903
--- /dev/null
+++ b/apps/rbac/serializers/rolebinding.py
@@ -0,0 +1,60 @@
+from rest_framework import serializers
+from django.utils.translation import ugettext_lazy as _
+
+from orgs.serializers import CurrentOrgDefault
+from orgs.utils import current_org
+from ..models import RoleBinding, SystemRoleBinding, OrgRoleBinding
+
+__all__ = [
+    'RoleBindingSerializer', 'OrgRoleBindingSerializer',  'SystemRoleBindingSerializer'
+]
+
+
+class RoleBindingSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = RoleBinding
+        fields = [
+            'id', 'user',  'user_display', 'role', 'role_display',
+            'scope', 'org',
+        ]
+        read_only_fields = ['scope']
+        extra_kwargs = {
+            'user_display': {'label': _('User display')},
+            'role_display': {'label': _('Role display')},
+        }
+
+
+class SystemRoleBindingSerializer(RoleBindingSerializer):
+    org = None
+
+    class Meta(RoleBindingSerializer.Meta):
+        model = SystemRoleBinding
+
+    def get_field_names(self, *args):
+        names = super().get_field_names(*args)
+        return list(set(names) - {'org'})
+
+
+class OrgRoleBindingSerializer(RoleBindingSerializer):
+    org = serializers.PrimaryKeyRelatedField(
+        default=CurrentOrgDefault(), label=_("Organization"), read_only=True
+    )
+
+    class Meta(RoleBindingSerializer.Meta):
+        model = OrgRoleBinding
+        validators = []
+
+    def validate(self, attrs):
+        data = self.initial_data
+        many = isinstance(data, list)
+        if not many:
+            user = attrs.get('user')
+            role = attrs.get('role')
+            role_bindings = OrgRoleBinding.objects.filter(user=user, role=role)
+
+            if not self.instance and role_bindings.exists():
+                raise serializers.ValidationError({'role': _('Has bound this role')})
+        return attrs
+
+
+
diff --git a/apps/rbac/signal_handlers.py b/apps/rbac/signal_handlers.py
new file mode 100644
index 000000000..316c38211
--- /dev/null
+++ b/apps/rbac/signal_handlers.py
@@ -0,0 +1,14 @@
+from django.dispatch import receiver
+from django.db.models.signals import post_migrate
+from django.apps import apps
+
+from .builtin import BuiltinRole
+
+
+@receiver(post_migrate)
+def after_migrate_update_builtin_role_permissions(sender, app_config, **kwargs):
+    # 最后一个 app migrations 后执行, 更新内置角色的权限
+    last_app = list(apps.get_app_configs())[-1]
+    if app_config.name == last_app.name:
+        print("After migration, update builtin role permissions")
+        BuiltinRole.sync_to_db()
diff --git a/apps/rbac/tests.py b/apps/rbac/tests.py
new file mode 100644
index 000000000..889a58af7
--- /dev/null
+++ b/apps/rbac/tests.py
@@ -0,0 +1,23 @@
+from django.test import TestCase
+
+# Create your tests here.
+
+# TODO: Model
+# 用户             User
+# 角色             Role
+# 权限             Permission
+# 用户-角色 关系    RoleBinding
+# 角色-权限 关系    Role
+
+
+# TODO:
+#  1. 创建用户、邀请用户 (给用户添加角色)
+#  2. 创建角色 (创建角色并指定权限集)
+#  3. APIView 控制用户访问权限 (获取用户访问API行为的codename,获取用户角色-权限,判断是否包含)
+#  4. 获取权限集 (分类获取 scope: system、org、app)
+#  5. 定义权限位 (整理所有权限位并分类,同时在Model中重新定义权限名称)
+#  7. 添加内置角色
+#  6. 修改用户Model/Serializer/API,删除旧role字段,关联新role
+#  8. 权限位名称翻译 (整理一个dict,key为codename,value为翻译)
+#  9. 修改用户-组织关联的角色,修改表结构
+#  10. 前端获取所有权限,给每个按钮添加对应的权限控制指令
diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py
new file mode 100644
index 000000000..38ca93d78
--- /dev/null
+++ b/apps/rbac/tree.py
@@ -0,0 +1,378 @@
+#!/usr/bin/python
+from collections import defaultdict
+from typing import Callable
+
+from django.utils.translation import gettext_lazy as _, gettext
+from django.conf import settings
+from django.apps import apps
+from django.db.models import F, Count
+from django.utils.translation import ugettext
+
+from common.tree import TreeNode
+from .models import Permission, ContentType
+
+# 根节点
+root_node_data = {
+    'id': '$ROOT$',
+    'name': _('All permissions'),
+    'title': _('All permissions'),
+    'pId': '',
+}
+
+# 第二层 view 节点,手动创建的
+view_nodes_data = [
+    {'id': 'view_console', 'name': _('Console view')},
+    {'id': 'view_workspace', 'name': _('Workspace view')},
+    {'id': 'view_audit', 'name': _('Audit view')},
+    {'id': 'view_setting', 'name': _('System setting')},
+    {'id': 'view_other', 'name': _('Other')},
+]
+
+# 第三层 app 节点,手动创建
+app_nodes_data = [
+    {'id': 'users', 'view': 'view_console'},
+    {'id': 'assets', 'view': 'view_console'},
+    {'id': 'applications', 'view': 'view_console'},
+    {'id': 'accounts', 'name': _('Accounts'), 'view': 'view_console'},
+    {'id': 'perms', 'view': 'view_console'},
+    {'id': 'acls', 'view': 'view_console'},
+    {'id': 'ops', 'view': 'view_console'},
+    {'id': 'terminal', 'name': _('Session audits'), 'view': 'view_audit'},
+    {'id': 'audits', 'view': 'view_audit'},
+    {'id': 'rbac', 'view': 'view_console'},
+    {'id': 'settings', 'view': 'view_setting'},
+    {'id': 'tickets', 'view': 'view_other'},
+    {'id': 'authentication', 'view': 'view_other'},
+]
+
+# 额外其他节点,可以在不同的层次,需要指定父节点,可以将一些 model 归类到这个节点下面
+extra_nodes_data = [
+    {"id": "cloud_import", "name": _("Cloud import"), "pId": "assets"},
+    {"id": "backup_account_node", "name": _("Backup account"), "pId": "accounts"},
+    {"id": "gather_account_node", "name": _("Gather account"), "pId": "accounts"},
+    {"id": "app_change_plan_node", "name": _("App change auth"), "pId": "accounts"},
+    {"id": "asset_change_plan_node", "name": _("Asset change auth"), "pId": "accounts"},
+    {"id": "terminal_node", "name": _("Terminal setting"), "pId": "view_setting"},
+    {'id': "my_assets", "name": _("My assets"), "pId": "view_workspace"},
+    {'id': "my_apps", "name": _("My apps"), "pId": "view_workspace"},
+]
+
+# 将 model 放到其它节点下,而不是本来的 app 中
+special_pid_mapper = {
+    'common.permission': 'view_other',
+    "assets.authbook": "accounts",
+    "applications.account": "accounts",
+    'xpack.account': 'cloud_import',
+    'xpack.syncinstancedetail': 'cloud_import',
+    'xpack.syncinstancetask': 'cloud_import',
+    'xpack.syncinstancetaskexecution': 'cloud_import',
+    'assets.accountbackupplan': "backup_account_node",
+    'assets.accountbackupplanexecution': "backup_account_node",
+    'xpack.applicationchangeauthplan': 'app_change_plan_node',
+    'xpack.applicationchangeauthplanexecution': 'app_change_plan_node',
+    'xpack.applicationchangeauthplantask': 'app_change_plan_node',
+    'xpack.changeauthplan': 'asset_change_plan_node',
+    'xpack.changeauthplanexecution': 'gather_account_node',
+    'xpack.changeauthplantask': 'asset_change_plan_node',
+    "assets.gathereduser": "gather_account_node",
+    'xpack.gatherusertask': 'gather_account_node',
+    'xpack.gatherusertaskexecution': 'gather_account_node',
+    'orgs.organization': 'view_setting',
+    'settings.setting': 'view_setting',
+    'terminal.terminal': 'terminal_node',
+    'terminal.commandstorage': 'terminal_node',
+    'terminal.replaystorage': 'terminal_node',
+    'terminal.status': 'terminal_node',
+    'terminal.task': 'terminal_node',
+    'audits.ftplog': 'terminal',
+    'rbac.menupermission': 'view_other',
+    'perms.view_myassets': 'my_assets',
+    'perms.connect_myassets': 'my_assets',
+    'perms.view_myapps': 'my_apps',
+    'perms.connect_myapps': 'my_apps',
+    'ops.commandexecution': 'view_workspace',
+    "perms.view_mykubernetsapp": "my_apps",
+    "perms.connect_mykubernetsapp": "my_apps",
+    "perms.view_myremoteapp": "my_apps",
+    "perms.connect_myremoteapp": "my_apps",
+    "perms.view_mydatabaseapp": "my_apps",
+    "perms.connect_mydatabaseapp": "my_apps",
+    "xpack.interface": "view_setting",
+    "settings.change_terminal": "terminal_node"
+}
+
+verbose_name_mapper = {
+    'orgs.organization': _("App organizations"),
+    'tickets.comment': _("Ticket comment"),
+    'settings.setting': _("Common setting"),
+}
+
+xpack_nodes = [
+    'xpack', 'tickets', 'applications.remoteapp',
+    "assets.accountbackupplan", "assets.accountbackupplanexecution",
+    "rbac.orgrole", "rbac.orgrolebinding",
+    "settings.change_interface",
+]
+
+
+class PermissionTreeUtil:
+    get_permissions: Callable
+
+    def __init__(self, permissions, scope, check_disabled=False):
+        self.permissions = self.prefetch_permissions(permissions)
+        self.all_permissions = self.prefetch_permissions(
+            Permission.get_permissions(scope)
+        ).order_by('-codename')
+        self.check_disabled = check_disabled
+        self.total_counts = defaultdict(int)
+        self.checked_counts = defaultdict(int)
+
+    @staticmethod
+    def prefetch_permissions(perms):
+        return perms.select_related('content_type') \
+            .annotate(app=F('content_type__app_label')) \
+            .annotate(model=F('content_type__model'))
+
+    def create_apps_nodes(self):
+        all_apps = apps.get_app_configs()
+        apps_name_mapper = {
+            app.name: app.verbose_name
+            for app in all_apps if hasattr(app, 'verbose_name')
+        }
+        nodes = []
+
+        for i in app_nodes_data:
+            app = i['id']
+            name = i.get('name') or apps_name_mapper.get(app, app)
+            view = i.get('view', 'other')
+
+            app_data = {
+                'id': app,
+                'name': name,
+                'pId': view,
+            }
+            total_count = self.total_counts[app]
+            checked_count = self.checked_counts[app]
+            if total_count == 0:
+                continue
+            self.total_counts[view] += total_count
+            self.checked_counts[view] += checked_count
+            node = self._create_node(
+                app_data, total_count, checked_count,
+                'app', is_open=False
+            )
+            nodes.append(node)
+        return nodes
+
+    def _get_model_counts_mapper(self):
+        model_counts = self.all_permissions \
+            .values('model', 'app', 'content_type') \
+            .order_by('content_type') \
+            .annotate(count=Count('content_type'))
+        model_check_counts = self.permissions \
+            .values('content_type', 'model') \
+            .order_by('content_type') \
+            .annotate(count=Count('content_type'))
+        model_counts_mapper = {
+            i['content_type']: i['count']
+            for i in model_counts
+        }
+        model_check_counts_mapper = {
+            i['content_type']: i['count']
+            for i in model_check_counts
+        }
+        return model_counts_mapper, model_check_counts_mapper
+
+    @staticmethod
+    def _check_model_xpack(model_id):
+        app, model = model_id.split('.', 2)
+        if settings.XPACK_ENABLED:
+            return True
+        if app in xpack_nodes:
+            return False
+        if model_id in xpack_nodes:
+            return False
+        return True
+
+    def _create_models_nodes(self):
+        content_types = ContentType.objects.all()
+
+        nodes = []
+        for ct in content_types:
+            model_id = '{}.{}'.format(ct.app_label, ct.model)
+            if not self._check_model_xpack(model_id):
+                continue
+
+            total_count = self.total_counts[model_id]
+            checked_count = self.checked_counts[model_id]
+            if total_count == 0:
+                continue
+
+            # 获取 pid
+            app = ct.app_label
+            if model_id in special_pid_mapper:
+                app = special_pid_mapper[model_id]
+            self.total_counts[app] += total_count
+            self.checked_counts[app] += checked_count
+
+            # 获取 name
+            name = f'{ct.name}'
+            if model_id in verbose_name_mapper:
+                name = verbose_name_mapper[model_id]
+
+            node = self._create_node({
+                'id': model_id,
+                'name': name,
+                'pId': app,
+            }, total_count, checked_count, 'model', is_open=False)
+            nodes.append(node)
+        return nodes
+
+    @staticmethod
+    def _get_permission_name(p, content_types_name_mapper):
+        code_name = p.codename
+        action_mapper = {
+            'add': ugettext('Create'),
+            'view': ugettext('View'),
+            'change': ugettext('Update'),
+            'delete': ugettext('Delete')
+        }
+        name = ''
+        ct = ''
+        if 'add_' in p.codename:
+            name = action_mapper['add']
+            ct = code_name.replace('add_', '')
+        elif 'view_' in p.codename:
+            name = action_mapper['view']
+            ct = code_name.replace('view_', '')
+        elif 'change_' in p.codename:
+            name = action_mapper['change']
+            ct = code_name.replace('change_', '')
+        elif 'delete' in code_name:
+            name = action_mapper['delete']
+            ct = code_name.replace('delete_', '')
+
+        if ct in content_types_name_mapper:
+            name += content_types_name_mapper[ct]
+        else:
+            name = gettext(p.name)
+            name = name.replace('Can ', '').replace('可以', '')
+        return name
+
+    def _create_perms_nodes(self):
+        permissions_id = self.permissions.values_list('id', flat=True)
+        nodes = []
+        content_types = ContentType.objects.all()
+        content_types_name_mapper = {ct.model: ct.name for ct in content_types}
+
+        for p in self.all_permissions:
+            model_id = f'{p.app}.{p.model}'
+            if not self._check_model_xpack(model_id):
+                continue
+            # name 要特殊处理,解决 i18n 问题
+            name = self._get_permission_name(p, content_types_name_mapper)
+            if settings.DEBUG:
+                name += '({})'.format(p.app_label_codename)
+
+            title = p.app_label_codename
+            pid = model_id
+            # perm node 的特殊设置用的是 title,因为 id 是数字,不一致
+            if title in special_pid_mapper:
+                pid = special_pid_mapper[title]
+
+            self.total_counts[pid] += 1
+            checked = p.id in permissions_id
+            if checked:
+                self.checked_counts[pid] += 1
+
+            node = TreeNode(**{
+                'id': p.id,
+                'name': name,
+                'title': title,
+                'pId': pid,
+                'isParent': False,
+                'chkDisabled': self.check_disabled,
+                'iconSkin': 'file',
+                'checked': p.id in permissions_id,
+                'open': False,
+                'meta': {
+                    'type': 'perm',
+                }
+            })
+            nodes.append(node)
+        return nodes
+
+    def _create_node(self, data, total_count, checked_count, tp,
+                     is_parent=True, is_open=True, icon='', checked=None):
+        assert data.get('id')
+        assert data.get('name')
+        assert data.get('pId') is not None
+        if checked is None:
+            checked = total_count == checked_count
+        node_data = {
+            'isParent': is_parent,
+            'iconSkin': icon,
+            'open': is_open,
+            'chkDisabled': self.check_disabled,
+            'checked': checked,
+            'meta': {
+                'type':  tp,
+            },
+            **data
+        }
+        if not node_data.get('title'):
+            node_data['title'] = node_data['name']
+        node = TreeNode(**node_data)
+        node.name += f'({checked_count}/{total_count})'
+        return node
+
+    def _create_root_tree_node(self):
+        total_count = self.all_permissions.count()
+        checked_count = self.permissions.count()
+        node = self._create_node(root_node_data, total_count, checked_count, 'root')
+        return node
+
+    def _create_views_node(self):
+        nodes = []
+        for view_data in view_nodes_data:
+            view = view_data['id']
+            data = {
+                **view_data,
+                'pId': '$ROOT$',
+            }
+            total_count = self.total_counts[view]
+            checked_count = self.checked_counts[view]
+            if total_count == 0:
+                continue
+            node = self._create_node(data, total_count, checked_count, 'view', is_open=True)
+            nodes.append(node)
+        return nodes
+
+    def _create_extra_nodes(self):
+        nodes = []
+        for data in extra_nodes_data:
+            i = data['id']
+            pid = data['pId']
+            checked_count = self.checked_counts[i]
+            total_count = self.total_counts[i]
+            if total_count == 0:
+                continue
+            self.total_counts[pid] += total_count
+            self.checked_counts[pid] += checked_count
+            node = self._create_node(
+                data, total_count, checked_count,
+                'extra', is_open=False
+            )
+            nodes.append(node)
+        return nodes
+
+    def create_tree_nodes(self):
+        nodes = [self._create_root_tree_node()]
+        perms_nodes = self._create_perms_nodes()
+        models_nodes = self._create_models_nodes()
+        apps_nodes = self.create_apps_nodes()
+        extra_nodes = self._create_extra_nodes()
+        views_nodes = self._create_views_node()
+
+        nodes += views_nodes + apps_nodes + models_nodes + perms_nodes + extra_nodes
+        return nodes
diff --git a/apps/rbac/urls/__init__.py b/apps/rbac/urls/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/rbac/urls/api_urls.py b/apps/rbac/urls/api_urls.py
new file mode 100644
index 000000000..5dc080930
--- /dev/null
+++ b/apps/rbac/urls/api_urls.py
@@ -0,0 +1,30 @@
+# coding:utf-8
+from rest_framework_bulk.routes import BulkRouter
+from rest_framework_nested import routers
+
+from .. import api
+
+app_name = 'rbac'
+
+
+router = BulkRouter()
+router.register(r'roles', api.RoleViewSet, 'role')
+router.register(r'role-bindings', api.RoleBindingViewSet, 'role-binding')
+
+router.register(r'system-roles', api.SystemRoleViewSet, 'system-role')
+router.register(r'system-role-bindings', api.SystemRoleBindingViewSet, 'system-role-binding')
+
+router.register(r'org-roles', api.OrgRoleViewSet, 'org-role')
+router.register(r'org-role-bindings', api.OrgRoleBindingViewSet, 'org-role-binding')
+
+router.register(r'permissions', api.PermissionViewSet, 'permission')
+
+system_role_router = routers.NestedDefaultRouter(router, r'system-roles', lookup='system_role')
+system_role_router.register(r'permissions', api.SystemRolePermissionsViewSet, 'system-role-permission')
+
+org_role_router = routers.NestedDefaultRouter(router, r'org-roles', lookup='org_role')
+org_role_router.register(r'permissions', api.OrgRolePermissionsViewSet, 'org-role-permission')
+
+urlpatterns = []
+
+urlpatterns += router.urls + system_role_router.urls + org_role_router.urls
diff --git a/apps/rbac/utils.py b/apps/rbac/utils.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/rbac/ztree/__init__.py b/apps/rbac/ztree/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/rbac/ztree/permissions.py b/apps/rbac/ztree/permissions.py
new file mode 100644
index 000000000..5c5a7a37b
--- /dev/null
+++ b/apps/rbac/ztree/permissions.py
@@ -0,0 +1,259 @@
+# @ 分割符  $ 企业版 # ! 系统级别 # # 组织级别 # 控制台
+flag_sep = '@'
+flag_license_required = '$'
+flag_scope_system = '!'
+# flag_scop_org = '#'
+
+permission_paths = [
+    # format: 权限树路径 / app.codename @ 企业版、系统级别
+    '/root/view/view_console/rbac.view_console',
+    '/root/view/view_console/rbac.view_dashboard',
+    '/root/view/view_console/user_management/user_list/users.view_user',
+    '/root/view/view_console/user_management/user_list/users.add_user',
+    '/root/view/view_console/user_management/user_list/users.change_user',
+    '/root/view/view_console/user_management/user_list/users.delete_user',
+    f'/root/view/view_console/user_management/user_list/users.invite_user{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/user_management/user_list/users.remove_user{flag_sep}{flag_license_required}',
+    '/root/view/view_console/user_management/user_list/user_detail/perms.view_userassets',
+    '/root/view/view_console/user_management/user_list/user_detail/asset_perm/perms.view_assetpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/asset_perm/perms.change_assetpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/asset_perm/perms.delete_assetpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/perms.view_userapps',
+    '/root/view/view_console/user_management/user_list/user_detail/app_perm/perms.view_applicationpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/app_perm/perms.change_applicationpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/app_perm/perms.delete_applicationpermission',
+    '/root/view/view_console/user_management/user_list/user_detail/user_login_acl/acls.view_loginacl',
+    '/root/view/view_console/user_management/user_list/user_detail/user_login_acl/acls.add_loginacl',
+    '/root/view/view_console/user_management/user_list/user_detail/user_login_acl/acls.change_loginacl',
+    '/root/view/view_console/user_management/user_list/user_detail/user_login_acl/acls.delete_loginacl',
+    '/root/view/view_console/user_management/user_group_list/users.view_usergroup',
+    '/root/view/view_console/user_management/user_group_list/users.add_usergroup',
+    '/root/view/view_console/user_management/user_group_list/users.change_usergroup',
+    '/root/view/view_console/user_management/user_group_list/users.delete_usergroup',
+    '/root/view/view_console/user_management/user_group_list/user_group_detail/perms.view_permusergroupasset',
+    '/root/view/view_console/user_management/role_list/permission_list/rbac.view_permission',
+    '/root/view/view_console/user_management/role_list/org_role/rbac.view_orgrole',
+    '/root/view/view_console/user_management/role_list/org_role/rbac.add_orgrole',
+    '/root/view/view_console/user_management/role_list/org_role/rbac.change_orgrole',
+    '/root/view/view_console/user_management/role_list/org_role/rbac.delete_orgrole',
+    '/root/view/view_console/user_management/role_list/org_role/org_role_detail/rbac.view_orgrolebinding',
+    '/root/view/view_console/user_management/role_list/org_role/org_role_detail/rbac.add_orgrolebinding',
+    '/root/view/view_console/user_management/role_list/org_role/org_role_detail/rbac.delete_orgrolebinding',
+    '/root/view/view_console/user_management/role_list/system_role/rbac.view_systemrole',
+    '/root/view/view_console/user_management/role_list/system_role/rbac.add_systemrole',
+    '/root/view/view_console/user_management/role_list/system_role/rbac.change_systemrole',
+    '/root/view/view_console/user_management/role_list/system_role/rbac.delete_systemrole',
+    '/root/view/view_console/user_management/role_list/system_role/system_role_detail/rbac.view_systemrolebinding',
+    '/root/view/view_console/user_management/role_list/system_role/system_role_detail/rbac.add_systemrolebinding',
+    '/root/view/view_console/user_management/role_list/system_role/system_role_detail/rbac.delete_systemrolebinding',
+
+    '/root/view/view_console/asset_management/asset_list/assets.view_asset',
+    '/root/view/view_console/asset_management/asset_list/assets.add_asset',
+    '/root/view/view_console/asset_management/asset_list/assets.change_asset',
+    '/root/view/view_console/asset_management/asset_list/assets.delete_asset',
+    '/root/view/view_console/asset_management/asset_list/assets.test_assetconnectivity',
+    '/root/view/view_console/asset_management/asset_list/assets.refresh_assethardwareinfo',
+    '/root/view/view_console/asset_management/asset_list/assets.push_assetsystemuser',
+    '/root/view/view_console/asset_management/asset_list/assets.match_asset',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.view_node',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.add_node',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.change_node',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.delete_node',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.add_assettonode',
+    '/root/view/view_console/asset_management/asset_list/node_tree/assets.move_assettonode',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/xpack.view_syncinstancetask{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/xpack.add_syncinstancetask{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/xpack.change_syncinstancetask{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/xpack.delete_syncinstancetask{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/xpack.add_syncinstancetaskexecution{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/sync_instance_task_detail/xpack.view_syncinstancetaskexecution{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/sync_instance_task_list/sync_instance_task_detail/xpack.view_syncinstancedetail{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/account_list/xpack.view_account{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/account_list/xpack.add_account{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/account_list/xpack.change_account{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/account_list/xpack.delete_account{flag_sep}{flag_license_required}',
+    f'/root/view/view_console/asset_management/asset_list/cloud_sync/account_list/xpack.test_account{flag_sep}{flag_license_required}',
+    '/root/view/view_console/asset_management/domain_list/assets.view_domain',
+    '/root/view/view_console/asset_management/domain_list/assets.add_domain',
+    '/root/view/view_console/asset_management/domain_list/assets.change_domain',
+    '/root/view/view_console/asset_management/domain_list/assets.delete_domain',
+    '/root/view/view_console/asset_management/domain_list/gateway_list/assets.view_gateway',
+    '/root/view/view_console/asset_management/domain_list/gateway_list/assets.add_gateway',
+    '/root/view/view_console/asset_management/domain_list/gateway_list/assets.change_gateway',
+    '/root/view/view_console/asset_management/domain_list/gateway_list/assets.delete_gateway',
+    '/root/view/view_console/asset_management/domain_list/gateway_list/assets.test_gateway',
+    '/root/view/view_console/asset_management/system_user/assets.view_systemuser',
+    '/root/view/view_console/asset_management/system_user/assets.add_systemuser',
+    '/root/view/view_console/asset_management/system_user/assets.change_systemuser',
+    '/root/view/view_console/asset_management/system_user/assets.delete_systemuser',
+    '/root/view/view_console/asset_management/system_user/assets.test_assetconnectivity',
+    '/root/view/view_console/asset_management/system_user/assets.push_assetsystemuser',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_asset_list/assets.view_systemuserasset',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_asset_list/assets.add_systemuserasset',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_asset_list/assets.remove_systemuserasset',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_account_list/assets.view_authbook',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_account_list/assets.change_authbook',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_account_list/assets.delete_authbook',
+    '/root/view/view_console/asset_management/system_user/system_user_detail/system_user_account_list/assets.test_authbook',
+    '/root/view/view_console/asset_management/command_filter/assets.view_commandfilter',
+    '/root/view/view_console/asset_management/command_filter/assets.add_commandfilter',
+    '/root/view/view_console/asset_management/command_filter/assets.change_commandfilter',
+    '/root/view/view_console/asset_management/command_filter/assets.delete_commandfilter',
+    '/root/view/view_console/asset_management/command_filter/command_filter_rule/assets.view_commandfilterrule',
+    '/root/view/view_console/asset_management/command_filter/command_filter_rule/assets.add_commandfilterrule',
+    '/root/view/view_console/asset_management/command_filter/command_filter_rule/assets.change_commandfilterrule',
+    '/root/view/view_console/asset_management/command_filter/command_filter_rule/assets.delete_commandfilterrule',
+    '/root/view/view_console/asset_management/platform_list/assets.view_platform',
+    '/root/view/view_console/asset_management/platform_list/assets.add_platform',
+    '/root/view/view_console/asset_management/platform_list/assets.change_platform',
+    '/root/view/view_console/asset_management/platform_list/assets.delete_platform',
+    '/root/view/view_console/asset_management/label_management/assets.view_label',
+    '/root/view/view_console/asset_management/label_management/assets.add_label',
+    '/root/view/view_console/asset_management/label_management/assets.change_label',
+    '/root/view/view_console/asset_management/label_management/assets.delete_label',
+
+    '/root/view/view_console/app_management/remote_app/applications.view_remoteapp',
+    '/root/view/view_console/app_management/remote_app/applications.add_remoteapp',
+    '/root/view/view_console/app_management/remote_app/applications.change_remoteapp',
+    '/root/view/view_console/app_management/remote_app/applications.delete_remoteapp',
+    '/root/view/view_console/app_management/db_app/applications.view_databaseapp',
+    '/root/view/view_console/app_management/db_app/applications.add_databaseapp',
+    '/root/view/view_console/app_management/db_app/applications.change_databaseapp',
+    '/root/view/view_console/app_management/db_app/applications.delete_databaseapp',
+    '/root/view/view_console/app_management/k8s_app/applications.view_kubernetesapp',
+    '/root/view/view_console/app_management/k8s_app/applications.add_kubernetesapp',
+    '/root/view/view_console/app_management/k8s_app/applications.change_kubernetesapp',
+    '/root/view/view_console/app_management/k8s_app/applications.delete_kubernetesapp',
+
+    '/root/view/view_console/account_management/asset_account/assets.view_authbook',
+    '/root/view/view_console/account_management/asset_account/assets.add_authbook',
+    '/root/view/view_console/account_management/asset_account/assets.change_authbook',
+    '/root/view/view_console/account_management/asset_account/assets.delete_authbook',
+    '/root/view/view_console/account_management/asset_account/assets.test_authbook',
+    '/root/view/view_console/account_management/application_account/applications.view_account',
+    '/root/view/view_console/account_management/application_account/applications.add_account',
+    '/root/view/view_console/account_management/application_account/applications.change_account',
+    '/root/view/view_console/account_management/application_account/applications.delete_account',
+    '/root/view/view_console/account_management/gather_user/gather_user_list/assets.view_gathereduser',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.view_gatherusertask',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.add_gatherusertask',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.change_gatherusertask',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.delete_gatherusertask',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.add_gatherusertaskexecution',
+    '/root/view/view_console/account_management/gather_user/gather_user_task_list/xpack.view_gatherusertaskexecution',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.view_changeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.add_changeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.change_changeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.delete_changeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.add_changeauthplanexecution',
+    '/root/view/view_console/account_management/change_auth_plan/asset_change_auth_plan/xpack.view_changeauthplanexecution',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.view_applicationchangeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.add_applicationchangeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.change_applicationchangeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.delete_applicationchangeauthplan',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.add_applicationchangeauthplanexecution',
+    '/root/view/view_console/account_management/change_auth_plan/app_change_auth_plan/xpack.view_applicationchangeauthplanexecution',
+    '/root/view/view_console/account_management/account_backup/assets.view_accountbackupplan',
+    '/root/view/view_console/account_management/account_backup/assets.add_accountbackupplan',
+    '/root/view/view_console/account_management/account_backup/assets.change_accountbackupplan',
+    '/root/view/view_console/account_management/account_backup/assets.delete_accountbackupplan',
+    '/root/view/view_console/account_management/account_backup/assets.add_accountbackupplanexecution',
+    '/root/view/view_console/account_management/account_backup/assets.view_accountbackupplanexecution',
+
+    '/root/view/view_console/perm_management/asset_permission/perms.view_assetpermission',
+    '/root/view/view_console/perm_management/asset_permission/perms.add_assetpermission',
+    '/root/view/view_console/perm_management/asset_permission/perms.change_assetpermission',
+    '/root/view/view_console/perm_management/asset_permission/perms.delete_assetpermission',
+    '/root/view/view_console/perm_management/app_permission/perms.view_applicationpermission',
+    '/root/view/view_console/perm_management/app_permission/perms.add_applicationpermission',
+    '/root/view/view_console/perm_management/app_permission/perms.change_applicationpermission',
+    '/root/view/view_console/perm_management/app_permission/perms.delete_applicationpermission',
+
+    '/root/view/view_console/access_control/asset_login/acls.view_loginassetacl',
+    '/root/view/view_console/access_control/asset_login/acls.add_loginassetacl',
+    '/root/view/view_console/access_control/asset_login/acls.change_loginassetacl',
+    '/root/view/view_console/access_control/asset_login/acls.delete_loginassetacl',
+
+    '/root/view/view_console/job_center/task_list/ops.view_task',
+    '/root/view/view_console/job_center/task_list/ops.delete_task',
+    '/root/view/view_console/job_center/task_list/ops.add_adhocexecution',
+    '/root/view/view_console/job_center/task_list/task_list_detail/ops.view_adhoc',
+    '/root/view/view_console/job_center/task_list/task_list_detail/ops.view_adhocexecution',
+    '/root/view/view_console/job_center/ops.view_taskmonitor',
+
+    '/root/view/view_audit/rbac.view_audit',
+    '/root/view/view_audit/rbac.view_dashboard',
+    '/root/view/view_audit/session_audit/session_record/terminal.view_session',
+    '/root/view/view_audit/session_audit/session_record/terminal.terminate_session',
+    '/root/view/view_audit/session_audit/session_record/terminal.monitor_session',
+    '/root/view/view_audit/session_audit/session_record/session_detail/terminal.view_command',
+    '/root/view/view_audit/session_audit/session_record/session_detail/terminal.view_sessionjoinrecord',
+    '/root/view/view_audit/session_audit/command_record/terminal.view_command',
+    '/root/view/view_audit/session_audit/command_record/terminal.view_commandstorage',
+    '/root/view/view_audit/session_audit/file_transfer/audits.view_ftplog',
+    '/root/view/view_audit/log_audit/audits.view_userloginlog',
+    '/root/view/view_audit/log_audit/audits.view_operatelog',
+    '/root/view/view_audit/log_audit/audits.view_passwordchangelog',
+    '/root/view/view_audit/log_audit/ops.view_commandexecution',
+
+    '/root/view/view_workspace/rbac.view_workspace',
+    '/root/view/view_workspace/rbac.view_overview',
+    '/root/view/view_workspace/my_asset/perms.view_myassets',
+    '/root/view/view_workspace/my_asset/perms.connect_myassets',
+    '/root/view/view_workspace/my_app/my_remote_app/perms.view_myremoteapp',
+    '/root/view/view_workspace/my_app/my_remote_app/perms.connect_myremoteapp',
+    '/root/view/view_workspace/my_app/my_db_app/perms.view_mydatabaseapp',
+    '/root/view/view_workspace/my_app/my_db_app/perms.connect_mydatabaseapp',
+    '/root/view/view_workspace/my_app/my_k8s_app/perms.view_mykubernetesapp',
+    '/root/view/view_workspace/my_app/my_k8s_app/perms.connect_mykubernetesapp',
+    '/root/view/view_workspace/ops.add_commandexecution',
+    '/root/view/view_workspace/rbac.view_webterminal',
+    '/root/view/view_workspace/rbac.view_filemanager',
+
+    '/root/notifications.view_sitemessage',
+    '/root/rbac.view_webterminal',
+
+    f'/root/system_setting/settings.change_basic{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/settings.change_email{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/settings.change_auth{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/notifications.change_systemmsgsubscription{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/settings.change_sms{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/terminal_setting/settings.change_terminal_basic_setting{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/terminal_management/terminal.view_terminal{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/terminal_management/terminal.change_terminal{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/terminal_management/terminal.delete_terminal{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/replay_storage/terminal.view_replaystorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/replay_storage/terminal.add_replaystorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/replay_storage/terminal.change_replaystorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/replay_storage/terminal.delete_replaystorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/command_storage/terminal.view_commandstorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/command_storage/terminal.add_commandstorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/command_storage/terminal.change_commandstorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/command_storage/terminal.delete_commandstorage{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/terminal_setting/terminal.view_status{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/settings.change_security{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/settings.change_clean{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/org_management/orgs.view_rootorg{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/org_management/orgs.view_organization{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/org_management/orgs.add_organization{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/org_management/orgs.change_organization{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/org_management/orgs.delete_organization{flag_sep}{flag_scope_system}{flag_license_required}',
+    f'/root/system_setting/settings.change_other{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/license/xpack.view_license{flag_sep}{flag_scope_system}',
+    f'/root/system_setting/license/xpack.add_license{flag_sep}{flag_scope_system}',
+
+    f'/root/ticket/tickets.view_ticket{flag_sep}{flag_license_required}',
+    f'/root/ticket/tickets.add_ticket{flag_sep}{flag_license_required}',
+    f'/root/ticket/ticket_detail/tickets.change_ticket{flag_sep}{flag_license_required}',
+    f'/root/ticket/ticket_detail/tickets.add_comment{flag_sep}{flag_license_required}',
+    f'/root/ticket/ticket_detail/tickets.view_comment{flag_sep}{flag_license_required}',
+    f'/root/ticket/ticket_detail/tickets.view_ticketsession{flag_sep}{flag_license_required}',
+
+    # '/root/rbac.view_help',
+    f'/root/api_permission/terminal.add_session',
+    '/root/api_permission/terminal.add_command',
+    f'/root/api_permission/tickets.add_superticket{flag_sep}{flag_license_required}',
+    '/root/api_permission/authentication.add_superconnectiontoken',
+    '/root/api_permission/authentication.view_connectiontokensecret',
+    # ...
+]
diff --git a/apps/rbac/ztree/tree.py b/apps/rbac/ztree/tree.py
new file mode 100644
index 000000000..2279135e8
--- /dev/null
+++ b/apps/rbac/ztree/tree.py
@@ -0,0 +1,207 @@
+import random
+from collections import defaultdict
+from django.utils.translation import ugettext
+from common.tree import TreeNode as RawTreeNode
+from django.utils.translation import gettext_lazy as _, gettext
+from rbac.models import Permission, ContentType
+from django.db.models import F, Count
+from .permissions import permission_paths, flag_license_required, flag_sep, flag_scope_system
+from .tree_nodes import permission_tree_nodes
+from ..const import Scope
+from jumpserver.utils import has_valid_xpack_license
+from django.conf import settings
+
+
+class TreeNode(RawTreeNode):
+    total_count = 0
+    checked_count = 0
+    app_label_codename = ''
+
+    def mark_checked_if_need(self):
+        if self.isParent:
+            self.checked = self.total_count == self.checked_count
+
+    def refresh_name_if_need(self):
+        if self.isParent:
+            self.name = str(self.name) + f'({self.checked_count}/{self.total_count})'
+        elif settings.DEBUG:
+            self.name = str(self.name) + f'({self.app_label_codename})'
+
+
+class TreeNodes:
+
+    def __init__(self):
+        self.tree_nodes = defaultdict(TreeNode)
+
+    def add_node(self, data):
+        tree_node = self.add(data)
+        tree_node.total_count += 1
+
+    def add_leaf(self, data):
+        tree_node = self.add(data)
+        if not data['checked']:
+            return
+
+        parent_node = self.tree_nodes.get(tree_node.pId)
+        while parent_node:
+            parent_node.checked_count += 1
+            parent_node = self.tree_nodes.get(parent_node.pId)
+
+    def add(self, data):
+        _id = data['id']
+        data['name'] = data.get('name') or data['id']
+        tree_node = self.tree_nodes.get(_id, TreeNode(**data))
+        self.tree_nodes[tree_node.id] = tree_node
+        return tree_node
+
+    def get(self):
+        tree_nodes = list(self.tree_nodes.values())
+        for tree_node in tree_nodes:
+            tree_node.mark_checked_if_need()
+            tree_node.refresh_name_if_need()
+        return tree_nodes
+
+
+class ZTree(object):
+
+    has_valid_license = has_valid_xpack_license()
+
+    def __init__(self, checked_permission, scope, check_disabled=False):
+        self.scope = scope
+        self.checked_permission = self.prefetch_permissions(
+            checked_permission
+        )
+        self.checked_permissions_mapper = {p.id: p for p in self.checked_permission}
+        self.permissions = self.prefetch_permissions(
+            Permission.get_permissions(scope)
+        )
+        self.permissions_mapper = {p.app_label_codename: p for p in self.permissions}
+        self.content_types_name_mapper = {ct.model: ct.name for ct in ContentType.objects.all()}
+        self.check_disabled = check_disabled
+        self.tree_nodes = TreeNodes()
+        self.show_node_level = 3
+
+    @staticmethod
+    def prefetch_permissions(permissions):
+        return permissions.select_related('content_type') \
+            .annotate(app=F('content_type__app_label')) \
+            .annotate(model=F('content_type__model'))
+
+    def get_tree_nodes(self):
+        perm_paths = self.__class__.get_permission_paths(self.scope)
+        for perm_path in perm_paths:
+            self.generate_tree_nodes_by_path(perm_path)
+        return self.tree_nodes.get()
+
+    def generate_tree_nodes_by_path(self, perm_path):
+        path, perm_app_label_codename = perm_path.rsplit('/', 1)
+
+        # add path
+        path_list = path.lstrip('/').split('/')
+        pid = ''
+        for level, tree_node_id in enumerate(path_list, start=1):
+            name = _('Detail') if 'detail' in tree_node_id else tree_node_id
+            data = dict({
+                'id': tree_node_id,
+                'name': name,
+                'title': name,
+                'pId': pid,
+                'isParent': True,
+                'chkDisabled': self.check_disabled,
+                'open': level < self.show_node_level,
+                'meta': {
+                    'type': 'perm',
+                }
+            })
+            _data = permission_tree_nodes.get(tree_node_id, {})
+            data.update(_data)
+            pid = data['id']
+            self.tree_nodes.add_node(data)
+
+        # add perm
+        if not perm_app_label_codename:
+            return
+        perm = self.permissions_mapper.get(perm_app_label_codename)
+        if perm:
+            # 解决同一个权限不能在多个节点的问题
+            _id = f'{pid}#{perm.id}'
+            name = self._get_permission_name(perm)
+            checked = perm.id in self.checked_permissions_mapper
+        else:
+            #  最终不应该走这里,所有权限都要在数据库里
+            _id = perm_app_label_codename
+            name = perm_app_label_codename
+            checked = False
+
+        data = {
+            'id': _id,
+            'pId': pid,
+            'name': name,
+            'title': perm_app_label_codename,
+            'chkDisabled': self.check_disabled,
+            'app_label_codename': perm_app_label_codename,
+            'isParent': False,
+            'iconSkin': 'file',
+            'open': False,
+            'checked': checked,
+            'meta': {
+                'type': 'perm',
+            }
+        }
+        _data = permission_tree_nodes.get(perm_app_label_codename, {})
+        data.update(_data)
+        self.tree_nodes.add_leaf(data)
+
+    def _get_permission_name(self, p):
+        code_name = p.codename
+        action_mapper = {
+            'add': ugettext('Create'),
+            'view': ugettext('View'),
+            'change': ugettext('Update'),
+            'delete': ugettext('Delete')
+        }
+        name = ''
+        ct = ''
+        if 'add_' in p.codename:
+            name = action_mapper['add']
+            ct = code_name.replace('add_', '')
+        elif 'view_' in p.codename:
+            name = action_mapper['view']
+            ct = code_name.replace('view_', '')
+        elif 'change_' in p.codename:
+            name = action_mapper['change']
+            ct = code_name.replace('change_', '')
+        elif 'delete' in code_name:
+            name = action_mapper['delete']
+            ct = code_name.replace('delete_', '')
+
+        if ct in self.content_types_name_mapper:
+            name += self.content_types_name_mapper[ct]
+        else:
+            name = gettext(p.name)
+            name = name.replace('Can ', '').replace('可以', '')
+        return name
+
+    @classmethod
+    def get_permissions_app_label_codename(cls, scope):
+        perm_paths = cls.get_permission_paths(scope)
+        perms = []
+        for path in perm_paths:
+            path, app_label_code_name = path.rsplit('/', 1)
+            if not app_label_code_name:
+                continue
+            perms.append(app_label_code_name)
+        return perms
+
+    @classmethod
+    def get_permission_paths(cls, scope):
+        perm_paths = []
+        for path in permission_paths:
+            if flag_sep in path:
+                path, flags = path.split(flag_sep)
+                if flag_scope_system in flags and scope == Scope.org:
+                    continue
+                if flag_license_required in flags and not cls.has_valid_license:
+                    continue
+            perm_paths.append(path)
+        return perm_paths
diff --git a/apps/rbac/ztree/tree_nodes.py b/apps/rbac/ztree/tree_nodes.py
new file mode 100644
index 000000000..3d1dacb1e
--- /dev/null
+++ b/apps/rbac/ztree/tree_nodes.py
@@ -0,0 +1,308 @@
+from django.utils.translation import gettext_lazy as _
+
+permission_tree_nodes = {
+    # 节点
+    'root': {
+        'name': _('All permissions'),
+    },
+    'view': {
+        'name': _("View menu")
+    },
+    'view_console': {
+        'name': _('Console view'),
+    },
+    'user_management': {
+        'name': _('User management')
+    },
+    'user_list': {
+        'name': _('User list')
+    },
+    'view_workspace': {
+        'name': _('Workspace view')
+    },
+    'view_audit': {
+        'name': _("Audit view")
+    },
+    'asset_perm': {
+        'name': _('Asset permission')
+    },
+    'session_audits': {
+        'name': _('Session audits')
+    },
+    'session_record': {
+        'name': _('Online/Offline Session record')
+    },
+    'asset_management': {
+        'name': _('Asset management')
+    },
+    'asset_list': {
+        'name': _('Asset list')
+    },
+    'my_asset': {
+        'name': _('My assets')
+    },
+    'my_app': {
+        'name': _('My application')
+    },
+    'bulk_command': {
+        'name': _('Bulk command')
+    },
+    'system_setting': {
+        'name': _('System setting')
+    },
+    'ticket': {
+        'name': _('Ticket system')
+    },
+    'help': {
+        'name': _('Help')
+    },
+    'api_permission': {
+        'name': _('API permission')
+    },
+    'app_management': {
+        'name': _('Application management')
+    },
+    'account_management': {
+        'name': _('Account management'),
+    },
+    'perm_management': {
+        'name': _('Permission management'),
+    },
+    'access_control': {
+        'name': _('Access control'),
+    },
+    'job_center': {
+        'name': _('Job center'),
+    },
+    'session_audit': {
+        'name': _('Session audit')
+    },
+    'log_audit': {
+        'name': _('Log audit')
+    },
+    'user_group_list': {
+        'name': _('User group')
+    },
+    'role_list': {
+        'name': _('Role list')
+    },
+    'app_perm': {
+        'name': _('Application permission')
+    },
+    'user_login_acl': {
+        'name': _('User login acl')
+    },
+    'user_group_detail': {
+        'name': _('Detail')
+    },
+    'permission_list': {
+        'name': _('Permission list')
+    },
+    'node_tree': {
+        'name': _('Node tree')
+    },
+    'cloud_sync': {
+        'name': _('Cloud sync')
+    },
+    'sync_instance_task_list': {
+        'name': _('Sync instance task list')
+    },
+    'account_list': {
+        'name': _('Account list')
+    },
+    'system_user': {
+        'name': _('Common/Admin User')
+    },
+    'system_user_asset_list': {
+        'name': _('Asset list'),
+    },
+    'system_user_account_list': {
+        'name': _('Account list')
+    },
+    'command_filter': {
+        'name': _('Command filter')
+    },
+    'command_filter_rule': {
+        'name': _('Command filter rule')
+    },
+    'platform_list': {
+        'name': _('Platform list')
+    },
+    'label_management': {
+        'name': _('Label management')
+    },
+    'remote_app': {
+        'name': _('Remote application')
+    },
+    'db_app': {
+        'name': _('Database application')
+    },
+    'k8s_app': {
+        'name': _('Kubernetes')
+    },
+    'asset_account': {
+        'name': _('Asset account')
+    },
+    'application_account': {
+        'name': _('Application account')
+    },
+    'gather_user': {
+        'name': _('Gathered user')
+    },
+    'gather_user_list': {
+        'name': _('Gathered user list')
+    },
+    'gather_user_task_list': {
+        'name': _('Gathered user task list')
+    },
+    'change_auth_plan': {
+        'name': _('Change auth plan')
+    },
+    'asset_change_auth_plan': {
+        'name': _('Asset change auth plan')
+    },
+    'app_change_auth_plan': {
+        'name': _('Application change auth plan')
+    },
+    'account_backup': {
+        'name': _('Account backup')
+    },
+    'asset_permission': {
+        'name': _('Asset permission')
+    },
+    'app_permission': {
+        'name': _('Application permission')
+    },
+    'asset_login': {
+        'name': _('Asset login')
+    },
+    'task_list': {
+        'name': _('Task list')
+    },
+    'command_record': {
+        'name': _('Command record')
+    },
+    'file_transfer': {
+        'name': _('File transfer')
+    },
+    'my_remote_app': {
+        'name': _('Remote App')
+    },
+    'my_db_app': {
+        'name': _('Database application')
+    },
+    'my_k8s_app': {
+        'name': _('Kubernetes')
+    },
+    'terminal_setting': {
+        'name': _('Terminal setting')
+    },
+    'terminal_management': {
+        'name': _('Terminal management')
+    },
+    'command_storage': {
+        'name': _('Command storage')
+    },
+    'replay_storage': {
+        'name': _('Replay storage')
+    },
+    'org_management': {
+        'name': _('Organization management')
+    },
+    'license': {
+        'name': _('License')
+    },
+
+    # 权限
+    'rbac.view_permission': {
+        'name': _('View all permission')
+    },
+    'domain_list': {
+        'name': _('Domain list')
+    },
+    'gateway_list': {
+        'name': _('Gateway list')
+    },
+    'org_role': {
+        'name': _('Organization role')
+    },
+    'system_role': {
+        'name': _('System role')
+    },
+    'xpack.add_gatherusertaskexecution': {
+        'name': _('Run gather user task')
+    },
+    'xpack.add_changeauthplanexecution': {
+        'name': _('Run asset change auth plan')
+    },
+    'xpack.add_applicationchangeauthplanexecution': {
+        'name': _('Run application change auth plan')
+    },
+    'assets.add_accountbackupplanexecution': {
+        'name': _('Run account backup plan')
+    },
+    'ops.add_adhocexecution': {
+        'name': _('Run task')
+    },
+    'ops.view_adhoc': {
+        'name': _('View task version')
+    },
+    'ops.view_adhocexecution': {
+        'name': _('View execution history')
+    },
+    'ops.add_commandexecution': {
+        'name': _('Bulk command')
+    },
+    'notifications.view_sitemessage': {
+        'name': _('Site message')
+    },
+    'notifications.change_systemmsgsubscription': {
+        'name': _('Message subscription')
+    },
+    'terminal.view_status': {
+        'name': _('Component monitor')
+    },
+    'tickets.view_ticket': {
+        'name': _('View my/assigned ticket')
+    },
+    'tickets.add_ticket': {
+        'name': _('Create asset/application ticket')
+    },
+    'tickets.change_ticket': {
+        'name': _('Change/close ticket')
+    },
+    'assets.match_asset': {
+        'name': _('View some of the assets searched')
+    },
+    'rbac.view_workspace': {
+        'checked': True,
+        'chkDisabled': True,
+    },
+    'rbac.view_overview': {
+        'name': _('Overview'),
+        'checked': True,
+        'chkDisabled': True,
+    },
+    'rbac.view_orgrolebinding': {
+        'name': _('View permission user')
+    },
+    'rbac.add_orgrolebinding': {
+        'name': _('Add user to role')
+    },
+    'rbac.delete_orgrolebinding': {
+        'name': _('Remove user from role')
+    },
+    'rbac.view_systemrolebinding': {
+        'name': _('View permission user')
+    },
+    'rbac.add_systemrolebinding': {
+        'name': _('Add user to role')
+    },
+    'rbac.delete_systemrolebinding': {
+        'name': _('Remove user from role')
+    },
+    'xpack.add_syncinstancetaskexecution': {
+        'name': _('Run sync instance task')
+    }
+
+}
diff --git a/apps/settings/api/alibaba_sms.py b/apps/settings/api/alibaba_sms.py
index ab79d58bc..b00487d51 100644
--- a/apps/settings/api/alibaba_sms.py
+++ b/apps/settings/api/alibaba_sms.py
@@ -6,15 +6,16 @@ from django.utils.translation import gettext_lazy as _
 
 from common.sdk.sms.alibaba import AlibabaSMS
 from settings.models import Setting
-from common.permissions import IsSuperUser
 from common.exceptions import JMSException
 
 from .. import serializers
 
 
 class AlibabaSMSTestingAPI(GenericAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.AlibabaSMSSettingSerializer
+    rbac_perms = {
+        'POST': 'settings.change_setting'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
diff --git a/apps/settings/api/dingtalk.py b/apps/settings/api/dingtalk.py
index 8164198de..196f259dc 100644
--- a/apps/settings/api/dingtalk.py
+++ b/apps/settings/api/dingtalk.py
@@ -5,14 +5,11 @@ from rest_framework import status
 from django.utils.translation import gettext_lazy as _
 
 from django.conf import settings
-from common.permissions import IsSuperUser
 from common.sdk.im.dingtalk import DingTalk
-
 from .. import serializers
 
 
 class DingTalkTestingAPI(GenericAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.DingTalkSettingSerializer
 
     def post(self, request):
diff --git a/apps/settings/api/email.py b/apps/settings/api/email.py
index 0d758237b..78a406b24 100644
--- a/apps/settings/api/email.py
+++ b/apps/settings/api/email.py
@@ -6,7 +6,6 @@ from rest_framework.views import Response, APIView
 from django.core.mail import send_mail, get_connection
 from django.utils.translation import ugettext_lazy as _
 
-from common.permissions import IsSuperUser
 from common.utils import get_logger
 from .. import serializers
 from django.conf import settings
@@ -17,9 +16,11 @@ __all__ = ['MailTestingAPI']
 
 
 class MailTestingAPI(APIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.MailTestSerializer
     success_message = _("Test mail sent to {}, please check")
+    rbac_perms = {
+        'POST': 'settings.change_setting'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
diff --git a/apps/settings/api/feishu.py b/apps/settings/api/feishu.py
index e9b3a391a..07d1d4ebd 100644
--- a/apps/settings/api/feishu.py
+++ b/apps/settings/api/feishu.py
@@ -5,15 +5,16 @@ from rest_framework import status
 from django.utils.translation import gettext_lazy as _
 
 from settings.models import Setting
-from common.permissions import IsSuperUser
 from common.sdk.im.feishu import FeiShu
 
 from .. import serializers
 
 
 class FeiShuTestingAPI(GenericAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.FeiShuSettingSerializer
+    rbac_perms = {
+        'POST': 'settings.change_setting'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
diff --git a/apps/settings/api/ldap.py b/apps/settings/api/ldap.py
index 1a35fb491..a66f0977e 100644
--- a/apps/settings/api/ldap.py
+++ b/apps/settings/api/ldap.py
@@ -8,12 +8,12 @@ from orgs.models import Organization
 from django.utils.translation import ugettext_lazy as _
 from django.conf import settings
 
+from ..models import Setting
 from ..utils import (
     LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
     LDAP_USE_CACHE_FLAGS, LDAPTestUtil
 )
 from ..tasks import sync_ldap_user
-from common.permissions import IsSuperUser
 from common.utils import get_logger, is_uuid
 from ..serializers import (
     LDAPTestConfigSerializer, LDAPUserSerializer,
@@ -26,8 +26,11 @@ logger = get_logger(__file__)
 
 
 class LDAPTestingConfigAPI(APIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = LDAPTestConfigSerializer
+    perm_model = Setting
+    rbac_perms = {
+        'POST': 'settings.change_auth'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
@@ -66,8 +69,11 @@ class LDAPTestingConfigAPI(APIView):
 
 
 class LDAPTestingLoginAPI(APIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = LDAPTestLoginSerializer
+    perm_model = Setting
+    rbac_perms = {
+        'POST': 'settings.change_auth'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
@@ -81,8 +87,11 @@ class LDAPTestingLoginAPI(APIView):
 
 
 class LDAPUserListApi(generics.ListAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = LDAPUserSerializer
+    perm_model = Setting
+    rbac_perms = {
+        'list': 'settings.change_auth'
+    }
 
     def get_queryset_from_cache(self):
         search_value = self.request.query_params.get('search')
@@ -96,7 +105,7 @@ class LDAPUserListApi(generics.ListAPIView):
 
     def get_queryset(self):
         if hasattr(self, 'swagger_fake_view'):
-            return []
+            return User.objects.none()
         cache_police = self.request.query_params.get('cache_police', True)
         if cache_police in LDAP_USE_CACHE_FLAGS:
             users = self.get_queryset_from_cache()
@@ -170,7 +179,10 @@ class LDAPUserListApi(generics.ListAPIView):
 
 
 class LDAPUserImportAPI(APIView):
-    permission_classes = (IsSuperUser,)
+    perm_model = Setting
+    rbac_perms = {
+        'POST': 'settings.change_auth'
+    }
 
     def get_org(self):
         org_id = self.request.data.get('org_id')
@@ -210,7 +222,10 @@ class LDAPUserImportAPI(APIView):
 
 
 class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
-    permission_classes = (IsSuperUser,)
+    perm_model = Setting
+    rbac_perms = {
+        'retrieve': 'settings.change_auth'
+    }
 
     def retrieve(self, request, *args, **kwargs):
         try:
diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py
index 02fb9004c..7864b7c1c 100644
--- a/apps/settings/api/settings.py
+++ b/apps/settings/api/settings.py
@@ -5,7 +5,7 @@ from rest_framework import generics
 from django.conf import settings
 
 from jumpserver.conf import Config
-from common.permissions import IsSuperUser
+from rbac.permissions import RBACPermission
 from common.utils import get_logger
 from .. import serializers
 from ..models import Setting
@@ -14,7 +14,8 @@ logger = get_logger(__file__)
 
 
 class SettingsApi(generics.RetrieveUpdateAPIView):
-    permission_classes = (IsSuperUser,)
+    permission_classes = (RBACPermission,)
+
     serializer_class_mapper = {
         'all': serializers.SettingsSerializer,
         'basic': serializers.BasicSettingSerializer,
@@ -40,6 +41,9 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
         'tencent': serializers.TencentSMSSettingSerializer,
     }
 
+    def get_queryset(self):
+        return Setting.objects.all()
+
     def get_serializer_class(self):
         category = self.request.query_params.get('category', 'basic')
         default = serializers.BasicSettingSerializer
diff --git a/apps/settings/api/sms.py b/apps/settings/api/sms.py
index de36b0317..bb30fa3aa 100644
--- a/apps/settings/api/sms.py
+++ b/apps/settings/api/sms.py
@@ -1,14 +1,15 @@
 from rest_framework.generics import ListAPIView
 from rest_framework.response import Response
 
-from common.permissions import IsSuperUser
 from common.sdk.sms import BACKENDS
 from settings.serializers.sms import SMSBackendSerializer
 
 
 class SMSBackendAPI(ListAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = SMSBackendSerializer
+    rbac_perms = {
+        'list': 'settings.view_setting'
+    }
 
     def list(self, request, *args, **kwargs):
         data = [
@@ -16,7 +17,7 @@ class SMSBackendAPI(ListAPIView):
                 'name': b,
                 'label': b.label
             }
-            for b in BACKENDS
+            for b in BACKENDS.choices
         ]
 
         return Response(data)
diff --git a/apps/settings/api/tencent_sms.py b/apps/settings/api/tencent_sms.py
index b944c163c..7b3d41061 100644
--- a/apps/settings/api/tencent_sms.py
+++ b/apps/settings/api/tencent_sms.py
@@ -8,15 +8,16 @@ from django.utils.translation import gettext_lazy as _
 
 from common.sdk.sms.tencent import TencentSMS
 from settings.models import Setting
-from common.permissions import IsSuperUser
 from common.exceptions import JMSException
 
 from .. import serializers
 
 
 class TencentSMSTestingAPI(GenericAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.TencentSMSSettingSerializer
+    rbac_perms = {
+        'POST': 'settings.change_setting'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
diff --git a/apps/settings/api/wecom.py b/apps/settings/api/wecom.py
index fce5ddad5..622fde923 100644
--- a/apps/settings/api/wecom.py
+++ b/apps/settings/api/wecom.py
@@ -5,15 +5,16 @@ from rest_framework import status
 from django.utils.translation import gettext_lazy as _
 
 from settings.models import Setting
-from common.permissions import IsSuperUser
 from common.sdk.im.wecom import WeCom
 
 from .. import serializers
 
 
 class WeComTestingAPI(GenericAPIView):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.WeComSettingSerializer
+    rbac_perms = {
+        'POST': 'settings.change_setting'
+    }
 
     def post(self, request):
         serializer = self.serializer_class(data=request.data)
diff --git a/apps/settings/apps.py b/apps/settings/apps.py
index 05af85c46..8bef81db3 100644
--- a/apps/settings/apps.py
+++ b/apps/settings/apps.py
@@ -1,8 +1,10 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class SettingsConfig(AppConfig):
     name = 'settings'
+    verbose_name = _('Settings')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
diff --git a/apps/settings/migrations/0004_auto_20220211_1401.py b/apps/settings/migrations/0004_auto_20220211_1401.py
new file mode 100644
index 000000000..72c9c62f8
--- /dev/null
+++ b/apps/settings/migrations/0004_auto_20220211_1401.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.12 on 2022-02-11 06:01
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('settings', '0003_auto_20210901_1035'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='setting',
+            options={'verbose_name': 'System setting'},
+        ),
+    ]
diff --git a/apps/settings/migrations/0005_auto_20220310_0616.py b/apps/settings/migrations/0005_auto_20220310_0616.py
new file mode 100644
index 000000000..f29f017c5
--- /dev/null
+++ b/apps/settings/migrations/0005_auto_20220310_0616.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-09 22:16
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('settings', '0004_auto_20220211_1401'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='setting',
+            options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_other', 'Can change other setting'), ('change_terminal_basic_setting', 'Can change terminal basic setting')], 'verbose_name': 'System setting'},
+        ),
+    ]
diff --git a/apps/settings/migrations/0006_auto_20220310_1952.py b/apps/settings/migrations/0006_auto_20220310_1952.py
new file mode 100644
index 000000000..55e4572bc
--- /dev/null
+++ b/apps/settings/migrations/0006_auto_20220310_1952.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-10 11:52
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('settings', '0005_auto_20220310_0616'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='setting',
+            options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_other', 'Can change other setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal_basic_setting', 'Can change terminal basic setting')], 'verbose_name': 'System setting'},
+        ),
+    ]
diff --git a/apps/settings/migrations/0007_auto_20220310_2006.py b/apps/settings/migrations/0007_auto_20220310_2006.py
new file mode 100644
index 000000000..257abde35
--- /dev/null
+++ b/apps/settings/migrations/0007_auto_20220310_2006.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-10 12:06
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('settings', '0006_auto_20220310_1952'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='setting',
+            options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sys_msg_sub', 'Can sys msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'},
+        ),
+    ]
diff --git a/apps/settings/models.py b/apps/settings/models.py
index 0690590b6..eee1a0d94 100644
--- a/apps/settings/models.py
+++ b/apps/settings/models.py
@@ -79,61 +79,9 @@ class Setting(models.Model):
         item.refresh_setting()
 
     def refresh_setting(self):
-        if hasattr(self.__class__, f'refresh_{self.name}'):
-            getattr(self.__class__, f'refresh_{self.name}')()
-        else:
-            setattr(settings, self.name, self.cleaned_value)
+        setattr(settings, self.name, self.cleaned_value)
         self.refresh_keycloak_to_openid_if_need()
 
-    @classmethod
-    def refresh_authentications(cls, name):
-        setting = cls.objects.filter(name=name).first()
-        if not setting:
-            return
-
-        backends_map = {
-            'AUTH_LDAP': [settings.AUTH_BACKEND_LDAP],
-            'AUTH_OPENID': [settings.AUTH_BACKEND_OIDC_CODE, settings.AUTH_BACKEND_OIDC_PASSWORD],
-            'AUTH_RADIUS': [settings.AUTH_BACKEND_RADIUS],
-            'AUTH_CAS': [settings.AUTH_BACKEND_CAS],
-            'AUTH_SAML2': [settings.AUTH_BACKEND_SAML2],
-        }
-        setting_backends = backends_map[name]
-        auth_backends = settings.AUTHENTICATION_BACKENDS
-
-        for backend in setting_backends:
-            has = backend in auth_backends
-
-            # 添加
-            if setting.cleaned_value and not has:
-                logger.debug('Add auth backend: {}'.format(name))
-                settings.AUTHENTICATION_BACKENDS.insert(0, backend)
-
-            # 去掉
-            if not setting.cleaned_value and has:
-                index = auth_backends.index(backend)
-                logger.debug('Pop auth backend: {}'.format(name))
-                auth_backends.pop(index)
-
-        # 设置内存值
-        setattr(settings, name, setting.cleaned_value)
-
-    @classmethod
-    def refresh_AUTH_CAS(cls):
-        cls.refresh_authentications('AUTH_CAS')
-
-    @classmethod
-    def refresh_AUTH_LDAP(cls):
-        cls.refresh_authentications('AUTH_LDAP')
-
-    @classmethod
-    def refresh_AUTH_OPENID(cls):
-        cls.refresh_authentications('AUTH_OPENID')
-
-    @classmethod
-    def refresh_AUTH_SAML2(cls):
-        cls.refresh_authentications('AUTH_SAML2')
-
     def refresh_keycloak_to_openid_if_need(self):
         watch_config_names = [
             'AUTH_OPENID', 'AUTH_OPENID_REALM_NAME', 'AUTH_OPENID_SERVER_URL',
@@ -168,10 +116,7 @@ class Setting(models.Model):
         # 刷新 settings
         for key, value in openid_config.items():
             setattr(settings, key, value)
-
-    @classmethod
-    def refresh_AUTH_RADIUS(cls):
-        cls.refresh_authentications('AUTH_RADIUS')
+            self.__class__.update_or_create(key, value, encrypted=False, category=self.category)
 
     @classmethod
     def update_or_create(cls, name='', value='', encrypted=False, category=''):
@@ -192,4 +137,17 @@ class Setting(models.Model):
 
     class Meta:
         db_table = "settings_setting"
-        verbose_name = _("Setting")
+        verbose_name = _("System setting")
+        permissions = [
+            ('change_basic', _('Can change basic setting')),
+            ('change_email', _('Can change email setting')),
+            ('change_auth', _('Can change auth setting')),
+            ('change_systemmsgsubscription', _('Can sys msg sub setting')),
+            ('change_sms', _('Can change sms setting')),
+            ('change_security', _('Can change security setting')),
+            ('change_clean', _('Can change clean setting')),
+            ('change_interface', _('Can change interface setting')),
+            ('change_license', _('Can change license setting')),
+            ('change_terminal', _('Can change terminal setting')),
+            ('change_other', _('Can change other setting')),
+        ]
diff --git a/apps/settings/serializers/auth/cas.py b/apps/settings/serializers/auth/cas.py
index a111a236e..7b6bb4373 100644
--- a/apps/settings/serializers/auth/cas.py
+++ b/apps/settings/serializers/auth/cas.py
@@ -10,7 +10,7 @@ __all__ = [
 class CASSettingSerializer(serializers.Serializer):
     AUTH_CAS = serializers.BooleanField(required=False, label=_('Enable CAS Auth'))
     CAS_SERVER_URL = serializers.CharField(required=False, max_length=1024, label=_('Server url'))
-    CAS_ROOT_PROXIED_AS = serializers.CharField(required=False, max_length=1024, label=_('Proxy server url'))
+    CAS_ROOT_PROXIED_AS = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=1024, label=_('Proxy server url'))
     CAS_LOGOUT_COMPLETELY = serializers.BooleanField(required=False, label=_('Logout completely'))
     CAS_VERSION = serializers.IntegerField(required=False, label=_('Version'), min_value=1, max_value=3)
     CAS_USERNAME_ATTRIBUTE = serializers.CharField(required=False, max_length=1024, label=_('Username attr'))
diff --git a/apps/settings/signals_handler.py b/apps/settings/signal_handlers.py
similarity index 100%
rename from apps/settings/signals_handler.py
rename to apps/settings/signal_handlers.py
diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py
index 68f856da9..22f4c2b19 100644
--- a/apps/settings/utils/ldap.py
+++ b/apps/settings/utils/ldap.py
@@ -376,8 +376,10 @@ class LDAPImportUtil(object):
             except Exception as e:
                 errors.append({user['username']: str(e)})
                 logger.error(e)
-        if org and not org.is_root():
-            org.members.add(*objs)
+        if org and org.is_root():
+            return
+        for obj in objs:
+            org.add_member(obj)
         logger.info('End perform import ldap users')
         return errors
 
diff --git a/apps/static/js/angular-route.min.js b/apps/static/js/angular-route.min.js
deleted file mode 100644
index e764c4d4e..000000000
--- a/apps/static/js/angular-route.min.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- AngularJS v1.2.5
- (c) 2010-2014 Google, Inc. http://angularjs.org
- License: MIT
-*/
-(function(h,e,A){'use strict';function u(w,q,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,n){function y(){l&&(l.$destroy(),l=null);g&&(k.leave(g),g=null)}function v(){var b=w.current&&w.current.locals;if(b&&b.$template){var b=a.$new(),f=w.current;g=n(b,function(d){k.enter(d,null,g||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||q()});y()});l=f.scope=b;l.$emit("$viewContentLoaded");l.$eval(h)}else y()}var l,g,t=b.autoscroll,h=b.onload||"";a.$on("$routeChangeSuccess",
-v);v()}}}function z(e,h,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var n=e(c.contents());b.controller&&(f.$scope=a,f=h(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));n(a)}}}h=e.module("ngRoute",["ng"]).provider("$route",function(){function h(a,c){return e.extend(new (e.extend(function(){},{prototype:a})),c)}function q(a,e){var b=e.caseInsensitiveMatch,
-f={originalPath:a,regexp:a},h=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&q(a,c));if(a){var b="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},
-q(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,n,q,v,l){function g(){var d=t(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!x)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)x=!1,a.$broadcast("$routeChangeStart",d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?
-c.path(u(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?n.get(d):n.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=l.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=b,c=q.get(b,{cache:v}).then(function(a){return a.data})));
-e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function t(){var a,b;e.forEach(k,function(f,k){var p;if(p=!b){var s=c.path();p=f.keys;var l={};if(f.regexp)if(s=f.regexp.exec(s)){for(var g=1,q=s.length;g<q;++g){var n=p[g-1],r="string"==typeof s[g]?decodeURIComponent(s[g]):s[g];n&&r&&(l[n.name]=r)}p=l}else p=null;else p=null;
-p=a=p}p&&(b=h(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&h(k[null],{params:{},pathParams:{}})}function u(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var x=!1,r={routes:k,reload:function(){x=!0;a.$evalAsync(g)}};a.$on("$locationChangeSuccess",g);return r}]});h.provider("$routeParams",function(){this.$get=function(){return{}}});
-h.directive("ngView",u);h.directive("ngView",z);u.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
diff --git a/apps/static/js/angular.min.js b/apps/static/js/angular.min.js
deleted file mode 100644
index 210cfb6b1..000000000
--- a/apps/static/js/angular.min.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- AngularJS v1.3.15
- (c) 2010-2014 Google, Inc. http://angularjs.org
- License: MIT
-*/
-(function(Q,W,t){'use strict';function R(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.15/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var d=encodeURIComponent,e;e=arguments[a];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return Error(c)}}function Sa(b){if(null==b||Ta(b))return!1;var a=b.length;return b.nodeType===
-qa&&a?!0:C(b)||H(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d,e;if(b)if(G(b))for(d in b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d)||a.call(c,b[d],d,b);else if(H(b)||Sa(b)){var f="object"!==typeof b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else if(b.forEach&&b.forEach!==r)b.forEach(a,c,b);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d,b);return b}function Ed(b,a,c){for(var d=Object.keys(b).sort(),e=0;e<d.length;e++)a.call(c,
-b[d[e]],d[e]);return d}function mc(b){return function(a,c){b(c,a)}}function Fd(){return++ob}function nc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function w(b){for(var a=b.$$hashKey,c=1,d=arguments.length;c<d;c++){var e=arguments[c];if(e)for(var f=Object.keys(e),g=0,h=f.length;g<h;g++){var l=f[g];b[l]=e[l]}}nc(b,a);return b}function aa(b){return parseInt(b,10)}function Ob(b,a){return w(Object.create(b),a)}function E(){}function ra(b){return b}function ea(b){return function(){return b}}function x(b){return"undefined"===
-typeof b}function y(b){return"undefined"!==typeof b}function J(b){return null!==b&&"object"===typeof b}function C(b){return"string"===typeof b}function Y(b){return"number"===typeof b}function ga(b){return"[object Date]"===Ca.call(b)}function G(b){return"function"===typeof b}function Ua(b){return"[object RegExp]"===Ca.call(b)}function Ta(b){return b&&b.window===b}function Va(b){return b&&b.$evalAsync&&b.$watch}function Wa(b){return"boolean"===typeof b}function oc(b){return!(!b||!(b.nodeName||b.prop&&
-b.attr&&b.find))}function Gd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function va(b){return z(b.nodeName||b[0]&&b[0].nodeName)}function Xa(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return a}function Da(b,a,c,d){if(Ta(b)||Va(b))throw Ja("cpws");if(a){if(b===a)throw Ja("cpi");c=c||[];d=d||[];if(J(b)){var e=c.indexOf(b);if(-1!==e)return d[e];c.push(b);d.push(a)}if(H(b))for(var f=a.length=0;f<b.length;f++)e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a.push(e);
-else{var g=a.$$hashKey;H(a)?a.length=0:r(a,function(b,c){delete a[c]});for(f in b)b.hasOwnProperty(f)&&(e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e);nc(a,g)}}else if(a=b)H(b)?a=Da(b,[],c,d):ga(b)?a=new Date(b.getTime()):Ua(b)?(a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):J(b)&&(e=Object.create(Object.getPrototypeOf(b)),a=Da(b,e,c,d));return a}function sa(b,a){if(H(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(J(b))for(c in a=a||{},
-b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ha(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(H(b)){if(!H(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ha(b[d],a[d]))return!1;return!0}}else{if(ga(b))return ga(a)?ha(b.getTime(),a.getTime()):!1;if(Ua(b))return Ua(a)?b.toString()==a.toString():!1;if(Va(b)||Va(a)||Ta(b)||Ta(a)||H(a)||ga(a)||Ua(a))return!1;c={};for(d in b)if("$"!==
-d.charAt(0)&&!G(b[d])){if(!ha(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==t&&!G(a[d]))return!1;return!0}return!1}function Ya(b,a,c){return b.concat(Za.call(a,c))}function pc(b,a){var c=2<arguments.length?Za.call(arguments,2):[];return!G(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,Ya(c,arguments,0)):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Hd(b,a){var c=a;"string"===typeof b&&
-"$"===b.charAt(0)&&"$"===b.charAt(1)?c=t:Ta(a)?c="$WINDOW":a&&W===a?c="$DOCUMENT":Va(a)&&(c="$SCOPE");return c}function $a(b,a){if("undefined"===typeof b)return t;Y(a)||(a=a?2:null);return JSON.stringify(b,Hd,a)}function qc(b){return C(b)?JSON.parse(b):b}function wa(b){b=A(b).clone();try{b.empty()}catch(a){}var c=A("<div>").append(b).html();try{return b[0].nodeType===pb?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function rc(b){try{return decodeURIComponent(b)}catch(a){}}
-function sc(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=rc(c[0]),y(d)&&(b=y(c[1])?rc(c[1]):!0,tc.call(a,d)?H(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Pb(b){var a=[];r(b,function(b,d){H(b)?r(b,function(b){a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))}):a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))});return a.length?a.join("&"):""}function qb(b){return Ea(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Ea(b,a){return encodeURIComponent(b).replace(/%40/gi,
-"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Id(b,a){var c,d,e=rb.length;b=A(b);for(d=0;d<e;++d)if(c=rb[d]+a,C(c=b.attr(c)))return c;return null}function Jd(b,a){var c,d,e={};r(rb,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});r(rb,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==Id(c,"strict-di"),
-a(c,d?[d]:[],e))}function uc(b,a,c){J(c)||(c={});c=w({strictDi:!1},c);var d=function(){b=A(b);if(b.injector()){var d=b[0]===W?"document":wa(b);throw Ja("btstrpd",d.replace(/</,"&lt;").replace(/>/,"&gt;"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=ab(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",
-d);c(b)(a)})}]);return d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;Q&&e.test(Q.name)&&(c.debugInfoEnabled=!0,Q.name=Q.name.replace(e,""));if(Q&&!f.test(Q.name))return d();Q.name=Q.name.replace(f,"");ca.resumeBootstrap=function(b){r(b,function(b){a.push(b)});return d()};G(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function Kd(){Q.name="NG_ENABLE_DEBUG_INFO!"+Q.name;Q.location.reload()}function Ld(b){b=ca.element(b).injector();if(!b)throw Ja("test");return b.get("$$testability")}
-function vc(b,a){a=a||"_";return b.replace(Md,function(b,d){return(d?a:"")+b.toLowerCase()})}function Nd(){var b;wc||((ta=Q.jQuery)&&ta.fn.on?(A=ta,w(ta.fn,{scope:Ka.scope,isolateScope:Ka.isolateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inheritedData}),b=ta.cleanData,ta.cleanData=function(a){var c;if(Qb)Qb=!1;else for(var d=0,e;null!=(e=a[d]);d++)(c=ta._data(e,"events"))&&c.$destroy&&ta(e).triggerHandler("$destroy");b(a)}):A=T,ca.element=A,wc=!0)}function Rb(b,a,c){if(!b)throw Ja("areq",
-a||"?",c||"required");return b}function sb(b,a,c){c&&H(b)&&(b=b[b.length-1]);Rb(G(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function La(b,a){if("hasOwnProperty"===b)throw Ja("badname",a);}function xc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&G(b)?pc(e,b):b}function tb(b){var a=b[0];b=b[b.length-1];var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return A(c)}function ia(){return Object.create(null)}
-function Od(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=R("$injector"),d=R("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||R;return a(b,"module",function(){var b={};return function(f,g,h){if("hasOwnProperty"===f)throw d("badname","module");g&&b.hasOwnProperty(f)&&(b[f]=null);return a(b,f,function(){function a(c,d,e,f){f||(f=b);return function(){f[e||"push"]([c,d,arguments]);return u}}if(!g)throw c("nomod",f);var b=[],d=[],e=[],q=a("$injector","invoke","push",d),u={_invokeQueue:b,_configBlocks:d,
-_runBlocks:e,requires:g,name:f,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animateProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:q,run:function(a){e.push(a);return this}};h&&q(h);return u})}})}function Pd(b){w(b,{bootstrap:uc,copy:Da,extend:w,equals:ha,
-element:A,forEach:r,injector:ab,noop:E,bind:pc,toJson:$a,fromJson:qc,identity:ra,isUndefined:x,isDefined:y,isString:C,isFunction:G,isObject:J,isNumber:Y,isElement:oc,isArray:H,version:Qd,isDate:ga,lowercase:z,uppercase:ub,callbacks:{counter:0},getTestability:Ld,$$minErr:R,$$csp:bb,reloadWithDebugInfo:Kd});cb=Od(Q);try{cb("ngLocale")}catch(a){cb("ngLocale",[]).provider("$locale",Rd)}cb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Sd});a.provider("$compile",yc).directive({a:Td,
-input:zc,textarea:zc,form:Ud,script:Vd,select:Wd,style:Xd,option:Yd,ngBind:Zd,ngBindHtml:$d,ngBindTemplate:ae,ngClass:be,ngClassEven:ce,ngClassOdd:de,ngCloak:ee,ngController:fe,ngForm:ge,ngHide:he,ngIf:ie,ngInclude:je,ngInit:ke,ngNonBindable:le,ngPluralize:me,ngRepeat:ne,ngShow:oe,ngStyle:pe,ngSwitch:qe,ngSwitchWhen:re,ngSwitchDefault:se,ngOptions:te,ngTransclude:ue,ngModel:ve,ngList:we,ngChange:xe,pattern:Ac,ngPattern:Ac,required:Bc,ngRequired:Bc,minlength:Cc,ngMinlength:Cc,maxlength:Dc,ngMaxlength:Dc,
-ngValue:ye,ngModelOptions:ze}).directive({ngInclude:Ae}).directive(vb).directive(Ec);a.provider({$anchorScroll:Be,$animate:Ce,$browser:De,$cacheFactory:Ee,$controller:Fe,$document:Ge,$exceptionHandler:He,$filter:Fc,$interpolate:Ie,$interval:Je,$http:Ke,$httpBackend:Le,$location:Me,$log:Ne,$parse:Oe,$rootScope:Pe,$q:Qe,$$q:Re,$sce:Se,$sceDelegate:Te,$sniffer:Ue,$templateCache:Ve,$templateRequest:We,$$testability:Xe,$timeout:Ye,$window:Ze,$$rAF:$e,$$asyncCallback:af,$$jqLite:bf})}])}function db(b){return b.replace(cf,
-function(a,b,d,e){return e?d.toUpperCase():d}).replace(df,"Moz$1")}function Gc(b){b=b.nodeType;return b===qa||!b||9===b}function Hc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Sb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(ef.exec(b)||["",""])[1].toLowerCase();d=ja[d]||ja._default;c.innerHTML=d[1]+b.replace(ff,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=Ya(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});
-return e}function T(b){if(b instanceof T)return b;var a;C(b)&&(b=N(b),a=!0);if(!(this instanceof T)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new T(b)}if(a){a=W;var c;b=(c=gf.exec(b))?[a.createElement(c[1])]:(c=Hc(b,a))?c.childNodes:[]}Ic(this,b)}function Ub(b){return b.cloneNode(!0)}function wb(b,a){a||xb(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)xb(c[d])}function Jc(b,a,c,d){if(y(d))throw Tb("offargs");var e=(d=yb(b))&&d.events,f=d&&d.handle;if(f)if(a)r(a.split(" "),
-function(a){if(y(c)){var d=e[a];Xa(d||[],c);if(d&&0<d.length)return}b.removeEventListener(a,f,!1);delete e[a]});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,f,!1),delete e[a]}function xb(b,a){var c=b.ng339,d=c&&zb[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Jc(b)),delete zb[c],b.ng339=t))}function yb(b,a){var c=b.ng339,c=c&&zb[c];a&&!c&&(b.ng339=c=++hf,c=zb[c]={events:{},data:{},handle:t});return c}function Vb(b,a,c){if(Gc(b)){var d=y(c),e=!d&&a&&!J(a),
-f=!a;b=(b=yb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b;if(e)return b&&b[a];w(b,a)}}}function Ab(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Bb(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",N((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+N(a)+" "," ")))})}function Cb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");
-r(a.split(" "),function(a){a=N(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",N(c))}}function Ic(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var c=a.length;if("number"===typeof c&&a.window!==a){if(c)for(var d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function Kc(b,a){return Db(b,"$"+(a||"ngController")+"Controller")}function Db(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=H(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=A.data(b,a[d]))!==t)return c;b=b.parentNode||
-11===b.nodeType&&b.host}}function Lc(b){for(wb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function Mc(b,a){a||wb(b);var c=b.parentNode;c&&c.removeChild(b)}function jf(b,a){a=a||Q;if("complete"===a.document.readyState)a.setTimeout(b);else A(a).on("load",b)}function Nc(b,a){var c=Eb[a.toLowerCase()];return c&&Oc[va(b)]&&c}function kf(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Pc[a]}function lf(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=
-a[e||c.type],g=f?f.length:0;if(g){if(x(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};1<g&&(f=sa(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return c}function bf(){this.$get=function(){return w(T,{hasClass:function(b,a){b.attr&&(b=b[0]);
-return Ab(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return Cb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return Bb(b,a)}})}}function Ma(b,a){var c=b&&b.$$hashKey;if(c)return"function"===typeof c&&(c=b.$$hashKey()),c;c=typeof b;return c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||Fd)():c+":"+b}function eb(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function mf(b){return(b=b.toString().replace(Qc,"").match(Rc))?"function("+(b[1]||"").replace(/[\s\r\n]+/,
-" ")+")":"fn"}function ab(b,a){function c(a){return function(b,c){if(J(b))r(b,mc(a));else return a(b,c)}}function d(a,b){La(a,"service");if(G(b)||H(b))b=q.instantiate(b);if(!b.$get)throw Fa("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var c=s.invoke(b,this);if(x(c))throw Fa("undef",a);return c}}function f(a,b,c){return d(a,{$get:!1!==c?e(a,b):b})}function g(a){var b=[],c;r(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=q.get(e[0]);f[e[1]].apply(f,
-e[2])}}if(!n.get(a)){n.put(a,!0);try{C(a)?(c=cb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):G(a)?b.push(q.invoke(a)):H(a)?b.push(q.invoke(a)):sb(a,"module")}catch(e){throw H(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Fa("modulerr",a,e.stack||e.message||e);}}});return b}function h(b,c){function d(a,e){if(b.hasOwnProperty(a)){if(b[a]===l)throw Fa("cdep",a+" <- "+k.join(" <- "));return b[a]}try{return k.unshift(a),
-b[a]=l,b[a]=c(a,e)}catch(f){throw b[a]===l&&delete b[a],f;}finally{k.shift()}}function e(b,c,f,g){"string"===typeof f&&(g=f,f=null);var k=[],h=ab.$$annotate(b,a,g),l,q,p;q=0;for(l=h.length;q<l;q++){p=h[q];if("string"!==typeof p)throw Fa("itkn",p);k.push(f&&f.hasOwnProperty(p)?f[p]:d(p,g))}H(b)&&(b=b[l]);return b.apply(c,k)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((H(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return J(a)||G(a)?a:d},get:d,annotate:ab.$$annotate,has:function(a){return p.hasOwnProperty(a+
-"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new eb([],!0),p={$provide:{provider:c(d),factory:c(f),service:c(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return f(a,ea(b),!1)}),constant:c(function(a,b){La(a,"constant");p[a]=b;u[a]=b}),decorator:function(a,b){var c=q.get(a+"Provider"),d=c.$get;c.$get=function(){var a=s.invoke(d,c);return s.invoke(b,null,{$delegate:a})}}}},q=p.$injector=h(p,function(a,b){ca.isString(b)&&k.push(b);
-throw Fa("unpr",k.join(" <- "));}),u={},s=u.$injector=h(u,function(a,b){var c=q.get(a+"Provider",b);return s.invoke(c.$get,c,t,a)});r(g(b),function(a){s.invoke(a||E)});return s}function Be(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===va(a))return b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var c;c=g.yOffset;G(c)?c=c():oc(c)?(c=c[0],c="fixed"!==
-a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Y(c)||(c=0);c&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else a.scrollTo(0,0)}function g(){var a=c.hash(),b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=a.document;b&&d.$watch(function(){return c.hash()},function(a,b){a===b&&""===a||jf(function(){d.$evalAsync(g)})});return g}]}function af(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:
-function(b){return a(b,0,!1)}}]}function nf(b,a,c,d){function e(a){try{a.apply(null,Za.call(arguments,1))}finally{if(m--,0===m)for(;F.length;)try{F.pop()()}catch(b){c.error(b)}}}function f(a,b){(function da(){r(Z,function(a){a()});L=b(da,a)})()}function g(){h();l()}function h(){a:{try{B=u.state;break a}catch(a){}B=void 0}B=x(B)?null:B;ha(B,O)&&(B=O);O=B}function l(){if(D!==n.url()||I!==B)D=n.url(),I=B,r(X,function(a){a(n.url(),B)})}function k(a){try{return decodeURIComponent(a)}catch(b){return a}}
-var n=this,p=a[0],q=b.location,u=b.history,s=b.setTimeout,M=b.clearTimeout,v={};n.isMock=!1;var m=0,F=[];n.$$completeOutstandingRequest=e;n.$$incOutstandingRequestCount=function(){m++};n.notifyWhenNoOutstandingRequests=function(a){r(Z,function(a){a()});0===m?a():F.push(a)};var Z=[],L;n.addPollFn=function(a){x(L)&&f(100,s);Z.push(a);return a};var B,I,D=q.href,S=a.find("base"),P=null;h();I=B;n.url=function(a,c,e){x(e)&&(e=null);q!==b.location&&(q=b.location);u!==b.history&&(u=b.history);if(a){var f=
-I===e;if(D===a&&(!d.history||f))return n;var g=D&&Ga(D)===Ga(a);D=a;I=e;!d.history||g&&f?(g||(P=a),c?q.replace(a):g?(c=q,e=a.indexOf("#"),a=-1===e?"":a.substr(e+1),c.hash=a):q.href=a):(u[c?"replaceState":"pushState"](e,"",a),h(),I=B);return n}return P||q.href.replace(/%27/g,"'")};n.state=function(){return B};var X=[],ba=!1,O=null;n.onUrlChange=function(a){if(!ba){if(d.history)A(b).on("popstate",g);A(b).on("hashchange",g);ba=!0}X.push(a);return a};n.$$checkUrlChange=l;n.baseHref=function(){var a=S.attr("href");
-return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var fa={},y="",ka=n.baseHref();n.cookies=function(a,b){var d,e,f,g;if(a)b===t?p.cookie=encodeURIComponent(a)+"=;path="+ka+";expires=Thu, 01 Jan 1970 00:00:00 GMT":C(b)&&(d=(p.cookie=encodeURIComponent(a)+"="+encodeURIComponent(b)+";path="+ka).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(p.cookie!==y)for(y=p.cookie,d=y.split("; "),fa={},f=0;f<d.length;f++)e=d[f],g=
-e.indexOf("="),0<g&&(a=k(e.substring(0,g)),fa[a]===t&&(fa[a]=k(e.substring(g+1))));return fa}};n.defer=function(a,b){var c;m++;c=s(function(){delete v[c];e(a)},b||0);v[c]=!0;return c};n.defer.cancel=function(a){return v[a]?(delete v[a],M(a),e(E),!0):!1}}function De(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new nf(b,d,a,c)}]}function Ee(){this.$get=function(){function b(b,d){function e(a){a!=p&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!=
-b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw R("$cacheFactory")("iid",b);var g=0,h=w({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,q=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}if(!x(b))return a in l||g++,l[a]=b,g>k&&this.remove(q.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==p&&(p=b.p);b==q&&(q=b.n);f(b.n,b.p);delete n[a]}delete l[a];
-g--},removeAll:function(){l={};g=0;n={};p=q=null},destroy:function(){n=h=l=null;delete a[b]},info:function(){return w({},h,{size:g})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function Ve(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function yc(b,a){function c(a,b){var c=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,d={};r(a,function(a,e){var f=a.match(c);if(!f)throw la("iscp",b,e,a);d[e]={mode:f[1][0],collection:"*"===
-f[2],optional:"?"===f[3],attrName:f[4]||e}});return d}var d={},e=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,f=/(([\w\-]+)(?:\:([^;]+))?;?)/,g=Gd("ngSrc,ngSrcset,src,srcset"),h=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/;this.directive=function p(a,e){La(a,"directive");C(a)?(Rb(e,"directiveFactory"),d.hasOwnProperty(a)||(d[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,e){var f=[];r(d[a],function(d,g){try{var h=b.invoke(d);G(h)?h={compile:ea(h)}:!h.compile&&h.link&&
-(h.compile=ea(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||a;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";J(h.scope)&&(h.$$isolateBindings=c(h.scope,h.name));f.push(h)}catch(k){e(k)}});return f}])),d[a].push(e)):r(a,mc(p));return this};this.aHrefSanitizationWhitelist=function(b){return y(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return y(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};
-var k=!0;this.debugInfoEnabled=function(a){return y(a)?(k=a,this):k};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,s,M,v,m,F,Z,L,B){function I(a,b){try{a.addClass(b)}catch(c){}}function D(a,b,c,d,e){a instanceof A||(a=A(a));r(a,function(b,c){b.nodeType==pb&&b.nodeValue.match(/\S+/)&&(a[c]=A(b).wrap("<span></span>").parent()[0])});var f=S(a,b,a,c,d,e);D.$$addScopeClass(a);
-var g=null;return function(b,c,d){Rb(b,"scope");d=d||{};var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==va(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?A(Xb(g,A("<div>").append(a).html())):c?Ka.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",h[k].instance);D.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,e);return d}}function S(a,b,c,d,e,f){function g(a,
-c,d,e){var f,k,l,q,p,s,M;if(m)for(M=Array(c.length),q=0;q<h.length;q+=3)f=h[q],M[f]=c[f];else M=c;q=0;for(p=h.length;q<p;)k=M[h[q++]],c=h[q++],f=h[q++],c?(c.scope?(l=a.$new(),D.$$addScopeInfo(A(k),l)):l=a,s=c.transcludeOnThisElement?P(a,c.transclude,e,c.elementTranscludeOnThisElement):!c.templateOnThisElement&&e?e:!e&&b?P(a,b):null,c(f,l,k,d,s)):f&&f(a,k.childNodes,t,e)}for(var h=[],k,l,q,p,m,s=0;s<a.length;s++){k=new Yb;l=X(a[s],[],k,0===s?d:t,e);(f=l.length?fa(l,a[s],k,b,c,null,[],[],f):null)&&
-f.scope&&D.$$addScopeClass(k.$$element);k=f&&f.terminal||!(q=a[s].childNodes)||!q.length?null:S(q,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(s,f,k),p=!0,m=m||f;f=null}return p?g:null}function P(a,b,c,d){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g})}}function X(a,b,c,d,g){var h=c.$attr,k;switch(a.nodeType){case qa:ka(b,xa(va(a)),"E",d,g);for(var l,
-q,p,m=a.attributes,s=0,M=m&&m.length;s<M;s++){var u=!1,L=!1;l=m[s];k=l.name;q=N(l.value);l=xa(k);if(p=U.test(l))k=k.replace(Sc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var B=l.replace(/(Start|End)$/,"");x(B)&&l===B+"Start"&&(u=k,L=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(p||!c.hasOwnProperty(l))c[l]=q,Nc(a,l)&&(c[l]=!0);Oa(a,b,q,l,p);ka(b,l,"A",d,g,u,L)}a=a.className;J(a)&&(a=a.animVal);if(C(a)&&""!==a)for(;k=f.exec(a);)l=xa(k[2]),
-ka(b,l,"C",d,g)&&(c[l]=N(k[3])),a=a.substr(k.index+k[0].length);break;case pb:za(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))l=xa(k[1]),ka(b,l,"M",d,g)&&(c[l]=N(k[2]))}catch(v){}}b.sort(da);return b}function ba(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw la("uterdir",b,c);a.nodeType==qa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return A(d)}function O(a,b,c){return function(d,e,f,g,h){e=ba(e[0],
-b,c);return a(d,e,f,g,h)}}function fa(a,d,e,f,g,k,l,p,m){function s(a,b,c,d){if(a){c&&(a=O(a,c,d));a.require=K.require;a.directiveName=da;if(P===K||K.$$isolateScope)a=Y(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=O(b,c,d));b.require=K.require;b.directiveName=da;if(P===K||K.$$isolateScope)b=Y(b,{isolateScope:!0});p.push(b)}}function L(a,b,c,d){var e,f="data",g=!1,k=c,l;if(C(b)){l=b.match(h);b=b.substring(l[0].length);l[3]&&(l[1]?l[3]=null:l[1]=l[3]);"^"===l[1]?f="inheritedData":"^^"===l[1]&&(f="inheritedData",
-k=c.parent());"?"===l[2]&&(g=!0);e=null;d&&"data"===f&&(e=d[b])&&(e=e.instance);e=e||k[f]("$"+b+"Controller");if(!e&&!g)throw la("ctreq",b,a);return e||null}H(b)&&(e=[],r(b,function(b){e.push(L(a,b,c,d))}));return e}function B(a,c,f,g,h){function k(a,b,c){var d;Va(a)||(c=b,b=a,a=t);E&&(d=F);c||(c=E?X.parent():X);return h(a,b,d,c,Wb)}var m,s,u,I,F,gb,X,O;d===f?(O=e,X=e.$$element):(X=A(f),O=new Yb(X,e));P&&(I=c.$new(!0));h&&(gb=k,gb.$$boundTransclude=h);S&&(Z={},F={},r(S,function(a){var b={$scope:a===
-P||a.$$isolateScope?I:c,$element:X,$attrs:O,$transclude:gb};u=a.controller;"@"==u&&(u=O[a.name]);b=v(u,b,!0,a.controllerAs);F[a.name]=b;E||X.data("$"+a.name+"Controller",b.instance);Z[a.name]=b}));if(P){D.$$addScopeInfo(X,I,!0,!(ma&&(ma===P||ma===P.$$originalDirective)));D.$$addScopeClass(X,!0);g=Z&&Z[P.name];var ba=I;g&&g.identifier&&!0===P.bindToController&&(ba=g.instance);r(I.$$isolateBindings=P.$$isolateBindings,function(a,d){var e=a.attrName,f=a.optional,g,h,k,l;switch(a.mode){case "@":O.$observe(e,
-function(a){ba[d]=a});O.$$observers[e].$$scope=c;O[e]&&(ba[d]=b(O[e])(c));break;case "=":if(f&&!O[e])break;h=M(O[e]);l=h.literal?ha:function(a,b){return a===b||a!==a&&b!==b};k=h.assign||function(){g=ba[d]=h(c);throw la("nonassign",O[e],P.name);};g=ba[d]=h(c);f=function(a){l(a,ba[d])||(l(a,g)?k(c,a=ba[d]):ba[d]=a);return g=a};f.$stateful=!0;f=a.collection?c.$watchCollection(O[e],f):c.$watch(M(O[e],f),null,h.literal);I.$on("$destroy",f);break;case "&":h=M(O[e]),ba[d]=function(a){return h(c,a)}}})}Z&&
-(r(Z,function(a){a()}),Z=null);g=0;for(m=l.length;g<m;g++)s=l[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb);var Wb=c;P&&(P.template||null===P.templateUrl)&&(Wb=I);a&&a(Wb,f.childNodes,t,h);for(g=p.length-1;0<=g;g--)s=p[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb)}m=m||{};for(var I=-Number.MAX_VALUE,F,S=m.controllerDirectives,Z,P=m.newIsolateScopeDirective,ma=m.templateDirective,fa=m.nonTlbTranscludeDirective,ka=!1,x=!1,E=m.hasElementTranscludeDirective,
-w=e.$$element=A(d),K,da,V,fb=f,za,z=0,Q=a.length;z<Q;z++){K=a[z];var Oa=K.$$start,U=K.$$end;Oa&&(w=ba(d,Oa,U));V=t;if(I>K.priority)break;if(V=K.scope)K.templateUrl||(J(V)?(Na("new/isolated scope",P||F,K,w),P=K):Na("new/isolated scope",P,K,w)),F=F||K;da=K.name;!K.templateUrl&&K.controller&&(V=K.controller,S=S||{},Na("'"+da+"' controller",S[da],K,w),S[da]=K);if(V=K.transclude)ka=!0,K.$$tlb||(Na("transclusion",fa,K,w),fa=K),"element"==V?(E=!0,I=K.priority,V=w,w=e.$$element=A(W.createComment(" "+da+": "+
-e[da]+" ")),d=w[0],T(g,Za.call(V,0),d),fb=D(V,f,I,k&&k.name,{nonTlbTranscludeDirective:fa})):(V=A(Ub(d)).contents(),w.empty(),fb=D(V,f));if(K.template)if(x=!0,Na("template",ma,K,w),ma=K,V=G(K.template)?K.template(w,e):K.template,V=Tc(V),K.replace){k=K;V=Sb.test(V)?Uc(Xb(K.templateNamespace,N(V))):[];d=V[0];if(1!=V.length||d.nodeType!==qa)throw la("tplrt",da,"");T(g,w,d);Q={$attr:{}};V=X(d,[],Q);var aa=a.splice(z+1,a.length-(z+1));P&&y(V);a=a.concat(V).concat(aa);R(e,Q);Q=a.length}else w.html(V);if(K.templateUrl)x=
-!0,Na("template",ma,K,w),ma=K,K.replace&&(k=K),B=of(a.splice(z,a.length-z),w,e,g,ka&&fb,l,p,{controllerDirectives:S,newIsolateScopeDirective:P,templateDirective:ma,nonTlbTranscludeDirective:fa}),Q=a.length;else if(K.compile)try{za=K.compile(w,e,fb),G(za)?s(null,za,Oa,U):za&&s(za.pre,za.post,Oa,U)}catch(pf){c(pf,wa(w))}K.terminal&&(B.terminal=!0,I=Math.max(I,K.priority))}B.scope=F&&!0===F.scope;B.transcludeOnThisElement=ka;B.elementTranscludeOnThisElement=E;B.templateOnThisElement=x;B.transclude=fb;
-m.hasElementTranscludeDirective=E;return B}function y(a){for(var b=0,c=a.length;b<c;b++)a[b]=Ob(a[b],{$$isolateScope:!0})}function ka(b,e,f,g,h,k,l){if(e===h)return null;h=null;if(d.hasOwnProperty(e)){var q;e=a.get(e+"Directive");for(var m=0,s=e.length;m<s;m++)try{q=e[m],(g===t||g>q.priority)&&-1!=q.restrict.indexOf(f)&&(k&&(q=Ob(q,{$$start:k,$$end:l})),b.push(q),h=q)}catch(M){c(M)}}return h}function x(b){if(d.hasOwnProperty(b))for(var c=a.get(b+"Directive"),e=0,f=c.length;e<f;e++)if(b=c[e],b.multiElement)return!0;
-return!1}function R(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(I(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function of(a,b,c,d,e,f,g,h){var k=[],l,q,p=b[0],m=a.shift(),M=Ob(m,{templateUrl:null,transclude:null,
-replace:null,$$originalDirective:m}),u=G(m.templateUrl)?m.templateUrl(b,c):m.templateUrl,L=m.templateNamespace;b.empty();s(Z.getTrustedResourceUrl(u)).then(function(s){var B,v;s=Tc(s);if(m.replace){s=Sb.test(s)?Uc(Xb(L,N(s))):[];B=s[0];if(1!=s.length||B.nodeType!==qa)throw la("tplrt",m.name,u);s={$attr:{}};T(d,b,B);var D=X(B,[],s);J(m.scope)&&y(D);a=D.concat(a);R(c,s)}else B=p,b.html(s);a.unshift(M);l=fa(a,B,c,e,b,m,f,g,h);r(d,function(a,c){a==B&&(d[c]=b[0])});for(q=S(b[0].childNodes,e);k.length;){s=
-k.shift();v=k.shift();var F=k.shift(),O=k.shift(),D=b[0];if(!s.$$destroyed){if(v!==p){var Z=v.className;h.hasElementTranscludeDirective&&m.replace||(D=Ub(B));T(F,A(v),D);I(A(D),Z)}v=l.transcludeOnThisElement?P(s,l.transclude,O):O;l(q,s,D,d,v)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=P(b,l.transclude,e)),l(q,b,c,d,a)))}}function da(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Na(a,
-b,c,d){if(b)throw la("multidir",b.name,c.name,a,wa(d));}function za(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&D.$$addBindingClass(a);return function(a,c){var e=c.parent();b||D.$$addBindingClass(e);D.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Xb(a,b){a=z(a||"html");switch(a){case "svg":case "math":var c=W.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}
-function Q(a,b){if("srcdoc"==b)return Z.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return Z.RESOURCE_URL}function Oa(a,c,d,e,f){var h=Q(a,e);f=g[e]||f;var k=b(d,!0,h,f);if(k){if("multiple"===e&&"select"===va(a))throw la("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,c,g){c=g.$$observers||(g.$$observers={});if(l.test(e))throw la("nodomevents");var m=g[e];m!==d&&(k=m&&b(m,!0,h,f),d=m);k&&(g[e]=k(a),(c[e]||(c[e]=[])).$$inter=
-!0,(g.$$observers&&g.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?g.$updateClass(a,b):g.$set(e,a)}))}}}})}}function T(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=W.createDocumentFragment();a.appendChild(d);A(c).data(A(d).data());ta?(Qb=!0,ta.cleanData([d])):delete A.cache[d[A.expando]];
-d=1;for(e=b.length;d<e;d++)f=b[d],A(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function Y(a,b){return w(function(){return a.apply(null,arguments)},a,b)}function $(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}var Yb=function(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};Yb.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&L.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&
-L.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Vc(a,b);c&&c.length&&L.addClass(this.$$element,c);(c=Vc(b,a))&&c.length&&L.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=this.$$element[0],g=Nc(f,a),h=kf(f,a),f=a;g?(this.$$element.prop(a,b),e=g):h&&(this[h]=b,f=h);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=vc(a,"-"));g=va(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this[a]=b=B(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g=
-"",h=N(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(h)?k:/(,)/,h=h.split(k),k=Math.floor(h.length/2),l=0;l<k;l++)var q=2*l,g=g+B(N(h[q]),!0),g=g+(" "+N(h[q+1]));h=N(h[2*l]).split(/\s/);g+=B(N(h[0]),!0);2===h.length&&(g+=" "+N(h[1]));this[a]=b=g}!1!==d&&(null===b||b===t?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&r(a[f],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=ia()),e=d[a]||(d[a]=[]);e.push(b);
-m.$evalAsync(function(){!e.$$inter&&c.hasOwnProperty(a)&&b(c[a])});return function(){Xa(e,b)}}};var V=b.startSymbol(),ma=b.endSymbol(),Tc="{{"==V||"}}"==ma?ra:function(a){return a.replace(/\{\{/g,V).replace(/}}/g,ma)},U=/^ngAttr[A-Z]/;D.$$addBindingInfo=k?function(a,b){var c=a.data("$binding")||[];H(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:E;D.$$addBindingClass=k?function(a){I(a,"ng-binding")}:E;D.$$addScopeInfo=k?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",
-b)}:E;D.$$addScopeClass=k?function(a,b){I(a,b?"ng-isolate-scope":"ng-scope")}:E;return D}]}function xa(b){return db(b.replace(Sc,""))}function Vc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function Uc(b){b=A(b);var a=b.length;if(1>=a)return b;for(;a--;)8===b[a].nodeType&&qf.call(b,a,1);return b}function Fe(){var b={},a=!1,c=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,c){La(a,
-"controller");J(a)?w(b,a):b[a]=c};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(d,e){function f(a,b,c,d){if(!a||!J(a.$scope))throw R("$controller")("noscp",d,b);a.$scope[b]=c}return function(g,h,l,k){var n,p,q;l=!0===l;k&&C(k)&&(q=k);if(C(g)){k=g.match(c);if(!k)throw rf("ctrlfmt",g);p=k[1];q=q||k[3];g=b.hasOwnProperty(p)?b[p]:xc(h.$scope,p,!0)||(a?xc(e,p,!0):t);sb(g,p,!0)}if(l)return l=(H(g)?g[g.length-1]:g).prototype,n=Object.create(l||null),q&&f(h,q,n,p||g.name),w(function(){d.invoke(g,
-n,h,p);return n},{instance:n,identifier:q});n=d.instantiate(g,h,p);q&&f(h,q,n,p||g.name);return n}}]}function Ge(){this.$get=["$window",function(b){return A(b.document)}]}function He(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Zb(b,a){if(C(b)){var c=b.replace(sf,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(Wc))||(d=(d=c.match(tf))&&uf[d[0]].test(c));d&&(b=qc(c))}}return b}function Xc(b){var a=ia(),c,d,e;if(!b)return a;r(b.split("\n"),
-function(b){e=b.indexOf(":");c=z(N(b.substr(0,e)));d=N(b.substr(e+1));c&&(a[c]=a[c]?a[c]+", "+d:d)});return a}function Yc(b){var a=J(b)?b:t;return function(c){a||(a=Xc(b));return c?(c=a[z(c)],void 0===c&&(c=null),c):a}}function Zc(b,a,c,d){if(G(d))return d(b,a,c);r(d,function(d){b=d(b,a,c)});return b}function Ke(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return J(a)&&"[object File]"!==Ca.call(a)&&"[object Blob]"!==Ca.call(a)&&"[object FormData]"!==Ca.call(a)?$a(a):
-a}],headers:{common:{Accept:"applications/json, text/plain, */*"},post:sa($b),put:sa($b),patch:sa($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},a=!1;this.useApplyAsync=function(b){return y(b)?(a=!!b,this):a};var c=this.interceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(d,e,f,g,h,l){function k(a){function c(a){var b=w({},a);b.data=a.data?Zc(a.data,a.headers,a.status,e.transformResponse):a.data;a=a.status;return 200<=a&&300>a?
-b:h.reject(b)}function d(a){var b,c={};r(a,function(a,d){G(a)?(b=a(),null!=b&&(c[d]=b)):c[d]=a});return c}if(!ca.isObject(a))throw R("$http")("badreq",a);var e=w({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse},a);e.headers=function(a){var c=b.headers,e=w({},a.headers),f,g,c=w({},c.common,c[z(a.method)]);a:for(f in c){a=z(f);for(g in e)if(z(g)===a)continue a;e[f]=c[f]}return d(e)}(a);e.method=ub(e.method);var f=[function(a){var d=a.headers,e=Zc(a.data,Yc(d),
-t,a.transformRequest);x(e)&&r(d,function(a,b){"content-type"===z(b)&&delete d[b]});x(a.withCredentials)&&!x(b.withCredentials)&&(a.withCredentials=b.withCredentials);return n(a,e).then(c,c)},t],g=h.when(e);for(r(u,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),g=g.then(a,k)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,e)});return g};g.error=
-function(a){g.then(null,function(b){a(b.data,b.status,b.headers,e)});return g};return g}function n(c,f){function l(b,c,d,e){function f(){m(c,b,d,e)}I&&(200<=b&&300>b?I.put(P,[b,c,Xc(d),e]):I.remove(P));a?g.$applyAsync(f):(f(),g.$$phase||g.$apply())}function m(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b,headers:Yc(d),config:c,statusText:e})}function n(a){m(a.data,a.status,sa(a.headers()),a.statusText)}function u(){var a=k.pendingRequests.indexOf(c);-1!==a&&k.pendingRequests.splice(a,
-1)}var L=h.defer(),B=L.promise,I,D,S=c.headers,P=p(c.url,c.params);k.pendingRequests.push(c);B.then(u,u);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(I=J(c.cache)?c.cache:J(b.cache)?b.cache:q);I&&(D=I.get(P),y(D)?D&&G(D.then)?D.then(n,n):H(D)?m(D[1],D[0],sa(D[2]),D[3]):m(D,200,{},"OK"):I.put(P,B));x(D)&&((D=$c(c.url)?e.cookies()[c.xsrfCookieName||b.xsrfCookieName]:t)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=D),d(c.method,P,f,l,S,c.timeout,c.withCredentials,c.responseType));
-return B}function p(a,b){if(!b)return a;var c=[];Ed(b,function(a,b){null===a||x(a)||(H(a)||(a=[a]),r(a,function(a){J(a)&&(a=ga(a)?a.toISOString():$a(a));c.push(Ea(b)+"="+Ea(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var q=f("$http"),u=[];r(c,function(a){u.unshift(C(a)?l.get(a):l.invoke(a))});k.pendingRequests=[];(function(a){r(arguments,function(a){k[a]=function(b,c){return k(w(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){k[a]=
-function(b,c,d){return k(w(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");k.defaults=b;return k}]}function vf(){return new Q.XMLHttpRequest}function Le(){this.$get=["$browser","$window","$document",function(b,a,c){return wf(b,vf,b.defer,a.angular.callbacks,c[0])}]}function wf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);
-f=null;var g=-1,u="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),u=a.type,g="error"===a.type?404:200);c&&c(g,u)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,l,k,n,p,q,u){function s(){m&&m();F&&F.abort()}function M(a,d,e,f,g){L!==t&&c.cancel(L);m=F=null;a(d,e,f,g);b.$$completeOutstandingRequest(E)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==z(e)){var v="_"+(d.counter++).toString(36);d[v]=function(a){d[v].data=
-a;d[v].called=!0};var m=f(h.replace("JSON_CALLBACK","angular.callbacks."+v),v,function(a,b){M(k,a,d[v].data,"",b);d[v]=E})}else{var F=a();F.open(e,h,!0);r(n,function(a,b){y(a)&&F.setRequestHeader(b,a)});F.onload=function(){var a=F.statusText||"",b="response"in F?F.response:F.responseText,c=1223===F.status?204:F.status;0===c&&(c=b?200:"file"==Aa(h).protocol?404:0);M(k,c,b,F.getAllResponseHeaders(),a)};e=function(){M(k,-1,null,null,"")};F.onerror=e;F.onabort=e;q&&(F.withCredentials=!0);if(u)try{F.responseType=
-u}catch(Z){if("json"!==u)throw Z;}F.send(l||null)}if(0<p)var L=c(s,p);else p&&G(p.then)&&p.then(s)}}function Ie(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(a){return"\\\\\\"+a}function g(f,g,u,s){function M(c){return c.replace(k,b).replace(n,a)}function v(a){try{var b=a;a=u?e.getTrusted(u,b):e.valueOf(b);var c;if(s&&!y(a))c=a;else if(null==a)c="";
-else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=$a(a)}c=a}return c}catch(g){c=ac("interr",f,g.toString()),d(c)}}s=!!s;for(var m,F,r=0,L=[],B=[],I=f.length,D=[],S=[];r<I;)if(-1!=(m=f.indexOf(b,r))&&-1!=(F=f.indexOf(a,m+h)))r!==m&&D.push(M(f.substring(r,m))),r=f.substring(m+h,F),L.push(r),B.push(c(r,v)),r=F+l,S.push(D.length),D.push("");else{r!==I&&D.push(M(f.substring(r)));break}if(u&&1<D.length)throw ac("noconcat",f);if(!g||L.length){var P=function(a){for(var b=0,c=
-L.length;b<c;b++){if(s&&x(a[b]))return;D[S[b]]=a[b]}return D.join("")};return w(function(a){var b=0,c=L.length,e=Array(c);try{for(;b<c;b++)e[b]=B[b](a);return P(e)}catch(g){a=ac("interr",f,g.toString()),d(a)}},{exp:f,expressions:L,$$watchDelegate:function(a,b,c){var d;return a.$watchGroup(B,function(c,e){var f=P(c);G(b)&&b.call(this,f,c!==e?d:f,a);d=f},c)}})}}var h=b.length,l=a.length,k=new RegExp(b.replace(/./g,f),"g"),n=new RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol=
-function(){return a};return g}]}function Je(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,h,l,k){var n=a.setInterval,p=a.clearInterval,q=0,u=y(k)&&!k,s=(u?d:c).defer(),M=s.promise;l=y(l)?l:0;M.then(null,null,e);M.$$intervalId=n(function(){s.notify(q++);0<l&&q>=l&&(s.resolve(q),p(M.$$intervalId),delete f[M.$$intervalId]);u||b.$apply()},h);f[M.$$intervalId]=s;return M}var f={};e.cancel=function(b){return b&&b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),
-delete f[b.$$intervalId],!0):!1};return e}]}function Rd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
-DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a",ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"]},pluralCat:function(b){return 1===b?"one":"other"}}}}function bc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=qb(b[a]);
-return b.join("/")}function ad(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=aa(c.port)||xf[c.protocol]||null}function bd(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=sc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ya(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ga(b){var a=b.indexOf("#");
-return-1==a?b:b.substr(0,a)}function Fb(b){return b.replace(/(#.+)|#$/,"$1")}function cc(b){return b.substr(0,Ga(b).lastIndexOf("/")+1)}function dc(b,a){this.$$html5=!0;a=a||"";var c=cc(b);ad(b,this);this.$$parse=function(a){var b=ya(c,a);if(!C(b))throw Gb("ipthprfx",a,c);bd(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Pb(this.$$search),b=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=
-function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ya(b,d))!==t?(g=f,g=(f=ya(a,f))!==t?c+(ya("/",f)||f):b+g):(f=ya(c,d))!==t?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function ec(b,a){var c=cc(b);ad(b,this);this.$$parse=function(d){d=ya(b,d)||ya(c,d);var e;"#"===d.charAt(0)?(e=ya(a,d),x(e)&&(e=d)):e=this.$$html5?d:"";bd(e,this);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};
-this.$$compose=function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ga(b)==Ga(a)?(this.$$parse(a),!0):!1}}function cd(b,a){this.$$html5=!0;ec.apply(this,arguments);var c=cc(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ga(d)?f=d:(g=ya(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);return!!f};this.$$compose=
-function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function Hb(b){return function(){return this[b]}}function dd(b,a){return function(c){if(x(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Me(){var b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){return y(a)?(b=a,this):b};this.html5Mode=function(b){return Wa(b)?(a.enabled=b,this):J(b)?(Wa(b.enabled)&&(a.enabled=
-b.enabled),Wa(b.requireBase)&&(a.requireBase=b.requireBase),Wa(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var p=d.url(),q;if(a.enabled){if(!n&&a.requireBase)throw Gb("nobase");
-q=p.substring(0,p.indexOf("/",p.indexOf("//")+2))+(n||"/");n=e.history?dc:cd}else q=Ga(p),n=ec;k=new n(q,"#"+b);k.$$parseLinkUrl(p,p);k.$$state=d.state();var u=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKey&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var e=A(b.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");J(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href);
-u.test(h)||!h||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(h,l)||(b.preventDefault(),k.absUrl()!=d.url()&&(c.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});Fb(k.absUrl())!=Fb(p)&&d.url(k.absUrl(),!0);var s=!0;d.onUrlChange(function(a,b){c.$evalAsync(function(){var d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h(d,!1,e)):(s=!1,l(d,e)))});c.$$phase||c.$digest()});
-c.$watch(function(){var a=Fb(d.url()),b=Fb(k.absUrl()),f=d.state(),g=k.$$replace,q=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(s||q)s=!1,c.$evalAsync(function(){var b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPrevented;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state=f):(q&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function Ne(){var b=!0,a=this;this.debugEnabled=function(a){return y(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof
-Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||E;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];r(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,
-arguments)}}()}}]}function ua(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw na("isecfld",a);return b}function oa(b,a){if(b){if(b.constructor===b)throw na("isecfn",a);if(b.window===b)throw na("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw na("isecdom",a);if(b===Object)throw na("isecobj",a);}return b}function fc(b){return b.constant}function hb(b,a,c,d,e){oa(b,e);oa(a,e);c=c.split(".");for(var f,
-g=0;1<c.length;g++){f=ua(c.shift(),e);var h=0===g&&a&&a[f]||b[f];h||(h={},b[f]=h);b=oa(h,e)}f=ua(c.shift(),e);oa(b[f],e);return b[f]=d}function Pa(b){return"constructor"==b}function ed(b,a,c,d,e,f,g){ua(b,f);ua(a,f);ua(c,f);ua(d,f);ua(e,f);var h=function(a){return oa(a,f)},l=g||Pa(b)?h:ra,k=g||Pa(a)?h:ra,n=g||Pa(c)?h:ra,p=g||Pa(d)?h:ra,q=g||Pa(e)?h:ra;return function(f,g){var h=g&&g.hasOwnProperty(b)?g:f;if(null==h)return h;h=l(h[b]);if(!a)return h;if(null==h)return t;h=k(h[a]);if(!c)return h;if(null==
-h)return t;h=n(h[c]);if(!d)return h;if(null==h)return t;h=p(h[d]);return e?null==h?t:h=q(h[e]):h}}function yf(b,a){return function(c,d){return b(c,d,oa,a)}}function zf(b,a,c){var d=a.expensiveChecks,e=d?Af:Bf,f=e[b];if(f)return f;var g=b.split("."),h=g.length;if(a.csp)f=6>h?ed(g[0],g[1],g[2],g[3],g[4],c,d):function(a,b){var e=0,f;do f=ed(g[e++],g[e++],g[e++],g[e++],g[e++],c,d)(a,b),b=t,a=f;while(e<h);return f};else{var l="";d&&(l+="s = eso(s, fe);\nl = eso(l, fe);\n");var k=d;r(g,function(a,b){ua(a,
-c);var e=(b?"s":'((l&&l.hasOwnProperty("'+a+'"))?l:s)')+"."+a;if(d||Pa(a))e="eso("+e+", fe)",k=!0;l+="if(s == null) return undefined;\ns="+e+";\n"});l+="return s;";a=new Function("s","l","eso","fe",l);a.toString=ea(l);k&&(a=yf(a,c));f=a}f.sharedGetter=!0;f.assign=function(a,c,d){return hb(a,d,b,c,b)};return e[b]=f}function gc(b){return G(b.valueOf)?b.valueOf():Cf.call(b)}function Oe(){var b=ia(),a=ia();this.$get=["$filter","$sniffer",function(c,d){function e(a){var b=a;a.sharedGetter&&(b=function(b,
-c){return a(b,c)},b.literal=a.literal,b.constant=a.constant,b.assign=a.assign);return b}function f(a,b){for(var c=0,d=a.length;c<d;c++){var e=a[c];e.constant||(e.inputs?f(e.inputs,b):-1===b.indexOf(e)&&b.push(e))}return b}function g(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=gc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function h(a,b,c,d){var e=d.$$inputs||(d.$$inputs=f(d.inputs,[])),h;if(1===e.length){var k=g,e=e[0];return a.$watch(function(a){var b=e(a);g(b,k)||(h=d(a),k=b&&
-gc(b));return h},b,c)}for(var l=[],q=0,p=e.length;q<p;q++)l[q]=g;return a.$watch(function(a){for(var b=!1,c=0,f=e.length;c<f;c++){var k=e[c](a);if(b||(b=!g(k,l[c])))l[c]=k&&gc(k)}b&&(h=d(a));return h},b,c)}function l(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;G(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(function(){y(f)&&e()})},c)}function k(a,b,c,d){function e(a){var b=!0;r(a,function(a){y(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},
-function(a,c,d){g=a;G(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function n(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){G(b)&&b.apply(this,arguments);e()},c)}function p(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==k&&c!==l?function(c,d){var e=a(c,d);return b(e,c,d)}:function(c,d){var e=a(c,d),f=b(e,c,d);return y(e)?f:e};a.$$watchDelegate&&a.$$watchDelegate!==h?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=h,c.inputs=
-[a]);return c}var q={csp:d.csp,expensiveChecks:!1},u={csp:d.csp,expensiveChecks:!0};return function(d,f,g){var m,r,t;switch(typeof d){case "string":t=d=d.trim();var L=g?a:b;m=L[t];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(r=!0,d=d.substring(2)),g=g?u:q,m=new hc(g),m=(new ib(m,c,g)).parse(d),m.constant?m.$$watchDelegate=n:r?(m=e(m),m.$$watchDelegate=m.literal?k:l):m.inputs&&(m.$$watchDelegate=h),L[t]=m);return p(m,f);case "function":return p(d,f);default:return p(E,f)}}}]}function Qe(){this.$get=
-["$rootScope","$exceptionHandler",function(b,a){return fd(function(a){b.$evalAsync(a)},a)}]}function Re(){this.$get=["$browser","$exceptionHandler",function(b,a){return fd(function(a){b.defer(a)},a)}]}function fd(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending;
-c.processScheduled=!1;c.pending=t;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{G(b)?d.resolve(b(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),a(h)}}}))}function g(){this.promise=new d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=R("$q",TypeError);d.prototype={then:function(a,b,c){var d=new g;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&
-f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(J(b)||G(b))d=b&&b.then;G(d)?(this.promise.$$state.status=-1,d.call(b,e[0],e[1],this.notify)):(this.promise.$$state.value=
-b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),a(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b,e,f=0,g=d.length;f<g;f++){e=d[f][0];b=d[f][3];try{e.notify(G(b)?b(c):c)}catch(h){a(h)}}})}};var l=function(a,b){var c=new g;b?c.resolve(a):
-c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{G(c)&&(d=c())}catch(e){return l(e,!1)}return d&&G(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},p=function u(a){if(!G(a))throw h("norslvr",a);if(!(this instanceof u))return new u(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};p.defer=function(){return new g};p.reject=function(a){var b=new g;
-b.reject(a);return b.promise};p.when=n;p.all=function(a){var b=new g,c=0,d=H(a)?[]:{};r(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return p}function $e(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b=
-c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function Pe(){function b(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++ob;this.$$ChildScope=null}b.prototype=a;return b}var a=10,c=R("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length&&(a=b);return a};this.$get=["$injector","$exceptionHandler",
-"$parse","$browser",function(f,g,h,l){function k(a){a.currentScope.$$destroyed=!0}function n(){this.$id=++ob;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings=null}function p(a){if(v.$$phase)throw c("inprog",v.$$phase);v.$$phase=a}function q(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];
-while(a=a.$parent)}function u(){}function s(){for(;t.length;)try{t.shift()()}catch(a){g(a)}e=null}function M(){null===e&&(e=l.defer(function(){v.$apply(s)}))}n.prototype={constructor:n,$new:function(a,c){var d;c=c||this;a?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=this)&&d.$on("$destroy",k);return d},
-$watch:function(a,b,c){var e=h(a);if(e.$$watchDelegate)return e.$$watchDelegate(this,b,c,e);var f=this.$$watchers,g={fn:b,last:u,get:e,exp:a,eq:!!c};d=null;G(b)||(g.fn=E);f||(f=this.$$watchers=[]);f.unshift(g);return function(){Xa(f,g);d=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],
-function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});r(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!x(e)){if(J(e))if(Sa(e))for(f!==p&&(f=p,u=f.length=0,l++),a=e.length,u!==a&&(l++,f.length=u=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==n&&(f=n={},u=0,l++);a=0;for(b in e)e.hasOwnProperty(b)&&(a++,g=e[b],h=
-f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(u++,f[b]=g,l++));if(u>a)for(b in l++,f)e.hasOwnProperty(b)||(u--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1<b.length,l=0,q=h(a,c),p=[],n={},m=!0,u=0;return this.$watch(q,function(){m?(m=!1,b(e,e,d)):b(e,g,d);if(k)if(J(e))if(Sa(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)tc.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var b,f,h,k,q,n,r=a,t,O=[],M,y;p("$digest");l.$$checkUrlChange();
-this===v&&null!==e&&(l.defer.cancel(e),s());d=null;do{n=!1;for(t=this;m.length;){try{y=m.shift(),y.scope.$eval(y.expression,y.locals)}catch(w){g(w)}d=null}a:do{if(k=t.$$watchers)for(q=k.length;q--;)try{if(b=k[q])if((f=b.get(t))!==(h=b.last)&&!(b.eq?ha(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))n=!0,d=b,b.last=b.eq?Da(f,null):f,b.fn(f,h===u?f:h,t),5>r&&(M=4-r,O[M]||(O[M]=[]),O[M].push({msg:G(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===
-d){n=!1;break a}}catch(A){g(A)}if(!(k=t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&!(k=t.$$nextSibling);)t=t.$parent}while(t=k);if((n||m.length)&&!r--)throw v.$$phase=null,c("infdig",a,O);}while(n||m.length);for(v.$$phase=null;F.length;)try{F.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(this!==v){for(var b in this.$$listenerCount)q(this,this.$$listenerCount[b],b);a.$$childHead==this&&(a.$$childHead=
-this.$$nextSibling);a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=E;this.$on=this.$watch=this.$watchGroup=function(){return E};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=this.$$watchers=null}}},$eval:function(a,
-b){return h(a)(this,b)},$evalAsync:function(a,b){v.$$phase||m.length||l.defer(function(){m.length&&v.$digest()});m.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){F.push(a)},$apply:function(a){try{return p("$apply"),this.$eval(a)}catch(b){g(b)}finally{v.$$phase=null;try{v.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&t.push(b);M()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||
-(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,q(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=Ya([h],arguments,1),l,q;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(q=d.length;l<q;l++)if(d[l])try{d[l].apply(null,k)}catch(p){g(p)}else d.splice(l,1),l--,q--;if(f)return h.currentScope=
-null,h;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var f=Ya([e],arguments,1),h,l;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(l=d.length;h<l;h++)if(d[h])try{d[h].apply(null,f)}catch(k){g(k)}else d.splice(h,1),h--,l--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=
-c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var v=new n,m=v.$$asyncQueue=[],F=v.$$postDigestQueue=[],t=v.$$applyAsyncQueue=[];return v}]}function Sd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return y(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return y(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;f=Aa(c).href;return""===f||f.match(e)?c:"unsafe:"+
-f}}}function Df(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw Ba("iwcard",b);b=gd(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(Ua(b))return new RegExp("^"+b.source+"$");throw Ba("imatcher");}function hd(b){var a=[];y(b)&&r(b,function(b){a.push(Df(b))});return a}function Te(){this.SCE_CONTEXTS=pa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=hd(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&
-(a=hd(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?$c(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw Ba("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[pa.HTML]=e(g);h[pa.CSS]=e(g);h[pa.URL]=
-e(g);h[pa.JS]=e(g);h[pa.RESOURCE_URL]=e(h[pa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ba("icontext",a,b);if(null===b||b===t||""===b)return b;if("string"!==typeof b)throw Ba("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===t||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(c===pa.RESOURCE_URL){var g=Aa(e.toString()),p,q,u=!1;p=0;for(q=b.length;p<q;p++)if(d(b[p],g)){u=!0;break}if(u)for(p=
-0,q=a.length;p<q;p++)if(d(a[p],g)){u=!1;break}if(u)return e;throw Ba("insecurl",e.toString());}if(c===pa.HTML)return f(e);throw Ba("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function Se(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&&8>Qa)throw Ba("iequirks");var d=sa(pa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf=c.valueOf;b||(d.trustAs=
-d.getTrusted=function(a,b){return b},d.valueOf=ra);d.parseAs=function(b,c){var e=a(c);return e.literal&&e.constant?e:a(c,function(a){return d.getTrusted(b,a)})};var e=d.parseAs,f=d.getTrusted,g=d.trustAs;r(pa,function(a,b){var c=z(b);d[db("parse_as_"+c)]=function(b){return e(a,b)};d[db("get_trusted_"+c)]=function(b){return f(a,b)};d[db("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function Ue(){this.$get=["$window","$document",function(b,a){var c={},d=aa((/android (\d+)/.exec(z((b.navigator||
-{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var p in l)if(k=h.exec(p)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||g+"Transition"in l);n=!!("animation"in l||g+"Animation"in l);!d||k&&n||(k=C(f.body.style.webkitTransition),n=C(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"===
-a&&11>=Qa)return!1;if(x(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:bb(),vendorPrefix:g,transitions:k,animations:n,android:d}}]}function We(){this.$get=["$templateCache","$http","$q",function(b,a,c){function d(e,f){d.totalPendingRequests++;var g=a.defaults&&a.defaults.transformResponse;H(g)?g=g.filter(function(a){return a!==Zb}):g===Zb&&(g=null);return a.get(e,{cache:b,transformResponse:g})["finally"](function(){d.totalPendingRequests--}).then(function(a){return a.data},
-function(a){if(!f)throw la("tpload",e);return c.reject(a)})}d.totalPendingRequests=0;return d}]}function Xe(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var d=ca.element(a).data("$binding");d&&r(d,function(d){c?(new RegExp("(^|\\s)"+gd(b)+"(\\s|\\||$)")).test(d)&&g.push(a):-1!=d.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,c){for(var g=["ng-","data-ng-","ng\\:"],
-h=0;h<g.length;++h){var l=a.querySelectorAll("["+g[h]+"model"+(c?"=":"*=")+'"'+b+'"]');if(l.length)return l}},getLocation:function(){return c.url()},setLocation:function(a){a!==c.url()&&(c.url(a),b.$digest())},whenStable:function(b){a.notifyWhenNoOutstandingRequests(b)}}}]}function Ye(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){var n=y(k)&&!k,p=(n?d:c).defer(),q=p.promise;l=a.defer(function(){try{p.resolve(f())}catch(a){p.reject(a),e(a)}finally{delete g[q.$$timeoutId]}n||
-b.$apply()},l);q.$$timeoutId=l;g[l]=p;return q}var g={};f.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return f}]}function Aa(b){Qa&&($.setAttribute("href",b),b=$.href);$.setAttribute("href",b);return{href:$.href,protocol:$.protocol?$.protocol.replace(/:$/,""):"",host:$.host,search:$.search?$.search.replace(/^\?/,""):"",hash:$.hash?$.hash.replace(/^#/,""):"",hostname:$.hostname,port:$.port,pathname:"/"===
-$.pathname.charAt(0)?$.pathname:"/"+$.pathname}}function $c(b){b=C(b)?Aa(b):b;return b.protocol===id.protocol&&b.host===id.host}function Ze(){this.$get=ea(Q)}function Fc(b){function a(c,d){if(J(c)){var e={};r(c,function(b,c){e[c]=a(c,b)});return e}return b.factory(c+"Filter",d)}this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];a("currency",jd);a("date",kd);a("filter",Ef);a("json",Ff);a("limitTo",Gf);a("lowercase",Hf);a("number",ld);a("orderBy",md);a("uppercase",
-If)}function Ef(){return function(b,a,c){if(!H(b))return b;var d;switch(typeof a){case "function":break;case "boolean":case "number":case "string":d=!0;case "object":a=Jf(a,c,d);break;default:return b}return b.filter(a)}}function Jf(b,a,c){var d=J(b)&&"$"in b;!0===a?a=ha:G(a)||(a=function(a,b){if(J(a)||J(b))return!1;a=z(""+a);b=z(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!J(e)?Ha(e,b.$,a,!1):Ha(e,b,a,c)}}function Ha(b,a,c,d,e){var f=null!==b?typeof b:"null",g=null!==a?typeof a:
-"null";if("string"===g&&"!"===a.charAt(0))return!Ha(b,a.substring(1),c,d);if(H(b))return b.some(function(b){return Ha(b,a,c,d)});switch(f){case "object":var h;if(d){for(h in b)if("$"!==h.charAt(0)&&Ha(b[h],a,c,!0))return!0;return e?!1:Ha(b,a,c,!1)}if("object"===g){for(h in a)if(e=a[h],!G(e)&&!x(e)&&(f="$"===h,!Ha(f?b:b[h],e,c,f,f)))return!1;return!0}return c(b,a);case "function":return!1;default:return c(b,a)}}function jd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){x(d)&&(d=a.CURRENCY_SYM);x(e)&&
-(e=a.PATTERNS[1].maxFrac);return null==b?b:nd(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,e).replace(/\u00A4/g,d)}}function ld(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==b?b:nd(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function nd(b,a,c,d,e){if(!isFinite(b)||J(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",l=[],k=!1;if(-1!==g.indexOf("e")){var n=g.match(/([\d\.]+)e(-?)(\d+)/);n&&"-"==n[2]&&n[3]>e+1?b=0:(h=g,k=!0)}if(k)0<e&&1>b&&(h=b.toFixed(e),b=parseFloat(h));else{g=(g.split(od)[1]||
-"").length;x(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);var g=(""+b).split(od),k=g[0],g=g[1]||"",p=0,q=a.lgSize,u=a.gSize;if(k.length>=q+u)for(p=k.length-q,n=0;n<p;n++)0===(p-n)%u&&0!==n&&(h+=c),h+=k.charAt(n);for(n=p;n<k.length;n++)0===(k.length-n)%q&&0!==n&&(h+=c),h+=k.charAt(n);for(;g.length<e;)g+="0";e&&"0"!==e&&(h+=d+g.substr(0,e))}0===b&&(f=!1);l.push(f?a.negPre:a.posPre,h,f?a.negSuf:a.posSuf);return l.join("")}function Ib(b,a,
-c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function U(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Ib(e,a,d)}}function Jb(b,a){return function(c,d){var e=c["get"+b](),f=ub(a?"SHORT"+b:b);return d[f][e]}}function pd(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function qd(b){return function(a){var c=pd(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+
-(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Ib(a,b)}}function ic(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function kd(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=aa(b[9]+b[10]),g=aa(b[9]+b[11]));h.call(a,aa(b[1]),aa(b[2])-1,aa(b[3]));f=aa(b[4]||0)-f;g=aa(b[5]||0)-g;h=aa(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
-return function(c,e,f){var g="",h=[],l,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;C(c)&&(c=Kf.test(c)?aa(c):a(c));Y(c)&&(c=new Date(c));if(!ga(c))return c;for(;e;)(k=Lf.exec(e))?(h=Ya(h,k,1),e=h.pop()):(h.push(e),e=null);f&&"UTC"===f&&(c=new Date(c.getTime()),c.setMinutes(c.getMinutes()+c.getTimezoneOffset()));r(h,function(a){l=Mf[a];g+=l?l(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Ff(){return function(b,a){x(a)&&(a=2);return $a(b,a)}}function Gf(){return function(b,
-a){Y(b)&&(b=b.toString());return H(b)||C(b)?(a=Infinity===Math.abs(Number(a))?Number(a):aa(a))?0<a?b.slice(0,a):b.slice(a):C(b)?"":[]:b}}function md(b){return function(a,c,d){function e(a,b){return b?function(b,c){return a(c,b)}:a}function f(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}function g(a){return null===a?"null":"function"===typeof a.valueOf&&(a=a.valueOf(),f(a))||"function"===typeof a.toString&&(a=a.toString(),f(a))?a:""}function h(a,b){var c=
-typeof a,d=typeof b;c===d&&"object"===c&&(a=g(a),b=g(b));return c===d?("string"===c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!Sa(a))return a;c=H(c)?c:[c];0===c.length&&(c=["+"]);c=c.map(function(a){var c=!1,d=a||ra;if(C(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);if(""===a)return e(h,c);d=b(a);if(d.constant){var f=d();return e(function(a,b){return h(a[f],b[f])},c)}}return e(function(a,b){return h(d(a),d(b))},c)});return Za.call(a).sort(e(function(a,
-b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function Ia(b){G(b)&&(b={link:b});b.restrict=b.restrict||"AC";return ea(b)}function rd(b,a,c,d,e){var f=this,g=[],h=f.$$parentForm=b.parent().controller("form")||Kb;f.$error={};f.$$success={};f.$pending=t;f.$name=e(a.name||a.ngForm||"")(c);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;h.$addControl(f);f.$rollbackViewValue=function(){r(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){r(g,
-function(a){a.$commitViewValue()})};f.$addControl=function(a){La(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a)};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];r(f.$pending,function(b,c){f.$setValidity(c,null,a)});r(f.$error,function(b,c){f.$setValidity(c,null,a)});r(f.$$success,function(b,c){f.$setValidity(c,null,a)});Xa(g,a)};sd({ctrl:this,$element:b,set:function(a,b,c){var d=a[b];
-d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Xa(d,c),0===d.length&&delete a[b])},parentForm:h,$animate:d});f.$setDirty=function(){d.removeClass(b,Ra);d.addClass(b,Lb);f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.setClass(b,Ra,Lb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;r(g,function(a){a.$setPristine()})};f.$setUntouched=function(){r(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,"ng-submitted");
-f.$submitted=!0;h.$setSubmitted()}}function jc(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function jb(b,a,c,d,e,f){var g=z(a[0].type);if(!e.android){var h=!1;a.on("compositionstart",function(a){h=!0});a.on("compositionend",function(){h=!1;l()})}var l=function(b){k&&(f.defer.cancel(k),k=null);if(!h){var e=a.val();b=b&&b.type;"password"===g||c.ngTrim&&"false"===c.ngTrim||(e=N(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};if(e.hasEvent("input"))a.on("input",
-l);else{var k,n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)}}function Mb(b,a){return function(c,d){var e,f;if(ga(c))return c;if(C(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));if(Nf.test(c))return new Date(c);b.lastIndex=
-0;if(e=b.exec(c))return e.shift(),f=d?{yyyy:d.getFullYear(),MM:d.getMonth()+1,dd:d.getDate(),HH:d.getHours(),mm:d.getMinutes(),ss:d.getSeconds(),sss:d.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},r(e,function(b,c){c<a.length&&(f[a[c]]=+b)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(b,a,c,d){return function(e,f,g,h,l,k,n){function p(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function q(a){return y(a)?ga(a)?a:c(a):t}td(e,f,g,h);
-jb(e,f,g,h,l,k);var u=h&&h.$options&&h.$options.timezone,s;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=c(b,s),"UTC"===u&&b.setMinutes(b.getMinutes()-b.getTimezoneOffset()),b):t});h.$formatters.push(function(a){if(a&&!ga(a))throw Nb("datefmt",a);if(p(a)){if((s=a)&&"UTC"===u){var b=6E4*s.getTimezoneOffset();s=new Date(s.getTime()+b)}return n("date")(a,d,u)}s=null;return""});if(y(g.min)||g.ngMin){var r;h.$validators.min=function(a){return!p(a)||x(r)||c(a)>=r};
-g.$observe("min",function(a){r=q(a);h.$validate()})}if(y(g.max)||g.ngMax){var v;h.$validators.max=function(a){return!p(a)||x(v)||c(a)<=v};g.$observe("max",function(a){v=q(a);h.$validate()})}}}function td(b,a,c,d){(d.$$hasNativeValidators=J(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?t:b})}function ud(b,a,c,d,e){if(y(d)){b=b(d);if(!b.constant)throw R("ngModel")("constexpr",c,d);return b(a)}return e}function kc(b,a){b="ngClass"+b;return["$animate",
-function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){if(!H(a)){if(C(a))return a.split(" ");if(J(a)){var b=[];r(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function l(a,b){var c=g.data("$classCounts")||{},d=[];r(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===
-a||f.$index%2===a){var k=e(b||[]);if(!n){var u=l(k,1);h.$addClass(u)}else if(!ha(b,n)){var s=e(n),u=d(k,s),k=d(s,k),u=l(u,1),k=l(k,-1);u&&u.length&&c.addClass(g,u);k&&k.length&&c.removeClass(g,k)}}n=sa(b)}var n;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=l(k,1),h.$addClass(g)):(g=l(k,-1),h.$removeClass(g))}})}}}]}function sd(b){function a(a,b){b&&!f[a]?(k.addClass(e,a),
-f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function c(b,c){b=b?"-"+vc(b,"-"):"";a(lb+b,!0===c);a(vd+b,!1===c)}var d=b.ctrl,e=b.$element,f={},g=b.set,h=b.unset,l=b.parentForm,k=b.$animate;f[vd]=!(f[lb]=e.hasClass(lb));d.$setValidity=function(b,e,f){e===t?(d.$pending||(d.$pending={}),g(d.$pending,b,f)):(d.$pending&&h(d.$pending,b,f),wd(d.$pending)&&(d.$pending=t));Wa(e)?e?(h(d.$error,b,f),g(d.$$success,b,f)):(g(d.$error,b,f),h(d.$$success,b,f)):(h(d.$error,b,f),h(d.$$success,b,f));d.$pending?(a(xd,
-!0),d.$valid=d.$invalid=t,c("",null)):(a(xd,!1),d.$valid=wd(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?t:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);l.$setValidity(b,e,d)}}function wd(b){if(b)for(var a in b)return!1;return!0}var Of=/^\/(.+)\/([a-z]*)$/,z=function(b){return C(b)?b.toLowerCase():b},tc=Object.prototype.hasOwnProperty,ub=function(b){return C(b)?b.toUpperCase():b},Qa,A,ta,Za=[].slice,qf=[].splice,Pf=[].push,Ca=Object.prototype.toString,Ja=R("ng"),ca=Q.angular||
-(Q.angular={}),cb,ob=0;Qa=W.documentMode;E.$inject=[];ra.$inject=[];var H=Array.isArray,N=function(b){return C(b)?b.trim():b},gd=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},bb=function(){if(y(bb.isActive_))return bb.isActive_;var b=!(!W.querySelector("[ng-csp]")&&!W.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return bb.isActive_=b},rb=["ng-","data-ng-","ng:","x-ng-"],Md=/[A-Z]/g,wc=!1,Qb,qa=1,pb=3,Qd={full:"1.3.15",major:1,
-minor:3,dot:15,codeName:"locality-filtration"};T.expando="ng339";var zb=T.cache={},hf=1;T._data=function(b){return this.cache[b[this.expando]]||{}};var cf=/([\:\-\_]+(.))/g,df=/^moz([A-Z])/,Qf={mouseleave:"mouseout",mouseenter:"mouseover"},Tb=R("jqLite"),gf=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Sb=/<|&#?\w+;/,ef=/<([\w:]+)/,ff=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>",
-"</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ja.optgroup=ja.option;ja.tbody=ja.tfoot=ja.colgroup=ja.caption=ja.thead;ja.th=ja.td;var Ka=T.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===W.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),T(Q).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=
-b?A(this[b]):A(this[this.length+b])},length:0,push:Pf,sort:[].sort,splice:[].splice},Eb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){Eb[z(b)]=b});var Oc={};r("input select option textarea button form details".split(" "),function(b){Oc[b]=!0});var Pc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};r({data:Vb,removeData:xb},function(b,a){T[a]=b});r({data:Vb,inheritedData:Db,scope:function(b){return A.data(b,"$scope")||
-Db(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return A.data(b,"$isolateScope")||A.data(b,"$isolateScopeNoTemplate")},controller:Kc,injector:function(b){return Db(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ab,css:function(b,a,c){a=db(a);if(y(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=z(a);if(Eb[d])if(y(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||E).specified?
-d:t;else if(y(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(y(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(x(b)){var d=a.nodeType;return d===qa||d===pb?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(x(a)){if(b.multiple&&"select"===va(b)){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(x(a))return b.innerHTML;
-wb(b,!0);b.innerHTML=a},empty:Lc},function(b,a){T.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Lc&&(2==b.length&&b!==Ab&&b!==Kc?a:d)===t){if(J(a)){for(e=0;e<g;e++)if(b===Vb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;g=e===t?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});r({removeData:xb,on:function a(c,d,e,f){if(y(f))throw Tb("onargs");if(Gc(c)){var g=yb(c,!0);f=g.events;var h=g.handle;h||(h=
-g.handle=lf(c,f));for(var g=0<=d.indexOf(" ")?d.split(" "):[d],l=g.length;l--;){d=g[l];var k=f[d];k||(f[d]=[],"mouseenter"===d||"mouseleave"===d?a(c,Qf[d],function(a){var c=a.relatedTarget;c&&(c===this||this.contains(c))||h(a,d)}):"$destroy"!==d&&c.addEventListener(d,h,!1),k=f[d]);k.push(e)}}},off:Jc,one:function(a,c,d){a=A(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;wb(a);r(new T(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,
-a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){a.nodeType===qa&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(d===qa||11===d){c=new T(c);for(var d=0,e=c.length;d<e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===qa){var d=a.firstChild;r(new T(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=A(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},
-remove:Mc,detach:function(a){Mc(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new T(c);for(var f=0,g=c.length;f<g;f++){var h=c[f];e.insertBefore(h,d.nextSibling);d=h}},addClass:Cb,removeClass:Bb,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=d;x(f)&&(f=!Ab(a,c));(f?Cb:Bb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Ub,
-triggerHandler:function(a,c,d){var e,f,g=c.type||c,h=yb(a);if(h=(h=h&&h.events)&&h[g])e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:E,type:g,target:a},c.type&&(e=w(e,c)),c=sa(h),f=d?[e].concat(d):[e],r(c,function(c){e.isImmediatePropagationStopped()||c.apply(a,
-f)})}},function(a,c){T.prototype[c]=function(c,e,f){for(var g,h=0,l=this.length;h<l;h++)x(g)?(g=a(this[h],c,e,f),y(g)&&(g=A(g))):Ic(g,a(this[h],c,e,f));return y(g)?g:this};T.prototype.bind=T.prototype.on;T.prototype.unbind=T.prototype.off});eb.prototype={put:function(a,c){this[Ma(a,this.nextUid)]=c},get:function(a){return this[Ma(a,this.nextUid)]},remove:function(a){var c=this[a=Ma(a,this.nextUid)];delete this[a];return c}};var Rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Rf=/,/,Sf=/^\s*(_?)(\S+?)\1\s*$/,
-Qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Fa=R("$injector");ab.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=[];if(a.length){if(c)throw C(d)&&d||(d=a.name||mf(a)),Fa("strictdi",d);c=a.toString().replace(Qc,"");c=c.match(Rc);r(c[1].split(Rf),function(a){a.replace(Sf,function(a,c,d){e.push(d)})})}a.$inject=e}}else H(a)?(c=a.length-1,sb(a[c],"fn"),e=a.slice(0,c)):sb(a,"fn",!0);return e};var Tf=R("$animate"),Ce=["$provide",function(a){this.$$selectors={};this.register=function(c,
-d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Tf("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$$q","$$asyncCallback","$rootScope",function(a,d,e){function f(d){var f,g=a.defer();g.promise.$$cancelFn=function(){f&&f()};e.$$postDigest(function(){f=d(function(){g.resolve()})});return g.promise}function g(a,c){var d=[],e=[],f=ia();
-r((a.attr("class")||"").split(/\s+/),function(a){f[a]=!0});r(c,function(a,c){var g=f[c];!1===a&&g?e.push(c):!0!==a||g||d.push(c)});return 0<d.length+e.length&&[d.length?d:null,e.length?e:null]}function h(a,c,d){for(var e=0,f=c.length;e<f;++e)a[c[e]]=d}function l(){n||(n=a.defer(),d(function(){n.resolve();n=null}));return n.promise}function k(a,c){if(ca.isObject(c)){var d=w(c.from||{},c.to||{});a.css(d)}}var n;return{animate:function(a,c,d){k(a,{from:c,to:d});return l()},enter:function(a,c,d,e){k(a,
-e);d?d.after(a):c.prepend(a);return l()},leave:function(a,c){k(a,c);a.remove();return l()},move:function(a,c,d,e){return this.enter(a,c,d,e)},addClass:function(a,c,d){return this.setClass(a,c,[],d)},$$addClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Cb(a,c)});k(a,d);return l()},removeClass:function(a,c,d){return this.setClass(a,[],c,d)},$$removeClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Bb(a,c)});k(a,d);return l()},setClass:function(a,
-c,d,e){var k=this,l=!1;a=A(a);var m=a.data("$$animateClasses");m?e&&m.options&&(m.options=ca.extend(m.options||{},e)):(m={classes:{},options:e},l=!0);e=m.classes;c=H(c)?c:c.split(" ");d=H(d)?d:d.split(" ");h(e,c,!0);h(e,d,!1);l&&(m.promise=f(function(c){var d=a.data("$$animateClasses");a.removeData("$$animateClasses");if(d){var e=g(a,d.classes);e&&k.$$setClassImmediately(a,e[0],e[1],d.options)}c()}),a.data("$$animateClasses",m));return m.promise},$$setClassImmediately:function(a,c,d,e){c&&this.$$addClassImmediately(a,
-c);d&&this.$$removeClassImmediately(a,d);k(a,e);return l()},enabled:E,cancel:E}}]}],la=R("$compile");yc.$inject=["$provide","$$sanitizeUriProvider"];var Sc=/^((?:x|data)[\:\-_])/i,rf=R("$controller"),Wc="applications/json",$b={"Content-Type":Wc+";charset=utf-8"},tf=/^\[|^\{(?!\{)/,uf={"[":/]$/,"{":/}$/},sf=/^\)\]\}',?\n/,ac=R("$interpolate"),Uf=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,xf={http:80,https:443,ftp:21},Gb=R("$location"),Vf={$$html5:!1,$$replace:!1,absUrl:Hb("$$absUrl"),url:function(a){if(x(a))return this.$$url;
-var c=Uf.exec(a);(c[1]||""===a)&&this.path(decodeURIComponent(c[1]));(c[2]||c[1]||""===a)&&this.search(c[3]||"");this.hash(c[5]||"");return this},protocol:Hb("$$protocol"),host:Hb("$$host"),port:Hb("$$port"),path:dd("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(C(a)||Y(a))a=a.toString(),this.$$search=sc(a);else if(J(a))a=Da(a,{}),r(a,function(c,e){null==c&&delete a[e]}),this.$$search=
-a;else throw Gb("isrcharg");break;default:x(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:dd("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};r([cd,ec,dc],function(a){a.prototype=Object.create(Vf);a.prototype.state=function(c){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Gb("nostate");this.$$state=x(c)?null:c;return this}});var na=R("$parse"),Wf=Function.prototype.call,
-Xf=Function.prototype.apply,Yf=Function.prototype.bind,mb=ia();r({"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:function(){}},function(a,c){a.constant=a.literal=a.sharedGetter=!0;mb[c]=a});mb["this"]=function(a){return a};mb["this"].sharedGetter=!0;var nb=w(ia(),{"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return y(d)?y(e)?d+e:d:y(e)?e:t},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(y(d)?d:0)-(y(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},
-"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},
-"||":function(a,c,d,e){return d(a,c)||e(a,c)},"!":function(a,c,d){return!d(a,c)},"=":!0,"|":!0}),Zf={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},hc=function(a){this.options=a};hc.prototype={constructor:hc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a,
-"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;else{var c=a+this.peek(),d=c+this.peek(2),e=nb[c],f=nb[d];nb[a]||e||f?(a=f?d:e?c:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,c){return-1!==c.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},
-isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=y(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw na("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<
-this.text.length;){var d=z(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:c,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var c=
-this.text.charAt(this.index);if(!this.isIdent(c)&&!this.isNumber(c))break;this.index++}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):
-d+=Zf[g]||g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,constant:!0,value:d});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var ib=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};ib.ZERO=w(function(){return 0},{sharedGetter:!0,constant:!0});ib.prototype={constructor:ib,parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);
-a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.peek().identifier&&this.peek().text in mb?a=mb[this.consume().text]:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var c,d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,
-d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw na("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw na("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){return this.peekAhead(0,a,c,d,e)},peekAhead:function(a,c,d,e,f){if(this.tokens.length>a){a=this.tokens[a];var g=a.text;if(g===c||g===d||g===e||g===
-f||!(c||d||e||f))return a}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},consume:function(a){if(0===this.tokens.length)throw na("ueoe",this.text);var c=this.expect(a);c||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return c},unaryFn:function(a,c){var d=nb[a];return w(function(a,f){return d(a,f,c)},{constant:c.constant,inputs:[c]})},binaryFn:function(a,c,d,e){var f=nb[c];return w(function(c,e){return f(c,e,a,d)},{constant:a.constant&&
-d.constant,inputs:!e&&[a,d]})},identifier:function(){for(var a=this.consume().text;this.peek(".")&&this.peekAhead(1).identifier&&!this.peekAhead(2,"(");)a+=this.consume().text+this.consume().text;return zf(a,this.options,this.text)},constant:function(){var a=this.consume().value;return w(function(){return a},{constant:!0,literal:!0})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,
-d){for(var e,f=0,g=a.length;f<g;f++)e=a[f](c,d);return e}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},filter:function(a){var c=this.$filter(this.consume().text),d,e;if(this.peek(":"))for(d=[],e=[];this.expect(":");)d.push(this.expression());var f=[a].concat(d||[]);return w(function(f,h){var l=a(f,h);if(e){e[0]=l;for(l=d.length;l--;)e[l+1]=d[l](f,h);return c.apply(t,e)}return c(l)},{constant:!c.$stateful&&f.every(fc),inputs:!c.$stateful&&f})},expression:function(){return this.assignment()},
-assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),w(function(d,f){return a.assign(d,c(d,f),f)},{inputs:[a,c]})):a},ternary:function(){var a=this.logicalOR(),c;if(this.expect("?")&&(c=this.assignment(),this.consume(":"))){var d=this.assignment();return w(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})}return a},
-logicalOR:function(){for(var a=this.logicalAND(),c;c=this.expect("||");)a=this.binaryFn(a,c.text,this.logicalAND(),!0);return a},logicalAND:function(){for(var a=this.equality(),c;c=this.expect("&&");)a=this.binaryFn(a,c.text,this.equality(),!0);return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a=this.binaryFn(a,c.text,this.relational());return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a=this.binaryFn(a,c.text,
-this.additive());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.text,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.text,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(ib.ZERO,a.text,this.unary()):(a=this.expect("!"))?this.unaryFn(a.text,this.unary()):this.primary()},fieldAccess:function(a){var c=
-this.identifier();return w(function(d,e,f){d=f||a(d,e);return null==d?t:c(d)},{assign:function(d,e,f){var g=a(d,f);g||a.assign(d,g={},f);return c.assign(g,e)}})},objectIndex:function(a){var c=this.text,d=this.expression();this.consume("]");return w(function(e,f){var g=a(e,f),h=d(e,f);ua(h,c);return g?oa(g[h],c):t},{assign:function(e,f,g){var h=ua(d(e,g),c),l=oa(a(e,g),c);l||a.assign(e,l={},g);return l[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());
-while(this.expect(","))}this.consume(")");var e=this.text,f=d.length?[]:null;return function(g,h){var l=c?c(g,h):y(c)?t:g,k=a(g,h,l)||E;if(f)for(var n=d.length;n--;)f[n]=oa(d[n](g,h),e);oa(l,e);if(k){if(k.constructor===k)throw na("isecfn",e);if(k===Wf||k===Xf||k===Yf)throw na("isecff",e);}l=k.apply?k.apply(l,f):k(f[0],f[1],f[2],f[3],f[4]);f&&(f.length=0);return oa(l,e)}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))
-}this.consume("]");return w(function(c,d){for(var e=[],f=0,g=a.length;f<g;f++)e.push(a[f](c,d));return e},{literal:!0,constant:a.every(fc),inputs:a})},object:function(){var a=[],c=[];if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.consume();d.constant?a.push(d.value):d.identifier?a.push(d.text):this.throwError("invalid key",d);this.consume(":");c.push(this.expression())}while(this.expect(","))}this.consume("}");return w(function(d,f){for(var g={},h=0,l=c.length;h<l;h++)g[a[h]]=
-c[h](d,f);return g},{literal:!0,constant:c.every(fc),inputs:c})}};var Bf=ia(),Af=ia(),Cf=Object.prototype.valueOf,Ba=R("$sce"),pa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},la=R("$compile"),$=W.createElement("a"),id=Aa(Q.location.href);Fc.$inject=["$provide"];jd.$inject=["$locale"];ld.$inject=["$locale"];var od=".",Mf={yyyy:U("FullYear",4),yy:U("FullYear",2,0,!0),y:U("FullYear",1),MMMM:Jb("Month"),MMM:Jb("Month",!0),MM:U("Month",2,1),M:U("Month",1,1),dd:U("Date",2),d:U("Date",
-1),HH:U("Hours",2),H:U("Hours",1),hh:U("Hours",2,-12),h:U("Hours",1,-12),mm:U("Minutes",2),m:U("Minutes",1),ss:U("Seconds",2),s:U("Seconds",1),sss:U("Milliseconds",3),EEEE:Jb("Day"),EEE:Jb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:qd(2),w:qd(1),G:ic,GG:ic,GGG:ic,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},Lf=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
-Kf=/^\-?\d+$/;kd.$inject=["$locale"];var Hf=ea(z),If=ea(ub);md.$inject=["$parse"];var Td=ea({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"===Ca.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),vb={};r(Eb,function(a,c){if("multiple"!=a){var d=xa("ng-"+c);vb[d]=function(){return{restrict:"A",priority:100,link:function(a,f,g){a.$watch(g[d],
-function(a){g.$set(c,!!a)})}}}}});r(Pc,function(a,c){vb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(Of))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});r(["src","srcset","href"],function(a){var c=xa("ng-"+a);vb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===Ca.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",
-g=null);f.$observe(c,function(c){c?(f.$set(h,c),Qa&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var Kb={$addControl:E,$$renameControl:function(a,c){a.$name=c},$removeControl:E,$setValidity:E,$setDirty:E,$setPristine:E,$setSubmitted:E};rd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var yd=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:rd,compile:function(d,e){d.addClass(Ra).addClass(lb);var f=e.name?"name":a&&e.ngForm?"ngForm":
-!1;return{pre:function(a,d,e,k){if(!("action"in e)){var n=function(c){a.$apply(function(){k.$commitViewValue();k.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",n,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",n,!1)},0,!1)})}var p=k.$$parentForm;f&&(hb(a,null,k.$name,k,k.$name),e.$observe(f,function(c){k.$name!==c&&(hb(a,null,k.$name,t,k.$name),p.$$renameControl(k,c),hb(a,null,k.$name,k,k.$name))}));d.on("$destroy",function(){p.$removeControl(k);
-f&&hb(a,null,e[f],t,k.$name);w(k,Kb)})}}}}}]},Ud=yd(),ge=yd(!0),Nf=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,$f=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,ag=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,bg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,zd=/^(\d{4})-(\d{2})-(\d{2})$/,Ad=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,lc=/^(\d{4})-W(\d\d)$/,Bd=/^(\d{4})-(\d\d)$/,
-Cd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Dd={text:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e)},date:kb("date",zd,Mb(zd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Ad,Mb(Ad,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Cd,Mb(Cd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",lc,function(a,c){if(ga(a))return a;if(C(a)){lc.lastIndex=0;var d=lc.exec(a);if(d){var e=+d[1],f=+d[2],g=d=0,h=0,l=0,k=pd(e),f=7*(f-1);c&&(d=c.getHours(),g=
-c.getMinutes(),h=c.getSeconds(),l=c.getMilliseconds());return new Date(e,0,k.getDate()+f,d,g,h,l)}}return NaN},"yyyy-Www"),month:kb("month",Bd,Mb(Bd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){td(a,c,d,e);jb(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:bg.test(a)?parseFloat(a):t});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!Y(a))throw Nb("numfmt",a);a=a.toString()}return a});if(y(d.min)||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)||
-x(h)||a>=h};d.$observe("min",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));h=Y(a)&&!isNaN(a)?a:t;e.$validate()})}if(y(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||x(l)||a<=l};d.$observe("max",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));l=Y(a)&&!isNaN(a)?a:t;e.$validate()})}},url:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||$f.test(d)}},email:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e);
-e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||ag.test(d)}},radio:function(a,c,d,e){x(d.name)&&c.attr("name",++ob);c.on("click",function(a){c[0].checked&&e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,g,h,l){var k=ud(l,a,"ngTrueValue",d.ngTrueValue,!0),n=ud(l,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&&
-a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return!1===a};e.$formatters.push(function(a){return ha(a,k)});e.$parsers.push(function(a){return a?k:n})},hidden:E,button:E,submit:E,reset:E,file:E},zc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Dd[z(h.type)]||Dd.text)(f,g,h,l[0],c,a,d,e)}}}}],cg=/^(true|false|\d+)$/,ye=function(){return{restrict:"A",priority:100,compile:function(a,
-c){return cg.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},Zd=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=a===t?"":a})}}}}],ae=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate));
-c.$$addBindingInfo(f,d.expressions);f=f[0];g.$observe("ngBindTemplate",function(a){f.textContent=a===t?"":a})}}}}],$d=["$sce","$parse","$compile",function(a,c,d){return{restrict:"A",compile:function(e,f){var g=c(f.ngBindHtml),h=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(h,function(){e.html(a.getTrustedHtml(g(c))||"")})}}}}],xe=ea({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
-be=kc("",!0),de=kc("Odd",0),ce=kc("Even",1),ee=Ia({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),fe=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ec={},dg={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=xa("ng-"+a);Ec[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,g){var h=
-d(g[c],null,!0);return function(c,d){d.on(a,function(d){var f=function(){h(c,{$event:d})};dg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var ie=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,l,k;c.$watch(e.ngIf,function(c){c?l||g(function(c,f){l=f;c[c.length++]=W.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),h&&(k=
-tb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],je=["$templateRequest","$anchorScroll","$animate","$sce",function(a,c,d,e){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(f,g){var h=g.ngInclude||g.src,l=g.onload||"",k=g.autoscroll;return function(f,g,q,r,s){var t=0,v,m,F,w=function(){m&&(m.remove(),m=null);v&&(v.$destroy(),v=null);F&&(d.leave(F).then(function(){m=null}),m=F,F=null)};f.$watch(e.parseAsResourceUrl(h),function(e){var h=
-function(){!y(k)||k&&!f.$eval(k)||c()},m=++t;e?(a(e,!0).then(function(a){if(m===t){var c=f.$new();r.template=a;a=s(c,function(a){w();d.enter(a,null,g).then(h)});v=c;F=a;v.$emit("$includeContentLoaded",e);f.$eval(l)}},function(){m===t&&(w(),f.$emit("$includeContentError",e))}),f.$emit("$includeContentRequested",e)):(w(),r.template=null)})}}}}],Ae=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Hc(f.template,
-W).childNodes)(c,function(a){d.append(a)},{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ke=Ia({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),we=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",g="false"!==d.ngTrim,h=g?N(f):f;e.$parsers.push(function(a){if(!x(a)){var c=[];a&&r(a.split(h),function(a){a&&c.push(g?N(a):a)});return c}});e.$formatters.push(function(a){return H(a)?
-a.join(f):t});e.$isEmpty=function(a){return!a||!a.length}}}},lb="ng-valid",vd="ng-invalid",Ra="ng-pristine",Lb="ng-dirty",xd="ng-pending",Nb=new R("ngModel"),eg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=t;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;
-this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=t;this.$name=n(d.name||"",!1)(a);var p=f(d.ngModel),q=p.assign,u=p,s=q,M=null,v,m=this;this.$$setOptions=function(a){if((m.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");u=function(a){var d=p(a);G(d)&&(d=c(a));return d};s=function(a,c){G(p(a))?g(a,{$$$p:m.$modelValue}):q(a,m.$modelValue)}}else if(!p.assign)throw Nb("nonassign",d.ngModel,wa(e));
-};this.$render=E;this.$isEmpty=function(a){return x(a)||""===a||null===a||a!==a};var F=e.inheritedData("$formController")||Kb,w=0;sd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:F,$animate:g});this.$setPristine=function(){m.$dirty=!1;m.$pristine=!0;g.removeClass(e,Lb);g.addClass(e,Ra)};this.$setDirty=function(){m.$dirty=!0;m.$pristine=!1;g.removeClass(e,Ra);g.addClass(e,Lb);F.$setDirty()};this.$setUntouched=function(){m.$touched=!1;m.$untouched=!0;g.setClass(e,
-"ng-untouched","ng-touched")};this.$setTouched=function(){m.$touched=!0;m.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(M);m.$viewValue=m.$$lastCommittedViewValue;m.$render()};this.$validate=function(){if(!Y(m.$modelValue)||!isNaN(m.$modelValue)){var a=m.$$rawModelValue,c=m.$valid,d=m.$modelValue,e=m.$options&&m.$options.allowInvalid;m.$$runValidators(a,m.$$lastCommittedViewValue,function(f){e||c===f||(m.$modelValue=f?a:t,m.$modelValue!==d&&m.$$writeModelToScope())})}};
-this.$$runValidators=function(a,c,d){function e(){var d=!0;r(m.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(r(m.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;r(m.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!G(k.then))throw Nb("$asyncValidators",k);g(h,t);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},E):h(!0)}function g(a,c){l===w&&m.$setValidity(a,c)}function h(a){l===w&&d(a)}w++;var l=
-w;(function(){var a=m.$$parserName||"parse";if(v===t)g(a,null);else return v||(r(m.$validators,function(a,c){g(c,null)}),r(m.$asyncValidators,function(a,c){g(c,null)})),g(a,v),v;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=m.$viewValue;h.cancel(M);if(m.$$lastCommittedViewValue!==a||""===a&&m.$$hasNativeValidators)m.$$lastCommittedViewValue=a,m.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=m.$$lastCommittedViewValue;if(v=
-x(c)?t:!0)for(var d=0;d<m.$parsers.length;d++)if(c=m.$parsers[d](c),x(c)){v=!1;break}Y(m.$modelValue)&&isNaN(m.$modelValue)&&(m.$modelValue=u(a));var e=m.$modelValue,f=m.$options&&m.$options.allowInvalid;m.$$rawModelValue=c;f&&(m.$modelValue=c,m.$modelValue!==e&&m.$$writeModelToScope());m.$$runValidators(c,m.$$lastCommittedViewValue,function(a){f||(m.$modelValue=a?c:t,m.$modelValue!==e&&m.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,m.$modelValue);r(m.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};
-this.$setViewValue=function(a,c){m.$viewValue=a;m.$options&&!m.$options.updateOnDefault||m.$$debounceViewValueCommit(c)};this.$$debounceViewValueCommit=function(c){var d=0,e=m.$options;e&&y(e.debounce)&&(e=e.debounce,Y(e)?d=e:Y(e[c])?d=e[c]:Y(e["default"])&&(d=e["default"]));h.cancel(M);d?M=h(function(){m.$commitViewValue()},d):l.$$phase?m.$commitViewValue():a.$apply(function(){m.$commitViewValue()})};a.$watch(function(){var c=u(a);if(c!==m.$modelValue){m.$modelValue=m.$$rawModelValue=c;v=t;for(var d=
-m.$formatters,e=d.length,f=c;e--;)f=d[e](f);m.$viewValue!==f&&(m.$viewValue=m.$$lastCommittedViewValue=f,m.$render(),m.$$runValidators(c,f,E))}return c})}],ve=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:eg,priority:1,compile:function(c){c.addClass(Ra).addClass("ng-untouched").addClass(lb);return{pre:function(a,c,f,g){var h=g[0],l=g[1]||Kb;h.$$setOptions(g[2]&&g[2].$options);l.$addControl(h);f.$observe("name",function(a){h.$name!==a&&l.$$renameControl(h,
-a)});a.$on("$destroy",function(){l.$removeControl(h)})},post:function(c,e,f,g){var h=g[0];if(h.$options&&h.$options.updateOn)e.on(h.$options.updateOn,function(a){h.$$debounceViewValueCommit(a&&a.type)});e.on("blur",function(e){h.$touched||(a.$$phase?c.$evalAsync(h.$setTouched):c.$apply(h.$setTouched))})}}}}}],fg=/(\s+|^)default(\s+|$)/,ze=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=a.$eval(c.ngModelOptions);this.$options.updateOn!==t?(this.$options.updateOnDefault=
-!1,this.$options.updateOn=N(this.$options.updateOn.replace(fg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},le=Ia({terminal:!0,priority:1E3}),me=["$locale","$interpolate",function(a,c){var d=/{}/g,e=/^when(Minus)?(.+)$/;return{restrict:"EA",link:function(f,g,h){function l(a){g.text(a||"")}var k=h.count,n=h.$attr.when&&g.attr(h.$attr.when),p=h.offset||0,q=f.$eval(n)||{},u={},n=c.startSymbol(),s=c.endSymbol(),t=n+k+"-"+p+s,v=ca.noop,m;r(h,function(a,c){var d=
-e.exec(c);d&&(d=(d[1]?"-":"")+z(d[2]),q[d]=g.attr(h.$attr[c]))});r(q,function(a,e){u[e]=c(a.replace(d,t))});f.$watch(k,function(c){c=parseFloat(c);var d=isNaN(c);d||c in q||(c=a.pluralCat(c-p));c===m||d&&isNaN(m)||(v(),v=f.$watch(u[c],l),m=c)})}}}],ne=["$parse","$animate",function(a,c){var d=R("ngRepeat"),e=function(a,c,d,e,k,n,p){a[d]=e;k&&(a[k]=n);a.$index=c;a.$first=0===c;a.$last=c===p-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(c&1))};return{restrict:"A",multiElement:!0,transclude:"element",
-priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,l=W.createComment(" end ngRepeat: "+h+" "),k=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!k)throw d("iexp",h);var n=k[1],p=k[2],q=k[3],u=k[4],k=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",n);var s=k[3]||k[1],y=k[2];if(q&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(q)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(q)))throw d("badident",
-q);var v,m,w,x,E={$id:Ma};u?v=a(u):(w=function(a,c){return Ma(c)},x=function(a){return a});return function(a,f,g,k,n){v&&(m=function(c,d,e){y&&(E[y]=c);E[s]=d;E.$index=e;return v(a,E)});var u=ia();a.$watchCollection(p,function(g){var k,p,v=f[0],D,E=ia(),G,H,L,S,J,C,z;q&&(a[q]=g);if(Sa(g))J=g,p=m||w;else{p=m||x;J=[];for(z in g)g.hasOwnProperty(z)&&"$"!=z.charAt(0)&&J.push(z);J.sort()}G=J.length;z=Array(G);for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],S=p(H,L,k),u[S])C=u[S],delete u[S],E[S]=C,z[k]=C;else{if(E[S])throw r(z,
-function(a){a&&a.scope&&(u[a.id]=a)}),d("dupes",h,S,L);z[k]={id:S,scope:t,clone:t};E[S]=!0}for(D in u){C=u[D];S=tb(C.clone);c.leave(S);if(S[0].parentNode)for(k=0,p=S.length;k<p;k++)S[k].$$NG_REMOVED=!0;C.scope.$destroy()}for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],C=z[k],C.scope){D=v;do D=D.nextSibling;while(D&&D.$$NG_REMOVED);C.clone[0]!=D&&c.move(tb(C.clone),null,A(v));v=C.clone[C.clone.length-1];e(C.scope,k,s,L,y,H,G)}else n(function(a,d){C.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a,
-null,A(v));v=f;C.clone=a;E[C.id]=C;e(C.scope,k,s,L,y,H,G)});u=E})}}}}],oe=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngShow,function(c){a[c?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],he=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngHide,function(c){a[c?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],pe=Ia(function(a,c,d){a.$watchCollection(d.ngStyle,
-function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)})}),qe=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],l=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=l.length;d<e;++d)a.cancel(l[d]);d=l.length=0;for(e=k.length;d<e;++d){var s=tb(h[d].clone);k[d].$destroy();(l[d]=a.leave(s)).then(n(l,d))}h.length=0;k.length=0;(g=
-f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=W.createComment(" end ngSwitchWhen: ");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],re=Ia({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),se=Ia({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,
-link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),ue=Ia({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw R("ngTransclude")("orphan",wa(c));f(function(a){c.empty();c.append(a)})}}),Vd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],gg=R("ngOptions"),te=ea({restrict:"A",terminal:!0}),Wd=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
-e={$setViewValue:E};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var l=this,k={},n=e,p;l.databound=d.ngModel;l.init=function(a,c,d){n=a;p=d};l.addOption=function(c,d){La(c,'"option value"');k[c]=!0;n.$viewValue==c&&(a.val(c),p.parent()&&p.remove());d&&d[0].hasAttribute("selected")&&(d[0].selected=!0)};l.removeOption=function(a){this.hasOption(a)&&(delete k[a],n.$viewValue===a&&this.renderUnknownOption(a))};l.renderUnknownOption=function(c){c=
-"? "+Ma(c)+" ?";p.val(c);a.prepend(p);a.val(c);p.prop("selected",!0)};l.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){l.renderUnknownOption=E})}],link:function(e,g,h,l){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(C.parent()&&C.remove(),c.val(a),""===a&&v.prop("selected",!0)):x(a)&&v?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){C.parent()&&C.remove();d.$setViewValue(c.val())})})}function n(a,c,d){var e;
-d.$render=function(){var a=new eb(d.$viewValue);r(c.find("option"),function(c){c.selected=y(a.get(c.value))})};a.$watch(function(){ha(e,d.$viewValue)||(e=sa(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function p(e,f,g){function h(a,c,d){T[x]=d;G&&(T[G]=c);return a(e,T)}function k(a){var c;if(u)if(I&&H(a)){c=new eb([]);for(var d=0;d<a.length;d++)c.put(h(I,null,a[d]),!0)}else c=
-new eb(a);else I&&(a=h(I,null,a));return function(d,e){var f;f=I?I:B?B:z;return u?y(c.remove(h(f,d,e))):a===h(f,d,e)}}function l(){m||(e.$$postDigest(p),m=!0)}function n(a,c,d){a[c]=a[c]||0;a[c]+=d?1:-1}function p(){m=!1;var a={"":[]},c=[""],d,l,s,t,v;s=g.$viewValue;t=L(e)||[];var B=G?Object.keys(t).sort():t,x,A,H,z,O={};v=k(s);var N=!1,U,W;Q={};for(z=0;H=B.length,z<H;z++){x=z;if(G&&(x=B[z],"$"===x.charAt(0)))continue;A=t[x];d=h(J,x,A)||"";(l=a[d])||(l=a[d]=[],c.push(d));d=v(x,A);N=N||d;A=h(C,x,A);
-A=y(A)?A:"";W=I?I(e,T):G?B[z]:z;I&&(Q[W]=x);l.push({id:W,label:A,selected:d})}u||(w||null===s?a[""].unshift({id:"",label:"",selected:!N}):N||a[""].unshift({id:"?",label:"",selected:!0}));x=0;for(B=c.length;x<B;x++){d=c[x];l=a[d];R.length<=x?(s={element:E.clone().attr("label",d),label:l.label},t=[s],R.push(t),f.append(s.element)):(t=R[x],s=t[0],s.label!=d&&s.element.attr("label",s.label=d));N=null;z=0;for(H=l.length;z<H;z++)d=l[z],(v=t[z+1])?(N=v.element,v.label!==d.label&&(n(O,v.label,!1),n(O,d.label,
-!0),N.text(v.label=d.label),N.prop("label",v.label)),v.id!==d.id&&N.val(v.id=d.id),N[0].selected!==d.selected&&(N.prop("selected",v.selected=d.selected),Qa&&N.prop("selected",v.selected))):(""===d.id&&w?U=w:(U=F.clone()).val(d.id).prop("selected",d.selected).attr("selected",d.selected).prop("label",d.label).text(d.label),t.push(v={element:U,label:d.label,id:d.id,selected:d.selected}),n(O,d.label,!0),N?N.after(U):s.element.append(U),N=U);for(z++;t.length>z;)d=t.pop(),n(O,d.label,!1),d.element.remove()}for(;R.length>
-x;){l=R.pop();for(z=1;z<l.length;++z)n(O,l[z].label,!1);l[0].element.remove()}r(O,function(a,c){0<a?q.addOption(c):0>a&&q.removeOption(c)})}var v;if(!(v=s.match(d)))throw gg("iexp",s,wa(f));var C=c(v[2]||v[1]),x=v[4]||v[6],A=/ as /.test(v[0])&&v[1],B=A?c(A):null,G=v[5],J=c(v[3]||""),z=c(v[2]?v[1]:x),L=c(v[7]),I=v[8]?c(v[8]):null,Q={},R=[[{element:f,label:""}]],T={};w&&(a(w)(e),w.removeClass("ng-scope"),w.remove());f.empty();f.on("change",function(){e.$apply(function(){var a=L(e)||[],c;if(u)c=[],r(f.val(),
-function(d){d=I?Q[d]:d;c.push("?"===d?t:""===d?null:h(B?B:z,d,a[d]))});else{var d=I?Q[f.val()]:f.val();c="?"===d?t:""===d?null:h(B?B:z,d,a[d])}g.$setViewValue(c);p()})});g.$render=p;e.$watchCollection(L,l);e.$watchCollection(function(){var a=L(e),c;if(a&&H(a)){c=Array(a.length);for(var d=0,f=a.length;d<f;d++)c[d]=h(C,d,a[d])}else if(a)for(d in c={},a)a.hasOwnProperty(d)&&(c[d]=h(C,d,a[d]));return c},l);u&&e.$watchCollection(function(){return g.$modelValue},l)}if(l[1]){var q=l[0];l=l[1];var u=h.multiple,
-s=h.ngOptions,w=!1,v,m=!1,F=A(W.createElement("option")),E=A(W.createElement("optgroup")),C=F.clone();h=0;for(var B=g.children(),G=B.length;h<G;h++)if(""===B[h].value){v=w=B.eq(h);break}q.init(l,w,C);u&&(l.$isEmpty=function(a){return!a||0===a.length});s?p(e,g,l):u?n(e,g,l):k(e,g,l,q)}}}}],Yd=["$interpolate",function(a){var c={addOption:E,removeOption:E};return{restrict:"E",priority:100,compile:function(d,e){if(x(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=
-d.parent(),n=k.data("$selectController")||k.parent().data("$selectController");n&&n.databound||(n=c);f?a.$watch(f,function(a,c){e.$set("value",a);c!==a&&n.removeOption(c);n.addOption(a,d)}):n.addOption(e.value,d);d.on("$destroy",function(){n.removeOption(e.value)})}}}}],Xd=ea({restrict:"E",terminal:!1}),Bc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){e&&(d.required=!0,e.$validators.required=function(a,c){return!d.required||!e.$isEmpty(c)},d.$observe("required",function(){e.$validate()}))}}},
-Ac=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f,g=d.ngPattern||d.pattern;d.$observe("pattern",function(a){C(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw R("ngPattern")("noregexp",g,a,wa(c));f=a||t;e.$validate()});e.$validators.pattern=function(a){return e.$isEmpty(a)||x(f)||f.test(a)}}}}},Dc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=aa(a);f=isNaN(a)?-1:a;e.$validate()});
-e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Cc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=aa(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};Q.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(Nd(),Pd(ca),A(W).ready(function(){Jd(W,uc)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');
diff --git a/apps/static/js/jquery-2.1.1.js b/apps/static/js/jquery-2.1.1.js
deleted file mode 100644
index 01c360f4f..000000000
--- a/apps/static/js/jquery-2.1.1.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)
-},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
-},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"applications/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"applications/xml, text/xml",json:"applications/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, applications/javascript, applications/ecmascript, applications/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Gc=[],Hc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Gc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Hc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("applications/x-www-form-urlencoded")&&Hc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Hc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Gc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Ic=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Ic)return Ic.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Jc=a.document.documentElement;function Kc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Kc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Jc;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Jc})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Kc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Lc=a.jQuery,Mc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Mc),b&&a.jQuery===n&&(a.jQuery=Lc),n},typeof b===U&&(a.jQuery=a.$=n),n});
\ No newline at end of file
diff --git a/apps/static/js/jquery-ui-1.10.4.min.js b/apps/static/js/jquery-ui-1.10.4.min.js
deleted file mode 100644
index ef3be2396..000000000
--- a/apps/static/js/jquery-ui-1.10.4.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*! jQuery UI - v1.10.4 - 2014-04-02
-* http://jqueryui.com
-* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.slider.js, jquery.ui.sortable.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
-* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
-
-(function(e,t){function i(t,i){var s,a,o,r=t.nodeName.toLowerCase();return"area"===r?(s=t.parentNode,a=s.name,t.href&&a&&"map"===s.nodeName.toLowerCase()?(o=e("img[usemap=#"+a+"]")[0],!!o&&n(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||i:i)&&n(t)}function n(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var s=0,a=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,n){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),n&&n.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var n,s,a=e(this[0]);a.length&&a[0]!==document;){if(n=a.css("position"),("absolute"===n||"relative"===n||"fixed"===n)&&(s=parseInt(a.css("zIndex"),10),!isNaN(s)&&0!==s))return s;a=a.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++s)})},removeUniqueId:function(){return this.each(function(){a.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,n){return!!e.data(t,n[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var n=e.attr(t,"tabindex"),s=isNaN(n);return(s||n>=0)&&i(t,!s)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,n){function s(t,i,n,s){return e.each(a,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var a="Width"===n?["Left","Right"]:["Top","Bottom"],o=n.toLowerCase(),r={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+n]=function(i){return i===t?r["inner"+n].call(this):this.each(function(){e(this).css(o,s(this,i)+"px")})},e.fn["outer"+n]=function(t,i){return"number"!=typeof t?r["outer"+n].call(this,t):this.each(function(){e(this).css(o,s(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,n){var s,a=e.ui[t].prototype;for(s in n)a.plugins[s]=a.plugins[s]||[],a.plugins[s].push([i,n[s]])},call:function(e,t,i){var n,s=e.plugins[t];if(s&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(n=0;s.length>n;n++)e.options[s[n][0]]&&s[n][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var n=i&&"left"===i?"scrollLeft":"scrollTop",s=!1;return t[n]>0?!0:(t[n]=1,s=t[n]>0,t[n]=0,s)}})})(jQuery);(function(t,e){var i=0,s=Array.prototype.slice,n=t.cleanData;t.cleanData=function(e){for(var i,s=0;null!=(i=e[s]);s++)try{t(i).triggerHandler("remove")}catch(o){}n(e)},t.widget=function(i,s,n){var o,a,r,h,l={},c=i.split(".")[0];i=i.split(".")[1],o=c+"-"+i,n||(n=s,s=t.Widget),t.expr[":"][o.toLowerCase()]=function(e){return!!t.data(e,o)},t[c]=t[c]||{},a=t[c][i],r=t[c][i]=function(t,i){return this._createWidget?(arguments.length&&this._createWidget(t,i),e):new r(t,i)},t.extend(r,a,{version:n.version,_proto:t.extend({},n),_childConstructors:[]}),h=new s,h.options=t.widget.extend({},h.options),t.each(n,function(i,n){return t.isFunction(n)?(l[i]=function(){var t=function(){return s.prototype[i].apply(this,arguments)},e=function(t){return s.prototype[i].apply(this,t)};return function(){var i,s=this._super,o=this._superApply;return this._super=t,this._superApply=e,i=n.apply(this,arguments),this._super=s,this._superApply=o,i}}(),e):(l[i]=n,e)}),r.prototype=t.widget.extend(h,{widgetEventPrefix:a?h.widgetEventPrefix||i:i},l,{constructor:r,namespace:c,widgetName:i,widgetFullName:o}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,r,i._proto)}),delete a._childConstructors):s._childConstructors.push(r),t.widget.bridge(i,r)},t.widget.extend=function(i){for(var n,o,a=s.call(arguments,1),r=0,h=a.length;h>r;r++)for(n in a[r])o=a[r][n],a[r].hasOwnProperty(n)&&o!==e&&(i[n]=t.isPlainObject(o)?t.isPlainObject(i[n])?t.widget.extend({},i[n],o):t.widget.extend({},o):o);return i},t.widget.bridge=function(i,n){var o=n.prototype.widgetFullName||i;t.fn[i]=function(a){var r="string"==typeof a,h=s.call(arguments,1),l=this;return a=!r&&h.length?t.widget.extend.apply(null,[a].concat(h)):a,r?this.each(function(){var s,n=t.data(this,o);return n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(s=n[a].apply(n,h),s!==n&&s!==e?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):e):t.error("no such method '"+a+"' for "+i+" widget instance"):t.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var e=t.data(this,o);e?e.option(a||{})._init():t.data(this,o,new n(a,this))}),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this.bindings=t(),this.hoverable=t(),this.focusable=t(),s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(i,s){var n,o,a,r=i;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof i)if(r={},n=i.split("."),i=n.shift(),n.length){for(o=r[i]=t.widget.extend({},this.options[i]),a=0;n.length-1>a;a++)o[n[a]]=o[n[a]]||{},o=o[n[a]];if(i=n.pop(),1===arguments.length)return o[i]===e?null:o[i];o[i]=s}else{if(1===arguments.length)return this.options[i]===e?null:this.options[i];r[i]=s}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!e).attr("aria-disabled",e),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var o,a=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=o=t(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,o=this.widget()),t.each(n,function(n,r){function h(){return i||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof r?a[r]:r).apply(a,arguments):e}"string"!=typeof r&&(h.guid=r.guid=r.guid||h.guid||t.guid++);var l=n.match(/^(\w+)\s*(.*)$/),c=l[1]+a.eventNamespace,u=l[2];u?o.delegate(u,c,h):s.bind(c,h)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(e).undelegate(e)},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}})})(jQuery);(function(t){var e=!1;t(document).mouseup(function(){e=!1}),t.widget("ui.mouse",{version:"1.10.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!e){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?t(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===t.data(i.target,this.widgetName+".preventClickEvent")&&t.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return s._mouseMove(t)},this._mouseUpDelegate=function(t){return s._mouseUp(t)},t(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),e=!0,!0)):!0}},_mouseMove:function(e){return t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button?this._mouseUp(e):this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t,e){function i(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function s(e,i){return parseInt(t.css(e,i),10)||0}function n(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var a,o=Math.max,r=Math.abs,l=Math.round,h=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(a!==e)return a;var i,s,n=t("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),o=n.children()[0];return t("body").append(n),i=o.offsetWidth,n.css("overflow","scroll"),s=o.offsetWidth,i===s&&(s=n[0].clientWidth),n.remove(),a=i-s},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth,a="scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight;return{width:a?t.position.scrollbarWidth():0,height:n?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:n,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s?i.width():i.outerWidth(),height:s?i.height():i.outerHeight()}}},t.fn.position=function(e){if(!e||!e.of)return f.apply(this,arguments);e=t.extend({},e);var a,p,g,m,v,_,b=t(e.of),y=t.position.getWithinInfo(e.within),k=t.position.getScrollInfo(y),w=(e.collision||"flip").split(" "),D={};return _=n(b),b[0].preventDefault&&(e.at="left top"),p=_.width,g=_.height,m=_.offset,v=t.extend({},m),t.each(["my","at"],function(){var t,i,s=(e[this]||"").split(" ");1===s.length&&(s=h.test(s[0])?s.concat(["center"]):c.test(s[0])?["center"].concat(s):["center","center"]),s[0]=h.test(s[0])?s[0]:"center",s[1]=c.test(s[1])?s[1]:"center",t=u.exec(s[0]),i=u.exec(s[1]),D[this]=[t?t[0]:0,i?i[0]:0],e[this]=[d.exec(s[0])[0],d.exec(s[1])[0]]}),1===w.length&&(w[1]=w[0]),"right"===e.at[0]?v.left+=p:"center"===e.at[0]&&(v.left+=p/2),"bottom"===e.at[1]?v.top+=g:"center"===e.at[1]&&(v.top+=g/2),a=i(D.at,p,g),v.left+=a[0],v.top+=a[1],this.each(function(){var n,h,c=t(this),u=c.outerWidth(),d=c.outerHeight(),f=s(this,"marginLeft"),_=s(this,"marginTop"),x=u+f+s(this,"marginRight")+k.width,C=d+_+s(this,"marginBottom")+k.height,M=t.extend({},v),T=i(D.my,c.outerWidth(),c.outerHeight());"right"===e.my[0]?M.left-=u:"center"===e.my[0]&&(M.left-=u/2),"bottom"===e.my[1]?M.top-=d:"center"===e.my[1]&&(M.top-=d/2),M.left+=T[0],M.top+=T[1],t.support.offsetFractions||(M.left=l(M.left),M.top=l(M.top)),n={marginLeft:f,marginTop:_},t.each(["left","top"],function(i,s){t.ui.position[w[i]]&&t.ui.position[w[i]][s](M,{targetWidth:p,targetHeight:g,elemWidth:u,elemHeight:d,collisionPosition:n,collisionWidth:x,collisionHeight:C,offset:[a[0]+T[0],a[1]+T[1]],my:e.my,at:e.at,within:y,elem:c})}),e.using&&(h=function(t){var i=m.left-M.left,s=i+p-u,n=m.top-M.top,a=n+g-d,l={target:{element:b,left:m.left,top:m.top,width:p,height:g},element:{element:c,left:M.left,top:M.top,width:u,height:d},horizontal:0>s?"left":i>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};u>p&&p>r(i+s)&&(l.horizontal="center"),d>g&&g>r(n+a)&&(l.vertical="middle"),l.important=o(r(i),r(s))>o(r(n),r(a))?"horizontal":"vertical",e.using.call(this,t,l)}),c.offset(t.extend(M,{using:h}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-o-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-o-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-o-a,t.top+p+f+g>c&&(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,t.top+p+f+g>u&&(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,o=document.getElementsByTagName("body")[0],r=document.createElement("div");e=document.createElement(o?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(r),i=o||document.documentElement,i.insertBefore(e,i.firstChild),r.style.cssText="position: absolute; left: 10.7432222px;",n=t(r).offset().left,t.support.offsetFractions=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()})(jQuery);(function(e){var t=0,i={},a={};i.height=i.paddingTop=i.paddingBottom=i.borderTopWidth=i.borderBottomWidth="hide",a.height=a.paddingTop=a.paddingBottom=a.borderTopWidth=a.borderBottomWidth="show",e.widget("ui.accordion",{version:"1.10.4",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e(),content:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),this._destroyIcons(),e=this.headers.next().css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),undefined):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t),undefined)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,a=this.headers.length,s=this.headers.index(t.target),n=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:n=this.headers[(s+1)%a];break;case i.LEFT:case i.UP:n=this.headers[(s-1+a)%a];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:n=this.headers[0];break;case i.END:n=this.headers[a-1]}n&&(e(t.target).attr("tabIndex",-1),e(n).attr("tabIndex",0),n.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var i,a=this.options,s=a.heightStyle,n=this.element.parent(),r=this.accordionId="ui-accordion-"+(this.element.attr("id")||++t);this.active=this._findActive(a.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(t){var i=e(this),a=i.attr("id"),s=i.next(),n=s.attr("id");a||(a=r+"-header-"+t,i.attr("id",a)),n||(n=r+"-panel-"+t,s.attr("id",n)),i.attr("aria-controls",n),s.attr("aria-labelledby",a)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(a.event),"fill"===s?(i=n.height(),this.element.siblings(":visible").each(function(){var t=e(this),a=t.css("position");"absolute"!==a&&"fixed"!==a&&(i-=t.outerHeight(!0))}),this.headers.each(function(){i-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(i=0,this.headers.next().each(function(){i=Math.max(i,e(this).css("height","").height())}).height(i))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,a=this.active,s=e(t.currentTarget),n=s[0]===a[0],r=n&&i.collapsible,o=r?e():s.next(),h=a.next(),d={oldHeader:a,oldPanel:h,newHeader:r?e():s,newPanel:o};t.preventDefault(),n&&!i.collapsible||this._trigger("beforeActivate",t,d)===!1||(i.active=r?!1:this.headers.index(s),this.active=n?e():s,this._toggle(d),a.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&a.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),n||(s.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),s.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,a=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=a,this.options.animate?this._animate(i,a,t):(a.hide(),i.show(),this._toggleComplete(t)),a.attr({"aria-hidden":"true"}),a.prev().attr("aria-selected","false"),i.length&&a.length?a.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,s){var n,r,o,h=this,d=0,c=e.length&&(!t.length||e.index()<t.index()),l=this.options.animate||{},u=c&&l.down||l,v=function(){h._toggleComplete(s)};return"number"==typeof u&&(o=u),"string"==typeof u&&(r=u),r=r||u.easing||l.easing,o=o||u.duration||l.duration,t.length?e.length?(n=e.show().outerHeight(),t.animate(i,{duration:o,easing:r,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(a,{duration:o,easing:r,complete:v,step:function(e,i){i.now=Math.round(e),"height"!==i.prop?d+=i.now:"content"!==h.options.heightStyle&&(i.now=Math.round(n-t.outerHeight()-d),d=0)}}),undefined):t.animate(i,o,r,v):e.animate(a,o,r,v)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}})})(jQuery);(function(e){e.widget("ui.autocomplete",{version:"1.10.4",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,undefined;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:case a.NUMPAD_ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),undefined;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),undefined):(this._searchTimeout(e),undefined)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,undefined):(clearTimeout(this.searching),this.close(e),this._change(e),undefined)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().data("ui-menu"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){if(this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type)))return this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),undefined;var s=i.item.data("ui-autocomplete-item");!1!==this._trigger("focus",t,{item:s})?t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(s.value):this.liveRegion.text(s.value)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("<span>",{role:"status","aria-live":"polite"}).addClass("ui-helper-hidden-accessible").insertBefore(this.element),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){this.term!==this._value()&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):undefined},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var t=++this.requestIndex;return e.proxy(function(e){t===this.requestIndex&&this.__response(e),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},this)},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({label:t.label||t.value,value:t.value||t.label},t)})},_suggest:function(t){var i=this.menu.element.empty();this._renderMenu(i,t),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,i){var s=this;e.each(i,function(e,i){s._renderItemData(t,i)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,i){return e("<li>").append(e("<a>").text(i.label)).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this._value(this.term),this.menu.blur(),undefined):(this.menu[e](t),undefined):(this.search(null,t),undefined)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow role_keys to navigate."}}},__response:function(e){var t;this._superApply(arguments),this.options.disabled||this.cancelSearch||(t=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.text(t))}})})(jQuery);(function(e){var t,i="ui-button ui-widget ui-state-default ui-corner-all",n="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",s=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},a=function(t){var i=t.name,n=t.form,s=e([]);return i&&(i=i.replace(/'/g,"\\'"),s=n?e(n).find("[name='"+i+"']"):e("[name='"+i+"']",t.ownerDocument).filter(function(){return!this.form})),s};e.widget("ui.button",{version:"1.10.4",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,s),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var n=this,o=this.options,r="checkbox"===this.type||"radio"===this.type,h=r?"":"ui-state-active";null===o.label&&(o.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(i).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){o.disabled||this===t&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){o.disabled||e(this).removeClass(h)}).bind("click"+this.eventNamespace,function(e){o.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass("ui-state-focus")},blur:function(){this.buttonElement.removeClass("ui-state-focus")}}),r&&this.element.bind("change"+this.eventNamespace,function(){n.refresh()}),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return o.disabled?!1:undefined}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(o.disabled)return!1;e(this).addClass("ui-state-active"),n.buttonElement.attr("aria-pressed","true");var t=n.element[0];a(t).not(t).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return o.disabled?!1:(e(this).addClass("ui-state-active"),t=this,n.document.one("mouseup",function(){t=null}),undefined)}).bind("mouseup"+this.eventNamespace,function(){return o.disabled?!1:(e(this).removeClass("ui-state-active"),undefined)}).bind("keydown"+this.eventNamespace,function(t){return o.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),undefined)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",o.disabled),this._resetButton()},_determineButtonType:function(){var e,t,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(i+" ui-state-active "+n).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(this.element.prop("disabled",!!t),t&&this.buttonElement.removeClass("ui-state-focus"),undefined):(this._resetButton(),undefined)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?a(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),undefined;var t=this.buttonElement.removeClass(n),i=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),s=this.options.icons,a=s.primary&&s.secondary,o=[];s.primary||s.secondary?(this.options.text&&o.push("ui-button-text-icon"+(a?"s":s.primary?"-primary":"-secondary")),s.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+s.primary+"'></span>"),s.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+s.secondary+"'></span>"),this.options.text||(o.push(a?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(i)))):o.push("ui-button-text-only"),t.addClass(o.join(" "))}}),e.widget("ui.buttonset",{version:"1.10.4",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction");this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}})})(jQuery);(function(e,t){function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.dpDiv=a(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){e.datepicker._isDisabledDatepicker(n.inline?t.parent()[0]:n.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function s(t,i){e.extend(t,i);for(var a in i)null==i[a]&&(t[a]=i[a]);return t}e.extend(e.ui,{datepicker:{version:"1.10.4"}});var n,r="datepicker";e.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return s(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var a,s,n;a=t.nodeName.toLowerCase(),s="div"===a||"span"===a,t.id||(this.uuid+=1,t.id="dp"+this.uuid),n=this._newInst(e(t),s),n.settings=e.extend({},i||{}),"input"===a?this._connectDatepicker(t,n):s&&this._inlineDatepicker(t,n)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,i){var a=e(t);i.append=e([]),i.trigger=e([]),a.hasClass(this.markerClassName)||(this._attachments(a,i),a.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,r,i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var a,s,n,r=this._get(i,"appendText"),o=this._get(i,"isRTL");i.append&&i.append.remove(),r&&(i.append=e("<span class='"+this._appendClass+"'>"+r+"</span>"),t[o?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),a=this._get(i,"showOn"),("focus"===a||"both"===a)&&t.focus(this._showDatepicker),("button"===a||"both"===a)&&(s=this._get(i,"buttonText"),n=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:n,alt:s,title:s}):e("<button type='button'></button>").addClass(this._triggerClass).html(n?e("<img/>").attr({src:n,alt:s,title:s}):s)),t[o?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,a,s,n=new Date(2009,11,20),r=this._get(e,"dateFormat");r.match(/[DM]/)&&(t=function(e){for(i=0,a=0,s=0;e.length>s;s++)e[s].length>i&&(i=e[s].length,a=s);return a},n.setMonth(t(this._get(e,r.match(/MM/)?"monthNames":"monthNamesShort"))),n.setDate(t(this._get(e,r.match(/DD/)?"dayNames":"dayNamesShort"))+20-n.getDay())),e.input.attr("size",this._formatDate(e,n).length)}},_inlineDatepicker:function(t,i){var a=e(t);a.hasClass(this.markerClassName)||(a.addClass(this.markerClassName).append(i.dpDiv),e.data(t,r,i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,a,n,o){var u,c,h,l,d,p=this._dialogInst;return p||(this.uuid+=1,u="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+u+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),p=this._dialogInst=this._newInst(this._dialogInput,!1),p.settings={},e.data(this._dialogInput[0],r,p)),s(p.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(p,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(c=document.documentElement.clientWidth,h=document.documentElement.clientHeight,l=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[c/2-100+l,h/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),p.settings.onSelect=a,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],r,p),this},_destroyDatepicker:function(t){var i,a=e(t),s=e.data(t,r);a.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,r),"input"===i?(s.append.remove(),s.trigger.remove(),a.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&a.removeClass(this.markerClassName).empty())},_enableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,n.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().removeClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,n.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().addClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,r)}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(i,a,n){var r,o,u,c,h=this._getInst(i);return 2===arguments.length&&"string"==typeof a?"defaults"===a?e.extend({},e.datepicker._defaults):h?"all"===a?e.extend({},h.settings):this._get(h,a):null:(r=a||{},"string"==typeof a&&(r={},r[a]=n),h&&(this._curInst===h&&this._hideDatepicker(),o=this._getDateDatepicker(i,!0),u=this._getMinMaxDate(h,"min"),c=this._getMinMaxDate(h,"max"),s(h.settings,r),null!==u&&r.dateFormat!==t&&r.minDate===t&&(h.settings.minDate=this._formatDate(h,u)),null!==c&&r.dateFormat!==t&&r.maxDate===t&&(h.settings.maxDate=this._formatDate(h,c)),"disabled"in r&&(r.disabled?this._disableDatepicker(i):this._enableDatepicker(i)),this._attachments(e(i),h),this._autoSize(h),this._setDate(h,o),this._updateAlternate(h),this._updateDatepicker(h)),t)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,a,s,n=e.datepicker._getInst(t.target),r=!0,o=n.dpDiv.is(".ui-datepicker-rtl");if(n._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),r=!1;break;case 13:return s=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",n.dpDiv),s[0]&&e.datepicker._selectDay(t.target,n.selectedMonth,n.selectedYear,s[0]),i=e.datepicker._get(n,"onSelect"),i?(a=e.datepicker._formatDate(n),i.apply(n.input?n.input[0]:null,[a,n])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),r=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),r=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?1:-1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),r=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?-1:1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),r=t.ctrlKey||t.metaKey;break;default:r=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):r=!1;r&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(i){var a,s,n=e.datepicker._getInst(i.target);return e.datepicker._get(n,"constrainInput")?(a=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==i.charCode?i.keyCode:i.charCode),i.ctrlKey||i.metaKey||" ">s||!a||a.indexOf(s)>-1):t},_doKeyUp:function(t){var i,a=e.datepicker._getInst(t.target);if(a.input.val()!==a.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,e.datepicker._getFormatConfig(a)),i&&(e.datepicker._setDateFromField(a),e.datepicker._updateAlternate(a),e.datepicker._updateDatepicker(a))}catch(s){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,a,n,r,o,u,c;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),a=e.datepicker._get(i,"beforeShow"),n=a?a.apply(t,[t,i]):{},n!==!1&&(s(i.settings,n),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),r=!1,e(t).parents().each(function(){return r|="fixed"===e(this).css("position"),!r}),o={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),o=e.datepicker._checkOffset(i,o,r),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":r?"fixed":"absolute",display:"none",left:o.left+"px",top:o.top+"px"}),i.inline||(u=e.datepicker._get(i,"showAnim"),c=e.datepicker._get(i,"duration"),i.dpDiv.zIndex(e(t).zIndex()+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[u]?i.dpDiv.show(u,e.datepicker._get(i,"showOptions"),c):i.dpDiv[u||"show"](u?c:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,n=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t),t.dpDiv.find("."+this._dayOverClass+" a").mouseover();var i,a=this._getNumberOfMonths(t),s=a[1],r=17;t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),s>1&&t.dpDiv.addClass("ui-datepicker-multi-"+s).css("width",r*s+"em"),t.dpDiv[(1!==a[0]||1!==a[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,a){var s=t.dpDiv.outerWidth(),n=t.dpDiv.outerHeight(),r=t.input?t.input.outerWidth():0,o=t.input?t.input.outerHeight():0,u=document.documentElement.clientWidth+(a?0:e(document).scrollLeft()),c=document.documentElement.clientHeight+(a?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?s-r:0,i.left-=a&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=a&&i.top===t.input.offset().top+o?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+s>u&&u>s?Math.abs(i.left+s-u):0),i.top-=Math.min(i.top,i.top+n>c&&c>n?Math.abs(n+o):0),i},_findPos:function(t){for(var i,a=this._getInst(t),s=this._get(a,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[s?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,a,s,n,o=this._curInst;!o||t&&o!==e.data(t,r)||this._datepickerShowing&&(i=this._get(o,"showAnim"),a=this._get(o,"duration"),s=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),a,s):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?a:null,s),i||s(),this._datepickerShowing=!1,n=this._get(o,"onClose"),n&&n.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),a=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==a)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,a){var s=e(t),n=this._getInst(s[0]);this._isDisabledDatepicker(s[0])||(this._adjustInstDate(n,i+("M"===a?this._get(n,"showCurrentAtPos"):0),a),this._updateDatepicker(n))},_gotoToday:function(t){var i,a=e(t),s=this._getInst(a[0]);this._get(s,"gotoCurrent")&&s.currentDay?(s.selectedDay=s.currentDay,s.drawMonth=s.selectedMonth=s.currentMonth,s.drawYear=s.selectedYear=s.currentYear):(i=new Date,s.selectedDay=i.getDate(),s.drawMonth=s.selectedMonth=i.getMonth(),s.drawYear=s.selectedYear=i.getFullYear()),this._notifyChange(s),this._adjustDate(a)},_selectMonthYear:function(t,i,a){var s=e(t),n=this._getInst(s[0]);n["selected"+("M"===a?"Month":"Year")]=n["draw"+("M"===a?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(n),this._adjustDate(s)},_selectDay:function(t,i,a,s){var n,r=e(t);e(s).hasClass(this._unselectableClass)||this._isDisabledDatepicker(r[0])||(n=this._getInst(r[0]),n.selectedDay=n.currentDay=e("a",s).html(),n.selectedMonth=n.currentMonth=i,n.selectedYear=n.currentYear=a,this._selectDate(t,this._formatDate(n,n.currentDay,n.currentMonth,n.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var a,s=e(t),n=this._getInst(s[0]);i=null!=i?i:this._formatDate(n),n.input&&n.input.val(i),this._updateAlternate(n),a=this._get(n,"onSelect"),a?a.apply(n.input?n.input[0]:null,[i,n]):n.input&&n.input.trigger("change"),n.inline?this._updateDatepicker(n):(this._hideDatepicker(),this._lastInput=n.input[0],"object"!=typeof n.input[0]&&n.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,a,s,n=this._get(t,"altField");n&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),a=this._getDate(t),s=this.formatDate(i,a,this._getFormatConfig(t)),e(n).each(function(){e(this).val(s)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(i,a,s){if(null==i||null==a)throw"Invalid arguments";if(a="object"==typeof a?""+a:a+"",""===a)return null;var n,r,o,u,c=0,h=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,l="string"!=typeof h?h:(new Date).getFullYear()%100+parseInt(h,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,p=(s?s.dayNames:null)||this._defaults.dayNames,g=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,m=(s?s.monthNames:null)||this._defaults.monthNames,f=-1,_=-1,v=-1,k=-1,y=!1,b=function(e){var t=i.length>n+1&&i.charAt(n+1)===e;return t&&n++,t},D=function(e){var t=b(e),i="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,s=RegExp("^\\d{1,"+i+"}"),n=a.substring(c).match(s);if(!n)throw"Missing number at position "+c;return c+=n[0].length,parseInt(n[0],10)},w=function(i,s,n){var r=-1,o=e.map(b(i)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,i){var s=i[1];return a.substr(c,s.length).toLowerCase()===s.toLowerCase()?(r=i[0],c+=s.length,!1):t}),-1!==r)return r+1;throw"Unknown name at position "+c},M=function(){if(a.charAt(c)!==i.charAt(n))throw"Unexpected literal at position "+c;c++};for(n=0;i.length>n;n++)if(y)"'"!==i.charAt(n)||b("'")?M():y=!1;else switch(i.charAt(n)){case"d":v=D("d");break;case"D":w("D",d,p);break;case"o":k=D("o");break;case"m":_=D("m");break;case"M":_=w("M",g,m);break;case"y":f=D("y");break;case"@":u=new Date(D("@")),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"!":u=new Date((D("!")-this._ticksTo1970)/1e4),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"'":b("'")?M():y=!0;break;default:M()}if(a.length>c&&(o=a.substr(c),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===f?f=(new Date).getFullYear():100>f&&(f+=(new Date).getFullYear()-(new Date).getFullYear()%100+(l>=f?0:-100)),k>-1)for(_=1,v=k;;){if(r=this._getDaysInMonth(f,_-1),r>=v)break;_++,v-=r}if(u=this._daylightSavingAdjust(new Date(f,_-1,v)),u.getFullYear()!==f||u.getMonth()+1!==_||u.getDate()!==v)throw"Invalid date";return u},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var a,s=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,n=(i?i.dayNames:null)||this._defaults.dayNames,r=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,o=(i?i.monthNames:null)||this._defaults.monthNames,u=function(t){var i=e.length>a+1&&e.charAt(a+1)===t;return i&&a++,i},c=function(e,t,i){var a=""+t;if(u(e))for(;i>a.length;)a="0"+a;return a},h=function(e,t,i,a){return u(e)?a[t]:i[t]},l="",d=!1;if(t)for(a=0;e.length>a;a++)if(d)"'"!==e.charAt(a)||u("'")?l+=e.charAt(a):d=!1;else switch(e.charAt(a)){case"d":l+=c("d",t.getDate(),2);break;case"D":l+=h("D",t.getDay(),s,n);break;case"o":l+=c("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":l+=c("m",t.getMonth()+1,2);break;case"M":l+=h("M",t.getMonth(),r,o);break;case"y":l+=u("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":l+=t.getTime();break;case"!":l+=1e4*t.getTime()+this._ticksTo1970;break;case"'":u("'")?l+="'":d=!0;break;default:l+=e.charAt(a)}return l},_possibleChars:function(e){var t,i="",a=!1,s=function(i){var a=e.length>t+1&&e.charAt(t+1)===i;return a&&t++,a};for(t=0;e.length>t;t++)if(a)"'"!==e.charAt(t)||s("'")?i+=e.charAt(t):a=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":s("'")?i+="'":a=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,i){return e.settings[i]!==t?e.settings[i]:this._defaults[i]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),a=e.lastVal=e.input?e.input.val():null,s=this._getDefaultDate(e),n=s,r=this._getFormatConfig(e);try{n=this.parseDate(i,a,r)||s}catch(o){a=t?"":a}e.selectedDay=n.getDate(),e.drawMonth=e.selectedMonth=n.getMonth(),e.drawYear=e.selectedYear=n.getFullYear(),e.currentDay=a?n.getDate():0,e.currentMonth=a?n.getMonth():0,e.currentYear=a?n.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,a){var s=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},n=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(a){}for(var s=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,n=s.getFullYear(),r=s.getMonth(),o=s.getDate(),u=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,c=u.exec(i);c;){switch(c[2]||"d"){case"d":case"D":o+=parseInt(c[1],10);break;case"w":case"W":o+=7*parseInt(c[1],10);break;case"m":case"M":r+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r));break;case"y":case"Y":n+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r))}c=u.exec(i)}return new Date(n,r,o)},r=null==i||""===i?a:"string"==typeof i?n(i):"number"==typeof i?isNaN(i)?a:s(i):new Date(i.getTime());return r=r&&"Invalid Date"==""+r?a:r,r&&(r.setHours(0),r.setMinutes(0),r.setSeconds(0),r.setMilliseconds(0)),this._daylightSavingAdjust(r)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var a=!t,s=e.selectedMonth,n=e.selectedYear,r=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=r.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=r.getMonth(),e.drawYear=e.selectedYear=e.currentYear=r.getFullYear(),s===e.selectedMonth&&n===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(a?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),a="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(a,-i,"M")},next:function(){e.datepicker._adjustDate(a,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(a)},selectDay:function(){return e.datepicker._selectDay(a,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(a,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(a,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,a,s,n,r,o,u,c,h,l,d,p,g,m,f,_,v,k,y,b,D,w,M,C,x,I,N,T,A,E,S,Y,F,P,O,j,K,R,H=new Date,W=this._daylightSavingAdjust(new Date(H.getFullYear(),H.getMonth(),H.getDate())),L=this._get(e,"isRTL"),U=this._get(e,"showButtonPanel"),B=this._get(e,"hideIfNoPrevNext"),z=this._get(e,"navigationAsDateFormat"),q=this._getNumberOfMonths(e),G=this._get(e,"showCurrentAtPos"),J=this._get(e,"stepMonths"),Q=1!==q[0]||1!==q[1],V=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),$=this._getMinMaxDate(e,"min"),X=this._getMinMaxDate(e,"max"),Z=e.drawMonth-G,et=e.drawYear;if(0>Z&&(Z+=12,et--),X)for(t=this._daylightSavingAdjust(new Date(X.getFullYear(),X.getMonth()-q[0]*q[1]+1,X.getDate())),t=$&&$>t?$:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=z?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-J,1)),this._getFormatConfig(e)):i,a=this._canAdjustMonth(e,-1,et,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"e":"w")+"'>"+i+"</span></a>":B?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"e":"w")+"'>"+i+"</span></a>",s=this._get(e,"nextText"),s=z?this.formatDate(s,this._daylightSavingAdjust(new Date(et,Z+J,1)),this._getFormatConfig(e)):s,n=this._canAdjustMonth(e,1,et,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+s+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"w":"e")+"'>"+s+"</span></a>":B?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+s+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"w":"e")+"'>"+s+"</span></a>",r=this._get(e,"currentText"),o=this._get(e,"gotoCurrent")&&e.currentDay?V:W,r=z?this.formatDate(r,o,this._getFormatConfig(e)):r,u=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",c=U?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(L?u:"")+(this._isInRange(e,o)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+r+"</button>":"")+(L?"":u)+"</div>":"",h=parseInt(this._get(e,"firstDay"),10),h=isNaN(h)?0:h,l=this._get(e,"showWeek"),d=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),g=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),f=this._get(e,"beforeShowDay"),_=this._get(e,"showOtherMonths"),v=this._get(e,"selectOtherMonths"),k=this._getDefaultDate(e),y="",D=0;q[0]>D;D++){for(w="",this.maxRows=4,M=0;q[1]>M;M++){if(C=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),x=" ui-corner-all",I="",Q){if(I+="<div class='ui-datepicker-group",q[1]>1)switch(M){case 0:I+=" ui-datepicker-group-first",x=" ui-corner-"+(L?"right":"left");break;case q[1]-1:I+=" ui-datepicker-group-last",x=" ui-corner-"+(L?"left":"right");break;default:I+=" ui-datepicker-group-middle",x=""}I+="'>"}for(I+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+x+"'>"+(/all|left/.test(x)&&0===D?L?n:a:"")+(/all|right/.test(x)&&0===D?L?a:n:"")+this._generateMonthYearHeader(e,Z,et,$,X,D>0||M>0,g,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",N=l?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",b=0;7>b;b++)T=(b+h)%7,N+="<th"+((b+h+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+d[T]+"'>"+p[T]+"</span></th>";for(I+=N+"</tr></thead><tbody>",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),E=(this._getFirstDayOfMonth(et,Z)-h+7)%7,S=Math.ceil((E+A)/7),Y=Q?this.maxRows>S?this.maxRows:S:S,this.maxRows=Y,F=this._daylightSavingAdjust(new Date(et,Z,1-E)),P=0;Y>P;P++){for(I+="<tr>",O=l?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(F)+"</td>":"",b=0;7>b;b++)j=f?f.apply(e.input?e.input[0]:null,[F]):[!0,""],K=F.getMonth()!==Z,R=K&&!v||!j[0]||$&&$>F||X&&F>X,O+="<td class='"+((b+h+6)%7>=5?" ui-datepicker-week-end":"")+(K?" ui-datepicker-other-month":"")+(F.getTime()===C.getTime()&&Z===e.selectedMonth&&e._keyEvent||k.getTime()===F.getTime()&&k.getTime()===C.getTime()?" "+this._dayOverClass:"")+(R?" "+this._unselectableClass+" ui-state-disabled":"")+(K&&!_?"":" "+j[1]+(F.getTime()===V.getTime()?" "+this._currentClass:"")+(F.getTime()===W.getTime()?" ui-datepicker-today":""))+"'"+(K&&!_||!j[2]?"":" title='"+j[2].replace(/'/g,"&#39;")+"'")+(R?"":" data-handler='selectDay' data-event='click' data-month='"+F.getMonth()+"' data-year='"+F.getFullYear()+"'")+">"+(K&&!_?"&#xa0;":R?"<span class='ui-state-default'>"+F.getDate()+"</span>":"<a class='ui-state-default"+(F.getTime()===W.getTime()?" ui-state-highlight":"")+(F.getTime()===V.getTime()?" ui-state-active":"")+(K?" ui-priority-secondary":"")+"' href='#'>"+F.getDate()+"</a>")+"</td>",F.setDate(F.getDate()+1),F=this._daylightSavingAdjust(F);I+=O+"</tr>"}Z++,Z>11&&(Z=0,et++),I+="</tbody></table>"+(Q?"</div>"+(q[0]>0&&M===q[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),w+=I}y+=w}return y+=c,e._keyEvent=!1,y},_generateMonthYearHeader:function(e,t,i,a,s,n,r,o){var u,c,h,l,d,p,g,m,f=this._get(e,"changeMonth"),_=this._get(e,"changeYear"),v=this._get(e,"showMonthAfterYear"),k="<div class='ui-datepicker-title'>",y="";if(n||!f)y+="<span class='ui-datepicker-month'>"+r[t]+"</span>";else{for(u=a&&a.getFullYear()===i,c=s&&s.getFullYear()===i,y+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",h=0;12>h;h++)(!u||h>=a.getMonth())&&(!c||s.getMonth()>=h)&&(y+="<option value='"+h+"'"+(h===t?" selected='selected'":"")+">"+o[h]+"</option>");y+="</select>"}if(v||(k+=y+(!n&&f&&_?"":"&#xa0;")),!e.yearshtml)if(e.yearshtml="",n||!_)k+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(l=this._get(e,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?d+parseInt(e,10):parseInt(e,10);
-return isNaN(t)?d:t},g=p(l[0]),m=Math.max(g,p(l[1]||"")),g=a?Math.max(g,a.getFullYear()):g,m=s?Math.min(m,s.getFullYear()):m,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=g;g++)e.yearshtml+="<option value='"+g+"'"+(g===i?" selected='selected'":"")+">"+g+"</option>";e.yearshtml+="</select>",k+=e.yearshtml,e.yearshtml=null}return k+=this._get(e,"yearSuffix"),v&&(k+=(!n&&f&&_?"":"&#xa0;")+y),k+="</div>"},_adjustInstDate:function(e,t,i){var a=e.drawYear+("Y"===i?t:0),s=e.drawMonth+("M"===i?t:0),n=Math.min(e.selectedDay,this._getDaysInMonth(a,s))+("D"===i?t:0),r=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(a,s,n)));e.selectedDay=r.getDate(),e.drawMonth=e.selectedMonth=r.getMonth(),e.drawYear=e.selectedYear=r.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),s=i&&i>t?i:t;return a&&s>a?a:s},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,a){var s=this._getNumberOfMonths(e),n=this._daylightSavingAdjust(new Date(i,a+(0>t?t:s[0]*s[1]),1));return 0>t&&n.setDate(this._getDaysInMonth(n.getFullYear(),n.getMonth())),this._isInRange(e,n)},_isInRange:function(e,t){var i,a,s=this._getMinMaxDate(e,"min"),n=this._getMinMaxDate(e,"max"),r=null,o=null,u=this._get(e,"yearRange");return u&&(i=u.split(":"),a=(new Date).getFullYear(),r=parseInt(i[0],10),o=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(r+=a),i[1].match(/[+\-].*/)&&(o+=a)),(!s||t.getTime()>=s.getTime())&&(!n||t.getTime()<=n.getTime())&&(!r||t.getFullYear()>=r)&&(!o||o>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,a){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var s=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(a,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),s,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new i,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.10.4"})(jQuery);(function(e){var t={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},i={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0};e.widget("ui.dialog",{version:"1.10.4",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,a=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._destroyOverlay(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(s){}this._hide(this.uiDialog,this.options.hide,function(){a._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,t){var i=!!this.uiDialog.nextAll(":visible").insertBefore(this.uiDialog).length;return i&&!t&&this._trigger("focus",e),i},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),undefined):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._trigger("open"),undefined)},_focusTabbable:function(){var e=this.element.find("[autofocus]");e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),undefined;if(t.keyCode===e.ui.keyCode.TAB){var i=this.uiDialog.find(":tabbable"),a=i.filter(":first"),s=i.filter(":last");t.target!==s[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==a[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(s.focus(1),t.preventDefault()):(a.focus(1),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button type='button'></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html("&#160;"),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),undefined):(e.each(i,function(i,a){var s,n;a=e.isFunction(a)?{click:a,text:i}:a,a=e.extend({type:"button"},a),s=a.click,a.click=function(){s.apply(t.element[0],arguments)},n={icons:a.icons,text:a.showText},delete a.icons,delete a.showText,e("<button></button>",a).button(n).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),undefined)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,a=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(a,s){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",a,t(s))},drag:function(e,a){i._trigger("drag",e,t(a))},stop:function(s,n){a.position=[n.position.left-i.document.scrollLeft(),n.position.top-i.document.scrollTop()],e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",s,t(n))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,a=this.options,s=a.resizable,n=this.uiDialog.css("position"),r="string"==typeof s?s:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:a.maxWidth,maxHeight:a.maxHeight,minWidth:a.minWidth,minHeight:this._minHeight(),handles:r,start:function(a,s){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",a,t(s))},resize:function(e,a){i._trigger("resize",e,t(a))},stop:function(s,n){a.height=e(this).height(),a.width=e(this).width(),e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",s,t(n))}}).css("position",n)},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(a){var s=this,n=!1,r={};e.each(a,function(e,a){s._setOption(e,a),e in t&&(n=!0),e in i&&(r[e]=a)}),n&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",r)},_setOption:function(e,t){var i,a,s=this.uiDialog;"dialogClass"===e&&s.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=s.is(":data(ui-draggable)"),i&&!t&&s.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(a=s.is(":data(ui-resizable)"),a&&!t&&s.resizable("destroy"),a&&"string"==typeof t&&s.resizable("option","handles",t),a||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,a=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),a.minWidth>a.width&&(a.width=a.minWidth),e=this.uiDialog.css({height:"auto",width:a.width}).outerHeight(),t=Math.max(0,a.minHeight-e),i="number"==typeof a.maxHeight?Math.max(0,a.maxHeight-e):"none","auto"===a.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,a.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=this,i=this.widgetFullName;e.ui.dialog.overlayInstances||this._delay(function(){e.ui.dialog.overlayInstances&&this.document.bind("focusin.dialog",function(a){t._allowInteraction(a)||(a.preventDefault(),e(".ui-dialog:visible:last .ui-dialog-content").data(i)._focusTabbable())})}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),e.ui.dialog.overlayInstances++}},_destroyOverlay:function(){this.options.modal&&this.overlay&&(e.ui.dialog.overlayInstances--,e.ui.dialog.overlayInstances||this.document.unbind("focusin.dialog"),this.overlay.remove(),this.overlay=null)}}),e.ui.dialog.overlayInstances=0,e.uiBackCompat!==!1&&e.widget("ui.dialog",e.ui.dialog,{_position:function(){var t,i=this.options.position,a=[],s=[0,0];i?(("string"==typeof i||"object"==typeof i&&"0"in i)&&(a=i.split?i.split(" "):[i[0],i[1]],1===a.length&&(a[1]=a[0]),e.each(["left","top"],function(e,t){+a[e]===a[e]&&(s[e]=a[e],a[e]=t)}),i={my:a[0]+(0>s[0]?s[0]:"+"+s[0])+" "+a[1]+(0>s[1]?s[1]:"+"+s[1]),at:a.join(" ")}),i=e.extend({},e.ui.dialog.prototype.options.position,i)):i=e.ui.dialog.prototype.options.position,t=this.uiDialog.is(":visible"),t||this.uiDialog.show(),this.uiDialog.position(i),t||this.uiDialog.hide()}})})(jQuery);(function(t){t.widget("ui.draggable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(t(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){t("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(t(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_mouseDrag:function(e,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||t.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1):!1},_mouseUp:function(e){return t("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.element.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,t(document).width()-this.helperProportions.width-this.margins.left,(t(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(e){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=e.pageX,h=e.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.left<i[0]&&(l=i[0]+this.offset.click.left),e.pageY-this.offset.click.top<i[1]&&(h=i[1]+this.offset.click.top),e.pageX-this.offset.click.left>i[2]&&(l=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,l=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s]),"drag"===e&&(this.positionAbs=this._convertPositionTo("absolute")),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i){var s=t(this).data("ui-draggable"),n=s.options,a=t.extend({},i,{item:s.element});s.sortables=[],t(n.connectToSortable).each(function(){var i=t.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",e,a))})},stop:function(e,i){var s=t(this).data("ui-draggable"),n=t.extend({},i,{item:s.element});t.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(e),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",e,n))})},drag:function(e,i){var s=t(this).data("ui-draggable"),n=this;t.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,t.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&t.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=t(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},e.target=this.instance.currentItem[0],this.instance._mouseCapture(e,!0),this.instance._mouseStart(e,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",e),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(e)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",e,this.instance._uiHash(this.instance)),this.instance._mouseStop(e,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",e),s.dropped=!1)})}}),t.ui.plugin.add("draggable","cursor",{start:function(){var e=t("body"),i=t(this).data("ui-draggable").options;e.css("cursor")&&(i._cursor=e.css("cursor")),e.css("cursor",i.cursor)},stop:function(){var e=t(this).data("ui-draggable").options;e._cursor&&t("body").css("cursor",e._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._opacity&&t(i.helper).css("opacity",s._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(){var e=t(this).data("ui-draggable");e.scrollParent[0]!==document&&"HTML"!==e.scrollParent[0].tagName&&(e.overflowOffset=e.scrollParent.offset())},drag:function(e){var i=t(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-e.pageY<s.scrollSensitivity?i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop+s.scrollSpeed:e.pageY-i.overflowOffset.top<s.scrollSensitivity&&(i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-e.pageX<s.scrollSensitivity?i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft+s.scrollSpeed:e.pageX-i.overflowOffset.left<s.scrollSensitivity&&(i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(e.pageY-t(document).scrollTop()<s.scrollSensitivity?n=t(document).scrollTop(t(document).scrollTop()-s.scrollSpeed):t(window).height()-(e.pageY-t(document).scrollTop())<s.scrollSensitivity&&(n=t(document).scrollTop(t(document).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(e.pageX-t(document).scrollLeft()<s.scrollSensitivity?n=t(document).scrollLeft(t(document).scrollLeft()-s.scrollSpeed):t(window).width()-(e.pageX-t(document).scrollLeft())<s.scrollSensitivity&&(n=t(document).scrollLeft(t(document).scrollLeft()+s.scrollSpeed)))),n!==!1&&t.ui.ddmanager&&!s.dropBehaviour&&t.ui.ddmanager.prepareOffsets(i,e)}}),t.ui.plugin.add("draggable","snap",{start:function(){var e=t(this).data("ui-draggable"),i=e.options;e.snapElements=[],t(i.snap.constructor!==String?i.snap.items||":data(ui-draggable)":i.snap).each(function(){var i=t(this),s=i.offset();this!==e.element[0]&&e.snapElements.push({item:this,width:i.outerWidth(),height:i.outerHeight(),top:s.top,left:s.left})})},drag:function(e,i){var s,n,a,o,r,l,h,c,u,d,p=t(this).data("ui-draggable"),g=p.options,f=g.snapTolerance,m=i.offset.left,_=m+p.helperProportions.width,v=i.offset.top,b=v+p.helperProportions.height;for(u=p.snapElements.length-1;u>=0;u--)r=p.snapElements[u].left,l=r+p.snapElements[u].width,h=p.snapElements[u].top,c=h+p.snapElements[u].height,r-f>_||m>l+f||h-f>b||v>c+f||!t.contains(p.snapElements[u].item.ownerDocument,p.snapElements[u].item)?(p.snapElements[u].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=!1):("inner"!==g.snapMode&&(s=f>=Math.abs(h-b),n=f>=Math.abs(c-v),a=f>=Math.abs(r-_),o=f>=Math.abs(l-m),s&&(i.position.top=p._convertPositionTo("relative",{top:h-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l}).left-p.margins.left)),d=s||n||a||o,"outer"!==g.snapMode&&(s=f>=Math.abs(h-v),n=f>=Math.abs(c-b),a=f>=Math.abs(r-m),o=f>=Math.abs(l-_),s&&(i.position.top=p._convertPositionTo("relative",{top:h,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[u].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=s||n||a||o||d)}}),t.ui.plugin.add("draggable","stack",{start:function(){var e,i=this.data("ui-draggable").options,s=t.makeArray(t(i.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});s.length&&(e=parseInt(t(s[0]).css("zIndex"),10)||0,t(s).each(function(i){t(this).css("zIndex",e+i)}),this.css("zIndex",e+s.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._zIndex&&t(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}t.widget("ui.droppable",{version:"1.10.4",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(s)?s:function(t){return t.is(s)},this.proportions=function(){return arguments.length?(e=arguments[0],undefined):e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},t.ui.ddmanager.droppables[i.scope]=t.ui.ddmanager.droppables[i.scope]||[],t.ui.ddmanager.droppables[i.scope].push(this),i.addClasses&&this.element.addClass("ui-droppable")},_destroy:function(){for(var e=0,i=t.ui.ddmanager.droppables[this.options.scope];i.length>e;e++)i[e]===this&&i.splice(e,1);this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(e,i){"accept"===e&&(this.accept=t.isFunction(i)?i:function(t){return t.is(i)}),t.Widget.prototype._setOption.apply(this,arguments)},_activate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var e=t.data(this,"ui-droppable");return e.options.greedy&&!e.options.disabled&&e.options.scope===s.options.scope&&e.accept.call(e.element[0],s.currentItem||s.element)&&t.ui.intersect(s,t.extend(e,{offset:e.element.offset()}),e.options.tolerance)?(n=!0,!1):undefined}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}}}),t.ui.intersect=function(t,i,s){if(!i.offset)return!1;var n,a,o=(t.positionAbs||t.position.absolute).left,r=(t.positionAbs||t.position.absolute).top,l=o+t.helperProportions.width,h=r+t.helperProportions.height,c=i.offset.left,u=i.offset.top,d=c+i.proportions().width,p=u+i.proportions().height;switch(s){case"fit":return o>=c&&d>=l&&r>=u&&p>=h;case"intersect":return o+t.helperProportions.width/2>c&&d>l-t.helperProportions.width/2&&r+t.helperProportions.height/2>u&&p>h-t.helperProportions.height/2;case"pointer":return n=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,a=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(a,u,i.proportions().height)&&e(n,c,i.proportions().width);case"touch":return(r>=u&&p>=r||h>=u&&p>=h||u>r&&h>p)&&(o>=c&&d>=o||l>=c&&d>=l||c>o&&l>d);default:return!1}},t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,a=t.ui.ddmanager.droppables[e.options.scope]||[],o=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||e&&!a[s].accept.call(a[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue t}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&t.ui.intersect(e,this,this.options.tolerance)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").bind("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=t.ui.intersect(e,this,this.options.tolerance),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return t.data(this,"ui-droppable").options.scope===n}),a.length&&(s=t.data(a[0],"ui-droppable"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").unbind("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}}})(jQuery);(function(t,e){var i="ui-effects-";t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=h(),n=s._rgba=[];return i=i.toLowerCase(),f(l,function(t,a){var o,r=a.re.exec(i),l=r&&a.parse(r),h=a.space||"rgba";return l?(o=s[h](l),s[c[h].cache]=o[c[h].cache],n=s._rgba=o._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,a.transparent),s):a[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,l=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],h=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=h.support={},p=t("<p>")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),h.fn=t.extend(h.prototype,{parse:function(n,o,r,l){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(o),o=e);var u=this,d=t.type(n),p=this._rgba=[];return o!==e&&(n=[n,o,r,l],d="array"),"string"===d?this.parse(s(n)||a._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof h?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var a=s.cache;f(s.props,function(t,e){if(!u[a]&&s.to){if("alpha"===t||null==n[t])return;u[a]=s.to(u._rgba)}u[a][e.idx]=i(n[t],e,!0)}),u[a]&&0>t.inArray(null,u[a].slice(0,3))&&(u[a][3]=1,s.from&&(u._rgba=s.from(u[a])))}),this):e},is:function(t){var i=h(t),s=!0,n=this;return f(c,function(t,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=h(t),n=s._space(),a=c[n],o=0===this.alpha()?h("transparent"):this,r=o[a.cache]||a.to(o._rgba),l=r.slice();return s=s[a.cache],f(a.props,function(t,n){var a=n.idx,o=r[a],h=s[a],c=u[n.type]||{};null!==h&&(null===o?l[a]=h:(c.mod&&(h-o>c.mod/2?o+=c.mod:o-h>c.mod/2&&(o-=c.mod)),l[a]=i((h-o)*e+o,n)))}),this[n](l)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=h(e)._rgba;return h(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),h.fn.parse.prototype=h.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,a=t[2]/255,o=t[3],r=Math.max(s,n,a),l=Math.min(s,n,a),h=r-l,c=r+l,u=.5*c;return e=l===r?0:s===r?60*(n-a)/h+360:n===r?60*(a-s)/h+120:60*(s-n)/h+240,i=0===h?0:.5>=u?h/c:h/(2-c),[Math.round(e)%360,i,u,null==o?1:o]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],a=t[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,e+1/3)),Math.round(255*n(r,o,e)),Math.round(255*n(r,o,e-1/3)),a]},f(c,function(s,n){var a=n.props,o=n.cache,l=n.to,c=n.from;h.fn[s]=function(s){if(l&&!this[o]&&(this[o]=l(this._rgba)),s===e)return this[o].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[o].slice();return f(a,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=h(c(d)),n[o]=d,n):h(d)},f(a,function(e,i){h.fn[e]||(h.fn[e]=function(n){var a,o=t.type(n),l="alpha"===e?this._hsla?"hsla":"rgba":s,h=this[l](),c=h[i.idx];return"undefined"===o?c:("function"===o&&(n=n.call(this,c),o=t.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=c+parseFloat(a[2])*("+"===a[1]?1:-1))),h[i.idx]=n,this[l](h)))})})}),h.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var a,o,r="";if("transparent"!==n&&("string"!==t.type(n)||(a=s(n)))){if(n=h(a||n),!d.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&o&&o.style;)try{r=t.css(o,"backgroundColor"),o=o.parentNode}catch(l){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(l){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=h(e.elem,i),e.end=h(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},h.hook(o),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},a=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function i(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function s(e,i){var s,n,o={};for(s in i)n=i[s],e[s]!==n&&(a[s]||(t.fx.step[s]||!isNaN(parseFloat(n)))&&(o[s]=n));return o}var n=["add","remove","toggle"],a={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(jQuery.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(e,a,o,r){var l=t.speed(a,o,r);return this.queue(function(){var a,o=t(this),r=o.attr("class")||"",h=l.children?o.find("*").addBack():o;h=h.map(function(){var e=t(this);return{el:e,start:i(this)}}),a=function(){t.each(n,function(t,i){e[i]&&o[i+"Class"](e[i])})},a(),h=h.map(function(){return this.end=i(this.el[0]),this.diff=s(this.start,this.end),this}),o.attr("class",r),h=h.map(function(){var e=this,i=t.Deferred(),s=t.extend({},l,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,h.get()).done(function(){a(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),l.complete.call(o[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,a){return s?t.effects.animateClass.call(this,{add:i},s,n,a):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,a){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,a):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(i){return function(s,n,a,o,r){return"boolean"==typeof n||n===e?a?t.effects.animateClass.call(this,n?{add:s}:{remove:s},a,o,r):i.apply(this,arguments):t.effects.animateClass.call(this,{toggle:s},n,a,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,a){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,a)}})}(),function(){function s(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function n(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}t.extend(t.effects,{version:"1.10.4",save:function(t,e){for(var s=0;e.length>s;s++)null!==e[s]&&t.data(i+e[s],t[0].style[e[s]])},restore:function(t,s){var n,a;for(a=0;s.length>a;a++)null!==s[a]&&(n=t.data(i+s[a]),n===e&&(n=""),t.css(s[a],n))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return e.wrap(s),(e[0]===a||t.contains(e[0],a))&&t(a).focus(),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).focus()),e},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var a=e.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),t.fn.extend({effect:function(){function e(e){function s(){t.isFunction(a)&&a.call(n[0]),t.isFunction(e)&&e()}var n=t(this),a=i.complete,r=i.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),s()):o.call(n[0],i,s)}var i=s.apply(this,arguments),n=i.mode,a=i.queue,o=t.effects.effect[i.effect];return t.fx.off||!o?n?this[n](i.duration,i.complete):this.each(function(){i.complete&&i.complete.call(this)}):a===!1?this.each(e):this.queue(a||"fx",e)},show:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="show",this.effect.call(this,i)}}(t.fn.show),hide:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="hide",this.effect.call(this,i)}}(t.fn.hide),toggle:function(t){return function(e){if(n(e)||"boolean"==typeof e)return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="toggle",this.effect.call(this,i)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s}})}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}()})(jQuery);(function(t){var e=/up|down|vertical/,i=/up|left|vertical|horizontal/;t.effects.effect.blind=function(s,n){var a,o,r,l=t(this),h=["position","top","bottom","left","right","height","width"],c=t.effects.setMode(l,s.mode||"hide"),u=s.direction||"up",d=e.test(u),p=d?"height":"width",f=d?"top":"left",g=i.test(u),m={},v="show"===c;l.parent().is(".ui-effects-wrapper")?t.effects.save(l.parent(),h):t.effects.save(l,h),l.show(),a=t.effects.createWrapper(l).css({overflow:"hidden"}),o=a[p](),r=parseFloat(a.css(f))||0,m[p]=v?o:0,g||(l.css(d?"bottom":"right",0).css(d?"top":"left","auto").css({position:"absolute"}),m[f]=v?r:o+r),v&&(a.css(p,0),g||a.css(f,r+o)),a.animate(m,{duration:s.duration,easing:s.easing,queue:!1,complete:function(){"hide"===c&&l.hide(),t.effects.restore(l,h),t.effects.removeWrapper(l),n()}})}})(jQuery);(function(t){t.effects.effect.bounce=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],l=t.effects.setMode(o,e.mode||"effect"),h="hide"===l,c="show"===l,u=e.direction||"up",d=e.distance,p=e.times||5,f=2*p+(c||h?1:0),g=e.duration/f,m=e.easing,v="up"===u||"down"===u?"top":"left",_="up"===u||"left"===u,b=o.queue(),y=b.length;for((c||h)&&r.push("opacity"),t.effects.save(o,r),o.show(),t.effects.createWrapper(o),d||(d=o["top"===v?"outerHeight":"outerWidth"]()/3),c&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,_?2*-d:2*d).animate(a,g,m)),h&&(d/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(_?"-=":"+=")+d,o.animate(n,g,m).animate(a,g,m),d=h?2*d:d/2;h&&(n={opacity:0},n[v]=(_?"-=":"+=")+d,o.animate(n,g,m)),o.queue(function(){h&&o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}),y>1&&b.splice.apply(b,[1,0].concat(b.splice(y,f+1))),o.dequeue()}})(jQuery);(function(t){t.effects.effect.clip=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],l=t.effects.setMode(o,e.mode||"hide"),h="show"===l,c=e.direction||"vertical",u="vertical"===c,d=u?"height":"width",p=u?"top":"left",f={};t.effects.save(o,r),o.show(),s=t.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[d](),h&&(n.css(d,0),n.css(p,a/2)),f[d]=h?a:0,f[p]=h?0:a/2,n.animate(f,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){h||o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.drop=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","opacity","height","width"],o=t.effects.setMode(n,e.mode||"hide"),r="show"===o,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l?"pos":"neg",u={opacity:r?1:0};t.effects.save(n,a),n.show(),t.effects.createWrapper(n),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(h,"pos"===c?-s:s),u[h]=(r?"pos"===c?"+=":"-=":"pos"===c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.explode=function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),g||p.hide(),i()}var a,o,r,l,h,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=t.effects.setMode(p,e.mode||"hide"),g="show"===f,m=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/d),_=Math.ceil(p.outerHeight()/u),b=[];for(a=0;u>a;a++)for(l=m.top+a*_,c=a-(u-1)/2,o=0;d>o;o++)r=m.left+o*v,h=o-(d-1)/2,p.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*_}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:_,left:r+(g?h*v:0),top:l+(g?c*_:0),opacity:g?0:1}).animate({left:r+(g?0:h*v),top:l+(g?0:c*_),opacity:g?1:0},e.duration||500,e.easing,s)}})(jQuery);(function(t){t.effects.effect.fade=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}})(jQuery);(function(t){t.effects.effect.fold=function(e,i){var s,n,a=t(this),o=["position","top","bottom","left","right","height","width"],r=t.effects.setMode(a,e.mode||"hide"),l="show"===r,h="hide"===r,c=e.size||15,u=/([0-9]+)%/.exec(c),d=!!e.horizFirst,p=l!==d,f=p?["width","height"]:["height","width"],g=e.duration/2,m={},v={};t.effects.save(a,o),a.show(),s=t.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],u&&(c=parseInt(u[1],10)/100*n[h?0:1]),l&&s.css(d?{height:0,width:c}:{height:c,width:0}),m[f[0]]=l?n[0]:c,v[f[1]]=l?n[1]:0,s.animate(m,g,e.easing).animate(v,g,e.easing,function(){h&&a.hide(),t.effects.restore(a,o),t.effects.removeWrapper(a),i()})}})(jQuery);(function(t){t.effects.effect.highlight=function(e,i){var s=t(this),n=["backgroundImage","backgroundColor","opacity"],a=t.effects.setMode(s,e.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),t.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(o,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===a&&s.hide(),t.effects.restore(s,n),i()}})}})(jQuery);(function(t){t.effects.effect.pulsate=function(e,i){var s,n=t(this),a=t.effects.setMode(n,e.mode||"show"),o="show"===a,r="hide"===a,l=o||"hide"===a,h=2*(e.times||5)+(l?1:0),c=e.duration/h,u=0,d=n.queue(),p=d.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),u=1),s=1;h>s;s++)n.animate({opacity:u},c,e.easing),u=1-u;n.animate({opacity:u},c,e.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&d.splice.apply(d,[1,0].concat(d.splice(p,h+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.puff=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"hide"),a="hide"===n,o=parseInt(e.percent,10)||150,r=o/100,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};t.extend(e,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?l:{height:l.height*r,width:l.width*r,outerHeight:l.outerHeight*r,outerWidth:l.outerWidth*r}}),s.effect(e)},t.effects.effect.scale=function(e,i){var s=t(this),n=t.extend(!0,{},e),a=t.effects.setMode(s,e.mode||"effect"),o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"hide"===a?0:100),r=e.direction||"both",l=e.origin,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},c={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=l||["middle","center"],n.restore=!0),n.from=e.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:h),n.to={height:h.height*c.y,width:h.width*c.x,outerHeight:h.outerHeight*c.y,outerWidth:h.outerWidth*c.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},t.effects.effect.size=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],l=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],c=["fontSize"],u=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],d=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=t.effects.setMode(o,e.mode||"effect"),f=e.restore||"effect"!==p,g=e.scale||"both",m=e.origin||["middle","center"],v=o.css("position"),_=f?r:l,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===e.mode&&"show"===p?(o.from=e.to||b,o.to=e.from||s):(o.from=e.from||("show"===p?b:s),o.to=e.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===g||"both"===g)&&(a.from.y!==a.to.y&&(_=_.concat(u),o.from=t.effects.setTransition(o,u,a.from.y,o.from),o.to=t.effects.setTransition(o,u,a.to.y,o.to)),a.from.x!==a.to.x&&(_=_.concat(d),o.from=t.effects.setTransition(o,d,a.from.x,o.from),o.to=t.effects.setTransition(o,d,a.to.x,o.to))),("content"===g||"both"===g)&&a.from.y!==a.to.y&&(_=_.concat(c).concat(h),o.from=t.effects.setTransition(o,c,a.from.y,o.from),o.to=t.effects.setTransition(o,c,a.to.y,o.to)),t.effects.save(o,_),o.show(),t.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),m&&(n=t.effects.getBaseline(m,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===g||"both"===g)&&(u=u.concat(["marginTop","marginBottom"]).concat(c),d=d.concat(["marginLeft","marginRight"]),h=r.concat(u).concat(d),o.find("*[width]").each(function(){var i=t(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&t.effects.save(i,h),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=t.effects.setTransition(i,u,a.from.y,i.from),i.to=t.effects.setTransition(i,u,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=t.effects.setTransition(i,d,a.from.x,i.from),i.to=t.effects.setTransition(i,d,a.to.x,i.to)),i.css(i.from),i.animate(i.to,e.duration,e.easing,function(){f&&t.effects.restore(i,h)})})),o.animate(o.to,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),t.effects.restore(o,_),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):t.each(["top","left"],function(t,e){o.css(e,function(e,i){var s=parseInt(i,10),n=t?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.shake=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","height","width"],o=t.effects.setMode(n,e.mode||"effect"),r=e.direction||"left",l=e.distance||20,h=e.times||3,c=2*h+1,u=Math.round(e.duration/c),d="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},g={},m={},v=n.queue(),_=v.length;for(t.effects.save(n,a),n.show(),t.effects.createWrapper(n),f[d]=(p?"-=":"+=")+l,g[d]=(p?"+=":"-=")+2*l,m[d]=(p?"-=":"+=")+2*l,n.animate(f,u,e.easing),s=1;h>s;s++)n.animate(g,u,e.easing).animate(m,u,e.easing);n.animate(g,u,e.easing).animate(f,u/2,e.easing).queue(function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}),_>1&&v.splice.apply(v,[1,0].concat(v.splice(_,c+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.slide=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","width","height"],o=t.effects.setMode(n,e.mode||"show"),r="show"===o,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l,u={};t.effects.save(n,a),n.show(),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0),t.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(h,c?isNaN(s)?"-"+s:-s:s),u[h]=(r?c?"+=":"-=":c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.transfer=function(e,i){var s=t(this),n=t(e.to),a="fixed"===n.css("position"),o=t("body"),r=a?o.scrollTop():0,l=a?o.scrollLeft():0,h=n.offset(),c={top:h.top-r,left:h.left-l,height:n.innerHeight(),width:n.innerWidth()},u=s.offset(),d=t("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(e.className).css({top:u.top-r,left:u.left-l,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){d.remove(),i()})}})(jQuery);(function(t){t.widget("ui.menu",{version:"1.10.4",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,t.proxy(function(t){this.options.disabled&&t.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(t){t.preventDefault()},"click .ui-state-disabled > a":function(t){t.preventDefault()},"click .ui-menu-item:has(a)":function(e){var i=t(e.target).closest(".ui-menu-item");!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&t(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){var i=t(e.currentTarget);i.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(e,i)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.children(".ui-menu-item").eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){t.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){t(e.target).closest(".ui-menu").length||this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var e=t(this);e.data("ui-menu-submenu-carat")&&e.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(e){function i(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var s,n,a,o,r,l=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:l=!1,n=this.previousFilter||"",a=String.fromCharCode(e.keyCode),o=!1,clearTimeout(this.filterTimer),a===n?o=!0:a=n+a,r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())}),s=o&&-1!==s.index(this.active.next())?this.active.nextAll(".ui-menu-item"):s,s.length||(a=String.fromCharCode(e.keyCode),r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())})),s.length?(this.focus(e,s),s.length>1?(this.previousFilter=a,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}l&&e.preventDefault()},_activate:function(t){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i=this.options.icons.submenu,s=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),s=e.prev("a"),n=t("<span>").addClass("ui-menu-icon ui-icon "+i).data("ui-menu-submenu-carat",!0);s.attr("aria-haspopup","true").prepend(n),e.attr("aria-labelledby",s.attr("id"))}),e=s.add(this.element),e.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),e.children(":not(.ui-menu-item)").each(function(){var e=t(this);/[^\-\u2014\u2013\s]/.test(e.text())||e.addClass("ui-widget-content ui-menu-divider")}),e.children(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){"icons"===t&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(e.submenu),this._super(t,e)},focus:function(t,e){var i,s;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=e.height(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",t,{item:this.active}))},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.children(".ui-menu-item")[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())),undefined):(this.next(e),undefined)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item").first())),undefined):(this.next(e),undefined)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(e){this.active=this.active||t(e.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(e,!0),this._trigger("select",e,i)}})})(jQuery);(function(t,e){t.widget("ui.progressbar",{version:"1.10.4",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=t("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(t){return t===e?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),e)},_constrainedValue:function(t){return t===e&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}})})(jQuery);(function(t){function e(t){return parseInt(t,10)||0}function i(t){return!isNaN(parseInt(t,10))}t.widget("ui.resizable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var e,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(t("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),e=this.handles.split(","),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),a="ui-resizable-"+s,n=t("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,a;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=t(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=t(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,a),this._proportionallyResize()),t(this.handles[i]).length},this._renderAxis(this.element),this._handles=t(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(t(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(t(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=e(this.helper.css("left")),n=e(this.helper.css("top")),o.containment&&(s+=t(o.containment).scrollLeft()||0,n+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(e){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,c=this.size.height,u=e.pageX-a.left||0,d=e.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[e,u,d]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==c&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(n)||this._trigger("resize",e,this.ui()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&t.ui.hasScroll(i[0],"left")?0:c.sizeDiff.height,a=s?0:c.sizeDiff.width,o={width:c.helper.width()-a,height:c.helper.height()-n},r=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null,h=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(o,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(t){var e,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),i(t.left)&&(this.position.left=t.left),i(t.top)&&(this.position.top=t.top),i(t.height)&&(this.size.height=t.height),i(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,s=this.size,n=this.axis;return i(t.height)?t.width=t.height*this.aspectRatio:i(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===n&&(t.left=e.left+(s.width-t.width),t.top=null),"nw"===n&&(t.top=e.top+(s.height-t.height),t.left=e.left+(s.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,s=this.axis,n=i(t.width)&&e.maxWidth&&e.maxWidth<t.width,a=i(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=i(t.width)&&e.minWidth&&e.minWidth>t.width,r=i(t.height)&&e.minHeight&&e.minHeight>t.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,c=/sw|nw|w/.test(s),u=/nw|ne|n/.test(s);return o&&(t.width=e.minWidth),r&&(t.height=e.minHeight),n&&(t.width=e.maxWidth),a&&(t.height=e.maxHeight),o&&c&&(t.left=h-e.minWidth),n&&c&&(t.left=h-e.maxWidth),r&&u&&(t.top=l-e.minHeight),a&&u&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var t,e,i,s,n,a=this.helper||this.element;for(t=0;this._proportionallyResizeElements.length>t;t++){if(n=this._proportionallyResizeElements[t],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],e=0;i.length>e;e++)this.borderDif[e]=(parseInt(i[e],10)||0)+(parseInt(s[e],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&t.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,c=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=t(this).data("ui-resizable"),c=l.options,u=l.element,d=c.containment,p=d instanceof t?d.get(0):/parent/.test(d)?u.parent().get(0):d;p&&(l.containerElement=t(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(i=t(p),s=[],t(["Top","Right","Left","Bottom"]).each(function(t,n){s[t]=e(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=t.ui.hasScroll(p,"left")?p.scrollWidth:o,h=t.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(e){var i,s,n,a,o=t(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,c=o._aspectRatio||e.shiftKey,u={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-u.left),c&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),c&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-u.left:o.offset.left-u.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-u.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=Math.abs(o.parentData.left)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,c&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,c&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.containerOffset,n=e.containerPosition,a=e.containerElement,o=t(e.helper),r=o.offset(),h=o.outerWidth()-e.sizeDiff.width,l=o.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=function(e){t(e).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css("left"),10),top:parseInt(e.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):t.each(i.alsoResize,function(t){s(t)})},resize:function(e,i){var s=t(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(e,s){t(e).each(function(){var e=t(this),n=t(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(n[e]||0)+(r[e]||0);i&&i>=0&&(a[e]=i||null)}),e.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):t.each(n.alsoResize,function(t,e){h(t,e)})},stop:function(){t(this).removeData("resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).data("ui-resizable");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).data("ui-resizable");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size,n=e.originalSize,a=e.originalPosition,o=e.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,c=Math.round((s.width-n.width)/h)*h,u=Math.round((s.height-n.height)/l)*l,d=n.width+c,p=n.height+u,f=i.maxWidth&&d>i.maxWidth,g=i.maxHeight&&p>i.maxHeight,m=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,m&&(d+=h),v&&(p+=l),f&&(d-=h),g&&(p-=l),/^(se|s|e)$/.test(o)?(e.size.width=d,e.size.height=p):/^(ne)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.top=a.top-u):/^(sw)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.left=a.left-c):(p-l>0?(e.size.height=p,e.position.top=a.top-u):(e.size.height=l,e.position.top=a.top+n.height-l),d-h>0?(e.size.width=d,e.position.left=a.left-c):(e.size.width=h,e.position.left=a.left+n.width-h))}})})(jQuery);(function(t){t.widget("ui.selectable",t.ui.mouse,{version:"1.10.4",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e=t(i.options.filter,i.element[0]),e.addClass("ui-selectee"),e.each(function(){var e=t(this),i=e.offset();t.data(this,"selectable-item",{element:this,$element:e,left:i.left,top:i.top,right:i.left+e.outerWidth(),bottom:i.top+e.outerHeight(),startselected:!1,selected:e.hasClass("ui-selected"),selecting:e.hasClass("ui-selecting"),unselecting:e.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=e.addClass("ui-selectee"),this._mouseInit(),this.helper=t("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):undefined}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=e.pageX,l=e.pageY;return a>r&&(i=r,r=a,a=i),o>l&&(i=l,l=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:l-o}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),h=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?h=!(i.left>r||a>i.right||i.top>l||o>i.bottom):"fit"===n.tolerance&&(h=i.left>a&&r>i.right&&i.top>o&&l>i.bottom),h?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}})})(jQuery);(function(t){var e=5;t.widget("ui.slider",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)o.push(a);this.handles=n.add(t(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e)})},_createRange:function(){var e=this.options,i="";e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=t("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===e.range||"max"===e.range?" ui-slider-range-"+e.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){var t=this.handles.add(this.range).filter("a");this._off(t),this._on(t,this._handleEvents),this._hoverable(t),this._focusable(t)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,a,o,r,l,h,u=this,c=this.options;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-u.values(e));(n>i||n===i&&(e===u._lastChangedValue||u.values(e)===c.min))&&(n=i,a=t(this),o=e)}),r=this._start(e,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),l=a.offset(),h=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=h?{left:0,top:0}:{left:e.pageX-l.left-a.width()/2,top:e.pageY-l.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,a;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(t,e){var i={handle:this.handles[e],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("start",t,i)},_slide:function(t,e,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(e?0:1),2===this.options.values.length&&this.options.range===!0&&(0===e&&i>s||1===e&&s>i)&&(i=s),i!==this.values(e)&&(n=this.values(),n[e]=i,a=this._trigger("slide",t,{handle:this.handles[e],value:i,values:n}),s=this.values(e?0:1),a!==!1&&this.values(e,i))):i!==this.value()&&(a=this._trigger("slide",t,{handle:this.handles[e],value:i}),a!==!1&&this.value(i))},_stop:function(t,e){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("stop",t,i)},_change:function(t,e){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._lastChangedValue=e,this._trigger("change",t,i)}},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),undefined):this._value()},values:function(e,i){var s,n,a;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),undefined;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(e):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),t.Widget.prototype._setOption.apply(this,arguments),e){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var e,i,s,n,a,o=this.options.range,r=this.options,l=this,h=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((l.values(s)-l._valueMin())/(l._valueMax()-l._valueMin())),u["horizontal"===l.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[h?"animate":"css"](u,r.animate),l.options.range===!0&&("horizontal"===l.orientation?(0===s&&l.range.stop(1,1)[h?"animate":"css"]({left:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&l.range.stop(1,1)[h?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[h?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[h?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[h?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(i){var s,n,a,o,r=t(i.target).data("ui-slider-handle-index");switch(i.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(i.preventDefault(),!this._keySliding&&(this._keySliding=!0,t(i.target).addClass("ui-state-active"),s=this._start(i,r),s===!1))return}switch(o=this.options.step,n=a=this.options.values&&this.options.values.length?this.values(r):this.value(),i.keyCode){case t.ui.keyCode.HOME:a=this._valueMin();break;case t.ui.keyCode.END:a=this._valueMax();break;case t.ui.keyCode.PAGE_UP:a=this._trimAlignValue(n+(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.PAGE_DOWN:a=this._trimAlignValue(n-(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(n===this._valueMax())return;a=this._trimAlignValue(n+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(n===this._valueMin())return;a=this._trimAlignValue(n-o)}this._slide(i,r,a)},click:function(t){t.preventDefault()},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),t(e.target).removeClass("ui-state-active"))}}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}function i(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))}t.widget("ui.sortable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var t=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===t.axis||i(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_setOption:function(e,i){"disabled"===e?(this.options[e]=i,this.widget().toggleClass("ui-sortable-disabled",!!i)):t.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):undefined}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("<style>*{ cursor: "+a.cursor+" !important; }</style>").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-t(document).scrollTop()<a.scrollSensitivity?r=t(document).scrollTop(t(document).scrollTop()-a.scrollSpeed):t(window).height()-(e.pageY-t(document).scrollTop())<a.scrollSensitivity&&(r=t(document).scrollTop(t(document).scrollTop()+a.scrollSpeed)),e.pageX-t(document).scrollLeft()<a.scrollSensitivity?r=t(document).scrollLeft(t(document).scrollLeft()-a.scrollSpeed):t(window).width()-(e.pageX-t(document).scrollLeft())<a.scrollSensitivity&&(r=t(document).scrollLeft(t(document).scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var i="x"===this.options.axis||e(this.positionAbs.top+this.offset.click.top,t.top,t.height),s="y"===this.options.axis||e(this.positionAbs.left+this.offset.click.left,t.left,t.width),n=i&&s,o=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return n?this.floating?a&&"right"===a||"down"===o?2:1:o&&("down"===o?2:1):!1},_intersectsWithSides:function(t){var i=e(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),s=e(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),n=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return this.floating&&o?"right"===o&&s||"left"===o&&!s:n&&("down"===n&&i||"up"===n&&!i)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]).addClass(i||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?e.currentItem.children().each(function(){t("<td>&#160;</td>",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,o,a,r,h,l,c,u,d,p,f=null,g=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],g=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[g].containerCache.over||(this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1);else{for(a=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],o=this.items.length-1;o>=0;o--)t.contains(this.containers[g].element[0],this.items[o].item[0])&&this.items[o].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[o].top,this.items[o].height))&&(u=this.items[o].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[o][l]-c)&&(d=!0,u+=this.items[o][l]),a>Math.abs(u-c)&&(a=Math.abs(u-c),r=this.items[o],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[g])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[g].element,!0),this._trigger("change",s,this._uiHash()),this.containers[g]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[g],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery);(function(t){function e(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.widget("ui.spinner",{version:"1.10.4",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e={},i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);void 0!==n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var t=this.element[0]===this.document[0].activeElement;t||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var t=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=t.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*t.height())&&t.height()>0&&t.height(t.height()),this.options.disabled&&this.disable()},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>&#9650;</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>&#9660;</span>"+"</a>"},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){if("culture"===t||"numberFormat"===t){var i=this._parse(this.element.val());return this.options[t]=e,this.element.val(this._format(i)),void 0}("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(e.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(e.down)),this._super(t,e),"disabled"===t&&(e?(this.element.prop("disabled",!0),this.buttons.button("disable")):(this.element.prop("disabled",!1),this.buttons.button("enable")))},_setOptions:e(function(t){this._super(t),this._value(this.element.val())}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:e(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:e(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:e(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:e(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(e(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}})})(jQuery);(function(t,e){function i(){return++n}function s(t){return t=t.cloneNode(!1),t.hash.length>1&&decodeURIComponent(t.href.replace(a,""))===decodeURIComponent(location.href.replace(a,""))}var n=0,a=/#.*$/;t.widget("ui.tabs",{version:"1.10.4",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var e=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var i=this.options.active,s=this.options.collapsible,n=location.hash.substring(1);return null===i&&(n&&this.tabs.each(function(s,a){return t(a).attr("aria-controls")===n?(i=s,!1):e}),null===i&&(i=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===i||-1===i)&&(i=this.tabs.length?0:!1)),i!==!1&&(i=this.tabs.index(this.tabs.eq(i)),-1===i&&(i=s?!1:0)),!s&&i===!1&&this.anchors.length&&(i=0),i},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(i){var s=t(this.document[0].activeElement).closest("li"),n=this.tabs.index(s),a=!0;if(!this._handlePageNav(i)){switch(i.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:n++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:a=!1,n--;break;case t.ui.keyCode.END:n=this.anchors.length-1;break;case t.ui.keyCode.HOME:n=0;break;case t.ui.keyCode.SPACE:return i.preventDefault(),clearTimeout(this.activating),this._activate(n),e;case t.ui.keyCode.ENTER:return i.preventDefault(),clearTimeout(this.activating),this._activate(n===this.options.active?!1:n),e;default:return}i.preventDefault(),clearTimeout(this.activating),n=this._focusNextTab(n,a),i.ctrlKey||(s.attr("aria-selected","false"),this.tabs.eq(n).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",n)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.focus())},_handlePageNav:function(i){return i.altKey&&i.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):i.altKey&&i.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):e},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).focus(),t},_setOption:function(t,i){return"active"===t?(this._activate(i),e):"disabled"===t?(this._setupDisabled(i),e):(this._super(t,i),"collapsible"===t&&(this.element.toggleClass("ui-tabs-collapsible",i),i||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(i),"heightStyle"===t&&this._setupHeightStyle(i),e)},_tabId:function(t){return t.attr("aria-controls")||"ui-tabs-"+i()},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=t(),this.anchors.each(function(i,n){var a,o,r,h=t(n).uniqueId().attr("id"),l=t(n).closest("li"),c=l.attr("aria-controls");s(n)?(a=n.hash,o=e.element.find(e._sanitizeSelector(a))):(r=e._tabId(l),a="#"+r,o=e.element.find(a),o.length||(o=e._createPanel(r),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),c&&l.data("ui-tabs-aria-controls",c),l.attr({"aria-controls":a.substring(1),"aria-labelledby":h}),o.attr("aria-labelledby",h)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(e){return t("<div>").attr("id",e).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(e){t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1);for(var i,s=0;i=this.tabs[s];s++)e===!0||-1!==t.inArray(s,e)?t(i).addClass("ui-state-disabled").attr("aria-disabled","true"):t(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=e},_setupEvents:function(e){var i={click:function(t){t.preventDefault()}};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?t():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():a,newPanel:h};e.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?t():a,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),e),this._toggle(e,c))},_toggle:function(e,i){function s(){a.running=!1,a._trigger("activate",e,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr({"aria-expanded":"false","aria-hidden":"true"}),i.oldTab.attr("aria-selected","false"),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr({"aria-expanded":"true","aria-hidden":"false"}),i.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(t){return"string"==typeof t&&(t=this.anchors.index(this.anchors.filter("[href$='"+t+"']"))),t},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(i){var s=this.options.disabled;s!==!1&&(i===e?s=!1:(i=this._getIndex(i),s=t.isArray(s)?t.map(s,function(t){return t!==i?t:null}):t.map(this.tabs,function(t,e){return e!==i?e:null})),this._setupDisabled(s))},disable:function(i){var s=this.options.disabled;if(s!==!0){if(i===e)s=!0;else{if(i=this._getIndex(i),-1!==t.inArray(i,s))return;s=t.isArray(s)?t.merge([i],s).sort():[i]}this._setupDisabled(s)}},load:function(e,i){e=this._getIndex(e);var n=this,a=this.tabs.eq(e),o=a.find(".ui-tabs-anchor"),r=this._getPanelForTab(a),h={tab:a,panel:r};s(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,h)),this.xhr&&"canceled"!==this.xhr.statusText&&(a.addClass("ui-tabs-loading"),r.attr("aria-busy","true"),this.xhr.success(function(t){setTimeout(function(){r.html(t),n._trigger("load",i,h)},1)}).complete(function(t,e){setTimeout(function(){"abort"===e&&n.panels.stop(!1,!0),a.removeClass("ui-tabs-loading"),r.removeAttr("aria-busy"),t===n.xhr&&delete n.xhr},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href"),beforeSend:function(e,a){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:a},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}})})(jQuery);(function(t){function e(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))}function i(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")}var s=0;t.widget("ui.tooltip",{version:"1.10.4",options:{content:function(){var e=t(this).attr("title")||"";return t("<a>").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(e,i){var s=this;return"disabled"===e?(this[i?"_disable":"_enable"](),this.options[e]=i,void 0):(this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e)}),void 0)},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.is("[title]")&&e.data("ui-tooltip-title",e.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))})},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s?this._open(e,t,s):(i=s.call(t[0],function(i){t.data("ui-tooltip-open")&&n._delay(function(){e&&(e.type=o),this._open(e,t,i)})}),i&&this._open(e,t,i),void 0)},_open:function(i,s,n){function o(t){l.of=t,a.is(":hidden")||a.position(l)}var a,r,h,l=t.extend({},this.options.position);if(n){if(a=this._find(s),a.length)return a.find(".ui-tooltip-content").html(n),void 0;s.is("[title]")&&(i&&"mouseover"===i.type?s.attr("title",""):s.removeAttr("title")),a=this._tooltip(s),e(s,a.attr("id")),a.find(".ui-tooltip-content").html(n),this.options.track&&i&&/^mouse/.test(i.type)?(this._on(this.document,{mousemove:o}),o(i)):a.position(t.extend({of:s},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.show&&this.options.show.delay&&(h=this.delayedShow=setInterval(function(){a.is(":visible")&&(o(l.of),clearInterval(h))},t.fx.interval)),this._trigger("open",i,{tooltip:a}),r={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var i=t.Event(e);i.currentTarget=s[0],this.close(i,!0)}},remove:function(){this._removeTooltip(a)}},i&&"mouseover"!==i.type||(r.mouseleave="close"),i&&"focusin"!==i.type||(r.focusout="close"),this._on(!0,s,r)}},close:function(e){var s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);this.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&n.attr("title",n.data("ui-tooltip-title")),i(n),o.stop(!0),this._hide(o,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),this.closing=!0,this._trigger("close",e,{tooltip:o}),this.closing=!1)},_tooltip:function(e){var i="ui-tooltip-"+s++,n=t("<div>").attr({id:i,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return t("<div>").addClass("ui-tooltip-content").appendTo(n),n.appendTo(this.document[0].body),this.tooltips[i]=e,n},_find:function(e){var i=e.data("ui-tooltip-id");return i?t("#"+i):t()},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0),t("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))})}})})(jQuery);
\ No newline at end of file
diff --git a/apps/static/js/jquery-ui-1.12.1.js b/apps/static/js/jquery-ui-1.12.1.js
deleted file mode 100644
index 021355237..000000000
--- a/apps/static/js/jquery-ui-1.12.1.js
+++ /dev/null
@@ -1,18706 +0,0 @@
-/*! jQuery UI - v1.12.1 - 2016-09-14
-* http://jqueryui.com
-* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js
-* Copyright jQuery Foundation and other contributors; Licensed MIT */
-
-(function( factory ) {
-	if ( typeof define === "function" && define.amd ) {
-
-		// AMD. Register as an anonymous module.
-		define([ "jquery" ], factory );
-	} else {
-
-		// Browser globals
-		factory( jQuery );
-	}
-}(function( $ ) {
-
-$.ui = $.ui || {};
-
-var version = $.ui.version = "1.12.1";
-
-
-/*!
- * jQuery UI Widget 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Widget
-//>>group: Core
-//>>description: Provides a factory for creating stateful widgets with a common API.
-//>>docs: http://api.jqueryui.com/jQuery.widget/
-//>>demos: http://jqueryui.com/widget/
-
-
-
-var widgetUuid = 0;
-var widgetSlice = Array.prototype.slice;
-
-$.cleanData = ( function( orig ) {
-	return function( elems ) {
-		var events, elem, i;
-		for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
-			try {
-
-				// Only trigger remove when necessary to save time
-				events = $._data( elem, "events" );
-				if ( events && events.remove ) {
-					$( elem ).triggerHandler( "remove" );
-				}
-
-			// Http://bugs.jquery.com/ticket/8235
-			} catch ( e ) {}
-		}
-		orig( elems );
-	};
-} )( $.cleanData );
-
-$.widget = function( name, base, prototype ) {
-	var existingConstructor, constructor, basePrototype;
-
-	// ProxiedPrototype allows the provided prototype to remain unmodified
-	// so that it can be used as a mixin for multiple widgets (#8876)
-	var proxiedPrototype = {};
-
-	var namespace = name.split( "." )[ 0 ];
-	name = name.split( "." )[ 1 ];
-	var fullName = namespace + "-" + name;
-
-	if ( !prototype ) {
-		prototype = base;
-		base = $.Widget;
-	}
-
-	if ( $.isArray( prototype ) ) {
-		prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
-	}
-
-	// Create selector for plugin
-	$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
-		return !!$.data( elem, fullName );
-	};
-
-	$[ namespace ] = $[ namespace ] || {};
-	existingConstructor = $[ namespace ][ name ];
-	constructor = $[ namespace ][ name ] = function( options, element ) {
-
-		// Allow instantiation without "new" keyword
-		if ( !this._createWidget ) {
-			return new constructor( options, element );
-		}
-
-		// Allow instantiation without initializing for simple inheritance
-		// must use "new" keyword (the code above always passes args)
-		if ( arguments.length ) {
-			this._createWidget( options, element );
-		}
-	};
-
-	// Extend with the existing constructor to carry over any static properties
-	$.extend( constructor, existingConstructor, {
-		version: prototype.version,
-
-		// Copy the object used to create the prototype in case we need to
-		// redefine the widget later
-		_proto: $.extend( {}, prototype ),
-
-		// Track widgets that inherit from this widget in case this widget is
-		// redefined after a widget inherits from it
-		_childConstructors: []
-	} );
-
-	basePrototype = new base();
-
-	// We need to make the options hash a property directly on the new instance
-	// otherwise we'll modify the options hash on the prototype that we're
-	// inheriting from
-	basePrototype.options = $.widget.extend( {}, basePrototype.options );
-	$.each( prototype, function( prop, value ) {
-		if ( !$.isFunction( value ) ) {
-			proxiedPrototype[ prop ] = value;
-			return;
-		}
-		proxiedPrototype[ prop ] = ( function() {
-			function _super() {
-				return base.prototype[ prop ].apply( this, arguments );
-			}
-
-			function _superApply( args ) {
-				return base.prototype[ prop ].apply( this, args );
-			}
-
-			return function() {
-				var __super = this._super;
-				var __superApply = this._superApply;
-				var returnValue;
-
-				this._super = _super;
-				this._superApply = _superApply;
-
-				returnValue = value.apply( this, arguments );
-
-				this._super = __super;
-				this._superApply = __superApply;
-
-				return returnValue;
-			};
-		} )();
-	} );
-	constructor.prototype = $.widget.extend( basePrototype, {
-
-		// TODO: remove support for widgetEventPrefix
-		// always use the name + a colon as the prefix, e.g., draggable:start
-		// don't prefix for widgets that aren't DOM-based
-		widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
-	}, proxiedPrototype, {
-		constructor: constructor,
-		namespace: namespace,
-		widgetName: name,
-		widgetFullName: fullName
-	} );
-
-	// If this widget is being redefined then we need to find all widgets that
-	// are inheriting from it and redefine all of them so that they inherit from
-	// the new version of this widget. We're essentially trying to replace one
-	// level in the prototype chain.
-	if ( existingConstructor ) {
-		$.each( existingConstructor._childConstructors, function( i, child ) {
-			var childPrototype = child.prototype;
-
-			// Redefine the child widget using the same prototype that was
-			// originally used, but inherit from the new version of the base
-			$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
-				child._proto );
-		} );
-
-		// Remove the list of existing child constructors from the old constructor
-		// so the old child constructors can be garbage collected
-		delete existingConstructor._childConstructors;
-	} else {
-		base._childConstructors.push( constructor );
-	}
-
-	$.widget.bridge( name, constructor );
-
-	return constructor;
-};
-
-$.widget.extend = function( target ) {
-	var input = widgetSlice.call( arguments, 1 );
-	var inputIndex = 0;
-	var inputLength = input.length;
-	var key;
-	var value;
-
-	for ( ; inputIndex < inputLength; inputIndex++ ) {
-		for ( key in input[ inputIndex ] ) {
-			value = input[ inputIndex ][ key ];
-			if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
-
-				// Clone objects
-				if ( $.isPlainObject( value ) ) {
-					target[ key ] = $.isPlainObject( target[ key ] ) ?
-						$.widget.extend( {}, target[ key ], value ) :
-
-						// Don't extend strings, arrays, etc. with objects
-						$.widget.extend( {}, value );
-
-				// Copy everything else by reference
-				} else {
-					target[ key ] = value;
-				}
-			}
-		}
-	}
-	return target;
-};
-
-$.widget.bridge = function( name, object ) {
-	var fullName = object.prototype.widgetFullName || name;
-	$.fn[ name ] = function( options ) {
-		var isMethodCall = typeof options === "string";
-		var args = widgetSlice.call( arguments, 1 );
-		var returnValue = this;
-
-		if ( isMethodCall ) {
-
-			// If this is an empty collection, we need to have the instance method
-			// return undefined instead of the jQuery instance
-			if ( !this.length && options === "instance" ) {
-				returnValue = undefined;
-			} else {
-				this.each( function() {
-					var methodValue;
-					var instance = $.data( this, fullName );
-
-					if ( options === "instance" ) {
-						returnValue = instance;
-						return false;
-					}
-
-					if ( !instance ) {
-						return $.error( "cannot call methods on " + name +
-							" prior to initialization; " +
-							"attempted to call method '" + options + "'" );
-					}
-
-					if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
-						return $.error( "no such method '" + options + "' for " + name +
-							" widget instance" );
-					}
-
-					methodValue = instance[ options ].apply( instance, args );
-
-					if ( methodValue !== instance && methodValue !== undefined ) {
-						returnValue = methodValue && methodValue.jquery ?
-							returnValue.pushStack( methodValue.get() ) :
-							methodValue;
-						return false;
-					}
-				} );
-			}
-		} else {
-
-			// Allow multiple hashes to be passed on init
-			if ( args.length ) {
-				options = $.widget.extend.apply( null, [ options ].concat( args ) );
-			}
-
-			this.each( function() {
-				var instance = $.data( this, fullName );
-				if ( instance ) {
-					instance.option( options || {} );
-					if ( instance._init ) {
-						instance._init();
-					}
-				} else {
-					$.data( this, fullName, new object( options, this ) );
-				}
-			} );
-		}
-
-		return returnValue;
-	};
-};
-
-$.Widget = function( /* options, element */ ) {};
-$.Widget._childConstructors = [];
-
-$.Widget.prototype = {
-	widgetName: "widget",
-	widgetEventPrefix: "",
-	defaultElement: "<div>",
-
-	options: {
-		classes: {},
-		disabled: false,
-
-		// Callbacks
-		create: null
-	},
-
-	_createWidget: function( options, element ) {
-		element = $( element || this.defaultElement || this )[ 0 ];
-		this.element = $( element );
-		this.uuid = widgetUuid++;
-		this.eventNamespace = "." + this.widgetName + this.uuid;
-
-		this.bindings = $();
-		this.hoverable = $();
-		this.focusable = $();
-		this.classesElementLookup = {};
-
-		if ( element !== this ) {
-			$.data( element, this.widgetFullName, this );
-			this._on( true, this.element, {
-				remove: function( event ) {
-					if ( event.target === element ) {
-						this.destroy();
-					}
-				}
-			} );
-			this.document = $( element.style ?
-
-				// Element within the document
-				element.ownerDocument :
-
-				// Element is window or document
-				element.document || element );
-			this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
-		}
-
-		this.options = $.widget.extend( {},
-			this.options,
-			this._getCreateOptions(),
-			options );
-
-		this._create();
-
-		if ( this.options.disabled ) {
-			this._setOptionDisabled( this.options.disabled );
-		}
-
-		this._trigger( "create", null, this._getCreateEventData() );
-		this._init();
-	},
-
-	_getCreateOptions: function() {
-		return {};
-	},
-
-	_getCreateEventData: $.noop,
-
-	_create: $.noop,
-
-	_init: $.noop,
-
-	destroy: function() {
-		var that = this;
-
-		this._destroy();
-		$.each( this.classesElementLookup, function( key, value ) {
-			that._removeClass( value, key );
-		} );
-
-		// We can probably remove the unbind calls in 2.0
-		// all event bindings should go through this._on()
-		this.element
-			.off( this.eventNamespace )
-			.removeData( this.widgetFullName );
-		this.widget()
-			.off( this.eventNamespace )
-			.removeAttr( "aria-disabled" );
-
-		// Clean up events and states
-		this.bindings.off( this.eventNamespace );
-	},
-
-	_destroy: $.noop,
-
-	widget: function() {
-		return this.element;
-	},
-
-	option: function( key, value ) {
-		var options = key;
-		var parts;
-		var curOption;
-		var i;
-
-		if ( arguments.length === 0 ) {
-
-			// Don't return a reference to the internal hash
-			return $.widget.extend( {}, this.options );
-		}
-
-		if ( typeof key === "string" ) {
-
-			// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
-			options = {};
-			parts = key.split( "." );
-			key = parts.shift();
-			if ( parts.length ) {
-				curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
-				for ( i = 0; i < parts.length - 1; i++ ) {
-					curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
-					curOption = curOption[ parts[ i ] ];
-				}
-				key = parts.pop();
-				if ( arguments.length === 1 ) {
-					return curOption[ key ] === undefined ? null : curOption[ key ];
-				}
-				curOption[ key ] = value;
-			} else {
-				if ( arguments.length === 1 ) {
-					return this.options[ key ] === undefined ? null : this.options[ key ];
-				}
-				options[ key ] = value;
-			}
-		}
-
-		this._setOptions( options );
-
-		return this;
-	},
-
-	_setOptions: function( options ) {
-		var key;
-
-		for ( key in options ) {
-			this._setOption( key, options[ key ] );
-		}
-
-		return this;
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "classes" ) {
-			this._setOptionClasses( value );
-		}
-
-		this.options[ key ] = value;
-
-		if ( key === "disabled" ) {
-			this._setOptionDisabled( value );
-		}
-
-		return this;
-	},
-
-	_setOptionClasses: function( value ) {
-		var classKey, elements, currentElements;
-
-		for ( classKey in value ) {
-			currentElements = this.classesElementLookup[ classKey ];
-			if ( value[ classKey ] === this.options.classes[ classKey ] ||
-					!currentElements ||
-					!currentElements.length ) {
-				continue;
-			}
-
-			// We are doing this to create a new jQuery object because the _removeClass() call
-			// on the next line is going to destroy the reference to the current elements being
-			// tracked. We need to save a copy of this collection so that we can add the new classes
-			// below.
-			elements = $( currentElements.get() );
-			this._removeClass( currentElements, classKey );
-
-			// We don't use _addClass() here, because that uses this.options.classes
-			// for generating the string of classes. We want to use the value passed in from
-			// _setOption(), this is the new value of the classes option which was passed to
-			// _setOption(). We pass this value directly to _classes().
-			elements.addClass( this._classes( {
-				element: elements,
-				keys: classKey,
-				classes: value,
-				add: true
-			} ) );
-		}
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
-
-		// If the widget is becoming disabled, then nothing is interactive
-		if ( value ) {
-			this._removeClass( this.hoverable, null, "ui-state-hover" );
-			this._removeClass( this.focusable, null, "ui-state-focus" );
-		}
-	},
-
-	enable: function() {
-		return this._setOptions( { disabled: false } );
-	},
-
-	disable: function() {
-		return this._setOptions( { disabled: true } );
-	},
-
-	_classes: function( options ) {
-		var full = [];
-		var that = this;
-
-		options = $.extend( {
-			element: this.element,
-			classes: this.options.classes || {}
-		}, options );
-
-		function processClassString( classes, checkOption ) {
-			var current, i;
-			for ( i = 0; i < classes.length; i++ ) {
-				current = that.classesElementLookup[ classes[ i ] ] || $();
-				if ( options.add ) {
-					current = $( $.unique( current.get().concat( options.element.get() ) ) );
-				} else {
-					current = $( current.not( options.element ).get() );
-				}
-				that.classesElementLookup[ classes[ i ] ] = current;
-				full.push( classes[ i ] );
-				if ( checkOption && options.classes[ classes[ i ] ] ) {
-					full.push( options.classes[ classes[ i ] ] );
-				}
-			}
-		}
-
-		this._on( options.element, {
-			"remove": "_untrackClassesElement"
-		} );
-
-		if ( options.keys ) {
-			processClassString( options.keys.match( /\S+/g ) || [], true );
-		}
-		if ( options.extra ) {
-			processClassString( options.extra.match( /\S+/g ) || [] );
-		}
-
-		return full.join( " " );
-	},
-
-	_untrackClassesElement: function( event ) {
-		var that = this;
-		$.each( that.classesElementLookup, function( key, value ) {
-			if ( $.inArray( event.target, value ) !== -1 ) {
-				that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
-			}
-		} );
-	},
-
-	_removeClass: function( element, keys, extra ) {
-		return this._toggleClass( element, keys, extra, false );
-	},
-
-	_addClass: function( element, keys, extra ) {
-		return this._toggleClass( element, keys, extra, true );
-	},
-
-	_toggleClass: function( element, keys, extra, add ) {
-		add = ( typeof add === "boolean" ) ? add : extra;
-		var shift = ( typeof element === "string" || element === null ),
-			options = {
-				extra: shift ? keys : extra,
-				keys: shift ? element : keys,
-				element: shift ? this.element : element,
-				add: add
-			};
-		options.element.toggleClass( this._classes( options ), add );
-		return this;
-	},
-
-	_on: function( suppressDisabledCheck, element, handlers ) {
-		var delegateElement;
-		var instance = this;
-
-		// No suppressDisabledCheck flag, shuffle arguments
-		if ( typeof suppressDisabledCheck !== "boolean" ) {
-			handlers = element;
-			element = suppressDisabledCheck;
-			suppressDisabledCheck = false;
-		}
-
-		// No element argument, shuffle and use this.element
-		if ( !handlers ) {
-			handlers = element;
-			element = this.element;
-			delegateElement = this.widget();
-		} else {
-			element = delegateElement = $( element );
-			this.bindings = this.bindings.add( element );
-		}
-
-		$.each( handlers, function( event, handler ) {
-			function handlerProxy() {
-
-				// Allow widgets to customize the disabled handling
-				// - disabled as an array instead of boolean
-				// - disabled class as method for disabling individual parts
-				if ( !suppressDisabledCheck &&
-						( instance.options.disabled === true ||
-						$( this ).hasClass( "ui-state-disabled" ) ) ) {
-					return;
-				}
-				return ( typeof handler === "string" ? instance[ handler ] : handler )
-					.apply( instance, arguments );
-			}
-
-			// Copy the guid so direct unbinding works
-			if ( typeof handler !== "string" ) {
-				handlerProxy.guid = handler.guid =
-					handler.guid || handlerProxy.guid || $.guid++;
-			}
-
-			var match = event.match( /^([\w:-]*)\s*(.*)$/ );
-			var eventName = match[ 1 ] + instance.eventNamespace;
-			var selector = match[ 2 ];
-
-			if ( selector ) {
-				delegateElement.on( eventName, selector, handlerProxy );
-			} else {
-				element.on( eventName, handlerProxy );
-			}
-		} );
-	},
-
-	_off: function( element, eventName ) {
-		eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
-			this.eventNamespace;
-		element.off( eventName ).off( eventName );
-
-		// Clear the stack to avoid memory leaks (#10056)
-		this.bindings = $( this.bindings.not( element ).get() );
-		this.focusable = $( this.focusable.not( element ).get() );
-		this.hoverable = $( this.hoverable.not( element ).get() );
-	},
-
-	_delay: function( handler, delay ) {
-		function handlerProxy() {
-			return ( typeof handler === "string" ? instance[ handler ] : handler )
-				.apply( instance, arguments );
-		}
-		var instance = this;
-		return setTimeout( handlerProxy, delay || 0 );
-	},
-
-	_hoverable: function( element ) {
-		this.hoverable = this.hoverable.add( element );
-		this._on( element, {
-			mouseenter: function( event ) {
-				this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
-			},
-			mouseleave: function( event ) {
-				this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
-			}
-		} );
-	},
-
-	_focusable: function( element ) {
-		this.focusable = this.focusable.add( element );
-		this._on( element, {
-			focusin: function( event ) {
-				this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
-			},
-			focusout: function( event ) {
-				this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
-			}
-		} );
-	},
-
-	_trigger: function( type, event, data ) {
-		var prop, orig;
-		var callback = this.options[ type ];
-
-		data = data || {};
-		event = $.Event( event );
-		event.type = ( type === this.widgetEventPrefix ?
-			type :
-			this.widgetEventPrefix + type ).toLowerCase();
-
-		// The original event may come from any element
-		// so we need to reset the target on the new event
-		event.target = this.element[ 0 ];
-
-		// Copy original event properties over to the new event
-		orig = event.originalEvent;
-		if ( orig ) {
-			for ( prop in orig ) {
-				if ( !( prop in event ) ) {
-					event[ prop ] = orig[ prop ];
-				}
-			}
-		}
-
-		this.element.trigger( event, data );
-		return !( $.isFunction( callback ) &&
-			callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
-			event.isDefaultPrevented() );
-	}
-};
-
-$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
-	$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
-		if ( typeof options === "string" ) {
-			options = { effect: options };
-		}
-
-		var hasOptions;
-		var effectName = !options ?
-			method :
-			options === true || typeof options === "number" ?
-				defaultEffect :
-				options.effect || defaultEffect;
-
-		options = options || {};
-		if ( typeof options === "number" ) {
-			options = { duration: options };
-		}
-
-		hasOptions = !$.isEmptyObject( options );
-		options.complete = callback;
-
-		if ( options.delay ) {
-			element.delay( options.delay );
-		}
-
-		if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
-			element[ method ]( options );
-		} else if ( effectName !== method && element[ effectName ] ) {
-			element[ effectName ]( options.duration, options.easing, callback );
-		} else {
-			element.queue( function( next ) {
-				$( this )[ method ]();
-				if ( callback ) {
-					callback.call( element[ 0 ] );
-				}
-				next();
-			} );
-		}
-	};
-} );
-
-var widget = $.widget;
-
-
-/*!
- * jQuery UI Position 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * http://api.jqueryui.com/position/
- */
-
-//>>label: Position
-//>>group: Core
-//>>description: Positions elements relative to other elements.
-//>>docs: http://api.jqueryui.com/position/
-//>>demos: http://jqueryui.com/position/
-
-
-( function() {
-var cachedScrollbarWidth,
-	max = Math.max,
-	abs = Math.abs,
-	rhorizontal = /left|center|right/,
-	rvertical = /top|center|bottom/,
-	roffset = /[\+\-]\d+(\.[\d]+)?%?/,
-	rposition = /^\w+/,
-	rpercent = /%$/,
-	_position = $.fn.position;
-
-function getOffsets( offsets, width, height ) {
-	return [
-		parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
-		parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
-	];
-}
-
-function parseCss( element, property ) {
-	return parseInt( $.css( element, property ), 10 ) || 0;
-}
-
-function getDimensions( elem ) {
-	var raw = elem[ 0 ];
-	if ( raw.nodeType === 9 ) {
-		return {
-			width: elem.width(),
-			height: elem.height(),
-			offset: { top: 0, left: 0 }
-		};
-	}
-	if ( $.isWindow( raw ) ) {
-		return {
-			width: elem.width(),
-			height: elem.height(),
-			offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
-		};
-	}
-	if ( raw.preventDefault ) {
-		return {
-			width: 0,
-			height: 0,
-			offset: { top: raw.pageY, left: raw.pageX }
-		};
-	}
-	return {
-		width: elem.outerWidth(),
-		height: elem.outerHeight(),
-		offset: elem.offset()
-	};
-}
-
-$.position = {
-	scrollbarWidth: function() {
-		if ( cachedScrollbarWidth !== undefined ) {
-			return cachedScrollbarWidth;
-		}
-		var w1, w2,
-			div = $( "<div " +
-				"style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
-				"<div style='height:100px;width:auto;'></div></div>" ),
-			innerDiv = div.children()[ 0 ];
-
-		$( "body" ).append( div );
-		w1 = innerDiv.offsetWidth;
-		div.css( "overflow", "scroll" );
-
-		w2 = innerDiv.offsetWidth;
-
-		if ( w1 === w2 ) {
-			w2 = div[ 0 ].clientWidth;
-		}
-
-		div.remove();
-
-		return ( cachedScrollbarWidth = w1 - w2 );
-	},
-	getScrollInfo: function( within ) {
-		var overflowX = within.isWindow || within.isDocument ? "" :
-				within.element.css( "overflow-x" ),
-			overflowY = within.isWindow || within.isDocument ? "" :
-				within.element.css( "overflow-y" ),
-			hasOverflowX = overflowX === "scroll" ||
-				( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
-			hasOverflowY = overflowY === "scroll" ||
-				( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
-		return {
-			width: hasOverflowY ? $.position.scrollbarWidth() : 0,
-			height: hasOverflowX ? $.position.scrollbarWidth() : 0
-		};
-	},
-	getWithinInfo: function( element ) {
-		var withinElement = $( element || window ),
-			isWindow = $.isWindow( withinElement[ 0 ] ),
-			isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
-			hasOffset = !isWindow && !isDocument;
-		return {
-			element: withinElement,
-			isWindow: isWindow,
-			isDocument: isDocument,
-			offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
-			scrollLeft: withinElement.scrollLeft(),
-			scrollTop: withinElement.scrollTop(),
-			width: withinElement.outerWidth(),
-			height: withinElement.outerHeight()
-		};
-	}
-};
-
-$.fn.position = function( options ) {
-	if ( !options || !options.of ) {
-		return _position.apply( this, arguments );
-	}
-
-	// Make a copy, we don't want to modify arguments
-	options = $.extend( {}, options );
-
-	var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
-		target = $( options.of ),
-		within = $.position.getWithinInfo( options.within ),
-		scrollInfo = $.position.getScrollInfo( within ),
-		collision = ( options.collision || "flip" ).split( " " ),
-		offsets = {};
-
-	dimensions = getDimensions( target );
-	if ( target[ 0 ].preventDefault ) {
-
-		// Force left top to allow flipping
-		options.at = "left top";
-	}
-	targetWidth = dimensions.width;
-	targetHeight = dimensions.height;
-	targetOffset = dimensions.offset;
-
-	// Clone to reuse original targetOffset later
-	basePosition = $.extend( {}, targetOffset );
-
-	// Force my and at to have valid horizontal and vertical positions
-	// if a value is missing or invalid, it will be converted to center
-	$.each( [ "my", "at" ], function() {
-		var pos = ( options[ this ] || "" ).split( " " ),
-			horizontalOffset,
-			verticalOffset;
-
-		if ( pos.length === 1 ) {
-			pos = rhorizontal.test( pos[ 0 ] ) ?
-				pos.concat( [ "center" ] ) :
-				rvertical.test( pos[ 0 ] ) ?
-					[ "center" ].concat( pos ) :
-					[ "center", "center" ];
-		}
-		pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
-		pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
-
-		// Calculate offsets
-		horizontalOffset = roffset.exec( pos[ 0 ] );
-		verticalOffset = roffset.exec( pos[ 1 ] );
-		offsets[ this ] = [
-			horizontalOffset ? horizontalOffset[ 0 ] : 0,
-			verticalOffset ? verticalOffset[ 0 ] : 0
-		];
-
-		// Reduce to just the positions without the offsets
-		options[ this ] = [
-			rposition.exec( pos[ 0 ] )[ 0 ],
-			rposition.exec( pos[ 1 ] )[ 0 ]
-		];
-	} );
-
-	// Normalize collision option
-	if ( collision.length === 1 ) {
-		collision[ 1 ] = collision[ 0 ];
-	}
-
-	if ( options.at[ 0 ] === "right" ) {
-		basePosition.left += targetWidth;
-	} else if ( options.at[ 0 ] === "center" ) {
-		basePosition.left += targetWidth / 2;
-	}
-
-	if ( options.at[ 1 ] === "bottom" ) {
-		basePosition.top += targetHeight;
-	} else if ( options.at[ 1 ] === "center" ) {
-		basePosition.top += targetHeight / 2;
-	}
-
-	atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
-	basePosition.left += atOffset[ 0 ];
-	basePosition.top += atOffset[ 1 ];
-
-	return this.each( function() {
-		var collisionPosition, using,
-			elem = $( this ),
-			elemWidth = elem.outerWidth(),
-			elemHeight = elem.outerHeight(),
-			marginLeft = parseCss( this, "marginLeft" ),
-			marginTop = parseCss( this, "marginTop" ),
-			collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
-				scrollInfo.width,
-			collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
-				scrollInfo.height,
-			position = $.extend( {}, basePosition ),
-			myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
-
-		if ( options.my[ 0 ] === "right" ) {
-			position.left -= elemWidth;
-		} else if ( options.my[ 0 ] === "center" ) {
-			position.left -= elemWidth / 2;
-		}
-
-		if ( options.my[ 1 ] === "bottom" ) {
-			position.top -= elemHeight;
-		} else if ( options.my[ 1 ] === "center" ) {
-			position.top -= elemHeight / 2;
-		}
-
-		position.left += myOffset[ 0 ];
-		position.top += myOffset[ 1 ];
-
-		collisionPosition = {
-			marginLeft: marginLeft,
-			marginTop: marginTop
-		};
-
-		$.each( [ "left", "top" ], function( i, dir ) {
-			if ( $.ui.position[ collision[ i ] ] ) {
-				$.ui.position[ collision[ i ] ][ dir ]( position, {
-					targetWidth: targetWidth,
-					targetHeight: targetHeight,
-					elemWidth: elemWidth,
-					elemHeight: elemHeight,
-					collisionPosition: collisionPosition,
-					collisionWidth: collisionWidth,
-					collisionHeight: collisionHeight,
-					offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
-					my: options.my,
-					at: options.at,
-					within: within,
-					elem: elem
-				} );
-			}
-		} );
-
-		if ( options.using ) {
-
-			// Adds feedback as second argument to using callback, if present
-			using = function( props ) {
-				var left = targetOffset.left - position.left,
-					right = left + targetWidth - elemWidth,
-					top = targetOffset.top - position.top,
-					bottom = top + targetHeight - elemHeight,
-					feedback = {
-						target: {
-							element: target,
-							left: targetOffset.left,
-							top: targetOffset.top,
-							width: targetWidth,
-							height: targetHeight
-						},
-						element: {
-							element: elem,
-							left: position.left,
-							top: position.top,
-							width: elemWidth,
-							height: elemHeight
-						},
-						horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
-						vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
-					};
-				if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
-					feedback.horizontal = "center";
-				}
-				if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
-					feedback.vertical = "middle";
-				}
-				if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
-					feedback.important = "horizontal";
-				} else {
-					feedback.important = "vertical";
-				}
-				options.using.call( this, props, feedback );
-			};
-		}
-
-		elem.offset( $.extend( position, { using: using } ) );
-	} );
-};
-
-$.ui.position = {
-	fit: {
-		left: function( position, data ) {
-			var within = data.within,
-				withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
-				outerWidth = within.width,
-				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
-				overLeft = withinOffset - collisionPosLeft,
-				overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
-				newOverRight;
-
-			// Element is wider than within
-			if ( data.collisionWidth > outerWidth ) {
-
-				// Element is initially over the left side of within
-				if ( overLeft > 0 && overRight <= 0 ) {
-					newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
-						withinOffset;
-					position.left += overLeft - newOverRight;
-
-				// Element is initially over right side of within
-				} else if ( overRight > 0 && overLeft <= 0 ) {
-					position.left = withinOffset;
-
-				// Element is initially over both left and right sides of within
-				} else {
-					if ( overLeft > overRight ) {
-						position.left = withinOffset + outerWidth - data.collisionWidth;
-					} else {
-						position.left = withinOffset;
-					}
-				}
-
-			// Too far left -> align with left edge
-			} else if ( overLeft > 0 ) {
-				position.left += overLeft;
-
-			// Too far right -> align with right edge
-			} else if ( overRight > 0 ) {
-				position.left -= overRight;
-
-			// Adjust based on position and margin
-			} else {
-				position.left = max( position.left - collisionPosLeft, position.left );
-			}
-		},
-		top: function( position, data ) {
-			var within = data.within,
-				withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
-				outerHeight = data.within.height,
-				collisionPosTop = position.top - data.collisionPosition.marginTop,
-				overTop = withinOffset - collisionPosTop,
-				overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
-				newOverBottom;
-
-			// Element is taller than within
-			if ( data.collisionHeight > outerHeight ) {
-
-				// Element is initially over the top of within
-				if ( overTop > 0 && overBottom <= 0 ) {
-					newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
-						withinOffset;
-					position.top += overTop - newOverBottom;
-
-				// Element is initially over bottom of within
-				} else if ( overBottom > 0 && overTop <= 0 ) {
-					position.top = withinOffset;
-
-				// Element is initially over both top and bottom of within
-				} else {
-					if ( overTop > overBottom ) {
-						position.top = withinOffset + outerHeight - data.collisionHeight;
-					} else {
-						position.top = withinOffset;
-					}
-				}
-
-			// Too far up -> align with top
-			} else if ( overTop > 0 ) {
-				position.top += overTop;
-
-			// Too far down -> align with bottom edge
-			} else if ( overBottom > 0 ) {
-				position.top -= overBottom;
-
-			// Adjust based on position and margin
-			} else {
-				position.top = max( position.top - collisionPosTop, position.top );
-			}
-		}
-	},
-	flip: {
-		left: function( position, data ) {
-			var within = data.within,
-				withinOffset = within.offset.left + within.scrollLeft,
-				outerWidth = within.width,
-				offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
-				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
-				overLeft = collisionPosLeft - offsetLeft,
-				overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
-				myOffset = data.my[ 0 ] === "left" ?
-					-data.elemWidth :
-					data.my[ 0 ] === "right" ?
-						data.elemWidth :
-						0,
-				atOffset = data.at[ 0 ] === "left" ?
-					data.targetWidth :
-					data.at[ 0 ] === "right" ?
-						-data.targetWidth :
-						0,
-				offset = -2 * data.offset[ 0 ],
-				newOverRight,
-				newOverLeft;
-
-			if ( overLeft < 0 ) {
-				newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
-					outerWidth - withinOffset;
-				if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
-					position.left += myOffset + atOffset + offset;
-				}
-			} else if ( overRight > 0 ) {
-				newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
-					atOffset + offset - offsetLeft;
-				if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
-					position.left += myOffset + atOffset + offset;
-				}
-			}
-		},
-		top: function( position, data ) {
-			var within = data.within,
-				withinOffset = within.offset.top + within.scrollTop,
-				outerHeight = within.height,
-				offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
-				collisionPosTop = position.top - data.collisionPosition.marginTop,
-				overTop = collisionPosTop - offsetTop,
-				overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
-				top = data.my[ 1 ] === "top",
-				myOffset = top ?
-					-data.elemHeight :
-					data.my[ 1 ] === "bottom" ?
-						data.elemHeight :
-						0,
-				atOffset = data.at[ 1 ] === "top" ?
-					data.targetHeight :
-					data.at[ 1 ] === "bottom" ?
-						-data.targetHeight :
-						0,
-				offset = -2 * data.offset[ 1 ],
-				newOverTop,
-				newOverBottom;
-			if ( overTop < 0 ) {
-				newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
-					outerHeight - withinOffset;
-				if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
-					position.top += myOffset + atOffset + offset;
-				}
-			} else if ( overBottom > 0 ) {
-				newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
-					offset - offsetTop;
-				if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
-					position.top += myOffset + atOffset + offset;
-				}
-			}
-		}
-	},
-	flipfit: {
-		left: function() {
-			$.ui.position.flip.left.apply( this, arguments );
-			$.ui.position.fit.left.apply( this, arguments );
-		},
-		top: function() {
-			$.ui.position.flip.top.apply( this, arguments );
-			$.ui.position.fit.top.apply( this, arguments );
-		}
-	}
-};
-
-} )();
-
-var position = $.ui.position;
-
-
-/*!
- * jQuery UI :data 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: :data Selector
-//>>group: Core
-//>>description: Selects elements which have data stored under the specified key.
-//>>docs: http://api.jqueryui.com/data-selector/
-
-
-var data = $.extend( $.expr[ ":" ], {
-	data: $.expr.createPseudo ?
-		$.expr.createPseudo( function( dataName ) {
-			return function( elem ) {
-				return !!$.data( elem, dataName );
-			};
-		} ) :
-
-		// Support: jQuery <1.8
-		function( elem, i, match ) {
-			return !!$.data( elem, match[ 3 ] );
-		}
-} );
-
-/*!
- * jQuery UI Disable Selection 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: disableSelection
-//>>group: Core
-//>>description: Disable selection of text content within the set of matched elements.
-//>>docs: http://api.jqueryui.com/disableSelection/
-
-// This file is deprecated
-
-
-var disableSelection = $.fn.extend( {
-	disableSelection: ( function() {
-		var eventType = "onselectstart" in document.createElement( "div" ) ?
-			"selectstart" :
-			"mousedown";
-
-		return function() {
-			return this.on( eventType + ".ui-disableSelection", function( event ) {
-				event.preventDefault();
-			} );
-		};
-	} )(),
-
-	enableSelection: function() {
-		return this.off( ".ui-disableSelection" );
-	}
-} );
-
-
-/*!
- * jQuery UI Effects 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Effects Core
-//>>group: Effects
-// jscs:disable maximumLineLength
-//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
-// jscs:enable maximumLineLength
-//>>docs: http://api.jqueryui.com/category/effects-core/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var dataSpace = "ui-effects-",
-	dataSpaceStyle = "ui-effects-style",
-	dataSpaceAnimated = "ui-effects-animated",
-
-	// Create a local jQuery because jQuery Color relies on it and the
-	// global may not exist with AMD and a custom build (#10199)
-	jQuery = $;
-
-$.effects = {
-	effect: {}
-};
-
-/*!
- * jQuery Color Animations v2.1.2
- * https://github.com/jquery/jquery-color
- *
- * Copyright 2014 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * Date: Wed Jan 16 08:47:09 2013 -0600
- */
-( function( jQuery, undefined ) {
-
-	var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " +
-		"borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
-
-	// Plusequals test for += 100 -= 100
-	rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
-
-	// A set of RE's that can match strings and generate color tuples.
-	stringParsers = [ {
-			re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
-			parse: function( execResult ) {
-				return [
-					execResult[ 1 ],
-					execResult[ 2 ],
-					execResult[ 3 ],
-					execResult[ 4 ]
-				];
-			}
-		}, {
-			re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
-			parse: function( execResult ) {
-				return [
-					execResult[ 1 ] * 2.55,
-					execResult[ 2 ] * 2.55,
-					execResult[ 3 ] * 2.55,
-					execResult[ 4 ]
-				];
-			}
-		}, {
-
-			// This regex ignores A-F because it's compared against an already lowercased string
-			re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
-			parse: function( execResult ) {
-				return [
-					parseInt( execResult[ 1 ], 16 ),
-					parseInt( execResult[ 2 ], 16 ),
-					parseInt( execResult[ 3 ], 16 )
-				];
-			}
-		}, {
-
-			// This regex ignores A-F because it's compared against an already lowercased string
-			re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
-			parse: function( execResult ) {
-				return [
-					parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
-					parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
-					parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
-				];
-			}
-		}, {
-			re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
-			space: "hsla",
-			parse: function( execResult ) {
-				return [
-					execResult[ 1 ],
-					execResult[ 2 ] / 100,
-					execResult[ 3 ] / 100,
-					execResult[ 4 ]
-				];
-			}
-		} ],
-
-	// JQuery.Color( )
-	color = jQuery.Color = function( color, green, blue, alpha ) {
-		return new jQuery.Color.fn.parse( color, green, blue, alpha );
-	},
-	spaces = {
-		rgba: {
-			props: {
-				red: {
-					idx: 0,
-					type: "byte"
-				},
-				green: {
-					idx: 1,
-					type: "byte"
-				},
-				blue: {
-					idx: 2,
-					type: "byte"
-				}
-			}
-		},
-
-		hsla: {
-			props: {
-				hue: {
-					idx: 0,
-					type: "degrees"
-				},
-				saturation: {
-					idx: 1,
-					type: "percent"
-				},
-				lightness: {
-					idx: 2,
-					type: "percent"
-				}
-			}
-		}
-	},
-	propTypes = {
-		"byte": {
-			floor: true,
-			max: 255
-		},
-		"percent": {
-			max: 1
-		},
-		"degrees": {
-			mod: 360,
-			floor: true
-		}
-	},
-	support = color.support = {},
-
-	// Element for support tests
-	supportElem = jQuery( "<p>" )[ 0 ],
-
-	// Colors = jQuery.Color.names
-	colors,
-
-	// Local aliases of functions called often
-	each = jQuery.each;
-
-// Determine rgba support immediately
-supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
-support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
-
-// Define cache name and alpha properties
-// for rgba and hsla spaces
-each( spaces, function( spaceName, space ) {
-	space.cache = "_" + spaceName;
-	space.props.alpha = {
-		idx: 3,
-		type: "percent",
-		def: 1
-	};
-} );
-
-function clamp( value, prop, allowEmpty ) {
-	var type = propTypes[ prop.type ] || {};
-
-	if ( value == null ) {
-		return ( allowEmpty || !prop.def ) ? null : prop.def;
-	}
-
-	// ~~ is an short way of doing floor for positive numbers
-	value = type.floor ? ~~value : parseFloat( value );
-
-	// IE will pass in empty strings as value for alpha,
-	// which will hit this case
-	if ( isNaN( value ) ) {
-		return prop.def;
-	}
-
-	if ( type.mod ) {
-
-		// We add mod before modding to make sure that negatives values
-		// get converted properly: -10 -> 350
-		return ( value + type.mod ) % type.mod;
-	}
-
-	// For now all property types without mod have min and max
-	return 0 > value ? 0 : type.max < value ? type.max : value;
-}
-
-function stringParse( string ) {
-	var inst = color(),
-		rgba = inst._rgba = [];
-
-	string = string.toLowerCase();
-
-	each( stringParsers, function( i, parser ) {
-		var parsed,
-			match = parser.re.exec( string ),
-			values = match && parser.parse( match ),
-			spaceName = parser.space || "rgba";
-
-		if ( values ) {
-			parsed = inst[ spaceName ]( values );
-
-			// If this was an rgba parse the assignment might happen twice
-			// oh well....
-			inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
-			rgba = inst._rgba = parsed._rgba;
-
-			// Exit each( stringParsers ) here because we matched
-			return false;
-		}
-	} );
-
-	// Found a stringParser that handled it
-	if ( rgba.length ) {
-
-		// If this came from a parsed string, force "transparent" when alpha is 0
-		// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
-		if ( rgba.join() === "0,0,0,0" ) {
-			jQuery.extend( rgba, colors.transparent );
-		}
-		return inst;
-	}
-
-	// Named colors
-	return colors[ string ];
-}
-
-color.fn = jQuery.extend( color.prototype, {
-	parse: function( red, green, blue, alpha ) {
-		if ( red === undefined ) {
-			this._rgba = [ null, null, null, null ];
-			return this;
-		}
-		if ( red.jquery || red.nodeType ) {
-			red = jQuery( red ).css( green );
-			green = undefined;
-		}
-
-		var inst = this,
-			type = jQuery.type( red ),
-			rgba = this._rgba = [];
-
-		// More than 1 argument specified - assume ( red, green, blue, alpha )
-		if ( green !== undefined ) {
-			red = [ red, green, blue, alpha ];
-			type = "array";
-		}
-
-		if ( type === "string" ) {
-			return this.parse( stringParse( red ) || colors._default );
-		}
-
-		if ( type === "array" ) {
-			each( spaces.rgba.props, function( key, prop ) {
-				rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
-			} );
-			return this;
-		}
-
-		if ( type === "object" ) {
-			if ( red instanceof color ) {
-				each( spaces, function( spaceName, space ) {
-					if ( red[ space.cache ] ) {
-						inst[ space.cache ] = red[ space.cache ].slice();
-					}
-				} );
-			} else {
-				each( spaces, function( spaceName, space ) {
-					var cache = space.cache;
-					each( space.props, function( key, prop ) {
-
-						// If the cache doesn't exist, and we know how to convert
-						if ( !inst[ cache ] && space.to ) {
-
-							// If the value was null, we don't need to copy it
-							// if the key was alpha, we don't need to copy it either
-							if ( key === "alpha" || red[ key ] == null ) {
-								return;
-							}
-							inst[ cache ] = space.to( inst._rgba );
-						}
-
-						// This is the only case where we allow nulls for ALL properties.
-						// call clamp with alwaysAllowEmpty
-						inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
-					} );
-
-					// Everything defined but alpha?
-					if ( inst[ cache ] &&
-							jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
-
-						// Use the default of 1
-						inst[ cache ][ 3 ] = 1;
-						if ( space.from ) {
-							inst._rgba = space.from( inst[ cache ] );
-						}
-					}
-				} );
-			}
-			return this;
-		}
-	},
-	is: function( compare ) {
-		var is = color( compare ),
-			same = true,
-			inst = this;
-
-		each( spaces, function( _, space ) {
-			var localCache,
-				isCache = is[ space.cache ];
-			if ( isCache ) {
-				localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
-				each( space.props, function( _, prop ) {
-					if ( isCache[ prop.idx ] != null ) {
-						same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
-						return same;
-					}
-				} );
-			}
-			return same;
-		} );
-		return same;
-	},
-	_space: function() {
-		var used = [],
-			inst = this;
-		each( spaces, function( spaceName, space ) {
-			if ( inst[ space.cache ] ) {
-				used.push( spaceName );
-			}
-		} );
-		return used.pop();
-	},
-	transition: function( other, distance ) {
-		var end = color( other ),
-			spaceName = end._space(),
-			space = spaces[ spaceName ],
-			startColor = this.alpha() === 0 ? color( "transparent" ) : this,
-			start = startColor[ space.cache ] || space.to( startColor._rgba ),
-			result = start.slice();
-
-		end = end[ space.cache ];
-		each( space.props, function( key, prop ) {
-			var index = prop.idx,
-				startValue = start[ index ],
-				endValue = end[ index ],
-				type = propTypes[ prop.type ] || {};
-
-			// If null, don't override start value
-			if ( endValue === null ) {
-				return;
-			}
-
-			// If null - use end
-			if ( startValue === null ) {
-				result[ index ] = endValue;
-			} else {
-				if ( type.mod ) {
-					if ( endValue - startValue > type.mod / 2 ) {
-						startValue += type.mod;
-					} else if ( startValue - endValue > type.mod / 2 ) {
-						startValue -= type.mod;
-					}
-				}
-				result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
-			}
-		} );
-		return this[ spaceName ]( result );
-	},
-	blend: function( opaque ) {
-
-		// If we are already opaque - return ourself
-		if ( this._rgba[ 3 ] === 1 ) {
-			return this;
-		}
-
-		var rgb = this._rgba.slice(),
-			a = rgb.pop(),
-			blend = color( opaque )._rgba;
-
-		return color( jQuery.map( rgb, function( v, i ) {
-			return ( 1 - a ) * blend[ i ] + a * v;
-		} ) );
-	},
-	toRgbaString: function() {
-		var prefix = "rgba(",
-			rgba = jQuery.map( this._rgba, function( v, i ) {
-				return v == null ? ( i > 2 ? 1 : 0 ) : v;
-			} );
-
-		if ( rgba[ 3 ] === 1 ) {
-			rgba.pop();
-			prefix = "rgb(";
-		}
-
-		return prefix + rgba.join() + ")";
-	},
-	toHslaString: function() {
-		var prefix = "hsla(",
-			hsla = jQuery.map( this.hsla(), function( v, i ) {
-				if ( v == null ) {
-					v = i > 2 ? 1 : 0;
-				}
-
-				// Catch 1 and 2
-				if ( i && i < 3 ) {
-					v = Math.round( v * 100 ) + "%";
-				}
-				return v;
-			} );
-
-		if ( hsla[ 3 ] === 1 ) {
-			hsla.pop();
-			prefix = "hsl(";
-		}
-		return prefix + hsla.join() + ")";
-	},
-	toHexString: function( includeAlpha ) {
-		var rgba = this._rgba.slice(),
-			alpha = rgba.pop();
-
-		if ( includeAlpha ) {
-			rgba.push( ~~( alpha * 255 ) );
-		}
-
-		return "#" + jQuery.map( rgba, function( v ) {
-
-			// Default to 0 when nulls exist
-			v = ( v || 0 ).toString( 16 );
-			return v.length === 1 ? "0" + v : v;
-		} ).join( "" );
-	},
-	toString: function() {
-		return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
-	}
-} );
-color.fn.parse.prototype = color.fn;
-
-// Hsla conversions adapted from:
-// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
-
-function hue2rgb( p, q, h ) {
-	h = ( h + 1 ) % 1;
-	if ( h * 6 < 1 ) {
-		return p + ( q - p ) * h * 6;
-	}
-	if ( h * 2 < 1 ) {
-		return q;
-	}
-	if ( h * 3 < 2 ) {
-		return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
-	}
-	return p;
-}
-
-spaces.hsla.to = function( rgba ) {
-	if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
-		return [ null, null, null, rgba[ 3 ] ];
-	}
-	var r = rgba[ 0 ] / 255,
-		g = rgba[ 1 ] / 255,
-		b = rgba[ 2 ] / 255,
-		a = rgba[ 3 ],
-		max = Math.max( r, g, b ),
-		min = Math.min( r, g, b ),
-		diff = max - min,
-		add = max + min,
-		l = add * 0.5,
-		h, s;
-
-	if ( min === max ) {
-		h = 0;
-	} else if ( r === max ) {
-		h = ( 60 * ( g - b ) / diff ) + 360;
-	} else if ( g === max ) {
-		h = ( 60 * ( b - r ) / diff ) + 120;
-	} else {
-		h = ( 60 * ( r - g ) / diff ) + 240;
-	}
-
-	// Chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
-	// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
-	if ( diff === 0 ) {
-		s = 0;
-	} else if ( l <= 0.5 ) {
-		s = diff / add;
-	} else {
-		s = diff / ( 2 - add );
-	}
-	return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ];
-};
-
-spaces.hsla.from = function( hsla ) {
-	if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
-		return [ null, null, null, hsla[ 3 ] ];
-	}
-	var h = hsla[ 0 ] / 360,
-		s = hsla[ 1 ],
-		l = hsla[ 2 ],
-		a = hsla[ 3 ],
-		q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
-		p = 2 * l - q;
-
-	return [
-		Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
-		Math.round( hue2rgb( p, q, h ) * 255 ),
-		Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
-		a
-	];
-};
-
-each( spaces, function( spaceName, space ) {
-	var props = space.props,
-		cache = space.cache,
-		to = space.to,
-		from = space.from;
-
-	// Makes rgba() and hsla()
-	color.fn[ spaceName ] = function( value ) {
-
-		// Generate a cache for this space if it doesn't exist
-		if ( to && !this[ cache ] ) {
-			this[ cache ] = to( this._rgba );
-		}
-		if ( value === undefined ) {
-			return this[ cache ].slice();
-		}
-
-		var ret,
-			type = jQuery.type( value ),
-			arr = ( type === "array" || type === "object" ) ? value : arguments,
-			local = this[ cache ].slice();
-
-		each( props, function( key, prop ) {
-			var val = arr[ type === "object" ? key : prop.idx ];
-			if ( val == null ) {
-				val = local[ prop.idx ];
-			}
-			local[ prop.idx ] = clamp( val, prop );
-		} );
-
-		if ( from ) {
-			ret = color( from( local ) );
-			ret[ cache ] = local;
-			return ret;
-		} else {
-			return color( local );
-		}
-	};
-
-	// Makes red() green() blue() alpha() hue() saturation() lightness()
-	each( props, function( key, prop ) {
-
-		// Alpha is included in more than one space
-		if ( color.fn[ key ] ) {
-			return;
-		}
-		color.fn[ key ] = function( value ) {
-			var vtype = jQuery.type( value ),
-				fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
-				local = this[ fn ](),
-				cur = local[ prop.idx ],
-				match;
-
-			if ( vtype === "undefined" ) {
-				return cur;
-			}
-
-			if ( vtype === "function" ) {
-				value = value.call( this, cur );
-				vtype = jQuery.type( value );
-			}
-			if ( value == null && prop.empty ) {
-				return this;
-			}
-			if ( vtype === "string" ) {
-				match = rplusequals.exec( value );
-				if ( match ) {
-					value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
-				}
-			}
-			local[ prop.idx ] = value;
-			return this[ fn ]( local );
-		};
-	} );
-} );
-
-// Add cssHook and .fx.step function for each named hook.
-// accept a space separated string of properties
-color.hook = function( hook ) {
-	var hooks = hook.split( " " );
-	each( hooks, function( i, hook ) {
-		jQuery.cssHooks[ hook ] = {
-			set: function( elem, value ) {
-				var parsed, curElem,
-					backgroundColor = "";
-
-				if ( value !== "transparent" && ( jQuery.type( value ) !== "string" ||
-						( parsed = stringParse( value ) ) ) ) {
-					value = color( parsed || value );
-					if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
-						curElem = hook === "backgroundColor" ? elem.parentNode : elem;
-						while (
-							( backgroundColor === "" || backgroundColor === "transparent" ) &&
-							curElem && curElem.style
-						) {
-							try {
-								backgroundColor = jQuery.css( curElem, "backgroundColor" );
-								curElem = curElem.parentNode;
-							} catch ( e ) {
-							}
-						}
-
-						value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
-							backgroundColor :
-							"_default" );
-					}
-
-					value = value.toRgbaString();
-				}
-				try {
-					elem.style[ hook ] = value;
-				} catch ( e ) {
-
-					// Wrapped to prevent IE from throwing errors on "invalid" values like
-					// 'auto' or 'inherit'
-				}
-			}
-		};
-		jQuery.fx.step[ hook ] = function( fx ) {
-			if ( !fx.colorInit ) {
-				fx.start = color( fx.elem, hook );
-				fx.end = color( fx.end );
-				fx.colorInit = true;
-			}
-			jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
-		};
-	} );
-
-};
-
-color.hook( stepHooks );
-
-jQuery.cssHooks.borderColor = {
-	expand: function( value ) {
-		var expanded = {};
-
-		each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
-			expanded[ "border" + part + "Color" ] = value;
-		} );
-		return expanded;
-	}
-};
-
-// Basic color names only.
-// Usage of any of the other color names requires adding yourself or including
-// jquery.color.svg-names.js.
-colors = jQuery.Color.names = {
-
-	// 4.1. Basic color keywords
-	aqua: "#00ffff",
-	black: "#000000",
-	blue: "#0000ff",
-	fuchsia: "#ff00ff",
-	gray: "#808080",
-	green: "#008000",
-	lime: "#00ff00",
-	maroon: "#800000",
-	navy: "#000080",
-	olive: "#808000",
-	purple: "#800080",
-	red: "#ff0000",
-	silver: "#c0c0c0",
-	teal: "#008080",
-	white: "#ffffff",
-	yellow: "#ffff00",
-
-	// 4.2.3. "transparent" color keyword
-	transparent: [ null, null, null, 0 ],
-
-	_default: "#ffffff"
-};
-
-} )( jQuery );
-
-/******************************************************************************/
-/****************************** CLASS ANIMATIONS ******************************/
-/******************************************************************************/
-( function() {
-
-var classAnimationActions = [ "add", "remove", "toggle" ],
-	shorthandStyles = {
-		border: 1,
-		borderBottom: 1,
-		borderColor: 1,
-		borderLeft: 1,
-		borderRight: 1,
-		borderTop: 1,
-		borderWidth: 1,
-		margin: 1,
-		padding: 1
-	};
-
-$.each(
-	[ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ],
-	function( _, prop ) {
-		$.fx.step[ prop ] = function( fx ) {
-			if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
-				jQuery.style( fx.elem, prop, fx.end );
-				fx.setAttr = true;
-			}
-		};
-	}
-);
-
-function getElementStyles( elem ) {
-	var key, len,
-		style = elem.ownerDocument.defaultView ?
-			elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
-			elem.currentStyle,
-		styles = {};
-
-	if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
-		len = style.length;
-		while ( len-- ) {
-			key = style[ len ];
-			if ( typeof style[ key ] === "string" ) {
-				styles[ $.camelCase( key ) ] = style[ key ];
-			}
-		}
-
-	// Support: Opera, IE <9
-	} else {
-		for ( key in style ) {
-			if ( typeof style[ key ] === "string" ) {
-				styles[ key ] = style[ key ];
-			}
-		}
-	}
-
-	return styles;
-}
-
-function styleDifference( oldStyle, newStyle ) {
-	var diff = {},
-		name, value;
-
-	for ( name in newStyle ) {
-		value = newStyle[ name ];
-		if ( oldStyle[ name ] !== value ) {
-			if ( !shorthandStyles[ name ] ) {
-				if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
-					diff[ name ] = value;
-				}
-			}
-		}
-	}
-
-	return diff;
-}
-
-// Support: jQuery <1.8
-if ( !$.fn.addBack ) {
-	$.fn.addBack = function( selector ) {
-		return this.add( selector == null ?
-			this.prevObject : this.prevObject.filter( selector )
-		);
-	};
-}
-
-$.effects.animateClass = function( value, duration, easing, callback ) {
-	var o = $.speed( duration, easing, callback );
-
-	return this.queue( function() {
-		var animated = $( this ),
-			baseClass = animated.attr( "class" ) || "",
-			applyClassChange,
-			allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
-
-		// Map the animated objects to store the original styles.
-		allAnimations = allAnimations.map( function() {
-			var el = $( this );
-			return {
-				el: el,
-				start: getElementStyles( this )
-			};
-		} );
-
-		// Apply class change
-		applyClassChange = function() {
-			$.each( classAnimationActions, function( i, action ) {
-				if ( value[ action ] ) {
-					animated[ action + "Class" ]( value[ action ] );
-				}
-			} );
-		};
-		applyClassChange();
-
-		// Map all animated objects again - calculate new styles and diff
-		allAnimations = allAnimations.map( function() {
-			this.end = getElementStyles( this.el[ 0 ] );
-			this.diff = styleDifference( this.start, this.end );
-			return this;
-		} );
-
-		// Apply original class
-		animated.attr( "class", baseClass );
-
-		// Map all animated objects again - this time collecting a promise
-		allAnimations = allAnimations.map( function() {
-			var styleInfo = this,
-				dfd = $.Deferred(),
-				opts = $.extend( {}, o, {
-					queue: false,
-					complete: function() {
-						dfd.resolve( styleInfo );
-					}
-				} );
-
-			this.el.animate( this.diff, opts );
-			return dfd.promise();
-		} );
-
-		// Once all animations have completed:
-		$.when.apply( $, allAnimations.get() ).done( function() {
-
-			// Set the final class
-			applyClassChange();
-
-			// For each animated element,
-			// clear all css properties that were animated
-			$.each( arguments, function() {
-				var el = this.el;
-				$.each( this.diff, function( key ) {
-					el.css( key, "" );
-				} );
-			} );
-
-			// This is guarnteed to be there if you use jQuery.speed()
-			// it also handles dequeuing the next anim...
-			o.complete.call( animated[ 0 ] );
-		} );
-	} );
-};
-
-$.fn.extend( {
-	addClass: ( function( orig ) {
-		return function( classNames, speed, easing, callback ) {
-			return speed ?
-				$.effects.animateClass.call( this,
-					{ add: classNames }, speed, easing, callback ) :
-				orig.apply( this, arguments );
-		};
-	} )( $.fn.addClass ),
-
-	removeClass: ( function( orig ) {
-		return function( classNames, speed, easing, callback ) {
-			return arguments.length > 1 ?
-				$.effects.animateClass.call( this,
-					{ remove: classNames }, speed, easing, callback ) :
-				orig.apply( this, arguments );
-		};
-	} )( $.fn.removeClass ),
-
-	toggleClass: ( function( orig ) {
-		return function( classNames, force, speed, easing, callback ) {
-			if ( typeof force === "boolean" || force === undefined ) {
-				if ( !speed ) {
-
-					// Without speed parameter
-					return orig.apply( this, arguments );
-				} else {
-					return $.effects.animateClass.call( this,
-						( force ? { add: classNames } : { remove: classNames } ),
-						speed, easing, callback );
-				}
-			} else {
-
-				// Without force parameter
-				return $.effects.animateClass.call( this,
-					{ toggle: classNames }, force, speed, easing );
-			}
-		};
-	} )( $.fn.toggleClass ),
-
-	switchClass: function( remove, add, speed, easing, callback ) {
-		return $.effects.animateClass.call( this, {
-			add: add,
-			remove: remove
-		}, speed, easing, callback );
-	}
-} );
-
-} )();
-
-/******************************************************************************/
-/*********************************** EFFECTS **********************************/
-/******************************************************************************/
-
-( function() {
-
-if ( $.expr && $.expr.filters && $.expr.filters.animated ) {
-	$.expr.filters.animated = ( function( orig ) {
-		return function( elem ) {
-			return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
-		};
-	} )( $.expr.filters.animated );
-}
-
-if ( $.uiBackCompat !== false ) {
-	$.extend( $.effects, {
-
-		// Saves a set of properties in a data storage
-		save: function( element, set ) {
-			var i = 0, length = set.length;
-			for ( ; i < length; i++ ) {
-				if ( set[ i ] !== null ) {
-					element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
-				}
-			}
-		},
-
-		// Restores a set of previously saved properties from a data storage
-		restore: function( element, set ) {
-			var val, i = 0, length = set.length;
-			for ( ; i < length; i++ ) {
-				if ( set[ i ] !== null ) {
-					val = element.data( dataSpace + set[ i ] );
-					element.css( set[ i ], val );
-				}
-			}
-		},
-
-		setMode: function( el, mode ) {
-			if ( mode === "toggle" ) {
-				mode = el.is( ":hidden" ) ? "show" : "hide";
-			}
-			return mode;
-		},
-
-		// Wraps the element around a wrapper that copies position properties
-		createWrapper: function( element ) {
-
-			// If the element is already wrapped, return it
-			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
-				return element.parent();
-			}
-
-			// Wrap the element
-			var props = {
-					width: element.outerWidth( true ),
-					height: element.outerHeight( true ),
-					"float": element.css( "float" )
-				},
-				wrapper = $( "<div></div>" )
-					.addClass( "ui-effects-wrapper" )
-					.css( {
-						fontSize: "100%",
-						background: "transparent",
-						border: "none",
-						margin: 0,
-						padding: 0
-					} ),
-
-				// Store the size in case width/height are defined in % - Fixes #5245
-				size = {
-					width: element.width(),
-					height: element.height()
-				},
-				active = document.activeElement;
-
-			// Support: Firefox
-			// Firefox incorrectly exposes anonymous content
-			// https://bugzilla.mozilla.org/show_bug.cgi?id=561664
-			try {
-				active.id;
-			} catch ( e ) {
-				active = document.body;
-			}
-
-			element.wrap( wrapper );
-
-			// Fixes #7595 - Elements lose focus when wrapped.
-			if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
-				$( active ).trigger( "focus" );
-			}
-
-			// Hotfix for jQuery 1.4 since some change in wrap() seems to actually
-			// lose the reference to the wrapped element
-			wrapper = element.parent();
-
-			// Transfer positioning properties to the wrapper
-			if ( element.css( "position" ) === "static" ) {
-				wrapper.css( { position: "relative" } );
-				element.css( { position: "relative" } );
-			} else {
-				$.extend( props, {
-					position: element.css( "position" ),
-					zIndex: element.css( "z-index" )
-				} );
-				$.each( [ "top", "left", "bottom", "right" ], function( i, pos ) {
-					props[ pos ] = element.css( pos );
-					if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
-						props[ pos ] = "auto";
-					}
-				} );
-				element.css( {
-					position: "relative",
-					top: 0,
-					left: 0,
-					right: "auto",
-					bottom: "auto"
-				} );
-			}
-			element.css( size );
-
-			return wrapper.css( props ).show();
-		},
-
-		removeWrapper: function( element ) {
-			var active = document.activeElement;
-
-			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
-				element.parent().replaceWith( element );
-
-				// Fixes #7595 - Elements lose focus when wrapped.
-				if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
-					$( active ).trigger( "focus" );
-				}
-			}
-
-			return element;
-		}
-	} );
-}
-
-$.extend( $.effects, {
-	version: "1.12.1",
-
-	define: function( name, mode, effect ) {
-		if ( !effect ) {
-			effect = mode;
-			mode = "effect";
-		}
-
-		$.effects.effect[ name ] = effect;
-		$.effects.effect[ name ].mode = mode;
-
-		return effect;
-	},
-
-	scaledDimensions: function( element, percent, direction ) {
-		if ( percent === 0 ) {
-			return {
-				height: 0,
-				width: 0,
-				outerHeight: 0,
-				outerWidth: 0
-			};
-		}
-
-		var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
-			y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;
-
-		return {
-			height: element.height() * y,
-			width: element.width() * x,
-			outerHeight: element.outerHeight() * y,
-			outerWidth: element.outerWidth() * x
-		};
-
-	},
-
-	clipToBox: function( animation ) {
-		return {
-			width: animation.clip.right - animation.clip.left,
-			height: animation.clip.bottom - animation.clip.top,
-			left: animation.clip.left,
-			top: animation.clip.top
-		};
-	},
-
-	// Injects recently queued functions to be first in line (after "inprogress")
-	unshift: function( element, queueLength, count ) {
-		var queue = element.queue();
-
-		if ( queueLength > 1 ) {
-			queue.splice.apply( queue,
-				[ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
-		}
-		element.dequeue();
-	},
-
-	saveStyle: function( element ) {
-		element.data( dataSpaceStyle, element[ 0 ].style.cssText );
-	},
-
-	restoreStyle: function( element ) {
-		element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
-		element.removeData( dataSpaceStyle );
-	},
-
-	mode: function( element, mode ) {
-		var hidden = element.is( ":hidden" );
-
-		if ( mode === "toggle" ) {
-			mode = hidden ? "show" : "hide";
-		}
-		if ( hidden ? mode === "hide" : mode === "show" ) {
-			mode = "none";
-		}
-		return mode;
-	},
-
-	// Translates a [top,left] array into a baseline value
-	getBaseline: function( origin, original ) {
-		var y, x;
-
-		switch ( origin[ 0 ] ) {
-		case "top":
-			y = 0;
-			break;
-		case "middle":
-			y = 0.5;
-			break;
-		case "bottom":
-			y = 1;
-			break;
-		default:
-			y = origin[ 0 ] / original.height;
-		}
-
-		switch ( origin[ 1 ] ) {
-		case "left":
-			x = 0;
-			break;
-		case "center":
-			x = 0.5;
-			break;
-		case "right":
-			x = 1;
-			break;
-		default:
-			x = origin[ 1 ] / original.width;
-		}
-
-		return {
-			x: x,
-			y: y
-		};
-	},
-
-	// Creates a placeholder element so that the original element can be made absolute
-	createPlaceholder: function( element ) {
-		var placeholder,
-			cssPosition = element.css( "position" ),
-			position = element.position();
-
-		// Lock in margins first to account for form elements, which
-		// will change margin if you explicitly set height
-		// see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
-		// Support: Safari
-		element.css( {
-			marginTop: element.css( "marginTop" ),
-			marginBottom: element.css( "marginBottom" ),
-			marginLeft: element.css( "marginLeft" ),
-			marginRight: element.css( "marginRight" )
-		} )
-		.outerWidth( element.outerWidth() )
-		.outerHeight( element.outerHeight() );
-
-		if ( /^(static|relative)/.test( cssPosition ) ) {
-			cssPosition = "absolute";
-
-			placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( {
-
-				// Convert inline to inline block to account for inline elements
-				// that turn to inline block based on content (like img)
-				display: /^(inline|ruby)/.test( element.css( "display" ) ) ?
-					"inline-block" :
-					"block",
-				visibility: "hidden",
-
-				// Margins need to be set to account for margin collapse
-				marginTop: element.css( "marginTop" ),
-				marginBottom: element.css( "marginBottom" ),
-				marginLeft: element.css( "marginLeft" ),
-				marginRight: element.css( "marginRight" ),
-				"float": element.css( "float" )
-			} )
-			.outerWidth( element.outerWidth() )
-			.outerHeight( element.outerHeight() )
-			.addClass( "ui-effects-placeholder" );
-
-			element.data( dataSpace + "placeholder", placeholder );
-		}
-
-		element.css( {
-			position: cssPosition,
-			left: position.left,
-			top: position.top
-		} );
-
-		return placeholder;
-	},
-
-	removePlaceholder: function( element ) {
-		var dataKey = dataSpace + "placeholder",
-				placeholder = element.data( dataKey );
-
-		if ( placeholder ) {
-			placeholder.remove();
-			element.removeData( dataKey );
-		}
-	},
-
-	// Removes a placeholder if it exists and restores
-	// properties that were modified during placeholder creation
-	cleanUp: function( element ) {
-		$.effects.restoreStyle( element );
-		$.effects.removePlaceholder( element );
-	},
-
-	setTransition: function( element, list, factor, value ) {
-		value = value || {};
-		$.each( list, function( i, x ) {
-			var unit = element.cssUnit( x );
-			if ( unit[ 0 ] > 0 ) {
-				value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
-			}
-		} );
-		return value;
-	}
-} );
-
-// Return an effect options object for the given parameters:
-function _normalizeArguments( effect, options, speed, callback ) {
-
-	// Allow passing all options as the first parameter
-	if ( $.isPlainObject( effect ) ) {
-		options = effect;
-		effect = effect.effect;
-	}
-
-	// Convert to an object
-	effect = { effect: effect };
-
-	// Catch (effect, null, ...)
-	if ( options == null ) {
-		options = {};
-	}
-
-	// Catch (effect, callback)
-	if ( $.isFunction( options ) ) {
-		callback = options;
-		speed = null;
-		options = {};
-	}
-
-	// Catch (effect, speed, ?)
-	if ( typeof options === "number" || $.fx.speeds[ options ] ) {
-		callback = speed;
-		speed = options;
-		options = {};
-	}
-
-	// Catch (effect, options, callback)
-	if ( $.isFunction( speed ) ) {
-		callback = speed;
-		speed = null;
-	}
-
-	// Add options to effect
-	if ( options ) {
-		$.extend( effect, options );
-	}
-
-	speed = speed || options.duration;
-	effect.duration = $.fx.off ? 0 :
-		typeof speed === "number" ? speed :
-		speed in $.fx.speeds ? $.fx.speeds[ speed ] :
-		$.fx.speeds._default;
-
-	effect.complete = callback || options.complete;
-
-	return effect;
-}
-
-function standardAnimationOption( option ) {
-
-	// Valid standard speeds (nothing, number, named speed)
-	if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
-		return true;
-	}
-
-	// Invalid strings - treat as "normal" speed
-	if ( typeof option === "string" && !$.effects.effect[ option ] ) {
-		return true;
-	}
-
-	// Complete callback
-	if ( $.isFunction( option ) ) {
-		return true;
-	}
-
-	// Options hash (but not naming an effect)
-	if ( typeof option === "object" && !option.effect ) {
-		return true;
-	}
-
-	// Didn't match any standard API
-	return false;
-}
-
-$.fn.extend( {
-	effect: function( /* effect, options, speed, callback */ ) {
-		var args = _normalizeArguments.apply( this, arguments ),
-			effectMethod = $.effects.effect[ args.effect ],
-			defaultMode = effectMethod.mode,
-			queue = args.queue,
-			queueName = queue || "fx",
-			complete = args.complete,
-			mode = args.mode,
-			modes = [],
-			prefilter = function( next ) {
-				var el = $( this ),
-					normalizedMode = $.effects.mode( el, mode ) || defaultMode;
-
-				// Sentinel for duck-punching the :animated psuedo-selector
-				el.data( dataSpaceAnimated, true );
-
-				// Save effect mode for later use,
-				// we can't just call $.effects.mode again later,
-				// as the .show() below destroys the initial state
-				modes.push( normalizedMode );
-
-				// See $.uiBackCompat inside of run() for removal of defaultMode in 1.13
-				if ( defaultMode && ( normalizedMode === "show" ||
-						( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
-					el.show();
-				}
-
-				if ( !defaultMode || normalizedMode !== "none" ) {
-					$.effects.saveStyle( el );
-				}
-
-				if ( $.isFunction( next ) ) {
-					next();
-				}
-			};
-
-		if ( $.fx.off || !effectMethod ) {
-
-			// Delegate to the original method (e.g., .show()) if possible
-			if ( mode ) {
-				return this[ mode ]( args.duration, complete );
-			} else {
-				return this.each( function() {
-					if ( complete ) {
-						complete.call( this );
-					}
-				} );
-			}
-		}
-
-		function run( next ) {
-			var elem = $( this );
-
-			function cleanup() {
-				elem.removeData( dataSpaceAnimated );
-
-				$.effects.cleanUp( elem );
-
-				if ( args.mode === "hide" ) {
-					elem.hide();
-				}
-
-				done();
-			}
-
-			function done() {
-				if ( $.isFunction( complete ) ) {
-					complete.call( elem[ 0 ] );
-				}
-
-				if ( $.isFunction( next ) ) {
-					next();
-				}
-			}
-
-			// Override mode option on a per element basis,
-			// as toggle can be either show or hide depending on element state
-			args.mode = modes.shift();
-
-			if ( $.uiBackCompat !== false && !defaultMode ) {
-				if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
-
-					// Call the core method to track "olddisplay" properly
-					elem[ mode ]();
-					done();
-				} else {
-					effectMethod.call( elem[ 0 ], args, done );
-				}
-			} else {
-				if ( args.mode === "none" ) {
-
-					// Call the core method to track "olddisplay" properly
-					elem[ mode ]();
-					done();
-				} else {
-					effectMethod.call( elem[ 0 ], args, cleanup );
-				}
-			}
-		}
-
-		// Run prefilter on all elements first to ensure that
-		// any showing or hiding happens before placeholder creation,
-		// which ensures that any layout changes are correctly captured.
-		return queue === false ?
-			this.each( prefilter ).each( run ) :
-			this.queue( queueName, prefilter ).queue( queueName, run );
-	},
-
-	show: ( function( orig ) {
-		return function( option ) {
-			if ( standardAnimationOption( option ) ) {
-				return orig.apply( this, arguments );
-			} else {
-				var args = _normalizeArguments.apply( this, arguments );
-				args.mode = "show";
-				return this.effect.call( this, args );
-			}
-		};
-	} )( $.fn.show ),
-
-	hide: ( function( orig ) {
-		return function( option ) {
-			if ( standardAnimationOption( option ) ) {
-				return orig.apply( this, arguments );
-			} else {
-				var args = _normalizeArguments.apply( this, arguments );
-				args.mode = "hide";
-				return this.effect.call( this, args );
-			}
-		};
-	} )( $.fn.hide ),
-
-	toggle: ( function( orig ) {
-		return function( option ) {
-			if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
-				return orig.apply( this, arguments );
-			} else {
-				var args = _normalizeArguments.apply( this, arguments );
-				args.mode = "toggle";
-				return this.effect.call( this, args );
-			}
-		};
-	} )( $.fn.toggle ),
-
-	cssUnit: function( key ) {
-		var style = this.css( key ),
-			val = [];
-
-		$.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
-			if ( style.indexOf( unit ) > 0 ) {
-				val = [ parseFloat( style ), unit ];
-			}
-		} );
-		return val;
-	},
-
-	cssClip: function( clipObj ) {
-		if ( clipObj ) {
-			return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " +
-				clipObj.bottom + "px " + clipObj.left + "px)" );
-		}
-		return parseClip( this.css( "clip" ), this );
-	},
-
-	transfer: function( options, done ) {
-		var element = $( this ),
-			target = $( options.to ),
-			targetFixed = target.css( "position" ) === "fixed",
-			body = $( "body" ),
-			fixTop = targetFixed ? body.scrollTop() : 0,
-			fixLeft = targetFixed ? body.scrollLeft() : 0,
-			endPosition = target.offset(),
-			animation = {
-				top: endPosition.top - fixTop,
-				left: endPosition.left - fixLeft,
-				height: target.innerHeight(),
-				width: target.innerWidth()
-			},
-			startPosition = element.offset(),
-			transfer = $( "<div class='ui-effects-transfer'></div>" )
-				.appendTo( "body" )
-				.addClass( options.className )
-				.css( {
-					top: startPosition.top - fixTop,
-					left: startPosition.left - fixLeft,
-					height: element.innerHeight(),
-					width: element.innerWidth(),
-					position: targetFixed ? "fixed" : "absolute"
-				} )
-				.animate( animation, options.duration, options.easing, function() {
-					transfer.remove();
-					if ( $.isFunction( done ) ) {
-						done();
-					}
-				} );
-	}
-} );
-
-function parseClip( str, element ) {
-		var outerWidth = element.outerWidth(),
-			outerHeight = element.outerHeight(),
-			clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
-			values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];
-
-		return {
-			top: parseFloat( values[ 1 ] ) || 0,
-			right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
-			bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
-			left: parseFloat( values[ 4 ] ) || 0
-		};
-}
-
-$.fx.step.clip = function( fx ) {
-	if ( !fx.clipInit ) {
-		fx.start = $( fx.elem ).cssClip();
-		if ( typeof fx.end === "string" ) {
-			fx.end = parseClip( fx.end, fx.elem );
-		}
-		fx.clipInit = true;
-	}
-
-	$( fx.elem ).cssClip( {
-		top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,
-		right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,
-		bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,
-		left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left
-	} );
-};
-
-} )();
-
-/******************************************************************************/
-/*********************************** EASING ***********************************/
-/******************************************************************************/
-
-( function() {
-
-// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
-
-var baseEasings = {};
-
-$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
-	baseEasings[ name ] = function( p ) {
-		return Math.pow( p, i + 2 );
-	};
-} );
-
-$.extend( baseEasings, {
-	Sine: function( p ) {
-		return 1 - Math.cos( p * Math.PI / 2 );
-	},
-	Circ: function( p ) {
-		return 1 - Math.sqrt( 1 - p * p );
-	},
-	Elastic: function( p ) {
-		return p === 0 || p === 1 ? p :
-			-Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );
-	},
-	Back: function( p ) {
-		return p * p * ( 3 * p - 2 );
-	},
-	Bounce: function( p ) {
-		var pow2,
-			bounce = 4;
-
-		while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
-		return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
-	}
-} );
-
-$.each( baseEasings, function( name, easeIn ) {
-	$.easing[ "easeIn" + name ] = easeIn;
-	$.easing[ "easeOut" + name ] = function( p ) {
-		return 1 - easeIn( 1 - p );
-	};
-	$.easing[ "easeInOut" + name ] = function( p ) {
-		return p < 0.5 ?
-			easeIn( p * 2 ) / 2 :
-			1 - easeIn( p * -2 + 2 ) / 2;
-	};
-} );
-
-} )();
-
-var effect = $.effects;
-
-
-/*!
- * jQuery UI Effects Blind 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Blind Effect
-//>>group: Effects
-//>>description: Blinds the element.
-//>>docs: http://api.jqueryui.com/blind-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectBlind = $.effects.define( "blind", "hide", function( options, done ) {
-	var map = {
-			up: [ "bottom", "top" ],
-			vertical: [ "bottom", "top" ],
-			down: [ "top", "bottom" ],
-			left: [ "right", "left" ],
-			horizontal: [ "right", "left" ],
-			right: [ "left", "right" ]
-		},
-		element = $( this ),
-		direction = options.direction || "up",
-		start = element.cssClip(),
-		animate = { clip: $.extend( {}, start ) },
-		placeholder = $.effects.createPlaceholder( element );
-
-	animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];
-
-	if ( options.mode === "show" ) {
-		element.cssClip( animate.clip );
-		if ( placeholder ) {
-			placeholder.css( $.effects.clipToBox( animate ) );
-		}
-
-		animate.clip = start;
-	}
-
-	if ( placeholder ) {
-		placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
-	}
-
-	element.animate( animate, {
-		queue: false,
-		duration: options.duration,
-		easing: options.easing,
-		complete: done
-	} );
-} );
-
-
-/*!
- * jQuery UI Effects Bounce 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Bounce Effect
-//>>group: Effects
-//>>description: Bounces an element horizontally or vertically n times.
-//>>docs: http://api.jqueryui.com/bounce-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectBounce = $.effects.define( "bounce", function( options, done ) {
-	var upAnim, downAnim, refValue,
-		element = $( this ),
-
-		// Defaults:
-		mode = options.mode,
-		hide = mode === "hide",
-		show = mode === "show",
-		direction = options.direction || "up",
-		distance = options.distance,
-		times = options.times || 5,
-
-		// Number of internal animations
-		anims = times * 2 + ( show || hide ? 1 : 0 ),
-		speed = options.duration / anims,
-		easing = options.easing,
-
-		// Utility:
-		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
-		motion = ( direction === "up" || direction === "left" ),
-		i = 0,
-
-		queuelen = element.queue().length;
-
-	$.effects.createPlaceholder( element );
-
-	refValue = element.css( ref );
-
-	// Default distance for the BIGGEST bounce is the outer Distance / 3
-	if ( !distance ) {
-		distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
-	}
-
-	if ( show ) {
-		downAnim = { opacity: 1 };
-		downAnim[ ref ] = refValue;
-
-		// If we are showing, force opacity 0 and set the initial position
-		// then do the "first" animation
-		element
-			.css( "opacity", 0 )
-			.css( ref, motion ? -distance * 2 : distance * 2 )
-			.animate( downAnim, speed, easing );
-	}
-
-	// Start at the smallest distance if we are hiding
-	if ( hide ) {
-		distance = distance / Math.pow( 2, times - 1 );
-	}
-
-	downAnim = {};
-	downAnim[ ref ] = refValue;
-
-	// Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
-	for ( ; i < times; i++ ) {
-		upAnim = {};
-		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
-		element
-			.animate( upAnim, speed, easing )
-			.animate( downAnim, speed, easing );
-
-		distance = hide ? distance * 2 : distance / 2;
-	}
-
-	// Last Bounce when Hiding
-	if ( hide ) {
-		upAnim = { opacity: 0 };
-		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
-		element.animate( upAnim, speed, easing );
-	}
-
-	element.queue( done );
-
-	$.effects.unshift( element, queuelen, anims + 1 );
-} );
-
-
-/*!
- * jQuery UI Effects Clip 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Clip Effect
-//>>group: Effects
-//>>description: Clips the element on and off like an old TV.
-//>>docs: http://api.jqueryui.com/clip-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectClip = $.effects.define( "clip", "hide", function( options, done ) {
-	var start,
-		animate = {},
-		element = $( this ),
-		direction = options.direction || "vertical",
-		both = direction === "both",
-		horizontal = both || direction === "horizontal",
-		vertical = both || direction === "vertical";
-
-	start = element.cssClip();
-	animate.clip = {
-		top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
-		right: horizontal ? ( start.right - start.left ) / 2 : start.right,
-		bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
-		left: horizontal ? ( start.right - start.left ) / 2 : start.left
-	};
-
-	$.effects.createPlaceholder( element );
-
-	if ( options.mode === "show" ) {
-		element.cssClip( animate.clip );
-		animate.clip = start;
-	}
-
-	element.animate( animate, {
-		queue: false,
-		duration: options.duration,
-		easing: options.easing,
-		complete: done
-	} );
-
-} );
-
-
-/*!
- * jQuery UI Effects Drop 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Drop Effect
-//>>group: Effects
-//>>description: Moves an element in one direction and hides it at the same time.
-//>>docs: http://api.jqueryui.com/drop-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectDrop = $.effects.define( "drop", "hide", function( options, done ) {
-
-	var distance,
-		element = $( this ),
-		mode = options.mode,
-		show = mode === "show",
-		direction = options.direction || "left",
-		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
-		motion = ( direction === "up" || direction === "left" ) ? "-=" : "+=",
-		oppositeMotion = ( motion === "+=" ) ? "-=" : "+=",
-		animation = {
-			opacity: 0
-		};
-
-	$.effects.createPlaceholder( element );
-
-	distance = options.distance ||
-		element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2;
-
-	animation[ ref ] = motion + distance;
-
-	if ( show ) {
-		element.css( animation );
-
-		animation[ ref ] = oppositeMotion + distance;
-		animation.opacity = 1;
-	}
-
-	// Animate
-	element.animate( animation, {
-		queue: false,
-		duration: options.duration,
-		easing: options.easing,
-		complete: done
-	} );
-} );
-
-
-/*!
- * jQuery UI Effects Explode 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Explode Effect
-//>>group: Effects
-// jscs:disable maximumLineLength
-//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness.
-// jscs:enable maximumLineLength
-//>>docs: http://api.jqueryui.com/explode-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectExplode = $.effects.define( "explode", "hide", function( options, done ) {
-
-	var i, j, left, top, mx, my,
-		rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3,
-		cells = rows,
-		element = $( this ),
-		mode = options.mode,
-		show = mode === "show",
-
-		// Show and then visibility:hidden the element before calculating offset
-		offset = element.show().css( "visibility", "hidden" ).offset(),
-
-		// Width and height of a piece
-		width = Math.ceil( element.outerWidth() / cells ),
-		height = Math.ceil( element.outerHeight() / rows ),
-		pieces = [];
-
-	// Children animate complete:
-	function childComplete() {
-		pieces.push( this );
-		if ( pieces.length === rows * cells ) {
-			animComplete();
-		}
-	}
-
-	// Clone the element for each row and cell.
-	for ( i = 0; i < rows; i++ ) { // ===>
-		top = offset.top + i * height;
-		my = i - ( rows - 1 ) / 2;
-
-		for ( j = 0; j < cells; j++ ) { // |||
-			left = offset.left + j * width;
-			mx = j - ( cells - 1 ) / 2;
-
-			// Create a clone of the now hidden main element that will be absolute positioned
-			// within a wrapper div off the -left and -top equal to size of our pieces
-			element
-				.clone()
-				.appendTo( "body" )
-				.wrap( "<div></div>" )
-				.css( {
-					position: "absolute",
-					visibility: "visible",
-					left: -j * width,
-					top: -i * height
-				} )
-
-				// Select the wrapper - make it overflow: hidden and absolute positioned based on
-				// where the original was located +left and +top equal to the size of pieces
-				.parent()
-					.addClass( "ui-effects-explode" )
-					.css( {
-						position: "absolute",
-						overflow: "hidden",
-						width: width,
-						height: height,
-						left: left + ( show ? mx * width : 0 ),
-						top: top + ( show ? my * height : 0 ),
-						opacity: show ? 0 : 1
-					} )
-					.animate( {
-						left: left + ( show ? 0 : mx * width ),
-						top: top + ( show ? 0 : my * height ),
-						opacity: show ? 1 : 0
-					}, options.duration || 500, options.easing, childComplete );
-		}
-	}
-
-	function animComplete() {
-		element.css( {
-			visibility: "visible"
-		} );
-		$( pieces ).remove();
-		done();
-	}
-} );
-
-
-/*!
- * jQuery UI Effects Fade 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Fade Effect
-//>>group: Effects
-//>>description: Fades the element.
-//>>docs: http://api.jqueryui.com/fade-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectFade = $.effects.define( "fade", "toggle", function( options, done ) {
-	var show = options.mode === "show";
-
-	$( this )
-		.css( "opacity", show ? 0 : 1 )
-		.animate( {
-			opacity: show ? 1 : 0
-		}, {
-			queue: false,
-			duration: options.duration,
-			easing: options.easing,
-			complete: done
-		} );
-} );
-
-
-/*!
- * jQuery UI Effects Fold 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Fold Effect
-//>>group: Effects
-//>>description: Folds an element first horizontally and then vertically.
-//>>docs: http://api.jqueryui.com/fold-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectFold = $.effects.define( "fold", "hide", function( options, done ) {
-
-	// Create element
-	var element = $( this ),
-		mode = options.mode,
-		show = mode === "show",
-		hide = mode === "hide",
-		size = options.size || 15,
-		percent = /([0-9]+)%/.exec( size ),
-		horizFirst = !!options.horizFirst,
-		ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ],
-		duration = options.duration / 2,
-
-		placeholder = $.effects.createPlaceholder( element ),
-
-		start = element.cssClip(),
-		animation1 = { clip: $.extend( {}, start ) },
-		animation2 = { clip: $.extend( {}, start ) },
-
-		distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ],
-
-		queuelen = element.queue().length;
-
-	if ( percent ) {
-		size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
-	}
-	animation1.clip[ ref[ 0 ] ] = size;
-	animation2.clip[ ref[ 0 ] ] = size;
-	animation2.clip[ ref[ 1 ] ] = 0;
-
-	if ( show ) {
-		element.cssClip( animation2.clip );
-		if ( placeholder ) {
-			placeholder.css( $.effects.clipToBox( animation2 ) );
-		}
-
-		animation2.clip = start;
-	}
-
-	// Animate
-	element
-		.queue( function( next ) {
-			if ( placeholder ) {
-				placeholder
-					.animate( $.effects.clipToBox( animation1 ), duration, options.easing )
-					.animate( $.effects.clipToBox( animation2 ), duration, options.easing );
-			}
-
-			next();
-		} )
-		.animate( animation1, duration, options.easing )
-		.animate( animation2, duration, options.easing )
-		.queue( done );
-
-	$.effects.unshift( element, queuelen, 4 );
-} );
-
-
-/*!
- * jQuery UI Effects Highlight 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Highlight Effect
-//>>group: Effects
-//>>description: Highlights the background of an element in a defined color for a custom duration.
-//>>docs: http://api.jqueryui.com/highlight-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectHighlight = $.effects.define( "highlight", "show", function( options, done ) {
-	var element = $( this ),
-		animation = {
-			backgroundColor: element.css( "backgroundColor" )
-		};
-
-	if ( options.mode === "hide" ) {
-		animation.opacity = 0;
-	}
-
-	$.effects.saveStyle( element );
-
-	element
-		.css( {
-			backgroundImage: "none",
-			backgroundColor: options.color || "#ffff99"
-		} )
-		.animate( animation, {
-			queue: false,
-			duration: options.duration,
-			easing: options.easing,
-			complete: done
-		} );
-} );
-
-
-/*!
- * jQuery UI Effects Size 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Size Effect
-//>>group: Effects
-//>>description: Resize an element to a specified width and height.
-//>>docs: http://api.jqueryui.com/size-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectSize = $.effects.define( "size", function( options, done ) {
-
-	// Create element
-	var baseline, factor, temp,
-		element = $( this ),
-
-		// Copy for children
-		cProps = [ "fontSize" ],
-		vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
-		hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
-
-		// Set options
-		mode = options.mode,
-		restore = mode !== "effect",
-		scale = options.scale || "both",
-		origin = options.origin || [ "middle", "center" ],
-		position = element.css( "position" ),
-		pos = element.position(),
-		original = $.effects.scaledDimensions( element ),
-		from = options.from || original,
-		to = options.to || $.effects.scaledDimensions( element, 0 );
-
-	$.effects.createPlaceholder( element );
-
-	if ( mode === "show" ) {
-		temp = from;
-		from = to;
-		to = temp;
-	}
-
-	// Set scaling factor
-	factor = {
-		from: {
-			y: from.height / original.height,
-			x: from.width / original.width
-		},
-		to: {
-			y: to.height / original.height,
-			x: to.width / original.width
-		}
-	};
-
-	// Scale the css box
-	if ( scale === "box" || scale === "both" ) {
-
-		// Vertical props scaling
-		if ( factor.from.y !== factor.to.y ) {
-			from = $.effects.setTransition( element, vProps, factor.from.y, from );
-			to = $.effects.setTransition( element, vProps, factor.to.y, to );
-		}
-
-		// Horizontal props scaling
-		if ( factor.from.x !== factor.to.x ) {
-			from = $.effects.setTransition( element, hProps, factor.from.x, from );
-			to = $.effects.setTransition( element, hProps, factor.to.x, to );
-		}
-	}
-
-	// Scale the content
-	if ( scale === "content" || scale === "both" ) {
-
-		// Vertical props scaling
-		if ( factor.from.y !== factor.to.y ) {
-			from = $.effects.setTransition( element, cProps, factor.from.y, from );
-			to = $.effects.setTransition( element, cProps, factor.to.y, to );
-		}
-	}
-
-	// Adjust the position properties based on the provided origin points
-	if ( origin ) {
-		baseline = $.effects.getBaseline( origin, original );
-		from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top;
-		from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left;
-		to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top;
-		to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left;
-	}
-	element.css( from );
-
-	// Animate the children if desired
-	if ( scale === "content" || scale === "both" ) {
-
-		vProps = vProps.concat( [ "marginTop", "marginBottom" ] ).concat( cProps );
-		hProps = hProps.concat( [ "marginLeft", "marginRight" ] );
-
-		// Only animate children with width attributes specified
-		// TODO: is this right? should we include anything with css width specified as well
-		element.find( "*[width]" ).each( function() {
-			var child = $( this ),
-				childOriginal = $.effects.scaledDimensions( child ),
-				childFrom = {
-					height: childOriginal.height * factor.from.y,
-					width: childOriginal.width * factor.from.x,
-					outerHeight: childOriginal.outerHeight * factor.from.y,
-					outerWidth: childOriginal.outerWidth * factor.from.x
-				},
-				childTo = {
-					height: childOriginal.height * factor.to.y,
-					width: childOriginal.width * factor.to.x,
-					outerHeight: childOriginal.height * factor.to.y,
-					outerWidth: childOriginal.width * factor.to.x
-				};
-
-			// Vertical props scaling
-			if ( factor.from.y !== factor.to.y ) {
-				childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom );
-				childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo );
-			}
-
-			// Horizontal props scaling
-			if ( factor.from.x !== factor.to.x ) {
-				childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom );
-				childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo );
-			}
-
-			if ( restore ) {
-				$.effects.saveStyle( child );
-			}
-
-			// Animate children
-			child.css( childFrom );
-			child.animate( childTo, options.duration, options.easing, function() {
-
-				// Restore children
-				if ( restore ) {
-					$.effects.restoreStyle( child );
-				}
-			} );
-		} );
-	}
-
-	// Animate
-	element.animate( to, {
-		queue: false,
-		duration: options.duration,
-		easing: options.easing,
-		complete: function() {
-
-			var offset = element.offset();
-
-			if ( to.opacity === 0 ) {
-				element.css( "opacity", from.opacity );
-			}
-
-			if ( !restore ) {
-				element
-					.css( "position", position === "static" ? "relative" : position )
-					.offset( offset );
-
-				// Need to save style here so that automatic style restoration
-				// doesn't restore to the original styles from before the animation.
-				$.effects.saveStyle( element );
-			}
-
-			done();
-		}
-	} );
-
-} );
-
-
-/*!
- * jQuery UI Effects Scale 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Scale Effect
-//>>group: Effects
-//>>description: Grows or shrinks an element and its content.
-//>>docs: http://api.jqueryui.com/scale-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectScale = $.effects.define( "scale", function( options, done ) {
-
-	// Create element
-	var el = $( this ),
-		mode = options.mode,
-		percent = parseInt( options.percent, 10 ) ||
-			( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ),
-
-		newOptions = $.extend( true, {
-			from: $.effects.scaledDimensions( el ),
-			to: $.effects.scaledDimensions( el, percent, options.direction || "both" ),
-			origin: options.origin || [ "middle", "center" ]
-		}, options );
-
-	// Fade option to support puff
-	if ( options.fade ) {
-		newOptions.from.opacity = 1;
-		newOptions.to.opacity = 0;
-	}
-
-	$.effects.effect.size.call( this, newOptions, done );
-} );
-
-
-/*!
- * jQuery UI Effects Puff 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Puff Effect
-//>>group: Effects
-//>>description: Creates a puff effect by scaling the element up and hiding it at the same time.
-//>>docs: http://api.jqueryui.com/puff-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectPuff = $.effects.define( "puff", "hide", function( options, done ) {
-	var newOptions = $.extend( true, {}, options, {
-		fade: true,
-		percent: parseInt( options.percent, 10 ) || 150
-	} );
-
-	$.effects.effect.scale.call( this, newOptions, done );
-} );
-
-
-/*!
- * jQuery UI Effects Pulsate 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Pulsate Effect
-//>>group: Effects
-//>>description: Pulsates an element n times by changing the opacity to zero and back.
-//>>docs: http://api.jqueryui.com/pulsate-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectPulsate = $.effects.define( "pulsate", "show", function( options, done ) {
-	var element = $( this ),
-		mode = options.mode,
-		show = mode === "show",
-		hide = mode === "hide",
-		showhide = show || hide,
-
-		// Showing or hiding leaves off the "last" animation
-		anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
-		duration = options.duration / anims,
-		animateTo = 0,
-		i = 1,
-		queuelen = element.queue().length;
-
-	if ( show || !element.is( ":visible" ) ) {
-		element.css( "opacity", 0 ).show();
-		animateTo = 1;
-	}
-
-	// Anims - 1 opacity "toggles"
-	for ( ; i < anims; i++ ) {
-		element.animate( { opacity: animateTo }, duration, options.easing );
-		animateTo = 1 - animateTo;
-	}
-
-	element.animate( { opacity: animateTo }, duration, options.easing );
-
-	element.queue( done );
-
-	$.effects.unshift( element, queuelen, anims + 1 );
-} );
-
-
-/*!
- * jQuery UI Effects Shake 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Shake Effect
-//>>group: Effects
-//>>description: Shakes an element horizontally or vertically n times.
-//>>docs: http://api.jqueryui.com/shake-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectShake = $.effects.define( "shake", function( options, done ) {
-
-	var i = 1,
-		element = $( this ),
-		direction = options.direction || "left",
-		distance = options.distance || 20,
-		times = options.times || 3,
-		anims = times * 2 + 1,
-		speed = Math.round( options.duration / anims ),
-		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
-		positiveMotion = ( direction === "up" || direction === "left" ),
-		animation = {},
-		animation1 = {},
-		animation2 = {},
-
-		queuelen = element.queue().length;
-
-	$.effects.createPlaceholder( element );
-
-	// Animation
-	animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
-	animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
-	animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
-
-	// Animate
-	element.animate( animation, speed, options.easing );
-
-	// Shakes
-	for ( ; i < times; i++ ) {
-		element
-			.animate( animation1, speed, options.easing )
-			.animate( animation2, speed, options.easing );
-	}
-
-	element
-		.animate( animation1, speed, options.easing )
-		.animate( animation, speed / 2, options.easing )
-		.queue( done );
-
-	$.effects.unshift( element, queuelen, anims + 1 );
-} );
-
-
-/*!
- * jQuery UI Effects Slide 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Slide Effect
-//>>group: Effects
-//>>description: Slides an element in and out of the viewport.
-//>>docs: http://api.jqueryui.com/slide-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effectsEffectSlide = $.effects.define( "slide", "show", function( options, done ) {
-	var startClip, startRef,
-		element = $( this ),
-		map = {
-			up: [ "bottom", "top" ],
-			down: [ "top", "bottom" ],
-			left: [ "right", "left" ],
-			right: [ "left", "right" ]
-		},
-		mode = options.mode,
-		direction = options.direction || "left",
-		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
-		positiveMotion = ( direction === "up" || direction === "left" ),
-		distance = options.distance ||
-			element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ),
-		animation = {};
-
-	$.effects.createPlaceholder( element );
-
-	startClip = element.cssClip();
-	startRef = element.position()[ ref ];
-
-	// Define hide animation
-	animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef;
-	animation.clip = element.cssClip();
-	animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ];
-
-	// Reverse the animation if we're showing
-	if ( mode === "show" ) {
-		element.cssClip( animation.clip );
-		element.css( ref, animation[ ref ] );
-		animation.clip = startClip;
-		animation[ ref ] = startRef;
-	}
-
-	// Actually animate
-	element.animate( animation, {
-		queue: false,
-		duration: options.duration,
-		easing: options.easing,
-		complete: done
-	} );
-} );
-
-
-/*!
- * jQuery UI Effects Transfer 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Transfer Effect
-//>>group: Effects
-//>>description: Displays a transfer effect from one element to another.
-//>>docs: http://api.jqueryui.com/transfer-effect/
-//>>demos: http://jqueryui.com/effect/
-
-
-
-var effect;
-if ( $.uiBackCompat !== false ) {
-	effect = $.effects.define( "transfer", function( options, done ) {
-		$( this ).transfer( options, done );
-	} );
-}
-var effectsEffectTransfer = effect;
-
-
-/*!
- * jQuery UI Focusable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: :focusable Selector
-//>>group: Core
-//>>description: Selects elements which can be focused.
-//>>docs: http://api.jqueryui.com/focusable-selector/
-
-
-
-// Selectors
-$.ui.focusable = function( element, hasTabindex ) {
-	var map, mapName, img, focusableIfVisible, fieldset,
-		nodeName = element.nodeName.toLowerCase();
-
-	if ( "area" === nodeName ) {
-		map = element.parentNode;
-		mapName = map.name;
-		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
-			return false;
-		}
-		img = $( "img[usemap='#" + mapName + "']" );
-		return img.length > 0 && img.is( ":visible" );
-	}
-
-	if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) {
-		focusableIfVisible = !element.disabled;
-
-		if ( focusableIfVisible ) {
-
-			// Form controls within a disabled fieldset are disabled.
-			// However, controls within the fieldset's legend do not get disabled.
-			// Since controls generally aren't placed inside legends, we skip
-			// this portion of the check.
-			fieldset = $( element ).closest( "fieldset" )[ 0 ];
-			if ( fieldset ) {
-				focusableIfVisible = !fieldset.disabled;
-			}
-		}
-	} else if ( "a" === nodeName ) {
-		focusableIfVisible = element.href || hasTabindex;
-	} else {
-		focusableIfVisible = hasTabindex;
-	}
-
-	return focusableIfVisible && $( element ).is( ":visible" ) && visible( $( element ) );
-};
-
-// Support: IE 8 only
-// IE 8 doesn't resolve inherit to visible/hidden for computed values
-function visible( element ) {
-	var visibility = element.css( "visibility" );
-	while ( visibility === "inherit" ) {
-		element = element.parent();
-		visibility = element.css( "visibility" );
-	}
-	return visibility !== "hidden";
-}
-
-$.extend( $.expr[ ":" ], {
-	focusable: function( element ) {
-		return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );
-	}
-} );
-
-var focusable = $.ui.focusable;
-
-
-
-
-// Support: IE8 Only
-// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop
-// with a string, so we need to find the proper form.
-var form = $.fn.form = function() {
-	return typeof this[ 0 ].form === "string" ? this.closest( "form" ) : $( this[ 0 ].form );
-};
-
-
-/*!
- * jQuery UI Form Reset Mixin 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Form Reset Mixin
-//>>group: Core
-//>>description: Refresh input widgets when their form is reset
-//>>docs: http://api.jqueryui.com/form-reset-mixin/
-
-
-
-var formResetMixin = $.ui.formResetMixin = {
-	_formResetHandler: function() {
-		var form = $( this );
-
-		// Wait for the form reset to actually happen before refreshing
-		setTimeout( function() {
-			var instances = form.data( "ui-form-reset-instances" );
-			$.each( instances, function() {
-				this.refresh();
-			} );
-		} );
-	},
-
-	_bindFormResetHandler: function() {
-		this.form = this.element.form();
-		if ( !this.form.length ) {
-			return;
-		}
-
-		var instances = this.form.data( "ui-form-reset-instances" ) || [];
-		if ( !instances.length ) {
-
-			// We don't use _on() here because we use a single event handler per form
-			this.form.on( "reset.ui-form-reset", this._formResetHandler );
-		}
-		instances.push( this );
-		this.form.data( "ui-form-reset-instances", instances );
-	},
-
-	_unbindFormResetHandler: function() {
-		if ( !this.form.length ) {
-			return;
-		}
-
-		var instances = this.form.data( "ui-form-reset-instances" );
-		instances.splice( $.inArray( this, instances ), 1 );
-		if ( instances.length ) {
-			this.form.data( "ui-form-reset-instances", instances );
-		} else {
-			this.form
-				.removeData( "ui-form-reset-instances" )
-				.off( "reset.ui-form-reset" );
-		}
-	}
-};
-
-
-/*!
- * jQuery UI Support for jQuery core 1.7.x 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- */
-
-//>>label: jQuery 1.7 Support
-//>>group: Core
-//>>description: Support version 1.7.x of jQuery core
-
-
-
-// Support: jQuery 1.7 only
-// Not a great way to check versions, but since we only support 1.7+ and only
-// need to detect <1.8, this is a simple check that should suffice. Checking
-// for "1.7." would be a bit safer, but the version string is 1.7, not 1.7.0
-// and we'll never reach 1.70.0 (if we do, we certainly won't be supporting
-// 1.7 anymore). See #11197 for why we're not using feature detection.
-if ( $.fn.jquery.substring( 0, 3 ) === "1.7" ) {
-
-	// Setters for .innerWidth(), .innerHeight(), .outerWidth(), .outerHeight()
-	// Unlike jQuery Core 1.8+, these only support numeric values to set the
-	// dimensions in pixels
-	$.each( [ "Width", "Height" ], function( i, name ) {
-		var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
-			type = name.toLowerCase(),
-			orig = {
-				innerWidth: $.fn.innerWidth,
-				innerHeight: $.fn.innerHeight,
-				outerWidth: $.fn.outerWidth,
-				outerHeight: $.fn.outerHeight
-			};
-
-		function reduce( elem, size, border, margin ) {
-			$.each( side, function() {
-				size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
-				if ( border ) {
-					size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
-				}
-				if ( margin ) {
-					size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
-				}
-			} );
-			return size;
-		}
-
-		$.fn[ "inner" + name ] = function( size ) {
-			if ( size === undefined ) {
-				return orig[ "inner" + name ].call( this );
-			}
-
-			return this.each( function() {
-				$( this ).css( type, reduce( this, size ) + "px" );
-			} );
-		};
-
-		$.fn[ "outer" + name ] = function( size, margin ) {
-			if ( typeof size !== "number" ) {
-				return orig[ "outer" + name ].call( this, size );
-			}
-
-			return this.each( function() {
-				$( this ).css( type, reduce( this, size, true, margin ) + "px" );
-			} );
-		};
-	} );
-
-	$.fn.addBack = function( selector ) {
-		return this.add( selector == null ?
-			this.prevObject : this.prevObject.filter( selector )
-		);
-	};
-}
-
-;
-/*!
- * jQuery UI Keycode 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Keycode
-//>>group: Core
-//>>description: Provide keycodes as keynames
-//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
-
-
-var keycode = $.ui.keyCode = {
-	BACKSPACE: 8,
-	COMMA: 188,
-	DELETE: 46,
-	DOWN: 40,
-	END: 35,
-	ENTER: 13,
-	ESCAPE: 27,
-	HOME: 36,
-	LEFT: 37,
-	PAGE_DOWN: 34,
-	PAGE_UP: 33,
-	PERIOD: 190,
-	RIGHT: 39,
-	SPACE: 32,
-	TAB: 9,
-	UP: 38
-};
-
-
-
-
-// Internal use only
-var escapeSelector = $.ui.escapeSelector = ( function() {
-	var selectorEscape = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
-	return function( selector ) {
-		return selector.replace( selectorEscape, "\\$1" );
-	};
-} )();
-
-
-/*!
- * jQuery UI Labels 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: labels
-//>>group: Core
-//>>description: Find all the labels associated with a given input
-//>>docs: http://api.jqueryui.com/labels/
-
-
-
-var labels = $.fn.labels = function() {
-	var ancestor, selector, id, labels, ancestors;
-
-	// Check control.labels first
-	if ( this[ 0 ].labels && this[ 0 ].labels.length ) {
-		return this.pushStack( this[ 0 ].labels );
-	}
-
-	// Support: IE <= 11, FF <= 37, Android <= 2.3 only
-	// Above browsers do not support control.labels. Everything below is to support them
-	// as well as document fragments. control.labels does not work on document fragments
-	labels = this.eq( 0 ).parents( "label" );
-
-	// Look for the label based on the id
-	id = this.attr( "id" );
-	if ( id ) {
-
-		// We don't search against the document in case the element
-		// is disconnected from the DOM
-		ancestor = this.eq( 0 ).parents().last();
-
-		// Get a full set of top level ancestors
-		ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );
-
-		// Create a selector for the label based on the id
-		selector = "label[for='" + $.ui.escapeSelector( id ) + "']";
-
-		labels = labels.add( ancestors.find( selector ).addBack( selector ) );
-
-	}
-
-	// Return whatever we have found for labels
-	return this.pushStack( labels );
-};
-
-
-/*!
- * jQuery UI Scroll Parent 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: scrollParent
-//>>group: Core
-//>>description: Get the closest ancestor element that is scrollable.
-//>>docs: http://api.jqueryui.com/scrollParent/
-
-
-
-var scrollParent = $.fn.scrollParent = function( includeHidden ) {
-	var position = this.css( "position" ),
-		excludeStaticParent = position === "absolute",
-		overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
-		scrollParent = this.parents().filter( function() {
-			var parent = $( this );
-			if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
-				return false;
-			}
-			return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
-				parent.css( "overflow-x" ) );
-		} ).eq( 0 );
-
-	return position === "fixed" || !scrollParent.length ?
-		$( this[ 0 ].ownerDocument || document ) :
-		scrollParent;
-};
-
-
-/*!
- * jQuery UI Tabbable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: :tabbable Selector
-//>>group: Core
-//>>description: Selects elements which can be tabbed to.
-//>>docs: http://api.jqueryui.com/tabbable-selector/
-
-
-
-var tabbable = $.extend( $.expr[ ":" ], {
-	tabbable: function( element ) {
-		var tabIndex = $.attr( element, "tabindex" ),
-			hasTabindex = tabIndex != null;
-		return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex );
-	}
-} );
-
-
-/*!
- * jQuery UI Unique ID 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: uniqueId
-//>>group: Core
-//>>description: Functions to generate and remove uniqueId's
-//>>docs: http://api.jqueryui.com/uniqueId/
-
-
-
-var uniqueId = $.fn.extend( {
-	uniqueId: ( function() {
-		var uuid = 0;
-
-		return function() {
-			return this.each( function() {
-				if ( !this.id ) {
-					this.id = "ui-id-" + ( ++uuid );
-				}
-			} );
-		};
-	} )(),
-
-	removeUniqueId: function() {
-		return this.each( function() {
-			if ( /^ui-id-\d+$/.test( this.id ) ) {
-				$( this ).removeAttr( "id" );
-			}
-		} );
-	}
-} );
-
-
-/*!
- * jQuery UI Accordion 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Accordion
-//>>group: Widgets
-// jscs:disable maximumLineLength
-//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
-// jscs:enable maximumLineLength
-//>>docs: http://api.jqueryui.com/accordion/
-//>>demos: http://jqueryui.com/accordion/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/accordion.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-var widgetsAccordion = $.widget( "ui.accordion", {
-	version: "1.12.1",
-	options: {
-		active: 0,
-		animate: {},
-		classes: {
-			"ui-accordion-header": "ui-corner-top",
-			"ui-accordion-header-collapsed": "ui-corner-all",
-			"ui-accordion-content": "ui-corner-bottom"
-		},
-		collapsible: false,
-		event: "click",
-		header: "> li > :first-child, > :not(li):even",
-		heightStyle: "auto",
-		icons: {
-			activeHeader: "ui-icon-triangle-1-s",
-			header: "ui-icon-triangle-1-e"
-		},
-
-		// Callbacks
-		activate: null,
-		beforeActivate: null
-	},
-
-	hideProps: {
-		borderTopWidth: "hide",
-		borderBottomWidth: "hide",
-		paddingTop: "hide",
-		paddingBottom: "hide",
-		height: "hide"
-	},
-
-	showProps: {
-		borderTopWidth: "show",
-		borderBottomWidth: "show",
-		paddingTop: "show",
-		paddingBottom: "show",
-		height: "show"
-	},
-
-	_create: function() {
-		var options = this.options;
-
-		this.prevShow = this.prevHide = $();
-		this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
-		this.element.attr( "role", "tablist" );
-
-		// Don't allow collapsible: false and active: false / null
-		if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
-			options.active = 0;
-		}
-
-		this._processPanels();
-
-		// handle negative values
-		if ( options.active < 0 ) {
-			options.active += this.headers.length;
-		}
-		this._refresh();
-	},
-
-	_getCreateEventData: function() {
-		return {
-			header: this.active,
-			panel: !this.active.length ? $() : this.active.next()
-		};
-	},
-
-	_createIcons: function() {
-		var icon, children,
-			icons = this.options.icons;
-
-		if ( icons ) {
-			icon = $( "<span>" );
-			this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
-			icon.prependTo( this.headers );
-			children = this.active.children( ".ui-accordion-header-icon" );
-			this._removeClass( children, icons.header )
-				._addClass( children, null, icons.activeHeader )
-				._addClass( this.headers, "ui-accordion-icons" );
-		}
-	},
-
-	_destroyIcons: function() {
-		this._removeClass( this.headers, "ui-accordion-icons" );
-		this.headers.children( ".ui-accordion-header-icon" ).remove();
-	},
-
-	_destroy: function() {
-		var contents;
-
-		// Clean up main element
-		this.element.removeAttr( "role" );
-
-		// Clean up headers
-		this.headers
-			.removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
-			.removeUniqueId();
-
-		this._destroyIcons();
-
-		// Clean up content panels
-		contents = this.headers.next()
-			.css( "display", "" )
-			.removeAttr( "role aria-hidden aria-labelledby" )
-			.removeUniqueId();
-
-		if ( this.options.heightStyle !== "content" ) {
-			contents.css( "height", "" );
-		}
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "active" ) {
-
-			// _activate() will handle invalid values and update this.options
-			this._activate( value );
-			return;
-		}
-
-		if ( key === "event" ) {
-			if ( this.options.event ) {
-				this._off( this.headers, this.options.event );
-			}
-			this._setupEvents( value );
-		}
-
-		this._super( key, value );
-
-		// Setting collapsible: false while collapsed; open first panel
-		if ( key === "collapsible" && !value && this.options.active === false ) {
-			this._activate( 0 );
-		}
-
-		if ( key === "icons" ) {
-			this._destroyIcons();
-			if ( value ) {
-				this._createIcons();
-			}
-		}
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this.element.attr( "aria-disabled", value );
-
-		// Support: IE8 Only
-		// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
-		// so we need to add the disabled class to the headers and panels
-		this._toggleClass( null, "ui-state-disabled", !!value );
-		this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
-			!!value );
-	},
-
-	_keydown: function( event ) {
-		if ( event.altKey || event.ctrlKey ) {
-			return;
-		}
-
-		var keyCode = $.ui.keyCode,
-			length = this.headers.length,
-			currentIndex = this.headers.index( event.target ),
-			toFocus = false;
-
-		switch ( event.keyCode ) {
-		case keyCode.RIGHT:
-		case keyCode.DOWN:
-			toFocus = this.headers[ ( currentIndex + 1 ) % length ];
-			break;
-		case keyCode.LEFT:
-		case keyCode.UP:
-			toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
-			break;
-		case keyCode.SPACE:
-		case keyCode.ENTER:
-			this._eventHandler( event );
-			break;
-		case keyCode.HOME:
-			toFocus = this.headers[ 0 ];
-			break;
-		case keyCode.END:
-			toFocus = this.headers[ length - 1 ];
-			break;
-		}
-
-		if ( toFocus ) {
-			$( event.target ).attr( "tabIndex", -1 );
-			$( toFocus ).attr( "tabIndex", 0 );
-			$( toFocus ).trigger( "focus" );
-			event.preventDefault();
-		}
-	},
-
-	_panelKeyDown: function( event ) {
-		if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
-			$( event.currentTarget ).prev().trigger( "focus" );
-		}
-	},
-
-	refresh: function() {
-		var options = this.options;
-		this._processPanels();
-
-		// Was collapsed or no panel
-		if ( ( options.active === false && options.collapsible === true ) ||
-				!this.headers.length ) {
-			options.active = false;
-			this.active = $();
-
-		// active false only when collapsible is true
-		} else if ( options.active === false ) {
-			this._activate( 0 );
-
-		// was active, but active panel is gone
-		} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
-
-			// all remaining panel are disabled
-			if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
-				options.active = false;
-				this.active = $();
-
-			// activate previous panel
-			} else {
-				this._activate( Math.max( 0, options.active - 1 ) );
-			}
-
-		// was active, active panel still exists
-		} else {
-
-			// make sure active index is correct
-			options.active = this.headers.index( this.active );
-		}
-
-		this._destroyIcons();
-
-		this._refresh();
-	},
-
-	_processPanels: function() {
-		var prevHeaders = this.headers,
-			prevPanels = this.panels;
-
-		this.headers = this.element.find( this.options.header );
-		this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
-			"ui-state-default" );
-
-		this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
-		this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );
-
-		// Avoid memory leaks (#10056)
-		if ( prevPanels ) {
-			this._off( prevHeaders.not( this.headers ) );
-			this._off( prevPanels.not( this.panels ) );
-		}
-	},
-
-	_refresh: function() {
-		var maxHeight,
-			options = this.options,
-			heightStyle = options.heightStyle,
-			parent = this.element.parent();
-
-		this.active = this._findActive( options.active );
-		this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
-			._removeClass( this.active, "ui-accordion-header-collapsed" );
-		this._addClass( this.active.next(), "ui-accordion-content-active" );
-		this.active.next().show();
-
-		this.headers
-			.attr( "role", "tab" )
-			.each( function() {
-				var header = $( this ),
-					headerId = header.uniqueId().attr( "id" ),
-					panel = header.next(),
-					panelId = panel.uniqueId().attr( "id" );
-				header.attr( "aria-controls", panelId );
-				panel.attr( "aria-labelledby", headerId );
-			} )
-			.next()
-				.attr( "role", "tabpanel" );
-
-		this.headers
-			.not( this.active )
-				.attr( {
-					"aria-selected": "false",
-					"aria-expanded": "false",
-					tabIndex: -1
-				} )
-				.next()
-					.attr( {
-						"aria-hidden": "true"
-					} )
-					.hide();
-
-		// Make sure at least one header is in the tab order
-		if ( !this.active.length ) {
-			this.headers.eq( 0 ).attr( "tabIndex", 0 );
-		} else {
-			this.active.attr( {
-				"aria-selected": "true",
-				"aria-expanded": "true",
-				tabIndex: 0
-			} )
-				.next()
-					.attr( {
-						"aria-hidden": "false"
-					} );
-		}
-
-		this._createIcons();
-
-		this._setupEvents( options.event );
-
-		if ( heightStyle === "fill" ) {
-			maxHeight = parent.height();
-			this.element.siblings( ":visible" ).each( function() {
-				var elem = $( this ),
-					position = elem.css( "position" );
-
-				if ( position === "absolute" || position === "fixed" ) {
-					return;
-				}
-				maxHeight -= elem.outerHeight( true );
-			} );
-
-			this.headers.each( function() {
-				maxHeight -= $( this ).outerHeight( true );
-			} );
-
-			this.headers.next()
-				.each( function() {
-					$( this ).height( Math.max( 0, maxHeight -
-						$( this ).innerHeight() + $( this ).height() ) );
-				} )
-				.css( "overflow", "auto" );
-		} else if ( heightStyle === "auto" ) {
-			maxHeight = 0;
-			this.headers.next()
-				.each( function() {
-					var isVisible = $( this ).is( ":visible" );
-					if ( !isVisible ) {
-						$( this ).show();
-					}
-					maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
-					if ( !isVisible ) {
-						$( this ).hide();
-					}
-				} )
-				.height( maxHeight );
-		}
-	},
-
-	_activate: function( index ) {
-		var active = this._findActive( index )[ 0 ];
-
-		// Trying to activate the already active panel
-		if ( active === this.active[ 0 ] ) {
-			return;
-		}
-
-		// Trying to collapse, simulate a click on the currently active header
-		active = active || this.active[ 0 ];
-
-		this._eventHandler( {
-			target: active,
-			currentTarget: active,
-			preventDefault: $.noop
-		} );
-	},
-
-	_findActive: function( selector ) {
-		return typeof selector === "number" ? this.headers.eq( selector ) : $();
-	},
-
-	_setupEvents: function( event ) {
-		var events = {
-			keydown: "_keydown"
-		};
-		if ( event ) {
-			$.each( event.split( " " ), function( index, eventName ) {
-				events[ eventName ] = "_eventHandler";
-			} );
-		}
-
-		this._off( this.headers.add( this.headers.next() ) );
-		this._on( this.headers, events );
-		this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
-		this._hoverable( this.headers );
-		this._focusable( this.headers );
-	},
-
-	_eventHandler: function( event ) {
-		var activeChildren, clickedChildren,
-			options = this.options,
-			active = this.active,
-			clicked = $( event.currentTarget ),
-			clickedIsActive = clicked[ 0 ] === active[ 0 ],
-			collapsing = clickedIsActive && options.collapsible,
-			toShow = collapsing ? $() : clicked.next(),
-			toHide = active.next(),
-			eventData = {
-				oldHeader: active,
-				oldPanel: toHide,
-				newHeader: collapsing ? $() : clicked,
-				newPanel: toShow
-			};
-
-		event.preventDefault();
-
-		if (
-
-				// click on active header, but not collapsible
-				( clickedIsActive && !options.collapsible ) ||
-
-				// allow canceling activation
-				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
-			return;
-		}
-
-		options.active = collapsing ? false : this.headers.index( clicked );
-
-		// When the call to ._toggle() comes after the class changes
-		// it causes a very odd bug in IE 8 (see #6720)
-		this.active = clickedIsActive ? $() : clicked;
-		this._toggle( eventData );
-
-		// Switch classes
-		// corner classes on the previously active header stay after the animation
-		this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
-		if ( options.icons ) {
-			activeChildren = active.children( ".ui-accordion-header-icon" );
-			this._removeClass( activeChildren, null, options.icons.activeHeader )
-				._addClass( activeChildren, null, options.icons.header );
-		}
-
-		if ( !clickedIsActive ) {
-			this._removeClass( clicked, "ui-accordion-header-collapsed" )
-				._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
-			if ( options.icons ) {
-				clickedChildren = clicked.children( ".ui-accordion-header-icon" );
-				this._removeClass( clickedChildren, null, options.icons.header )
-					._addClass( clickedChildren, null, options.icons.activeHeader );
-			}
-
-			this._addClass( clicked.next(), "ui-accordion-content-active" );
-		}
-	},
-
-	_toggle: function( data ) {
-		var toShow = data.newPanel,
-			toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
-
-		// Handle activating a panel during the animation for another activation
-		this.prevShow.add( this.prevHide ).stop( true, true );
-		this.prevShow = toShow;
-		this.prevHide = toHide;
-
-		if ( this.options.animate ) {
-			this._animate( toShow, toHide, data );
-		} else {
-			toHide.hide();
-			toShow.show();
-			this._toggleComplete( data );
-		}
-
-		toHide.attr( {
-			"aria-hidden": "true"
-		} );
-		toHide.prev().attr( {
-			"aria-selected": "false",
-			"aria-expanded": "false"
-		} );
-
-		// if we're switching panels, remove the old header from the tab order
-		// if we're opening from collapsed state, remove the previous header from the tab order
-		// if we're collapsing, then keep the collapsing header in the tab order
-		if ( toShow.length && toHide.length ) {
-			toHide.prev().attr( {
-				"tabIndex": -1,
-				"aria-expanded": "false"
-			} );
-		} else if ( toShow.length ) {
-			this.headers.filter( function() {
-				return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
-			} )
-				.attr( "tabIndex", -1 );
-		}
-
-		toShow
-			.attr( "aria-hidden", "false" )
-			.prev()
-				.attr( {
-					"aria-selected": "true",
-					"aria-expanded": "true",
-					tabIndex: 0
-				} );
-	},
-
-	_animate: function( toShow, toHide, data ) {
-		var total, easing, duration,
-			that = this,
-			adjust = 0,
-			boxSizing = toShow.css( "box-sizing" ),
-			down = toShow.length &&
-				( !toHide.length || ( toShow.index() < toHide.index() ) ),
-			animate = this.options.animate || {},
-			options = down && animate.down || animate,
-			complete = function() {
-				that._toggleComplete( data );
-			};
-
-		if ( typeof options === "number" ) {
-			duration = options;
-		}
-		if ( typeof options === "string" ) {
-			easing = options;
-		}
-
-		// fall back from options to animation in case of partial down settings
-		easing = easing || options.easing || animate.easing;
-		duration = duration || options.duration || animate.duration;
-
-		if ( !toHide.length ) {
-			return toShow.animate( this.showProps, duration, easing, complete );
-		}
-		if ( !toShow.length ) {
-			return toHide.animate( this.hideProps, duration, easing, complete );
-		}
-
-		total = toShow.show().outerHeight();
-		toHide.animate( this.hideProps, {
-			duration: duration,
-			easing: easing,
-			step: function( now, fx ) {
-				fx.now = Math.round( now );
-			}
-		} );
-		toShow
-			.hide()
-			.animate( this.showProps, {
-				duration: duration,
-				easing: easing,
-				complete: complete,
-				step: function( now, fx ) {
-					fx.now = Math.round( now );
-					if ( fx.prop !== "height" ) {
-						if ( boxSizing === "content-box" ) {
-							adjust += fx.now;
-						}
-					} else if ( that.options.heightStyle !== "content" ) {
-						fx.now = Math.round( total - toHide.outerHeight() - adjust );
-						adjust = 0;
-					}
-				}
-			} );
-	},
-
-	_toggleComplete: function( data ) {
-		var toHide = data.oldPanel,
-			prev = toHide.prev();
-
-		this._removeClass( toHide, "ui-accordion-content-active" );
-		this._removeClass( prev, "ui-accordion-header-active" )
-			._addClass( prev, "ui-accordion-header-collapsed" );
-
-		// Work around for rendering bug in IE (#5421)
-		if ( toHide.length ) {
-			toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
-		}
-		this._trigger( "activate", null, data );
-	}
-} );
-
-
-
-var safeActiveElement = $.ui.safeActiveElement = function( document ) {
-	var activeElement;
-
-	// Support: IE 9 only
-	// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
-	try {
-		activeElement = document.activeElement;
-	} catch ( error ) {
-		activeElement = document.body;
-	}
-
-	// Support: IE 9 - 11 only
-	// IE may return null instead of an element
-	// Interestingly, this only seems to occur when NOT in an iframe
-	if ( !activeElement ) {
-		activeElement = document.body;
-	}
-
-	// Support: IE 11 only
-	// IE11 returns a seemingly empty object in some cases when accessing
-	// document.activeElement from an <iframe>
-	if ( !activeElement.nodeName ) {
-		activeElement = document.body;
-	}
-
-	return activeElement;
-};
-
-
-/*!
- * jQuery UI Menu 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Menu
-//>>group: Widgets
-//>>description: Creates nestable menus.
-//>>docs: http://api.jqueryui.com/menu/
-//>>demos: http://jqueryui.com/menu/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/menu.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-var widgetsMenu = $.widget( "ui.menu", {
-	version: "1.12.1",
-	defaultElement: "<ul>",
-	delay: 300,
-	options: {
-		icons: {
-			submenu: "ui-icon-caret-1-e"
-		},
-		items: "> *",
-		menus: "ul",
-		position: {
-			my: "left top",
-			at: "right top"
-		},
-		role: "menu",
-
-		// Callbacks
-		blur: null,
-		focus: null,
-		select: null
-	},
-
-	_create: function() {
-		this.activeMenu = this.element;
-
-		// Flag used to prevent firing of the click handler
-		// as the event bubbles up through nested menus
-		this.mouseHandled = false;
-		this.element
-			.uniqueId()
-			.attr( {
-				role: this.options.role,
-				tabIndex: 0
-			} );
-
-		this._addClass( "ui-menu", "ui-widget ui-widget-content" );
-		this._on( {
-
-			// Prevent focus from sticking to links inside menu after clicking
-			// them (focus should always stay on UL during navigation).
-			"mousedown .ui-menu-item": function( event ) {
-				event.preventDefault();
-			},
-			"click .ui-menu-item": function( event ) {
-				var target = $( event.target );
-				var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
-				if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
-					this.select( event );
-
-					// Only set the mouseHandled flag if the event will bubble, see #9469.
-					if ( !event.isPropagationStopped() ) {
-						this.mouseHandled = true;
-					}
-
-					// Open submenu on click
-					if ( target.has( ".ui-menu" ).length ) {
-						this.expand( event );
-					} else if ( !this.element.is( ":focus" ) &&
-							active.closest( ".ui-menu" ).length ) {
-
-						// Redirect focus to the menu
-						this.element.trigger( "focus", [ true ] );
-
-						// If the active item is on the top level, let it stay active.
-						// Otherwise, blur the active item since it is no longer visible.
-						if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
-							clearTimeout( this.timer );
-						}
-					}
-				}
-			},
-			"mouseenter .ui-menu-item": function( event ) {
-
-				// Ignore mouse events while typeahead is active, see #10458.
-				// Prevents focusing the wrong item when typeahead causes a scroll while the mouse
-				// is over an item in the menu
-				if ( this.previousFilter ) {
-					return;
-				}
-
-				var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
-					target = $( event.currentTarget );
-
-				// Ignore bubbled events on parent items, see #11641
-				if ( actualTarget[ 0 ] !== target[ 0 ] ) {
-					return;
-				}
-
-				// Remove ui-state-active class from siblings of the newly focused menu item
-				// to avoid a jump caused by adjacent elements both having a class with a border
-				this._removeClass( target.siblings().children( ".ui-state-active" ),
-					null, "ui-state-active" );
-				this.focus( event, target );
-			},
-			mouseleave: "collapseAll",
-			"mouseleave .ui-menu": "collapseAll",
-			focus: function( event, keepActiveItem ) {
-
-				// If there's already an active item, keep it active
-				// If not, activate the first item
-				var item = this.active || this.element.find( this.options.items ).eq( 0 );
-
-				if ( !keepActiveItem ) {
-					this.focus( event, item );
-				}
-			},
-			blur: function( event ) {
-				this._delay( function() {
-					var notContained = !$.contains(
-						this.element[ 0 ],
-						$.ui.safeActiveElement( this.document[ 0 ] )
-					);
-					if ( notContained ) {
-						this.collapseAll( event );
-					}
-				} );
-			},
-			keydown: "_keydown"
-		} );
-
-		this.refresh();
-
-		// Clicks outside of a menu collapse any open menus
-		this._on( this.document, {
-			click: function( event ) {
-				if ( this._closeOnDocumentClick( event ) ) {
-					this.collapseAll( event );
-				}
-
-				// Reset the mouseHandled flag
-				this.mouseHandled = false;
-			}
-		} );
-	},
-
-	_destroy: function() {
-		var items = this.element.find( ".ui-menu-item" )
-				.removeAttr( "role aria-disabled" ),
-			submenus = items.children( ".ui-menu-item-wrapper" )
-				.removeUniqueId()
-				.removeAttr( "tabIndex role aria-haspopup" );
-
-		// Destroy (sub)menus
-		this.element
-			.removeAttr( "aria-activedescendant" )
-			.find( ".ui-menu" ).addBack()
-				.removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
-					"tabIndex" )
-				.removeUniqueId()
-				.show();
-
-		submenus.children().each( function() {
-			var elem = $( this );
-			if ( elem.data( "ui-menu-submenu-caret" ) ) {
-				elem.remove();
-			}
-		} );
-	},
-
-	_keydown: function( event ) {
-		var match, prev, character, skip,
-			preventDefault = true;
-
-		switch ( event.keyCode ) {
-		case $.ui.keyCode.PAGE_UP:
-			this.previousPage( event );
-			break;
-		case $.ui.keyCode.PAGE_DOWN:
-			this.nextPage( event );
-			break;
-		case $.ui.keyCode.HOME:
-			this._move( "first", "first", event );
-			break;
-		case $.ui.keyCode.END:
-			this._move( "last", "last", event );
-			break;
-		case $.ui.keyCode.UP:
-			this.previous( event );
-			break;
-		case $.ui.keyCode.DOWN:
-			this.next( event );
-			break;
-		case $.ui.keyCode.LEFT:
-			this.collapse( event );
-			break;
-		case $.ui.keyCode.RIGHT:
-			if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
-				this.expand( event );
-			}
-			break;
-		case $.ui.keyCode.ENTER:
-		case $.ui.keyCode.SPACE:
-			this._activate( event );
-			break;
-		case $.ui.keyCode.ESCAPE:
-			this.collapse( event );
-			break;
-		default:
-			preventDefault = false;
-			prev = this.previousFilter || "";
-			skip = false;
-
-			// Support number pad values
-			character = event.keyCode >= 96 && event.keyCode <= 105 ?
-				( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );
-
-			clearTimeout( this.filterTimer );
-
-			if ( character === prev ) {
-				skip = true;
-			} else {
-				character = prev + character;
-			}
-
-			match = this._filterMenuItems( character );
-			match = skip && match.index( this.active.next() ) !== -1 ?
-				this.active.nextAll( ".ui-menu-item" ) :
-				match;
-
-			// If no matches on the current filter, reset to the last character pressed
-			// to move down the menu to the first item that starts with that character
-			if ( !match.length ) {
-				character = String.fromCharCode( event.keyCode );
-				match = this._filterMenuItems( character );
-			}
-
-			if ( match.length ) {
-				this.focus( event, match );
-				this.previousFilter = character;
-				this.filterTimer = this._delay( function() {
-					delete this.previousFilter;
-				}, 1000 );
-			} else {
-				delete this.previousFilter;
-			}
-		}
-
-		if ( preventDefault ) {
-			event.preventDefault();
-		}
-	},
-
-	_activate: function( event ) {
-		if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
-			if ( this.active.children( "[aria-haspopup='true']" ).length ) {
-				this.expand( event );
-			} else {
-				this.select( event );
-			}
-		}
-	},
-
-	refresh: function() {
-		var menus, items, newSubmenus, newItems, newWrappers,
-			that = this,
-			icon = this.options.icons.submenu,
-			submenus = this.element.find( this.options.menus );
-
-		this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
-
-		// Initialize nested menus
-		newSubmenus = submenus.filter( ":not(.ui-menu)" )
-			.hide()
-			.attr( {
-				role: this.options.role,
-				"aria-hidden": "true",
-				"aria-expanded": "false"
-			} )
-			.each( function() {
-				var menu = $( this ),
-					item = menu.prev(),
-					submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
-
-				that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
-				item
-					.attr( "aria-haspopup", "true" )
-					.prepend( submenuCaret );
-				menu.attr( "aria-labelledby", item.attr( "id" ) );
-			} );
-
-		this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
-
-		menus = submenus.add( this.element );
-		items = menus.find( this.options.items );
-
-		// Initialize menu-items containing spaces and/or dashes only as dividers
-		items.not( ".ui-menu-item" ).each( function() {
-			var item = $( this );
-			if ( that._isDivider( item ) ) {
-				that._addClass( item, "ui-menu-divider", "ui-widget-content" );
-			}
-		} );
-
-		// Don't refresh list items that are already adapted
-		newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
-		newWrappers = newItems.children()
-			.not( ".ui-menu" )
-				.uniqueId()
-				.attr( {
-					tabIndex: -1,
-					role: this._itemRole()
-				} );
-		this._addClass( newItems, "ui-menu-item" )
-			._addClass( newWrappers, "ui-menu-item-wrapper" );
-
-		// Add aria-disabled attribute to any disabled menu item
-		items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
-
-		// If the active item has been removed, blur the menu
-		if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
-			this.blur();
-		}
-	},
-
-	_itemRole: function() {
-		return {
-			menu: "menuitem",
-			listbox: "option"
-		}[ this.options.role ];
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "icons" ) {
-			var icons = this.element.find( ".ui-menu-icon" );
-			this._removeClass( icons, null, this.options.icons.submenu )
-				._addClass( icons, null, value.submenu );
-		}
-		this._super( key, value );
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this.element.attr( "aria-disabled", String( value ) );
-		this._toggleClass( null, "ui-state-disabled", !!value );
-	},
-
-	focus: function( event, item ) {
-		var nested, focused, activeParent;
-		this.blur( event, event && event.type === "focus" );
-
-		this._scrollIntoView( item );
-
-		this.active = item.first();
-
-		focused = this.active.children( ".ui-menu-item-wrapper" );
-		this._addClass( focused, null, "ui-state-active" );
-
-		// Only update aria-activedescendant if there's a role
-		// otherwise we assume focus is managed elsewhere
-		if ( this.options.role ) {
-			this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
-		}
-
-		// Highlight active parent menu item, if any
-		activeParent = this.active
-			.parent()
-				.closest( ".ui-menu-item" )
-					.children( ".ui-menu-item-wrapper" );
-		this._addClass( activeParent, null, "ui-state-active" );
-
-		if ( event && event.type === "keydown" ) {
-			this._close();
-		} else {
-			this.timer = this._delay( function() {
-				this._close();
-			}, this.delay );
-		}
-
-		nested = item.children( ".ui-menu" );
-		if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
-			this._startOpening( nested );
-		}
-		this.activeMenu = item.parent();
-
-		this._trigger( "focus", event, { item: item } );
-	},
-
-	_scrollIntoView: function( item ) {
-		var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
-		if ( this._hasScroll() ) {
-			borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
-			paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
-			offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
-			scroll = this.activeMenu.scrollTop();
-			elementHeight = this.activeMenu.height();
-			itemHeight = item.outerHeight();
-
-			if ( offset < 0 ) {
-				this.activeMenu.scrollTop( scroll + offset );
-			} else if ( offset + itemHeight > elementHeight ) {
-				this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
-			}
-		}
-	},
-
-	blur: function( event, fromFocus ) {
-		if ( !fromFocus ) {
-			clearTimeout( this.timer );
-		}
-
-		if ( !this.active ) {
-			return;
-		}
-
-		this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
-			null, "ui-state-active" );
-
-		this._trigger( "blur", event, { item: this.active } );
-		this.active = null;
-	},
-
-	_startOpening: function( submenu ) {
-		clearTimeout( this.timer );
-
-		// Don't open if already open fixes a Firefox bug that caused a .5 pixel
-		// shift in the submenu position when mousing over the caret icon
-		if ( submenu.attr( "aria-hidden" ) !== "true" ) {
-			return;
-		}
-
-		this.timer = this._delay( function() {
-			this._close();
-			this._open( submenu );
-		}, this.delay );
-	},
-
-	_open: function( submenu ) {
-		var position = $.extend( {
-			of: this.active
-		}, this.options.position );
-
-		clearTimeout( this.timer );
-		this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
-			.hide()
-			.attr( "aria-hidden", "true" );
-
-		submenu
-			.show()
-			.removeAttr( "aria-hidden" )
-			.attr( "aria-expanded", "true" )
-			.position( position );
-	},
-
-	collapseAll: function( event, all ) {
-		clearTimeout( this.timer );
-		this.timer = this._delay( function() {
-
-			// If we were passed an event, look for the submenu that contains the event
-			var currentMenu = all ? this.element :
-				$( event && event.target ).closest( this.element.find( ".ui-menu" ) );
-
-			// If we found no valid submenu ancestor, use the main menu to close all
-			// sub menus anyway
-			if ( !currentMenu.length ) {
-				currentMenu = this.element;
-			}
-
-			this._close( currentMenu );
-
-			this.blur( event );
-
-			// Work around active item staying active after menu is blurred
-			this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );
-
-			this.activeMenu = currentMenu;
-		}, this.delay );
-	},
-
-	// With no arguments, closes the currently active menu - if nothing is active
-	// it closes all menus.  If passed an argument, it will search for menus BELOW
-	_close: function( startMenu ) {
-		if ( !startMenu ) {
-			startMenu = this.active ? this.active.parent() : this.element;
-		}
-
-		startMenu.find( ".ui-menu" )
-			.hide()
-			.attr( "aria-hidden", "true" )
-			.attr( "aria-expanded", "false" );
-	},
-
-	_closeOnDocumentClick: function( event ) {
-		return !$( event.target ).closest( ".ui-menu" ).length;
-	},
-
-	_isDivider: function( item ) {
-
-		// Match hyphen, em dash, en dash
-		return !/[^\-\u2014\u2013\s]/.test( item.text() );
-	},
-
-	collapse: function( event ) {
-		var newItem = this.active &&
-			this.active.parent().closest( ".ui-menu-item", this.element );
-		if ( newItem && newItem.length ) {
-			this._close();
-			this.focus( event, newItem );
-		}
-	},
-
-	expand: function( event ) {
-		var newItem = this.active &&
-			this.active
-				.children( ".ui-menu " )
-					.find( this.options.items )
-						.first();
-
-		if ( newItem && newItem.length ) {
-			this._open( newItem.parent() );
-
-			// Delay so Firefox will not hide activedescendant change in expanding submenu from AT
-			this._delay( function() {
-				this.focus( event, newItem );
-			} );
-		}
-	},
-
-	next: function( event ) {
-		this._move( "next", "first", event );
-	},
-
-	previous: function( event ) {
-		this._move( "prev", "last", event );
-	},
-
-	isFirstItem: function() {
-		return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
-	},
-
-	isLastItem: function() {
-		return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
-	},
-
-	_move: function( direction, filter, event ) {
-		var next;
-		if ( this.active ) {
-			if ( direction === "first" || direction === "last" ) {
-				next = this.active
-					[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
-					.eq( -1 );
-			} else {
-				next = this.active
-					[ direction + "All" ]( ".ui-menu-item" )
-					.eq( 0 );
-			}
-		}
-		if ( !next || !next.length || !this.active ) {
-			next = this.activeMenu.find( this.options.items )[ filter ]();
-		}
-
-		this.focus( event, next );
-	},
-
-	nextPage: function( event ) {
-		var item, base, height;
-
-		if ( !this.active ) {
-			this.next( event );
-			return;
-		}
-		if ( this.isLastItem() ) {
-			return;
-		}
-		if ( this._hasScroll() ) {
-			base = this.active.offset().top;
-			height = this.element.height();
-			this.active.nextAll( ".ui-menu-item" ).each( function() {
-				item = $( this );
-				return item.offset().top - base - height < 0;
-			} );
-
-			this.focus( event, item );
-		} else {
-			this.focus( event, this.activeMenu.find( this.options.items )
-				[ !this.active ? "first" : "last" ]() );
-		}
-	},
-
-	previousPage: function( event ) {
-		var item, base, height;
-		if ( !this.active ) {
-			this.next( event );
-			return;
-		}
-		if ( this.isFirstItem() ) {
-			return;
-		}
-		if ( this._hasScroll() ) {
-			base = this.active.offset().top;
-			height = this.element.height();
-			this.active.prevAll( ".ui-menu-item" ).each( function() {
-				item = $( this );
-				return item.offset().top - base + height > 0;
-			} );
-
-			this.focus( event, item );
-		} else {
-			this.focus( event, this.activeMenu.find( this.options.items ).first() );
-		}
-	},
-
-	_hasScroll: function() {
-		return this.element.outerHeight() < this.element.prop( "scrollHeight" );
-	},
-
-	select: function( event ) {
-
-		// TODO: It should never be possible to not have an active item at this
-		// point, but the tests don't trigger mouseenter before click.
-		this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
-		var ui = { item: this.active };
-		if ( !this.active.has( ".ui-menu" ).length ) {
-			this.collapseAll( event, true );
-		}
-		this._trigger( "select", event, ui );
-	},
-
-	_filterMenuItems: function( character ) {
-		var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
-			regex = new RegExp( "^" + escapedCharacter, "i" );
-
-		return this.activeMenu
-			.find( this.options.items )
-
-				// Only match on items, not dividers or other content (#10571)
-				.filter( ".ui-menu-item" )
-					.filter( function() {
-						return regex.test(
-							$.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
-					} );
-	}
-} );
-
-
-/*!
- * jQuery UI Autocomplete 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Autocomplete
-//>>group: Widgets
-//>>description: Lists suggested words as the user is typing.
-//>>docs: http://api.jqueryui.com/autocomplete/
-//>>demos: http://jqueryui.com/autocomplete/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/autocomplete.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.autocomplete", {
-	version: "1.12.1",
-	defaultElement: "<input>",
-	options: {
-		appendTo: null,
-		autoFocus: false,
-		delay: 300,
-		minLength: 1,
-		position: {
-			my: "left top",
-			at: "left bottom",
-			collision: "none"
-		},
-		source: null,
-
-		// Callbacks
-		change: null,
-		close: null,
-		focus: null,
-		open: null,
-		response: null,
-		search: null,
-		select: null
-	},
-
-	requestIndex: 0,
-	pending: 0,
-
-	_create: function() {
-
-		// Some browsers only repeat keydown events, not keypress events,
-		// so we use the suppressKeyPress flag to determine if we've already
-		// handled the keydown event. #7269
-		// Unfortunately the code for & in keypress is the same as the up arrow,
-		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
-		// events when we know the keydown event was used to modify the
-		// search term. #7799
-		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
-			nodeName = this.element[ 0 ].nodeName.toLowerCase(),
-			isTextarea = nodeName === "textarea",
-			isInput = nodeName === "input";
-
-		// Textareas are always multi-line
-		// Inputs are always single-line, even if inside a contentEditable element
-		// IE also treats inputs as contentEditable
-		// All other element types are determined by whether or not they're contentEditable
-		this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
-
-		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
-		this.isNewMenu = true;
-
-		this._addClass( "ui-autocomplete-input" );
-		this.element.attr( "autocomplete", "off" );
-
-		this._on( this.element, {
-			keydown: function( event ) {
-				if ( this.element.prop( "readOnly" ) ) {
-					suppressKeyPress = true;
-					suppressInput = true;
-					suppressKeyPressRepeat = true;
-					return;
-				}
-
-				suppressKeyPress = false;
-				suppressInput = false;
-				suppressKeyPressRepeat = false;
-				var keyCode = $.ui.keyCode;
-				switch ( event.keyCode ) {
-				case keyCode.PAGE_UP:
-					suppressKeyPress = true;
-					this._move( "previousPage", event );
-					break;
-				case keyCode.PAGE_DOWN:
-					suppressKeyPress = true;
-					this._move( "nextPage", event );
-					break;
-				case keyCode.UP:
-					suppressKeyPress = true;
-					this._keyEvent( "previous", event );
-					break;
-				case keyCode.DOWN:
-					suppressKeyPress = true;
-					this._keyEvent( "next", event );
-					break;
-				case keyCode.ENTER:
-
-					// when menu is open and has focus
-					if ( this.menu.active ) {
-
-						// #6055 - Opera still allows the keypress to occur
-						// which causes forms to submit
-						suppressKeyPress = true;
-						event.preventDefault();
-						this.menu.select( event );
-					}
-					break;
-				case keyCode.TAB:
-					if ( this.menu.active ) {
-						this.menu.select( event );
-					}
-					break;
-				case keyCode.ESCAPE:
-					if ( this.menu.element.is( ":visible" ) ) {
-						if ( !this.isMultiLine ) {
-							this._value( this.term );
-						}
-						this.close( event );
-
-						// Different browsers have different default behavior for escape
-						// Single press can mean undo or clear
-						// Double press in IE means clear the whole form
-						event.preventDefault();
-					}
-					break;
-				default:
-					suppressKeyPressRepeat = true;
-
-					// search timeout should be triggered before the input value is changed
-					this._searchTimeout( event );
-					break;
-				}
-			},
-			keypress: function( event ) {
-				if ( suppressKeyPress ) {
-					suppressKeyPress = false;
-					if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
-						event.preventDefault();
-					}
-					return;
-				}
-				if ( suppressKeyPressRepeat ) {
-					return;
-				}
-
-				// Replicate some key handlers to allow them to repeat in Firefox and Opera
-				var keyCode = $.ui.keyCode;
-				switch ( event.keyCode ) {
-				case keyCode.PAGE_UP:
-					this._move( "previousPage", event );
-					break;
-				case keyCode.PAGE_DOWN:
-					this._move( "nextPage", event );
-					break;
-				case keyCode.UP:
-					this._keyEvent( "previous", event );
-					break;
-				case keyCode.DOWN:
-					this._keyEvent( "next", event );
-					break;
-				}
-			},
-			input: function( event ) {
-				if ( suppressInput ) {
-					suppressInput = false;
-					event.preventDefault();
-					return;
-				}
-				this._searchTimeout( event );
-			},
-			focus: function() {
-				this.selectedItem = null;
-				this.previous = this._value();
-			},
-			blur: function( event ) {
-				if ( this.cancelBlur ) {
-					delete this.cancelBlur;
-					return;
-				}
-
-				clearTimeout( this.searching );
-				this.close( event );
-				this._change( event );
-			}
-		} );
-
-		this._initSource();
-		this.menu = $( "<ul>" )
-			.appendTo( this._appendTo() )
-			.menu( {
-
-				// disable ARIA support, the live region takes care of that
-				role: null
-			} )
-			.hide()
-			.menu( "instance" );
-
-		this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
-		this._on( this.menu.element, {
-			mousedown: function( event ) {
-
-				// prevent moving focus out of the text field
-				event.preventDefault();
-
-				// IE doesn't prevent moving focus even with event.preventDefault()
-				// so we set a flag to know when we should ignore the blur event
-				this.cancelBlur = true;
-				this._delay( function() {
-					delete this.cancelBlur;
-
-					// Support: IE 8 only
-					// Right clicking a menu item or selecting text from the menu items will
-					// result in focus moving out of the input. However, we've already received
-					// and ignored the blur event because of the cancelBlur flag set above. So
-					// we restore focus to ensure that the menu closes properly based on the user's
-					// next actions.
-					if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
-						this.element.trigger( "focus" );
-					}
-				} );
-			},
-			menufocus: function( event, ui ) {
-				var label, item;
-
-				// support: Firefox
-				// Prevent accidental activation of menu items in Firefox (#7024 #9118)
-				if ( this.isNewMenu ) {
-					this.isNewMenu = false;
-					if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
-						this.menu.blur();
-
-						this.document.one( "mousemove", function() {
-							$( event.target ).trigger( event.originalEvent );
-						} );
-
-						return;
-					}
-				}
-
-				item = ui.item.data( "ui-autocomplete-item" );
-				if ( false !== this._trigger( "focus", event, { item: item } ) ) {
-
-					// use value to match what will end up in the input, if it was a key event
-					if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
-						this._value( item.value );
-					}
-				}
-
-				// Announce the value in the liveRegion
-				label = ui.item.attr( "aria-label" ) || item.value;
-				if ( label && $.trim( label ).length ) {
-					this.liveRegion.children().hide();
-					$( "<div>" ).text( label ).appendTo( this.liveRegion );
-				}
-			},
-			menuselect: function( event, ui ) {
-				var item = ui.item.data( "ui-autocomplete-item" ),
-					previous = this.previous;
-
-				// Only trigger when focus was lost (click on menu)
-				if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
-					this.element.trigger( "focus" );
-					this.previous = previous;
-
-					// #6109 - IE triggers two focus events and the second
-					// is asynchronous, so we need to reset the previous
-					// term synchronously and asynchronously :-(
-					this._delay( function() {
-						this.previous = previous;
-						this.selectedItem = item;
-					} );
-				}
-
-				if ( false !== this._trigger( "select", event, { item: item } ) ) {
-					this._value( item.value );
-				}
-
-				// reset the term after the select event
-				// this allows custom select handling to work properly
-				this.term = this._value();
-
-				this.close( event );
-				this.selectedItem = item;
-			}
-		} );
-
-		this.liveRegion = $( "<div>", {
-			role: "status",
-			"aria-live": "assertive",
-			"aria-relevant": "additions"
-		} )
-			.appendTo( this.document[ 0 ].body );
-
-		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
-
-		// Turning off autocomplete prevents the browser from remembering the
-		// value when navigating through history, so we re-enable autocomplete
-		// if the page is unloaded before the widget is destroyed. #7790
-		this._on( this.window, {
-			beforeunload: function() {
-				this.element.removeAttr( "autocomplete" );
-			}
-		} );
-	},
-
-	_destroy: function() {
-		clearTimeout( this.searching );
-		this.element.removeAttr( "autocomplete" );
-		this.menu.element.remove();
-		this.liveRegion.remove();
-	},
-
-	_setOption: function( key, value ) {
-		this._super( key, value );
-		if ( key === "source" ) {
-			this._initSource();
-		}
-		if ( key === "appendTo" ) {
-			this.menu.element.appendTo( this._appendTo() );
-		}
-		if ( key === "disabled" && value && this.xhr ) {
-			this.xhr.abort();
-		}
-	},
-
-	_isEventTargetInWidget: function( event ) {
-		var menuElement = this.menu.element[ 0 ];
-
-		return event.target === this.element[ 0 ] ||
-			event.target === menuElement ||
-			$.contains( menuElement, event.target );
-	},
-
-	_closeOnClickOutside: function( event ) {
-		if ( !this._isEventTargetInWidget( event ) ) {
-			this.close();
-		}
-	},
-
-	_appendTo: function() {
-		var element = this.options.appendTo;
-
-		if ( element ) {
-			element = element.jquery || element.nodeType ?
-				$( element ) :
-				this.document.find( element ).eq( 0 );
-		}
-
-		if ( !element || !element[ 0 ] ) {
-			element = this.element.closest( ".ui-front, dialog" );
-		}
-
-		if ( !element.length ) {
-			element = this.document[ 0 ].body;
-		}
-
-		return element;
-	},
-
-	_initSource: function() {
-		var array, url,
-			that = this;
-		if ( $.isArray( this.options.source ) ) {
-			array = this.options.source;
-			this.source = function( request, response ) {
-				response( $.ui.autocomplete.filter( array, request.term ) );
-			};
-		} else if ( typeof this.options.source === "string" ) {
-			url = this.options.source;
-			this.source = function( request, response ) {
-				if ( that.xhr ) {
-					that.xhr.abort();
-				}
-				that.xhr = $.ajax( {
-					url: url,
-					data: request,
-					dataType: "json",
-					success: function( data ) {
-						response( data );
-					},
-					error: function() {
-						response( [] );
-					}
-				} );
-			};
-		} else {
-			this.source = this.options.source;
-		}
-	},
-
-	_searchTimeout: function( event ) {
-		clearTimeout( this.searching );
-		this.searching = this._delay( function() {
-
-			// Search if the value has changed, or if the user retypes the same value (see #7434)
-			var equalValues = this.term === this._value(),
-				menuVisible = this.menu.element.is( ":visible" ),
-				modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
-
-			if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
-				this.selectedItem = null;
-				this.search( null, event );
-			}
-		}, this.options.delay );
-	},
-
-	search: function( value, event ) {
-		value = value != null ? value : this._value();
-
-		// Always save the actual value, not the one passed as an argument
-		this.term = this._value();
-
-		if ( value.length < this.options.minLength ) {
-			return this.close( event );
-		}
-
-		if ( this._trigger( "search", event ) === false ) {
-			return;
-		}
-
-		return this._search( value );
-	},
-
-	_search: function( value ) {
-		this.pending++;
-		this._addClass( "ui-autocomplete-loading" );
-		this.cancelSearch = false;
-
-		this.source( { term: value }, this._response() );
-	},
-
-	_response: function() {
-		var index = ++this.requestIndex;
-
-		return $.proxy( function( content ) {
-			if ( index === this.requestIndex ) {
-				this.__response( content );
-			}
-
-			this.pending--;
-			if ( !this.pending ) {
-				this._removeClass( "ui-autocomplete-loading" );
-			}
-		}, this );
-	},
-
-	__response: function( content ) {
-		if ( content ) {
-			content = this._normalize( content );
-		}
-		this._trigger( "response", null, { content: content } );
-		if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
-			this._suggest( content );
-			this._trigger( "open" );
-		} else {
-
-			// use ._close() instead of .close() so we don't cancel future searches
-			this._close();
-		}
-	},
-
-	close: function( event ) {
-		this.cancelSearch = true;
-		this._close( event );
-	},
-
-	_close: function( event ) {
-
-		// Remove the handler that closes the menu on outside clicks
-		this._off( this.document, "mousedown" );
-
-		if ( this.menu.element.is( ":visible" ) ) {
-			this.menu.element.hide();
-			this.menu.blur();
-			this.isNewMenu = true;
-			this._trigger( "close", event );
-		}
-	},
-
-	_change: function( event ) {
-		if ( this.previous !== this._value() ) {
-			this._trigger( "change", event, { item: this.selectedItem } );
-		}
-	},
-
-	_normalize: function( items ) {
-
-		// assume all items have the right format when the first item is complete
-		if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
-			return items;
-		}
-		return $.map( items, function( item ) {
-			if ( typeof item === "string" ) {
-				return {
-					label: item,
-					value: item
-				};
-			}
-			return $.extend( {}, item, {
-				label: item.label || item.value,
-				value: item.value || item.label
-			} );
-		} );
-	},
-
-	_suggest: function( items ) {
-		var ul = this.menu.element.empty();
-		this._renderMenu( ul, items );
-		this.isNewMenu = true;
-		this.menu.refresh();
-
-		// Size and position menu
-		ul.show();
-		this._resizeMenu();
-		ul.position( $.extend( {
-			of: this.element
-		}, this.options.position ) );
-
-		if ( this.options.autoFocus ) {
-			this.menu.next();
-		}
-
-		// Listen for interactions outside of the widget (#6642)
-		this._on( this.document, {
-			mousedown: "_closeOnClickOutside"
-		} );
-	},
-
-	_resizeMenu: function() {
-		var ul = this.menu.element;
-		ul.outerWidth( Math.max(
-
-			// Firefox wraps long text (possibly a rounding bug)
-			// so we add 1px to avoid the wrapping (#7513)
-			ul.width( "" ).outerWidth() + 1,
-			this.element.outerWidth()
-		) );
-	},
-
-	_renderMenu: function( ul, items ) {
-		var that = this;
-		$.each( items, function( index, item ) {
-			that._renderItemData( ul, item );
-		} );
-	},
-
-	_renderItemData: function( ul, item ) {
-		return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
-	},
-
-	_renderItem: function( ul, item ) {
-		return $( "<li>" )
-			.append( $( "<div>" ).text( item.label ) )
-			.appendTo( ul );
-	},
-
-	_move: function( direction, event ) {
-		if ( !this.menu.element.is( ":visible" ) ) {
-			this.search( null, event );
-			return;
-		}
-		if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
-				this.menu.isLastItem() && /^next/.test( direction ) ) {
-
-			if ( !this.isMultiLine ) {
-				this._value( this.term );
-			}
-
-			this.menu.blur();
-			return;
-		}
-		this.menu[ direction ]( event );
-	},
-
-	widget: function() {
-		return this.menu.element;
-	},
-
-	_value: function() {
-		return this.valueMethod.apply( this.element, arguments );
-	},
-
-	_keyEvent: function( keyEvent, event ) {
-		if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
-			this._move( keyEvent, event );
-
-			// Prevents moving cursor to beginning/end of the text field in some browsers
-			event.preventDefault();
-		}
-	},
-
-	// Support: Chrome <=50
-	// We should be able to just use this.element.prop( "isContentEditable" )
-	// but hidden elements always report false in Chrome.
-	// https://code.google.com/p/chromium/issues/detail?id=313082
-	_isContentEditable: function( element ) {
-		if ( !element.length ) {
-			return false;
-		}
-
-		var editable = element.prop( "contentEditable" );
-
-		if ( editable === "inherit" ) {
-		  return this._isContentEditable( element.parent() );
-		}
-
-		return editable === "true";
-	}
-} );
-
-$.extend( $.ui.autocomplete, {
-	escapeRegex: function( value ) {
-		return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
-	},
-	filter: function( array, term ) {
-		var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
-		return $.grep( array, function( value ) {
-			return matcher.test( value.label || value.value || value );
-		} );
-	}
-} );
-
-// Live region extension, adding a `messages` option
-// NOTE: This is an experimental API. We are still investigating
-// a full solution for string manipulation and internationalization.
-$.widget( "ui.autocomplete", $.ui.autocomplete, {
-	options: {
-		messages: {
-			noResults: "No search results.",
-			results: function( amount ) {
-				return amount + ( amount > 1 ? " results are" : " result is" ) +
-					" available, use up and down arrow keys to navigate.";
-			}
-		}
-	},
-
-	__response: function( content ) {
-		var message;
-		this._superApply( arguments );
-		if ( this.options.disabled || this.cancelSearch ) {
-			return;
-		}
-		if ( content && content.length ) {
-			message = this.options.messages.results( content.length );
-		} else {
-			message = this.options.messages.noResults;
-		}
-		this.liveRegion.children().hide();
-		$( "<div>" ).text( message ).appendTo( this.liveRegion );
-	}
-} );
-
-var widgetsAutocomplete = $.ui.autocomplete;
-
-
-/*!
- * jQuery UI Controlgroup 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Controlgroup
-//>>group: Widgets
-//>>description: Visually groups form control widgets
-//>>docs: http://api.jqueryui.com/controlgroup/
-//>>demos: http://jqueryui.com/controlgroup/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/controlgroup.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g;
-
-var widgetsControlgroup = $.widget( "ui.controlgroup", {
-	version: "1.12.1",
-	defaultElement: "<div>",
-	options: {
-		direction: "horizontal",
-		disabled: null,
-		onlyVisible: true,
-		items: {
-			"button": "input[type=button], input[type=submit], input[type=reset], button, a",
-			"controlgroupLabel": ".ui-controlgroup-label",
-			"checkboxradio": "input[type='checkbox'], input[type='radio']",
-			"selectmenu": "select",
-			"spinner": ".ui-spinner-input"
-		}
-	},
-
-	_create: function() {
-		this._enhance();
-	},
-
-	// To support the enhanced option in jQuery Mobile, we isolate DOM manipulation
-	_enhance: function() {
-		this.element.attr( "role", "toolbar" );
-		this.refresh();
-	},
-
-	_destroy: function() {
-		this._callChildMethod( "destroy" );
-		this.childWidgets.removeData( "ui-controlgroup-data" );
-		this.element.removeAttr( "role" );
-		if ( this.options.items.controlgroupLabel ) {
-			this.element
-				.find( this.options.items.controlgroupLabel )
-				.find( ".ui-controlgroup-label-contents" )
-				.contents().unwrap();
-		}
-	},
-
-	_initWidgets: function() {
-		var that = this,
-			childWidgets = [];
-
-		// First we iterate over each of the items options
-		$.each( this.options.items, function( widget, selector ) {
-			var labels;
-			var options = {};
-
-			// Make sure the widget has a selector set
-			if ( !selector ) {
-				return;
-			}
-
-			if ( widget === "controlgroupLabel" ) {
-				labels = that.element.find( selector );
-				labels.each( function() {
-					var element = $( this );
-
-					if ( element.children( ".ui-controlgroup-label-contents" ).length ) {
-						return;
-					}
-					element.contents()
-						.wrapAll( "<span class='ui-controlgroup-label-contents'></span>" );
-				} );
-				that._addClass( labels, null, "ui-widget ui-widget-content ui-state-default" );
-				childWidgets = childWidgets.concat( labels.get() );
-				return;
-			}
-
-			// Make sure the widget actually exists
-			if ( !$.fn[ widget ] ) {
-				return;
-			}
-
-			// We assume everything is in the middle to start because we can't determine
-			// first / last elements until all enhancments are done.
-			if ( that[ "_" + widget + "Options" ] ) {
-				options = that[ "_" + widget + "Options" ]( "middle" );
-			} else {
-				options = { classes: {} };
-			}
-
-			// Find instances of this widget inside controlgroup and init them
-			that.element
-				.find( selector )
-				.each( function() {
-					var element = $( this );
-					var instance = element[ widget ]( "instance" );
-
-					// We need to clone the default options for this type of widget to avoid
-					// polluting the variable options which has a wider scope than a single widget.
-					var instanceOptions = $.widget.extend( {}, options );
-
-					// If the button is the child of a spinner ignore it
-					// TODO: Find a more generic solution
-					if ( widget === "button" && element.parent( ".ui-spinner" ).length ) {
-						return;
-					}
-
-					// Create the widget if it doesn't exist
-					if ( !instance ) {
-						instance = element[ widget ]()[ widget ]( "instance" );
-					}
-					if ( instance ) {
-						instanceOptions.classes =
-							that._resolveClassesValues( instanceOptions.classes, instance );
-					}
-					element[ widget ]( instanceOptions );
-
-					// Store an instance of the controlgroup to be able to reference
-					// from the outermost element for changing options and refresh
-					var widgetElement = element[ widget ]( "widget" );
-					$.data( widgetElement[ 0 ], "ui-controlgroup-data",
-						instance ? instance : element[ widget ]( "instance" ) );
-
-					childWidgets.push( widgetElement[ 0 ] );
-				} );
-		} );
-
-		this.childWidgets = $( $.unique( childWidgets ) );
-		this._addClass( this.childWidgets, "ui-controlgroup-item" );
-	},
-
-	_callChildMethod: function( method ) {
-		this.childWidgets.each( function() {
-			var element = $( this ),
-				data = element.data( "ui-controlgroup-data" );
-			if ( data && data[ method ] ) {
-				data[ method ]();
-			}
-		} );
-	},
-
-	_updateCornerClass: function( element, position ) {
-		var remove = "ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all";
-		var add = this._buildSimpleOptions( position, "label" ).classes.label;
-
-		this._removeClass( element, null, remove );
-		this._addClass( element, null, add );
-	},
-
-	_buildSimpleOptions: function( position, key ) {
-		var direction = this.options.direction === "vertical";
-		var result = {
-			classes: {}
-		};
-		result.classes[ key ] = {
-			"middle": "",
-			"first": "ui-corner-" + ( direction ? "top" : "left" ),
-			"last": "ui-corner-" + ( direction ? "bottom" : "right" ),
-			"only": "ui-corner-all"
-		}[ position ];
-
-		return result;
-	},
-
-	_spinnerOptions: function( position ) {
-		var options = this._buildSimpleOptions( position, "ui-spinner" );
-
-		options.classes[ "ui-spinner-up" ] = "";
-		options.classes[ "ui-spinner-down" ] = "";
-
-		return options;
-	},
-
-	_buttonOptions: function( position ) {
-		return this._buildSimpleOptions( position, "ui-button" );
-	},
-
-	_checkboxradioOptions: function( position ) {
-		return this._buildSimpleOptions( position, "ui-checkboxradio-label" );
-	},
-
-	_selectmenuOptions: function( position ) {
-		var direction = this.options.direction === "vertical";
-		return {
-			width: direction ? "auto" : false,
-			classes: {
-				middle: {
-					"ui-selectmenu-button-open": "",
-					"ui-selectmenu-button-closed": ""
-				},
-				first: {
-					"ui-selectmenu-button-open": "ui-corner-" + ( direction ? "top" : "tl" ),
-					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "top" : "left" )
-				},
-				last: {
-					"ui-selectmenu-button-open": direction ? "" : "ui-corner-tr",
-					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "bottom" : "right" )
-				},
-				only: {
-					"ui-selectmenu-button-open": "ui-corner-top",
-					"ui-selectmenu-button-closed": "ui-corner-all"
-				}
-
-			}[ position ]
-		};
-	},
-
-	_resolveClassesValues: function( classes, instance ) {
-		var result = {};
-		$.each( classes, function( key ) {
-			var current = instance.options.classes[ key ] || "";
-			current = $.trim( current.replace( controlgroupCornerRegex, "" ) );
-			result[ key ] = ( current + " " + classes[ key ] ).replace( /\s+/g, " " );
-		} );
-		return result;
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "direction" ) {
-			this._removeClass( "ui-controlgroup-" + this.options.direction );
-		}
-
-		this._super( key, value );
-		if ( key === "disabled" ) {
-			this._callChildMethod( value ? "disable" : "enable" );
-			return;
-		}
-
-		this.refresh();
-	},
-
-	refresh: function() {
-		var children,
-			that = this;
-
-		this._addClass( "ui-controlgroup ui-controlgroup-" + this.options.direction );
-
-		if ( this.options.direction === "horizontal" ) {
-			this._addClass( null, "ui-helper-clearfix" );
-		}
-		this._initWidgets();
-
-		children = this.childWidgets;
-
-		// We filter here because we need to track all childWidgets not just the visible ones
-		if ( this.options.onlyVisible ) {
-			children = children.filter( ":visible" );
-		}
-
-		if ( children.length ) {
-
-			// We do this last because we need to make sure all enhancment is done
-			// before determining first and last
-			$.each( [ "first", "last" ], function( index, value ) {
-				var instance = children[ value ]().data( "ui-controlgroup-data" );
-
-				if ( instance && that[ "_" + instance.widgetName + "Options" ] ) {
-					var options = that[ "_" + instance.widgetName + "Options" ](
-						children.length === 1 ? "only" : value
-					);
-					options.classes = that._resolveClassesValues( options.classes, instance );
-					instance.element[ instance.widgetName ]( options );
-				} else {
-					that._updateCornerClass( children[ value ](), value );
-				}
-			} );
-
-			// Finally call the refresh method on each of the child widgets.
-			this._callChildMethod( "refresh" );
-		}
-	}
-} );
-
-/*!
- * jQuery UI Checkboxradio 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Checkboxradio
-//>>group: Widgets
-//>>description: Enhances a form with multiple themeable checkboxes or radio buttons.
-//>>docs: http://api.jqueryui.com/checkboxradio/
-//>>demos: http://jqueryui.com/checkboxradio/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/button.css
-//>>css.structure: ../../themes/base/checkboxradio.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
-	version: "1.12.1",
-	options: {
-		disabled: null,
-		label: null,
-		icon: true,
-		classes: {
-			"ui-checkboxradio-label": "ui-corner-all",
-			"ui-checkboxradio-icon": "ui-corner-all"
-		}
-	},
-
-	_getCreateOptions: function() {
-		var disabled, labels;
-		var that = this;
-		var options = this._super() || {};
-
-		// We read the type here, because it makes more sense to throw a element type error first,
-		// rather then the error for lack of a label. Often if its the wrong type, it
-		// won't have a label (e.g. calling on a div, btn, etc)
-		this._readType();
-
-		labels = this.element.labels();
-
-		// If there are multiple labels, use the last one
-		this.label = $( labels[ labels.length - 1 ] );
-		if ( !this.label.length ) {
-			$.error( "No label found for checkboxradio widget" );
-		}
-
-		this.originalLabel = "";
-
-		// We need to get the label text but this may also need to make sure it does not contain the
-		// input itself.
-		this.label.contents().not( this.element[ 0 ] ).each( function() {
-
-			// The label contents could be text, html, or a mix. We concat each element to get a
-			// string representation of the label, without the input as part of it.
-			that.originalLabel += this.nodeType === 3 ? $( this ).text() : this.outerHTML;
-		} );
-
-		// Set the label option if we found label text
-		if ( this.originalLabel ) {
-			options.label = this.originalLabel;
-		}
-
-		disabled = this.element[ 0 ].disabled;
-		if ( disabled != null ) {
-			options.disabled = disabled;
-		}
-		return options;
-	},
-
-	_create: function() {
-		var checked = this.element[ 0 ].checked;
-
-		this._bindFormResetHandler();
-
-		if ( this.options.disabled == null ) {
-			this.options.disabled = this.element[ 0 ].disabled;
-		}
-
-		this._setOption( "disabled", this.options.disabled );
-		this._addClass( "ui-checkboxradio", "ui-helper-hidden-accessible" );
-		this._addClass( this.label, "ui-checkboxradio-label", "ui-button ui-widget" );
-
-		if ( this.type === "radio" ) {
-			this._addClass( this.label, "ui-checkboxradio-radio-label" );
-		}
-
-		if ( this.options.label && this.options.label !== this.originalLabel ) {
-			this._updateLabel();
-		} else if ( this.originalLabel ) {
-			this.options.label = this.originalLabel;
-		}
-
-		this._enhance();
-
-		if ( checked ) {
-			this._addClass( this.label, "ui-checkboxradio-checked", "ui-state-active" );
-			if ( this.icon ) {
-				this._addClass( this.icon, null, "ui-state-hover" );
-			}
-		}
-
-		this._on( {
-			change: "_toggleClasses",
-			focus: function() {
-				this._addClass( this.label, null, "ui-state-focus ui-visual-focus" );
-			},
-			blur: function() {
-				this._removeClass( this.label, null, "ui-state-focus ui-visual-focus" );
-			}
-		} );
-	},
-
-	_readType: function() {
-		var nodeName = this.element[ 0 ].nodeName.toLowerCase();
-		this.type = this.element[ 0 ].type;
-		if ( nodeName !== "input" || !/radio|checkbox/.test( this.type ) ) {
-			$.error( "Can't create checkboxradio on element.nodeName=" + nodeName +
-				" and element.type=" + this.type );
-		}
-	},
-
-	// Support jQuery Mobile enhanced option
-	_enhance: function() {
-		this._updateIcon( this.element[ 0 ].checked );
-	},
-
-	widget: function() {
-		return this.label;
-	},
-
-	_getRadioGroup: function() {
-		var group;
-		var name = this.element[ 0 ].name;
-		var nameSelector = "input[name='" + $.ui.escapeSelector( name ) + "']";
-
-		if ( !name ) {
-			return $( [] );
-		}
-
-		if ( this.form.length ) {
-			group = $( this.form[ 0 ].elements ).filter( nameSelector );
-		} else {
-
-			// Not inside a form, check all inputs that also are not inside a form
-			group = $( nameSelector ).filter( function() {
-				return $( this ).form().length === 0;
-			} );
-		}
-
-		return group.not( this.element );
-	},
-
-	_toggleClasses: function() {
-		var checked = this.element[ 0 ].checked;
-		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
-
-		if ( this.options.icon && this.type === "checkbox" ) {
-			this._toggleClass( this.icon, null, "ui-icon-check ui-state-checked", checked )
-				._toggleClass( this.icon, null, "ui-icon-blank", !checked );
-		}
-
-		if ( this.type === "radio" ) {
-			this._getRadioGroup()
-				.each( function() {
-					var instance = $( this ).checkboxradio( "instance" );
-
-					if ( instance ) {
-						instance._removeClass( instance.label,
-							"ui-checkboxradio-checked", "ui-state-active" );
-					}
-				} );
-		}
-	},
-
-	_destroy: function() {
-		this._unbindFormResetHandler();
-
-		if ( this.icon ) {
-			this.icon.remove();
-			this.iconSpace.remove();
-		}
-	},
-
-	_setOption: function( key, value ) {
-
-		// We don't allow the value to be set to nothing
-		if ( key === "label" && !value ) {
-			return;
-		}
-
-		this._super( key, value );
-
-		if ( key === "disabled" ) {
-			this._toggleClass( this.label, null, "ui-state-disabled", value );
-			this.element[ 0 ].disabled = value;
-
-			// Don't refresh when setting disabled
-			return;
-		}
-		this.refresh();
-	},
-
-	_updateIcon: function( checked ) {
-		var toAdd = "ui-icon ui-icon-background ";
-
-		if ( this.options.icon ) {
-			if ( !this.icon ) {
-				this.icon = $( "<span>" );
-				this.iconSpace = $( "<span> </span>" );
-				this._addClass( this.iconSpace, "ui-checkboxradio-icon-space" );
-			}
-
-			if ( this.type === "checkbox" ) {
-				toAdd += checked ? "ui-icon-check ui-state-checked" : "ui-icon-blank";
-				this._removeClass( this.icon, null, checked ? "ui-icon-blank" : "ui-icon-check" );
-			} else {
-				toAdd += "ui-icon-blank";
-			}
-			this._addClass( this.icon, "ui-checkboxradio-icon", toAdd );
-			if ( !checked ) {
-				this._removeClass( this.icon, null, "ui-icon-check ui-state-checked" );
-			}
-			this.icon.prependTo( this.label ).after( this.iconSpace );
-		} else if ( this.icon !== undefined ) {
-			this.icon.remove();
-			this.iconSpace.remove();
-			delete this.icon;
-		}
-	},
-
-	_updateLabel: function() {
-
-		// Remove the contents of the label ( minus the icon, icon space, and input )
-		var contents = this.label.contents().not( this.element[ 0 ] );
-		if ( this.icon ) {
-			contents = contents.not( this.icon[ 0 ] );
-		}
-		if ( this.iconSpace ) {
-			contents = contents.not( this.iconSpace[ 0 ] );
-		}
-		contents.remove();
-
-		this.label.append( this.options.label );
-	},
-
-	refresh: function() {
-		var checked = this.element[ 0 ].checked,
-			isDisabled = this.element[ 0 ].disabled;
-
-		this._updateIcon( checked );
-		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
-		if ( this.options.label !== null ) {
-			this._updateLabel();
-		}
-
-		if ( isDisabled !== this.options.disabled ) {
-			this._setOptions( { "disabled": isDisabled } );
-		}
-	}
-
-} ] );
-
-var widgetsCheckboxradio = $.ui.checkboxradio;
-
-
-/*!
- * jQuery UI Button 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Button
-//>>group: Widgets
-//>>description: Enhances a form with themeable buttons.
-//>>docs: http://api.jqueryui.com/button/
-//>>demos: http://jqueryui.com/button/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/button.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.button", {
-	version: "1.12.1",
-	defaultElement: "<button>",
-	options: {
-		classes: {
-			"ui-button": "ui-corner-all"
-		},
-		disabled: null,
-		icon: null,
-		iconPosition: "beginning",
-		label: null,
-		showLabel: true
-	},
-
-	_getCreateOptions: function() {
-		var disabled,
-
-			// This is to support cases like in jQuery Mobile where the base widget does have
-			// an implementation of _getCreateOptions
-			options = this._super() || {};
-
-		this.isInput = this.element.is( "input" );
-
-		disabled = this.element[ 0 ].disabled;
-		if ( disabled != null ) {
-			options.disabled = disabled;
-		}
-
-		this.originalLabel = this.isInput ? this.element.val() : this.element.html();
-		if ( this.originalLabel ) {
-			options.label = this.originalLabel;
-		}
-
-		return options;
-	},
-
-	_create: function() {
-		if ( !this.option.showLabel & !this.options.icon ) {
-			this.options.showLabel = true;
-		}
-
-		// We have to check the option again here even though we did in _getCreateOptions,
-		// because null may have been passed on init which would override what was set in
-		// _getCreateOptions
-		if ( this.options.disabled == null ) {
-			this.options.disabled = this.element[ 0 ].disabled || false;
-		}
-
-		this.hasTitle = !!this.element.attr( "title" );
-
-		// Check to see if the label needs to be set or if its already correct
-		if ( this.options.label && this.options.label !== this.originalLabel ) {
-			if ( this.isInput ) {
-				this.element.val( this.options.label );
-			} else {
-				this.element.html( this.options.label );
-			}
-		}
-		this._addClass( "ui-button", "ui-widget" );
-		this._setOption( "disabled", this.options.disabled );
-		this._enhance();
-
-		if ( this.element.is( "a" ) ) {
-			this._on( {
-				"keyup": function( event ) {
-					if ( event.keyCode === $.ui.keyCode.SPACE ) {
-						event.preventDefault();
-
-						// Support: PhantomJS <= 1.9, IE 8 Only
-						// If a native click is available use it so we actually cause navigation
-						// otherwise just trigger a click event
-						if ( this.element[ 0 ].click ) {
-							this.element[ 0 ].click();
-						} else {
-							this.element.trigger( "click" );
-						}
-					}
-				}
-			} );
-		}
-	},
-
-	_enhance: function() {
-		if ( !this.element.is( "button" ) ) {
-			this.element.attr( "role", "button" );
-		}
-
-		if ( this.options.icon ) {
-			this._updateIcon( "icon", this.options.icon );
-			this._updateTooltip();
-		}
-	},
-
-	_updateTooltip: function() {
-		this.title = this.element.attr( "title" );
-
-		if ( !this.options.showLabel && !this.title ) {
-			this.element.attr( "title", this.options.label );
-		}
-	},
-
-	_updateIcon: function( option, value ) {
-		var icon = option !== "iconPosition",
-			position = icon ? this.options.iconPosition : value,
-			displayBlock = position === "top" || position === "bottom";
-
-		// Create icon
-		if ( !this.icon ) {
-			this.icon = $( "<span>" );
-
-			this._addClass( this.icon, "ui-button-icon", "ui-icon" );
-
-			if ( !this.options.showLabel ) {
-				this._addClass( "ui-button-icon-only" );
-			}
-		} else if ( icon ) {
-
-			// If we are updating the icon remove the old icon class
-			this._removeClass( this.icon, null, this.options.icon );
-		}
-
-		// If we are updating the icon add the new icon class
-		if ( icon ) {
-			this._addClass( this.icon, null, value );
-		}
-
-		this._attachIcon( position );
-
-		// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
-		// the iconSpace if there is one.
-		if ( displayBlock ) {
-			this._addClass( this.icon, null, "ui-widget-icon-block" );
-			if ( this.iconSpace ) {
-				this.iconSpace.remove();
-			}
-		} else {
-
-			// Position is beginning or end so remove the ui-widget-icon-block class and add the
-			// space if it does not exist
-			if ( !this.iconSpace ) {
-				this.iconSpace = $( "<span> </span>" );
-				this._addClass( this.iconSpace, "ui-button-icon-space" );
-			}
-			this._removeClass( this.icon, null, "ui-wiget-icon-block" );
-			this._attachIconSpace( position );
-		}
-	},
-
-	_destroy: function() {
-		this.element.removeAttr( "role" );
-
-		if ( this.icon ) {
-			this.icon.remove();
-		}
-		if ( this.iconSpace ) {
-			this.iconSpace.remove();
-		}
-		if ( !this.hasTitle ) {
-			this.element.removeAttr( "title" );
-		}
-	},
-
-	_attachIconSpace: function( iconPosition ) {
-		this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
-	},
-
-	_attachIcon: function( iconPosition ) {
-		this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
-	},
-
-	_setOptions: function( options ) {
-		var newShowLabel = options.showLabel === undefined ?
-				this.options.showLabel :
-				options.showLabel,
-			newIcon = options.icon === undefined ? this.options.icon : options.icon;
-
-		if ( !newShowLabel && !newIcon ) {
-			options.showLabel = true;
-		}
-		this._super( options );
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "icon" ) {
-			if ( value ) {
-				this._updateIcon( key, value );
-			} else if ( this.icon ) {
-				this.icon.remove();
-				if ( this.iconSpace ) {
-					this.iconSpace.remove();
-				}
-			}
-		}
-
-		if ( key === "iconPosition" ) {
-			this._updateIcon( key, value );
-		}
-
-		// Make sure we can't end up with a button that has neither text nor icon
-		if ( key === "showLabel" ) {
-				this._toggleClass( "ui-button-icon-only", null, !value );
-				this._updateTooltip();
-		}
-
-		if ( key === "label" ) {
-			if ( this.isInput ) {
-				this.element.val( value );
-			} else {
-
-				// If there is an icon, append it, else nothing then append the value
-				// this avoids removal of the icon when setting label text
-				this.element.html( value );
-				if ( this.icon ) {
-					this._attachIcon( this.options.iconPosition );
-					this._attachIconSpace( this.options.iconPosition );
-				}
-			}
-		}
-
-		this._super( key, value );
-
-		if ( key === "disabled" ) {
-			this._toggleClass( null, "ui-state-disabled", value );
-			this.element[ 0 ].disabled = value;
-			if ( value ) {
-				this.element.blur();
-			}
-		}
-	},
-
-	refresh: function() {
-
-		// Make sure to only check disabled if its an element that supports this otherwise
-		// check for the disabled class to determine state
-		var isDisabled = this.element.is( "input, button" ) ?
-			this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );
-
-		if ( isDisabled !== this.options.disabled ) {
-			this._setOptions( { disabled: isDisabled } );
-		}
-
-		this._updateTooltip();
-	}
-} );
-
-// DEPRECATED
-if ( $.uiBackCompat !== false ) {
-
-	// Text and Icons options
-	$.widget( "ui.button", $.ui.button, {
-		options: {
-			text: true,
-			icons: {
-				primary: null,
-				secondary: null
-			}
-		},
-
-		_create: function() {
-			if ( this.options.showLabel && !this.options.text ) {
-				this.options.showLabel = this.options.text;
-			}
-			if ( !this.options.showLabel && this.options.text ) {
-				this.options.text = this.options.showLabel;
-			}
-			if ( !this.options.icon && ( this.options.icons.primary ||
-					this.options.icons.secondary ) ) {
-				if ( this.options.icons.primary ) {
-					this.options.icon = this.options.icons.primary;
-				} else {
-					this.options.icon = this.options.icons.secondary;
-					this.options.iconPosition = "end";
-				}
-			} else if ( this.options.icon ) {
-				this.options.icons.primary = this.options.icon;
-			}
-			this._super();
-		},
-
-		_setOption: function( key, value ) {
-			if ( key === "text" ) {
-				this._super( "showLabel", value );
-				return;
-			}
-			if ( key === "showLabel" ) {
-				this.options.text = value;
-			}
-			if ( key === "icon" ) {
-				this.options.icons.primary = value;
-			}
-			if ( key === "icons" ) {
-				if ( value.primary ) {
-					this._super( "icon", value.primary );
-					this._super( "iconPosition", "beginning" );
-				} else if ( value.secondary ) {
-					this._super( "icon", value.secondary );
-					this._super( "iconPosition", "end" );
-				}
-			}
-			this._superApply( arguments );
-		}
-	} );
-
-	$.fn.button = ( function( orig ) {
-		return function() {
-			if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
-					( this.length && this[ 0 ].tagName === "INPUT" && (
-						this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
-					) ) ) {
-				return orig.apply( this, arguments );
-			}
-			if ( !$.ui.checkboxradio ) {
-				$.error( "Checkboxradio widget missing" );
-			}
-			if ( arguments.length === 0 ) {
-				return this.checkboxradio( {
-					"icon": false
-				} );
-			}
-			return this.checkboxradio.apply( this, arguments );
-		};
-	} )( $.fn.button );
-
-	$.fn.buttonset = function() {
-		if ( !$.ui.controlgroup ) {
-			$.error( "Controlgroup widget missing" );
-		}
-		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
-			return this.controlgroup.apply( this,
-				[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
-		}
-		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
-			return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
-		}
-		if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
-			arguments[ 0 ].items = {
-				button: arguments[ 0 ].items
-			};
-		}
-		return this.controlgroup.apply( this, arguments );
-	};
-}
-
-var widgetsButton = $.ui.button;
-
-
-// jscs:disable maximumLineLength
-/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
-/*!
- * jQuery UI Datepicker 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Datepicker
-//>>group: Widgets
-//>>description: Displays a calendar from an input or inline for selecting dates.
-//>>docs: http://api.jqueryui.com/datepicker/
-//>>demos: http://jqueryui.com/datepicker/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/datepicker.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.extend( $.ui, { datepicker: { version: "1.12.1" } } );
-
-var datepicker_instActive;
-
-function datepicker_getZindex( elem ) {
-	var position, value;
-	while ( elem.length && elem[ 0 ] !== document ) {
-
-		// Ignore z-index if position is set to a value where z-index is ignored by the browser
-		// This makes behavior of this function consistent across browsers
-		// WebKit always returns auto if the element is positioned
-		position = elem.css( "position" );
-		if ( position === "absolute" || position === "relative" || position === "fixed" ) {
-
-			// IE returns 0 when zIndex is not specified
-			// other browsers return a string
-			// we ignore the case of nested elements with an explicit value of 0
-			// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
-			value = parseInt( elem.css( "zIndex" ), 10 );
-			if ( !isNaN( value ) && value !== 0 ) {
-				return value;
-			}
-		}
-		elem = elem.parent();
-	}
-
-	return 0;
-}
-/* Date picker manager.
-   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
-   Settings for (groups of) date pickers are maintained in an instance object,
-   allowing multiple different settings on the same page. */
-
-function Datepicker() {
-	this._curInst = null; // The current instance in use
-	this._keyEvent = false; // If the last event was a key event
-	this._disabledInputs = []; // List of date picker inputs that have been disabled
-	this._datepickerShowing = false; // True if the popup picker is showing , false if not
-	this._inDialog = false; // True if showing within a "dialog", false if not
-	this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
-	this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
-	this._appendClass = "ui-datepicker-append"; // The name of the append marker class
-	this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
-	this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
-	this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
-	this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
-	this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
-	this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
-	this.regional = []; // Available regional settings, indexed by language code
-	this.regional[ "" ] = { // Default regional settings
-		closeText: "Done", // Display text for close link
-		prevText: "Prev", // Display text for previous month link
-		nextText: "Next", // Display text for next month link
-		currentText: "Today", // Display text for current month link
-		monthNames: [ "January","February","March","April","May","June",
-			"July","August","September","October","November","December" ], // Names of months for drop-down and formatting
-		monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
-		dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
-		dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
-		dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday
-		weekHeader: "Wk", // Column header for week of the year
-		dateFormat: "mm/dd/yy", // See format options on parseDate
-		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
-		isRTL: false, // True if right-to-left language, false if left-to-right
-		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
-		yearSuffix: "" // Additional text to append to the year in the month headers
-	};
-	this._defaults = { // Global defaults for all the date picker instances
-		showOn: "focus", // "focus" for popup on focus,
-			// "button" for trigger button, or "both" for either
-		showAnim: "fadeIn", // Name of jQuery animation for popup
-		showOptions: {}, // Options for enhanced animations
-		defaultDate: null, // Used when field is blank: actual date,
-			// +/-number for offset from today, null for today
-		appendText: "", // Display text following the input box, e.g. showing the format
-		buttonText: "...", // Text for trigger button
-		buttonImage: "", // URL for trigger button image
-		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
-		hideIfNoPrevNext: false, // True to hide next/previous month links
-			// if not applicable, false to just disable them
-		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
-		gotoCurrent: false, // True if today link goes back to current selection instead
-		changeMonth: false, // True if month can be selected directly, false if only prev/next
-		changeYear: false, // True if year can be selected directly, false if only prev/next
-		yearRange: "c-10:c+10", // Range of years to display in drop-down,
-			// either relative to today's year (-nn:+nn), relative to currently displayed year
-			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
-		showOtherMonths: false, // True to show dates in other months, false to leave blank
-		selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
-		showWeek: false, // True to show week of the year, false to not show it
-		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
-			// takes a Date and returns the number of the week for it
-		shortYearCutoff: "+10", // Short year values < this are in the current century,
-			// > this are in the previous century,
-			// string value starting with "+" for current year + value
-		minDate: null, // The earliest selectable date, or null for no limit
-		maxDate: null, // The latest selectable date, or null for no limit
-		duration: "fast", // Duration of display/closure
-		beforeShowDay: null, // Function that takes a date and returns an array with
-			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
-			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
-		beforeShow: null, // Function that takes an input field and
-			// returns a set of custom settings for the date picker
-		onSelect: null, // Define a callback function when a date is selected
-		onChangeMonthYear: null, // Define a callback function when the month or year is changed
-		onClose: null, // Define a callback function when the datepicker is closed
-		numberOfMonths: 1, // Number of months to show at a time
-		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
-		stepMonths: 1, // Number of months to step back/forward
-		stepBigMonths: 12, // Number of months to step back/forward for the big links
-		altField: "", // Selector for an alternate field to store selected dates into
-		altFormat: "", // The date format to use for the alternate field
-		constrainInput: true, // The input is constrained by the current date format
-		showButtonPanel: false, // True to show button panel, false to not show it
-		autoSize: false, // True to size the input for the date format, false to leave as is
-		disabled: false // The initial disabled state
-	};
-	$.extend( this._defaults, this.regional[ "" ] );
-	this.regional.en = $.extend( true, {}, this.regional[ "" ] );
-	this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
-	this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
-}
-
-$.extend( Datepicker.prototype, {
-	/* Class name added to elements to indicate already configured with a date picker. */
-	markerClassName: "hasDatepicker",
-
-	//Keep track of the maximum number of rows displayed (see #7043)
-	maxRows: 4,
-
-	// TODO rename to "widget" when switching to widget factory
-	_widgetDatepicker: function() {
-		return this.dpDiv;
-	},
-
-	/* Override the default settings for all instances of the date picker.
-	 * @param  settings  object - the new settings to use as defaults (anonymous object)
-	 * @return the manager object
-	 */
-	setDefaults: function( settings ) {
-		datepicker_extendRemove( this._defaults, settings || {} );
-		return this;
-	},
-
-	/* Attach the date picker to a jQuery selection.
-	 * @param  target	element - the target input field or division or span
-	 * @param  settings  object - the new settings to use for this date picker instance (anonymous)
-	 */
-	_attachDatepicker: function( target, settings ) {
-		var nodeName, inline, inst;
-		nodeName = target.nodeName.toLowerCase();
-		inline = ( nodeName === "div" || nodeName === "span" );
-		if ( !target.id ) {
-			this.uuid += 1;
-			target.id = "dp" + this.uuid;
-		}
-		inst = this._newInst( $( target ), inline );
-		inst.settings = $.extend( {}, settings || {} );
-		if ( nodeName === "input" ) {
-			this._connectDatepicker( target, inst );
-		} else if ( inline ) {
-			this._inlineDatepicker( target, inst );
-		}
-	},
-
-	/* Create a new instance object. */
-	_newInst: function( target, inline ) {
-		var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
-		return { id: id, input: target, // associated target
-			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
-			drawMonth: 0, drawYear: 0, // month being drawn
-			inline: inline, // is datepicker inline or not
-			dpDiv: ( !inline ? this.dpDiv : // presentation div
-			datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
-	},
-
-	/* Attach the date picker to an input field. */
-	_connectDatepicker: function( target, inst ) {
-		var input = $( target );
-		inst.append = $( [] );
-		inst.trigger = $( [] );
-		if ( input.hasClass( this.markerClassName ) ) {
-			return;
-		}
-		this._attachments( input, inst );
-		input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
-			on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
-		this._autoSize( inst );
-		$.data( target, "datepicker", inst );
-
-		//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
-		if ( inst.settings.disabled ) {
-			this._disableDatepicker( target );
-		}
-	},
-
-	/* Make attachments based on settings. */
-	_attachments: function( input, inst ) {
-		var showOn, buttonText, buttonImage,
-			appendText = this._get( inst, "appendText" ),
-			isRTL = this._get( inst, "isRTL" );
-
-		if ( inst.append ) {
-			inst.append.remove();
-		}
-		if ( appendText ) {
-			inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
-			input[ isRTL ? "before" : "after" ]( inst.append );
-		}
-
-		input.off( "focus", this._showDatepicker );
-
-		if ( inst.trigger ) {
-			inst.trigger.remove();
-		}
-
-		showOn = this._get( inst, "showOn" );
-		if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
-			input.on( "focus", this._showDatepicker );
-		}
-		if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
-			buttonText = this._get( inst, "buttonText" );
-			buttonImage = this._get( inst, "buttonImage" );
-			inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
-				$( "<img/>" ).addClass( this._triggerClass ).
-					attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
-				$( "<button type='button'></button>" ).addClass( this._triggerClass ).
-					html( !buttonImage ? buttonText : $( "<img/>" ).attr(
-					{ src:buttonImage, alt:buttonText, title:buttonText } ) ) );
-			input[ isRTL ? "before" : "after" ]( inst.trigger );
-			inst.trigger.on( "click", function() {
-				if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
-					$.datepicker._hideDatepicker();
-				} else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
-					$.datepicker._hideDatepicker();
-					$.datepicker._showDatepicker( input[ 0 ] );
-				} else {
-					$.datepicker._showDatepicker( input[ 0 ] );
-				}
-				return false;
-			} );
-		}
-	},
-
-	/* Apply the maximum length for the date format. */
-	_autoSize: function( inst ) {
-		if ( this._get( inst, "autoSize" ) && !inst.inline ) {
-			var findMax, max, maxI, i,
-				date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
-				dateFormat = this._get( inst, "dateFormat" );
-
-			if ( dateFormat.match( /[DM]/ ) ) {
-				findMax = function( names ) {
-					max = 0;
-					maxI = 0;
-					for ( i = 0; i < names.length; i++ ) {
-						if ( names[ i ].length > max ) {
-							max = names[ i ].length;
-							maxI = i;
-						}
-					}
-					return maxI;
-				};
-				date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
-					"monthNames" : "monthNamesShort" ) ) ) );
-				date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
-					"dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
-			}
-			inst.input.attr( "size", this._formatDate( inst, date ).length );
-		}
-	},
-
-	/* Attach an inline date picker to a div. */
-	_inlineDatepicker: function( target, inst ) {
-		var divSpan = $( target );
-		if ( divSpan.hasClass( this.markerClassName ) ) {
-			return;
-		}
-		divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
-		$.data( target, "datepicker", inst );
-		this._setDate( inst, this._getDefaultDate( inst ), true );
-		this._updateDatepicker( inst );
-		this._updateAlternate( inst );
-
-		//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
-		if ( inst.settings.disabled ) {
-			this._disableDatepicker( target );
-		}
-
-		// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
-		// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
-		inst.dpDiv.css( "display", "block" );
-	},
-
-	/* Pop-up the date picker in a "dialog" box.
-	 * @param  input element - ignored
-	 * @param  date	string or Date - the initial date to display
-	 * @param  onSelect  function - the function to call when a date is selected
-	 * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
-	 * @param  pos int[2] - coordinates for the dialog's position within the screen or
-	 *					event - with x/y coordinates or
-	 *					leave empty for default (screen centre)
-	 * @return the manager object
-	 */
-	_dialogDatepicker: function( input, date, onSelect, settings, pos ) {
-		var id, browserWidth, browserHeight, scrollX, scrollY,
-			inst = this._dialogInst; // internal instance
-
-		if ( !inst ) {
-			this.uuid += 1;
-			id = "dp" + this.uuid;
-			this._dialogInput = $( "<input type='text' id='" + id +
-				"' style='position: absolute; top: -100px; width: 0px;'/>" );
-			this._dialogInput.on( "keydown", this._doKeyDown );
-			$( "body" ).append( this._dialogInput );
-			inst = this._dialogInst = this._newInst( this._dialogInput, false );
-			inst.settings = {};
-			$.data( this._dialogInput[ 0 ], "datepicker", inst );
-		}
-		datepicker_extendRemove( inst.settings, settings || {} );
-		date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
-		this._dialogInput.val( date );
-
-		this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
-		if ( !this._pos ) {
-			browserWidth = document.documentElement.clientWidth;
-			browserHeight = document.documentElement.clientHeight;
-			scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
-			scrollY = document.documentElement.scrollTop || document.body.scrollTop;
-			this._pos = // should use actual width/height below
-				[ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
-		}
-
-		// Move input on screen for focus, but hidden behind dialog
-		this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
-		inst.settings.onSelect = onSelect;
-		this._inDialog = true;
-		this.dpDiv.addClass( this._dialogClass );
-		this._showDatepicker( this._dialogInput[ 0 ] );
-		if ( $.blockUI ) {
-			$.blockUI( this.dpDiv );
-		}
-		$.data( this._dialogInput[ 0 ], "datepicker", inst );
-		return this;
-	},
-
-	/* Detach a datepicker from its control.
-	 * @param  target	element - the target input field or division or span
-	 */
-	_destroyDatepicker: function( target ) {
-		var nodeName,
-			$target = $( target ),
-			inst = $.data( target, "datepicker" );
-
-		if ( !$target.hasClass( this.markerClassName ) ) {
-			return;
-		}
-
-		nodeName = target.nodeName.toLowerCase();
-		$.removeData( target, "datepicker" );
-		if ( nodeName === "input" ) {
-			inst.append.remove();
-			inst.trigger.remove();
-			$target.removeClass( this.markerClassName ).
-				off( "focus", this._showDatepicker ).
-				off( "keydown", this._doKeyDown ).
-				off( "keypress", this._doKeyPress ).
-				off( "keyup", this._doKeyUp );
-		} else if ( nodeName === "div" || nodeName === "span" ) {
-			$target.removeClass( this.markerClassName ).empty();
-		}
-
-		if ( datepicker_instActive === inst ) {
-			datepicker_instActive = null;
-		}
-	},
-
-	/* Enable the date picker to a jQuery selection.
-	 * @param  target	element - the target input field or division or span
-	 */
-	_enableDatepicker: function( target ) {
-		var nodeName, inline,
-			$target = $( target ),
-			inst = $.data( target, "datepicker" );
-
-		if ( !$target.hasClass( this.markerClassName ) ) {
-			return;
-		}
-
-		nodeName = target.nodeName.toLowerCase();
-		if ( nodeName === "input" ) {
-			target.disabled = false;
-			inst.trigger.filter( "button" ).
-				each( function() { this.disabled = false; } ).end().
-				filter( "img" ).css( { opacity: "1.0", cursor: "" } );
-		} else if ( nodeName === "div" || nodeName === "span" ) {
-			inline = $target.children( "." + this._inlineClass );
-			inline.children().removeClass( "ui-state-disabled" );
-			inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
-				prop( "disabled", false );
-		}
-		this._disabledInputs = $.map( this._disabledInputs,
-			function( value ) { return ( value === target ? null : value ); } ); // delete entry
-	},
-
-	/* Disable the date picker to a jQuery selection.
-	 * @param  target	element - the target input field or division or span
-	 */
-	_disableDatepicker: function( target ) {
-		var nodeName, inline,
-			$target = $( target ),
-			inst = $.data( target, "datepicker" );
-
-		if ( !$target.hasClass( this.markerClassName ) ) {
-			return;
-		}
-
-		nodeName = target.nodeName.toLowerCase();
-		if ( nodeName === "input" ) {
-			target.disabled = true;
-			inst.trigger.filter( "button" ).
-				each( function() { this.disabled = true; } ).end().
-				filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
-		} else if ( nodeName === "div" || nodeName === "span" ) {
-			inline = $target.children( "." + this._inlineClass );
-			inline.children().addClass( "ui-state-disabled" );
-			inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
-				prop( "disabled", true );
-		}
-		this._disabledInputs = $.map( this._disabledInputs,
-			function( value ) { return ( value === target ? null : value ); } ); // delete entry
-		this._disabledInputs[ this._disabledInputs.length ] = target;
-	},
-
-	/* Is the first field in a jQuery collection disabled as a datepicker?
-	 * @param  target	element - the target input field or division or span
-	 * @return boolean - true if disabled, false if enabled
-	 */
-	_isDisabledDatepicker: function( target ) {
-		if ( !target ) {
-			return false;
-		}
-		for ( var i = 0; i < this._disabledInputs.length; i++ ) {
-			if ( this._disabledInputs[ i ] === target ) {
-				return true;
-			}
-		}
-		return false;
-	},
-
-	/* Retrieve the instance data for the target control.
-	 * @param  target  element - the target input field or division or span
-	 * @return  object - the associated instance data
-	 * @throws  error if a jQuery problem getting data
-	 */
-	_getInst: function( target ) {
-		try {
-			return $.data( target, "datepicker" );
-		}
-		catch ( err ) {
-			throw "Missing instance data for this datepicker";
-		}
-	},
-
-	/* Update or retrieve the settings for a date picker attached to an input field or division.
-	 * @param  target  element - the target input field or division or span
-	 * @param  name	object - the new settings to update or
-	 *				string - the name of the setting to change or retrieve,
-	 *				when retrieving also "all" for all instance settings or
-	 *				"defaults" for all global defaults
-	 * @param  value   any - the new value for the setting
-	 *				(omit if above is an object or to retrieve a value)
-	 */
-	_optionDatepicker: function( target, name, value ) {
-		var settings, date, minDate, maxDate,
-			inst = this._getInst( target );
-
-		if ( arguments.length === 2 && typeof name === "string" ) {
-			return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
-				( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
-				this._get( inst, name ) ) : null ) );
-		}
-
-		settings = name || {};
-		if ( typeof name === "string" ) {
-			settings = {};
-			settings[ name ] = value;
-		}
-
-		if ( inst ) {
-			if ( this._curInst === inst ) {
-				this._hideDatepicker();
-			}
-
-			date = this._getDateDatepicker( target, true );
-			minDate = this._getMinMaxDate( inst, "min" );
-			maxDate = this._getMinMaxDate( inst, "max" );
-			datepicker_extendRemove( inst.settings, settings );
-
-			// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
-			if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
-				inst.settings.minDate = this._formatDate( inst, minDate );
-			}
-			if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
-				inst.settings.maxDate = this._formatDate( inst, maxDate );
-			}
-			if ( "disabled" in settings ) {
-				if ( settings.disabled ) {
-					this._disableDatepicker( target );
-				} else {
-					this._enableDatepicker( target );
-				}
-			}
-			this._attachments( $( target ), inst );
-			this._autoSize( inst );
-			this._setDate( inst, date );
-			this._updateAlternate( inst );
-			this._updateDatepicker( inst );
-		}
-	},
-
-	// Change method deprecated
-	_changeDatepicker: function( target, name, value ) {
-		this._optionDatepicker( target, name, value );
-	},
-
-	/* Redraw the date picker attached to an input field or division.
-	 * @param  target  element - the target input field or division or span
-	 */
-	_refreshDatepicker: function( target ) {
-		var inst = this._getInst( target );
-		if ( inst ) {
-			this._updateDatepicker( inst );
-		}
-	},
-
-	/* Set the dates for a jQuery selection.
-	 * @param  target element - the target input field or division or span
-	 * @param  date	Date - the new date
-	 */
-	_setDateDatepicker: function( target, date ) {
-		var inst = this._getInst( target );
-		if ( inst ) {
-			this._setDate( inst, date );
-			this._updateDatepicker( inst );
-			this._updateAlternate( inst );
-		}
-	},
-
-	/* Get the date(s) for the first entry in a jQuery selection.
-	 * @param  target element - the target input field or division or span
-	 * @param  noDefault boolean - true if no default date is to be used
-	 * @return Date - the current date
-	 */
-	_getDateDatepicker: function( target, noDefault ) {
-		var inst = this._getInst( target );
-		if ( inst && !inst.inline ) {
-			this._setDateFromField( inst, noDefault );
-		}
-		return ( inst ? this._getDate( inst ) : null );
-	},
-
-	/* Handle keystrokes. */
-	_doKeyDown: function( event ) {
-		var onSelect, dateStr, sel,
-			inst = $.datepicker._getInst( event.target ),
-			handled = true,
-			isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );
-
-		inst._keyEvent = true;
-		if ( $.datepicker._datepickerShowing ) {
-			switch ( event.keyCode ) {
-				case 9: $.datepicker._hideDatepicker();
-						handled = false;
-						break; // hide on tab out
-				case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
-									$.datepicker._currentClass + ")", inst.dpDiv );
-						if ( sel[ 0 ] ) {
-							$.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
-						}
-
-						onSelect = $.datepicker._get( inst, "onSelect" );
-						if ( onSelect ) {
-							dateStr = $.datepicker._formatDate( inst );
-
-							// Trigger custom callback
-							onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
-						} else {
-							$.datepicker._hideDatepicker();
-						}
-
-						return false; // don't submit the form
-				case 27: $.datepicker._hideDatepicker();
-						break; // hide on escape
-				case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
-							-$.datepicker._get( inst, "stepBigMonths" ) :
-							-$.datepicker._get( inst, "stepMonths" ) ), "M" );
-						break; // previous month/year on page up/+ ctrl
-				case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
-							+$.datepicker._get( inst, "stepBigMonths" ) :
-							+$.datepicker._get( inst, "stepMonths" ) ), "M" );
-						break; // next month/year on page down/+ ctrl
-				case 35: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._clearDate( event.target );
-						}
-						handled = event.ctrlKey || event.metaKey;
-						break; // clear on ctrl or command +end
-				case 36: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._gotoToday( event.target );
-						}
-						handled = event.ctrlKey || event.metaKey;
-						break; // current on ctrl or command +home
-				case 37: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
-						}
-						handled = event.ctrlKey || event.metaKey;
-
-						// -1 day on ctrl or command +left
-						if ( event.originalEvent.altKey ) {
-							$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
-								-$.datepicker._get( inst, "stepBigMonths" ) :
-								-$.datepicker._get( inst, "stepMonths" ) ), "M" );
-						}
-
-						// next month/year on alt +left on Mac
-						break;
-				case 38: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._adjustDate( event.target, -7, "D" );
-						}
-						handled = event.ctrlKey || event.metaKey;
-						break; // -1 week on ctrl or command +up
-				case 39: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
-						}
-						handled = event.ctrlKey || event.metaKey;
-
-						// +1 day on ctrl or command +right
-						if ( event.originalEvent.altKey ) {
-							$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
-								+$.datepicker._get( inst, "stepBigMonths" ) :
-								+$.datepicker._get( inst, "stepMonths" ) ), "M" );
-						}
-
-						// next month/year on alt +right
-						break;
-				case 40: if ( event.ctrlKey || event.metaKey ) {
-							$.datepicker._adjustDate( event.target, +7, "D" );
-						}
-						handled = event.ctrlKey || event.metaKey;
-						break; // +1 week on ctrl or command +down
-				default: handled = false;
-			}
-		} else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
-			$.datepicker._showDatepicker( this );
-		} else {
-			handled = false;
-		}
-
-		if ( handled ) {
-			event.preventDefault();
-			event.stopPropagation();
-		}
-	},
-
-	/* Filter entered characters - based on date format. */
-	_doKeyPress: function( event ) {
-		var chars, chr,
-			inst = $.datepicker._getInst( event.target );
-
-		if ( $.datepicker._get( inst, "constrainInput" ) ) {
-			chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
-			chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
-			return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
-		}
-	},
-
-	/* Synchronise manual entry and field/alternate field. */
-	_doKeyUp: function( event ) {
-		var date,
-			inst = $.datepicker._getInst( event.target );
-
-		if ( inst.input.val() !== inst.lastVal ) {
-			try {
-				date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
-					( inst.input ? inst.input.val() : null ),
-					$.datepicker._getFormatConfig( inst ) );
-
-				if ( date ) { // only if valid
-					$.datepicker._setDateFromField( inst );
-					$.datepicker._updateAlternate( inst );
-					$.datepicker._updateDatepicker( inst );
-				}
-			}
-			catch ( err ) {
-			}
-		}
-		return true;
-	},
-
-	/* Pop-up the date picker for a given input field.
-	 * If false returned from beforeShow event handler do not show.
-	 * @param  input  element - the input field attached to the date picker or
-	 *					event - if triggered by focus
-	 */
-	_showDatepicker: function( input ) {
-		input = input.target || input;
-		if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
-			input = $( "input", input.parentNode )[ 0 ];
-		}
-
-		if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
-			return;
-		}
-
-		var inst, beforeShow, beforeShowSettings, isFixed,
-			offset, showAnim, duration;
-
-		inst = $.datepicker._getInst( input );
-		if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
-			$.datepicker._curInst.dpDiv.stop( true, true );
-			if ( inst && $.datepicker._datepickerShowing ) {
-				$.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
-			}
-		}
-
-		beforeShow = $.datepicker._get( inst, "beforeShow" );
-		beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
-		if ( beforeShowSettings === false ) {
-			return;
-		}
-		datepicker_extendRemove( inst.settings, beforeShowSettings );
-
-		inst.lastVal = null;
-		$.datepicker._lastInput = input;
-		$.datepicker._setDateFromField( inst );
-
-		if ( $.datepicker._inDialog ) { // hide cursor
-			input.value = "";
-		}
-		if ( !$.datepicker._pos ) { // position below input
-			$.datepicker._pos = $.datepicker._findPos( input );
-			$.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
-		}
-
-		isFixed = false;
-		$( input ).parents().each( function() {
-			isFixed |= $( this ).css( "position" ) === "fixed";
-			return !isFixed;
-		} );
-
-		offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
-		$.datepicker._pos = null;
-
-		//to avoid flashes on Firefox
-		inst.dpDiv.empty();
-
-		// determine sizing offscreen
-		inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
-		$.datepicker._updateDatepicker( inst );
-
-		// fix width for dynamic number of date pickers
-		// and adjust position before showing
-		offset = $.datepicker._checkOffset( inst, offset, isFixed );
-		inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
-			"static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
-			left: offset.left + "px", top: offset.top + "px" } );
-
-		if ( !inst.inline ) {
-			showAnim = $.datepicker._get( inst, "showAnim" );
-			duration = $.datepicker._get( inst, "duration" );
-			inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
-			$.datepicker._datepickerShowing = true;
-
-			if ( $.effects && $.effects.effect[ showAnim ] ) {
-				inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
-			} else {
-				inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
-			}
-
-			if ( $.datepicker._shouldFocusInput( inst ) ) {
-				inst.input.trigger( "focus" );
-			}
-
-			$.datepicker._curInst = inst;
-		}
-	},
-
-	/* Generate the date picker content. */
-	_updateDatepicker: function( inst ) {
-		this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
-		datepicker_instActive = inst; // for delegate hover events
-		inst.dpDiv.empty().append( this._generateHTML( inst ) );
-		this._attachHandlers( inst );
-
-		var origyearshtml,
-			numMonths = this._getNumberOfMonths( inst ),
-			cols = numMonths[ 1 ],
-			width = 17,
-			activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
-
-		if ( activeCell.length > 0 ) {
-			datepicker_handleMouseover.apply( activeCell.get( 0 ) );
-		}
-
-		inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
-		if ( cols > 1 ) {
-			inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
-		}
-		inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
-			"Class" ]( "ui-datepicker-multi" );
-		inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
-			"Class" ]( "ui-datepicker-rtl" );
-
-		if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
-			inst.input.trigger( "focus" );
-		}
-
-		// Deffered render of the years select (to avoid flashes on Firefox)
-		if ( inst.yearshtml ) {
-			origyearshtml = inst.yearshtml;
-			setTimeout( function() {
-
-				//assure that inst.yearshtml didn't change.
-				if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
-					inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml );
-				}
-				origyearshtml = inst.yearshtml = null;
-			}, 0 );
-		}
-	},
-
-	// #6694 - don't focus the input if it's already focused
-	// this breaks the change event in IE
-	// Support: IE and jQuery <1.9
-	_shouldFocusInput: function( inst ) {
-		return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
-	},
-
-	/* Check positioning to remain on screen. */
-	_checkOffset: function( inst, offset, isFixed ) {
-		var dpWidth = inst.dpDiv.outerWidth(),
-			dpHeight = inst.dpDiv.outerHeight(),
-			inputWidth = inst.input ? inst.input.outerWidth() : 0,
-			inputHeight = inst.input ? inst.input.outerHeight() : 0,
-			viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
-			viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );
-
-		offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
-		offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
-		offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;
-
-		// Now check if datepicker is showing outside window viewport - move to a better place if so.
-		offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
-			Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
-		offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
-			Math.abs( dpHeight + inputHeight ) : 0 );
-
-		return offset;
-	},
-
-	/* Find an object's position on the screen. */
-	_findPos: function( obj ) {
-		var position,
-			inst = this._getInst( obj ),
-			isRTL = this._get( inst, "isRTL" );
-
-		while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) {
-			obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
-		}
-
-		position = $( obj ).offset();
-		return [ position.left, position.top ];
-	},
-
-	/* Hide the date picker from view.
-	 * @param  input  element - the input field attached to the date picker
-	 */
-	_hideDatepicker: function( input ) {
-		var showAnim, duration, postProcess, onClose,
-			inst = this._curInst;
-
-		if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
-			return;
-		}
-
-		if ( this._datepickerShowing ) {
-			showAnim = this._get( inst, "showAnim" );
-			duration = this._get( inst, "duration" );
-			postProcess = function() {
-				$.datepicker._tidyDialog( inst );
-			};
-
-			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
-			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
-				inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
-			} else {
-				inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
-					( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
-			}
-
-			if ( !showAnim ) {
-				postProcess();
-			}
-			this._datepickerShowing = false;
-
-			onClose = this._get( inst, "onClose" );
-			if ( onClose ) {
-				onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
-			}
-
-			this._lastInput = null;
-			if ( this._inDialog ) {
-				this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
-				if ( $.blockUI ) {
-					$.unblockUI();
-					$( "body" ).append( this.dpDiv );
-				}
-			}
-			this._inDialog = false;
-		}
-	},
-
-	/* Tidy up after a dialog display. */
-	_tidyDialog: function( inst ) {
-		inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
-	},
-
-	/* Close date picker if clicked elsewhere. */
-	_checkExternalClick: function( event ) {
-		if ( !$.datepicker._curInst ) {
-			return;
-		}
-
-		var $target = $( event.target ),
-			inst = $.datepicker._getInst( $target[ 0 ] );
-
-		if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
-				$target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
-				!$target.hasClass( $.datepicker.markerClassName ) &&
-				!$target.closest( "." + $.datepicker._triggerClass ).length &&
-				$.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
-			( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
-				$.datepicker._hideDatepicker();
-		}
-	},
-
-	/* Adjust one of the date sub-fields. */
-	_adjustDate: function( id, offset, period ) {
-		var target = $( id ),
-			inst = this._getInst( target[ 0 ] );
-
-		if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
-			return;
-		}
-		this._adjustInstDate( inst, offset +
-			( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning
-			period );
-		this._updateDatepicker( inst );
-	},
-
-	/* Action for current link. */
-	_gotoToday: function( id ) {
-		var date,
-			target = $( id ),
-			inst = this._getInst( target[ 0 ] );
-
-		if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
-			inst.selectedDay = inst.currentDay;
-			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
-			inst.drawYear = inst.selectedYear = inst.currentYear;
-		} else {
-			date = new Date();
-			inst.selectedDay = date.getDate();
-			inst.drawMonth = inst.selectedMonth = date.getMonth();
-			inst.drawYear = inst.selectedYear = date.getFullYear();
-		}
-		this._notifyChange( inst );
-		this._adjustDate( target );
-	},
-
-	/* Action for selecting a new month/year. */
-	_selectMonthYear: function( id, select, period ) {
-		var target = $( id ),
-			inst = this._getInst( target[ 0 ] );
-
-		inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
-		inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
-			parseInt( select.options[ select.selectedIndex ].value, 10 );
-
-		this._notifyChange( inst );
-		this._adjustDate( target );
-	},
-
-	/* Action for selecting a day. */
-	_selectDay: function( id, month, year, td ) {
-		var inst,
-			target = $( id );
-
-		if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
-			return;
-		}
-
-		inst = this._getInst( target[ 0 ] );
-		inst.selectedDay = inst.currentDay = $( "a", td ).html();
-		inst.selectedMonth = inst.currentMonth = month;
-		inst.selectedYear = inst.currentYear = year;
-		this._selectDate( id, this._formatDate( inst,
-			inst.currentDay, inst.currentMonth, inst.currentYear ) );
-	},
-
-	/* Erase the input field and hide the date picker. */
-	_clearDate: function( id ) {
-		var target = $( id );
-		this._selectDate( target, "" );
-	},
-
-	/* Update the input field with the selected date. */
-	_selectDate: function( id, dateStr ) {
-		var onSelect,
-			target = $( id ),
-			inst = this._getInst( target[ 0 ] );
-
-		dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
-		if ( inst.input ) {
-			inst.input.val( dateStr );
-		}
-		this._updateAlternate( inst );
-
-		onSelect = this._get( inst, "onSelect" );
-		if ( onSelect ) {
-			onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );  // trigger custom callback
-		} else if ( inst.input ) {
-			inst.input.trigger( "change" ); // fire the change event
-		}
-
-		if ( inst.inline ) {
-			this._updateDatepicker( inst );
-		} else {
-			this._hideDatepicker();
-			this._lastInput = inst.input[ 0 ];
-			if ( typeof( inst.input[ 0 ] ) !== "object" ) {
-				inst.input.trigger( "focus" ); // restore focus
-			}
-			this._lastInput = null;
-		}
-	},
-
-	/* Update any alternate field to synchronise with the main field. */
-	_updateAlternate: function( inst ) {
-		var altFormat, date, dateStr,
-			altField = this._get( inst, "altField" );
-
-		if ( altField ) { // update alternate field too
-			altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
-			date = this._getDate( inst );
-			dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
-			$( altField ).val( dateStr );
-		}
-	},
-
-	/* Set as beforeShowDay function to prevent selection of weekends.
-	 * @param  date  Date - the date to customise
-	 * @return [boolean, string] - is this date selectable?, what is its CSS class?
-	 */
-	noWeekends: function( date ) {
-		var day = date.getDay();
-		return [ ( day > 0 && day < 6 ), "" ];
-	},
-
-	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
-	 * @param  date  Date - the date to get the week for
-	 * @return  number - the number of the week within the year that contains this date
-	 */
-	iso8601Week: function( date ) {
-		var time,
-			checkDate = new Date( date.getTime() );
-
-		// Find Thursday of this week starting on Monday
-		checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
-
-		time = checkDate.getTime();
-		checkDate.setMonth( 0 ); // Compare with Jan 1
-		checkDate.setDate( 1 );
-		return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
-	},
-
-	/* Parse a string value into a date object.
-	 * See formatDate below for the possible formats.
-	 *
-	 * @param  format string - the expected format of the date
-	 * @param  value string - the date in the above format
-	 * @param  settings Object - attributes include:
-	 *					shortYearCutoff  number - the cutoff year for determining the century (optional)
-	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
-	 *					dayNames		string[7] - names of the days from Sunday (optional)
-	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
-	 *					monthNames		string[12] - names of the months (optional)
-	 * @return  Date - the extracted date value or null if value is blank
-	 */
-	parseDate: function( format, value, settings ) {
-		if ( format == null || value == null ) {
-			throw "Invalid arguments";
-		}
-
-		value = ( typeof value === "object" ? value.toString() : value + "" );
-		if ( value === "" ) {
-			return null;
-		}
-
-		var iFormat, dim, extra,
-			iValue = 0,
-			shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
-			shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
-				new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
-			dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
-			dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
-			monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
-			monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
-			year = -1,
-			month = -1,
-			day = -1,
-			doy = -1,
-			literal = false,
-			date,
-
-			// Check whether a format character is doubled
-			lookAhead = function( match ) {
-				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
-				if ( matches ) {
-					iFormat++;
-				}
-				return matches;
-			},
-
-			// Extract a number from the string value
-			getNumber = function( match ) {
-				var isDoubled = lookAhead( match ),
-					size = ( match === "@" ? 14 : ( match === "!" ? 20 :
-					( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
-					minSize = ( match === "y" ? size : 1 ),
-					digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
-					num = value.substring( iValue ).match( digits );
-				if ( !num ) {
-					throw "Missing number at position " + iValue;
-				}
-				iValue += num[ 0 ].length;
-				return parseInt( num[ 0 ], 10 );
-			},
-
-			// Extract a name from the string value and convert to an index
-			getName = function( match, shortNames, longNames ) {
-				var index = -1,
-					names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
-						return [ [ k, v ] ];
-					} ).sort( function( a, b ) {
-						return -( a[ 1 ].length - b[ 1 ].length );
-					} );
-
-				$.each( names, function( i, pair ) {
-					var name = pair[ 1 ];
-					if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
-						index = pair[ 0 ];
-						iValue += name.length;
-						return false;
-					}
-				} );
-				if ( index !== -1 ) {
-					return index + 1;
-				} else {
-					throw "Unknown name at position " + iValue;
-				}
-			},
-
-			// Confirm that a literal character matches the string value
-			checkLiteral = function() {
-				if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
-					throw "Unexpected literal at position " + iValue;
-				}
-				iValue++;
-			};
-
-		for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
-			if ( literal ) {
-				if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
-					literal = false;
-				} else {
-					checkLiteral();
-				}
-			} else {
-				switch ( format.charAt( iFormat ) ) {
-					case "d":
-						day = getNumber( "d" );
-						break;
-					case "D":
-						getName( "D", dayNamesShort, dayNames );
-						break;
-					case "o":
-						doy = getNumber( "o" );
-						break;
-					case "m":
-						month = getNumber( "m" );
-						break;
-					case "M":
-						month = getName( "M", monthNamesShort, monthNames );
-						break;
-					case "y":
-						year = getNumber( "y" );
-						break;
-					case "@":
-						date = new Date( getNumber( "@" ) );
-						year = date.getFullYear();
-						month = date.getMonth() + 1;
-						day = date.getDate();
-						break;
-					case "!":
-						date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
-						year = date.getFullYear();
-						month = date.getMonth() + 1;
-						day = date.getDate();
-						break;
-					case "'":
-						if ( lookAhead( "'" ) ) {
-							checkLiteral();
-						} else {
-							literal = true;
-						}
-						break;
-					default:
-						checkLiteral();
-				}
-			}
-		}
-
-		if ( iValue < value.length ) {
-			extra = value.substr( iValue );
-			if ( !/^\s+/.test( extra ) ) {
-				throw "Extra/unparsed characters found in date: " + extra;
-			}
-		}
-
-		if ( year === -1 ) {
-			year = new Date().getFullYear();
-		} else if ( year < 100 ) {
-			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
-				( year <= shortYearCutoff ? 0 : -100 );
-		}
-
-		if ( doy > -1 ) {
-			month = 1;
-			day = doy;
-			do {
-				dim = this._getDaysInMonth( year, month - 1 );
-				if ( day <= dim ) {
-					break;
-				}
-				month++;
-				day -= dim;
-			} while ( true );
-		}
-
-		date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
-		if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
-			throw "Invalid date"; // E.g. 31/02/00
-		}
-		return date;
-	},
-
-	/* Standard date formats. */
-	ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
-	COOKIE: "D, dd M yy",
-	ISO_8601: "yy-mm-dd",
-	RFC_822: "D, d M y",
-	RFC_850: "DD, dd-M-y",
-	RFC_1036: "D, d M y",
-	RFC_1123: "D, d M yy",
-	RFC_2822: "D, d M yy",
-	RSS: "D, d M y", // RFC 822
-	TICKS: "!",
-	TIMESTAMP: "@",
-	W3C: "yy-mm-dd", // ISO 8601
-
-	_ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
-		Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),
-
-	/* Format a date object into a string value.
-	 * The format can be combinations of the following:
-	 * d  - day of month (no leading zero)
-	 * dd - day of month (two digit)
-	 * o  - day of year (no leading zeros)
-	 * oo - day of year (three digit)
-	 * D  - day name short
-	 * DD - day name long
-	 * m  - month of year (no leading zero)
-	 * mm - month of year (two digit)
-	 * M  - month name short
-	 * MM - month name long
-	 * y  - year (two digit)
-	 * yy - year (four digit)
-	 * @ - Unix timestamp (ms since 01/01/1970)
-	 * ! - Windows ticks (100ns since 01/01/0001)
-	 * "..." - literal text
-	 * '' - single quote
-	 *
-	 * @param  format string - the desired format of the date
-	 * @param  date Date - the date value to format
-	 * @param  settings Object - attributes include:
-	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
-	 *					dayNames		string[7] - names of the days from Sunday (optional)
-	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
-	 *					monthNames		string[12] - names of the months (optional)
-	 * @return  string - the date in the above format
-	 */
-	formatDate: function( format, date, settings ) {
-		if ( !date ) {
-			return "";
-		}
-
-		var iFormat,
-			dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
-			dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
-			monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
-			monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
-
-			// Check whether a format character is doubled
-			lookAhead = function( match ) {
-				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
-				if ( matches ) {
-					iFormat++;
-				}
-				return matches;
-			},
-
-			// Format a number, with leading zero if necessary
-			formatNumber = function( match, value, len ) {
-				var num = "" + value;
-				if ( lookAhead( match ) ) {
-					while ( num.length < len ) {
-						num = "0" + num;
-					}
-				}
-				return num;
-			},
-
-			// Format a name, short or long as requested
-			formatName = function( match, value, shortNames, longNames ) {
-				return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
-			},
-			output = "",
-			literal = false;
-
-		if ( date ) {
-			for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
-				if ( literal ) {
-					if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
-						literal = false;
-					} else {
-						output += format.charAt( iFormat );
-					}
-				} else {
-					switch ( format.charAt( iFormat ) ) {
-						case "d":
-							output += formatNumber( "d", date.getDate(), 2 );
-							break;
-						case "D":
-							output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
-							break;
-						case "o":
-							output += formatNumber( "o",
-								Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
-							break;
-						case "m":
-							output += formatNumber( "m", date.getMonth() + 1, 2 );
-							break;
-						case "M":
-							output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
-							break;
-						case "y":
-							output += ( lookAhead( "y" ) ? date.getFullYear() :
-								( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
-							break;
-						case "@":
-							output += date.getTime();
-							break;
-						case "!":
-							output += date.getTime() * 10000 + this._ticksTo1970;
-							break;
-						case "'":
-							if ( lookAhead( "'" ) ) {
-								output += "'";
-							} else {
-								literal = true;
-							}
-							break;
-						default:
-							output += format.charAt( iFormat );
-					}
-				}
-			}
-		}
-		return output;
-	},
-
-	/* Extract all possible characters from the date format. */
-	_possibleChars: function( format ) {
-		var iFormat,
-			chars = "",
-			literal = false,
-
-			// Check whether a format character is doubled
-			lookAhead = function( match ) {
-				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
-				if ( matches ) {
-					iFormat++;
-				}
-				return matches;
-			};
-
-		for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
-			if ( literal ) {
-				if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
-					literal = false;
-				} else {
-					chars += format.charAt( iFormat );
-				}
-			} else {
-				switch ( format.charAt( iFormat ) ) {
-					case "d": case "m": case "y": case "@":
-						chars += "0123456789";
-						break;
-					case "D": case "M":
-						return null; // Accept anything
-					case "'":
-						if ( lookAhead( "'" ) ) {
-							chars += "'";
-						} else {
-							literal = true;
-						}
-						break;
-					default:
-						chars += format.charAt( iFormat );
-				}
-			}
-		}
-		return chars;
-	},
-
-	/* Get a setting value, defaulting if necessary. */
-	_get: function( inst, name ) {
-		return inst.settings[ name ] !== undefined ?
-			inst.settings[ name ] : this._defaults[ name ];
-	},
-
-	/* Parse existing date and initialise date picker. */
-	_setDateFromField: function( inst, noDefault ) {
-		if ( inst.input.val() === inst.lastVal ) {
-			return;
-		}
-
-		var dateFormat = this._get( inst, "dateFormat" ),
-			dates = inst.lastVal = inst.input ? inst.input.val() : null,
-			defaultDate = this._getDefaultDate( inst ),
-			date = defaultDate,
-			settings = this._getFormatConfig( inst );
-
-		try {
-			date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
-		} catch ( event ) {
-			dates = ( noDefault ? "" : dates );
-		}
-		inst.selectedDay = date.getDate();
-		inst.drawMonth = inst.selectedMonth = date.getMonth();
-		inst.drawYear = inst.selectedYear = date.getFullYear();
-		inst.currentDay = ( dates ? date.getDate() : 0 );
-		inst.currentMonth = ( dates ? date.getMonth() : 0 );
-		inst.currentYear = ( dates ? date.getFullYear() : 0 );
-		this._adjustInstDate( inst );
-	},
-
-	/* Retrieve the default date shown on opening. */
-	_getDefaultDate: function( inst ) {
-		return this._restrictMinMax( inst,
-			this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
-	},
-
-	/* A date may be specified as an exact value or a relative one. */
-	_determineDate: function( inst, date, defaultDate ) {
-		var offsetNumeric = function( offset ) {
-				var date = new Date();
-				date.setDate( date.getDate() + offset );
-				return date;
-			},
-			offsetString = function( offset ) {
-				try {
-					return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
-						offset, $.datepicker._getFormatConfig( inst ) );
-				}
-				catch ( e ) {
-
-					// Ignore
-				}
-
-				var date = ( offset.toLowerCase().match( /^c/ ) ?
-					$.datepicker._getDate( inst ) : null ) || new Date(),
-					year = date.getFullYear(),
-					month = date.getMonth(),
-					day = date.getDate(),
-					pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
-					matches = pattern.exec( offset );
-
-				while ( matches ) {
-					switch ( matches[ 2 ] || "d" ) {
-						case "d" : case "D" :
-							day += parseInt( matches[ 1 ], 10 ); break;
-						case "w" : case "W" :
-							day += parseInt( matches[ 1 ], 10 ) * 7; break;
-						case "m" : case "M" :
-							month += parseInt( matches[ 1 ], 10 );
-							day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
-							break;
-						case "y": case "Y" :
-							year += parseInt( matches[ 1 ], 10 );
-							day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
-							break;
-					}
-					matches = pattern.exec( offset );
-				}
-				return new Date( year, month, day );
-			},
-			newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
-				( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );
-
-		newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
-		if ( newDate ) {
-			newDate.setHours( 0 );
-			newDate.setMinutes( 0 );
-			newDate.setSeconds( 0 );
-			newDate.setMilliseconds( 0 );
-		}
-		return this._daylightSavingAdjust( newDate );
-	},
-
-	/* Handle switch to/from daylight saving.
-	 * Hours may be non-zero on daylight saving cut-over:
-	 * > 12 when midnight changeover, but then cannot generate
-	 * midnight datetime, so jump to 1AM, otherwise reset.
-	 * @param  date  (Date) the date to check
-	 * @return  (Date) the corrected date
-	 */
-	_daylightSavingAdjust: function( date ) {
-		if ( !date ) {
-			return null;
-		}
-		date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
-		return date;
-	},
-
-	/* Set the date(s) directly. */
-	_setDate: function( inst, date, noChange ) {
-		var clear = !date,
-			origMonth = inst.selectedMonth,
-			origYear = inst.selectedYear,
-			newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );
-
-		inst.selectedDay = inst.currentDay = newDate.getDate();
-		inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
-		inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
-		if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
-			this._notifyChange( inst );
-		}
-		this._adjustInstDate( inst );
-		if ( inst.input ) {
-			inst.input.val( clear ? "" : this._formatDate( inst ) );
-		}
-	},
-
-	/* Retrieve the date(s) directly. */
-	_getDate: function( inst ) {
-		var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
-			this._daylightSavingAdjust( new Date(
-			inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
-			return startDate;
-	},
-
-	/* Attach the onxxx handlers.  These are declared statically so
-	 * they work with static code transformers like Caja.
-	 */
-	_attachHandlers: function( inst ) {
-		var stepMonths = this._get( inst, "stepMonths" ),
-			id = "#" + inst.id.replace( /\\\\/g, "\\" );
-		inst.dpDiv.find( "[data-handler]" ).map( function() {
-			var handler = {
-				prev: function() {
-					$.datepicker._adjustDate( id, -stepMonths, "M" );
-				},
-				next: function() {
-					$.datepicker._adjustDate( id, +stepMonths, "M" );
-				},
-				hide: function() {
-					$.datepicker._hideDatepicker();
-				},
-				today: function() {
-					$.datepicker._gotoToday( id );
-				},
-				selectDay: function() {
-					$.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
-					return false;
-				},
-				selectMonth: function() {
-					$.datepicker._selectMonthYear( id, this, "M" );
-					return false;
-				},
-				selectYear: function() {
-					$.datepicker._selectMonthYear( id, this, "Y" );
-					return false;
-				}
-			};
-			$( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
-		} );
-	},
-
-	/* Generate the HTML for the current state of the date picker. */
-	_generateHTML: function( inst ) {
-		var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
-			controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
-			monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
-			selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
-			cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
-			printDate, dRow, tbody, daySettings, otherMonth, unselectable,
-			tempDate = new Date(),
-			today = this._daylightSavingAdjust(
-				new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
-			isRTL = this._get( inst, "isRTL" ),
-			showButtonPanel = this._get( inst, "showButtonPanel" ),
-			hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
-			navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
-			numMonths = this._getNumberOfMonths( inst ),
-			showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
-			stepMonths = this._get( inst, "stepMonths" ),
-			isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
-			currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
-				new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
-			minDate = this._getMinMaxDate( inst, "min" ),
-			maxDate = this._getMinMaxDate( inst, "max" ),
-			drawMonth = inst.drawMonth - showCurrentAtPos,
-			drawYear = inst.drawYear;
-
-		if ( drawMonth < 0 ) {
-			drawMonth += 12;
-			drawYear--;
-		}
-		if ( maxDate ) {
-			maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
-				maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
-			maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
-			while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
-				drawMonth--;
-				if ( drawMonth < 0 ) {
-					drawMonth = 11;
-					drawYear--;
-				}
-			}
-		}
-		inst.drawMonth = drawMonth;
-		inst.drawYear = drawYear;
-
-		prevText = this._get( inst, "prevText" );
-		prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
-			this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
-			this._getFormatConfig( inst ) ) );
-
-		prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
-			"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
-			" title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
-			( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" ) );
-
-		nextText = this._get( inst, "nextText" );
-		nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
-			this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
-			this._getFormatConfig( inst ) ) );
-
-		next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
-			"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
-			" title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
-			( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" ) );
-
-		currentText = this._get( inst, "currentText" );
-		gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
-		currentText = ( !navigationAsDateFormat ? currentText :
-			this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
-
-		controls = ( !inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
-			this._get( inst, "closeText" ) + "</button>" : "" );
-
-		buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
-			( this._isInRange( inst, gotoDate ) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
-			">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";
-
-		firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
-		firstDay = ( isNaN( firstDay ) ? 0 : firstDay );
-
-		showWeek = this._get( inst, "showWeek" );
-		dayNames = this._get( inst, "dayNames" );
-		dayNamesMin = this._get( inst, "dayNamesMin" );
-		monthNames = this._get( inst, "monthNames" );
-		monthNamesShort = this._get( inst, "monthNamesShort" );
-		beforeShowDay = this._get( inst, "beforeShowDay" );
-		showOtherMonths = this._get( inst, "showOtherMonths" );
-		selectOtherMonths = this._get( inst, "selectOtherMonths" );
-		defaultDate = this._getDefaultDate( inst );
-		html = "";
-
-		for ( row = 0; row < numMonths[ 0 ]; row++ ) {
-			group = "";
-			this.maxRows = 4;
-			for ( col = 0; col < numMonths[ 1 ]; col++ ) {
-				selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
-				cornerClass = " ui-corner-all";
-				calender = "";
-				if ( isMultiMonth ) {
-					calender += "<div class='ui-datepicker-group";
-					if ( numMonths[ 1 ] > 1 ) {
-						switch ( col ) {
-							case 0: calender += " ui-datepicker-group-first";
-								cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
-							case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
-								cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
-							default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
-						}
-					}
-					calender += "'>";
-				}
-				calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
-					( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
-					( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
-					this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
-					row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
-					"</div><table class='ui-datepicker-calendar'><thead>" +
-					"<tr>";
-				thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
-				for ( dow = 0; dow < 7; dow++ ) { // days of the week
-					day = ( dow + firstDay ) % 7;
-					thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
-						"<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
-				}
-				calender += thead + "</tr></thead><tbody>";
-				daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
-				if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
-					inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
-				}
-				leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
-				curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
-				numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
-				this.maxRows = numRows;
-				printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
-				for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
-					calender += "<tr>";
-					tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
-						this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
-					for ( dow = 0; dow < 7; dow++ ) { // create date picker days
-						daySettings = ( beforeShowDay ?
-							beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
-						otherMonth = ( printDate.getMonth() !== drawMonth );
-						unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
-							( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
-						tbody += "<td class='" +
-							( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
-							( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
-							( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
-							( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?
-
-							// or defaultDate is current printedDate and defaultDate is selectedDate
-							" " + this._dayOverClass : "" ) + // highlight selected day
-							( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) +  // highlight unselectable days
-							( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
-							( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
-							( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
-							( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "&#39;" ) + "'" : "" ) + // cell title
-							( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
-							( otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
-							( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
-							( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
-							( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
-							( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
-							"' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
-						printDate.setDate( printDate.getDate() + 1 );
-						printDate = this._daylightSavingAdjust( printDate );
-					}
-					calender += tbody + "</tr>";
-				}
-				drawMonth++;
-				if ( drawMonth > 11 ) {
-					drawMonth = 0;
-					drawYear++;
-				}
-				calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
-							( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
-				group += calender;
-			}
-			html += group;
-		}
-		html += buttonPanel;
-		inst._keyEvent = false;
-		return html;
-	},
-
-	/* Generate the month and year header. */
-	_generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
-			secondary, monthNames, monthNamesShort ) {
-
-		var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
-			changeMonth = this._get( inst, "changeMonth" ),
-			changeYear = this._get( inst, "changeYear" ),
-			showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
-			html = "<div class='ui-datepicker-title'>",
-			monthHtml = "";
-
-		// Month selection
-		if ( secondary || !changeMonth ) {
-			monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
-		} else {
-			inMinYear = ( minDate && minDate.getFullYear() === drawYear );
-			inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
-			monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
-			for ( month = 0; month < 12; month++ ) {
-				if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
-					monthHtml += "<option value='" + month + "'" +
-						( month === drawMonth ? " selected='selected'" : "" ) +
-						">" + monthNamesShort[ month ] + "</option>";
-				}
-			}
-			monthHtml += "</select>";
-		}
-
-		if ( !showMonthAfterYear ) {
-			html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" );
-		}
-
-		// Year selection
-		if ( !inst.yearshtml ) {
-			inst.yearshtml = "";
-			if ( secondary || !changeYear ) {
-				html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
-			} else {
-
-				// determine range of years to display
-				years = this._get( inst, "yearRange" ).split( ":" );
-				thisYear = new Date().getFullYear();
-				determineYear = function( value ) {
-					var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
-						( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
-						parseInt( value, 10 ) ) );
-					return ( isNaN( year ) ? thisYear : year );
-				};
-				year = determineYear( years[ 0 ] );
-				endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
-				year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
-				endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
-				inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
-				for ( ; year <= endYear; year++ ) {
-					inst.yearshtml += "<option value='" + year + "'" +
-						( year === drawYear ? " selected='selected'" : "" ) +
-						">" + year + "</option>";
-				}
-				inst.yearshtml += "</select>";
-
-				html += inst.yearshtml;
-				inst.yearshtml = null;
-			}
-		}
-
-		html += this._get( inst, "yearSuffix" );
-		if ( showMonthAfterYear ) {
-			html += ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" ) + monthHtml;
-		}
-		html += "</div>"; // Close datepicker_header
-		return html;
-	},
-
-	/* Adjust one of the date sub-fields. */
-	_adjustInstDate: function( inst, offset, period ) {
-		var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
-			month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
-			day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
-			date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
-
-		inst.selectedDay = date.getDate();
-		inst.drawMonth = inst.selectedMonth = date.getMonth();
-		inst.drawYear = inst.selectedYear = date.getFullYear();
-		if ( period === "M" || period === "Y" ) {
-			this._notifyChange( inst );
-		}
-	},
-
-	/* Ensure a date is within any min/max bounds. */
-	_restrictMinMax: function( inst, date ) {
-		var minDate = this._getMinMaxDate( inst, "min" ),
-			maxDate = this._getMinMaxDate( inst, "max" ),
-			newDate = ( minDate && date < minDate ? minDate : date );
-		return ( maxDate && newDate > maxDate ? maxDate : newDate );
-	},
-
-	/* Notify change of month/year. */
-	_notifyChange: function( inst ) {
-		var onChange = this._get( inst, "onChangeMonthYear" );
-		if ( onChange ) {
-			onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
-				[ inst.selectedYear, inst.selectedMonth + 1, inst ] );
-		}
-	},
-
-	/* Determine the number of months to show. */
-	_getNumberOfMonths: function( inst ) {
-		var numMonths = this._get( inst, "numberOfMonths" );
-		return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
-	},
-
-	/* Determine the current maximum date - ensure no time components are set. */
-	_getMinMaxDate: function( inst, minMax ) {
-		return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
-	},
-
-	/* Find the number of days in a given month. */
-	_getDaysInMonth: function( year, month ) {
-		return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
-	},
-
-	/* Find the day of the week of the first of a month. */
-	_getFirstDayOfMonth: function( year, month ) {
-		return new Date( year, month, 1 ).getDay();
-	},
-
-	/* Determines if we should allow a "next/prev" month display change. */
-	_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
-		var numMonths = this._getNumberOfMonths( inst ),
-			date = this._daylightSavingAdjust( new Date( curYear,
-			curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
-
-		if ( offset < 0 ) {
-			date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
-		}
-		return this._isInRange( inst, date );
-	},
-
-	/* Is the given date in the accepted range? */
-	_isInRange: function( inst, date ) {
-		var yearSplit, currentYear,
-			minDate = this._getMinMaxDate( inst, "min" ),
-			maxDate = this._getMinMaxDate( inst, "max" ),
-			minYear = null,
-			maxYear = null,
-			years = this._get( inst, "yearRange" );
-			if ( years ) {
-				yearSplit = years.split( ":" );
-				currentYear = new Date().getFullYear();
-				minYear = parseInt( yearSplit[ 0 ], 10 );
-				maxYear = parseInt( yearSplit[ 1 ], 10 );
-				if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
-					minYear += currentYear;
-				}
-				if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
-					maxYear += currentYear;
-				}
-			}
-
-		return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
-			( !maxDate || date.getTime() <= maxDate.getTime() ) &&
-			( !minYear || date.getFullYear() >= minYear ) &&
-			( !maxYear || date.getFullYear() <= maxYear ) );
-	},
-
-	/* Provide the configuration settings for formatting/parsing. */
-	_getFormatConfig: function( inst ) {
-		var shortYearCutoff = this._get( inst, "shortYearCutoff" );
-		shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
-			new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
-		return { shortYearCutoff: shortYearCutoff,
-			dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
-			monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
-	},
-
-	/* Format the given date for display. */
-	_formatDate: function( inst, day, month, year ) {
-		if ( !day ) {
-			inst.currentDay = inst.selectedDay;
-			inst.currentMonth = inst.selectedMonth;
-			inst.currentYear = inst.selectedYear;
-		}
-		var date = ( day ? ( typeof day === "object" ? day :
-			this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
-			this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
-		return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
-	}
-} );
-
-/*
- * Bind hover events for datepicker elements.
- * Done via delegate so the binding only occurs once in the lifetime of the parent div.
- * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
- */
-function datepicker_bindHover( dpDiv ) {
-	var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
-	return dpDiv.on( "mouseout", selector, function() {
-			$( this ).removeClass( "ui-state-hover" );
-			if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
-				$( this ).removeClass( "ui-datepicker-prev-hover" );
-			}
-			if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
-				$( this ).removeClass( "ui-datepicker-next-hover" );
-			}
-		} )
-		.on( "mouseover", selector, datepicker_handleMouseover );
-}
-
-function datepicker_handleMouseover() {
-	if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
-		$( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
-		$( this ).addClass( "ui-state-hover" );
-		if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
-			$( this ).addClass( "ui-datepicker-prev-hover" );
-		}
-		if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
-			$( this ).addClass( "ui-datepicker-next-hover" );
-		}
-	}
-}
-
-/* jQuery extend now ignores nulls! */
-function datepicker_extendRemove( target, props ) {
-	$.extend( target, props );
-	for ( var name in props ) {
-		if ( props[ name ] == null ) {
-			target[ name ] = props[ name ];
-		}
-	}
-	return target;
-}
-
-/* Invoke the datepicker functionality.
-   @param  options  string - a command, optionally followed by additional parameters or
-					Object - settings for attaching new datepicker functionality
-   @return  jQuery object */
-$.fn.datepicker = function( options ) {
-
-	/* Verify an empty collection wasn't passed - Fixes #6976 */
-	if ( !this.length ) {
-		return this;
-	}
-
-	/* Initialise the date picker. */
-	if ( !$.datepicker.initialized ) {
-		$( document ).on( "mousedown", $.datepicker._checkExternalClick );
-		$.datepicker.initialized = true;
-	}
-
-	/* Append datepicker main container to body if not exist. */
-	if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
-		$( "body" ).append( $.datepicker.dpDiv );
-	}
-
-	var otherArgs = Array.prototype.slice.call( arguments, 1 );
-	if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
-		return $.datepicker[ "_" + options + "Datepicker" ].
-			apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
-	}
-	if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
-		return $.datepicker[ "_" + options + "Datepicker" ].
-			apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
-	}
-	return this.each( function() {
-		typeof options === "string" ?
-			$.datepicker[ "_" + options + "Datepicker" ].
-				apply( $.datepicker, [ this ].concat( otherArgs ) ) :
-			$.datepicker._attachDatepicker( this, options );
-	} );
-};
-
-$.datepicker = new Datepicker(); // singleton instance
-$.datepicker.initialized = false;
-$.datepicker.uuid = new Date().getTime();
-$.datepicker.version = "1.12.1";
-
-var widgetsDatepicker = $.datepicker;
-
-
-
-
-// This file is deprecated
-var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
-
-/*!
- * jQuery UI Mouse 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Mouse
-//>>group: Widgets
-//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
-//>>docs: http://api.jqueryui.com/mouse/
-
-
-
-var mouseHandled = false;
-$( document ).on( "mouseup", function() {
-	mouseHandled = false;
-} );
-
-var widgetsMouse = $.widget( "ui.mouse", {
-	version: "1.12.1",
-	options: {
-		cancel: "input, textarea, button, select, option",
-		distance: 1,
-		delay: 0
-	},
-	_mouseInit: function() {
-		var that = this;
-
-		this.element
-			.on( "mousedown." + this.widgetName, function( event ) {
-				return that._mouseDown( event );
-			} )
-			.on( "click." + this.widgetName, function( event ) {
-				if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
-					$.removeData( event.target, that.widgetName + ".preventClickEvent" );
-					event.stopImmediatePropagation();
-					return false;
-				}
-			} );
-
-		this.started = false;
-	},
-
-	// TODO: make sure destroying one instance of mouse doesn't mess with
-	// other instances of mouse
-	_mouseDestroy: function() {
-		this.element.off( "." + this.widgetName );
-		if ( this._mouseMoveDelegate ) {
-			this.document
-				.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
-				.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
-		}
-	},
-
-	_mouseDown: function( event ) {
-
-		// don't let more than one widget handle mouseStart
-		if ( mouseHandled ) {
-			return;
-		}
-
-		this._mouseMoved = false;
-
-		// We may have missed mouseup (out of window)
-		( this._mouseStarted && this._mouseUp( event ) );
-
-		this._mouseDownEvent = event;
-
-		var that = this,
-			btnIsLeft = ( event.which === 1 ),
-
-			// event.target.nodeName works around a bug in IE 8 with
-			// disabled inputs (#7620)
-			elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
-				$( event.target ).closest( this.options.cancel ).length : false );
-		if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
-			return true;
-		}
-
-		this.mouseDelayMet = !this.options.delay;
-		if ( !this.mouseDelayMet ) {
-			this._mouseDelayTimer = setTimeout( function() {
-				that.mouseDelayMet = true;
-			}, this.options.delay );
-		}
-
-		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
-			this._mouseStarted = ( this._mouseStart( event ) !== false );
-			if ( !this._mouseStarted ) {
-				event.preventDefault();
-				return true;
-			}
-		}
-
-		// Click event may never have fired (Gecko & Opera)
-		if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
-			$.removeData( event.target, this.widgetName + ".preventClickEvent" );
-		}
-
-		// These delegates are required to keep context
-		this._mouseMoveDelegate = function( event ) {
-			return that._mouseMove( event );
-		};
-		this._mouseUpDelegate = function( event ) {
-			return that._mouseUp( event );
-		};
-
-		this.document
-			.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
-			.on( "mouseup." + this.widgetName, this._mouseUpDelegate );
-
-		event.preventDefault();
-
-		mouseHandled = true;
-		return true;
-	},
-
-	_mouseMove: function( event ) {
-
-		// Only check for mouseups outside the document if you've moved inside the document
-		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
-		// fire a mousemove event if content is placed under the cursor. See #7778
-		// Support: IE <9
-		if ( this._mouseMoved ) {
-
-			// IE mouseup check - mouseup happened when mouse was out of window
-			if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
-					!event.button ) {
-				return this._mouseUp( event );
-
-			// Iframe mouseup check - mouseup occurred in another document
-			} else if ( !event.which ) {
-
-				// Support: Safari <=8 - 9
-				// Safari sets which to 0 if you press any of the following keys
-				// during a drag (#14461)
-				if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
-						event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
-					this.ignoreMissingWhich = true;
-				} else if ( !this.ignoreMissingWhich ) {
-					return this._mouseUp( event );
-				}
-			}
-		}
-
-		if ( event.which || event.button ) {
-			this._mouseMoved = true;
-		}
-
-		if ( this._mouseStarted ) {
-			this._mouseDrag( event );
-			return event.preventDefault();
-		}
-
-		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
-			this._mouseStarted =
-				( this._mouseStart( this._mouseDownEvent, event ) !== false );
-			( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
-		}
-
-		return !this._mouseStarted;
-	},
-
-	_mouseUp: function( event ) {
-		this.document
-			.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
-			.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
-
-		if ( this._mouseStarted ) {
-			this._mouseStarted = false;
-
-			if ( event.target === this._mouseDownEvent.target ) {
-				$.data( event.target, this.widgetName + ".preventClickEvent", true );
-			}
-
-			this._mouseStop( event );
-		}
-
-		if ( this._mouseDelayTimer ) {
-			clearTimeout( this._mouseDelayTimer );
-			delete this._mouseDelayTimer;
-		}
-
-		this.ignoreMissingWhich = false;
-		mouseHandled = false;
-		event.preventDefault();
-	},
-
-	_mouseDistanceMet: function( event ) {
-		return ( Math.max(
-				Math.abs( this._mouseDownEvent.pageX - event.pageX ),
-				Math.abs( this._mouseDownEvent.pageY - event.pageY )
-			) >= this.options.distance
-		);
-	},
-
-	_mouseDelayMet: function( /* event */ ) {
-		return this.mouseDelayMet;
-	},
-
-	// These are placeholder methods, to be overriden by extending plugin
-	_mouseStart: function( /* event */ ) {},
-	_mouseDrag: function( /* event */ ) {},
-	_mouseStop: function( /* event */ ) {},
-	_mouseCapture: function( /* event */ ) { return true; }
-} );
-
-
-
-
-// $.ui.plugin is deprecated. Use $.widget() extensions instead.
-var plugin = $.ui.plugin = {
-	add: function( module, option, set ) {
-		var i,
-			proto = $.ui[ module ].prototype;
-		for ( i in set ) {
-			proto.plugins[ i ] = proto.plugins[ i ] || [];
-			proto.plugins[ i ].push( [ option, set[ i ] ] );
-		}
-	},
-	call: function( instance, name, args, allowDisconnected ) {
-		var i,
-			set = instance.plugins[ name ];
-
-		if ( !set ) {
-			return;
-		}
-
-		if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||
-				instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
-			return;
-		}
-
-		for ( i = 0; i < set.length; i++ ) {
-			if ( instance.options[ set[ i ][ 0 ] ] ) {
-				set[ i ][ 1 ].apply( instance.element, args );
-			}
-		}
-	}
-};
-
-
-
-var safeBlur = $.ui.safeBlur = function( element ) {
-
-	// Support: IE9 - 10 only
-	// If the <body> is blurred, IE will switch windows, see #9420
-	if ( element && element.nodeName.toLowerCase() !== "body" ) {
-		$( element ).trigger( "blur" );
-	}
-};
-
-
-/*!
- * jQuery UI Draggable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Draggable
-//>>group: Interactions
-//>>description: Enables dragging functionality for any element.
-//>>docs: http://api.jqueryui.com/draggable/
-//>>demos: http://jqueryui.com/draggable/
-//>>css.structure: ../../themes/base/draggable.css
-
-
-
-$.widget( "ui.draggable", $.ui.mouse, {
-	version: "1.12.1",
-	widgetEventPrefix: "drag",
-	options: {
-		addClasses: true,
-		appendTo: "parent",
-		axis: false,
-		connectToSortable: false,
-		containment: false,
-		cursor: "auto",
-		cursorAt: false,
-		grid: false,
-		handle: false,
-		helper: "original",
-		iframeFix: false,
-		opacity: false,
-		refreshPositions: false,
-		revert: false,
-		revertDuration: 500,
-		scope: "default",
-		scroll: true,
-		scrollSensitivity: 20,
-		scrollSpeed: 20,
-		snap: false,
-		snapMode: "both",
-		snapTolerance: 20,
-		stack: false,
-		zIndex: false,
-
-		// Callbacks
-		drag: null,
-		start: null,
-		stop: null
-	},
-	_create: function() {
-
-		if ( this.options.helper === "original" ) {
-			this._setPositionRelative();
-		}
-		if ( this.options.addClasses ) {
-			this._addClass( "ui-draggable" );
-		}
-		this._setHandleClassName();
-
-		this._mouseInit();
-	},
-
-	_setOption: function( key, value ) {
-		this._super( key, value );
-		if ( key === "handle" ) {
-			this._removeHandleClassName();
-			this._setHandleClassName();
-		}
-	},
-
-	_destroy: function() {
-		if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
-			this.destroyOnClear = true;
-			return;
-		}
-		this._removeHandleClassName();
-		this._mouseDestroy();
-	},
-
-	_mouseCapture: function( event ) {
-		var o = this.options;
-
-		// Among others, prevent a drag on a resizable-handle
-		if ( this.helper || o.disabled ||
-				$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
-			return false;
-		}
-
-		//Quit if we're not on a valid handle
-		this.handle = this._getHandle( event );
-		if ( !this.handle ) {
-			return false;
-		}
-
-		this._blurActiveElement( event );
-
-		this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
-
-		return true;
-
-	},
-
-	_blockFrames: function( selector ) {
-		this.iframeBlocks = this.document.find( selector ).map( function() {
-			var iframe = $( this );
-
-			return $( "<div>" )
-				.css( "position", "absolute" )
-				.appendTo( iframe.parent() )
-				.outerWidth( iframe.outerWidth() )
-				.outerHeight( iframe.outerHeight() )
-				.offset( iframe.offset() )[ 0 ];
-		} );
-	},
-
-	_unblockFrames: function() {
-		if ( this.iframeBlocks ) {
-			this.iframeBlocks.remove();
-			delete this.iframeBlocks;
-		}
-	},
-
-	_blurActiveElement: function( event ) {
-		var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
-			target = $( event.target );
-
-		// Don't blur if the event occurred on an element that is within
-		// the currently focused element
-		// See #10527, #12472
-		if ( target.closest( activeElement ).length ) {
-			return;
-		}
-
-		// Blur any element that currently has focus, see #4261
-		$.ui.safeBlur( activeElement );
-	},
-
-	_mouseStart: function( event ) {
-
-		var o = this.options;
-
-		//Create and append the visible helper
-		this.helper = this._createHelper( event );
-
-		this._addClass( this.helper, "ui-draggable-dragging" );
-
-		//Cache the helper size
-		this._cacheHelperProportions();
-
-		//If ddmanager is used for droppables, set the global draggable
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.current = this;
-		}
-
-		/*
-		 * - Position generation -
-		 * This block generates everything position related - it's the core of draggables.
-		 */
-
-		//Cache the margins of the original element
-		this._cacheMargins();
-
-		//Store the helper's css position
-		this.cssPosition = this.helper.css( "position" );
-		this.scrollParent = this.helper.scrollParent( true );
-		this.offsetParent = this.helper.offsetParent();
-		this.hasFixedAncestor = this.helper.parents().filter( function() {
-				return $( this ).css( "position" ) === "fixed";
-			} ).length > 0;
-
-		//The element's absolute position on the page minus margins
-		this.positionAbs = this.element.offset();
-		this._refreshOffsets( event );
-
-		//Generate the original position
-		this.originalPosition = this.position = this._generatePosition( event, false );
-		this.originalPageX = event.pageX;
-		this.originalPageY = event.pageY;
-
-		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
-		( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
-
-		//Set a containment if given in the options
-		this._setContainment();
-
-		//Trigger event + callbacks
-		if ( this._trigger( "start", event ) === false ) {
-			this._clear();
-			return false;
-		}
-
-		//Recache the helper size
-		this._cacheHelperProportions();
-
-		//Prepare the droppable offsets
-		if ( $.ui.ddmanager && !o.dropBehaviour ) {
-			$.ui.ddmanager.prepareOffsets( this, event );
-		}
-
-		// Execute the drag once - this causes the helper not to be visible before getting its
-		// correct position
-		this._mouseDrag( event, true );
-
-		// If the ddmanager is used for droppables, inform the manager that dragging has started
-		// (see #5003)
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.dragStart( this, event );
-		}
-
-		return true;
-	},
-
-	_refreshOffsets: function( event ) {
-		this.offset = {
-			top: this.positionAbs.top - this.margins.top,
-			left: this.positionAbs.left - this.margins.left,
-			scroll: false,
-			parent: this._getParentOffset(),
-			relative: this._getRelativeOffset()
-		};
-
-		this.offset.click = {
-			left: event.pageX - this.offset.left,
-			top: event.pageY - this.offset.top
-		};
-	},
-
-	_mouseDrag: function( event, noPropagation ) {
-
-		// reset any necessary cached properties (see #5009)
-		if ( this.hasFixedAncestor ) {
-			this.offset.parent = this._getParentOffset();
-		}
-
-		//Compute the helpers position
-		this.position = this._generatePosition( event, true );
-		this.positionAbs = this._convertPositionTo( "absolute" );
-
-		//Call plugins and callbacks and use the resulting position if something is returned
-		if ( !noPropagation ) {
-			var ui = this._uiHash();
-			if ( this._trigger( "drag", event, ui ) === false ) {
-				this._mouseUp( new $.Event( "mouseup", event ) );
-				return false;
-			}
-			this.position = ui.position;
-		}
-
-		this.helper[ 0 ].style.left = this.position.left + "px";
-		this.helper[ 0 ].style.top = this.position.top + "px";
-
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.drag( this, event );
-		}
-
-		return false;
-	},
-
-	_mouseStop: function( event ) {
-
-		//If we are using droppables, inform the manager about the drop
-		var that = this,
-			dropped = false;
-		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
-			dropped = $.ui.ddmanager.drop( this, event );
-		}
-
-		//if a drop comes from outside (a sortable)
-		if ( this.dropped ) {
-			dropped = this.dropped;
-			this.dropped = false;
-		}
-
-		if ( ( this.options.revert === "invalid" && !dropped ) ||
-				( this.options.revert === "valid" && dropped ) ||
-				this.options.revert === true || ( $.isFunction( this.options.revert ) &&
-				this.options.revert.call( this.element, dropped ) )
-		) {
-			$( this.helper ).animate(
-				this.originalPosition,
-				parseInt( this.options.revertDuration, 10 ),
-				function() {
-					if ( that._trigger( "stop", event ) !== false ) {
-						that._clear();
-					}
-				}
-			);
-		} else {
-			if ( this._trigger( "stop", event ) !== false ) {
-				this._clear();
-			}
-		}
-
-		return false;
-	},
-
-	_mouseUp: function( event ) {
-		this._unblockFrames();
-
-		// If the ddmanager is used for droppables, inform the manager that dragging has stopped
-		// (see #5003)
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.dragStop( this, event );
-		}
-
-		// Only need to focus if the event occurred on the draggable itself, see #10527
-		if ( this.handleElement.is( event.target ) ) {
-
-			// The interaction is over; whether or not the click resulted in a drag,
-			// focus the element
-			this.element.trigger( "focus" );
-		}
-
-		return $.ui.mouse.prototype._mouseUp.call( this, event );
-	},
-
-	cancel: function() {
-
-		if ( this.helper.is( ".ui-draggable-dragging" ) ) {
-			this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
-		} else {
-			this._clear();
-		}
-
-		return this;
-
-	},
-
-	_getHandle: function( event ) {
-		return this.options.handle ?
-			!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
-			true;
-	},
-
-	_setHandleClassName: function() {
-		this.handleElement = this.options.handle ?
-			this.element.find( this.options.handle ) : this.element;
-		this._addClass( this.handleElement, "ui-draggable-handle" );
-	},
-
-	_removeHandleClassName: function() {
-		this._removeClass( this.handleElement, "ui-draggable-handle" );
-	},
-
-	_createHelper: function( event ) {
-
-		var o = this.options,
-			helperIsFunction = $.isFunction( o.helper ),
-			helper = helperIsFunction ?
-				$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
-				( o.helper === "clone" ?
-					this.element.clone().removeAttr( "id" ) :
-					this.element );
-
-		if ( !helper.parents( "body" ).length ) {
-			helper.appendTo( ( o.appendTo === "parent" ?
-				this.element[ 0 ].parentNode :
-				o.appendTo ) );
-		}
-
-		// Http://bugs.jqueryui.com/ticket/9446
-		// a helper function can return the original element
-		// which wouldn't have been set to relative in _create
-		if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
-			this._setPositionRelative();
-		}
-
-		if ( helper[ 0 ] !== this.element[ 0 ] &&
-				!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
-			helper.css( "position", "absolute" );
-		}
-
-		return helper;
-
-	},
-
-	_setPositionRelative: function() {
-		if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
-			this.element[ 0 ].style.position = "relative";
-		}
-	},
-
-	_adjustOffsetFromHelper: function( obj ) {
-		if ( typeof obj === "string" ) {
-			obj = obj.split( " " );
-		}
-		if ( $.isArray( obj ) ) {
-			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
-		}
-		if ( "left" in obj ) {
-			this.offset.click.left = obj.left + this.margins.left;
-		}
-		if ( "right" in obj ) {
-			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
-		}
-		if ( "top" in obj ) {
-			this.offset.click.top = obj.top + this.margins.top;
-		}
-		if ( "bottom" in obj ) {
-			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
-		}
-	},
-
-	_isRootNode: function( element ) {
-		return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
-	},
-
-	_getParentOffset: function() {
-
-		//Get the offsetParent and cache its position
-		var po = this.offsetParent.offset(),
-			document = this.document[ 0 ];
-
-		// This is a special case where we need to modify a offset calculated on start, since the
-		// following happened:
-		// 1. The position of the helper is absolute, so it's position is calculated based on the
-		// next positioned parent
-		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
-		// the document, which means that the scroll is included in the initial calculation of the
-		// offset of the parent, and never recalculated upon drag
-		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
-				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
-			po.left += this.scrollParent.scrollLeft();
-			po.top += this.scrollParent.scrollTop();
-		}
-
-		if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
-			po = { top: 0, left: 0 };
-		}
-
-		return {
-			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
-			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
-		};
-
-	},
-
-	_getRelativeOffset: function() {
-		if ( this.cssPosition !== "relative" ) {
-			return { top: 0, left: 0 };
-		}
-
-		var p = this.element.position(),
-			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
-
-		return {
-			top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
-				( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
-			left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
-				( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
-		};
-
-	},
-
-	_cacheMargins: function() {
-		this.margins = {
-			left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
-			top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
-			right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
-			bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
-		};
-	},
-
-	_cacheHelperProportions: function() {
-		this.helperProportions = {
-			width: this.helper.outerWidth(),
-			height: this.helper.outerHeight()
-		};
-	},
-
-	_setContainment: function() {
-
-		var isUserScrollable, c, ce,
-			o = this.options,
-			document = this.document[ 0 ];
-
-		this.relativeContainer = null;
-
-		if ( !o.containment ) {
-			this.containment = null;
-			return;
-		}
-
-		if ( o.containment === "window" ) {
-			this.containment = [
-				$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
-				$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
-				$( window ).scrollLeft() + $( window ).width() -
-					this.helperProportions.width - this.margins.left,
-				$( window ).scrollTop() +
-					( $( window ).height() || document.body.parentNode.scrollHeight ) -
-					this.helperProportions.height - this.margins.top
-			];
-			return;
-		}
-
-		if ( o.containment === "document" ) {
-			this.containment = [
-				0,
-				0,
-				$( document ).width() - this.helperProportions.width - this.margins.left,
-				( $( document ).height() || document.body.parentNode.scrollHeight ) -
-					this.helperProportions.height - this.margins.top
-			];
-			return;
-		}
-
-		if ( o.containment.constructor === Array ) {
-			this.containment = o.containment;
-			return;
-		}
-
-		if ( o.containment === "parent" ) {
-			o.containment = this.helper[ 0 ].parentNode;
-		}
-
-		c = $( o.containment );
-		ce = c[ 0 ];
-
-		if ( !ce ) {
-			return;
-		}
-
-		isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
-
-		this.containment = [
-			( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
-				( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
-			( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
-				( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
-			( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
-				( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
-				( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
-				this.helperProportions.width -
-				this.margins.left -
-				this.margins.right,
-			( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
-				( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
-				( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
-				this.helperProportions.height -
-				this.margins.top -
-				this.margins.bottom
-		];
-		this.relativeContainer = c;
-	},
-
-	_convertPositionTo: function( d, pos ) {
-
-		if ( !pos ) {
-			pos = this.position;
-		}
-
-		var mod = d === "absolute" ? 1 : -1,
-			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
-
-		return {
-			top: (
-
-				// The absolute mouse position
-				pos.top	+
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.top * mod +
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.top * mod -
-				( ( this.cssPosition === "fixed" ?
-					-this.offset.scroll.top :
-					( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
-			),
-			left: (
-
-				// The absolute mouse position
-				pos.left +
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.left * mod +
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.left * mod	-
-				( ( this.cssPosition === "fixed" ?
-					-this.offset.scroll.left :
-					( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
-			)
-		};
-
-	},
-
-	_generatePosition: function( event, constrainPosition ) {
-
-		var containment, co, top, left,
-			o = this.options,
-			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
-			pageX = event.pageX,
-			pageY = event.pageY;
-
-		// Cache the scroll
-		if ( !scrollIsRootNode || !this.offset.scroll ) {
-			this.offset.scroll = {
-				top: this.scrollParent.scrollTop(),
-				left: this.scrollParent.scrollLeft()
-			};
-		}
-
-		/*
-		 * - Position constraining -
-		 * Constrain the position to a mix of grid, containment.
-		 */
-
-		// If we are not dragging yet, we won't check for options
-		if ( constrainPosition ) {
-			if ( this.containment ) {
-				if ( this.relativeContainer ) {
-					co = this.relativeContainer.offset();
-					containment = [
-						this.containment[ 0 ] + co.left,
-						this.containment[ 1 ] + co.top,
-						this.containment[ 2 ] + co.left,
-						this.containment[ 3 ] + co.top
-					];
-				} else {
-					containment = this.containment;
-				}
-
-				if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
-					pageX = containment[ 0 ] + this.offset.click.left;
-				}
-				if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
-					pageY = containment[ 1 ] + this.offset.click.top;
-				}
-				if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
-					pageX = containment[ 2 ] + this.offset.click.left;
-				}
-				if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
-					pageY = containment[ 3 ] + this.offset.click.top;
-				}
-			}
-
-			if ( o.grid ) {
-
-				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
-				// argument errors in IE (see ticket #6950)
-				top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
-					this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
-				pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
-					top - this.offset.click.top > containment[ 3 ] ) ?
-						top :
-						( ( top - this.offset.click.top >= containment[ 1 ] ) ?
-							top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
-
-				left = o.grid[ 0 ] ? this.originalPageX +
-					Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
-					this.originalPageX;
-				pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
-					left - this.offset.click.left > containment[ 2 ] ) ?
-						left :
-						( ( left - this.offset.click.left >= containment[ 0 ] ) ?
-							left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
-			}
-
-			if ( o.axis === "y" ) {
-				pageX = this.originalPageX;
-			}
-
-			if ( o.axis === "x" ) {
-				pageY = this.originalPageY;
-			}
-		}
-
-		return {
-			top: (
-
-				// The absolute mouse position
-				pageY -
-
-				// Click offset (relative to the element)
-				this.offset.click.top -
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.top -
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.top +
-				( this.cssPosition === "fixed" ?
-					-this.offset.scroll.top :
-					( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
-			),
-			left: (
-
-				// The absolute mouse position
-				pageX -
-
-				// Click offset (relative to the element)
-				this.offset.click.left -
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.left -
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.left +
-				( this.cssPosition === "fixed" ?
-					-this.offset.scroll.left :
-					( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
-			)
-		};
-
-	},
-
-	_clear: function() {
-		this._removeClass( this.helper, "ui-draggable-dragging" );
-		if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
-			this.helper.remove();
-		}
-		this.helper = null;
-		this.cancelHelperRemoval = false;
-		if ( this.destroyOnClear ) {
-			this.destroy();
-		}
-	},
-
-	// From now on bulk stuff - mainly helpers
-
-	_trigger: function( type, event, ui ) {
-		ui = ui || this._uiHash();
-		$.ui.plugin.call( this, type, [ event, ui, this ], true );
-
-		// Absolute position and offset (see #6884 ) have to be recalculated after plugins
-		if ( /^(drag|start|stop)/.test( type ) ) {
-			this.positionAbs = this._convertPositionTo( "absolute" );
-			ui.offset = this.positionAbs;
-		}
-		return $.Widget.prototype._trigger.call( this, type, event, ui );
-	},
-
-	plugins: {},
-
-	_uiHash: function() {
-		return {
-			helper: this.helper,
-			position: this.position,
-			originalPosition: this.originalPosition,
-			offset: this.positionAbs
-		};
-	}
-
-} );
-
-$.ui.plugin.add( "draggable", "connectToSortable", {
-	start: function( event, ui, draggable ) {
-		var uiSortable = $.extend( {}, ui, {
-			item: draggable.element
-		} );
-
-		draggable.sortables = [];
-		$( draggable.options.connectToSortable ).each( function() {
-			var sortable = $( this ).sortable( "instance" );
-
-			if ( sortable && !sortable.options.disabled ) {
-				draggable.sortables.push( sortable );
-
-				// RefreshPositions is called at drag start to refresh the containerCache
-				// which is used in drag. This ensures it's initialized and synchronized
-				// with any changes that might have happened on the page since initialization.
-				sortable.refreshPositions();
-				sortable._trigger( "activate", event, uiSortable );
-			}
-		} );
-	},
-	stop: function( event, ui, draggable ) {
-		var uiSortable = $.extend( {}, ui, {
-			item: draggable.element
-		} );
-
-		draggable.cancelHelperRemoval = false;
-
-		$.each( draggable.sortables, function() {
-			var sortable = this;
-
-			if ( sortable.isOver ) {
-				sortable.isOver = 0;
-
-				// Allow this sortable to handle removing the helper
-				draggable.cancelHelperRemoval = true;
-				sortable.cancelHelperRemoval = false;
-
-				// Use _storedCSS To restore properties in the sortable,
-				// as this also handles revert (#9675) since the draggable
-				// may have modified them in unexpected ways (#8809)
-				sortable._storedCSS = {
-					position: sortable.placeholder.css( "position" ),
-					top: sortable.placeholder.css( "top" ),
-					left: sortable.placeholder.css( "left" )
-				};
-
-				sortable._mouseStop( event );
-
-				// Once drag has ended, the sortable should return to using
-				// its original helper, not the shared helper from draggable
-				sortable.options.helper = sortable.options._helper;
-			} else {
-
-				// Prevent this Sortable from removing the helper.
-				// However, don't set the draggable to remove the helper
-				// either as another connected Sortable may yet handle the removal.
-				sortable.cancelHelperRemoval = true;
-
-				sortable._trigger( "deactivate", event, uiSortable );
-			}
-		} );
-	},
-	drag: function( event, ui, draggable ) {
-		$.each( draggable.sortables, function() {
-			var innermostIntersecting = false,
-				sortable = this;
-
-			// Copy over variables that sortable's _intersectsWith uses
-			sortable.positionAbs = draggable.positionAbs;
-			sortable.helperProportions = draggable.helperProportions;
-			sortable.offset.click = draggable.offset.click;
-
-			if ( sortable._intersectsWith( sortable.containerCache ) ) {
-				innermostIntersecting = true;
-
-				$.each( draggable.sortables, function() {
-
-					// Copy over variables that sortable's _intersectsWith uses
-					this.positionAbs = draggable.positionAbs;
-					this.helperProportions = draggable.helperProportions;
-					this.offset.click = draggable.offset.click;
-
-					if ( this !== sortable &&
-							this._intersectsWith( this.containerCache ) &&
-							$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
-						innermostIntersecting = false;
-					}
-
-					return innermostIntersecting;
-				} );
-			}
-
-			if ( innermostIntersecting ) {
-
-				// If it intersects, we use a little isOver variable and set it once,
-				// so that the move-in stuff gets fired only once.
-				if ( !sortable.isOver ) {
-					sortable.isOver = 1;
-
-					// Store draggable's parent in case we need to reappend to it later.
-					draggable._parent = ui.helper.parent();
-
-					sortable.currentItem = ui.helper
-						.appendTo( sortable.element )
-						.data( "ui-sortable-item", true );
-
-					// Store helper option to later restore it
-					sortable.options._helper = sortable.options.helper;
-
-					sortable.options.helper = function() {
-						return ui.helper[ 0 ];
-					};
-
-					// Fire the start events of the sortable with our passed browser event,
-					// and our own helper (so it doesn't create a new one)
-					event.target = sortable.currentItem[ 0 ];
-					sortable._mouseCapture( event, true );
-					sortable._mouseStart( event, true, true );
-
-					// Because the browser event is way off the new appended portlet,
-					// modify necessary variables to reflect the changes
-					sortable.offset.click.top = draggable.offset.click.top;
-					sortable.offset.click.left = draggable.offset.click.left;
-					sortable.offset.parent.left -= draggable.offset.parent.left -
-						sortable.offset.parent.left;
-					sortable.offset.parent.top -= draggable.offset.parent.top -
-						sortable.offset.parent.top;
-
-					draggable._trigger( "toSortable", event );
-
-					// Inform draggable that the helper is in a valid drop zone,
-					// used solely in the revert option to handle "valid/invalid".
-					draggable.dropped = sortable.element;
-
-					// Need to refreshPositions of all sortables in the case that
-					// adding to one sortable changes the location of the other sortables (#9675)
-					$.each( draggable.sortables, function() {
-						this.refreshPositions();
-					} );
-
-					// Hack so receive/update callbacks work (mostly)
-					draggable.currentItem = draggable.element;
-					sortable.fromOutside = draggable;
-				}
-
-				if ( sortable.currentItem ) {
-					sortable._mouseDrag( event );
-
-					// Copy the sortable's position because the draggable's can potentially reflect
-					// a relative position, while sortable is always absolute, which the dragged
-					// element has now become. (#8809)
-					ui.position = sortable.position;
-				}
-			} else {
-
-				// If it doesn't intersect with the sortable, and it intersected before,
-				// we fake the drag stop of the sortable, but make sure it doesn't remove
-				// the helper by using cancelHelperRemoval.
-				if ( sortable.isOver ) {
-
-					sortable.isOver = 0;
-					sortable.cancelHelperRemoval = true;
-
-					// Calling sortable's mouseStop would trigger a revert,
-					// so revert must be temporarily false until after mouseStop is called.
-					sortable.options._revert = sortable.options.revert;
-					sortable.options.revert = false;
-
-					sortable._trigger( "out", event, sortable._uiHash( sortable ) );
-					sortable._mouseStop( event, true );
-
-					// Restore sortable behaviors that were modfied
-					// when the draggable entered the sortable area (#9481)
-					sortable.options.revert = sortable.options._revert;
-					sortable.options.helper = sortable.options._helper;
-
-					if ( sortable.placeholder ) {
-						sortable.placeholder.remove();
-					}
-
-					// Restore and recalculate the draggable's offset considering the sortable
-					// may have modified them in unexpected ways. (#8809, #10669)
-					ui.helper.appendTo( draggable._parent );
-					draggable._refreshOffsets( event );
-					ui.position = draggable._generatePosition( event, true );
-
-					draggable._trigger( "fromSortable", event );
-
-					// Inform draggable that the helper is no longer in a valid drop zone
-					draggable.dropped = false;
-
-					// Need to refreshPositions of all sortables just in case removing
-					// from one sortable changes the location of other sortables (#9675)
-					$.each( draggable.sortables, function() {
-						this.refreshPositions();
-					} );
-				}
-			}
-		} );
-	}
-} );
-
-$.ui.plugin.add( "draggable", "cursor", {
-	start: function( event, ui, instance ) {
-		var t = $( "body" ),
-			o = instance.options;
-
-		if ( t.css( "cursor" ) ) {
-			o._cursor = t.css( "cursor" );
-		}
-		t.css( "cursor", o.cursor );
-	},
-	stop: function( event, ui, instance ) {
-		var o = instance.options;
-		if ( o._cursor ) {
-			$( "body" ).css( "cursor", o._cursor );
-		}
-	}
-} );
-
-$.ui.plugin.add( "draggable", "opacity", {
-	start: function( event, ui, instance ) {
-		var t = $( ui.helper ),
-			o = instance.options;
-		if ( t.css( "opacity" ) ) {
-			o._opacity = t.css( "opacity" );
-		}
-		t.css( "opacity", o.opacity );
-	},
-	stop: function( event, ui, instance ) {
-		var o = instance.options;
-		if ( o._opacity ) {
-			$( ui.helper ).css( "opacity", o._opacity );
-		}
-	}
-} );
-
-$.ui.plugin.add( "draggable", "scroll", {
-	start: function( event, ui, i ) {
-		if ( !i.scrollParentNotHidden ) {
-			i.scrollParentNotHidden = i.helper.scrollParent( false );
-		}
-
-		if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
-				i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
-			i.overflowOffset = i.scrollParentNotHidden.offset();
-		}
-	},
-	drag: function( event, ui, i  ) {
-
-		var o = i.options,
-			scrolled = false,
-			scrollParent = i.scrollParentNotHidden[ 0 ],
-			document = i.document[ 0 ];
-
-		if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
-			if ( !o.axis || o.axis !== "x" ) {
-				if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
-						o.scrollSensitivity ) {
-					scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
-				} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
-					scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
-				}
-			}
-
-			if ( !o.axis || o.axis !== "y" ) {
-				if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
-						o.scrollSensitivity ) {
-					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
-				} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
-					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
-				}
-			}
-
-		} else {
-
-			if ( !o.axis || o.axis !== "x" ) {
-				if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
-					scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
-				} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
-						o.scrollSensitivity ) {
-					scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
-				}
-			}
-
-			if ( !o.axis || o.axis !== "y" ) {
-				if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
-					scrolled = $( document ).scrollLeft(
-						$( document ).scrollLeft() - o.scrollSpeed
-					);
-				} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
-						o.scrollSensitivity ) {
-					scrolled = $( document ).scrollLeft(
-						$( document ).scrollLeft() + o.scrollSpeed
-					);
-				}
-			}
-
-		}
-
-		if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
-			$.ui.ddmanager.prepareOffsets( i, event );
-		}
-
-	}
-} );
-
-$.ui.plugin.add( "draggable", "snap", {
-	start: function( event, ui, i ) {
-
-		var o = i.options;
-
-		i.snapElements = [];
-
-		$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
-			.each( function() {
-				var $t = $( this ),
-					$o = $t.offset();
-				if ( this !== i.element[ 0 ] ) {
-					i.snapElements.push( {
-						item: this,
-						width: $t.outerWidth(), height: $t.outerHeight(),
-						top: $o.top, left: $o.left
-					} );
-				}
-			} );
-
-	},
-	drag: function( event, ui, inst ) {
-
-		var ts, bs, ls, rs, l, r, t, b, i, first,
-			o = inst.options,
-			d = o.snapTolerance,
-			x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
-			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
-
-		for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
-
-			l = inst.snapElements[ i ].left - inst.margins.left;
-			r = l + inst.snapElements[ i ].width;
-			t = inst.snapElements[ i ].top - inst.margins.top;
-			b = t + inst.snapElements[ i ].height;
-
-			if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
-					!$.contains( inst.snapElements[ i ].item.ownerDocument,
-					inst.snapElements[ i ].item ) ) {
-				if ( inst.snapElements[ i ].snapping ) {
-					( inst.options.snap.release &&
-						inst.options.snap.release.call(
-							inst.element,
-							event,
-							$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
-						) );
-				}
-				inst.snapElements[ i ].snapping = false;
-				continue;
-			}
-
-			if ( o.snapMode !== "inner" ) {
-				ts = Math.abs( t - y2 ) <= d;
-				bs = Math.abs( b - y1 ) <= d;
-				ls = Math.abs( l - x2 ) <= d;
-				rs = Math.abs( r - x1 ) <= d;
-				if ( ts ) {
-					ui.position.top = inst._convertPositionTo( "relative", {
-						top: t - inst.helperProportions.height,
-						left: 0
-					} ).top;
-				}
-				if ( bs ) {
-					ui.position.top = inst._convertPositionTo( "relative", {
-						top: b,
-						left: 0
-					} ).top;
-				}
-				if ( ls ) {
-					ui.position.left = inst._convertPositionTo( "relative", {
-						top: 0,
-						left: l - inst.helperProportions.width
-					} ).left;
-				}
-				if ( rs ) {
-					ui.position.left = inst._convertPositionTo( "relative", {
-						top: 0,
-						left: r
-					} ).left;
-				}
-			}
-
-			first = ( ts || bs || ls || rs );
-
-			if ( o.snapMode !== "outer" ) {
-				ts = Math.abs( t - y1 ) <= d;
-				bs = Math.abs( b - y2 ) <= d;
-				ls = Math.abs( l - x1 ) <= d;
-				rs = Math.abs( r - x2 ) <= d;
-				if ( ts ) {
-					ui.position.top = inst._convertPositionTo( "relative", {
-						top: t,
-						left: 0
-					} ).top;
-				}
-				if ( bs ) {
-					ui.position.top = inst._convertPositionTo( "relative", {
-						top: b - inst.helperProportions.height,
-						left: 0
-					} ).top;
-				}
-				if ( ls ) {
-					ui.position.left = inst._convertPositionTo( "relative", {
-						top: 0,
-						left: l
-					} ).left;
-				}
-				if ( rs ) {
-					ui.position.left = inst._convertPositionTo( "relative", {
-						top: 0,
-						left: r - inst.helperProportions.width
-					} ).left;
-				}
-			}
-
-			if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
-				( inst.options.snap.snap &&
-					inst.options.snap.snap.call(
-						inst.element,
-						event,
-						$.extend( inst._uiHash(), {
-							snapItem: inst.snapElements[ i ].item
-						} ) ) );
-			}
-			inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
-
-		}
-
-	}
-} );
-
-$.ui.plugin.add( "draggable", "stack", {
-	start: function( event, ui, instance ) {
-		var min,
-			o = instance.options,
-			group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
-				return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
-					( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
-			} );
-
-		if ( !group.length ) { return; }
-
-		min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
-		$( group ).each( function( i ) {
-			$( this ).css( "zIndex", min + i );
-		} );
-		this.css( "zIndex", ( min + group.length ) );
-	}
-} );
-
-$.ui.plugin.add( "draggable", "zIndex", {
-	start: function( event, ui, instance ) {
-		var t = $( ui.helper ),
-			o = instance.options;
-
-		if ( t.css( "zIndex" ) ) {
-			o._zIndex = t.css( "zIndex" );
-		}
-		t.css( "zIndex", o.zIndex );
-	},
-	stop: function( event, ui, instance ) {
-		var o = instance.options;
-
-		if ( o._zIndex ) {
-			$( ui.helper ).css( "zIndex", o._zIndex );
-		}
-	}
-} );
-
-var widgetsDraggable = $.ui.draggable;
-
-
-/*!
- * jQuery UI Resizable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Resizable
-//>>group: Interactions
-//>>description: Enables resize functionality for any element.
-//>>docs: http://api.jqueryui.com/resizable/
-//>>demos: http://jqueryui.com/resizable/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/resizable.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.resizable", $.ui.mouse, {
-	version: "1.12.1",
-	widgetEventPrefix: "resize",
-	options: {
-		alsoResize: false,
-		animate: false,
-		animateDuration: "slow",
-		animateEasing: "swing",
-		aspectRatio: false,
-		autoHide: false,
-		classes: {
-			"ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se"
-		},
-		containment: false,
-		ghost: false,
-		grid: false,
-		handles: "e,s,se",
-		helper: false,
-		maxHeight: null,
-		maxWidth: null,
-		minHeight: 10,
-		minWidth: 10,
-
-		// See #7960
-		zIndex: 90,
-
-		// Callbacks
-		resize: null,
-		start: null,
-		stop: null
-	},
-
-	_num: function( value ) {
-		return parseFloat( value ) || 0;
-	},
-
-	_isNumber: function( value ) {
-		return !isNaN( parseFloat( value ) );
-	},
-
-	_hasScroll: function( el, a ) {
-
-		if ( $( el ).css( "overflow" ) === "hidden" ) {
-			return false;
-		}
-
-		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
-			has = false;
-
-		if ( el[ scroll ] > 0 ) {
-			return true;
-		}
-
-		// TODO: determine which cases actually cause this to happen
-		// if the element doesn't have the scroll set, see if it's possible to
-		// set the scroll
-		el[ scroll ] = 1;
-		has = ( el[ scroll ] > 0 );
-		el[ scroll ] = 0;
-		return has;
-	},
-
-	_create: function() {
-
-		var margins,
-			o = this.options,
-			that = this;
-		this._addClass( "ui-resizable" );
-
-		$.extend( this, {
-			_aspectRatio: !!( o.aspectRatio ),
-			aspectRatio: o.aspectRatio,
-			originalElement: this.element,
-			_proportionallyResizeElements: [],
-			_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
-		} );
-
-		// Wrap the element if it cannot hold child nodes
-		if ( this.element[ 0 ].nodeName.match( /^(canvas|textarea|input|select|button|img)$/i ) ) {
-
-			this.element.wrap(
-				$( "<div class='ui-wrapper' style='overflow: hidden;'></div>" ).css( {
-					position: this.element.css( "position" ),
-					width: this.element.outerWidth(),
-					height: this.element.outerHeight(),
-					top: this.element.css( "top" ),
-					left: this.element.css( "left" )
-				} )
-			);
-
-			this.element = this.element.parent().data(
-				"ui-resizable", this.element.resizable( "instance" )
-			);
-
-			this.elementIsWrapper = true;
-
-			margins = {
-				marginTop: this.originalElement.css( "marginTop" ),
-				marginRight: this.originalElement.css( "marginRight" ),
-				marginBottom: this.originalElement.css( "marginBottom" ),
-				marginLeft: this.originalElement.css( "marginLeft" )
-			};
-
-			this.element.css( margins );
-			this.originalElement.css( "margin", 0 );
-
-			// support: Safari
-			// Prevent Safari textarea resize
-			this.originalResizeStyle = this.originalElement.css( "resize" );
-			this.originalElement.css( "resize", "none" );
-
-			this._proportionallyResizeElements.push( this.originalElement.css( {
-				position: "static",
-				zoom: 1,
-				display: "block"
-			} ) );
-
-			// Support: IE9
-			// avoid IE jump (hard set the margin)
-			this.originalElement.css( margins );
-
-			this._proportionallyResize();
-		}
-
-		this._setupHandles();
-
-		if ( o.autoHide ) {
-			$( this.element )
-				.on( "mouseenter", function() {
-					if ( o.disabled ) {
-						return;
-					}
-					that._removeClass( "ui-resizable-autohide" );
-					that._handles.show();
-				} )
-				.on( "mouseleave", function() {
-					if ( o.disabled ) {
-						return;
-					}
-					if ( !that.resizing ) {
-						that._addClass( "ui-resizable-autohide" );
-						that._handles.hide();
-					}
-				} );
-		}
-
-		this._mouseInit();
-	},
-
-	_destroy: function() {
-
-		this._mouseDestroy();
-
-		var wrapper,
-			_destroy = function( exp ) {
-				$( exp )
-					.removeData( "resizable" )
-					.removeData( "ui-resizable" )
-					.off( ".resizable" )
-					.find( ".ui-resizable-handle" )
-						.remove();
-			};
-
-		// TODO: Unwrap at same DOM position
-		if ( this.elementIsWrapper ) {
-			_destroy( this.element );
-			wrapper = this.element;
-			this.originalElement.css( {
-				position: wrapper.css( "position" ),
-				width: wrapper.outerWidth(),
-				height: wrapper.outerHeight(),
-				top: wrapper.css( "top" ),
-				left: wrapper.css( "left" )
-			} ).insertAfter( wrapper );
-			wrapper.remove();
-		}
-
-		this.originalElement.css( "resize", this.originalResizeStyle );
-		_destroy( this.originalElement );
-
-		return this;
-	},
-
-	_setOption: function( key, value ) {
-		this._super( key, value );
-
-		switch ( key ) {
-		case "handles":
-			this._removeHandles();
-			this._setupHandles();
-			break;
-		default:
-			break;
-		}
-	},
-
-	_setupHandles: function() {
-		var o = this.options, handle, i, n, hname, axis, that = this;
-		this.handles = o.handles ||
-			( !$( ".ui-resizable-handle", this.element ).length ?
-				"e,s,se" : {
-					n: ".ui-resizable-n",
-					e: ".ui-resizable-e",
-					s: ".ui-resizable-s",
-					w: ".ui-resizable-w",
-					se: ".ui-resizable-se",
-					sw: ".ui-resizable-sw",
-					ne: ".ui-resizable-ne",
-					nw: ".ui-resizable-nw"
-				} );
-
-		this._handles = $();
-		if ( this.handles.constructor === String ) {
-
-			if ( this.handles === "all" ) {
-				this.handles = "n,e,s,w,se,sw,ne,nw";
-			}
-
-			n = this.handles.split( "," );
-			this.handles = {};
-
-			for ( i = 0; i < n.length; i++ ) {
-
-				handle = $.trim( n[ i ] );
-				hname = "ui-resizable-" + handle;
-				axis = $( "<div>" );
-				this._addClass( axis, "ui-resizable-handle " + hname );
-
-				axis.css( { zIndex: o.zIndex } );
-
-				this.handles[ handle ] = ".ui-resizable-" + handle;
-				this.element.append( axis );
-			}
-
-		}
-
-		this._renderAxis = function( target ) {
-
-			var i, axis, padPos, padWrapper;
-
-			target = target || this.element;
-
-			for ( i in this.handles ) {
-
-				if ( this.handles[ i ].constructor === String ) {
-					this.handles[ i ] = this.element.children( this.handles[ i ] ).first().show();
-				} else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
-					this.handles[ i ] = $( this.handles[ i ] );
-					this._on( this.handles[ i ], { "mousedown": that._mouseDown } );
-				}
-
-				if ( this.elementIsWrapper &&
-						this.originalElement[ 0 ]
-							.nodeName
-							.match( /^(textarea|input|select|button)$/i ) ) {
-					axis = $( this.handles[ i ], this.element );
-
-					padWrapper = /sw|ne|nw|se|n|s/.test( i ) ?
-						axis.outerHeight() :
-						axis.outerWidth();
-
-					padPos = [ "padding",
-						/ne|nw|n/.test( i ) ? "Top" :
-						/se|sw|s/.test( i ) ? "Bottom" :
-						/^e$/.test( i ) ? "Right" : "Left" ].join( "" );
-
-					target.css( padPos, padWrapper );
-
-					this._proportionallyResize();
-				}
-
-				this._handles = this._handles.add( this.handles[ i ] );
-			}
-		};
-
-		// TODO: make renderAxis a prototype function
-		this._renderAxis( this.element );
-
-		this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
-		this._handles.disableSelection();
-
-		this._handles.on( "mouseover", function() {
-			if ( !that.resizing ) {
-				if ( this.className ) {
-					axis = this.className.match( /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i );
-				}
-				that.axis = axis && axis[ 1 ] ? axis[ 1 ] : "se";
-			}
-		} );
-
-		if ( o.autoHide ) {
-			this._handles.hide();
-			this._addClass( "ui-resizable-autohide" );
-		}
-	},
-
-	_removeHandles: function() {
-		this._handles.remove();
-	},
-
-	_mouseCapture: function( event ) {
-		var i, handle,
-			capture = false;
-
-		for ( i in this.handles ) {
-			handle = $( this.handles[ i ] )[ 0 ];
-			if ( handle === event.target || $.contains( handle, event.target ) ) {
-				capture = true;
-			}
-		}
-
-		return !this.options.disabled && capture;
-	},
-
-	_mouseStart: function( event ) {
-
-		var curleft, curtop, cursor,
-			o = this.options,
-			el = this.element;
-
-		this.resizing = true;
-
-		this._renderProxy();
-
-		curleft = this._num( this.helper.css( "left" ) );
-		curtop = this._num( this.helper.css( "top" ) );
-
-		if ( o.containment ) {
-			curleft += $( o.containment ).scrollLeft() || 0;
-			curtop += $( o.containment ).scrollTop() || 0;
-		}
-
-		this.offset = this.helper.offset();
-		this.position = { left: curleft, top: curtop };
-
-		this.size = this._helper ? {
-				width: this.helper.width(),
-				height: this.helper.height()
-			} : {
-				width: el.width(),
-				height: el.height()
-			};
-
-		this.originalSize = this._helper ? {
-				width: el.outerWidth(),
-				height: el.outerHeight()
-			} : {
-				width: el.width(),
-				height: el.height()
-			};
-
-		this.sizeDiff = {
-			width: el.outerWidth() - el.width(),
-			height: el.outerHeight() - el.height()
-		};
-
-		this.originalPosition = { left: curleft, top: curtop };
-		this.originalMousePosition = { left: event.pageX, top: event.pageY };
-
-		this.aspectRatio = ( typeof o.aspectRatio === "number" ) ?
-			o.aspectRatio :
-			( ( this.originalSize.width / this.originalSize.height ) || 1 );
-
-		cursor = $( ".ui-resizable-" + this.axis ).css( "cursor" );
-		$( "body" ).css( "cursor", cursor === "auto" ? this.axis + "-resize" : cursor );
-
-		this._addClass( "ui-resizable-resizing" );
-		this._propagate( "start", event );
-		return true;
-	},
-
-	_mouseDrag: function( event ) {
-
-		var data, props,
-			smp = this.originalMousePosition,
-			a = this.axis,
-			dx = ( event.pageX - smp.left ) || 0,
-			dy = ( event.pageY - smp.top ) || 0,
-			trigger = this._change[ a ];
-
-		this._updatePrevProperties();
-
-		if ( !trigger ) {
-			return false;
-		}
-
-		data = trigger.apply( this, [ event, dx, dy ] );
-
-		this._updateVirtualBoundaries( event.shiftKey );
-		if ( this._aspectRatio || event.shiftKey ) {
-			data = this._updateRatio( data, event );
-		}
-
-		data = this._respectSize( data, event );
-
-		this._updateCache( data );
-
-		this._propagate( "resize", event );
-
-		props = this._applyChanges();
-
-		if ( !this._helper && this._proportionallyResizeElements.length ) {
-			this._proportionallyResize();
-		}
-
-		if ( !$.isEmptyObject( props ) ) {
-			this._updatePrevProperties();
-			this._trigger( "resize", event, this.ui() );
-			this._applyChanges();
-		}
-
-		return false;
-	},
-
-	_mouseStop: function( event ) {
-
-		this.resizing = false;
-		var pr, ista, soffseth, soffsetw, s, left, top,
-			o = this.options, that = this;
-
-		if ( this._helper ) {
-
-			pr = this._proportionallyResizeElements;
-			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName );
-			soffseth = ista && this._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height;
-			soffsetw = ista ? 0 : that.sizeDiff.width;
-
-			s = {
-				width: ( that.helper.width()  - soffsetw ),
-				height: ( that.helper.height() - soffseth )
-			};
-			left = ( parseFloat( that.element.css( "left" ) ) +
-				( that.position.left - that.originalPosition.left ) ) || null;
-			top = ( parseFloat( that.element.css( "top" ) ) +
-				( that.position.top - that.originalPosition.top ) ) || null;
-
-			if ( !o.animate ) {
-				this.element.css( $.extend( s, { top: top, left: left } ) );
-			}
-
-			that.helper.height( that.size.height );
-			that.helper.width( that.size.width );
-
-			if ( this._helper && !o.animate ) {
-				this._proportionallyResize();
-			}
-		}
-
-		$( "body" ).css( "cursor", "auto" );
-
-		this._removeClass( "ui-resizable-resizing" );
-
-		this._propagate( "stop", event );
-
-		if ( this._helper ) {
-			this.helper.remove();
-		}
-
-		return false;
-
-	},
-
-	_updatePrevProperties: function() {
-		this.prevPosition = {
-			top: this.position.top,
-			left: this.position.left
-		};
-		this.prevSize = {
-			width: this.size.width,
-			height: this.size.height
-		};
-	},
-
-	_applyChanges: function() {
-		var props = {};
-
-		if ( this.position.top !== this.prevPosition.top ) {
-			props.top = this.position.top + "px";
-		}
-		if ( this.position.left !== this.prevPosition.left ) {
-			props.left = this.position.left + "px";
-		}
-		if ( this.size.width !== this.prevSize.width ) {
-			props.width = this.size.width + "px";
-		}
-		if ( this.size.height !== this.prevSize.height ) {
-			props.height = this.size.height + "px";
-		}
-
-		this.helper.css( props );
-
-		return props;
-	},
-
-	_updateVirtualBoundaries: function( forceAspectRatio ) {
-		var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
-			o = this.options;
-
-		b = {
-			minWidth: this._isNumber( o.minWidth ) ? o.minWidth : 0,
-			maxWidth: this._isNumber( o.maxWidth ) ? o.maxWidth : Infinity,
-			minHeight: this._isNumber( o.minHeight ) ? o.minHeight : 0,
-			maxHeight: this._isNumber( o.maxHeight ) ? o.maxHeight : Infinity
-		};
-
-		if ( this._aspectRatio || forceAspectRatio ) {
-			pMinWidth = b.minHeight * this.aspectRatio;
-			pMinHeight = b.minWidth / this.aspectRatio;
-			pMaxWidth = b.maxHeight * this.aspectRatio;
-			pMaxHeight = b.maxWidth / this.aspectRatio;
-
-			if ( pMinWidth > b.minWidth ) {
-				b.minWidth = pMinWidth;
-			}
-			if ( pMinHeight > b.minHeight ) {
-				b.minHeight = pMinHeight;
-			}
-			if ( pMaxWidth < b.maxWidth ) {
-				b.maxWidth = pMaxWidth;
-			}
-			if ( pMaxHeight < b.maxHeight ) {
-				b.maxHeight = pMaxHeight;
-			}
-		}
-		this._vBoundaries = b;
-	},
-
-	_updateCache: function( data ) {
-		this.offset = this.helper.offset();
-		if ( this._isNumber( data.left ) ) {
-			this.position.left = data.left;
-		}
-		if ( this._isNumber( data.top ) ) {
-			this.position.top = data.top;
-		}
-		if ( this._isNumber( data.height ) ) {
-			this.size.height = data.height;
-		}
-		if ( this._isNumber( data.width ) ) {
-			this.size.width = data.width;
-		}
-	},
-
-	_updateRatio: function( data ) {
-
-		var cpos = this.position,
-			csize = this.size,
-			a = this.axis;
-
-		if ( this._isNumber( data.height ) ) {
-			data.width = ( data.height * this.aspectRatio );
-		} else if ( this._isNumber( data.width ) ) {
-			data.height = ( data.width / this.aspectRatio );
-		}
-
-		if ( a === "sw" ) {
-			data.left = cpos.left + ( csize.width - data.width );
-			data.top = null;
-		}
-		if ( a === "nw" ) {
-			data.top = cpos.top + ( csize.height - data.height );
-			data.left = cpos.left + ( csize.width - data.width );
-		}
-
-		return data;
-	},
-
-	_respectSize: function( data ) {
-
-		var o = this._vBoundaries,
-			a = this.axis,
-			ismaxw = this._isNumber( data.width ) && o.maxWidth && ( o.maxWidth < data.width ),
-			ismaxh = this._isNumber( data.height ) && o.maxHeight && ( o.maxHeight < data.height ),
-			isminw = this._isNumber( data.width ) && o.minWidth && ( o.minWidth > data.width ),
-			isminh = this._isNumber( data.height ) && o.minHeight && ( o.minHeight > data.height ),
-			dw = this.originalPosition.left + this.originalSize.width,
-			dh = this.originalPosition.top + this.originalSize.height,
-			cw = /sw|nw|w/.test( a ), ch = /nw|ne|n/.test( a );
-		if ( isminw ) {
-			data.width = o.minWidth;
-		}
-		if ( isminh ) {
-			data.height = o.minHeight;
-		}
-		if ( ismaxw ) {
-			data.width = o.maxWidth;
-		}
-		if ( ismaxh ) {
-			data.height = o.maxHeight;
-		}
-
-		if ( isminw && cw ) {
-			data.left = dw - o.minWidth;
-		}
-		if ( ismaxw && cw ) {
-			data.left = dw - o.maxWidth;
-		}
-		if ( isminh && ch ) {
-			data.top = dh - o.minHeight;
-		}
-		if ( ismaxh && ch ) {
-			data.top = dh - o.maxHeight;
-		}
-
-		// Fixing jump error on top/left - bug #2330
-		if ( !data.width && !data.height && !data.left && data.top ) {
-			data.top = null;
-		} else if ( !data.width && !data.height && !data.top && data.left ) {
-			data.left = null;
-		}
-
-		return data;
-	},
-
-	_getPaddingPlusBorderDimensions: function( element ) {
-		var i = 0,
-			widths = [],
-			borders = [
-				element.css( "borderTopWidth" ),
-				element.css( "borderRightWidth" ),
-				element.css( "borderBottomWidth" ),
-				element.css( "borderLeftWidth" )
-			],
-			paddings = [
-				element.css( "paddingTop" ),
-				element.css( "paddingRight" ),
-				element.css( "paddingBottom" ),
-				element.css( "paddingLeft" )
-			];
-
-		for ( ; i < 4; i++ ) {
-			widths[ i ] = ( parseFloat( borders[ i ] ) || 0 );
-			widths[ i ] += ( parseFloat( paddings[ i ] ) || 0 );
-		}
-
-		return {
-			height: widths[ 0 ] + widths[ 2 ],
-			width: widths[ 1 ] + widths[ 3 ]
-		};
-	},
-
-	_proportionallyResize: function() {
-
-		if ( !this._proportionallyResizeElements.length ) {
-			return;
-		}
-
-		var prel,
-			i = 0,
-			element = this.helper || this.element;
-
-		for ( ; i < this._proportionallyResizeElements.length; i++ ) {
-
-			prel = this._proportionallyResizeElements[ i ];
-
-			// TODO: Seems like a bug to cache this.outerDimensions
-			// considering that we are in a loop.
-			if ( !this.outerDimensions ) {
-				this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
-			}
-
-			prel.css( {
-				height: ( element.height() - this.outerDimensions.height ) || 0,
-				width: ( element.width() - this.outerDimensions.width ) || 0
-			} );
-
-		}
-
-	},
-
-	_renderProxy: function() {
-
-		var el = this.element, o = this.options;
-		this.elementOffset = el.offset();
-
-		if ( this._helper ) {
-
-			this.helper = this.helper || $( "<div style='overflow:hidden;'></div>" );
-
-			this._addClass( this.helper, this._helper );
-			this.helper.css( {
-				width: this.element.outerWidth(),
-				height: this.element.outerHeight(),
-				position: "absolute",
-				left: this.elementOffset.left + "px",
-				top: this.elementOffset.top + "px",
-				zIndex: ++o.zIndex //TODO: Don't modify option
-			} );
-
-			this.helper
-				.appendTo( "body" )
-				.disableSelection();
-
-		} else {
-			this.helper = this.element;
-		}
-
-	},
-
-	_change: {
-		e: function( event, dx ) {
-			return { width: this.originalSize.width + dx };
-		},
-		w: function( event, dx ) {
-			var cs = this.originalSize, sp = this.originalPosition;
-			return { left: sp.left + dx, width: cs.width - dx };
-		},
-		n: function( event, dx, dy ) {
-			var cs = this.originalSize, sp = this.originalPosition;
-			return { top: sp.top + dy, height: cs.height - dy };
-		},
-		s: function( event, dx, dy ) {
-			return { height: this.originalSize.height + dy };
-		},
-		se: function( event, dx, dy ) {
-			return $.extend( this._change.s.apply( this, arguments ),
-				this._change.e.apply( this, [ event, dx, dy ] ) );
-		},
-		sw: function( event, dx, dy ) {
-			return $.extend( this._change.s.apply( this, arguments ),
-				this._change.w.apply( this, [ event, dx, dy ] ) );
-		},
-		ne: function( event, dx, dy ) {
-			return $.extend( this._change.n.apply( this, arguments ),
-				this._change.e.apply( this, [ event, dx, dy ] ) );
-		},
-		nw: function( event, dx, dy ) {
-			return $.extend( this._change.n.apply( this, arguments ),
-				this._change.w.apply( this, [ event, dx, dy ] ) );
-		}
-	},
-
-	_propagate: function( n, event ) {
-		$.ui.plugin.call( this, n, [ event, this.ui() ] );
-		( n !== "resize" && this._trigger( n, event, this.ui() ) );
-	},
-
-	plugins: {},
-
-	ui: function() {
-		return {
-			originalElement: this.originalElement,
-			element: this.element,
-			helper: this.helper,
-			position: this.position,
-			size: this.size,
-			originalSize: this.originalSize,
-			originalPosition: this.originalPosition
-		};
-	}
-
-} );
-
-/*
- * Resizable Extensions
- */
-
-$.ui.plugin.add( "resizable", "animate", {
-
-	stop: function( event ) {
-		var that = $( this ).resizable( "instance" ),
-			o = that.options,
-			pr = that._proportionallyResizeElements,
-			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ),
-			soffseth = ista && that._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height,
-			soffsetw = ista ? 0 : that.sizeDiff.width,
-			style = {
-				width: ( that.size.width - soffsetw ),
-				height: ( that.size.height - soffseth )
-			},
-			left = ( parseFloat( that.element.css( "left" ) ) +
-				( that.position.left - that.originalPosition.left ) ) || null,
-			top = ( parseFloat( that.element.css( "top" ) ) +
-				( that.position.top - that.originalPosition.top ) ) || null;
-
-		that.element.animate(
-			$.extend( style, top && left ? { top: top, left: left } : {} ), {
-				duration: o.animateDuration,
-				easing: o.animateEasing,
-				step: function() {
-
-					var data = {
-						width: parseFloat( that.element.css( "width" ) ),
-						height: parseFloat( that.element.css( "height" ) ),
-						top: parseFloat( that.element.css( "top" ) ),
-						left: parseFloat( that.element.css( "left" ) )
-					};
-
-					if ( pr && pr.length ) {
-						$( pr[ 0 ] ).css( { width: data.width, height: data.height } );
-					}
-
-					// Propagating resize, and updating values for each animation step
-					that._updateCache( data );
-					that._propagate( "resize", event );
-
-				}
-			}
-		);
-	}
-
-} );
-
-$.ui.plugin.add( "resizable", "containment", {
-
-	start: function() {
-		var element, p, co, ch, cw, width, height,
-			that = $( this ).resizable( "instance" ),
-			o = that.options,
-			el = that.element,
-			oc = o.containment,
-			ce = ( oc instanceof $ ) ?
-				oc.get( 0 ) :
-				( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;
-
-		if ( !ce ) {
-			return;
-		}
-
-		that.containerElement = $( ce );
-
-		if ( /document/.test( oc ) || oc === document ) {
-			that.containerOffset = {
-				left: 0,
-				top: 0
-			};
-			that.containerPosition = {
-				left: 0,
-				top: 0
-			};
-
-			that.parentData = {
-				element: $( document ),
-				left: 0,
-				top: 0,
-				width: $( document ).width(),
-				height: $( document ).height() || document.body.parentNode.scrollHeight
-			};
-		} else {
-			element = $( ce );
-			p = [];
-			$( [ "Top", "Right", "Left", "Bottom" ] ).each( function( i, name ) {
-				p[ i ] = that._num( element.css( "padding" + name ) );
-			} );
-
-			that.containerOffset = element.offset();
-			that.containerPosition = element.position();
-			that.containerSize = {
-				height: ( element.innerHeight() - p[ 3 ] ),
-				width: ( element.innerWidth() - p[ 1 ] )
-			};
-
-			co = that.containerOffset;
-			ch = that.containerSize.height;
-			cw = that.containerSize.width;
-			width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
-			height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;
-
-			that.parentData = {
-				element: ce,
-				left: co.left,
-				top: co.top,
-				width: width,
-				height: height
-			};
-		}
-	},
-
-	resize: function( event ) {
-		var woset, hoset, isParent, isOffsetRelative,
-			that = $( this ).resizable( "instance" ),
-			o = that.options,
-			co = that.containerOffset,
-			cp = that.position,
-			pRatio = that._aspectRatio || event.shiftKey,
-			cop = {
-				top: 0,
-				left: 0
-			},
-			ce = that.containerElement,
-			continueResize = true;
-
-		if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
-			cop = co;
-		}
-
-		if ( cp.left < ( that._helper ? co.left : 0 ) ) {
-			that.size.width = that.size.width +
-				( that._helper ?
-					( that.position.left - co.left ) :
-					( that.position.left - cop.left ) );
-
-			if ( pRatio ) {
-				that.size.height = that.size.width / that.aspectRatio;
-				continueResize = false;
-			}
-			that.position.left = o.helper ? co.left : 0;
-		}
-
-		if ( cp.top < ( that._helper ? co.top : 0 ) ) {
-			that.size.height = that.size.height +
-				( that._helper ?
-					( that.position.top - co.top ) :
-					that.position.top );
-
-			if ( pRatio ) {
-				that.size.width = that.size.height * that.aspectRatio;
-				continueResize = false;
-			}
-			that.position.top = that._helper ? co.top : 0;
-		}
-
-		isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
-		isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );
-
-		if ( isParent && isOffsetRelative ) {
-			that.offset.left = that.parentData.left + that.position.left;
-			that.offset.top = that.parentData.top + that.position.top;
-		} else {
-			that.offset.left = that.element.offset().left;
-			that.offset.top = that.element.offset().top;
-		}
-
-		woset = Math.abs( that.sizeDiff.width +
-			( that._helper ?
-				that.offset.left - cop.left :
-				( that.offset.left - co.left ) ) );
-
-		hoset = Math.abs( that.sizeDiff.height +
-			( that._helper ?
-				that.offset.top - cop.top :
-				( that.offset.top - co.top ) ) );
-
-		if ( woset + that.size.width >= that.parentData.width ) {
-			that.size.width = that.parentData.width - woset;
-			if ( pRatio ) {
-				that.size.height = that.size.width / that.aspectRatio;
-				continueResize = false;
-			}
-		}
-
-		if ( hoset + that.size.height >= that.parentData.height ) {
-			that.size.height = that.parentData.height - hoset;
-			if ( pRatio ) {
-				that.size.width = that.size.height * that.aspectRatio;
-				continueResize = false;
-			}
-		}
-
-		if ( !continueResize ) {
-			that.position.left = that.prevPosition.left;
-			that.position.top = that.prevPosition.top;
-			that.size.width = that.prevSize.width;
-			that.size.height = that.prevSize.height;
-		}
-	},
-
-	stop: function() {
-		var that = $( this ).resizable( "instance" ),
-			o = that.options,
-			co = that.containerOffset,
-			cop = that.containerPosition,
-			ce = that.containerElement,
-			helper = $( that.helper ),
-			ho = helper.offset(),
-			w = helper.outerWidth() - that.sizeDiff.width,
-			h = helper.outerHeight() - that.sizeDiff.height;
-
-		if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
-			$( this ).css( {
-				left: ho.left - cop.left - co.left,
-				width: w,
-				height: h
-			} );
-		}
-
-		if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
-			$( this ).css( {
-				left: ho.left - cop.left - co.left,
-				width: w,
-				height: h
-			} );
-		}
-	}
-} );
-
-$.ui.plugin.add( "resizable", "alsoResize", {
-
-	start: function() {
-		var that = $( this ).resizable( "instance" ),
-			o = that.options;
-
-		$( o.alsoResize ).each( function() {
-			var el = $( this );
-			el.data( "ui-resizable-alsoresize", {
-				width: parseFloat( el.width() ), height: parseFloat( el.height() ),
-				left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
-			} );
-		} );
-	},
-
-	resize: function( event, ui ) {
-		var that = $( this ).resizable( "instance" ),
-			o = that.options,
-			os = that.originalSize,
-			op = that.originalPosition,
-			delta = {
-				height: ( that.size.height - os.height ) || 0,
-				width: ( that.size.width - os.width ) || 0,
-				top: ( that.position.top - op.top ) || 0,
-				left: ( that.position.left - op.left ) || 0
-			};
-
-			$( o.alsoResize ).each( function() {
-				var el = $( this ), start = $( this ).data( "ui-resizable-alsoresize" ), style = {},
-					css = el.parents( ui.originalElement[ 0 ] ).length ?
-							[ "width", "height" ] :
-							[ "width", "height", "top", "left" ];
-
-				$.each( css, function( i, prop ) {
-					var sum = ( start[ prop ] || 0 ) + ( delta[ prop ] || 0 );
-					if ( sum && sum >= 0 ) {
-						style[ prop ] = sum || null;
-					}
-				} );
-
-				el.css( style );
-			} );
-	},
-
-	stop: function() {
-		$( this ).removeData( "ui-resizable-alsoresize" );
-	}
-} );
-
-$.ui.plugin.add( "resizable", "ghost", {
-
-	start: function() {
-
-		var that = $( this ).resizable( "instance" ), cs = that.size;
-
-		that.ghost = that.originalElement.clone();
-		that.ghost.css( {
-			opacity: 0.25,
-			display: "block",
-			position: "relative",
-			height: cs.height,
-			width: cs.width,
-			margin: 0,
-			left: 0,
-			top: 0
-		} );
-
-		that._addClass( that.ghost, "ui-resizable-ghost" );
-
-		// DEPRECATED
-		// TODO: remove after 1.12
-		if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) {
-
-			// Ghost option
-			that.ghost.addClass( this.options.ghost );
-		}
-
-		that.ghost.appendTo( that.helper );
-
-	},
-
-	resize: function() {
-		var that = $( this ).resizable( "instance" );
-		if ( that.ghost ) {
-			that.ghost.css( {
-				position: "relative",
-				height: that.size.height,
-				width: that.size.width
-			} );
-		}
-	},
-
-	stop: function() {
-		var that = $( this ).resizable( "instance" );
-		if ( that.ghost && that.helper ) {
-			that.helper.get( 0 ).removeChild( that.ghost.get( 0 ) );
-		}
-	}
-
-} );
-
-$.ui.plugin.add( "resizable", "grid", {
-
-	resize: function() {
-		var outerDimensions,
-			that = $( this ).resizable( "instance" ),
-			o = that.options,
-			cs = that.size,
-			os = that.originalSize,
-			op = that.originalPosition,
-			a = that.axis,
-			grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
-			gridX = ( grid[ 0 ] || 1 ),
-			gridY = ( grid[ 1 ] || 1 ),
-			ox = Math.round( ( cs.width - os.width ) / gridX ) * gridX,
-			oy = Math.round( ( cs.height - os.height ) / gridY ) * gridY,
-			newWidth = os.width + ox,
-			newHeight = os.height + oy,
-			isMaxWidth = o.maxWidth && ( o.maxWidth < newWidth ),
-			isMaxHeight = o.maxHeight && ( o.maxHeight < newHeight ),
-			isMinWidth = o.minWidth && ( o.minWidth > newWidth ),
-			isMinHeight = o.minHeight && ( o.minHeight > newHeight );
-
-		o.grid = grid;
-
-		if ( isMinWidth ) {
-			newWidth += gridX;
-		}
-		if ( isMinHeight ) {
-			newHeight += gridY;
-		}
-		if ( isMaxWidth ) {
-			newWidth -= gridX;
-		}
-		if ( isMaxHeight ) {
-			newHeight -= gridY;
-		}
-
-		if ( /^(se|s|e)$/.test( a ) ) {
-			that.size.width = newWidth;
-			that.size.height = newHeight;
-		} else if ( /^(ne)$/.test( a ) ) {
-			that.size.width = newWidth;
-			that.size.height = newHeight;
-			that.position.top = op.top - oy;
-		} else if ( /^(sw)$/.test( a ) ) {
-			that.size.width = newWidth;
-			that.size.height = newHeight;
-			that.position.left = op.left - ox;
-		} else {
-			if ( newHeight - gridY <= 0 || newWidth - gridX <= 0 ) {
-				outerDimensions = that._getPaddingPlusBorderDimensions( this );
-			}
-
-			if ( newHeight - gridY > 0 ) {
-				that.size.height = newHeight;
-				that.position.top = op.top - oy;
-			} else {
-				newHeight = gridY - outerDimensions.height;
-				that.size.height = newHeight;
-				that.position.top = op.top + os.height - newHeight;
-			}
-			if ( newWidth - gridX > 0 ) {
-				that.size.width = newWidth;
-				that.position.left = op.left - ox;
-			} else {
-				newWidth = gridX - outerDimensions.width;
-				that.size.width = newWidth;
-				that.position.left = op.left + os.width - newWidth;
-			}
-		}
-	}
-
-} );
-
-var widgetsResizable = $.ui.resizable;
-
-
-/*!
- * jQuery UI Dialog 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Dialog
-//>>group: Widgets
-//>>description: Displays customizable dialog windows.
-//>>docs: http://api.jqueryui.com/dialog/
-//>>demos: http://jqueryui.com/dialog/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/dialog.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.dialog", {
-	version: "1.12.1",
-	options: {
-		appendTo: "body",
-		autoOpen: true,
-		buttons: [],
-		classes: {
-			"ui-dialog": "ui-corner-all",
-			"ui-dialog-titlebar": "ui-corner-all"
-		},
-		closeOnEscape: true,
-		closeText: "Close",
-		draggable: true,
-		hide: null,
-		height: "auto",
-		maxHeight: null,
-		maxWidth: null,
-		minHeight: 150,
-		minWidth: 150,
-		modal: false,
-		position: {
-			my: "center",
-			at: "center",
-			of: window,
-			collision: "fit",
-
-			// Ensure the titlebar is always visible
-			using: function( pos ) {
-				var topOffset = $( this ).css( pos ).offset().top;
-				if ( topOffset < 0 ) {
-					$( this ).css( "top", pos.top - topOffset );
-				}
-			}
-		},
-		resizable: true,
-		show: null,
-		title: null,
-		width: 300,
-
-		// Callbacks
-		beforeClose: null,
-		close: null,
-		drag: null,
-		dragStart: null,
-		dragStop: null,
-		focus: null,
-		open: null,
-		resize: null,
-		resizeStart: null,
-		resizeStop: null
-	},
-
-	sizeRelatedOptions: {
-		buttons: true,
-		height: true,
-		maxHeight: true,
-		maxWidth: true,
-		minHeight: true,
-		minWidth: true,
-		width: true
-	},
-
-	resizableRelatedOptions: {
-		maxHeight: true,
-		maxWidth: true,
-		minHeight: true,
-		minWidth: true
-	},
-
-	_create: function() {
-		this.originalCss = {
-			display: this.element[ 0 ].style.display,
-			width: this.element[ 0 ].style.width,
-			minHeight: this.element[ 0 ].style.minHeight,
-			maxHeight: this.element[ 0 ].style.maxHeight,
-			height: this.element[ 0 ].style.height
-		};
-		this.originalPosition = {
-			parent: this.element.parent(),
-			index: this.element.parent().children().index( this.element )
-		};
-		this.originalTitle = this.element.attr( "title" );
-		if ( this.options.title == null && this.originalTitle != null ) {
-			this.options.title = this.originalTitle;
-		}
-
-		// Dialogs can't be disabled
-		if ( this.options.disabled ) {
-			this.options.disabled = false;
-		}
-
-		this._createWrapper();
-
-		this.element
-			.show()
-			.removeAttr( "title" )
-			.appendTo( this.uiDialog );
-
-		this._addClass( "ui-dialog-content", "ui-widget-content" );
-
-		this._createTitlebar();
-		this._createButtonPane();
-
-		if ( this.options.draggable && $.fn.draggable ) {
-			this._makeDraggable();
-		}
-		if ( this.options.resizable && $.fn.resizable ) {
-			this._makeResizable();
-		}
-
-		this._isOpen = false;
-
-		this._trackFocus();
-	},
-
-	_init: function() {
-		if ( this.options.autoOpen ) {
-			this.open();
-		}
-	},
-
-	_appendTo: function() {
-		var element = this.options.appendTo;
-		if ( element && ( element.jquery || element.nodeType ) ) {
-			return $( element );
-		}
-		return this.document.find( element || "body" ).eq( 0 );
-	},
-
-	_destroy: function() {
-		var next,
-			originalPosition = this.originalPosition;
-
-		this._untrackInstance();
-		this._destroyOverlay();
-
-		this.element
-			.removeUniqueId()
-			.css( this.originalCss )
-
-			// Without detaching first, the following becomes really slow
-			.detach();
-
-		this.uiDialog.remove();
-
-		if ( this.originalTitle ) {
-			this.element.attr( "title", this.originalTitle );
-		}
-
-		next = originalPosition.parent.children().eq( originalPosition.index );
-
-		// Don't try to place the dialog next to itself (#8613)
-		if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
-			next.before( this.element );
-		} else {
-			originalPosition.parent.append( this.element );
-		}
-	},
-
-	widget: function() {
-		return this.uiDialog;
-	},
-
-	disable: $.noop,
-	enable: $.noop,
-
-	close: function( event ) {
-		var that = this;
-
-		if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
-			return;
-		}
-
-		this._isOpen = false;
-		this._focusedElement = null;
-		this._destroyOverlay();
-		this._untrackInstance();
-
-		if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) {
-
-			// Hiding a focused element doesn't trigger blur in WebKit
-			// so in case we have nothing to focus on, explicitly blur the active element
-			// https://bugs.webkit.org/show_bug.cgi?id=47182
-			$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
-		}
-
-		this._hide( this.uiDialog, this.options.hide, function() {
-			that._trigger( "close", event );
-		} );
-	},
-
-	isOpen: function() {
-		return this._isOpen;
-	},
-
-	moveToTop: function() {
-		this._moveToTop();
-	},
-
-	_moveToTop: function( event, silent ) {
-		var moved = false,
-			zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() {
-				return +$( this ).css( "z-index" );
-			} ).get(),
-			zIndexMax = Math.max.apply( null, zIndices );
-
-		if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
-			this.uiDialog.css( "z-index", zIndexMax + 1 );
-			moved = true;
-		}
-
-		if ( moved && !silent ) {
-			this._trigger( "focus", event );
-		}
-		return moved;
-	},
-
-	open: function() {
-		var that = this;
-		if ( this._isOpen ) {
-			if ( this._moveToTop() ) {
-				this._focusTabbable();
-			}
-			return;
-		}
-
-		this._isOpen = true;
-		this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
-
-		this._size();
-		this._position();
-		this._createOverlay();
-		this._moveToTop( null, true );
-
-		// Ensure the overlay is moved to the top with the dialog, but only when
-		// opening. The overlay shouldn't move after the dialog is open so that
-		// modeless dialogs opened after the modal dialog stack properly.
-		if ( this.overlay ) {
-			this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
-		}
-
-		this._show( this.uiDialog, this.options.show, function() {
-			that._focusTabbable();
-			that._trigger( "focus" );
-		} );
-
-		// Track the dialog immediately upon openening in case a focus event
-		// somehow occurs outside of the dialog before an element inside the
-		// dialog is focused (#10152)
-		this._makeFocusTarget();
-
-		this._trigger( "open" );
-	},
-
-	_focusTabbable: function() {
-
-		// Set focus to the first match:
-		// 1. An element that was focused previously
-		// 2. First element inside the dialog matching [autofocus]
-		// 3. Tabbable element inside the content element
-		// 4. Tabbable element inside the buttonpane
-		// 5. The close button
-		// 6. The dialog itself
-		var hasFocus = this._focusedElement;
-		if ( !hasFocus ) {
-			hasFocus = this.element.find( "[autofocus]" );
-		}
-		if ( !hasFocus.length ) {
-			hasFocus = this.element.find( ":tabbable" );
-		}
-		if ( !hasFocus.length ) {
-			hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
-		}
-		if ( !hasFocus.length ) {
-			hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
-		}
-		if ( !hasFocus.length ) {
-			hasFocus = this.uiDialog;
-		}
-		hasFocus.eq( 0 ).trigger( "focus" );
-	},
-
-	_keepFocus: function( event ) {
-		function checkFocus() {
-			var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
-				isActive = this.uiDialog[ 0 ] === activeElement ||
-					$.contains( this.uiDialog[ 0 ], activeElement );
-			if ( !isActive ) {
-				this._focusTabbable();
-			}
-		}
-		event.preventDefault();
-		checkFocus.call( this );
-
-		// support: IE
-		// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
-		// so we check again later
-		this._delay( checkFocus );
-	},
-
-	_createWrapper: function() {
-		this.uiDialog = $( "<div>" )
-			.hide()
-			.attr( {
-
-				// Setting tabIndex makes the div focusable
-				tabIndex: -1,
-				role: "dialog"
-			} )
-			.appendTo( this._appendTo() );
-
-		this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
-		this._on( this.uiDialog, {
-			keydown: function( event ) {
-				if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
-						event.keyCode === $.ui.keyCode.ESCAPE ) {
-					event.preventDefault();
-					this.close( event );
-					return;
-				}
-
-				// Prevent tabbing out of dialogs
-				if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
-					return;
-				}
-				var tabbables = this.uiDialog.find( ":tabbable" ),
-					first = tabbables.filter( ":first" ),
-					last = tabbables.filter( ":last" );
-
-				if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) &&
-						!event.shiftKey ) {
-					this._delay( function() {
-						first.trigger( "focus" );
-					} );
-					event.preventDefault();
-				} else if ( ( event.target === first[ 0 ] ||
-						event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) {
-					this._delay( function() {
-						last.trigger( "focus" );
-					} );
-					event.preventDefault();
-				}
-			},
-			mousedown: function( event ) {
-				if ( this._moveToTop( event ) ) {
-					this._focusTabbable();
-				}
-			}
-		} );
-
-		// We assume that any existing aria-describedby attribute means
-		// that the dialog content is marked up properly
-		// otherwise we brute force the content as the description
-		if ( !this.element.find( "[aria-describedby]" ).length ) {
-			this.uiDialog.attr( {
-				"aria-describedby": this.element.uniqueId().attr( "id" )
-			} );
-		}
-	},
-
-	_createTitlebar: function() {
-		var uiDialogTitle;
-
-		this.uiDialogTitlebar = $( "<div>" );
-		this._addClass( this.uiDialogTitlebar,
-			"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
-		this._on( this.uiDialogTitlebar, {
-			mousedown: function( event ) {
-
-				// Don't prevent click on close button (#8838)
-				// Focusing a dialog that is partially scrolled out of view
-				// causes the browser to scroll it into view, preventing the click event
-				if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
-
-					// Dialog isn't getting focus when dragging (#8063)
-					this.uiDialog.trigger( "focus" );
-				}
-			}
-		} );
-
-		// Support: IE
-		// Use type="button" to prevent enter keypresses in textboxes from closing the
-		// dialog in IE (#9312)
-		this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
-			.button( {
-				label: $( "<a>" ).text( this.options.closeText ).html(),
-				icon: "ui-icon-closethick",
-				showLabel: false
-			} )
-			.appendTo( this.uiDialogTitlebar );
-
-		this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
-		this._on( this.uiDialogTitlebarClose, {
-			click: function( event ) {
-				event.preventDefault();
-				this.close( event );
-			}
-		} );
-
-		uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
-		this._addClass( uiDialogTitle, "ui-dialog-title" );
-		this._title( uiDialogTitle );
-
-		this.uiDialogTitlebar.prependTo( this.uiDialog );
-
-		this.uiDialog.attr( {
-			"aria-labelledby": uiDialogTitle.attr( "id" )
-		} );
-	},
-
-	_title: function( title ) {
-		if ( this.options.title ) {
-			title.text( this.options.title );
-		} else {
-			title.html( "&#160;" );
-		}
-	},
-
-	_createButtonPane: function() {
-		this.uiDialogButtonPane = $( "<div>" );
-		this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
-			"ui-widget-content ui-helper-clearfix" );
-
-		this.uiButtonSet = $( "<div>" )
-			.appendTo( this.uiDialogButtonPane );
-		this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );
-
-		this._createButtons();
-	},
-
-	_createButtons: function() {
-		var that = this,
-			buttons = this.options.buttons;
-
-		// If we already have a button pane, remove it
-		this.uiDialogButtonPane.remove();
-		this.uiButtonSet.empty();
-
-		if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) {
-			this._removeClass( this.uiDialog, "ui-dialog-buttons" );
-			return;
-		}
-
-		$.each( buttons, function( name, props ) {
-			var click, buttonOptions;
-			props = $.isFunction( props ) ?
-				{ click: props, text: name } :
-				props;
-
-			// Default to a non-submitting button
-			props = $.extend( { type: "button" }, props );
-
-			// Change the context for the click callback to be the main element
-			click = props.click;
-			buttonOptions = {
-				icon: props.icon,
-				iconPosition: props.iconPosition,
-				showLabel: props.showLabel,
-
-				// Deprecated options
-				icons: props.icons,
-				text: props.text
-			};
-
-			delete props.click;
-			delete props.icon;
-			delete props.iconPosition;
-			delete props.showLabel;
-
-			// Deprecated options
-			delete props.icons;
-			if ( typeof props.text === "boolean" ) {
-				delete props.text;
-			}
-
-			$( "<button></button>", props )
-				.button( buttonOptions )
-				.appendTo( that.uiButtonSet )
-				.on( "click", function() {
-					click.apply( that.element[ 0 ], arguments );
-				} );
-		} );
-		this._addClass( this.uiDialog, "ui-dialog-buttons" );
-		this.uiDialogButtonPane.appendTo( this.uiDialog );
-	},
-
-	_makeDraggable: function() {
-		var that = this,
-			options = this.options;
-
-		function filteredUi( ui ) {
-			return {
-				position: ui.position,
-				offset: ui.offset
-			};
-		}
-
-		this.uiDialog.draggable( {
-			cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
-			handle: ".ui-dialog-titlebar",
-			containment: "document",
-			start: function( event, ui ) {
-				that._addClass( $( this ), "ui-dialog-dragging" );
-				that._blockFrames();
-				that._trigger( "dragStart", event, filteredUi( ui ) );
-			},
-			drag: function( event, ui ) {
-				that._trigger( "drag", event, filteredUi( ui ) );
-			},
-			stop: function( event, ui ) {
-				var left = ui.offset.left - that.document.scrollLeft(),
-					top = ui.offset.top - that.document.scrollTop();
-
-				options.position = {
-					my: "left top",
-					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
-						"top" + ( top >= 0 ? "+" : "" ) + top,
-					of: that.window
-				};
-				that._removeClass( $( this ), "ui-dialog-dragging" );
-				that._unblockFrames();
-				that._trigger( "dragStop", event, filteredUi( ui ) );
-			}
-		} );
-	},
-
-	_makeResizable: function() {
-		var that = this,
-			options = this.options,
-			handles = options.resizable,
-
-			// .ui-resizable has position: relative defined in the stylesheet
-			// but dialogs have to use absolute or fixed positioning
-			position = this.uiDialog.css( "position" ),
-			resizeHandles = typeof handles === "string" ?
-				handles :
-				"n,e,s,w,se,sw,ne,nw";
-
-		function filteredUi( ui ) {
-			return {
-				originalPosition: ui.originalPosition,
-				originalSize: ui.originalSize,
-				position: ui.position,
-				size: ui.size
-			};
-		}
-
-		this.uiDialog.resizable( {
-			cancel: ".ui-dialog-content",
-			containment: "document",
-			alsoResize: this.element,
-			maxWidth: options.maxWidth,
-			maxHeight: options.maxHeight,
-			minWidth: options.minWidth,
-			minHeight: this._minHeight(),
-			handles: resizeHandles,
-			start: function( event, ui ) {
-				that._addClass( $( this ), "ui-dialog-resizing" );
-				that._blockFrames();
-				that._trigger( "resizeStart", event, filteredUi( ui ) );
-			},
-			resize: function( event, ui ) {
-				that._trigger( "resize", event, filteredUi( ui ) );
-			},
-			stop: function( event, ui ) {
-				var offset = that.uiDialog.offset(),
-					left = offset.left - that.document.scrollLeft(),
-					top = offset.top - that.document.scrollTop();
-
-				options.height = that.uiDialog.height();
-				options.width = that.uiDialog.width();
-				options.position = {
-					my: "left top",
-					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
-						"top" + ( top >= 0 ? "+" : "" ) + top,
-					of: that.window
-				};
-				that._removeClass( $( this ), "ui-dialog-resizing" );
-				that._unblockFrames();
-				that._trigger( "resizeStop", event, filteredUi( ui ) );
-			}
-		} )
-			.css( "position", position );
-	},
-
-	_trackFocus: function() {
-		this._on( this.widget(), {
-			focusin: function( event ) {
-				this._makeFocusTarget();
-				this._focusedElement = $( event.target );
-			}
-		} );
-	},
-
-	_makeFocusTarget: function() {
-		this._untrackInstance();
-		this._trackingInstances().unshift( this );
-	},
-
-	_untrackInstance: function() {
-		var instances = this._trackingInstances(),
-			exists = $.inArray( this, instances );
-		if ( exists !== -1 ) {
-			instances.splice( exists, 1 );
-		}
-	},
-
-	_trackingInstances: function() {
-		var instances = this.document.data( "ui-dialog-instances" );
-		if ( !instances ) {
-			instances = [];
-			this.document.data( "ui-dialog-instances", instances );
-		}
-		return instances;
-	},
-
-	_minHeight: function() {
-		var options = this.options;
-
-		return options.height === "auto" ?
-			options.minHeight :
-			Math.min( options.minHeight, options.height );
-	},
-
-	_position: function() {
-
-		// Need to show the dialog to get the actual offset in the position plugin
-		var isVisible = this.uiDialog.is( ":visible" );
-		if ( !isVisible ) {
-			this.uiDialog.show();
-		}
-		this.uiDialog.position( this.options.position );
-		if ( !isVisible ) {
-			this.uiDialog.hide();
-		}
-	},
-
-	_setOptions: function( options ) {
-		var that = this,
-			resize = false,
-			resizableOptions = {};
-
-		$.each( options, function( key, value ) {
-			that._setOption( key, value );
-
-			if ( key in that.sizeRelatedOptions ) {
-				resize = true;
-			}
-			if ( key in that.resizableRelatedOptions ) {
-				resizableOptions[ key ] = value;
-			}
-		} );
-
-		if ( resize ) {
-			this._size();
-			this._position();
-		}
-		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
-			this.uiDialog.resizable( "option", resizableOptions );
-		}
-	},
-
-	_setOption: function( key, value ) {
-		var isDraggable, isResizable,
-			uiDialog = this.uiDialog;
-
-		if ( key === "disabled" ) {
-			return;
-		}
-
-		this._super( key, value );
-
-		if ( key === "appendTo" ) {
-			this.uiDialog.appendTo( this._appendTo() );
-		}
-
-		if ( key === "buttons" ) {
-			this._createButtons();
-		}
-
-		if ( key === "closeText" ) {
-			this.uiDialogTitlebarClose.button( {
-
-				// Ensure that we always pass a string
-				label: $( "<a>" ).text( "" + this.options.closeText ).html()
-			} );
-		}
-
-		if ( key === "draggable" ) {
-			isDraggable = uiDialog.is( ":data(ui-draggable)" );
-			if ( isDraggable && !value ) {
-				uiDialog.draggable( "destroy" );
-			}
-
-			if ( !isDraggable && value ) {
-				this._makeDraggable();
-			}
-		}
-
-		if ( key === "position" ) {
-			this._position();
-		}
-
-		if ( key === "resizable" ) {
-
-			// currently resizable, becoming non-resizable
-			isResizable = uiDialog.is( ":data(ui-resizable)" );
-			if ( isResizable && !value ) {
-				uiDialog.resizable( "destroy" );
-			}
-
-			// Currently resizable, changing handles
-			if ( isResizable && typeof value === "string" ) {
-				uiDialog.resizable( "option", "handles", value );
-			}
-
-			// Currently non-resizable, becoming resizable
-			if ( !isResizable && value !== false ) {
-				this._makeResizable();
-			}
-		}
-
-		if ( key === "title" ) {
-			this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
-		}
-	},
-
-	_size: function() {
-
-		// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
-		// divs will both have width and height set, so we need to reset them
-		var nonContentHeight, minContentHeight, maxContentHeight,
-			options = this.options;
-
-		// Reset content sizing
-		this.element.show().css( {
-			width: "auto",
-			minHeight: 0,
-			maxHeight: "none",
-			height: 0
-		} );
-
-		if ( options.minWidth > options.width ) {
-			options.width = options.minWidth;
-		}
-
-		// Reset wrapper sizing
-		// determine the height of all the non-content elements
-		nonContentHeight = this.uiDialog.css( {
-			height: "auto",
-			width: options.width
-		} )
-			.outerHeight();
-		minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
-		maxContentHeight = typeof options.maxHeight === "number" ?
-			Math.max( 0, options.maxHeight - nonContentHeight ) :
-			"none";
-
-		if ( options.height === "auto" ) {
-			this.element.css( {
-				minHeight: minContentHeight,
-				maxHeight: maxContentHeight,
-				height: "auto"
-			} );
-		} else {
-			this.element.height( Math.max( 0, options.height - nonContentHeight ) );
-		}
-
-		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
-			this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
-		}
-	},
-
-	_blockFrames: function() {
-		this.iframeBlocks = this.document.find( "iframe" ).map( function() {
-			var iframe = $( this );
-
-			return $( "<div>" )
-				.css( {
-					position: "absolute",
-					width: iframe.outerWidth(),
-					height: iframe.outerHeight()
-				} )
-				.appendTo( iframe.parent() )
-				.offset( iframe.offset() )[ 0 ];
-		} );
-	},
-
-	_unblockFrames: function() {
-		if ( this.iframeBlocks ) {
-			this.iframeBlocks.remove();
-			delete this.iframeBlocks;
-		}
-	},
-
-	_allowInteraction: function( event ) {
-		if ( $( event.target ).closest( ".ui-dialog" ).length ) {
-			return true;
-		}
-
-		// TODO: Remove hack when datepicker implements
-		// the .ui-front logic (#8989)
-		return !!$( event.target ).closest( ".ui-datepicker" ).length;
-	},
-
-	_createOverlay: function() {
-		if ( !this.options.modal ) {
-			return;
-		}
-
-		// We use a delay in case the overlay is created from an
-		// event that we're going to be cancelling (#2804)
-		var isOpening = true;
-		this._delay( function() {
-			isOpening = false;
-		} );
-
-		if ( !this.document.data( "ui-dialog-overlays" ) ) {
-
-			// Prevent use of anchors and inputs
-			// Using _on() for an event handler shared across many instances is
-			// safe because the dialogs stack and must be closed in reverse order
-			this._on( this.document, {
-				focusin: function( event ) {
-					if ( isOpening ) {
-						return;
-					}
-
-					if ( !this._allowInteraction( event ) ) {
-						event.preventDefault();
-						this._trackingInstances()[ 0 ]._focusTabbable();
-					}
-				}
-			} );
-		}
-
-		this.overlay = $( "<div>" )
-			.appendTo( this._appendTo() );
-
-		this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
-		this._on( this.overlay, {
-			mousedown: "_keepFocus"
-		} );
-		this.document.data( "ui-dialog-overlays",
-			( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 );
-	},
-
-	_destroyOverlay: function() {
-		if ( !this.options.modal ) {
-			return;
-		}
-
-		if ( this.overlay ) {
-			var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
-
-			if ( !overlays ) {
-				this._off( this.document, "focusin" );
-				this.document.removeData( "ui-dialog-overlays" );
-			} else {
-				this.document.data( "ui-dialog-overlays", overlays );
-			}
-
-			this.overlay.remove();
-			this.overlay = null;
-		}
-	}
-} );
-
-// DEPRECATED
-// TODO: switch return back to widget declaration at top of file when this is removed
-if ( $.uiBackCompat !== false ) {
-
-	// Backcompat for dialogClass option
-	$.widget( "ui.dialog", $.ui.dialog, {
-		options: {
-			dialogClass: ""
-		},
-		_createWrapper: function() {
-			this._super();
-			this.uiDialog.addClass( this.options.dialogClass );
-		},
-		_setOption: function( key, value ) {
-			if ( key === "dialogClass" ) {
-				this.uiDialog
-					.removeClass( this.options.dialogClass )
-					.addClass( value );
-			}
-			this._superApply( arguments );
-		}
-	} );
-}
-
-var widgetsDialog = $.ui.dialog;
-
-
-/*!
- * jQuery UI Droppable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Droppable
-//>>group: Interactions
-//>>description: Enables drop targets for draggable elements.
-//>>docs: http://api.jqueryui.com/droppable/
-//>>demos: http://jqueryui.com/droppable/
-
-
-
-$.widget( "ui.droppable", {
-	version: "1.12.1",
-	widgetEventPrefix: "drop",
-	options: {
-		accept: "*",
-		addClasses: true,
-		greedy: false,
-		scope: "default",
-		tolerance: "intersect",
-
-		// Callbacks
-		activate: null,
-		deactivate: null,
-		drop: null,
-		out: null,
-		over: null
-	},
-	_create: function() {
-
-		var proportions,
-			o = this.options,
-			accept = o.accept;
-
-		this.isover = false;
-		this.isout = true;
-
-		this.accept = $.isFunction( accept ) ? accept : function( d ) {
-			return d.is( accept );
-		};
-
-		this.proportions = function( /* valueToWrite */ ) {
-			if ( arguments.length ) {
-
-				// Store the droppable's proportions
-				proportions = arguments[ 0 ];
-			} else {
-
-				// Retrieve or derive the droppable's proportions
-				return proportions ?
-					proportions :
-					proportions = {
-						width: this.element[ 0 ].offsetWidth,
-						height: this.element[ 0 ].offsetHeight
-					};
-			}
-		};
-
-		this._addToManager( o.scope );
-
-		o.addClasses && this._addClass( "ui-droppable" );
-
-	},
-
-	_addToManager: function( scope ) {
-
-		// Add the reference and positions to the manager
-		$.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
-		$.ui.ddmanager.droppables[ scope ].push( this );
-	},
-
-	_splice: function( drop ) {
-		var i = 0;
-		for ( ; i < drop.length; i++ ) {
-			if ( drop[ i ] === this ) {
-				drop.splice( i, 1 );
-			}
-		}
-	},
-
-	_destroy: function() {
-		var drop = $.ui.ddmanager.droppables[ this.options.scope ];
-
-		this._splice( drop );
-	},
-
-	_setOption: function( key, value ) {
-
-		if ( key === "accept" ) {
-			this.accept = $.isFunction( value ) ? value : function( d ) {
-				return d.is( value );
-			};
-		} else if ( key === "scope" ) {
-			var drop = $.ui.ddmanager.droppables[ this.options.scope ];
-
-			this._splice( drop );
-			this._addToManager( value );
-		}
-
-		this._super( key, value );
-	},
-
-	_activate: function( event ) {
-		var draggable = $.ui.ddmanager.current;
-
-		this._addActiveClass();
-		if ( draggable ) {
-			this._trigger( "activate", event, this.ui( draggable ) );
-		}
-	},
-
-	_deactivate: function( event ) {
-		var draggable = $.ui.ddmanager.current;
-
-		this._removeActiveClass();
-		if ( draggable ) {
-			this._trigger( "deactivate", event, this.ui( draggable ) );
-		}
-	},
-
-	_over: function( event ) {
-
-		var draggable = $.ui.ddmanager.current;
-
-		// Bail if draggable and droppable are same element
-		if ( !draggable || ( draggable.currentItem ||
-				draggable.element )[ 0 ] === this.element[ 0 ] ) {
-			return;
-		}
-
-		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
-				draggable.element ) ) ) {
-			this._addHoverClass();
-			this._trigger( "over", event, this.ui( draggable ) );
-		}
-
-	},
-
-	_out: function( event ) {
-
-		var draggable = $.ui.ddmanager.current;
-
-		// Bail if draggable and droppable are same element
-		if ( !draggable || ( draggable.currentItem ||
-				draggable.element )[ 0 ] === this.element[ 0 ] ) {
-			return;
-		}
-
-		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
-				draggable.element ) ) ) {
-			this._removeHoverClass();
-			this._trigger( "out", event, this.ui( draggable ) );
-		}
-
-	},
-
-	_drop: function( event, custom ) {
-
-		var draggable = custom || $.ui.ddmanager.current,
-			childrenIntersection = false;
-
-		// Bail if draggable and droppable are same element
-		if ( !draggable || ( draggable.currentItem ||
-				draggable.element )[ 0 ] === this.element[ 0 ] ) {
-			return false;
-		}
-
-		this.element
-			.find( ":data(ui-droppable)" )
-			.not( ".ui-draggable-dragging" )
-			.each( function() {
-				var inst = $( this ).droppable( "instance" );
-				if (
-					inst.options.greedy &&
-					!inst.options.disabled &&
-					inst.options.scope === draggable.options.scope &&
-					inst.accept.call(
-						inst.element[ 0 ], ( draggable.currentItem || draggable.element )
-					) &&
-					intersect(
-						draggable,
-						$.extend( inst, { offset: inst.element.offset() } ),
-						inst.options.tolerance, event
-					)
-				) {
-					childrenIntersection = true;
-					return false; }
-			} );
-		if ( childrenIntersection ) {
-			return false;
-		}
-
-		if ( this.accept.call( this.element[ 0 ],
-				( draggable.currentItem || draggable.element ) ) ) {
-			this._removeActiveClass();
-			this._removeHoverClass();
-
-			this._trigger( "drop", event, this.ui( draggable ) );
-			return this.element;
-		}
-
-		return false;
-
-	},
-
-	ui: function( c ) {
-		return {
-			draggable: ( c.currentItem || c.element ),
-			helper: c.helper,
-			position: c.position,
-			offset: c.positionAbs
-		};
-	},
-
-	// Extension points just to make backcompat sane and avoid duplicating logic
-	// TODO: Remove in 1.13 along with call to it below
-	_addHoverClass: function() {
-		this._addClass( "ui-droppable-hover" );
-	},
-
-	_removeHoverClass: function() {
-		this._removeClass( "ui-droppable-hover" );
-	},
-
-	_addActiveClass: function() {
-		this._addClass( "ui-droppable-active" );
-	},
-
-	_removeActiveClass: function() {
-		this._removeClass( "ui-droppable-active" );
-	}
-} );
-
-var intersect = $.ui.intersect = ( function() {
-	function isOverAxis( x, reference, size ) {
-		return ( x >= reference ) && ( x < ( reference + size ) );
-	}
-
-	return function( draggable, droppable, toleranceMode, event ) {
-
-		if ( !droppable.offset ) {
-			return false;
-		}
-
-		var x1 = ( draggable.positionAbs ||
-				draggable.position.absolute ).left + draggable.margins.left,
-			y1 = ( draggable.positionAbs ||
-				draggable.position.absolute ).top + draggable.margins.top,
-			x2 = x1 + draggable.helperProportions.width,
-			y2 = y1 + draggable.helperProportions.height,
-			l = droppable.offset.left,
-			t = droppable.offset.top,
-			r = l + droppable.proportions().width,
-			b = t + droppable.proportions().height;
-
-		switch ( toleranceMode ) {
-		case "fit":
-			return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
-		case "intersect":
-			return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
-				x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
-				t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
-				y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
-		case "pointer":
-			return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
-				isOverAxis( event.pageX, l, droppable.proportions().width );
-		case "touch":
-			return (
-				( y1 >= t && y1 <= b ) || // Top edge touching
-				( y2 >= t && y2 <= b ) || // Bottom edge touching
-				( y1 < t && y2 > b ) // Surrounded vertically
-			) && (
-				( x1 >= l && x1 <= r ) || // Left edge touching
-				( x2 >= l && x2 <= r ) || // Right edge touching
-				( x1 < l && x2 > r ) // Surrounded horizontally
-			);
-		default:
-			return false;
-		}
-	};
-} )();
-
-/*
-	This manager tracks offsets of draggables and droppables
-*/
-$.ui.ddmanager = {
-	current: null,
-	droppables: { "default": [] },
-	prepareOffsets: function( t, event ) {
-
-		var i, j,
-			m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
-			type = event ? event.type : null, // workaround for #2317
-			list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
-
-		droppablesLoop: for ( i = 0; i < m.length; i++ ) {
-
-			// No disabled and non-accepted
-			if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
-					( t.currentItem || t.element ) ) ) ) {
-				continue;
-			}
-
-			// Filter out elements in the current dragged item
-			for ( j = 0; j < list.length; j++ ) {
-				if ( list[ j ] === m[ i ].element[ 0 ] ) {
-					m[ i ].proportions().height = 0;
-					continue droppablesLoop;
-				}
-			}
-
-			m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
-			if ( !m[ i ].visible ) {
-				continue;
-			}
-
-			// Activate the droppable if used directly from draggables
-			if ( type === "mousedown" ) {
-				m[ i ]._activate.call( m[ i ], event );
-			}
-
-			m[ i ].offset = m[ i ].element.offset();
-			m[ i ].proportions( {
-				width: m[ i ].element[ 0 ].offsetWidth,
-				height: m[ i ].element[ 0 ].offsetHeight
-			} );
-
-		}
-
-	},
-	drop: function( draggable, event ) {
-
-		var dropped = false;
-
-		// Create a copy of the droppables in case the list changes during the drop (#9116)
-		$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
-
-			if ( !this.options ) {
-				return;
-			}
-			if ( !this.options.disabled && this.visible &&
-					intersect( draggable, this, this.options.tolerance, event ) ) {
-				dropped = this._drop.call( this, event ) || dropped;
-			}
-
-			if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
-					( draggable.currentItem || draggable.element ) ) ) {
-				this.isout = true;
-				this.isover = false;
-				this._deactivate.call( this, event );
-			}
-
-		} );
-		return dropped;
-
-	},
-	dragStart: function( draggable, event ) {
-
-		// Listen for scrolling so that if the dragging causes scrolling the position of the
-		// droppables can be recalculated (see #5003)
-		draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
-			if ( !draggable.options.refreshPositions ) {
-				$.ui.ddmanager.prepareOffsets( draggable, event );
-			}
-		} );
-	},
-	drag: function( draggable, event ) {
-
-		// If you have a highly dynamic page, you might try this option. It renders positions
-		// every time you move the mouse.
-		if ( draggable.options.refreshPositions ) {
-			$.ui.ddmanager.prepareOffsets( draggable, event );
-		}
-
-		// Run through all droppables and check their positions based on specific tolerance options
-		$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
-
-			if ( this.options.disabled || this.greedyChild || !this.visible ) {
-				return;
-			}
-
-			var parentInstance, scope, parent,
-				intersects = intersect( draggable, this, this.options.tolerance, event ),
-				c = !intersects && this.isover ?
-					"isout" :
-					( intersects && !this.isover ? "isover" : null );
-			if ( !c ) {
-				return;
-			}
-
-			if ( this.options.greedy ) {
-
-				// find droppable parents with same scope
-				scope = this.options.scope;
-				parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
-					return $( this ).droppable( "instance" ).options.scope === scope;
-				} );
-
-				if ( parent.length ) {
-					parentInstance = $( parent[ 0 ] ).droppable( "instance" );
-					parentInstance.greedyChild = ( c === "isover" );
-				}
-			}
-
-			// We just moved into a greedy child
-			if ( parentInstance && c === "isover" ) {
-				parentInstance.isover = false;
-				parentInstance.isout = true;
-				parentInstance._out.call( parentInstance, event );
-			}
-
-			this[ c ] = true;
-			this[ c === "isout" ? "isover" : "isout" ] = false;
-			this[ c === "isover" ? "_over" : "_out" ].call( this, event );
-
-			// We just moved out of a greedy child
-			if ( parentInstance && c === "isout" ) {
-				parentInstance.isout = false;
-				parentInstance.isover = true;
-				parentInstance._over.call( parentInstance, event );
-			}
-		} );
-
-	},
-	dragStop: function( draggable, event ) {
-		draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );
-
-		// Call prepareOffsets one final time since IE does not fire return scroll events when
-		// overflow was caused by drag (see #5003)
-		if ( !draggable.options.refreshPositions ) {
-			$.ui.ddmanager.prepareOffsets( draggable, event );
-		}
-	}
-};
-
-// DEPRECATED
-// TODO: switch return back to widget declaration at top of file when this is removed
-if ( $.uiBackCompat !== false ) {
-
-	// Backcompat for activeClass and hoverClass options
-	$.widget( "ui.droppable", $.ui.droppable, {
-		options: {
-			hoverClass: false,
-			activeClass: false
-		},
-		_addActiveClass: function() {
-			this._super();
-			if ( this.options.activeClass ) {
-				this.element.addClass( this.options.activeClass );
-			}
-		},
-		_removeActiveClass: function() {
-			this._super();
-			if ( this.options.activeClass ) {
-				this.element.removeClass( this.options.activeClass );
-			}
-		},
-		_addHoverClass: function() {
-			this._super();
-			if ( this.options.hoverClass ) {
-				this.element.addClass( this.options.hoverClass );
-			}
-		},
-		_removeHoverClass: function() {
-			this._super();
-			if ( this.options.hoverClass ) {
-				this.element.removeClass( this.options.hoverClass );
-			}
-		}
-	} );
-}
-
-var widgetsDroppable = $.ui.droppable;
-
-
-/*!
- * jQuery UI Progressbar 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Progressbar
-//>>group: Widgets
-// jscs:disable maximumLineLength
-//>>description: Displays a status indicator for loading state, standard percentage, and other progress indicators.
-// jscs:enable maximumLineLength
-//>>docs: http://api.jqueryui.com/progressbar/
-//>>demos: http://jqueryui.com/progressbar/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/progressbar.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-var widgetsProgressbar = $.widget( "ui.progressbar", {
-	version: "1.12.1",
-	options: {
-		classes: {
-			"ui-progressbar": "ui-corner-all",
-			"ui-progressbar-value": "ui-corner-left",
-			"ui-progressbar-complete": "ui-corner-right"
-		},
-		max: 100,
-		value: 0,
-
-		change: null,
-		complete: null
-	},
-
-	min: 0,
-
-	_create: function() {
-
-		// Constrain initial value
-		this.oldValue = this.options.value = this._constrainedValue();
-
-		this.element.attr( {
-
-			// Only set static values; aria-valuenow and aria-valuemax are
-			// set inside _refreshValue()
-			role: "progressbar",
-			"aria-valuemin": this.min
-		} );
-		this._addClass( "ui-progressbar", "ui-widget ui-widget-content" );
-
-		this.valueDiv = $( "<div>" ).appendTo( this.element );
-		this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" );
-		this._refreshValue();
-	},
-
-	_destroy: function() {
-		this.element.removeAttr( "role aria-valuemin aria-valuemax aria-valuenow" );
-
-		this.valueDiv.remove();
-	},
-
-	value: function( newValue ) {
-		if ( newValue === undefined ) {
-			return this.options.value;
-		}
-
-		this.options.value = this._constrainedValue( newValue );
-		this._refreshValue();
-	},
-
-	_constrainedValue: function( newValue ) {
-		if ( newValue === undefined ) {
-			newValue = this.options.value;
-		}
-
-		this.indeterminate = newValue === false;
-
-		// Sanitize value
-		if ( typeof newValue !== "number" ) {
-			newValue = 0;
-		}
-
-		return this.indeterminate ? false :
-			Math.min( this.options.max, Math.max( this.min, newValue ) );
-	},
-
-	_setOptions: function( options ) {
-
-		// Ensure "value" option is set after other values (like max)
-		var value = options.value;
-		delete options.value;
-
-		this._super( options );
-
-		this.options.value = this._constrainedValue( value );
-		this._refreshValue();
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "max" ) {
-
-			// Don't allow a max less than min
-			value = Math.max( this.min, value );
-		}
-		this._super( key, value );
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this.element.attr( "aria-disabled", value );
-		this._toggleClass( null, "ui-state-disabled", !!value );
-	},
-
-	_percentage: function() {
-		return this.indeterminate ?
-			100 :
-			100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
-	},
-
-	_refreshValue: function() {
-		var value = this.options.value,
-			percentage = this._percentage();
-
-		this.valueDiv
-			.toggle( this.indeterminate || value > this.min )
-			.width( percentage.toFixed( 0 ) + "%" );
-
-		this
-			._toggleClass( this.valueDiv, "ui-progressbar-complete", null,
-				value === this.options.max )
-			._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate );
-
-		if ( this.indeterminate ) {
-			this.element.removeAttr( "aria-valuenow" );
-			if ( !this.overlayDiv ) {
-				this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv );
-				this._addClass( this.overlayDiv, "ui-progressbar-overlay" );
-			}
-		} else {
-			this.element.attr( {
-				"aria-valuemax": this.options.max,
-				"aria-valuenow": value
-			} );
-			if ( this.overlayDiv ) {
-				this.overlayDiv.remove();
-				this.overlayDiv = null;
-			}
-		}
-
-		if ( this.oldValue !== value ) {
-			this.oldValue = value;
-			this._trigger( "change" );
-		}
-		if ( value === this.options.max ) {
-			this._trigger( "complete" );
-		}
-	}
-} );
-
-
-/*!
- * jQuery UI Selectable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Selectable
-//>>group: Interactions
-//>>description: Allows groups of elements to be selected with the mouse.
-//>>docs: http://api.jqueryui.com/selectable/
-//>>demos: http://jqueryui.com/selectable/
-//>>css.structure: ../../themes/base/selectable.css
-
-
-
-var widgetsSelectable = $.widget( "ui.selectable", $.ui.mouse, {
-	version: "1.12.1",
-	options: {
-		appendTo: "body",
-		autoRefresh: true,
-		distance: 0,
-		filter: "*",
-		tolerance: "touch",
-
-		// Callbacks
-		selected: null,
-		selecting: null,
-		start: null,
-		stop: null,
-		unselected: null,
-		unselecting: null
-	},
-	_create: function() {
-		var that = this;
-
-		this._addClass( "ui-selectable" );
-
-		this.dragged = false;
-
-		// Cache selectee children based on filter
-		this.refresh = function() {
-			that.elementPos = $( that.element[ 0 ] ).offset();
-			that.selectees = $( that.options.filter, that.element[ 0 ] );
-			that._addClass( that.selectees, "ui-selectee" );
-			that.selectees.each( function() {
-				var $this = $( this ),
-					selecteeOffset = $this.offset(),
-					pos = {
-						left: selecteeOffset.left - that.elementPos.left,
-						top: selecteeOffset.top - that.elementPos.top
-					};
-				$.data( this, "selectable-item", {
-					element: this,
-					$element: $this,
-					left: pos.left,
-					top: pos.top,
-					right: pos.left + $this.outerWidth(),
-					bottom: pos.top + $this.outerHeight(),
-					startselected: false,
-					selected: $this.hasClass( "ui-selected" ),
-					selecting: $this.hasClass( "ui-selecting" ),
-					unselecting: $this.hasClass( "ui-unselecting" )
-				} );
-			} );
-		};
-		this.refresh();
-
-		this._mouseInit();
-
-		this.helper = $( "<div>" );
-		this._addClass( this.helper, "ui-selectable-helper" );
-	},
-
-	_destroy: function() {
-		this.selectees.removeData( "selectable-item" );
-		this._mouseDestroy();
-	},
-
-	_mouseStart: function( event ) {
-		var that = this,
-			options = this.options;
-
-		this.opos = [ event.pageX, event.pageY ];
-		this.elementPos = $( this.element[ 0 ] ).offset();
-
-		if ( this.options.disabled ) {
-			return;
-		}
-
-		this.selectees = $( options.filter, this.element[ 0 ] );
-
-		this._trigger( "start", event );
-
-		$( options.appendTo ).append( this.helper );
-
-		// position helper (lasso)
-		this.helper.css( {
-			"left": event.pageX,
-			"top": event.pageY,
-			"width": 0,
-			"height": 0
-		} );
-
-		if ( options.autoRefresh ) {
-			this.refresh();
-		}
-
-		this.selectees.filter( ".ui-selected" ).each( function() {
-			var selectee = $.data( this, "selectable-item" );
-			selectee.startselected = true;
-			if ( !event.metaKey && !event.ctrlKey ) {
-				that._removeClass( selectee.$element, "ui-selected" );
-				selectee.selected = false;
-				that._addClass( selectee.$element, "ui-unselecting" );
-				selectee.unselecting = true;
-
-				// selectable UNSELECTING callback
-				that._trigger( "unselecting", event, {
-					unselecting: selectee.element
-				} );
-			}
-		} );
-
-		$( event.target ).parents().addBack().each( function() {
-			var doSelect,
-				selectee = $.data( this, "selectable-item" );
-			if ( selectee ) {
-				doSelect = ( !event.metaKey && !event.ctrlKey ) ||
-					!selectee.$element.hasClass( "ui-selected" );
-				that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" )
-					._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" );
-				selectee.unselecting = !doSelect;
-				selectee.selecting = doSelect;
-				selectee.selected = doSelect;
-
-				// selectable (UN)SELECTING callback
-				if ( doSelect ) {
-					that._trigger( "selecting", event, {
-						selecting: selectee.element
-					} );
-				} else {
-					that._trigger( "unselecting", event, {
-						unselecting: selectee.element
-					} );
-				}
-				return false;
-			}
-		} );
-
-	},
-
-	_mouseDrag: function( event ) {
-
-		this.dragged = true;
-
-		if ( this.options.disabled ) {
-			return;
-		}
-
-		var tmp,
-			that = this,
-			options = this.options,
-			x1 = this.opos[ 0 ],
-			y1 = this.opos[ 1 ],
-			x2 = event.pageX,
-			y2 = event.pageY;
-
-		if ( x1 > x2 ) { tmp = x2; x2 = x1; x1 = tmp; }
-		if ( y1 > y2 ) { tmp = y2; y2 = y1; y1 = tmp; }
-		this.helper.css( { left: x1, top: y1, width: x2 - x1, height: y2 - y1 } );
-
-		this.selectees.each( function() {
-			var selectee = $.data( this, "selectable-item" ),
-				hit = false,
-				offset = {};
-
-			//prevent helper from being selected if appendTo: selectable
-			if ( !selectee || selectee.element === that.element[ 0 ] ) {
-				return;
-			}
-
-			offset.left   = selectee.left   + that.elementPos.left;
-			offset.right  = selectee.right  + that.elementPos.left;
-			offset.top    = selectee.top    + that.elementPos.top;
-			offset.bottom = selectee.bottom + that.elementPos.top;
-
-			if ( options.tolerance === "touch" ) {
-				hit = ( !( offset.left > x2 || offset.right < x1 || offset.top > y2 ||
-                    offset.bottom < y1 ) );
-			} else if ( options.tolerance === "fit" ) {
-				hit = ( offset.left > x1 && offset.right < x2 && offset.top > y1 &&
-                    offset.bottom < y2 );
-			}
-
-			if ( hit ) {
-
-				// SELECT
-				if ( selectee.selected ) {
-					that._removeClass( selectee.$element, "ui-selected" );
-					selectee.selected = false;
-				}
-				if ( selectee.unselecting ) {
-					that._removeClass( selectee.$element, "ui-unselecting" );
-					selectee.unselecting = false;
-				}
-				if ( !selectee.selecting ) {
-					that._addClass( selectee.$element, "ui-selecting" );
-					selectee.selecting = true;
-
-					// selectable SELECTING callback
-					that._trigger( "selecting", event, {
-						selecting: selectee.element
-					} );
-				}
-			} else {
-
-				// UNSELECT
-				if ( selectee.selecting ) {
-					if ( ( event.metaKey || event.ctrlKey ) && selectee.startselected ) {
-						that._removeClass( selectee.$element, "ui-selecting" );
-						selectee.selecting = false;
-						that._addClass( selectee.$element, "ui-selected" );
-						selectee.selected = true;
-					} else {
-						that._removeClass( selectee.$element, "ui-selecting" );
-						selectee.selecting = false;
-						if ( selectee.startselected ) {
-							that._addClass( selectee.$element, "ui-unselecting" );
-							selectee.unselecting = true;
-						}
-
-						// selectable UNSELECTING callback
-						that._trigger( "unselecting", event, {
-							unselecting: selectee.element
-						} );
-					}
-				}
-				if ( selectee.selected ) {
-					if ( !event.metaKey && !event.ctrlKey && !selectee.startselected ) {
-						that._removeClass( selectee.$element, "ui-selected" );
-						selectee.selected = false;
-
-						that._addClass( selectee.$element, "ui-unselecting" );
-						selectee.unselecting = true;
-
-						// selectable UNSELECTING callback
-						that._trigger( "unselecting", event, {
-							unselecting: selectee.element
-						} );
-					}
-				}
-			}
-		} );
-
-		return false;
-	},
-
-	_mouseStop: function( event ) {
-		var that = this;
-
-		this.dragged = false;
-
-		$( ".ui-unselecting", this.element[ 0 ] ).each( function() {
-			var selectee = $.data( this, "selectable-item" );
-			that._removeClass( selectee.$element, "ui-unselecting" );
-			selectee.unselecting = false;
-			selectee.startselected = false;
-			that._trigger( "unselected", event, {
-				unselected: selectee.element
-			} );
-		} );
-		$( ".ui-selecting", this.element[ 0 ] ).each( function() {
-			var selectee = $.data( this, "selectable-item" );
-			that._removeClass( selectee.$element, "ui-selecting" )
-				._addClass( selectee.$element, "ui-selected" );
-			selectee.selecting = false;
-			selectee.selected = true;
-			selectee.startselected = true;
-			that._trigger( "selected", event, {
-				selected: selectee.element
-			} );
-		} );
-		this._trigger( "stop", event );
-
-		this.helper.remove();
-
-		return false;
-	}
-
-} );
-
-
-/*!
- * jQuery UI Selectmenu 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Selectmenu
-//>>group: Widgets
-// jscs:disable maximumLineLength
-//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
-// jscs:enable maximumLineLength
-//>>docs: http://api.jqueryui.com/selectmenu/
-//>>demos: http://jqueryui.com/selectmenu/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-var widgetsSelectmenu = $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
-	version: "1.12.1",
-	defaultElement: "<select>",
-	options: {
-		appendTo: null,
-		classes: {
-			"ui-selectmenu-button-open": "ui-corner-top",
-			"ui-selectmenu-button-closed": "ui-corner-all"
-		},
-		disabled: null,
-		icons: {
-			button: "ui-icon-triangle-1-s"
-		},
-		position: {
-			my: "left top",
-			at: "left bottom",
-			collision: "none"
-		},
-		width: false,
-
-		// Callbacks
-		change: null,
-		close: null,
-		focus: null,
-		open: null,
-		select: null
-	},
-
-	_create: function() {
-		var selectmenuId = this.element.uniqueId().attr( "id" );
-		this.ids = {
-			element: selectmenuId,
-			button: selectmenuId + "-button",
-			menu: selectmenuId + "-menu"
-		};
-
-		this._drawButton();
-		this._drawMenu();
-		this._bindFormResetHandler();
-
-		this._rendered = false;
-		this.menuItems = $();
-	},
-
-	_drawButton: function() {
-		var icon,
-			that = this,
-			item = this._parseOption(
-				this.element.find( "option:selected" ),
-				this.element[ 0 ].selectedIndex
-			);
-
-		// Associate existing label with the new button
-		this.labels = this.element.labels().attr( "for", this.ids.button );
-		this._on( this.labels, {
-			click: function( event ) {
-				this.button.focus();
-				event.preventDefault();
-			}
-		} );
-
-		// Hide original select element
-		this.element.hide();
-
-		// Create button
-		this.button = $( "<span>", {
-			tabindex: this.options.disabled ? -1 : 0,
-			id: this.ids.button,
-			role: "combobox",
-			"aria-expanded": "false",
-			"aria-autocomplete": "list",
-			"aria-owns": this.ids.menu,
-			"aria-haspopup": "true",
-			title: this.element.attr( "title" )
-		} )
-			.insertAfter( this.element );
-
-		this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed",
-			"ui-button ui-widget" );
-
-		icon = $( "<span>" ).appendTo( this.button );
-		this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button );
-		this.buttonItem = this._renderButtonItem( item )
-			.appendTo( this.button );
-
-		if ( this.options.width !== false ) {
-			this._resizeButton();
-		}
-
-		this._on( this.button, this._buttonEvents );
-		this.button.one( "focusin", function() {
-
-			// Delay rendering the menu items until the button receives focus.
-			// The menu may have already been rendered via a programmatic open.
-			if ( !that._rendered ) {
-				that._refreshMenu();
-			}
-		} );
-	},
-
-	_drawMenu: function() {
-		var that = this;
-
-		// Create menu
-		this.menu = $( "<ul>", {
-			"aria-hidden": "true",
-			"aria-labelledby": this.ids.button,
-			id: this.ids.menu
-		} );
-
-		// Wrap menu
-		this.menuWrap = $( "<div>" ).append( this.menu );
-		this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" );
-		this.menuWrap.appendTo( this._appendTo() );
-
-		// Initialize menu widget
-		this.menuInstance = this.menu
-			.menu( {
-				classes: {
-					"ui-menu": "ui-corner-bottom"
-				},
-				role: "listbox",
-				select: function( event, ui ) {
-					event.preventDefault();
-
-					// Support: IE8
-					// If the item was selected via a click, the text selection
-					// will be destroyed in IE
-					that._setSelection();
-
-					that._select( ui.item.data( "ui-selectmenu-item" ), event );
-				},
-				focus: function( event, ui ) {
-					var item = ui.item.data( "ui-selectmenu-item" );
-
-					// Prevent inital focus from firing and check if its a newly focused item
-					if ( that.focusIndex != null && item.index !== that.focusIndex ) {
-						that._trigger( "focus", event, { item: item } );
-						if ( !that.isOpen ) {
-							that._select( item, event );
-						}
-					}
-					that.focusIndex = item.index;
-
-					that.button.attr( "aria-activedescendant",
-						that.menuItems.eq( item.index ).attr( "id" ) );
-				}
-			} )
-			.menu( "instance" );
-
-		// Don't close the menu on mouseleave
-		this.menuInstance._off( this.menu, "mouseleave" );
-
-		// Cancel the menu's collapseAll on document click
-		this.menuInstance._closeOnDocumentClick = function() {
-			return false;
-		};
-
-		// Selects often contain empty items, but never contain dividers
-		this.menuInstance._isDivider = function() {
-			return false;
-		};
-	},
-
-	refresh: function() {
-		this._refreshMenu();
-		this.buttonItem.replaceWith(
-			this.buttonItem = this._renderButtonItem(
-
-				// Fall back to an empty object in case there are no options
-				this._getSelectedItem().data( "ui-selectmenu-item" ) || {}
-			)
-		);
-		if ( this.options.width === null ) {
-			this._resizeButton();
-		}
-	},
-
-	_refreshMenu: function() {
-		var item,
-			options = this.element.find( "option" );
-
-		this.menu.empty();
-
-		this._parseOptions( options );
-		this._renderMenu( this.menu, this.items );
-
-		this.menuInstance.refresh();
-		this.menuItems = this.menu.find( "li" )
-			.not( ".ui-selectmenu-optgroup" )
-				.find( ".ui-menu-item-wrapper" );
-
-		this._rendered = true;
-
-		if ( !options.length ) {
-			return;
-		}
-
-		item = this._getSelectedItem();
-
-		// Update the menu to have the correct item focused
-		this.menuInstance.focus( null, item );
-		this._setAria( item.data( "ui-selectmenu-item" ) );
-
-		// Set disabled state
-		this._setOption( "disabled", this.element.prop( "disabled" ) );
-	},
-
-	open: function( event ) {
-		if ( this.options.disabled ) {
-			return;
-		}
-
-		// If this is the first time the menu is being opened, render the items
-		if ( !this._rendered ) {
-			this._refreshMenu();
-		} else {
-
-			// Menu clears focus on close, reset focus to selected item
-			this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" );
-			this.menuInstance.focus( null, this._getSelectedItem() );
-		}
-
-		// If there are no options, don't open the menu
-		if ( !this.menuItems.length ) {
-			return;
-		}
-
-		this.isOpen = true;
-		this._toggleAttr();
-		this._resizeMenu();
-		this._position();
-
-		this._on( this.document, this._documentClick );
-
-		this._trigger( "open", event );
-	},
-
-	_position: function() {
-		this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
-	},
-
-	close: function( event ) {
-		if ( !this.isOpen ) {
-			return;
-		}
-
-		this.isOpen = false;
-		this._toggleAttr();
-
-		this.range = null;
-		this._off( this.document );
-
-		this._trigger( "close", event );
-	},
-
-	widget: function() {
-		return this.button;
-	},
-
-	menuWidget: function() {
-		return this.menu;
-	},
-
-	_renderButtonItem: function( item ) {
-		var buttonItem = $( "<span>" );
-
-		this._setText( buttonItem, item.label );
-		this._addClass( buttonItem, "ui-selectmenu-text" );
-
-		return buttonItem;
-	},
-
-	_renderMenu: function( ul, items ) {
-		var that = this,
-			currentOptgroup = "";
-
-		$.each( items, function( index, item ) {
-			var li;
-
-			if ( item.optgroup !== currentOptgroup ) {
-				li = $( "<li>", {
-					text: item.optgroup
-				} );
-				that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" +
-					( item.element.parent( "optgroup" ).prop( "disabled" ) ?
-						" ui-state-disabled" :
-						"" ) );
-
-				li.appendTo( ul );
-
-				currentOptgroup = item.optgroup;
-			}
-
-			that._renderItemData( ul, item );
-		} );
-	},
-
-	_renderItemData: function( ul, item ) {
-		return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
-	},
-
-	_renderItem: function( ul, item ) {
-		var li = $( "<li>" ),
-			wrapper = $( "<div>", {
-				title: item.element.attr( "title" )
-			} );
-
-		if ( item.disabled ) {
-			this._addClass( li, null, "ui-state-disabled" );
-		}
-		this._setText( wrapper, item.label );
-
-		return li.append( wrapper ).appendTo( ul );
-	},
-
-	_setText: function( element, value ) {
-		if ( value ) {
-			element.text( value );
-		} else {
-			element.html( "&#160;" );
-		}
-	},
-
-	_move: function( direction, event ) {
-		var item, next,
-			filter = ".ui-menu-item";
-
-		if ( this.isOpen ) {
-			item = this.menuItems.eq( this.focusIndex ).parent( "li" );
-		} else {
-			item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
-			filter += ":not(.ui-state-disabled)";
-		}
-
-		if ( direction === "first" || direction === "last" ) {
-			next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
-		} else {
-			next = item[ direction + "All" ]( filter ).eq( 0 );
-		}
-
-		if ( next.length ) {
-			this.menuInstance.focus( event, next );
-		}
-	},
-
-	_getSelectedItem: function() {
-		return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
-	},
-
-	_toggle: function( event ) {
-		this[ this.isOpen ? "close" : "open" ]( event );
-	},
-
-	_setSelection: function() {
-		var selection;
-
-		if ( !this.range ) {
-			return;
-		}
-
-		if ( window.getSelection ) {
-			selection = window.getSelection();
-			selection.removeAllRanges();
-			selection.addRange( this.range );
-
-		// Support: IE8
-		} else {
-			this.range.select();
-		}
-
-		// Support: IE
-		// Setting the text selection kills the button focus in IE, but
-		// restoring the focus doesn't kill the selection.
-		this.button.focus();
-	},
-
-	_documentClick: {
-		mousedown: function( event ) {
-			if ( !this.isOpen ) {
-				return;
-			}
-
-			if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
-					$.ui.escapeSelector( this.ids.button ) ).length ) {
-				this.close( event );
-			}
-		}
-	},
-
-	_buttonEvents: {
-
-		// Prevent text selection from being reset when interacting with the selectmenu (#10144)
-		mousedown: function() {
-			var selection;
-
-			if ( window.getSelection ) {
-				selection = window.getSelection();
-				if ( selection.rangeCount ) {
-					this.range = selection.getRangeAt( 0 );
-				}
-
-			// Support: IE8
-			} else {
-				this.range = document.selection.createRange();
-			}
-		},
-
-		click: function( event ) {
-			this._setSelection();
-			this._toggle( event );
-		},
-
-		keydown: function( event ) {
-			var preventDefault = true;
-			switch ( event.keyCode ) {
-			case $.ui.keyCode.TAB:
-			case $.ui.keyCode.ESCAPE:
-				this.close( event );
-				preventDefault = false;
-				break;
-			case $.ui.keyCode.ENTER:
-				if ( this.isOpen ) {
-					this._selectFocusedItem( event );
-				}
-				break;
-			case $.ui.keyCode.UP:
-				if ( event.altKey ) {
-					this._toggle( event );
-				} else {
-					this._move( "prev", event );
-				}
-				break;
-			case $.ui.keyCode.DOWN:
-				if ( event.altKey ) {
-					this._toggle( event );
-				} else {
-					this._move( "next", event );
-				}
-				break;
-			case $.ui.keyCode.SPACE:
-				if ( this.isOpen ) {
-					this._selectFocusedItem( event );
-				} else {
-					this._toggle( event );
-				}
-				break;
-			case $.ui.keyCode.LEFT:
-				this._move( "prev", event );
-				break;
-			case $.ui.keyCode.RIGHT:
-				this._move( "next", event );
-				break;
-			case $.ui.keyCode.HOME:
-			case $.ui.keyCode.PAGE_UP:
-				this._move( "first", event );
-				break;
-			case $.ui.keyCode.END:
-			case $.ui.keyCode.PAGE_DOWN:
-				this._move( "last", event );
-				break;
-			default:
-				this.menu.trigger( event );
-				preventDefault = false;
-			}
-
-			if ( preventDefault ) {
-				event.preventDefault();
-			}
-		}
-	},
-
-	_selectFocusedItem: function( event ) {
-		var item = this.menuItems.eq( this.focusIndex ).parent( "li" );
-		if ( !item.hasClass( "ui-state-disabled" ) ) {
-			this._select( item.data( "ui-selectmenu-item" ), event );
-		}
-	},
-
-	_select: function( item, event ) {
-		var oldIndex = this.element[ 0 ].selectedIndex;
-
-		// Change native select element
-		this.element[ 0 ].selectedIndex = item.index;
-		this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) );
-		this._setAria( item );
-		this._trigger( "select", event, { item: item } );
-
-		if ( item.index !== oldIndex ) {
-			this._trigger( "change", event, { item: item } );
-		}
-
-		this.close( event );
-	},
-
-	_setAria: function( item ) {
-		var id = this.menuItems.eq( item.index ).attr( "id" );
-
-		this.button.attr( {
-			"aria-labelledby": id,
-			"aria-activedescendant": id
-		} );
-		this.menu.attr( "aria-activedescendant", id );
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "icons" ) {
-			var icon = this.button.find( "span.ui-icon" );
-			this._removeClass( icon, null, this.options.icons.button )
-				._addClass( icon, null, value.button );
-		}
-
-		this._super( key, value );
-
-		if ( key === "appendTo" ) {
-			this.menuWrap.appendTo( this._appendTo() );
-		}
-
-		if ( key === "width" ) {
-			this._resizeButton();
-		}
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this.menuInstance.option( "disabled", value );
-		this.button.attr( "aria-disabled", value );
-		this._toggleClass( this.button, null, "ui-state-disabled", value );
-
-		this.element.prop( "disabled", value );
-		if ( value ) {
-			this.button.attr( "tabindex", -1 );
-			this.close();
-		} else {
-			this.button.attr( "tabindex", 0 );
-		}
-	},
-
-	_appendTo: function() {
-		var element = this.options.appendTo;
-
-		if ( element ) {
-			element = element.jquery || element.nodeType ?
-				$( element ) :
-				this.document.find( element ).eq( 0 );
-		}
-
-		if ( !element || !element[ 0 ] ) {
-			element = this.element.closest( ".ui-front, dialog" );
-		}
-
-		if ( !element.length ) {
-			element = this.document[ 0 ].body;
-		}
-
-		return element;
-	},
-
-	_toggleAttr: function() {
-		this.button.attr( "aria-expanded", this.isOpen );
-
-		// We can't use two _toggleClass() calls here, because we need to make sure
-		// we always remove classes first and add them second, otherwise if both classes have the
-		// same theme class, it will be removed after we add it.
-		this._removeClass( this.button, "ui-selectmenu-button-" +
-			( this.isOpen ? "closed" : "open" ) )
-			._addClass( this.button, "ui-selectmenu-button-" +
-				( this.isOpen ? "open" : "closed" ) )
-			._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen );
-
-		this.menu.attr( "aria-hidden", !this.isOpen );
-	},
-
-	_resizeButton: function() {
-		var width = this.options.width;
-
-		// For `width: false`, just remove inline style and stop
-		if ( width === false ) {
-			this.button.css( "width", "" );
-			return;
-		}
-
-		// For `width: null`, match the width of the original element
-		if ( width === null ) {
-			width = this.element.show().outerWidth();
-			this.element.hide();
-		}
-
-		this.button.outerWidth( width );
-	},
-
-	_resizeMenu: function() {
-		this.menu.outerWidth( Math.max(
-			this.button.outerWidth(),
-
-			// Support: IE10
-			// IE10 wraps long text (possibly a rounding bug)
-			// so we add 1px to avoid the wrapping
-			this.menu.width( "" ).outerWidth() + 1
-		) );
-	},
-
-	_getCreateOptions: function() {
-		var options = this._super();
-
-		options.disabled = this.element.prop( "disabled" );
-
-		return options;
-	},
-
-	_parseOptions: function( options ) {
-		var that = this,
-			data = [];
-		options.each( function( index, item ) {
-			data.push( that._parseOption( $( item ), index ) );
-		} );
-		this.items = data;
-	},
-
-	_parseOption: function( option, index ) {
-		var optgroup = option.parent( "optgroup" );
-
-		return {
-			element: option,
-			index: index,
-			value: option.val(),
-			label: option.text(),
-			optgroup: optgroup.attr( "label" ) || "",
-			disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
-		};
-	},
-
-	_destroy: function() {
-		this._unbindFormResetHandler();
-		this.menuWrap.remove();
-		this.button.remove();
-		this.element.show();
-		this.element.removeUniqueId();
-		this.labels.attr( "for", this.ids.element );
-	}
-} ] );
-
-
-/*!
- * jQuery UI Slider 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Slider
-//>>group: Widgets
-//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
-//>>docs: http://api.jqueryui.com/slider/
-//>>demos: http://jqueryui.com/slider/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/slider.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, {
-	version: "1.12.1",
-	widgetEventPrefix: "slide",
-
-	options: {
-		animate: false,
-		classes: {
-			"ui-slider": "ui-corner-all",
-			"ui-slider-handle": "ui-corner-all",
-
-			// Note: ui-widget-header isn't the most fittingly semantic framework class for this
-			// element, but worked best visually with a variety of themes
-			"ui-slider-range": "ui-corner-all ui-widget-header"
-		},
-		distance: 0,
-		max: 100,
-		min: 0,
-		orientation: "horizontal",
-		range: false,
-		step: 1,
-		value: 0,
-		values: null,
-
-		// Callbacks
-		change: null,
-		slide: null,
-		start: null,
-		stop: null
-	},
-
-	// Number of pages in a slider
-	// (how many times can you page up/down to go through the whole range)
-	numPages: 5,
-
-	_create: function() {
-		this._keySliding = false;
-		this._mouseSliding = false;
-		this._animateOff = true;
-		this._handleIndex = null;
-		this._detectOrientation();
-		this._mouseInit();
-		this._calculateNewMax();
-
-		this._addClass( "ui-slider ui-slider-" + this.orientation,
-			"ui-widget ui-widget-content" );
-
-		this._refresh();
-
-		this._animateOff = false;
-	},
-
-	_refresh: function() {
-		this._createRange();
-		this._createHandles();
-		this._setupEvents();
-		this._refreshValue();
-	},
-
-	_createHandles: function() {
-		var i, handleCount,
-			options = this.options,
-			existingHandles = this.element.find( ".ui-slider-handle" ),
-			handle = "<span tabindex='0'></span>",
-			handles = [];
-
-		handleCount = ( options.values && options.values.length ) || 1;
-
-		if ( existingHandles.length > handleCount ) {
-			existingHandles.slice( handleCount ).remove();
-			existingHandles = existingHandles.slice( 0, handleCount );
-		}
-
-		for ( i = existingHandles.length; i < handleCount; i++ ) {
-			handles.push( handle );
-		}
-
-		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
-
-		this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );
-
-		this.handle = this.handles.eq( 0 );
-
-		this.handles.each( function( i ) {
-			$( this )
-				.data( "ui-slider-handle-index", i )
-				.attr( "tabIndex", 0 );
-		} );
-	},
-
-	_createRange: function() {
-		var options = this.options;
-
-		if ( options.range ) {
-			if ( options.range === true ) {
-				if ( !options.values ) {
-					options.values = [ this._valueMin(), this._valueMin() ];
-				} else if ( options.values.length && options.values.length !== 2 ) {
-					options.values = [ options.values[ 0 ], options.values[ 0 ] ];
-				} else if ( $.isArray( options.values ) ) {
-					options.values = options.values.slice( 0 );
-				}
-			}
-
-			if ( !this.range || !this.range.length ) {
-				this.range = $( "<div>" )
-					.appendTo( this.element );
-
-				this._addClass( this.range, "ui-slider-range" );
-			} else {
-				this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );
-
-				// Handle range switching from true to min/max
-				this.range.css( {
-					"left": "",
-					"bottom": ""
-				} );
-			}
-			if ( options.range === "min" || options.range === "max" ) {
-				this._addClass( this.range, "ui-slider-range-" + options.range );
-			}
-		} else {
-			if ( this.range ) {
-				this.range.remove();
-			}
-			this.range = null;
-		}
-	},
-
-	_setupEvents: function() {
-		this._off( this.handles );
-		this._on( this.handles, this._handleEvents );
-		this._hoverable( this.handles );
-		this._focusable( this.handles );
-	},
-
-	_destroy: function() {
-		this.handles.remove();
-		if ( this.range ) {
-			this.range.remove();
-		}
-
-		this._mouseDestroy();
-	},
-
-	_mouseCapture: function( event ) {
-		var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
-			that = this,
-			o = this.options;
-
-		if ( o.disabled ) {
-			return false;
-		}
-
-		this.elementSize = {
-			width: this.element.outerWidth(),
-			height: this.element.outerHeight()
-		};
-		this.elementOffset = this.element.offset();
-
-		position = { x: event.pageX, y: event.pageY };
-		normValue = this._normValueFromMouse( position );
-		distance = this._valueMax() - this._valueMin() + 1;
-		this.handles.each( function( i ) {
-			var thisDistance = Math.abs( normValue - that.values( i ) );
-			if ( ( distance > thisDistance ) ||
-				( distance === thisDistance &&
-					( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
-				distance = thisDistance;
-				closestHandle = $( this );
-				index = i;
-			}
-		} );
-
-		allowed = this._start( event, index );
-		if ( allowed === false ) {
-			return false;
-		}
-		this._mouseSliding = true;
-
-		this._handleIndex = index;
-
-		this._addClass( closestHandle, null, "ui-state-active" );
-		closestHandle.trigger( "focus" );
-
-		offset = closestHandle.offset();
-		mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
-		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
-			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
-			top: event.pageY - offset.top -
-				( closestHandle.height() / 2 ) -
-				( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
-				( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
-				( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
-		};
-
-		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
-			this._slide( event, index, normValue );
-		}
-		this._animateOff = true;
-		return true;
-	},
-
-	_mouseStart: function() {
-		return true;
-	},
-
-	_mouseDrag: function( event ) {
-		var position = { x: event.pageX, y: event.pageY },
-			normValue = this._normValueFromMouse( position );
-
-		this._slide( event, this._handleIndex, normValue );
-
-		return false;
-	},
-
-	_mouseStop: function( event ) {
-		this._removeClass( this.handles, null, "ui-state-active" );
-		this._mouseSliding = false;
-
-		this._stop( event, this._handleIndex );
-		this._change( event, this._handleIndex );
-
-		this._handleIndex = null;
-		this._clickOffset = null;
-		this._animateOff = false;
-
-		return false;
-	},
-
-	_detectOrientation: function() {
-		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
-	},
-
-	_normValueFromMouse: function( position ) {
-		var pixelTotal,
-			pixelMouse,
-			percentMouse,
-			valueTotal,
-			valueMouse;
-
-		if ( this.orientation === "horizontal" ) {
-			pixelTotal = this.elementSize.width;
-			pixelMouse = position.x - this.elementOffset.left -
-				( this._clickOffset ? this._clickOffset.left : 0 );
-		} else {
-			pixelTotal = this.elementSize.height;
-			pixelMouse = position.y - this.elementOffset.top -
-				( this._clickOffset ? this._clickOffset.top : 0 );
-		}
-
-		percentMouse = ( pixelMouse / pixelTotal );
-		if ( percentMouse > 1 ) {
-			percentMouse = 1;
-		}
-		if ( percentMouse < 0 ) {
-			percentMouse = 0;
-		}
-		if ( this.orientation === "vertical" ) {
-			percentMouse = 1 - percentMouse;
-		}
-
-		valueTotal = this._valueMax() - this._valueMin();
-		valueMouse = this._valueMin() + percentMouse * valueTotal;
-
-		return this._trimAlignValue( valueMouse );
-	},
-
-	_uiHash: function( index, value, values ) {
-		var uiHash = {
-			handle: this.handles[ index ],
-			handleIndex: index,
-			value: value !== undefined ? value : this.value()
-		};
-
-		if ( this._hasMultipleValues() ) {
-			uiHash.value = value !== undefined ? value : this.values( index );
-			uiHash.values = values || this.values();
-		}
-
-		return uiHash;
-	},
-
-	_hasMultipleValues: function() {
-		return this.options.values && this.options.values.length;
-	},
-
-	_start: function( event, index ) {
-		return this._trigger( "start", event, this._uiHash( index ) );
-	},
-
-	_slide: function( event, index, newVal ) {
-		var allowed, otherVal,
-			currentValue = this.value(),
-			newValues = this.values();
-
-		if ( this._hasMultipleValues() ) {
-			otherVal = this.values( index ? 0 : 1 );
-			currentValue = this.values( index );
-
-			if ( this.options.values.length === 2 && this.options.range === true ) {
-				newVal =  index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
-			}
-
-			newValues[ index ] = newVal;
-		}
-
-		if ( newVal === currentValue ) {
-			return;
-		}
-
-		allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );
-
-		// A slide can be canceled by returning false from the slide callback
-		if ( allowed === false ) {
-			return;
-		}
-
-		if ( this._hasMultipleValues() ) {
-			this.values( index, newVal );
-		} else {
-			this.value( newVal );
-		}
-	},
-
-	_stop: function( event, index ) {
-		this._trigger( "stop", event, this._uiHash( index ) );
-	},
-
-	_change: function( event, index ) {
-		if ( !this._keySliding && !this._mouseSliding ) {
-
-			//store the last changed value index for reference when handles overlap
-			this._lastChangedValue = index;
-			this._trigger( "change", event, this._uiHash( index ) );
-		}
-	},
-
-	value: function( newValue ) {
-		if ( arguments.length ) {
-			this.options.value = this._trimAlignValue( newValue );
-			this._refreshValue();
-			this._change( null, 0 );
-			return;
-		}
-
-		return this._value();
-	},
-
-	values: function( index, newValue ) {
-		var vals,
-			newValues,
-			i;
-
-		if ( arguments.length > 1 ) {
-			this.options.values[ index ] = this._trimAlignValue( newValue );
-			this._refreshValue();
-			this._change( null, index );
-			return;
-		}
-
-		if ( arguments.length ) {
-			if ( $.isArray( arguments[ 0 ] ) ) {
-				vals = this.options.values;
-				newValues = arguments[ 0 ];
-				for ( i = 0; i < vals.length; i += 1 ) {
-					vals[ i ] = this._trimAlignValue( newValues[ i ] );
-					this._change( null, i );
-				}
-				this._refreshValue();
-			} else {
-				if ( this._hasMultipleValues() ) {
-					return this._values( index );
-				} else {
-					return this.value();
-				}
-			}
-		} else {
-			return this._values();
-		}
-	},
-
-	_setOption: function( key, value ) {
-		var i,
-			valsLength = 0;
-
-		if ( key === "range" && this.options.range === true ) {
-			if ( value === "min" ) {
-				this.options.value = this._values( 0 );
-				this.options.values = null;
-			} else if ( value === "max" ) {
-				this.options.value = this._values( this.options.values.length - 1 );
-				this.options.values = null;
-			}
-		}
-
-		if ( $.isArray( this.options.values ) ) {
-			valsLength = this.options.values.length;
-		}
-
-		this._super( key, value );
-
-		switch ( key ) {
-			case "orientation":
-				this._detectOrientation();
-				this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
-					._addClass( "ui-slider-" + this.orientation );
-				this._refreshValue();
-				if ( this.options.range ) {
-					this._refreshRange( value );
-				}
-
-				// Reset positioning from previous orientation
-				this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
-				break;
-			case "value":
-				this._animateOff = true;
-				this._refreshValue();
-				this._change( null, 0 );
-				this._animateOff = false;
-				break;
-			case "values":
-				this._animateOff = true;
-				this._refreshValue();
-
-				// Start from the last handle to prevent unreachable handles (#9046)
-				for ( i = valsLength - 1; i >= 0; i-- ) {
-					this._change( null, i );
-				}
-				this._animateOff = false;
-				break;
-			case "step":
-			case "min":
-			case "max":
-				this._animateOff = true;
-				this._calculateNewMax();
-				this._refreshValue();
-				this._animateOff = false;
-				break;
-			case "range":
-				this._animateOff = true;
-				this._refresh();
-				this._animateOff = false;
-				break;
-		}
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this._toggleClass( null, "ui-state-disabled", !!value );
-	},
-
-	//internal value getter
-	// _value() returns value trimmed by min and max, aligned by step
-	_value: function() {
-		var val = this.options.value;
-		val = this._trimAlignValue( val );
-
-		return val;
-	},
-
-	//internal values getter
-	// _values() returns array of values trimmed by min and max, aligned by step
-	// _values( index ) returns single value trimmed by min and max, aligned by step
-	_values: function( index ) {
-		var val,
-			vals,
-			i;
-
-		if ( arguments.length ) {
-			val = this.options.values[ index ];
-			val = this._trimAlignValue( val );
-
-			return val;
-		} else if ( this._hasMultipleValues() ) {
-
-			// .slice() creates a copy of the array
-			// this copy gets trimmed by min and max and then returned
-			vals = this.options.values.slice();
-			for ( i = 0; i < vals.length; i += 1 ) {
-				vals[ i ] = this._trimAlignValue( vals[ i ] );
-			}
-
-			return vals;
-		} else {
-			return [];
-		}
-	},
-
-	// Returns the step-aligned value that val is closest to, between (inclusive) min and max
-	_trimAlignValue: function( val ) {
-		if ( val <= this._valueMin() ) {
-			return this._valueMin();
-		}
-		if ( val >= this._valueMax() ) {
-			return this._valueMax();
-		}
-		var step = ( this.options.step > 0 ) ? this.options.step : 1,
-			valModStep = ( val - this._valueMin() ) % step,
-			alignValue = val - valModStep;
-
-		if ( Math.abs( valModStep ) * 2 >= step ) {
-			alignValue += ( valModStep > 0 ) ? step : ( -step );
-		}
-
-		// Since JavaScript has problems with large floats, round
-		// the final value to 5 digits after the decimal point (see #4124)
-		return parseFloat( alignValue.toFixed( 5 ) );
-	},
-
-	_calculateNewMax: function() {
-		var max = this.options.max,
-			min = this._valueMin(),
-			step = this.options.step,
-			aboveMin = Math.round( ( max - min ) / step ) * step;
-		max = aboveMin + min;
-		if ( max > this.options.max ) {
-
-			//If max is not divisible by step, rounding off may increase its value
-			max -= step;
-		}
-		this.max = parseFloat( max.toFixed( this._precision() ) );
-	},
-
-	_precision: function() {
-		var precision = this._precisionOf( this.options.step );
-		if ( this.options.min !== null ) {
-			precision = Math.max( precision, this._precisionOf( this.options.min ) );
-		}
-		return precision;
-	},
-
-	_precisionOf: function( num ) {
-		var str = num.toString(),
-			decimal = str.indexOf( "." );
-		return decimal === -1 ? 0 : str.length - decimal - 1;
-	},
-
-	_valueMin: function() {
-		return this.options.min;
-	},
-
-	_valueMax: function() {
-		return this.max;
-	},
-
-	_refreshRange: function( orientation ) {
-		if ( orientation === "vertical" ) {
-			this.range.css( { "width": "", "left": "" } );
-		}
-		if ( orientation === "horizontal" ) {
-			this.range.css( { "height": "", "bottom": "" } );
-		}
-	},
-
-	_refreshValue: function() {
-		var lastValPercent, valPercent, value, valueMin, valueMax,
-			oRange = this.options.range,
-			o = this.options,
-			that = this,
-			animate = ( !this._animateOff ) ? o.animate : false,
-			_set = {};
-
-		if ( this._hasMultipleValues() ) {
-			this.handles.each( function( i ) {
-				valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() -
-					that._valueMin() ) * 100;
-				_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
-				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
-				if ( that.options.range === true ) {
-					if ( that.orientation === "horizontal" ) {
-						if ( i === 0 ) {
-							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-								left: valPercent + "%"
-							}, o.animate );
-						}
-						if ( i === 1 ) {
-							that.range[ animate ? "animate" : "css" ]( {
-								width: ( valPercent - lastValPercent ) + "%"
-							}, {
-								queue: false,
-								duration: o.animate
-							} );
-						}
-					} else {
-						if ( i === 0 ) {
-							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-								bottom: ( valPercent ) + "%"
-							}, o.animate );
-						}
-						if ( i === 1 ) {
-							that.range[ animate ? "animate" : "css" ]( {
-								height: ( valPercent - lastValPercent ) + "%"
-							}, {
-								queue: false,
-								duration: o.animate
-							} );
-						}
-					}
-				}
-				lastValPercent = valPercent;
-			} );
-		} else {
-			value = this.value();
-			valueMin = this._valueMin();
-			valueMax = this._valueMax();
-			valPercent = ( valueMax !== valueMin ) ?
-					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
-					0;
-			_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
-			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
-
-			if ( oRange === "min" && this.orientation === "horizontal" ) {
-				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-					width: valPercent + "%"
-				}, o.animate );
-			}
-			if ( oRange === "max" && this.orientation === "horizontal" ) {
-				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-					width: ( 100 - valPercent ) + "%"
-				}, o.animate );
-			}
-			if ( oRange === "min" && this.orientation === "vertical" ) {
-				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-					height: valPercent + "%"
-				}, o.animate );
-			}
-			if ( oRange === "max" && this.orientation === "vertical" ) {
-				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
-					height: ( 100 - valPercent ) + "%"
-				}, o.animate );
-			}
-		}
-	},
-
-	_handleEvents: {
-		keydown: function( event ) {
-			var allowed, curVal, newVal, step,
-				index = $( event.target ).data( "ui-slider-handle-index" );
-
-			switch ( event.keyCode ) {
-				case $.ui.keyCode.HOME:
-				case $.ui.keyCode.END:
-				case $.ui.keyCode.PAGE_UP:
-				case $.ui.keyCode.PAGE_DOWN:
-				case $.ui.keyCode.UP:
-				case $.ui.keyCode.RIGHT:
-				case $.ui.keyCode.DOWN:
-				case $.ui.keyCode.LEFT:
-					event.preventDefault();
-					if ( !this._keySliding ) {
-						this._keySliding = true;
-						this._addClass( $( event.target ), null, "ui-state-active" );
-						allowed = this._start( event, index );
-						if ( allowed === false ) {
-							return;
-						}
-					}
-					break;
-			}
-
-			step = this.options.step;
-			if ( this._hasMultipleValues() ) {
-				curVal = newVal = this.values( index );
-			} else {
-				curVal = newVal = this.value();
-			}
-
-			switch ( event.keyCode ) {
-				case $.ui.keyCode.HOME:
-					newVal = this._valueMin();
-					break;
-				case $.ui.keyCode.END:
-					newVal = this._valueMax();
-					break;
-				case $.ui.keyCode.PAGE_UP:
-					newVal = this._trimAlignValue(
-						curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
-					);
-					break;
-				case $.ui.keyCode.PAGE_DOWN:
-					newVal = this._trimAlignValue(
-						curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) );
-					break;
-				case $.ui.keyCode.UP:
-				case $.ui.keyCode.RIGHT:
-					if ( curVal === this._valueMax() ) {
-						return;
-					}
-					newVal = this._trimAlignValue( curVal + step );
-					break;
-				case $.ui.keyCode.DOWN:
-				case $.ui.keyCode.LEFT:
-					if ( curVal === this._valueMin() ) {
-						return;
-					}
-					newVal = this._trimAlignValue( curVal - step );
-					break;
-			}
-
-			this._slide( event, index, newVal );
-		},
-		keyup: function( event ) {
-			var index = $( event.target ).data( "ui-slider-handle-index" );
-
-			if ( this._keySliding ) {
-				this._keySliding = false;
-				this._stop( event, index );
-				this._change( event, index );
-				this._removeClass( $( event.target ), null, "ui-state-active" );
-			}
-		}
-	}
-} );
-
-
-/*!
- * jQuery UI Sortable 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Sortable
-//>>group: Interactions
-//>>description: Enables items in a list to be sorted using the mouse.
-//>>docs: http://api.jqueryui.com/sortable/
-//>>demos: http://jqueryui.com/sortable/
-//>>css.structure: ../../themes/base/sortable.css
-
-
-
-var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, {
-	version: "1.12.1",
-	widgetEventPrefix: "sort",
-	ready: false,
-	options: {
-		appendTo: "parent",
-		axis: false,
-		connectWith: false,
-		containment: false,
-		cursor: "auto",
-		cursorAt: false,
-		dropOnEmpty: true,
-		forcePlaceholderSize: false,
-		forceHelperSize: false,
-		grid: false,
-		handle: false,
-		helper: "original",
-		items: "> *",
-		opacity: false,
-		placeholder: false,
-		revert: false,
-		scroll: true,
-		scrollSensitivity: 20,
-		scrollSpeed: 20,
-		scope: "default",
-		tolerance: "intersect",
-		zIndex: 1000,
-
-		// Callbacks
-		activate: null,
-		beforeStop: null,
-		change: null,
-		deactivate: null,
-		out: null,
-		over: null,
-		receive: null,
-		remove: null,
-		sort: null,
-		start: null,
-		stop: null,
-		update: null
-	},
-
-	_isOverAxis: function( x, reference, size ) {
-		return ( x >= reference ) && ( x < ( reference + size ) );
-	},
-
-	_isFloating: function( item ) {
-		return ( /left|right/ ).test( item.css( "float" ) ) ||
-			( /inline|table-cell/ ).test( item.css( "display" ) );
-	},
-
-	_create: function() {
-		this.containerCache = {};
-		this._addClass( "ui-sortable" );
-
-		//Get the items
-		this.refresh();
-
-		//Let's determine the parent's offset
-		this.offset = this.element.offset();
-
-		//Initialize mouse events for interaction
-		this._mouseInit();
-
-		this._setHandleClassName();
-
-		//We're ready to go
-		this.ready = true;
-
-	},
-
-	_setOption: function( key, value ) {
-		this._super( key, value );
-
-		if ( key === "handle" ) {
-			this._setHandleClassName();
-		}
-	},
-
-	_setHandleClassName: function() {
-		var that = this;
-		this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
-		$.each( this.items, function() {
-			that._addClass(
-				this.instance.options.handle ?
-					this.item.find( this.instance.options.handle ) :
-					this.item,
-				"ui-sortable-handle"
-			);
-		} );
-	},
-
-	_destroy: function() {
-		this._mouseDestroy();
-
-		for ( var i = this.items.length - 1; i >= 0; i-- ) {
-			this.items[ i ].item.removeData( this.widgetName + "-item" );
-		}
-
-		return this;
-	},
-
-	_mouseCapture: function( event, overrideHandle ) {
-		var currentItem = null,
-			validHandle = false,
-			that = this;
-
-		if ( this.reverting ) {
-			return false;
-		}
-
-		if ( this.options.disabled || this.options.type === "static" ) {
-			return false;
-		}
-
-		//We have to refresh the items data once first
-		this._refreshItems( event );
-
-		//Find out if the clicked node (or one of its parents) is a actual item in this.items
-		$( event.target ).parents().each( function() {
-			if ( $.data( this, that.widgetName + "-item" ) === that ) {
-				currentItem = $( this );
-				return false;
-			}
-		} );
-		if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
-			currentItem = $( event.target );
-		}
-
-		if ( !currentItem ) {
-			return false;
-		}
-		if ( this.options.handle && !overrideHandle ) {
-			$( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
-				if ( this === event.target ) {
-					validHandle = true;
-				}
-			} );
-			if ( !validHandle ) {
-				return false;
-			}
-		}
-
-		this.currentItem = currentItem;
-		this._removeCurrentsFromItems();
-		return true;
-
-	},
-
-	_mouseStart: function( event, overrideHandle, noActivation ) {
-
-		var i, body,
-			o = this.options;
-
-		this.currentContainer = this;
-
-		//We only need to call refreshPositions, because the refreshItems call has been moved to
-		// mouseCapture
-		this.refreshPositions();
-
-		//Create and append the visible helper
-		this.helper = this._createHelper( event );
-
-		//Cache the helper size
-		this._cacheHelperProportions();
-
-		/*
-		 * - Position generation -
-		 * This block generates everything position related - it's the core of draggables.
-		 */
-
-		//Cache the margins of the original element
-		this._cacheMargins();
-
-		//Get the next scrolling parent
-		this.scrollParent = this.helper.scrollParent();
-
-		//The element's absolute position on the page minus margins
-		this.offset = this.currentItem.offset();
-		this.offset = {
-			top: this.offset.top - this.margins.top,
-			left: this.offset.left - this.margins.left
-		};
-
-		$.extend( this.offset, {
-			click: { //Where the click happened, relative to the element
-				left: event.pageX - this.offset.left,
-				top: event.pageY - this.offset.top
-			},
-			parent: this._getParentOffset(),
-
-			// This is a relative to absolute position minus the actual position calculation -
-			// only used for relative positioned helper
-			relative: this._getRelativeOffset()
-		} );
-
-		// Only after we got the offset, we can change the helper's position to absolute
-		// TODO: Still need to figure out a way to make relative sorting possible
-		this.helper.css( "position", "absolute" );
-		this.cssPosition = this.helper.css( "position" );
-
-		//Generate the original position
-		this.originalPosition = this._generatePosition( event );
-		this.originalPageX = event.pageX;
-		this.originalPageY = event.pageY;
-
-		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
-		( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
-
-		//Cache the former DOM position
-		this.domPosition = {
-			prev: this.currentItem.prev()[ 0 ],
-			parent: this.currentItem.parent()[ 0 ]
-		};
-
-		// If the helper is not the original, hide the original so it's not playing any role during
-		// the drag, won't cause anything bad this way
-		if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
-			this.currentItem.hide();
-		}
-
-		//Create the placeholder
-		this._createPlaceholder();
-
-		//Set a containment if given in the options
-		if ( o.containment ) {
-			this._setContainment();
-		}
-
-		if ( o.cursor && o.cursor !== "auto" ) { // cursor option
-			body = this.document.find( "body" );
-
-			// Support: IE
-			this.storedCursor = body.css( "cursor" );
-			body.css( "cursor", o.cursor );
-
-			this.storedStylesheet =
-				$( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
-		}
-
-		if ( o.opacity ) { // opacity option
-			if ( this.helper.css( "opacity" ) ) {
-				this._storedOpacity = this.helper.css( "opacity" );
-			}
-			this.helper.css( "opacity", o.opacity );
-		}
-
-		if ( o.zIndex ) { // zIndex option
-			if ( this.helper.css( "zIndex" ) ) {
-				this._storedZIndex = this.helper.css( "zIndex" );
-			}
-			this.helper.css( "zIndex", o.zIndex );
-		}
-
-		//Prepare scrolling
-		if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-				this.scrollParent[ 0 ].tagName !== "HTML" ) {
-			this.overflowOffset = this.scrollParent.offset();
-		}
-
-		//Call callbacks
-		this._trigger( "start", event, this._uiHash() );
-
-		//Recache the helper size
-		if ( !this._preserveHelperProportions ) {
-			this._cacheHelperProportions();
-		}
-
-		//Post "activate" events to possible containers
-		if ( !noActivation ) {
-			for ( i = this.containers.length - 1; i >= 0; i-- ) {
-				this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
-			}
-		}
-
-		//Prepare possible droppables
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.current = this;
-		}
-
-		if ( $.ui.ddmanager && !o.dropBehaviour ) {
-			$.ui.ddmanager.prepareOffsets( this, event );
-		}
-
-		this.dragging = true;
-
-		this._addClass( this.helper, "ui-sortable-helper" );
-
-		// Execute the drag once - this causes the helper not to be visiblebefore getting its
-		// correct position
-		this._mouseDrag( event );
-		return true;
-
-	},
-
-	_mouseDrag: function( event ) {
-		var i, item, itemElement, intersection,
-			o = this.options,
-			scrolled = false;
-
-		//Compute the helpers position
-		this.position = this._generatePosition( event );
-		this.positionAbs = this._convertPositionTo( "absolute" );
-
-		if ( !this.lastPositionAbs ) {
-			this.lastPositionAbs = this.positionAbs;
-		}
-
-		//Do scrolling
-		if ( this.options.scroll ) {
-			if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-					this.scrollParent[ 0 ].tagName !== "HTML" ) {
-
-				if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
-						event.pageY < o.scrollSensitivity ) {
-					this.scrollParent[ 0 ].scrollTop =
-						scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
-				} else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
-					this.scrollParent[ 0 ].scrollTop =
-						scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
-				}
-
-				if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
-						event.pageX < o.scrollSensitivity ) {
-					this.scrollParent[ 0 ].scrollLeft = scrolled =
-						this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
-				} else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
-					this.scrollParent[ 0 ].scrollLeft = scrolled =
-						this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
-				}
-
-			} else {
-
-				if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
-					scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
-				} else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
-						o.scrollSensitivity ) {
-					scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
-				}
-
-				if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
-					scrolled = this.document.scrollLeft(
-						this.document.scrollLeft() - o.scrollSpeed
-					);
-				} else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
-						o.scrollSensitivity ) {
-					scrolled = this.document.scrollLeft(
-						this.document.scrollLeft() + o.scrollSpeed
-					);
-				}
-
-			}
-
-			if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
-				$.ui.ddmanager.prepareOffsets( this, event );
-			}
-		}
-
-		//Regenerate the absolute position used for position checks
-		this.positionAbs = this._convertPositionTo( "absolute" );
-
-		//Set the helper position
-		if ( !this.options.axis || this.options.axis !== "y" ) {
-			this.helper[ 0 ].style.left = this.position.left + "px";
-		}
-		if ( !this.options.axis || this.options.axis !== "x" ) {
-			this.helper[ 0 ].style.top = this.position.top + "px";
-		}
-
-		//Rearrange
-		for ( i = this.items.length - 1; i >= 0; i-- ) {
-
-			//Cache variables and intersection, continue if no intersection
-			item = this.items[ i ];
-			itemElement = item.item[ 0 ];
-			intersection = this._intersectsWithPointer( item );
-			if ( !intersection ) {
-				continue;
-			}
-
-			// Only put the placeholder inside the current Container, skip all
-			// items from other containers. This works because when moving
-			// an item from one container to another the
-			// currentContainer is switched before the placeholder is moved.
-			//
-			// Without this, moving items in "sub-sortables" can cause
-			// the placeholder to jitter between the outer and inner container.
-			if ( item.instance !== this.currentContainer ) {
-				continue;
-			}
-
-			// Cannot intersect with itself
-			// no useless actions that have been done before
-			// no action if the item moved is the parent of the item checked
-			if ( itemElement !== this.currentItem[ 0 ] &&
-				this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement &&
-				!$.contains( this.placeholder[ 0 ], itemElement ) &&
-				( this.options.type === "semi-dynamic" ?
-					!$.contains( this.element[ 0 ], itemElement ) :
-					true
-				)
-			) {
-
-				this.direction = intersection === 1 ? "down" : "up";
-
-				if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) {
-					this._rearrange( event, item );
-				} else {
-					break;
-				}
-
-				this._trigger( "change", event, this._uiHash() );
-				break;
-			}
-		}
-
-		//Post events to containers
-		this._contactContainers( event );
-
-		//Interconnect with droppables
-		if ( $.ui.ddmanager ) {
-			$.ui.ddmanager.drag( this, event );
-		}
-
-		//Call callbacks
-		this._trigger( "sort", event, this._uiHash() );
-
-		this.lastPositionAbs = this.positionAbs;
-		return false;
-
-	},
-
-	_mouseStop: function( event, noPropagation ) {
-
-		if ( !event ) {
-			return;
-		}
-
-		//If we are using droppables, inform the manager about the drop
-		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
-			$.ui.ddmanager.drop( this, event );
-		}
-
-		if ( this.options.revert ) {
-			var that = this,
-				cur = this.placeholder.offset(),
-				axis = this.options.axis,
-				animation = {};
-
-			if ( !axis || axis === "x" ) {
-				animation.left = cur.left - this.offset.parent.left - this.margins.left +
-					( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
-						0 :
-						this.offsetParent[ 0 ].scrollLeft
-					);
-			}
-			if ( !axis || axis === "y" ) {
-				animation.top = cur.top - this.offset.parent.top - this.margins.top +
-					( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
-						0 :
-						this.offsetParent[ 0 ].scrollTop
-					);
-			}
-			this.reverting = true;
-			$( this.helper ).animate(
-				animation,
-				parseInt( this.options.revert, 10 ) || 500,
-				function() {
-					that._clear( event );
-				}
-			);
-		} else {
-			this._clear( event, noPropagation );
-		}
-
-		return false;
-
-	},
-
-	cancel: function() {
-
-		if ( this.dragging ) {
-
-			this._mouseUp( new $.Event( "mouseup", { target: null } ) );
-
-			if ( this.options.helper === "original" ) {
-				this.currentItem.css( this._storedCSS );
-				this._removeClass( this.currentItem, "ui-sortable-helper" );
-			} else {
-				this.currentItem.show();
-			}
-
-			//Post deactivating events to containers
-			for ( var i = this.containers.length - 1; i >= 0; i-- ) {
-				this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
-				if ( this.containers[ i ].containerCache.over ) {
-					this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
-					this.containers[ i ].containerCache.over = 0;
-				}
-			}
-
-		}
-
-		if ( this.placeholder ) {
-
-			//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
-			// it unbinds ALL events from the original node!
-			if ( this.placeholder[ 0 ].parentNode ) {
-				this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
-			}
-			if ( this.options.helper !== "original" && this.helper &&
-					this.helper[ 0 ].parentNode ) {
-				this.helper.remove();
-			}
-
-			$.extend( this, {
-				helper: null,
-				dragging: false,
-				reverting: false,
-				_noFinalSort: null
-			} );
-
-			if ( this.domPosition.prev ) {
-				$( this.domPosition.prev ).after( this.currentItem );
-			} else {
-				$( this.domPosition.parent ).prepend( this.currentItem );
-			}
-		}
-
-		return this;
-
-	},
-
-	serialize: function( o ) {
-
-		var items = this._getItemsAsjQuery( o && o.connected ),
-			str = [];
-		o = o || {};
-
-		$( items ).each( function() {
-			var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
-				.match( o.expression || ( /(.+)[\-=_](.+)/ ) );
-			if ( res ) {
-				str.push(
-					( o.key || res[ 1 ] + "[]" ) +
-					"=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
-			}
-		} );
-
-		if ( !str.length && o.key ) {
-			str.push( o.key + "=" );
-		}
-
-		return str.join( "&" );
-
-	},
-
-	toArray: function( o ) {
-
-		var items = this._getItemsAsjQuery( o && o.connected ),
-			ret = [];
-
-		o = o || {};
-
-		items.each( function() {
-			ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
-		} );
-		return ret;
-
-	},
-
-	/* Be careful with the following core functions */
-	_intersectsWith: function( item ) {
-
-		var x1 = this.positionAbs.left,
-			x2 = x1 + this.helperProportions.width,
-			y1 = this.positionAbs.top,
-			y2 = y1 + this.helperProportions.height,
-			l = item.left,
-			r = l + item.width,
-			t = item.top,
-			b = t + item.height,
-			dyClick = this.offset.click.top,
-			dxClick = this.offset.click.left,
-			isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
-				( y1 + dyClick ) < b ),
-			isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
-				( x1 + dxClick ) < r ),
-			isOverElement = isOverElementHeight && isOverElementWidth;
-
-		if ( this.options.tolerance === "pointer" ||
-			this.options.forcePointerForContainers ||
-			( this.options.tolerance !== "pointer" &&
-				this.helperProportions[ this.floating ? "width" : "height" ] >
-				item[ this.floating ? "width" : "height" ] )
-		) {
-			return isOverElement;
-		} else {
-
-			return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
-				x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
-				t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
-				y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half
-
-		}
-	},
-
-	_intersectsWithPointer: function( item ) {
-		var verticalDirection, horizontalDirection,
-			isOverElementHeight = ( this.options.axis === "x" ) ||
-				this._isOverAxis(
-					this.positionAbs.top + this.offset.click.top, item.top, item.height ),
-			isOverElementWidth = ( this.options.axis === "y" ) ||
-				this._isOverAxis(
-					this.positionAbs.left + this.offset.click.left, item.left, item.width ),
-			isOverElement = isOverElementHeight && isOverElementWidth;
-
-		if ( !isOverElement ) {
-			return false;
-		}
-
-		verticalDirection = this._getDragVerticalDirection();
-		horizontalDirection = this._getDragHorizontalDirection();
-
-		return this.floating ?
-			( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 )
-			: ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );
-
-	},
-
-	_intersectsWithSides: function( item ) {
-
-		var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
-				this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
-			isOverRightHalf = this._isOverAxis( this.positionAbs.left +
-				this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
-			verticalDirection = this._getDragVerticalDirection(),
-			horizontalDirection = this._getDragHorizontalDirection();
-
-		if ( this.floating && horizontalDirection ) {
-			return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
-				( horizontalDirection === "left" && !isOverRightHalf ) );
-		} else {
-			return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
-				( verticalDirection === "up" && !isOverBottomHalf ) );
-		}
-
-	},
-
-	_getDragVerticalDirection: function() {
-		var delta = this.positionAbs.top - this.lastPositionAbs.top;
-		return delta !== 0 && ( delta > 0 ? "down" : "up" );
-	},
-
-	_getDragHorizontalDirection: function() {
-		var delta = this.positionAbs.left - this.lastPositionAbs.left;
-		return delta !== 0 && ( delta > 0 ? "right" : "left" );
-	},
-
-	refresh: function( event ) {
-		this._refreshItems( event );
-		this._setHandleClassName();
-		this.refreshPositions();
-		return this;
-	},
-
-	_connectWith: function() {
-		var options = this.options;
-		return options.connectWith.constructor === String ?
-			[ options.connectWith ] :
-			options.connectWith;
-	},
-
-	_getItemsAsjQuery: function( connected ) {
-
-		var i, j, cur, inst,
-			items = [],
-			queries = [],
-			connectWith = this._connectWith();
-
-		if ( connectWith && connected ) {
-			for ( i = connectWith.length - 1; i >= 0; i-- ) {
-				cur = $( connectWith[ i ], this.document[ 0 ] );
-				for ( j = cur.length - 1; j >= 0; j-- ) {
-					inst = $.data( cur[ j ], this.widgetFullName );
-					if ( inst && inst !== this && !inst.options.disabled ) {
-						queries.push( [ $.isFunction( inst.options.items ) ?
-							inst.options.items.call( inst.element ) :
-							$( inst.options.items, inst.element )
-								.not( ".ui-sortable-helper" )
-								.not( ".ui-sortable-placeholder" ), inst ] );
-					}
-				}
-			}
-		}
-
-		queries.push( [ $.isFunction( this.options.items ) ?
-			this.options.items
-				.call( this.element, null, { options: this.options, item: this.currentItem } ) :
-			$( this.options.items, this.element )
-				.not( ".ui-sortable-helper" )
-				.not( ".ui-sortable-placeholder" ), this ] );
-
-		function addItems() {
-			items.push( this );
-		}
-		for ( i = queries.length - 1; i >= 0; i-- ) {
-			queries[ i ][ 0 ].each( addItems );
-		}
-
-		return $( items );
-
-	},
-
-	_removeCurrentsFromItems: function() {
-
-		var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );
-
-		this.items = $.grep( this.items, function( item ) {
-			for ( var j = 0; j < list.length; j++ ) {
-				if ( list[ j ] === item.item[ 0 ] ) {
-					return false;
-				}
-			}
-			return true;
-		} );
-
-	},
-
-	_refreshItems: function( event ) {
-
-		this.items = [];
-		this.containers = [ this ];
-
-		var i, j, cur, inst, targetData, _queries, item, queriesLength,
-			items = this.items,
-			queries = [ [ $.isFunction( this.options.items ) ?
-				this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
-				$( this.options.items, this.element ), this ] ],
-			connectWith = this._connectWith();
-
-		//Shouldn't be run the first time through due to massive slow-down
-		if ( connectWith && this.ready ) {
-			for ( i = connectWith.length - 1; i >= 0; i-- ) {
-				cur = $( connectWith[ i ], this.document[ 0 ] );
-				for ( j = cur.length - 1; j >= 0; j-- ) {
-					inst = $.data( cur[ j ], this.widgetFullName );
-					if ( inst && inst !== this && !inst.options.disabled ) {
-						queries.push( [ $.isFunction( inst.options.items ) ?
-							inst.options.items
-								.call( inst.element[ 0 ], event, { item: this.currentItem } ) :
-							$( inst.options.items, inst.element ), inst ] );
-						this.containers.push( inst );
-					}
-				}
-			}
-		}
-
-		for ( i = queries.length - 1; i >= 0; i-- ) {
-			targetData = queries[ i ][ 1 ];
-			_queries = queries[ i ][ 0 ];
-
-			for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
-				item = $( _queries[ j ] );
-
-				// Data for target checking (mouse manager)
-				item.data( this.widgetName + "-item", targetData );
-
-				items.push( {
-					item: item,
-					instance: targetData,
-					width: 0, height: 0,
-					left: 0, top: 0
-				} );
-			}
-		}
-
-	},
-
-	refreshPositions: function( fast ) {
-
-		// Determine whether items are being displayed horizontally
-		this.floating = this.items.length ?
-			this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
-			false;
-
-		//This has to be redone because due to the item being moved out/into the offsetParent,
-		// the offsetParent's position will change
-		if ( this.offsetParent && this.helper ) {
-			this.offset.parent = this._getParentOffset();
-		}
-
-		var i, item, t, p;
-
-		for ( i = this.items.length - 1; i >= 0; i-- ) {
-			item = this.items[ i ];
-
-			//We ignore calculating positions of all connected containers when we're not over them
-			if ( item.instance !== this.currentContainer && this.currentContainer &&
-					item.item[ 0 ] !== this.currentItem[ 0 ] ) {
-				continue;
-			}
-
-			t = this.options.toleranceElement ?
-				$( this.options.toleranceElement, item.item ) :
-				item.item;
-
-			if ( !fast ) {
-				item.width = t.outerWidth();
-				item.height = t.outerHeight();
-			}
-
-			p = t.offset();
-			item.left = p.left;
-			item.top = p.top;
-		}
-
-		if ( this.options.custom && this.options.custom.refreshContainers ) {
-			this.options.custom.refreshContainers.call( this );
-		} else {
-			for ( i = this.containers.length - 1; i >= 0; i-- ) {
-				p = this.containers[ i ].element.offset();
-				this.containers[ i ].containerCache.left = p.left;
-				this.containers[ i ].containerCache.top = p.top;
-				this.containers[ i ].containerCache.width =
-					this.containers[ i ].element.outerWidth();
-				this.containers[ i ].containerCache.height =
-					this.containers[ i ].element.outerHeight();
-			}
-		}
-
-		return this;
-	},
-
-	_createPlaceholder: function( that ) {
-		that = that || this;
-		var className,
-			o = that.options;
-
-		if ( !o.placeholder || o.placeholder.constructor === String ) {
-			className = o.placeholder;
-			o.placeholder = {
-				element: function() {
-
-					var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(),
-						element = $( "<" + nodeName + ">", that.document[ 0 ] );
-
-						that._addClass( element, "ui-sortable-placeholder",
-								className || that.currentItem[ 0 ].className )
-							._removeClass( element, "ui-sortable-helper" );
-
-					if ( nodeName === "tbody" ) {
-						that._createTrPlaceholder(
-							that.currentItem.find( "tr" ).eq( 0 ),
-							$( "<tr>", that.document[ 0 ] ).appendTo( element )
-						);
-					} else if ( nodeName === "tr" ) {
-						that._createTrPlaceholder( that.currentItem, element );
-					} else if ( nodeName === "img" ) {
-						element.attr( "src", that.currentItem.attr( "src" ) );
-					}
-
-					if ( !className ) {
-						element.css( "visibility", "hidden" );
-					}
-
-					return element;
-				},
-				update: function( container, p ) {
-
-					// 1. If a className is set as 'placeholder option, we don't force sizes -
-					// the class is responsible for that
-					// 2. The option 'forcePlaceholderSize can be enabled to force it even if a
-					// class name is specified
-					if ( className && !o.forcePlaceholderSize ) {
-						return;
-					}
-
-					//If the element doesn't have a actual height by itself (without styles coming
-					// from a stylesheet), it receives the inline height from the dragged item
-					if ( !p.height() ) {
-						p.height(
-							that.currentItem.innerHeight() -
-							parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
-							parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
-					}
-					if ( !p.width() ) {
-						p.width(
-							that.currentItem.innerWidth() -
-							parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
-							parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
-					}
-				}
-			};
-		}
-
-		//Create the placeholder
-		that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );
-
-		//Append it after the actual current item
-		that.currentItem.after( that.placeholder );
-
-		//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
-		o.placeholder.update( that, that.placeholder );
-
-	},
-
-	_createTrPlaceholder: function( sourceTr, targetTr ) {
-		var that = this;
-
-		sourceTr.children().each( function() {
-			$( "<td>&#160;</td>", that.document[ 0 ] )
-				.attr( "colspan", $( this ).attr( "colspan" ) || 1 )
-				.appendTo( targetTr );
-		} );
-	},
-
-	_contactContainers: function( event ) {
-		var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
-			floating, axis,
-			innermostContainer = null,
-			innermostIndex = null;
-
-		// Get innermost container that intersects with item
-		for ( i = this.containers.length - 1; i >= 0; i-- ) {
-
-			// Never consider a container that's located within the item itself
-			if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
-				continue;
-			}
-
-			if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {
-
-				// If we've already found a container and it's more "inner" than this, then continue
-				if ( innermostContainer &&
-						$.contains(
-							this.containers[ i ].element[ 0 ],
-							innermostContainer.element[ 0 ] ) ) {
-					continue;
-				}
-
-				innermostContainer = this.containers[ i ];
-				innermostIndex = i;
-
-			} else {
-
-				// container doesn't intersect. trigger "out" event if necessary
-				if ( this.containers[ i ].containerCache.over ) {
-					this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
-					this.containers[ i ].containerCache.over = 0;
-				}
-			}
-
-		}
-
-		// If no intersecting containers found, return
-		if ( !innermostContainer ) {
-			return;
-		}
-
-		// Move the item into the container if it's not there already
-		if ( this.containers.length === 1 ) {
-			if ( !this.containers[ innermostIndex ].containerCache.over ) {
-				this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
-				this.containers[ innermostIndex ].containerCache.over = 1;
-			}
-		} else {
-
-			// When entering a new container, we will find the item with the least distance and
-			// append our item near it
-			dist = 10000;
-			itemWithLeastDistance = null;
-			floating = innermostContainer.floating || this._isFloating( this.currentItem );
-			posProperty = floating ? "left" : "top";
-			sizeProperty = floating ? "width" : "height";
-			axis = floating ? "pageX" : "pageY";
-
-			for ( j = this.items.length - 1; j >= 0; j-- ) {
-				if ( !$.contains(
-						this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
-				) {
-					continue;
-				}
-				if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
-					continue;
-				}
-
-				cur = this.items[ j ].item.offset()[ posProperty ];
-				nearBottom = false;
-				if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
-					nearBottom = true;
-				}
-
-				if ( Math.abs( event[ axis ] - cur ) < dist ) {
-					dist = Math.abs( event[ axis ] - cur );
-					itemWithLeastDistance = this.items[ j ];
-					this.direction = nearBottom ? "up" : "down";
-				}
-			}
-
-			//Check if dropOnEmpty is enabled
-			if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
-				return;
-			}
-
-			if ( this.currentContainer === this.containers[ innermostIndex ] ) {
-				if ( !this.currentContainer.containerCache.over ) {
-					this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
-					this.currentContainer.containerCache.over = 1;
-				}
-				return;
-			}
-
-			itemWithLeastDistance ?
-				this._rearrange( event, itemWithLeastDistance, null, true ) :
-				this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
-			this._trigger( "change", event, this._uiHash() );
-			this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
-			this.currentContainer = this.containers[ innermostIndex ];
-
-			//Update the placeholder
-			this.options.placeholder.update( this.currentContainer, this.placeholder );
-
-			this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
-			this.containers[ innermostIndex ].containerCache.over = 1;
-		}
-
-	},
-
-	_createHelper: function( event ) {
-
-		var o = this.options,
-			helper = $.isFunction( o.helper ) ?
-				$( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
-				( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );
-
-		//Add the helper to the DOM if that didn't happen already
-		if ( !helper.parents( "body" ).length ) {
-			$( o.appendTo !== "parent" ?
-				o.appendTo :
-				this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] );
-		}
-
-		if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
-			this._storedCSS = {
-				width: this.currentItem[ 0 ].style.width,
-				height: this.currentItem[ 0 ].style.height,
-				position: this.currentItem.css( "position" ),
-				top: this.currentItem.css( "top" ),
-				left: this.currentItem.css( "left" )
-			};
-		}
-
-		if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
-			helper.width( this.currentItem.width() );
-		}
-		if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
-			helper.height( this.currentItem.height() );
-		}
-
-		return helper;
-
-	},
-
-	_adjustOffsetFromHelper: function( obj ) {
-		if ( typeof obj === "string" ) {
-			obj = obj.split( " " );
-		}
-		if ( $.isArray( obj ) ) {
-			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
-		}
-		if ( "left" in obj ) {
-			this.offset.click.left = obj.left + this.margins.left;
-		}
-		if ( "right" in obj ) {
-			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
-		}
-		if ( "top" in obj ) {
-			this.offset.click.top = obj.top + this.margins.top;
-		}
-		if ( "bottom" in obj ) {
-			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
-		}
-	},
-
-	_getParentOffset: function() {
-
-		//Get the offsetParent and cache its position
-		this.offsetParent = this.helper.offsetParent();
-		var po = this.offsetParent.offset();
-
-		// This is a special case where we need to modify a offset calculated on start, since the
-		// following happened:
-		// 1. The position of the helper is absolute, so it's position is calculated based on the
-		// next positioned parent
-		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
-		// the document, which means that the scroll is included in the initial calculation of the
-		// offset of the parent, and never recalculated upon drag
-		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
-			po.left += this.scrollParent.scrollLeft();
-			po.top += this.scrollParent.scrollTop();
-		}
-
-		// This needs to be actually done for all browsers, since pageX/pageY includes this
-		// information with an ugly IE fix
-		if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
-				( this.offsetParent[ 0 ].tagName &&
-				this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
-			po = { top: 0, left: 0 };
-		}
-
-		return {
-			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
-			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
-		};
-
-	},
-
-	_getRelativeOffset: function() {
-
-		if ( this.cssPosition === "relative" ) {
-			var p = this.currentItem.position();
-			return {
-				top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
-					this.scrollParent.scrollTop(),
-				left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
-					this.scrollParent.scrollLeft()
-			};
-		} else {
-			return { top: 0, left: 0 };
-		}
-
-	},
-
-	_cacheMargins: function() {
-		this.margins = {
-			left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
-			top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
-		};
-	},
-
-	_cacheHelperProportions: function() {
-		this.helperProportions = {
-			width: this.helper.outerWidth(),
-			height: this.helper.outerHeight()
-		};
-	},
-
-	_setContainment: function() {
-
-		var ce, co, over,
-			o = this.options;
-		if ( o.containment === "parent" ) {
-			o.containment = this.helper[ 0 ].parentNode;
-		}
-		if ( o.containment === "document" || o.containment === "window" ) {
-			this.containment = [
-				0 - this.offset.relative.left - this.offset.parent.left,
-				0 - this.offset.relative.top - this.offset.parent.top,
-				o.containment === "document" ?
-					this.document.width() :
-					this.window.width() - this.helperProportions.width - this.margins.left,
-				( o.containment === "document" ?
-					( this.document.height() || document.body.parentNode.scrollHeight ) :
-					this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
-				) - this.helperProportions.height - this.margins.top
-			];
-		}
-
-		if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
-			ce = $( o.containment )[ 0 ];
-			co = $( o.containment ).offset();
-			over = ( $( ce ).css( "overflow" ) !== "hidden" );
-
-			this.containment = [
-				co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
-					( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
-				co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
-					( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
-				co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
-					( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
-					( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
-					this.helperProportions.width - this.margins.left,
-				co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
-					( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
-					( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
-					this.helperProportions.height - this.margins.top
-			];
-		}
-
-	},
-
-	_convertPositionTo: function( d, pos ) {
-
-		if ( !pos ) {
-			pos = this.position;
-		}
-		var mod = d === "absolute" ? 1 : -1,
-			scroll = this.cssPosition === "absolute" &&
-				!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
-					this.offsetParent :
-					this.scrollParent,
-			scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
-
-		return {
-			top: (
-
-				// The absolute mouse position
-				pos.top	+
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.top * mod +
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.top * mod -
-				( ( this.cssPosition === "fixed" ?
-					-this.scrollParent.scrollTop() :
-					( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
-			),
-			left: (
-
-				// The absolute mouse position
-				pos.left +
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.left * mod +
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.left * mod	-
-				( ( this.cssPosition === "fixed" ?
-					-this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
-					scroll.scrollLeft() ) * mod )
-			)
-		};
-
-	},
-
-	_generatePosition: function( event ) {
-
-		var top, left,
-			o = this.options,
-			pageX = event.pageX,
-			pageY = event.pageY,
-			scroll = this.cssPosition === "absolute" &&
-				!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
-					this.offsetParent :
-					this.scrollParent,
-				scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
-
-		// This is another very weird special case that only happens for relative elements:
-		// 1. If the css position is relative
-		// 2. and the scroll parent is the document or similar to the offset parent
-		// we have to refresh the relative offset during the scroll so there are no jumps
-		if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
-				this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
-			this.offset.relative = this._getRelativeOffset();
-		}
-
-		/*
-		 * - Position constraining -
-		 * Constrain the position to a mix of grid, containment.
-		 */
-
-		if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options
-
-			if ( this.containment ) {
-				if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
-					pageX = this.containment[ 0 ] + this.offset.click.left;
-				}
-				if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
-					pageY = this.containment[ 1 ] + this.offset.click.top;
-				}
-				if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
-					pageX = this.containment[ 2 ] + this.offset.click.left;
-				}
-				if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
-					pageY = this.containment[ 3 ] + this.offset.click.top;
-				}
-			}
-
-			if ( o.grid ) {
-				top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
-					o.grid[ 1 ] ) * o.grid[ 1 ];
-				pageY = this.containment ?
-					( ( top - this.offset.click.top >= this.containment[ 1 ] &&
-						top - this.offset.click.top <= this.containment[ 3 ] ) ?
-							top :
-							( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
-								top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
-								top;
-
-				left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
-					o.grid[ 0 ] ) * o.grid[ 0 ];
-				pageX = this.containment ?
-					( ( left - this.offset.click.left >= this.containment[ 0 ] &&
-						left - this.offset.click.left <= this.containment[ 2 ] ) ?
-							left :
-							( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
-								left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
-								left;
-			}
-
-		}
-
-		return {
-			top: (
-
-				// The absolute mouse position
-				pageY -
-
-				// Click offset (relative to the element)
-				this.offset.click.top -
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.top -
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.top +
-				( ( this.cssPosition === "fixed" ?
-					-this.scrollParent.scrollTop() :
-					( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
-			),
-			left: (
-
-				// The absolute mouse position
-				pageX -
-
-				// Click offset (relative to the element)
-				this.offset.click.left -
-
-				// Only for relative positioned nodes: Relative offset from element to offset parent
-				this.offset.relative.left -
-
-				// The offsetParent's offset without borders (offset + border)
-				this.offset.parent.left +
-				( ( this.cssPosition === "fixed" ?
-					-this.scrollParent.scrollLeft() :
-					scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
-			)
-		};
-
-	},
-
-	_rearrange: function( event, i, a, hardRefresh ) {
-
-		a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) :
-			i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
-				( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
-
-		//Various things done here to improve the performance:
-		// 1. we create a setTimeout, that calls refreshPositions
-		// 2. on the instance, we have a counter variable, that get's higher after every append
-		// 3. on the local scope, we copy the counter variable, and check in the timeout,
-		// if it's still the same
-		// 4. this lets only the last addition to the timeout stack through
-		this.counter = this.counter ? ++this.counter : 1;
-		var counter = this.counter;
-
-		this._delay( function() {
-			if ( counter === this.counter ) {
-
-				//Precompute after each DOM insertion, NOT on mousemove
-				this.refreshPositions( !hardRefresh );
-			}
-		} );
-
-	},
-
-	_clear: function( event, noPropagation ) {
-
-		this.reverting = false;
-
-		// We delay all events that have to be triggered to after the point where the placeholder
-		// has been removed and everything else normalized again
-		var i,
-			delayedTriggers = [];
-
-		// We first have to update the dom position of the actual currentItem
-		// Note: don't do it if the current item is already removed (by a user), or it gets
-		// reappended (see #4088)
-		if ( !this._noFinalSort && this.currentItem.parent().length ) {
-			this.placeholder.before( this.currentItem );
-		}
-		this._noFinalSort = null;
-
-		if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
-			for ( i in this._storedCSS ) {
-				if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
-					this._storedCSS[ i ] = "";
-				}
-			}
-			this.currentItem.css( this._storedCSS );
-			this._removeClass( this.currentItem, "ui-sortable-helper" );
-		} else {
-			this.currentItem.show();
-		}
-
-		if ( this.fromOutside && !noPropagation ) {
-			delayedTriggers.push( function( event ) {
-				this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
-			} );
-		}
-		if ( ( this.fromOutside ||
-				this.domPosition.prev !==
-				this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
-				this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {
-
-			// Trigger update callback if the DOM position has changed
-			delayedTriggers.push( function( event ) {
-				this._trigger( "update", event, this._uiHash() );
-			} );
-		}
-
-		// Check if the items Container has Changed and trigger appropriate
-		// events.
-		if ( this !== this.currentContainer ) {
-			if ( !noPropagation ) {
-				delayedTriggers.push( function( event ) {
-					this._trigger( "remove", event, this._uiHash() );
-				} );
-				delayedTriggers.push( ( function( c ) {
-					return function( event ) {
-						c._trigger( "receive", event, this._uiHash( this ) );
-					};
-				} ).call( this, this.currentContainer ) );
-				delayedTriggers.push( ( function( c ) {
-					return function( event ) {
-						c._trigger( "update", event, this._uiHash( this ) );
-					};
-				} ).call( this, this.currentContainer ) );
-			}
-		}
-
-		//Post events to containers
-		function delayEvent( type, instance, container ) {
-			return function( event ) {
-				container._trigger( type, event, instance._uiHash( instance ) );
-			};
-		}
-		for ( i = this.containers.length - 1; i >= 0; i-- ) {
-			if ( !noPropagation ) {
-				delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
-			}
-			if ( this.containers[ i ].containerCache.over ) {
-				delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
-				this.containers[ i ].containerCache.over = 0;
-			}
-		}
-
-		//Do what was originally in plugins
-		if ( this.storedCursor ) {
-			this.document.find( "body" ).css( "cursor", this.storedCursor );
-			this.storedStylesheet.remove();
-		}
-		if ( this._storedOpacity ) {
-			this.helper.css( "opacity", this._storedOpacity );
-		}
-		if ( this._storedZIndex ) {
-			this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
-		}
-
-		this.dragging = false;
-
-		if ( !noPropagation ) {
-			this._trigger( "beforeStop", event, this._uiHash() );
-		}
-
-		//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
-		// it unbinds ALL events from the original node!
-		this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
-
-		if ( !this.cancelHelperRemoval ) {
-			if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
-				this.helper.remove();
-			}
-			this.helper = null;
-		}
-
-		if ( !noPropagation ) {
-			for ( i = 0; i < delayedTriggers.length; i++ ) {
-
-				// Trigger all delayed events
-				delayedTriggers[ i ].call( this, event );
-			}
-			this._trigger( "stop", event, this._uiHash() );
-		}
-
-		this.fromOutside = false;
-		return !this.cancelHelperRemoval;
-
-	},
-
-	_trigger: function() {
-		if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
-			this.cancel();
-		}
-	},
-
-	_uiHash: function( _inst ) {
-		var inst = _inst || this;
-		return {
-			helper: inst.helper,
-			placeholder: inst.placeholder || $( [] ),
-			position: inst.position,
-			originalPosition: inst.originalPosition,
-			offset: inst.positionAbs,
-			item: inst.currentItem,
-			sender: _inst ? _inst.element : null
-		};
-	}
-
-} );
-
-
-/*!
- * jQuery UI Spinner 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Spinner
-//>>group: Widgets
-//>>description: Displays buttons to easily input numbers via the keyboard or mouse.
-//>>docs: http://api.jqueryui.com/spinner/
-//>>demos: http://jqueryui.com/spinner/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/spinner.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-function spinnerModifer( fn ) {
-	return function() {
-		var previous = this.element.val();
-		fn.apply( this, arguments );
-		this._refresh();
-		if ( previous !== this.element.val() ) {
-			this._trigger( "change" );
-		}
-	};
-}
-
-$.widget( "ui.spinner", {
-	version: "1.12.1",
-	defaultElement: "<input>",
-	widgetEventPrefix: "spin",
-	options: {
-		classes: {
-			"ui-spinner": "ui-corner-all",
-			"ui-spinner-down": "ui-corner-br",
-			"ui-spinner-up": "ui-corner-tr"
-		},
-		culture: null,
-		icons: {
-			down: "ui-icon-triangle-1-s",
-			up: "ui-icon-triangle-1-n"
-		},
-		incremental: true,
-		max: null,
-		min: null,
-		numberFormat: null,
-		page: 10,
-		step: 1,
-
-		change: null,
-		spin: null,
-		start: null,
-		stop: null
-	},
-
-	_create: function() {
-
-		// handle string values that need to be parsed
-		this._setOption( "max", this.options.max );
-		this._setOption( "min", this.options.min );
-		this._setOption( "step", this.options.step );
-
-		// Only format if there is a value, prevents the field from being marked
-		// as invalid in Firefox, see #9573.
-		if ( this.value() !== "" ) {
-
-			// Format the value, but don't constrain.
-			this._value( this.element.val(), true );
-		}
-
-		this._draw();
-		this._on( this._events );
-		this._refresh();
-
-		// Turning off autocomplete prevents the browser from remembering the
-		// value when navigating through history, so we re-enable autocomplete
-		// if the page is unloaded before the widget is destroyed. #7790
-		this._on( this.window, {
-			beforeunload: function() {
-				this.element.removeAttr( "autocomplete" );
-			}
-		} );
-	},
-
-	_getCreateOptions: function() {
-		var options = this._super();
-		var element = this.element;
-
-		$.each( [ "min", "max", "step" ], function( i, option ) {
-			var value = element.attr( option );
-			if ( value != null && value.length ) {
-				options[ option ] = value;
-			}
-		} );
-
-		return options;
-	},
-
-	_events: {
-		keydown: function( event ) {
-			if ( this._start( event ) && this._keydown( event ) ) {
-				event.preventDefault();
-			}
-		},
-		keyup: "_stop",
-		focus: function() {
-			this.previous = this.element.val();
-		},
-		blur: function( event ) {
-			if ( this.cancelBlur ) {
-				delete this.cancelBlur;
-				return;
-			}
-
-			this._stop();
-			this._refresh();
-			if ( this.previous !== this.element.val() ) {
-				this._trigger( "change", event );
-			}
-		},
-		mousewheel: function( event, delta ) {
-			if ( !delta ) {
-				return;
-			}
-			if ( !this.spinning && !this._start( event ) ) {
-				return false;
-			}
-
-			this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event );
-			clearTimeout( this.mousewheelTimer );
-			this.mousewheelTimer = this._delay( function() {
-				if ( this.spinning ) {
-					this._stop( event );
-				}
-			}, 100 );
-			event.preventDefault();
-		},
-		"mousedown .ui-spinner-button": function( event ) {
-			var previous;
-
-			// We never want the buttons to have focus; whenever the user is
-			// interacting with the spinner, the focus should be on the input.
-			// If the input is focused then this.previous is properly set from
-			// when the input first received focus. If the input is not focused
-			// then we need to set this.previous based on the value before spinning.
-			previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ?
-				this.previous : this.element.val();
-			function checkFocus() {
-				var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] );
-				if ( !isActive ) {
-					this.element.trigger( "focus" );
-					this.previous = previous;
-
-					// support: IE
-					// IE sets focus asynchronously, so we need to check if focus
-					// moved off of the input because the user clicked on the button.
-					this._delay( function() {
-						this.previous = previous;
-					} );
-				}
-			}
-
-			// Ensure focus is on (or stays on) the text field
-			event.preventDefault();
-			checkFocus.call( this );
-
-			// Support: IE
-			// IE doesn't prevent moving focus even with event.preventDefault()
-			// so we set a flag to know when we should ignore the blur event
-			// and check (again) if focus moved off of the input.
-			this.cancelBlur = true;
-			this._delay( function() {
-				delete this.cancelBlur;
-				checkFocus.call( this );
-			} );
-
-			if ( this._start( event ) === false ) {
-				return;
-			}
-
-			this._repeat( null, $( event.currentTarget )
-				.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
-		},
-		"mouseup .ui-spinner-button": "_stop",
-		"mouseenter .ui-spinner-button": function( event ) {
-
-			// button will add ui-state-active if mouse was down while mouseleave and kept down
-			if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
-				return;
-			}
-
-			if ( this._start( event ) === false ) {
-				return false;
-			}
-			this._repeat( null, $( event.currentTarget )
-				.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
-		},
-
-		// TODO: do we really want to consider this a stop?
-		// shouldn't we just stop the repeater and wait until mouseup before
-		// we trigger the stop event?
-		"mouseleave .ui-spinner-button": "_stop"
-	},
-
-	// Support mobile enhanced option and make backcompat more sane
-	_enhance: function() {
-		this.uiSpinner = this.element
-			.attr( "autocomplete", "off" )
-			.wrap( "<span>" )
-			.parent()
-
-				// Add buttons
-				.append(
-					"<a></a><a></a>"
-				);
-	},
-
-	_draw: function() {
-		this._enhance();
-
-		this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
-		this._addClass( "ui-spinner-input" );
-
-		this.element.attr( "role", "spinbutton" );
-
-		// Button bindings
-		this.buttons = this.uiSpinner.children( "a" )
-			.attr( "tabIndex", -1 )
-			.attr( "aria-hidden", true )
-			.button( {
-				classes: {
-					"ui-button": ""
-				}
-			} );
-
-		// TODO: Right now button does not support classes this is already updated in button PR
-		this._removeClass( this.buttons, "ui-corner-all" );
-
-		this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
-		this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
-		this.buttons.first().button( {
-			"icon": this.options.icons.up,
-			"showLabel": false
-		} );
-		this.buttons.last().button( {
-			"icon": this.options.icons.down,
-			"showLabel": false
-		} );
-
-		// IE 6 doesn't understand height: 50% for the buttons
-		// unless the wrapper has an explicit height
-		if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) &&
-				this.uiSpinner.height() > 0 ) {
-			this.uiSpinner.height( this.uiSpinner.height() );
-		}
-	},
-
-	_keydown: function( event ) {
-		var options = this.options,
-			keyCode = $.ui.keyCode;
-
-		switch ( event.keyCode ) {
-		case keyCode.UP:
-			this._repeat( null, 1, event );
-			return true;
-		case keyCode.DOWN:
-			this._repeat( null, -1, event );
-			return true;
-		case keyCode.PAGE_UP:
-			this._repeat( null, options.page, event );
-			return true;
-		case keyCode.PAGE_DOWN:
-			this._repeat( null, -options.page, event );
-			return true;
-		}
-
-		return false;
-	},
-
-	_start: function( event ) {
-		if ( !this.spinning && this._trigger( "start", event ) === false ) {
-			return false;
-		}
-
-		if ( !this.counter ) {
-			this.counter = 1;
-		}
-		this.spinning = true;
-		return true;
-	},
-
-	_repeat: function( i, steps, event ) {
-		i = i || 500;
-
-		clearTimeout( this.timer );
-		this.timer = this._delay( function() {
-			this._repeat( 40, steps, event );
-		}, i );
-
-		this._spin( steps * this.options.step, event );
-	},
-
-	_spin: function( step, event ) {
-		var value = this.value() || 0;
-
-		if ( !this.counter ) {
-			this.counter = 1;
-		}
-
-		value = this._adjustValue( value + step * this._increment( this.counter ) );
-
-		if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) {
-			this._value( value );
-			this.counter++;
-		}
-	},
-
-	_increment: function( i ) {
-		var incremental = this.options.incremental;
-
-		if ( incremental ) {
-			return $.isFunction( incremental ) ?
-				incremental( i ) :
-				Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
-		}
-
-		return 1;
-	},
-
-	_precision: function() {
-		var precision = this._precisionOf( this.options.step );
-		if ( this.options.min !== null ) {
-			precision = Math.max( precision, this._precisionOf( this.options.min ) );
-		}
-		return precision;
-	},
-
-	_precisionOf: function( num ) {
-		var str = num.toString(),
-			decimal = str.indexOf( "." );
-		return decimal === -1 ? 0 : str.length - decimal - 1;
-	},
-
-	_adjustValue: function( value ) {
-		var base, aboveMin,
-			options = this.options;
-
-		// Make sure we're at a valid step
-		// - find out where we are relative to the base (min or 0)
-		base = options.min !== null ? options.min : 0;
-		aboveMin = value - base;
-
-		// - round to the nearest step
-		aboveMin = Math.round( aboveMin / options.step ) * options.step;
-
-		// - rounding is based on 0, so adjust back to our base
-		value = base + aboveMin;
-
-		// Fix precision from bad JS floating point math
-		value = parseFloat( value.toFixed( this._precision() ) );
-
-		// Clamp the value
-		if ( options.max !== null && value > options.max ) {
-			return options.max;
-		}
-		if ( options.min !== null && value < options.min ) {
-			return options.min;
-		}
-
-		return value;
-	},
-
-	_stop: function( event ) {
-		if ( !this.spinning ) {
-			return;
-		}
-
-		clearTimeout( this.timer );
-		clearTimeout( this.mousewheelTimer );
-		this.counter = 0;
-		this.spinning = false;
-		this._trigger( "stop", event );
-	},
-
-	_setOption: function( key, value ) {
-		var prevValue, first, last;
-
-		if ( key === "culture" || key === "numberFormat" ) {
-			prevValue = this._parse( this.element.val() );
-			this.options[ key ] = value;
-			this.element.val( this._format( prevValue ) );
-			return;
-		}
-
-		if ( key === "max" || key === "min" || key === "step" ) {
-			if ( typeof value === "string" ) {
-				value = this._parse( value );
-			}
-		}
-		if ( key === "icons" ) {
-			first = this.buttons.first().find( ".ui-icon" );
-			this._removeClass( first, null, this.options.icons.up );
-			this._addClass( first, null, value.up );
-			last = this.buttons.last().find( ".ui-icon" );
-			this._removeClass( last, null, this.options.icons.down );
-			this._addClass( last, null, value.down );
-		}
-
-		this._super( key, value );
-	},
-
-	_setOptionDisabled: function( value ) {
-		this._super( value );
-
-		this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
-		this.element.prop( "disabled", !!value );
-		this.buttons.button( value ? "disable" : "enable" );
-	},
-
-	_setOptions: spinnerModifer( function( options ) {
-		this._super( options );
-	} ),
-
-	_parse: function( val ) {
-		if ( typeof val === "string" && val !== "" ) {
-			val = window.Globalize && this.options.numberFormat ?
-				Globalize.parseFloat( val, 10, this.options.culture ) : +val;
-		}
-		return val === "" || isNaN( val ) ? null : val;
-	},
-
-	_format: function( value ) {
-		if ( value === "" ) {
-			return "";
-		}
-		return window.Globalize && this.options.numberFormat ?
-			Globalize.format( value, this.options.numberFormat, this.options.culture ) :
-			value;
-	},
-
-	_refresh: function() {
-		this.element.attr( {
-			"aria-valuemin": this.options.min,
-			"aria-valuemax": this.options.max,
-
-			// TODO: what should we do with values that can't be parsed?
-			"aria-valuenow": this._parse( this.element.val() )
-		} );
-	},
-
-	isValid: function() {
-		var value = this.value();
-
-		// Null is invalid
-		if ( value === null ) {
-			return false;
-		}
-
-		// If value gets adjusted, it's invalid
-		return value === this._adjustValue( value );
-	},
-
-	// Update the value without triggering change
-	_value: function( value, allowAny ) {
-		var parsed;
-		if ( value !== "" ) {
-			parsed = this._parse( value );
-			if ( parsed !== null ) {
-				if ( !allowAny ) {
-					parsed = this._adjustValue( parsed );
-				}
-				value = this._format( parsed );
-			}
-		}
-		this.element.val( value );
-		this._refresh();
-	},
-
-	_destroy: function() {
-		this.element
-			.prop( "disabled", false )
-			.removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" );
-
-		this.uiSpinner.replaceWith( this.element );
-	},
-
-	stepUp: spinnerModifer( function( steps ) {
-		this._stepUp( steps );
-	} ),
-	_stepUp: function( steps ) {
-		if ( this._start() ) {
-			this._spin( ( steps || 1 ) * this.options.step );
-			this._stop();
-		}
-	},
-
-	stepDown: spinnerModifer( function( steps ) {
-		this._stepDown( steps );
-	} ),
-	_stepDown: function( steps ) {
-		if ( this._start() ) {
-			this._spin( ( steps || 1 ) * -this.options.step );
-			this._stop();
-		}
-	},
-
-	pageUp: spinnerModifer( function( pages ) {
-		this._stepUp( ( pages || 1 ) * this.options.page );
-	} ),
-
-	pageDown: spinnerModifer( function( pages ) {
-		this._stepDown( ( pages || 1 ) * this.options.page );
-	} ),
-
-	value: function( newVal ) {
-		if ( !arguments.length ) {
-			return this._parse( this.element.val() );
-		}
-		spinnerModifer( this._value ).call( this, newVal );
-	},
-
-	widget: function() {
-		return this.uiSpinner;
-	}
-} );
-
-// DEPRECATED
-// TODO: switch return back to widget declaration at top of file when this is removed
-if ( $.uiBackCompat !== false ) {
-
-	// Backcompat for spinner html extension points
-	$.widget( "ui.spinner", $.ui.spinner, {
-		_enhance: function() {
-			this.uiSpinner = this.element
-				.attr( "autocomplete", "off" )
-				.wrap( this._uiSpinnerHtml() )
-				.parent()
-
-					// Add buttons
-					.append( this._buttonHtml() );
-		},
-		_uiSpinnerHtml: function() {
-			return "<span>";
-		},
-
-		_buttonHtml: function() {
-			return "<a></a><a></a>";
-		}
-	} );
-}
-
-var widgetsSpinner = $.ui.spinner;
-
-
-/*!
- * jQuery UI Tabs 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Tabs
-//>>group: Widgets
-//>>description: Transforms a set of container elements into a tab structure.
-//>>docs: http://api.jqueryui.com/tabs/
-//>>demos: http://jqueryui.com/tabs/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/tabs.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.tabs", {
-	version: "1.12.1",
-	delay: 300,
-	options: {
-		active: null,
-		classes: {
-			"ui-tabs": "ui-corner-all",
-			"ui-tabs-nav": "ui-corner-all",
-			"ui-tabs-panel": "ui-corner-bottom",
-			"ui-tabs-tab": "ui-corner-top"
-		},
-		collapsible: false,
-		event: "click",
-		heightStyle: "content",
-		hide: null,
-		show: null,
-
-		// Callbacks
-		activate: null,
-		beforeActivate: null,
-		beforeLoad: null,
-		load: null
-	},
-
-	_isLocal: ( function() {
-		var rhash = /#.*$/;
-
-		return function( anchor ) {
-			var anchorUrl, locationUrl;
-
-			anchorUrl = anchor.href.replace( rhash, "" );
-			locationUrl = location.href.replace( rhash, "" );
-
-			// Decoding may throw an error if the URL isn't UTF-8 (#9518)
-			try {
-				anchorUrl = decodeURIComponent( anchorUrl );
-			} catch ( error ) {}
-			try {
-				locationUrl = decodeURIComponent( locationUrl );
-			} catch ( error ) {}
-
-			return anchor.hash.length > 1 && anchorUrl === locationUrl;
-		};
-	} )(),
-
-	_create: function() {
-		var that = this,
-			options = this.options;
-
-		this.running = false;
-
-		this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
-		this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
-
-		this._processTabs();
-		options.active = this._initialActive();
-
-		// Take disabling tabs via class attribute from HTML
-		// into account and update option properly.
-		if ( $.isArray( options.disabled ) ) {
-			options.disabled = $.unique( options.disabled.concat(
-				$.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
-					return that.tabs.index( li );
-				} )
-			) ).sort();
-		}
-
-		// Check for length avoids error when initializing empty list
-		if ( this.options.active !== false && this.anchors.length ) {
-			this.active = this._findActive( options.active );
-		} else {
-			this.active = $();
-		}
-
-		this._refresh();
-
-		if ( this.active.length ) {
-			this.load( options.active );
-		}
-	},
-
-	_initialActive: function() {
-		var active = this.options.active,
-			collapsible = this.options.collapsible,
-			locationHash = location.hash.substring( 1 );
-
-		if ( active === null ) {
-
-			// check the fragment identifier in the URL
-			if ( locationHash ) {
-				this.tabs.each( function( i, tab ) {
-					if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
-						active = i;
-						return false;
-					}
-				} );
-			}
-
-			// Check for a tab marked active via a class
-			if ( active === null ) {
-				active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
-			}
-
-			// No active tab, set to false
-			if ( active === null || active === -1 ) {
-				active = this.tabs.length ? 0 : false;
-			}
-		}
-
-		// Handle numbers: negative, out of range
-		if ( active !== false ) {
-			active = this.tabs.index( this.tabs.eq( active ) );
-			if ( active === -1 ) {
-				active = collapsible ? false : 0;
-			}
-		}
-
-		// Don't allow collapsible: false and active: false
-		if ( !collapsible && active === false && this.anchors.length ) {
-			active = 0;
-		}
-
-		return active;
-	},
-
-	_getCreateEventData: function() {
-		return {
-			tab: this.active,
-			panel: !this.active.length ? $() : this._getPanelForTab( this.active )
-		};
-	},
-
-	_tabKeydown: function( event ) {
-		var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
-			selectedIndex = this.tabs.index( focusedTab ),
-			goingForward = true;
-
-		if ( this._handlePageNav( event ) ) {
-			return;
-		}
-
-		switch ( event.keyCode ) {
-		case $.ui.keyCode.RIGHT:
-		case $.ui.keyCode.DOWN:
-			selectedIndex++;
-			break;
-		case $.ui.keyCode.UP:
-		case $.ui.keyCode.LEFT:
-			goingForward = false;
-			selectedIndex--;
-			break;
-		case $.ui.keyCode.END:
-			selectedIndex = this.anchors.length - 1;
-			break;
-		case $.ui.keyCode.HOME:
-			selectedIndex = 0;
-			break;
-		case $.ui.keyCode.SPACE:
-
-			// Activate only, no collapsing
-			event.preventDefault();
-			clearTimeout( this.activating );
-			this._activate( selectedIndex );
-			return;
-		case $.ui.keyCode.ENTER:
-
-			// Toggle (cancel delayed activation, allow collapsing)
-			event.preventDefault();
-			clearTimeout( this.activating );
-
-			// Determine if we should collapse or activate
-			this._activate( selectedIndex === this.options.active ? false : selectedIndex );
-			return;
-		default:
-			return;
-		}
-
-		// Focus the appropriate tab, based on which key was pressed
-		event.preventDefault();
-		clearTimeout( this.activating );
-		selectedIndex = this._focusNextTab( selectedIndex, goingForward );
-
-		// Navigating with control/command key will prevent automatic activation
-		if ( !event.ctrlKey && !event.metaKey ) {
-
-			// Update aria-selected immediately so that AT think the tab is already selected.
-			// Otherwise AT may confuse the user by stating that they need to activate the tab,
-			// but the tab will already be activated by the time the announcement finishes.
-			focusedTab.attr( "aria-selected", "false" );
-			this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
-
-			this.activating = this._delay( function() {
-				this.option( "active", selectedIndex );
-			}, this.delay );
-		}
-	},
-
-	_panelKeydown: function( event ) {
-		if ( this._handlePageNav( event ) ) {
-			return;
-		}
-
-		// Ctrl+up moves focus to the current tab
-		if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
-			event.preventDefault();
-			this.active.trigger( "focus" );
-		}
-	},
-
-	// Alt+page up/down moves focus to the previous/next tab (and activates)
-	_handlePageNav: function( event ) {
-		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
-			this._activate( this._focusNextTab( this.options.active - 1, false ) );
-			return true;
-		}
-		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
-			this._activate( this._focusNextTab( this.options.active + 1, true ) );
-			return true;
-		}
-	},
-
-	_findNextTab: function( index, goingForward ) {
-		var lastTabIndex = this.tabs.length - 1;
-
-		function constrain() {
-			if ( index > lastTabIndex ) {
-				index = 0;
-			}
-			if ( index < 0 ) {
-				index = lastTabIndex;
-			}
-			return index;
-		}
-
-		while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
-			index = goingForward ? index + 1 : index - 1;
-		}
-
-		return index;
-	},
-
-	_focusNextTab: function( index, goingForward ) {
-		index = this._findNextTab( index, goingForward );
-		this.tabs.eq( index ).trigger( "focus" );
-		return index;
-	},
-
-	_setOption: function( key, value ) {
-		if ( key === "active" ) {
-
-			// _activate() will handle invalid values and update this.options
-			this._activate( value );
-			return;
-		}
-
-		this._super( key, value );
-
-		if ( key === "collapsible" ) {
-			this._toggleClass( "ui-tabs-collapsible", null, value );
-
-			// Setting collapsible: false while collapsed; open first panel
-			if ( !value && this.options.active === false ) {
-				this._activate( 0 );
-			}
-		}
-
-		if ( key === "event" ) {
-			this._setupEvents( value );
-		}
-
-		if ( key === "heightStyle" ) {
-			this._setupHeightStyle( value );
-		}
-	},
-
-	_sanitizeSelector: function( hash ) {
-		return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
-	},
-
-	refresh: function() {
-		var options = this.options,
-			lis = this.tablist.children( ":has(a[href])" );
-
-		// Get disabled tabs from class attribute from HTML
-		// this will get converted to a boolean if needed in _refresh()
-		options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
-			return lis.index( tab );
-		} );
-
-		this._processTabs();
-
-		// Was collapsed or no tabs
-		if ( options.active === false || !this.anchors.length ) {
-			options.active = false;
-			this.active = $();
-
-		// was active, but active tab is gone
-		} else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
-
-			// all remaining tabs are disabled
-			if ( this.tabs.length === options.disabled.length ) {
-				options.active = false;
-				this.active = $();
-
-			// activate previous tab
-			} else {
-				this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
-			}
-
-		// was active, active tab still exists
-		} else {
-
-			// make sure active index is correct
-			options.active = this.tabs.index( this.active );
-		}
-
-		this._refresh();
-	},
-
-	_refresh: function() {
-		this._setOptionDisabled( this.options.disabled );
-		this._setupEvents( this.options.event );
-		this._setupHeightStyle( this.options.heightStyle );
-
-		this.tabs.not( this.active ).attr( {
-			"aria-selected": "false",
-			"aria-expanded": "false",
-			tabIndex: -1
-		} );
-		this.panels.not( this._getPanelForTab( this.active ) )
-			.hide()
-			.attr( {
-				"aria-hidden": "true"
-			} );
-
-		// Make sure one tab is in the tab order
-		if ( !this.active.length ) {
-			this.tabs.eq( 0 ).attr( "tabIndex", 0 );
-		} else {
-			this.active
-				.attr( {
-					"aria-selected": "true",
-					"aria-expanded": "true",
-					tabIndex: 0
-				} );
-			this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
-			this._getPanelForTab( this.active )
-				.show()
-				.attr( {
-					"aria-hidden": "false"
-				} );
-		}
-	},
-
-	_processTabs: function() {
-		var that = this,
-			prevTabs = this.tabs,
-			prevAnchors = this.anchors,
-			prevPanels = this.panels;
-
-		this.tablist = this._getList().attr( "role", "tablist" );
-		this._addClass( this.tablist, "ui-tabs-nav",
-			"ui-helper-reset ui-helper-clearfix ui-widget-header" );
-
-		// Prevent users from focusing disabled tabs via click
-		this.tablist
-			.on( "mousedown" + this.eventNamespace, "> li", function( event ) {
-				if ( $( this ).is( ".ui-state-disabled" ) ) {
-					event.preventDefault();
-				}
-			} )
-
-			// Support: IE <9
-			// Preventing the default action in mousedown doesn't prevent IE
-			// from focusing the element, so if the anchor gets focused, blur.
-			// We don't have to worry about focusing the previously focused
-			// element since clicking on a non-focusable element should focus
-			// the body anyway.
-			.on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
-				if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
-					this.blur();
-				}
-			} );
-
-		this.tabs = this.tablist.find( "> li:has(a[href])" )
-			.attr( {
-				role: "tab",
-				tabIndex: -1
-			} );
-		this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
-
-		this.anchors = this.tabs.map( function() {
-			return $( "a", this )[ 0 ];
-		} )
-			.attr( {
-				role: "presentation",
-				tabIndex: -1
-			} );
-		this._addClass( this.anchors, "ui-tabs-anchor" );
-
-		this.panels = $();
-
-		this.anchors.each( function( i, anchor ) {
-			var selector, panel, panelId,
-				anchorId = $( anchor ).uniqueId().attr( "id" ),
-				tab = $( anchor ).closest( "li" ),
-				originalAriaControls = tab.attr( "aria-controls" );
-
-			// Inline tab
-			if ( that._isLocal( anchor ) ) {
-				selector = anchor.hash;
-				panelId = selector.substring( 1 );
-				panel = that.element.find( that._sanitizeSelector( selector ) );
-
-			// remote tab
-			} else {
-
-				// If the tab doesn't already have aria-controls,
-				// generate an id by using a throw-away element
-				panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
-				selector = "#" + panelId;
-				panel = that.element.find( selector );
-				if ( !panel.length ) {
-					panel = that._createPanel( panelId );
-					panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
-				}
-				panel.attr( "aria-live", "polite" );
-			}
-
-			if ( panel.length ) {
-				that.panels = that.panels.add( panel );
-			}
-			if ( originalAriaControls ) {
-				tab.data( "ui-tabs-aria-controls", originalAriaControls );
-			}
-			tab.attr( {
-				"aria-controls": panelId,
-				"aria-labelledby": anchorId
-			} );
-			panel.attr( "aria-labelledby", anchorId );
-		} );
-
-		this.panels.attr( "role", "tabpanel" );
-		this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
-
-		// Avoid memory leaks (#10056)
-		if ( prevTabs ) {
-			this._off( prevTabs.not( this.tabs ) );
-			this._off( prevAnchors.not( this.anchors ) );
-			this._off( prevPanels.not( this.panels ) );
-		}
-	},
-
-	// Allow overriding how to find the list for rare usage scenarios (#7715)
-	_getList: function() {
-		return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
-	},
-
-	_createPanel: function( id ) {
-		return $( "<div>" )
-			.attr( "id", id )
-			.data( "ui-tabs-destroy", true );
-	},
-
-	_setOptionDisabled: function( disabled ) {
-		var currentItem, li, i;
-
-		if ( $.isArray( disabled ) ) {
-			if ( !disabled.length ) {
-				disabled = false;
-			} else if ( disabled.length === this.anchors.length ) {
-				disabled = true;
-			}
-		}
-
-		// Disable tabs
-		for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
-			currentItem = $( li );
-			if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
-				currentItem.attr( "aria-disabled", "true" );
-				this._addClass( currentItem, null, "ui-state-disabled" );
-			} else {
-				currentItem.removeAttr( "aria-disabled" );
-				this._removeClass( currentItem, null, "ui-state-disabled" );
-			}
-		}
-
-		this.options.disabled = disabled;
-
-		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
-			disabled === true );
-	},
-
-	_setupEvents: function( event ) {
-		var events = {};
-		if ( event ) {
-			$.each( event.split( " " ), function( index, eventName ) {
-				events[ eventName ] = "_eventHandler";
-			} );
-		}
-
-		this._off( this.anchors.add( this.tabs ).add( this.panels ) );
-
-		// Always prevent the default action, even when disabled
-		this._on( true, this.anchors, {
-			click: function( event ) {
-				event.preventDefault();
-			}
-		} );
-		this._on( this.anchors, events );
-		this._on( this.tabs, { keydown: "_tabKeydown" } );
-		this._on( this.panels, { keydown: "_panelKeydown" } );
-
-		this._focusable( this.tabs );
-		this._hoverable( this.tabs );
-	},
-
-	_setupHeightStyle: function( heightStyle ) {
-		var maxHeight,
-			parent = this.element.parent();
-
-		if ( heightStyle === "fill" ) {
-			maxHeight = parent.height();
-			maxHeight -= this.element.outerHeight() - this.element.height();
-
-			this.element.siblings( ":visible" ).each( function() {
-				var elem = $( this ),
-					position = elem.css( "position" );
-
-				if ( position === "absolute" || position === "fixed" ) {
-					return;
-				}
-				maxHeight -= elem.outerHeight( true );
-			} );
-
-			this.element.children().not( this.panels ).each( function() {
-				maxHeight -= $( this ).outerHeight( true );
-			} );
-
-			this.panels.each( function() {
-				$( this ).height( Math.max( 0, maxHeight -
-					$( this ).innerHeight() + $( this ).height() ) );
-			} )
-				.css( "overflow", "auto" );
-		} else if ( heightStyle === "auto" ) {
-			maxHeight = 0;
-			this.panels.each( function() {
-				maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
-			} ).height( maxHeight );
-		}
-	},
-
-	_eventHandler: function( event ) {
-		var options = this.options,
-			active = this.active,
-			anchor = $( event.currentTarget ),
-			tab = anchor.closest( "li" ),
-			clickedIsActive = tab[ 0 ] === active[ 0 ],
-			collapsing = clickedIsActive && options.collapsible,
-			toShow = collapsing ? $() : this._getPanelForTab( tab ),
-			toHide = !active.length ? $() : this._getPanelForTab( active ),
-			eventData = {
-				oldTab: active,
-				oldPanel: toHide,
-				newTab: collapsing ? $() : tab,
-				newPanel: toShow
-			};
-
-		event.preventDefault();
-
-		if ( tab.hasClass( "ui-state-disabled" ) ||
-
-				// tab is already loading
-				tab.hasClass( "ui-tabs-loading" ) ||
-
-				// can't switch durning an animation
-				this.running ||
-
-				// click on active header, but not collapsible
-				( clickedIsActive && !options.collapsible ) ||
-
-				// allow canceling activation
-				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
-			return;
-		}
-
-		options.active = collapsing ? false : this.tabs.index( tab );
-
-		this.active = clickedIsActive ? $() : tab;
-		if ( this.xhr ) {
-			this.xhr.abort();
-		}
-
-		if ( !toHide.length && !toShow.length ) {
-			$.error( "jQuery UI Tabs: Mismatching fragment identifier." );
-		}
-
-		if ( toShow.length ) {
-			this.load( this.tabs.index( tab ), event );
-		}
-		this._toggle( event, eventData );
-	},
-
-	// Handles show/hide for selecting tabs
-	_toggle: function( event, eventData ) {
-		var that = this,
-			toShow = eventData.newPanel,
-			toHide = eventData.oldPanel;
-
-		this.running = true;
-
-		function complete() {
-			that.running = false;
-			that._trigger( "activate", event, eventData );
-		}
-
-		function show() {
-			that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
-
-			if ( toShow.length && that.options.show ) {
-				that._show( toShow, that.options.show, complete );
-			} else {
-				toShow.show();
-				complete();
-			}
-		}
-
-		// Start out by hiding, then showing, then completing
-		if ( toHide.length && this.options.hide ) {
-			this._hide( toHide, this.options.hide, function() {
-				that._removeClass( eventData.oldTab.closest( "li" ),
-					"ui-tabs-active", "ui-state-active" );
-				show();
-			} );
-		} else {
-			this._removeClass( eventData.oldTab.closest( "li" ),
-				"ui-tabs-active", "ui-state-active" );
-			toHide.hide();
-			show();
-		}
-
-		toHide.attr( "aria-hidden", "true" );
-		eventData.oldTab.attr( {
-			"aria-selected": "false",
-			"aria-expanded": "false"
-		} );
-
-		// If we're switching tabs, remove the old tab from the tab order.
-		// If we're opening from collapsed state, remove the previous tab from the tab order.
-		// If we're collapsing, then keep the collapsing tab in the tab order.
-		if ( toShow.length && toHide.length ) {
-			eventData.oldTab.attr( "tabIndex", -1 );
-		} else if ( toShow.length ) {
-			this.tabs.filter( function() {
-				return $( this ).attr( "tabIndex" ) === 0;
-			} )
-				.attr( "tabIndex", -1 );
-		}
-
-		toShow.attr( "aria-hidden", "false" );
-		eventData.newTab.attr( {
-			"aria-selected": "true",
-			"aria-expanded": "true",
-			tabIndex: 0
-		} );
-	},
-
-	_activate: function( index ) {
-		var anchor,
-			active = this._findActive( index );
-
-		// Trying to activate the already active panel
-		if ( active[ 0 ] === this.active[ 0 ] ) {
-			return;
-		}
-
-		// Trying to collapse, simulate a click on the current active header
-		if ( !active.length ) {
-			active = this.active;
-		}
-
-		anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
-		this._eventHandler( {
-			target: anchor,
-			currentTarget: anchor,
-			preventDefault: $.noop
-		} );
-	},
-
-	_findActive: function( index ) {
-		return index === false ? $() : this.tabs.eq( index );
-	},
-
-	_getIndex: function( index ) {
-
-		// meta-function to give users option to provide a href string instead of a numerical index.
-		if ( typeof index === "string" ) {
-			index = this.anchors.index( this.anchors.filter( "[href$='" +
-				$.ui.escapeSelector( index ) + "']" ) );
-		}
-
-		return index;
-	},
-
-	_destroy: function() {
-		if ( this.xhr ) {
-			this.xhr.abort();
-		}
-
-		this.tablist
-			.removeAttr( "role" )
-			.off( this.eventNamespace );
-
-		this.anchors
-			.removeAttr( "role tabIndex" )
-			.removeUniqueId();
-
-		this.tabs.add( this.panels ).each( function() {
-			if ( $.data( this, "ui-tabs-destroy" ) ) {
-				$( this ).remove();
-			} else {
-				$( this ).removeAttr( "role tabIndex " +
-					"aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
-			}
-		} );
-
-		this.tabs.each( function() {
-			var li = $( this ),
-				prev = li.data( "ui-tabs-aria-controls" );
-			if ( prev ) {
-				li
-					.attr( "aria-controls", prev )
-					.removeData( "ui-tabs-aria-controls" );
-			} else {
-				li.removeAttr( "aria-controls" );
-			}
-		} );
-
-		this.panels.show();
-
-		if ( this.options.heightStyle !== "content" ) {
-			this.panels.css( "height", "" );
-		}
-	},
-
-	enable: function( index ) {
-		var disabled = this.options.disabled;
-		if ( disabled === false ) {
-			return;
-		}
-
-		if ( index === undefined ) {
-			disabled = false;
-		} else {
-			index = this._getIndex( index );
-			if ( $.isArray( disabled ) ) {
-				disabled = $.map( disabled, function( num ) {
-					return num !== index ? num : null;
-				} );
-			} else {
-				disabled = $.map( this.tabs, function( li, num ) {
-					return num !== index ? num : null;
-				} );
-			}
-		}
-		this._setOptionDisabled( disabled );
-	},
-
-	disable: function( index ) {
-		var disabled = this.options.disabled;
-		if ( disabled === true ) {
-			return;
-		}
-
-		if ( index === undefined ) {
-			disabled = true;
-		} else {
-			index = this._getIndex( index );
-			if ( $.inArray( index, disabled ) !== -1 ) {
-				return;
-			}
-			if ( $.isArray( disabled ) ) {
-				disabled = $.merge( [ index ], disabled ).sort();
-			} else {
-				disabled = [ index ];
-			}
-		}
-		this._setOptionDisabled( disabled );
-	},
-
-	load: function( index, event ) {
-		index = this._getIndex( index );
-		var that = this,
-			tab = this.tabs.eq( index ),
-			anchor = tab.find( ".ui-tabs-anchor" ),
-			panel = this._getPanelForTab( tab ),
-			eventData = {
-				tab: tab,
-				panel: panel
-			},
-			complete = function( jqXHR, status ) {
-				if ( status === "abort" ) {
-					that.panels.stop( false, true );
-				}
-
-				that._removeClass( tab, "ui-tabs-loading" );
-				panel.removeAttr( "aria-busy" );
-
-				if ( jqXHR === that.xhr ) {
-					delete that.xhr;
-				}
-			};
-
-		// Not remote
-		if ( this._isLocal( anchor[ 0 ] ) ) {
-			return;
-		}
-
-		this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
-
-		// Support: jQuery <1.8
-		// jQuery <1.8 returns false if the request is canceled in beforeSend,
-		// but as of 1.8, $.ajax() always returns a jqXHR object.
-		if ( this.xhr && this.xhr.statusText !== "canceled" ) {
-			this._addClass( tab, "ui-tabs-loading" );
-			panel.attr( "aria-busy", "true" );
-
-			this.xhr
-				.done( function( response, status, jqXHR ) {
-
-					// support: jQuery <1.8
-					// http://bugs.jquery.com/ticket/11778
-					setTimeout( function() {
-						panel.html( response );
-						that._trigger( "load", event, eventData );
-
-						complete( jqXHR, status );
-					}, 1 );
-				} )
-				.fail( function( jqXHR, status ) {
-
-					// support: jQuery <1.8
-					// http://bugs.jquery.com/ticket/11778
-					setTimeout( function() {
-						complete( jqXHR, status );
-					}, 1 );
-				} );
-		}
-	},
-
-	_ajaxSettings: function( anchor, event, eventData ) {
-		var that = this;
-		return {
-
-			// Support: IE <11 only
-			// Strip any hash that exists to prevent errors with the Ajax request
-			url: anchor.attr( "href" ).replace( /#.*$/, "" ),
-			beforeSend: function( jqXHR, settings ) {
-				return that._trigger( "beforeLoad", event,
-					$.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
-			}
-		};
-	},
-
-	_getPanelForTab: function( tab ) {
-		var id = $( tab ).attr( "aria-controls" );
-		return this.element.find( this._sanitizeSelector( "#" + id ) );
-	}
-} );
-
-// DEPRECATED
-// TODO: Switch return back to widget declaration at top of file when this is removed
-if ( $.uiBackCompat !== false ) {
-
-	// Backcompat for ui-tab class (now ui-tabs-tab)
-	$.widget( "ui.tabs", $.ui.tabs, {
-		_processTabs: function() {
-			this._superApply( arguments );
-			this._addClass( this.tabs, "ui-tab" );
-		}
-	} );
-}
-
-var widgetsTabs = $.ui.tabs;
-
-
-/*!
- * jQuery UI Tooltip 1.12.1
- * http://jqueryui.com
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-//>>label: Tooltip
-//>>group: Widgets
-//>>description: Shows additional information for any element on hover or focus.
-//>>docs: http://api.jqueryui.com/tooltip/
-//>>demos: http://jqueryui.com/tooltip/
-//>>css.structure: ../../themes/base/core.css
-//>>css.structure: ../../themes/base/tooltip.css
-//>>css.theme: ../../themes/base/theme.css
-
-
-
-$.widget( "ui.tooltip", {
-	version: "1.12.1",
-	options: {
-		classes: {
-			"ui-tooltip": "ui-corner-all ui-widget-shadow"
-		},
-		content: function() {
-
-			// support: IE<9, Opera in jQuery <1.7
-			// .text() can't accept undefined, so coerce to a string
-			var title = $( this ).attr( "title" ) || "";
-
-			// Escape title, since we're going from an attribute to raw HTML
-			return $( "<a>" ).text( title ).html();
-		},
-		hide: true,
-
-		// Disabled elements have inconsistent behavior across browsers (#8661)
-		items: "[title]:not([disabled])",
-		position: {
-			my: "left top+15",
-			at: "left bottom",
-			collision: "flipfit flip"
-		},
-		show: true,
-		track: false,
-
-		// Callbacks
-		close: null,
-		open: null
-	},
-
-	_addDescribedBy: function( elem, id ) {
-		var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
-		describedby.push( id );
-		elem
-			.data( "ui-tooltip-id", id )
-			.attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
-	},
-
-	_removeDescribedBy: function( elem ) {
-		var id = elem.data( "ui-tooltip-id" ),
-			describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
-			index = $.inArray( id, describedby );
-
-		if ( index !== -1 ) {
-			describedby.splice( index, 1 );
-		}
-
-		elem.removeData( "ui-tooltip-id" );
-		describedby = $.trim( describedby.join( " " ) );
-		if ( describedby ) {
-			elem.attr( "aria-describedby", describedby );
-		} else {
-			elem.removeAttr( "aria-describedby" );
-		}
-	},
-
-	_create: function() {
-		this._on( {
-			mouseover: "open",
-			focusin: "open"
-		} );
-
-		// IDs of generated tooltips, needed for destroy
-		this.tooltips = {};
-
-		// IDs of parent tooltips where we removed the title attribute
-		this.parents = {};
-
-		// Append the aria-live region so tooltips announce correctly
-		this.liveRegion = $( "<div>" )
-			.attr( {
-				role: "log",
-				"aria-live": "assertive",
-				"aria-relevant": "additions"
-			} )
-			.appendTo( this.document[ 0 ].body );
-		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
-
-		this.disabledTitles = $( [] );
-	},
-
-	_setOption: function( key, value ) {
-		var that = this;
-
-		this._super( key, value );
-
-		if ( key === "content" ) {
-			$.each( this.tooltips, function( id, tooltipData ) {
-				that._updateContent( tooltipData.element );
-			} );
-		}
-	},
-
-	_setOptionDisabled: function( value ) {
-		this[ value ? "_disable" : "_enable" ]();
-	},
-
-	_disable: function() {
-		var that = this;
-
-		// Close open tooltips
-		$.each( this.tooltips, function( id, tooltipData ) {
-			var event = $.Event( "blur" );
-			event.target = event.currentTarget = tooltipData.element[ 0 ];
-			that.close( event, true );
-		} );
-
-		// Remove title attributes to prevent native tooltips
-		this.disabledTitles = this.disabledTitles.add(
-			this.element.find( this.options.items ).addBack()
-				.filter( function() {
-					var element = $( this );
-					if ( element.is( "[title]" ) ) {
-						return element
-							.data( "ui-tooltip-title", element.attr( "title" ) )
-							.removeAttr( "title" );
-					}
-				} )
-		);
-	},
-
-	_enable: function() {
-
-		// restore title attributes
-		this.disabledTitles.each( function() {
-			var element = $( this );
-			if ( element.data( "ui-tooltip-title" ) ) {
-				element.attr( "title", element.data( "ui-tooltip-title" ) );
-			}
-		} );
-		this.disabledTitles = $( [] );
-	},
-
-	open: function( event ) {
-		var that = this,
-			target = $( event ? event.target : this.element )
-
-				// we need closest here due to mouseover bubbling,
-				// but always pointing at the same event target
-				.closest( this.options.items );
-
-		// No element to show a tooltip for or the tooltip is already open
-		if ( !target.length || target.data( "ui-tooltip-id" ) ) {
-			return;
-		}
-
-		if ( target.attr( "title" ) ) {
-			target.data( "ui-tooltip-title", target.attr( "title" ) );
-		}
-
-		target.data( "ui-tooltip-open", true );
-
-		// Kill parent tooltips, custom or native, for hover
-		if ( event && event.type === "mouseover" ) {
-			target.parents().each( function() {
-				var parent = $( this ),
-					blurEvent;
-				if ( parent.data( "ui-tooltip-open" ) ) {
-					blurEvent = $.Event( "blur" );
-					blurEvent.target = blurEvent.currentTarget = this;
-					that.close( blurEvent, true );
-				}
-				if ( parent.attr( "title" ) ) {
-					parent.uniqueId();
-					that.parents[ this.id ] = {
-						element: this,
-						title: parent.attr( "title" )
-					};
-					parent.attr( "title", "" );
-				}
-			} );
-		}
-
-		this._registerCloseHandlers( event, target );
-		this._updateContent( target, event );
-	},
-
-	_updateContent: function( target, event ) {
-		var content,
-			contentOption = this.options.content,
-			that = this,
-			eventType = event ? event.type : null;
-
-		if ( typeof contentOption === "string" || contentOption.nodeType ||
-				contentOption.jquery ) {
-			return this._open( event, target, contentOption );
-		}
-
-		content = contentOption.call( target[ 0 ], function( response ) {
-
-			// IE may instantly serve a cached response for ajax requests
-			// delay this call to _open so the other call to _open runs first
-			that._delay( function() {
-
-				// Ignore async response if tooltip was closed already
-				if ( !target.data( "ui-tooltip-open" ) ) {
-					return;
-				}
-
-				// JQuery creates a special event for focusin when it doesn't
-				// exist natively. To improve performance, the native event
-				// object is reused and the type is changed. Therefore, we can't
-				// rely on the type being correct after the event finished
-				// bubbling, so we set it back to the previous value. (#8740)
-				if ( event ) {
-					event.type = eventType;
-				}
-				this._open( event, target, response );
-			} );
-		} );
-		if ( content ) {
-			this._open( event, target, content );
-		}
-	},
-
-	_open: function( event, target, content ) {
-		var tooltipData, tooltip, delayedShow, a11yContent,
-			positionOption = $.extend( {}, this.options.position );
-
-		if ( !content ) {
-			return;
-		}
-
-		// Content can be updated multiple times. If the tooltip already
-		// exists, then just update the content and bail.
-		tooltipData = this._find( target );
-		if ( tooltipData ) {
-			tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
-			return;
-		}
-
-		// If we have a title, clear it to prevent the native tooltip
-		// we have to check first to avoid defining a title if none exists
-		// (we don't want to cause an element to start matching [title])
-		//
-		// We use removeAttr only for key events, to allow IE to export the correct
-		// accessible attributes. For mouse events, set to empty string to avoid
-		// native tooltip showing up (happens only when removing inside mouseover).
-		if ( target.is( "[title]" ) ) {
-			if ( event && event.type === "mouseover" ) {
-				target.attr( "title", "" );
-			} else {
-				target.removeAttr( "title" );
-			}
-		}
-
-		tooltipData = this._tooltip( target );
-		tooltip = tooltipData.tooltip;
-		this._addDescribedBy( target, tooltip.attr( "id" ) );
-		tooltip.find( ".ui-tooltip-content" ).html( content );
-
-		// Support: Voiceover on OS X, JAWS on IE <= 9
-		// JAWS announces deletions even when aria-relevant="additions"
-		// Voiceover will sometimes re-read the entire log region's contents from the beginning
-		this.liveRegion.children().hide();
-		a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
-		a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
-		a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
-		a11yContent.appendTo( this.liveRegion );
-
-		function position( event ) {
-			positionOption.of = event;
-			if ( tooltip.is( ":hidden" ) ) {
-				return;
-			}
-			tooltip.position( positionOption );
-		}
-		if ( this.options.track && event && /^mouse/.test( event.type ) ) {
-			this._on( this.document, {
-				mousemove: position
-			} );
-
-			// trigger once to override element-relative positioning
-			position( event );
-		} else {
-			tooltip.position( $.extend( {
-				of: target
-			}, this.options.position ) );
-		}
-
-		tooltip.hide();
-
-		this._show( tooltip, this.options.show );
-
-		// Handle tracking tooltips that are shown with a delay (#8644). As soon
-		// as the tooltip is visible, position the tooltip using the most recent
-		// event.
-		// Adds the check to add the timers only when both delay and track options are set (#14682)
-		if ( this.options.track && this.options.show && this.options.show.delay ) {
-			delayedShow = this.delayedShow = setInterval( function() {
-				if ( tooltip.is( ":visible" ) ) {
-					position( positionOption.of );
-					clearInterval( delayedShow );
-				}
-			}, $.fx.interval );
-		}
-
-		this._trigger( "open", event, { tooltip: tooltip } );
-	},
-
-	_registerCloseHandlers: function( event, target ) {
-		var events = {
-			keyup: function( event ) {
-				if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
-					var fakeEvent = $.Event( event );
-					fakeEvent.currentTarget = target[ 0 ];
-					this.close( fakeEvent, true );
-				}
-			}
-		};
-
-		// Only bind remove handler for delegated targets. Non-delegated
-		// tooltips will handle this in destroy.
-		if ( target[ 0 ] !== this.element[ 0 ] ) {
-			events.remove = function() {
-				this._removeTooltip( this._find( target ).tooltip );
-			};
-		}
-
-		if ( !event || event.type === "mouseover" ) {
-			events.mouseleave = "close";
-		}
-		if ( !event || event.type === "focusin" ) {
-			events.focusout = "close";
-		}
-		this._on( true, target, events );
-	},
-
-	close: function( event ) {
-		var tooltip,
-			that = this,
-			target = $( event ? event.currentTarget : this.element ),
-			tooltipData = this._find( target );
-
-		// The tooltip may already be closed
-		if ( !tooltipData ) {
-
-			// We set ui-tooltip-open immediately upon open (in open()), but only set the
-			// additional data once there's actually content to show (in _open()). So even if the
-			// tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
-			// the period between open() and _open().
-			target.removeData( "ui-tooltip-open" );
-			return;
-		}
-
-		tooltip = tooltipData.tooltip;
-
-		// Disabling closes the tooltip, so we need to track when we're closing
-		// to avoid an infinite loop in case the tooltip becomes disabled on close
-		if ( tooltipData.closing ) {
-			return;
-		}
-
-		// Clear the interval for delayed tracking tooltips
-		clearInterval( this.delayedShow );
-
-		// Only set title if we had one before (see comment in _open())
-		// If the title attribute has changed since open(), don't restore
-		if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
-			target.attr( "title", target.data( "ui-tooltip-title" ) );
-		}
-
-		this._removeDescribedBy( target );
-
-		tooltipData.hiding = true;
-		tooltip.stop( true );
-		this._hide( tooltip, this.options.hide, function() {
-			that._removeTooltip( $( this ) );
-		} );
-
-		target.removeData( "ui-tooltip-open" );
-		this._off( target, "mouseleave focusout keyup" );
-
-		// Remove 'remove' binding only on delegated targets
-		if ( target[ 0 ] !== this.element[ 0 ] ) {
-			this._off( target, "remove" );
-		}
-		this._off( this.document, "mousemove" );
-
-		if ( event && event.type === "mouseleave" ) {
-			$.each( this.parents, function( id, parent ) {
-				$( parent.element ).attr( "title", parent.title );
-				delete that.parents[ id ];
-			} );
-		}
-
-		tooltipData.closing = true;
-		this._trigger( "close", event, { tooltip: tooltip } );
-		if ( !tooltipData.hiding ) {
-			tooltipData.closing = false;
-		}
-	},
-
-	_tooltip: function( element ) {
-		var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
-			content = $( "<div>" ).appendTo( tooltip ),
-			id = tooltip.uniqueId().attr( "id" );
-
-		this._addClass( content, "ui-tooltip-content" );
-		this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
-
-		tooltip.appendTo( this._appendTo( element ) );
-
-		return this.tooltips[ id ] = {
-			element: element,
-			tooltip: tooltip
-		};
-	},
-
-	_find: function( target ) {
-		var id = target.data( "ui-tooltip-id" );
-		return id ? this.tooltips[ id ] : null;
-	},
-
-	_removeTooltip: function( tooltip ) {
-		tooltip.remove();
-		delete this.tooltips[ tooltip.attr( "id" ) ];
-	},
-
-	_appendTo: function( target ) {
-		var element = target.closest( ".ui-front, dialog" );
-
-		if ( !element.length ) {
-			element = this.document[ 0 ].body;
-		}
-
-		return element;
-	},
-
-	_destroy: function() {
-		var that = this;
-
-		// Close open tooltips
-		$.each( this.tooltips, function( id, tooltipData ) {
-
-			// Delegate to close method to handle common cleanup
-			var event = $.Event( "blur" ),
-				element = tooltipData.element;
-			event.target = event.currentTarget = element[ 0 ];
-			that.close( event, true );
-
-			// Remove immediately; destroying an open tooltip doesn't use the
-			// hide animation
-			$( "#" + id ).remove();
-
-			// Restore the title
-			if ( element.data( "ui-tooltip-title" ) ) {
-
-				// If the title attribute has changed since open(), don't restore
-				if ( !element.attr( "title" ) ) {
-					element.attr( "title", element.data( "ui-tooltip-title" ) );
-				}
-				element.removeData( "ui-tooltip-title" );
-			}
-		} );
-		this.liveRegion.remove();
-	}
-} );
-
-// DEPRECATED
-// TODO: Switch return back to widget declaration at top of file when this is removed
-if ( $.uiBackCompat !== false ) {
-
-	// Backcompat for tooltipClass option
-	$.widget( "ui.tooltip", $.ui.tooltip, {
-		options: {
-			tooltipClass: null
-		},
-		_tooltip: function() {
-			var tooltipData = this._superApply( arguments );
-			if ( this.options.tooltipClass ) {
-				tooltipData.tooltip.addClass( this.options.tooltipClass );
-			}
-			return tooltipData;
-		}
-	} );
-}
-
-var widgetsTooltip = $.ui.tooltip;
-
-
-
-
-}));
\ No newline at end of file
diff --git a/apps/static/js/plugins/codemirror/codemirror.js b/apps/static/js/plugins/codemirror/codemirror.js
deleted file mode 100755
index 4f8a23bde..000000000
--- a/apps/static/js/plugins/codemirror/codemirror.js
+++ /dev/null
@@ -1,7830 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-// This is CodeMirror (http://codemirror.net), a code editor
-// implemented in JavaScript on top of the browser's DOM.
-//
-// You can find some technical background for some of the code below
-// at http://marijnhaverbeke.nl/blog/#cm-internals .
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    module.exports = mod();
-  else if (typeof define == "function" && define.amd) // AMD
-    return define([], mod);
-  else // Plain browser env
-    this.CodeMirror = mod();
-})(function() {
-  "use strict";
-
-  // BROWSER SNIFFING
-
-  // Kludges for bugs and behavior differences that can't be feature
-  // detected are enabled based on userAgent etc sniffing.
-
-  var gecko = /gecko\/\d/i.test(navigator.userAgent);
-  // ie_uptoN means Internet Explorer version N or lower
-  var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
-  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
-  var ie = ie_upto10 || ie_11up;
-  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
-  var webkit = /WebKit\//.test(navigator.userAgent);
-  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
-  var chrome = /Chrome\//.test(navigator.userAgent);
-  var presto = /Opera\//.test(navigator.userAgent);
-  var safari = /Apple Computer/.test(navigator.vendor);
-  var khtml = /KHTML\//.test(navigator.userAgent);
-  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
-  var phantom = /PhantomJS/.test(navigator.userAgent);
-
-  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
-  // This is woefully incomplete. Suggestions for alternative methods welcome.
-  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
-  var mac = ios || /Mac/.test(navigator.platform);
-  var windows = /win/i.test(navigator.platform);
-
-  var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
-  if (presto_version) presto_version = Number(presto_version[1]);
-  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
-  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
-  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
-  var captureRightClick = gecko || (ie && ie_version >= 9);
-
-  // Optimize some code when these features are not used.
-  var sawReadOnlySpans = false, sawCollapsedSpans = false;
-
-  // EDITOR CONSTRUCTOR
-
-  // A CodeMirror instance represents an editor. This is the object
-  // that user code is usually dealing with.
-
-  function CodeMirror(place, options) {
-    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
-
-    this.options = options = options ? copyObj(options) : {};
-    // Determine effective options based on given values and defaults.
-    copyObj(defaults, options, false);
-    setGuttersForLineNumbers(options);
-
-    var doc = options.value;
-    if (typeof doc == "string") doc = new Doc(doc, options.mode);
-    this.doc = doc;
-
-    var display = this.display = new Display(place, doc);
-    display.wrapper.CodeMirror = this;
-    updateGutters(this);
-    themeChanged(this);
-    if (options.lineWrapping)
-      this.display.wrapper.className += " CodeMirror-wrap";
-    if (options.autofocus && !mobile) focusInput(this);
-
-    this.state = {
-      keyMaps: [],  // stores maps added by addKeyMap
-      overlays: [], // highlighting overlays, as added by addOverlay
-      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
-      overwrite: false, focused: false,
-      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
-      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
-      draggingText: false,
-      highlight: new Delayed() // stores highlight worker timeout
-    };
-
-    // Override magic textarea content restore that IE sometimes does
-    // on our hidden textarea on reload
-    if (ie && ie_version < 11) setTimeout(bind(resetInput, this, true), 20);
-
-    registerEventHandlers(this);
-    ensureGlobalHandlers();
-
-    startOperation(this);
-    this.curOp.forceUpdate = true;
-    attachDoc(this, doc);
-
-    if ((options.autofocus && !mobile) || activeElt() == display.input)
-      setTimeout(bind(onFocus, this), 20);
-    else
-      onBlur(this);
-
-    for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
-      optionHandlers[opt](this, options[opt], Init);
-    maybeUpdateLineNumberWidth(this);
-    for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
-    endOperation(this);
-  }
-
-  // DISPLAY CONSTRUCTOR
-
-  // The display handles the DOM integration, both for input reading
-  // and content drawing. It holds references to DOM nodes and
-  // display-related state.
-
-  function Display(place, doc) {
-    var d = this;
-
-    // The semihidden textarea that is focused when the editor is
-    // focused, and receives input.
-    var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
-    // The textarea is kept positioned near the cursor to prevent the
-    // fact that it'll be scrolled into view on input from scrolling
-    // our fake cursor out of view. On webkit, when wrap=off, paste is
-    // very slow. So make the area wide instead.
-    if (webkit) input.style.width = "1000px";
-    else input.setAttribute("wrap", "off");
-    // If border: 0; -- iOS fails to open keyboard (issue #1287)
-    if (ios) input.style.border = "1px solid black";
-    input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
-
-    // Wraps and hides input textarea
-    d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
-    // The fake scrollbar elements.
-    d.scrollbarH = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
-    d.scrollbarV = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
-    // Covers bottom-right square when both scrollbars are present.
-    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
-    // Covers bottom of gutter when coverGutterNextToScrollbar is on
-    // and h scrollbar is present.
-    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
-    // Will contain the actual code, positioned to cover the viewport.
-    d.lineDiv = elt("div", null, "CodeMirror-code");
-    // Elements are added to these to represent selection and cursors.
-    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
-    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
-    // A visibility: hidden element used to find the size of things.
-    d.measure = elt("div", null, "CodeMirror-measure");
-    // When lines outside of the viewport are measured, they are drawn in this.
-    d.lineMeasure = elt("div", null, "CodeMirror-measure");
-    // Wraps everything that needs to exist inside the vertically-padded coordinate system
-    d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
-                      null, "position: relative; outline: none");
-    // Moved around its parent to cover visible view.
-    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
-    // Set to the height of the document, allowing scrolling.
-    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
-    // Behavior of elts with overflow: auto and padding is
-    // inconsistent across browsers. This is used to ensure the
-    // scrollable area is big enough.
-    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
-    // Will contain the gutters, if any.
-    d.gutters = elt("div", null, "CodeMirror-gutters");
-    d.lineGutter = null;
-    // Actual scrollable element.
-    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
-    d.scroller.setAttribute("tabIndex", "-1");
-    // The element in which the editor lives.
-    d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
-                            d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
-
-    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
-    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    // Needed to hide big blue blinking cursor on Mobile Safari
-    if (ios) input.style.width = "0px";
-    if (!webkit) d.scroller.draggable = true;
-    // Needed to handle Tab key in KHTML
-    if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
-    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
-    if (ie && ie_version < 8) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px";
-
-    if (place.appendChild) place.appendChild(d.wrapper);
-    else place(d.wrapper);
-
-    // Current rendered range (may be bigger than the view window).
-    d.viewFrom = d.viewTo = doc.first;
-    // Information about the rendered lines.
-    d.view = [];
-    // Holds info about a single rendered line when it was rendered
-    // for measurement, while not in view.
-    d.externalMeasured = null;
-    // Empty space (in pixels) above the view
-    d.viewOffset = 0;
-    d.lastSizeC = 0;
-    d.updateLineNumbers = null;
-
-    // Used to only resize the line number gutter when necessary (when
-    // the amount of lines crosses a boundary that makes its width change)
-    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
-    // See readInput and resetInput
-    d.prevInput = "";
-    // Set to true when a non-horizontal-scrolling line widget is
-    // added. As an optimization, line widget aligning is skipped when
-    // this is false.
-    d.alignWidgets = false;
-    // Flag that indicates whether we expect input to appear real soon
-    // now (after some event like 'keypress' or 'input') and are
-    // polling intensively.
-    d.pollingFast = false;
-    // Self-resetting timeout for the poller
-    d.poll = new Delayed();
-
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-
-    // Tracks when resetInput has punted to just putting a short
-    // string into the textarea instead of the full selection.
-    d.inaccurateSelection = false;
-
-    // Tracks the maximum line length so that the horizontal scrollbar
-    // can be kept static when scrolling.
-    d.maxLine = null;
-    d.maxLineLength = 0;
-    d.maxLineChanged = false;
-
-    // Used for measuring wheel scrolling granularity
-    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
-    // True when shift is held down.
-    d.shift = false;
-
-    // Used to track whether anything happened since the context menu
-    // was opened.
-    d.selForContextMenu = null;
-  }
-
-  // STATE UPDATES
-
-  // Used to get the editor into a consistent state again when options change.
-
-  function loadMode(cm) {
-    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
-    resetModeState(cm);
-  }
-
-  function resetModeState(cm) {
-    cm.doc.iter(function(line) {
-      if (line.stateAfter) line.stateAfter = null;
-      if (line.styles) line.styles = null;
-    });
-    cm.doc.frontier = cm.doc.first;
-    startWorker(cm, 100);
-    cm.state.modeGen++;
-    if (cm.curOp) regChange(cm);
-  }
-
-  function wrappingChanged(cm) {
-    if (cm.options.lineWrapping) {
-      addClass(cm.display.wrapper, "CodeMirror-wrap");
-      cm.display.sizer.style.minWidth = "";
-    } else {
-      rmClass(cm.display.wrapper, "CodeMirror-wrap");
-      findMaxLine(cm);
-    }
-    estimateLineHeights(cm);
-    regChange(cm);
-    clearCaches(cm);
-    setTimeout(function(){updateScrollbars(cm);}, 100);
-  }
-
-  // Returns a function that estimates the height of a line, to use as
-  // first approximation until the line becomes visible (and is thus
-  // properly measurable).
-  function estimateHeight(cm) {
-    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
-    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
-    return function(line) {
-      if (lineIsHidden(cm.doc, line)) return 0;
-
-      var widgetsHeight = 0;
-      if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
-        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
-      }
-
-      if (wrapping)
-        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
-      else
-        return widgetsHeight + th;
-    };
-  }
-
-  function estimateLineHeights(cm) {
-    var doc = cm.doc, est = estimateHeight(cm);
-    doc.iter(function(line) {
-      var estHeight = est(line);
-      if (estHeight != line.height) updateLineHeight(line, estHeight);
-    });
-  }
-
-  function keyMapChanged(cm) {
-    var map = keyMap[cm.options.keyMap], style = map.style;
-    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
-      (style ? " cm-keymap-" + style : "");
-  }
-
-  function themeChanged(cm) {
-    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
-      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
-    clearCaches(cm);
-  }
-
-  function guttersChanged(cm) {
-    updateGutters(cm);
-    regChange(cm);
-    setTimeout(function(){alignHorizontally(cm);}, 20);
-  }
-
-  // Rebuild the gutter elements, ensure the margin to the left of the
-  // code matches their width.
-  function updateGutters(cm) {
-    var gutters = cm.display.gutters, specs = cm.options.gutters;
-    removeChildren(gutters);
-    for (var i = 0; i < specs.length; ++i) {
-      var gutterClass = specs[i];
-      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
-      if (gutterClass == "CodeMirror-linenumbers") {
-        cm.display.lineGutter = gElt;
-        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
-      }
-    }
-    gutters.style.display = i ? "" : "none";
-    updateGutterSpace(cm);
-  }
-
-  function updateGutterSpace(cm) {
-    var width = cm.display.gutters.offsetWidth;
-    cm.display.sizer.style.marginLeft = width + "px";
-    cm.display.scrollbarH.style.left = cm.options.fixedGutter ? width + "px" : 0;
-  }
-
-  // Compute the character length of a line, taking into account
-  // collapsed ranges (see markText) that might hide parts, and join
-  // other lines onto it.
-  function lineLength(line) {
-    if (line.height == 0) return 0;
-    var len = line.text.length, merged, cur = line;
-    while (merged = collapsedSpanAtStart(cur)) {
-      var found = merged.find(0, true);
-      cur = found.from.line;
-      len += found.from.ch - found.to.ch;
-    }
-    cur = line;
-    while (merged = collapsedSpanAtEnd(cur)) {
-      var found = merged.find(0, true);
-      len -= cur.text.length - found.from.ch;
-      cur = found.to.line;
-      len += cur.text.length - found.to.ch;
-    }
-    return len;
-  }
-
-  // Find the longest line in the document.
-  function findMaxLine(cm) {
-    var d = cm.display, doc = cm.doc;
-    d.maxLine = getLine(doc, doc.first);
-    d.maxLineLength = lineLength(d.maxLine);
-    d.maxLineChanged = true;
-    doc.iter(function(line) {
-      var len = lineLength(line);
-      if (len > d.maxLineLength) {
-        d.maxLineLength = len;
-        d.maxLine = line;
-      }
-    });
-  }
-
-  // Make sure the gutters options contains the element
-  // "CodeMirror-linenumbers" when the lineNumbers option is true.
-  function setGuttersForLineNumbers(options) {
-    var found = indexOf(options.gutters, "CodeMirror-linenumbers");
-    if (found == -1 && options.lineNumbers) {
-      options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
-    } else if (found > -1 && !options.lineNumbers) {
-      options.gutters = options.gutters.slice(0);
-      options.gutters.splice(found, 1);
-    }
-  }
-
-  // SCROLLBARS
-
-  function hScrollbarTakesSpace(cm) {
-    return cm.display.scroller.clientHeight - cm.display.wrapper.clientHeight < scrollerCutOff - 3;
-  }
-
-  // Prepare DOM reads needed to update the scrollbars. Done in one
-  // shot to minimize update/measure roundtrips.
-  function measureForScrollbars(cm) {
-    var scroll = cm.display.scroller;
-    return {
-      clientHeight: scroll.clientHeight,
-      barHeight: cm.display.scrollbarV.clientHeight,
-      scrollWidth: scroll.scrollWidth, clientWidth: scroll.clientWidth,
-      hScrollbarTakesSpace: hScrollbarTakesSpace(cm),
-      barWidth: cm.display.scrollbarH.clientWidth,
-      docHeight: Math.round(cm.doc.height + paddingVert(cm.display))
-    };
-  }
-
-  // Re-synchronize the fake scrollbars with the actual size of the
-  // content.
-  function updateScrollbars(cm, measure) {
-    if (!measure) measure = measureForScrollbars(cm);
-    var d = cm.display, sWidth = scrollbarWidth(d.measure);
-    var scrollHeight = measure.docHeight + scrollerCutOff;
-    var needsH = measure.scrollWidth > measure.clientWidth;
-    if (needsH && measure.scrollWidth <= measure.clientWidth + 1 &&
-        sWidth > 0 && !measure.hScrollbarTakesSpace)
-      needsH = false; // (Issue #2562)
-    var needsV = scrollHeight > measure.clientHeight;
-
-    if (needsV) {
-      d.scrollbarV.style.display = "block";
-      d.scrollbarV.style.bottom = needsH ? sWidth + "px" : "0";
-      // A bug in IE8 can cause this value to be negative, so guard it.
-      d.scrollbarV.firstChild.style.height =
-        Math.max(0, scrollHeight - measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + "px";
-    } else {
-      d.scrollbarV.style.display = "";
-      d.scrollbarV.firstChild.style.height = "0";
-    }
-    if (needsH) {
-      d.scrollbarH.style.display = "block";
-      d.scrollbarH.style.right = needsV ? sWidth + "px" : "0";
-      d.scrollbarH.firstChild.style.width =
-        (measure.scrollWidth - measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + "px";
-    } else {
-      d.scrollbarH.style.display = "";
-      d.scrollbarH.firstChild.style.width = "0";
-    }
-    if (needsH && needsV) {
-      d.scrollbarFiller.style.display = "block";
-      d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = sWidth + "px";
-    } else d.scrollbarFiller.style.display = "";
-    if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
-      d.gutterFiller.style.display = "block";
-      d.gutterFiller.style.height = sWidth + "px";
-      d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
-    } else d.gutterFiller.style.display = "";
-
-    if (!cm.state.checkedOverlayScrollbar && measure.clientHeight > 0) {
-      if (sWidth === 0) {
-        var w = mac && !mac_geMountainLion ? "12px" : "18px";
-        d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = w;
-        var barMouseDown = function(e) {
-          if (e_target(e) != d.scrollbarV && e_target(e) != d.scrollbarH)
-            operation(cm, onMouseDown)(e);
-        };
-        on(d.scrollbarV, "mousedown", barMouseDown);
-        on(d.scrollbarH, "mousedown", barMouseDown);
-      }
-      cm.state.checkedOverlayScrollbar = true;
-    }
-  }
-
-  // Compute the lines that are visible in a given viewport (defaults
-  // the the current scroll position). viewport may contain top,
-  // height, and ensure (see op.scrollToPos) properties.
-  function visibleLines(display, doc, viewport) {
-    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
-    top = Math.floor(top - paddingTop(display));
-    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
-
-    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
-    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
-    // forces those lines into the viewport (if possible).
-    if (viewport && viewport.ensure) {
-      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
-      if (ensureFrom < from)
-        return {from: ensureFrom,
-                to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
-      if (Math.min(ensureTo, doc.lastLine()) >= to)
-        return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight),
-                to: ensureTo};
-    }
-    return {from: from, to: Math.max(to, from + 1)};
-  }
-
-  // LINE NUMBERS
-
-  // Re-align line numbers and gutter marks to compensate for
-  // horizontal scrolling.
-  function alignHorizontally(cm) {
-    var display = cm.display, view = display.view;
-    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
-    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
-    var gutterW = display.gutters.offsetWidth, left = comp + "px";
-    for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
-      if (cm.options.fixedGutter && view[i].gutter)
-        view[i].gutter.style.left = left;
-      var align = view[i].alignable;
-      if (align) for (var j = 0; j < align.length; j++)
-        align[j].style.left = left;
-    }
-    if (cm.options.fixedGutter)
-      display.gutters.style.left = (comp + gutterW) + "px";
-  }
-
-  // Used to ensure that the line number gutter is still the right
-  // size for the current document size. Returns true when an update
-  // is needed.
-  function maybeUpdateLineNumberWidth(cm) {
-    if (!cm.options.lineNumbers) return false;
-    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
-    if (last.length != display.lineNumChars) {
-      var test = display.measure.appendChild(elt("div", [elt("div", last)],
-                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
-      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
-      display.lineGutter.style.width = "";
-      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
-      display.lineNumWidth = display.lineNumInnerWidth + padding;
-      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
-      display.lineGutter.style.width = display.lineNumWidth + "px";
-      updateGutterSpace(cm);
-      return true;
-    }
-    return false;
-  }
-
-  function lineNumberFor(options, i) {
-    return String(options.lineNumberFormatter(i + options.firstLineNumber));
-  }
-
-  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
-  // but using getBoundingClientRect to get a sub-pixel-accurate
-  // result.
-  function compensateForHScroll(display) {
-    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
-  }
-
-  // DISPLAY DRAWING
-
-  function DisplayUpdate(cm, viewport, force) {
-    var display = cm.display;
-
-    this.viewport = viewport;
-    // Store some values that we'll need later (but don't want to force a relayout for)
-    this.visible = visibleLines(display, cm.doc, viewport);
-    this.editorIsHidden = !display.wrapper.offsetWidth;
-    this.wrapperHeight = display.wrapper.clientHeight;
-    this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
-    this.oldScrollerWidth = display.scroller.clientWidth;
-    this.force = force;
-    this.dims = getDimensions(cm);
-  }
-
-  // Does the actual updating of the line display. Bails out
-  // (returning false) when there is nothing to be done and forced is
-  // false.
-  function updateDisplayIfNeeded(cm, update) {
-    var display = cm.display, doc = cm.doc;
-    if (update.editorIsHidden) {
-      resetView(cm);
-      return false;
-    }
-
-    // Bail out if the visible area is already rendered and nothing changed.
-    if (!update.force &&
-        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
-        countDirtyView(cm) == 0)
-      return false;
-
-    if (maybeUpdateLineNumberWidth(cm)) {
-      resetView(cm);
-      update.dims = getDimensions(cm);
-    }
-
-    // Compute a suitable new viewport (from & to)
-    var end = doc.first + doc.size;
-    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
-    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
-    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
-    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
-    if (sawCollapsedSpans) {
-      from = visualLineNo(cm.doc, from);
-      to = visualLineEndNo(cm.doc, to);
-    }
-
-    var different = from != display.viewFrom || to != display.viewTo ||
-      display.lastSizeC != update.wrapperHeight;
-    adjustView(cm, from, to);
-
-    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
-    // Position the mover div to align with the current scroll position
-    cm.display.mover.style.top = display.viewOffset + "px";
-
-    var toUpdate = countDirtyView(cm);
-    if (!different && toUpdate == 0 && !update.force &&
-        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
-      return false;
-
-    // For big changes, we hide the enclosing element during the
-    // update, since that speeds up the operations on most browsers.
-    var focused = activeElt();
-    if (toUpdate > 4) display.lineDiv.style.display = "none";
-    patchDisplay(cm, display.updateLineNumbers, update.dims);
-    if (toUpdate > 4) display.lineDiv.style.display = "";
-    // There might have been a widget with a focused element that got
-    // hidden or updated, if so re-focus it.
-    if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
-
-    // Prevent selection and cursors from interfering with the scroll
-    // width.
-    removeChildren(display.cursorDiv);
-    removeChildren(display.selectionDiv);
-
-    if (different) {
-      display.lastSizeC = update.wrapperHeight;
-      startWorker(cm, 400);
-    }
-
-    display.updateLineNumbers = null;
-
-    return true;
-  }
-
-  function postUpdateDisplay(cm, update) {
-    var force = update.force, viewport = update.viewport;
-    for (var first = true;; first = false) {
-      if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) {
-        force = true;
-      } else {
-        force = false;
-        // Clip forced viewport to actual scrollable area.
-        if (viewport && viewport.top != null)
-          viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - scrollerCutOff -
-                                    cm.display.scroller.clientHeight, viewport.top)};
-        // Updated line heights might result in the drawn area not
-        // actually covering the viewport. Keep looping until it does.
-        update.visible = visibleLines(cm.display, cm.doc, viewport);
-        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
-          break;
-      }
-      if (!updateDisplayIfNeeded(cm, update)) break;
-      updateHeightsInViewport(cm);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      setDocumentHeight(cm, barMeasure);
-      updateScrollbars(cm, barMeasure);
-    }
-
-    signalLater(cm, "update", cm);
-    if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update.oldViewTo)
-      signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
-  }
-
-  function updateDisplaySimple(cm, viewport) {
-    var update = new DisplayUpdate(cm, viewport);
-    if (updateDisplayIfNeeded(cm, update)) {
-      updateHeightsInViewport(cm);
-      postUpdateDisplay(cm, update);
-      var barMeasure = measureForScrollbars(cm);
-      updateSelection(cm);
-      setDocumentHeight(cm, barMeasure);
-      updateScrollbars(cm, barMeasure);
-    }
-  }
-
-  function setDocumentHeight(cm, measure) {
-    cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measure.docHeight + "px";
-    cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + "px";
-  }
-
-  function checkForWebkitWidthBug(cm, measure) {
-    // Work around Webkit bug where it sometimes reserves space for a
-    // non-existing phantom scrollbar in the scroller (Issue #2420)
-    if (cm.display.sizer.offsetWidth + cm.display.gutters.offsetWidth < cm.display.scroller.clientWidth - 1) {
-      cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = "0px";
-      cm.display.gutters.style.height = measure.docHeight + "px";
-    }
-  }
-
-  // Read the actual heights of the rendered lines, and update their
-  // stored heights to match.
-  function updateHeightsInViewport(cm) {
-    var display = cm.display;
-    var prevBottom = display.lineDiv.offsetTop;
-    for (var i = 0; i < display.view.length; i++) {
-      var cur = display.view[i], height;
-      if (cur.hidden) continue;
-      if (ie && ie_version < 8) {
-        var bot = cur.node.offsetTop + cur.node.offsetHeight;
-        height = bot - prevBottom;
-        prevBottom = bot;
-      } else {
-        var box = cur.node.getBoundingClientRect();
-        height = box.bottom - box.top;
-      }
-      var diff = cur.line.height - height;
-      if (height < 2) height = textHeight(display);
-      if (diff > .001 || diff < -.001) {
-        updateLineHeight(cur.line, height);
-        updateWidgetHeight(cur.line);
-        if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
-          updateWidgetHeight(cur.rest[j]);
-      }
-    }
-  }
-
-  // Read and store the height of line widgets associated with the
-  // given line.
-  function updateWidgetHeight(line) {
-    if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
-      line.widgets[i].height = line.widgets[i].node.offsetHeight;
-  }
-
-  // Do a bulk-read of the DOM positions and sizes needed to draw the
-  // view, so that we don't interleave reading and writing to the DOM.
-  function getDimensions(cm) {
-    var d = cm.display, left = {}, width = {};
-    var gutterLeft = d.gutters.clientLeft;
-    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
-      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
-      width[cm.options.gutters[i]] = n.clientWidth;
-    }
-    return {fixedPos: compensateForHScroll(d),
-            gutterTotalWidth: d.gutters.offsetWidth,
-            gutterLeft: left,
-            gutterWidth: width,
-            wrapperWidth: d.wrapper.clientWidth};
-  }
-
-  // Sync the actual display DOM structure with display.view, removing
-  // nodes for lines that are no longer in view, and creating the ones
-  // that are not there yet, and updating the ones that are out of
-  // date.
-  function patchDisplay(cm, updateNumbersFrom, dims) {
-    var display = cm.display, lineNumbers = cm.options.lineNumbers;
-    var container = display.lineDiv, cur = container.firstChild;
-
-    function rm(node) {
-      var next = node.nextSibling;
-      // Works around a throw-scroll bug in OS X Webkit
-      if (webkit && mac && cm.display.currentWheelTarget == node)
-        node.style.display = "none";
-      else
-        node.parentNode.removeChild(node);
-      return next;
-    }
-
-    var view = display.view, lineN = display.viewFrom;
-    // Loop over the elements in the view, syncing cur (the DOM nodes
-    // in display.lineDiv) with the view as we go.
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (lineView.hidden) {
-      } else if (!lineView.node) { // Not drawn yet
-        var node = buildLineElement(cm, lineView, lineN, dims);
-        container.insertBefore(node, cur);
-      } else { // Already drawn
-        while (cur != lineView.node) cur = rm(cur);
-        var updateNumber = lineNumbers && updateNumbersFrom != null &&
-          updateNumbersFrom <= lineN && lineView.lineNumber;
-        if (lineView.changes) {
-          if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
-          updateLineForChanges(cm, lineView, lineN, dims);
-        }
-        if (updateNumber) {
-          removeChildren(lineView.lineNumber);
-          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
-        }
-        cur = lineView.node.nextSibling;
-      }
-      lineN += lineView.size;
-    }
-    while (cur) cur = rm(cur);
-  }
-
-  // When an aspect of a line changes, a string is added to
-  // lineView.changes. This updates the relevant part of the line's
-  // DOM structure.
-  function updateLineForChanges(cm, lineView, lineN, dims) {
-    for (var j = 0; j < lineView.changes.length; j++) {
-      var type = lineView.changes[j];
-      if (type == "text") updateLineText(cm, lineView);
-      else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
-      else if (type == "class") updateLineClasses(lineView);
-      else if (type == "widget") updateLineWidgets(lineView, dims);
-    }
-    lineView.changes = null;
-  }
-
-  // Lines with gutter elements, widgets or a background class need to
-  // be wrapped, and have the extra elements added to the wrapper div
-  function ensureLineWrapped(lineView) {
-    if (lineView.node == lineView.text) {
-      lineView.node = elt("div", null, null, "position: relative");
-      if (lineView.text.parentNode)
-        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
-      lineView.node.appendChild(lineView.text);
-      if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
-    }
-    return lineView.node;
-  }
-
-  function updateLineBackground(lineView) {
-    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
-    if (cls) cls += " CodeMirror-linebackground";
-    if (lineView.background) {
-      if (cls) lineView.background.className = cls;
-      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
-    } else if (cls) {
-      var wrap = ensureLineWrapped(lineView);
-      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
-    }
-  }
-
-  // Wrapper around buildLineContent which will reuse the structure
-  // in display.externalMeasured when possible.
-  function getLineContent(cm, lineView) {
-    var ext = cm.display.externalMeasured;
-    if (ext && ext.line == lineView.line) {
-      cm.display.externalMeasured = null;
-      lineView.measure = ext.measure;
-      return ext.built;
-    }
-    return buildLineContent(cm, lineView);
-  }
-
-  // Redraw the line's text. Interacts with the background and text
-  // classes because the mode may output tokens that influence these
-  // classes.
-  function updateLineText(cm, lineView) {
-    var cls = lineView.text.className;
-    var built = getLineContent(cm, lineView);
-    if (lineView.text == lineView.node) lineView.node = built.pre;
-    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
-    lineView.text = built.pre;
-    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
-      lineView.bgClass = built.bgClass;
-      lineView.textClass = built.textClass;
-      updateLineClasses(lineView);
-    } else if (cls) {
-      lineView.text.className = cls;
-    }
-  }
-
-  function updateLineClasses(lineView) {
-    updateLineBackground(lineView);
-    if (lineView.line.wrapClass)
-      ensureLineWrapped(lineView).className = lineView.line.wrapClass;
-    else if (lineView.node != lineView.text)
-      lineView.node.className = "";
-    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
-    lineView.text.className = textClass || "";
-  }
-
-  function updateLineGutter(cm, lineView, lineN, dims) {
-    if (lineView.gutter) {
-      lineView.node.removeChild(lineView.gutter);
-      lineView.gutter = null;
-    }
-    var markers = lineView.line.gutterMarkers;
-    if (cm.options.lineNumbers || markers) {
-      var wrap = ensureLineWrapped(lineView);
-      var gutterWrap = lineView.gutter =
-        wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
-                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
-                          lineView.text);
-      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
-        lineView.lineNumber = gutterWrap.appendChild(
-          elt("div", lineNumberFor(cm.options, lineN),
-              "CodeMirror-linenumber CodeMirror-gutter-elt",
-              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
-              + cm.display.lineNumInnerWidth + "px"));
-      if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
-        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
-        if (found)
-          gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
-                                     dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
-      }
-    }
-  }
-
-  function updateLineWidgets(lineView, dims) {
-    if (lineView.alignable) lineView.alignable = null;
-    for (var node = lineView.node.firstChild, next; node; node = next) {
-      var next = node.nextSibling;
-      if (node.className == "CodeMirror-linewidget")
-        lineView.node.removeChild(node);
-    }
-    insertLineWidgets(lineView, dims);
-  }
-
-  // Build a line's DOM representation from scratch
-  function buildLineElement(cm, lineView, lineN, dims) {
-    var built = getLineContent(cm, lineView);
-    lineView.text = lineView.node = built.pre;
-    if (built.bgClass) lineView.bgClass = built.bgClass;
-    if (built.textClass) lineView.textClass = built.textClass;
-
-    updateLineClasses(lineView);
-    updateLineGutter(cm, lineView, lineN, dims);
-    insertLineWidgets(lineView, dims);
-    return lineView.node;
-  }
-
-  // A lineView may contain multiple logical lines (when merged by
-  // collapsed spans). The widgets for all of them need to be drawn.
-  function insertLineWidgets(lineView, dims) {
-    insertLineWidgetsFor(lineView.line, lineView, dims, true);
-    if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
-      insertLineWidgetsFor(lineView.rest[i], lineView, dims, false);
-  }
-
-  function insertLineWidgetsFor(line, lineView, dims, allowAbove) {
-    if (!line.widgets) return;
-    var wrap = ensureLineWrapped(lineView);
-    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
-      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
-      if (!widget.handleMouseEvents) node.ignoreEvents = true;
-      positionLineWidget(widget, node, lineView, dims);
-      if (allowAbove && widget.above)
-        wrap.insertBefore(node, lineView.gutter || lineView.text);
-      else
-        wrap.appendChild(node);
-      signalLater(widget, "redraw");
-    }
-  }
-
-  function positionLineWidget(widget, node, lineView, dims) {
-    if (widget.noHScroll) {
-      (lineView.alignable || (lineView.alignable = [])).push(node);
-      var width = dims.wrapperWidth;
-      node.style.left = dims.fixedPos + "px";
-      if (!widget.coverGutter) {
-        width -= dims.gutterTotalWidth;
-        node.style.paddingLeft = dims.gutterTotalWidth + "px";
-      }
-      node.style.width = width + "px";
-    }
-    if (widget.coverGutter) {
-      node.style.zIndex = 5;
-      node.style.position = "relative";
-      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
-    }
-  }
-
-  // POSITION OBJECT
-
-  // A Pos instance represents a position within the text.
-  var Pos = CodeMirror.Pos = function(line, ch) {
-    if (!(this instanceof Pos)) return new Pos(line, ch);
-    this.line = line; this.ch = ch;
-  };
-
-  // Compare two positions, return 0 if they are the same, a negative
-  // number when a is less, and a positive number otherwise.
-  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
-
-  function copyPos(x) {return Pos(x.line, x.ch);}
-  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
-  function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
-
-  // SELECTION / CURSOR
-
-  // Selection objects are immutable. A new one is created every time
-  // the selection changes. A selection is one or more non-overlapping
-  // (and non-touching) ranges, sorted, and an integer that indicates
-  // which one is the primary selection (the one that's scrolled into
-  // view, that getCursor returns, etc).
-  function Selection(ranges, primIndex) {
-    this.ranges = ranges;
-    this.primIndex = primIndex;
-  }
-
-  Selection.prototype = {
-    primary: function() { return this.ranges[this.primIndex]; },
-    equals: function(other) {
-      if (other == this) return true;
-      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
-      for (var i = 0; i < this.ranges.length; i++) {
-        var here = this.ranges[i], there = other.ranges[i];
-        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
-      }
-      return true;
-    },
-    deepCopy: function() {
-      for (var out = [], i = 0; i < this.ranges.length; i++)
-        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
-      return new Selection(out, this.primIndex);
-    },
-    somethingSelected: function() {
-      for (var i = 0; i < this.ranges.length; i++)
-        if (!this.ranges[i].empty()) return true;
-      return false;
-    },
-    contains: function(pos, end) {
-      if (!end) end = pos;
-      for (var i = 0; i < this.ranges.length; i++) {
-        var range = this.ranges[i];
-        if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
-          return i;
-      }
-      return -1;
-    }
-  };
-
-  function Range(anchor, head) {
-    this.anchor = anchor; this.head = head;
-  }
-
-  Range.prototype = {
-    from: function() { return minPos(this.anchor, this.head); },
-    to: function() { return maxPos(this.anchor, this.head); },
-    empty: function() {
-      return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
-    }
-  };
-
-  // Take an unsorted, potentially overlapping set of ranges, and
-  // build a selection out of it. 'Consumes' ranges array (modifying
-  // it).
-  function normalizeSelection(ranges, primIndex) {
-    var prim = ranges[primIndex];
-    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
-    primIndex = indexOf(ranges, prim);
-    for (var i = 1; i < ranges.length; i++) {
-      var cur = ranges[i], prev = ranges[i - 1];
-      if (cmp(prev.to(), cur.from()) >= 0) {
-        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
-        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
-        if (i <= primIndex) --primIndex;
-        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
-      }
-    }
-    return new Selection(ranges, primIndex);
-  }
-
-  function simpleSelection(anchor, head) {
-    return new Selection([new Range(anchor, head || anchor)], 0);
-  }
-
-  // Most of the external API clips given positions to make sure they
-  // actually exist within the document.
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
-  function clipPos(doc, pos) {
-    if (pos.line < doc.first) return Pos(doc.first, 0);
-    var last = doc.first + doc.size - 1;
-    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
-    return clipToLen(pos, getLine(doc, pos.line).text.length);
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch > linelen) return Pos(pos.line, linelen);
-    else if (ch < 0) return Pos(pos.line, 0);
-    else return pos;
-  }
-  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
-  function clipPosArray(doc, array) {
-    for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
-    return out;
-  }
-
-  // SELECTION UPDATES
-
-  // The 'scroll' parameter given to many of these indicated whether
-  // the new cursor position should be scrolled into view after
-  // modifying the selection.
-
-  // If shift is held or the extend flag is set, extends a range to
-  // include a given position (and optionally a second position).
-  // Otherwise, simply returns the range between the given positions.
-  // Used for cursor motion and such.
-  function extendRange(doc, range, head, other) {
-    if (doc.cm && doc.cm.display.shift || doc.extend) {
-      var anchor = range.anchor;
-      if (other) {
-        var posBefore = cmp(head, anchor) < 0;
-        if (posBefore != (cmp(other, anchor) < 0)) {
-          anchor = head;
-          head = other;
-        } else if (posBefore != (cmp(head, other) < 0)) {
-          head = other;
-        }
-      }
-      return new Range(anchor, head);
-    } else {
-      return new Range(other || head, head);
-    }
-  }
-
-  // Extend the primary selection range, discard the rest.
-  function extendSelection(doc, head, other, options) {
-    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
-  }
-
-  // Extend all selections (pos is an array of selections with length
-  // equal the number of selections)
-  function extendSelections(doc, heads, options) {
-    for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
-      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
-    var newSel = normalizeSelection(out, doc.sel.primIndex);
-    setSelection(doc, newSel, options);
-  }
-
-  // Updates a single range in the selection.
-  function replaceOneSelection(doc, i, range, options) {
-    var ranges = doc.sel.ranges.slice(0);
-    ranges[i] = range;
-    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
-  }
-
-  // Reset the selection to a single range.
-  function setSimpleSelection(doc, anchor, head, options) {
-    setSelection(doc, simpleSelection(anchor, head), options);
-  }
-
-  // Give beforeSelectionChange handlers a change to influence a
-  // selection update.
-  function filterSelectionChange(doc, sel) {
-    var obj = {
-      ranges: sel.ranges,
-      update: function(ranges) {
-        this.ranges = [];
-        for (var i = 0; i < ranges.length; i++)
-          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
-                                     clipPos(doc, ranges[i].head));
-      }
-    };
-    signal(doc, "beforeSelectionChange", doc, obj);
-    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
-    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
-    else return sel;
-  }
-
-  function setSelectionReplaceHistory(doc, sel, options) {
-    var done = doc.history.done, last = lst(done);
-    if (last && last.ranges) {
-      done[done.length - 1] = sel;
-      setSelectionNoUndo(doc, sel, options);
-    } else {
-      setSelection(doc, sel, options);
-    }
-  }
-
-  // Set a new selection.
-  function setSelection(doc, sel, options) {
-    setSelectionNoUndo(doc, sel, options);
-    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
-  }
-
-  function setSelectionNoUndo(doc, sel, options) {
-    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
-      sel = filterSelectionChange(doc, sel);
-
-    var bias = options && options.bias ||
-      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
-    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
-
-    if (!(options && options.scroll === false) && doc.cm)
-      ensureCursorVisible(doc.cm);
-  }
-
-  function setSelectionInner(doc, sel) {
-    if (sel.equals(doc.sel)) return;
-
-    doc.sel = sel;
-
-    if (doc.cm) {
-      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
-      signalCursorActivity(doc.cm);
-    }
-    signalLater(doc, "cursorActivity", doc);
-  }
-
-  // Verify that the selection does not partially select any atomic
-  // marked ranges.
-  function reCheckSelection(doc) {
-    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
-  }
-
-  // Return a selection that does not partially select any atomic
-  // ranges.
-  function skipAtomicInSelection(doc, sel, bias, mayClear) {
-    var out;
-    for (var i = 0; i < sel.ranges.length; i++) {
-      var range = sel.ranges[i];
-      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
-      var newHead = skipAtomic(doc, range.head, bias, mayClear);
-      if (out || newAnchor != range.anchor || newHead != range.head) {
-        if (!out) out = sel.ranges.slice(0, i);
-        out[i] = new Range(newAnchor, newHead);
-      }
-    }
-    return out ? normalizeSelection(out, sel.primIndex) : sel;
-  }
-
-  // Ensure a given position is not inside an atomic range.
-  function skipAtomic(doc, pos, bias, mayClear) {
-    var flipped = false, curPos = pos;
-    var dir = bias || 1;
-    doc.cantEdit = false;
-    search: for (;;) {
-      var line = getLine(doc, curPos.line);
-      if (line.markedSpans) {
-        for (var i = 0; i < line.markedSpans.length; ++i) {
-          var sp = line.markedSpans[i], m = sp.marker;
-          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
-              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
-            if (mayClear) {
-              signal(m, "beforeCursorEnter");
-              if (m.explicitlyCleared) {
-                if (!line.markedSpans) break;
-                else {--i; continue;}
-              }
-            }
-            if (!m.atomic) continue;
-            var newPos = m.find(dir < 0 ? -1 : 1);
-            if (cmp(newPos, curPos) == 0) {
-              newPos.ch += dir;
-              if (newPos.ch < 0) {
-                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
-                else newPos = null;
-              } else if (newPos.ch > line.text.length) {
-                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
-                else newPos = null;
-              }
-              if (!newPos) {
-                if (flipped) {
-                  // Driven in a corner -- no valid cursor position found at all
-                  // -- try again *with* clearing, if we didn't already
-                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
-                  // Otherwise, turn off editing until further notice, and return the start of the doc
-                  doc.cantEdit = true;
-                  return Pos(doc.first, 0);
-                }
-                flipped = true; newPos = pos; dir = -dir;
-              }
-            }
-            curPos = newPos;
-            continue search;
-          }
-        }
-      }
-      return curPos;
-    }
-  }
-
-  // SELECTION DRAWING
-
-  // Redraw the selection and/or cursor
-  function drawSelection(cm) {
-    var display = cm.display, doc = cm.doc, result = {};
-    var curFragment = result.cursors = document.createDocumentFragment();
-    var selFragment = result.selection = document.createDocumentFragment();
-
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      var range = doc.sel.ranges[i];
-      var collapsed = range.empty();
-      if (collapsed || cm.options.showCursorWhenSelecting)
-        drawSelectionCursor(cm, range, curFragment);
-      if (!collapsed)
-        drawSelectionRange(cm, range, selFragment);
-    }
-
-    // Move the hidden textarea near the cursor to prevent scrolling artifacts
-    if (cm.options.moveInputWithCursor) {
-      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
-      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
-      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                          headPos.top + lineOff.top - wrapOff.top));
-      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                           headPos.left + lineOff.left - wrapOff.left));
-    }
-
-    return result;
-  }
-
-  function showSelection(cm, drawn) {
-    removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
-    removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
-    if (drawn.teTop != null) {
-      cm.display.inputDiv.style.top = drawn.teTop + "px";
-      cm.display.inputDiv.style.left = drawn.teLeft + "px";
-    }
-  }
-
-  function updateSelection(cm) {
-    showSelection(cm, drawSelection(cm));
-  }
-
-  // Draws a cursor for the given range
-  function drawSelectionCursor(cm, range, output) {
-    var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
-
-    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
-    cursor.style.left = pos.left + "px";
-    cursor.style.top = pos.top + "px";
-    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
-
-    if (pos.other) {
-      // Secondary cursor, shown when on a 'jump' in bi-directional text
-      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
-      otherCursor.style.display = "";
-      otherCursor.style.left = pos.other.left + "px";
-      otherCursor.style.top = pos.other.top + "px";
-      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
-    }
-  }
-
-  // Draws the given range as a highlighted selection
-  function drawSelectionRange(cm, range, output) {
-    var display = cm.display, doc = cm.doc;
-    var fragment = document.createDocumentFragment();
-    var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right;
-
-    function add(left, top, width, bottom) {
-      if (top < 0) top = 0;
-      top = Math.round(top);
-      bottom = Math.round(bottom);
-      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
-                               "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
-                               "px; height: " + (bottom - top) + "px"));
-    }
-
-    function drawForLine(line, fromArg, toArg) {
-      var lineObj = getLine(doc, line);
-      var lineLen = lineObj.text.length;
-      var start, end;
-      function coords(ch, bias) {
-        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
-      }
-
-      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
-        var leftPos = coords(from, "left"), rightPos, left, right;
-        if (from == to) {
-          rightPos = leftPos;
-          left = right = leftPos.left;
-        } else {
-          rightPos = coords(to - 1, "right");
-          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
-          left = leftPos.left;
-          right = rightPos.right;
-        }
-        if (fromArg == null && from == 0) left = leftSide;
-        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
-          add(left, leftPos.top, null, leftPos.bottom);
-          left = leftSide;
-          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
-        }
-        if (toArg == null && to == lineLen) right = rightSide;
-        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
-          start = leftPos;
-        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
-          end = rightPos;
-        if (left < leftSide + 1) left = leftSide;
-        add(left, rightPos.top, right - left, rightPos.bottom);
-      });
-      return {start: start, end: end};
-    }
-
-    var sFrom = range.from(), sTo = range.to();
-    if (sFrom.line == sTo.line) {
-      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
-    } else {
-      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
-      var singleVLine = visualLine(fromLine) == visualLine(toLine);
-      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
-      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
-      if (singleVLine) {
-        if (leftEnd.top < rightStart.top - 2) {
-          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
-          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
-        } else {
-          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
-        }
-      }
-      if (leftEnd.bottom < rightStart.top)
-        add(leftSide, leftEnd.bottom, null, rightStart.top);
-    }
-
-    output.appendChild(fragment);
-  }
-
-  // Cursor-blinking
-  function restartBlink(cm) {
-    if (!cm.state.focused) return;
-    var display = cm.display;
-    clearInterval(display.blinker);
-    var on = true;
-    display.cursorDiv.style.visibility = "";
-    if (cm.options.cursorBlinkRate > 0)
-      display.blinker = setInterval(function() {
-        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
-      }, cm.options.cursorBlinkRate);
-    else if (cm.options.cursorBlinkRate < 0)
-      display.cursorDiv.style.visibility = "hidden";
-  }
-
-  // HIGHLIGHT WORKER
-
-  function startWorker(cm, time) {
-    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
-      cm.state.highlight.set(time, bind(highlightWorker, cm));
-  }
-
-  function highlightWorker(cm) {
-    var doc = cm.doc;
-    if (doc.frontier < doc.first) doc.frontier = doc.first;
-    if (doc.frontier >= cm.display.viewTo) return;
-    var end = +new Date + cm.options.workTime;
-    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
-    var changedLines = [];
-
-    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
-      if (doc.frontier >= cm.display.viewFrom) { // Visible
-        var oldStyles = line.styles;
-        var highlighted = highlightLine(cm, line, state, true);
-        line.styles = highlighted.styles;
-        var oldCls = line.styleClasses, newCls = highlighted.classes;
-        if (newCls) line.styleClasses = newCls;
-        else if (oldCls) line.styleClasses = null;
-        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
-          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
-        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
-        if (ischange) changedLines.push(doc.frontier);
-        line.stateAfter = copyState(doc.mode, state);
-      } else {
-        processLine(cm, line.text, state);
-        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
-      }
-      ++doc.frontier;
-      if (+new Date > end) {
-        startWorker(cm, cm.options.workDelay);
-        return true;
-      }
-    });
-    if (changedLines.length) runInOp(cm, function() {
-      for (var i = 0; i < changedLines.length; i++)
-        regLineChange(cm, changedLines[i], "text");
-    });
-  }
-
-  // Finds the line to start with when starting a parse. Tries to
-  // find a line with a stateAfter, so that it can start with a
-  // valid state. If that fails, it returns the line with the
-  // smallest indentation, which tends to need the least context to
-  // parse correctly.
-  function findStartLine(cm, n, precise) {
-    var minindent, minline, doc = cm.doc;
-    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
-    for (var search = n; search > lim; --search) {
-      if (search <= doc.first) return doc.first;
-      var line = getLine(doc, search - 1);
-      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
-      var indented = countColumn(line.text, null, cm.options.tabSize);
-      if (minline == null || minindent > indented) {
-        minline = search - 1;
-        minindent = indented;
-      }
-    }
-    return minline;
-  }
-
-  function getStateBefore(cm, n, precise) {
-    var doc = cm.doc, display = cm.display;
-    if (!doc.mode.startState) return true;
-    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
-    if (!state) state = startState(doc.mode);
-    else state = copyState(doc.mode, state);
-    doc.iter(pos, n, function(line) {
-      processLine(cm, line.text, state);
-      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
-      line.stateAfter = save ? copyState(doc.mode, state) : null;
-      ++pos;
-    });
-    if (precise) doc.frontier = pos;
-    return state;
-  }
-
-  // POSITION MEASUREMENT
-
-  function paddingTop(display) {return display.lineSpace.offsetTop;}
-  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
-  function paddingH(display) {
-    if (display.cachedPaddingH) return display.cachedPaddingH;
-    var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
-    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
-    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
-    if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
-    return data;
-  }
-
-  // Ensure the lineView.wrapping.heights array is populated. This is
-  // an array of bottom offsets for the lines that make up a drawn
-  // line. When lineWrapping is on, there might be more than one
-  // height.
-  function ensureLineHeights(cm, lineView, rect) {
-    var wrapping = cm.options.lineWrapping;
-    var curWidth = wrapping && cm.display.scroller.clientWidth;
-    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
-      var heights = lineView.measure.heights = [];
-      if (wrapping) {
-        lineView.measure.width = curWidth;
-        var rects = lineView.text.firstChild.getClientRects();
-        for (var i = 0; i < rects.length - 1; i++) {
-          var cur = rects[i], next = rects[i + 1];
-          if (Math.abs(cur.bottom - next.bottom) > 2)
-            heights.push((cur.bottom + next.top) / 2 - rect.top);
-        }
-      }
-      heights.push(rect.bottom - rect.top);
-    }
-  }
-
-  // Find a line map (mapping character offsets to text nodes) and a
-  // measurement cache for the given line number. (A line view might
-  // contain multiple lines when collapsed ranges are present.)
-  function mapFromLineView(lineView, line, lineN) {
-    if (lineView.line == line)
-      return {map: lineView.measure.map, cache: lineView.measure.cache};
-    for (var i = 0; i < lineView.rest.length; i++)
-      if (lineView.rest[i] == line)
-        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
-    for (var i = 0; i < lineView.rest.length; i++)
-      if (lineNo(lineView.rest[i]) > lineN)
-        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
-  }
-
-  // Render a line into the hidden node display.externalMeasured. Used
-  // when measurement is needed for a line that's not in the viewport.
-  function updateExternalMeasurement(cm, line) {
-    line = visualLine(line);
-    var lineN = lineNo(line);
-    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
-    view.lineN = lineN;
-    var built = view.built = buildLineContent(cm, view);
-    view.text = built.pre;
-    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
-    return view;
-  }
-
-  // Get a {top, bottom, left, right} box (in line-local coordinates)
-  // for a given character.
-  function measureChar(cm, line, ch, bias) {
-    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
-  }
-
-  // Find a line view that corresponds to the given line number.
-  function findViewForLine(cm, lineN) {
-    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
-      return cm.display.view[findViewIndex(cm, lineN)];
-    var ext = cm.display.externalMeasured;
-    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
-      return ext;
-  }
-
-  // Measurement can be split in two steps, the set-up work that
-  // applies to the whole line, and the measurement of the actual
-  // character. Functions like coordsChar, that need to do a lot of
-  // measurements in a row, can thus ensure that the set-up work is
-  // only done once.
-  function prepareMeasureForLine(cm, line) {
-    var lineN = lineNo(line);
-    var view = findViewForLine(cm, lineN);
-    if (view && !view.text)
-      view = null;
-    else if (view && view.changes)
-      updateLineForChanges(cm, view, lineN, getDimensions(cm));
-    if (!view)
-      view = updateExternalMeasurement(cm, line);
-
-    var info = mapFromLineView(view, line, lineN);
-    return {
-      line: line, view: view, rect: null,
-      map: info.map, cache: info.cache, before: info.before,
-      hasHeights: false
-    };
-  }
-
-  // Given a prepared measurement object, measures the position of an
-  // actual character (or fetches it from the cache).
-  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
-    if (prepared.before) ch = -1;
-    var key = ch + (bias || ""), found;
-    if (prepared.cache.hasOwnProperty(key)) {
-      found = prepared.cache[key];
-    } else {
-      if (!prepared.rect)
-        prepared.rect = prepared.view.text.getBoundingClientRect();
-      if (!prepared.hasHeights) {
-        ensureLineHeights(cm, prepared.view, prepared.rect);
-        prepared.hasHeights = true;
-      }
-      found = measureCharInner(cm, prepared, ch, bias);
-      if (!found.bogus) prepared.cache[key] = found;
-    }
-    return {left: found.left, right: found.right,
-            top: varHeight ? found.rtop : found.top,
-            bottom: varHeight ? found.rbottom : found.bottom};
-  }
-
-  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
-
-  function measureCharInner(cm, prepared, ch, bias) {
-    var map = prepared.map;
-
-    var node, start, end, collapse;
-    // First, search the line map for the text node corresponding to,
-    // or closest to, the target character.
-    for (var i = 0; i < map.length; i += 3) {
-      var mStart = map[i], mEnd = map[i + 1];
-      if (ch < mStart) {
-        start = 0; end = 1;
-        collapse = "left";
-      } else if (ch < mEnd) {
-        start = ch - mStart;
-        end = start + 1;
-      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
-        end = mEnd - mStart;
-        start = end - 1;
-        if (ch >= mEnd) collapse = "right";
-      }
-      if (start != null) {
-        node = map[i + 2];
-        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
-          collapse = bias;
-        if (bias == "left" && start == 0)
-          while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
-            node = map[(i -= 3) + 2];
-            collapse = "left";
-          }
-        if (bias == "right" && start == mEnd - mStart)
-          while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
-            node = map[(i += 3) + 2];
-            collapse = "right";
-          }
-        break;
-      }
-    }
-
-    var rect;
-    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
-      for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
-        while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
-        while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
-        if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) {
-          rect = node.parentNode.getBoundingClientRect();
-        } else if (ie && cm.options.lineWrapping) {
-          var rects = range(node, start, end).getClientRects();
-          if (rects.length)
-            rect = rects[bias == "right" ? rects.length - 1 : 0];
-          else
-            rect = nullRect;
-        } else {
-          rect = range(node, start, end).getBoundingClientRect() || nullRect;
-        }
-        if (rect.left || rect.right || start == 0) break;
-        end = start;
-        start = start - 1;
-        collapse = "right";
-      }
-      if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
-    } else { // If it is a widget, simply get the box for the whole widget.
-      if (start > 0) collapse = bias = "right";
-      var rects;
-      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
-        rect = rects[bias == "right" ? rects.length - 1 : 0];
-      else
-        rect = node.getBoundingClientRect();
-    }
-    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
-      var rSpan = node.parentNode.getClientRects()[0];
-      if (rSpan)
-        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
-      else
-        rect = nullRect;
-    }
-
-    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
-    var mid = (rtop + rbot) / 2;
-    var heights = prepared.view.measure.heights;
-    for (var i = 0; i < heights.length - 1; i++)
-      if (mid < heights[i]) break;
-    var top = i ? heights[i - 1] : 0, bot = heights[i];
-    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
-                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
-                  top: top, bottom: bot};
-    if (!rect.left && !rect.right) result.bogus = true;
-    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
-
-    return result;
-  }
-
-  // Work around problem with bounding client rects on ranges being
-  // returned incorrectly when zoomed on IE10 and below.
-  function maybeUpdateRectForZooming(measure, rect) {
-    if (!window.screen || screen.logicalXDPI == null ||
-        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
-      return rect;
-    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
-    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
-    return {left: rect.left * scaleX, right: rect.right * scaleX,
-            top: rect.top * scaleY, bottom: rect.bottom * scaleY};
-  }
-
-  function clearLineMeasurementCacheFor(lineView) {
-    if (lineView.measure) {
-      lineView.measure.cache = {};
-      lineView.measure.heights = null;
-      if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
-        lineView.measure.caches[i] = {};
-    }
-  }
-
-  function clearLineMeasurementCache(cm) {
-    cm.display.externalMeasure = null;
-    removeChildren(cm.display.lineMeasure);
-    for (var i = 0; i < cm.display.view.length; i++)
-      clearLineMeasurementCacheFor(cm.display.view[i]);
-  }
-
-  function clearCaches(cm) {
-    clearLineMeasurementCache(cm);
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
-    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
-    cm.display.lineNumChars = null;
-  }
-
-  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
-  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
-
-  // Converts a {top, bottom, left, right} box from line-local
-  // coordinates into another coordinate system. Context may be one of
-  // "line", "div" (display.lineDiv), "local"/null (editor), or "page".
-  function intoCoordSystem(cm, lineObj, rect, context) {
-    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
-      var size = widgetHeight(lineObj.widgets[i]);
-      rect.top += size; rect.bottom += size;
-    }
-    if (context == "line") return rect;
-    if (!context) context = "local";
-    var yOff = heightAtLine(lineObj);
-    if (context == "local") yOff += paddingTop(cm.display);
-    else yOff -= cm.display.viewOffset;
-    if (context == "page" || context == "window") {
-      var lOff = cm.display.lineSpace.getBoundingClientRect();
-      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
-      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
-      rect.left += xOff; rect.right += xOff;
-    }
-    rect.top += yOff; rect.bottom += yOff;
-    return rect;
-  }
-
-  // Coverts a box from "div" coords to another coordinate system.
-  // Context may be "window", "page", "div", or "local"/null.
-  function fromCoordSystem(cm, coords, context) {
-    if (context == "div") return coords;
-    var left = coords.left, top = coords.top;
-    // First move into "page" coordinate system
-    if (context == "page") {
-      left -= pageScrollX();
-      top -= pageScrollY();
-    } else if (context == "local" || !context) {
-      var localBox = cm.display.sizer.getBoundingClientRect();
-      left += localBox.left;
-      top += localBox.top;
-    }
-
-    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
-    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
-  }
-
-  function charCoords(cm, pos, context, lineObj, bias) {
-    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
-  }
-
-  // Returns a box for a given cursor position, which may have an
-  // 'other' property containing the position of the secondary cursor
-  // on a bidi boundary.
-  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
-    lineObj = lineObj || getLine(cm.doc, pos.line);
-    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
-    function get(ch, right) {
-      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
-      if (right) m.left = m.right; else m.right = m.left;
-      return intoCoordSystem(cm, lineObj, m, context);
-    }
-    function getBidi(ch, partPos) {
-      var part = order[partPos], right = part.level % 2;
-      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
-        part = order[--partPos];
-        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
-        right = true;
-      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
-        part = order[++partPos];
-        ch = bidiLeft(part) - part.level % 2;
-        right = false;
-      }
-      if (right && ch == part.to && ch > part.from) return get(ch - 1);
-      return get(ch, right);
-    }
-    var order = getOrder(lineObj), ch = pos.ch;
-    if (!order) return get(ch);
-    var partPos = getBidiPartAt(order, ch);
-    var val = getBidi(ch, partPos);
-    if (bidiOther != null) val.other = getBidi(ch, bidiOther);
-    return val;
-  }
-
-  // Used to cheaply estimate the coordinates for a position. Used for
-  // intermediate scroll updates.
-  function estimateCoords(cm, pos) {
-    var left = 0, pos = clipPos(cm.doc, pos);
-    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
-    var lineObj = getLine(cm.doc, pos.line);
-    var top = heightAtLine(lineObj) + paddingTop(cm.display);
-    return {left: left, right: left, top: top, bottom: top + lineObj.height};
-  }
-
-  // Positions returned by coordsChar contain some extra information.
-  // xRel is the relative x position of the input coordinates compared
-  // to the found position (so xRel > 0 means the coordinates are to
-  // the right of the character position, for example). When outside
-  // is true, that means the coordinates lie outside the line's
-  // vertical range.
-  function PosWithInfo(line, ch, outside, xRel) {
-    var pos = Pos(line, ch);
-    pos.xRel = xRel;
-    if (outside) pos.outside = true;
-    return pos;
-  }
-
-  // Compute the character position closest to the given coordinates.
-  // Input must be lineSpace-local ("div" coordinate system).
-  function coordsChar(cm, x, y) {
-    var doc = cm.doc;
-    y += cm.display.viewOffset;
-    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
-    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineN > last)
-      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
-    if (x < 0) x = 0;
-
-    var lineObj = getLine(doc, lineN);
-    for (;;) {
-      var found = coordsCharInner(cm, lineObj, lineN, x, y);
-      var merged = collapsedSpanAtEnd(lineObj);
-      var mergedPos = merged && merged.find(0, true);
-      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
-        lineN = lineNo(lineObj = mergedPos.to.line);
-      else
-        return found;
-    }
-  }
-
-  function coordsCharInner(cm, lineObj, lineNo, x, y) {
-    var innerOff = y - heightAtLine(lineObj);
-    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
-    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
-
-    function getX(ch) {
-      var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
-      wrongLine = true;
-      if (innerOff > sp.bottom) return sp.left - adjust;
-      else if (innerOff < sp.top) return sp.left + adjust;
-      else wrongLine = false;
-      return sp.left;
-    }
-
-    var bidi = getOrder(lineObj), dist = lineObj.text.length;
-    var from = lineLeft(lineObj), to = lineRight(lineObj);
-    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
-
-    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
-    // Do a binary search between these bounds.
-    for (;;) {
-      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
-        var ch = x < fromX || x - fromX <= toX - x ? from : to;
-        var xDiff = x - (ch == from ? fromX : toX);
-        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
-        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
-                              xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
-        return pos;
-      }
-      var step = Math.ceil(dist / 2), middle = from + step;
-      if (bidi) {
-        middle = from;
-        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
-      }
-      var middleX = getX(middle);
-      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
-      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
-    }
-  }
-
-  var measureText;
-  // Compute the default text height.
-  function textHeight(display) {
-    if (display.cachedTextHeight != null) return display.cachedTextHeight;
-    if (measureText == null) {
-      measureText = elt("pre");
-      // Measure a bunch of lines, for browsers that compute
-      // fractional heights.
-      for (var i = 0; i < 49; ++i) {
-        measureText.appendChild(document.createTextNode("x"));
-        measureText.appendChild(elt("br"));
-      }
-      measureText.appendChild(document.createTextNode("x"));
-    }
-    removeChildrenAndAdd(display.measure, measureText);
-    var height = measureText.offsetHeight / 50;
-    if (height > 3) display.cachedTextHeight = height;
-    removeChildren(display.measure);
-    return height || 1;
-  }
-
-  // Compute the default character width.
-  function charWidth(display) {
-    if (display.cachedCharWidth != null) return display.cachedCharWidth;
-    var anchor = elt("span", "xxxxxxxxxx");
-    var pre = elt("pre", [anchor]);
-    removeChildrenAndAdd(display.measure, pre);
-    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
-    if (width > 2) display.cachedCharWidth = width;
-    return width || 10;
-  }
-
-  // OPERATIONS
-
-  // Operations are used to wrap a series of changes to the editor
-  // state in such a way that each change won't have to update the
-  // cursor and display (which would be awkward, slow, and
-  // error-prone). Instead, display updates are batched and then all
-  // combined and executed at once.
-
-  var operationGroup = null;
-
-  var nextOpId = 0;
-  // Start a new operation.
-  function startOperation(cm) {
-    cm.curOp = {
-      cm: cm,
-      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
-      startHeight: cm.doc.height, // Used to detect need to update scrollbar
-      forceUpdate: false,      // Used to force a redraw
-      updateInput: null,       // Whether to reset the input textarea
-      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
-      changeObjs: null,        // Accumulated changes, for firing change events
-      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
-      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
-      selectionChanged: false, // Whether the selection needs to be redrawn
-      updateMaxLine: false,    // Set when the widest line needs to be determined anew
-      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
-      scrollToPos: null,       // Used to scroll to a specific position
-      id: ++nextOpId           // Unique ID
-    };
-    if (operationGroup) {
-      operationGroup.ops.push(cm.curOp);
-    } else {
-      cm.curOp.ownsGroup = operationGroup = {
-        ops: [cm.curOp],
-        delayedCallbacks: []
-      };
-    }
-  }
-
-  function fireCallbacksForOps(group) {
-    // Calls delayed callbacks and cursorActivity handlers until no
-    // new ones appear
-    var callbacks = group.delayedCallbacks, i = 0;
-    do {
-      for (; i < callbacks.length; i++)
-        callbacks[i]();
-      for (var j = 0; j < group.ops.length; j++) {
-        var op = group.ops[j];
-        if (op.cursorActivityHandlers)
-          while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
-            op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
-      }
-    } while (i < callbacks.length);
-  }
-
-  // Finish an operation, updating the display and signalling delayed events
-  function endOperation(cm) {
-    var op = cm.curOp, group = op.ownsGroup;
-    if (!group) return;
-
-    try { fireCallbacksForOps(group); }
-    finally {
-      operationGroup = null;
-      for (var i = 0; i < group.ops.length; i++)
-        group.ops[i].cm.curOp = null;
-      endOperations(group);
-    }
-  }
-
-  // The DOM updates done when an operation finishes are batched so
-  // that the minimum number of relayouts are required.
-  function endOperations(group) {
-    var ops = group.ops;
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      endOperation_R1(ops[i]);
-    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
-      endOperation_W1(ops[i]);
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      endOperation_R2(ops[i]);
-    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
-      endOperation_W2(ops[i]);
-    for (var i = 0; i < ops.length; i++) // Read DOM
-      endOperation_finish(ops[i]);
-  }
-
-  function endOperation_R1(op) {
-    var cm = op.cm, display = cm.display;
-    if (op.updateMaxLine) findMaxLine(cm);
-
-    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
-      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
-                         op.scrollToPos.to.line >= display.viewTo) ||
-      display.maxLineChanged && cm.options.lineWrapping;
-    op.update = op.mustUpdate &&
-      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
-  }
-
-  function endOperation_W1(op) {
-    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
-  }
-
-  function endOperation_R2(op) {
-    var cm = op.cm, display = cm.display;
-    if (op.updatedDisplay) updateHeightsInViewport(cm);
-
-    op.barMeasure = measureForScrollbars(cm);
-
-    // If the max line changed since it was last measured, measure it,
-    // and ensure the document's width matches it.
-    // updateDisplay_W2 will use these properties to do the actual resizing
-    if (display.maxLineChanged && !cm.options.lineWrapping) {
-      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
-      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
-                                  scrollerCutOff - display.scroller.clientWidth);
-    }
-
-    if (op.updatedDisplay || op.selectionChanged)
-      op.newSelectionNodes = drawSelection(cm);
-  }
-
-  function endOperation_W2(op) {
-    var cm = op.cm;
-
-    if (op.adjustWidthTo != null) {
-      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
-      if (op.maxScrollLeft < cm.doc.scrollLeft)
-        setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
-      cm.display.maxLineChanged = false;
-    }
-
-    if (op.newSelectionNodes)
-      showSelection(cm, op.newSelectionNodes);
-    if (op.updatedDisplay)
-      setDocumentHeight(cm, op.barMeasure);
-    if (op.updatedDisplay || op.startHeight != cm.doc.height)
-      updateScrollbars(cm, op.barMeasure);
-
-    if (op.selectionChanged) restartBlink(cm);
-
-    if (cm.state.focused && op.updateInput)
-      resetInput(cm, op.typing);
-  }
-
-  function endOperation_finish(op) {
-    var cm = op.cm, display = cm.display, doc = cm.doc;
-
-    if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.display.scroller.scrollWidth) > 1)
-      updateScrollbars(cm);
-
-    if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
-
-    // Abort mouse wheel delta measurement, when scrolling explicitly
-    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
-      display.wheelStartX = display.wheelStartY = null;
-
-    // Propagate the scroll position to the actual DOM scroller
-    if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
-      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
-      display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
-    }
-    if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
-      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
-      display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
-      alignHorizontally(cm);
-    }
-    // If we need to scroll a specific position into view, do so.
-    if (op.scrollToPos) {
-      var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
-                                     clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
-      if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
-    }
-
-    // Fire events for markers that are hidden/unidden by editing or
-    // undoing
-    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
-    if (hidden) for (var i = 0; i < hidden.length; ++i)
-      if (!hidden[i].lines.length) signal(hidden[i], "hide");
-    if (unhidden) for (var i = 0; i < unhidden.length; ++i)
-      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
-
-    if (display.wrapper.offsetHeight)
-      doc.scrollTop = cm.display.scroller.scrollTop;
-
-    // Apply workaround for two webkit bugs
-    if (op.updatedDisplay && webkit) {
-      if (cm.options.lineWrapping)
-        checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420)
-      if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth &&
-          op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 &&
-          !hScrollbarTakesSpace(cm))
-        updateScrollbars(cm); // (Issue #2562)
-    }
-
-    // Fire change events, and delayed event handlers
-    if (op.changeObjs)
-      signal(cm, "changes", cm, op.changeObjs);
-  }
-
-  // Run the given function in an operation
-  function runInOp(cm, f) {
-    if (cm.curOp) return f();
-    startOperation(cm);
-    try { return f(); }
-    finally { endOperation(cm); }
-  }
-  // Wraps a function in an operation. Returns the wrapped function.
-  function operation(cm, f) {
-    return function() {
-      if (cm.curOp) return f.apply(cm, arguments);
-      startOperation(cm);
-      try { return f.apply(cm, arguments); }
-      finally { endOperation(cm); }
-    };
-  }
-  // Used to add methods to editor and doc instances, wrapping them in
-  // operations.
-  function methodOp(f) {
-    return function() {
-      if (this.curOp) return f.apply(this, arguments);
-      startOperation(this);
-      try { return f.apply(this, arguments); }
-      finally { endOperation(this); }
-    };
-  }
-  function docMethodOp(f) {
-    return function() {
-      var cm = this.cm;
-      if (!cm || cm.curOp) return f.apply(this, arguments);
-      startOperation(cm);
-      try { return f.apply(this, arguments); }
-      finally { endOperation(cm); }
-    };
-  }
-
-  // VIEW TRACKING
-
-  // These objects are used to represent the visible (currently drawn)
-  // part of the document. A LineView may correspond to multiple
-  // logical lines, if those are connected by collapsed ranges.
-  function LineView(doc, line, lineN) {
-    // The starting line
-    this.line = line;
-    // Continuing lines, if any
-    this.rest = visualLineContinued(line);
-    // Number of logical lines in this visual line
-    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
-    this.node = this.text = null;
-    this.hidden = lineIsHidden(doc, line);
-  }
-
-  // Create a range of LineView objects for the given lines.
-  function buildViewArray(cm, from, to) {
-    var array = [], nextPos;
-    for (var pos = from; pos < to; pos = nextPos) {
-      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
-      nextPos = pos + view.size;
-      array.push(view);
-    }
-    return array;
-  }
-
-  // Updates the display.view data structure for a given change to the
-  // document. From and to are in pre-change coordinates. Lendiff is
-  // the amount of lines added or subtracted by the change. This is
-  // used for changes that span multiple lines, or change the way
-  // lines are divided into visual lines. regLineChange (below)
-  // registers single-line changes.
-  function regChange(cm, from, to, lendiff) {
-    if (from == null) from = cm.doc.first;
-    if (to == null) to = cm.doc.first + cm.doc.size;
-    if (!lendiff) lendiff = 0;
-
-    var display = cm.display;
-    if (lendiff && to < display.viewTo &&
-        (display.updateLineNumbers == null || display.updateLineNumbers > from))
-      display.updateLineNumbers = from;
-
-    cm.curOp.viewChanged = true;
-
-    if (from >= display.viewTo) { // Change after
-      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
-        resetView(cm);
-    } else if (to <= display.viewFrom) { // Change before
-      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
-        resetView(cm);
-      } else {
-        display.viewFrom += lendiff;
-        display.viewTo += lendiff;
-      }
-    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
-      resetView(cm);
-    } else if (from <= display.viewFrom) { // Top overlap
-      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cut) {
-        display.view = display.view.slice(cut.index);
-        display.viewFrom = cut.lineN;
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    } else if (to >= display.viewTo) { // Bottom overlap
-      var cut = viewCuttingPoint(cm, from, from, -1);
-      if (cut) {
-        display.view = display.view.slice(0, cut.index);
-        display.viewTo = cut.lineN;
-      } else {
-        resetView(cm);
-      }
-    } else { // Gap in the middle
-      var cutTop = viewCuttingPoint(cm, from, from, -1);
-      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
-      if (cutTop && cutBot) {
-        display.view = display.view.slice(0, cutTop.index)
-          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
-          .concat(display.view.slice(cutBot.index));
-        display.viewTo += lendiff;
-      } else {
-        resetView(cm);
-      }
-    }
-
-    var ext = display.externalMeasured;
-    if (ext) {
-      if (to < ext.lineN)
-        ext.lineN += lendiff;
-      else if (from < ext.lineN + ext.size)
-        display.externalMeasured = null;
-    }
-  }
-
-  // Register a change to a single line. Type must be one of "text",
-  // "gutter", "class", "widget"
-  function regLineChange(cm, line, type) {
-    cm.curOp.viewChanged = true;
-    var display = cm.display, ext = cm.display.externalMeasured;
-    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
-      display.externalMeasured = null;
-
-    if (line < display.viewFrom || line >= display.viewTo) return;
-    var lineView = display.view[findViewIndex(cm, line)];
-    if (lineView.node == null) return;
-    var arr = lineView.changes || (lineView.changes = []);
-    if (indexOf(arr, type) == -1) arr.push(type);
-  }
-
-  // Clear the view.
-  function resetView(cm) {
-    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
-    cm.display.view = [];
-    cm.display.viewOffset = 0;
-  }
-
-  // Find the view element corresponding to a given line. Return null
-  // when the line isn't visible.
-  function findViewIndex(cm, n) {
-    if (n >= cm.display.viewTo) return null;
-    n -= cm.display.viewFrom;
-    if (n < 0) return null;
-    var view = cm.display.view;
-    for (var i = 0; i < view.length; i++) {
-      n -= view[i].size;
-      if (n < 0) return i;
-    }
-  }
-
-  function viewCuttingPoint(cm, oldN, newN, dir) {
-    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
-    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
-      return {index: index, lineN: newN};
-    for (var i = 0, n = cm.display.viewFrom; i < index; i++)
-      n += view[i].size;
-    if (n != oldN) {
-      if (dir > 0) {
-        if (index == view.length - 1) return null;
-        diff = (n + view[index].size) - oldN;
-        index++;
-      } else {
-        diff = n - oldN;
-      }
-      oldN += diff; newN += diff;
-    }
-    while (visualLineNo(cm.doc, newN) != newN) {
-      if (index == (dir < 0 ? 0 : view.length - 1)) return null;
-      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
-      index += dir;
-    }
-    return {index: index, lineN: newN};
-  }
-
-  // Force the view to cover a given range, adding empty view element
-  // or clipping off existing ones as needed.
-  function adjustView(cm, from, to) {
-    var display = cm.display, view = display.view;
-    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
-      display.view = buildViewArray(cm, from, to);
-      display.viewFrom = from;
-    } else {
-      if (display.viewFrom > from)
-        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
-      else if (display.viewFrom < from)
-        display.view = display.view.slice(findViewIndex(cm, from));
-      display.viewFrom = from;
-      if (display.viewTo < to)
-        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
-      else if (display.viewTo > to)
-        display.view = display.view.slice(0, findViewIndex(cm, to));
-    }
-    display.viewTo = to;
-  }
-
-  // Count the number of lines in the view whose DOM representation is
-  // out of date (or nonexistent).
-  function countDirtyView(cm) {
-    var view = cm.display.view, dirty = 0;
-    for (var i = 0; i < view.length; i++) {
-      var lineView = view[i];
-      if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
-    }
-    return dirty;
-  }
-
-  // INPUT HANDLING
-
-  // Poll for input changes, using the normal rate of polling. This
-  // runs as long as the editor is focused.
-  function slowPoll(cm) {
-    if (cm.display.pollingFast) return;
-    cm.display.poll.set(cm.options.pollInterval, function() {
-      readInput(cm);
-      if (cm.state.focused) slowPoll(cm);
-    });
-  }
-
-  // When an event has just come in that is likely to add or change
-  // something in the input textarea, we poll faster, to ensure that
-  // the change appears on the screen quickly.
-  function fastPoll(cm) {
-    var missed = false;
-    cm.display.pollingFast = true;
-    function p() {
-      var changed = readInput(cm);
-      if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
-      else {cm.display.pollingFast = false; slowPoll(cm);}
-    }
-    cm.display.poll.set(20, p);
-  }
-
-  // This will be set to an array of strings when copying, so that,
-  // when pasting, we know what kind of selections the copied text
-  // was made out of.
-  var lastCopied = null;
-
-  // Read input from the textarea, and update the document to match.
-  // When something is selected, it is present in the textarea, and
-  // selected (unless it is huge, in which case a placeholder is
-  // used). When nothing is selected, the cursor sits after previously
-  // seen text (can be empty), which is stored in prevInput (we must
-  // not reset the textarea when typing, because that breaks IME).
-  function readInput(cm) {
-    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc;
-    // Since this is called a *lot*, try to bail out as cheaply as
-    // possible when it is clear that nothing happened. hasSelection
-    // will be the case when there is a lot of text in the textarea,
-    // in which case reading its value would be expensive.
-    if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput)
-      return false;
-    // See paste handler for more on the fakedLastChar kludge
-    if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
-      input.value = input.value.substring(0, input.value.length - 1);
-      cm.state.fakedLastChar = false;
-    }
-    var text = input.value;
-    // If nothing changed, bail.
-    if (text == prevInput && !cm.somethingSelected()) return false;
-    // Work around nonsensical selection resetting in IE9/10, and
-    // inexplicable appearance of private area unicode characters on
-    // some key combos in Mac (#2689).
-    if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
-        mac && /[\uf700-\uf7ff]/.test(text)) {
-      resetInput(cm);
-      return false;
-    }
-
-    var withOp = !cm.curOp;
-    if (withOp) startOperation(cm);
-    cm.display.shift = false;
-
-    if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
-      prevInput = "\u200b";
-    // Find the part of the input that is actually new
-    var same = 0, l = Math.min(prevInput.length, text.length);
-    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
-    var inserted = text.slice(same), textLines = splitLines(inserted);
-
-    // When pasing N lines into N selections, insert one line per selection
-    var multiPaste = null;
-    if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
-      if (lastCopied && lastCopied.join("\n") == inserted)
-        multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
-      else if (textLines.length == doc.sel.ranges.length)
-        multiPaste = map(textLines, function(l) { return [l]; });
-    }
-
-    // Normal behavior is to insert the new text into every selection
-    for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
-      var range = doc.sel.ranges[i];
-      var from = range.from(), to = range.to();
-      // Handle deletion
-      if (same < prevInput.length)
-        from = Pos(from.line, from.ch - (prevInput.length - same));
-      // Handle overwrite
-      else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
-        to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
-      var updateInput = cm.curOp.updateInput;
-      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
-                         origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
-      makeChange(cm.doc, changeEvent);
-      signalLater(cm, "inputRead", cm, changeEvent);
-      // When an 'electric' character is inserted, immediately trigger a reindent
-      if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
-          cm.options.smartIndent && range.head.ch < 100 &&
-          (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
-        var mode = cm.getModeAt(range.head);
-        var end = changeEnd(changeEvent);
-        if (mode.electricChars) {
-          for (var j = 0; j < mode.electricChars.length; j++)
-            if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
-              indentLine(cm, end.line, "smart");
-              break;
-            }
-        } else if (mode.electricInput) {
-          if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
-            indentLine(cm, end.line, "smart");
-        }
-      }
-    }
-    ensureCursorVisible(cm);
-    cm.curOp.updateInput = updateInput;
-    cm.curOp.typing = true;
-
-    // Don't leave long text in the textarea, since it makes further polling slow
-    if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
-    else cm.display.prevInput = text;
-    if (withOp) endOperation(cm);
-    cm.state.pasteIncoming = cm.state.cutIncoming = false;
-    return true;
-  }
-
-  // Reset the input to correspond to the selection (or to be empty,
-  // when not typing and nothing is selected)
-  function resetInput(cm, typing) {
-    var minimal, selected, doc = cm.doc;
-    if (cm.somethingSelected()) {
-      cm.display.prevInput = "";
-      var range = doc.sel.primary();
-      minimal = hasCopyEvent &&
-        (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
-      var content = minimal ? "-" : selected || cm.getSelection();
-      cm.display.input.value = content;
-      if (cm.state.focused) selectInput(cm.display.input);
-      if (ie && ie_version >= 9) cm.display.inputHasSelection = content;
-    } else if (!typing) {
-      cm.display.prevInput = cm.display.input.value = "";
-      if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
-    }
-    cm.display.inaccurateSelection = minimal;
-  }
-
-  function focusInput(cm) {
-    if (cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.display.input))
-      cm.display.input.focus();
-  }
-
-  function ensureFocus(cm) {
-    if (!cm.state.focused) { focusInput(cm); onFocus(cm); }
-  }
-
-  function isReadOnly(cm) {
-    return cm.options.readOnly || cm.doc.cantEdit;
-  }
-
-  // EVENT HANDLERS
-
-  // Attach the necessary event handlers when initializing the editor
-  function registerEventHandlers(cm) {
-    var d = cm.display;
-    on(d.scroller, "mousedown", operation(cm, onMouseDown));
-    // Older IE's will not fire a second mousedown for a double click
-    if (ie && ie_version < 11)
-      on(d.scroller, "dblclick", operation(cm, function(e) {
-        if (signalDOMEvent(cm, e)) return;
-        var pos = posFromMouse(cm, e);
-        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
-        e_preventDefault(e);
-        var word = cm.findWordAt(pos);
-        extendSelection(cm.doc, word.anchor, word.head);
-      }));
-    else
-      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
-    // Prevent normal selection in the editor (we handle our own)
-    on(d.lineSpace, "selectstart", function(e) {
-      if (!eventInWidget(d, e)) e_preventDefault(e);
-    });
-    // Some browsers fire contextmenu *after* opening the menu, at
-    // which point we can't mess with it anymore. Context menu is
-    // handled in onMouseDown for these browsers.
-    if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
-
-    // Sync scrolling between fake scrollbars and real scrollable
-    // area, ensure viewport is updated when scrolling.
-    on(d.scroller, "scroll", function() {
-      if (d.scroller.clientHeight) {
-        setScrollTop(cm, d.scroller.scrollTop);
-        setScrollLeft(cm, d.scroller.scrollLeft, true);
-        signal(cm, "scroll", cm);
-      }
-    });
-    on(d.scrollbarV, "scroll", function() {
-      if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
-    });
-    on(d.scrollbarH, "scroll", function() {
-      if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
-    });
-
-    // Listen to wheel events in order to try and update the viewport on time.
-    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
-    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
-
-    // Prevent clicks in the scrollbars from killing focus
-    function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
-    on(d.scrollbarH, "mousedown", reFocus);
-    on(d.scrollbarV, "mousedown", reFocus);
-    // Prevent wrapper from ever scrolling
-    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
-
-    on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
-    on(d.input, "input", function() {
-      if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
-      fastPoll(cm);
-    });
-    on(d.input, "keydown", operation(cm, onKeyDown));
-    on(d.input, "keypress", operation(cm, onKeyPress));
-    on(d.input, "focus", bind(onFocus, cm));
-    on(d.input, "blur", bind(onBlur, cm));
-
-    function drag_(e) {
-      if (!signalDOMEvent(cm, e)) e_stop(e);
-    }
-    if (cm.options.dragDrop) {
-      on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
-      on(d.scroller, "dragenter", drag_);
-      on(d.scroller, "dragover", drag_);
-      on(d.scroller, "drop", operation(cm, onDrop));
-    }
-    on(d.scroller, "paste", function(e) {
-      if (eventInWidget(d, e)) return;
-      cm.state.pasteIncoming = true;
-      focusInput(cm);
-      fastPoll(cm);
-    });
-    on(d.input, "paste", function() {
-      // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
-      // Add a char to the end of textarea before paste occur so that
-      // selection doesn't span to the end of textarea.
-      if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
-        var start = d.input.selectionStart, end = d.input.selectionEnd;
-        d.input.value += "$";
-        // The selection end needs to be set before the start, otherwise there
-        // can be an intermediate non-empty selection between the two, which
-        // can override the middle-click paste buffer on linux and cause the
-        // wrong thing to get pasted.
-        d.input.selectionEnd = end;
-        d.input.selectionStart = start;
-        cm.state.fakedLastChar = true;
-      }
-      cm.state.pasteIncoming = true;
-      fastPoll(cm);
-    });
-
-    function prepareCopyCut(e) {
-      if (cm.somethingSelected()) {
-        lastCopied = cm.getSelections();
-        if (d.inaccurateSelection) {
-          d.prevInput = "";
-          d.inaccurateSelection = false;
-          d.input.value = lastCopied.join("\n");
-          selectInput(d.input);
-        }
-      } else {
-        var text = [], ranges = [];
-        for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
-          var line = cm.doc.sel.ranges[i].head.line;
-          var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
-          ranges.push(lineRange);
-          text.push(cm.getRange(lineRange.anchor, lineRange.head));
-        }
-        if (e.type == "cut") {
-          cm.setSelections(ranges, null, sel_dontScroll);
-        } else {
-          d.prevInput = "";
-          d.input.value = text.join("\n");
-          selectInput(d.input);
-        }
-        lastCopied = text;
-      }
-      if (e.type == "cut") cm.state.cutIncoming = true;
-    }
-    on(d.input, "cut", prepareCopyCut);
-    on(d.input, "copy", prepareCopyCut);
-
-    // Needed to handle Tab key in KHTML
-    if (khtml) on(d.sizer, "mouseup", function() {
-      if (activeElt() == d.input) d.input.blur();
-      focusInput(cm);
-    });
-  }
-
-  // Called when the window resizes
-  function onResize(cm) {
-    // Might be a text scaling operation, clear size caches.
-    var d = cm.display;
-    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
-    cm.setSize();
-  }
-
-  // MOUSE EVENTS
-
-  // Return true when the given mouse event happened in a widget
-  function eventInWidget(display, e) {
-    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
-      if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
-    }
-  }
-
-  // Given a mouse event, find the corresponding position. If liberal
-  // is false, it checks whether a gutter or scrollbar was clicked,
-  // and returns null if it was. forRect is used by rectangular
-  // selections, and tries to estimate a character position even for
-  // coordinates beyond the right of the text.
-  function posFromMouse(cm, e, liberal, forRect) {
-    var display = cm.display;
-    if (!liberal) {
-      var target = e_target(e);
-      if (target == display.scrollbarH || target == display.scrollbarV ||
-          target == display.scrollbarFiller || target == display.gutterFiller) return null;
-    }
-    var x, y, space = display.lineSpace.getBoundingClientRect();
-    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
-    try { x = e.clientX - space.left; y = e.clientY - space.top; }
-    catch (e) { return null; }
-    var coords = coordsChar(cm, x, y), line;
-    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
-      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
-      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
-    }
-    return coords;
-  }
-
-  // A mouse down can be a single click, double click, triple click,
-  // start of selection drag, start of text drag, new cursor
-  // (ctrl-click), rectangle drag (alt-drag), or xwin
-  // middle-click-paste. Or it might be a click on something we should
-  // not interfere with, such as a scrollbar or widget.
-  function onMouseDown(e) {
-    if (signalDOMEvent(this, e)) return;
-    var cm = this, display = cm.display;
-    display.shift = e.shiftKey;
-
-    if (eventInWidget(display, e)) {
-      if (!webkit) {
-        // Briefly turn off draggability, to allow widgets to do
-        // normal dragging things.
-        display.scroller.draggable = false;
-        setTimeout(function(){display.scroller.draggable = true;}, 100);
-      }
-      return;
-    }
-    if (clickInGutter(cm, e)) return;
-    var start = posFromMouse(cm, e);
-    window.focus();
-
-    switch (e_button(e)) {
-    case 1:
-      if (start)
-        leftButtonDown(cm, e, start);
-      else if (e_target(e) == display.scroller)
-        e_preventDefault(e);
-      break;
-    case 2:
-      if (webkit) cm.state.lastMiddleDown = +new Date;
-      if (start) extendSelection(cm.doc, start);
-      setTimeout(bind(focusInput, cm), 20);
-      e_preventDefault(e);
-      break;
-    case 3:
-      if (captureRightClick) onContextMenu(cm, e);
-      break;
-    }
-  }
-
-  var lastClick, lastDoubleClick;
-  function leftButtonDown(cm, e, start) {
-    setTimeout(bind(ensureFocus, cm), 0);
-
-    var now = +new Date, type;
-    if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
-      type = "triple";
-    } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
-      type = "double";
-      lastDoubleClick = {time: now, pos: start};
-    } else {
-      type = "single";
-      lastClick = {time: now, pos: start};
-    }
-
-    var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey;
-    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
-        type == "single" && sel.contains(start) > -1 && sel.somethingSelected())
-      leftButtonStartDrag(cm, e, start, modifier);
-    else
-      leftButtonSelect(cm, e, start, type, modifier);
-  }
-
-  // Start a text drag. When it ends, see if any dragging actually
-  // happen, and treat as a click if it didn't.
-  function leftButtonStartDrag(cm, e, start, modifier) {
-    var display = cm.display;
-    var dragEnd = operation(cm, function(e2) {
-      if (webkit) display.scroller.draggable = false;
-      cm.state.draggingText = false;
-      off(document, "mouseup", dragEnd);
-      off(display.scroller, "drop", dragEnd);
-      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
-        e_preventDefault(e2);
-        if (!modifier)
-          extendSelection(cm.doc, start);
-        focusInput(cm);
-        // Work around unexplainable focus problem in IE9 (#2127)
-        if (ie && ie_version == 9)
-          setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
-      }
-    });
-    // Let the drag handler handle this.
-    if (webkit) display.scroller.draggable = true;
-    cm.state.draggingText = dragEnd;
-    // IE's approach to draggable
-    if (display.scroller.dragDrop) display.scroller.dragDrop();
-    on(document, "mouseup", dragEnd);
-    on(display.scroller, "drop", dragEnd);
-  }
-
-  // Normal selection, as opposed to text dragging.
-  function leftButtonSelect(cm, e, start, type, addNew) {
-    var display = cm.display, doc = cm.doc;
-    e_preventDefault(e);
-
-    var ourRange, ourIndex, startSel = doc.sel;
-    if (addNew && !e.shiftKey) {
-      ourIndex = doc.sel.contains(start);
-      if (ourIndex > -1)
-        ourRange = doc.sel.ranges[ourIndex];
-      else
-        ourRange = new Range(start, start);
-    } else {
-      ourRange = doc.sel.primary();
-    }
-
-    if (e.altKey) {
-      type = "rect";
-      if (!addNew) ourRange = new Range(start, start);
-      start = posFromMouse(cm, e, true, true);
-      ourIndex = -1;
-    } else if (type == "double") {
-      var word = cm.findWordAt(start);
-      if (cm.display.shift || doc.extend)
-        ourRange = extendRange(doc, ourRange, word.anchor, word.head);
-      else
-        ourRange = word;
-    } else if (type == "triple") {
-      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
-      if (cm.display.shift || doc.extend)
-        ourRange = extendRange(doc, ourRange, line.anchor, line.head);
-      else
-        ourRange = line;
-    } else {
-      ourRange = extendRange(doc, ourRange, start);
-    }
-
-    if (!addNew) {
-      ourIndex = 0;
-      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
-      startSel = doc.sel;
-    } else if (ourIndex > -1) {
-      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
-    } else {
-      ourIndex = doc.sel.ranges.length;
-      setSelection(doc, normalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
-                   {scroll: false, origin: "*mouse"});
-    }
-
-    var lastPos = start;
-    function extendTo(pos) {
-      if (cmp(lastPos, pos) == 0) return;
-      lastPos = pos;
-
-      if (type == "rect") {
-        var ranges = [], tabSize = cm.options.tabSize;
-        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
-        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
-        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
-        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
-             line <= end; line++) {
-          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
-          if (left == right)
-            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
-          else if (text.length > leftPos)
-            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
-        }
-        if (!ranges.length) ranges.push(new Range(start, start));
-        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
-                     {origin: "*mouse", scroll: false});
-        cm.scrollIntoView(pos);
-      } else {
-        var oldRange = ourRange;
-        var anchor = oldRange.anchor, head = pos;
-        if (type != "single") {
-          if (type == "double")
-            var range = cm.findWordAt(pos);
-          else
-            var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
-          if (cmp(range.anchor, anchor) > 0) {
-            head = range.head;
-            anchor = minPos(oldRange.from(), range.anchor);
-          } else {
-            head = range.anchor;
-            anchor = maxPos(oldRange.to(), range.head);
-          }
-        }
-        var ranges = startSel.ranges.slice(0);
-        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
-        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
-      }
-    }
-
-    var editorSize = display.wrapper.getBoundingClientRect();
-    // Used to ensure timeout re-tries don't fire when another extend
-    // happened in the meantime (clearTimeout isn't reliable -- at
-    // least on Chrome, the timeouts still happen even when cleared,
-    // if the clear happens after their scheduled firing time).
-    var counter = 0;
-
-    function extend(e) {
-      var curCount = ++counter;
-      var cur = posFromMouse(cm, e, true, type == "rect");
-      if (!cur) return;
-      if (cmp(cur, lastPos) != 0) {
-        ensureFocus(cm);
-        extendTo(cur);
-        var visible = visibleLines(display, doc);
-        if (cur.line >= visible.to || cur.line < visible.from)
-          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
-      } else {
-        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
-        if (outside) setTimeout(operation(cm, function() {
-          if (counter != curCount) return;
-          display.scroller.scrollTop += outside;
-          extend(e);
-        }), 50);
-      }
-    }
-
-    function done(e) {
-      counter = Infinity;
-      e_preventDefault(e);
-      focusInput(cm);
-      off(document, "mousemove", move);
-      off(document, "mouseup", up);
-      doc.history.lastSelOrigin = null;
-    }
-
-    var move = operation(cm, function(e) {
-      if (!e_button(e)) done(e);
-      else extend(e);
-    });
-    var up = operation(cm, done);
-    on(document, "mousemove", move);
-    on(document, "mouseup", up);
-  }
-
-  // Determines whether an event happened in the gutter, and fires the
-  // handlers for the corresponding event.
-  function gutterEvent(cm, e, type, prevent, signalfn) {
-    try { var mX = e.clientX, mY = e.clientY; }
-    catch(e) { return false; }
-    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
-    if (prevent) e_preventDefault(e);
-
-    var display = cm.display;
-    var lineBox = display.lineDiv.getBoundingClientRect();
-
-    if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
-    mY -= lineBox.top - display.viewOffset;
-
-    for (var i = 0; i < cm.options.gutters.length; ++i) {
-      var g = display.gutters.childNodes[i];
-      if (g && g.getBoundingClientRect().right >= mX) {
-        var line = lineAtHeight(cm.doc, mY);
-        var gutter = cm.options.gutters[i];
-        signalfn(cm, type, cm, line, gutter, e);
-        return e_defaultPrevented(e);
-      }
-    }
-  }
-
-  function clickInGutter(cm, e) {
-    return gutterEvent(cm, e, "gutterClick", true, signalLater);
-  }
-
-  // Kludge to work around strange IE behavior where it'll sometimes
-  // re-fire a series of drag-related events right after the drop (#1551)
-  var lastDrop = 0;
-
-  function onDrop(e) {
-    var cm = this;
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
-      return;
-    e_preventDefault(e);
-    if (ie) lastDrop = +new Date;
-    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
-    if (!pos || isReadOnly(cm)) return;
-    // Might be a file drop, in which case we simply extract the text
-    // and insert it.
-    if (files && files.length && window.FileReader && window.File) {
-      var n = files.length, text = Array(n), read = 0;
-      var loadFile = function(file, i) {
-        var reader = new FileReader;
-        reader.onload = operation(cm, function() {
-          text[i] = reader.result;
-          if (++read == n) {
-            pos = clipPos(cm.doc, pos);
-            var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"};
-            makeChange(cm.doc, change);
-            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
-          }
-        });
-        reader.readAsText(file);
-      };
-      for (var i = 0; i < n; ++i) loadFile(files[i], i);
-    } else { // Normal drop
-      // Don't do a replace if the drop happened inside of the selected text.
-      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
-        cm.state.draggingText(e);
-        // Ensure the editor is re-focused
-        setTimeout(bind(focusInput, cm), 20);
-        return;
-      }
-      try {
-        var text = e.dataTransfer.getData("Text");
-        if (text) {
-          if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey))
-            var selected = cm.listSelections();
-          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
-          if (selected) for (var i = 0; i < selected.length; ++i)
-            replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
-          cm.replaceSelection(text, "around", "paste");
-          focusInput(cm);
-        }
-      }
-      catch(e){}
-    }
-  }
-
-  function onDragStart(cm, e) {
-    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
-
-    e.dataTransfer.setData("Text", cm.getSelection());
-
-    // Use dummy image instead of default browsers image.
-    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
-    if (e.dataTransfer.setDragImage && !safari) {
-      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
-      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-      if (presto) {
-        img.width = img.height = 1;
-        cm.display.wrapper.appendChild(img);
-        // Force a relayout, or Opera won't use our image for some obscure reason
-        img._top = img.offsetTop;
-      }
-      e.dataTransfer.setDragImage(img, 0, 0);
-      if (presto) img.parentNode.removeChild(img);
-    }
-  }
-
-  // SCROLL EVENTS
-
-  // Sync the scrollable area and scrollbars, ensure the viewport
-  // covers the visible area.
-  function setScrollTop(cm, val) {
-    if (Math.abs(cm.doc.scrollTop - val) < 2) return;
-    cm.doc.scrollTop = val;
-    if (!gecko) updateDisplaySimple(cm, {top: val});
-    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
-    if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
-    if (gecko) updateDisplaySimple(cm);
-    startWorker(cm, 100);
-  }
-  // Sync scroller and scrollbar, ensure the gutter elements are
-  // aligned.
-  function setScrollLeft(cm, val, isScroller) {
-    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
-    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
-    cm.doc.scrollLeft = val;
-    alignHorizontally(cm);
-    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
-    if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
-  }
-
-  // Since the delta values reported on mouse wheel events are
-  // unstandardized between browsers and even browser versions, and
-  // generally horribly unpredictable, this code starts by measuring
-  // the scroll effect that the first few mouse wheel events have,
-  // and, from that, detects the way it can convert deltas to pixel
-  // offsets afterwards.
-  //
-  // The reason we want to know the amount a wheel event will scroll
-  // is that it gives us a chance to update the display before the
-  // actual scrolling happens, reducing flickering.
-
-  var wheelSamples = 0, wheelPixelsPerUnit = null;
-  // Fill in a browser-detected starting value on browsers where we
-  // know one. These don't have to be accurate -- the result of them
-  // being wrong would just be a slight flicker on the first wheel
-  // scroll (if it is large enough).
-  if (ie) wheelPixelsPerUnit = -.53;
-  else if (gecko) wheelPixelsPerUnit = 15;
-  else if (chrome) wheelPixelsPerUnit = -.7;
-  else if (safari) wheelPixelsPerUnit = -1/3;
-
-  function onScrollWheel(cm, e) {
-    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
-    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
-    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
-    else if (dy == null) dy = e.wheelDelta;
-
-    var display = cm.display, scroll = display.scroller;
-    // Quit if there's nothing to scroll here
-    if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
-          dy && scroll.scrollHeight > scroll.clientHeight)) return;
-
-    // Webkit browsers on OS X abort momentum scrolls when the target
-    // of the scroll event is removed from the scrollable element.
-    // This hack (see related code in patchDisplay) makes sure the
-    // element is kept around.
-    if (dy && mac && webkit) {
-      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
-        for (var i = 0; i < view.length; i++) {
-          if (view[i].node == cur) {
-            cm.display.currentWheelTarget = cur;
-            break outer;
-          }
-        }
-      }
-    }
-
-    // On some browsers, horizontal scrolling will cause redraws to
-    // happen before the gutter has been realigned, causing it to
-    // wriggle around in a most unseemly way. When we have an
-    // estimated pixels/delta value, we just handle horizontal
-    // scrolling entirely here. It'll be slightly off from native, but
-    // better than glitching out.
-    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
-      if (dy)
-        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
-      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
-      e_preventDefault(e);
-      display.wheelStartX = null; // Abort measurement, if in progress
-      return;
-    }
-
-    // 'Project' the visible viewport to cover the area that is being
-    // scrolled into view (if we know enough to estimate it).
-    if (dy && wheelPixelsPerUnit != null) {
-      var pixels = dy * wheelPixelsPerUnit;
-      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
-      if (pixels < 0) top = Math.max(0, top + pixels - 50);
-      else bot = Math.min(cm.doc.height, bot + pixels + 50);
-      updateDisplaySimple(cm, {top: top, bottom: bot});
-    }
-
-    if (wheelSamples < 20) {
-      if (display.wheelStartX == null) {
-        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
-        display.wheelDX = dx; display.wheelDY = dy;
-        setTimeout(function() {
-          if (display.wheelStartX == null) return;
-          var movedX = scroll.scrollLeft - display.wheelStartX;
-          var movedY = scroll.scrollTop - display.wheelStartY;
-          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
-            (movedX && display.wheelDX && movedX / display.wheelDX);
-          display.wheelStartX = display.wheelStartY = null;
-          if (!sample) return;
-          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
-          ++wheelSamples;
-        }, 200);
-      } else {
-        display.wheelDX += dx; display.wheelDY += dy;
-      }
-    }
-  }
-
-  // KEY EVENTS
-
-  // Run a handler that was bound to a key.
-  function doHandleBinding(cm, bound, dropShift) {
-    if (typeof bound == "string") {
-      bound = commands[bound];
-      if (!bound) return false;
-    }
-    // Ensure previous input has been read, so that the handler sees a
-    // consistent view of the document
-    if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
-    var prevShift = cm.display.shift, done = false;
-    try {
-      if (isReadOnly(cm)) cm.state.suppressEdits = true;
-      if (dropShift) cm.display.shift = false;
-      done = bound(cm) != Pass;
-    } finally {
-      cm.display.shift = prevShift;
-      cm.state.suppressEdits = false;
-    }
-    return done;
-  }
-
-  // Collect the currently active keymaps.
-  function allKeyMaps(cm) {
-    var maps = cm.state.keyMaps.slice(0);
-    if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
-    maps.push(cm.options.keyMap);
-    return maps;
-  }
-
-  var maybeTransition;
-  // Handle a key from the keydown event.
-  function handleKeyBinding(cm, e) {
-    // Handle automatic keymap transitions
-    var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
-    clearTimeout(maybeTransition);
-    if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
-      if (getKeyMap(cm.options.keyMap) == startMap) {
-        cm.options.keyMap = (next.call ? next.call(null, cm) : next);
-        keyMapChanged(cm);
-      }
-    }, 50);
-
-    var name = keyName(e, true), handled = false;
-    if (!name) return false;
-    var keymaps = allKeyMaps(cm);
-
-    if (e.shiftKey) {
-      // First try to resolve full name (including 'Shift-'). Failing
-      // that, see if there is a cursor-motion command (starting with
-      // 'go') bound to the keyname without 'Shift-'.
-      handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
-             || lookupKey(name, keymaps, function(b) {
-                  if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
-                    return doHandleBinding(cm, b);
-                });
-    } else {
-      handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
-    }
-
-    if (handled) {
-      e_preventDefault(e);
-      restartBlink(cm);
-      signalLater(cm, "keyHandled", cm, name, e);
-    }
-    return handled;
-  }
-
-  // Handle a key from the keypress event
-  function handleCharBinding(cm, e, ch) {
-    var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
-                            function(b) { return doHandleBinding(cm, b, true); });
-    if (handled) {
-      e_preventDefault(e);
-      restartBlink(cm);
-      signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
-    }
-    return handled;
-  }
-
-  var lastStoppedKey = null;
-  function onKeyDown(e) {
-    var cm = this;
-    ensureFocus(cm);
-    if (signalDOMEvent(cm, e)) return;
-    // IE does strange things with escape.
-    if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
-    var code = e.keyCode;
-    cm.display.shift = code == 16 || e.shiftKey;
-    var handled = handleKeyBinding(cm, e);
-    if (presto) {
-      lastStoppedKey = handled ? code : null;
-      // Opera has no cut event... we try to at least catch the key combo
-      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
-        cm.replaceSelection("", null, "cut");
-    }
-
-    // Turn mouse into crosshair when Alt is held on Mac.
-    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
-      showCrossHair(cm);
-  }
-
-  function showCrossHair(cm) {
-    var lineDiv = cm.display.lineDiv;
-    addClass(lineDiv, "CodeMirror-crosshair");
-
-    function up(e) {
-      if (e.keyCode == 18 || !e.altKey) {
-        rmClass(lineDiv, "CodeMirror-crosshair");
-        off(document, "keyup", up);
-        off(document, "mouseover", up);
-      }
-    }
-    on(document, "keyup", up);
-    on(document, "mouseover", up);
-  }
-
-  function onKeyUp(e) {
-    if (e.keyCode == 16) this.doc.sel.shift = false;
-    signalDOMEvent(this, e);
-  }
-
-  function onKeyPress(e) {
-    var cm = this;
-    if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
-    var keyCode = e.keyCode, charCode = e.charCode;
-    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
-    if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
-    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
-    if (handleCharBinding(cm, e, ch)) return;
-    if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
-    fastPoll(cm);
-  }
-
-  // FOCUS/BLUR EVENTS
-
-  function onFocus(cm) {
-    if (cm.options.readOnly == "nocursor") return;
-    if (!cm.state.focused) {
-      signal(cm, "focus", cm);
-      cm.state.focused = true;
-      addClass(cm.display.wrapper, "CodeMirror-focused");
-      // The prevInput test prevents this from firing when a context
-      // menu is closed (since the resetInput would kill the
-      // select-all detection hack)
-      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
-        resetInput(cm);
-        if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
-      }
-    }
-    slowPoll(cm);
-    restartBlink(cm);
-  }
-  function onBlur(cm) {
-    if (cm.state.focused) {
-      signal(cm, "blur", cm);
-      cm.state.focused = false;
-      rmClass(cm.display.wrapper, "CodeMirror-focused");
-    }
-    clearInterval(cm.display.blinker);
-    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
-  }
-
-  // CONTEXT MENU HANDLING
-
-  // To make the context menu work, we need to briefly unhide the
-  // textarea (making it as unobtrusive as possible) to let the
-  // right-click take effect on it.
-  function onContextMenu(cm, e) {
-    if (signalDOMEvent(cm, e, "contextmenu")) return;
-    var display = cm.display;
-    if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
-
-    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
-    if (!pos || presto) return; // Opera is difficult.
-
-    // Reset the current text selection only if the click is done outside of the selection
-    // and 'resetSelectionOnContextMenu' option is true.
-    var reset = cm.options.resetSelectionOnContextMenu;
-    if (reset && cm.doc.sel.contains(pos) == -1)
-      operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
-
-    var oldCSS = display.input.style.cssText;
-    display.inputDiv.style.position = "absolute";
-    display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
-      "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
-      (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
-      "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
-    if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
-    focusInput(cm);
-    if (webkit) window.scrollTo(null, oldScrollY);
-    resetInput(cm);
-    // Adds "Select all" to context menu in FF
-    if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
-    display.selForContextMenu = cm.doc.sel;
-    clearTimeout(display.detectingSelectAll);
-
-    // Select-all will be greyed out if there's nothing to select, so
-    // this adds a zero-width space so that we can later check whether
-    // it got selected.
-    function prepareSelectAllHack() {
-      if (display.input.selectionStart != null) {
-        var selected = cm.somethingSelected();
-        var extval = display.input.value = "\u200b" + (selected ? display.input.value : "");
-        display.prevInput = selected ? "" : "\u200b";
-        display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
-        // Re-set this, in case some other handler touched the
-        // selection in the meantime.
-        display.selForContextMenu = cm.doc.sel;
-      }
-    }
-    function rehide() {
-      display.inputDiv.style.position = "relative";
-      display.input.style.cssText = oldCSS;
-      if (ie && ie_version < 9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
-      slowPoll(cm);
-
-      // Try to detect the user choosing select-all
-      if (display.input.selectionStart != null) {
-        if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
-        var i = 0, poll = function() {
-          if (display.selForContextMenu == cm.doc.sel && display.input.selectionStart == 0)
-            operation(cm, commands.selectAll)(cm);
-          else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
-          else resetInput(cm);
-        };
-        display.detectingSelectAll = setTimeout(poll, 200);
-      }
-    }
-
-    if (ie && ie_version >= 9) prepareSelectAllHack();
-    if (captureRightClick) {
-      e_stop(e);
-      var mouseup = function() {
-        off(window, "mouseup", mouseup);
-        setTimeout(rehide, 20);
-      };
-      on(window, "mouseup", mouseup);
-    } else {
-      setTimeout(rehide, 50);
-    }
-  }
-
-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, "gutterContextMenu")) return false;
-    return gutterEvent(cm, e, "gutterContextMenu", false, signal);
-  }
-
-  // UPDATING
-
-  // Compute the position of the end of a change (its 'to' property
-  // refers to the pre-change end).
-  var changeEnd = CodeMirror.changeEnd = function(change) {
-    if (!change.text) return change.to;
-    return Pos(change.from.line + change.text.length - 1,
-               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
-  };
-
-  // Adjust a position to refer to the post-change position of the
-  // same text, or the end of the change if the change covers it.
-  function adjustForChange(pos, change) {
-    if (cmp(pos, change.from) < 0) return pos;
-    if (cmp(pos, change.to) <= 0) return changeEnd(change);
-
-    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
-    return Pos(line, ch);
-  }
-
-  function computeSelAfterChange(doc, change) {
-    var out = [];
-    for (var i = 0; i < doc.sel.ranges.length; i++) {
-      var range = doc.sel.ranges[i];
-      out.push(new Range(adjustForChange(range.anchor, change),
-                         adjustForChange(range.head, change)));
-    }
-    return normalizeSelection(out, doc.sel.primIndex);
-  }
-
-  function offsetPos(pos, old, nw) {
-    if (pos.line == old.line)
-      return Pos(nw.line, pos.ch - old.ch + nw.ch);
-    else
-      return Pos(nw.line + (pos.line - old.line), pos.ch);
-  }
-
-  // Used by replaceSelections to allow moving the selection to the
-  // start or around the replaced test. Hint may be "start" or "around".
-  function computeReplacedSel(doc, changes, hint) {
-    var out = [];
-    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
-    for (var i = 0; i < changes.length; i++) {
-      var change = changes[i];
-      var from = offsetPos(change.from, oldPrev, newPrev);
-      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
-      oldPrev = change.to;
-      newPrev = to;
-      if (hint == "around") {
-        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
-        out[i] = new Range(inv ? to : from, inv ? from : to);
-      } else {
-        out[i] = new Range(from, from);
-      }
-    }
-    return new Selection(out, doc.sel.primIndex);
-  }
-
-  // Allow "beforeChange" event handlers to influence a change
-  function filterChange(doc, change, update) {
-    var obj = {
-      canceled: false,
-      from: change.from,
-      to: change.to,
-      text: change.text,
-      origin: change.origin,
-      cancel: function() { this.canceled = true; }
-    };
-    if (update) obj.update = function(from, to, text, origin) {
-      if (from) this.from = clipPos(doc, from);
-      if (to) this.to = clipPos(doc, to);
-      if (text) this.text = text;
-      if (origin !== undefined) this.origin = origin;
-    };
-    signal(doc, "beforeChange", doc, obj);
-    if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
-
-    if (obj.canceled) return null;
-    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
-  }
-
-  // Apply a change to a document, and add it to the document's
-  // history, and propagating it to all linked documents.
-  function makeChange(doc, change, ignoreReadOnly) {
-    if (doc.cm) {
-      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
-      if (doc.cm.state.suppressEdits) return;
-    }
-
-    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
-      change = filterChange(doc, change, true);
-      if (!change) return;
-    }
-
-    // Possibly split or suppress the update based on the presence
-    // of read-only spans in its range.
-    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
-    if (split) {
-      for (var i = split.length - 1; i >= 0; --i)
-        makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
-    } else {
-      makeChangeInner(doc, change);
-    }
-  }
-
-  function makeChangeInner(doc, change) {
-    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
-    var selAfter = computeSelAfterChange(doc, change);
-    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
-
-    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
-    var rebased = [];
-
-    linkedDocs(doc, function(doc, sharedHist) {
-      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-        rebaseHist(doc.history, change);
-        rebased.push(doc.history);
-      }
-      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
-    });
-  }
-
-  // Revert a change stored in a document's history.
-  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
-    if (doc.cm && doc.cm.state.suppressEdits) return;
-
-    var hist = doc.history, event, selAfter = doc.sel;
-    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
-
-    // Verify that there is a useable event (so that ctrl-z won't
-    // needlessly clear selection events)
-    for (var i = 0; i < source.length; i++) {
-      event = source[i];
-      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
-        break;
-    }
-    if (i == source.length) return;
-    hist.lastOrigin = hist.lastSelOrigin = null;
-
-    for (;;) {
-      event = source.pop();
-      if (event.ranges) {
-        pushSelectionToHistory(event, dest);
-        if (allowSelectionOnly && !event.equals(doc.sel)) {
-          setSelection(doc, event, {clearRedo: false});
-          return;
-        }
-        selAfter = event;
-      }
-      else break;
-    }
-
-    // Build up a reverse change object to add to the opposite history
-    // stack (redo when undoing, and vice versa).
-    var antiChanges = [];
-    pushSelectionToHistory(selAfter, dest);
-    dest.push({changes: antiChanges, generation: hist.generation});
-    hist.generation = event.generation || ++hist.maxGeneration;
-
-    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
-
-    for (var i = event.changes.length - 1; i >= 0; --i) {
-      var change = event.changes[i];
-      change.origin = type;
-      if (filter && !filterChange(doc, change, false)) {
-        source.length = 0;
-        return;
-      }
-
-      antiChanges.push(historyChangeFromChange(doc, change));
-
-      var after = i ? computeSelAfterChange(doc, change) : lst(source);
-      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
-      if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
-      var rebased = [];
-
-      // Propagate to the linked documents
-      linkedDocs(doc, function(doc, sharedHist) {
-        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
-          rebaseHist(doc.history, change);
-          rebased.push(doc.history);
-        }
-        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
-      });
-    }
-  }
-
-  // Sub-views need their line numbers shifted when text is added
-  // above or below them in the parent document.
-  function shiftDoc(doc, distance) {
-    if (distance == 0) return;
-    doc.first += distance;
-    doc.sel = new Selection(map(doc.sel.ranges, function(range) {
-      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
-                       Pos(range.head.line + distance, range.head.ch));
-    }), doc.sel.primIndex);
-    if (doc.cm) {
-      regChange(doc.cm, doc.first, doc.first - distance, distance);
-      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
-        regLineChange(doc.cm, l, "gutter");
-    }
-  }
-
-  // More lower-level change function, handling only a single document
-  // (not linked ones).
-  function makeChangeSingleDoc(doc, change, selAfter, spans) {
-    if (doc.cm && !doc.cm.curOp)
-      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
-
-    if (change.to.line < doc.first) {
-      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
-      return;
-    }
-    if (change.from.line > doc.lastLine()) return;
-
-    // Clip the change to the size of this doc
-    if (change.from.line < doc.first) {
-      var shift = change.text.length - 1 - (doc.first - change.from.line);
-      shiftDoc(doc, shift);
-      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
-                text: [lst(change.text)], origin: change.origin};
-    }
-    var last = doc.lastLine();
-    if (change.to.line > last) {
-      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
-                text: [change.text[0]], origin: change.origin};
-    }
-
-    change.removed = getBetween(doc, change.from, change.to);
-
-    if (!selAfter) selAfter = computeSelAfterChange(doc, change);
-    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
-    else updateDoc(doc, change, spans);
-    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
-  }
-
-  // Handle the interaction of a change to a document with the editor
-  // that this document is part of.
-  function makeChangeSingleDocInEditor(cm, change, spans) {
-    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
-
-    var recomputeMaxLength = false, checkWidthStart = from.line;
-    if (!cm.options.lineWrapping) {
-      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
-      doc.iter(checkWidthStart, to.line + 1, function(line) {
-        if (line == display.maxLine) {
-          recomputeMaxLength = true;
-          return true;
-        }
-      });
-    }
-
-    if (doc.sel.contains(change.from, change.to) > -1)
-      signalCursorActivity(cm);
-
-    updateDoc(doc, change, spans, estimateHeight(cm));
-
-    if (!cm.options.lineWrapping) {
-      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
-        var len = lineLength(line);
-        if (len > display.maxLineLength) {
-          display.maxLine = line;
-          display.maxLineLength = len;
-          display.maxLineChanged = true;
-          recomputeMaxLength = false;
-        }
-      });
-      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
-    }
-
-    // Adjust frontier, schedule worker
-    doc.frontier = Math.min(doc.frontier, from.line);
-    startWorker(cm, 400);
-
-    var lendiff = change.text.length - (to.line - from.line) - 1;
-    // Remember that these lines changed, for updating the display
-    if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
-      regLineChange(cm, from.line, "text");
-    else
-      regChange(cm, from.line, to.line + 1, lendiff);
-
-    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
-    if (changeHandler || changesHandler) {
-      var obj = {
-        from: from, to: to,
-        text: change.text,
-        removed: change.removed,
-        origin: change.origin
-      };
-      if (changeHandler) signalLater(cm, "change", cm, obj);
-      if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
-    }
-    cm.display.selForContextMenu = null;
-  }
-
-  function replaceRange(doc, code, from, to, origin) {
-    if (!to) to = from;
-    if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
-    if (typeof code == "string") code = splitLines(code);
-    makeChange(doc, {from: from, to: to, text: code, origin: origin});
-  }
-
-  // SCROLLING THINGS INTO VIEW
-
-  // If an editor sits on the top or bottom of the window, partially
-  // scrolled out of view, this ensures that the cursor is visible.
-  function maybeScrollWindow(cm, coords) {
-    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
-    if (coords.top + box.top < 0) doScroll = true;
-    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
-    if (doScroll != null && !phantom) {
-      var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
-                           (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
-                           (coords.bottom - coords.top + scrollerCutOff) + "px; left: " +
-                           coords.left + "px; width: 2px;");
-      cm.display.lineSpace.appendChild(scrollNode);
-      scrollNode.scrollIntoView(doScroll);
-      cm.display.lineSpace.removeChild(scrollNode);
-    }
-  }
-
-  // Scroll a given position into view (immediately), verifying that
-  // it actually became visible (as line heights are accurately
-  // measured, the position of something may 'drift' during drawing).
-  function scrollPosIntoView(cm, pos, end, margin) {
-    if (margin == null) margin = 0;
-    for (var limit = 0; limit < 5; limit++) {
-      var changed = false, coords = cursorCoords(cm, pos);
-      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
-      var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
-                                         Math.min(coords.top, endCoords.top) - margin,
-                                         Math.max(coords.left, endCoords.left),
-                                         Math.max(coords.bottom, endCoords.bottom) + margin);
-      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
-      if (scrollPos.scrollTop != null) {
-        setScrollTop(cm, scrollPos.scrollTop);
-        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
-      }
-      if (scrollPos.scrollLeft != null) {
-        setScrollLeft(cm, scrollPos.scrollLeft);
-        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
-      }
-      if (!changed) return coords;
-    }
-  }
-
-  // Scroll a given set of coordinates into view (immediately).
-  function scrollIntoView(cm, x1, y1, x2, y2) {
-    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
-    if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
-    if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
-  }
-
-  // Calculate a new scroll position needed to scroll the given
-  // rectangle into view. Returns an object with scrollTop and
-  // scrollLeft properties. When these are undefined, the
-  // vertical/horizontal position does not need to be adjusted.
-  function calculateScrollPos(cm, x1, y1, x2, y2) {
-    var display = cm.display, snapMargin = textHeight(cm.display);
-    if (y1 < 0) y1 = 0;
-    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
-    var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
-    if (y2 - y1 > screen) y2 = y1 + screen;
-    var docBottom = cm.doc.height + paddingVert(display);
-    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
-    if (y1 < screentop) {
-      result.scrollTop = atTop ? 0 : y1;
-    } else if (y2 > screentop + screen) {
-      var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
-      if (newTop != screentop) result.scrollTop = newTop;
-    }
-
-    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
-    var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutters.offsetWidth;
-    var tooWide = x2 - x1 > screenw;
-    if (tooWide) x2 = x1 + screenw;
-    if (x1 < 10)
-      result.scrollLeft = 0;
-    else if (x1 < screenleft)
-      result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
-    else if (x2 > screenw + screenleft - 3)
-      result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
-
-    return result;
-  }
-
-  // Store a relative adjustment to the scroll position in the current
-  // operation (to be applied when the operation finishes).
-  function addToScrollPos(cm, left, top) {
-    if (left != null || top != null) resolveScrollToPos(cm);
-    if (left != null)
-      cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
-    if (top != null)
-      cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
-  }
-
-  // Make sure that at the end of the operation the current cursor is
-  // shown.
-  function ensureCursorVisible(cm) {
-    resolveScrollToPos(cm);
-    var cur = cm.getCursor(), from = cur, to = cur;
-    if (!cm.options.lineWrapping) {
-      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
-      to = Pos(cur.line, cur.ch + 1);
-    }
-    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
-  }
-
-  // When an operation has its scrollToPos property set, and another
-  // scroll action is applied before the end of the operation, this
-  // 'simulates' scrolling that position into view in a cheap way, so
-  // that the effect of intermediate scroll commands is not ignored.
-  function resolveScrollToPos(cm) {
-    var range = cm.curOp.scrollToPos;
-    if (range) {
-      cm.curOp.scrollToPos = null;
-      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
-      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
-                                    Math.min(from.top, to.top) - range.margin,
-                                    Math.max(from.right, to.right),
-                                    Math.max(from.bottom, to.bottom) + range.margin);
-      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
-    }
-  }
-
-  // API UTILITIES
-
-  // Indent the given line. The how parameter can be "smart",
-  // "add"/null, "subtract", or "prev". When aggressive is false
-  // (typically set to true for forced single-line indents), empty
-  // lines are not indented, and places where the mode returns Pass
-  // are left alone.
-  function indentLine(cm, n, how, aggressive) {
-    var doc = cm.doc, state;
-    if (how == null) how = "add";
-    if (how == "smart") {
-      // Fall back to "prev" when the mode doesn't have an indentation
-      // method.
-      if (!doc.mode.indent) how = "prev";
-      else state = getStateBefore(cm, n);
-    }
-
-    var tabSize = cm.options.tabSize;
-    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
-    if (line.stateAfter) line.stateAfter = null;
-    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
-    if (!aggressive && !/\S/.test(line.text)) {
-      indentation = 0;
-      how = "not";
-    } else if (how == "smart") {
-      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
-      if (indentation == Pass || indentation > 150) {
-        if (!aggressive) return;
-        how = "prev";
-      }
-    }
-    if (how == "prev") {
-      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
-      else indentation = 0;
-    } else if (how == "add") {
-      indentation = curSpace + cm.options.indentUnit;
-    } else if (how == "subtract") {
-      indentation = curSpace - cm.options.indentUnit;
-    } else if (typeof how == "number") {
-      indentation = curSpace + how;
-    }
-    indentation = Math.max(0, indentation);
-
-    var indentString = "", pos = 0;
-    if (cm.options.indentWithTabs)
-      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
-    if (pos < indentation) indentString += spaceStr(indentation - pos);
-
-    if (indentString != curSpaceString) {
-      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
-    } else {
-      // Ensure that, if the cursor was in the whitespace at the start
-      // of the line, it is moved to the end of that space.
-      for (var i = 0; i < doc.sel.ranges.length; i++) {
-        var range = doc.sel.ranges[i];
-        if (range.head.line == n && range.head.ch < curSpaceString.length) {
-          var pos = Pos(n, curSpaceString.length);
-          replaceOneSelection(doc, i, new Range(pos, pos));
-          break;
-        }
-      }
-    }
-    line.stateAfter = null;
-  }
-
-  // Utility for applying a change to a line by handle or number,
-  // returning the number and optionally registering the line as
-  // changed.
-  function changeLine(doc, handle, changeType, op) {
-    var no = handle, line = handle;
-    if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
-    else no = lineNo(handle);
-    if (no == null) return null;
-    if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
-    return line;
-  }
-
-  // Helper for deleting text near the selection(s), used to implement
-  // backspace, delete, and similar functionality.
-  function deleteNearSelection(cm, compute) {
-    var ranges = cm.doc.sel.ranges, kill = [];
-    // Build up a set of ranges to kill first, merging overlapping
-    // ranges.
-    for (var i = 0; i < ranges.length; i++) {
-      var toKill = compute(ranges[i]);
-      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
-        var replaced = kill.pop();
-        if (cmp(replaced.from, toKill.from) < 0) {
-          toKill.from = replaced.from;
-          break;
-        }
-      }
-      kill.push(toKill);
-    }
-    // Next, remove those actual ranges.
-    runInOp(cm, function() {
-      for (var i = kill.length - 1; i >= 0; i--)
-        replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
-      ensureCursorVisible(cm);
-    });
-  }
-
-  // Used for horizontal relative motion. Dir is -1 or 1 (left or
-  // right), unit can be "char", "column" (like char, but doesn't
-  // cross line boundaries), "word" (across next word), or "group" (to
-  // the start of next group of word or non-word-non-whitespace
-  // chars). The visually param controls whether, in right-to-left
-  // text, direction 1 means to move towards the next index in the
-  // string, or towards the character to the right of the current
-  // position. The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosH(doc, pos, dir, unit, visually) {
-    var line = pos.line, ch = pos.ch, origDir = dir;
-    var lineObj = getLine(doc, line);
-    var possible = true;
-    function findNextLine() {
-      var l = line + dir;
-      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
-      line = l;
-      return lineObj = getLine(doc, l);
-    }
-    function moveOnce(boundToLine) {
-      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
-      if (next == null) {
-        if (!boundToLine && findNextLine()) {
-          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
-          else ch = dir < 0 ? lineObj.text.length : 0;
-        } else return (possible = false);
-      } else ch = next;
-      return true;
-    }
-
-    if (unit == "char") moveOnce();
-    else if (unit == "column") moveOnce(true);
-    else if (unit == "word" || unit == "group") {
-      var sawType = null, group = unit == "group";
-      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
-      for (var first = true;; first = false) {
-        if (dir < 0 && !moveOnce(!first)) break;
-        var cur = lineObj.text.charAt(ch) || "\n";
-        var type = isWordChar(cur, helper) ? "w"
-          : group && cur == "\n" ? "n"
-          : !group || /\s/.test(cur) ? null
-          : "p";
-        if (group && !first && !type) type = "s";
-        if (sawType && sawType != type) {
-          if (dir < 0) {dir = 1; moveOnce();}
-          break;
-        }
-
-        if (type) sawType = type;
-        if (dir > 0 && !moveOnce(!first)) break;
-      }
-    }
-    var result = skipAtomic(doc, Pos(line, ch), origDir, true);
-    if (!possible) result.hitSide = true;
-    return result;
-  }
-
-  // For relative vertical movement. Dir may be -1 or 1. Unit can be
-  // "page" or "line". The resulting position will have a hitSide=true
-  // property if it reached the end of the document.
-  function findPosV(cm, pos, dir, unit) {
-    var doc = cm.doc, x = pos.left, y;
-    if (unit == "page") {
-      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
-      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
-    } else if (unit == "line") {
-      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
-    }
-    for (;;) {
-      var target = coordsChar(cm, x, y);
-      if (!target.outside) break;
-      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
-      y += dir * 5;
-    }
-    return target;
-  }
-
-  // EDITOR METHODS
-
-  // The publicly visible API. Note that methodOp(f) means
-  // 'wrap f in an operation, performed on its `this` parameter'.
-
-  // This is not the complete set of editor methods. Most of the
-  // methods defined on the Doc type are also injected into
-  // CodeMirror.prototype, for backwards compatibility and
-  // convenience.
-
-  CodeMirror.prototype = {
-    constructor: CodeMirror,
-    focus: function(){window.focus(); focusInput(this); fastPoll(this);},
-
-    setOption: function(option, value) {
-      var options = this.options, old = options[option];
-      if (options[option] == value && option != "mode") return;
-      options[option] = value;
-      if (optionHandlers.hasOwnProperty(option))
-        operation(this, optionHandlers[option])(this, value, old);
-    },
-
-    getOption: function(option) {return this.options[option];},
-    getDoc: function() {return this.doc;},
-
-    addKeyMap: function(map, bottom) {
-      this.state.keyMaps[bottom ? "push" : "unshift"](map);
-    },
-    removeKeyMap: function(map) {
-      var maps = this.state.keyMaps;
-      for (var i = 0; i < maps.length; ++i)
-        if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
-          maps.splice(i, 1);
-          return true;
-        }
-    },
-
-    addOverlay: methodOp(function(spec, options) {
-      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
-      if (mode.startState) throw new Error("Overlays may not be stateful.");
-      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
-      this.state.modeGen++;
-      regChange(this);
-    }),
-    removeOverlay: methodOp(function(spec) {
-      var overlays = this.state.overlays;
-      for (var i = 0; i < overlays.length; ++i) {
-        var cur = overlays[i].modeSpec;
-        if (cur == spec || typeof spec == "string" && cur.name == spec) {
-          overlays.splice(i, 1);
-          this.state.modeGen++;
-          regChange(this);
-          return;
-        }
-      }
-    }),
-
-    indentLine: methodOp(function(n, dir, aggressive) {
-      if (typeof dir != "string" && typeof dir != "number") {
-        if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
-        else dir = dir ? "add" : "subtract";
-      }
-      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
-    }),
-    indentSelection: methodOp(function(how) {
-      var ranges = this.doc.sel.ranges, end = -1;
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        if (!range.empty()) {
-          var from = range.from(), to = range.to();
-          var start = Math.max(end, from.line);
-          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
-          for (var j = start; j < end; ++j)
-            indentLine(this, j, how);
-          var newRanges = this.doc.sel.ranges;
-          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
-            replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
-        } else if (range.head.line > end) {
-          indentLine(this, range.head.line, how, true);
-          end = range.head.line;
-          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
-        }
-      }
-    }),
-
-    // Fetch the parser token for a given character. Useful for hacks
-    // that want to inspect the mode state (say, for completion).
-    getTokenAt: function(pos, precise) {
-      var doc = this.doc;
-      pos = clipPos(doc, pos);
-      var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
-      var line = getLine(doc, pos.line);
-      var stream = new StringStream(line.text, this.options.tabSize);
-      while (stream.pos < pos.ch && !stream.eol()) {
-        stream.start = stream.pos;
-        var style = readToken(mode, stream, state);
-      }
-      return {start: stream.start,
-              end: stream.pos,
-              string: stream.current(),
-              type: style || null,
-              state: state};
-    },
-
-    getTokenTypeAt: function(pos) {
-      pos = clipPos(this.doc, pos);
-      var styles = getLineStyles(this, getLine(this.doc, pos.line));
-      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
-      var type;
-      if (ch == 0) type = styles[2];
-      else for (;;) {
-        var mid = (before + after) >> 1;
-        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
-        else if (styles[mid * 2 + 1] < ch) before = mid + 1;
-        else { type = styles[mid * 2 + 2]; break; }
-      }
-      var cut = type ? type.indexOf("cm-overlay ") : -1;
-      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
-    },
-
-    getModeAt: function(pos) {
-      var mode = this.doc.mode;
-      if (!mode.innerMode) return mode;
-      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
-    },
-
-    getHelper: function(pos, type) {
-      return this.getHelpers(pos, type)[0];
-    },
-
-    getHelpers: function(pos, type) {
-      var found = [];
-      if (!helpers.hasOwnProperty(type)) return helpers;
-      var help = helpers[type], mode = this.getModeAt(pos);
-      if (typeof mode[type] == "string") {
-        if (help[mode[type]]) found.push(help[mode[type]]);
-      } else if (mode[type]) {
-        for (var i = 0; i < mode[type].length; i++) {
-          var val = help[mode[type][i]];
-          if (val) found.push(val);
-        }
-      } else if (mode.helperType && help[mode.helperType]) {
-        found.push(help[mode.helperType]);
-      } else if (help[mode.name]) {
-        found.push(help[mode.name]);
-      }
-      for (var i = 0; i < help._global.length; i++) {
-        var cur = help._global[i];
-        if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
-          found.push(cur.val);
-      }
-      return found;
-    },
-
-    getStateAfter: function(line, precise) {
-      var doc = this.doc;
-      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
-      return getStateBefore(this, line + 1, precise);
-    },
-
-    cursorCoords: function(start, mode) {
-      var pos, range = this.doc.sel.primary();
-      if (start == null) pos = range.head;
-      else if (typeof start == "object") pos = clipPos(this.doc, start);
-      else pos = start ? range.from() : range.to();
-      return cursorCoords(this, pos, mode || "page");
-    },
-
-    charCoords: function(pos, mode) {
-      return charCoords(this, clipPos(this.doc, pos), mode || "page");
-    },
-
-    coordsChar: function(coords, mode) {
-      coords = fromCoordSystem(this, coords, mode || "page");
-      return coordsChar(this, coords.left, coords.top);
-    },
-
-    lineAtHeight: function(height, mode) {
-      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
-      return lineAtHeight(this.doc, height + this.display.viewOffset);
-    },
-    heightAtLine: function(line, mode) {
-      var end = false, last = this.doc.first + this.doc.size - 1;
-      if (line < this.doc.first) line = this.doc.first;
-      else if (line > last) { line = last; end = true; }
-      var lineObj = getLine(this.doc, line);
-      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
-        (end ? this.doc.height - heightAtLine(lineObj) : 0);
-    },
-
-    defaultTextHeight: function() { return textHeight(this.display); },
-    defaultCharWidth: function() { return charWidth(this.display); },
-
-    setGutterMarker: methodOp(function(line, gutterID, value) {
-      return changeLine(this.doc, line, "gutter", function(line) {
-        var markers = line.gutterMarkers || (line.gutterMarkers = {});
-        markers[gutterID] = value;
-        if (!value && isEmpty(markers)) line.gutterMarkers = null;
-        return true;
-      });
-    }),
-
-    clearGutter: methodOp(function(gutterID) {
-      var cm = this, doc = cm.doc, i = doc.first;
-      doc.iter(function(line) {
-        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
-          line.gutterMarkers[gutterID] = null;
-          regLineChange(cm, i, "gutter");
-          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
-        }
-        ++i;
-      });
-    }),
-
-    addLineWidget: methodOp(function(handle, node, options) {
-      return addLineWidget(this, handle, node, options);
-    }),
-
-    removeLineWidget: function(widget) { widget.clear(); },
-
-    lineInfo: function(line) {
-      if (typeof line == "number") {
-        if (!isLine(this.doc, line)) return null;
-        var n = line;
-        line = getLine(this.doc, line);
-        if (!line) return null;
-      } else {
-        var n = lineNo(line);
-        if (n == null) return null;
-      }
-      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
-              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
-              widgets: line.widgets};
-    },
-
-    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
-
-    addWidget: function(pos, node, scroll, vert, horiz) {
-      var display = this.display;
-      pos = cursorCoords(this, clipPos(this.doc, pos));
-      var top = pos.bottom, left = pos.left;
-      node.style.position = "absolute";
-      display.sizer.appendChild(node);
-      if (vert == "over") {
-        top = pos.top;
-      } else if (vert == "above" || vert == "near") {
-        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
-        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
-        // Default to positioning above (if specified and possible); otherwise default to positioning below
-        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
-          top = pos.top - node.offsetHeight;
-        else if (pos.bottom + node.offsetHeight <= vspace)
-          top = pos.bottom;
-        if (left + node.offsetWidth > hspace)
-          left = hspace - node.offsetWidth;
-      }
-      node.style.top = top + "px";
-      node.style.left = node.style.right = "";
-      if (horiz == "right") {
-        left = display.sizer.clientWidth - node.offsetWidth;
-        node.style.right = "0px";
-      } else {
-        if (horiz == "left") left = 0;
-        else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
-        node.style.left = left + "px";
-      }
-      if (scroll)
-        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
-    },
-
-    triggerOnKeyDown: methodOp(onKeyDown),
-    triggerOnKeyPress: methodOp(onKeyPress),
-    triggerOnKeyUp: onKeyUp,
-
-    execCommand: function(cmd) {
-      if (commands.hasOwnProperty(cmd))
-        return commands[cmd](this);
-    },
-
-    findPosH: function(from, amount, unit, visually) {
-      var dir = 1;
-      if (amount < 0) { dir = -1; amount = -amount; }
-      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
-        cur = findPosH(this.doc, cur, dir, unit, visually);
-        if (cur.hitSide) break;
-      }
-      return cur;
-    },
-
-    moveH: methodOp(function(dir, unit) {
-      var cm = this;
-      cm.extendSelectionsBy(function(range) {
-        if (cm.display.shift || cm.doc.extend || range.empty())
-          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
-        else
-          return dir < 0 ? range.from() : range.to();
-      }, sel_move);
-    }),
-
-    deleteH: methodOp(function(dir, unit) {
-      var sel = this.doc.sel, doc = this.doc;
-      if (sel.somethingSelected())
-        doc.replaceSelection("", null, "+delete");
-      else
-        deleteNearSelection(this, function(range) {
-          var other = findPosH(doc, range.head, dir, unit, false);
-          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
-        });
-    }),
-
-    findPosV: function(from, amount, unit, goalColumn) {
-      var dir = 1, x = goalColumn;
-      if (amount < 0) { dir = -1; amount = -amount; }
-      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
-        var coords = cursorCoords(this, cur, "div");
-        if (x == null) x = coords.left;
-        else coords.left = x;
-        cur = findPosV(this, coords, dir, unit);
-        if (cur.hitSide) break;
-      }
-      return cur;
-    },
-
-    moveV: methodOp(function(dir, unit) {
-      var cm = this, doc = this.doc, goals = [];
-      var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
-      doc.extendSelectionsBy(function(range) {
-        if (collapse)
-          return dir < 0 ? range.from() : range.to();
-        var headPos = cursorCoords(cm, range.head, "div");
-        if (range.goalColumn != null) headPos.left = range.goalColumn;
-        goals.push(headPos.left);
-        var pos = findPosV(cm, headPos, dir, unit);
-        if (unit == "page" && range == doc.sel.primary())
-          addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
-        return pos;
-      }, sel_move);
-      if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
-        doc.sel.ranges[i].goalColumn = goals[i];
-    }),
-
-    // Find the word at the given position (as returned by coordsChar).
-    findWordAt: function(pos) {
-      var doc = this.doc, line = getLine(doc, pos.line).text;
-      var start = pos.ch, end = pos.ch;
-      if (line) {
-        var helper = this.getHelper(pos, "wordChars");
-        if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
-        var startChar = line.charAt(start);
-        var check = isWordChar(startChar, helper)
-          ? function(ch) { return isWordChar(ch, helper); }
-          : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
-          : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
-        while (start > 0 && check(line.charAt(start - 1))) --start;
-        while (end < line.length && check(line.charAt(end))) ++end;
-      }
-      return new Range(Pos(pos.line, start), Pos(pos.line, end));
-    },
-
-    toggleOverwrite: function(value) {
-      if (value != null && value == this.state.overwrite) return;
-      if (this.state.overwrite = !this.state.overwrite)
-        addClass(this.display.cursorDiv, "CodeMirror-overwrite");
-      else
-        rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
-
-      signal(this, "overwriteToggle", this, this.state.overwrite);
-    },
-    hasFocus: function() { return activeElt() == this.display.input; },
-
-    scrollTo: methodOp(function(x, y) {
-      if (x != null || y != null) resolveScrollToPos(this);
-      if (x != null) this.curOp.scrollLeft = x;
-      if (y != null) this.curOp.scrollTop = y;
-    }),
-    getScrollInfo: function() {
-      var scroller = this.display.scroller, co = scrollerCutOff;
-      return {left: scroller.scrollLeft, top: scroller.scrollTop,
-              height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
-              clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
-    },
-
-    scrollIntoView: methodOp(function(range, margin) {
-      if (range == null) {
-        range = {from: this.doc.sel.primary().head, to: null};
-        if (margin == null) margin = this.options.cursorScrollMargin;
-      } else if (typeof range == "number") {
-        range = {from: Pos(range, 0), to: null};
-      } else if (range.from == null) {
-        range = {from: range, to: null};
-      }
-      if (!range.to) range.to = range.from;
-      range.margin = margin || 0;
-
-      if (range.from.line != null) {
-        resolveScrollToPos(this);
-        this.curOp.scrollToPos = range;
-      } else {
-        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
-                                      Math.min(range.from.top, range.to.top) - range.margin,
-                                      Math.max(range.from.right, range.to.right),
-                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);
-        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
-      }
-    }),
-
-    setSize: methodOp(function(width, height) {
-      var cm = this;
-      function interpret(val) {
-        return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
-      }
-      if (width != null) cm.display.wrapper.style.width = interpret(width);
-      if (height != null) cm.display.wrapper.style.height = interpret(height);
-      if (cm.options.lineWrapping) clearLineMeasurementCache(this);
-      var lineNo = cm.display.viewFrom;
-      cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
-        if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
-          if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
-        ++lineNo;
-      });
-      cm.curOp.forceUpdate = true;
-      signal(cm, "refresh", this);
-    }),
-
-    operation: function(f){return runInOp(this, f);},
-
-    refresh: methodOp(function() {
-      var oldHeight = this.display.cachedTextHeight;
-      regChange(this);
-      this.curOp.forceUpdate = true;
-      clearCaches(this);
-      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
-      updateGutterSpace(this);
-      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
-        estimateLineHeights(this);
-      signal(this, "refresh", this);
-    }),
-
-    swapDoc: methodOp(function(doc) {
-      var old = this.doc;
-      old.cm = null;
-      attachDoc(this, doc);
-      clearCaches(this);
-      resetInput(this);
-      this.scrollTo(doc.scrollLeft, doc.scrollTop);
-      this.curOp.forceScroll = true;
-      signalLater(this, "swapDoc", this, old);
-      return old;
-    }),
-
-    getInputField: function(){return this.display.input;},
-    getWrapperElement: function(){return this.display.wrapper;},
-    getScrollerElement: function(){return this.display.scroller;},
-    getGutterElement: function(){return this.display.gutters;}
-  };
-  eventMixin(CodeMirror);
-
-  // OPTION DEFAULTS
-
-  // The default configuration options.
-  var defaults = CodeMirror.defaults = {};
-  // Functions to run when options are changed.
-  var optionHandlers = CodeMirror.optionHandlers = {};
-
-  function option(name, deflt, handle, notOnInit) {
-    CodeMirror.defaults[name] = deflt;
-    if (handle) optionHandlers[name] =
-      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
-  }
-
-  // Passed to option handlers when there is no old value.
-  var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
-
-  // These two are, on init, called from the constructor because they
-  // have to be initialized before the editor can start at all.
-  option("value", "", function(cm, val) {
-    cm.setValue(val);
-  }, true);
-  option("mode", null, function(cm, val) {
-    cm.doc.modeOption = val;
-    loadMode(cm);
-  }, true);
-
-  option("indentUnit", 2, loadMode, true);
-  option("indentWithTabs", false);
-  option("smartIndent", true);
-  option("tabSize", 4, function(cm) {
-    resetModeState(cm);
-    clearCaches(cm);
-    regChange(cm);
-  }, true);
-  option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
-    cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
-    cm.refresh();
-  }, true);
-  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
-  option("electricChars", true);
-  option("rtlMoveVisually", !windows);
-  option("wholeLineUpdateBefore", true);
-
-  option("theme", "default", function(cm) {
-    themeChanged(cm);
-    guttersChanged(cm);
-  }, true);
-  option("keyMap", "default", keyMapChanged);
-  option("extraKeys", null);
-
-  option("lineWrapping", false, wrappingChanged, true);
-  option("gutters", [], function(cm) {
-    setGuttersForLineNumbers(cm.options);
-    guttersChanged(cm);
-  }, true);
-  option("fixedGutter", true, function(cm, val) {
-    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
-    cm.refresh();
-  }, true);
-  option("coverGutterNextToScrollbar", false, updateScrollbars, true);
-  option("lineNumbers", false, function(cm) {
-    setGuttersForLineNumbers(cm.options);
-    guttersChanged(cm);
-  }, true);
-  option("firstLineNumber", 1, guttersChanged, true);
-  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
-  option("showCursorWhenSelecting", false, updateSelection, true);
-
-  option("resetSelectionOnContextMenu", true);
-
-  option("readOnly", false, function(cm, val) {
-    if (val == "nocursor") {
-      onBlur(cm);
-      cm.display.input.blur();
-      cm.display.disabled = true;
-    } else {
-      cm.display.disabled = false;
-      if (!val) resetInput(cm);
-    }
-  });
-  option("disableInput", false, function(cm, val) {if (!val) resetInput(cm);}, true);
-  option("dragDrop", true);
-
-  option("cursorBlinkRate", 530);
-  option("cursorScrollMargin", 0);
-  option("cursorHeight", 1, updateSelection, true);
-  option("singleCursorHeightPerLine", true, updateSelection, true);
-  option("workTime", 100);
-  option("workDelay", 100);
-  option("flattenSpans", true, resetModeState, true);
-  option("addModeClass", false, resetModeState, true);
-  option("pollInterval", 100);
-  option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
-  option("historyEventDelay", 1250);
-  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
-  option("maxHighlightLength", 10000, resetModeState, true);
-  option("moveInputWithCursor", true, function(cm, val) {
-    if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
-  });
-
-  option("tabindex", null, function(cm, val) {
-    cm.display.input.tabIndex = val || "";
-  });
-  option("autofocus", null);
-
-  // MODE DEFINITION AND QUERYING
-
-  // Known modes, by name and by MIME
-  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
-
-  // Extra arguments are stored as the mode's dependencies, which is
-  // used by (legacy) mechanisms like loadmode.js to automatically
-  // load a mode. (Preferred mechanism is the require/define calls.)
-  CodeMirror.defineMode = function(name, mode) {
-    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
-    if (arguments.length > 2)
-      mode.dependencies = Array.prototype.slice.call(arguments, 2);
-    modes[name] = mode;
-  };
-
-  CodeMirror.defineMIME = function(mime, spec) {
-    mimeModes[mime] = spec;
-  };
-
-  // Given a MIME type, a {name, ...options} config object, or a name
-  // string, return a mode config object.
-  CodeMirror.resolveMode = function(spec) {
-    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
-      spec = mimeModes[spec];
-    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
-      var found = mimeModes[spec.name];
-      if (typeof found == "string") found = {name: found};
-      spec = createObj(found, spec);
-      spec.name = found.name;
-    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
-      return CodeMirror.resolveMode("application/xml");
-    }
-    if (typeof spec == "string") return {name: spec};
-    else return spec || {name: "null"};
-  };
-
-  // Given a mode spec (anything that resolveMode accepts), find and
-  // initialize an actual mode object.
-  CodeMirror.getMode = function(options, spec) {
-    var spec = CodeMirror.resolveMode(spec);
-    var mfactory = modes[spec.name];
-    if (!mfactory) return CodeMirror.getMode(options, "text/plain");
-    var modeObj = mfactory(options, spec);
-    if (modeExtensions.hasOwnProperty(spec.name)) {
-      var exts = modeExtensions[spec.name];
-      for (var prop in exts) {
-        if (!exts.hasOwnProperty(prop)) continue;
-        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
-        modeObj[prop] = exts[prop];
-      }
-    }
-    modeObj.name = spec.name;
-    if (spec.helperType) modeObj.helperType = spec.helperType;
-    if (spec.modeProps) for (var prop in spec.modeProps)
-      modeObj[prop] = spec.modeProps[prop];
-
-    return modeObj;
-  };
-
-  // Minimal default mode.
-  CodeMirror.defineMode("null", function() {
-    return {token: function(stream) {stream.skipToEnd();}};
-  });
-  CodeMirror.defineMIME("text/plain", "null");
-
-  // This can be used to attach properties to mode objects from
-  // outside the actual mode definition.
-  var modeExtensions = CodeMirror.modeExtensions = {};
-  CodeMirror.extendMode = function(mode, properties) {
-    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
-    copyObj(properties, exts);
-  };
-
-  // EXTENSIONS
-
-  CodeMirror.defineExtension = function(name, func) {
-    CodeMirror.prototype[name] = func;
-  };
-  CodeMirror.defineDocExtension = function(name, func) {
-    Doc.prototype[name] = func;
-  };
-  CodeMirror.defineOption = option;
-
-  var initHooks = [];
-  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
-
-  var helpers = CodeMirror.helpers = {};
-  CodeMirror.registerHelper = function(type, name, value) {
-    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
-    helpers[type][name] = value;
-  };
-  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
-    CodeMirror.registerHelper(type, name, value);
-    helpers[type]._global.push({pred: predicate, val: value});
-  };
-
-  // MODE STATE HANDLING
-
-  // Utility functions for working with state. Exported because nested
-  // modes need to do this for their inner modes.
-
-  var copyState = CodeMirror.copyState = function(mode, state) {
-    if (state === true) return state;
-    if (mode.copyState) return mode.copyState(state);
-    var nstate = {};
-    for (var n in state) {
-      var val = state[n];
-      if (val instanceof Array) val = val.concat([]);
-      nstate[n] = val;
-    }
-    return nstate;
-  };
-
-  var startState = CodeMirror.startState = function(mode, a1, a2) {
-    return mode.startState ? mode.startState(a1, a2) : true;
-  };
-
-  // Given a mode and a state (for that mode), find the inner mode and
-  // state at the position that the state refers to.
-  CodeMirror.innerMode = function(mode, state) {
-    while (mode.innerMode) {
-      var info = mode.innerMode(state);
-      if (!info || info.mode == mode) break;
-      state = info.state;
-      mode = info.mode;
-    }
-    return info || {mode: mode, state: state};
-  };
-
-  // STANDARD COMMANDS
-
-  // Commands are parameter-less actions that can be performed on an
-  // editor, mostly used for keybindings.
-  var commands = CodeMirror.commands = {
-    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
-    singleSelection: function(cm) {
-      cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
-    },
-    killLine: function(cm) {
-      deleteNearSelection(cm, function(range) {
-        if (range.empty()) {
-          var len = getLine(cm.doc, range.head.line).text.length;
-          if (range.head.ch == len && range.head.line < cm.lastLine())
-            return {from: range.head, to: Pos(range.head.line + 1, 0)};
-          else
-            return {from: range.head, to: Pos(range.head.line, len)};
-        } else {
-          return {from: range.from(), to: range.to()};
-        }
-      });
-    },
-    deleteLine: function(cm) {
-      deleteNearSelection(cm, function(range) {
-        return {from: Pos(range.from().line, 0),
-                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
-      });
-    },
-    delLineLeft: function(cm) {
-      deleteNearSelection(cm, function(range) {
-        return {from: Pos(range.from().line, 0), to: range.from()};
-      });
-    },
-    delWrappedLineLeft: function(cm) {
-      deleteNearSelection(cm, function(range) {
-        var top = cm.charCoords(range.head, "div").top + 5;
-        var leftPos = cm.coordsChar({left: 0, top: top}, "div");
-        return {from: leftPos, to: range.from()};
-      });
-    },
-    delWrappedLineRight: function(cm) {
-      deleteNearSelection(cm, function(range) {
-        var top = cm.charCoords(range.head, "div").top + 5;
-        var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
-        return {from: range.from(), to: rightPos };
-      });
-    },
-    undo: function(cm) {cm.undo();},
-    redo: function(cm) {cm.redo();},
-    undoSelection: function(cm) {cm.undoSelection();},
-    redoSelection: function(cm) {cm.redoSelection();},
-    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
-    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
-    goLineStart: function(cm) {
-      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
-                            {origin: "+move", bias: 1});
-    },
-    goLineStartSmart: function(cm) {
-      cm.extendSelectionsBy(function(range) {
-        return lineStartSmart(cm, range.head);
-      }, {origin: "+move", bias: 1});
-    },
-    goLineEnd: function(cm) {
-      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
-                            {origin: "+move", bias: -1});
-    },
-    goLineRight: function(cm) {
-      cm.extendSelectionsBy(function(range) {
-        var top = cm.charCoords(range.head, "div").top + 5;
-        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
-      }, sel_move);
-    },
-    goLineLeft: function(cm) {
-      cm.extendSelectionsBy(function(range) {
-        var top = cm.charCoords(range.head, "div").top + 5;
-        return cm.coordsChar({left: 0, top: top}, "div");
-      }, sel_move);
-    },
-    goLineLeftSmart: function(cm) {
-      cm.extendSelectionsBy(function(range) {
-        var top = cm.charCoords(range.head, "div").top + 5;
-        var pos = cm.coordsChar({left: 0, top: top}, "div");
-        if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
-        return pos;
-      }, sel_move);
-    },
-    goLineUp: function(cm) {cm.moveV(-1, "line");},
-    goLineDown: function(cm) {cm.moveV(1, "line");},
-    goPageUp: function(cm) {cm.moveV(-1, "page");},
-    goPageDown: function(cm) {cm.moveV(1, "page");},
-    goCharLeft: function(cm) {cm.moveH(-1, "char");},
-    goCharRight: function(cm) {cm.moveH(1, "char");},
-    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
-    goColumnRight: function(cm) {cm.moveH(1, "column");},
-    goWordLeft: function(cm) {cm.moveH(-1, "word");},
-    goGroupRight: function(cm) {cm.moveH(1, "group");},
-    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
-    goWordRight: function(cm) {cm.moveH(1, "word");},
-    delCharBefore: function(cm) {cm.deleteH(-1, "char");},
-    delCharAfter: function(cm) {cm.deleteH(1, "char");},
-    delWordBefore: function(cm) {cm.deleteH(-1, "word");},
-    delWordAfter: function(cm) {cm.deleteH(1, "word");},
-    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
-    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
-    indentAuto: function(cm) {cm.indentSelection("smart");},
-    indentMore: function(cm) {cm.indentSelection("add");},
-    indentLess: function(cm) {cm.indentSelection("subtract");},
-    insertTab: function(cm) {cm.replaceSelection("\t");},
-    insertSoftTab: function(cm) {
-      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
-      for (var i = 0; i < ranges.length; i++) {
-        var pos = ranges[i].from();
-        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
-        spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
-      }
-      cm.replaceSelections(spaces);
-    },
-    defaultTab: function(cm) {
-      if (cm.somethingSelected()) cm.indentSelection("add");
-      else cm.execCommand("insertTab");
-    },
-    transposeChars: function(cm) {
-      runInOp(cm, function() {
-        var ranges = cm.listSelections(), newSel = [];
-        for (var i = 0; i < ranges.length; i++) {
-          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
-          if (line) {
-            if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
-            if (cur.ch > 0) {
-              cur = new Pos(cur.line, cur.ch + 1);
-              cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
-                              Pos(cur.line, cur.ch - 2), cur, "+transpose");
-            } else if (cur.line > cm.doc.first) {
-              var prev = getLine(cm.doc, cur.line - 1).text;
-              if (prev)
-                cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
-                                Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
-            }
-          }
-          newSel.push(new Range(cur, cur));
-        }
-        cm.setSelections(newSel);
-      });
-    },
-    newlineAndIndent: function(cm) {
-      runInOp(cm, function() {
-        var len = cm.listSelections().length;
-        for (var i = 0; i < len; i++) {
-          var range = cm.listSelections()[i];
-          cm.replaceRange("\n", range.anchor, range.head, "+input");
-          cm.indentLine(range.from().line + 1, null, true);
-          ensureCursorVisible(cm);
-        }
-      });
-    },
-    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
-  };
-
-  // STANDARD KEYMAPS
-
-  var keyMap = CodeMirror.keyMap = {};
-  keyMap.basic = {
-    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
-    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
-    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
-    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
-    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
-    "Esc": "singleSelection"
-  };
-  // Note that the save and find-related commands aren't defined by
-  // default. User code or addons can define them. Unknown commands
-  // are simply ignored.
-  keyMap.pcDefault = {
-    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
-    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
-    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
-    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
-    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
-    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
-    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
-    fallthrough: "basic"
-  };
-  keyMap.macDefault = {
-    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
-    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
-    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
-    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
-    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
-    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
-    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
-    fallthrough: ["basic", "emacsy"]
-  };
-  // Very basic readline/emacs-style bindings, which are standard on Mac.
-  keyMap.emacsy = {
-    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
-    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
-    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
-    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
-  };
-  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
-
-  // KEYMAP DISPATCH
-
-  function getKeyMap(val) {
-    if (typeof val == "string") return keyMap[val];
-    else return val;
-  }
-
-  // Given an array of keymaps and a key name, call handle on any
-  // bindings found, until that returns a truthy value, at which point
-  // we consider the key handled. Implements things like binding a key
-  // to false stopping further handling and keymap fallthrough.
-  var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
-    function lookup(map) {
-      map = getKeyMap(map);
-      var found = map[name];
-      if (found === false) return "stop";
-      if (found != null && handle(found)) return true;
-      if (map.nofallthrough) return "stop";
-
-      var fallthrough = map.fallthrough;
-      if (fallthrough == null) return false;
-      if (Object.prototype.toString.call(fallthrough) != "[object Array]")
-        return lookup(fallthrough);
-      for (var i = 0; i < fallthrough.length; ++i) {
-        var done = lookup(fallthrough[i]);
-        if (done) return done;
-      }
-      return false;
-    }
-
-    for (var i = 0; i < maps.length; ++i) {
-      var done = lookup(maps[i]);
-      if (done) return done != "stop";
-    }
-  };
-
-  // Modifier key presses don't count as 'real' key presses for the
-  // purpose of keymap fallthrough.
-  var isModifierKey = CodeMirror.isModifierKey = function(event) {
-    var name = keyNames[event.keyCode];
-    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
-  };
-
-  // Look up the name of a key as indicated by an event object.
-  var keyName = CodeMirror.keyName = function(event, noShift) {
-    if (presto && event.keyCode == 34 && event["char"]) return false;
-    var name = keyNames[event.keyCode];
-    if (name == null || event.altGraphKey) return false;
-    if (event.altKey) name = "Alt-" + name;
-    if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
-    if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
-    if (!noShift && event.shiftKey) name = "Shift-" + name;
-    return name;
-  };
-
-  // FROMTEXTAREA
-
-  CodeMirror.fromTextArea = function(textarea, options) {
-    if (!options) options = {};
-    options.value = textarea.value;
-    if (!options.tabindex && textarea.tabindex)
-      options.tabindex = textarea.tabindex;
-    if (!options.placeholder && textarea.placeholder)
-      options.placeholder = textarea.placeholder;
-    // Set autofocus to true if this textarea is focused, or if it has
-    // autofocus and no other element is focused.
-    if (options.autofocus == null) {
-      var hasFocus = activeElt();
-      options.autofocus = hasFocus == textarea ||
-        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
-    }
-
-    function save() {textarea.value = cm.getValue();}
-    if (textarea.form) {
-      on(textarea.form, "submit", save);
-      // Deplorable hack to make the submit method do the right thing.
-      if (!options.leaveSubmitMethodAlone) {
-        var form = textarea.form, realSubmit = form.submit;
-        try {
-          var wrappedSubmit = form.submit = function() {
-            save();
-            form.submit = realSubmit;
-            form.submit();
-            form.submit = wrappedSubmit;
-          };
-        } catch(e) {}
-      }
-    }
-
-    textarea.style.display = "none";
-    var cm = CodeMirror(function(node) {
-      textarea.parentNode.insertBefore(node, textarea.nextSibling);
-    }, options);
-    cm.save = save;
-    cm.getTextArea = function() { return textarea; };
-    cm.toTextArea = function() {
-      cm.toTextArea = isNaN; // Prevent this from being ran twice
-      save();
-      textarea.parentNode.removeChild(cm.getWrapperElement());
-      textarea.style.display = "";
-      if (textarea.form) {
-        off(textarea.form, "submit", save);
-        if (typeof textarea.form.submit == "function")
-          textarea.form.submit = realSubmit;
-      }
-    };
-    return cm;
-  };
-
-  // STRING STREAM
-
-  // Fed to the mode parsers, provides helper functions to make
-  // parsers more succinct.
-
-  var StringStream = CodeMirror.StringStream = function(string, tabSize) {
-    this.pos = this.start = 0;
-    this.string = string;
-    this.tabSize = tabSize || 8;
-    this.lastColumnPos = this.lastColumnValue = 0;
-    this.lineStart = 0;
-  };
-
-  StringStream.prototype = {
-    eol: function() {return this.pos >= this.string.length;},
-    sol: function() {return this.pos == this.lineStart;},
-    peek: function() {return this.string.charAt(this.pos) || undefined;},
-    next: function() {
-      if (this.pos < this.string.length)
-        return this.string.charAt(this.pos++);
-    },
-    eat: function(match) {
-      var ch = this.string.charAt(this.pos);
-      if (typeof match == "string") var ok = ch == match;
-      else var ok = ch && (match.test ? match.test(ch) : match(ch));
-      if (ok) {++this.pos; return ch;}
-    },
-    eatWhile: function(match) {
-      var start = this.pos;
-      while (this.eat(match)){}
-      return this.pos > start;
-    },
-    eatSpace: function() {
-      var start = this.pos;
-      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
-      return this.pos > start;
-    },
-    skipToEnd: function() {this.pos = this.string.length;},
-    skipTo: function(ch) {
-      var found = this.string.indexOf(ch, this.pos);
-      if (found > -1) {this.pos = found; return true;}
-    },
-    backUp: function(n) {this.pos -= n;},
-    column: function() {
-      if (this.lastColumnPos < this.start) {
-        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
-        this.lastColumnPos = this.start;
-      }
-      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
-    },
-    indentation: function() {
-      return countColumn(this.string, null, this.tabSize) -
-        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
-    },
-    match: function(pattern, consume, caseInsensitive) {
-      if (typeof pattern == "string") {
-        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
-        var substr = this.string.substr(this.pos, pattern.length);
-        if (cased(substr) == cased(pattern)) {
-          if (consume !== false) this.pos += pattern.length;
-          return true;
-        }
-      } else {
-        var match = this.string.slice(this.pos).match(pattern);
-        if (match && match.index > 0) return null;
-        if (match && consume !== false) this.pos += match[0].length;
-        return match;
-      }
-    },
-    current: function(){return this.string.slice(this.start, this.pos);},
-    hideFirstChars: function(n, inner) {
-      this.lineStart += n;
-      try { return inner(); }
-      finally { this.lineStart -= n; }
-    }
-  };
-
-  // TEXTMARKERS
-
-  // Created with markText and setBookmark methods. A TextMarker is a
-  // handle that can be used to clear or find a marked position in the
-  // document. Line objects hold arrays (markedSpans) containing
-  // {from, to, marker} object pointing to such marker objects, and
-  // indicating that such a marker is present on that line. Multiple
-  // lines may point to the same marker when it spans across lines.
-  // The spans will have null for their from/to properties when the
-  // marker continues beyond the start/end of the line. Markers have
-  // links back to the lines they currently touch.
-
-  var TextMarker = CodeMirror.TextMarker = function(doc, type) {
-    this.lines = [];
-    this.type = type;
-    this.doc = doc;
-  };
-  eventMixin(TextMarker);
-
-  // Clear the marker.
-  TextMarker.prototype.clear = function() {
-    if (this.explicitlyCleared) return;
-    var cm = this.doc.cm, withOp = cm && !cm.curOp;
-    if (withOp) startOperation(cm);
-    if (hasHandler(this, "clear")) {
-      var found = this.find();
-      if (found) signalLater(this, "clear", found.from, found.to);
-    }
-    var min = null, max = null;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
-      else if (cm) {
-        if (span.to != null) max = lineNo(line);
-        if (span.from != null) min = lineNo(line);
-      }
-      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
-      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
-        updateLineHeight(line, textHeight(cm.display));
-    }
-    if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
-      var visual = visualLine(this.lines[i]), len = lineLength(visual);
-      if (len > cm.display.maxLineLength) {
-        cm.display.maxLine = visual;
-        cm.display.maxLineLength = len;
-        cm.display.maxLineChanged = true;
-      }
-    }
-
-    if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
-    this.lines.length = 0;
-    this.explicitlyCleared = true;
-    if (this.atomic && this.doc.cantEdit) {
-      this.doc.cantEdit = false;
-      if (cm) reCheckSelection(cm.doc);
-    }
-    if (cm) signalLater(cm, "markerCleared", cm, this);
-    if (withOp) endOperation(cm);
-    if (this.parent) this.parent.clear();
-  };
-
-  // Find the position of the marker in the document. Returns a {from,
-  // to} object by default. Side can be passed to get a specific side
-  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
-  // Pos objects returned contain a line object, rather than a line
-  // number (used to prevent looking up the same line twice).
-  TextMarker.prototype.find = function(side, lineObj) {
-    if (side == null && this.type == "bookmark") side = 1;
-    var from, to;
-    for (var i = 0; i < this.lines.length; ++i) {
-      var line = this.lines[i];
-      var span = getMarkedSpanFor(line.markedSpans, this);
-      if (span.from != null) {
-        from = Pos(lineObj ? line : lineNo(line), span.from);
-        if (side == -1) return from;
-      }
-      if (span.to != null) {
-        to = Pos(lineObj ? line : lineNo(line), span.to);
-        if (side == 1) return to;
-      }
-    }
-    return from && {from: from, to: to};
-  };
-
-  // Signals that the marker's widget changed, and surrounding layout
-  // should be recomputed.
-  TextMarker.prototype.changed = function() {
-    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
-    if (!pos || !cm) return;
-    runInOp(cm, function() {
-      var line = pos.line, lineN = lineNo(pos.line);
-      var view = findViewForLine(cm, lineN);
-      if (view) {
-        clearLineMeasurementCacheFor(view);
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
-      }
-      cm.curOp.updateMaxLine = true;
-      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
-        var oldHeight = widget.height;
-        widget.height = null;
-        var dHeight = widgetHeight(widget) - oldHeight;
-        if (dHeight)
-          updateLineHeight(line, line.height + dHeight);
-      }
-    });
-  };
-
-  TextMarker.prototype.attachLine = function(line) {
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp;
-      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
-        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
-    }
-    this.lines.push(line);
-  };
-  TextMarker.prototype.detachLine = function(line) {
-    this.lines.splice(indexOf(this.lines, line), 1);
-    if (!this.lines.length && this.doc.cm) {
-      var op = this.doc.cm.curOp;
-      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
-    }
-  };
-
-  // Collapsed markers have unique ids, in order to be able to order
-  // them, which is needed for uniquely determining an outer marker
-  // when they overlap (they may nest, but not partially overlap).
-  var nextMarkerId = 0;
-
-  // Create a marker, wire it up to the right lines, and
-  function markText(doc, from, to, options, type) {
-    // Shared markers (across linked documents) are handled separately
-    // (markTextShared will call out to this again, once per
-    // document).
-    if (options && options.shared) return markTextShared(doc, from, to, options, type);
-    // Ensure we are in an operation.
-    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
-
-    var marker = new TextMarker(doc, type), diff = cmp(from, to);
-    if (options) copyObj(options, marker, false);
-    // Don't connect empty markers unless clearWhenEmpty is false
-    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
-      return marker;
-    if (marker.replacedWith) {
-      // Showing up as a widget implies collapsed (widget replaces text)
-      marker.collapsed = true;
-      marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
-      if (!options.handleMouseEvents) marker.widgetNode.ignoreEvents = true;
-      if (options.insertLeft) marker.widgetNode.insertLeft = true;
-    }
-    if (marker.collapsed) {
-      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
-          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
-        throw new Error("Inserting collapsed marker partially overlapping an existing one");
-      sawCollapsedSpans = true;
-    }
-
-    if (marker.addToHistory)
-      addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
-
-    var curLine = from.line, cm = doc.cm, updateMaxLine;
-    doc.iter(curLine, to.line + 1, function(line) {
-      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
-        updateMaxLine = true;
-      if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
-      addMarkedSpan(line, new MarkedSpan(marker,
-                                         curLine == from.line ? from.ch : null,
-                                         curLine == to.line ? to.ch : null));
-      ++curLine;
-    });
-    // lineIsHidden depends on the presence of the spans, so needs a second pass
-    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
-      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
-    });
-
-    if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
-
-    if (marker.readOnly) {
-      sawReadOnlySpans = true;
-      if (doc.history.done.length || doc.history.undone.length)
-        doc.clearHistory();
-    }
-    if (marker.collapsed) {
-      marker.id = ++nextMarkerId;
-      marker.atomic = true;
-    }
-    if (cm) {
-      // Sync editor state
-      if (updateMaxLine) cm.curOp.updateMaxLine = true;
-      if (marker.collapsed)
-        regChange(cm, from.line, to.line + 1);
-      else if (marker.className || marker.title || marker.startStyle || marker.endStyle)
-        for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
-      if (marker.atomic) reCheckSelection(cm.doc);
-      signalLater(cm, "markerAdded", cm, marker);
-    }
-    return marker;
-  }
-
-  // SHARED TEXTMARKERS
-
-  // A shared marker spans multiple linked documents. It is
-  // implemented as a meta-marker-object controlling multiple normal
-  // markers.
-  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
-    this.markers = markers;
-    this.primary = primary;
-    for (var i = 0; i < markers.length; ++i)
-      markers[i].parent = this;
-  };
-  eventMixin(SharedTextMarker);
-
-  SharedTextMarker.prototype.clear = function() {
-    if (this.explicitlyCleared) return;
-    this.explicitlyCleared = true;
-    for (var i = 0; i < this.markers.length; ++i)
-      this.markers[i].clear();
-    signalLater(this, "clear");
-  };
-  SharedTextMarker.prototype.find = function(side, lineObj) {
-    return this.primary.find(side, lineObj);
-  };
-
-  function markTextShared(doc, from, to, options, type) {
-    options = copyObj(options);
-    options.shared = false;
-    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
-    var widget = options.widgetNode;
-    linkedDocs(doc, function(doc) {
-      if (widget) options.widgetNode = widget.cloneNode(true);
-      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
-      for (var i = 0; i < doc.linked.length; ++i)
-        if (doc.linked[i].isParent) return;
-      primary = lst(markers);
-    });
-    return new SharedTextMarker(markers, primary);
-  }
-
-  function findSharedMarkers(doc) {
-    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
-                         function(m) { return m.parent; });
-  }
-
-  function copySharedMarkers(doc, markers) {
-    for (var i = 0; i < markers.length; i++) {
-      var marker = markers[i], pos = marker.find();
-      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
-      if (cmp(mFrom, mTo)) {
-        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
-        marker.markers.push(subMark);
-        subMark.parent = marker;
-      }
-    }
-  }
-
-  function detachSharedMarkers(markers) {
-    for (var i = 0; i < markers.length; i++) {
-      var marker = markers[i], linked = [marker.primary.doc];;
-      linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
-      for (var j = 0; j < marker.markers.length; j++) {
-        var subMarker = marker.markers[j];
-        if (indexOf(linked, subMarker.doc) == -1) {
-          subMarker.parent = null;
-          marker.markers.splice(j--, 1);
-        }
-      }
-    }
-  }
-
-  // TEXTMARKER SPANS
-
-  function MarkedSpan(marker, from, to) {
-    this.marker = marker;
-    this.from = from; this.to = to;
-  }
-
-  // Search an array of spans for a span matching the given marker.
-  function getMarkedSpanFor(spans, marker) {
-    if (spans) for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.marker == marker) return span;
-    }
-  }
-  // Remove a span from an array, returning undefined if no spans are
-  // left (we don't store arrays for lines without spans).
-  function removeMarkedSpan(spans, span) {
-    for (var r, i = 0; i < spans.length; ++i)
-      if (spans[i] != span) (r || (r = [])).push(spans[i]);
-    return r;
-  }
-  // Add a span to a line.
-  function addMarkedSpan(line, span) {
-    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
-    span.marker.attachLine(line);
-  }
-
-  // Used for the algorithm that adjusts markers for a change in the
-  // document. These functions cut an array of spans at a given
-  // character position, returning an array of remaining chunks (or
-  // undefined if nothing remains).
-  function markedSpansBefore(old, startCh, isInsert) {
-    if (old) for (var i = 0, nw; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
-      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
-        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
-        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
-      }
-    }
-    return nw;
-  }
-  function markedSpansAfter(old, endCh, isInsert) {
-    if (old) for (var i = 0, nw; i < old.length; ++i) {
-      var span = old[i], marker = span.marker;
-      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
-      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
-        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
-        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
-                                              span.to == null ? null : span.to - endCh));
-      }
-    }
-    return nw;
-  }
-
-  // Given a change object, compute the new set of marker spans that
-  // cover the line in which the change took place. Removes spans
-  // entirely within the change, reconnects spans belonging to the
-  // same marker that appear on both sides of the change, and cuts off
-  // spans partially within the change. Returns an array of span
-  // arrays with one element for each line in (after) the change.
-  function stretchSpansOverChange(doc, change) {
-    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
-    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
-    if (!oldFirst && !oldLast) return null;
-
-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
-    // Get the spans that 'stick out' on both sides
-    var first = markedSpansBefore(oldFirst, startCh, isInsert);
-    var last = markedSpansAfter(oldLast, endCh, isInsert);
-
-    // Next, merge those two ends
-    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
-    if (first) {
-      // Fix up .to properties of first
-      for (var i = 0; i < first.length; ++i) {
-        var span = first[i];
-        if (span.to == null) {
-          var found = getMarkedSpanFor(last, span.marker);
-          if (!found) span.to = startCh;
-          else if (sameLine) span.to = found.to == null ? null : found.to + offset;
-        }
-      }
-    }
-    if (last) {
-      // Fix up .from in last (or move them into first in case of sameLine)
-      for (var i = 0; i < last.length; ++i) {
-        var span = last[i];
-        if (span.to != null) span.to += offset;
-        if (span.from == null) {
-          var found = getMarkedSpanFor(first, span.marker);
-          if (!found) {
-            span.from = offset;
-            if (sameLine) (first || (first = [])).push(span);
-          }
-        } else {
-          span.from += offset;
-          if (sameLine) (first || (first = [])).push(span);
-        }
-      }
-    }
-    // Make sure we didn't create any zero-length spans
-    if (first) first = clearEmptySpans(first);
-    if (last && last != first) last = clearEmptySpans(last);
-
-    var newMarkers = [first];
-    if (!sameLine) {
-      // Fill gap with whole-line-spans
-      var gap = change.text.length - 2, gapMarkers;
-      if (gap > 0 && first)
-        for (var i = 0; i < first.length; ++i)
-          if (first[i].to == null)
-            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
-      for (var i = 0; i < gap; ++i)
-        newMarkers.push(gapMarkers);
-      newMarkers.push(last);
-    }
-    return newMarkers;
-  }
-
-  // Remove spans that are empty and don't have a clearWhenEmpty
-  // option of false.
-  function clearEmptySpans(spans) {
-    for (var i = 0; i < spans.length; ++i) {
-      var span = spans[i];
-      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
-        spans.splice(i--, 1);
-    }
-    if (!spans.length) return null;
-    return spans;
-  }
-
-  // Used for un/re-doing changes from the history. Combines the
-  // result of computing the existing spans with the set of spans that
-  // existed in the history (so that deleting around a span and then
-  // undoing brings back the span).
-  function mergeOldSpans(doc, change) {
-    var old = getOldSpans(doc, change);
-    var stretched = stretchSpansOverChange(doc, change);
-    if (!old) return stretched;
-    if (!stretched) return old;
-
-    for (var i = 0; i < old.length; ++i) {
-      var oldCur = old[i], stretchCur = stretched[i];
-      if (oldCur && stretchCur) {
-        spans: for (var j = 0; j < stretchCur.length; ++j) {
-          var span = stretchCur[j];
-          for (var k = 0; k < oldCur.length; ++k)
-            if (oldCur[k].marker == span.marker) continue spans;
-          oldCur.push(span);
-        }
-      } else if (stretchCur) {
-        old[i] = stretchCur;
-      }
-    }
-    return old;
-  }
-
-  // Used to 'clip' out readOnly ranges when making a change.
-  function removeReadOnlyRanges(doc, from, to) {
-    var markers = null;
-    doc.iter(from.line, to.line + 1, function(line) {
-      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
-        var mark = line.markedSpans[i].marker;
-        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
-          (markers || (markers = [])).push(mark);
-      }
-    });
-    if (!markers) return null;
-    var parts = [{from: from, to: to}];
-    for (var i = 0; i < markers.length; ++i) {
-      var mk = markers[i], m = mk.find(0);
-      for (var j = 0; j < parts.length; ++j) {
-        var p = parts[j];
-        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
-        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
-        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
-          newParts.push({from: p.from, to: m.from});
-        if (dto > 0 || !mk.inclusiveRight && !dto)
-          newParts.push({from: m.to, to: p.to});
-        parts.splice.apply(parts, newParts);
-        j += newParts.length - 1;
-      }
-    }
-    return parts;
-  }
-
-  // Connect or disconnect spans from a line.
-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) return;
-    for (var i = 0; i < spans.length; ++i)
-      spans[i].marker.detachLine(line);
-    line.markedSpans = null;
-  }
-  function attachMarkedSpans(line, spans) {
-    if (!spans) return;
-    for (var i = 0; i < spans.length; ++i)
-      spans[i].marker.attachLine(line);
-    line.markedSpans = spans;
-  }
-
-  // Helpers used when computing which overlapping collapsed span
-  // counts as the larger one.
-  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
-  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
-
-  // Returns a number indicating which of two overlapping collapsed
-  // spans is larger (and thus includes the other). Falls back to
-  // comparing ids when the spans cover exactly the same range.
-  function compareCollapsedMarkers(a, b) {
-    var lenDiff = a.lines.length - b.lines.length;
-    if (lenDiff != 0) return lenDiff;
-    var aPos = a.find(), bPos = b.find();
-    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
-    if (fromCmp) return -fromCmp;
-    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
-    if (toCmp) return toCmp;
-    return b.id - a.id;
-  }
-
-  // Find out whether a line ends or starts in a collapsed span. If
-  // so, return the marker for that span.
-  function collapsedSpanAtSide(line, start) {
-    var sps = sawCollapsedSpans && line.markedSpans, found;
-    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
-          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
-        found = sp.marker;
-    }
-    return found;
-  }
-  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
-
-  // Test whether there exists a collapsed span that partially
-  // overlaps (covers the start or end, but not both) of a new span.
-  // Such overlap is not allowed.
-  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
-    var line = getLine(doc, lineNo);
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) for (var i = 0; i < sps.length; ++i) {
-      var sp = sps[i];
-      if (!sp.marker.collapsed) continue;
-      var found = sp.marker.find(0);
-      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
-      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
-      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
-      if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
-          fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
-        return true;
-    }
-  }
-
-  // A visual line is a line as drawn on the screen. Folding, for
-  // example, can cause multiple logical lines to appear on the same
-  // visual line. This finds the start of the visual line that the
-  // given line is part of (usually that is the line itself).
-  function visualLine(line) {
-    var merged;
-    while (merged = collapsedSpanAtStart(line))
-      line = merged.find(-1, true).line;
-    return line;
-  }
-
-  // Returns an array of logical lines that continue the visual line
-  // started by the argument, or undefined if there are no such lines.
-  function visualLineContinued(line) {
-    var merged, lines;
-    while (merged = collapsedSpanAtEnd(line)) {
-      line = merged.find(1, true).line;
-      (lines || (lines = [])).push(line);
-    }
-    return lines;
-  }
-
-  // Get the line number of the start of the visual line that the
-  // given line number is part of.
-  function visualLineNo(doc, lineN) {
-    var line = getLine(doc, lineN), vis = visualLine(line);
-    if (line == vis) return lineN;
-    return lineNo(vis);
-  }
-  // Get the line number of the start of the next visual line after
-  // the given line.
-  function visualLineEndNo(doc, lineN) {
-    if (lineN > doc.lastLine()) return lineN;
-    var line = getLine(doc, lineN), merged;
-    if (!lineIsHidden(doc, line)) return lineN;
-    while (merged = collapsedSpanAtEnd(line))
-      line = merged.find(1, true).line;
-    return lineNo(line) + 1;
-  }
-
-  // Compute whether a line is hidden. Lines count as hidden when they
-  // are part of a visual line that starts with another line, or when
-  // they are entirely covered by collapsed, non-widget span.
-  function lineIsHidden(doc, line) {
-    var sps = sawCollapsedSpans && line.markedSpans;
-    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
-      sp = sps[i];
-      if (!sp.marker.collapsed) continue;
-      if (sp.from == null) return true;
-      if (sp.marker.widgetNode) continue;
-      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
-        return true;
-    }
-  }
-  function lineIsHiddenInner(doc, line, span) {
-    if (span.to == null) {
-      var end = span.marker.find(1, true);
-      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
-    }
-    if (span.marker.inclusiveRight && span.to == line.text.length)
-      return true;
-    for (var sp, i = 0; i < line.markedSpans.length; ++i) {
-      sp = line.markedSpans[i];
-      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
-          (sp.to == null || sp.to != span.from) &&
-          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
-          lineIsHiddenInner(doc, line, sp)) return true;
-    }
-  }
-
-  // LINE WIDGETS
-
-  // Line widgets are block elements displayed above or below a line.
-
-  var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
-    if (options) for (var opt in options) if (options.hasOwnProperty(opt))
-      this[opt] = options[opt];
-    this.cm = cm;
-    this.node = node;
-  };
-  eventMixin(LineWidget);
-
-  function adjustScrollWhenAboveVisible(cm, line, diff) {
-    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
-      addToScrollPos(cm, null, diff);
-  }
-
-  LineWidget.prototype.clear = function() {
-    var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
-    if (no == null || !ws) return;
-    for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
-    if (!ws.length) line.widgets = null;
-    var height = widgetHeight(this);
-    runInOp(cm, function() {
-      adjustScrollWhenAboveVisible(cm, line, -height);
-      regLineChange(cm, no, "widget");
-      updateLineHeight(line, Math.max(0, line.height - height));
-    });
-  };
-  LineWidget.prototype.changed = function() {
-    var oldH = this.height, cm = this.cm, line = this.line;
-    this.height = null;
-    var diff = widgetHeight(this) - oldH;
-    if (!diff) return;
-    runInOp(cm, function() {
-      cm.curOp.forceUpdate = true;
-      adjustScrollWhenAboveVisible(cm, line, diff);
-      updateLineHeight(line, line.height + diff);
-    });
-  };
-
-  function widgetHeight(widget) {
-    if (widget.height != null) return widget.height;
-    if (!contains(document.body, widget.node)) {
-      var parentStyle = "position: relative;";
-      if (widget.coverGutter)
-        parentStyle += "margin-left: -" + widget.cm.getGutterElement().offsetWidth + "px;";
-      removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle));
-    }
-    return widget.height = widget.node.offsetHeight;
-  }
-
-  function addLineWidget(cm, handle, node, options) {
-    var widget = new LineWidget(cm, node, options);
-    if (widget.noHScroll) cm.display.alignWidgets = true;
-    changeLine(cm.doc, handle, "widget", function(line) {
-      var widgets = line.widgets || (line.widgets = []);
-      if (widget.insertAt == null) widgets.push(widget);
-      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
-      widget.line = line;
-      if (!lineIsHidden(cm.doc, line)) {
-        var aboveVisible = heightAtLine(line) < cm.doc.scrollTop;
-        updateLineHeight(line, line.height + widgetHeight(widget));
-        if (aboveVisible) addToScrollPos(cm, null, widget.height);
-        cm.curOp.forceUpdate = true;
-      }
-      return true;
-    });
-    return widget;
-  }
-
-  // LINE DATA STRUCTURE
-
-  // Line objects. These hold state related to a line, including
-  // highlighting info (the styles array).
-  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
-    this.text = text;
-    attachMarkedSpans(this, markedSpans);
-    this.height = estimateHeight ? estimateHeight(this) : 1;
-  };
-  eventMixin(Line);
-  Line.prototype.lineNo = function() { return lineNo(this); };
-
-  // Change the content (text, markers) of a line. Automatically
-  // invalidates cached information and tries to re-estimate the
-  // line's height.
-  function updateLine(line, text, markedSpans, estimateHeight) {
-    line.text = text;
-    if (line.stateAfter) line.stateAfter = null;
-    if (line.styles) line.styles = null;
-    if (line.order != null) line.order = null;
-    detachMarkedSpans(line);
-    attachMarkedSpans(line, markedSpans);
-    var estHeight = estimateHeight ? estimateHeight(line) : 1;
-    if (estHeight != line.height) updateLineHeight(line, estHeight);
-  }
-
-  // Detach a line from the document tree and its markers.
-  function cleanUpLine(line) {
-    line.parent = null;
-    detachMarkedSpans(line);
-  }
-
-  function extractLineClasses(type, output) {
-    if (type) for (;;) {
-      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
-      if (!lineClass) break;
-      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? "bgClass" : "textClass";
-      if (output[prop] == null)
-        output[prop] = lineClass[2];
-      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
-        output[prop] += " " + lineClass[2];
-    }
-    return type;
-  }
-
-  function callBlankLine(mode, state) {
-    if (mode.blankLine) return mode.blankLine(state);
-    if (!mode.innerMode) return;
-    var inner = CodeMirror.innerMode(mode, state);
-    if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
-  }
-
-  function readToken(mode, stream, state) {
-    for (var i = 0; i < 10; i++) {
-      var style = mode.token(stream, state);
-      if (stream.pos > stream.start) return style;
-    }
-    throw new Error("Mode " + mode.name + " failed to advance stream.");
-  }
-
-  // Run the given mode's parser over a line, calling f for each token.
-  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
-    var flattenSpans = mode.flattenSpans;
-    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
-    var curStart = 0, curStyle = null;
-    var stream = new StringStream(text, cm.options.tabSize), style;
-    if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
-    while (!stream.eol()) {
-      if (stream.pos > cm.options.maxHighlightLength) {
-        flattenSpans = false;
-        if (forceToEnd) processLine(cm, text, state, stream.pos);
-        stream.pos = text.length;
-        style = null;
-      } else {
-        style = extractLineClasses(readToken(mode, stream, state), lineClasses);
-      }
-      if (cm.options.addModeClass) {
-        var mName = CodeMirror.innerMode(mode, state).mode.name;
-        if (mName) style = "m-" + (style ? mName + " " + style : mName);
-      }
-      if (!flattenSpans || curStyle != style) {
-        if (curStart < stream.start) f(stream.start, curStyle);
-        curStart = stream.start; curStyle = style;
-      }
-      stream.start = stream.pos;
-    }
-    while (curStart < stream.pos) {
-      // Webkit seems to refuse to render text nodes longer than 57444 characters
-      var pos = Math.min(stream.pos, curStart + 50000);
-      f(pos, curStyle);
-      curStart = pos;
-    }
-  }
-
-  // Compute a style array (an array starting with a mode generation
-  // -- for invalidation -- followed by pairs of end positions and
-  // style strings), which is used to highlight the tokens on the
-  // line.
-  function highlightLine(cm, line, state, forceToEnd) {
-    // A styles array always starts with a number identifying the
-    // mode/overlays that it is based on (for easy invalidation).
-    var st = [cm.state.modeGen], lineClasses = {};
-    // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
-      st.push(end, style);
-    }, lineClasses, forceToEnd);
-
-    // Run overlays, adjust style array.
-    for (var o = 0; o < cm.state.overlays.length; ++o) {
-      var overlay = cm.state.overlays[o], i = 1, at = 0;
-      runMode(cm, line.text, overlay.mode, true, function(end, style) {
-        var start = i;
-        // Ensure there's a token end at the current position, and that i points at it
-        while (at < end) {
-          var i_end = st[i];
-          if (i_end > end)
-            st.splice(i, 1, end, st[i+1], i_end);
-          i += 2;
-          at = Math.min(end, i_end);
-        }
-        if (!style) return;
-        if (overlay.opaque) {
-          st.splice(start, i - start, end, "cm-overlay " + style);
-          i = start + 2;
-        } else {
-          for (; start < i; start += 2) {
-            var cur = st[start+1];
-            st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
-          }
-        }
-      }, lineClasses);
-    }
-
-    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
-  }
-
-  function getLineStyles(cm, line) {
-    if (!line.styles || line.styles[0] != cm.state.modeGen) {
-      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
-      line.styles = result.styles;
-      if (result.classes) line.styleClasses = result.classes;
-      else if (line.styleClasses) line.styleClasses = null;
-    }
-    return line.styles;
-  }
-
-  // Lightweight form of highlight -- proceed over this line and
-  // update state, but don't save a style array. Used for lines that
-  // aren't currently visible.
-  function processLine(cm, text, state, startAt) {
-    var mode = cm.doc.mode;
-    var stream = new StringStream(text, cm.options.tabSize);
-    stream.start = stream.pos = startAt || 0;
-    if (text == "") callBlankLine(mode, state);
-    while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
-      readToken(mode, stream, state);
-      stream.start = stream.pos;
-    }
-  }
-
-  // Convert a style as returned by a mode (either null, or a string
-  // containing one or more styles) to a CSS style. This is cached,
-  // and also looks for line-wide styles.
-  var styleToClassCache = {}, styleToClassCacheWithMode = {};
-  function interpretTokenStyle(style, options) {
-    if (!style || /^\s*$/.test(style)) return null;
-    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
-    return cache[style] ||
-      (cache[style] = style.replace(/\S+/g, "cm-$&"));
-  }
-
-  // Render the DOM representation of the text of a line. Also builds
-  // up a 'line map', which points at the DOM nodes that represent
-  // specific stretches of text, and is used by the measuring code.
-  // The returned object contains the DOM node, this map, and
-  // information about line-wide styles that were set by the mode.
-  function buildLineContent(cm, lineView) {
-    // The padding-right forces the element to have a 'border', which
-    // is needed on Webkit to be able to get line-level bounding
-    // rectangles for it (in measureChar).
-    var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
-    var builder = {pre: elt("pre", [content]), content: content, col: 0, pos: 0, cm: cm};
-    lineView.measure = {};
-
-    // Iterate over the logical lines that make up this visual line.
-    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
-      var line = i ? lineView.rest[i - 1] : lineView.line, order;
-      builder.pos = 0;
-      builder.addToken = buildToken;
-      // Optionally wire in some hacks into the token-rendering
-      // algorithm, to deal with browser quirks.
-      if ((ie || webkit) && cm.getOption("lineWrapping"))
-        builder.addToken = buildTokenSplitSpaces(builder.addToken);
-      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
-        builder.addToken = buildTokenBadBidi(builder.addToken, order);
-      builder.map = [];
-      insertLineContent(line, builder, getLineStyles(cm, line));
-      if (line.styleClasses) {
-        if (line.styleClasses.bgClass)
-          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
-        if (line.styleClasses.textClass)
-          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
-      }
-
-      // Ensure at least a single node is present, for measuring.
-      if (builder.map.length == 0)
-        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
-
-      // Store the map and a cache object for the current logical line
-      if (i == 0) {
-        lineView.measure.map = builder.map;
-        lineView.measure.cache = {};
-      } else {
-        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
-        (lineView.measure.caches || (lineView.measure.caches = [])).push({});
-      }
-    }
-
-    signal(cm, "renderLine", cm, lineView.line, builder.pre);
-    if (builder.pre.className)
-      builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
-    return builder;
-  }
-
-  function defaultSpecialCharPlaceholder(ch) {
-    var token = elt("span", "\u2022", "cm-invalidchar");
-    token.title = "\\u" + ch.charCodeAt(0).toString(16);
-    return token;
-  }
-
-  // Build up the DOM representation for a single token, and add it to
-  // the line map. Takes care to render special characters separately.
-  function buildToken(builder, text, style, startStyle, endStyle, title) {
-    if (!text) return;
-    var special = builder.cm.options.specialChars, mustWrap = false;
-    if (!special.test(text)) {
-      builder.col += text.length;
-      var content = document.createTextNode(text);
-      builder.map.push(builder.pos, builder.pos + text.length, content);
-      if (ie && ie_version < 9) mustWrap = true;
-      builder.pos += text.length;
-    } else {
-      var content = document.createDocumentFragment(), pos = 0;
-      while (true) {
-        special.lastIndex = pos;
-        var m = special.exec(text);
-        var skipped = m ? m.index - pos : text.length - pos;
-        if (skipped) {
-          var txt = document.createTextNode(text.slice(pos, pos + skipped));
-          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
-          else content.appendChild(txt);
-          builder.map.push(builder.pos, builder.pos + skipped, txt);
-          builder.col += skipped;
-          builder.pos += skipped;
-        }
-        if (!m) break;
-        pos += skipped + 1;
-        if (m[0] == "\t") {
-          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
-          var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
-          builder.col += tabWidth;
-        } else {
-          var txt = builder.cm.options.specialCharPlaceholder(m[0]);
-          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
-          else content.appendChild(txt);
-          builder.col += 1;
-        }
-        builder.map.push(builder.pos, builder.pos + 1, txt);
-        builder.pos++;
-      }
-    }
-    if (style || startStyle || endStyle || mustWrap) {
-      var fullStyle = style || "";
-      if (startStyle) fullStyle += startStyle;
-      if (endStyle) fullStyle += endStyle;
-      var token = elt("span", [content], fullStyle);
-      if (title) token.title = title;
-      return builder.content.appendChild(token);
-    }
-    builder.content.appendChild(content);
-  }
-
-  function buildTokenSplitSpaces(inner) {
-    function split(old) {
-      var out = " ";
-      for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
-      out += " ";
-      return out;
-    }
-    return function(builder, text, style, startStyle, endStyle, title) {
-      inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
-    };
-  }
-
-  // Work around nonsense dimensions being reported for stretches of
-  // right-to-left text.
-  function buildTokenBadBidi(inner, order) {
-    return function(builder, text, style, startStyle, endStyle, title) {
-      style = style ? style + " cm-force-border" : "cm-force-border";
-      var start = builder.pos, end = start + text.length;
-      for (;;) {
-        // Find the part that overlaps with the start of this text
-        for (var i = 0; i < order.length; i++) {
-          var part = order[i];
-          if (part.to > start && part.from <= start) break;
-        }
-        if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title);
-        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title);
-        startStyle = null;
-        text = text.slice(part.to - start);
-        start = part.to;
-      }
-    };
-  }
-
-  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
-    var widget = !ignoreWidget && marker.widgetNode;
-    if (widget) {
-      builder.map.push(builder.pos, builder.pos + size, widget);
-      builder.content.appendChild(widget);
-    }
-    builder.pos += size;
-  }
-
-  // Outputs a number of spans to make up a line, taking highlighting
-  // and marked text into account.
-  function insertLineContent(line, builder, styles) {
-    var spans = line.markedSpans, allText = line.text, at = 0;
-    if (!spans) {
-      for (var i = 1; i < styles.length; i+=2)
-        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
-      return;
-    }
-
-    var len = allText.length, pos = 0, i = 1, text = "", style;
-    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
-    for (;;) {
-      if (nextChange == pos) { // Update current marker set
-        spanStyle = spanEndStyle = spanStartStyle = title = "";
-        collapsed = null; nextChange = Infinity;
-        var foundBookmarks = [];
-        for (var j = 0; j < spans.length; ++j) {
-          var sp = spans[j], m = sp.marker;
-          if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
-            if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
-            if (m.className) spanStyle += " " + m.className;
-            if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
-            if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
-            if (m.title && !title) title = m.title;
-            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
-              collapsed = sp;
-          } else if (sp.from > pos && nextChange > sp.from) {
-            nextChange = sp.from;
-          }
-          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookmarks.push(m);
-        }
-        if (collapsed && (collapsed.from || 0) == pos) {
-          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
-                             collapsed.marker, collapsed.from == null);
-          if (collapsed.to == null) return;
-        }
-        if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
-          buildCollapsedSpan(builder, 0, foundBookmarks[j]);
-      }
-      if (pos >= len) break;
-
-      var upto = Math.min(len, nextChange);
-      while (true) {
-        if (text) {
-          var end = pos + text.length;
-          if (!collapsed) {
-            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
-            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
-                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
-          }
-          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
-          pos = end;
-          spanStartStyle = "";
-        }
-        text = allText.slice(at, at = styles[i++]);
-        style = interpretTokenStyle(styles[i++], builder.cm.options);
-      }
-    }
-  }
-
-  // DOCUMENT DATA STRUCTURE
-
-  // By default, updates that start and end at the beginning of a line
-  // are treated specially, in order to make the association of line
-  // widgets and marker elements with the text behave more intuitive.
-  function isWholeLineUpdate(doc, change) {
-    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
-      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
-  }
-
-  // Perform a change on the document data structure.
-  function updateDoc(doc, change, markedSpans, estimateHeight) {
-    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
-    function update(line, text, spans) {
-      updateLine(line, text, spans, estimateHeight);
-      signalLater(line, "change", line, change);
-    }
-
-    var from = change.from, to = change.to, text = change.text;
-    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
-    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
-
-    // Adjust the line structure
-    if (isWholeLineUpdate(doc, change)) {
-      // This is a whole-line replace. Treated specially to make
-      // sure line objects move the way they are supposed to.
-      for (var i = 0, added = []; i < text.length - 1; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
-      update(lastLine, lastLine.text, lastSpans);
-      if (nlines) doc.remove(from.line, nlines);
-      if (added.length) doc.insert(from.line, added);
-    } else if (firstLine == lastLine) {
-      if (text.length == 1) {
-        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
-      } else {
-        for (var added = [], i = 1; i < text.length - 1; ++i)
-          added.push(new Line(text[i], spansFor(i), estimateHeight));
-        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
-        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-        doc.insert(from.line + 1, added);
-      }
-    } else if (text.length == 1) {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
-      doc.remove(from.line + 1, nlines);
-    } else {
-      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
-      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
-      for (var i = 1, added = []; i < text.length - 1; ++i)
-        added.push(new Line(text[i], spansFor(i), estimateHeight));
-      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
-      doc.insert(from.line + 1, added);
-    }
-
-    signalLater(doc, "change", doc, change);
-  }
-
-  // The document is represented as a BTree consisting of leaves, with
-  // chunk of lines in them, and branches, with up to ten leaves or
-  // other branch nodes below them. The top node is always a branch
-  // node, and is the document object itself (meaning it has
-  // additional methods and properties).
-  //
-  // All nodes have parent links. The tree is used both to go from
-  // line numbers to line objects, and to go from objects to numbers.
-  // It also indexes by height, and is used to convert between height
-  // and line object, and to find the total height of the document.
-  //
-  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
-
-  function LeafChunk(lines) {
-    this.lines = lines;
-    this.parent = null;
-    for (var i = 0, height = 0; i < lines.length; ++i) {
-      lines[i].parent = this;
-      height += lines[i].height;
-    }
-    this.height = height;
-  }
-
-  LeafChunk.prototype = {
-    chunkSize: function() { return this.lines.length; },
-    // Remove the n lines at offset 'at'.
-    removeInner: function(at, n) {
-      for (var i = at, e = at + n; i < e; ++i) {
-        var line = this.lines[i];
-        this.height -= line.height;
-        cleanUpLine(line);
-        signalLater(line, "delete");
-      }
-      this.lines.splice(at, n);
-    },
-    // Helper used to collapse a small branch into a single leaf.
-    collapse: function(lines) {
-      lines.push.apply(lines, this.lines);
-    },
-    // Insert the given array of lines at offset 'at', count them as
-    // having the given height.
-    insertInner: function(at, lines, height) {
-      this.height += height;
-      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
-      for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
-    },
-    // Used to iterate over a part of the tree.
-    iterN: function(at, n, op) {
-      for (var e = at + n; at < e; ++at)
-        if (op(this.lines[at])) return true;
-    }
-  };
-
-  function BranchChunk(children) {
-    this.children = children;
-    var size = 0, height = 0;
-    for (var i = 0; i < children.length; ++i) {
-      var ch = children[i];
-      size += ch.chunkSize(); height += ch.height;
-      ch.parent = this;
-    }
-    this.size = size;
-    this.height = height;
-    this.parent = null;
-  }
-
-  BranchChunk.prototype = {
-    chunkSize: function() { return this.size; },
-    removeInner: function(at, n) {
-      this.size -= n;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var rm = Math.min(n, sz - at), oldHeight = child.height;
-          child.removeInner(at, rm);
-          this.height -= oldHeight - child.height;
-          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
-          if ((n -= rm) == 0) break;
-          at = 0;
-        } else at -= sz;
-      }
-      // If the result is smaller than 25 lines, ensure that it is a
-      // single leaf node.
-      if (this.size - n < 25 &&
-          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
-        var lines = [];
-        this.collapse(lines);
-        this.children = [new LeafChunk(lines)];
-        this.children[0].parent = this;
-      }
-    },
-    collapse: function(lines) {
-      for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
-    },
-    insertInner: function(at, lines, height) {
-      this.size += lines.length;
-      this.height += height;
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at <= sz) {
-          child.insertInner(at, lines, height);
-          if (child.lines && child.lines.length > 50) {
-            while (child.lines.length > 50) {
-              var spilled = child.lines.splice(child.lines.length - 25, 25);
-              var newleaf = new LeafChunk(spilled);
-              child.height -= newleaf.height;
-              this.children.splice(i + 1, 0, newleaf);
-              newleaf.parent = this;
-            }
-            this.maybeSpill();
-          }
-          break;
-        }
-        at -= sz;
-      }
-    },
-    // When a node has grown, check whether it should be split.
-    maybeSpill: function() {
-      if (this.children.length <= 10) return;
-      var me = this;
-      do {
-        var spilled = me.children.splice(me.children.length - 5, 5);
-        var sibling = new BranchChunk(spilled);
-        if (!me.parent) { // Become the parent node
-          var copy = new BranchChunk(me.children);
-          copy.parent = me;
-          me.children = [copy, sibling];
-          me = copy;
-        } else {
-          me.size -= sibling.size;
-          me.height -= sibling.height;
-          var myIndex = indexOf(me.parent.children, me);
-          me.parent.children.splice(myIndex + 1, 0, sibling);
-        }
-        sibling.parent = me.parent;
-      } while (me.children.length > 10);
-      me.parent.maybeSpill();
-    },
-    iterN: function(at, n, op) {
-      for (var i = 0; i < this.children.length; ++i) {
-        var child = this.children[i], sz = child.chunkSize();
-        if (at < sz) {
-          var used = Math.min(n, sz - at);
-          if (child.iterN(at, used, op)) return true;
-          if ((n -= used) == 0) break;
-          at = 0;
-        } else at -= sz;
-      }
-    }
-  };
-
-  var nextDocId = 0;
-  var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
-    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
-    if (firstLine == null) firstLine = 0;
-
-    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
-    this.first = firstLine;
-    this.scrollTop = this.scrollLeft = 0;
-    this.cantEdit = false;
-    this.cleanGeneration = 1;
-    this.frontier = firstLine;
-    var start = Pos(firstLine, 0);
-    this.sel = simpleSelection(start);
-    this.history = new History(null);
-    this.id = ++nextDocId;
-    this.modeOption = mode;
-
-    if (typeof text == "string") text = splitLines(text);
-    updateDoc(this, {from: start, to: start, text: text});
-    setSelection(this, simpleSelection(start), sel_dontScroll);
-  };
-
-  Doc.prototype = createObj(BranchChunk.prototype, {
-    constructor: Doc,
-    // Iterate over the document. Supports two forms -- with only one
-    // argument, it calls that for each line in the document. With
-    // three, it iterates over the range given by the first two (with
-    // the second being non-inclusive).
-    iter: function(from, to, op) {
-      if (op) this.iterN(from - this.first, to - from, op);
-      else this.iterN(this.first, this.first + this.size, from);
-    },
-
-    // Non-public interface for adding and removing lines.
-    insert: function(at, lines) {
-      var height = 0;
-      for (var i = 0; i < lines.length; ++i) height += lines[i].height;
-      this.insertInner(at - this.first, lines, height);
-    },
-    remove: function(at, n) { this.removeInner(at - this.first, n); },
-
-    // From here, the methods are part of the public interface. Most
-    // are also available from CodeMirror (editor) instances.
-
-    getValue: function(lineSep) {
-      var lines = getLines(this, this.first, this.first + this.size);
-      if (lineSep === false) return lines;
-      return lines.join(lineSep || "\n");
-    },
-    setValue: docMethodOp(function(code) {
-      var top = Pos(this.first, 0), last = this.first + this.size - 1;
-      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
-                        text: splitLines(code), origin: "setValue"}, true);
-      setSelection(this, simpleSelection(top));
-    }),
-    replaceRange: function(code, from, to, origin) {
-      from = clipPos(this, from);
-      to = to ? clipPos(this, to) : from;
-      replaceRange(this, code, from, to, origin);
-    },
-    getRange: function(from, to, lineSep) {
-      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
-      if (lineSep === false) return lines;
-      return lines.join(lineSep || "\n");
-    },
-
-    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
-
-    getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
-    getLineNumber: function(line) {return lineNo(line);},
-
-    getLineHandleVisualStart: function(line) {
-      if (typeof line == "number") line = getLine(this, line);
-      return visualLine(line);
-    },
-
-    lineCount: function() {return this.size;},
-    firstLine: function() {return this.first;},
-    lastLine: function() {return this.first + this.size - 1;},
-
-    clipPos: function(pos) {return clipPos(this, pos);},
-
-    getCursor: function(start) {
-      var range = this.sel.primary(), pos;
-      if (start == null || start == "head") pos = range.head;
-      else if (start == "anchor") pos = range.anchor;
-      else if (start == "end" || start == "to" || start === false) pos = range.to();
-      else pos = range.from();
-      return pos;
-    },
-    listSelections: function() { return this.sel.ranges; },
-    somethingSelected: function() {return this.sel.somethingSelected();},
-
-    setCursor: docMethodOp(function(line, ch, options) {
-      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
-    }),
-    setSelection: docMethodOp(function(anchor, head, options) {
-      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
-    }),
-    extendSelection: docMethodOp(function(head, other, options) {
-      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
-    }),
-    extendSelections: docMethodOp(function(heads, options) {
-      extendSelections(this, clipPosArray(this, heads, options));
-    }),
-    extendSelectionsBy: docMethodOp(function(f, options) {
-      extendSelections(this, map(this.sel.ranges, f), options);
-    }),
-    setSelections: docMethodOp(function(ranges, primary, options) {
-      if (!ranges.length) return;
-      for (var i = 0, out = []; i < ranges.length; i++)
-        out[i] = new Range(clipPos(this, ranges[i].anchor),
-                           clipPos(this, ranges[i].head));
-      if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
-      setSelection(this, normalizeSelection(out, primary), options);
-    }),
-    addSelection: docMethodOp(function(anchor, head, options) {
-      var ranges = this.sel.ranges.slice(0);
-      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
-      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
-    }),
-
-    getSelection: function(lineSep) {
-      var ranges = this.sel.ranges, lines;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        lines = lines ? lines.concat(sel) : sel;
-      }
-      if (lineSep === false) return lines;
-      else return lines.join(lineSep || "\n");
-    },
-    getSelections: function(lineSep) {
-      var parts = [], ranges = this.sel.ranges;
-      for (var i = 0; i < ranges.length; i++) {
-        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
-        if (lineSep !== false) sel = sel.join(lineSep || "\n");
-        parts[i] = sel;
-      }
-      return parts;
-    },
-    replaceSelection: function(code, collapse, origin) {
-      var dup = [];
-      for (var i = 0; i < this.sel.ranges.length; i++)
-        dup[i] = code;
-      this.replaceSelections(dup, collapse, origin || "+input");
-    },
-    replaceSelections: docMethodOp(function(code, collapse, origin) {
-      var changes = [], sel = this.sel;
-      for (var i = 0; i < sel.ranges.length; i++) {
-        var range = sel.ranges[i];
-        changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
-      }
-      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
-      for (var i = changes.length - 1; i >= 0; i--)
-        makeChange(this, changes[i]);
-      if (newSel) setSelectionReplaceHistory(this, newSel);
-      else if (this.cm) ensureCursorVisible(this.cm);
-    }),
-    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
-    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
-    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
-    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
-
-    setExtending: function(val) {this.extend = val;},
-    getExtending: function() {return this.extend;},
-
-    historySize: function() {
-      var hist = this.history, done = 0, undone = 0;
-      for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
-      for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
-      return {undo: done, redo: undone};
-    },
-    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
-
-    markClean: function() {
-      this.cleanGeneration = this.changeGeneration(true);
-    },
-    changeGeneration: function(forceSplit) {
-      if (forceSplit)
-        this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
-      return this.history.generation;
-    },
-    isClean: function (gen) {
-      return this.history.generation == (gen || this.cleanGeneration);
-    },
-
-    getHistory: function() {
-      return {done: copyHistoryArray(this.history.done),
-              undone: copyHistoryArray(this.history.undone)};
-    },
-    setHistory: function(histData) {
-      var hist = this.history = new History(this.history.maxGeneration);
-      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
-      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
-    },
-
-    addLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, "class", function(line) {
-        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
-        if (!line[prop]) line[prop] = cls;
-        else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
-        else line[prop] += " " + cls;
-        return true;
-      });
-    }),
-    removeLineClass: docMethodOp(function(handle, where, cls) {
-      return changeLine(this, handle, "class", function(line) {
-        var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
-        var cur = line[prop];
-        if (!cur) return false;
-        else if (cls == null) line[prop] = null;
-        else {
-          var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
-          if (!found) return false;
-          var end = found.index + found[0].length;
-          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
-        }
-        return true;
-      });
-    }),
-
-    markText: function(from, to, options) {
-      return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
-    },
-    setBookmark: function(pos, options) {
-      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
-                      insertLeft: options && options.insertLeft,
-                      clearWhenEmpty: false, shared: options && options.shared};
-      pos = clipPos(this, pos);
-      return markText(this, pos, pos, realOpts, "bookmark");
-    },
-    findMarksAt: function(pos) {
-      pos = clipPos(this, pos);
-      var markers = [], spans = getLine(this, pos.line).markedSpans;
-      if (spans) for (var i = 0; i < spans.length; ++i) {
-        var span = spans[i];
-        if ((span.from == null || span.from <= pos.ch) &&
-            (span.to == null || span.to >= pos.ch))
-          markers.push(span.marker.parent || span.marker);
-      }
-      return markers;
-    },
-    findMarks: function(from, to, filter) {
-      from = clipPos(this, from); to = clipPos(this, to);
-      var found = [], lineNo = from.line;
-      this.iter(from.line, to.line + 1, function(line) {
-        var spans = line.markedSpans;
-        if (spans) for (var i = 0; i < spans.length; i++) {
-          var span = spans[i];
-          if (!(lineNo == from.line && from.ch > span.to ||
-                span.from == null && lineNo != from.line||
-                lineNo == to.line && span.from > to.ch) &&
-              (!filter || filter(span.marker)))
-            found.push(span.marker.parent || span.marker);
-        }
-        ++lineNo;
-      });
-      return found;
-    },
-    getAllMarks: function() {
-      var markers = [];
-      this.iter(function(line) {
-        var sps = line.markedSpans;
-        if (sps) for (var i = 0; i < sps.length; ++i)
-          if (sps[i].from != null) markers.push(sps[i].marker);
-      });
-      return markers;
-    },
-
-    posFromIndex: function(off) {
-      var ch, lineNo = this.first;
-      this.iter(function(line) {
-        var sz = line.text.length + 1;
-        if (sz > off) { ch = off; return true; }
-        off -= sz;
-        ++lineNo;
-      });
-      return clipPos(this, Pos(lineNo, ch));
-    },
-    indexFromPos: function (coords) {
-      coords = clipPos(this, coords);
-      var index = coords.ch;
-      if (coords.line < this.first || coords.ch < 0) return 0;
-      this.iter(this.first, coords.line, function (line) {
-        index += line.text.length + 1;
-      });
-      return index;
-    },
-
-    copy: function(copyHistory) {
-      var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
-      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
-      doc.sel = this.sel;
-      doc.extend = false;
-      if (copyHistory) {
-        doc.history.undoDepth = this.history.undoDepth;
-        doc.setHistory(this.getHistory());
-      }
-      return doc;
-    },
-
-    linkedDoc: function(options) {
-      if (!options) options = {};
-      var from = this.first, to = this.first + this.size;
-      if (options.from != null && options.from > from) from = options.from;
-      if (options.to != null && options.to < to) to = options.to;
-      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
-      if (options.sharedHist) copy.history = this.history;
-      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
-      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
-      copySharedMarkers(copy, findSharedMarkers(this));
-      return copy;
-    },
-    unlinkDoc: function(other) {
-      if (other instanceof CodeMirror) other = other.doc;
-      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
-        var link = this.linked[i];
-        if (link.doc != other) continue;
-        this.linked.splice(i, 1);
-        other.unlinkDoc(this);
-        detachSharedMarkers(findSharedMarkers(this));
-        break;
-      }
-      // If the histories were shared, split them again
-      if (other.history == this.history) {
-        var splitIds = [other.id];
-        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
-        other.history = new History(null);
-        other.history.done = copyHistoryArray(this.history.done, splitIds);
-        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
-      }
-    },
-    iterLinkedDocs: function(f) {linkedDocs(this, f);},
-
-    getMode: function() {return this.mode;},
-    getEditor: function() {return this.cm;}
-  });
-
-  // Public alias.
-  Doc.prototype.eachLine = Doc.prototype.iter;
-
-  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
-  var dontDelegate = "iter insert remove copy getEditor".split(" ");
-  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
-    CodeMirror.prototype[prop] = (function(method) {
-      return function() {return method.apply(this.doc, arguments);};
-    })(Doc.prototype[prop]);
-
-  eventMixin(Doc);
-
-  // Call f for all linked documents.
-  function linkedDocs(doc, f, sharedHistOnly) {
-    function propagate(doc, skip, sharedHist) {
-      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
-        var rel = doc.linked[i];
-        if (rel.doc == skip) continue;
-        var shared = sharedHist && rel.sharedHist;
-        if (sharedHistOnly && !shared) continue;
-        f(rel.doc, shared);
-        propagate(rel.doc, doc, shared);
-      }
-    }
-    propagate(doc, null, true);
-  }
-
-  // Attach a document to an editor.
-  function attachDoc(cm, doc) {
-    if (doc.cm) throw new Error("This document is already in use.");
-    cm.doc = doc;
-    doc.cm = cm;
-    estimateLineHeights(cm);
-    loadMode(cm);
-    if (!cm.options.lineWrapping) findMaxLine(cm);
-    cm.options.mode = doc.modeOption;
-    regChange(cm);
-  }
-
-  // LINE UTILITIES
-
-  // Find the line object corresponding to the given line number.
-  function getLine(doc, n) {
-    n -= doc.first;
-    if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
-    for (var chunk = doc; !chunk.lines;) {
-      for (var i = 0;; ++i) {
-        var child = chunk.children[i], sz = child.chunkSize();
-        if (n < sz) { chunk = child; break; }
-        n -= sz;
-      }
-    }
-    return chunk.lines[n];
-  }
-
-  // Get the part of a document between two positions, as an array of
-  // strings.
-  function getBetween(doc, start, end) {
-    var out = [], n = start.line;
-    doc.iter(start.line, end.line + 1, function(line) {
-      var text = line.text;
-      if (n == end.line) text = text.slice(0, end.ch);
-      if (n == start.line) text = text.slice(start.ch);
-      out.push(text);
-      ++n;
-    });
-    return out;
-  }
-  // Get the lines between from and to, as array of strings.
-  function getLines(doc, from, to) {
-    var out = [];
-    doc.iter(from, to, function(line) { out.push(line.text); });
-    return out;
-  }
-
-  // Update the height of a line, propagating the height change
-  // upwards to parent nodes.
-  function updateLineHeight(line, height) {
-    var diff = height - line.height;
-    if (diff) for (var n = line; n; n = n.parent) n.height += diff;
-  }
-
-  // Given a line object, find its line number by walking up through
-  // its parent links.
-  function lineNo(line) {
-    if (line.parent == null) return null;
-    var cur = line.parent, no = indexOf(cur.lines, line);
-    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
-      for (var i = 0;; ++i) {
-        if (chunk.children[i] == cur) break;
-        no += chunk.children[i].chunkSize();
-      }
-    }
-    return no + cur.first;
-  }
-
-  // Find the line at the given vertical position, using the height
-  // information in the document tree.
-  function lineAtHeight(chunk, h) {
-    var n = chunk.first;
-    outer: do {
-      for (var i = 0; i < chunk.children.length; ++i) {
-        var child = chunk.children[i], ch = child.height;
-        if (h < ch) { chunk = child; continue outer; }
-        h -= ch;
-        n += child.chunkSize();
-      }
-      return n;
-    } while (!chunk.lines);
-    for (var i = 0; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i], lh = line.height;
-      if (h < lh) break;
-      h -= lh;
-    }
-    return n + i;
-  }
-
-
-  // Find the height above the given line.
-  function heightAtLine(lineObj) {
-    lineObj = visualLine(lineObj);
-
-    var h = 0, chunk = lineObj.parent;
-    for (var i = 0; i < chunk.lines.length; ++i) {
-      var line = chunk.lines[i];
-      if (line == lineObj) break;
-      else h += line.height;
-    }
-    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
-      for (var i = 0; i < p.children.length; ++i) {
-        var cur = p.children[i];
-        if (cur == chunk) break;
-        else h += cur.height;
-      }
-    }
-    return h;
-  }
-
-  // Get the bidi ordering for the given line (and cache it). Returns
-  // false for lines that are fully left-to-right, and an array of
-  // BidiSpan objects otherwise.
-  function getOrder(line) {
-    var order = line.order;
-    if (order == null) order = line.order = bidiOrdering(line.text);
-    return order;
-  }
-
-  // HISTORY
-
-  function History(startGen) {
-    // Arrays of change events and selections. Doing something adds an
-    // event to done and clears undo. Undoing moves events from done
-    // to undone, redoing moves them in the other direction.
-    this.done = []; this.undone = [];
-    this.undoDepth = Infinity;
-    // Used to track when changes can be merged into a single undo
-    // event
-    this.lastModTime = this.lastSelTime = 0;
-    this.lastOp = this.lastSelOp = null;
-    this.lastOrigin = this.lastSelOrigin = null;
-    // Used by the isClean() method
-    this.generation = this.maxGeneration = startGen || 1;
-  }
-
-  // Create a history change event from an updateDoc-style change
-  // object.
-  function historyChangeFromChange(doc, change) {
-    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
-    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
-    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
-    return histChange;
-  }
-
-  // Pop all selection events off the end of a history array. Stop at
-  // a change event.
-  function clearSelectionEvents(array) {
-    while (array.length) {
-      var last = lst(array);
-      if (last.ranges) array.pop();
-      else break;
-    }
-  }
-
-  // Find the top change event in the history. Pop off selection
-  // events that are in the way.
-  function lastChangeEvent(hist, force) {
-    if (force) {
-      clearSelectionEvents(hist.done);
-      return lst(hist.done);
-    } else if (hist.done.length && !lst(hist.done).ranges) {
-      return lst(hist.done);
-    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
-      hist.done.pop();
-      return lst(hist.done);
-    }
-  }
-
-  // Register a change in the history. Merges changes that are within
-  // a single operation, ore are close together with an origin that
-  // allows merging (starting with "+") into a single event.
-  function addChangeToHistory(doc, change, selAfter, opId) {
-    var hist = doc.history;
-    hist.undone.length = 0;
-    var time = +new Date, cur;
-
-    if ((hist.lastOp == opId ||
-         hist.lastOrigin == change.origin && change.origin &&
-         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
-          change.origin.charAt(0) == "*")) &&
-        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
-      // Merge this change into the last event
-      var last = lst(cur.changes);
-      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
-        // Optimized case for simple insertion -- don't want to add
-        // new changesets for every character typed
-        last.to = changeEnd(change);
-      } else {
-        // Add new sub-event
-        cur.changes.push(historyChangeFromChange(doc, change));
-      }
-    } else {
-      // Can not be merged, start a new event.
-      var before = lst(hist.done);
-      if (!before || !before.ranges)
-        pushSelectionToHistory(doc.sel, hist.done);
-      cur = {changes: [historyChangeFromChange(doc, change)],
-             generation: hist.generation};
-      hist.done.push(cur);
-      while (hist.done.length > hist.undoDepth) {
-        hist.done.shift();
-        if (!hist.done[0].ranges) hist.done.shift();
-      }
-    }
-    hist.done.push(selAfter);
-    hist.generation = ++hist.maxGeneration;
-    hist.lastModTime = hist.lastSelTime = time;
-    hist.lastOp = hist.lastSelOp = opId;
-    hist.lastOrigin = hist.lastSelOrigin = change.origin;
-
-    if (!last) signal(doc, "historyAdded");
-  }
-
-  function selectionEventCanBeMerged(doc, origin, prev, sel) {
-    var ch = origin.charAt(0);
-    return ch == "*" ||
-      ch == "+" &&
-      prev.ranges.length == sel.ranges.length &&
-      prev.somethingSelected() == sel.somethingSelected() &&
-      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
-  }
-
-  // Called whenever the selection changes, sets the new selection as
-  // the pending selection in the history, and pushes the old pending
-  // selection into the 'done' array when it was significantly
-  // different (in number of selected ranges, emptiness, or time).
-  function addSelectionToHistory(doc, sel, opId, options) {
-    var hist = doc.history, origin = options && options.origin;
-
-    // A new event is started when the previous origin does not match
-    // the current, or the origins don't allow matching. Origins
-    // starting with * are always merged, those starting with + are
-    // merged when similar and close together in time.
-    if (opId == hist.lastSelOp ||
-        (origin && hist.lastSelOrigin == origin &&
-         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
-          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
-      hist.done[hist.done.length - 1] = sel;
-    else
-      pushSelectionToHistory(sel, hist.done);
-
-    hist.lastSelTime = +new Date;
-    hist.lastSelOrigin = origin;
-    hist.lastSelOp = opId;
-    if (options && options.clearRedo !== false)
-      clearSelectionEvents(hist.undone);
-  }
-
-  function pushSelectionToHistory(sel, dest) {
-    var top = lst(dest);
-    if (!(top && top.ranges && top.equals(sel)))
-      dest.push(sel);
-  }
-
-  // Used to store marked span information in the history.
-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change["spans_" + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
-      if (line.markedSpans)
-        (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
-      ++n;
-    });
-  }
-
-  // When un/re-doing restores text containing marked spans, those
-  // that have been explicitly cleared should not be restored.
-  function removeClearedSpans(spans) {
-    if (!spans) return null;
-    for (var i = 0, out; i < spans.length; ++i) {
-      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
-      else if (out) out.push(spans[i]);
-    }
-    return !out ? spans : out.length ? out : null;
-  }
-
-  // Retrieve and filter the old marked spans stored in a change event.
-  function getOldSpans(doc, change) {
-    var found = change["spans_" + doc.id];
-    if (!found) return null;
-    for (var i = 0, nw = []; i < change.text.length; ++i)
-      nw.push(removeClearedSpans(found[i]));
-    return nw;
-  }
-
-  // Used both to provide a JSON-safe object in .getHistory, and, when
-  // detaching a document, to split the history in two
-  function copyHistoryArray(events, newGroup, instantiateSel) {
-    for (var i = 0, copy = []; i < events.length; ++i) {
-      var event = events[i];
-      if (event.ranges) {
-        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
-        continue;
-      }
-      var changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges});
-      for (var j = 0; j < changes.length; ++j) {
-        var change = changes[j], m;
-        newChanges.push({from: change.from, to: change.to, text: change.text});
-        if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
-          if (indexOf(newGroup, Number(m[1])) > -1) {
-            lst(newChanges)[prop] = change[prop];
-            delete change[prop];
-          }
-        }
-      }
-    }
-    return copy;
-  }
-
-  // Rebasing/resetting history to deal with externally-sourced changes
-
-  function rebaseHistSelSingle(pos, from, to, diff) {
-    if (to < pos.line) {
-      pos.line += diff;
-    } else if (from < pos.line) {
-      pos.line = from;
-      pos.ch = 0;
-    }
-  }
-
-  // Tries to rebase an array of history events given a change in the
-  // document. If the change touches the same lines as the event, the
-  // event, and everything 'behind' it, is discarded. If the change is
-  // before the event, the event's positions are updated. Uses a
-  // copy-on-write scheme for the positions, to avoid having to
-  // reallocate them all on every rebase, but also avoid problems with
-  // shared position objects being unsafely updated.
-  function rebaseHistArray(array, from, to, diff) {
-    for (var i = 0; i < array.length; ++i) {
-      var sub = array[i], ok = true;
-      if (sub.ranges) {
-        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
-        for (var j = 0; j < sub.ranges.length; j++) {
-          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
-          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
-        }
-        continue;
-      }
-      for (var j = 0; j < sub.changes.length; ++j) {
-        var cur = sub.changes[j];
-        if (to < cur.from.line) {
-          cur.from = Pos(cur.from.line + diff, cur.from.ch);
-          cur.to = Pos(cur.to.line + diff, cur.to.ch);
-        } else if (from <= cur.to.line) {
-          ok = false;
-          break;
-        }
-      }
-      if (!ok) {
-        array.splice(0, i + 1);
-        i = 0;
-      }
-    }
-  }
-
-  function rebaseHist(hist, change) {
-    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
-    rebaseHistArray(hist.done, from, to, diff);
-    rebaseHistArray(hist.undone, from, to, diff);
-  }
-
-  // EVENT UTILITIES
-
-  // Due to the fact that we still support jurassic IE versions, some
-  // compatibility wrappers are needed.
-
-  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
-    if (e.preventDefault) e.preventDefault();
-    else e.returnValue = false;
-  };
-  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
-    if (e.stopPropagation) e.stopPropagation();
-    else e.cancelBubble = true;
-  };
-  function e_defaultPrevented(e) {
-    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
-  }
-  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
-
-  function e_target(e) {return e.target || e.srcElement;}
-  function e_button(e) {
-    var b = e.which;
-    if (b == null) {
-      if (e.button & 1) b = 1;
-      else if (e.button & 2) b = 3;
-      else if (e.button & 4) b = 2;
-    }
-    if (mac && e.ctrlKey && b == 1) b = 3;
-    return b;
-  }
-
-  // EVENT HANDLING
-
-  // Lightweight event framework. on/off also work on DOM nodes,
-  // registering native DOM handlers.
-
-  var on = CodeMirror.on = function(emitter, type, f) {
-    if (emitter.addEventListener)
-      emitter.addEventListener(type, f, false);
-    else if (emitter.attachEvent)
-      emitter.attachEvent("on" + type, f);
-    else {
-      var map = emitter._handlers || (emitter._handlers = {});
-      var arr = map[type] || (map[type] = []);
-      arr.push(f);
-    }
-  };
-
-  var off = CodeMirror.off = function(emitter, type, f) {
-    if (emitter.removeEventListener)
-      emitter.removeEventListener(type, f, false);
-    else if (emitter.detachEvent)
-      emitter.detachEvent("on" + type, f);
-    else {
-      var arr = emitter._handlers && emitter._handlers[type];
-      if (!arr) return;
-      for (var i = 0; i < arr.length; ++i)
-        if (arr[i] == f) { arr.splice(i, 1); break; }
-    }
-  };
-
-  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
-    var arr = emitter._handlers && emitter._handlers[type];
-    if (!arr) return;
-    var args = Array.prototype.slice.call(arguments, 2);
-    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
-  };
-
-  var orphanDelayedCallbacks = null;
-
-  // Often, we want to signal events at a point where we are in the
-  // middle of some work, but don't want the handler to start calling
-  // other methods on the editor, which might be in an inconsistent
-  // state or simply not expect any other events to happen.
-  // signalLater looks whether there are any handlers, and schedules
-  // them to be executed when the last operation ends, or, if no
-  // operation is active, when a timeout fires.
-  function signalLater(emitter, type /*, values...*/) {
-    var arr = emitter._handlers && emitter._handlers[type];
-    if (!arr) return;
-    var args = Array.prototype.slice.call(arguments, 2), list;
-    if (operationGroup) {
-      list = operationGroup.delayedCallbacks;
-    } else if (orphanDelayedCallbacks) {
-      list = orphanDelayedCallbacks;
-    } else {
-      list = orphanDelayedCallbacks = [];
-      setTimeout(fireOrphanDelayed, 0);
-    }
-    function bnd(f) {return function(){f.apply(null, args);};};
-    for (var i = 0; i < arr.length; ++i)
-      list.push(bnd(arr[i]));
-  }
-
-  function fireOrphanDelayed() {
-    var delayed = orphanDelayedCallbacks;
-    orphanDelayedCallbacks = null;
-    for (var i = 0; i < delayed.length; ++i) delayed[i]();
-  }
-
-  // The DOM events that CodeMirror handles can be overridden by
-  // registering a (non-DOM) handler on the editor for the event name,
-  // and preventDefault-ing the event in that handler.
-  function signalDOMEvent(cm, e, override) {
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore;
-  }
-
-  function signalCursorActivity(cm) {
-    var arr = cm._handlers && cm._handlers.cursorActivity;
-    if (!arr) return;
-    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
-    for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
-      set.push(arr[i]);
-  }
-
-  function hasHandler(emitter, type) {
-    var arr = emitter._handlers && emitter._handlers[type];
-    return arr && arr.length > 0;
-  }
-
-  // Add on and off methods to a constructor's prototype, to make
-  // registering events on such objects more convenient.
-  function eventMixin(ctor) {
-    ctor.prototype.on = function(type, f) {on(this, type, f);};
-    ctor.prototype.off = function(type, f) {off(this, type, f);};
-  }
-
-  // MISC UTILITIES
-
-  // Number of pixels added to scroller and sizer to hide scrollbar
-  var scrollerCutOff = 30;
-
-  // Returned or thrown by various protocols to signal 'I'm not
-  // handling this'.
-  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
-
-  // Reused option objects for setSelection & friends
-  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
-
-  function Delayed() {this.id = null;}
-  Delayed.prototype.set = function(ms, f) {
-    clearTimeout(this.id);
-    this.id = setTimeout(f, ms);
-  };
-
-  // Counts the column offset in a string, taking tabs into account.
-  // Used mostly to find indentation.
-  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
-    if (end == null) {
-      end = string.search(/[^\s\u00a0]/);
-      if (end == -1) end = string.length;
-    }
-    for (var i = startIndex || 0, n = startValue || 0;;) {
-      var nextTab = string.indexOf("\t", i);
-      if (nextTab < 0 || nextTab >= end)
-        return n + (end - i);
-      n += nextTab - i;
-      n += tabSize - (n % tabSize);
-      i = nextTab + 1;
-    }
-  };
-
-  // The inverse of countColumn -- find the offset that corresponds to
-  // a particular column.
-  function findColumn(string, goal, tabSize) {
-    for (var pos = 0, col = 0;;) {
-      var nextTab = string.indexOf("\t", pos);
-      if (nextTab == -1) nextTab = string.length;
-      var skipped = nextTab - pos;
-      if (nextTab == string.length || col + skipped >= goal)
-        return pos + Math.min(skipped, goal - col);
-      col += nextTab - pos;
-      col += tabSize - (col % tabSize);
-      pos = nextTab + 1;
-      if (col >= goal) return pos;
-    }
-  }
-
-  var spaceStrs = [""];
-  function spaceStr(n) {
-    while (spaceStrs.length <= n)
-      spaceStrs.push(lst(spaceStrs) + " ");
-    return spaceStrs[n];
-  }
-
-  function lst(arr) { return arr[arr.length-1]; }
-
-  var selectInput = function(node) { node.select(); };
-  if (ios) // Mobile Safari apparently has a bug where select() is broken.
-    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
-  else if (ie) // Suppress mysterious IE10 errors
-    selectInput = function(node) { try { node.select(); } catch(_e) {} };
-
-  function indexOf(array, elt) {
-    for (var i = 0; i < array.length; ++i)
-      if (array[i] == elt) return i;
-    return -1;
-  }
-  if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); };
-  function map(array, f) {
-    var out = [];
-    for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
-    return out;
-  }
-  if ([].map) map = function(array, f) { return array.map(f); };
-
-  function createObj(base, props) {
-    var inst;
-    if (Object.create) {
-      inst = Object.create(base);
-    } else {
-      var ctor = function() {};
-      ctor.prototype = base;
-      inst = new ctor();
-    }
-    if (props) copyObj(props, inst);
-    return inst;
-  };
-
-  function copyObj(obj, target, overwrite) {
-    if (!target) target = {};
-    for (var prop in obj)
-      if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
-        target[prop] = obj[prop];
-    return target;
-  }
-
-  function bind(f) {
-    var args = Array.prototype.slice.call(arguments, 1);
-    return function(){return f.apply(null, args);};
-  }
-
-  var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
-    return /\w/.test(ch) || ch > "\x80" &&
-      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
-  };
-  function isWordChar(ch, helper) {
-    if (!helper) return isWordCharBasic(ch);
-    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
-    return helper.test(ch);
-  }
-
-  function isEmpty(obj) {
-    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
-    return true;
-  }
-
-  // Extending unicode characters. A series of a non-extending char +
-  // any number of extending chars is treated as a single unit as far
-  // as editing and measuring is concerned. This is not fully correct,
-  // since some scripts/fonts/browsers also treat other configurations
-  // of code points as a group.
-  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
-  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
-
-  // DOM UTILITIES
-
-  function elt(tag, content, className, style) {
-    var e = document.createElement(tag);
-    if (className) e.className = className;
-    if (style) e.style.cssText = style;
-    if (typeof content == "string") e.appendChild(document.createTextNode(content));
-    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
-    return e;
-  }
-
-  var range;
-  if (document.createRange) range = function(node, start, end) {
-    var r = document.createRange();
-    r.setEnd(node, end);
-    r.setStart(node, start);
-    return r;
-  };
-  else range = function(node, start, end) {
-    var r = document.body.createTextRange();
-    r.moveToElementText(node.parentNode);
-    r.collapse(true);
-    r.moveEnd("character", end);
-    r.moveStart("character", start);
-    return r;
-  };
-
-  function removeChildren(e) {
-    for (var count = e.childNodes.length; count > 0; --count)
-      e.removeChild(e.firstChild);
-    return e;
-  }
-
-  function removeChildrenAndAdd(parent, e) {
-    return removeChildren(parent).appendChild(e);
-  }
-
-  function contains(parent, child) {
-    if (parent.contains)
-      return parent.contains(child);
-    while (child = child.parentNode)
-      if (child == parent) return true;
-  }
-
-  function activeElt() { return document.activeElement; }
-  // Older versions of IE throws unspecified error when touching
-  // document.activeElement in some cases (during loading, in iframe)
-  if (ie && ie_version < 11) activeElt = function() {
-    try { return document.activeElement; }
-    catch(e) { return document.body; }
-  };
-
-  function classTest(cls) { return new RegExp("\\b" + cls + "\\b\\s*"); }
-  function rmClass(node, cls) {
-    var test = classTest(cls);
-    if (test.test(node.className)) node.className = node.className.replace(test, "");
-  }
-  function addClass(node, cls) {
-    if (!classTest(cls).test(node.className)) node.className += " " + cls;
-  }
-  function joinClasses(a, b) {
-    var as = a.split(" ");
-    for (var i = 0; i < as.length; i++)
-      if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
-    return b;
-  }
-
-  // WINDOW-WIDE EVENTS
-
-  // These must be handled carefully, because naively registering a
-  // handler for each editor will cause the editors to never be
-  // garbage collected.
-
-  function forEachCodeMirror(f) {
-    if (!document.body.getElementsByClassName) return;
-    var byClass = document.body.getElementsByClassName("CodeMirror");
-    for (var i = 0; i < byClass.length; i++) {
-      var cm = byClass[i].CodeMirror;
-      if (cm) f(cm);
-    }
-  }
-
-  var globalsRegistered = false;
-  function ensureGlobalHandlers() {
-    if (globalsRegistered) return;
-    registerGlobalHandlers();
-    globalsRegistered = true;
-  }
-  function registerGlobalHandlers() {
-    // When the window resizes, we need to refresh active editors.
-    var resizeTimer;
-    on(window, "resize", function() {
-      if (resizeTimer == null) resizeTimer = setTimeout(function() {
-        resizeTimer = null;
-        knownScrollbarWidth = null;
-        forEachCodeMirror(onResize);
-      }, 100);
-    });
-    // When the window loses focus, we want to show the editor as blurred
-    on(window, "blur", function() {
-      forEachCodeMirror(onBlur);
-    });
-  }
-
-  // FEATURE DETECTION
-
-  // Detect drag-and-drop
-  var dragAndDrop = function() {
-    // There is *some* kind of drag-and-drop support in IE6-8, but I
-    // couldn't get it to work yet.
-    if (ie && ie_version < 9) return false;
-    var div = elt('div');
-    return "draggable" in div || "dragDrop" in div;
-  }();
-
-  var knownScrollbarWidth;
-  function scrollbarWidth(measure) {
-    if (knownScrollbarWidth != null) return knownScrollbarWidth;
-    var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
-    removeChildrenAndAdd(measure, test);
-    if (test.offsetWidth)
-      knownScrollbarWidth = test.offsetHeight - test.clientHeight;
-    return knownScrollbarWidth || 0;
-  }
-
-  var zwspSupported;
-  function zeroWidthElement(measure) {
-    if (zwspSupported == null) {
-      var test = elt("span", "\u200b");
-      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
-      if (measure.firstChild.offsetHeight != 0)
-        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
-    }
-    if (zwspSupported) return elt("span", "\u200b");
-    else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
-  }
-
-  // Feature-detect IE's crummy client rect reporting for bidi text
-  var badBidiRects;
-  function hasBadBidiRects(measure) {
-    if (badBidiRects != null) return badBidiRects;
-    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
-    var r0 = range(txt, 0, 1).getBoundingClientRect();
-    if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
-    var r1 = range(txt, 1, 2).getBoundingClientRect();
-    return badBidiRects = (r1.right - r0.right < 3);
-  }
-
-  // See if "".split is the broken IE version, if so, provide an
-  // alternative way to split lines.
-  var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
-    var pos = 0, result = [], l = string.length;
-    while (pos <= l) {
-      var nl = string.indexOf("\n", pos);
-      if (nl == -1) nl = string.length;
-      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
-      var rt = line.indexOf("\r");
-      if (rt != -1) {
-        result.push(line.slice(0, rt));
-        pos += rt + 1;
-      } else {
-        result.push(line);
-        pos = nl + 1;
-      }
-    }
-    return result;
-  } : function(string){return string.split(/\r\n?|\n/);};
-
-  var hasSelection = window.getSelection ? function(te) {
-    try { return te.selectionStart != te.selectionEnd; }
-    catch(e) { return false; }
-  } : function(te) {
-    try {var range = te.ownerDocument.selection.createRange();}
-    catch(e) {}
-    if (!range || range.parentElement() != te) return false;
-    return range.compareEndPoints("StartToEnd", range) != 0;
-  };
-
-  var hasCopyEvent = (function() {
-    var e = elt("div");
-    if ("oncopy" in e) return true;
-    e.setAttribute("oncopy", "return;");
-    return typeof e.oncopy == "function";
-  })();
-
-  var badZoomedRects = null;
-  function hasBadZoomedRects(measure) {
-    if (badZoomedRects != null) return badZoomedRects;
-    var node = removeChildrenAndAdd(measure, elt("span", "x"));
-    var normal = node.getBoundingClientRect();
-    var fromRange = range(node, 0, 1).getBoundingClientRect();
-    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
-  }
-
-  // KEY NAMES
-
-  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
-                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
-                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
-                  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
-                  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
-                  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
-                  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
-  CodeMirror.keyNames = keyNames;
-  (function() {
-    // Number keys
-    for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
-    // Alphabetic keys
-    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
-    // Function keys
-    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
-  })();
-
-  // BIDI HELPERS
-
-  function iterateBidiSections(order, from, to, f) {
-    if (!order) return f(from, to, "ltr");
-    var found = false;
-    for (var i = 0; i < order.length; ++i) {
-      var part = order[i];
-      if (part.from < to && part.to > from || from == to && part.to == from) {
-        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
-        found = true;
-      }
-    }
-    if (!found) f(from, to, "ltr");
-  }
-
-  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
-  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
-
-  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
-  function lineRight(line) {
-    var order = getOrder(line);
-    if (!order) return line.text.length;
-    return bidiRight(lst(order));
-  }
-
-  function lineStart(cm, lineN) {
-    var line = getLine(cm.doc, lineN);
-    var visual = visualLine(line);
-    if (visual != line) lineN = lineNo(visual);
-    var order = getOrder(visual);
-    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
-    return Pos(lineN, ch);
-  }
-  function lineEnd(cm, lineN) {
-    var merged, line = getLine(cm.doc, lineN);
-    while (merged = collapsedSpanAtEnd(line)) {
-      line = merged.find(1, true).line;
-      lineN = null;
-    }
-    var order = getOrder(line);
-    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
-    return Pos(lineN == null ? lineNo(line) : lineN, ch);
-  }
-  function lineStartSmart(cm, pos) {
-    var start = lineStart(cm, pos.line);
-    var line = getLine(cm.doc, start.line);
-    var order = getOrder(line);
-    if (!order || order[0].level == 0) {
-      var firstNonWS = Math.max(0, line.text.search(/\S/));
-      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
-      return Pos(start.line, inWS ? 0 : firstNonWS);
-    }
-    return start;
-  }
-
-  function compareBidiLevel(order, a, b) {
-    var linedir = order[0].level;
-    if (a == linedir) return true;
-    if (b == linedir) return false;
-    return a < b;
-  }
-  var bidiOther;
-  function getBidiPartAt(order, pos) {
-    bidiOther = null;
-    for (var i = 0, found; i < order.length; ++i) {
-      var cur = order[i];
-      if (cur.from < pos && cur.to > pos) return i;
-      if ((cur.from == pos || cur.to == pos)) {
-        if (found == null) {
-          found = i;
-        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
-          if (cur.from != cur.to) bidiOther = found;
-          return i;
-        } else {
-          if (cur.from != cur.to) bidiOther = i;
-          return found;
-        }
-      }
-    }
-    return found;
-  }
-
-  function moveInLine(line, pos, dir, byUnit) {
-    if (!byUnit) return pos + dir;
-    do pos += dir;
-    while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
-    return pos;
-  }
-
-  // This is needed in order to move 'visually' through bi-directional
-  // text -- i.e., pressing left should make the cursor go left, even
-  // when in RTL text. The tricky part is the 'jumps', where RTL and
-  // LTR text touch each other. This often requires the cursor offset
-  // to move more than one unit, in order to visually move one unit.
-  function moveVisually(line, start, dir, byUnit) {
-    var bidi = getOrder(line);
-    if (!bidi) return moveLogically(line, start, dir, byUnit);
-    var pos = getBidiPartAt(bidi, start), part = bidi[pos];
-    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
-
-    for (;;) {
-      if (target > part.from && target < part.to) return target;
-      if (target == part.from || target == part.to) {
-        if (getBidiPartAt(bidi, target) == pos) return target;
-        part = bidi[pos += dir];
-        return (dir > 0) == part.level % 2 ? part.to : part.from;
-      } else {
-        part = bidi[pos += dir];
-        if (!part) return null;
-        if ((dir > 0) == part.level % 2)
-          target = moveInLine(line, part.to, -1, byUnit);
-        else
-          target = moveInLine(line, part.from, 1, byUnit);
-      }
-    }
-  }
-
-  function moveLogically(line, start, dir, byUnit) {
-    var target = start + dir;
-    if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
-    return target < 0 || target > line.text.length ? null : target;
-  }
-
-  // Bidirectional ordering algorithm
-  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
-  // that this (partially) implements.
-
-  // One-char codes used for character types:
-  // L (L):   Left-to-Right
-  // R (R):   Right-to-Left
-  // r (AL):  Right-to-Left Arabic
-  // 1 (EN):  European Number
-  // + (ES):  European Number Separator
-  // % (ET):  European Number Terminator
-  // n (AN):  Arabic Number
-  // , (CS):  Common Number Separator
-  // m (NSM): Non-Spacing Mark
-  // b (BN):  Boundary Neutral
-  // s (B):   Paragraph Separator
-  // t (S):   Segment Separator
-  // w (WS):  Whitespace
-  // N (ON):  Other Neutrals
-
-  // Returns null if characters are ordered as they appear
-  // (left-to-right), or an array of sections ({from, to, level}
-  // objects) in the order in which they occur visually.
-  var bidiOrdering = (function() {
-    // Character types for codepoints 0 to 0xff
-    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
-    // Character types for codepoints 0x600 to 0x6ff
-    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
-    function charType(code) {
-      if (code <= 0xf7) return lowTypes.charAt(code);
-      else if (0x590 <= code && code <= 0x5f4) return "R";
-      else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
-      else if (0x6ee <= code && code <= 0x8ac) return "r";
-      else if (0x2000 <= code && code <= 0x200b) return "w";
-      else if (code == 0x200c) return "b";
-      else return "L";
-    }
-
-    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
-    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
-    // Browsers seem to always treat the boundaries of block elements as being L.
-    var outerType = "L";
-
-    function BidiSpan(level, from, to) {
-      this.level = level;
-      this.from = from; this.to = to;
-    }
-
-    return function(str) {
-      if (!bidiRE.test(str)) return false;
-      var len = str.length, types = [];
-      for (var i = 0, type; i < len; ++i)
-        types.push(type = charType(str.charCodeAt(i)));
-
-      // W1. Examine each non-spacing mark (NSM) in the level run, and
-      // change the type of the NSM to the type of the previous
-      // character. If the NSM is at the start of the level run, it will
-      // get the type of sor.
-      for (var i = 0, prev = outerType; i < len; ++i) {
-        var type = types[i];
-        if (type == "m") types[i] = prev;
-        else prev = type;
-      }
-
-      // W2. Search backwards from each instance of a European number
-      // until the first strong type (R, L, AL, or sor) is found. If an
-      // AL is found, change the type of the European number to Arabic
-      // number.
-      // W3. Change all ALs to R.
-      for (var i = 0, cur = outerType; i < len; ++i) {
-        var type = types[i];
-        if (type == "1" && cur == "r") types[i] = "n";
-        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
-      }
-
-      // W4. A single European separator between two European numbers
-      // changes to a European number. A single common separator between
-      // two numbers of the same type changes to that type.
-      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
-        var type = types[i];
-        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
-        else if (type == "," && prev == types[i+1] &&
-                 (prev == "1" || prev == "n")) types[i] = prev;
-        prev = type;
-      }
-
-      // W5. A sequence of European terminators adjacent to European
-      // numbers changes to all European numbers.
-      // W6. Otherwise, separators and terminators change to Other
-      // Neutral.
-      for (var i = 0; i < len; ++i) {
-        var type = types[i];
-        if (type == ",") types[i] = "N";
-        else if (type == "%") {
-          for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
-          var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
-          for (var j = i; j < end; ++j) types[j] = replace;
-          i = end - 1;
-        }
-      }
-
-      // W7. Search backwards from each instance of a European number
-      // until the first strong type (R, L, or sor) is found. If an L is
-      // found, then change the type of the European number to L.
-      for (var i = 0, cur = outerType; i < len; ++i) {
-        var type = types[i];
-        if (cur == "L" && type == "1") types[i] = "L";
-        else if (isStrong.test(type)) cur = type;
-      }
-
-      // N1. A sequence of neutrals takes the direction of the
-      // surrounding strong text if the text on both sides has the same
-      // direction. European and Arabic numbers act as if they were R in
-      // terms of their influence on neutrals. Start-of-level-run (sor)
-      // and end-of-level-run (eor) are used at level run boundaries.
-      // N2. Any remaining neutrals take the embedding direction.
-      for (var i = 0; i < len; ++i) {
-        if (isNeutral.test(types[i])) {
-          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
-          var before = (i ? types[i-1] : outerType) == "L";
-          var after = (end < len ? types[end] : outerType) == "L";
-          var replace = before || after ? "L" : "R";
-          for (var j = i; j < end; ++j) types[j] = replace;
-          i = end - 1;
-        }
-      }
-
-      // Here we depart from the documented algorithm, in order to avoid
-      // building up an actual levels array. Since there are only three
-      // levels (0, 1, 2) in an implementation that doesn't take
-      // explicit embedding into account, we can build up the order on
-      // the fly, without following the level-based algorithm.
-      var order = [], m;
-      for (var i = 0; i < len;) {
-        if (countsAsLeft.test(types[i])) {
-          var start = i;
-          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
-          order.push(new BidiSpan(0, start, i));
-        } else {
-          var pos = i, at = order.length;
-          for (++i; i < len && types[i] != "L"; ++i) {}
-          for (var j = pos; j < i;) {
-            if (countsAsNum.test(types[j])) {
-              if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
-              var nstart = j;
-              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
-              order.splice(at, 0, new BidiSpan(2, nstart, j));
-              pos = j;
-            } else ++j;
-          }
-          if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
-        }
-      }
-      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
-        order[0].from = m[0].length;
-        order.unshift(new BidiSpan(0, 0, m[0].length));
-      }
-      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
-        lst(order).to -= m[0].length;
-        order.push(new BidiSpan(0, len - m[0].length, len));
-      }
-      if (order[0].level != lst(order).level)
-        order.push(new BidiSpan(order[0].level, len, len));
-
-      return order;
-    };
-  })();
-
-  // THE END
-
-  CodeMirror.version = "4.7.0";
-
-  return CodeMirror;
-});
diff --git a/apps/static/js/plugins/codemirror/mode/index.html b/apps/static/js/plugins/codemirror/mode/index.html
deleted file mode 100755
index bb656d2a7..000000000
--- a/apps/static/js/plugins/codemirror/mode/index.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!doctype html>
-
-<title>CodeMirror: Language Modes</title>
-<meta charset="utf-8"/>
-<link rel=stylesheet href="../doc/docs.css">
-
-<div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
-
-  <ul>
-    <li><a href="../index.html">Home</a>
-    <li><a href="../doc/manual.html">Manual</a>
-    <li><a href="https://github.com/codemirror/codemirror">Code</a>
-  </ul>
-  <ul>
-    <li><a class=active href="#">Language modes</a>
-  </ul>
-</div>
-
-<article>
-
-<h2>Language modes</h2>
-
-<p>This is a list of every mode in the distribution. Each mode lives
-in a subdirectory of the <code>mode/</code> directory, and typically
-defines a single JavaScript file that implements the mode. Loading
-such file will make the language available to CodeMirror, through
-the <a href="manual.html#option_mode"><code>mode</code></a>
-option.</p>
-
-<div style="-webkit-columns: 100px 2; -moz-columns: 100px 2; columns: 100px 2;">
-    <ul style="margin-top: 0">
-      <li><a href="apl/index.html">APL</a></li>
-      <li><a href="asterisk/index.html">Asterisk dialplan</a></li>
-      <li><a href="clike/index.html">C, C++, C#</a></li>
-      <li><a href="clojure/index.html">Clojure</a></li>
-      <li><a href="cobol/index.html">COBOL</a></li>
-      <li><a href="coffeescript/index.html">CoffeeScript</a></li>
-      <li><a href="commonlisp/index.html">Common Lisp</a></li>
-      <li><a href="css/index.html">CSS</a></li>
-      <li><a href="cypher/index.html">Cypher</a></li>
-      <li><a href="python/index.html">Cython</a></li>
-      <li><a href="d/index.html">D</a></li>
-      <li><a href="django/index.html">Django</a> (templating language)</li>
-      <li><a href="diff/index.html">diff</a></li>
-      <li><a href="dtd/index.html">DTD</a></li>
-      <li><a href="dylan/index.html">Dylan</a></li>
-      <li><a href="ecl/index.html">ECL</a></li>
-      <li><a href="eiffel/index.html">Eiffel</a></li>
-      <li><a href="erlang/index.html">Erlang</a></li>
-      <li><a href="fortran/index.html">Fortran</a></li>
-      <li><a href="mllike/index.html">F#</a></li>
-      <li><a href="gas/index.html">Gas</a> (AT&amp;T-style assembly)</li>
-      <li><a href="gherkin/index.html">Gherkin</a></li>
-      <li><a href="go/index.html">Go</a></li>
-      <li><a href="groovy/index.html">Groovy</a></li>
-      <li><a href="haml/index.html">HAML</a></li>
-      <li><a href="haskell/index.html">Haskell</a></li>
-      <li><a href="haxe/index.html">Haxe</a></li>
-      <li><a href="htmlembedded/index.html">HTML embedded scripts</a></li>
-      <li><a href="htmlmixed/index.html">HTML mixed-mode</a></li>
-      <li><a href="http/index.html">HTTP</a></li>
-      <li><a href="clike/index.html">Java</a></li>
-      <li><a href="jade/index.html">Jade</a></li>
-      <li><a href="javascript/index.html">JavaScript</a></li>
-      <li><a href="jinja2/index.html">Jinja2</a></li>
-      <li><a href="julia/index.html">Julia</a></li>
-      <li><a href="kotlin/index.html">Kotlin</a></li>
-      <li><a href="css/less.html">LESS</a></li>
-      <li><a href="livescript/index.html">LiveScript</a></li>
-      <li><a href="lua/index.html">Lua</a></li>
-      <li><a href="markdown/index.html">Markdown</a> (<a href="gfm/index.html">GitHub-flavour</a>)</li>
-      <li><a href="mirc/index.html">mIRC</a></li>
-      <li><a href="modelica/index.html">Modelica</a></li>
-      <li><a href="nginx/index.html">Nginx</a></li>
-      <li><a href="ntriples/index.html">NTriples</a></li>
-      <li><a href="mllike/index.html">OCaml</a></li>
-      <li><a href="octave/index.html">Octave</a> (MATLAB)</li>
-      <li><a href="pascal/index.html">Pascal</a></li>
-      <li><a href="pegjs/index.html">PEG.js</a></li>
-      <li><a href="perl/index.html">Perl</a></li>
-      <li><a href="php/index.html">PHP</a></li>
-      <li><a href="pig/index.html">Pig Latin</a></li>
-      <li><a href="properties/index.html">Properties files</a></li>
-      <li><a href="puppet/index.html">Puppet</a></li>
-      <li><a href="python/index.html">Python</a></li>
-      <li><a href="q/index.html">Q</a></li>
-      <li><a href="r/index.html">R</a></li>
-      <li><a href="rpm/index.html">RPM</a></li>
-      <li><a href="rst/index.html">reStructuredText</a></li>
-      <li><a href="ruby/index.html">Ruby</a></li>
-      <li><a href="rust/index.html">Rust</a></li>
-      <li><a href="sass/index.html">Sass</a></li>
-      <li><a href="clike/scala.html">Scala</a></li>
-      <li><a href="scheme/index.html">Scheme</a></li>
-      <li><a href="css/scss.html">SCSS</a></li>
-      <li><a href="shell/index.html">Shell</a></li>
-      <li><a href="sieve/index.html">Sieve</a></li>
-      <li><a href="slim/index.html">Slim</a></li>
-      <li><a href="smalltalk/index.html">Smalltalk</a></li>
-      <li><a href="smarty/index.html">Smarty</a></li>
-      <li><a href="smartymixed/index.html">Smarty/HTML mixed</a></li>
-      <li><a href="solr/index.html">Solr</a></li>
-      <li><a href="sql/index.html">SQL</a> (several dialects)</li>
-      <li><a href="sparql/index.html">SPARQL</a></li>
-      <li><a href="stex/index.html">sTeX, LaTeX</a></li>
-      <li><a href="tcl/index.html">Tcl</a></li>
-      <li><a href="textile/index.html">Textile</a></li>
-      <li><a href="tiddlywiki/index.html">Tiddlywiki</a></li>
-      <li><a href="tiki/index.html">Tiki wiki</a></li>
-      <li><a href="toml/index.html">TOML</a></li>
-      <li><a href="tornado/index.html">Tornado</a> (templating language)</li>
-      <li><a href="turtle/index.html">Turtle</a></li>
-      <li><a href="vb/index.html">VB.NET</a></li>
-      <li><a href="vbscript/index.html">VBScript</a></li>
-      <li><a href="velocity/index.html">Velocity</a></li>
-      <li><a href="verilog/index.html">Verilog/SystemVerilog</a></li>
-      <li><a href="xml/index.html">XML/HTML</a></li>
-      <li><a href="xquery/index.html">XQuery</a></li>
-      <li><a href="yaml/index.html">YAML</a></li>
-      <li><a href="z80/index.html">Z80</a></li>
-    </ul>
-  </div>
-
-</article>
diff --git a/apps/static/js/plugins/codemirror/mode/meta.js b/apps/static/js/plugins/codemirror/mode/meta.js
deleted file mode 100755
index cee33e542..000000000
--- a/apps/static/js/plugins/codemirror/mode/meta.js
+++ /dev/null
@@ -1,144 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-  "use strict";
-
-  CodeMirror.modeInfo = [
-    {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
-    {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk"},
-    {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
-    {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "hpp", "h++"]},
-    {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
-    {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"]},
-    {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
-    {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"]},
-    {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"]},
-    {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher"},
-    {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
-    {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
-    {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
-    {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
-    {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
-    {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
-    {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
-    {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
-    {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
-    {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
-    {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
-    {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
-    {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"]},
-    {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
-    {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
-    {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm"},
-    {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
-    {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
-    {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
-    {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
-    {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
-    {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
-    {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"]},
-    {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"]},
-    {name: "HTTP", mime: "message/http", mode: "http"},
-    {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
-    {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
-    {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"]},
-    {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
-     mode: "javascript", ext: ["js"]},
-    {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"]},
-    {name: "JSON-LD", mime: "application/ld+json", mode: "javascript"},
-    {name: "Jinja2", mime: "null", mode: "jinja2"},
-    {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
-    {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin", ext: ["kt"]},
-    {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
-    {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"]},
-    {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
-    {name: "Markdown (GitHub-flavour)", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
-    {name: "mIRC", mime: "text/mirc", mode: "mirc"},
-    {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
-    {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
-    {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
-    {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
-    {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx"},
-    {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
-    {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
-    {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
-    {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
-    {name: "PEG.js", mime: "null", mode: "pegjs"},
-    {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
-    {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
-    {name: "Pig", mime: "text/x-pig", mode: "pig"},
-    {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
-    {name: "PLSQL", mime: "text/x-plsql", mode: "sql"},
-    {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"]},
-    {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
-    {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
-    {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
-    {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"]},
-    {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"]},
-    {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"]},
-    {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
-    {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
-    {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
-    {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
-    {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
-    {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"]},
-    {name: "Sieve", mime: "application/sieve", mode: "sieve"},
-    {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim"},
-    {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
-    {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
-    {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"},
-    {name: "Solr", mime: "text/x-solr", mode: "solr"},
-    {name: "SPARQL", mime: "application/x-sparql-query", mode: "sparql", ext: ["sparql"]},
-    {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
-    {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
-    {name: "sTeX", mime: "text/x-stex", mode: "stex"},
-    {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"]},
-    {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
-    {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
-    {name: "Textile", mime: "text/x-textile", mode: "textile"},
-    {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
-    {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
-    {name: "TOML", mime: "text/x-toml", mode: "toml"},
-    {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
-    {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
-    {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"]},
-    {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
-    {name: "VBScript", mime: "text/vbscript", mode: "vbscript"},
-    {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
-    {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
-    {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"]},
-    {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
-    {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml"]},
-    {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}
-  ];
-  // Ensure all modes have a mime property for backwards compatibility
-  for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
-    var info = CodeMirror.modeInfo[i];
-    if (info.mimes) info.mime = info.mimes[0];
-  }
-
-  CodeMirror.findModeByMIME = function(mime) {
-    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
-      var info = CodeMirror.modeInfo[i];
-      if (info.mime == mime) return info;
-      if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
-        if (info.mimes[j] == mime) return info;
-    }
-  };
-
-  CodeMirror.findModeByExtension = function(ext) {
-    for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
-      var info = CodeMirror.modeInfo[i];
-      if (info.ext) for (var j = 0; j < info.ext.length; j++)
-        if (info.ext[j] == ext) return info;
-    }
-  };
-});
diff --git a/apps/static/js/plugins/codemirror/mode/shell/index.html b/apps/static/js/plugins/codemirror/mode/shell/index.html
deleted file mode 100755
index 0b56300b1..000000000
--- a/apps/static/js/plugins/codemirror/mode/shell/index.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!doctype html>
-
-<title>CodeMirror: Shell mode</title>
-<meta charset="utf-8"/>
-<link rel=stylesheet href="../../doc/docs.css">
-
-<link rel=stylesheet href=../../lib/codemirror.css>
-<script src=../../lib/codemirror.js></script>
-<script src="../../addon/edit/matchbrackets.js"></script>
-<script src=shell.js></script>
-<style type=text/css>
-  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
-</style>
-<div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
-
-  <ul>
-    <li><a href="../../index.html">Home</a>
-    <li><a href="../../doc/manual.html">Manual</a>
-    <li><a href="https://github.com/codemirror/codemirror">Code</a>
-  </ul>
-  <ul>
-    <li><a href="../index.html">Language modes</a>
-    <li><a class=active href="#">Shell</a>
-  </ul>
-</div>
-
-<article>
-<h2>Shell mode</h2>
-
-
-<textarea id=code>
-#!/bin/bash
-
-# clone the repository
-git clone http://github.com/garden/tree
-
-# generate HTTPS credentials
-cd tree
-openssl genrsa -aes256 -out https.key 1024
-openssl req -new -nodes -key https.key -out https.csr
-openssl x509 -req -days 365 -in https.csr -signkey https.key -out https.crt
-cp https.key{,.orig}
-openssl rsa -in https.key.orig -out https.key
-
-# start the server in HTTPS mode
-cd web
-sudo node ../server.js 443 'yes' &gt;&gt; ../node.log &amp;
-
-# here is how to stop the server
-for pid in `ps aux | grep 'node ../server.js' | awk '{print $2}'` ; do
-  sudo kill -9 $pid 2&gt; /dev/null
-done
-
-exit 0</textarea>
-
-<script>
-  var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
-    mode: 'shell',
-    lineNumbers: true,
-    matchBrackets: true
-  });
-</script>
-
-<p><strong>MIME types defined:</strong> <code>text/x-sh</code>.</p>
-</article>
diff --git a/apps/static/js/plugins/codemirror/mode/shell/shell.js b/apps/static/js/plugins/codemirror/mode/shell/shell.js
deleted file mode 100755
index 8e31f6f30..000000000
--- a/apps/static/js/plugins/codemirror/mode/shell/shell.js
+++ /dev/null
@@ -1,138 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-(function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
-  else // Plain browser env
-    mod(CodeMirror);
-})(function(CodeMirror) {
-"use strict";
-
-CodeMirror.defineMode('shell', function() {
-
-  var words = {};
-  function define(style, string) {
-    var split = string.split(' ');
-    for(var i = 0; i < split.length; i++) {
-      words[split[i]] = style;
-    }
-  };
-
-  // Atoms
-  define('atom', 'true false');
-
-  // Keywords
-  define('keyword', 'if then do else elif while until for in esac fi fin ' +
-    'fil done exit set unset export function');
-
-  // Commands
-  define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' +
-    'curl cut diff echo find gawk gcc get git grep kill killall ln ls make ' +
-    'mkdir openssl mv nc node npm ping ps restart rm rmdir sed service sh ' +
-    'shopt shred source sort sleep ssh start stop su sudo tee telnet top ' +
-    'touch vi vim wall wc wget who write yes zsh');
-
-  function tokenBase(stream, state) {
-    if (stream.eatSpace()) return null;
-
-    var sol = stream.sol();
-    var ch = stream.next();
-
-    if (ch === '\\') {
-      stream.next();
-      return null;
-    }
-    if (ch === '\'' || ch === '"' || ch === '`') {
-      state.tokens.unshift(tokenString(ch));
-      return tokenize(stream, state);
-    }
-    if (ch === '#') {
-      if (sol && stream.eat('!')) {
-        stream.skipToEnd();
-        return 'meta'; // 'comment'?
-      }
-      stream.skipToEnd();
-      return 'comment';
-    }
-    if (ch === '$') {
-      state.tokens.unshift(tokenDollar);
-      return tokenize(stream, state);
-    }
-    if (ch === '+' || ch === '=') {
-      return 'operator';
-    }
-    if (ch === '-') {
-      stream.eat('-');
-      stream.eatWhile(/\w/);
-      return 'attribute';
-    }
-    if (/\d/.test(ch)) {
-      stream.eatWhile(/\d/);
-      if(stream.eol() || !/\w/.test(stream.peek())) {
-        return 'number';
-      }
-    }
-    stream.eatWhile(/[\w-]/);
-    var cur = stream.current();
-    if (stream.peek() === '=' && /\w+/.test(cur)) return 'def';
-    return words.hasOwnProperty(cur) ? words[cur] : null;
-  }
-
-  function tokenString(quote) {
-    return function(stream, state) {
-      var next, end = false, escaped = false;
-      while ((next = stream.next()) != null) {
-        if (next === quote && !escaped) {
-          end = true;
-          break;
-        }
-        if (next === '$' && !escaped && quote !== '\'') {
-          escaped = true;
-          stream.backUp(1);
-          state.tokens.unshift(tokenDollar);
-          break;
-        }
-        escaped = !escaped && next === '\\';
-      }
-      if (end || !escaped) {
-        state.tokens.shift();
-      }
-      return (quote === '`' || quote === ')' ? 'quote' : 'string');
-    };
-  };
-
-  var tokenDollar = function(stream, state) {
-    if (state.tokens.length > 1) stream.eat('$');
-    var ch = stream.next(), hungry = /\w/;
-    if (ch === '{') hungry = /[^}]/;
-    if (ch === '(') {
-      state.tokens[0] = tokenString(')');
-      return tokenize(stream, state);
-    }
-    if (!/\d/.test(ch)) {
-      stream.eatWhile(hungry);
-      stream.eat('}');
-    }
-    state.tokens.shift();
-    return 'def';
-  };
-
-  function tokenize(stream, state) {
-    return (state.tokens[0] || tokenBase) (stream, state);
-  };
-
-  return {
-    startState: function() {return {tokens:[]};},
-    token: function(stream, state) {
-      return tokenize(stream, state);
-    },
-    lineComment: '#'
-  };
-});
-
-CodeMirror.defineMIME('text/x-sh', 'shell');
-
-});
diff --git a/apps/static/js/plugins/codemirror/mode/shell/test.js b/apps/static/js/plugins/codemirror/mode/shell/test.js
deleted file mode 100755
index a413b5a40..000000000
--- a/apps/static/js/plugins/codemirror/mode/shell/test.js
+++ /dev/null
@@ -1,58 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-(function() {
-  var mode = CodeMirror.getMode({}, "shell");
-  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
-
-  MT("var",
-     "text [def $var] text");
-  MT("varBraces",
-     "text[def ${var}]text");
-  MT("varVar",
-     "text [def $a$b] text");
-  MT("varBracesVarBraces",
-     "text[def ${a}${b}]text");
-
-  MT("singleQuotedVar",
-     "[string 'text $var text']");
-  MT("singleQuotedVarBraces",
-     "[string 'text ${var} text']");
-
-  MT("doubleQuotedVar",
-     '[string "text ][def $var][string  text"]');
-  MT("doubleQuotedVarBraces",
-     '[string "text][def ${var}][string text"]');
-  MT("doubleQuotedVarPunct",
-     '[string "text ][def $@][string  text"]');
-  MT("doubleQuotedVarVar",
-     '[string "][def $a$b][string "]');
-  MT("doubleQuotedVarBracesVarBraces",
-     '[string "][def ${a}${b}][string "]');
-
-  MT("notAString",
-     "text\\'text");
-  MT("escapes",
-     "outside\\'\\\"\\`\\\\[string \"inside\\`\\'\\\"\\\\`\\$notAVar\"]outside\\$\\(notASubShell\\)");
-
-  MT("subshell",
-     "[builtin echo] [quote $(whoami)] s log, stardate [quote `date`].");
-  MT("doubleQuotedSubshell",
-     "[builtin echo] [string \"][quote $(whoami)][string 's log, stardate `date`.\"]");
-
-  MT("hashbang",
-     "[meta #!/bin/bash]");
-  MT("comment",
-     "text [comment # Blurb]");
-
-  MT("numbers",
-     "[number 0] [number 1] [number 2]");
-  MT("keywords",
-     "[keyword while] [atom true]; [keyword do]",
-     "  [builtin sleep] [number 3]",
-     "[keyword done]");
-  MT("options",
-     "[builtin ls] [attribute -l] [attribute --human-readable]");
-  MT("operator",
-     "[def var][operator =]value");
-})();
diff --git a/apps/static/js/plugins/daterangepicker/daterangepicker.min.js b/apps/static/js/plugins/daterangepicker/daterangepicker.min.js
deleted file mode 100644
index 32b0e7571..000000000
--- a/apps/static/js/plugins/daterangepicker/daterangepicker.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Minified by jsDelivr using UglifyJS v3.4.5.
- * Original file: /npm/daterangepicker@3.0.3/daterangepicker.js
- *
- * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
- */
-!function(t,a){if("function"==typeof define&&define.amd)define(["moment","jquery"],function(t,e){return e.fn||(e.fn={}),a(t,e)});else if("object"==typeof module&&module.exports){var e="undefined"!=typeof window?window.jQuery:void 0;e||(e=require("jquery")).fn||(e.fn={});var i="undefined"!=typeof window&&void 0!==window.moment?window.moment:require("moment");module.exports=a(i,e)}else t.daterangepicker=a(t.moment,t.jQuery)}(this,function(H,R){var i=function(t,e,a){if(this.parentEl="body",this.element=R(t),this.startDate=H().startOf("day"),this.endDate=H().endOf("day"),this.minDate=!1,this.maxDate=!1,this.maxSpan=!1,this.autoApply=!1,this.singleDatePicker=!1,this.showDropdowns=!1,this.minYear=H().subtract(100,"year").format("YYYY"),this.maxYear=H().add(100,"year").format("YYYY"),this.showWeekNumbers=!1,this.showISOWeekNumbers=!1,this.showCustomRangeLabel=!0,this.timePicker=!1,this.timePicker24Hour=!1,this.timePickerIncrement=1,this.timePickerSeconds=!1,this.linkedCalendars=!0,this.autoUpdateInput=!0,this.alwaysShowCalendars=!1,this.ranges={},this.opens="right",this.element.hasClass("pull-right")&&(this.opens="left"),this.drops="down",this.element.hasClass("dropup")&&(this.drops="up"),this.buttonClasses="btn btn-sm",this.applyButtonClasses="btn-primary",this.cancelButtonClasses="btn-default",this.locale={direction:"ltr",format:H.localeData().longDateFormat("L"),separator:" - ",applyLabel:"Apply",cancelLabel:"Cancel",weekLabel:"W",customRangeLabel:"Custom Range",daysOfWeek:H.weekdaysMin(),monthNames:H.monthsShort(),firstDay:H.localeData().firstDayOfWeek()},this.callback=function(){},this.isShowing=!1,this.leftCalendar={},this.rightCalendar={},"object"==typeof e&&null!==e||(e={}),"string"==typeof(e=R.extend(this.element.data(),e)).template||e.template instanceof R||(e.template='<div class="daterangepicker"><div class="ranges"></div><div class="drp-calendar left"><div class="calendar-table"></div><div class="calendar-time"></div></div><div class="drp-calendar right"><div class="calendar-table"></div><div class="calendar-time"></div></div><div class="drp-buttons"><span class="drp-selected"></span><button class="cancelBtn" type="button"></button><button class="applyBtn" disabled="disabled" type="button"></button> </div></div>'),this.parentEl=e.parentEl&&R(e.parentEl).length?R(e.parentEl):R(this.parentEl),this.container=R(e.template).appendTo(this.parentEl),"object"==typeof e.locale&&("string"==typeof e.locale.direction&&(this.locale.direction=e.locale.direction),"string"==typeof e.locale.format&&(this.locale.format=e.locale.format),"string"==typeof e.locale.separator&&(this.locale.separator=e.locale.separator),"object"==typeof e.locale.daysOfWeek&&(this.locale.daysOfWeek=e.locale.daysOfWeek.slice()),"object"==typeof e.locale.monthNames&&(this.locale.monthNames=e.locale.monthNames.slice()),"number"==typeof e.locale.firstDay&&(this.locale.firstDay=e.locale.firstDay),"string"==typeof e.locale.applyLabel&&(this.locale.applyLabel=e.locale.applyLabel),"string"==typeof e.locale.cancelLabel&&(this.locale.cancelLabel=e.locale.cancelLabel),"string"==typeof e.locale.weekLabel&&(this.locale.weekLabel=e.locale.weekLabel),"string"==typeof e.locale.customRangeLabel)){(d=document.createElement("textarea")).innerHTML=e.locale.customRangeLabel;var i=d.value;this.locale.customRangeLabel=i}if(this.container.addClass(this.locale.direction),"string"==typeof e.startDate&&(this.startDate=H(e.startDate,this.locale.format)),"string"==typeof e.endDate&&(this.endDate=H(e.endDate,this.locale.format)),"string"==typeof e.minDate&&(this.minDate=H(e.minDate,this.locale.format)),"string"==typeof e.maxDate&&(this.maxDate=H(e.maxDate,this.locale.format)),"object"==typeof e.startDate&&(this.startDate=H(e.startDate)),"object"==typeof e.endDate&&(this.endDate=H(e.endDate)),"object"==typeof e.minDate&&(this.minDate=H(e.minDate)),"object"==typeof e.maxDate&&(this.maxDate=H(e.maxDate)),this.minDate&&this.startDate.isBefore(this.minDate)&&(this.startDate=this.minDate.clone()),this.maxDate&&this.endDate.isAfter(this.maxDate)&&(this.endDate=this.maxDate.clone()),"string"==typeof e.applyButtonClasses&&(this.applyButtonClasses=e.applyButtonClasses),"string"==typeof e.applyClass&&(this.applyButtonClasses=e.applyClass),"string"==typeof e.cancelButtonClasses&&(this.cancelButtonClasses=e.cancelButtonClasses),"string"==typeof e.cancelClass&&(this.cancelButtonClasses=e.cancelClass),"object"==typeof e.maxSpan&&(this.maxSpan=e.maxSpan),"object"==typeof e.dateLimit&&(this.maxSpan=e.dateLimit),"string"==typeof e.opens&&(this.opens=e.opens),"string"==typeof e.drops&&(this.drops=e.drops),"boolean"==typeof e.showWeekNumbers&&(this.showWeekNumbers=e.showWeekNumbers),"boolean"==typeof e.showISOWeekNumbers&&(this.showISOWeekNumbers=e.showISOWeekNumbers),"string"==typeof e.buttonClasses&&(this.buttonClasses=e.buttonClasses),"object"==typeof e.buttonClasses&&(this.buttonClasses=e.buttonClasses.join(" ")),"boolean"==typeof e.showDropdowns&&(this.showDropdowns=e.showDropdowns),"number"==typeof e.minYear&&(this.minYear=e.minYear),"number"==typeof e.maxYear&&(this.maxYear=e.maxYear),"boolean"==typeof e.showCustomRangeLabel&&(this.showCustomRangeLabel=e.showCustomRangeLabel),"boolean"==typeof e.singleDatePicker&&(this.singleDatePicker=e.singleDatePicker,this.singleDatePicker&&(this.endDate=this.startDate.clone())),"boolean"==typeof e.timePicker&&(this.timePicker=e.timePicker),"boolean"==typeof e.timePickerSeconds&&(this.timePickerSeconds=e.timePickerSeconds),"number"==typeof e.timePickerIncrement&&(this.timePickerIncrement=e.timePickerIncrement),"boolean"==typeof e.timePicker24Hour&&(this.timePicker24Hour=e.timePicker24Hour),"boolean"==typeof e.autoApply&&(this.autoApply=e.autoApply),"boolean"==typeof e.autoUpdateInput&&(this.autoUpdateInput=e.autoUpdateInput),"boolean"==typeof e.linkedCalendars&&(this.linkedCalendars=e.linkedCalendars),"function"==typeof e.isInvalidDate&&(this.isInvalidDate=e.isInvalidDate),"function"==typeof e.isCustomDate&&(this.isCustomDate=e.isCustomDate),"boolean"==typeof e.alwaysShowCalendars&&(this.alwaysShowCalendars=e.alwaysShowCalendars),0!=this.locale.firstDay)for(var s=this.locale.firstDay;0<s;)this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()),s--;var n,r,o;if(void 0===e.startDate&&void 0===e.endDate&&R(this.element).is(":text")){var h=R(this.element).val(),l=h.split(this.locale.separator);n=r=null,2==l.length?(n=H(l[0],this.locale.format),r=H(l[1],this.locale.format)):this.singleDatePicker&&""!==h&&(n=H(h,this.locale.format),r=H(h,this.locale.format)),null!==n&&null!==r&&(this.setStartDate(n),this.setEndDate(r))}if("object"==typeof e.ranges){for(o in e.ranges){n="string"==typeof e.ranges[o][0]?H(e.ranges[o][0],this.locale.format):H(e.ranges[o][0]),r="string"==typeof e.ranges[o][1]?H(e.ranges[o][1],this.locale.format):H(e.ranges[o][1]),this.minDate&&n.isBefore(this.minDate)&&(n=this.minDate.clone());var c=this.maxDate;if(this.maxSpan&&c&&n.clone().add(this.maxSpan).isAfter(c)&&(c=n.clone().add(this.maxSpan)),c&&r.isAfter(c)&&(r=c.clone()),!(this.minDate&&r.isBefore(this.minDate,this.timepicker?"minute":"day")||c&&n.isAfter(c,this.timepicker?"minute":"day"))){var d;(d=document.createElement("textarea")).innerHTML=o;i=d.value;this.ranges[i]=[n,r]}}var m="<ul>";for(o in this.ranges)m+='<li data-range-key="'+o+'">'+o+"</li>";this.showCustomRangeLabel&&(m+='<li data-range-key="'+this.locale.customRangeLabel+'">'+this.locale.customRangeLabel+"</li>"),m+="</ul>",this.container.find(".ranges").prepend(m)}"function"==typeof a&&(this.callback=a),this.timePicker||(this.startDate=this.startDate.startOf("day"),this.endDate=this.endDate.endOf("day"),this.container.find(".calendar-time").hide()),this.timePicker&&this.autoApply&&(this.autoApply=!1),this.autoApply&&this.container.addClass("auto-apply"),"object"==typeof e.ranges&&this.container.addClass("show-ranges"),this.singleDatePicker&&(this.container.addClass("single"),this.container.find(".drp-calendar.left").addClass("single"),this.container.find(".drp-calendar.left").show(),this.container.find(".drp-calendar.right").hide(),this.timePicker||this.container.addClass("auto-apply")),(void 0===e.ranges&&!this.singleDatePicker||this.alwaysShowCalendars)&&this.container.addClass("show-calendar"),this.container.addClass("opens"+this.opens),this.container.find(".applyBtn, .cancelBtn").addClass(this.buttonClasses),this.applyButtonClasses.length&&this.container.find(".applyBtn").addClass(this.applyButtonClasses),this.cancelButtonClasses.length&&this.container.find(".cancelBtn").addClass(this.cancelButtonClasses),this.container.find(".applyBtn").html(this.locale.applyLabel),this.container.find(".cancelBtn").html(this.locale.cancelLabel),this.container.find(".drp-calendar").on("click.daterangepicker",".prev",R.proxy(this.clickPrev,this)).on("click.daterangepicker",".next",R.proxy(this.clickNext,this)).on("mousedown.daterangepicker","td.available",R.proxy(this.clickDate,this)).on("mouseenter.daterangepicker","td.available",R.proxy(this.hoverDate,this)).on("change.daterangepicker","select.yearselect",R.proxy(this.monthOrYearChanged,this)).on("change.daterangepicker","select.monthselect",R.proxy(this.monthOrYearChanged,this)).on("change.daterangepicker","select.hourselect,select.minuteselect,select.secondselect,select.ampmselect",R.proxy(this.timeChanged,this)),this.container.find(".ranges").on("click.daterangepicker","li",R.proxy(this.clickRange,this)),this.container.find(".drp-buttons").on("click.daterangepicker","button.applyBtn",R.proxy(this.clickApply,this)).on("click.daterangepicker","button.cancelBtn",R.proxy(this.clickCancel,this)),this.element.is("input")||this.element.is("button")?this.element.on({"click.daterangepicker":R.proxy(this.show,this),"focus.daterangepicker":R.proxy(this.show,this),"keyup.daterangepicker":R.proxy(this.elementChanged,this),"keydown.daterangepicker":R.proxy(this.keydown,this)}):(this.element.on("click.daterangepicker",R.proxy(this.toggle,this)),this.element.on("keydown.daterangepicker",R.proxy(this.toggle,this))),this.updateElement()};return i.prototype={constructor:i,setStartDate:function(t){"string"==typeof t&&(this.startDate=H(t,this.locale.format)),"object"==typeof t&&(this.startDate=H(t)),this.timePicker||(this.startDate=this.startDate.startOf("day")),this.timePicker&&this.timePickerIncrement&&this.startDate.minute(Math.round(this.startDate.minute()/this.timePickerIncrement)*this.timePickerIncrement),this.minDate&&this.startDate.isBefore(this.minDate)&&(this.startDate=this.minDate.clone(),this.timePicker&&this.timePickerIncrement&&this.startDate.minute(Math.round(this.startDate.minute()/this.timePickerIncrement)*this.timePickerIncrement)),this.maxDate&&this.startDate.isAfter(this.maxDate)&&(this.startDate=this.maxDate.clone(),this.timePicker&&this.timePickerIncrement&&this.startDate.minute(Math.floor(this.startDate.minute()/this.timePickerIncrement)*this.timePickerIncrement)),this.isShowing||this.updateElement(),this.updateMonthsInView()},setEndDate:function(t){"string"==typeof t&&(this.endDate=H(t,this.locale.format)),"object"==typeof t&&(this.endDate=H(t)),this.timePicker||(this.endDate=this.endDate.add(1,"d").startOf("day").subtract(1,"second")),this.timePicker&&this.timePickerIncrement&&this.endDate.minute(Math.round(this.endDate.minute()/this.timePickerIncrement)*this.timePickerIncrement),this.endDate.isBefore(this.startDate)&&(this.endDate=this.startDate.clone()),this.maxDate&&this.endDate.isAfter(this.maxDate)&&(this.endDate=this.maxDate.clone()),this.maxSpan&&this.startDate.clone().add(this.maxSpan).isBefore(this.endDate)&&(this.endDate=this.startDate.clone().add(this.maxSpan)),this.previousRightTime=this.endDate.clone(),this.container.find(".drp-selected").html(this.startDate.format(this.locale.format)+this.locale.separator+this.endDate.format(this.locale.format)),this.isShowing||this.updateElement(),this.updateMonthsInView()},isInvalidDate:function(){return!1},isCustomDate:function(){return!1},updateView:function(){this.timePicker&&(this.renderTimePicker("left"),this.renderTimePicker("right"),this.endDate?this.container.find(".right .calendar-time select").removeAttr("disabled").removeClass("disabled"):this.container.find(".right .calendar-time select").attr("disabled","disabled").addClass("disabled")),this.endDate&&this.container.find(".drp-selected").html(this.startDate.format(this.locale.format)+this.locale.separator+this.endDate.format(this.locale.format)),this.updateMonthsInView(),this.updateCalendars(),this.updateFormInputs()},updateMonthsInView:function(){if(this.endDate){if(!this.singleDatePicker&&this.leftCalendar.month&&this.rightCalendar.month&&(this.startDate.format("YYYY-MM")==this.leftCalendar.month.format("YYYY-MM")||this.startDate.format("YYYY-MM")==this.rightCalendar.month.format("YYYY-MM"))&&(this.endDate.format("YYYY-MM")==this.leftCalendar.month.format("YYYY-MM")||this.endDate.format("YYYY-MM")==this.rightCalendar.month.format("YYYY-MM")))return;this.leftCalendar.month=this.startDate.clone().date(2),this.linkedCalendars||this.endDate.month()==this.startDate.month()&&this.endDate.year()==this.startDate.year()?this.rightCalendar.month=this.startDate.clone().date(2).add(1,"month"):this.rightCalendar.month=this.endDate.clone().date(2)}else this.leftCalendar.month.format("YYYY-MM")!=this.startDate.format("YYYY-MM")&&this.rightCalendar.month.format("YYYY-MM")!=this.startDate.format("YYYY-MM")&&(this.leftCalendar.month=this.startDate.clone().date(2),this.rightCalendar.month=this.startDate.clone().date(2).add(1,"month"));this.maxDate&&this.linkedCalendars&&!this.singleDatePicker&&this.rightCalendar.month>this.maxDate&&(this.rightCalendar.month=this.maxDate.clone().date(2),this.leftCalendar.month=this.maxDate.clone().date(2).subtract(1,"month"))},updateCalendars:function(){if(this.timePicker){var t,e,a,i;if(this.endDate){if(t=parseInt(this.container.find(".left .hourselect").val(),10),e=parseInt(this.container.find(".left .minuteselect").val(),10),a=this.timePickerSeconds?parseInt(this.container.find(".left .secondselect").val(),10):0,!this.timePicker24Hour)"PM"===(i=this.container.find(".left .ampmselect").val())&&t<12&&(t+=12),"AM"===i&&12===t&&(t=0)}else if(t=parseInt(this.container.find(".right .hourselect").val(),10),e=parseInt(this.container.find(".right .minuteselect").val(),10),a=this.timePickerSeconds?parseInt(this.container.find(".right .secondselect").val(),10):0,!this.timePicker24Hour)"PM"===(i=this.container.find(".right .ampmselect").val())&&t<12&&(t+=12),"AM"===i&&12===t&&(t=0);this.leftCalendar.month.hour(t).minute(e).second(a),this.rightCalendar.month.hour(t).minute(e).second(a)}this.renderCalendar("left"),this.renderCalendar("right"),this.container.find(".ranges li").removeClass("active"),null!=this.endDate&&this.calculateChosenLabel()},renderCalendar:function(t){var e,a=(e="left"==t?this.leftCalendar:this.rightCalendar).month.month(),i=e.month.year(),s=e.month.hour(),n=e.month.minute(),r=e.month.second(),o=H([i,a]).daysInMonth(),h=H([i,a,1]),l=H([i,a,o]),c=H(h).subtract(1,"month").month(),d=H(h).subtract(1,"month").year(),m=H([d,c]).daysInMonth(),f=h.day();(e=[]).firstDay=h,e.lastDay=l;for(var p=0;p<6;p++)e[p]=[];var u=m-f+this.locale.firstDay+1;m<u&&(u-=7),f==this.locale.firstDay&&(u=m-6);for(var D=H([d,c,u,12,n,r]),g=(p=0,0),y=0;p<42;p++,g++,D=H(D).add(24,"hour"))0<p&&g%7==0&&(g=0,y++),e[y][g]=D.clone().hour(s).minute(n).second(r),D.hour(12),this.minDate&&e[y][g].format("YYYY-MM-DD")==this.minDate.format("YYYY-MM-DD")&&e[y][g].isBefore(this.minDate)&&"left"==t&&(e[y][g]=this.minDate.clone()),this.maxDate&&e[y][g].format("YYYY-MM-DD")==this.maxDate.format("YYYY-MM-DD")&&e[y][g].isAfter(this.maxDate)&&"right"==t&&(e[y][g]=this.maxDate.clone());"left"==t?this.leftCalendar.calendar=e:this.rightCalendar.calendar=e;var k="left"==t?this.minDate:this.startDate,b=this.maxDate,C=("left"==t?this.startDate:this.endDate,this.locale.direction,'<table class="table-condensed">');C+="<thead>",C+="<tr>",(this.showWeekNumbers||this.showISOWeekNumbers)&&(C+="<th></th>"),k&&!k.isBefore(e.firstDay)||this.linkedCalendars&&"left"!=t?C+="<th></th>":C+='<th class="prev available"><span></span></th>';var v=this.locale.monthNames[e[1][1].month()]+e[1][1].format(" YYYY");if(this.showDropdowns){for(var Y=e[1][1].month(),w=e[1][1].year(),P=b&&b.year()||this.maxYear,x=k&&k.year()||this.minYear,M=w==x,S=w==P,I='<select class="monthselect">',B=0;B<12;B++)(!M||B>=k.month())&&(!S||B<=b.month())?I+="<option value='"+B+"'"+(B===Y?" selected='selected'":"")+">"+this.locale.monthNames[B]+"</option>":I+="<option value='"+B+"'"+(B===Y?" selected='selected'":"")+" disabled='disabled'>"+this.locale.monthNames[B]+"</option>";I+="</select>";for(var A='<select class="yearselect">',L=x;L<=P;L++)A+='<option value="'+L+'"'+(L===w?' selected="selected"':"")+">"+L+"</option>";v=I+(A+="</select>")}if(C+='<th colspan="5" class="month">'+v+"</th>",b&&!b.isAfter(e.lastDay)||this.linkedCalendars&&"right"!=t&&!this.singleDatePicker?C+="<th></th>":C+='<th class="next available"><span></span></th>',C+="</tr>",C+="<tr>",(this.showWeekNumbers||this.showISOWeekNumbers)&&(C+='<th class="week">'+this.locale.weekLabel+"</th>"),R.each(this.locale.daysOfWeek,function(t,e){C+="<th>"+e+"</th>"}),C+="</tr>",C+="</thead>",C+="<tbody>",null==this.endDate&&this.maxSpan){var E=this.startDate.clone().add(this.maxSpan).endOf("day");b&&!E.isBefore(b)||(b=E)}for(y=0;y<6;y++){C+="<tr>",this.showWeekNumbers?C+='<td class="week">'+e[y][0].week()+"</td>":this.showISOWeekNumbers&&(C+='<td class="week">'+e[y][0].isoWeek()+"</td>");for(g=0;g<7;g++){var W=[];e[y][g].isSame(new Date,"day")&&W.push("today"),5<e[y][g].isoWeekday()&&W.push("weekend"),e[y][g].month()!=e[1][1].month()&&W.push("off"),this.minDate&&e[y][g].isBefore(this.minDate,"day")&&W.push("off","disabled"),b&&e[y][g].isAfter(b,"day")&&W.push("off","disabled"),this.isInvalidDate(e[y][g])&&W.push("off","disabled"),e[y][g].format("YYYY-MM-DD")==this.startDate.format("YYYY-MM-DD")&&W.push("active","start-date"),null!=this.endDate&&e[y][g].format("YYYY-MM-DD")==this.endDate.format("YYYY-MM-DD")&&W.push("active","end-date"),null!=this.endDate&&e[y][g]>this.startDate&&e[y][g]<this.endDate&&W.push("in-range");var O=this.isCustomDate(e[y][g]);!1!==O&&("string"==typeof O?W.push(O):Array.prototype.push.apply(W,O));var N="",j=!1;for(p=0;p<W.length;p++)N+=W[p]+" ","disabled"==W[p]&&(j=!0);j||(N+="available"),C+='<td class="'+N.replace(/^\s+|\s+$/g,"")+'" data-title="r'+y+"c"+g+'">'+e[y][g].date()+"</td>"}C+="</tr>"}C+="</tbody>",C+="</table>",this.container.find(".drp-calendar."+t+" .calendar-table").html(C)},renderTimePicker:function(t){if("right"!=t||this.endDate){var e,a,i,s=this.maxDate;if(!this.maxSpan||this.maxDate&&!this.startDate.clone().add(this.maxSpan).isAfter(this.maxDate)||(s=this.startDate.clone().add(this.maxSpan)),"left"==t)a=this.startDate.clone(),i=this.minDate;else if("right"==t){a=this.endDate.clone(),i=this.startDate;var n=this.container.find(".drp-calendar.right .calendar-time");if(""!=n.html()&&(a.hour(a.hour()||n.find(".hourselect option:selected").val()),a.minute(a.minute()||n.find(".minuteselect option:selected").val()),a.second(a.second()||n.find(".secondselect option:selected").val()),!this.timePicker24Hour)){var r=n.find(".ampmselect option:selected").val();"PM"===r&&a.hour()<12&&a.hour(a.hour()+12),"AM"===r&&12===a.hour()&&a.hour(0)}a.isBefore(this.startDate)&&(a=this.startDate.clone()),s&&a.isAfter(s)&&(a=s.clone())}e='<select class="hourselect">';for(var o=this.timePicker24Hour?0:1,h=this.timePicker24Hour?23:12,l=o;l<=h;l++){var c=l;this.timePicker24Hour||(c=12<=a.hour()?12==l?12:l+12:12==l?0:l);var d=a.clone().hour(c),m=!1;i&&d.minute(59).isBefore(i)&&(m=!0),s&&d.minute(0).isAfter(s)&&(m=!0),c!=a.hour()||m?e+=m?'<option value="'+l+'" disabled="disabled" class="disabled">'+l+"</option>":'<option value="'+l+'">'+l+"</option>":e+='<option value="'+l+'" selected="selected">'+l+"</option>"}e+="</select> ",e+=': <select class="minuteselect">';for(l=0;l<60;l+=this.timePickerIncrement){var f=l<10?"0"+l:l;d=a.clone().minute(l),m=!1;i&&d.second(59).isBefore(i)&&(m=!0),s&&d.second(0).isAfter(s)&&(m=!0),a.minute()!=l||m?e+=m?'<option value="'+l+'" disabled="disabled" class="disabled">'+f+"</option>":'<option value="'+l+'">'+f+"</option>":e+='<option value="'+l+'" selected="selected">'+f+"</option>"}if(e+="</select> ",this.timePickerSeconds){e+=': <select class="secondselect">';for(l=0;l<60;l++){f=l<10?"0"+l:l,d=a.clone().second(l),m=!1;i&&d.isBefore(i)&&(m=!0),s&&d.isAfter(s)&&(m=!0),a.second()!=l||m?e+=m?'<option value="'+l+'" disabled="disabled" class="disabled">'+f+"</option>":'<option value="'+l+'">'+f+"</option>":e+='<option value="'+l+'" selected="selected">'+f+"</option>"}e+="</select> "}if(!this.timePicker24Hour){e+='<select class="ampmselect">';var p="",u="";i&&a.clone().hour(12).minute(0).second(0).isBefore(i)&&(p=' disabled="disabled" class="disabled"'),s&&a.clone().hour(0).minute(0).second(0).isAfter(s)&&(u=' disabled="disabled" class="disabled"'),12<=a.hour()?e+='<option value="AM"'+p+'>AM</option><option value="PM" selected="selected"'+u+">PM</option>":e+='<option value="AM" selected="selected"'+p+'>AM</option><option value="PM"'+u+">PM</option>",e+="</select>"}this.container.find(".drp-calendar."+t+" .calendar-time").html(e)}},updateFormInputs:function(){this.singleDatePicker||this.endDate&&(this.startDate.isBefore(this.endDate)||this.startDate.isSame(this.endDate))?this.container.find("button.applyBtn").removeAttr("disabled"):this.container.find("button.applyBtn").attr("disabled","disabled")},move:function(){var t,e={top:0,left:0},a=R(window).width();this.parentEl.is("body")||(e={top:this.parentEl.offset().top-this.parentEl.scrollTop(),left:this.parentEl.offset().left-this.parentEl.scrollLeft()},a=this.parentEl[0].clientWidth+this.parentEl.offset().left),t="up"==this.drops?this.element.offset().top-this.container.outerHeight()-e.top:this.element.offset().top+this.element.outerHeight()-e.top,this.container["up"==this.drops?"addClass":"removeClass"]("drop-up"),"left"==this.opens?(this.container.css({top:t,right:a-this.element.offset().left-this.element.outerWidth(),left:"auto"}),this.container.offset().left<0&&this.container.css({right:"auto",left:9})):"center"==this.opens?(this.container.css({top:t,left:this.element.offset().left-e.left+this.element.outerWidth()/2-this.container.outerWidth()/2,right:"auto"}),this.container.offset().left<0&&this.container.css({right:"auto",left:9})):(this.container.css({top:t,left:this.element.offset().left-e.left,right:"auto"}),this.container.offset().left+this.container.outerWidth()>R(window).width()&&this.container.css({left:"auto",right:0}))},show:function(t){this.isShowing||(this._outsideClickProxy=R.proxy(function(t){this.outsideClick(t)},this),R(document).on("mousedown.daterangepicker",this._outsideClickProxy).on("touchend.daterangepicker",this._outsideClickProxy).on("click.daterangepicker","[data-toggle=dropdown]",this._outsideClickProxy).on("focusin.daterangepicker",this._outsideClickProxy),R(window).on("resize.daterangepicker",R.proxy(function(t){this.move(t)},this)),this.oldStartDate=this.startDate.clone(),this.oldEndDate=this.endDate.clone(),this.previousRightTime=this.endDate.clone(),this.updateView(),this.container.show(),this.move(),this.element.trigger("show.daterangepicker",this),this.isShowing=!0)},hide:function(t){this.isShowing&&(this.endDate||(this.startDate=this.oldStartDate.clone(),this.endDate=this.oldEndDate.clone()),this.startDate.isSame(this.oldStartDate)&&this.endDate.isSame(this.oldEndDate)||this.callback(this.startDate.clone(),this.endDate.clone(),this.chosenLabel),this.updateElement(),R(document).off(".daterangepicker"),R(window).off(".daterangepicker"),this.container.hide(),this.element.trigger("hide.daterangepicker",this),this.isShowing=!1)},toggle:function(t){this.isShowing?this.hide():this.show()},outsideClick:function(t){var e=R(t.target);"focusin"==t.type||e.closest(this.element).length||e.closest(this.container).length||e.closest(".calendar-table").length||(this.hide(),this.element.trigger("outsideClick.daterangepicker",this))},showCalendars:function(){this.container.addClass("show-calendar"),this.move(),this.element.trigger("showCalendar.daterangepicker",this)},hideCalendars:function(){this.container.removeClass("show-calendar"),this.element.trigger("hideCalendar.daterangepicker",this)},clickRange:function(t){var e=t.target.getAttribute("data-range-key");if((this.chosenLabel=e)==this.locale.customRangeLabel)this.showCalendars();else{var a=this.ranges[e];this.startDate=a[0],this.endDate=a[1],this.timePicker||(this.startDate.startOf("day"),this.endDate.endOf("day")),this.alwaysShowCalendars||this.hideCalendars(),this.clickApply()}},clickPrev:function(t){R(t.target).parents(".drp-calendar").hasClass("left")?(this.leftCalendar.month.subtract(1,"month"),this.linkedCalendars&&this.rightCalendar.month.subtract(1,"month")):this.rightCalendar.month.subtract(1,"month"),this.updateCalendars()},clickNext:function(t){R(t.target).parents(".drp-calendar").hasClass("left")?this.leftCalendar.month.add(1,"month"):(this.rightCalendar.month.add(1,"month"),this.linkedCalendars&&this.leftCalendar.month.add(1,"month")),this.updateCalendars()},hoverDate:function(t){if(R(t.target).hasClass("available")){var e=R(t.target).attr("data-title"),a=e.substr(1,1),i=e.substr(3,1),r=R(t.target).parents(".drp-calendar").hasClass("left")?this.leftCalendar.calendar[a][i]:this.rightCalendar.calendar[a][i],o=this.leftCalendar,h=this.rightCalendar,l=this.startDate;this.endDate||this.container.find(".drp-calendar tbody td").each(function(t,e){if(!R(e).hasClass("week")){var a=R(e).attr("data-title"),i=a.substr(1,1),s=a.substr(3,1),n=R(e).parents(".drp-calendar").hasClass("left")?o.calendar[i][s]:h.calendar[i][s];n.isAfter(l)&&n.isBefore(r)||n.isSame(r,"day")?R(e).addClass("in-range"):R(e).removeClass("in-range")}})}},clickDate:function(t){if(R(t.target).hasClass("available")){var e=R(t.target).attr("data-title"),a=e.substr(1,1),i=e.substr(3,1),s=R(t.target).parents(".drp-calendar").hasClass("left")?this.leftCalendar.calendar[a][i]:this.rightCalendar.calendar[a][i];if(this.endDate||s.isBefore(this.startDate,"day")){if(this.timePicker){var n=parseInt(this.container.find(".left .hourselect").val(),10);if(!this.timePicker24Hour)"PM"===(h=this.container.find(".left .ampmselect").val())&&n<12&&(n+=12),"AM"===h&&12===n&&(n=0);var r=parseInt(this.container.find(".left .minuteselect").val(),10),o=this.timePickerSeconds?parseInt(this.container.find(".left .secondselect").val(),10):0;s=s.clone().hour(n).minute(r).second(o)}this.endDate=null,this.setStartDate(s.clone())}else if(!this.endDate&&s.isBefore(this.startDate))this.setEndDate(this.startDate.clone());else{if(this.timePicker){var h;n=parseInt(this.container.find(".right .hourselect").val(),10);if(!this.timePicker24Hour)"PM"===(h=this.container.find(".right .ampmselect").val())&&n<12&&(n+=12),"AM"===h&&12===n&&(n=0);r=parseInt(this.container.find(".right .minuteselect").val(),10),o=this.timePickerSeconds?parseInt(this.container.find(".right .secondselect").val(),10):0;s=s.clone().hour(n).minute(r).second(o)}this.setEndDate(s.clone()),this.autoApply&&(this.calculateChosenLabel(),this.clickApply())}this.singleDatePicker&&(this.setEndDate(this.startDate),this.timePicker||this.clickApply()),this.updateView(),t.stopPropagation()}},calculateChosenLabel:function(){var t=!0,e=0;for(var a in this.ranges){if(this.timePicker){var i=this.timePickerSeconds?"YYYY-MM-DD hh:mm:ss":"YYYY-MM-DD hh:mm";if(this.startDate.format(i)==this.ranges[a][0].format(i)&&this.endDate.format(i)==this.ranges[a][1].format(i)){t=!1,this.chosenLabel=this.container.find(".ranges li:eq("+e+")").addClass("active").attr("data-range-key");break}}else if(this.startDate.format("YYYY-MM-DD")==this.ranges[a][0].format("YYYY-MM-DD")&&this.endDate.format("YYYY-MM-DD")==this.ranges[a][1].format("YYYY-MM-DD")){t=!1,this.chosenLabel=this.container.find(".ranges li:eq("+e+")").addClass("active").attr("data-range-key");break}e++}t&&(this.showCustomRangeLabel?this.chosenLabel=this.container.find(".ranges li:last").addClass("active").attr("data-range-key"):this.chosenLabel=null,this.showCalendars())},clickApply:function(t){this.hide(),this.element.trigger("apply.daterangepicker",this)},clickCancel:function(t){this.startDate=this.oldStartDate,this.endDate=this.oldEndDate,this.hide(),this.element.trigger("cancel.daterangepicker",this)},monthOrYearChanged:function(t){var e=R(t.target).closest(".drp-calendar").hasClass("left"),a=e?"left":"right",i=this.container.find(".drp-calendar."+a),s=parseInt(i.find(".monthselect").val(),10),n=i.find(".yearselect").val();e||(n<this.startDate.year()||n==this.startDate.year()&&s<this.startDate.month())&&(s=this.startDate.month(),n=this.startDate.year()),this.minDate&&(n<this.minDate.year()||n==this.minDate.year()&&s<this.minDate.month())&&(s=this.minDate.month(),n=this.minDate.year()),this.maxDate&&(n>this.maxDate.year()||n==this.maxDate.year()&&s>this.maxDate.month())&&(s=this.maxDate.month(),n=this.maxDate.year()),e?(this.leftCalendar.month.month(s).year(n),this.linkedCalendars&&(this.rightCalendar.month=this.leftCalendar.month.clone().add(1,"month"))):(this.rightCalendar.month.month(s).year(n),this.linkedCalendars&&(this.leftCalendar.month=this.rightCalendar.month.clone().subtract(1,"month"))),this.updateCalendars()},timeChanged:function(t){var e=R(t.target).closest(".drp-calendar"),a=e.hasClass("left"),i=parseInt(e.find(".hourselect").val(),10),s=parseInt(e.find(".minuteselect").val(),10),n=this.timePickerSeconds?parseInt(e.find(".secondselect").val(),10):0;if(!this.timePicker24Hour){var r=e.find(".ampmselect").val();"PM"===r&&i<12&&(i+=12),"AM"===r&&12===i&&(i=0)}if(a){var o=this.startDate.clone();o.hour(i),o.minute(s),o.second(n),this.setStartDate(o),this.singleDatePicker?this.endDate=this.startDate.clone():this.endDate&&this.endDate.format("YYYY-MM-DD")==o.format("YYYY-MM-DD")&&this.endDate.isBefore(o)&&this.setEndDate(o.clone())}else if(this.endDate){var h=this.endDate.clone();h.hour(i),h.minute(s),h.second(n),this.setEndDate(h)}this.updateCalendars(),this.updateFormInputs(),this.renderTimePicker("left"),this.renderTimePicker("right")},elementChanged:function(){if(this.element.is("input")&&this.element.val().length){var t=this.element.val().split(this.locale.separator),e=null,a=null;2===t.length&&(e=H(t[0],this.locale.format),a=H(t[1],this.locale.format)),(this.singleDatePicker||null===e||null===a)&&(a=e=H(this.element.val(),this.locale.format)),e.isValid()&&a.isValid()&&(this.setStartDate(e),this.setEndDate(a),this.updateView())}},keydown:function(t){9!==t.keyCode&&13!==t.keyCode||this.hide(),27===t.keyCode&&(t.preventDefault(),t.stopPropagation(),this.hide())},updateElement:function(){if(this.element.is("input")&&this.autoUpdateInput){var t=this.startDate.format(this.locale.format);this.singleDatePicker||(t+=this.locale.separator+this.endDate.format(this.locale.format)),t!==this.element.val()&&this.element.val(t).trigger("change")}},remove:function(){this.container.remove(),this.element.off(".daterangepicker"),this.element.removeData()}},R.fn.daterangepicker=function(t,e){var a=R.extend(!0,{},R.fn.daterangepicker.defaultOptions,t);return this.each(function(){var t=R(this);t.data("daterangepicker")&&t.data("daterangepicker").remove(),t.data("daterangepicker",new i(t,a,e))}),this},i});
-//# sourceMappingURL=/sm/8cfffddf058dc09b67d92f8d849675e6b459dfb8ede5136cf5c98d10acf78cc3.map
\ No newline at end of file
diff --git a/apps/static/js/plugins/daterangepicker/moment.min.js b/apps/static/js/plugins/daterangepicker/moment.min.js
deleted file mode 100644
index 770f8bc54..000000000
--- a/apps/static/js/plugins/daterangepicker/moment.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-//! moment.js
-//! version : 2.18.1
-//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
-//! license : MIT
-//! momentjs.com
-!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return sd.apply(null,arguments)}function b(a){sd=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a)return!1;return!0}function f(a){return void 0===a}function g(a){return"number"==typeof a||"[object Number]"===Object.prototype.toString.call(a)}function h(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function i(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function j(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function k(a,b){for(var c in b)j(b,c)&&(a[c]=b[c]);return j(b,"toString")&&(a.toString=b.toString),j(b,"valueOf")&&(a.valueOf=b.valueOf),a}function l(a,b,c,d){return sb(a,b,c,d,!0).utc()}function m(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}}function n(a){return null==a._pf&&(a._pf=m()),a._pf}function o(a){if(null==a._isValid){var b=n(a),c=ud.call(b.parsedDateParts,function(a){return null!=a}),d=!isNaN(a._d.getTime())&&b.overflow<0&&!b.empty&&!b.invalidMonth&&!b.invalidWeekday&&!b.nullInput&&!b.invalidFormat&&!b.userInvalidated&&(!b.meridiem||b.meridiem&&c);if(a._strict&&(d=d&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour),null!=Object.isFrozen&&Object.isFrozen(a))return d;a._isValid=d}return a._isValid}function p(a){var b=l(NaN);return null!=a?k(n(b),a):n(b).userInvalidated=!0,b}function q(a,b){var c,d,e;if(f(b._isAMomentObject)||(a._isAMomentObject=b._isAMomentObject),f(b._i)||(a._i=b._i),f(b._f)||(a._f=b._f),f(b._l)||(a._l=b._l),f(b._strict)||(a._strict=b._strict),f(b._tzm)||(a._tzm=b._tzm),f(b._isUTC)||(a._isUTC=b._isUTC),f(b._offset)||(a._offset=b._offset),f(b._pf)||(a._pf=n(b)),f(b._locale)||(a._locale=b._locale),vd.length>0)for(c=0;c<vd.length;c++)d=vd[c],e=b[d],f(e)||(a[d]=e);return a}function r(b){q(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),wd===!1&&(wd=!0,a.updateOffset(this),wd=!1)}function s(a){return a instanceof r||null!=a&&null!=a._isAMomentObject}function t(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=t(b)),c}function v(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d<e;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function w(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function x(b,c){var d=!0;return k(function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,b),d){for(var e,f=[],g=0;g<arguments.length;g++){if(e="","object"==typeof arguments[g]){e+="\n["+g+"] ";for(var h in arguments[0])e+=h+": "+arguments[0][h]+", ";e=e.slice(0,-2)}else e=arguments[g];f.push(e)}w(b+"\nArguments: "+Array.prototype.slice.call(f).join("")+"\n"+(new Error).stack),d=!1}return c.apply(this,arguments)},c)}function y(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),xd[b]||(w(c),xd[b]=!0)}function z(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function A(a){var b,c;for(c in a)b=a[c],z(b)?this[c]=b:this["_"+c]=b;this._config=a,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)}function B(a,b){var c,e=k({},a);for(c in b)j(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},k(e[c],a[c]),k(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)j(a,c)&&!j(b,c)&&d(a[c])&&(e[c]=k({},e[c]));return e}function C(a){null!=a&&this.set(a)}function D(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return z(d)?d.call(b,c):d}function E(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function F(){return this._invalidDate}function G(a){return this._ordinal.replace("%d",a)}function H(a,b,c,d){var e=this._relativeTime[c];return z(e)?e(a,b,c,d):e.replace(/%d/i,a)}function I(a,b){var c=this._relativeTime[a>0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Hd[c]=Hd[c+"s"]=Hd[b]=a}function K(a){return"string"==typeof a?Hd[a]||Hd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)j(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Id[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Id[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)}function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d<c.length;d++)this[c[d].unit](a[c[d].unit])}else if(a=K(a),z(this[a]))return this[a](b);return this}function T(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Md[a]=e),b&&(Md[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Md[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Jd);for(b=0,c=d.length;b<c;b++)Md[d[b]]?d[b]=Md[d[b]]:d[b]=V(d[b]);return function(b){var e,f="";for(e=0;e<c;e++)f+=z(d[e])?d[e].call(b,a):d[e];return f}}function X(a,b){return a.isValid()?(b=Y(b,a.localeData()),Ld[b]=Ld[b]||W(b),Ld[b](a)):a.localeData().invalidDate()}function Y(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Kd.lastIndex=0;d>=0&&Kd.test(a);)a=a.replace(Kd,c),Kd.lastIndex=0,d-=1;return a}function Z(a,b,c){ce[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return j(ce,a)?ce[a](b._strict,b._locale):new RegExp(_(a))}function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),g(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c<a.length;c++)de[a[c]]=d}function ca(a,b){ba(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function da(a,b,c){null!=b&&j(de,a)&&de[a](b,c._a,c,a)}function ea(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function fa(a,b){return a?c(this._months)?this._months[a.month()]:this._months[(this._months.isFormat||oe).test(b)?"format":"standalone"][a.month()]:c(this._months)?this._months:this._months.standalone}function ga(a,b){return a?c(this._monthsShort)?this._monthsShort[a.month()]:this._monthsShort[oe.test(b)?"format":"standalone"][a.month()]:c(this._monthsShort)?this._monthsShort:this._monthsShort.standalone}function ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],d=0;d<12;++d)f=l([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=ne.call(this._shortMonthsParse,g),e!==-1?e:null):(e=ne.call(this._longMonthsParse,g),e!==-1?e:null):"MMM"===b?(e=ne.call(this._shortMonthsParse,g),e!==-1?e:(e=ne.call(this._longMonthsParse,g),e!==-1?e:null)):(e=ne.call(this._longMonthsParse,g),e!==-1?e:(e=ne.call(this._shortMonthsParse,g),e!==-1?e:null))}function ia(a,b,c){var d,e,f;if(this._monthsParseExact)return ha.call(this,a,b,c);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;d<12;d++){if(e=l([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function ja(a,b){var c;if(!a.isValid())return a;if("string"==typeof b)if(/^\d+$/.test(b))b=u(b);else if(b=a.localeData().monthsParse(b),!g(b))return a;return c=Math.min(a.date(),ea(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ka(b){return null!=b?(ja(this,b),a.updateOffset(this,!0),this):P(this,"Month")}function la(){return ea(this.year(),this.month())}function ma(a){return this._monthsParseExact?(j(this,"_monthsRegex")||oa.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(j(this,"_monthsShortRegex")||(this._monthsShortRegex=re),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function na(a){return this._monthsParseExact?(j(this,"_monthsRegex")||oa.call(this),a?this._monthsStrictRegex:this._monthsRegex):(j(this,"_monthsRegex")||(this._monthsRegex=se),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function oa(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;b<12;b++)c=l([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for(d.sort(a),e.sort(a),f.sort(a),b=0;b<12;b++)d[b]=aa(d[b]),e[b]=aa(e[b]);for(b=0;b<24;b++)f[b]=aa(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")}function pa(a){return qa(a)?366:365}function qa(a){return a%4===0&&a%100!==0||a%400===0}function ra(){return qa(this.year())}function sa(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return a<100&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments));return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b}function ua(a,b,c){var d=7+b-c,e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1}function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7}function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy}function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:c(this._weekdays)?this._weekdays:this._weekdays.standalone}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=l([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=ne.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){if(e=l([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}}function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN;if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(j(this,"_weekdaysRegex")||(this._weekdaysRegex=ye),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(j(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ze),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(j(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Ae),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++)c=l([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for(g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")}function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Ua(a,b){return b._meridiemParse}function Va(a){return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a}function Ya(a){for(var b,c,d,e,f=0;f<a.length;){for(e=Xa(a[f]).split("-"),b=e.length,c=Xa(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1)break;b--}f++}return null}function Za(a){var b=null;if(!Fe[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Be._abbr,require("./locale/"+a),$a(b)}catch(a){}return Fe[a]}function $a(a,b){var c;return a&&(c=f(b)?bb(a):_a(a,b),c&&(Be=c)),Be._abbr}function _a(a,b){if(null!==b){var c=Ee;if(b.abbr=a,null!=Fe[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Fe[a]._config;else if(null!=b.parentLocale){if(null==Fe[b.parentLocale])return Ge[b.parentLocale]||(Ge[b.parentLocale]=[]),Ge[b.parentLocale].push({name:a,config:b}),null;c=Fe[b.parentLocale]._config}return Fe[a]=new C(B(c,b)),Ge[a]&&Ge[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Fe[a]}return delete Fe[a],null}function ab(a,b){if(null!=b){var c,d=Ee;null!=Fe[a]&&(d=Fe[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Fe[a],Fe[a]=c,$a(a)}else null!=Fe[a]&&(null!=Fe[a].parentLocale?Fe[a]=Fe[a].parentLocale:null!=Fe[a]&&delete Fe[a]);return Fe[a]}function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Be;if(!c(a)){if(b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return Ad(Fe)}function db(a){var b,c=a._a;return c&&n(a).overflow===-2&&(b=c[fe]<0||c[fe]>11?fe:c[ge]<1||c[ge]>ea(c[ee],c[fe])?ge:c[he]<0||c[he]>24||24===c[he]&&(0!==c[ie]||0!==c[je]||0!==c[ke])?he:c[ie]<0||c[ie]>59?ie:c[je]<0||c[je]>59?je:c[ke]<0||c[ke]>999?ke:-1,n(a)._overflowDayOfYear&&(b<ee||b>ge)&&(b=ge),n(a)._overflowWeeks&&b===-1&&(b=le),n(a)._overflowWeekday&&b===-1&&(b=me),n(a).overflow=b),a}function eb(a){var b,c,d,e,f,g,h=a._i,i=He.exec(h)||Ie.exec(h);if(i){for(n(a).iso=!0,b=0,c=Ke.length;b<c;b++)if(Ke[b][1].exec(i[1])){e=Ke[b][0],d=Ke[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=Le.length;b<c;b++)if(Le[b][1].exec(i[3])){f=(i[2]||" ")+Le[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!Je.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),lb(a)}else a._isValid=!1}function fb(a){var b,c,d,e,f,g,h,i,j={" GMT":" +0000"," EDT":" -0400"," EST":" -0500"," CDT":" -0500"," CST":" -0600"," MDT":" -0600"," MST":" -0700"," PDT":" -0700"," PST":" -0800"},k="YXWVUTSRQPONZABCDEFGHIKLM";if(b=a._i.replace(/\([^\)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s|\s$/g,""),c=Ne.exec(b)){if(d=c[1]?"ddd"+(5===c[1].length?", ":" "):"",e="D MMM "+(c[2].length>10?"YYYY ":"YY "),f="HH:mm"+(c[4]?":ss":""),c[1]){var l=new Date(c[2]),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][l.getDay()];if(c[1].substr(0,3)!==m)return n(a).weekdayMismatch=!0,void(a._isValid=!1)}switch(c[5].length){case 2:0===i?h=" +0000":(i=k.indexOf(c[5][1].toUpperCase())-12,h=(i<0?" -":" +")+(""+i).replace(/^-?/,"0").match(/..$/)[0]+"00");break;case 4:h=j[c[5]];break;default:h=j[" GMT"]}c[5]=h,a._i=c.splice(1).join(""),g=" ZZ",a._f=d+e+f+g,lb(a),n(a).rfc2822=!0}else a._isValid=!1}function gb(b){var c=Me.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(eb(b),void(b._isValid===!1&&(delete b._isValid,fb(b),b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b)))))}function hb(a,b,c){return null!=a?a:null!=b?b:c}function ib(b){var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]}function jb(a){var b,c,d,e,f=[];if(!a._d){for(d=ib(a),a._w&&null==a._a[ge]&&null==a._a[fe]&&kb(a),null!=a._dayOfYear&&(e=hb(a._a[ee],d[ee]),(a._dayOfYear>pa(e)||0===a._dayOfYear)&&(n(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[fe]=c.getUTCMonth(),a._a[ge]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[he]&&0===a._a[ie]&&0===a._a[je]&&0===a._a[ke]&&(a._nextDay=!0,a._a[he]=0),a._d=(a._useUTC?ta:sa).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[he]=24)}}function kb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4,c=hb(b.GG,a._a[ee],wa(tb(),1,4).year),d=hb(b.W,1),e=hb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(tb(),f,g);c=hb(b.gg,a._a[ee],j.year),d=hb(b.w,j.week),null!=b.d?(e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?(e=b.e+f,(b.e<0||b.e>6)&&(i=!0)):e=f}d<1||d>xa(c,f,g)?n(a)._overflowWeeks=!0:null!=i?n(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ee]=h.year,a._dayOfYear=h.dayOfYear)}function lb(b){if(b._f===a.ISO_8601)return void eb(b);if(b._f===a.RFC_2822)return void fb(b);b._a=[],n(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Jd)||[],c=0;c<e.length;c++)f=e[c],d=(h.match($(f,b))||[])[0],d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&n(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),Md[f]?(d?n(b).empty=!1:n(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&n(b).unusedTokens.push(f);n(b).charsLeftOver=i-j,h.length>0&&n(b).unusedInput.push(h),b._a[he]<=12&&n(b).bigHour===!0&&b._a[he]>0&&(n(b).bigHour=void 0),n(b).parsedDateParts=b._a.slice(0),n(b).meridiem=b._meridiem,b._a[he]=mb(b._locale,b._a[he],b._meridiem),jb(b),db(b)}function mb(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b}function nb(a){var b,c,d,e,f;if(0===a._f.length)return n(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=q({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],lb(b),o(b)&&(f+=n(b).charsLeftOver,f+=10*n(b).unusedTokens.length,n(b).score=f,(null==d||f<d)&&(d=f,c=b));k(a,c||b)}function ob(a){if(!a._d){var b=L(a._i);a._a=i([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),jb(a)}}function pb(a){var b=new r(db(qb(a)));return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function qb(a){var b=a._i,d=a._f;return a._locale=a._locale||bb(a._l),null===b||void 0===d&&""===b?p({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),s(b)?new r(db(b)):(h(b)?a._d=b:c(d)?nb(a):d?lb(a):rb(a),o(a)||(a._d=null),a))}function rb(b){var e=b._i;f(e)?b._d=new Date(a.now()):h(e)?b._d=new Date(e.valueOf()):"string"==typeof e?gb(b):c(e)?(b._a=i(e.slice(0),function(a){return parseInt(a,10)}),jb(b)):d(e)?ob(b):g(e)?b._d=new Date(e):a.createFromInputFallback(b)}function sb(a,b,f,g,h){var i={};return f!==!0&&f!==!1||(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,pb(i)}function tb(a,b,c,d){return sb(a,b,c,d,!1)}function ub(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return tb();for(d=b[0],e=1;e<b.length;++e)b[e].isValid()&&!b[e][a](d)||(d=b[e]);return d}function vb(){var a=[].slice.call(arguments,0);return ub("isBefore",a)}function wb(){var a=[].slice.call(arguments,0);return ub("isAfter",a)}function xb(a){for(var b in a)if(Re.indexOf(b)===-1||null!=a[b]&&isNaN(a[b]))return!1;for(var c=!1,d=0;d<Re.length;++d)if(a[Re[d]]){if(c)return!1;parseFloat(a[Re[d]])!==u(a[Re[d]])&&(c=!0)}return!0}function yb(){return this._isValid}function zb(){return Sb(NaN)}function Ab(a){var b=L(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._isValid=xb(b),this._milliseconds=+k+1e3*j+6e4*i+1e3*h*60*60,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._locale=bb(),this._bubble()}function Bb(a){return a instanceof Ab}function Cb(a){return a<0?Math.round(-1*a)*-1:Math.round(a)}function Db(a,b){U(a,0,0,function(){var a=this.utcOffset(),c="+";return a<0&&(a=-a,c="-"),c+T(~~(a/60),2)+b+T(~~a%60,2)})}function Eb(a,b){var c=(b||"").match(a);if(null===c)return null;var d=c[c.length-1]||[],e=(d+"").match(Se)||["-",0,0],f=+(60*e[1])+u(e[2]);return 0===f?0:"+"===e[0]?f:-f}function Fb(b,c){var d,e;return c._isUTC?(d=c.clone(),e=(s(b)||h(b)?b.valueOf():tb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):tb(b).local()}function Gb(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Hb(b,c,d){var e,f=this._offset||0;if(!this.isValid())return null!=b?this:NaN;if(null!=b){if("string"==typeof b){if(b=Eb(_d,b),null===b)return this}else Math.abs(b)<16&&!d&&(b=60*b);return!this._isUTC&&c&&(e=Gb(this)),this._offset=b,this._isUTC=!0,null!=e&&this.add(e,"m"),f!==b&&(!c||this._changeInProgress?Xb(this,Sb(b-f,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?f:Gb(this)}function Ib(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Jb(a){return this.utcOffset(0,a)}function Kb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Gb(this),"m")),this}function Lb(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var a=Eb($d,this._i);null!=a?this.utcOffset(a):this.utcOffset(0,!0)}return this}function Mb(a){return!!this.isValid()&&(a=a?tb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Nb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ob(){if(!f(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=qb(a),a._a){var b=a._isUTC?l(a._a):tb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Pb(){return!!this.isValid()&&!this._isUTC}function Qb(){return!!this.isValid()&&this._isUTC}function Rb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Sb(a,b){var c,d,e,f=a,h=null;return Bb(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:g(a)?(f={},b?f[b]=a:f.milliseconds=a):(h=Te.exec(a))?(c="-"===h[1]?-1:1,f={y:0,d:u(h[ge])*c,h:u(h[he])*c,m:u(h[ie])*c,s:u(h[je])*c,ms:u(Cb(1e3*h[ke]))*c}):(h=Ue.exec(a))?(c="-"===h[1]?-1:1,f={y:Tb(h[2],c),M:Tb(h[3],c),w:Tb(h[4],c),d:Tb(h[5],c),h:Tb(h[6],c),m:Tb(h[7],c),s:Tb(h[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Vb(tb(f.from),tb(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new Ab(f),Bb(a)&&j(a,"_locale")&&(d._locale=a._locale),d}function Tb(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function Ub(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Vb(a,b){var c;return a.isValid()&&b.isValid()?(b=Fb(b,a),a.isBefore(b)?c=Ub(a,b):(c=Ub(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}}function Wb(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Sb(c,d),Xb(this,e,a),this}}function Xb(b,c,d,e){var f=c._milliseconds,g=Cb(c._days),h=Cb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Yb(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Zb(b,c){var d=b||tb(),e=Fb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,tb(d)))}function $b(){return new r(this)}function _b(a,b){var c=s(a)?a:tb(a);return!(!this.isValid()||!c.isValid())&&(b=K(f(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()<this.clone().startOf(b).valueOf())}function ac(a,b){var c=s(a)?a:tb(a);return!(!this.isValid()||!c.isValid())&&(b=K(f(b)?"millisecond":b),"millisecond"===b?this.valueOf()<c.valueOf():this.clone().endOf(b).valueOf()<c.valueOf())}function bc(a,b,c,d){return d=d||"()",("("===d[0]?this.isAfter(a,c):!this.isBefore(a,c))&&(")"===d[1]?this.isBefore(b,c):!this.isAfter(b,c))}function cc(a,b){var c,d=s(a)?a:tb(a);return!(!this.isValid()||!d.isValid())&&(b=K(b||"millisecond"),"millisecond"===b?this.valueOf()===d.valueOf():(c=d.valueOf(),this.clone().startOf(b).valueOf()<=c&&c<=this.clone().endOf(b).valueOf()))}function dc(a,b){return this.isSame(a,b)||this.isAfter(a,b)}function ec(a,b){return this.isSame(a,b)||this.isBefore(a,b)}function fc(a,b,c){var d,e,f,g;return this.isValid()?(d=Fb(a,this),d.isValid()?(e=6e4*(d.utcOffset()-this.utcOffset()),b=K(b),"year"===b||"month"===b||"quarter"===b?(g=gc(this,d),"quarter"===b?g/=3:"year"===b&&(g/=12)):(f=this-d,g="second"===b?f/1e3:"minute"===b?f/6e4:"hour"===b?f/36e5:"day"===b?(f-e)/864e5:"week"===b?(f-e)/6048e5:f),c?g:t(g)):NaN):NaN}function gc(a,b){var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),f=a.clone().add(e,"months");return b-f<0?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function hc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function ic(){if(!this.isValid())return null;var a=this.clone().utc();return a.year()<0||a.year()>9999?X(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):z(Date.prototype.toISOString)?this.toDate().toISOString():X(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}function jc(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var a="moment",b="";this.isLocal()||(a=0===this.utcOffset()?"moment.utc":"moment.parseZone",b="Z");var c="["+a+'("]',d=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",e="-MM-DD[T]HH:mm:ss.SSS",f=b+'[")]';return this.format(c+d+e+f)}function kc(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=X(this,b);return this.localeData().postformat(c)}function lc(a,b){return this.isValid()&&(s(a)&&a.isValid()||tb(a).isValid())?Sb({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function mc(a){return this.from(tb(),a)}function nc(a,b){return this.isValid()&&(s(a)&&a.isValid()||tb(a).isValid())?Sb({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function oc(a){return this.to(tb(),a)}function pc(a){var b;return void 0===a?this._locale._abbr:(b=bb(a),null!=b&&(this._locale=b),this)}function qc(){return this._locale}function rc(a){switch(a=K(a)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":case"date":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}function sc(a){return a=K(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function tc(){return this._d.valueOf()-6e4*(this._offset||0)}function uc(){return Math.floor(this.valueOf()/1e3)}function vc(){return new Date(this.valueOf())}function wc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function xc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function yc(){return this.isValid()?this.toISOString():null}function zc(){return o(this)}function Ac(){
-return k({},n(this))}function Bc(){return n(this).overflow}function Cc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function Dc(a,b){U(0,[a,a.length],0,b)}function Ec(a){return Ic.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function Fc(a){return Ic.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Gc(){return xa(this.year(),1,4)}function Hc(){var a=this.localeData()._week;return xa(this.year(),a.dow,a.doy)}function Ic(a,b,c,d,e){var f;return null==a?wa(this,d,e).year:(f=xa(a,d,e),b>f&&(b=f),Jc.call(this,a,b,c,d,e))}function Jc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this}function Kc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)}function Lc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Mc(a,b){b[ke]=u(1e3*("0."+a))}function Nc(){return this._isUTC?"UTC":""}function Oc(){return this._isUTC?"Coordinated Universal Time":""}function Pc(a){return tb(1e3*a)}function Qc(){return tb.apply(null,arguments).parseZone()}function Rc(a){return a}function Sc(a,b,c,d){var e=bb(),f=l().set(d,b);return e[c](f,a)}function Tc(a,b,c){if(g(a)&&(b=a,a=void 0),a=a||"",null!=b)return Sc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Sc(a,d,c,"month");return e}function Uc(a,b,c,d){"boolean"==typeof a?(g(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,g(b)&&(c=b,b=void 0),b=b||"");var e=bb(),f=a?e._week.dow:0;if(null!=c)return Sc(b,(c+f)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Sc(b,(h+f)%7,d,"day");return i}function Vc(a,b){return Tc(a,b,"months")}function Wc(a,b){return Tc(a,b,"monthsShort")}function Xc(a,b,c){return Uc(a,b,c,"weekdays")}function Yc(a,b,c){return Uc(a,b,c,"weekdaysShort")}function Zc(a,b,c){return Uc(a,b,c,"weekdaysMin")}function $c(){var a=this._data;return this._milliseconds=df(this._milliseconds),this._days=df(this._days),this._months=df(this._months),a.milliseconds=df(a.milliseconds),a.seconds=df(a.seconds),a.minutes=df(a.minutes),a.hours=df(a.hours),a.months=df(a.months),a.years=df(a.years),this}function _c(a,b,c,d){var e=Sb(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function ad(a,b){return _c(this,a,b,1)}function bd(a,b){return _c(this,a,b,-1)}function cd(a){return a<0?Math.floor(a):Math.ceil(a)}function dd(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*cd(fd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ed(g)),h+=e,g-=cd(fd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ed(a){return 4800*a/146097}function fd(a){return 146097*a/4800}function gd(a){if(!this.isValid())return NaN;var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ed(b),"month"===a?c:c/12;switch(b=this._days+Math.round(fd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function hd(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12):NaN}function id(a){return function(){return this.as(a)}}function jd(a){return a=K(a),this.isValid()?this[a+"s"]():NaN}function kd(a){return function(){return this.isValid()?this._data[a]:NaN}}function ld(){return t(this.days()/7)}function md(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function nd(a,b,c){var d=Sb(a).abs(),e=uf(d.as("s")),f=uf(d.as("m")),g=uf(d.as("h")),h=uf(d.as("d")),i=uf(d.as("M")),j=uf(d.as("y")),k=e<=vf.ss&&["s",e]||e<vf.s&&["ss",e]||f<=1&&["m"]||f<vf.m&&["mm",f]||g<=1&&["h"]||g<vf.h&&["hh",g]||h<=1&&["d"]||h<vf.d&&["dd",h]||i<=1&&["M"]||i<vf.M&&["MM",i]||j<=1&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,md.apply(null,k)}function od(a){return void 0===a?uf:"function"==typeof a&&(uf=a,!0)}function pd(a,b){return void 0!==vf[a]&&(void 0===b?vf[a]:(vf[a]=b,"s"===a&&(vf.ss=b-1),!0))}function qd(a){if(!this.isValid())return this.localeData().invalidDate();var b=this.localeData(),c=nd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function rd(){if(!this.isValid())return this.localeData().invalidDate();var a,b,c,d=wf(this._milliseconds)/1e3,e=wf(this._days),f=wf(this._months);a=t(d/60),b=t(a/60),d%=60,a%=60,c=t(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var sd,td;td=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d<c;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1};var ud=td,vd=a.momentProperties=[],wd=!1,xd={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var yd;yd=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)j(a,b)&&c.push(b);return c};var zd,Ad=yd,Bd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Cd={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Dd="Invalid date",Ed="%d",Fd=/\d{1,2}/,Gd={future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d 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"},Hd={},Id={},Jd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Kd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Ld={},Md={},Nd=/\d/,Od=/\d\d/,Pd=/\d{3}/,Qd=/\d{4}/,Rd=/[+-]?\d{6}/,Sd=/\d\d?/,Td=/\d\d\d\d?/,Ud=/\d\d\d\d\d\d?/,Vd=/\d{1,3}/,Wd=/\d{1,4}/,Xd=/[+-]?\d{1,6}/,Yd=/\d+/,Zd=/[+-]?\d+/,$d=/Z|[+-]\d\d:?\d\d/gi,_d=/Z|[+-]\d\d(?::?\d\d)?/gi,ae=/[+-]?\d+(\.\d{1,3})?/,be=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,ce={},de={},ee=0,fe=1,ge=2,he=3,ie=4,je=5,ke=6,le=7,me=8;zd=Array.prototype.indexOf?Array.prototype.indexOf:function(a){var b;for(b=0;b<this.length;++b)if(this[b]===a)return b;return-1};var ne=zd;U("M",["MM",2],"Mo",function(){return this.month()+1}),U("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),U("MMMM",0,0,function(a){return this.localeData().months(this,a)}),J("month","M"),M("month",8),Z("M",Sd),Z("MM",Sd,Od),Z("MMM",function(a,b){return b.monthsShortRegex(a)}),Z("MMMM",function(a,b){return b.monthsRegex(a)}),ba(["M","MM"],function(a,b){b[fe]=u(a)-1}),ba(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[fe]=e:n(c).invalidMonth=a});var oe=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,pe="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),qe="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),re=be,se=be;U("Y",0,0,function(){var a=this.year();return a<=9999?""+a:"+"+a}),U(0,["YY",2],0,function(){return this.year()%100}),U(0,["YYYY",4],0,"year"),U(0,["YYYYY",5],0,"year"),U(0,["YYYYYY",6,!0],0,"year"),J("year","y"),M("year",1),Z("Y",Zd),Z("YY",Sd,Od),Z("YYYY",Wd,Qd),Z("YYYYY",Xd,Rd),Z("YYYYYY",Xd,Rd),ba(["YYYYY","YYYYYY"],ee),ba("YYYY",function(b,c){c[ee]=2===b.length?a.parseTwoDigitYear(b):u(b)}),ba("YY",function(b,c){c[ee]=a.parseTwoDigitYear(b)}),ba("Y",function(a,b){b[ee]=parseInt(a,10)}),a.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)};var te=O("FullYear",!0);U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"),J("week","w"),J("isoWeek","W"),M("week",5),M("isoWeek",5),Z("w",Sd),Z("ww",Sd,Od),Z("W",Sd),Z("WW",Sd,Od),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var ue={dow:0,doy:6};U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"),J("day","d"),J("weekday","e"),J("isoWeekday","E"),M("day",11),M("weekday",11),M("isoWeekday",11),Z("d",Sd),Z("e",Sd),Z("E",Sd),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict);null!=e?b.d=e:n(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)});var ve="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),we="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),xe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ye=be,ze=be,Ae=be;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1),J("hour","h"),M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Sd),Z("h",Sd),Z("k",Sd),Z("HH",Sd,Od),Z("hh",Sd,Od),Z("kk",Sd,Od),Z("hmm",Td),Z("hmmss",Ud),Z("Hmm",Td),Z("Hmmss",Ud),ba(["H","HH"],he),ba(["k","kk"],function(a,b,c){var d=u(a);b[he]=24===d?0:d}),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[he]=u(a),n(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d)),n(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d,2)),b[je]=u(a.substr(e)),n(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d,2)),b[je]=u(a.substr(e))});var Be,Ce=/[ap]\.?m?\.?/i,De=O("Hours",!0),Ee={calendar:Bd,longDateFormat:Cd,invalidDate:Dd,ordinal:Ed,dayOfMonthOrdinalParse:Fd,relativeTime:Gd,months:pe,monthsShort:qe,week:ue,weekdays:ve,weekdaysMin:xe,weekdaysShort:we,meridiemParse:Ce},Fe={},Ge={},He=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ie=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Je=/Z|[+-]\d\d(?::?\d\d)?/,Ke=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Le=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Me=/^\/?Date\((\-?\d+)/i,Ne=/^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;a.createFromInputFallback=x("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),a.ISO_8601=function(){},a.RFC_2822=function(){};var Oe=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=tb.apply(null,arguments);return this.isValid()&&a.isValid()?a<this?this:a:p()}),Pe=x("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=tb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:p()}),Qe=function(){return Date.now?Date.now():+new Date},Re=["year","quarter","month","week","day","hour","minute","second","millisecond"];Db("Z",":"),Db("ZZ",""),Z("Z",_d),Z("ZZ",_d),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Eb(_d,a)});var Se=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var Te=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Ue=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Sb.fn=Ab.prototype,Sb.invalid=zb;var Ve=Wb(1,"add"),We=Wb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Xe=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Dc("gggg","weekYear"),Dc("ggggg","weekYear"),Dc("GGGG","isoWeekYear"),Dc("GGGGG","isoWeekYear"),J("weekYear","gg"),J("isoWeekYear","GG"),M("weekYear",1),M("isoWeekYear",1),Z("G",Zd),Z("g",Zd),Z("GG",Sd,Od),Z("gg",Sd,Od),Z("GGGG",Wd,Qd),Z("gggg",Wd,Qd),Z("GGGGG",Xd,Rd),Z("ggggg",Xd,Rd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),U("Q",0,"Qo","quarter"),J("quarter","Q"),M("quarter",7),Z("Q",Nd),ba("Q",function(a,b){b[fe]=3*(u(a)-1)}),U("D",["DD",2],"Do","date"),J("date","D"),M("date",9),Z("D",Sd),Z("DD",Sd,Od),Z("Do",function(a,b){return a?b._dayOfMonthOrdinalParse||b._ordinalParse:b._dayOfMonthOrdinalParseLenient}),ba(["D","DD"],ge),ba("Do",function(a,b){b[ge]=u(a.match(Sd)[0],10)});var Ye=O("Date",!0);U("DDD",["DDDD",3],"DDDo","dayOfYear"),J("dayOfYear","DDD"),M("dayOfYear",4),Z("DDD",Vd),Z("DDDD",Pd),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}),U("m",["mm",2],0,"minute"),J("minute","m"),M("minute",14),Z("m",Sd),Z("mm",Sd,Od),ba(["m","mm"],ie);var Ze=O("Minutes",!1);U("s",["ss",2],0,"second"),J("second","s"),M("second",15),Z("s",Sd),Z("ss",Sd,Od),ba(["s","ss"],je);var $e=O("Seconds",!1);U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),J("millisecond","ms"),M("millisecond",16),Z("S",Vd,Nd),Z("SS",Vd,Od),Z("SSS",Vd,Pd);var _e;for(_e="SSSS";_e.length<=9;_e+="S")Z(_e,Yd);for(_e="S";_e.length<=9;_e+="S")ba(_e,Mc);var af=O("Milliseconds",!1);U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var bf=r.prototype;bf.add=Ve,bf.calendar=Zb,bf.clone=$b,bf.diff=fc,bf.endOf=sc,bf.format=kc,bf.from=lc,bf.fromNow=mc,bf.to=nc,bf.toNow=oc,bf.get=R,bf.invalidAt=Bc,bf.isAfter=_b,bf.isBefore=ac,bf.isBetween=bc,bf.isSame=cc,bf.isSameOrAfter=dc,bf.isSameOrBefore=ec,bf.isValid=zc,bf.lang=Xe,bf.locale=pc,bf.localeData=qc,bf.max=Pe,bf.min=Oe,bf.parsingFlags=Ac,bf.set=S,bf.startOf=rc,bf.subtract=We,bf.toArray=wc,bf.toObject=xc,bf.toDate=vc,bf.toISOString=ic,bf.inspect=jc,bf.toJSON=yc,bf.toString=hc,bf.unix=uc,bf.valueOf=tc,bf.creationData=Cc,bf.year=te,bf.isLeapYear=ra,bf.weekYear=Ec,bf.isoWeekYear=Fc,bf.quarter=bf.quarters=Kc,bf.month=ka,bf.daysInMonth=la,bf.week=bf.weeks=Ba,bf.isoWeek=bf.isoWeeks=Ca,bf.weeksInYear=Hc,bf.isoWeeksInYear=Gc,bf.date=Ye,bf.day=bf.days=Ka,bf.weekday=La,bf.isoWeekday=Ma,bf.dayOfYear=Lc,bf.hour=bf.hours=De,bf.minute=bf.minutes=Ze,bf.second=bf.seconds=$e,bf.millisecond=bf.milliseconds=af,bf.utcOffset=Hb,bf.utc=Jb,bf.local=Kb,bf.parseZone=Lb,bf.hasAlignedHourOffset=Mb,bf.isDST=Nb,bf.isLocal=Pb,bf.isUtcOffset=Qb,bf.isUtc=Rb,bf.isUTC=Rb,bf.zoneAbbr=Nc,bf.zoneName=Oc,bf.dates=x("dates accessor is deprecated. Use date instead.",Ye),bf.months=x("months accessor is deprecated. Use month instead",ka),bf.years=x("years accessor is deprecated. Use year instead",te),bf.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Ib),bf.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Ob);var cf=C.prototype;cf.calendar=D,cf.longDateFormat=E,cf.invalidDate=F,cf.ordinal=G,cf.preparse=Rc,cf.postformat=Rc,cf.relativeTime=H,cf.pastFuture=I,cf.set=A,cf.months=fa,cf.monthsShort=ga,cf.monthsParse=ia,cf.monthsRegex=na,cf.monthsShortRegex=ma,cf.week=ya,cf.firstDayOfYear=Aa,cf.firstDayOfWeek=za,cf.weekdays=Fa,cf.weekdaysMin=Ha,cf.weekdaysShort=Ga,cf.weekdaysParse=Ja,cf.weekdaysRegex=Na,cf.weekdaysShortRegex=Oa,cf.weekdaysMinRegex=Pa,cf.isPM=Va,cf.meridiem=Wa,$a("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,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}}),a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var df=Math.abs,ef=id("ms"),ff=id("s"),gf=id("m"),hf=id("h"),jf=id("d"),kf=id("w"),lf=id("M"),mf=id("y"),nf=kd("milliseconds"),of=kd("seconds"),pf=kd("minutes"),qf=kd("hours"),rf=kd("days"),sf=kd("months"),tf=kd("years"),uf=Math.round,vf={ss:44,s:45,m:45,h:22,d:26,M:11},wf=Math.abs,xf=Ab.prototype;return xf.isValid=yb,xf.abs=$c,xf.add=ad,xf.subtract=bd,xf.as=gd,xf.asMilliseconds=ef,xf.asSeconds=ff,xf.asMinutes=gf,xf.asHours=hf,xf.asDays=jf,xf.asWeeks=kf,xf.asMonths=lf,xf.asYears=mf,xf.valueOf=hd,xf._bubble=dd,xf.get=jd,xf.milliseconds=nf,xf.seconds=of,xf.minutes=pf,xf.hours=qf,xf.days=rf,xf.weeks=ld,xf.months=sf,xf.years=tf,xf.humanize=qd,xf.toISOString=rd,xf.toString=rd,xf.toJSON=rd,xf.locale=pc,xf.localeData=qc,xf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",rd),xf.lang=Xe,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Zd),Z("X",ae),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.18.1",b(tb),a.fn=bf,a.min=vb,a.max=wb,a.now=Qe,a.utc=l,a.unix=Pc,a.months=Vc,a.isDate=h,a.locale=$a,a.invalid=p,a.duration=Sb,a.isMoment=s,a.weekdays=Xc,a.parseZone=Qc,a.localeData=bb,a.isDuration=Bb,a.monthsShort=Wc,a.weekdaysMin=Zc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Yc,a.normalizeUnits=K,a.relativeTimeRounding=od,a.relativeTimeThreshold=pd,a.calendarFormat=Yb,a.prototype=bf,a});
\ No newline at end of file
diff --git a/apps/static/js/plugins/demo/peity-demo.js b/apps/static/js/plugins/demo/peity-demo.js
deleted file mode 100644
index 93cb5a3e7..000000000
--- a/apps/static/js/plugins/demo/peity-demo.js
+++ /dev/null
@@ -1,33 +0,0 @@
-$(function() {
-    $("span.pie").peity("pie", {
-        fill: ['#1ab394', '#d7d7d7', '#ffffff']
-    })
-
-    $(".line").peity("line",{
-        fill: '#1ab394',
-        stroke:'#169c81',
-    })
-
-    $(".bar").peity("bar", {
-        fill: ["#1ab394", "#d7d7d7"]
-    })
-
-    $(".bar_dashboard").peity("bar", {
-        fill: ["#1ab394", "#d7d7d7"],
-        width:100
-    })
-
-    var updatingChart = $(".updating-chart").peity("line", { fill: '#1ab394',stroke:'#169c81', width: 64 })
-
-    setInterval(function() {
-        var random = Math.round(Math.random() * 10)
-        var values = updatingChart.text().split(",")
-        values.shift()
-        values.push(random)
-
-        updatingChart
-            .text(values.join(","))
-            .change()
-    }, 1000);
-
-});
diff --git a/apps/static/js/plugins/dropzone/dropzone.js b/apps/static/js/plugins/dropzone/dropzone.js
deleted file mode 100644
index 8e74afb6a..000000000
--- a/apps/static/js/plugins/dropzone/dropzone.js
+++ /dev/null
@@ -1,1841 +0,0 @@
-
-;(function(){
-
-    /**
-     * Require the module at `name`.
-     *
-     * @param {String} name
-     * @return {Object} exports
-     * @api public
-     */
-
-    function require(name) {
-        var module = require.modules[name];
-        if (!module) throw new Error('failed to require "' + name + '"');
-
-        if (!('exports' in module) && typeof module.definition === 'function') {
-            module.client = module.component = true;
-            module.definition.call(this, module.exports = {}, module);
-            delete module.definition;
-        }
-
-        return module.exports;
-    }
-
-    /**
-     * Registered modules.
-     */
-
-    require.modules = {};
-
-    /**
-     * Register module at `name` with callback `definition`.
-     *
-     * @param {String} name
-     * @param {Function} definition
-     * @api private
-     */
-
-    require.register = function (name, definition) {
-        require.modules[name] = {
-            definition: definition
-        };
-    };
-
-    /**
-     * Define a module's exports immediately with `exports`.
-     *
-     * @param {String} name
-     * @param {Generic} exports
-     * @api private
-     */
-
-    require.define = function (name, exports) {
-        require.modules[name] = {
-            exports: exports
-        };
-    };
-    require.register("component~emitter@1.1.2", function (exports, module) {
-
-        /**
-         * Expose `Emitter`.
-         */
-
-        module.exports = Emitter;
-
-        /**
-         * Initialize a new `Emitter`.
-         *
-         * @api public
-         */
-
-        function Emitter(obj) {
-            if (obj) return mixin(obj);
-        };
-
-        /**
-         * Mixin the emitter properties.
-         *
-         * @param {Object} obj
-         * @return {Object}
-         * @api private
-         */
-
-        function mixin(obj) {
-            for (var key in Emitter.prototype) {
-                obj[key] = Emitter.prototype[key];
-            }
-            return obj;
-        }
-
-        /**
-         * Listen on the given `event` with `fn`.
-         *
-         * @param {String} event
-         * @param {Function} fn
-         * @return {Emitter}
-         * @api public
-         */
-
-        Emitter.prototype.on =
-            Emitter.prototype.addEventListener = function(event, fn){
-                this._callbacks = this._callbacks || {};
-                (this._callbacks[event] = this._callbacks[event] || [])
-                    .push(fn);
-                return this;
-            };
-
-        /**
-         * Adds an `event` listener that will be invoked a single
-         * time then automatically removed.
-         *
-         * @param {String} event
-         * @param {Function} fn
-         * @return {Emitter}
-         * @api public
-         */
-
-        Emitter.prototype.once = function(event, fn){
-            var self = this;
-            this._callbacks = this._callbacks || {};
-
-            function on() {
-                self.off(event, on);
-                fn.apply(this, arguments);
-            }
-
-            on.fn = fn;
-            this.on(event, on);
-            return this;
-        };
-
-        /**
-         * Remove the given callback for `event` or all
-         * registered callbacks.
-         *
-         * @param {String} event
-         * @param {Function} fn
-         * @return {Emitter}
-         * @api public
-         */
-
-        Emitter.prototype.off =
-            Emitter.prototype.removeListener =
-                Emitter.prototype.removeAllListeners =
-                    Emitter.prototype.removeEventListener = function(event, fn){
-                        this._callbacks = this._callbacks || {};
-
-                        // all
-                        if (0 == arguments.length) {
-                            this._callbacks = {};
-                            return this;
-                        }
-
-                        // specific event
-                        var callbacks = this._callbacks[event];
-                        if (!callbacks) return this;
-
-                        // remove all handlers
-                        if (1 == arguments.length) {
-                            delete this._callbacks[event];
-                            return this;
-                        }
-
-                        // remove specific handler
-                        var cb;
-                        for (var i = 0; i < callbacks.length; i++) {
-                            cb = callbacks[i];
-                            if (cb === fn || cb.fn === fn) {
-                                callbacks.splice(i, 1);
-                                break;
-                            }
-                        }
-                        return this;
-                    };
-
-        /**
-         * Emit `event` with the given args.
-         *
-         * @param {String} event
-         * @param {Mixed} ...
-         * @return {Emitter}
-         */
-
-        Emitter.prototype.emit = function(event){
-            this._callbacks = this._callbacks || {};
-            var args = [].slice.call(arguments, 1)
-                , callbacks = this._callbacks[event];
-
-            if (callbacks) {
-                callbacks = callbacks.slice(0);
-                for (var i = 0, len = callbacks.length; i < len; ++i) {
-                    callbacks[i].apply(this, args);
-                }
-            }
-
-            return this;
-        };
-
-        /**
-         * Return array of callbacks for `event`.
-         *
-         * @param {String} event
-         * @return {Array}
-         * @api public
-         */
-
-        Emitter.prototype.listeners = function(event){
-            this._callbacks = this._callbacks || {};
-            return this._callbacks[event] || [];
-        };
-
-        /**
-         * Check if this emitter has `event` handlers.
-         *
-         * @param {String} event
-         * @return {Boolean}
-         * @api public
-         */
-
-        Emitter.prototype.hasListeners = function(event){
-            return !! this.listeners(event).length;
-        };
-
-    });
-
-    require.register("dropzone", function (exports, module) {
-
-
-        /**
-         * Exposing dropzone
-         */
-        module.exports = require("dropzone/lib/dropzone.js");
-
-    });
-
-    require.register("dropzone/lib/dropzone.js", function (exports, module) {
-
-        /*
-         *
-         * More info at [www.dropzonejs.com](http://www.dropzonejs.com)
-         *
-         * Copyright (c) 2012, Matias Meno
-         *
-         * 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() {
-            var Dropzone, Em, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without,
-                __hasProp = {}.hasOwnProperty,
-                __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
-                __slice = [].slice;
-
-            Em = typeof Emitter !== "undefined" && Emitter !== null ? Emitter : require("component~emitter@1.1.2");
-
-            noop = function() {};
-
-            Dropzone = (function(_super) {
-                var extend;
-
-                __extends(Dropzone, _super);
-
-
-                /*
-                 This is a list of all available events you can register on a dropzone object.
-
-                 You can register an event handler like this:
-
-                 dropzone.on("dragEnter", function() { });
-                 */
-
-                Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached"];
-
-                Dropzone.prototype.defaultOptions = {
-                    url: null,
-                    method: "post",
-                    withCredentials: false,
-                    parallelUploads: 2,
-                    uploadMultiple: false,
-                    maxFilesize: 256,
-                    paramName: "file",
-                    createImageThumbnails: true,
-                    maxThumbnailFilesize: 10,
-                    thumbnailWidth: 100,
-                    thumbnailHeight: 100,
-                    maxFiles: null,
-                    params: {},
-                    clickable: true,
-                    ignoreHiddenFiles: true,
-                    acceptedFiles: null,
-                    acceptedMimeTypes: null,
-                    autoProcessQueue: true,
-                    autoQueue: true,
-                    addRemoveLinks: false,
-                    previewsContainer: null,
-                    dictDefaultMessage: "Drop files here to upload",
-                    dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.",
-                    dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.",
-                    dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",
-                    dictInvalidFileType: "You can't upload files of this type.",
-                    dictResponseError: "Server responded with {{statusCode}} code.",
-                    dictCancelUpload: "Cancel upload",
-                    dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
-                    dictRemoveFile: "Remove file",
-                    dictRemoveFileConfirmation: null,
-                    dictMaxFilesExceeded: "You can not upload any more files.",
-                    accept: function(file, done) {
-                        return done();
-                    },
-                    init: function() {
-                        return noop;
-                    },
-                    forceFallback: false,
-                    fallback: function() {
-                        var child, messageElement, span, _i, _len, _ref;
-                        this.element.className = "" + this.element.className + " dz-browser-not-supported";
-                        _ref = this.element.getElementsByTagName("div");
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            child = _ref[_i];
-                            if (/(^| )dz-message($| )/.test(child.className)) {
-                                messageElement = child;
-                                child.className = "dz-message";
-                                continue;
-                            }
-                        }
-                        if (!messageElement) {
-                            messageElement = Dropzone.createElement("<div class=\"dz-message\"><span></span></div>");
-                            this.element.appendChild(messageElement);
-                        }
-                        span = messageElement.getElementsByTagName("span")[0];
-                        if (span) {
-                            span.textContent = this.options.dictFallbackMessage;
-                        }
-                        return this.element.appendChild(this.getFallbackForm());
-                    },
-                    resize: function(file) {
-                        var info, srcRatio, trgRatio;
-                        info = {
-                            srcX: 0,
-                            srcY: 0,
-                            srcWidth: file.width,
-                            srcHeight: file.height
-                        };
-                        srcRatio = file.width / file.height;
-                        trgRatio = this.options.thumbnailWidth / this.options.thumbnailHeight;
-                        if (file.height < this.options.thumbnailHeight || file.width < this.options.thumbnailWidth) {
-                            info.trgHeight = info.srcHeight;
-                            info.trgWidth = info.srcWidth;
-                        } else {
-                            if (srcRatio > trgRatio) {
-                                info.srcHeight = file.height;
-                                info.srcWidth = info.srcHeight * trgRatio;
-                            } else {
-                                info.srcWidth = file.width;
-                                info.srcHeight = info.srcWidth / trgRatio;
-                            }
-                        }
-                        info.srcX = (file.width - info.srcWidth) / 2;
-                        info.srcY = (file.height - info.srcHeight) / 2;
-                        return info;
-                    },
-
-                    /*
-                     Those functions register themselves to the events on init and handle all
-                     the user interface specific stuff. Overwriting them won't break the upload
-                     but can break the way it's displayed.
-                     You can overwrite them if you don't like the default behavior. If you just
-                     want to add an additional event handler, register it on the dropzone object
-                     and don't overwrite those options.
-                     */
-                    drop: function(e) {
-                        return this.element.classList.remove("dz-drag-hover");
-                    },
-                    dragstart: noop,
-                    dragend: function(e) {
-                        return this.element.classList.remove("dz-drag-hover");
-                    },
-                    dragenter: function(e) {
-                        return this.element.classList.add("dz-drag-hover");
-                    },
-                    dragover: function(e) {
-                        return this.element.classList.add("dz-drag-hover");
-                    },
-                    dragleave: function(e) {
-                        return this.element.classList.remove("dz-drag-hover");
-                    },
-                    paste: noop,
-                    reset: function() {
-                        return this.element.classList.remove("dz-started");
-                    },
-                    addedfile: function(file) {
-                        var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results;
-                        if (this.element === this.previewsContainer) {
-                            this.element.classList.add("dz-started");
-                        }
-                        file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());
-                        file.previewTemplate = file.previewElement;
-                        this.previewsContainer.appendChild(file.previewElement);
-                        _ref = file.previewElement.querySelectorAll("[data-dz-name]");
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            node = _ref[_i];
-                            node.textContent = file.name;
-                        }
-                        _ref1 = file.previewElement.querySelectorAll("[data-dz-size]");
-                        for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
-                            node = _ref1[_j];
-                            node.innerHTML = this.filesize(file.size);
-                        }
-                        if (this.options.addRemoveLinks) {
-                            file._removeLink = Dropzone.createElement("<a class=\"dz-remove\" href=\"javascript:undefined;\" data-dz-remove>" + this.options.dictRemoveFile + "</a>");
-                            file.previewElement.appendChild(file._removeLink);
-                        }
-                        removeFileEvent = (function(_this) {
-                            return function(e) {
-                                e.preventDefault();
-                                e.stopPropagation();
-                                if (file.status === Dropzone.UPLOADING) {
-                                    return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() {
-                                        return _this.removeFile(file);
-                                    });
-                                } else {
-                                    if (_this.options.dictRemoveFileConfirmation) {
-                                        return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() {
-                                            return _this.removeFile(file);
-                                        });
-                                    } else {
-                                        return _this.removeFile(file);
-                                    }
-                                }
-                            };
-                        })(this);
-                        _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]");
-                        _results = [];
-                        for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-                            removeLink = _ref2[_k];
-                            _results.push(removeLink.addEventListener("click", removeFileEvent));
-                        }
-                        return _results;
-                    },
-                    removedfile: function(file) {
-                        var _ref;
-                        if ((_ref = file.previewElement) != null) {
-                            _ref.parentNode.removeChild(file.previewElement);
-                        }
-                        return this._updateMaxFilesReachedClass();
-                    },
-                    thumbnail: function(file, dataUrl) {
-                        var thumbnailElement, _i, _len, _ref, _results;
-                        file.previewElement.classList.remove("dz-file-preview");
-                        file.previewElement.classList.add("dz-image-preview");
-                        _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]");
-                        _results = [];
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            thumbnailElement = _ref[_i];
-                            thumbnailElement.alt = file.name;
-                            _results.push(thumbnailElement.src = dataUrl);
-                        }
-                        return _results;
-                    },
-                    error: function(file, message) {
-                        var node, _i, _len, _ref, _results;
-                        file.previewElement.classList.add("dz-error");
-                        if (typeof message !== "String" && message.error) {
-                            message = message.error;
-                        }
-                        _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]");
-                        _results = [];
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            node = _ref[_i];
-                            _results.push(node.textContent = message);
-                        }
-                        return _results;
-                    },
-                    errormultiple: noop,
-                    processing: function(file) {
-                        file.previewElement.classList.add("dz-processing");
-                        if (file._removeLink) {
-                            return file._removeLink.textContent = this.options.dictCancelUpload;
-                        }
-                    },
-                    processingmultiple: noop,
-                    uploadprogress: function(file, progress, bytesSent) {
-                        var node, _i, _len, _ref, _results;
-                        _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]");
-                        _results = [];
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            node = _ref[_i];
-                            _results.push(node.style.width = "" + progress + "%");
-                        }
-                        return _results;
-                    },
-                    totaluploadprogress: noop,
-                    sending: noop,
-                    sendingmultiple: noop,
-                    success: function(file) {
-                        return file.previewElement.classList.add("dz-success");
-                    },
-                    successmultiple: noop,
-                    canceled: function(file) {
-                        return this.emit("error", file, "Upload canceled.");
-                    },
-                    canceledmultiple: noop,
-                    complete: function(file) {
-                        if (file._removeLink) {
-                            return file._removeLink.textContent = this.options.dictRemoveFile;
-                        }
-                    },
-                    completemultiple: noop,
-                    maxfilesexceeded: noop,
-                    maxfilesreached: noop,
-                    previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-details\">\n    <div class=\"dz-filename\"><span data-dz-name></span></div>\n    <div class=\"dz-size\" data-dz-size></div>\n    <img data-dz-thumbnail />\n  </div>\n  <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n  <div class=\"dz-success-mark\"><span>✔</span></div>\n  <div class=\"dz-error-mark\"><span>✘</span></div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>"
-                };
-
-                extend = function() {
-                    var key, object, objects, target, val, _i, _len;
-                    target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
-                    for (_i = 0, _len = objects.length; _i < _len; _i++) {
-                        object = objects[_i];
-                        for (key in object) {
-                            val = object[key];
-                            target[key] = val;
-                        }
-                    }
-                    return target;
-                };
-
-                function Dropzone(element, options) {
-                    var elementOptions, fallback, _ref;
-                    this.element = element;
-                    this.version = Dropzone.version;
-                    this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
-                    this.clickableElements = [];
-                    this.listeners = [];
-                    this.files = [];
-                    if (typeof this.element === "string") {
-                        this.element = document.querySelector(this.element);
-                    }
-                    if (!(this.element && (this.element.nodeType != null))) {
-                        throw new Error("Invalid dropzone element.");
-                    }
-                    if (this.element.dropzone) {
-                        throw new Error("Dropzone already attached.");
-                    }
-                    Dropzone.instances.push(this);
-                    this.element.dropzone = this;
-                    elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {};
-                    this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
-                    if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
-                        return this.options.fallback.call(this);
-                    }
-                    if (this.options.url == null) {
-                        this.options.url = this.element.getAttribute("action");
-                    }
-                    if (!this.options.url) {
-                        throw new Error("No URL provided.");
-                    }
-                    if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
-                        throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
-                    }
-                    if (this.options.acceptedMimeTypes) {
-                        this.options.acceptedFiles = this.options.acceptedMimeTypes;
-                        delete this.options.acceptedMimeTypes;
-                    }
-                    this.options.method = this.options.method.toUpperCase();
-                    if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
-                        fallback.parentNode.removeChild(fallback);
-                    }
-                    if (this.options.previewsContainer) {
-                        this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
-                    } else {
-                        this.previewsContainer = this.element;
-                    }
-                    if (this.options.clickable) {
-                        if (this.options.clickable === true) {
-                            this.clickableElements = [this.element];
-                        } else {
-                            this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
-                        }
-                    }
-                    this.init();
-                }
-
-                Dropzone.prototype.getAcceptedFiles = function() {
-                    var file, _i, _len, _ref, _results;
-                    _ref = this.files;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        if (file.accepted) {
-                            _results.push(file);
-                        }
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.getRejectedFiles = function() {
-                    var file, _i, _len, _ref, _results;
-                    _ref = this.files;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        if (!file.accepted) {
-                            _results.push(file);
-                        }
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.getFilesWithStatus = function(status) {
-                    var file, _i, _len, _ref, _results;
-                    _ref = this.files;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        if (file.status === status) {
-                            _results.push(file);
-                        }
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.getQueuedFiles = function() {
-                    return this.getFilesWithStatus(Dropzone.QUEUED);
-                };
-
-                Dropzone.prototype.getUploadingFiles = function() {
-                    return this.getFilesWithStatus(Dropzone.UPLOADING);
-                };
-
-                Dropzone.prototype.getActiveFiles = function() {
-                    var file, _i, _len, _ref, _results;
-                    _ref = this.files;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) {
-                            _results.push(file);
-                        }
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.init = function() {
-                    var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1;
-                    if (this.element.tagName === "form") {
-                        this.element.setAttribute("enctype", "multipart/form-data");
-                    }
-                    if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
-                        this.element.appendChild(Dropzone.createElement("<div class=\"dz-default dz-message\" style=\"z-index:1;\"><span>" + this.options.dictDefaultMessage + "</span></div>"));
-                    }
-                    if (this.clickableElements.length) {
-                        setupHiddenFileInput = (function(_this) {
-                            return function() {
-                                if (_this.hiddenFileInput) {
-                                    document.body.removeChild(_this.hiddenFileInput);
-                                }
-                                _this.hiddenFileInput = document.createElement("input");
-                                _this.hiddenFileInput.setAttribute("type", "file");
-                                if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) {
-                                    _this.hiddenFileInput.setAttribute("multiple", "multiple");
-                                }
-                                _this.hiddenFileInput.className = "dz-hidden-input";
-                                if (_this.options.acceptedFiles != null) {
-                                    _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles);
-                                }
-                                _this.hiddenFileInput.style.visibility = "hidden";
-                                _this.hiddenFileInput.style.position = "absolute";
-                                _this.hiddenFileInput.style.top = "0";
-                                _this.hiddenFileInput.style.left = "0";
-                                _this.hiddenFileInput.style.height = "0";
-                                _this.hiddenFileInput.style.width = "0";
-                                document.body.appendChild(_this.hiddenFileInput);
-                                return _this.hiddenFileInput.addEventListener("change", function() {
-                                    var file, files, _i, _len;
-                                    files = _this.hiddenFileInput.files;
-                                    if (files.length) {
-                                        for (_i = 0, _len = files.length; _i < _len; _i++) {
-                                            file = files[_i];
-                                            _this.addFile(file);
-                                        }
-                                    }
-                                    return setupHiddenFileInput();
-                                });
-                            };
-                        })(this);
-                        setupHiddenFileInput();
-                    }
-                    this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL;
-                    _ref1 = this.events;
-                    for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
-                        eventName = _ref1[_i];
-                        this.on(eventName, this.options[eventName]);
-                    }
-                    this.on("uploadprogress", (function(_this) {
-                        return function() {
-                            return _this.updateTotalUploadProgress();
-                        };
-                    })(this));
-                    this.on("removedfile", (function(_this) {
-                        return function() {
-                            return _this.updateTotalUploadProgress();
-                        };
-                    })(this));
-                    this.on("canceled", (function(_this) {
-                        return function(file) {
-                            return _this.emit("complete", file);
-                        };
-                    })(this));
-                    this.on("complete", (function(_this) {
-                        return function(file) {
-                            if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) {
-                                return setTimeout((function() {
-                                    return _this.emit("queuecomplete");
-                                }), 0);
-                            }
-                        };
-                    })(this));
-                    noPropagation = function(e) {
-                        e.stopPropagation();
-                        if (e.preventDefault) {
-                            return e.preventDefault();
-                        } else {
-                            return e.returnValue = false;
-                        }
-                    };
-                    this.listeners = [
-                        {
-                            element: this.element,
-                            events: {
-                                "dragstart": (function(_this) {
-                                    return function(e) {
-                                        return _this.emit("dragstart", e);
-                                    };
-                                })(this),
-                                "dragenter": (function(_this) {
-                                    return function(e) {
-                                        noPropagation(e);
-                                        return _this.emit("dragenter", e);
-                                    };
-                                })(this),
-                                "dragover": (function(_this) {
-                                    return function(e) {
-                                        var efct;
-                                        try {
-                                            efct = e.dataTransfer.effectAllowed;
-                                        } catch (_error) {}
-                                        e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy';
-                                        noPropagation(e);
-                                        return _this.emit("dragover", e);
-                                    };
-                                })(this),
-                                "dragleave": (function(_this) {
-                                    return function(e) {
-                                        return _this.emit("dragleave", e);
-                                    };
-                                })(this),
-                                "drop": (function(_this) {
-                                    return function(e) {
-                                        noPropagation(e);
-                                        return _this.drop(e);
-                                    };
-                                })(this),
-                                "dragend": (function(_this) {
-                                    return function(e) {
-                                        return _this.emit("dragend", e);
-                                    };
-                                })(this)
-                            }
-                        }
-                    ];
-                    this.clickableElements.forEach((function(_this) {
-                        return function(clickableElement) {
-                            return _this.listeners.push({
-                                element: clickableElement,
-                                events: {
-                                    "click": function(evt) {
-                                        if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) {
-                                            return _this.hiddenFileInput.click();
-                                        }
-                                    }
-                                }
-                            });
-                        };
-                    })(this));
-                    this.enable();
-                    return this.options.init.call(this);
-                };
-
-                Dropzone.prototype.destroy = function() {
-                    var _ref;
-                    this.disable();
-                    this.removeAllFiles(true);
-                    if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) {
-                        this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
-                        this.hiddenFileInput = null;
-                    }
-                    delete this.element.dropzone;
-                    return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);
-                };
-
-                Dropzone.prototype.updateTotalUploadProgress = function() {
-                    var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref;
-                    totalBytesSent = 0;
-                    totalBytes = 0;
-                    activeFiles = this.getActiveFiles();
-                    if (activeFiles.length) {
-                        _ref = this.getActiveFiles();
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            file = _ref[_i];
-                            totalBytesSent += file.upload.bytesSent;
-                            totalBytes += file.upload.total;
-                        }
-                        totalUploadProgress = 100 * totalBytesSent / totalBytes;
-                    } else {
-                        totalUploadProgress = 100;
-                    }
-                    return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
-                };
-
-                Dropzone.prototype.getFallbackForm = function() {
-                    var existingFallback, fields, fieldsString, form;
-                    if (existingFallback = this.getExistingFallback()) {
-                        return existingFallback;
-                    }
-                    fieldsString = "<div class=\"dz-fallback\">";
-                    if (this.options.dictFallbackText) {
-                        fieldsString += "<p>" + this.options.dictFallbackText + "</p>";
-                    }
-                    fieldsString += "<input type=\"file\" name=\"" + this.options.paramName + (this.options.uploadMultiple ? "[]" : "") + "\" " + (this.options.uploadMultiple ? 'multiple="multiple"' : void 0) + " /><input type=\"submit\" value=\"Upload!\"></div>";
-                    fields = Dropzone.createElement(fieldsString);
-                    if (this.element.tagName !== "FORM") {
-                        form = Dropzone.createElement("<form action=\"" + this.options.url + "\" enctype=\"multipart/form-data\" method=\"" + this.options.method + "\"></form>");
-                        form.appendChild(fields);
-                    } else {
-                        this.element.setAttribute("enctype", "multipart/form-data");
-                        this.element.setAttribute("method", this.options.method);
-                    }
-                    return form != null ? form : fields;
-                };
-
-                Dropzone.prototype.getExistingFallback = function() {
-                    var fallback, getFallback, tagName, _i, _len, _ref;
-                    getFallback = function(elements) {
-                        var el, _i, _len;
-                        for (_i = 0, _len = elements.length; _i < _len; _i++) {
-                            el = elements[_i];
-                            if (/(^| )fallback($| )/.test(el.className)) {
-                                return el;
-                            }
-                        }
-                    };
-                    _ref = ["div", "form"];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        tagName = _ref[_i];
-                        if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
-                            return fallback;
-                        }
-                    }
-                };
-
-                Dropzone.prototype.setupEventListeners = function() {
-                    var elementListeners, event, listener, _i, _len, _ref, _results;
-                    _ref = this.listeners;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        elementListeners = _ref[_i];
-                        _results.push((function() {
-                            var _ref1, _results1;
-                            _ref1 = elementListeners.events;
-                            _results1 = [];
-                            for (event in _ref1) {
-                                listener = _ref1[event];
-                                _results1.push(elementListeners.element.addEventListener(event, listener, false));
-                            }
-                            return _results1;
-                        })());
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.removeEventListeners = function() {
-                    var elementListeners, event, listener, _i, _len, _ref, _results;
-                    _ref = this.listeners;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        elementListeners = _ref[_i];
-                        _results.push((function() {
-                            var _ref1, _results1;
-                            _ref1 = elementListeners.events;
-                            _results1 = [];
-                            for (event in _ref1) {
-                                listener = _ref1[event];
-                                _results1.push(elementListeners.element.removeEventListener(event, listener, false));
-                            }
-                            return _results1;
-                        })());
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.disable = function() {
-                    var file, _i, _len, _ref, _results;
-                    this.clickableElements.forEach(function(element) {
-                        return element.classList.remove("dz-clickable");
-                    });
-                    this.removeEventListeners();
-                    _ref = this.files;
-                    _results = [];
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        _results.push(this.cancelUpload(file));
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype.enable = function() {
-                    this.clickableElements.forEach(function(element) {
-                        return element.classList.add("dz-clickable");
-                    });
-                    return this.setupEventListeners();
-                };
-
-                Dropzone.prototype.filesize = function(size) {
-                    var string;
-                    if (size >= 1024 * 1024 * 1024 * 1024 / 10) {
-                        size = size / (1024 * 1024 * 1024 * 1024 / 10);
-                        string = "TiB";
-                    } else if (size >= 1024 * 1024 * 1024 / 10) {
-                        size = size / (1024 * 1024 * 1024 / 10);
-                        string = "GiB";
-                    } else if (size >= 1024 * 1024 / 10) {
-                        size = size / (1024 * 1024 / 10);
-                        string = "MiB";
-                    } else if (size >= 1024 / 10) {
-                        size = size / (1024 / 10);
-                        string = "KiB";
-                    } else {
-                        size = size * 10;
-                        string = "b";
-                    }
-                    return "<strong>" + (Math.round(size) / 10) + "</strong> " + string;
-                };
-
-                Dropzone.prototype._updateMaxFilesReachedClass = function() {
-                    if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
-                        if (this.getAcceptedFiles().length === this.options.maxFiles) {
-                            this.emit('maxfilesreached', this.files);
-                        }
-                        return this.element.classList.add("dz-max-files-reached");
-                    } else {
-                        return this.element.classList.remove("dz-max-files-reached");
-                    }
-                };
-
-                Dropzone.prototype.drop = function(e) {
-                    var files, items;
-                    if (!e.dataTransfer) {
-                        return;
-                    }
-                    this.emit("drop", e);
-                    files = e.dataTransfer.files;
-                    if (files.length) {
-                        items = e.dataTransfer.items;
-                        if (items && items.length && (items[0].webkitGetAsEntry != null)) {
-                            this._addFilesFromItems(items);
-                        } else {
-                            this.handleFiles(files);
-                        }
-                    }
-                };
-
-                Dropzone.prototype.paste = function(e) {
-                    var items, _ref;
-                    if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) {
-                        return;
-                    }
-                    this.emit("paste", e);
-                    items = e.clipboardData.items;
-                    if (items.length) {
-                        return this._addFilesFromItems(items);
-                    }
-                };
-
-                Dropzone.prototype.handleFiles = function(files) {
-                    var file, _i, _len, _results;
-                    _results = [];
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        _results.push(this.addFile(file));
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype._addFilesFromItems = function(items) {
-                    var entry, item, _i, _len, _results;
-                    _results = [];
-                    for (_i = 0, _len = items.length; _i < _len; _i++) {
-                        item = items[_i];
-                        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
-                            if (entry.isFile) {
-                                _results.push(this.addFile(item.getAsFile()));
-                            } else if (entry.isDirectory) {
-                                _results.push(this._addFilesFromDirectory(entry, entry.name));
-                            } else {
-                                _results.push(void 0);
-                            }
-                        } else if (item.getAsFile != null) {
-                            if ((item.kind == null) || item.kind === "file") {
-                                _results.push(this.addFile(item.getAsFile()));
-                            } else {
-                                _results.push(void 0);
-                            }
-                        } else {
-                            _results.push(void 0);
-                        }
-                    }
-                    return _results;
-                };
-
-                Dropzone.prototype._addFilesFromDirectory = function(directory, path) {
-                    var dirReader, entriesReader;
-                    dirReader = directory.createReader();
-                    entriesReader = (function(_this) {
-                        return function(entries) {
-                            var entry, _i, _len;
-                            for (_i = 0, _len = entries.length; _i < _len; _i++) {
-                                entry = entries[_i];
-                                if (entry.isFile) {
-                                    entry.file(function(file) {
-                                        if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') {
-                                            return;
-                                        }
-                                        file.fullPath = "" + path + "/" + file.name;
-                                        return _this.addFile(file);
-                                    });
-                                } else if (entry.isDirectory) {
-                                    _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name);
-                                }
-                            }
-                        };
-                    })(this);
-                    return dirReader.readEntries(entriesReader, function(error) {
-                        return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0;
-                    });
-                };
-
-                Dropzone.prototype.accept = function(file, done) {
-                    if (file.size > this.options.maxFilesize * 1024 * 1024) {
-                        return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
-                    } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
-                        return done(this.options.dictInvalidFileType);
-                    } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
-                        done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
-                        return this.emit("maxfilesexceeded", file);
-                    } else {
-                        return this.options.accept.call(this, file, done);
-                    }
-                };
-
-                Dropzone.prototype.addFile = function(file) {
-                    file.upload = {
-                        progress: 0,
-                        total: file.size,
-                        bytesSent: 0
-                    };
-                    this.files.push(file);
-                    file.status = Dropzone.ADDED;
-                    this.emit("addedfile", file);
-                    this._enqueueThumbnail(file);
-                    return this.accept(file, (function(_this) {
-                        return function(error) {
-                            if (error) {
-                                file.accepted = false;
-                                _this._errorProcessing([file], error);
-                            } else {
-                                file.accepted = true;
-                                if (_this.options.autoQueue) {
-                                    _this.enqueueFile(file);
-                                }
-                            }
-                            return _this._updateMaxFilesReachedClass();
-                        };
-                    })(this));
-                };
-
-                Dropzone.prototype.enqueueFiles = function(files) {
-                    var file, _i, _len;
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        this.enqueueFile(file);
-                    }
-                    return null;
-                };
-
-                Dropzone.prototype.enqueueFile = function(file) {
-                    if (file.status === Dropzone.ADDED && file.accepted === true) {
-                        file.status = Dropzone.QUEUED;
-                        if (this.options.autoProcessQueue) {
-                            return setTimeout(((function(_this) {
-                                return function() {
-                                    return _this.processQueue();
-                                };
-                            })(this)), 0);
-                        }
-                    } else {
-                        throw new Error("This file can't be queued because it has already been processed or was rejected.");
-                    }
-                };
-
-                Dropzone.prototype._thumbnailQueue = [];
-
-                Dropzone.prototype._processingThumbnail = false;
-
-                Dropzone.prototype._enqueueThumbnail = function(file) {
-                    if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {
-                        this._thumbnailQueue.push(file);
-                        return setTimeout(((function(_this) {
-                            return function() {
-                                return _this._processThumbnailQueue();
-                            };
-                        })(this)), 0);
-                    }
-                };
-
-                Dropzone.prototype._processThumbnailQueue = function() {
-                    if (this._processingThumbnail || this._thumbnailQueue.length === 0) {
-                        return;
-                    }
-                    this._processingThumbnail = true;
-                    return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) {
-                        return function() {
-                            _this._processingThumbnail = false;
-                            return _this._processThumbnailQueue();
-                        };
-                    })(this));
-                };
-
-                Dropzone.prototype.removeFile = function(file) {
-                    if (file.status === Dropzone.UPLOADING) {
-                        this.cancelUpload(file);
-                    }
-                    this.files = without(this.files, file);
-                    this.emit("removedfile", file);
-                    if (this.files.length === 0) {
-                        return this.emit("reset");
-                    }
-                };
-
-                Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) {
-                    var file, _i, _len, _ref;
-                    if (cancelIfNecessary == null) {
-                        cancelIfNecessary = false;
-                    }
-                    _ref = this.files.slice();
-                    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                        file = _ref[_i];
-                        if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) {
-                            this.removeFile(file);
-                        }
-                    }
-                    return null;
-                };
-
-                Dropzone.prototype.createThumbnail = function(file, callback) {
-                    var fileReader;
-                    fileReader = new FileReader;
-                    fileReader.onload = (function(_this) {
-                        return function() {
-                            var img;
-                            img = document.createElement("img");
-                            img.onload = function() {
-                                var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3;
-                                file.width = img.width;
-                                file.height = img.height;
-                                resizeInfo = _this.options.resize.call(_this, file);
-                                if (resizeInfo.trgWidth == null) {
-                                    resizeInfo.trgWidth = _this.options.thumbnailWidth;
-                                }
-                                if (resizeInfo.trgHeight == null) {
-                                    resizeInfo.trgHeight = _this.options.thumbnailHeight;
-                                }
-                                canvas = document.createElement("canvas");
-                                ctx = canvas.getContext("2d");
-                                canvas.width = resizeInfo.trgWidth;
-                                canvas.height = resizeInfo.trgHeight;
-                                drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);
-                                thumbnail = canvas.toDataURL("image/png");
-                                _this.emit("thumbnail", file, thumbnail);
-                                if (callback != null) {
-                                    return callback();
-                                }
-                            };
-                            return img.src = fileReader.result;
-                        };
-                    })(this);
-                    return fileReader.readAsDataURL(file);
-                };
-
-                Dropzone.prototype.processQueue = function() {
-                    var i, parallelUploads, processingLength, queuedFiles;
-                    parallelUploads = this.options.parallelUploads;
-                    processingLength = this.getUploadingFiles().length;
-                    i = processingLength;
-                    if (processingLength >= parallelUploads) {
-                        return;
-                    }
-                    queuedFiles = this.getQueuedFiles();
-                    if (!(queuedFiles.length > 0)) {
-                        return;
-                    }
-                    if (this.options.uploadMultiple) {
-                        return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
-                    } else {
-                        while (i < parallelUploads) {
-                            if (!queuedFiles.length) {
-                                return;
-                            }
-                            this.processFile(queuedFiles.shift());
-                            i++;
-                        }
-                    }
-                };
-
-                Dropzone.prototype.processFile = function(file) {
-                    return this.processFiles([file]);
-                };
-
-                Dropzone.prototype.processFiles = function(files) {
-                    var file, _i, _len;
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        file.processing = true;
-                        file.status = Dropzone.UPLOADING;
-                        this.emit("processing", file);
-                    }
-                    if (this.options.uploadMultiple) {
-                        this.emit("processingmultiple", files);
-                    }
-                    return this.uploadFiles(files);
-                };
-
-                Dropzone.prototype._getFilesWithXhr = function(xhr) {
-                    var file, files;
-                    return files = (function() {
-                        var _i, _len, _ref, _results;
-                        _ref = this.files;
-                        _results = [];
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            file = _ref[_i];
-                            if (file.xhr === xhr) {
-                                _results.push(file);
-                            }
-                        }
-                        return _results;
-                    }).call(this);
-                };
-
-                Dropzone.prototype.cancelUpload = function(file) {
-                    var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref;
-                    if (file.status === Dropzone.UPLOADING) {
-                        groupedFiles = this._getFilesWithXhr(file.xhr);
-                        for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) {
-                            groupedFile = groupedFiles[_i];
-                            groupedFile.status = Dropzone.CANCELED;
-                        }
-                        file.xhr.abort();
-                        for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) {
-                            groupedFile = groupedFiles[_j];
-                            this.emit("canceled", groupedFile);
-                        }
-                        if (this.options.uploadMultiple) {
-                            this.emit("canceledmultiple", groupedFiles);
-                        }
-                    } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) {
-                        file.status = Dropzone.CANCELED;
-                        this.emit("canceled", file);
-                        if (this.options.uploadMultiple) {
-                            this.emit("canceledmultiple", [file]);
-                        }
-                    }
-                    if (this.options.autoProcessQueue) {
-                        return this.processQueue();
-                    }
-                };
-
-                Dropzone.prototype.uploadFile = function(file) {
-                    return this.uploadFiles([file]);
-                };
-
-                Dropzone.prototype.uploadFiles = function(files) {
-                    var file, formData, handleError, headerName, headerValue, headers, input, inputName, inputType, key, option, progressObj, response, updateProgress, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4;
-                    xhr = new XMLHttpRequest();
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        file.xhr = xhr;
-                    }
-                    xhr.open(this.options.method, this.options.url, true);
-                    xhr.withCredentials = !!this.options.withCredentials;
-                    response = null;
-                    handleError = (function(_this) {
-                        return function() {
-                            var _j, _len1, _results;
-                            _results = [];
-                            for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
-                                file = files[_j];
-                                _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr));
-                            }
-                            return _results;
-                        };
-                    })(this);
-                    updateProgress = (function(_this) {
-                        return function(e) {
-                            var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results;
-                            if (e != null) {
-                                progress = 100 * e.loaded / e.total;
-                                for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
-                                    file = files[_j];
-                                    file.upload = {
-                                        progress: progress,
-                                        total: e.total,
-                                        bytesSent: e.loaded
-                                    };
-                                }
-                            } else {
-                                allFilesFinished = true;
-                                progress = 100;
-                                for (_k = 0, _len2 = files.length; _k < _len2; _k++) {
-                                    file = files[_k];
-                                    if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) {
-                                        allFilesFinished = false;
-                                    }
-                                    file.upload.progress = progress;
-                                    file.upload.bytesSent = file.upload.total;
-                                }
-                                if (allFilesFinished) {
-                                    return;
-                                }
-                            }
-                            _results = [];
-                            for (_l = 0, _len3 = files.length; _l < _len3; _l++) {
-                                file = files[_l];
-                                _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent));
-                            }
-                            return _results;
-                        };
-                    })(this);
-                    xhr.onload = (function(_this) {
-                        return function(e) {
-                            var _ref;
-                            if (files[0].status === Dropzone.CANCELED) {
-                                return;
-                            }
-                            if (xhr.readyState !== 4) {
-                                return;
-                            }
-                            response = xhr.responseText;
-                            if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("applications/json")) {
-                                try {
-                                    response = JSON.parse(response);
-                                } catch (_error) {
-                                    e = _error;
-                                    response = "Invalid JSON response from server.";
-                                }
-                            }
-                            updateProgress();
-                            if (!((200 <= (_ref = xhr.status) && _ref < 300))) {
-                                return handleError();
-                            } else {
-                                return _this._finished(files, response, e);
-                            }
-                        };
-                    })(this);
-                    xhr.onerror = (function(_this) {
-                        return function() {
-                            if (files[0].status === Dropzone.CANCELED) {
-                                return;
-                            }
-                            return handleError();
-                        };
-                    })(this);
-                    progressObj = (_ref = xhr.upload) != null ? _ref : xhr;
-                    progressObj.onprogress = updateProgress;
-                    headers = {
-                        "Accept": "applications/json",
-                        "Cache-Control": "no-cache",
-                        "X-Requested-With": "XMLHttpRequest"
-                    };
-                    if (this.options.headers) {
-                        extend(headers, this.options.headers);
-                    }
-                    for (headerName in headers) {
-                        headerValue = headers[headerName];
-                        xhr.setRequestHeader(headerName, headerValue);
-                    }
-                    formData = new FormData();
-                    if (this.options.params) {
-                        _ref1 = this.options.params;
-                        for (key in _ref1) {
-                            value = _ref1[key];
-                            formData.append(key, value);
-                        }
-                    }
-                    for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
-                        file = files[_j];
-                        this.emit("sending", file, xhr, formData);
-                    }
-                    if (this.options.uploadMultiple) {
-                        this.emit("sendingmultiple", files, xhr, formData);
-                    }
-                    if (this.element.tagName === "FORM") {
-                        _ref2 = this.element.querySelectorAll("input, textarea, select, button");
-                        for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-                            input = _ref2[_k];
-                            inputName = input.getAttribute("name");
-                            inputType = input.getAttribute("type");
-                            if (input.tagName === "SELECT" && input.hasAttribute("multiple")) {
-                                _ref3 = input.options;
-                                for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
-                                    option = _ref3[_l];
-                                    if (option.selected) {
-                                        formData.append(inputName, option.value);
-                                    }
-                                }
-                            } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) {
-                                formData.append(inputName, input.value);
-                            }
-                        }
-                    }
-                    for (_m = 0, _len4 = files.length; _m < _len4; _m++) {
-                        file = files[_m];
-                        formData.append("" + this.options.paramName + (this.options.uploadMultiple ? "[]" : ""), file, file.name);
-                    }
-                    return xhr.send(formData);
-                };
-
-                Dropzone.prototype._finished = function(files, responseText, e) {
-                    var file, _i, _len;
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        file.status = Dropzone.SUCCESS;
-                        this.emit("success", file, responseText, e);
-                        this.emit("complete", file);
-                    }
-                    if (this.options.uploadMultiple) {
-                        this.emit("successmultiple", files, responseText, e);
-                        this.emit("completemultiple", files);
-                    }
-                    if (this.options.autoProcessQueue) {
-                        return this.processQueue();
-                    }
-                };
-
-                Dropzone.prototype._errorProcessing = function(files, message, xhr) {
-                    var file, _i, _len;
-                    for (_i = 0, _len = files.length; _i < _len; _i++) {
-                        file = files[_i];
-                        file.status = Dropzone.ERROR;
-                        this.emit("error", file, message, xhr);
-                        this.emit("complete", file);
-                    }
-                    if (this.options.uploadMultiple) {
-                        this.emit("errormultiple", files, message, xhr);
-                        this.emit("completemultiple", files);
-                    }
-                    if (this.options.autoProcessQueue) {
-                        return this.processQueue();
-                    }
-                };
-
-                return Dropzone;
-
-            })(Em);
-
-            Dropzone.version = "3.8.7";
-
-            Dropzone.options = {};
-
-            Dropzone.optionsForElement = function(element) {
-                if (element.getAttribute("id")) {
-                    return Dropzone.options[camelize(element.getAttribute("id"))];
-                } else {
-                    return void 0;
-                }
-            };
-
-            Dropzone.instances = [];
-
-            Dropzone.forElement = function(element) {
-                if (typeof element === "string") {
-                    element = document.querySelector(element);
-                }
-                if ((element != null ? element.dropzone : void 0) == null) {
-                    throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");
-                }
-                return element.dropzone;
-            };
-
-            Dropzone.autoDiscover = true;
-
-            Dropzone.discover = function() {
-                var checkElements, dropzone, dropzones, _i, _len, _results;
-                if (document.querySelectorAll) {
-                    dropzones = document.querySelectorAll(".dropzone");
-                } else {
-                    dropzones = [];
-                    checkElements = function(elements) {
-                        var el, _i, _len, _results;
-                        _results = [];
-                        for (_i = 0, _len = elements.length; _i < _len; _i++) {
-                            el = elements[_i];
-                            if (/(^| )dropzone($| )/.test(el.className)) {
-                                _results.push(dropzones.push(el));
-                            } else {
-                                _results.push(void 0);
-                            }
-                        }
-                        return _results;
-                    };
-                    checkElements(document.getElementsByTagName("div"));
-                    checkElements(document.getElementsByTagName("form"));
-                }
-                _results = [];
-                for (_i = 0, _len = dropzones.length; _i < _len; _i++) {
-                    dropzone = dropzones[_i];
-                    if (Dropzone.optionsForElement(dropzone) !== false) {
-                        _results.push(new Dropzone(dropzone));
-                    } else {
-                        _results.push(void 0);
-                    }
-                }
-                return _results;
-            };
-
-            Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i];
-
-            Dropzone.isBrowserSupported = function() {
-                var capableBrowser, regex, _i, _len, _ref;
-                capableBrowser = true;
-                if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
-                    if (!("classList" in document.createElement("a"))) {
-                        capableBrowser = false;
-                    } else {
-                        _ref = Dropzone.blacklistedBrowsers;
-                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-                            regex = _ref[_i];
-                            if (regex.test(navigator.userAgent)) {
-                                capableBrowser = false;
-                                continue;
-                            }
-                        }
-                    }
-                } else {
-                    capableBrowser = false;
-                }
-                return capableBrowser;
-            };
-
-            without = function(list, rejectedItem) {
-                var item, _i, _len, _results;
-                _results = [];
-                for (_i = 0, _len = list.length; _i < _len; _i++) {
-                    item = list[_i];
-                    if (item !== rejectedItem) {
-                        _results.push(item);
-                    }
-                }
-                return _results;
-            };
-
-            camelize = function(str) {
-                return str.replace(/[\-_](\w)/g, function(match) {
-                    return match.charAt(1).toUpperCase();
-                });
-            };
-
-            Dropzone.createElement = function(string) {
-                var div;
-                div = document.createElement("div");
-                div.innerHTML = string;
-                return div.childNodes[0];
-            };
-
-            Dropzone.elementInside = function(element, container) {
-                if (element === container) {
-                    return true;
-                }
-                while (element = element.parentNode) {
-                    if (element === container) {
-                        return true;
-                    }
-                }
-                return false;
-            };
-
-            Dropzone.getElement = function(el, name) {
-                var element;
-                if (typeof el === "string") {
-                    element = document.querySelector(el);
-                } else if (el.nodeType != null) {
-                    element = el;
-                }
-                if (element == null) {
-                    throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element.");
-                }
-                return element;
-            };
-
-            Dropzone.getElements = function(els, name) {
-                var e, el, elements, _i, _j, _len, _len1, _ref;
-                if (els instanceof Array) {
-                    elements = [];
-                    try {
-                        for (_i = 0, _len = els.length; _i < _len; _i++) {
-                            el = els[_i];
-                            elements.push(this.getElement(el, name));
-                        }
-                    } catch (_error) {
-                        e = _error;
-                        elements = null;
-                    }
-                } else if (typeof els === "string") {
-                    elements = [];
-                    _ref = document.querySelectorAll(els);
-                    for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
-                        el = _ref[_j];
-                        elements.push(el);
-                    }
-                } else if (els.nodeType != null) {
-                    elements = [els];
-                }
-                if (!((elements != null) && elements.length)) {
-                    throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");
-                }
-                return elements;
-            };
-
-            Dropzone.confirm = function(question, accepted, rejected) {
-                if (window.confirm(question)) {
-                    return accepted();
-                } else if (rejected != null) {
-                    return rejected();
-                }
-            };
-
-            Dropzone.isValidFile = function(file, acceptedFiles) {
-                var baseMimeType, mimeType, validType, _i, _len;
-                if (!acceptedFiles) {
-                    return true;
-                }
-                acceptedFiles = acceptedFiles.split(",");
-                mimeType = file.type;
-                baseMimeType = mimeType.replace(/\/.*$/, "");
-                for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) {
-                    validType = acceptedFiles[_i];
-                    validType = validType.trim();
-                    if (validType.charAt(0) === ".") {
-                        if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {
-                            return true;
-                        }
-                    } else if (/\/\*$/.test(validType)) {
-                        if (baseMimeType === validType.replace(/\/.*$/, "")) {
-                            return true;
-                        }
-                    } else {
-                        if (mimeType === validType) {
-                            return true;
-                        }
-                    }
-                }
-                return false;
-            };
-
-            if (typeof jQuery !== "undefined" && jQuery !== null) {
-                jQuery.fn.dropzone = function(options) {
-                    return this.each(function() {
-                        return new Dropzone(this, options);
-                    });
-                };
-            }
-
-            if (typeof module !== "undefined" && module !== null) {
-                module.exports = Dropzone;
-            } else {
-                window.Dropzone = Dropzone;
-            }
-
-            Dropzone.ADDED = "added";
-
-            Dropzone.QUEUED = "queued";
-
-            Dropzone.ACCEPTED = Dropzone.QUEUED;
-
-            Dropzone.UPLOADING = "uploading";
-
-            Dropzone.PROCESSING = Dropzone.UPLOADING;
-
-            Dropzone.CANCELED = "canceled";
-
-            Dropzone.ERROR = "error";
-
-            Dropzone.SUCCESS = "success";
-
-
-            /*
-
-             Bugfix for iOS 6 and 7
-             Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
-             based on the work of https://github.com/stomita/ios-imagefile-megapixel
-             */
-
-            detectVerticalSquash = function(img) {
-                var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy;
-                iw = img.naturalWidth;
-                ih = img.naturalHeight;
-                canvas = document.createElement("canvas");
-                canvas.width = 1;
-                canvas.height = ih;
-                ctx = canvas.getContext("2d");
-                ctx.drawImage(img, 0, 0);
-                data = ctx.getImageData(0, 0, 1, ih).data;
-                sy = 0;
-                ey = ih;
-                py = ih;
-                while (py > sy) {
-                    alpha = data[(py - 1) * 4 + 3];
-                    if (alpha === 0) {
-                        ey = py;
-                    } else {
-                        sy = py;
-                    }
-                    py = (ey + sy) >> 1;
-                }
-                ratio = py / ih;
-                if (ratio === 0) {
-                    return 1;
-                } else {
-                    return ratio;
-                }
-            };
-
-            drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
-                var vertSquashRatio;
-                vertSquashRatio = detectVerticalSquash(img);
-                return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
-            };
-
-
-            /*
-             * contentloaded.js
-             *
-             * Author: Diego Perini (diego.perini at gmail.com)
-             * Summary: cross-browser wrapper for DOMContentLoaded
-             * Updated: 20101020
-             * License: MIT
-             * Version: 1.2
-             *
-             * URL:
-             * http://javascript.nwbox.com/ContentLoaded/
-             * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
-             */
-
-            contentLoaded = function(win, fn) {
-                var add, doc, done, init, poll, pre, rem, root, top;
-                done = false;
-                top = true;
-                doc = win.document;
-                root = doc.documentElement;
-                add = (doc.addEventListener ? "addEventListener" : "attachEvent");
-                rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
-                pre = (doc.addEventListener ? "" : "on");
-                init = function(e) {
-                    if (e.type === "readystatechange" && doc.readyState !== "complete") {
-                        return;
-                    }
-                    (e.type === "load" ? win : doc)[rem](pre + e.type, init, false);
-                    if (!done && (done = true)) {
-                        return fn.call(win, e.type || e);
-                    }
-                };
-                poll = function() {
-                    var e;
-                    try {
-                        root.doScroll("left");
-                    } catch (_error) {
-                        e = _error;
-                        setTimeout(poll, 50);
-                        return;
-                    }
-                    return init("poll");
-                };
-                if (doc.readyState !== "complete") {
-                    if (doc.createEventObject && root.doScroll) {
-                        try {
-                            top = !win.frameElement;
-                        } catch (_error) {}
-                        if (top) {
-                            poll();
-                        }
-                    }
-                    doc[add](pre + "DOMContentLoaded", init, false);
-                    doc[add](pre + "readystatechange", init, false);
-                    return win[add](pre + "load", init, false);
-                }
-            };
-
-            Dropzone._autoDiscoverFunction = function() {
-                if (Dropzone.autoDiscover) {
-                    return Dropzone.discover();
-                }
-            };
-
-            contentLoaded(window, Dropzone._autoDiscoverFunction);
-
-        }).call(this);
-
-    });
-
-    if (typeof exports == "object") {
-        module.exports = require("dropzone");
-    } else if (typeof define == "function" && define.amd) {
-        define([], function(){ return require("dropzone"); });
-    } else {
-        this["Dropzone"] = require("dropzone");
-    }
-})()
diff --git a/apps/static/js/plugins/footable/footable.all.min.js b/apps/static/js/plugins/footable/footable.all.min.js
deleted file mode 100755
index 8c2e30c05..000000000
--- a/apps/static/js/plugins/footable/footable.all.min.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*!
- * FooTable - Awesome Responsive Tables
- * Version : 2.0.3
- * http://fooplugins.com/plugins/footable-jquery/
- *
- * Requires jQuery - http://jquery.com/
- *
- * Copyright 2014 Steven Usher & Brad Vincent
- * Released under the MIT license
- * You are free to use FooTable in commercial projects as long as this copyright header is left intact.
- *
- * Date: 11 Nov 2014
- */
-(function(e,t){function a(){var e=this;e.id=null,e.busy=!1,e.start=function(t,a){e.busy||(e.stop(),e.id=setTimeout(function(){t(),e.id=null,e.busy=!1},a),e.busy=!0)},e.stop=function(){null!==e.id&&(clearTimeout(e.id),e.id=null,e.busy=!1)}}function i(i,o,n){var r=this;r.id=n,r.table=i,r.options=o,r.breakpoints=[],r.breakpointNames="",r.columns={},r.plugins=t.footable.plugins.load(r);var l=r.options,d=l.classes,s=l.events,u=l.triggers,f=0;return r.timers={resize:new a,register:function(e){return r.timers[e]=new a,r.timers[e]}},r.init=function(){var a=e(t),i=e(r.table);if(t.footable.plugins.init(r),i.hasClass(d.loaded))return r.raise(s.alreadyInitialized),undefined;r.raise(s.initializing),i.addClass(d.loading),i.find(l.columnDataSelector).each(function(){var e=r.getColumnData(this);r.columns[e.index]=e});for(var o in l.breakpoints)r.breakpoints.push({name:o,width:l.breakpoints[o]}),r.breakpointNames+=o+" ";r.breakpoints.sort(function(e,t){return e.width-t.width}),i.unbind(u.initialize).bind(u.initialize,function(){i.removeData("footable_info"),i.data("breakpoint",""),i.trigger(u.resize),i.removeClass(d.loading),i.addClass(d.loaded).addClass(d.main),r.raise(s.initialized)}).unbind(u.redraw).bind(u.redraw,function(){r.redraw()}).unbind(u.resize).bind(u.resize,function(){r.resize()}).unbind(u.expandFirstRow).bind(u.expandFirstRow,function(){i.find(l.toggleSelector).first().not("."+d.detailShow).trigger(u.toggleRow)}).unbind(u.expandAll).bind(u.expandAll,function(){i.find(l.toggleSelector).not("."+d.detailShow).trigger(u.toggleRow)}).unbind(u.collapseAll).bind(u.collapseAll,function(){i.find("."+d.detailShow).trigger(u.toggleRow)}),i.trigger(u.initialize),a.bind("resize.footable",function(){r.timers.resize.stop(),r.timers.resize.start(function(){r.raise(u.resize)},l.delay)})},r.addRowToggle=function(){if(l.addRowToggle){var t=e(r.table),a=!1;t.find("span."+d.toggle).remove();for(var i in r.columns){var o=r.columns[i];if(o.toggle){a=!0;var n="> tbody > tr:not(."+d.detail+",."+d.disabled+") > td:nth-child("+(parseInt(o.index,10)+1)+"),"+"> tbody > tr:not(."+d.detail+",."+d.disabled+") > th:nth-child("+(parseInt(o.index,10)+1)+")";return t.find(n).not("."+d.detailCell).prepend(e(l.toggleHTMLElement).addClass(d.toggle)),undefined}}a||t.find("> tbody > tr:not(."+d.detail+",."+d.disabled+") > td:first-child").add("> tbody > tr:not(."+d.detail+",."+d.disabled+") > th:first-child").not("."+d.detailCell).prepend(e(l.toggleHTMLElement).addClass(d.toggle))}},r.setColumnClasses=function(){var t=e(r.table);for(var a in r.columns){var i=r.columns[a];if(null!==i.className){var o="",n=!0;e.each(i.matches,function(e,t){n||(o+=", "),o+="> tbody > tr:not(."+d.detail+") > td:nth-child("+(parseInt(t,10)+1)+")",n=!1}),t.find(o).not("."+d.detailCell).addClass(i.className)}}},r.bindToggleSelectors=function(){var t=e(r.table);r.hasAnyBreakpointColumn()&&(t.find(l.toggleSelector).unbind(u.toggleRow).bind(u.toggleRow,function(){var t=e(this).is("tr")?e(this):e(this).parents("tr:first");r.toggleDetail(t)}),t.find(l.toggleSelector).unbind("click.footable").bind("click.footable",function(a){t.is(".breakpoint")&&e(a.target).is("td,th,."+d.toggle)&&e(this).trigger(u.toggleRow)}))},r.parse=function(e,t){var a=l.parsers[t.type]||l.parsers.alpha;return a(e)},r.getColumnData=function(t){var a=e(t),i=a.data("hide"),o=a.index();i=i||"",i=jQuery.map(i.split(","),function(e){return jQuery.trim(e)});var n={index:o,hide:{},type:a.data("type")||"alpha",name:a.data("name")||e.trim(a.text()),ignore:a.data("ignore")||!1,toggle:a.data("toggle")||!1,className:a.data("class")||null,matches:[],names:{},group:a.data("group")||null,groupName:null,isEditable:a.data("editable")};if(null!==n.group){var d=e(r.table).find('> thead > tr.footable-group-row > th[data-group="'+n.group+'"], > thead > tr.footable-group-row > td[data-group="'+n.group+'"]').first();n.groupName=r.parse(d,{type:"alpha"})}var u=parseInt(a.prev().attr("colspan")||0,10);f+=u>1?u-1:0;var p=parseInt(a.attr("colspan")||0,10),c=n.index+f;if(p>1){var b=a.data("names");b=b||"",b=b.split(",");for(var g=0;p>g;g++)n.matches.push(g+c),b.length>g&&(n.names[g+c]=b[g])}else n.matches.push(c);n.hide["default"]="all"===a.data("hide")||e.inArray("default",i)>=0;var h=!1;for(var m in l.breakpoints)n.hide[m]="all"===a.data("hide")||e.inArray(m,i)>=0,h=h||n.hide[m];n.hasBreakpoint=h;var v=r.raise(s.columnData,{column:{data:n,th:t}});return v.column.data},r.getViewportWidth=function(){return window.innerWidth||(document.body?document.body.offsetWidth:0)},r.calculateWidth=function(e,t){return jQuery.isFunction(l.calculateWidthOverride)?l.calculateWidthOverride(e,t):(t.viewportWidth<t.width&&(t.width=t.viewportWidth),t.parentWidth<t.width&&(t.width=t.parentWidth),t)},r.hasBreakpointColumn=function(e){for(var t in r.columns)if(r.columns[t].hide[e]){if(r.columns[t].ignore)continue;return!0}return!1},r.hasAnyBreakpointColumn=function(){for(var e in r.columns)if(r.columns[e].hasBreakpoint)return!0;return!1},r.resize=function(){var t=e(r.table);if(t.is(":visible")){if(!r.hasAnyBreakpointColumn())return t.trigger(u.redraw),undefined;var a={width:t.width(),viewportWidth:r.getViewportWidth(),parentWidth:t.parent().width()};a=r.calculateWidth(t,a);var i=t.data("footable_info");if(t.data("footable_info",a),r.raise(s.resizing,{old:i,info:a}),!i||i&&i.width&&i.width!==a.width){for(var o,n=null,l=0;r.breakpoints.length>l;l++)if(o=r.breakpoints[l],o&&o.width&&a.width<=o.width){n=o;break}var d=null===n?"default":n.name,f=r.hasBreakpointColumn(d),p=t.data("breakpoint");t.data("breakpoint",d).removeClass("default breakpoint").removeClass(r.breakpointNames).addClass(d+(f?" breakpoint":"")),d!==p&&(t.trigger(u.redraw),r.raise(s.breakpoint,{breakpoint:d,info:a}))}r.raise(s.resized,{old:i,info:a})}},r.redraw=function(){r.addRowToggle(),r.bindToggleSelectors(),r.setColumnClasses();var t=e(r.table),a=t.data("breakpoint"),i=r.hasBreakpointColumn(a);t.find("> tbody > tr:not(."+d.detail+")").data("detail_created",!1).end().find("> thead > tr:last-child > th").each(function(){var i=r.columns[e(this).index()],o="",n=!0;e.each(i.matches,function(e,t){n||(o+=", ");var a=t+1;o+="> tbody > tr:not(."+d.detail+") > td:nth-child("+a+")",o+=", > tfoot > tr:not(."+d.detail+") > td:nth-child("+a+")",o+=", > colgroup > col:nth-child("+a+")",n=!1}),o+=', > thead > tr[data-group-row="true"] > th[data-group="'+i.group+'"]';var l=t.find(o).add(this);if(""!==a&&(i.hide[a]===!1?l.addClass("footable-visible").show():l.removeClass("footable-visible").hide()),1===t.find("> thead > tr.footable-group-row").length){var s=t.find('> thead > tr:last-child > th[data-group="'+i.group+'"]:visible, > thead > tr:last-child > th[data-group="'+i.group+'"]:visible'),u=t.find('> thead > tr.footable-group-row > th[data-group="'+i.group+'"], > thead > tr.footable-group-row > td[data-group="'+i.group+'"]'),f=0;e.each(s,function(){f+=parseInt(e(this).attr("colspan")||1,10)}),f>0?u.attr("colspan",f).show():u.hide()}}).end().find("> tbody > tr."+d.detailShow).each(function(){r.createOrUpdateDetailRow(this)}),t.find("[data-bind-name]").each(function(){r.toggleInput(this)}),t.find("> tbody > tr."+d.detailShow+":visible").each(function(){var t=e(this).next();t.hasClass(d.detail)&&(i?t.show():t.hide())}),t.find("> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column").removeClass("footable-last-column"),t.find("> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column").removeClass("footable-first-column"),t.find("> thead > tr, > tbody > tr").find("> th.footable-visible:last, > td.footable-visible:last").addClass("footable-last-column").end().find("> th.footable-visible:first, > td.footable-visible:first").addClass("footable-first-column"),r.raise(s.redrawn)},r.toggleDetail=function(t){var a=t.jquery?t:e(t),i=a.next();a.hasClass(d.detailShow)?(a.removeClass(d.detailShow),i.hasClass(d.detail)&&i.hide(),r.raise(s.rowCollapsed,{row:a[0]})):(r.createOrUpdateDetailRow(a[0]),a.addClass(d.detailShow).next().show(),r.raise(s.rowExpanded,{row:a[0]}))},r.removeRow=function(t){var a=t.jquery?t:e(t);a.hasClass(d.detail)&&(a=a.prev());var i=a.next();a.data("detail_created")===!0&&i.remove(),a.remove(),r.raise(s.rowRemoved)},r.appendRow=function(t){var a=t.jquery?t:e(t);e(r.table).find("tbody").append(a),r.redraw()},r.getColumnFromTdIndex=function(t){var a=null;for(var i in r.columns)if(e.inArray(t,r.columns[i].matches)>=0){a=r.columns[i];break}return a},r.createOrUpdateDetailRow=function(t){var a,i=e(t),o=i.next(),n=[];if(i.data("detail_created")===!0)return!0;if(i.is(":hidden"))return!1;if(r.raise(s.rowDetailUpdating,{row:i,detail:o}),i.find("> td:hidden").each(function(){var t=e(this).index(),a=r.getColumnFromTdIndex(t),i=a.name;if(a.ignore===!0)return!0;t in a.names&&(i=a.names[t]);var o=e(this).attr("data-bind-name");if(null!=o&&e(this).is(":empty")){var l=e("."+d.detailInnerValue+"["+'data-bind-value="'+o+'"]');e(this).html(e(l).contents().detach())}var s;return a.isEditable!==!1&&(a.isEditable||e(this).find(":input").length>0)&&(null==o&&(o="bind-"+e.now()+"-"+t,e(this).attr("data-bind-name",o)),s=e(this).contents().detach()),s||(s=e(this).contents().clone(!0,!0)),n.push({name:i,value:r.parse(this,a),display:s,group:a.group,groupName:a.groupName,bindName:o}),!0}),0===n.length)return!1;var u=i.find("> td:visible").length,f=o.hasClass(d.detail);return f||(o=e('<tr class="'+d.detail+'"><td class="'+d.detailCell+'"><div class="'+d.detailInner+'"></div></td></tr>'),i.after(o)),o.find("> td:first").attr("colspan",u),a=o.find("."+d.detailInner).empty(),l.createDetail(a,n,l.createGroupedDetail,l.detailSeparator,d),i.data("detail_created",!0),r.raise(s.rowDetailUpdated,{row:i,detail:o}),!f},r.raise=function(t,a){r.options.debug===!0&&e.isFunction(r.options.log)&&r.options.log(t,"event"),a=a||{};var i={ft:r};e.extend(!0,i,a);var o=e.Event(t,i);return o.ft||e.extend(!0,o,i),e(r.table).trigger(o),o},r.reset=function(){var t=e(r.table);t.removeData("footable_info").data("breakpoint","").removeClass(d.loading).removeClass(d.loaded),t.find(l.toggleSelector).unbind(u.toggleRow).unbind("click.footable"),t.find("> tbody > tr").removeClass(d.detailShow),t.find("> tbody > tr."+d.detail).remove(),r.raise(s.reset)},r.toggleInput=function(t){var a=e(t).attr("data-bind-name");if(null!=a){var i=e("."+d.detailInnerValue+"["+'data-bind-value="'+a+'"]');null!=i&&(e(t).is(":visible")?e(i).is(":empty")||e(t).html(e(i).contents().detach()):e(t).is(":empty")||e(i).html(e(t).contents().detach()))}},r.init(),r}t.footable={options:{delay:100,breakpoints:{phone:480,tablet:1024},parsers:{alpha:function(t){return e(t).data("value")||e.trim(e(t).text())},numeric:function(t){var a=e(t).data("value")||e(t).text().replace(/[^0-9.\-]/g,"");return a=parseFloat(a),isNaN(a)&&(a=0),a}},addRowToggle:!0,calculateWidthOverride:null,toggleSelector:" > tbody > tr:not(.footable-row-detail)",columnDataSelector:"> thead > tr:last-child > th, > thead > tr:last-child > td",detailSeparator:":",toggleHTMLElement:"<span />",createGroupedDetail:function(e){for(var t={_none:{name:null,data:[]}},a=0;e.length>a;a++){var i=e[a].group;null!==i?(i in t||(t[i]={name:e[a].groupName||e[a].group,data:[]}),t[i].data.push(e[a])):t._none.data.push(e[a])}return t},createDetail:function(t,a,i,o,n){var r=i(a);for(var l in r)if(0!==r[l].data.length){"_none"!==l&&t.append('<div class="'+n.detailInnerGroup+'">'+r[l].name+"</div>");for(var d=0;r[l].data.length>d;d++){var s=r[l].data[d].name?o:"";t.append(e("<div></div>").addClass(n.detailInnerRow).append(e("<div></div>").addClass(n.detailInnerName).append(r[l].data[d].name+s)).append(e("<div></div>").addClass(n.detailInnerValue).attr("data-bind-value",r[l].data[d].bindName).append(r[l].data[d].display)))}}},classes:{main:"footable",loading:"footable-loading",loaded:"footable-loaded",toggle:"footable-toggle",disabled:"footable-disabled",detail:"footable-row-detail",detailCell:"footable-row-detail-cell",detailInner:"footable-row-detail-inner",detailInnerRow:"footable-row-detail-row",detailInnerGroup:"footable-row-detail-group",detailInnerName:"footable-row-detail-name",detailInnerValue:"footable-row-detail-value",detailShow:"footable-detail-show"},triggers:{initialize:"footable_initialize",resize:"footable_resize",redraw:"footable_redraw",toggleRow:"footable_toggle_row",expandFirstRow:"footable_expand_first_row",expandAll:"footable_expand_all",collapseAll:"footable_collapse_all"},events:{alreadyInitialized:"footable_already_initialized",initializing:"footable_initializing",initialized:"footable_initialized",resizing:"footable_resizing",resized:"footable_resized",redrawn:"footable_redrawn",breakpoint:"footable_breakpoint",columnData:"footable_column_data",rowDetailUpdating:"footable_row_detail_updating",rowDetailUpdated:"footable_row_detail_updated",rowCollapsed:"footable_row_collapsed",rowExpanded:"footable_row_expanded",rowRemoved:"footable_row_removed",reset:"footable_reset"},debug:!1,log:null},version:{major:0,minor:5,toString:function(){return t.footable.version.major+"."+t.footable.version.minor},parse:function(e){var t=/(\d+)\.?(\d+)?\.?(\d+)?/.exec(e);return{major:parseInt(t[1],10)||0,minor:parseInt(t[2],10)||0,patch:parseInt(t[3],10)||0}}},plugins:{_validate:function(a){if(!e.isFunction(a))return t.footable.options.debug===!0&&console.error('Validation failed, expected type "function", received type "{0}".',typeof a),!1;var i=new a;return"string"!=typeof i.name?(t.footable.options.debug===!0&&console.error('Validation failed, plugin does not implement a string property called "name".',i),!1):e.isFunction(i.init)?(t.footable.options.debug===!0&&console.log('Validation succeeded for plugin "'+i.name+'".',i),!0):(t.footable.options.debug===!0&&console.error('Validation failed, plugin "'+i.name+'" does not implement a function called "init".',i),!1)},registered:[],register:function(a,i){t.footable.plugins._validate(a)&&(t.footable.plugins.registered.push(a),"object"==typeof i&&e.extend(!0,t.footable.options,i))},load:function(e){var a,i,o=[];for(i=0;t.footable.plugins.registered.length>i;i++)try{a=t.footable.plugins.registered[i],o.push(new a(e))}catch(n){t.footable.options.debug===!0&&console.error(n)}return o},init:function(e){for(var a=0;e.plugins.length>a;a++)try{e.plugins[a].init(e)}catch(i){t.footable.options.debug===!0&&console.error(i)}}}};var o=0;e.fn.footable=function(a){a=a||{};var n=e.extend(!0,{},t.footable.options,a);return this.each(function(){o++;var t=new i(this,n,o);e(this).data("footable",t)})}})(jQuery,window);;(function(e,t,undefined){function a(t){var a=e("<th>"+t.title+"</th>");return e.isPlainObject(t.data)&&a.data(t.data),e.isPlainObject(t.style)&&a.css(t.style),t.className&&a.addClass(t.className),a}function o(t,o){var i=t.find("thead");0===i.size()&&(i=e("<thead>").appendTo(t));for(var n=e("<tr>").appendTo(i),r=0,l=o.cols.length;l>r;r++)n.append(a(o.cols[r]))}function i(t){var a=t.find("tbody");0===a.size()&&(a=e("<tbody>").appendTo(t))}function n(t,a,o){if(o){t.attr("data-page-size",o["page-size"]);var i=t.find("tfoot");0===i.size()&&(i=e('<tfoot class="hide-if-no-paging"></tfoot>').appendTo(t)),i.append("<tr><td colspan="+a.length+"></td></tr>");var n=e("<div>").appendTo(i.find("tr:last-child td"));n.addClass(o["pagination-class"])}}function r(t){for(var a=t[0],o=0,i=t.length;i>o;o++){var n=t[o];if(n.data&&(n.data.toggle===!0||"true"===n.data.toggle))return}a.data=e.extend(a.data,{toggle:!0})}function l(e,t,a){0===e.find("tr.emptyInfo").size()&&e.find("tbody").append('<tr class="emptyInfo"><td colspan="'+t.length+'">'+a+"</td></tr>")}function d(t,a,o,i){t.find("tr:not(."+o+")").each(function(){var t=e(this),o=a.data("index"),n=parseInt(t.data("index"),0),r=n+i;n>=o&&this!==a.get(0)&&t.attr("data-index",r).data("index",r)})}function s(){function t(t,a,o){var i=e("<td>");return t.formatter?i.html(t.formatter(a,i,o)):i.html(a||""),i}var a=this;a.name="Footable Grid",a.init=function(t){var d=t.options.classes.toggle,s=t.options.classes.detail,f=t.options.grid;if(f.cols){a.footable=t;var u=e(t.table);u.data("grid",a),e.isPlainObject(f.data)&&u.data(f.data),a._items=[],r(f.cols),f.showCheckbox&&(f.multiSelect=!0,f.cols.unshift({title:f.checkboxFormatter(!0),name:"",data:{"sort-ignore":!0},formatter:f.checkboxFormatter})),f.showIndex&&f.cols.unshift({title:"#",name:"index",data:{"sort-ignore":!0},formatter:f.indexFormatter}),o(u,f),i(u),n(u,f.cols,f.pagination),u.off(".grid").on({"footable_initialized.grid":function(){f.url||f.ajax?e.ajax(f.ajax||{url:f.url}).then(function(e){a.newItem(e),t.raise(f.events.loaded)},function(){throw"load data from "+(f.url||f.ajax.url)+" fail"}):(a.newItem(f.items||[]),t.raise(f.events.loaded))},"footable_sorted.grid footable_grid_created.grid footable_grid_removed.grid":function(){f.showIndex&&a.getItem().length>0&&u.find("tbody tr:not(."+s+")").each(function(t){var a=e(this).find("td:first");a.html(f.indexFormatter(null,a,t))})},"footable_redrawn.grid footable_row_removed.grid":function(){0===a.getItem().length&&f.showEmptyInfo&&l(u,f.cols,f.emptyInfo)}}).on({"click.grid":function(a){if(e(a.target).closest("td").find(">."+d).size()>0)return!0;var o=e(a.currentTarget);return o.hasClass(s)?!0:(f.multiSelect||o.hasClass(f.activeClass)||u.find("tbody tr."+f.activeClass).removeClass(f.activeClass),o.toggleClass(f.activeClass),f.showCheckbox&&o.find("input:checkbox.check").prop("checked",function(e,t){return a.target===this?t:!t}),t.toggleDetail(o),undefined)}},"tbody tr").on("click.grid","thead input:checkbox.checkAll",function(e){var t=!!e.currentTarget.checked;t?u.find("tbody tr").addClass(f.activeClass):u.find("tbody tr").removeClass(f.activeClass),u.find("tbody input:checkbox.check").prop("checked",t)})}},a.getSelected=function(){var t=a.footable.options.grid,o=e(a.footable.table).find("tbody>tr."+t.activeClass);return o.map(function(){return e(this).data("index")})},a.getItem=function(t){return t!==undefined?e.isArray(t)?e.map(t,function(e){return a._items[e]}):a._items[t]:a._items},a._makeRow=function(o,i){var n,r=a.footable.options.grid;if(e.isFunction(r.template))n=e(r.template(e.extend({},{__index:i},o)));else{n=e("<tr>");for(var l=0,d=r.cols.length;d>l;l++){var s=r.cols[l];n.append(t(s,o[s.name]||"",i))}}return n.attr("data-index",i),n},a.newItem=function(t,o,i){var n=e(a.footable.table).find("tbody"),r=a.footable.options.classes.detail;if(n.find("tr.emptyInfo").remove(),e.isArray(t)){for(var l;l=t.pop();)a.newItem(l,o,!0);return a.footable.redraw(),a.footable.raise(a.footable.options.grid.events.created,{item:t,index:o}),undefined}if(e.isPlainObject(t)){var s,f=a._items.length;if(o===undefined||0>o||o>f)s=a._makeRow(t,f++),a._items.push(t),n.append(s);else{if(s=a._makeRow(t,o),0===o)a._items.unshift(t),n.prepend(s);else{var u=n.find("tr[data-index="+(o-1)+"]");a._items.splice(o,0,t),u.data("detail_created")===!0&&(u=u.next()),u.after(s)}d(n,s,r,1)}i||(a.footable.redraw(),a.footable.raise(a.footable.options.grid.events.created,{item:t,index:o}))}},a.setItem=function(t,o){if(e.isPlainObject(t)){var i=e(a.footable.table).find("tbody"),n=a._makeRow(t,o);e.extend(a._items[o],t);var r=i.find("tr").eq(o);r.html(n.html()),a.footable.redraw(),a.footable.raise(a.footable.options.grid.events.updated,{item:t,index:o})}},a.removeItem=function(t){var o=e(a.footable.table).find("tbody"),i=a.footable.options.classes.detail,n=[];if(e.isArray(t)){for(var r;r=t.pop();)n.push(a.removeItem(r));return a.footable.raise(a.footable.options.grid.events.removed,{item:n,index:t}),n}if(t===undefined)o.find("tr").each(function(){n.push(a._items.shift()),a.footable.removeRow(this)});else{var l=o.find("tr[data-index="+t+"]");n=a._items.splice(t,1)[0],a.footable.removeRow(l),d(o,l,i,-1)}return a.footable.raise(a.footable.options.grid.events.removed,{item:n,index:t}),n}}if(t.footable===undefined||null===t.foobox)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var f={grid:{enabled:!0,data:null,template:null,cols:null,items:null,url:null,ajax:null,activeClass:"active",multiSelect:!1,showIndex:!1,showCheckbox:!1,showEmptyInfo:!1,emptyInfo:'<p class="text-center text-warning">No Data</p>',pagination:{"page-size":20,"pagination-class":"pagination pagination-centered"},indexFormatter:function(e,t,a){return a+1},checkboxFormatter:function(e){return'<input type="checkbox" class="'+(e?"checkAll":"check")+'">'},events:{loaded:"footable_grid_loaded",created:"footable_grid_created",removed:"footable_grid_removed",updated:"footable_grid_updated"}}};t.footable.plugins.register(s,f)})(jQuery,window);;(function(t,e,undefined){function a(){var e=this;e.name="Footable Filter",e.init=function(a){if(e.footable=a,a.options.filter.enabled===!0){if(t(a.table).data("filter")===!1)return;a.timers.register("filter"),t(a.table).unbind(".filtering").bind({"footable_initialized.filtering":function(){var i=t(a.table),o={input:i.data("filter")||a.options.filter.input,timeout:i.data("filter-timeout")||a.options.filter.timeout,minimum:i.data("filter-minimum")||a.options.filter.minimum,disableEnter:i.data("filter-disable-enter")||a.options.filter.disableEnter};o.disableEnter&&t(o.input).keypress(function(t){return window.event?13!==window.event.keyCode:13!==t.which}),i.bind("footable_clear_filter",function(){t(o.input).val(""),e.clearFilter()}),i.bind("footable_filter",function(t,a){e.filter(a.filter)}),t(o.input).keyup(function(i){a.timers.filter.stop(),27===i.which&&t(o.input).val(""),a.timers.filter.start(function(){var a=t(o.input).val()||"";e.filter(a)},o.timeout)})},"footable_redrawn.filtering":function(){var i=t(a.table),o=i.data("filter-string");o&&e.filter(o)}}).data("footable-filter",e)}},e.filter=function(a){var i=e.footable,o=t(i.table),n=o.data("filter-minimum")||i.options.filter.minimum,r=!a,l=i.raise("footable_filtering",{filter:a,clear:r});if(!(l&&l.result===!1||l.filter&&n>l.filter.length))if(l.clear)e.clearFilter();else{var d=l.filter.split(" ");o.find("> tbody > tr").hide().addClass("footable-filtered");var s=o.find("> tbody > tr:not(.footable-row-detail)");t.each(d,function(t,e){e&&e.length>0&&(o.data("current-filter",e),s=s.filter(i.options.filter.filterFunction))}),s.each(function(){e.showRow(this,i),t(this).removeClass("footable-filtered")}),o.data("filter-string",l.filter),i.raise("footable_filtered",{filter:l.filter,clear:!1})}},e.clearFilter=function(){var a=e.footable,i=t(a.table);i.find("> tbody > tr:not(.footable-row-detail)").removeClass("footable-filtered").each(function(){e.showRow(this,a)}),i.removeData("filter-string"),a.raise("footable_filtered",{clear:!0})},e.showRow=function(e,a){var i=t(e),o=i.next(),n=t(a.table);i.is(":visible")||(n.hasClass("breakpoint")&&i.hasClass("footable-detail-show")&&o.hasClass("footable-row-detail")?(i.add(o).show(),a.createOrUpdateDetailRow(e)):i.show())}}if(e.footable===undefined||null===e.footable)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var i={filter:{enabled:!0,input:".footable-filter",timeout:300,minimum:2,disableEnter:!1,filterFunction:function(){var e=t(this),a=e.parents("table:first"),i=a.data("current-filter").toUpperCase(),o=e.find("td").text();return a.data("filter-text-only")||e.find("td[data-value]").each(function(){o+=t(this).data("value")}),o.toUpperCase().indexOf(i)>=0}}};e.footable.plugins.register(a,i)})(jQuery,window);;(function(e,t,undefined){function a(t){var a=e(t.table),i=a.data();this.pageNavigation=i.pageNavigation||t.options.pageNavigation,this.pageSize=i.pageSize||t.options.pageSize,this.firstText=i.firstText||t.options.firstText,this.previousText=i.previousText||t.options.previousText,this.nextText=i.nextText||t.options.nextText,this.lastText=i.lastText||t.options.lastText,this.limitNavigation=parseInt(i.limitNavigation||t.options.limitNavigation||o.limitNavigation,10),this.limitPreviousText=i.limitPreviousText||t.options.limitPreviousText,this.limitNextText=i.limitNextText||t.options.limitNextText,this.limit=this.limitNavigation>0,this.currentPage=i.currentPage||0,this.pages=[],this.control=!1}function i(){var t=this;t.name="Footable Paginate",t.init=function(a){if(a.options.paginate===!0){if(e(a.table).data("page")===!1)return;t.footable=a,e(a.table).unbind(".paging").bind({"footable_initialized.paging footable_row_removed.paging footable_redrawn.paging footable_sorted.paging footable_filtered.paging":function(){t.setupPaging()}}).data("footable-paging",t)}},t.setupPaging=function(){var i=t.footable,o=e(i.table).find("> tbody");i.pageInfo=new a(i),t.createPages(i,o),t.createNavigation(i,o),t.fillPage(i,o,i.pageInfo.currentPage)},t.createPages=function(t,a){var i=1,o=t.pageInfo,n=i*o.pageSize,r=[],l=[];o.pages=[];var d=a.find("> tr:not(.footable-filtered,.footable-row-detail)");d.each(function(e,t){r.push(t),e===n-1?(o.pages.push(r),i++,n=i*o.pageSize,r=[]):e>=d.length-d.length%o.pageSize&&l.push(t)}),l.length>0&&o.pages.push(l),o.currentPage>=o.pages.length&&(o.currentPage=o.pages.length-1),0>o.currentPage&&(o.currentPage=0),1===o.pages.length?e(t.table).addClass("no-paging"):e(t.table).removeClass("no-paging")},t.createNavigation=function(a){var i=e(a.table).find(a.pageInfo.pageNavigation);if(0===i.length){if(i=e(a.pageInfo.pageNavigation),i.parents("table:first").length>0&&i.parents("table:first")!==e(a.table))return;i.length>1&&a.options.debug===!0&&console.error("More than one pagination control was found!")}if(0!==i.length){i.is("ul")||(0===i.find("ul:first").length&&i.append("<ul />"),i=i.find("ul")),i.find("li").remove();var o=a.pageInfo;o.control=i,o.pages.length>0&&(i.append('<li class="footable-page-arrow"><a data-page="first" href="#first">'+a.pageInfo.firstText+"</a>"),i.append('<li class="footable-page-arrow"><a data-page="prev" href="#prev">'+a.pageInfo.previousText+"</a></li>"),o.limit&&i.append('<li class="footable-page-arrow"><a data-page="limit-prev" href="#limit-prev">'+a.pageInfo.limitPreviousText+"</a></li>"),o.limit||e.each(o.pages,function(e,t){t.length>0&&i.append('<li class="footable-page"><a data-page="'+e+'" href="#">'+(e+1)+"</a></li>")}),o.limit&&(i.append('<li class="footable-page-arrow"><a data-page="limit-next" href="#limit-next">'+a.pageInfo.limitNextText+"</a></li>"),t.createLimited(i,o,0)),i.append('<li class="footable-page-arrow"><a data-page="next" href="#next">'+a.pageInfo.nextText+"</a></li>"),i.append('<li class="footable-page-arrow"><a data-page="last" href="#last">'+a.pageInfo.lastText+"</a></li>")),i.off("click","a[data-page]").on("click","a[data-page]",function(n){n.preventDefault();var r=e(this).data("page"),l=o.currentPage;if("first"===r)l=0;else if("prev"===r)l>0&&l--;else if("next"===r)o.pages.length-1>l&&l++;else if("last"===r)l=o.pages.length-1;else if("limit-prev"===r){l=-1;var d=i.find(".footable-page:first a").data("page");t.createLimited(i,o,d-o.limitNavigation),t.setPagingClasses(i,o.currentPage,o.pages.length)}else if("limit-next"===r){l=-1;var s=i.find(".footable-page:last a").data("page");t.createLimited(i,o,s+1),t.setPagingClasses(i,o.currentPage,o.pages.length)}else l=r;if(l>=0){if(o.limit&&o.currentPage!=l){for(var f=l;0!==f%o.limitNavigation;)f-=1;t.createLimited(i,o,f)}t.paginate(a,l)}}),t.setPagingClasses(i,o.currentPage,o.pages.length)}},t.createLimited=function(e,t,a){a=a||0,e.find("li.footable-page").remove();var i,o,n=e.find('li.footable-page-arrow > a[data-page="limit-prev"]').parent(),r=e.find('li.footable-page-arrow > a[data-page="limit-next"]').parent();for(i=t.pages.length-1;i>=0;i--)o=t.pages[i],i>=a&&a+t.limitNavigation>i&&o.length>0&&n.after('<li class="footable-page"><a data-page="'+i+'" href="#">'+(i+1)+"</a></li>");0===a?n.hide():n.show(),a+t.limitNavigation>=t.pages.length?r.hide():r.show()},t.paginate=function(a,i){var o=a.pageInfo;if(o.currentPage!==i){var n=e(a.table).find("> tbody"),r=a.raise("footable_paging",{page:i,size:o.pageSize});if(r&&r.result===!1)return;t.fillPage(a,n,i),o.control.find("li").removeClass("active disabled"),t.setPagingClasses(o.control,o.currentPage,o.pages.length)}},t.setPagingClasses=function(e,t,a){e.find("li.footable-page > a[data-page="+t+"]").parent().addClass("active"),t>=a-1&&(e.find('li.footable-page-arrow > a[data-page="next"]').parent().addClass("disabled"),e.find('li.footable-page-arrow > a[data-page="last"]').parent().addClass("disabled")),1>t&&(e.find('li.footable-page-arrow > a[data-page="first"]').parent().addClass("disabled"),e.find('li.footable-page-arrow > a[data-page="prev"]').parent().addClass("disabled"))},t.fillPage=function(a,i,o){a.pageInfo.currentPage=o,e(a.table).data("currentPage",o),i.find("> tr").hide(),e(a.pageInfo.pages[o]).each(function(){t.showRow(this,a)}),a.raise("footable_page_filled")},t.showRow=function(t,a){var i=e(t),o=i.next(),n=e(a.table);n.hasClass("breakpoint")&&i.hasClass("footable-detail-show")&&o.hasClass("footable-row-detail")?(i.add(o).show(),a.createOrUpdateDetailRow(t)):i.show()}}if(t.footable===undefined||null===t.footable)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var o={paginate:!0,pageSize:10,pageNavigation:".pagination",firstText:"&laquo;",previousText:"&lsaquo;",nextText:"&rsaquo;",lastText:"&raquo;",limitNavigation:0,limitPreviousText:"...",limitNextText:"..."};t.footable.plugins.register(i,o)})(jQuery,window);;(function(t,e,undefined){function a(){var e=this;e.name="Footable Sortable",e.init=function(a){e.footable=a,a.options.sort===!0&&t(a.table).unbind(".sorting").bind({"footable_initialized.sorting":function(){var i,o,n=t(a.table),r=(n.find("> tbody"),a.options.classes.sort);if(n.data("sort")!==!1){n.find("> thead > tr:last-child > th, > thead > tr:last-child > td").each(function(){var e=t(this),i=a.columns[e.index()];i.sort.ignore===!0||e.hasClass(r.sortable)||(e.addClass(r.sortable),t("<span />").addClass(r.indicator).appendTo(e))}),n.find("> thead > tr:last-child > th."+r.sortable+", > thead > tr:last-child > td."+r.sortable).unbind("click.footable").bind("click.footable",function(a){a.preventDefault(),o=t(this);var i=!o.hasClass(r.sorted);return e.doSort(o.index(),i),!1});var l=!1;for(var s in a.columns)if(i=a.columns[s],i.sort.initial){var d="descending"!==i.sort.initial;e.doSort(i.index,d);break}l&&a.bindToggleSelectors()}},"footable_redrawn.sorting":function(){var i=t(a.table),o=a.options.classes.sort;i.data("sorted")>=0&&i.find("> thead > tr:last-child > th").each(function(a){var i=t(this);return i.hasClass(o.sorted)||i.hasClass(o.descending)?(e.doSort(a),undefined):undefined})},"footable_column_data.sorting":function(e){var a=t(e.column.th);e.column.data.sort=e.column.data.sort||{},e.column.data.sort.initial=a.data("sort-initial")||!1,e.column.data.sort.ignore=a.data("sort-ignore")||!1,e.column.data.sort.selector=a.data("sort-selector")||null;var i=a.data("sort-match")||0;i>=e.column.data.matches.length&&(i=0),e.column.data.sort.match=e.column.data.matches[i]}}).data("footable-sort",e)},e.doSort=function(a,i){var o=e.footable;if(t(o.table).data("sort")!==!1){var n=t(o.table),r=n.find("> tbody"),l=o.columns[a],s=n.find("> thead > tr:last-child > th:eq("+a+")"),d=o.options.classes.sort,f=o.options.events.sort;if(i=i===undefined?s.hasClass(d.sorted):"toggle"===i?!s.hasClass(d.sorted):i,l.sort.ignore===!0)return!0;var u=o.raise(f.sorting,{column:l,direction:i?"ASC":"DESC"});u&&u.result===!1||(n.data("sorted",l.index),n.find("> thead > tr:last-child > th, > thead > tr:last-child > td").not(s).removeClass(d.sorted+" "+d.descending),i===undefined&&(i=s.hasClass(d.sorted)),i?s.removeClass(d.descending).addClass(d.sorted):s.removeClass(d.sorted).addClass(d.descending),e.sort(o,r,l,i),o.bindToggleSelectors(),o.raise(f.sorted,{column:l,direction:i?"ASC":"DESC"}))}},e.rows=function(e,a,i){var o=[];return a.find("> tr").each(function(){var a=t(this),n=null;if(a.hasClass(e.options.classes.detail))return!0;a.next().hasClass(e.options.classes.detail)&&(n=a.next().get(0));var r={row:a,detail:n};return i!==undefined&&(r.value=e.parse(this.cells[i.sort.match],i)),o.push(r),!0}).detach(),o},e.sort=function(t,a,i,o){var n=e.rows(t,a,i),r=t.options.sorters[i.type]||t.options.sorters.alpha;n.sort(function(t,e){return o?r(t.value,e.value):r(e.value,t.value)});for(var l=0;n.length>l;l++)a.append(n[l].row),null!==n[l].detail&&a.append(n[l].detail)}}if(e.footable===undefined||null===e.footable)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var i={sort:!0,sorters:{alpha:function(t,e){return"string"==typeof t&&(t=t.toLowerCase()),"string"==typeof e&&(e=e.toLowerCase()),t===e?0:e>t?-1:1},numeric:function(t,e){return t-e}},classes:{sort:{sortable:"footable-sortable",sorted:"footable-sorted",descending:"footable-sorted-desc",indicator:"footable-sort-indicator"}},events:{sort:{sorting:"footable_sorting",sorted:"footable_sorted"}}};e.footable.plugins.register(a,i)})(jQuery,window);;(function(t,e,undefined){function a(){var e=this;e.name="Footable Striping",e.init=function(a){e.footable=a,t(a.table).unbind("striping").bind({"footable_initialized.striping footable_row_removed.striping footable_redrawn.striping footable_sorted.striping footable_filtered.striping":function(){t(this).data("striping")!==!1&&e.setupStriping(a)}})},e.setupStriping=function(e){var a=0;t(e.table).find("> tbody > tr:not(.footable-row-detail)").each(function(){var i=t(this);i.removeClass(e.options.classes.striping.even).removeClass(e.options.classes.striping.odd),0===a%2?i.addClass(e.options.classes.striping.even):i.addClass(e.options.classes.striping.odd),a++})}}if(e.footable===undefined||null===e.foobox)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var i={striping:{enabled:!0},classes:{striping:{odd:"footable-odd",even:"footable-even"}}};e.footable.plugins.register(a,i)})(jQuery,window);;(function(t,e,undefined){function a(t,e){e=e?e:location.hash;var a=RegExp("&"+t+"(?:=([^&]*))?(?=&|$)","i");return(e=e.replace(/^\#/,"&").match(a))?e[1]===undefined?"":decodeURIComponent(e[1]):undefined}function i(e,a){var i=t(e.table).find("tbody").find("tr:not(.footable-row-detail, .footable-filtered)").length;t(e.table).data("status_num_total",i);var o=t(e.table).find("tbody").find("tr:not(.footable-row-detail)").filter(":visible").length;t(e.table).data("status_num_shown",o);var n=t(e.table).data("sorted"),r=t(e.table).find("th")[n],l=t(r).hasClass("footable-sorted-desc");if(t(e.table).data("status_descending",l),e.pageInfo){var s=e.pageInfo.currentPage;t(e.table).data("status_pagenum",s)}var d="",f=t(e.table).data("filter");t(f).length&&(d=t(f).val()),t(e.table).data("status_filter_val",d);var u,p,c;if("footable_row_expanded"==a.type&&(u=a.row,u&&(p=t(e.table).data("expanded_rows"),c=[],p&&(c=p.split(",")),c.push(u.rowIndex),t(e.table).data("expanded_rows",c.join(",")))),"footable_row_collapsed"==a.type&&(u=a.row)){p=t(e.table).data("expanded_rows"),c=[],p&&(c=p.split(","));var g=[];for(var b in c)if(c[b]==u.rowIndex){g=c.splice(b,1);break}t(e.table).data("expanded_rows",g.join(","))}}function o(){var e=this;e.name="Footable LucidBookmarkable",e.init=function(e){e.options.bookmarkable.enabled&&t(e.table).bind({footable_initialized:function(){var i=e.table.id,o=a(i+"_f"),n=a(i+"_p"),r=a(i+"_s"),l=a(i+"_d"),s=a(i+"_e");if(o){var d=t(e.table).data("filter");t(d).val(o),t(e.table).trigger("footable_filter",{filter:o})}if(n&&t(e.table).data("currentPage",n),r!==undefined){var f=t(e.table).data("footable-sort"),u=!0;"true"==l&&(u=!1),f.doSort(r,u)}else t(e.table).trigger("footable_setup_paging");if(s){var p=s.split(",");for(var c in p){var g=t(e.table.rows[p[c]]);g.find("> td:first").trigger("footable_toggle_row")}}e.lucid_bookmark_read=!0},"footable_page_filled footable_redrawn footable_filtered footable_sorted footable_row_expanded footable_row_collapsed":function(a){if(i(e,a),e.lucid_bookmark_read){var o=e.table.id,n=o+"_f",r=o+"_p",l=o+"_s",s=o+"_d",d=o+"_e",f=location.hash.replace(/^\#/,"&"),u=[n,r,l,s,d];for(var p in u){var c=RegExp("&"+u[p]+"=([^&]*)","g");f=f.replace(c,"")}var g={};g[n]=t(e.table).data("status_filter_val"),g[r]=t(e.table).data("status_pagenum"),g[l]=t(e.table).data("sorted"),g[s]=t(e.table).data("status_descending"),g[d]=t(e.table).data("expanded_rows");var b=[];for(var h in g)g[h]!==undefined&&b.push(h+"="+encodeURIComponent(g[h]));f.length&&b.push(f),location.hash=b.join("&")}}})}}if(e.footable===undefined||null===e.foobox)throw Error("Please check and make sure footable.js is included in the page and is loaded prior to this script.");var n={bookmarkable:{enabled:!1}};e.footable.plugins.register(o,n)})(jQuery,window);
\ No newline at end of file
diff --git a/apps/static/js/plugins/footable/footable.min.js b/apps/static/js/plugins/footable/footable.min.js
deleted file mode 100644
index 7c3330e02..000000000
--- a/apps/static/js/plugins/footable/footable.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
-* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome.
-* @version 3.1.2
-* @link http://fooplugins.com
-* @copyright Steven Usher & Brad Vincent 2015
-* @license Released under the GPLv3 license.
-*/
-!function(a,b){window.console=window.console||{log:function(){},error:function(){}},a.fn.footable=function(a,c){return a=a||{},this.filter("table").each(function(d,e){b.init(e,a,c)})};var c={events:[]};b.__debug__=JSON.parse(localStorage.getItem("footable_debug"))||!1,b.__debug_options__=JSON.parse(localStorage.getItem("footable_debug_options"))||c,b.debug=function(d,e){return b.is["boolean"](d)?(b.__debug__=d,void(b.__debug__?(localStorage.setItem("footable_debug",JSON.stringify(b.__debug__)),b.__debug_options__=a.extend(!0,{},c,e||{}),b.is.hash(e)&&localStorage.setItem("footable_debug_options",JSON.stringify(b.__debug_options__))):(localStorage.removeItem("footable_debug"),localStorage.removeItem("footable_debug_options")))):b.__debug__},b.get=function(b){return a(b).first().data("__FooTable__")},b.init=function(a,c,d){var e=b.get(a);return e instanceof b.Table&&e.destroy(),new b.Table(a,c,d)},b.getRow=function(b){var c=a(b).closest("tr");return c.hasClass("footable-detail-row")&&(c=c.prev()),c.data("__FooTableRow__")}}(jQuery,FooTable=window.FooTable||{}),function(a){var b=function(){return!0};a.arr={},a.arr.each=function(b,c){if(a.is.array(b)&&a.is.fn(c))for(var d=0,e=b.length;e>d&&c(b[d],d)!==!1;d++);},a.arr.get=function(b,c){var d=[];if(!a.is.array(b))return d;if(!a.is.fn(c))return b;for(var e=0,f=b.length;f>e;e++)c(b[e],e)&&d.push(b[e]);return d},a.arr.any=function(c,d){if(!a.is.array(c))return!1;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return!0;return!1},a.arr.contains=function(b,c){if(!a.is.array(b)||a.is.undef(c))return!1;for(var d=0,e=b.length;e>d;d++)if(b[d]==c)return!0;return!1},a.arr.first=function(c,d){if(!a.is.array(c))return null;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return c[e];return null},a.arr.map=function(b,c){var d=[],e=null;if(!a.is.array(b)||!a.is.fn(c))return d;for(var f=0,g=b.length;g>f;f++)null!=(e=c(b[f],f))&&d.push(e);return d},a.arr.remove=function(b,c){var d=[],e=[];if(!a.is.array(b)||!a.is.fn(c))return e;for(var f=0,g=b.length;g>f;f++)c(b[f],f,e)&&(d.push(f),e.push(b[f]));for(d.sort(function(a,b){return b-a}),f=0,g=d.length;g>f;f++){var h=d[f]-f;b.splice(h,1)}return e},a.arr["delete"]=function(b,c){var d=-1,e=null;if(!a.is.array(b)||a.is.undef(c))return e;for(var f=0,g=b.length;g>f;f++)if(b[f]==c){d=f,e=b[f];break}return-1!=d&&b.splice(d,1),e},a.arr.replace=function(a,b,c){var d=a.indexOf(b);-1!==d&&(a[d]=c)}}(FooTable),function(a){a.is={},a.is.type=function(a,b){return typeof a===b},a.is.defined=function(a){return"undefined"!=typeof a},a.is.undef=function(a){return"undefined"==typeof a},a.is.array=function(a){return"[object Array]"===Object.prototype.toString.call(a)},a.is.date=function(a){return"[object Date]"===Object.prototype.toString.call(a)&&!isNaN(a.getTime())},a.is["boolean"]=function(a){return"[object Boolean]"===Object.prototype.toString.call(a)},a.is.string=function(a){return"[object String]"===Object.prototype.toString.call(a)},a.is.number=function(a){return"[object Number]"===Object.prototype.toString.call(a)&&!isNaN(a)},a.is.fn=function(b){return a.is.defined(window)&&b===window.alert||"[object Function]"===Object.prototype.toString.call(b)},a.is.error=function(a){return"[object Error]"===Object.prototype.toString.call(a)},a.is.object=function(a){return"[object Object]"===Object.prototype.toString.call(a)},a.is.hash=function(b){return a.is.object(b)&&b.constructor===Object&&!b.nodeType&&!b.setInterval},a.is.element=function(a){return"object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName},a.is.promise=function(b){return a.is.object(b)&&a.is.fn(b.then)&&a.is.fn(b.promise)},a.is.jq=function(b){return a.is.defined(window.jQuery)&&b instanceof jQuery&&b.length>0},a.is.moment=function(b){return a.is.defined(window.moment)&&a.is.object(b)&&a.is["boolean"](b._isAMomentObject)},a.is.emptyObject=function(b){if(!a.is.hash(b))return!1;for(var c in b)if(b.hasOwnProperty(c))return!1;return!0},a.is.emptyArray=function(b){return a.is.array(b)?0===b.length:!0},a.is.emptyString=function(b){return a.is.string(b)?0===b.length:!0}}(FooTable),function(a){a.str={},a.str.contains=function(b,c,d){return a.is.emptyString(b)||a.is.emptyString(c)?!1:c.length<=b.length&&-1!==(d?b.toUpperCase().indexOf(c.toUpperCase()):b.indexOf(c))},a.str.containsWord=function(b,c,d){if(a.is.emptyString(b)||a.is.emptyString(c)||b.length<c.length)return!1;for(var e=b.split(/\W/),f=0,g=e.length;g>f;f++)if(d?e[f].toUpperCase()==c.toUpperCase():e[f]==c)return!0;return!1},a.str.from=function(b,c){return a.is.emptyString(b)?b:a.str.contains(b,c)?b.substring(b.indexOf(c)+1):b},a.str.startsWith=function(b,c){return a.is.emptyString(b)?b==c:b.slice(0,c.length)==c},a.str.toCamelCase=function(b){return a.is.emptyString(b)?b:b.toUpperCase()===b?b.toLowerCase():b.replace(/^([A-Z])|[-\s_](\w)/g,function(b,c,d){return a.is.string(d)?d.toUpperCase():c.toLowerCase()})},a.str.random=function(b){return b=a.is.emptyString(b)?"":b,b+Math.random().toString(36).substr(2,9)},a.str.escapeRegExp=function(b){return a.is.emptyString(b)?b:b.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}}(FooTable),function(a){"use strict";function b(){}Object.create||(Object.create=function(){var b=function(){};return function(c){if(arguments.length>1)throw Error("Second argument not supported");if(!a.is.object(c))throw TypeError("Argument must be an object");b.prototype=c;var d=new b;return b.prototype=null,d}}());var c=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;b.__extend__=function(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b){return function(){var a,c;return a=this._super,this._super=f,c=b.apply(this,arguments),this._super=a,c}}(d,e):e},b.extend=function(d,e){function f(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b,c){return function(){var a,d;return a=this._super,this._super=c,d=b.apply(this,arguments),this._super=a,d}}(d,e,f):e}var g=Array.prototype.slice.call(arguments);if(d=g.shift(),e=g.shift(),a.is.hash(d)){var h=Object.create(this.prototype),i=this.prototype;for(var j in d)"__ctor__"!==j&&f(h,j,d[j],i[j]);var k=a.is.fn(h.__ctor__)?h.__ctor__:function(){if(!a.is.fn(this.construct))throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.');this.construct.apply(this,arguments)};return h.construct=a.is.fn(h.construct)?h.construct:function(){},k.prototype=h,h.constructor=k,k.extend=b.extend,k}a.is.string(d)&&a.is.fn(e)&&f(this.prototype,d,e,this.prototype[d])},a.Class=b,a.ClassFactory=a.Class.extend({construct:function(){this.registered={}},contains:function(b){return a.is.defined(this.registered[b])},names:function(){var a,b=[];for(a in this.registered)this.registered.hasOwnProperty(a)&&b.push(a);return b},register:function(b,c,d){if(a.is.string(b)&&a.is.fn(c)){var e=this.registered[b];this.registered[b]={name:b,klass:c,priority:a.is.number(d)?d:a.is.defined(e)?e.priority:0}}},load:function(b,c,d){var e,f,g=this,h=Array.prototype.slice.call(arguments),i=[],j=[];b=h.shift()||{};for(e in g.registered)if(g.registered.hasOwnProperty(e)){var k=g.registered[e];b.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&(k={name:e,klass:f,priority:g.registered[e].priority})),i.push(k)}for(e in b)b.hasOwnProperty(e)&&!g.registered.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&i.push({name:e,klass:f,priority:0}));return i.sort(function(a,b){return b.priority-a.priority}),a.arr.each(i,function(b){a.is.fn(b.klass)&&j.push(g._make(b.klass,h))}),j},make:function(b,c,d){var e,f=this,g=Array.prototype.slice.call(arguments);return b=g.shift(),e=f.registered[b],a.is.fn(e.klass)?f._make(e.klass,g):null},_make:function(a,b){function c(){return a.apply(this,b)}return c.prototype=a.prototype,new c}})}(FooTable),function(a,b){b.css2json=function(c){if(b.is.emptyString(c))return{};for(var d,e,f,g={},h=c.split(";"),i=0,j=h.length;j>i;i++)b.is.emptyString(h[i])||(d=h[i].split(":"),b.is.emptyString(d[0])||b.is.emptyString(d[1])||(e=b.str.toCamelCase(a.trim(d[0])),f=a.trim(d[1]),g[e]=f));return g},b.getFnPointer=function(a){if(b.is.emptyString(a))return null;var c=window,d=a.split(".");return b.arr.each(d,function(a){c[a]&&(c=c[a])}),b.is.fn(c)?c:null},b.checkFnValue=function(a,c,d){function e(a,c,d){return b.is.fn(c)?function(){return c.apply(a,arguments)}:d}return d=b.is.fn(d)?d:null,b.is.fn(c)?e(a,c,d):b.is.type(c,"string")?e(a,b.getFnPointer(c),d):d}}(jQuery,FooTable),function(a,b){b.Cell=b.Class.extend({construct:function(a,b,c,d){this.ft=a,this.row=b,this.column=c,this.created=!1,this.define(d)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$detail=null;var d=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);this.value=this.column.parser.call(this.column,b.is.jq(this.$el)?this.$el:d?c.value:c,this.ft.o),this.o=a.extend(!0,{classes:null,style:null},d?c.options:{}),this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{}},$create:function(){this.created||((this.$el=b.is.jq(this.$el)?this.$el:a("<td/>")).data("value",this.value).contents().detach().end().append(this.format(this.value)),this._setClasses(this.$el),this._setStyle(this.$el),this.$detail=a("<tr/>").addClass(this.row.classes.join(" ")).data("__FooTableCell__",this).append(a("<th/>")).append(a("<td/>")),this.created=!0)},collapse:function(){this.created&&(this.$detail.children("th").html(this.column.title),this.$detail.children("td").first().attr("class",this.$el.attr("class")).attr("style",this.$el.attr("style")).css("display","table-cell").append(this.$el.contents().detach()),b.is.jq(this.$detail.parent())||this.$detail.appendTo(this.row.$details.find(".footable-details > tbody")))},restore:function(){if(this.created){if(b.is.jq(this.$detail.parent())){var a=this.$detail.children("td").first();this.$el.attr("class",a.attr("class")).attr("style",a.attr("style")).css("display",this.column.hidden||!this.column.visible?"none":"table-cell").append(a.contents().detach())}this.$detail.detach()}},parse:function(){return this.column.parser.call(this.column,this.$el,this.ft.o)},format:function(a){return this.column.formatter.call(this.column,a,this.ft.o)},val:function(c,d){if(b.is.undef(c))return this.value;var e=this,f=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);if(this.o=a.extend(!0,{classes:e.classes,style:e.style},f?c.options:{}),this.value=f?c.value:c,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.created){this.$el.data("value",this.value).empty();var g=this.$detail.children("td").first().empty(),h=b.is.jq(this.$detail.parent())?g:this.$el;h.append(this.format(this.value)),this._setClasses(h),this._setStyle(h),(b.is["boolean"](d)?d:!0)&&this.row.draw()}},_setClasses:function(a){var c=!b.is.emptyArray(this.column.classes),d=!b.is.emptyArray(this.classes),e=null;a.removeAttr("class"),(c||d)&&(c&&d?e=this.classes.concat(this.column.classes).join(" "):c?e=this.column.classes.join(" "):d&&(e=this.classes.join(" ")),b.is.emptyString(e)||a.addClass(e))},_setStyle:function(c){var d=!b.is.emptyObject(this.column.style),e=!b.is.emptyObject(this.style),f=null;c.removeAttr("style"),(d||e)&&(d&&e?f=a.extend({},this.column.style,this.style):d?f=this.column.style:e&&(f=this.style),b.is.hash(f)&&c.css(f))}})}(jQuery,FooTable),function(a,b){b.Column=b.Class.extend({construct:function(a,c,d){this.ft=a,this.type=b.is.emptyString(d)?"text":d,this.virtual=b.is["boolean"](c.virtual)?c.virtual:!1,this.$el=b.is.jq(c.$el)?c.$el:null,this.index=b.is.number(c.index)?c.index:-1,this.define(c),this.$create()},define:function(a){this.hidden=b.is["boolean"](a.hidden)?a.hidden:!1,this.visible=b.is["boolean"](a.visible)?a.visible:!0,this.name=b.is.string(a.name)?a.name:null,null==this.name&&(this.name="col"+(a.index+1)),this.title=b.is.string(a.title)?a.title:null,!this.virtual&&null==this.title&&b.is.jq(this.$el)&&(this.title=this.$el.html()),null==this.title&&(this.title="Column "+(a.index+1)),this.style=b.is.hash(a.style)?a.style:b.is.string(a.style)?b.css2json(a.style):{},this.classes=b.is.array(a.classes)?a.classes:b.is.string(a.classes)?a.classes.match(/\S+/g):[],this.parser=b.checkFnValue(this,a.parser,this.parser),this.formatter=b.checkFnValue(this,a.formatter,this.formatter)},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("<th/>")).html(this.title)},parser:function(c){return b.is.element(c)||b.is.jq(c)?a(c).data("value")||a(c).text():b.is.defined(c)&&null!=c?c+"":null},formatter:function(a){return null==a?"":a},createCell:function(a){var c=b.is.jq(a.$el)?a.$el.children("td,th").get(this.index):null,d=b.is.hash(a.value)?a.value[this.name]:null;return new b.Cell(this.ft,a,this,c||d)}}),b.columns=new b.ClassFactory,b.columns.register("text",b.Column)}(jQuery,FooTable),function(a,b){b.Component=b.Class.extend({construct:function(a,c){if(!(a instanceof b.Table))throw new TypeError("The instance parameter must be an instance of FooTable.Table.");this.ft=a,this.enabled=b.is["boolean"](c)?c:!1},preinit:function(a){},init:function(){},destroy:function(){},predraw:function(){},draw:function(){},postdraw:function(){}}),b.components=new b.ClassFactory}(jQuery,FooTable),function(a,b){b.Defaults=function(){this.stopPropagation=!1,this.on=null},b.defaults=new b.Defaults}(jQuery,FooTable),function(a,b){b.Row=b.Class.extend({construct:function(a,b,c){this.ft=a,this.columns=b,this.created=!1,this.define(c)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$toggle=a("<span/>",{"class":"footable-toggle fooicon fooicon-plus"});var d=b.is.hash(c),e=d&&b.is.hash(c.options)&&b.is.hash(c.value);this.value=d?e?c.value:c:null,this.o=a.extend(!0,{expanded:!1,classes:null,style:null},e?c.options:{}),this.expanded=b.is.jq(this.$el)?this.$el.data("expanded")||this.o.expanded:this.o.expanded,this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.cells=this.createCells();var f=this;f.value={},b.arr.each(f.cells,function(a){f.value[a.column.name]=a.val()})},$create:function(){if(!this.created){(this.$el=b.is.jq(this.$el)?this.$el:a("<tr/>")).data("__FooTableRow__",this),this._setClasses(this.$el),this._setStyle(this.$el),"last"==this.ft.rows.toggleColumn&&this.$toggle.addClass("last-column"),this.$details=a("<tr/>",{"class":"footable-detail-row"}).append(a("<td/>",{colspan:this.ft.columns.visibleColspan}).append(a("<table/>",{"class":"footable-details "+this.ft.classes.join(" ")}).append("<tbody/>")));var c=this;b.arr.each(c.cells,function(a){a.created||a.$create(),c.$el.append(a.$el)}),c.$el.off("click.ft.row").on("click.ft.row",{self:c},c._onToggle),this.created=!0}},createCells:function(){var a=this;return b.arr.map(a.columns,function(b){return b.createCell(a)})},val:function(c,d){var e=this;if(!b.is.hash(c))return b.is.hash(this.value)&&!b.is.emptyObject(this.value)||(this.value={},b.arr.each(this.cells,function(a){e.value[a.column.name]=a.val()})),this.value;this.collapse(!1);var f=b.is.hash(c),g=f&&b.is.hash(c.options)&&b.is.hash(c.value);if(this.o=a.extend(!0,{expanded:e.expanded,classes:e.classes,style:e.style},g?c.options:{}),this.expanded=this.o.expanded,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},f)if(g&&(c=c.value),b.is.hash(this.value))for(var h in c)c.hasOwnProperty(h)&&(this.value[h]=c[h]);else this.value=c;else this.value=null;b.arr.each(this.cells,function(a){b.is.defined(e.value[a.column.name])&&a.val(e.value[a.column.name],!1)}),this.created&&(this._setClasses(this.$el),this._setStyle(this.$el),(b.is["boolean"](d)?d:!0)&&this.draw())},_setClasses:function(a){var c=!b.is.emptyArray(this.classes),d=null;a.removeAttr("class"),c&&(d=this.classes.join(" "),b.is.emptyString(d)||a.addClass(d))},_setStyle:function(a){var c=!b.is.emptyObject(this.style),d=null;a.removeAttr("style"),c&&(d=this.style,b.is.hash(d)&&a.css(d))},expand:function(){if(this.created){var a=this;a.ft.raise("expand.ft.row",[a]).then(function(){a.__hidden__=b.arr.map(a.cells,function(a){return a.column.hidden&&a.column.visible?a:null}),a.__hidden__.length>0&&(a.$details.insertAfter(a.$el).children("td").first().attr("colspan",a.ft.columns.visibleColspan),b.arr.each(a.__hidden__,function(a){a.collapse()})),a.$el.attr("data-expanded",!0),a.$toggle.removeClass("fooicon-plus").addClass("fooicon-minus"),a.expanded=!0})}},collapse:function(a){if(this.created){var c=this;c.ft.raise("collapse.ft.row",[c]).then(function(){b.arr.each(c.__hidden__,function(a){a.restore()}),c.$details.detach(),c.$el.removeAttr("data-expanded"),c.$toggle.removeClass("fooicon-minus").addClass("fooicon-plus"),(b.is["boolean"](a)?a:!0)&&(c.expanded=!1)})}},predraw:function(a){this.created&&(this.expanded&&this.collapse(!1),this.$toggle.detach(),a=b.is["boolean"](a)?a:!0,a&&this.$el.detach())},draw:function(a){this.created||this.$create(),b.is.jq(a)&&a.append(this.$el);var c=this;b.arr.each(c.cells,function(a){a.$el.css("display",a.column.hidden||!a.column.visible?"none":"table-cell"),c.ft.rows.showToggle&&c.ft.columns.hasHidden&&("first"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.firstVisibleIndex||"last"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.lastVisibleIndex)&&a.$el.prepend(c.$toggle)}),this.expanded&&this.expand()},toggle:function(){this.created&&this.ft.columns.hasHidden&&(this.expanded?this.collapse():this.expand())},_onToggle:function(b){var c=b.data.self;a(b.target).is(c.ft.rows.toggleSelector)&&c.toggle()}})}(jQuery,FooTable),function(a,b){b.instances=[],b.Table=b.Class.extend({construct:function(c,d,e){this._resizeTimeout=null,this.id=b.instances.push(this),this.initialized=!1,this.$el=(b.is.jq(c)?c:a(c)).first(),this.o=a.extend(!0,{},b.defaults,d),this.data=this.$el.data()||{},this.classes=[],this.components=b.components.load(b.is.hash(this.data.components)?this.data.components:this.o.components,this),this.breakpoints=this.use(FooTable.Breakpoints),this.columns=this.use(FooTable.Columns),this.rows=this.use(FooTable.Rows),this._construct(e)},_construct:function(a){var c=this;this._preinit().then(function(){return c._init()}).always(function(d){return b.is.error(d)?void console.error("FooTable: unhandled error thrown during initialization.",d):c.raise("ready.ft.table").then(function(){b.is.fn(a)&&a.call(c,c)})})},_preinit:function(){var c=this;return this.raise("preinit.ft.table",[c.data]).then(function(){var d=(c.$el.attr("class")||"").match(/\S+/g)||[];c.o.ajax=b.checkFnValue(c,c.data.ajax,c.o.ajax),c.o.stopPropagation=b.is["boolean"](c.data.stopPropagation)?c.data.stopPropagation:c.o.stopPropagation;for(var e=0,f=d.length;f>e;e++)b.str.startsWith(d[e],"footable")||c.classes.push(d[e]);var g=a("<div/>",{"class":"footable-loader"}).append(a("<span/>",{"class":"fooicon fooicon-loader"}));return c.$el.hide().after(g),c.execute(!1,!1,"preinit",c.data).always(function(){c.$el.show(),g.remove()})})},_init:function(){var c=this;return c.raise("init.ft.table").then(function(){var d=c.$el.children("thead"),e=c.$el.children("tbody"),f=c.$el.children("tfoot");return c.$el.addClass("footable footable-"+c.id),b.is.hash(c.o.on)&&c.$el.on(c.o.on),0==f.length&&c.$el.append(f=a("<tfoot/>")),0==e.length&&c.$el.append("<tbody/>"),0==d.length&&c.$el.prepend(d=a("<thead/>")),c.execute(!1,!0,"init").then(function(){return c.$el.data("__FooTable__",c),0==f.children("tr").length&&f.remove(),0==d.children("tr").length&&d.remove(),c.raise("postinit.ft.table").then(function(){return c.draw()}).always(function(){a(window).off("resize.ft"+c.id,c._onWindowResize).on("resize.ft"+c.id,{self:c},c._onWindowResize),c.initialized=!0})})})},destroy:function(){var a=this;return a.raise("destroy.ft.table").then(function(){return a.execute(!0,!0,"destroy").then(function(){a.$el.removeData("__FooTable__").removeClass("footable-"+a.id),b.is.hash(a.o.on)&&a.$el.off(a.o.on),a.initialized=!1})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown while destroying the plugin.",a)})},raise:function(c,d){var e=this,f=b.__debug__&&(b.is.emptyArray(b.__debug_options__.events)||b.arr.any(b.__debug_options__.events,function(a){return b.str.contains(c,a)}));return d=d||[],d.unshift(this),a.Deferred(function(b){var g=a.Event(c);1==e.o.stopPropagation&&e.$el.one(c,function(a){a.stopPropagation()}),f&&console.log("FooTable:"+c+": ",d),e.$el.trigger(g,d),g.isDefaultPrevented()?(f&&console.log('FooTable: default prevented for the "'+c+'" event.'),b.reject(g)):b.resolve(g)})},use:function(a){for(var b=0,c=this.components.length;c>b;b++)if(this.components[b]instanceof a)return this.components[b];return null},draw:function(){var a=this;return a.execute(!1,!0,"predraw").then(function(){return a.raise("predraw.ft.table").then(function(){return a.execute(!1,!0,"draw").then(function(){return a.raise("draw.ft.table").then(function(){return a.execute(!1,!0,"postdraw").then(function(){return a.raise("postdraw.ft.table")})})})})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown during a draw operation.",a)})},execute:function(a,c,d,e,f){var g=this,h=Array.prototype.slice.call(arguments);a=h.shift(),c=h.shift();var i=c?b.arr.get(g.components,function(a){return a.enabled}):g.components.slice(0);return h.unshift(a?i.reverse():i),g._execute.apply(g,h)},_execute:function(c,d,e,f){if(!c||!c.length)return a.when();var g,h=this,i=Array.prototype.slice.call(arguments);return c=i.shift(),d=i.shift(),g=c.shift(),b.is.fn(g[d])?a.Deferred(function(a){try{var c=g[d].apply(g,i);if(b.is.promise(c))return c.then(a.resolve,a.reject);a.resolve(c)}catch(e){a.reject(e)}}).then(function(){return h._execute.apply(h,[c,d].concat(i))}):h._execute.apply(h,[c,d].concat(i))},_onWindowResize:function(a){var b=a.data.self;null!=b._resizeTimeout&&clearTimeout(b._resizeTimeout),b._resizeTimeout=setTimeout(function(){b._resizeTimeout=null,b.raise("resize.ft.table").then(function(){b.breakpoints.check()})},300)}})}(jQuery,FooTable),function(a,b){b.is.undef(window.moment)||(b.DateColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"date"),this.formatString=b.is.string(c.formatString)?c.formatString:"MM-DD-YYYY"},parser:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("value")||a(c).text(),b.is.string(c)&&(c=isNaN(c)?c:+c)),b.is.date(c))return moment(c);if(b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c;if(b.is.string(c)){if(isNaN(c))return moment(c,this.formatString);c=+c}return b.is.number(c)?moment(c):null},formatter:function(a){return b.is.object(a)&&b.is["boolean"](a._isAMomentObject)?a.format(this.formatString):""},filterValue:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("filterValue")||a(c).text()),b.is.hash(c)&&b.is.hash(c.options)&&(b.is.string(c.options.filterValue)&&(c=c.options.filterValue),b.is.defined(c.value)&&(c=c.value)),b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c.format(this.formatString);if(b.is.string(c)){if(isNaN(c))return c;c=+c}return b.is.number(c)||b.is.date(c)?moment(c).format(this.formatString):b.is.defined(c)&&null!=c?c+"":""}}),b.columns.register("date",b.DateColumn))}(jQuery,FooTable),function(a,b){b.HTMLColumn=b.Column.extend({construct:function(a,b){this._super(a,b,"html")},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();return"td"==d||"th"==d?c.data("value")||c.contents():c}return null}}),b.columns.register("html",b.HTMLColumn)}(jQuery,FooTable),function(a,b){b.NumberColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"number"),this.decimalSeparator=b.is.string(c.decimalSeparator)?c.decimalSeparator:".",this.thousandSeparator=b.is.string(c.thousandSeparator)?c.thousandSeparator:",",this.decimalSeparatorRegex=new RegExp(b.str.escapeRegExp(this.decimalSeparator),"g"),this.thousandSeparatorRegex=new RegExp(b.str.escapeRegExp(this.thousandSeparator),"g"),this.cleanRegex=new RegExp("[^0-9"+b.str.escapeRegExp(this.decimalSeparator)+"]","g")},parser:function(c){return(b.is.element(c)||b.is.jq(c))&&(c=a(c).data("value")||a(c).text().replace(this.cleanRegex,"")),b.is.string(c)&&(c=c.replace(this.thousandSeparatorRegex,"").replace(this.decimalSeparatorRegex,"."),c=parseFloat(c)),b.is.number(c)?c:null},formatter:function(a){if(null==a)return"";var b=(a+"").split(".");return 2==b.length&&b[0].length>3&&(b[0]=b[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,this.thousandSeparator)),b.join(this.decimalSeparator)}}),b.columns.register("number",b.NumberColumn)}(jQuery,FooTable),function(a,b){b.Breakpoint=b.Class.extend({construct:function(a,b){this.name=a,this.width=b}})}(jQuery,FooTable),function(a,b){b.Breakpoints=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.current=null,this.array=[],this.cascade=this.o.cascade,this.useParentWidth=this.o.useParentWidth,this.hidden=null,this._classNames="",this.getWidth=b.checkFnValue(this,this.o.getWidth,this.getWidth)},preinit:function(a){var c=this;return this.ft.raise("preinit.ft.breakpoints",[a]).then(function(){c.cascade=b.is["boolean"](a.cascade)?a.cascade:c.cascade,c.o.breakpoints=b.is.hash(a.breakpoints)?a.breakpoints:c.o.breakpoints,c.getWidth=b.checkFnValue(c,a.getWidth,c.getWidth),null==c.o.breakpoints&&(c.o.breakpoints={xs:480,sm:768,md:992,lg:1200});for(var d in c.o.breakpoints)c.o.breakpoints.hasOwnProperty(d)&&(c.array.push(new b.Breakpoint(d,c.o.breakpoints[d])),c._classNames+="breakpoint-"+d+" ");c.array.sort(function(a,b){return b.width-a.width})})},init:function(){var a=this;return this.ft.raise("init.ft.breakpoints").then(function(){a.current=a.get()})},draw:function(){this.ft.$el.removeClass(this._classNames).addClass("breakpoint-"+this.current.name)},calculate:function(){for(var a,c=this,d=null,e=[],f=null,g=c.getWidth(),h=0,i=c.array.length;i>h;h++)a=c.array[h],(!d&&h==i-1||g>=a.width&&(f instanceof b.Breakpoint?g<f.width:!0))&&(d=a),d||e.push(a.name),f=a;return e.push(d.name),c.hidden=e.join(" "),d},visible:function(a){if(b.is.emptyString(a))return!0;if("all"===a)return!1;for(var c=a.split(" "),d=0,e=c.length;e>d;d++)if(this.cascade?b.str.containsWord(this.hidden,c[d]):c[d]==this.current.name)return!1;return!0},check:function(){var a=this,c=a.get();c instanceof b.Breakpoint&&c!=a.current&&a.ft.raise("before.ft.breakpoints",[a.current,c]).then(function(){var b=a.current;return a.current=c,a.ft.draw().then(function(){a.ft.raise("after.ft.breakpoints",[a.current,b])})})},get:function(a){return b.is.undef(a)?this.calculate():a instanceof b.Breakpoint?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)&&a>=0&&a<this.array.length?this.array[a]:null},getWidth:function(){return b.is.fn(this.o.getWidth)?this.o.getWidth(this.ft):1==this.useParentWidth?this.getParentWidth():this.getViewportWidth()},getParentWidth:function(){return this.ft.$el.parent().width()},getViewportWidth:function(){return Math.max(document.documentElement.clientWidth,window.innerWidth,0)}}),b.components.register("breakpoints",b.Breakpoints,1e3)}(jQuery,FooTable),function(a){a.Column.prototype.breakpoints=null,a.Column.prototype.__breakpoints_define__=function(b){this.breakpoints=a.is.emptyString(b.breakpoints)?null:b.breakpoints},a.Column.extend("define",function(a){this._super(a),this.__breakpoints_define__(a)})}(FooTable),function(a){a.Defaults.prototype.breakpoints=null,a.Defaults.prototype.cascade=!1,a.Defaults.prototype.useParentWidth=!1,a.Defaults.prototype.getWidth=null}(FooTable),function(a,b){b.Columns=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.array=[],this.$header=null,this.showHeader=a.o.showHeader,this._fromHTML=b.is.emptyArray(a.o.columns)},parse:function(c){var d=this;return a.Deferred(function(c){function e(c,d){var e=[];if(0==c.length||0==d.length)e=c.concat(d);else{var f=0;b.arr.each(c.concat(d),function(a){a.index>f&&(f=a.index)}),f++;for(var g,h,i=0;f>i;i++)g={},b.arr.each(c,function(a){return a.index==i?(g=a,!1):void 0}),h={},b.arr.each(d,function(a){return a.index==i?(h=a,!1):void 0}),e.push(a.extend(!0,{},g,h))}return e}var f,g,h=[],i=[],j=d.ft.$el.find("tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first").first();if(j.length>0){var k=j.parent().is("tbody")&&j.children().length==j.children("td").length;k||(d.$header=j.addClass("footable-header")),j.children("td,th").each(function(b,c){f=a(c),g=f.data(),g.index=b,g.$el=f,g.virtual=k,i.push(g)}),k&&(d.showHeader=!1)}b.is.array(d.o.columns)&&!b.is.emptyArray(d.o.columns)?(b.arr.each(d.o.columns,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))):b.is.promise(d.o.columns)?d.o.columns.then(function(a){b.arr.each(a,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))},function(a){c.reject(Error("Columns ajax request error: "+a.status+" ("+a.statusText+")"))}):d.parseFinalize(c,e(h,i))})},parseFinalize:function(a,c){var d,e=this,f=[];b.arr.each(c,function(a){(d=b.columns.contains(a.type)?b.columns.make(a.type,e.ft,a):new b.Column(e.ft,a))&&f.push(d)}),b.is.emptyArray(f)?a.reject(Error("No columns supplied.")):(f.sort(function(a,b){return a.index-b.index}),a.resolve(f))},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.columns",[a]).then(function(){return c.parse(a).then(function(d){c.array=d,c.showHeader=b.is["boolean"](a.showHeader)?a.showHeader:c.showHeader})})},init:function(){var a=this;return this.ft.raise("init.ft.columns",[a.array]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.columns").then(function(){a._fromHTML||a.$header.remove()})},predraw:function(){var a=this,c=!0;a.visibleColspan=0,a.firstVisibleIndex=0,a.lastVisibleIndex=0,a.hasHidden=!1,b.arr.each(a.array,function(b){b.hidden=!a.ft.breakpoints.visible(b.breakpoints),!b.hidden&&b.visible&&(c&&(a.firstVisibleIndex=b.index,c=!1),a.lastVisibleIndex=b.index,a.visibleColspan++),b.hidden&&(a.hasHidden=!0)}),a.ft.$el.toggleClass("breakpoint",a.hasHidden)},draw:function(){b.arr.each(this.array,function(a){a.$el.css("display",a.hidden||!a.visible?"none":"table-cell")}),!this.showHeader&&b.is.jq(this.$header.parent())&&this.$header.detach()},$create:function(){var c=this;c.$header=b.is.jq(c.$header)?c.$header:a("<tr/>",{"class":"footable-header"}),c.$header.children("th,td").detach(),b.arr.each(c.array,function(a){c.$header.append(a.$el)}),c.showHeader&&!b.is.jq(c.$header.parent())&&c.ft.$el.children("thead").append(c.$header)},get:function(a){return a instanceof b.Column?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)?b.arr.first(this.array,function(b){return b.index==a}):b.is.fn(a)?b.arr.get(this.array,a):null},ensure:function(a){var c=this,d=[];return b.is.array(a)?(b.arr.each(a,function(a){d.push(c.get(a))}),d):d}}),b.components.register("columns",b.Columns,900)}(jQuery,FooTable),function(a){a.Defaults.prototype.columns=[],a.Defaults.prototype.showHeader=!0}(FooTable),function(a,b){b.Rows=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.array=[],this.all=[],this.showToggle=a.o.showToggle,this.toggleSelector=a.o.toggleSelector,
-this.toggleColumn=a.o.toggleColumn,this.emptyString=a.o.empty,this.expandFirst=a.o.expandFirst,this.expandAll=a.o.expandAll,this.$empty=null,this._fromHTML=b.is.emptyArray(a.o.rows)},parse:function(){var c=this;return a.Deferred(function(a){var d=c.ft.$el.children("tbody").children("tr");b.is.jq(d)?(c.parseFinalize(a,d),d.detach()):b.is.array(c.o.rows)&&c.o.rows.length>0?c.parseFinalize(a,c.o.rows):b.is.promise(c.o.rows)?c.o.rows.then(function(b){c.parseFinalize(a,b)},function(b){a.reject(Error("Rows ajax request error: "+b.status+" ("+b.statusText+")"))}):c.parseFinalize(a,[])})},parseFinalize:function(c,d){var e=this,f=a.map(d,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});c.resolve(f)},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.rows",[a]).then(function(){return c.parse().then(function(d){c.all=d,c.array=c.all.slice(0),c.showToggle=b.is["boolean"](a.showToggle)?a.showToggle:c.showToggle,c.toggleSelector=b.is.string(a.toggleSelector)?a.toggleSelector:c.toggleSelector,c.toggleColumn=b.is.string(a.toggleColumn)?a.toggleColumn:c.toggleColumn,"first"!=c.toggleColumn&&"last"!=c.toggleColumn&&(c.toggleColumn="first"),c.emptyString=b.is.string(a.empty)?a.empty:c.emptyString,c.expandFirst=b.is["boolean"](a.expandFirst)?a.expandFirst:c.expandFirst,c.expandAll=b.is["boolean"](a.expandAll)?a.expandAll:c.expandAll})})},init:function(){var a=this;return a.ft.raise("init.ft.rows",[a.all]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.rows").then(function(){b.arr.each(a.array,function(b){b.predraw(!a._fromHTML)})})},predraw:function(){b.arr.each(this.array,function(a){a.predraw()}),this.array=this.all.slice(0)},$create:function(){this.$empty=a("<tr/>",{"class":"footable-empty"}).append(a("<td/>").text(this.emptyString))},draw:function(){var a=this,c=a.ft.$el.children("tbody"),d=!0;a.array.length>0?(a.$empty.detach(),b.arr.each(a.array,function(b){(a.expandFirst&&d||a.expandAll)&&(b.expanded=!0,d=!1),b.draw(c)})):(a.$empty.children("td").attr("colspan",a.ft.columns.visibleColspan),c.append(a.$empty))},load:function(c,d){var e=this,f=a.map(c,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});b.arr.each(this.array,function(a){a.predraw()}),this.all=(b.is["boolean"](d)?d:!1)?this.all.concat(f):f,this.array=this.all.slice(0),this.ft.draw()},expand:function(){b.arr.each(this.array,function(a){a.expand()})},collapse:function(){b.arr.each(this.array,function(a){a.collapse()})}}),b.components.register("rows",b.Rows,800)}(jQuery,FooTable),function(a){a.Defaults.prototype.rows=[],a.Defaults.prototype.empty="No results",a.Defaults.prototype.showToggle=!0,a.Defaults.prototype.toggleSelector="tr,td,.footable-toggle",a.Defaults.prototype.toggleColumn="first",a.Defaults.prototype.expandFirst=!1,a.Defaults.prototype.expandAll=!1}(FooTable),function(a){a.Table.prototype.loadRows=function(a,b){this.rows.load(a,b)}}(FooTable),function(a){a.Filter=a.Class.extend({construct:function(b,c,d,e,f,g,h){this.name=b,this.space=!a.is.string(e)||"OR"!=e&&"AND"!=e?"AND":e,this.connectors=a.is["boolean"](f)?f:!0,this.ignoreCase=a.is["boolean"](g)?g:!0,this.hidden=a.is["boolean"](h)?h:!1,this.query=c instanceof a.Query?c:new a.Query(c,this.space,this.connectors,this.ignoreCase),this.columns=d},match:function(b){return a.is.string(b)?(a.is.string(this.query)&&(this.query=new a.Query(this.query,this.space,this.connectors,this.ignoreCase)),this.query instanceof a.Query?this.query.match(b):!1):!1},matchRow:function(b){var c=this,d=a.arr.map(b.cells,function(b){return a.arr.contains(c.columns,b.column)?b.filterValue:null}).join(" ");return c.match(d)}})}(FooTable),function(a,b){b.Filtering=b.Component.extend({construct:function(a){this._super(a,a.o.filtering.enabled),this.filters=a.o.filtering.filters,this.delay=a.o.filtering.delay,this.min=a.o.filtering.min,this.space=a.o.filtering.space,this.connectors=a.o.filtering.connectors,this.ignoreCase=a.o.filtering.ignoreCase,this.placeholder=a.o.filtering.placeholder,this.position=a.o.filtering.position,this.$row=null,this.$cell=null,this.$dropdown=null,this.$input=null,this.$button=null,this._filterTimeout=null},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.filtering").then(function(){c.ft.$el.hasClass("footable-filtering")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.filtering)?a.filtering:c.enabled,c.enabled&&(c.space=b.is.string(a.filterSpace)?a.filterSpace:c.space,c.min=b.is.number(a.filterMin)?a.filterMin:c.min,c.connectors=b.is["boolean"](a.filterConnectors)?a.filterConnectors:c.connectors,c.ignoreCase=b.is["boolean"](a.filterIgnoreCase)?a.filterIgnoreCase:c.ignoreCase,c.delay=b.is.number(a.filterDelay)?a.filterDelay:c.delay,c.placeholder=b.is.string(a.filterPlaceholder)?a.filterPlaceholder:c.placeholder,c.filters=b.is.array(a.filterFilters)?c.ensure(a.filterFilters):c.ensure(c.filters),c.ft.$el.hasClass("footable-filtering-left")&&(c.position="left"),c.ft.$el.hasClass("footable-filtering-center")&&(c.position="center"),c.ft.$el.hasClass("footable-filtering-right")&&(c.position="right"),c.position=b.is.string(a.filterPosition)?a.filterPosition:c.position)},function(){c.enabled=!1})},init:function(){var a=this;return a.ft.raise("init.ft.filtering").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;return a.ft.raise("destroy.ft.filtering").then(function(){a.ft.$el.removeClass("footable-filtering").find("thead > tr.footable-filtering").remove()})},$create:function(){var c,d=this,e=a("<div/>",{"class":"form-group"}).append(a("<label/>",{"class":"sr-only",text:"Search"})),f=a("<div/>",{"class":"input-group"}).appendTo(e),g=a("<div/>",{"class":"input-group-btn"}),h=a("<button/>",{type:"button","class":"btn btn-default dropdown-toggle"}).on("click",{self:d},d._onDropdownToggleClicked).append(a("<span/>",{"class":"caret"}));switch(d.position){case"left":c="footable-filtering-left";break;case"center":c="footable-filtering-center";break;default:c="footable-filtering-right"}d.ft.$el.addClass("footable-filtering").addClass(c),d.$row=a("<tr/>",{"class":"footable-filtering"}).prependTo(d.ft.$el.children("thead")),d.$cell=a("<th/>").attr("colspan",d.ft.columns.visibleColspan).appendTo(d.$row),d.$form=a("<form/>",{"class":"form-inline"}).append(e).appendTo(d.$cell),d.$input=a("<input/>",{type:"text","class":"form-control",placeholder:d.placeholder}),d.$button=a("<button/>",{type:"button","class":"btn btn-primary"}).on("click",{self:d},d._onSearchButtonClicked).append(a("<span/>",{"class":"fooicon fooicon-search"})),d.$dropdown=a("<ul/>",{"class":"dropdown-menu dropdown-menu-right"}).append(b.arr.map(d.ft.columns.array,function(b){return b.filterable?a("<li/>").append(a("<a/>",{"class":"checkbox"}).append(a("<label/>",{text:b.title}).prepend(a("<input/>",{type:"checkbox",checked:!0}).data("__FooTableColumn__",b)))):null})),d.delay>0&&(d.$input.on("keypress keyup paste",{self:d},d._onSearchInputChanged),d.$dropdown.on("click",'input[type="checkbox"]',{self:d},d._onSearchColumnClicked)),g.append(d.$button,h,d.$dropdown),f.append(d.$input,g)},predraw:function(){if(!b.is.emptyArray(this.filters)){var c=this;c.ft.rows.array=a.grep(c.ft.rows.array,function(a){return a.filtered(c.filters)})}},draw:function(){this.$cell.attr("colspan",this.ft.columns.visibleColspan);var a=this.find("search");a instanceof b.Filter?this.$input.val(a.query.val()):this.$input.val(null),this.setButton(!b.arr.any(this.filters,function(a){return!a.hidden}))},addFilter:function(a,c,d,e,f,g,h){var i=this.createFilter(a,c,d,e,f,g,h);i instanceof b.Filter&&(this.removeFilter(i.name),this.filters.push(i))},removeFilter:function(a){b.arr.remove(this.filters,function(b){return b.name==a})},filter:function(){var a=this;return a.filters=a.ensure(a.filters),a.ft.raise("before.ft.filtering",[a.filters]).then(function(){return a.filters=a.ensure(a.filters),a.ft.draw().then(function(){a.ft.raise("after.ft.filtering",[a.filters])})})},clear:function(){return this.filters=b.arr.get(this.filters,function(a){return a.hidden}),this.filter()},setButton:function(a){a?this.$button.children(".fooicon").removeClass("fooicon-remove").addClass("fooicon-search"):this.$button.children(".fooicon").removeClass("fooicon-search").addClass("fooicon-remove")},find:function(a){return b.arr.first(this.filters,function(b){return b.name==a})},columns:function(){return b.is.jq(this.$dropdown)?this.$dropdown.find("input:checked").map(function(){return a(this).data("__FooTableColumn__")}).get():this.ft.columns.get(function(a){return a.filterable})},ensure:function(a){var c=this,d=[],e=c.columns();return b.is.emptyArray(a)||b.arr.each(a,function(a){a=c._ensure(a,e),a instanceof b.Filter&&d.push(a)}),d},createFilter:function(a,c,d,e,f,g,h){return b.is.string(a)&&(a={name:a,query:c,columns:d,ignoreCase:e,connectors:f,space:g,hidden:h}),this._ensure(a,this.columns())},_ensure:function(a,c){return(b.is.hash(a)||a instanceof b.Filter)&&!b.is.emptyString(a.name)&&(!b.is.emptyString(a.query)||a.query instanceof b.Query)?(a.columns=b.is.emptyArray(a.columns)?c:this.ft.columns.ensure(a.columns),a.ignoreCase=b.is["boolean"](a.ignoreCase)?a.ignoreCase:this.ignoreCase,a.connectors=b.is["boolean"](a.connectors)?a.connectors:this.connectors,a.hidden=b.is["boolean"](a.hidden)?a.hidden:!1,a.space=!b.is.string(a.space)||"AND"!==a.space&&"OR"!==a.space?this.space:a.space,a.query=b.is.string(a.query)?new b.Query(a.query,a.space,a.connectors,a.ignoreCase):a.query,a instanceof b.Filter?a:new b.Filter(a.name,a.query,a.columns,a.space,a.connectors,a.ignoreCase,a.hidden)):null},_onSearchInputChanged:function(a){var c=a.data.self,d="keypress"==a.type&&!b.is.emptyString(String.fromCharCode(a.charCode)),e="keyup"==a.type&&(8==a.which||46==a.which),f="paste"==a.type;(d||e||f)&&(13==a.which&&a.preventDefault(),null!=c._filterTimeout&&clearTimeout(c._filterTimeout),c._filterTimeout=setTimeout(function(){c._filterTimeout=null;var a=c.$input.val();a.length>=c.min&&(c.addFilter("search",a),c.filter())},c.delay))},_onSearchButtonClicked:function(a){a.preventDefault();var b=a.data.self;null!=b._filterTimeout&&clearTimeout(b._filterTimeout);var c=b.$button.children(".fooicon");if(c.hasClass("fooicon-remove"))b.clear();else{var d=b.$input.val();d.length>=b.min&&(b.addFilter("search",d),b.filter())}},_onSearchColumnClicked:function(a){var b=a.data.self;null!=b._filterTimeout&&clearTimeout(b._filterTimeout),b._filterTimeout=setTimeout(function(){b._filterTimeout=null;var a=b.$button.children(".fooicon");a.hasClass("fooicon-remove")&&(a.removeClass("fooicon-remove").addClass("fooicon-search"),b.addFilter("search",b.$input.val()),b.filter())},b.delay)},_onDropdownToggleClicked:function(b){b.preventDefault(),b.stopPropagation();var c=b.data.self;c.$dropdown.parent().toggleClass("open"),c.$dropdown.parent().hasClass("open")?a(document).on("click.footable",{self:c},c._onDocumentClicked):a(document).off("click.footable",c._onDocumentClicked)},_onDocumentClicked:function(b){if(0==a(b.target).closest(".dropdown-menu").length){b.preventDefault();var c=b.data.self;c.$dropdown.parent().removeClass("open"),a(document).off("click.footable",c._onDocumentClicked)}}}),b.components.register("filtering",b.Filtering,500)}(jQuery,FooTable),function(a){a.Query=a.Class.extend({construct:function(b,c,d,e){this._original=null,this._value=null,this.space=!a.is.string(c)||"OR"!=c&&"AND"!=c?"AND":c,this.connectors=a.is["boolean"](d)?d:!0,this.ignoreCase=a.is["boolean"](e)?e:!0,this.left=null,this.right=null,this.parts=[],this.operator=null,this.val(b)},val:function(b){if(a.is.emptyString(b))return this._value;if(a.is.emptyString(this._original))this._original=b;else if(this._original==b)return;this._value=b,this._parse()},match:function(b){return a.is.emptyString(this.operator)||"OR"===this.operator?this._left(b,!1)||this._match(b,!1)||this._right(b,!1):"AND"===this.operator?this._left(b,!0)&&this._match(b,!0)&&this._right(b,!0):void 0},_match:function(b,c){var d=this,e=!1,f=a.is.emptyString(b);return a.is.emptyArray(d.parts)&&d.left instanceof a.Query?c:a.is.emptyArray(d.parts)?e:("OR"===d.space?a.arr.each(d.parts,function(c){if(c.empty&&f){if(e=!0,c.negate)return e=!1}else{var g=a.str.contains(b,c.query,d.ignoreCase);if(g&&!c.negate&&(e=!0),g&&c.negate)return e=!1}}):(e=!0,a.arr.each(d.parts,function(c){if(c.empty)return(!f&&!c.negate||f&&c.negate)&&(e=!1),e;var g=a.str.contains(b,c.query,d.ignoreCase);return(!g&&!c.negate||g&&c.negate)&&(e=!1),e})),e)},_left:function(b,c){return this.left instanceof a.Query?this.left.match(b):c},_right:function(b,c){return this.right instanceof a.Query?this.right.match(b):c},_parse:function(){if(!a.is.emptyString(this._value))if(/\sOR\s/.test(this._value)){this.operator="OR";var b=this._value.split(/(?:\sOR\s)(.*)?/);this.left=new a.Query(b[0],this.space,this.connectors,this.ignoreCase),this.right=new a.Query(b[1],this.space,this.connectors,this.ignoreCase)}else if(/\sAND\s/.test(this._value)){this.operator="AND";var c=this._value.split(/(?:\sAND\s)(.*)?/);this.left=new a.Query(c[0],this.space,this.connectors,this.ignoreCase),this.right=new a.Query(c[1],this.space,this.connectors,this.ignoreCase)}else{var d=this;this.parts=a.arr.map(this._value.match(/(?:[^\s"]+|"[^"]*")+/g),function(a){return d._part(a)})}},_part:function(b){var c={query:b,negate:!1,phrase:!1,exact:!1,empty:!1};return a.str.startsWith(c.query,"-")&&(c.query=a.str.from(c.query,"-"),c.negate=!0),/^"(.*?)"$/.test(c.query)?(c.query=c.query.replace(/^"(.*?)"$/,"$1"),c.phrase=!0,c.exact=!0):this.connectors&&/(?:\w)+?([-_\+\.])(?:\w)+?/.test(c.query)&&(c.query=c.query.replace(/(?:\w)+?([-_\+\.])(?:\w)+?/g,function(a,b){return a.replace(b," ")}),c.phrase=!0),c.empty=c.phrase&&a.is.emptyString(c.query),c}})}(FooTable),function(a){a.Cell.prototype.filterValue=null,a.Cell.prototype.__filtering_define__=function(a){this.filterValue=this.column.filterValue.call(this.column,a)},a.Cell.prototype.__filtering_val__=function(b){a.is.defined(b)&&(this.filterValue=this.column.filterValue.call(this.column,b))},a.Cell.extend("define",function(a){this._super(a),this.__filtering_define__(a)}),a.Cell.extend("val",function(a){var b=this._super(a);return this.__filtering_val__(a),b})}(FooTable),function(a,b){b.Column.prototype.filterable=!0,b.Column.prototype.filterValue=function(c){if(b.is.element(c)||b.is.jq(c))return a(c).data("filterValue")||a(c).text();if(b.is.hash(c)&&b.is.hash(c.options)){if(b.is.string(c.options.filterValue))return c.options.filterValue;b.is.defined(c.value)&&(c=c.value)}return b.is.defined(c)&&null!=c?c+"":""},b.Column.prototype.__filtering_define__=function(a){this.filterable=b.is["boolean"](a.filterable)?a.filterable:this.filterable,this.filterValue=b.checkFnValue(this,a.filterValue,this.filterValue)},b.Column.extend("define",function(a){this._super(a),this.__filtering_define__(a)})}(jQuery,FooTable),function(a){a.Defaults.prototype.filtering={enabled:!1,filters:[],delay:1200,min:3,space:"AND",placeholder:"Search",position:"right",connectors:!0,ignoreCase:!0}}(FooTable),function(a){a.Row.prototype.filtered=function(b){var c=!0,d=this;return a.arr.each(b,function(a){return 0==(c=a.matchRow(d))?!1:void 0}),c}}(FooTable),function(a,b){b.Sorter=b.Class.extend({construct:function(a,b){this.column=a,this.direction=b}})}(jQuery,FooTable),function(a,b){b.Sorting=b.Component.extend({construct:function(a){this._super(a,a.o.sorting.enabled),this.o=a.o.sorting,this.column=null,this.allowed=!0,this.initial=null},preinit:function(a){var c=this;this.ft.raise("preinit.ft.sorting",[a]).then(function(){c.ft.$el.hasClass("footable-sorting")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.sorting)?a.sorting:c.enabled,c.enabled&&(c.column=b.arr.first(c.ft.columns.array,function(a){return a.sorted}))},function(){c.enabled=!1})},init:function(){var c=this;this.ft.raise("init.ft.sorting").then(function(){if(!c.initial){var d=!!c.column;c.initial={isset:d,rows:c.ft.rows.all.slice(0),column:d?c.column.name:null,direction:d?c.column.direction:null}}b.arr.each(c.ft.columns.array,function(b){b.sortable&&b.$el.addClass("footable-sortable").append(a("<span/>",{"class":"fooicon fooicon-sort"}))}),c.ft.$el.on("click.footable",".footable-sortable",{self:c},c._onSortClicked)},function(){c.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.paging").then(function(){a.ft.$el.off("click.footable",".footable-sortable",a._onSortClicked),a.ft.$el.children("thead").children("tr.footable-header").children(".footable-sortable").removeClass("footable-sortable footable-asc footable-desc").find("span.fooicon").remove()})},predraw:function(){if(this.column){var a=this,b=a.column;a.ft.rows.array.sort(function(a,c){return"DESC"==b.direction?b.sorter(c.cells[b.index].sortValue,a.cells[b.index].sortValue):b.sorter(a.cells[b.index].sortValue,c.cells[b.index].sortValue)})}},draw:function(){if(this.column){var a=this,b=a.ft.$el.find("thead > tr > .footable-sortable"),c=a.column.$el;b.removeClass("footable-asc footable-desc").children(".fooicon").removeClass("fooicon-sort fooicon-sort-asc fooicon-sort-desc"),b.not(c).children(".fooicon").addClass("fooicon-sort"),c.addClass("DESC"==a.column.direction?"footable-desc":"footable-asc").children(".fooicon").addClass("DESC"==a.column.direction?"fooicon-sort-desc":"fooicon-sort-asc")}},sort:function(a,b){return this._sort(a,b)},toggleAllowed:function(a){a=b.is["boolean"](a)?a:!this.allowed,this.allowed=a,this.ft.$el.toggleClass("footable-sorting-disabled",!this.allowed)},hasChanged:function(){return!(!this.initial||!this.column||this.column.name===this.initial.column&&(this.column.direction===this.initial.direction||null===this.initial.direction&&"ASC"===this.column.direction))},reset:function(){this.initial&&(this.initial.isset?this.sort(this.initial.column,this.initial.direction):(this.column&&(this.column.$el.removeClass("footable-asc footable-desc"),this.column=null),this.ft.rows.all=this.initial.rows,this.ft.draw()))},_sort:function(c,d){if(!this.allowed)return a.Deferred().reject("sorting disabled");var e=this,f=new b.Sorter(e.ft.columns.get(c),b.Sorting.dir(d));return e.ft.raise("before.ft.sorting",[f]).then(function(){return b.arr.each(e.ft.columns.array,function(a){a!=e.column&&(a.direction=null)}),e.column=e.ft.columns.get(f.column),e.column&&(e.column.direction=b.Sorting.dir(f.direction)),e.ft.draw().then(function(){e.ft.raise("after.ft.sorting",[f])})})},_onSortClicked:function(b){var c=b.data.self,d=a(this).closest("th,td"),e=d.is(".footable-asc, .footable-desc")?d.hasClass("footable-desc")?"ASC":"DESC":"ASC";c._sort(d.index(),e)}}),b.Sorting.dir=function(a){return!b.is.string(a)||"ASC"!=a&&"DESC"!=a?"ASC":a},b.components.register("sorting",b.Sorting,600)}(jQuery,FooTable),function(a){a.Cell.prototype.sortValue=null,a.Cell.prototype.__sorting_define__=function(a){this.sortValue=this.column.sortValue.call(this.column,a)},a.Cell.prototype.__sorting_val__=function(b){a.is.defined(b)&&(this.sortValue=this.column.sortValue.call(this.column,b))},a.Cell.extend("define",function(a){this._super(a),this.__sorting_define__(a)}),a.Cell.extend("val",function(a){var b=this._super(a);return this.__sorting_val__(a),b})}(FooTable),function(a,b){b.Column.prototype.direction=null,b.Column.prototype.sortable=!0,b.Column.prototype.sorted=!1,b.Column.prototype.sorter=function(a,b){return"string"==typeof a&&(a=a.toLowerCase()),"string"==typeof b&&(b=b.toLowerCase()),a===b?0:b>a?-1:1},b.Column.prototype.sortValue=function(c){if(b.is.element(c)||b.is.jq(c))return a(c).data("sortValue")||this.parser(c);if(b.is.hash(c)&&b.is.hash(c.options)){if(b.is.string(c.options.sortValue))return c.options.sortValue;b.is.defined(c.value)&&(c=c.value)}return b.is.defined(c)&&null!=c?c:null},b.Column.prototype.__sorting_define__=function(a){this.sorter=b.checkFnValue(this,a.sorter,this.sorter),this.direction=b.is.type(a.direction,"string")?b.Sorting.dir(a.direction):null,this.sortable=b.is["boolean"](a.sortable)?a.sortable:!0,this.sorted=b.is["boolean"](a.sorted)?a.sorted:!1,this.sortValue=b.checkFnValue(this,a.sortValue,this.sortValue)},b.Column.extend("define",function(a){this._super(a),this.__sorting_define__(a)})}(jQuery,FooTable),function(a){a.Defaults.prototype.sorting={enabled:!1}}(FooTable),function(a,b){b.HTMLColumn.extend("__sorting_define__",function(c){this._super(c),this.sortUse=b.is.string(c.sortUse)&&-1!==a.inArray(c.sortUse,["html","text"])?c.sortUse:"html"}),b.HTMLColumn.prototype.sortValue=function(c){if(b.is.element(c)||b.is.jq(c))return a(c).data("sortValue")||a.trim(a(c)[this.sortUse]());if(b.is.hash(c)&&b.is.hash(c.options)){if(b.is.string(c.options.sortValue))return c.options.sortValue;b.is.defined(c.value)&&(c=c.value)}return b.is.defined(c)&&null!=c?c:null}}(jQuery,FooTable),function(a){a.Table.prototype.sort=function(b,c){return this.use(a.Sorting).sort(b,c)}}(FooTable),function(a,b){b.Pager=b.Class.extend({construct:function(a,b,c,d,e){this.total=a,this.current=b,this.size=c,this.page=d,this.forward=e}})}(jQuery,FooTable),function(a,b){b.Paging=b.Component.extend({construct:function(a){this._super(a,a.o.paging.enabled),this.strings=a.o.paging.strings,this.current=a.o.paging.current,this.size=a.o.paging.size,this.limit=a.o.paging.limit,this.position=a.o.paging.position,this.countFormat=a.o.paging.countFormat,this.total=-1,this.totalRows=0,this.previous=-1,this.formattedCount=null,this.$row=null,this.$cell=null,this.$pagination=null,this.$count=null,this.detached=!1,this._createdLinks=0},preinit:function(a){var c=this;this.ft.raise("preinit.ft.paging",[a]).then(function(){c.ft.$el.hasClass("footable-paging")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.paging)?a.paging:c.enabled,c.enabled&&(c.size=b.is.number(a.pagingSize)?a.pagingSize:c.size,c.current=b.is.number(a.pagingCurrent)?a.pagingCurrent:c.current,c.limit=b.is.number(a.pagingLimit)?a.pagingLimit:c.limit,c.ft.$el.hasClass("footable-paging-left")&&(c.position="left"),c.ft.$el.hasClass("footable-paging-center")&&(c.position="center"),c.ft.$el.hasClass("footable-paging-right")&&(c.position="right"),c.position=b.is.string(a.pagingPosition)?a.pagingPosition:c.position,c.countFormat=b.is.string(a.pagingCountFormat)?a.pagingCountFormat:c.countFormat,c.total=Math.ceil(c.ft.rows.all.length/c.size))},function(){c.enabled=!1})},init:function(){var a=this;this.ft.raise("init.ft.paging").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.paging").then(function(){a.ft.$el.removeClass("footable-paging").find("tfoot > tr.footable-paging").remove(),a.detached=!1})},predraw:function(){this.total=Math.ceil(this.ft.rows.array.length/this.size),this.current=this.current>this.total?this.total:this.current<1?1:this.current,this.totalRows=this.ft.rows.array.length,this.totalRows>this.size&&(this.ft.rows.array=this.ft.rows.array.splice((this.current-1)*this.size,this.size));var a=this.size*(this.current-1)+1,b=this.size*this.current;0==this.ft.rows.array.length?(a=0,b=0):b=b>this.totalRows?this.totalRows:b,this.formattedCount=this._countFormat(this.current,this.total,a,b,this.totalRows)},draw:function(){if(this.total<=1)this.detached||(this.$row.detach(),this.detached=!0);else{if(this.detached){var b=this.ft.$el.children("tfoot");0==b.length&&(b=a("<tfoot/>"),this.ft.$el.append(b)),this.$row.appendTo(b),this.detached=!1}this.$cell.attr("colspan",this.ft.columns.visibleColspan),this._createLinks(),this._setVisible(this.current,this.current>this.previous),this._setNavigation(!0),this.$count.text(this.formattedCount)}},$create:function(){var b="footable-paging-center";switch(this.position){case"left":b="footable-paging-left";break;case"right":b="footable-paging-right"}this.ft.$el.addClass("footable-paging").addClass(b),this.$cell=a("<td/>").attr("colspan",this.ft.columns.visibleColspan);var c=this.ft.$el.children("tfoot");0==c.length&&(c=a("<tfoot/>"),this.ft.$el.append(c)),this.$row=a("<tr/>",{"class":"footable-paging"}).append(this.$cell).appendTo(c),this.$pagination=a("<ul/>",{"class":"pagination"}).on("click.footable","a.footable-page-link",{self:this},this._onPageClicked),this.$count=a("<span/>",{"class":"label label-default"}),this.$cell.append(this.$pagination,a("<div/>",{"class":"divider"}),this.$count),this.detached=!1},first:function(){return this._set(1)},prev:function(){return this._set(this.current-1>0?this.current-1:1)},next:function(){return this._set(this.current+1<this.total?this.current+1:this.total)},last:function(){return this._set(this.total)},"goto":function(a){return this._set(a>this.total?this.total:1>a?1:a)},prevPages:function(){var a=this.$pagination.children("li.footable-page.visible:first").data("page")-1;this._setVisible(a,!0),this._setNavigation(!1)},nextPages:function(){var a=this.$pagination.children("li.footable-page.visible:last").data("page")+1;this._setVisible(a,!1),this._setNavigation(!1)},pageSize:function(a){return b.is.number(a)?(this.size=a,this.total=Math.ceil(this.ft.rows.all.length/this.size),b.is.jq(this.$row)&&this.$row.remove(),this.$create(),void this.ft.draw()):this.size},_set:function(c){var d=this,e=new b.Pager(d.total,d.current,d.size,c,c>d.current);return d.ft.raise("before.ft.paging",[e]).then(function(){return e.page=e.page>e.total?e.total:e.page,e.page=e.page<1?1:e.page,d.current==c?a.when():(d.previous=d.current,d.current=e.page,d.ft.draw().then(function(){d.ft.raise("after.ft.paging",[e])}))})},_createLinks:function(){if(this._createdLinks!==this.total){var b=this,c=b.total>1,d=function(b,c,d){return a("<li/>",{"class":d}).attr("data-page",b).append(a("<a/>",{"class":"footable-page-link",href:"#"}).data("page",b).html(c))};b.$pagination.empty(),c&&(b.$pagination.append(d("first",b.strings.first,"footable-page-nav")),b.$pagination.append(d("prev",b.strings.prev,"footable-page-nav")),b.limit>0&&b.limit<b.total&&b.$pagination.append(d("prev-limit",b.strings.prevPages,"footable-page-nav")));for(var e,f=0;f<b.total;f++)e=d(f+1,f+1,"footable-page"),b.$pagination.append(e);c&&(b.limit>0&&b.limit<b.total&&b.$pagination.append(d("next-limit",b.strings.nextPages,"footable-page-nav")),b.$pagination.append(d("next",b.strings.next,"footable-page-nav")),b.$pagination.append(d("last",b.strings.last,"footable-page-nav"))),b._createdLinks=b.total}},_setNavigation:function(a){1==this.current?this.$pagination.children('li[data-page="first"],li[data-page="prev"]').addClass("disabled"):this.$pagination.children('li[data-page="first"],li[data-page="prev"]').removeClass("disabled"),this.current==this.total?this.$pagination.children('li[data-page="next"],li[data-page="last"]').addClass("disabled"):this.$pagination.children('li[data-page="next"],li[data-page="last"]').removeClass("disabled"),1==(this.$pagination.children("li.footable-page.visible:first").data("page")||1)?this.$pagination.children('li[data-page="prev-limit"]').addClass("disabled"):this.$pagination.children('li[data-page="prev-limit"]').removeClass("disabled"),(this.$pagination.children("li.footable-page.visible:last").data("page")||this.limit)==this.total?this.$pagination.children('li[data-page="next-limit"]').addClass("disabled"):this.$pagination.children('li[data-page="next-limit"]').removeClass("disabled"),this.limit>0&&this.total<this.limit?this.$pagination.children('li[data-page="prev-limit"],li[data-page="next-limit"]').hide():this.$pagination.children('li[data-page="prev-limit"],li[data-page="next-limit"]').show(),a&&this.$pagination.children("li.footable-page").removeClass("active").filter('li[data-page="'+this.current+'"]').addClass("active")},_setVisible:function(a,b){if(this.limit>0&&this.total>this.limit){if(!this.$pagination.children('li.footable-page[data-page="'+a+'"]').hasClass("visible")){var c=0,d=0;1==b?(d=a>this.total?this.total:a,c=d-this.limit):(c=1>a?0:a-1,d=c+this.limit),0>c&&(c=0,d=this.limit>this.total?this.total:this.limit),d>this.total&&(d=this.total,c=this.total-this.limit<0?0:this.total-this.limit),this.$pagination.children("li.footable-page").removeClass("visible").slice(c,d).addClass("visible")}}else this.$pagination.children("li.footable-page").removeClass("visible").slice(0,this.total).addClass("visible")},_countFormat:function(a,b,c,d,e){return this.countFormat.replace(/\{CP}/g,a).replace(/\{TP}/g,b).replace(/\{PF}/g,c).replace(/\{PL}/g,d).replace(/\{TR}/g,e)},_onPageClicked:function(b){if(b.preventDefault(),!a(b.target).closest("li").is(".active,.disabled")){var c=b.data.self,d=a(this).data("page");switch(d){case"first":return void c.first();case"prev":return void c.prev();case"next":return void c.next();case"last":return void c.last();case"prev-limit":return void c.prevPages();case"next-limit":return void c.nextPages();default:return void c._set(d)}}}}),b.components.register("paging",b.Paging,400)}(jQuery,FooTable),function(a){a.Defaults.prototype.paging={enabled:!1,countFormat:"{CP} of {TP}",current:1,limit:5,position:"center",size:10,strings:{first:"&laquo;",prev:"&lsaquo;",next:"&rsaquo;",last:"&raquo;",prevPages:"...",nextPages:"..."}}}(FooTable),function(a){a.Table.prototype.gotoPage=function(b){return this.use(a.Paging)["goto"](b)},a.Table.prototype.nextPage=function(){return this.use(a.Paging).next()},a.Table.prototype.prevPage=function(){return this.use(a.Paging).prev()},a.Table.prototype.firstPage=function(){return this.use(a.Paging).first()},a.Table.prototype.lastPage=function(){return this.use(a.Paging).last()},a.Table.prototype.nextPages=function(){return this.use(a.Paging).nextPages()},a.Table.prototype.prevPages=function(){return this.use(a.Paging).prevPages()},a.Table.prototype.pageSize=function(b){return this.use(a.Paging).pageSize(b)}}(FooTable),function(a,b){b.Editing=b.Component.extend({construct:function(c){this._super(c,c.o.editing.enabled),this.pageToNew=c.o.editing.pageToNew,this.alwaysShow=c.o.editing.alwaysShow,this.column=a.extend(!0,{},c.o.editing.column,{visible:this.alwaysShow}),this.position=c.o.editing.position,this.showText=c.o.editing.showText,this.hideText=c.o.editing.hideText,this.addText=c.o.editing.addText,this.editText=c.o.editing.editText,this.deleteText=c.o.editing.deleteText,this.viewText=c.o.editing.viewText,this.allowAdd=c.o.editing.allowAdd,this.allowEdit=c.o.editing.allowEdit,this.allowDelete=c.o.editing.allowDelete,this.allowView=c.o.editing.allowView,this._$buttons=null,this.callbacks={addRow:b.checkFnValue(this,c.o.editing.addRow),editRow:b.checkFnValue(this,c.o.editing.editRow),deleteRow:b.checkFnValue(this,c.o.editing.deleteRow),viewRow:b.checkFnValue(this,c.o.editing.viewRow)}},preinit:function(c){var d=this;this.ft.raise("preinit.ft.editing",[c]).then(function(){if(d.ft.$el.hasClass("footable-editing")&&(d.enabled=!0),d.enabled=b.is["boolean"](c.editing)?c.editing:d.enabled,d.enabled){if(d.pageToNew=b.is["boolean"](c.editingPageToNew)?c.editingPageToNew:d.pageToNew,d.alwaysShow=b.is["boolean"](c.editingAlwaysShow)?c.editingAlwaysShow:d.alwaysShow,d.position=b.is.string(c.editingPosition)?c.editingPosition:d.position,d.showText=b.is.string(c.editingShowText)?c.editingShowText:d.showText,d.hideText=b.is.string(c.editingHideText)?c.editingHideText:d.hideText,d.addText=b.is.string(c.editingAddText)?c.editingAddText:d.addText,d.editText=b.is.string(c.editingEditText)?c.editingEditText:d.editText,d.deleteText=b.is.string(c.editingDeleteText)?c.editingDeleteText:d.deleteText,d.viewText=b.is.string(c.editingViewText)?c.editingViewText:d.viewText,d.allowAdd=b.is["boolean"](c.editingAllowAdd)?c.editingAllowAdd:d.allowAdd,d.allowEdit=b.is["boolean"](c.editingAllowEdit)?c.editingAllowEdit:d.allowEdit,d.allowDelete=b.is["boolean"](c.editingAllowDelete)?c.editingAllowDelete:d.allowDelete,d.allowView=b.is["boolean"](c.editingAllowView)?c.editingAllowView:d.allowView,d.column=new b.EditingColumn(d.ft,d,a.extend(!0,{},d.column,c.editingColumn,{visible:d.alwaysShow})),d.ft.$el.hasClass("footable-editing-left")&&(d.position="left"),d.ft.$el.hasClass("footable-editing-right")&&(d.position="right"),"right"===d.position)d.column.index=d.ft.columns.array.length;else{
-d.column.index=0;for(var e=0,f=d.ft.columns.array.length;f>e;e++)d.ft.columns.array[e].index+=1}d.ft.columns.array.push(d.column),d.ft.columns.array.sort(function(a,b){return a.index-b.index}),d.callbacks.addRow=b.checkFnValue(d,c.editingAddRow,d.callbacks.addRow),d.callbacks.editRow=b.checkFnValue(d,c.editingEditRow,d.callbacks.editRow),d.callbacks.deleteRow=b.checkFnValue(d,c.editingDeleteRow,d.callbacks.deleteRow),d.callbacks.viewRow=b.checkFnValue(d,c.editingViewRow,d.callbacks.viewRow)}},function(){d.enabled=!1})},init:function(){var a=this;this.ft.raise("init.ft.editing").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.editing").then(function(){a.ft.$el.removeClass("footable-editing footable-editing-always-show footable-editing-no-add footable-editing-no-edit footable-editing-no-delete footable-editing-no-view").off("click.ft.editing").find("tfoot > tr.footable-editing").remove()})},$create:function(){var b=this,c="right"===b.position?"footable-editing-right":"footable-editing-left";b.ft.$el.addClass("footable-editing").addClass(c).on("click.ft.editing",".footable-show",{self:b},b._onShowClick).on("click.ft.editing",".footable-hide",{self:b},b._onHideClick).on("click.ft.editing",".footable-edit",{self:b},b._onEditClick).on("click.ft.editing",".footable-delete",{self:b},b._onDeleteClick).on("click.ft.editing",".footable-view",{self:b},b._onViewClick).on("click.ft.editing",".footable-add",{self:b},b._onAddClick),b.$cell=a("<td/>").attr("colspan",b.ft.columns.visibleColspan).append(b.$buttonShow()),b.allowAdd&&b.$cell.append(b.$buttonAdd()),b.$cell.append(b.$buttonHide()),b.alwaysShow&&b.ft.$el.addClass("footable-editing-always-show"),b.allowAdd||b.ft.$el.addClass("footable-editing-no-add"),b.allowEdit||b.ft.$el.addClass("footable-editing-no-edit"),b.allowDelete||b.ft.$el.addClass("footable-editing-no-delete"),b.allowView||b.ft.$el.addClass("footable-editing-no-view");var d=b.ft.$el.children("tfoot");0==d.length&&(d=a("<tfoot/>"),b.ft.$el.append(d)),b.$row=a("<tr/>",{"class":"footable-editing"}).append(b.$cell).appendTo(d)},$buttonShow:function(){return'<button type="button" class="btn btn-primary footable-show">'+this.showText+"</button>"},$buttonHide:function(){return'<button type="button" class="btn btn-default footable-hide">'+this.hideText+"</button>"},$buttonAdd:function(){return'<button type="button" class="btn btn-primary footable-add">'+this.addText+"</button> "},$buttonEdit:function(){return'<button type="button" class="btn btn-default footable-edit">'+this.editText+"</button> "},$buttonDelete:function(){return'<button type="button" class="btn btn-default footable-delete">'+this.deleteText+"</button>"},$buttonView:function(){return'<button type="button" class="btn btn-default footable-view">'+this.viewText+"</button> "},$rowButtons:function(){return b.is.jq(this._$buttons)?this._$buttons.clone():(this._$buttons=a('<div class="btn-group btn-group-xs" role="group"></div>'),this.allowView&&this._$buttons.append(this.$buttonView()),this.allowEdit&&this._$buttons.append(this.$buttonEdit()),this.allowDelete&&this._$buttons.append(this.$buttonDelete()),this._$buttons)},draw:function(){this.$cell.attr("colspan",this.ft.columns.visibleColspan)},_onEditClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("edit.ft.editing",[e]).then(function(){d.callbacks.editRow.call(d.ft,e)})},_onDeleteClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("delete.ft.editing",[e]).then(function(){d.callbacks.deleteRow.call(d.ft,e)})},_onViewClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("view.ft.editing",[e]).then(function(){d.callbacks.viewRow.call(d.ft,e)})},_onAddClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("add.ft.editing").then(function(){b.callbacks.addRow.call(b.ft)})},_onShowClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("show.ft.editing").then(function(){b.ft.$el.addClass("footable-editing-show"),b.column.visible=!0,b.ft.draw()})},_onHideClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("hide.ft.editing").then(function(){b.ft.$el.removeClass("footable-editing-show"),b.column.visible=!1,b.ft.draw()})}}),b.components.register("editing",b.Editing,850)}(jQuery,FooTable),function(a,b){b.EditingColumn=b.Column.extend({construct:function(a,b,c){this._super(a,c,"editing"),this.editing=b},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("<th/>",{"class":"footable-editing"})).html(this.title)},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();return"td"==d||"th"==d?c.data("value")||c.contents():c}return null},createCell:function(c){var d=this.editing.$rowButtons(),e=a("<td/>").append(d);return b.is.jq(c.$el)&&(0===this.index?e.prependTo(c.$el):e.insertAfter(c.$el.children().eq(this.index-1))),new b.Cell(this.ft,c,this,e||e.html())}}),b.columns.register("editing",b.EditingColumn)}(jQuery,FooTable),function(a,b){b.Defaults.prototype.editing={enabled:!1,pageToNew:!0,position:"right",alwaysShow:!1,addRow:function(){},editRow:function(a){},deleteRow:function(a){},viewRow:function(a){},showText:'<span class="fooicon fooicon-pencil" aria-hidden="true"></span> Edit rows',hideText:"Cancel",addText:"New row",editText:'<span class="fooicon fooicon-pencil" aria-hidden="true"></span>',deleteText:'<span class="fooicon fooicon-trash" aria-hidden="true"></span>',viewText:'<span class="fooicon fooicon-stats" aria-hidden="true"></span>',allowAdd:!0,allowEdit:!0,allowDelete:!0,allowView:!1,column:{classes:"footable-editing",name:"editing",title:"",filterable:!1,sortable:!1}}}(jQuery,FooTable),function(a,b){b.is.defined(b.Paging)&&(b.Paging.prototype.unpaged=[],b.Paging.extend("predraw",function(){this.unpaged=this.ft.rows.array.slice(0),this._super()}))}(jQuery,FooTable),function(a,b){b.Row.prototype.add=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var b=d.ft.rows.all.push(d)-1;return c?d.ft.draw().then(function(){a.resolve(b)}):void a.resolve(b)})},b.Row.prototype["delete"]=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var e=d.ft.rows.all.indexOf(d);return b.is.number(e)&&e>=0&&e<d.ft.rows.all.length&&(d.ft.rows.all.splice(e,1),c)?d.ft.draw().then(function(){a.resolve(d)}):void a.resolve(d)})},b.is.defined(b.Paging)&&b.Row.extend("add",function(a){a=b.is["boolean"](a)?a:!0;var c,d=this,e=this._super(a),f=d.ft.use(b.Editing);return f&&f.pageToNew&&(c=d.ft.use(b.Paging))&&a?e.then(function(){var a=c.unpaged.indexOf(d),b=Math.ceil((a+1)/c.size);return c.current!==b?c["goto"](b):void 0}):e}),b.is.defined(b.Sorting)&&b.Row.extend("val",function(a,c){c=b.is["boolean"](c)?c:!0;var d=this._super(a);if(!b.is.hash(a))return d;var e=this;return c&&e.ft.draw().then(function(){var a,c=e.ft.use(b.Editing);if(b.is.defined(b.Paging)&&c&&c.pageToNew&&(a=e.ft.use(b.Paging))){var d=a.unpaged.indexOf(e),f=Math.ceil((d+1)/a.size);if(a.current!==f)return a["goto"](f)}}),d})}(jQuery,FooTable),function(a){a.Rows.prototype.add=function(b,c){var d=b;a.is.hash(b)&&(d=new FooTable.Row(this.ft,this.ft.columns.array,b)),d instanceof FooTable.Row&&d.add(c)},a.Rows.prototype.update=function(b,c,d){var e=this.ft.rows.all.length,f=b;a.is.number(b)&&b>=0&&e>b&&(f=this.ft.rows.all[b]),f instanceof FooTable.Row&&a.is.hash(c)&&f.val(c,d)},a.Rows.prototype["delete"]=function(b,c){var d=this.ft.rows.all.length,e=b;a.is.number(b)&&b>=0&&d>b&&(e=this.ft.rows.all[b]),e instanceof FooTable.Row&&e["delete"](c)}}(FooTable),function(a,b){var c=0,d=function(a){var b,c,d=2166136261;for(b=0,c=a.length;c>b;b++)d^=a.charCodeAt(b),d+=(d<<1)+(d<<4)+(d<<7)+(d<<8)+(d<<24);return d>>>0}(location.origin+location.pathname);b.State=b.Component.extend({construct:function(a){this._super(a,a.o.state.enabled),this._key="1",this.key=this._key+(b.is.string(a.o.state.key)?a.o.state.key:this._uid()),this.filtering=b.is["boolean"](a.o.state.filtering)?a.o.state.filtering:!0,this.paging=b.is["boolean"](a.o.state.paging)?a.o.state.paging:!0,this.sorting=b.is["boolean"](a.o.state.sorting)?a.o.state.sorting:!0},preinit:function(a){var c=this;this.ft.raise("preinit.ft.state",[a]).then(function(){c.enabled=b.is["boolean"](a.state)?a.state:c.enabled,c.enabled&&(c.key=c._key+(b.is.string(a.stateKey)?a.stateKey:c.key),c.filtering=b.is["boolean"](a.stateFiltering)?a.stateFiltering:c.filtering,c.paging=b.is["boolean"](a.statePaging)?a.statePaging:c.paging,c.sorting=b.is["boolean"](a.stateSorting)?a.stateSorting:c.sorting)},function(){c.enabled=!1})},get:function(a){return JSON.parse(localStorage.getItem(this.key+":"+a))},set:function(a,b){localStorage.setItem(this.key+":"+a,JSON.stringify(b))},remove:function(a){localStorage.removeItem(this.key+":"+a)},read:function(){this.ft.execute(!1,!0,"readState")},write:function(){this.ft.execute(!1,!0,"writeState")},clear:function(){this.ft.execute(!1,!0,"clearState")},_uid:function(){var a=this.ft.$el.attr("id");return d+"_"+(b.is.string(a)?a:++c)}}),b.components.register("state",b.State,700)}(jQuery,FooTable),function(a){a.Component.prototype.readState=function(){},a.Component.prototype.writeState=function(){},a.Component.prototype.clearState=function(){}}(FooTable),function(a){a.Defaults.prototype.state={enabled:!1,filtering:!0,paging:!0,sorting:!0,key:null}}(FooTable),function(a){a.Filtering&&(a.Filtering.prototype.readState=function(){if(this.ft.state.filtering){var b=this.ft.state.get("filtering");a.is.hash(b)&&!a.is.emptyArray(b.filters)&&(this.filters=this.ensure(b.filters))}},a.Filtering.prototype.writeState=function(){if(this.ft.state.filtering){var b=a.arr.map(this.filters,function(b){return{name:b.name,query:b.query instanceof a.Query?b.query.val():b.query,columns:a.arr.map(b.columns,function(a){return a.name}),hidden:b.hidden,space:b.space,connectors:b.connectors,ignoreCase:b.ignoreCase}});this.ft.state.set("filtering",{filters:b})}},a.Filtering.prototype.clearState=function(){this.ft.state.filtering&&this.ft.state.remove("filtering")})}(FooTable),function(a){a.Paging&&(a.Paging.prototype.readState=function(){if(this.ft.state.paging){var b=this.ft.state.get("paging");a.is.hash(b)&&(this.current=b.current,this.size=b.size)}},a.Paging.prototype.writeState=function(){this.ft.state.paging&&this.ft.state.set("paging",{current:this.current,size:this.size})},a.Paging.prototype.clearState=function(){this.ft.state.paging&&this.ft.state.remove("paging")})}(FooTable),function(a){a.Sorting&&(a.Sorting.prototype.readState=function(){if(this.ft.state.sorting){var b=this.ft.state.get("sorting");if(a.is.hash(b)){var c=this.ft.columns.get(b.column);c instanceof a.Column&&(this.column=c,this.column.direction=b.direction)}}},a.Sorting.prototype.writeState=function(){this.ft.state.sorting&&this.column instanceof a.Column&&this.ft.state.set("sorting",{column:this.column.name,direction:this.column.direction})},a.Sorting.prototype.clearState=function(){this.ft.state.sorting&&this.ft.state.remove("sorting")})}(FooTable),function(a){a.Table.extend("_construct",function(a){this.state=this.use(FooTable.State),this._super(a)}),a.Table.extend("_preinit",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.read()})}),a.Table.extend("draw",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.write()})})}(FooTable);
\ No newline at end of file
diff --git a/apps/static/js/plugins/fullcalendar/fullcalendar.min.js b/apps/static/js/plugins/fullcalendar/fullcalendar.min.js
deleted file mode 100644
index fcbf126f1..000000000
--- a/apps/static/js/plugins/fullcalendar/fullcalendar.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * FullCalendar v2.2.0
- * Docs & License: http://arshaw.com/fullcalendar/
- * (c) 2013 Adam Shaw
- */
-(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e){return e.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"t")}function i(t,e){var n=e.longDateFormat("L");return n=n.replace(/^Y+[^\w\s]*|[^\w\s]*Y+$/g,""),t.isRTL?n+=" ddd":n="ddd "+n,n}function r(t){s(Fe,t)}function s(e){function n(n,i){t.isPlainObject(i)&&t.isPlainObject(e[n])&&!o(n)?e[n]=s({},e[n],i):void 0!==i&&(e[n]=i)}for(var i=1;arguments.length>i;i++)t.each(arguments[i],n);return e}function o(t){return/(Time|Duration)$/.test(t)}function l(n,i){function r(t){var n=e.localeData||e.langData;return n.call(e,t)||n.call(e,"en")}function o(t){ne?u()&&(p(),f(t)):l()}function l(){ie=K.theme?"ui":"fc",n.addClass("fc"),K.isRTL?n.addClass("fc-rtl"):n.addClass("fc-ltr"),K.theme?n.addClass("ui-widget"):n.addClass("fc-unthemed"),ne=t("<div class='fc-view-container'/>").prependTo(n),te=new a(q,K),ee=te.render(),ee&&n.prepend(ee),h(K.defaultView),K.handleWindowResize&&(oe=_(v,K.windowResizeDelay),t(window).resize(oe))}function d(){re&&re.destroy(),te.destroy(),ne.remove(),n.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),t(window).unbind("resize",oe)}function u(){return n.is(":visible")}function h(t){f(0,t)}function f(e,n){ue++,re&&n&&re.name!==n&&(te.deactivateButton(re.name),B(),re.start&&re.destroy(),re.el.remove(),re=null),!re&&n&&(re=new Ge[n](q),re.el=t("<div class='fc-view fc-"+n+"-view' />").appendTo(ne),te.activateButton(n)),re&&(e&&(le=re.incrementDate(le,e)),re.start&&!e&&le.isWithin(re.intervalStart,re.intervalEnd)||u()&&(B(),re.start&&re.destroy(),re.render(le),I(),C(),H(),b())),I(),ue--}function g(t){return u()?(t&&m(),ue++,re.updateSize(!0),ue--,!0):void 0}function p(){u()&&m()}function m(){se="number"==typeof K.contentHeight?K.contentHeight:"number"==typeof K.height?K.height-(ee?ee.outerHeight(!0):0):Math.round(ne.width()/Math.max(K.aspectRatio,.5))}function v(t){!ue&&t.target===window&&re.start&&g(!0)&&re.trigger("windowResize",de)}function y(){S(),E()}function w(){u()&&(B(),re.destroyEvents(),re.renderEvents(he),I())}function S(){B(),re.destroyEvents(),I()}function b(){!K.lazyFetching||ae(re.start,re.end)?E():w()}function E(){ce(re.start,re.end)}function D(t){he=t,w()}function T(){w()}function C(){te.updateTitle(re.title)}function H(){var t=q.getNow();t.isWithin(re.intervalStart,re.intervalEnd)?te.disableButton("today"):te.enableButton("today")}function x(t,e){t=q.moment(t),e=e?q.moment(e):t.hasTime()?t.clone().add(q.defaultTimedEventDuration):t.clone().add(q.defaultAllDayEventDuration),re.select(t,e)}function k(){re&&re.unselect()}function R(){f(-1)}function P(){f(1)}function F(){le.add(-1,"years"),f()}function L(){le.add(1,"years"),f()}function G(){le=q.getNow(),f()}function N(t){le=q.moment(t),f()}function A(t){le.add(e.duration(t)),f()}function Y(t,e){var n,i;e&&void 0!==Ge[e]||(e=e||"day",n=te.getViewsWithButtons().join(" "),i=n.match(RegExp("\\w+"+z(e))),i||(i=n.match(/\w+Day/)),e=i?i[0]:"agendaDay"),le=t,h(e)}function V(){return le.clone()}function B(){ne.css({width:"100%",height:ne.height(),overflow:"hidden"})}function I(){ne.css({width:"",height:"",overflow:""})}function Z(){return q}function j(){return re}function X(t,e){return void 0===e?K[t]:(("height"==t||"contentHeight"==t||"aspectRatio"==t)&&(K[t]=e,g(!0)),void 0)}function $(t,e){return K[t]?K[t].apply(e||de,Array.prototype.slice.call(arguments,2)):void 0}var q=this;i=i||{};var U,K=s({},Fe,i);U=K.lang in Le?Le[K.lang]:Le[Fe.lang],U&&(K=s({},Fe,U,i)),K.isRTL&&(K=s({},Fe,ze,U||{},i)),q.options=K,q.render=o,q.destroy=d,q.refetchEvents=y,q.reportEvents=D,q.reportEventChange=T,q.rerenderEvents=w,q.changeView=h,q.select=x,q.unselect=k,q.prev=R,q.next=P,q.prevYear=F,q.nextYear=L,q.today=G,q.gotoDate=N,q.incrementDate=A,q.zoomTo=Y,q.getDate=V,q.getCalendar=Z,q.getView=j,q.option=X,q.trigger=$;var Q=M(r(K.lang));if(K.monthNames&&(Q._months=K.monthNames),K.monthNamesShort&&(Q._monthsShort=K.monthNamesShort),K.dayNames&&(Q._weekdays=K.dayNames),K.dayNamesShort&&(Q._weekdaysShort=K.dayNamesShort),null!=K.firstDay){var J=M(Q._week);J.dow=K.firstDay,Q._week=J}q.defaultAllDayEventDuration=e.duration(K.defaultAllDayEventDuration),q.defaultTimedEventDuration=e.duration(K.defaultTimedEventDuration),q.moment=function(){var t;return"local"===K.timezone?(t=_e.moment.apply(null,arguments),t.hasTime()&&t.local()):t="UTC"===K.timezone?_e.moment.utc.apply(null,arguments):_e.moment.parseZone.apply(null,arguments),"_locale"in t?t._locale=Q:t._lang=Q,t},q.getIsAmbigTimezone=function(){return"local"!==K.timezone&&"UTC"!==K.timezone},q.rezoneDate=function(t){return q.moment(t.toArray())},q.getNow=function(){var t=K.now;return"function"==typeof t&&(t=t()),q.moment(t)},q.calculateWeekNumber=function(t){var e=K.weekNumberCalculation;return"function"==typeof e?e(t):"local"===e?t.week():"ISO"===e.toUpperCase()?t.isoWeek():void 0},q.getEventEnd=function(t){return t.end?t.end.clone():q.getDefaultEventEnd(t.allDay,t.start)},q.getDefaultEventEnd=function(t,e){var n=e.clone();return t?n.stripTime().add(q.defaultAllDayEventDuration):n.add(q.defaultTimedEventDuration),q.getIsAmbigTimezone()&&n.stripZone(),n},q.formatRange=function(t,e,n){return"function"==typeof n&&(n=n.call(q,K,Q)),W(t,e,n,null,K.isRTL)},q.formatDate=function(t,e){return"function"==typeof e&&(e=e.call(q,K,Q)),O(t,e)},c.call(q,K);var te,ee,ne,ie,re,se,oe,le,ae=q.isFetchNeeded,ce=q.fetchEvents,de=n[0],ue=0,he=[];le=null!=K.defaultDate?q.moment(K.defaultDate):q.getNow(),q.getSuggestedViewHeight=function(){return void 0===se&&p(),se},q.isHeightAuto=function(){return"auto"===K.contentHeight||"auto"===K.height}}function a(e,n){function i(){var e=n.header;return f=n.theme?"ui":"fc",e?g=t("<div class='fc-toolbar'/>").append(s("left")).append(s("right")).append(s("center")).append('<div class="fc-clear"/>'):void 0}function r(){g.remove()}function s(i){var r=t('<div class="fc-'+i+'"/>'),s=n.header[i];return s&&t.each(s.split(" "),function(){var i,s=t(),o=!0;t.each(this.split(","),function(i,r){var l,a,c,d,u,h,g,m;"title"==r?(s=s.add(t("<h2>&nbsp;</h2>")),o=!1):(e[r]?l=function(){e[r]()}:Ge[r]&&(l=function(){e.changeView(r)},p.push(r)),l&&(a=T(n.themeButtonIcons,r),c=T(n.buttonIcons,r),d=T(n.defaultButtonText,r),u=T(n.buttonText,r),h=u?F(u):a&&n.theme?"<span class='ui-icon ui-icon-"+a+"'></span>":c&&!n.theme?"<span class='fc-icon fc-icon-"+c+"'></span>":F(d||r),g=["fc-"+r+"-button",f+"-button",f+"-state-default"],m=t('<button type="button" class="'+g.join(" ")+'">'+h+"</button>").click(function(){m.hasClass(f+"-state-disabled")||(l(),(m.hasClass(f+"-state-active")||m.hasClass(f+"-state-disabled"))&&m.removeClass(f+"-state-hover"))}).mousedown(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-down")}).mouseup(function(){m.removeClass(f+"-state-down")}).hover(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-hover")},function(){m.removeClass(f+"-state-hover").removeClass(f+"-state-down")}),s=s.add(m)))}),o&&s.first().addClass(f+"-corner-left").end().last().addClass(f+"-corner-right").end(),s.length>1?(i=t("<div/>"),o&&i.addClass("fc-button-group"),i.append(s),r.append(i)):r.append(s)}),r}function o(t){g.find("h2").text(t)}function l(t){g.find(".fc-"+t+"-button").addClass(f+"-state-active")}function a(t){g.find(".fc-"+t+"-button").removeClass(f+"-state-active")}function c(t){g.find(".fc-"+t+"-button").attr("disabled","disabled").addClass(f+"-state-disabled")}function d(t){g.find(".fc-"+t+"-button").removeAttr("disabled").removeClass(f+"-state-disabled")}function u(){return p}var h=this;h.render=i,h.destroy=r,h.updateTitle=o,h.activateButton=l,h.deactivateButton=a,h.disableButton=c,h.enableButton=d,h.getViewsWithButtons=u;var f,g=t(),p=[]}function c(n){function i(t,e){return!A||t.clone().stripZone()<A.clone().stripZone()||e.clone().stripZone()>Y.clone().stripZone()}function r(t,e){A=t,Y=e,q=[];var n=++j,i=Z.length;X=i;for(var r=0;i>r;r++)s(Z[r],n)}function s(e,n){o(e,function(i){var r,s,o,l=t.isArray(e.events);if(n==j){if(i)for(r=0;i.length>r;r++)s=i[r],o=l?s:S(s,e),o&&q.push.apply(q,E(o));X--,X||B(q)}})}function o(e,i){var r,s,l=_e.sourceFetchers;for(r=0;l.length>r;r++){if(s=l[r].call(N,e,A.clone(),Y.clone(),n.timezone,i),s===!0)return;if("object"==typeof s)return o(s,i),void 0}var a=e.events;if(a)t.isFunction(a)?(y(),a.call(N,A.clone(),Y.clone(),n.timezone,function(t){i(t),w()})):t.isArray(a)?i(a):i();else{var c=e.url;if(c){var d,u=e.success,h=e.error,f=e.complete;d=t.isFunction(e.data)?e.data():e.data;var g=t.extend({},d||{}),p=P(e.startParam,n.startParam),m=P(e.endParam,n.endParam),v=P(e.timezoneParam,n.timezoneParam);p&&(g[p]=A.format()),m&&(g[m]=Y.format()),n.timezone&&"local"!=n.timezone&&(g[v]=n.timezone),y(),t.ajax(t.extend({},Ne,e,{data:g,success:function(e){e=e||[];var n=R(u,this,arguments);t.isArray(n)&&(e=n),i(e)},error:function(){R(h,this,arguments),i()},complete:function(){R(f,this,arguments),w()}}))}else i()}}function l(t){var e=a(t);e&&(Z.push(e),X++,s(e,j))}function a(e){var n,i,r=_e.sourceNormalizers;if(t.isFunction(e)||t.isArray(e)?n={events:e}:"string"==typeof e?n={url:e}:"object"==typeof e&&(n=t.extend({},e)),n){for(n.className?"string"==typeof n.className&&(n.className=n.className.split(/\s+/)):n.className=[],t.isArray(n.events)&&(n.origArray=n.events,n.events=t.map(n.events,function(t){return S(t,n)})),i=0;r.length>i;i++)r[i].call(N,n);return n}}function c(e){Z=t.grep(Z,function(t){return!u(t,e)}),q=t.grep(q,function(t){return!u(t.source,e)}),B(q)}function u(t,e){return t&&e&&h(t)==h(e)}function h(t){return("object"==typeof t?t.origArray||t.url||t.events:null)||t}function f(t){t.start=N.moment(t.start),t.end&&(t.end=N.moment(t.end)),D(t),g(t),B(q)}function g(t){var e,n,i,r;for(e=0;q.length>e;e++)if(n=q[e],n._id==t._id&&n!==t)for(i=0;U.length>i;i++)r=U[i],void 0!==t[r]&&(n[r]=t[r])}function p(t,e){var n,i,r,s=S(t);if(s){for(n=E(s),i=0;n.length>i;i++)r=n[i],r.source||(e&&(W.events.push(r),r.source=W),q.push(r));return B(q),n}return[]}function m(e){var n,i;for(null==e?e=function(){return!0}:t.isFunction(e)||(n=e+"",e=function(t){return t._id==n}),q=t.grep(q,e,!0),i=0;Z.length>i;i++)t.isArray(Z[i].events)&&(Z[i].events=t.grep(Z[i].events,e,!0));B(q)}function v(e){return t.isFunction(e)?t.grep(q,e):null!=e?(e+="",t.grep(q,function(t){return t._id==e})):q}function y(){$++||V("loading",null,!0,O())}function w(){--$||V("loading",null,!1,O())}function S(i,r){var s,o,l,a,c={};if(n.eventDataTransform&&(i=n.eventDataTransform(i)),r&&r.eventDataTransform&&(i=r.eventDataTransform(i)),t.extend(c,i),r&&(c.source=r),c._id=i._id||(void 0===i.id?"_fc"+Ae++:i.id+""),c.className=i.className?"string"==typeof i.className?i.className.split(/\s+/):i.className:[],s=i.start||i.date,o=i.end,k(s)&&(s=e.duration(s)),k(o)&&(o=e.duration(o)),i.dow||e.isDuration(s)||e.isDuration(o))c.start=s?e.duration(s):null,c.end=o?e.duration(o):null,c._recurring=!0;else{if(s&&(s=N.moment(s),!s.isValid()))return!1;if(o&&(o=N.moment(o),!o.isValid()))return!1;l=i.allDay,void 0===l&&(a=P(r?r.allDayDefault:void 0,n.allDayDefault),l=void 0!==a?a:!(s.hasTime()||o&&o.hasTime())),b(s,o,l,c)}return c}function b(t,e,i,r){i?(t.hasTime()&&t.stripTime(),e&&e.hasTime()&&e.stripTime()):(t.hasTime()||(t=N.rezoneDate(t)),e&&!e.hasTime()&&(e=N.rezoneDate(e))),r.allDay=i,r.start=t,r.end=e||null,n.forceEventDuration&&!r.end&&(r.end=I(r)),d(r)}function E(e){var n,i,r,s,o,l,a,c,d,u,h=[],f=A,g=Y;if(f&&g||(n=N.getView(),f=n.start,g=n.end),e)if(e._recurring){if(r=e.dow)for(i={},s=0;r.length>s;s++)i[r[s]]=!0;for(o=f.clone().stripTime();o.isBefore(g);)(!i||i[o.day()])&&(l=e.start,a=e.end,c=o.clone(),d=null,l&&(c=c.time(l)),a&&(d=o.clone().time(a)),u=t.extend({},e),b(c,d,!l&&!a,u),h.push(u)),o.add(1,"days")}else h.push(e);return h}function D(t,e,n){var i,r,s,o,l=t._allDay,a=t._start,c=t._end,d=!1;return e||n||(e=t.start,n=t.end),i=t.allDay!=l?t.allDay:!(e||n).hasTime(),i&&(e&&(e=e.clone().stripTime()),n&&(n=n.clone().stripTime())),e&&(r=i?C(e,a.clone().stripTime()):C(e,a)),i!=l?d=!0:n&&(s=C(n||N.getDefaultEventEnd(i,e||a),e||a).subtract(C(c||N.getDefaultEventEnd(l,a),a))),o=T(v(t._id),d,i,r,s),{dateDelta:r,durationDelta:s,undo:o}}function T(e,i,r,s,o){var l=N.getIsAmbigTimezone(),a=[];return t.each(e,function(t,e){var c=e._allDay,u=e._start,h=e._end,f=null!=r?r:c,g=u.clone(),p=!i&&h?h.clone():null;f?(g.stripTime(),p&&p.stripTime()):(g.hasTime()||(g=N.rezoneDate(g)),p&&!p.hasTime()&&(p=N.rezoneDate(p))),p||!n.forceEventDuration&&!+o||(p=N.getDefaultEventEnd(f,g)),g.add(s),p&&p.add(s).add(o),l&&(+s||+o)&&(g.stripZone(),p&&p.stripZone()),e.allDay=f,e.start=g,e.end=p,d(e),a.push(function(){e.allDay=c,e.start=u,e.end=h,d(e)})}),function(){for(var t=0;a.length>t;t++)a[t]()}}function H(){var e,i=n.businessHours,r={className:"fc-nonbusiness",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"};return i&&(e="object"==typeof i?t.extend({},r,i):r),e?E(S(e)):[]}function x(t,e,i){var r=t.source||{},s=P(t.constraint,r.constraint,n.eventConstraint),o=P(t.overlap,r.overlap,n.eventOverlap);return L(e,i,s,o,t)}function M(t,e){return L(t,e,n.selectConstraint,n.selectOverlap)}function F(t,e,n){var i;return n&&(i=E(S(n))[0])?x(i,t,e):M(t,e)}function L(t,e,n,i,r){var s,o,l,a,c;if(t=t.clone().stripZone(),e=e.clone().stripZone(),null!=n){for(s=z(n),o=!1,l=0;s.length>l;l++)if(_(s[l],t,e)){o=!0;break}if(!o)return!1}for(l=0;q.length>l;l++)if(a=q[l],(!r||r._id!==a._id)&&G(a,t,e)){if(i===!1)return!1;if("function"==typeof i&&!i(a,r))return!1;if(r){if(c=P(a.overlap,(a.source||{}).overlap),c===!1)return!1;if("function"==typeof c&&!c(r,a))return!1}}return!0}function z(t){return"businessHours"===t?H():"object"==typeof t?E(S(t)):v(t)}function _(t,e,n){var i=t.start.clone().stripZone(),r=N.getEventEnd(t).stripZone();return e>=i&&r>=n}function G(t,e,n){var i=t.start.clone().stripZone(),r=N.getEventEnd(t).stripZone();return r>e&&n>i}var N=this;N.isFetchNeeded=i,N.fetchEvents=r,N.addEventSource=l,N.removeEventSource=c,N.updateEvent=f,N.renderEvent=p,N.removeEvents=m,N.clientEvents=v,N.mutateEvent=D;var A,Y,V=N.trigger,O=N.getView,B=N.reportEvents,I=N.getEventEnd,W={events:[]},Z=[W],j=0,X=0,$=0,q=[];t.each((n.events?[n.events]:[]).concat(n.eventSources||[]),function(t,e){var n=a(e);n&&Z.push(n)});var U=["title","url","allDay","className","editable","color","backgroundColor","borderColor","textColor"];N.getBusinessHoursEvents=H,N.isEventAllowedInRange=x,N.isSelectionAllowedInRange=M,N.isExternalDragAllowedInRange=F}function d(t){t._allDay=t.allDay,t._start=t.start.clone(),t._end=t.end?t.end.clone():null}function u(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function h(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function f(){t("body").addClass("fc-not-allowed")}function g(){t("body").removeClass("fc-not-allowed")}function p(e,n,i){var r=Math.floor(n/e.length),s=Math.floor(n-r*(e.length-1)),o=[],l=[],a=[],c=0;m(e),e.each(function(n,i){var d=n===e.length-1?s:r,u=t(i).outerHeight(!0);d>u?(o.push(i),l.push(u),a.push(t(i).height())):c+=u}),i&&(n-=c,r=Math.floor(n/o.length),s=Math.floor(n-r*(o.length-1))),t(o).each(function(e,n){var i=e===o.length-1?s:r,c=l[e],d=a[e],u=i-(c-d);i>c&&t(n).height(u)})}function m(t){t.height("")}function v(e){var n=0;return e.find("> *").each(function(e,i){var r=t(i).outerWidth();r>n&&(n=r)}),n++,e.width(n),n}function y(t,e){return t.height(e).addClass("fc-scroller"),t[0].scrollHeight-1>t[0].clientHeight?!0:(w(t),!1)}function w(t){t.height("").removeClass("fc-scroller")}function S(e){var n=e.css("position"),i=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==n&&i.length?i:t(e[0].ownerDocument||document)}function b(t){var e=t.offset().left,n=e+t.width(),i=t.children(),r=i.offset().left,s=r+i.outerWidth();return{left:r-e,right:n-s}}function E(t){return 1==t.which&&!t.ctrlKey}function D(t,e,n,i){var r,s,o,l;return e>n&&i>t?(t>=n?(r=t.clone(),o=!0):(r=n.clone(),o=!1),i>=e?(s=e.clone(),l=!0):(s=i.clone(),l=!1),{start:r,end:s,isStart:o,isEnd:l}):void 0}function T(t,e){if(t=t||{},void 0!==t[e])return t[e];for(var n,i=e.split(/(?=[A-Z])/),r=i.length-1;r>=0;r--)if(n=t[i[r].toLowerCase()],void 0!==n)return n;return t["default"]}function C(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days"),ms:t.time()-n.time()})}function H(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function x(t,e){return t-e}function k(t){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function M(t){var e=function(){};return e.prototype=t,new e}function R(e,n,i){if(t.isFunction(e)&&(e=[e]),e){var r,s;for(r=0;e.length>r;r++)s=e[r].apply(n,i)||s;return s}}function P(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t]}function F(t){return(t+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#039;").replace(/"/g,"&quot;").replace(/\n/g,"<br />")}function L(t){return t.replace(/&.*?;/g,"")}function z(t){return t.charAt(0).toUpperCase()+t.slice(1)}function _(t,e){var n,i,r,s,o=function(){var l=+new Date-s;e>l&&l>0?n=setTimeout(o,e-l):(n=null,t.apply(r,i),n||(r=i=null))};return function(){r=this,i=arguments,s=+new Date,n||(n=setTimeout(o,e))}}function G(n,i,r){var s,o,l,a,c=n[0],d=1==n.length&&"string"==typeof c;return e.isMoment(c)?(a=e.apply(null,n),A(c,a)):H(c)||void 0===c?a=e.apply(null,n):(s=!1,o=!1,d?Ie.test(c)?(c+="-01",n=[c],s=!0,o=!0):(l=We.exec(c))&&(s=!l[5],o=!0):t.isArray(c)&&(o=!0),a=i?e.utc.apply(e,n):e.apply(null,n),s?(a._ambigTime=!0,a._ambigZone=!0):r&&(o?a._ambigZone=!0:d&&a.zone(c))),a._fullCalendar=!0,a}function N(t,e){var n,i=[],r=!1,s=!1;for(n=0;t.length>n;n++)i.push(_e.moment.parseZone(t[n])),r=r||i[n]._ambigTime,s=s||i[n]._ambigZone;for(n=0;i.length>n;n++)r&&!e?i[n].stripTime():s&&i[n].stripZone();return i}function A(t,e){t._ambigTime?e._ambigTime=!0:e._ambigTime&&(e._ambigTime=!1),t._ambigZone?e._ambigZone=!0:e._ambigZone&&(e._ambigZone=!1)}function Y(t,e){t.year(e[0]||0).month(e[1]||0).date(e[2]||0).hours(e[3]||0).minutes(e[4]||0).seconds(e[5]||0).milliseconds(e[6]||0)}function V(t,e){return je.format.call(t,e)}function O(t,e){return B(t,X(e))}function B(t,e){var n,i="";for(n=0;e.length>n;n++)i+=I(t,e[n]);return i}function I(t,e){var n,i;return"string"==typeof e?e:(n=e.token)?Xe[n]?Xe[n](t):V(t,n):e.maybe&&(i=B(t,e.maybe),i.match(/[1-9]/))?i:""}function W(t,e,n,i,r){var s;return t=_e.moment.parseZone(t),e=_e.moment.parseZone(e),s=(t.localeData||t.lang).call(t),n=s.longDateFormat(n)||n,i=i||" - ",Z(t,e,X(n),i,r)}function Z(t,e,n,i,r){var s,o,l,a,c="",d="",u="",h="",f="";for(o=0;n.length>o&&(s=j(t,e,n[o]),s!==!1);o++)c+=s;for(l=n.length-1;l>o&&(s=j(t,e,n[l]),s!==!1);l--)d=s+d;for(a=o;l>=a;a++)u+=I(t,n[a]),h+=I(e,n[a]);return(u||h)&&(f=r?h+i+u:u+i+h),c+f+d}function j(t,e,n){var i,r;return"string"==typeof n?n:(i=n.token)&&(r=$e[i.charAt(0)],r&&t.isSame(e,r))?V(t,i):!1}function X(t){return t in qe?qe[t]:qe[t]=$(t)}function $(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push(e[1]):e[2]?n.push({maybe:$(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push(e[5]);return n}function q(t){this.options=t||{}}function U(t){this.grid=t}function K(t){this.coordMaps=t}function Q(t,e){this.coordMap=t,this.options=e||{}}function J(t,e){return t||e?t&&e?t.grid===e.grid&&t.row===e.row&&t.col===e.col:!1:!0}function te(e,n){this.options=n=n||{},this.sourceEl=e,this.parentEl=n.parentEl?t(n.parentEl):e.parent()}function ee(t){this.view=t}function ne(t){ee.call(this,t),this.coordMap=new U(this),this.elsByFill={}}function ie(t){var e=se(t);return"background"===e||"inverse-background"===e}function re(t){return"inverse-background"===se(t)}function se(t){return P((t.source||{}).rendering,t.rendering)}function oe(t){var e,n,i={};for(e=0;t.length>e;e++)n=t[e],(i[n._id]||(i[n._id]=[])).push(n);return i}function le(t,e){return t.eventStartMS-e.eventStartMS}function ae(t,e){return t.eventStartMS-e.eventStartMS||e.eventDurationMS-t.eventDurationMS||e.event.allDay-t.event.allDay||(t.event.title||"").localeCompare(e.event.title)}function ce(t){ne.call(this,t)}function de(t,e){var n,i;for(n=0;e.length>n;n++)if(i=e[n],i.leftCol<=t.rightCol&&i.rightCol>=t.leftCol)return!0;return!1}function ue(t,e){return t.leftCol-e.leftCol}function he(t){ne.call(this,t)}function fe(t){var e,n,i;if(t.sort(ae),e=ge(t),pe(e),n=e[0]){for(i=0;n.length>i;i++)me(n[i]);for(i=0;n.length>i;i++)ve(n[i],0,0)}}function ge(t){var e,n,i,r=[];for(e=0;t.length>e;e++){for(n=t[e],i=0;r.length>i&&ye(n,r[i]).length;i++);n.level=i,(r[i]||(r[i]=[])).push(n)}return r}function pe(t){var e,n,i,r,s;for(e=0;t.length>e;e++)for(n=t[e],i=0;n.length>i;i++)for(r=n[i],r.forwardSegs=[],s=e+1;t.length>s;s++)ye(r,t[s],r.forwardSegs)}function me(t){var e,n,i=t.forwardSegs,r=0;if(void 0===t.forwardPressure){for(e=0;i.length>e;e++)n=i[e],me(n),r=Math.max(r,1+n.forwardPressure);t.forwardPressure=r}}function ve(t,e,n){var i,r=t.forwardSegs;if(void 0===t.forwardCoord)for(r.length?(r.sort(Se),ve(r[0],e+1,n),t.forwardCoord=r[0].backwardCoord):t.forwardCoord=1,t.backwardCoord=t.forwardCoord-(t.forwardCoord-n)/(e+1),i=0;r.length>i;i++)ve(r[i],0,t.forwardCoord)}function ye(t,e,n){n=n||[];for(var i=0;e.length>i;i++)we(t,e[i])&&n.push(e[i]);return n}function we(t,e){return t.bottom>e.top&&t.top<e.bottom}function Se(t,e){return e.forwardPressure-t.forwardPressure||(t.backwardCoord||0)-(e.backwardCoord||0)||ae(t,e)}function be(n){function i(e){var n=x[e];return t.isPlainObject(n)&&!o(e)?T(n,C.name):n}function r(t,e){return n.trigger.apply(n,[t,e||C].concat(Array.prototype.slice.call(arguments,2),[C]))}function s(t){var e=t.source||{};return P(t.startEditable,e.startEditable,i("eventStartEditable"),t.editable,e.editable,i("editable"))}function l(t){var e=t.source||{};return P(t.durationEditable,e.durationEditable,i("eventDurationEditable"),t.editable,e.editable,i("editable"))}function a(t,e,i,s){var o=n.mutateEvent(e,i,null);r("eventDrop",t,e,o.dateDelta,function(){o.undo(),H()},s,{}),H()}function c(t,e,i,s){var o=n.mutateEvent(e,null,i);r("eventResize",t,e,o.durationDelta,function(){o.undo(),H()},s,{}),H()}function d(t){return e.isMoment(t)&&(t=t.day()),F[t]}function u(){return M}function h(t,e,n){var i=t.clone();for(e=e||1;F[(i.day()+(n?e:0)+7)%7];)i.add(e,"days");return i}function f(){var t=g.apply(null,arguments),e=p(t),n=m(e);return n}function g(t,e){var n=C.colCnt,i=_?-1:1,r=_?n-1:0;"object"==typeof t&&(e=t.col,t=t.row);var s=t*n+(e*i+r);return s}function p(t){var e=C.start.day();return t+=L[e],7*Math.floor(t/M)+z[(t%M+M)%M]-e}function m(t){return C.start.clone().add(t,"days")}function v(t){var e=y(t),n=w(e),i=S(n);return i}function y(t){return t.clone().stripTime().diff(C.start,"days")}function w(t){var e=C.start.day();return t+=e,Math.floor(t/7)*M+L[(t%7+7)%7]-L[e]}function S(t){var e=C.colCnt,n=_?-1:1,i=_?e-1:0,r=Math.floor(t/e),s=(t%e+e)%e*n+i;return{row:r,col:s}}function b(t,e){for(var n=C.rowCnt,i=C.colCnt,r=[],s=E(t,e),o=y(s.start),l=y(s.end),a=w(o),c=w(l)-1,d=0;n>d;d++){var u=d*i,h=u+i-1,f=Math.max(a,u),g=Math.min(c,h);if(g>=f){var m=S(f),v=S(g),b=[m.col,v.col].sort(),D=p(f)==o,T=p(g)+1==l;r.push({row:d,leftCol:b[0],rightCol:b[1],isStart:D,isEnd:T})}}return r}function E(t,e){var n,i,r=t.clone().stripTime();return e&&(n=e.clone().stripTime(),i=+e.time(),i&&i>=k&&n.add(1,"days")),(!e||r>=n)&&(n=r.clone().add(1,"days")),{start:r,end:n}}function D(t){var e=E(t.start,t.end);return e.end.diff(e.start,"days")>1}var C=this;C.calendar=n,C.opt=i,C.trigger=r,C.isEventDraggable=s,C.isEventResizable=l,C.eventDrop=a,C.eventResize=c;var H=n.reportEventChange,x=n.options,k=e.duration(x.nextDayThreshold);C.init(),C.getEventTimeText=function(t,e){var r,s;return"object"==typeof t&&"object"==typeof e?(r=t,s=e,e=arguments[2]):(r=t.start,s=t.end),e=e||i("timeFormat"),s&&i("displayEventEnd")?n.formatRange(r,s,e):n.formatDate(r,e)},C.isHiddenDay=d,C.skipHiddenDays=h,C.getCellsPerWeek=u,C.dateToCell=v,C.dateToDayOffset=y,C.dayOffsetToCellOffset=w,C.cellOffsetToCell=S,C.cellToDate=f,C.cellToCellOffset=g,C.cellOffsetToDayOffset=p,C.dayOffsetToDate=m,C.rangeToSegments=b,C.isMultiDayEvent=D;var M,R=i("hiddenDays")||[],F=[],L=[],z=[],_=i("isRTL");(function(){i("weekends")===!1&&R.push(0,6);for(var e=0,n=0;7>e;e++)L[e]=n,F[e]=-1!=t.inArray(e,R),F[e]||(z[n]=e,n++);if(M=n,!M)throw"invalid hiddenDays"})()}function Ee(n){var i,r,s,o,l=_e.dataAttrPrefix;return l&&(l+="-"),i=n.data(l+"event")||null,i&&(i="object"==typeof i?t.extend({},i):{},r=i.start,null==r&&(r=i.time),s=i.duration,o=i.stick,delete i.start,delete i.time,delete i.duration,delete i.stick),null==r&&(r=n.data(l+"start")),null==r&&(r=n.data(l+"time")),null==s&&(s=n.data(l+"duration")),null==o&&(o=n.data(l+"stick")),r=null!=r?e.duration(r):null,s=null!=s?e.duration(s):null,o=Boolean(o),{eventProps:i,startTime:r,duration:s,stick:o}}function De(t){be.call(this,t),this.dayGrid=new ce(this),this.coordMap=this.dayGrid.coordMap}function Te(t){De.call(this,t)}function Ce(t){De.call(this,t)}function He(t){De.call(this,t)}function xe(t,e){return e.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")}function ke(t,e){return e.longDateFormat("LT").replace(/\s*a$/i,"")}function Me(t){be.call(this,t),this.timeGrid=new he(this),this.opt("allDaySlot")?(this.dayGrid=new ce(this),this.coordMap=new K([this.dayGrid.coordMap,this.timeGrid.coordMap])):this.coordMap=this.timeGrid.coordMap}function Re(t){Me.call(this,t)}function Pe(t){Me.call(this,t)}var Fe={lang:"en",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,titleFormat:{month:"MMMM YYYY",week:"ll",day:"LL"},columnFormat:{month:"ddd",week:i,day:"dddd"},timeFormat:{"default":n},displayEventEnd:{month:!1,basicWeek:!1,"default":!0},isRTL:!1,defaultButtonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:200},Le={en:{columnFormat:{week:"ddd M/D"},dayPopoverFormat:"dddd, MMMM D"}},ze={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}},_e=t.fullCalendar={version:"2.2.0"},Ge=_e.views={};t.fn.fullCalendar=function(e){var n=Array.prototype.slice.call(arguments,1),i=this;return this.each(function(r,s){var o,a=t(s),c=a.data("fullCalendar");"string"==typeof e?c&&t.isFunction(c[e])&&(o=c[e].apply(c,n),r||(i=o),"destroy"===e&&a.removeData("fullCalendar")):c||(c=new l(a,e),a.data("fullCalendar",c),c.render())}),i},_e.langs=Le,_e.datepickerLang=function(e,n,i){var r=Le[e];r||(r=Le[e]={}),s(r,{isRTL:i.isRTL,weekNumberTitle:i.weekHeader,titleFormat:{month:i.showMonthAfterYear?"YYYY["+i.yearSuffix+"] MMMM":"MMMM YYYY["+i.yearSuffix+"]"},defaultButtonText:{prev:L(i.prevText),next:L(i.nextText),today:L(i.currentText)}}),t.datepicker&&(t.datepicker.regional[n]=t.datepicker.regional[e]=i,t.datepicker.regional.en=t.datepicker.regional[""],t.datepicker.setDefaults(i))},_e.lang=function(t,e){var n;e&&(n=Le[t],n||(n=Le[t]={}),s(n,e||{})),Fe.lang=t},_e.sourceNormalizers=[],_e.sourceFetchers=[];var Ne={dataType:"json",cache:!1},Ae=1,Ye=["sun","mon","tue","wed","thu","fri","sat"];_e.applyAll=R;var Ve,Oe,Be,Ie=/^\s*\d{4}-\d\d$/,We=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/,Ze=e.fn,je=t.extend({},Ze);_e.moment=function(){return G(arguments)},_e.moment.utc=function(){var t=G(arguments,!0);return t.hasTime()&&t.utc(),t},_e.moment.parseZone=function(){return G(arguments,!0,!0)},Ze.clone=function(){var t=je.clone.apply(this,arguments);return A(this,t),this._fullCalendar&&(t._fullCalendar=!0),t},Ze.time=function(t){if(!this._fullCalendar)return je.time.apply(this,arguments);if(null==t)return e.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});this._ambigTime=!1,e.isDuration(t)||e.isMoment(t)||(t=e.duration(t));var n=0;return e.isDuration(t)&&(n=24*Math.floor(t.asDays())),this.hours(n+t.hours()).minutes(t.minutes()).seconds(t.seconds()).milliseconds(t.milliseconds())},Ze.stripTime=function(){var t=this.toArray();return this.utc(),Oe(this,t.slice(0,3)),this._ambigTime=!0,this._ambigZone=!0,this},Ze.hasTime=function(){return!this._ambigTime},Ze.stripZone=function(){var t=this.toArray(),e=this._ambigTime;return this.utc(),Oe(this,t),e&&(this._ambigTime=!0),this._ambigZone=!0,this},Ze.hasZone=function(){return!this._ambigZone},Ze.zone=function(t){return null!=t&&(this._ambigTime=!1,this._ambigZone=!1),je.zone.apply(this,arguments)},Ze.local=function(){var t=this.toArray(),e=this._ambigZone;return je.local.apply(this,arguments),e&&Be(this,t),this},Ze.format=function(){return this._fullCalendar&&arguments[0]?O(this,arguments[0]):this._ambigTime?V(this,"YYYY-MM-DD"):this._ambigZone?V(this,"YYYY-MM-DD[T]HH:mm:ss"):je.format.apply(this,arguments)},Ze.toISOString=function(){return this._ambigTime?V(this,"YYYY-MM-DD"):this._ambigZone?V(this,"YYYY-MM-DD[T]HH:mm:ss"):je.toISOString.apply(this,arguments)},Ze.isWithin=function(t,e){var n=N([this,t,e]);return n[0]>=n[1]&&n[0]<n[2]},Ze.isSame=function(t,e){var n;return this._fullCalendar?e?(n=N([this,t],!0),je.isSame.call(n[0],n[1],e)):(t=_e.moment.parseZone(t),je.isSame.call(this,t)&&Boolean(this._ambigTime)===Boolean(t._ambigTime)&&Boolean(this._ambigZone)===Boolean(t._ambigZone)):je.isSame.apply(this,arguments)},t.each(["isBefore","isAfter"],function(t,e){Ze[e]=function(t,n){var i;return this._fullCalendar?(i=N([this,t]),je[e].call(i[0],i[1],n)):je[e].apply(this,arguments)}}),Ve="_d"in e()&&"updateOffset"in e,Oe=Ve?function(t,n){t._d.setTime(Date.UTC.apply(Date,n)),e.updateOffset(t,!1)}:Y,Be=Ve?function(t,n){t._d.setTime(+new Date(n[0]||0,n[1]||0,n[2]||0,n[3]||0,n[4]||0,n[5]||0,n[6]||0)),e.updateOffset(t,!1)}:Y;var Xe={t:function(t){return V(t,"a").charAt(0)},T:function(t){return V(t,"A").charAt(0)}};_e.formatRange=W;var $e={Y:"year",M:"month",D:"day",d:"day",A:"second",a:"second",T:"second",t:"second",H:"second",h:"second",m:"second",s:"second"},qe={};q.prototype={isHidden:!0,options:null,el:null,documentMousedownProxy:null,margin:10,show:function(){this.isHidden&&(this.el||this.render(),this.el.show(),this.position(),this.isHidden=!1,this.trigger("show"))},hide:function(){this.isHidden||(this.el.hide(),this.isHidden=!0,this.trigger("hide"))},render:function(){var e=this,n=this.options;this.el=t('<div class="fc-popover"/>').addClass(n.className||"").css({top:0,left:0}).append(n.content).appendTo(n.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),n.autoHide&&t(document).on("mousedown",this.documentMousedownProxy=t.proxy(this,"documentMousedown"))},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},destroy:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),t(document).off("mousedown",this.documentMousedownProxy)
-},position:function(){var e,n,i,r,s,o=this.options,l=this.el.offsetParent().offset(),a=this.el.outerWidth(),c=this.el.outerHeight(),d=t(window),u=S(this.el);r=o.top||0,s=void 0!==o.left?o.left:void 0!==o.right?o.right-a:0,u.is(window)||u.is(document)?(u=d,e=0,n=0):(i=u.offset(),e=i.top,n=i.left),e+=d.scrollTop(),n+=d.scrollLeft(),o.viewportConstrain!==!1&&(r=Math.min(r,e+u.outerHeight()-c-this.margin),r=Math.max(r,e+this.margin),s=Math.min(s,n+u.outerWidth()-a-this.margin),s=Math.max(s,n+this.margin)),this.el.css({top:r-l.top,left:s-l.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}},U.prototype={grid:null,rows:null,cols:null,containerEl:null,minX:null,maxX:null,minY:null,maxY:null,build:function(){this.grid.buildCoords(this.rows=[],this.cols=[]),this.computeBounds()},getCell:function(t,e){var n,i=null,r=this.rows,s=this.cols,o=-1,l=-1;if(this.inBounds(t,e)){for(n=0;r.length>n;n++)if(e>=r[n][0]&&r[n][1]>e){o=n;break}for(n=0;s.length>n;n++)if(t>=s[n][0]&&s[n][1]>t){l=n;break}o>=0&&l>=0&&(i={row:o,col:l},i.grid=this.grid,i.date=this.grid.getCellDate(i))}return i},computeBounds:function(){var t;this.containerEl&&(t=this.containerEl.offset(),this.minX=t.left,this.maxX=t.left+this.containerEl.outerWidth(),this.minY=t.top,this.maxY=t.top+this.containerEl.outerHeight())},inBounds:function(t,e){return this.containerEl?t>=this.minX&&this.maxX>t&&e>=this.minY&&this.maxY>e:!0}},K.prototype={coordMaps:null,build:function(){var t,e=this.coordMaps;for(t=0;e.length>t;t++)e[t].build()},getCell:function(t,e){var n,i=this.coordMaps,r=null;for(n=0;i.length>n&&!r;n++)r=i[n].getCell(t,e);return r}},Q.prototype={coordMap:null,options:null,isListening:!1,isDragging:!1,origCell:null,origDate:null,cell:null,date:null,mouseX0:null,mouseY0:null,mousemoveProxy:null,mouseupProxy:null,scrollEl:null,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollHandlerProxy:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,mousedown:function(t){E(t)&&(t.preventDefault(),this.startListening(t),this.options.distance||this.startDrag(t))},startListening:function(e){var n,i;this.isListening||(e&&this.options.scroll&&(n=S(t(e.target)),n.is(window)||n.is(document)||(this.scrollEl=n,this.scrollHandlerProxy=_(t.proxy(this,"scrollHandler"),100),this.scrollEl.on("scroll",this.scrollHandlerProxy))),this.computeCoords(),e&&(i=this.getCell(e),this.origCell=i,this.origDate=i?i.date:null,this.mouseX0=e.pageX,this.mouseY0=e.pageY),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")).on("mouseup",this.mouseupProxy=t.proxy(this,"mouseup")).on("selectstart",this.preventDefault),this.isListening=!0,this.trigger("listenStart",e))},computeCoords:function(){this.coordMap.build(),this.computeScrollBounds()},mousemove:function(t){var e,n;this.isDragging||(e=this.options.distance||1,n=Math.pow(t.pageX-this.mouseX0,2)+Math.pow(t.pageY-this.mouseY0,2),n>=e*e&&this.startDrag(t)),this.isDragging&&this.drag(t)},startDrag:function(t){var e;this.isListening||this.startListening(),this.isDragging||(this.isDragging=!0,this.trigger("dragStart",t),e=this.getCell(t),e&&this.cellOver(e,!0))},drag:function(t){var e;this.isDragging&&(e=this.getCell(t),J(e,this.cell)||(this.cell&&this.cellOut(),e&&this.cellOver(e)),this.dragScroll(t))},cellOver:function(t){this.cell=t,this.date=t.date,this.trigger("cellOver",t,t.date)},cellOut:function(){this.cell&&(this.trigger("cellOut",this.cell),this.cell=null,this.date=null)},mouseup:function(t){this.stopDrag(t),this.stopListening(t)},stopDrag:function(t){this.isDragging&&(this.stopScrolling(),this.trigger("dragStop",t),this.isDragging=!1)},stopListening:function(e){this.isListening&&(this.scrollEl&&(this.scrollEl.off("scroll",this.scrollHandlerProxy),this.scrollHandlerProxy=null),t(document).off("mousemove",this.mousemoveProxy).off("mouseup",this.mouseupProxy).off("selectstart",this.preventDefault),this.mousemoveProxy=null,this.mouseupProxy=null,this.isListening=!1,this.trigger("listenStop",e),this.origCell=this.cell=null,this.origDate=this.date=null)},getCell:function(t){return this.coordMap.getCell(t.pageX,t.pageY)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))},preventDefault:function(t){t.preventDefault()},computeScrollBounds:function(){var t,e=this.scrollEl;e&&(t=e.offset(),this.scrollBounds={top:t.top,left:t.left,bottom:t.top+e.outerHeight(),right:t.left+e.outerWidth()})},dragScroll:function(t){var e,n,i,r,s=this.scrollSensitivity,o=this.scrollBounds,l=0,a=0;o&&(e=(s-(t.pageY-o.top))/s,n=(s-(o.bottom-t.pageY))/s,i=(s-(t.pageX-o.left))/s,r=(s-(o.right-t.pageX))/s,e>=0&&1>=e?l=-1*e*this.scrollSpeed:n>=0&&1>=n&&(l=n*this.scrollSpeed),i>=0&&1>=i?a=-1*i*this.scrollSpeed:r>=0&&1>=r&&(a=r*this.scrollSpeed)),this.setScrollVel(l,a)},setScrollVel:function(e,n){this.scrollTopVel=e,this.scrollLeftVel=n,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(t.proxy(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;0>this.scrollTopVel?0>=t.scrollTop()&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),0>this.scrollLeftVel?0>=t.scrollLeft()&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.stopScrolling()},stopScrolling:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.computeCoords())},scrollHandler:function(){this.scrollIntervalId||this.computeCoords()}},te.prototype={options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,mouseY0:null,mouseX0:null,topDelta:null,leftDelta:null,mousemoveProxy:null,isFollowing:!1,isHidden:!1,isAnimating:!1,start:function(e){this.isFollowing||(this.isFollowing=!0,this.mouseY0=e.pageY,this.mouseX0=e.pageX,this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")))},stop:function(e,n){function i(){this.isAnimating=!1,r.destroyEl(),this.top0=this.left0=null,n&&n()}var r=this,s=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,t(document).off("mousemove",this.mousemoveProxy),e&&s&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:s,complete:i})):i())},getEl:function(){var t=this.el;return t||(this.sourceEl.width(),t=this.el=this.sourceEl.clone().css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}).appendTo(this.parentEl)),t},destroyEl:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(this.sourceEl.width(),t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},mousemove:function(t){this.topDelta=t.pageY-this.mouseY0,this.leftDelta=t.pageX-this.mouseX0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}},ee.prototype={view:null,cellHtml:"<td/>",rowHtml:function(t,e){var n,i,r=this.view,s=this.getHtmlRenderer("cell",t),o="";for(e=e||0,n=0;r.colCnt>n;n++)i=r.cellToDate(e,n),o+=s(e,n,i);return o=this.bookendCells(o,t,e),"<tr>"+o+"</tr>"},bookendCells:function(t,e,n){var i=this.view,r=this.getHtmlRenderer("intro",e)(n||0),s=this.getHtmlRenderer("outro",e)(n||0),o=i.opt("isRTL"),l=o?s:r,a=o?r:s;return"string"==typeof t?l+t+a:t.prepend(l).append(a)},getHtmlRenderer:function(t,e){var n,i,r,s,o=this.view;return n=t+"Html",e&&(i=e+z(t)+"Html"),i&&(s=o[i])?r=o:i&&(s=this[i])?r=this:(s=o[n])?r=o:(s=this[n])&&(r=this),"function"==typeof s?function(){return s.apply(r,arguments)||""}:function(){return s||""}}},ne.prototype=M(ee.prototype),t.extend(ne.prototype,{el:null,coordMap:null,cellDuration:null,elsByFill:null,render:function(){this.bindHandlers()},destroy:function(){},buildCoords:function(){},getCellDate:function(){},getCellDayEl:function(){},rangeToSegs:function(){},bindHandlers:function(){var e=this;this.el.on("mousedown",function(n){t(n.target).is(".fc-event-container *, .fc-more")||t(n.target).closest(".fc-popover").length||e.dayMousedown(n)}),this.bindSegHandlers()},dayMousedown:function(t){var e,n,i,r=this,s=this.view,o=s.calendar,l=s.opt("selectable"),a=null,c=new Q(this.coordMap,{scroll:s.opt("dragScroll"),dragStart:function(){s.unselect()},cellOver:function(t,s){c.origDate&&(i=r.getCellDayEl(t),a=[s,c.origDate].sort(x),e=a[0],n=a[1].clone().add(r.cellDuration),l&&(o.isSelectionAllowedInRange(e,n)?r.renderSelection(e,n):(a=null,f())))},cellOut:function(){a=null,r.destroySelection(),g()},listenStop:function(t){a&&(a[0].isSame(a[1])&&s.trigger("dayClick",i[0],e,t),l&&s.reportSelection(e,n,t)),g()}});c.mousedown(t)},renderDrag:function(){},destroyDrag:function(){},renderResize:function(){},destroyResize:function(){},renderRangeHelper:function(t,e,n){var i,r=this.view;!e&&r.opt("forceEventDuration")&&(e=r.calendar.getDefaultEventEnd(!t.hasTime(),t)),i=n?M(n.event):{},i.start=t,i.end=e,i.allDay=!(t.hasTime()||e&&e.hasTime()),i.className=(i.className||[]).concat("fc-helper"),n||(i.editable=!1),this.renderHelper(i,n)},renderHelper:function(){},destroyHelper:function(){},renderSelection:function(t,e){this.renderHighlight(t,e)},destroySelection:function(){this.destroyHighlight()},renderHighlight:function(t,e){this.renderFill("highlight",this.rangeToSegs(t,e))},destroyHighlight:function(){this.destroyFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderFill:function(){},destroyFill:function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},renderFillSegEls:function(e,n){var i,r=this,s=this[e+"SegEl"],o="",l=[];if(n.length){for(i=0;n.length>i;i++)o+=this.fillSegHtml(e,n[i]);t(o).each(function(e,i){var o=n[e],a=t(i);s&&(a=s.call(r,o,a)),a&&(a=t(a),a.is(r.fillSegTag)&&(o.el=a,l.push(o)))})}return l},fillSegTag:"div",fillSegHtml:function(t,e){var n=this[t+"SegClasses"],i=this[t+"SegStyles"],r=n?n.call(this,e):[],s=i?i.call(this,e):"";return"<"+this.fillSegTag+(r.length?' class="'+r.join(" ")+'"':"")+(s?' style="'+s+'"':"")+" />"},headHtml:function(){return'<div class="fc-row '+this.view.widgetHeaderClass+'">'+"<table>"+"<thead>"+this.rowHtml("head")+"</thead>"+"</table>"+"</div>"},headCellHtml:function(t,e,n){var i=this.view,r=i.calendar,s=i.opt("columnFormat");return'<th class="fc-day-header '+i.widgetHeaderClass+" fc-"+Ye[n.day()]+'">'+F(r.formatDate(n,s))+"</th>"},bgCellHtml:function(t,e,n){var i=this.view,r=this.getDayClasses(n);return r.unshift("fc-day",i.widgetContentClass),'<td class="'+r.join(" ")+'" data-date="'+n.format()+'"></td>'},getDayClasses:function(t){var e=this.view,n=e.calendar.getNow().stripTime(),i=["fc-"+Ye[t.day()]];return"month"===e.name&&t.month()!=e.intervalStart.month()&&i.push("fc-other-month"),t.isSame(n,"day")?i.push("fc-today",e.highlightStateClass):n>t?i.push("fc-past"):i.push("fc-future"),i}}),t.extend(ne.prototype,{mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,segs:null,renderEvents:function(t){var e,n,i=this.eventsToSegs(t),r=[],s=[];for(e=0;i.length>e;e++)n=i[e],ie(n.event)?r.push(n):s.push(n);r=this.renderBgSegs(r)||r,s=this.renderFgSegs(s)||s,this.segs=r.concat(s)},destroyEvents:function(){this.triggerSegMouseout(),this.destroyFgSegs(),this.destroyBgSegs(),this.segs=null},getSegs:function(){return this.segs||[]},renderFgSegs:function(){},destroyFgSegs:function(){},renderFgSegEls:function(e,n){var i,r=this.view,s="",o=[];if(e.length){for(i=0;e.length>i;i++)s+=this.fgSegHtml(e[i],n);t(s).each(function(n,i){var s=e[n],l=r.resolveEventEl(s.event,t(i));l&&(l.data("fc-seg",s),s.el=l,o.push(s))})}return o},fgSegHtml:function(){},renderBgSegs:function(t){return this.renderFill("bgEvent",t)},destroyBgSegs:function(){this.destroyFill("bgEvent")},bgEventSegEl:function(t,e){return this.view.resolveEventEl(t.event,e)},bgEventSegClasses:function(t){var e=t.event,n=e.source||{};return["fc-bgevent"].concat(e.className,n.className||[])},bgEventSegStyles:function(t){var e=this.view,n=t.event,i=n.source||{},r=n.color,s=i.color,o=e.opt("eventColor"),l=n.backgroundColor||r||i.backgroundColor||s||e.opt("eventBackgroundColor")||o;return l?"background-color:"+l:""},businessHoursSegClasses:function(){return["fc-nonbusiness","fc-bgevent"]},bindSegHandlers:function(){var e=this,n=this.view;t.each({mouseenter:function(t,n){e.triggerSegMouseover(t,n)},mouseleave:function(t,n){e.triggerSegMouseout(t,n)},click:function(t,e){return n.trigger("eventClick",this,t.event,e)},mousedown:function(i,r){t(r.target).is(".fc-resizer")&&n.isEventResizable(i.event)?e.segResizeMousedown(i,r):n.isEventDraggable(i.event)&&e.segDragMousedown(i,r)}},function(n,i){e.el.on(n,".fc-event-container > *",function(n){var r=t(this).data("fc-seg");return!r||e.isDraggingSeg||e.isResizingSeg?void 0:i.call(this,r,n)})})},triggerSegMouseover:function(t,e){this.mousedOverSeg||(this.mousedOverSeg=t,this.view.trigger("eventMouseover",t.el[0],t.event,e))},triggerSegMouseout:function(t,e){e=e||{},this.mousedOverSeg&&(t=t||this.mousedOverSeg,this.mousedOverSeg=null,this.view.trigger("eventMouseout",t.el[0],t.event,e))},segDragMousedown:function(t,e){var n,i,r=this,s=this.view,o=s.calendar,l=t.el,a=t.event,c=new te(t.el,{parentEl:s.el,opacity:s.opt("dragOpacity"),revertDuration:s.opt("dragRevertDuration"),zIndex:2}),d=new Q(s.coordMap,{distance:5,scroll:s.opt("dragScroll"),listenStart:function(t){c.hide(),c.start(t)},dragStart:function(e){r.triggerSegMouseout(t,e),r.isDraggingSeg=!0,s.hideEvent(a),s.trigger("eventDragStart",l[0],a,e,{})},cellOver:function(e,l){var u=t.cellDate||d.origDate,h=r.computeDraggedEventDates(t,u,l);n=h.start,i=h.end,o.isEventAllowedInRange(a,n,h.visibleEnd)?s.renderDrag(n,i,t)?c.hide():c.show():(n=null,c.show(),f())},cellOut:function(){n=null,s.destroyDrag(),c.show(),g()},dragStop:function(t){var e=n&&!n.isSame(a.start);c.stop(!e,function(){r.isDraggingSeg=!1,s.destroyDrag(),s.showEvent(a),s.trigger("eventDragStop",l[0],a,t,{}),e&&s.eventDrop(l[0],a,n,t)}),g()},listenStop:function(){c.stop()}});d.mousedown(e)},computeDraggedEventDates:function(t,e,n){var i,r,s,o,l,a=this.view,c=t.event,d=c.start,u=a.calendar.getEventEnd(c);return n.hasTime()===e.hasTime()?(i=C(n,e),r=d.clone().add(i),s=null===c.end?null:u.clone().add(i),o=c.allDay):(r=n,s=null,o=!n.hasTime()),l=s||a.calendar.getDefaultEventEnd(o,r),{start:r,end:s,visibleEnd:l}},segResizeMousedown:function(t,e){function n(){r.destroyResize(),s.showEvent(a)}var i,r=this,s=this.view,o=s.calendar,l=t.el,a=t.event,c=a.start,d=s.calendar.getEventEnd(a),u=null;i=new Q(this.coordMap,{distance:5,scroll:s.opt("dragScroll"),dragStart:function(e){r.triggerSegMouseout(t,e),r.isResizingSeg=!0,s.trigger("eventResizeStart",l[0],a,e,{})},cellOver:function(e,i){i.isBefore(c)&&(i=c),u=i.clone().add(r.cellDuration),o.isEventAllowedInRange(a,c,u)?u.isSame(d)?(u=null,n()):(r.renderResize(c,u,t),s.hideEvent(a)):(u=null,n(),f())},cellOut:function(){u=null,n(),g()},dragStop:function(t){r.isResizingSeg=!1,n(),g(),s.trigger("eventResizeStop",l[0],a,t,{}),u&&s.eventResize(l[0],a,u,t)}}),i.mousedown(e)},getSegClasses:function(t,e,n){var i=t.event,r=["fc-event",t.isStart?"fc-start":"fc-not-start",t.isEnd?"fc-end":"fc-not-end"].concat(i.className,i.source?i.source.className:[]);return e&&r.push("fc-draggable"),n&&r.push("fc-resizable"),r},getEventSkinCss:function(t){var e=this.view,n=t.source||{},i=t.color,r=n.color,s=e.opt("eventColor"),o=t.backgroundColor||i||n.backgroundColor||r||e.opt("eventBackgroundColor")||s,l=t.borderColor||i||n.borderColor||r||e.opt("eventBorderColor")||s,a=t.textColor||n.textColor||e.opt("eventTextColor"),c=[];return o&&c.push("background-color:"+o),l&&c.push("border-color:"+l),a&&c.push("color:"+a),c.join(";")},eventsToSegs:function(t,e){var n,i=this.eventsToRanges(t),r=[];for(n=0;i.length>n;n++)r.push.apply(r,this.eventRangeToSegs(i[n],e));return r},eventsToRanges:function(e){var n=this,i=oe(e),r=[];return t.each(i,function(t,e){e.length&&r.push.apply(r,re(e[0])?n.eventsToInverseRanges(e):n.eventsToNormalRanges(e))}),r},eventsToNormalRanges:function(t){var e,n,i,r,s=this.view.calendar,o=[];for(e=0;t.length>e;e++)n=t[e],i=n.start.clone().stripZone(),r=s.getEventEnd(n).stripZone(),o.push({event:n,start:i,end:r,eventStartMS:+i,eventDurationMS:r-i});return o},eventsToInverseRanges:function(t){var e,n,i=this.view,r=i.start.clone().stripZone(),s=i.end.clone().stripZone(),o=this.eventsToNormalRanges(t),l=[],a=t[0],c=r;for(o.sort(le),e=0;o.length>e;e++)n=o[e],n.start>c&&l.push({event:a,start:c,end:n.start}),c=n.end;return s>c&&l.push({event:a,start:c,end:s}),l},eventRangeToSegs:function(t,e){var n,i,r;for(n=e?e(t.start,t.end):this.rangeToSegs(t.start,t.end),i=0;n.length>i;i++)r=n[i],r.event=t.event,r.eventStartMS=t.eventStartMS,r.eventDurationMS=t.eventDurationMS;return n}}),ce.prototype=M(ne.prototype),t.extend(ce.prototype,{numbersVisible:!1,cellDuration:e.duration({days:1}),bottomCoordPadding:0,rowEls:null,dayEls:null,helperEls:null,render:function(e){var n,i=this.view,r="";for(n=0;i.rowCnt>n;n++)r+=this.dayRowHtml(n,e);this.el.html(r),this.rowEls=this.el.find(".fc-row"),this.dayEls=this.el.find(".fc-day"),this.dayEls.each(function(e,n){var r=i.cellToDate(Math.floor(e/i.colCnt),e%i.colCnt);i.trigger("dayRender",null,r,t(n))}),ne.prototype.render.call(this)},destroy:function(){this.destroySegPopover()},dayRowHtml:function(t,e){var n=this.view,i=["fc-row","fc-week",n.widgetContentClass];return e&&i.push("fc-rigid"),'<div class="'+i.join(" ")+'">'+'<div class="fc-bg">'+"<table>"+this.rowHtml("day",t)+"</table>"+"</div>"+'<div class="fc-content-skeleton">'+"<table>"+(this.numbersVisible?"<thead>"+this.rowHtml("number",t)+"</thead>":"")+"</table>"+"</div>"+"</div>"},dayCellHtml:function(t,e,n){return this.bgCellHtml(t,e,n)},buildCoords:function(e,n){var i,r,s,o=this.view.colCnt;this.dayEls.slice(0,o).each(function(e,o){i=t(o),r=i.offset().left,e&&(s[1]=r),s=[r],n[e]=s}),s[1]=r+i.outerWidth(),this.rowEls.each(function(n,o){i=t(o),r=i.offset().top,n&&(s[1]=r),s=[r],e[n]=s}),s[1]=r+i.outerHeight()+this.bottomCoordPadding},getCellDate:function(t){return this.view.cellToDate(t)},getCellDayEl:function(t){return this.dayEls.eq(t.row*this.view.colCnt+t.col)},rangeToSegs:function(t,e){return this.view.rangeToSegments(t,e)},renderDrag:function(t,e,n){var i;return this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!0,t)),n&&!n.el.closest(this.el).length?(this.renderRangeHelper(t,e,n),i=this.view.opt("dragOpacity"),void 0!==i&&this.helperEls.css("opacity",i),!0):void 0},destroyDrag:function(){this.destroyHighlight(),this.destroyHelper()},renderResize:function(t,e,n){this.renderHighlight(t,e),this.renderRangeHelper(t,e,n)},destroyResize:function(){this.destroyHighlight(),this.destroyHelper()},renderHelper:function(e,n){var i,r=[],s=this.eventsToSegs([e]);s=this.renderFgSegEls(s),i=this.renderSegRows(s),this.rowEls.each(function(e,s){var o,l=t(s),a=t('<div class="fc-helper-skeleton"><table/></div>');o=n&&n.row===e?n.el.position().top:l.find(".fc-content-skeleton tbody").position().top,a.css("top",o).find("table").append(i[e].tbodyEl),l.append(a),r.push(a[0])}),this.helperEls=t(r)},destroyHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},fillSegTag:"td",renderFill:function(e,n){var i,r,s,o=[];for(n=this.renderFillSegEls(e,n),i=0;n.length>i;i++)r=n[i],s=this.renderFillRow(e,r),this.rowEls.eq(r.row).append(s),o.push(s[0]);return this.elsByFill[e]=t(o),n},renderFillRow:function(e,n){var i,r,s=this.view.colCnt,o=n.leftCol,l=n.rightCol+1;return i=t('<div class="fc-'+e.toLowerCase()+'-skeleton">'+"<table><tr/></table>"+"</div>"),r=i.find("tr"),o>0&&r.append('<td colspan="'+o+'"/>'),r.append(n.el.attr("colspan",l-o)),s>l&&r.append('<td colspan="'+(s-l)+'"/>'),this.bookendCells(r,e),i}}),t.extend(ce.prototype,{rowStructs:null,destroyEvents:function(){this.destroySegPopover(),ne.prototype.destroyEvents.apply(this,arguments)},getSegs:function(){return ne.prototype.getSegs.call(this).concat(this.popoverSegs||[])},renderBgSegs:function(e){var n=t.grep(e,function(t){return t.event.allDay});return ne.prototype.renderBgSegs.call(this,n)},renderFgSegs:function(e){var n;return e=this.renderFgSegEls(e),n=this.rowStructs=this.renderSegRows(e),this.rowEls.each(function(e,i){t(i).find(".fc-content-skeleton > table").append(n[e].tbodyEl)}),e},destroyFgSegs:function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(t){var e,n,i=[];for(e=this.groupSegRows(t),n=0;e.length>n;n++)i.push(this.renderSegRow(n,e[n]));return i},fgSegHtml:function(t,e){var n,i=this.view,r=i.opt("isRTL"),s=t.event,o=i.isEventDraggable(s),l=!e&&s.allDay&&t.isEnd&&i.isEventResizable(s),a=this.getSegClasses(t,o,l),c=this.getEventSkinCss(s),d="";return a.unshift("fc-day-grid-event"),!s.allDay&&t.isStart&&(d='<span class="fc-time">'+F(i.getEventTimeText(s))+"</span>"),n='<span class="fc-title">'+(F(s.title||"")||"&nbsp;")+"</span>",'<a class="'+a.join(" ")+'"'+(s.url?' href="'+F(s.url)+'"':"")+(c?' style="'+c+'"':"")+">"+'<div class="fc-content">'+(r?n+" "+d:d+" "+n)+"</div>"+(l?'<div class="fc-resizer"/>':"")+"</a>"},renderSegRow:function(e,n){function i(e){for(;e>o;)d=(y[r-1]||[])[o],d?d.attr("rowspan",parseInt(d.attr("rowspan")||1,10)+1):(d=t("<td/>"),l.append(d)),v[r][o]=d,y[r][o]=d,o++}var r,s,o,l,a,c,d,u=this.view,h=u.colCnt,f=this.buildSegLevels(n),g=Math.max(1,f.length),p=t("<tbody/>"),m=[],v=[],y=[];for(r=0;g>r;r++){if(s=f[r],o=0,l=t("<tr/>"),m.push([]),v.push([]),y.push([]),s)for(a=0;s.length>a;a++){for(c=s[a],i(c.leftCol),d=t('<td class="fc-event-container"/>').append(c.el),c.leftCol!=c.rightCol?d.attr("colspan",c.rightCol-c.leftCol+1):y[r][o]=d;c.rightCol>=o;)v[r][o]=d,m[r][o]=c,o++;l.append(d)}i(h),this.bookendCells(l,"eventSkeleton"),p.append(l)}return{row:e,tbodyEl:p,cellMatrix:v,segMatrix:m,segLevels:f,segs:n}},buildSegLevels:function(t){var e,n,i,r=[];for(t.sort(ae),e=0;t.length>e;e++){for(n=t[e],i=0;r.length>i&&de(n,r[i]);i++);n.level=i,(r[i]||(r[i]=[])).push(n)}for(i=0;r.length>i;i++)r[i].sort(ue);return r},groupSegRows:function(t){var e,n=this.view,i=[];for(e=0;n.rowCnt>e;e++)i.push([]);for(e=0;t.length>e;e++)i[t[e].row].push(t[e]);return i}}),t.extend(ce.prototype,{segPopover:null,popoverSegs:null,destroySegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(t){var e,n,i=this.rowStructs||[];for(e=0;i.length>e;e++)this.unlimitRow(e),n=t?"number"==typeof t?t:this.computeRowLevelLimit(e):!1,n!==!1&&this.limitRow(e,n)},computeRowLevelLimit:function(t){var e,n,i=this.rowEls.eq(t),r=i.height(),s=this.rowStructs[t].tbodyEl.children();for(e=0;s.length>e;e++)if(n=s.eq(e).removeClass("fc-limited"),n.position().top+n.outerHeight()>r)return e;return!1},limitRow:function(e,n){function i(i){for(;i>T;)r={row:e,col:T},d=S.getCellSegs(r,n),d.length&&(f=o[n-1][T],w=S.renderMoreLink(r,d),y=t("<div/>").append(w),f.append(y),D.push(y[0])),T++}var r,s,o,l,a,c,d,u,h,f,g,p,m,v,y,w,S=this,b=this.view,E=this.rowStructs[e],D=[],T=0;if(n&&E.segLevels.length>n){for(s=E.segLevels[n-1],o=E.cellMatrix,l=E.tbodyEl.children().slice(n).addClass("fc-limited").get(),a=0;s.length>a;a++){for(c=s[a],i(c.leftCol),h=[],u=0;c.rightCol>=T;)r={row:e,col:T},d=this.getCellSegs(r,n),h.push(d),u+=d.length,T++;if(u){for(f=o[n-1][c.leftCol],g=f.attr("rowspan")||1,p=[],m=0;h.length>m;m++)v=t('<td class="fc-more-cell"/>').attr("rowspan",g),d=h[m],r={row:e,col:c.leftCol+m},w=this.renderMoreLink(r,[c].concat(d)),y=t("<div/>").append(w),v.append(y),p.push(v[0]),D.push(v[0]);f.addClass("fc-limited").after(t(p)),l.push(f[0])}}i(b.colCnt),E.moreEls=t(D),E.limitedEls=t(l)}},unlimitRow:function(t){var e=this.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},renderMoreLink:function(e,n){var i=this,r=this.view;return t('<a class="fc-more"/>').text(this.getMoreLinkText(n.length)).on("click",function(s){var o=r.opt("eventLimitClick"),l=r.cellToDate(e),a=t(this),c=i.getCellDayEl(e),d=i.getCellSegs(e),u=i.resliceDaySegs(d,l),h=i.resliceDaySegs(n,l);"function"==typeof o&&(o=r.trigger("eventLimitClick",null,{date:l,dayEl:c,moreEl:a,segs:u,hiddenSegs:h},s)),"popover"===o?i.showSegPopover(l,e,a,u):"string"==typeof o&&r.calendar.zoomTo(l,o)})},showSegPopover:function(t,e,n,i){var r,s,o=this,l=this.view,a=n.parent();r=1==l.rowCnt?this.view.el:this.rowEls.eq(e.row),s={className:"fc-more-popover",content:this.renderSegPopoverContent(t,i),parentEl:this.el,top:r.offset().top,autoHide:!0,viewportConstrain:l.opt("popoverViewportConstrain"),hide:function(){o.segPopover.destroy(),o.segPopover=null,o.popoverSegs=null}},l.opt("isRTL")?s.right=a.offset().left+a.outerWidth()+1:s.left=a.offset().left-1,this.segPopover=new q(s),this.segPopover.show()},renderSegPopoverContent:function(e,n){var i,r=this.view,s=r.opt("theme"),o=e.format(r.opt("dayPopoverFormat")),l=t('<div class="fc-header '+r.widgetHeaderClass+'">'+'<span class="fc-close '+(s?"ui-icon ui-icon-closethick":"fc-icon fc-icon-x")+'"></span>'+'<span class="fc-title">'+F(o)+"</span>"+'<div class="fc-clear"/>'+"</div>"+'<div class="fc-body '+r.widgetContentClass+'">'+'<div class="fc-event-container"></div>'+"</div>"),a=l.find(".fc-event-container");for(n=this.renderFgSegEls(n,!0),this.popoverSegs=n,i=0;n.length>i;i++)n[i].cellDate=e,a.append(n[i].el);return l},resliceDaySegs:function(e,n){var i=t.map(e,function(t){return t.event}),r=n.clone().stripTime(),s=r.clone().add(1,"days");return this.eventsToSegs(i,function(t,e){var n=D(t,e,r,s);return n?[n]:[]})},getMoreLinkText:function(t){var e=this.view,n=e.opt("eventLimitText");return"function"==typeof n?n(t):"+"+t+" "+n},getCellSegs:function(t,e){for(var n,i=this.rowStructs[t.row].segMatrix,r=e||0,s=[];i.length>r;)n=i[r][t.col],n&&s.push(n),r++;return s}}),he.prototype=M(ne.prototype),t.extend(he.prototype,{slotDuration:null,snapDuration:null,minTime:null,maxTime:null,dayEls:null,slatEls:null,slatTops:null,helperEl:null,businessHourSegs:null,render:function(){this.processOptions(),this.el.html(this.renderHtml()),this.dayEls=this.el.find(".fc-day"),this.slatEls=this.el.find(".fc-slats tr"),this.computeSlatTops(),this.renderBusinessHours(),ne.prototype.render.call(this)},renderBusinessHours:function(){var t=this.view.calendar.getBusinessHoursEvents();this.businessHourSegs=this.renderFill("businessHours",this.eventsToSegs(t),"bgevent")},renderHtml:function(){return'<div class="fc-bg"><table>'+this.rowHtml("slotBg")+"</table>"+"</div>"+'<div class="fc-slats">'+"<table>"+this.slatRowHtml()+"</table>"+"</div>"},slotBgCellHtml:function(t,e,n){return this.bgCellHtml(t,e,n)},slatRowHtml:function(){for(var t,n,i,r=this.view,s=r.calendar,o=r.opt("isRTL"),l="",a=0===this.slotDuration.asMinutes()%15,c=e.duration(+this.minTime);this.maxTime>c;)t=r.start.clone().time(c),n=t.minutes(),i='<td class="fc-axis fc-time '+r.widgetContentClass+'" '+r.axisStyleAttr()+">"+(a&&n?"":"<span>"+F(s.formatDate(t,r.opt("axisFormat")))+"</span>")+"</td>",l+="<tr "+(n?'class="fc-minor"':"")+">"+(o?"":i)+'<td class="'+r.widgetContentClass+'"/>'+(o?i:"")+"</tr>",c.add(this.slotDuration);return l},processOptions:function(){var t=this.view,n=t.opt("slotDuration"),i=t.opt("snapDuration");n=e.duration(n),i=i?e.duration(i):n,this.slotDuration=n,this.snapDuration=i,this.cellDuration=i,this.minTime=e.duration(t.opt("minTime")),this.maxTime=e.duration(t.opt("maxTime"))},rangeToSegs:function(t,e){var n,i,r,s,o,l=this.view,a=[];for(t=t.clone().stripZone(),e=e.clone().stripZone(),i=0;l.colCnt>i;i++)r=l.cellToDate(0,i),s=r.clone().time(this.minTime),o=r.clone().time(this.maxTime),n=D(t,e,s,o),n&&(n.col=i,a.push(n));return a},resize:function(){this.computeSlatTops(),this.updateSegVerticals()},buildCoords:function(n,i){var r,s,o=this.view.colCnt,l=this.el.offset().top,a=e.duration(+this.minTime),c=null;for(this.dayEls.slice(0,o).each(function(e,n){r=t(n),s=r.offset().left,c&&(c[1]=s),c=[s],i[e]=c}),c[1]=s+r.outerWidth(),c=null;this.maxTime>a;)s=l+this.computeTimeTop(a),c&&(c[1]=s),c=[s],n.push(c),a.add(this.snapDuration);c[1]=l+this.computeTimeTop(a)},getCellDate:function(t){var e=this.view,n=e.calendar;return n.rezoneDate(e.cellToDate(0,t.col).time(this.minTime+this.snapDuration*t.row))},getCellDayEl:function(t){return this.dayEls.eq(t.col)},computeDateTop:function(t,n){return this.computeTimeTop(e.duration(t.clone().stripZone()-n.clone().stripTime()))},computeTimeTop:function(t){var e,n,i,r,s=(t-this.minTime)/this.slotDuration;return s=Math.max(0,s),s=Math.min(this.slatEls.length,s),e=Math.floor(s),n=s-e,i=this.slatTops[e],n?(r=this.slatTops[e+1],i+(r-i)*n):i},computeSlatTops:function(){var e,n=[];this.slatEls.each(function(i,r){e=t(r).position().top,n.push(e)}),n.push(e+this.slatEls.last().outerHeight()),this.slatTops=n},renderDrag:function(t,e,n){var i;return n?(this.renderRangeHelper(t,e,n),i=this.view.opt("dragOpacity"),void 0!==i&&this.helperEl.css("opacity",i),!0):(this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!1,t)),void 0)},destroyDrag:function(){this.destroyHelper(),this.destroyHighlight()},renderResize:function(t,e,n){this.renderRangeHelper(t,e,n)},destroyResize:function(){this.destroyHelper()},renderHelper:function(e,n){var i,r,s,o,l=this.eventsToSegs([e]);for(l=this.renderFgSegEls(l),i=this.renderSegTable(l),r=0;l.length>r;r++)s=l[r],n&&n.col===s.col&&(o=n.el,s.el.css({left:o.css("left"),right:o.css("right"),"margin-left":o.css("margin-left"),"margin-right":o.css("margin-right")}));this.helperEl=t('<div class="fc-helper-skeleton"/>').append(i).appendTo(this.el)},destroyHelper:function(){this.helperEl&&(this.helperEl.remove(),this.helperEl=null)},renderSelection:function(t,e){this.view.opt("selectHelper")?this.renderRangeHelper(t,e):this.renderHighlight(t,e)},destroySelection:function(){this.destroyHelper(),this.destroyHighlight()},renderFill:function(e,n,i){var r,s,o,l,a,c,d,u,h,f,g=this.view;if(n.length){for(n=this.renderFillSegEls(e,n),r=this.groupSegCols(n),i=i||e.toLowerCase(),s=t('<div class="fc-'+i+'-skeleton">'+"<table><tr/></table>"+"</div>"),o=s.find("tr"),l=0;r.length>l;l++)if(a=r[l],c=t("<td/>").appendTo(o),a.length)for(d=t('<div class="fc-'+i+'-container"/>').appendTo(c),u=g.cellToDate(0,l),h=0;a.length>h;h++)f=a[h],d.append(f.el.css({top:this.computeDateTop(f.start,u),bottom:-this.computeDateTop(f.end,u)}));this.bookendCells(o,e),this.el.append(s),this.elsByFill[e]=s}return n}}),t.extend(he.prototype,{eventSkeletonEl:null,renderFgSegs:function(e){return e=this.renderFgSegEls(e),this.el.append(this.eventSkeletonEl=t('<div class="fc-content-skeleton"/>').append(this.renderSegTable(e))),e},destroyFgSegs:function(){this.eventSkeletonEl&&(this.eventSkeletonEl.remove(),this.eventSkeletonEl=null)},renderSegTable:function(e){var n,i,r,s,o,l,a=t("<table><tr/></table>"),c=a.find("tr");for(n=this.groupSegCols(e),this.computeSegVerticals(e),s=0;n.length>s;s++){for(o=n[s],fe(o),l=t('<div class="fc-event-container"/>'),i=0;o.length>i;i++)r=o[i],r.el.css(this.generateSegPositionCss(r)),30>r.bottom-r.top&&r.el.addClass("fc-short"),l.append(r.el);c.append(t("<td/>").append(l))}return this.bookendCells(c,"eventSkeleton"),a
-},updateSegVerticals:function(){var t,e=(this.segs||[]).concat(this.businessHourSegs||[]);for(this.computeSegVerticals(e),t=0;e.length>t;t++)e[t].el.css(this.generateSegVerticalCss(e[t]))},computeSegVerticals:function(t){var e,n;for(e=0;t.length>e;e++)n=t[e],n.top=this.computeDateTop(n.start,n.start),n.bottom=this.computeDateTop(n.end,n.start)},fgSegHtml:function(t,e){var n,i,r,s=this.view,o=t.event,l=s.isEventDraggable(o),a=!e&&t.isEnd&&s.isEventResizable(o),c=this.getSegClasses(t,l,a),d=this.getEventSkinCss(o);return c.unshift("fc-time-grid-event"),s.isMultiDayEvent(o)?(t.isStart||t.isEnd)&&(n=s.getEventTimeText(t.start,t.end),i=s.getEventTimeText(t.start,t.end,"LT"),r=s.getEventTimeText(t.start,null)):(n=s.getEventTimeText(o),i=s.getEventTimeText(o,"LT"),r=s.getEventTimeText(o.start,null)),'<a class="'+c.join(" ")+'"'+(o.url?' href="'+F(o.url)+'"':"")+(d?' style="'+d+'"':"")+">"+'<div class="fc-content">'+(n?'<div class="fc-time" data-start="'+F(r)+'"'+' data-full="'+F(i)+'"'+">"+"<span>"+F(n)+"</span>"+"</div>":"")+(o.title?'<div class="fc-title">'+F(o.title)+"</div>":"")+"</div>"+'<div class="fc-bg"/>'+(a?'<div class="fc-resizer"/>':"")+"</a>"},generateSegPositionCss:function(t){var e,n,i=this.view,r=i.opt("isRTL"),s=i.opt("slotEventOverlap"),o=t.backwardCoord,l=t.forwardCoord,a=this.generateSegVerticalCss(t);return s&&(l=Math.min(1,o+2*(l-o))),r?(e=1-l,n=o):(e=o,n=1-l),a.zIndex=t.level+1,a.left=100*e+"%",a.right=100*n+"%",s&&t.forwardPressure&&(a[r?"marginLeft":"marginRight"]=20),a},generateSegVerticalCss:function(t){return{top:t.top,bottom:-t.bottom}},groupSegCols:function(t){var e,n=this.view,i=[];for(e=0;n.colCnt>e;e++)i.push([]);for(e=0;t.length>e;e++)i[t[e].col].push(t[e]);return i}}),be.prototype={calendar:null,coordMap:null,el:null,start:null,end:null,intervalStart:null,intervalEnd:null,rowCnt:null,colCnt:null,isSelected:!1,scrollerEl:null,scrollTop:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,documentMousedownProxy:null,documentDragStartProxy:null,init:function(){var e=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=e+"-widget-header",this.widgetContentClass=e+"-widget-content",this.highlightStateClass=e+"-state-highlight",this.documentMousedownProxy=t.proxy(this,"documentMousedown"),this.documentDragStartProxy=t.proxy(this,"documentDragStart")},render:function(){this.updateSize(),this.trigger("viewRender",this,this,this.el),t(document).on("mousedown",this.documentMousedownProxy).on("dragstart",this.documentDragStartProxy)},destroy:function(){this.unselect(),this.trigger("viewDestroy",this,this,this.el),this.destroyEvents(),this.el.empty(),t(document).off("mousedown",this.documentMousedownProxy).off("dragstart",this.documentDragStartProxy)},incrementDate:function(){},updateSize:function(t){t&&this.recordScroll(),this.updateHeight(),this.updateWidth()},updateWidth:function(){},updateHeight:function(){var t=this.calendar;this.setHeight(t.getSuggestedViewHeight(),t.isHeightAuto())},setHeight:function(){},computeScrollerHeight:function(t){var e,n=this.el.add(this.scrollerEl);return n.css({position:"relative",left:-1}),e=this.el.outerHeight()-this.scrollerEl.height(),n.css({position:"",left:""}),t-e},recordScroll:function(){this.scrollerEl&&(this.scrollTop=this.scrollerEl.scrollTop())},restoreScroll:function(){null!==this.scrollTop&&this.scrollerEl.scrollTop(this.scrollTop)},renderEvents:function(){this.segEach(function(t){this.trigger("eventAfterRender",t.event,t.event,t.el)}),this.trigger("eventAfterAllRender")},destroyEvents:function(){this.segEach(function(t){this.trigger("eventDestroy",t.event,t.event,t.el)})},resolveEventEl:function(e,n){var i=this.trigger("eventRender",e,e,n);return i===!1?n=null:i&&i!==!0&&(n=t(i)),n},showEvent:function(t){this.segEach(function(t){t.el.css("visibility","")},t)},hideEvent:function(t){this.segEach(function(t){t.el.css("visibility","hidden")},t)},segEach:function(t,e){var n,i=this.getSegs();for(n=0;i.length>n;n++)e&&i[n].event._id!==e._id||t.call(this,i[n])},getSegs:function(){},renderDrag:function(){},destroyDrag:function(){},documentDragStart:function(e){var n,i,r,s,o,l=this,a=this.calendar,c=null,d=null,u=null;this.opt("droppable")&&(n=t(e.target),i=this.opt("dropAccept"),(t.isFunction(i)?i.call(n[0],n):n.is(i))&&(r=Ee(n),s=r.eventProps,o=new Q(this.coordMap,{cellOver:function(e,n){c=n,d=r.duration?c.clone().add(r.duration):null,u=d||a.getDefaultEventEnd(!c.hasTime(),c),s&&t.extend(s,{start:c,end:d}),a.isExternalDragAllowedInRange(c,u,s)?l.renderDrag(c,u):(c=null,f())},cellOut:function(){c=null,l.destroyDrag(),g()}}),t(document).one("dragstop",function(t,e){var i;l.destroyDrag(),g(),c&&(r.startTime&&!c.hasTime()&&c.time(r.startTime),l.trigger("drop",n[0],c,t,e),s&&(i=a.renderEvent(s,r.stick),l.trigger("eventReceive",null,i[0])))}),o.startDrag(e)))},select:function(t,e,n){this.unselect(n),this.renderSelection(t,e),this.reportSelection(t,e,n)},renderSelection:function(){},reportSelection:function(t,e,n){this.isSelected=!0,this.trigger("select",null,t,e,n)},unselect:function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection(),this.trigger("unselect",null,t))},destroySelection:function(){},documentMousedown:function(e){var n;this.isSelected&&this.opt("unselectAuto")&&E(e)&&(n=this.opt("unselectCancel"),n&&t(e.target).closest(n).length||this.unselect(e))}},_e.dataAttrPrefix="",De.prototype=M(be.prototype),t.extend(De.prototype,{dayGrid:null,dayNumbersVisible:!1,weekNumbersVisible:!1,weekNumberWidth:null,headRowEl:null,render:function(t,e,n){this.rowCnt=t,this.colCnt=e,this.dayNumbersVisible=n,this.weekNumbersVisible=this.opt("weekNumbers"),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.weekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderHtml()),this.headRowEl=this.el.find("thead .fc-row"),this.scrollerEl=this.el.find(".fc-day-grid-container"),this.dayGrid.coordMap.containerEl=this.scrollerEl,this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(this.hasRigidRows()),be.prototype.render.call(this)},destroy:function(){this.dayGrid.destroy(),be.prototype.destroy.call(this)},renderHtml:function(){return'<table><thead><tr><td class="'+this.widgetHeaderClass+'">'+this.dayGrid.headHtml()+"</td>"+"</tr>"+"</thead>"+"<tbody>"+"<tr>"+'<td class="'+this.widgetContentClass+'">'+'<div class="fc-day-grid-container">'+'<div class="fc-day-grid"/>'+"</div>"+"</td>"+"</tr>"+"</tbody>"+"</table>"},headIntroHtml:function(){return this.weekNumbersVisible?'<th class="fc-week-number '+this.widgetHeaderClass+'" '+this.weekNumberStyleAttr()+">"+"<span>"+F(this.opt("weekNumberTitle"))+"</span>"+"</th>":void 0},numberIntroHtml:function(t){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+">"+"<span>"+this.calendar.calculateWeekNumber(this.cellToDate(t,0))+"</span>"+"</td>":void 0},dayIntroHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number '+this.widgetContentClass+'" '+this.weekNumberStyleAttr()+"></td>":void 0},introHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+"></td>":void 0},numberCellHtml:function(t,e,n){var i;return this.dayNumbersVisible?(i=this.dayGrid.getDayClasses(n),i.unshift("fc-day-number"),'<td class="'+i.join(" ")+'" data-date="'+n.format()+'">'+n.date()+"</td>"):"<td/>"},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateWidth:function(){this.weekNumbersVisible&&(this.weekNumberWidth=v(this.el.find(".fc-week-number")))},setHeight:function(t,e){var n,i=this.opt("eventLimit");w(this.scrollerEl),h(this.headRowEl),this.dayGrid.destroySegPopover(),i&&"number"==typeof i&&this.dayGrid.limitRows(i),n=this.computeScrollerHeight(t),this.setGridHeight(n,e),i&&"number"!=typeof i&&this.dayGrid.limitRows(i),!e&&y(this.scrollerEl,n)&&(u(this.headRowEl,b(this.scrollerEl)),n=this.computeScrollerHeight(t),this.scrollerEl.height(n),this.restoreScroll())},setGridHeight:function(t,e){e?m(this.dayGrid.rowEls):p(this.dayGrid.rowEls,t,!0)},renderEvents:function(t){this.dayGrid.renderEvents(t),this.updateHeight(),be.prototype.renderEvents.call(this,t)},getSegs:function(){return this.dayGrid.getSegs()},destroyEvents:function(){be.prototype.destroyEvents.call(this),this.recordScroll(),this.dayGrid.destroyEvents()},renderDrag:function(t,e,n){return this.dayGrid.renderDrag(t,e,n)},destroyDrag:function(){this.dayGrid.destroyDrag()},renderSelection:function(t,e){this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.dayGrid.destroySelection()}}),r({fixedWeekCount:!0}),Ge.month=Te,Te.prototype=M(De.prototype),t.extend(Te.prototype,{name:"month",incrementDate:function(t,e){return t.clone().stripTime().add(e,"months").startOf("month")},render:function(t){var e;this.intervalStart=t.clone().stripTime().startOf("month"),this.intervalEnd=this.intervalStart.clone().add(1,"months"),this.start=this.intervalStart.clone(),this.start=this.skipHiddenDays(this.start),this.start.startOf("week"),this.start=this.skipHiddenDays(this.start),this.end=this.intervalEnd.clone(),this.end=this.skipHiddenDays(this.end,-1,!0),this.end.add((7-this.end.weekday())%7,"days"),this.end=this.skipHiddenDays(this.end,-1,!0),e=Math.ceil(this.end.diff(this.start,"weeks",!0)),this.isFixedWeeks()&&(this.end.add(6-e,"weeks"),e=6),this.title=this.calendar.formatDate(this.intervalStart,this.opt("titleFormat")),De.prototype.render.call(this,e,this.getCellsPerWeek(),!0)},setGridHeight:function(t,e){e=e||"variable"===this.opt("weekMode"),e&&(t*=this.rowCnt/6),p(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){var t=this.opt("weekMode");return t?"fixed"===t:this.opt("fixedWeekCount")}}),Ge.basicWeek=Ce,Ce.prototype=M(De.prototype),t.extend(Ce.prototype,{name:"basicWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),De.prototype.render.call(this,1,this.getCellsPerWeek(),!1)}}),Ge.basicDay=He,He.prototype=M(De.prototype),t.extend(He.prototype,{name:"basicDay",incrementDate:function(t,e){var n=t.clone().stripTime().add(e,"days");return n=this.skipHiddenDays(n,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),De.prototype.render.call(this,1,1,!1)}}),r({allDaySlot:!0,allDayText:"all-day",scrollTime:"06:00:00",slotDuration:"00:30:00",axisFormat:xe,timeFormat:{agenda:ke},minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0});var Ue=5;Me.prototype=M(be.prototype),t.extend(Me.prototype,{timeGrid:null,dayGrid:null,axisWidth:null,noScrollRowEls:null,bottomRuleEl:null,bottomRuleHeight:null,render:function(e){this.rowCnt=1,this.colCnt=e,this.el.addClass("fc-agenda-view").html(this.renderHtml()),this.scrollerEl=this.el.find(".fc-time-grid-container"),this.timeGrid.coordMap.containerEl=this.scrollerEl,this.timeGrid.el=this.el.find(".fc-time-grid"),this.timeGrid.render(),this.bottomRuleEl=t('<hr class="'+this.widgetHeaderClass+'"/>').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)"),be.prototype.render.call(this),this.resetScroll()},destroy:function(){this.timeGrid.destroy(),this.dayGrid&&this.dayGrid.destroy(),be.prototype.destroy.call(this)},renderHtml:function(){return'<table><thead><tr><td class="'+this.widgetHeaderClass+'">'+this.timeGrid.headHtml()+"</td>"+"</tr>"+"</thead>"+"<tbody>"+"<tr>"+'<td class="'+this.widgetContentClass+'">'+(this.dayGrid?'<div class="fc-day-grid"/><hr class="'+this.widgetHeaderClass+'"/>':"")+'<div class="fc-time-grid-container">'+'<div class="fc-time-grid"/>'+"</div>"+"</td>"+"</tr>"+"</tbody>"+"</table>"},headIntroHtml:function(){var t,e,n,i;return this.opt("weekNumbers")?(t=this.cellToDate(0,0),e=this.calendar.calculateWeekNumber(t),n=this.opt("weekNumberTitle"),i=this.opt("isRTL")?e+n:n+e,'<th class="fc-axis fc-week-number '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+">"+"<span>"+F(i)+"</span>"+"</th>"):'<th class="fc-axis '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+"></th>"},dayIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+">"+"<span>"+(this.opt("allDayHtml")||F(this.opt("allDayText")))+"</span>"+"</td>"},slotBgIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+"></td>"},introHtml:function(){return'<td class="fc-axis" '+this.axisStyleAttr()+"></td>"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},updateSize:function(t){t&&this.timeGrid.resize(),be.prototype.updateSize.call(this,t)},updateWidth:function(){this.axisWidth=v(this.el.find(".fc-axis"))},setHeight:function(t,e){var n,i;null===this.bottomRuleHeight&&(this.bottomRuleHeight=this.bottomRuleEl.outerHeight()),this.bottomRuleEl.hide(),this.scrollerEl.css("overflow",""),w(this.scrollerEl),h(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.destroySegPopover(),n=this.opt("eventLimit"),n&&"number"!=typeof n&&(n=Ue),n&&this.dayGrid.limitRows(n)),e||(i=this.computeScrollerHeight(t),y(this.scrollerEl,i)?(u(this.noScrollRowEls,b(this.scrollerEl)),i=this.computeScrollerHeight(t),this.scrollerEl.height(i),this.restoreScroll()):(this.scrollerEl.height(i).css("overflow","hidden"),this.bottomRuleEl.show()))},resetScroll:function(){function t(){n.scrollerEl.scrollTop(r)}var n=this,i=e.duration(this.opt("scrollTime")),r=this.timeGrid.computeTimeTop(i);r=Math.ceil(r),r&&r++,t(),setTimeout(t,0)},renderEvents:function(t){var e,n,i=[],r=[],s=[];for(n=0;t.length>n;n++)t[n].allDay?i.push(t[n]):r.push(t[n]);e=this.timeGrid.renderEvents(r),this.dayGrid&&(s=this.dayGrid.renderEvents(i)),this.updateHeight(),be.prototype.renderEvents.call(this,t)},getSegs:function(){return this.timeGrid.getSegs().concat(this.dayGrid?this.dayGrid.getSegs():[])},destroyEvents:function(){be.prototype.destroyEvents.call(this),this.recordScroll(),this.timeGrid.destroyEvents(),this.dayGrid&&this.dayGrid.destroyEvents()},renderDrag:function(t,e,n){return t.hasTime()?this.timeGrid.renderDrag(t,e,n):this.dayGrid?this.dayGrid.renderDrag(t,e,n):void 0},destroyDrag:function(){this.timeGrid.destroyDrag(),this.dayGrid&&this.dayGrid.destroyDrag()},renderSelection:function(t,e){t.hasTime()||e.hasTime()?this.timeGrid.renderSelection(t,e):this.dayGrid&&this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.timeGrid.destroySelection(),this.dayGrid&&this.dayGrid.destroySelection()}}),Ge.agendaWeek=Re,Re.prototype=M(Me.prototype),t.extend(Re.prototype,{name:"agendaWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),Me.prototype.render.call(this,this.getCellsPerWeek())}}),Ge.agendaDay=Pe,Pe.prototype=M(Me.prototype),t.extend(Pe.prototype,{name:"agendaDay",incrementDate:function(t,e){var n=t.clone().stripTime().add(e,"days");return n=this.skipHiddenDays(n,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),Me.prototype.render.call(this,1)}})});
\ No newline at end of file
diff --git a/apps/static/js/plugins/fullcalendar/moment.min.js b/apps/static/js/plugins/fullcalendar/moment.min.js
deleted file mode 100644
index 8b54f130f..000000000
--- a/apps/static/js/plugins/fullcalendar/moment.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-//! moment.js
-//! version : 2.8.3
-//! 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(a,b){return zb.call(a,b)}function d(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function e(a){tb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}function f(a,b){var c=!0;return m(function(){return c&&(e(a),c=!1),b.apply(this,arguments)},b)}function g(a,b){qc[a]||(e(b),qc[a]=!0)}function h(a,b){return function(c){return p(a.call(this,c),b)}}function i(a,b){return function(c){return this.localeData().ordinal(a.call(this,c),b)}}function j(){}function k(a,b){b!==!1&&F(a),n(this,a),this._d=new Date(+a._d)}function l(a){var b=y(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._locale=tb.localeData(),this._bubble()}function m(a,b){for(var d in b)c(b,d)&&(a[d]=b[d]);return c(b,"toString")&&(a.toString=b.toString),c(b,"valueOf")&&(a.valueOf=b.valueOf),a}function n(a,b){var c,d,e;if("undefined"!=typeof b._isAMomentObject&&(a._isAMomentObject=b._isAMomentObject),"undefined"!=typeof b._i&&(a._i=b._i),"undefined"!=typeof b._f&&(a._f=b._f),"undefined"!=typeof b._l&&(a._l=b._l),"undefined"!=typeof b._strict&&(a._strict=b._strict),"undefined"!=typeof b._tzm&&(a._tzm=b._tzm),"undefined"!=typeof b._isUTC&&(a._isUTC=b._isUTC),"undefined"!=typeof b._offset&&(a._offset=b._offset),"undefined"!=typeof b._pf&&(a._pf=b._pf),"undefined"!=typeof b._locale&&(a._locale=b._locale),Ib.length>0)for(c in Ib)d=Ib[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function o(a){return 0>a?Math.ceil(a):Math.floor(a)}function p(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.length<b;)d="0"+d;return(e?c?"+":"":"-")+d}function q(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function r(a,b){var c;return b=K(b,a),a.isBefore(b)?c=q(a,b):(c=q(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function s(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(g(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=tb.duration(c,d),t(this,e,a),this}}function t(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&&nb(a,"Date",mb(a,"Date")+f*c),g&&lb(a,mb(a,"Month")+g*c),d&&tb.updateOffset(a,f||g)}function u(a){return"[object Array]"===Object.prototype.toString.call(a)}function v(a){return"[object Date]"===Object.prototype.toString.call(a)||a instanceof Date}function w(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&&A(a[d])!==A(b[d]))&&g++;return g+f}function x(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=jc[a]||kc[b]||b}return a}function y(a){var b,d,e={};for(d in a)c(a,d)&&(b=x(d),b&&(e[b]=a[d]));return e}function z(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}tb[b]=function(e,f){var g,h,i=tb._locale[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=tb().utc().set(d,a);return i.call(tb._locale,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function A(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function B(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function C(a,b,c){return hb(tb([a,11,31+b-c]),b,c).week}function D(a){return E(a)?366:365}function E(a){return a%4===0&&a%100!==0||a%400===0}function F(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[Bb]<0||a._a[Bb]>11?Bb:a._a[Cb]<1||a._a[Cb]>B(a._a[Ab],a._a[Bb])?Cb:a._a[Db]<0||a._a[Db]>23?Db:a._a[Eb]<0||a._a[Eb]>59?Eb:a._a[Fb]<0||a._a[Fb]>59?Fb:a._a[Gb]<0||a._a[Gb]>999?Gb:-1,a._pf._overflowDayOfYear&&(Ab>b||b>Cb)&&(b=Cb),a._pf.overflow=b)}function G(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 H(a){return a?a.toLowerCase().replace("_","-"):a}function I(a){for(var b,c,d,e,f=0;f<a.length;){for(e=H(a[f]).split("-"),b=e.length,c=H(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=J(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&w(e,c,!0)>=b-1)break;b--}f++}return null}function J(a){var b=null;if(!Hb[a]&&Jb)try{b=tb.locale(),require("./locale/"+a),tb.locale(b)}catch(c){}return Hb[a]}function K(a,b){return b._isUTC?tb(a).zone(b._offset||0):tb(a).local()}function L(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function M(a){var b,c,d=a.match(Nb);for(b=0,c=d.length;c>b;b++)d[b]=pc[d[b]]?pc[d[b]]:L(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 N(a,b){return a.isValid()?(b=O(b,a.localeData()),lc[b]||(lc[b]=M(b)),lc[b](a)):a.localeData().invalidDate()}function O(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Ob.lastIndex=0;d>=0&&Ob.test(a);)a=a.replace(Ob,c),Ob.lastIndex=0,d-=1;return a}function P(a,b){var c,d=b._strict;switch(a){case"Q":return Zb;case"DDDD":return _b;case"YYYY":case"GGGG":case"gggg":return d?ac:Rb;case"Y":case"G":case"g":return cc;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?bc:Sb;case"S":if(d)return Zb;case"SS":if(d)return $b;case"SSS":if(d)return _b;case"DDD":return Qb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ub;case"a":case"A":return b._locale._meridiemParse;case"X":return Xb;case"Z":case"ZZ":return Vb;case"T":return Wb;case"SSSS":return Tb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?$b:Pb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Pb;case"Do":return Yb;default:return c=new RegExp(Y(X(a.replace("\\","")),"i"))}}function Q(a){a=a||"";var b=a.match(Vb)||[],c=b[b.length-1]||[],d=(c+"").match(hc)||["-",0,0],e=+(60*d[1])+A(d[2]);return"+"===d[0]?-e:e}function R(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[Bb]=3*(A(b)-1));break;case"M":case"MM":null!=b&&(e[Bb]=A(b)-1);break;case"MMM":case"MMMM":d=c._locale.monthsParse(b),null!=d?e[Bb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[Cb]=A(b));break;case"Do":null!=b&&(e[Cb]=A(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=A(b));break;case"YY":e[Ab]=tb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[Ab]=A(b);break;case"a":case"A":c._isPm=c._locale.isPM(b);break;case"H":case"HH":case"h":case"hh":e[Db]=A(b);break;case"m":case"mm":e[Eb]=A(b);break;case"s":case"ss":e[Fb]=A(b);break;case"S":case"SS":case"SSS":case"SSSS":e[Gb]=A(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=Q(b);break;case"dd":case"ddd":case"dddd":d=c._locale.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]=A(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=tb.parseTwoDigitYear(b)}}function S(a){var c,d,e,f,g,h,i;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[Ab],hb(tb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(g=a._locale._week.dow,h=a._locale._week.doy,d=b(c.gg,a._a[Ab],hb(tb(),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=ib(d,e,f,h,g),a._a[Ab]=i.year,a._dayOfYear=i.dayOfYear}function T(a){var c,d,e,f,g=[];if(!a._d){for(e=V(a),a._w&&null==a._a[Cb]&&null==a._a[Bb]&&S(a),a._dayOfYear&&(f=b(a._a[Ab],e[Ab]),a._dayOfYear>D(f)&&(a._pf._overflowDayOfYear=!0),d=db(f,0,a._dayOfYear),a._a[Bb]=d.getUTCMonth(),a._a[Cb]=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?db:cb).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function U(a){var b;a._d||(b=y(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],T(a))}function V(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function W(a){if(a._f===tb.ISO_8601)return void $(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=""+a._i,h=g.length,i=0;for(d=O(a._f,a._locale).match(Nb)||[],b=0;b<d.length;b++)e=d[b],c=(g.match(P(e,a))||[])[0],c&&(f=g.substr(0,g.indexOf(c)),f.length>0&&a._pf.unusedInput.push(f),g=g.slice(g.indexOf(c)+c.length),i+=c.length),pc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),R(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=h-i,g.length>0&&a._pf.unusedInput.push(g),a._isPm&&a._a[Db]<12&&(a._a[Db]+=12),a._isPm===!1&&12===a._a[Db]&&(a._a[Db]=0),T(a),F(a)}function X(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function Y(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Z(a){var b,c,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=n({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._pf=d(),b._f=a._f[f],W(b),G(b)&&(g+=b._pf.charsLeftOver,g+=10*b._pf.unusedTokens.length,b._pf.score=g,(null==e||e>g)&&(e=g,c=b));m(a,c||b)}function $(a){var b,c,d=a._i,e=dc.exec(d);if(e){for(a._pf.iso=!0,b=0,c=fc.length;c>b;b++)if(fc[b][1].exec(d)){a._f=fc[b][0]+(e[6]||" ");break}for(b=0,c=gc.length;c>b;b++)if(gc[b][1].exec(d)){a._f+=gc[b][0];break}d.match(Vb)&&(a._f+="Z"),W(a)}else a._isValid=!1}function _(a){$(a),a._isValid===!1&&(delete a._isValid,tb.createFromInputFallback(a))}function ab(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function bb(b){var c,d=b._i;d===a?b._d=new Date:v(d)?b._d=new Date(+d):null!==(c=Kb.exec(d))?b._d=new Date(+c[1]):"string"==typeof d?_(b):u(d)?(b._a=ab(d.slice(0),function(a){return parseInt(a,10)}),T(b)):"object"==typeof d?U(b):"number"==typeof d?b._d=new Date(d):tb.createFromInputFallback(b)}function cb(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 db(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function eb(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 fb(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function gb(a,b,c){var d=tb.duration(a).abs(),e=yb(d.as("s")),f=yb(d.as("m")),g=yb(d.as("h")),h=yb(d.as("d")),i=yb(d.as("M")),j=yb(d.as("y")),k=e<mc.s&&["s",e]||1===f&&["m"]||f<mc.m&&["mm",f]||1===g&&["h"]||g<mc.h&&["hh",g]||1===h&&["d"]||h<mc.d&&["dd",h]||1===i&&["M"]||i<mc.M&&["MM",i]||1===j&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,fb.apply({},k)}function hb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=tb(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ib(a,b,c,d,e){var f,g,h=db(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:D(a-1)+g}}function jb(b){var c=b._i,d=b._f;return b._locale=b._locale||tb.localeData(b._l),null===c||d===a&&""===c?tb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=b._locale.preparse(c)),tb.isMoment(c)?new k(c,!0):(d?u(d)?Z(b):W(b):bb(b),new k(b)))}function kb(a,b){var c,d;if(1===b.length&&u(b[0])&&(b=b[0]),!b.length)return tb();for(c=b[0],d=1;d<b.length;++d)b[d][a](c)&&(c=b[d]);return c}function lb(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),B(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function mb(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function nb(a,b,c){return"Month"===b?lb(a,c):a._d["set"+(a._isUTC?"UTC":"")+b](c)}function ob(a,b){return function(c){return null!=c?(nb(this,a,c),tb.updateOffset(this,b),this):mb(this,a)}}function pb(a){return 400*a/146097}function qb(a){return 146097*a/400}function rb(a){tb.duration.fn[a]=function(){return this._data[a]}}function sb(a){"undefined"==typeof ender&&(ub=xb.moment,xb.moment=a?f("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.",tb):tb)}for(var tb,ub,vb,wb="2.8.3",xb="undefined"!=typeof global?global:this,yb=Math.round,zb=Object.prototype.hasOwnProperty,Ab=0,Bb=1,Cb=2,Db=3,Eb=4,Fb=5,Gb=6,Hb={},Ib=[],Jb="undefined"!=typeof module&&module.exports,Kb=/^\/?Date\((\-?\d+)/i,Lb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,Mb=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,Nb=/(\[[^\[]*\])|(\\)?(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,Ob=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,Pb=/\d\d?/,Qb=/\d{1,3}/,Rb=/\d{1,4}/,Sb=/[+\-]?\d{1,6}/,Tb=/\d+/,Ub=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Vb=/Z|[\+\-]\d\d:?\d\d/gi,Wb=/T/i,Xb=/[\+\-]?\d+(\.\d{1,3})?/,Yb=/\d{1,2}/,Zb=/\d/,$b=/\d\d/,_b=/\d{3}/,ac=/\d{4}/,bc=/[+-]?\d{6}/,cc=/[+-]?\d+/,dc=/^\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)?)?$/,ec="YYYY-MM-DDTHH:mm:ssZ",fc=[["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}/]],gc=[["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/]],hc=/([\+\-]|\d\d)/gi,ic=("Date|Hours|Minutes|Seconds|Milliseconds".split("|"),{Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6}),jc={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"},kc={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},lc={},mc={s:45,m:45,h:22,d:26,M:11},nc="DDD w W M D d".split(" "),oc="M D H h m s w W".split(" "),pc={M:function(){return this.month()+1},MMM:function(a){return this.localeData().monthsShort(this,a)},MMMM:function(a){return this.localeData().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.localeData().weekdaysMin(this,a)},ddd:function(a){return this.localeData().weekdaysShort(this,a)},dddd:function(a){return this.localeData().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return p(this.year()%100,2)},YYYY:function(){return p(this.year(),4)},YYYYY:function(){return p(this.year(),5)},YYYYYY:function(){var a=this.year(),b=a>=0?"+":"-";return b+p(Math.abs(a),6)},gg:function(){return p(this.weekYear()%100,2)},gggg:function(){return p(this.weekYear(),4)},ggggg:function(){return p(this.weekYear(),5)},GG:function(){return p(this.isoWeekYear()%100,2)},GGGG:function(){return p(this.isoWeekYear(),4)},GGGGG:function(){return p(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().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 A(this.milliseconds()/100)},SS:function(){return p(A(this.milliseconds()/10),2)},SSS:function(){return p(this.milliseconds(),3)},SSSS:function(){return p(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+":"+p(A(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+p(A(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},qc={},rc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];nc.length;)vb=nc.pop(),pc[vb+"o"]=i(pc[vb],vb);for(;oc.length;)vb=oc.pop(),pc[vb+vb]=h(pc[vb],2);pc.DDDD=h(pc.DDD,3),m(j.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=tb.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=tb([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 hb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),tb=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=c,g._l=e,g._strict=f,g._isUTC=!1,g._pf=d(),jb(g)},tb.suppressDeprecationWarnings=!1,tb.createFromInputFallback=f("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)}),tb.min=function(){var a=[].slice.call(arguments,0);return kb("isBefore",a)},tb.max=function(){var a=[].slice.call(arguments,0);return kb("isAfter",a)},tb.utc=function(b,c,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=c,g._strict=f,g._pf=d(),jb(g).utc()},tb.unix=function(a){return tb(1e3*a)},tb.duration=function(a,b){var d,e,f,g,h=a,i=null;return tb.isDuration(a)?h={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(h={},b?h[b]=a:h.milliseconds=a):(i=Lb.exec(a))?(d="-"===i[1]?-1:1,h={y:0,d:A(i[Cb])*d,h:A(i[Db])*d,m:A(i[Eb])*d,s:A(i[Fb])*d,ms:A(i[Gb])*d}):(i=Mb.exec(a))?(d="-"===i[1]?-1:1,f=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*d},h={y:f(i[2]),M:f(i[3]),d:f(i[4]),h:f(i[5]),m:f(i[6]),s:f(i[7]),w:f(i[8])}):"object"==typeof h&&("from"in h||"to"in h)&&(g=r(tb(h.from),tb(h.to)),h={},h.ms=g.milliseconds,h.M=g.months),e=new l(h),tb.isDuration(a)&&c(a,"_locale")&&(e._locale=a._locale),e},tb.version=wb,tb.defaultFormat=ec,tb.ISO_8601=function(){},tb.momentProperties=Ib,tb.updateOffset=function(){},tb.relativeTimeThreshold=function(b,c){return mc[b]===a?!1:c===a?mc[b]:(mc[b]=c,!0)},tb.lang=f("moment.lang is deprecated. Use moment.locale instead.",function(a,b){return tb.locale(a,b)}),tb.locale=function(a,b){var c;return a&&(c="undefined"!=typeof b?tb.defineLocale(a,b):tb.localeData(a),c&&(tb.duration._locale=tb._locale=c)),tb._locale._abbr},tb.defineLocale=function(a,b){return null!==b?(b.abbr=a,Hb[a]||(Hb[a]=new j),Hb[a].set(b),tb.locale(a),Hb[a]):(delete Hb[a],null)},tb.langData=f("moment.langData is deprecated. Use moment.localeData instead.",function(a){return tb.localeData(a)}),tb.localeData=function(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return tb._locale;if(!u(a)){if(b=J(a))return b;a=[a]}return I(a)},tb.isMoment=function(a){return a instanceof k||null!=a&&c(a,"_isAMomentObject")},tb.isDuration=function(a){return a instanceof l};for(vb=rc.length-1;vb>=0;--vb)z(rc[vb]);tb.normalizeUnits=function(a){return x(a)},tb.invalid=function(a){var b=tb.utc(0/0);return null!=a?m(b._pf,a):b._pf.userInvalidated=!0,b},tb.parseZone=function(){return tb.apply(null,arguments).parseZone()},tb.parseTwoDigitYear=function(a){return A(a)+(A(a)>68?1900:2e3)},m(tb.fn=k.prototype,{clone:function(){return tb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("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=tb(this).utc();return 0<a.year()&&a.year()<=9999?N(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):N(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 G(this)},isDSTShifted:function(){return this._a?this.isValid()&&w(this._a,(this._isUTC?tb.utc(this._a):tb(this._a)).toArray())>0:!1},parsingFlags:function(){return m({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(a){return this.zone(0,a)},local:function(a){return this._isUTC&&(this.zone(0,a),this._isUTC=!1,a&&this.add(this._dateTzOffset(),"m")),this},format:function(a){var b=N(this,a||tb.defaultFormat);return this.localeData().postformat(b)},add:s(1,"add"),subtract:s(-1,"subtract"),diff:function(a,b,c){var d,e,f,g=K(a,this),h=6e4*(this.zone()-g.zone());return b=x(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+g.daysInMonth()),e=12*(this.year()-g.year())+(this.month()-g.month()),f=this-tb(this).startOf("month")-(g-tb(g).startOf("month")),f-=6e4*(this.zone()-tb(this).startOf("month").zone()-(g.zone()-tb(g).startOf("month").zone())),e+=f/d,"year"===b&&(e/=12)):(d=this-g,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-h)/864e5:"week"===b?(d-h)/6048e5:d),c?e:o(e)},from:function(a,b){return tb.duration({to:this,from:a}).locale(this.locale()).humanize(!b)},fromNow:function(a){return this.from(tb(),a)},calendar:function(a){var b=a||tb(),c=K(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.localeData().calendar(e,this))},isLeapYear:function(){return E(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=eb(a,this.localeData()),this.add(a-b,"d")):b},month:ob("Month",!0),startOf:function(a){switch(a=x(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=x(a),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms")},isAfter:function(a,b){return b=x("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=tb.isMoment(a)?a:tb(a),+this>+a):+this.clone().startOf(b)>+tb(a).startOf(b)},isBefore:function(a,b){return b=x("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=tb.isMoment(a)?a:tb(a),+a>+this):+this.clone().startOf(b)<+tb(a).startOf(b)},isSame:function(a,b){return b=x(b||"millisecond"),"millisecond"===b?(a=tb.isMoment(a)?a:tb(a),+this===+a):+this.clone().startOf(b)===+K(a,this).startOf(b)},min:f("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=tb.apply(null,arguments),this>a?this:a}),max:f("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=tb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c,d=this._offset||0;return null==a?this._isUTC?d:this._dateTzOffset():("string"==typeof a&&(a=Q(a)),Math.abs(a)<16&&(a=60*a),!this._isUTC&&b&&(c=this._dateTzOffset()),this._offset=a,this._isUTC=!0,null!=c&&this.subtract(c,"m"),d!==a&&(!b||this._changeInProgress?t(this,tb.duration(d-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,tb.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?tb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return B(this.year(),this.month())},dayOfYear:function(a){var b=yb((tb(this).startOf("day")-tb(this).startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")},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=hb(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==a?b:this.add(a-b,"y")},isoWeekYear:function(a){var b=hb(this,1,4).year;return null==a?b:this.add(a-b,"y")},week:function(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")},isoWeek:function(a){var b=hb(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")},weekday:function(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return C(this.year(),1,4)},weeksInYear:function(){var a=this.localeData()._week;return C(this.year(),a.dow,a.doy)},get:function(a){return a=x(a),this[a]()},set:function(a,b){return a=x(a),"function"==typeof this[a]&&this[a](b),this},locale:function(b){var c;return b===a?this._locale._abbr:(c=tb.localeData(b),null!=c&&(this._locale=c),this)},lang:f("moment().lang() is deprecated. Use moment().localeData() instead.",function(b){return b===a?this.localeData():this.locale(b)}),localeData:function(){return this._locale},_dateTzOffset:function(){return 15*Math.round(this._d.getTimezoneOffset()/15)}}),tb.fn.millisecond=tb.fn.milliseconds=ob("Milliseconds",!1),tb.fn.second=tb.fn.seconds=ob("Seconds",!1),tb.fn.minute=tb.fn.minutes=ob("Minutes",!1),tb.fn.hour=tb.fn.hours=ob("Hours",!0),tb.fn.date=ob("Date",!0),tb.fn.dates=f("dates accessor is deprecated. Use date instead.",ob("Date",!0)),tb.fn.year=ob("FullYear",!0),tb.fn.years=f("years accessor is deprecated. Use year instead.",ob("FullYear",!0)),tb.fn.days=tb.fn.day,tb.fn.months=tb.fn.month,tb.fn.weeks=tb.fn.week,tb.fn.isoWeeks=tb.fn.isoWeek,tb.fn.quarters=tb.fn.quarter,tb.fn.toJSON=tb.fn.toISOString,m(tb.duration.fn=l.prototype,{_bubble:function(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;g.milliseconds=d%1e3,a=o(d/1e3),g.seconds=a%60,b=o(a/60),g.minutes=b%60,c=o(b/60),g.hours=c%24,e+=o(c/24),h=o(pb(e)),e-=o(qb(h)),f+=o(e/30),e%=30,h+=o(f/12),f%=12,g.days=e,g.months=f,g.years=h},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return o(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*A(this._months/12)},humanize:function(a){var b=gb(this,!a,this.localeData());return a&&(b=this.localeData().pastFuture(+this,b)),this.localeData().postformat(b)},add:function(a,b){var c=tb.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=tb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=x(a),this[a.toLowerCase()+"s"]()},as:function(a){var b,c;if(a=x(a),"month"===a||"year"===a)return b=this._days+this._milliseconds/864e5,c=this._months+12*pb(b),"month"===a?c:c/12;switch(b=this._days+qb(this._months/12),a){case"week":return b/7+this._milliseconds/6048e5;case"day":return b+this._milliseconds/864e5;case"hour":return 24*b+this._milliseconds/36e5;case"minute":return 24*b*60+this._milliseconds/6e4;case"second":return 24*b*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*b*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+a)}},lang:tb.fn.lang,locale:tb.fn.locale,toIsoString:f("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),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"},localeData:function(){return this._locale}}),tb.duration.fn.toString=tb.duration.fn.toISOString;for(vb in ic)c(ic,vb)&&rb(vb.toLowerCase());tb.duration.fn.asMilliseconds=function(){return this.as("ms")},tb.duration.fn.asSeconds=function(){return this.as("s")},tb.duration.fn.asMinutes=function(){return this.as("m")},tb.duration.fn.asHours=function(){return this.as("h")},tb.duration.fn.asDays=function(){return this.as("d")},tb.duration.fn.asWeeks=function(){return this.as("weeks")},tb.duration.fn.asMonths=function(){return this.as("M")},tb.duration.fn.asYears=function(){return this.as("y")},tb.locale("en",{ordinal:function(a){var b=a%10,c=1===A(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";
-return a+c}}),Jb?module.exports=tb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(xb.moment=ub),tb}),sb(!0)):sb()}).call(this);
\ No newline at end of file
diff --git a/apps/static/js/plugins/highcharts/adapters/standalone-framework.js b/apps/static/js/plugins/highcharts/adapters/standalone-framework.js
deleted file mode 100644
index 0d2d45768..000000000
--- a/apps/static/js/plugins/highcharts/adapters/standalone-framework.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- Standalone Highcharts Framework
-
- License: MIT License
-*/
-var HighchartsAdapter=function(){function o(c){function b(b,a,d){b.removeEventListener(a,d,!1)}function d(b,a,d){d=b.HCProxiedMethods[d.toString()];b.detachEvent("on"+a,d)}function a(a,c){var f=a.HCEvents,i,g,k,j;if(a.removeEventListener)i=b;else if(a.attachEvent)i=d;else return;c?(g={},g[c]=!0):g=f;for(j in g)if(f[j])for(k=f[j].length;k--;)i(a,j,f[j][k])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(b,a){var d=this,c=this.HCEvents,g;if(d.addEventListener)d.addEventListener(b,
-a,!1);else if(d.attachEvent){g=function(b){b.target=b.srcElement||window;a.call(d,b)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[a.toString()]=g;d.attachEvent("on"+b,g)}c[b]===r&&(c[b]=[]);c[b].push(a)},unbind:function(c,h){var f,i;c?(f=this.HCEvents[c]||[],h?(i=HighchartsAdapter.inArray(h,f),i>-1&&(f.splice(i,1),this.HCEvents[c]=f),this.removeEventListener?b(this,c,h):this.attachEvent&&d(this,c,h)):(a(this,c),this.HCEvents[c]=[])):(a(this),this.HCEvents={})},trigger:function(b,
-a){var d=this.HCEvents[b]||[],c=d.length,g,k,j;k=function(){a.defaultPrevented=!0};for(g=0;g<c;g++){j=d[g];if(a.stopped)break;a.preventDefault=k;a.target=this;if(!a.type)a.type=b;j.call(this,a)===!1&&a.preventDefault()}}});return c}var r,l=document,p=[],m=[],q,n;Math.easeInOutSine=function(c,b,d,a){return-d/2*(Math.cos(Math.PI*c/a)-1)+b};return{init:function(c){if(!l.defaultView)this._getStyle=function(b,d){var a;return b.style[d]?b.style[d]:(d==="opacity"&&(d="filter"),a=b.currentStyle[d.replace(/\-(\w)/g,
-function(a,b){return b.toUpperCase()})],d==="filter"&&(a=a.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),a===""?1:a)},this.adapterRun=function(b,d){var a={width:"clientWidth",height:"clientHeight"}[d];if(a)return b.style.zoom=1,b[a]-2*parseInt(HighchartsAdapter._getStyle(b,"padding"),10)};if(!Array.prototype.forEach)this.each=function(b,d){for(var a=0,c=b.length;a<c;a++)if(d.call(b[a],b[a],a,b)===!1)return a};if(!Array.prototype.indexOf)this.inArray=function(b,d){var a,c=0;if(d)for(a=
-d.length;c<a;c++)if(d[c]===b)return c;return-1};if(!Array.prototype.filter)this.grep=function(b,d){for(var a=[],c=0,h=b.length;c<h;c++)d(b[c],c)&&a.push(b[c]);return a};n=function(b,c,a){this.options=c;this.elem=b;this.prop=a};n.prototype={update:function(){var b;b=this.paths;var d=this.elem,a=d.element;b&&a?d.attr("d",c.step(b[0],b[1],this.now,this.toD)):d.attr?a&&d.attr(this.prop,this.now):(b={},b[this.prop]=this.now+this.unit,Highcharts.css(d,b));this.options.step&&this.options.step.call(this.elem,
-this.now,this)},custom:function(b,c,a){var e=this,h=function(a){return e.step(a)},f;this.startTime=+new Date;this.start=b;this.end=c;this.unit=a;this.now=this.start;this.pos=this.state=0;h.elem=this.elem;h()&&m.push(h)===1&&(q=setInterval(function(){for(f=0;f<m.length;f++)m[f]()||m.splice(f--,1);m.length||clearInterval(q)},13))},step:function(b){var c=+new Date,a;a=this.options;var e=this.elem,h;if(e.stopAnimation||e.attr&&!e.element)a=!1;else if(b||c>=a.duration+this.startTime){this.now=this.end;
-this.pos=this.state=1;this.update();b=this.options.curAnim[this.prop]=!0;for(h in a.curAnim)a.curAnim[h]!==!0&&(b=!1);b&&a.complete&&a.complete.call(e);a=!1}else e=c-this.startTime,this.state=e/a.duration,this.pos=a.easing(e,0,1,a.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),a=!0;return a}};this.animate=function(b,d,a){var e,h="",f,i,g;b.stopAnimation=!1;if(typeof a!=="object"||a===null)e=arguments,a={duration:e[2],easing:e[3],complete:e[4]};if(typeof a.duration!=="number")a.duration=
-400;a.easing=Math[a.easing]||Math.easeInOutSine;a.curAnim=Highcharts.extend({},d);for(g in d)i=new n(b,a,g),f=null,g==="d"?(i.paths=c.init(b,b.d,d.d),i.toD=d.d,e=0,f=1):b.attr?e=b.attr(g):(e=parseFloat(HighchartsAdapter._getStyle(b,g))||0,g!=="opacity"&&(h="px")),f||(f=parseFloat(d[g])),i.custom(e,f,h)}},_getStyle:function(c,b){return window.getComputedStyle(c,void 0).getPropertyValue(b)},getScript:function(c,b){var d=l.getElementsByTagName("head")[0],a=l.createElement("script");a.type="text/javascript";
-a.src=c;a.onload=b;d.appendChild(a)},inArray:function(c,b){return b.indexOf?b.indexOf(c):p.indexOf.call(b,c)},adapterRun:function(c,b){return parseInt(HighchartsAdapter._getStyle(c,b),10)},grep:function(c,b){return p.filter.call(c,b)},map:function(c,b){for(var d=[],a=0,e=c.length;a<e;a++)d[a]=b.call(c[a],c[a],a,c);return d},offset:function(c){var b=document.documentElement,c=c.getBoundingClientRect();return{top:c.top+(window.pageYOffset||b.scrollTop)-(b.clientTop||0),left:c.left+(window.pageXOffset||
-b.scrollLeft)-(b.clientLeft||0)}},addEvent:function(c,b,d){o(c).bind(b,d)},removeEvent:function(c,b,d){o(c).unbind(b,d)},fireEvent:function(c,b,d,a){var e;l.createEvent&&(c.dispatchEvent||c.fireEvent)?(e=l.createEvent("Events"),e.initEvent(b,!0,!0),e.target=c,Highcharts.extend(e,d),c.dispatchEvent?c.dispatchEvent(e):c.fireEvent(b,e)):c.HCExtended===!0&&(d=d||{},c.trigger(b,d));d&&d.defaultPrevented&&(a=null);a&&a(d)},washMouseEvent:function(c){return c},stop:function(c){c.stopAnimation=!0},each:function(c,
-b){return Array.prototype.forEach.call(c,b)}}}();
diff --git a/apps/static/js/plugins/highcharts/adapters/standalone-framework.src.js b/apps/static/js/plugins/highcharts/adapters/standalone-framework.src.js
deleted file mode 100644
index e76928ef4..000000000
--- a/apps/static/js/plugins/highcharts/adapters/standalone-framework.src.js
+++ /dev/null
@@ -1,590 +0,0 @@
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- *
- * Standalone Highcharts Framework
- *
- * License: MIT License
- */
-
-
-/*global Highcharts */
-var HighchartsAdapter = (function () {
-
-var UNDEFINED,
-	doc = document,
-	emptyArray = [],
-	timers = [],
-	timerId,
-	Fx;
-
-Math.easeInOutSine = function (t, b, c, d) {
-	return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
-};
-
-
-
-/**
- * Extend given object with custom events
- */
-function augment(obj) {
-	function removeOneEvent(el, type, fn) {
-		el.removeEventListener(type, fn, false);
-	}
-
-	function IERemoveOneEvent(el, type, fn) {
-		fn = el.HCProxiedMethods[fn.toString()];
-		el.detachEvent('on' + type, fn);
-	}
-
-	function removeAllEvents(el, type) {
-		var events = el.HCEvents,
-			remove,
-			types,
-			len,
-			n;
-
-		if (el.removeEventListener) {
-			remove = removeOneEvent;
-		} else if (el.attachEvent) {
-			remove = IERemoveOneEvent;
-		} else {
-			return; // break on non-DOM events
-		}
-
-
-		if (type) {
-			types = {};
-			types[type] = true;
-		} else {
-			types = events;
-		}
-
-		for (n in types) {
-			if (events[n]) {
-				len = events[n].length;
-				while (len--) {
-					remove(el, n, events[n][len]);
-				}
-			}
-		}
-	}
-
-	if (!obj.HCExtended) {
-		Highcharts.extend(obj, {
-			HCExtended: true,
-
-			HCEvents: {},
-
-			bind: function (name, fn) {
-				var el = this,
-					events = this.HCEvents,
-					wrappedFn;
-
-				// handle DOM events in modern browsers
-				if (el.addEventListener) {
-					el.addEventListener(name, fn, false);
-
-				// handle old IE implementation
-				} else if (el.attachEvent) {
-					
-					wrappedFn = function (e) {
-						e.target = e.srcElement || window; // #2820
-						fn.call(el, e);
-					};
-
-					if (!el.HCProxiedMethods) {
-						el.HCProxiedMethods = {};
-					}
-
-					// link wrapped fn with original fn, so we can get this in removeEvent
-					el.HCProxiedMethods[fn.toString()] = wrappedFn;
-
-					el.attachEvent('on' + name, wrappedFn);
-				}
-
-
-				if (events[name] === UNDEFINED) {
-					events[name] = [];
-				}
-
-				events[name].push(fn);
-			},
-
-			unbind: function (name, fn) {
-				var events,
-					index;
-
-				if (name) {
-					events = this.HCEvents[name] || [];
-					if (fn) {
-						index = HighchartsAdapter.inArray(fn, events);
-						if (index > -1) {
-							events.splice(index, 1);
-							this.HCEvents[name] = events;
-						}
-						if (this.removeEventListener) {
-							removeOneEvent(this, name, fn);
-						} else if (this.attachEvent) {
-							IERemoveOneEvent(this, name, fn);
-						}
-					} else {
-						removeAllEvents(this, name);
-						this.HCEvents[name] = [];
-					}
-				} else {
-					removeAllEvents(this);
-					this.HCEvents = {};
-				}
-			},
-
-			trigger: function (name, args) {
-				var events = this.HCEvents[name] || [],
-					target = this,
-					len = events.length,
-					i,
-					preventDefault,
-					fn;
-
-				// Attach a simple preventDefault function to skip default handler if called
-				preventDefault = function () {
-					args.defaultPrevented = true;
-				};
-				
-				for (i = 0; i < len; i++) {
-					fn = events[i];
-
-					// args is never null here
-					if (args.stopped) {
-						return;
-					}
-
-					args.preventDefault = preventDefault;
-					args.target = target;
-
-					// If the type is not set, we're running a custom event (#2297). If it is set,
-					// we're running a browser event, and setting it will cause en error in
-					// IE8 (#2465).
-					if (!args.type) {
-						args.type = name;
-					}
-					
-
-					
-					// If the event handler return false, prevent the default handler from executing
-					if (fn.call(this, args) === false) {
-						args.preventDefault();
-					}
-				}
-			}
-		});
-	}
-
-	return obj;
-}
-
-
-return {
-	/**
-	 * Initialize the adapter. This is run once as Highcharts is first run.
-	 */
-	init: function (pathAnim) {
-
-		/**
-		 * Compatibility section to add support for legacy IE. This can be removed if old IE 
-		 * support is not needed.
-		 */
-		if (!doc.defaultView) {
-			this._getStyle = function (el, prop) {
-				var val;
-				if (el.style[prop]) {
-					return el.style[prop];
-				} else {
-					if (prop === 'opacity') {
-						prop = 'filter';
-					}
-					/*jslint unparam: true*/
-					val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b) { return b.toUpperCase(); })];
-					if (prop === 'filter') {
-						val = val.replace(
-							/alpha\(opacity=([0-9]+)\)/, 
-							function (a, b) { 
-								return b / 100; 
-							}
-						);
-					}
-					/*jslint unparam: false*/
-					return val === '' ? 1 : val;
-				} 
-			};
-			this.adapterRun = function (elem, method) {
-				var alias = { width: 'clientWidth', height: 'clientHeight' }[method];
-
-				if (alias) {
-					elem.style.zoom = 1;
-					return elem[alias] - 2 * parseInt(HighchartsAdapter._getStyle(elem, 'padding'), 10);
-				}
-			};
-		}
-
-		if (!Array.prototype.forEach) {
-			this.each = function (arr, fn) { // legacy
-				var i = 0, 
-					len = arr.length;
-				for (; i < len; i++) {
-					if (fn.call(arr[i], arr[i], i, arr) === false) {
-						return i;
-					}
-				}
-			};
-		}
-
-		if (!Array.prototype.indexOf) {
-			this.inArray = function (item, arr) {
-				var len, 
-					i = 0;
-
-				if (arr) {
-					len = arr.length;
-					
-					for (; i < len; i++) {
-						if (arr[i] === item) {
-							return i;
-						}
-					}
-				}
-
-				return -1;
-			};
-		}
-
-		if (!Array.prototype.filter) {
-			this.grep = function (elements, callback) {
-				var ret = [],
-					i = 0,
-					length = elements.length;
-
-				for (; i < length; i++) {
-					if (!!callback(elements[i], i)) {
-						ret.push(elements[i]);
-					}
-				}
-
-				return ret;
-			};
-		}
-
-		//--- End compatibility section ---
-
-
-		/**
-		 * Start of animation specific code
-		 */
-		Fx = function (elem, options, prop) {
-			this.options = options;
-			this.elem = elem;
-			this.prop = prop;
-		};
-		Fx.prototype = {
-			
-			update: function () {
-				var styles,
-					paths = this.paths,
-					elem = this.elem,
-					elemelem = elem.element; // if destroyed, it is null
-
-				// Animating a path definition on SVGElement
-				if (paths && elemelem) {
-					elem.attr('d', pathAnim.step(paths[0], paths[1], this.now, this.toD));
-				
-				// Other animations on SVGElement
-				} else if (elem.attr) {
-					if (elemelem) {
-						elem.attr(this.prop, this.now);
-					}
-
-				// HTML styles
-				} else {
-					styles = {};
-					styles[this.prop] = this.now + this.unit;
-					Highcharts.css(elem, styles);
-				}
-				
-				if (this.options.step) {
-					this.options.step.call(this.elem, this.now, this);
-				}
-
-			},
-			custom: function (from, to, unit) {
-				var self = this,
-					t = function (gotoEnd) {
-						return self.step(gotoEnd);
-					},
-					i;
-
-				this.startTime = +new Date();
-				this.start = from;
-				this.end = to;
-				this.unit = unit;
-				this.now = this.start;
-				this.pos = this.state = 0;
-
-				t.elem = this.elem;
-
-				if (t() && timers.push(t) === 1) {
-					timerId = setInterval(function () {
-						
-						for (i = 0; i < timers.length; i++) {
-							if (!timers[i]()) {
-								timers.splice(i--, 1);
-							}
-						}
-
-						if (!timers.length) {
-							clearInterval(timerId);
-						}
-					}, 13);
-				}
-			},
-			
-			step: function (gotoEnd) {
-				var t = +new Date(),
-					ret,
-					done,
-					options = this.options,
-					elem = this.elem,
-					i;
-				
-				if (elem.stopAnimation || (elem.attr && !elem.element)) { // #2616, element including flag is destroyed
-					ret = false;
-
-				} else if (gotoEnd || t >= options.duration + this.startTime) {
-					this.now = this.end;
-					this.pos = this.state = 1;
-					this.update();
-
-					this.options.curAnim[this.prop] = true;
-
-					done = true;
-					for (i in options.curAnim) {
-						if (options.curAnim[i] !== true) {
-							done = false;
-						}
-					}
-
-					if (done) {
-						if (options.complete) {
-							options.complete.call(elem);
-						}
-					}
-					ret = false;
-
-				} else {
-					var n = t - this.startTime;
-					this.state = n / options.duration;
-					this.pos = options.easing(n, 0, 1, options.duration);
-					this.now = this.start + ((this.end - this.start) * this.pos);
-					this.update();
-					ret = true;
-				}
-				return ret;
-			}
-		};
-
-		/**
-		 * The adapter animate method
-		 */
-		this.animate = function (el, prop, opt) {
-			var start,
-				unit = '',
-				end,
-				fx,
-				args,
-				name;
-
-			el.stopAnimation = false; // ready for new
-
-			if (typeof opt !== 'object' || opt === null) {
-				args = arguments;
-				opt = {
-					duration: args[2],
-					easing: args[3],
-					complete: args[4]
-				};
-			}
-			if (typeof opt.duration !== 'number') {
-				opt.duration = 400;
-			}
-			opt.easing = Math[opt.easing] || Math.easeInOutSine;
-			opt.curAnim = Highcharts.extend({}, prop);
-			
-			for (name in prop) {
-				fx = new Fx(el, opt, name);
-				end = null;
-				
-				if (name === 'd') {
-					fx.paths = pathAnim.init(
-						el,
-						el.d,
-						prop.d
-					);
-					fx.toD = prop.d;
-					start = 0;
-					end = 1;
-				} else if (el.attr) {
-					start = el.attr(name);
-				} else {
-					start = parseFloat(HighchartsAdapter._getStyle(el, name)) || 0;
-					if (name !== 'opacity') {
-						unit = 'px';
-					}
-				}
-	
-				if (!end) {
-					end = parseFloat(prop[name]);
-				}
-				fx.custom(start, end, unit);
-			}	
-		};
-	},
-
-	/**
-	 * Internal method to return CSS value for given element and property
-	 */
-	_getStyle: function (el, prop) {
-		return window.getComputedStyle(el, undefined).getPropertyValue(prop);
-	},
-
-	/**
-	 * Downloads a script and executes a callback when done.
-	 * @param {String} scriptLocation
-	 * @param {Function} callback
-	 */
-	getScript: function (scriptLocation, callback) {
-		// We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script.
-		var head = doc.getElementsByTagName('head')[0],
-			script = doc.createElement('script');
-
-		script.type = 'text/javascript';
-		script.src = scriptLocation;
-		script.onload = callback;
-
-		head.appendChild(script);
-	},
-
-	/**
-	 * Return the index of an item in an array, or -1 if not found
-	 */
-	inArray: function (item, arr) {
-		return arr.indexOf ? arr.indexOf(item) : emptyArray.indexOf.call(arr, item);
-	},
-
-
-	/**
-	 * A direct link to adapter methods
-	 */
-	adapterRun: function (elem, method) {
-		return parseInt(HighchartsAdapter._getStyle(elem, method), 10);
-	},
-
-	/**
-	 * Filter an array
-	 */
-	grep: function (elements, callback) {
-		return emptyArray.filter.call(elements, callback);
-	},
-
-	/**
-	 * Map an array
-	 */
-	map: function (arr, fn) {
-		var results = [], i = 0, len = arr.length;
-
-		for (; i < len; i++) {
-			results[i] = fn.call(arr[i], arr[i], i, arr);
-		}
-
-		return results;
-	},
-
-	/**
-	 * Get the element's offset position, corrected by overflow:auto. Loosely based on jQuery's offset method.
-	 */
-	offset: function (el) {
-		var docElem = document.documentElement,
-			box = el.getBoundingClientRect();
-
-		return {
-			top: box.top  + (window.pageYOffset || docElem.scrollTop)  - (docElem.clientTop  || 0),
-			left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)
-		};
-	},
-
-	/**
-	 * Add an event listener
-	 */
-	addEvent: function (el, type, fn) {
-		augment(el).bind(type, fn);
-	},
-
-	/**
-	 * Remove event added with addEvent
-	 */
-	removeEvent: function (el, type, fn) {
-		augment(el).unbind(type, fn);
-	},
-
-	/**
-	 * Fire an event on a custom object
-	 */
-	fireEvent: function (el, type, eventArguments, defaultFunction) {
-		var e;
-
-		if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) {
-			e = doc.createEvent('Events');
-			e.initEvent(type, true, true);
-			e.target = el;
-
-			Highcharts.extend(e, eventArguments);
-
-			if (el.dispatchEvent) {
-				el.dispatchEvent(e);
-			} else {
-				el.fireEvent(type, e);
-			}
-
-		} else if (el.HCExtended === true) {
-			eventArguments = eventArguments || {};
-			el.trigger(type, eventArguments);
-		}
-
-		if (eventArguments && eventArguments.defaultPrevented) {
-			defaultFunction = null;
-		}
-
-		if (defaultFunction) {
-			defaultFunction(eventArguments);
-		}
-	},
-
-	washMouseEvent: function (e) {
-		return e;
-	},
-
-
-	/**
-	 * Stop running animation
-	 */
-	stop: function (el) {
-		el.stopAnimation = true;
-	},
-
-	/**
-	 * Utility for iterating over an array. Parameters are reversed compared to jQuery.
-	 * @param {Array} arr
-	 * @param {Function} fn
-	 */
-	each: function (arr, fn) { // modern browsers
-		return Array.prototype.forEach.call(arr, fn);
-	}
-};
-}());
diff --git a/apps/static/js/plugins/highcharts/highcharts-3d.js b/apps/static/js/plugins/highcharts/highcharts-3d.js
deleted file mode 100644
index c797c3a7c..000000000
--- a/apps/static/js/plugins/highcharts/highcharts-3d.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2013 Torstein Hønsi
-
- License: www.highcharts.com/license
-*/
-(function(c){function x(e,a,b,d){var f,g,h;b*=n;a*=n;var i=[],j,o,t;b*=-1;j=d.x;o=d.y;t=(d.z===0?1.0E-4:d.z)*(d.vd||25);var y=k(b),v=l(b),m=k(a),q=l(a),r,u,s;c.each(e,function(a){r=a.x-j;u=a.y-o;s=a.z||0;f=v*r-y*s;g=-y*m*r-v*m*s+q*u;h=y*q*r+v*q*s+m*u;f=f*((t-h)/t)+j;g=g*((t-h)/t)+o;i.push({x:C(f),y:C(g),z:C(h)})});return i}function z(e,a,b,d,f,c,h,i){var j=[];return c>f&&c-f>m/2+1.0E-4?(j=j.concat(z(e,a,b,d,f,f+m/2,h,i)),j=j.concat(z(e,a,b,d,f+m/2,c,h,i))):c<f&&f-c>m/2+1.0E-4?(j=j.concat(z(e,a,b,
-d,f,f-m/2,h,i)),j=j.concat(z(e,a,b,d,f-m/2,c,h,i))):(j=c-f,["C",e+b*l(f)-b*D*j*k(f)+h,a+d*k(f)+d*D*j*l(f)+i,e+b*l(c)+b*D*j*k(c)+h,a+d*k(c)-d*D*j*l(c)+i,e+b*l(c)+h,a+d*k(c)+i])}function F(e){if(this.chart.is3d()){var a=this.chart.options.plotOptions.column.grouping;a!==void 0&&!a&&this.group.zIndex!==void 0&&this.group.attr({zIndex:this.group.zIndex*10});if(this.userOptions.borderColor===void 0)this.options.borderColor=this.color;c.each(this.data,function(a){var d=a.options.borderColor||a.color||a.series.userOptions.borderColor;
-a.options.borderColor=d;a.borderColor=d;a.pointAttr[""].stroke=d;a.pointAttr.hover.stroke=d;a.pointAttr.select.stroke=d})}e.apply(this,[].slice.call(arguments,1))}var m=Math.PI,n=m/180,k=Math.sin,l=Math.cos,C=Math.round,D=4*(Math.sqrt(2)-1)/3/(m/2);c.SVGRenderer.prototype.toLinePath=function(e,a){var b=[];c.each(e,function(a){b.push("L",a.x,a.y)});b[0]="M";a&&b.push("Z");return b};c.SVGRenderer.prototype.cuboid=function(e){var a=this.g(),e=this.cuboidPath(e);a.front=this.path(e[0]).attr({zIndex:e[3],
-"stroke-linejoin":"round"}).add(a);a.top=this.path(e[1]).attr({zIndex:e[4],"stroke-linejoin":"round"}).add(a);a.side=this.path(e[2]).attr({zIndex:e[5],"stroke-linejoin":"round"}).add(a);a.fillSetter=function(a){var d=c.Color(a).brighten(0.1).get(),e=c.Color(a).brighten(-0.1).get();this.front.attr({fill:a});this.top.attr({fill:d});this.side.attr({fill:e});this.color=a;return this};a.opacitySetter=function(a){this.front.attr({opacity:a});this.top.attr({opacity:a});this.side.attr({opacity:a});return this};
-a.attr=function(a){a.shapeArgs||a.x?(a=this.renderer.cuboidPath(a.shapeArgs||a),this.front.attr({d:a[0],zIndex:a[3]}),this.top.attr({d:a[1],zIndex:a[4]}),this.side.attr({d:a[2],zIndex:a[5]})):c.SVGElement.prototype.attr.call(this,a);return this};a.animate=function(a,d,e){a.x&&a.y?(a=this.renderer.cuboidPath(a),this.front.attr({zIndex:a[3]}).animate({d:a[0]},d,e),this.top.attr({zIndex:a[4]}).animate({d:a[1]},d,e),this.side.attr({zIndex:a[5]}).animate({d:a[2]},d,e)):a.opacity?(this.front.animate(a,
-d,e),this.top.animate(a,d,e),this.side.animate(a,d,e)):c.SVGElement.prototype.animate.call(this,a,d,e);return this};a.destroy=function(){this.front.destroy();this.top.destroy();this.side.destroy();return null};a.attr({zIndex:-e[3]});return a};c.SVGRenderer.prototype.cuboidPath=function(e){var a=e.x,b=e.y,d=e.z,c=e.height,g=e.width,h=e.depth,i=e.alpha,j=e.beta,a=[{x:a,y:b,z:d},{x:a+g,y:b,z:d},{x:a+g,y:b+c,z:d},{x:a,y:b+c,z:d},{x:a,y:b+c,z:d+h},{x:a+g,y:b+c,z:d+h},{x:a+g,y:b,z:d+h},{x:a,y:b,z:d+h}],
-a=x(a,i,j,e.origin),e=["M",a[0].x,a[0].y,"L",a[7].x,a[7].y,"L",a[6].x,a[6].y,"L",a[1].x,a[1].y,"Z"],b=["M",a[3].x,a[3].y,"L",a[2].x,a[2].y,"L",a[5].x,a[5].y,"L",a[4].x,a[4].y,"Z"],d=["M",a[1].x,a[1].y,"L",a[2].x,a[2].y,"L",a[5].x,a[5].y,"L",a[6].x,a[6].y,"Z"],c=["M",a[0].x,a[0].y,"L",a[7].x,a[7].y,"L",a[4].x,a[4].y,"L",a[3].x,a[3].y,"Z"];return[["M",a[0].x,a[0].y,"L",a[1].x,a[1].y,"L",a[2].x,a[2].y,"L",a[3].x,a[3].y,"Z"],a[7].y<a[1].y?e:a[4].y>a[2].y?b:[],a[6].x>a[1].x?d:a[7].x<a[0].x?c:[],(a[0].z+
-a[1].z+a[2].z+a[3].z)/4,j>0?(a[0].z+a[7].z+a[6].z+a[1].z)/4:(a[3].z+a[2].z+a[5].z+a[4].z)/4,i>0?(a[1].z+a[2].z+a[5].z+a[6].z)/4:(a[0].z+a[7].z+a[4].z+a[3].z)/4]};c.SVGRenderer.prototype.arc3d=function(e){e.alpha*=n;e.beta*=n;var a=this.g(),b=this.arc3dPath(e),d=a.renderer,f=b.zAll*100;a.shapeArgs=e;a.side1=d.path(b.side2).attr({zIndex:b.zSide2}).add(a);a.side2=d.path(b.side1).attr({zIndex:b.zSide1}).add(a);a.inn=d.path(b.inn).attr({zIndex:b.zInn}).add(a);a.out=d.path(b.out).attr({zIndex:b.zOut}).add(a);
-a.top=d.path(b.top).attr({zIndex:b.zTop}).add(a);a.fillSetter=function(a){this.color=a;var b=c.Color(a).brighten(-0.1).get();this.side1.attr({fill:b});this.side2.attr({fill:b});this.inn.attr({fill:b});this.out.attr({fill:b});this.top.attr({fill:a});return this};a.animate=function(a,b,d){c.SVGElement.prototype.animate.call(this,a,b,d);if(a.x&&a.y)b=this.renderer,a=c.splat(a)[0],a.alpha*=n,a.beta*=n,b=b.arc3dPath(a),this.shapeArgs=a,this.inn.attr({d:b.inn,zIndex:b.zInn}),this.out.attr({d:b.out,zIndex:b.zOut}),
-this.side1.attr({d:b.side1,zIndex:b.zSide2}),this.side2.attr({d:b.side2,zIndex:b.zSide1}),this.top.attr({d:b.top,zIndex:b.zTop}),this.attr({fill:this.color}),this.attr({zIndex:b.zAll*100});return this};a.zIndex=f;a.attr({zIndex:f});return a};c.SVGRenderer.prototype.arc3dPath=function(e){var a=e.x,b=e.y,d=e.start,c=e.end-1.0E-5,g=e.r,h=e.innerR,i=e.depth,j=e.alpha,o=e.beta,t=l(d),y=k(d),v=l(c),n=k(c),q=g*l(o),r=g*l(j),u=h*l(o),s=h*l(j),A=i*k(o),B=i*k(j),i=["M",a+q*t,b+r*y],i=i.concat(z(a,b,q,r,d,c,
-0,0)),i=i.concat(["L",a+u*v,b+s*n]),i=i.concat(z(a,b,u,s,c,d,0,0)),i=i.concat(["Z"]),e=(e.start+e.end)/2,e=k(o)*l(e)+k(-j)*k(-e),p=o>0?m/2:0,w=j>0?0:m/2,p=d>-p?d:c>-p?-p:d,x=c<m-w?c:d<m-w?m-w:c,w=["M",a+q*l(p),b+r*k(p)],w=w.concat(z(a,b,q,r,p,x,0,0)),w=w.concat(["L",a+q*l(x)+A,b+r*k(x)+B]),w=w.concat(z(a,b,q,r,x,p,A,B)),w=w.concat(["Z"]),p=["M",a+u*t,b+s*y],p=p.concat(z(a,b,u,s,d,c,0,0)),p=p.concat(["L",a+u*l(c)+A,b+s*k(c)+B]),p=p.concat(z(a,b,u,s,c,d,A,B)),p=p.concat(["Z"]),t=["M",a+q*t,b+r*y,"L",
-a+q*t+A,b+r*y+B,"L",a+u*t+A,b+s*y+B,"L",a+u*t,b+s*y,"Z"],a=["M",a+q*v,b+r*n,"L",a+q*v+A,b+r*n+B,"L",a+u*v+A,b+s*n+B,"L",a+u*v,b+s*n,"Z"],v=h+(g-h)/2,b=Math.abs(e*2*v);g*=e;h*=e;d=(k(o)*l(d)+k(-j)*k(-d))*v;c=(k(o)*l(c)+k(-j)*k(-c))*v;return{top:i,zTop:b*100,out:w,zOut:g*100,inn:p,zInn:h*100,side1:t,zSide1:d*100,side2:a,zSide2:c*100,zAll:e}};c.Chart.prototype.is3d=function(){return this.options.chart.options3d&&this.options.chart.options3d.enabled};c.wrap(c.Chart.prototype,"isInsidePlot",function(c){return this.is3d()?
-!0:c.apply(this,[].slice.call(arguments,1))});c.wrap(c.Chart.prototype,"init",function(e){var a=arguments;a[1]=c.merge({chart:{options3d:{enabled:!1,alpha:0,beta:0,depth:100,viewDistance:25,frame:{bottom:{size:1,color:"rgba(255,255,255,0)"},side:{size:1,color:"rgba(255,255,255,0)"},back:{size:1,color:"rgba(255,255,255,0)"}}}}},a[1]);e.apply(this,[].slice.call(a,1))});c.wrap(c.Chart.prototype,"setChartSize",function(c){c.apply(this,[].slice.call(arguments,1));if(this.is3d()){var a=this.inverted,b=
-this.clipBox,d=this.margin;b[a?"y":"x"]=-(d[3]||0);b[a?"x":"y"]=-(d[0]||0);b[a?"height":"width"]=this.chartWidth+(d[3]||0)+(d[1]||0);b[a?"width":"height"]=this.chartHeight+(d[0]||0)+(d[2]||0)}});c.wrap(c.Chart.prototype,"redraw",function(c){if(this.is3d())this.isDirtyBox=!0;c.apply(this,[].slice.call(arguments,1))});c.Chart.prototype.retrieveStacks=function(){var e={},a=this.options.plotOptions[this.options.chart.type],b=a.stacking,d=1;if(a.grouping||!b)return this.series;c.each(this.series,function(a){e[a.options.stack||
-0]?e[a.options.stack||0].series.push(a):(e[a.options.stack||0]={series:[a],position:d},d++)});e.totalStacks=d+1;return e};c.wrap(c.Axis.prototype,"init",function(e){var a=arguments;if(a[1].is3d())a[2].tickWidth=c.pick(a[2].tickWidth,0),a[2].gridLineWidth=c.pick(a[2].gridLineWidth,1);e.apply(this,[].slice.call(arguments,1))});c.wrap(c.Axis.prototype,"render",function(c){c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.renderer,d=a.options.chart.options3d,f=d.alpha,
-g=d.beta*(a.yAxis[0].opposite?-1:1),h=d.frame,i=h.bottom,j=h.back,h=h.side,o=d.depth,k=this.height,l=this.width,m=this.left,n=this.top,d={x:a.plotLeft+a.plotWidth/2,y:a.plotTop+a.plotHeight/2,z:o,vd:d.viewDistance};if(this.horiz)this.axisLine&&this.axisLine.hide(),g={x:m,y:n+(a.yAxis[0].reversed?-i.size:k),z:0,width:l,height:i.size,depth:o,alpha:f,beta:g,origin:d},this.bottomFrame?this.bottomFrame.animate(g):this.bottomFrame=b.cuboid(g).attr({fill:i.color,zIndex:a.yAxis[0].reversed&&f>0?4:-1}).css({stroke:i.color}).add();
-else{var q={x:m,y:n,z:o+1,width:l,height:k+i.size,depth:j.size,alpha:f,beta:g,origin:d};this.backFrame?this.backFrame.animate(q):this.backFrame=b.cuboid(q).attr({fill:j.color,zIndex:-3}).css({stroke:j.color}).add();this.axisLine&&this.axisLine.hide();a={x:(a.yAxis[0].opposite?l:0)+m-h.size,y:n,z:0,width:h.size,height:k+i.size,depth:o+j.size,alpha:f,beta:g,origin:d};this.sideFrame?this.sideFrame.animate(a):this.sideFrame=b.cuboid(a).attr({fill:h.color,zIndex:-2}).css({stroke:h.color}).add()}}});c.wrap(c.Axis.prototype,
-"getPlotLinePath",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.chart.is3d())return a;if(a===null)return a;var b=this.chart,d=b.options.chart.options3d,f=d.depth;d.origin={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:f,vd:d.viewDistance};var a=[{x:a[1],y:a[2],z:this.horiz||this.opposite?f:0},{x:a[1],y:a[2],z:f},{x:a[4],y:a[5],z:f},{x:a[4],y:a[5],z:this.horiz||this.opposite?0:f}],f=b.options.inverted?d.beta:d.alpha,g=b.options.inverted?d.alpha:d.beta;g*=b.yAxis[0].opposite?
--1:1;a=x(a,f,g,d.origin);return a=this.chart.renderer.toLinePath(a,!1)});c.wrap(c.Tick.prototype,"getMarkPath",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.axis.chart.is3d())return a;var b=this.axis.chart,d=b.options.chart.options3d,f={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:d.depth,vd:d.viewDistance},a=[{x:a[1],y:a[2],z:0},{x:a[4],y:a[5],z:0}],g=b.inverted?d.beta:d.alpha,d=b.inverted?d.alpha:d.beta;d*=b.yAxis[0].opposite?-1:1;a=x(a,g,d,f);return a=["M",a[0].x,
-a[0].y,"L",a[1].x,a[1].y]});c.wrap(c.Tick.prototype,"getLabelPosition",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.axis.chart.is3d())return a;var b=this.axis.chart,d=b.options.chart.options3d,f={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:d.depth,vd:d.viewDistance},g=b.inverted?d.beta:d.alpha,d=b.inverted?d.alpha:d.beta;d*=b.yAxis[0].opposite?-1:1;return a=x([{x:a.x,y:a.y,z:0}],g,d,f)[0]});c.wrap(c.Axis.prototype,"drawCrosshair",function(c){var a=arguments;this.chart.is3d()&&
-a[2]&&(a[2]={plotX:a[2].plotXold||a[2].plotX,plotY:a[2].plotYold||a[2].plotY});c.apply(this,[].slice.call(a,1))});c.wrap(c.seriesTypes.column.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.options,d=b.plotOptions[this.chart.options.chart.type],b=b.chart.options3d,f=d.depth||25,g={x:a.plotWidth/2,y:a.plotHeight/2,z:b.depth,vd:b.viewDistance},h=b.alpha,i=b.beta*(a.yAxis[0].opposite?-1:1),j=(d.stacking?this.options.stack||0:this._i)*
-(f+(d.groupZPadding||1));d.grouping!==!1&&(j=0);j+=d.groupZPadding||1;c.each(this.data,function(a){var b=a.shapeArgs,c=a.tooltipPos;a.shapeType="cuboid";b.alpha=h;b.beta=i;b.z=j;b.origin=g;b.depth=f;c=x([{x:c[0],y:c[1],z:j}],h,i,g)[0];a.tooltipPos=[c.x,c.y]})}});c.wrap(c.seriesTypes.column.prototype,"animate",function(e){if(this.chart.is3d()){var a=arguments[1],b=this.yAxis,d=this,f=this.yAxis.reversed;if(c.svg)a?c.each(d.data,function(a){a.height=a.shapeArgs.height;a.shapeArgs.height=1;if(!f)a.shapeArgs.y=
-a.stackY?a.plotY+b.translate(a.stackY):a.plotY+(a.negative?-a.height:a.height)}):(c.each(d.data,function(a){a.shapeArgs.height=a.height;if(!f)a.shapeArgs.y=a.plotY-(a.negative?a.height:0);a.graphic&&a.graphic.animate(a.shapeArgs,d.options.animation)}),d.animate=null)}else e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.column.prototype,"init",function(c){c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart.options.plotOptions.column.grouping,b=this.chart.options.plotOptions.column.stacking,
-d=this.options.zIndex;if(!d&&(a===void 0||a)&&b){a=this.chart.retrieveStacks();b=this.options.stack||0;for(d=0;d<a[b].series.length;d++)if(a[b].series[d]===this)break;d=a.totalStacks*10-10*(a.totalStacks-a[b].position)-d;this.options.zIndex=d}}});c.seriesTypes.columnrange&&c.wrap(c.seriesTypes.columnrange.prototype,"drawPoints",F);c.wrap(c.seriesTypes.column.prototype,"drawPoints",F);var E=c.getOptions();E.plotOptions.cylinder=c.merge(E.plotOptions.column);E=c.extendClass(c.seriesTypes.column,{type:"cylinder"});
-c.seriesTypes.cylinder=E;c.wrap(c.seriesTypes.cylinder.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.options,d=b.plotOptions.cylinder,b=b.chart.options3d,f=d.depth||0,g={x:a.inverted?a.plotHeight/2:a.plotWidth/2,y:a.inverted?a.plotWidth/2:a.plotHeight/2,z:b.depth,vd:b.viewDistance},h=b.alpha,i=d.stacking?(this.options.stack||0)*f:this._i*f;i+=f/2;d.grouping!==!1&&(i=0);c.each(this.data,function(a){var b=a.shapeArgs;a.shapeType=
-"arc3d";b.x+=f/2;b.z=i;b.start=0;b.end=2*m;b.r=f*0.95;b.innerR=0;b.depth=b.height*(1/k((90-h)*n))-i;b.alpha=90-h;b.beta=0;b.origin=g})}});c.wrap(c.seriesTypes.pie.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this,b=a.chart,d=b.options,f=d.plotOptions.pie,g=f.depth||0,d=d.chart.options3d,h={x:b.plotWidth/2,y:b.plotHeight/2,z:d.depth},i=d.alpha,j=d.beta,o=f.stacking?(this.options.stack||0)*g:a._i*g;o+=g/2;f.grouping!==!1&&(o=0);c.each(a.data,
-function(b){b.shapeType="arc3d";var c=b.shapeArgs;c.z=o;c.depth=g*0.75;c.origin=h;c.alpha=i;c.beta=j;c=(c.end+c.start)/2;b.slicedTranslation={translateX:C(l(c)*a.options.slicedOffset*l(i*n)),translateY:C(k(c)*a.options.slicedOffset*l(i*n))}})}});c.wrap(c.seriesTypes.pie.prototype.pointClass.prototype,"haloPath",function(c){return this.series.chart.is3d()?[]:c.call(this)});c.wrap(c.seriesTypes.pie.prototype,"drawPoints",function(e){this.chart.is3d()&&c.each(this.data,function(a){var b=a.options.borderColor||
-a.color||a.series.userOptions.borderColor||a.series.color;a.options.borderColor=b;a.borderColor=b;a.pointAttr[""].stroke=b;a.pointAttr.hover.stroke=b;a.pointAttr.select.stroke=b});e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.pie.prototype,"drawDataLabels",function(e){e.apply(this,[].slice.call(arguments,1));this.chart.is3d()&&c.each(this.data,function(a){var b=a.shapeArgs,c=b.r,e=b.depth,g=b.alpha*n,h=b.beta*n,b=(b.start+b.end)/2;a.connector&&a.connector.translate(-c*(1-l(h))*l(b)+
-(l(b)>0?k(h)*e:0),-c*(1-l(g))*k(b)+(k(b)>0?k(g)*e:0));a.dataLabel&&a.dataLabel.attr({x:a.dataLabel.connX+-c*(1-l(h))*l(b)+(l(b)>0?l(h)*e:0)-a.dataLabel.width/2,y:a.dataLabel.connY+-c*(1-l(g))*k(b)+(k(b)>0?k(g)*e:0)-a.dataLabel.height/2})})});c.wrap(c.seriesTypes.pie.prototype,"addPoint",function(c){c.apply(this,[].slice.call(arguments,1));this.chart.is3d()&&this.update()});c.wrap(c.seriesTypes.pie.prototype,"animate",function(e){if(this.chart.is3d()){var a=arguments[1],b=this.options.animation,d=
-this.center,f=this.group,g=this.markerGroup;if(c.svg)if(b===!0&&(b={}),a){if(this.oldtranslateX=f.translateX,this.oldtranslateY=f.translateY,a={translateX:d[0],translateY:d[1],scaleX:0.001,scaleY:0.001},f.attr(a),g)g.attrSetters=f.attrSetters,g.attr(a)}else a={translateX:this.oldtranslateX,translateY:this.oldtranslateY,scaleX:1,scaleY:1},f.animate(a,b),g&&g.animate(a,b),this.animate=null}else e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.scatter.prototype,"translate",function(e){e.apply(this,
-[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=this.chart.options.chart.options3d,d=b.alpha,f=b.beta,g={x:a.inverted?a.plotHeight/2:a.plotWidth/2,y:a.inverted?a.plotWidth/2:a.plotHeight/2,z:b.depth,vd:b.viewDistance},b=b.depth,h=a.options.zAxis||{min:0,max:b},i=b/(h.max-h.min);c.each(this.data,function(a){var b={x:a.plotX,y:a.plotY,z:(a.z-h.min)*i},b=x([b],d,f,g)[0];a.plotXold=a.plotX;a.plotYold=a.plotY;a.plotX=b.x;a.plotY=b.y;a.plotZ=b.z})}});c.wrap(c.seriesTypes.scatter.prototype,
-"init",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d())this.pointArrayMap=["x","y","z"],this.tooltipOptions.pointFormat=this.userOptions.tooltip?this.userOptions.tooltip.pointFormat||"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>":"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>";return a});if(c.VMLRenderer)c.setOptions({animate:!1}),c.VMLRenderer.prototype.cuboid=c.SVGRenderer.prototype.cuboid,c.VMLRenderer.prototype.cuboidPath=
-c.SVGRenderer.prototype.cuboidPath,c.VMLRenderer.prototype.toLinePath=c.SVGRenderer.prototype.toLinePath,c.VMLRenderer.prototype.createElement3D=c.SVGRenderer.prototype.createElement3D,c.VMLRenderer.prototype.arc3d=function(e){e=c.SVGRenderer.prototype.arc3d.call(this,e);e.css({zIndex:e.zIndex});return e},c.VMLRenderer.prototype.arc3dPath=c.SVGRenderer.prototype.arc3dPath,c.Chart.prototype.renderSeries=function(){for(var c,a=this.series.length;a--;)c=this.series[a],c.translate(),c.setTooltipPoints&&
-c.setTooltipPoints(),c.render()},c.wrap(c.Axis.prototype,"render",function(c){c.apply(this,[].slice.call(arguments,1));this.sideFrame&&(this.sideFrame.css({zIndex:0}),this.sideFrame.front.attr({fill:this.sideFrame.color}));this.bottomFrame&&(this.bottomFrame.css({zIndex:1}),this.bottomFrame.front.attr({fill:this.bottomFrame.color}));this.backFrame&&(this.backFrame.css({zIndex:0}),this.backFrame.front.attr({fill:this.backFrame.color}))})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/highcharts-3d.src.js b/apps/static/js/plugins/highcharts/highcharts-3d.src.js
deleted file mode 100644
index 9ddb7b537..000000000
--- a/apps/static/js/plugins/highcharts/highcharts-3d.src.js
+++ /dev/null
@@ -1,1248 +0,0 @@
-// ==ClosureCompiler==
-// @compilation_level SIMPLE_OPTIMIZATIONS
-
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- *
- * (c) 2009-2013 Torstein Hønsi
- *
- * License: www.highcharts.com/license
- */
-
-// JSLint options:
-/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
-
-(function (Highcharts) {
-	/**
-	Shorthands for often used function
-*/ 
-/**
- *	Mathematical Functionility
- */
-var PI = Math.PI,
-	deg2rad = (PI / 180), // degrees to radians 
-	sin = Math.sin,
-	cos = Math.cos, 
-
-	round = Math.round;
-
-function perspective(points, angle2, angle1, origin) {
-	angle1 *= deg2rad;
-	angle2 *= deg2rad;
-
-	var result = [],
-		xe, 
-		ye, 
-		ze;
-
-	angle1 *= -1;
-	
-	xe = origin.x;
-	ye = origin.y;
-	ze = (origin.z === 0 ? 0.0001 : origin.z) * (origin.vd || 25);
-
-	// some kind of minimum?
-
-	var s1 = sin(angle1),
-		c1 = cos(angle1),
-		s2 = sin(angle2),
-		c2 = cos(angle2);
-
-	var x, y, z, p;
-
-	Highcharts.each(points, function (point) {
-		x = point.x - xe;
-		y = point.y - ye;
-		z = point.z || 0;
-
-		p = {
-			x: c1 * x - s1 * z,
-			y: -s1 * s2 * x - c1 * s2 * z + c2 * y,		
-			z: s1 * c2 * x + c1 * c2 * z + s2 * y
-		};
-
-		p.x = p.x * ((ze - p.z) / ze) + xe;
-		p.y = p.y * ((ze - p.z) / ze) + ye;
-
-		result.push({x: round(p.x), y: round(p.y), z: round(p.z)});
-	});
-	return result;
-}
-/*** 
-	EXTENSION TO THE SVG-RENDERER TO ENABLE 3D SHAPES
-	***/
-////// HELPER METHODS //////
-var dFactor = (4 * (Math.sqrt(2) - 1) / 3) / (PI / 2);
-
-function curveTo(cx, cy, rx, ry, start, end, dx, dy) {
-	var result = [];
-	if ((end > start) && (end - start > PI / 2 + 0.0001)) {
-		result = result.concat(curveTo(cx, cy, rx, ry, start, start + (PI / 2), dx, dy));
-		result = result.concat(curveTo(cx, cy, rx, ry, start + (PI / 2), end, dx, dy));
-		return result;
-	} else if ((end < start) && (start - end > PI / 2 + 0.0001)) {			
-		result = result.concat(curveTo(cx, cy, rx, ry, start, start - (PI / 2), dx, dy));
-		result = result.concat(curveTo(cx, cy, rx, ry, start - (PI / 2), end, dx, dy));
-		return result;
-	} else {
-		var arcAngle = end - start;
-		return [
-			'C', 
-			cx + (rx * cos(start)) - ((rx * dFactor * arcAngle) * sin(start)) + dx,
-			cy + (ry * sin(start)) + ((ry * dFactor * arcAngle) * cos(start)) + dy,
-			cx + (rx * cos(end)) + ((rx * dFactor * arcAngle) * sin(end)) + dx,
-			cy + (ry * sin(end)) - ((ry * dFactor * arcAngle) * cos(end)) + dy,
-
-			cx + (rx * cos(end)) + dx,
-			cy + (ry * sin(end)) + dy
-		];
-	}
-}
-
-Highcharts.SVGRenderer.prototype.toLinePath = function (points, closed) {
-	var result = [];
-
-	// Put "L x y" for each point
-	Highcharts.each(points, function (point) {
-		result.push('L', point.x, point.y);
-	});
-
-	// Set the first element to M
-	result[0] = 'M';
-
-	// If it is a closed line, add Z
-	if (closed) {
-		result.push('Z');
-	}
-	
-	return result;
-};
-
-////// CUBOIDS //////
-Highcharts.SVGRenderer.prototype.cuboid = function (shapeArgs) {
-
-	var result = this.g(),
-	paths = this.cuboidPath(shapeArgs);
-
-	result.front = this.path(paths[0]).attr({zIndex: paths[3], 'stroke-linejoin': 'round'}).add(result);
-	result.top = this.path(paths[1]).attr({zIndex: paths[4], 'stroke-linejoin': 'round'}).add(result);
-	result.side = this.path(paths[2]).attr({zIndex: paths[5], 'stroke-linejoin': 'round'}).add(result);
-
-	result.fillSetter = function (color) {
-		var c0 = color,
-		c1 = Highcharts.Color(color).brighten(0.1).get(),
-		c2 = Highcharts.Color(color).brighten(-0.1).get();
-
-		this.front.attr({fill: c0});
-		this.top.attr({fill: c1});
-		this.side.attr({fill: c2});
-
-		this.color = color;
-		return this;
-	};
-
-	result.opacitySetter = function (opacity) {
-		this.front.attr({opacity: opacity});
-		this.top.attr({opacity: opacity});
-		this.side.attr({opacity: opacity});
-		return this;
-	};
-
-	result.attr = function (args) {
-		if (args.shapeArgs || args.x) {
-			var shapeArgs = args.shapeArgs || args;
-			var paths = this.renderer.cuboidPath(shapeArgs);
-			this.front.attr({d: paths[0], zIndex: paths[3]});
-			this.top.attr({d: paths[1], zIndex: paths[4]});
-			this.side.attr({d: paths[2], zIndex: paths[5]});			
-		} else {
-			Highcharts.SVGElement.prototype.attr.call(this, args);
-		}
-
-		return this;
-	};
-	
-	result.animate = function (args, duration, complete) {
-		if (args.x && args.y) {
-			var paths = this.renderer.cuboidPath(args);
-			this.front.attr({zIndex: paths[3]}).animate({d: paths[0]}, duration, complete);
-			this.top.attr({zIndex: paths[4]}).animate({d: paths[1]}, duration, complete);
-			this.side.attr({zIndex: paths[5]}).animate({d: paths[2]}, duration, complete);
-		} else if (args.opacity) {				
-				this.front.animate(args, duration, complete);
-				this.top.animate(args, duration, complete);
-				this.side.animate(args, duration, complete);
-		} else {
-			Highcharts.SVGElement.prototype.animate.call(this, args, duration, complete);
-		}
-		return this;
-	};
-
-	result.destroy = function () {
-		this.front.destroy();
-		this.top.destroy();
-		this.side.destroy();
-
-		return null;
-	};
-
-	// Apply the Z index to the cuboid group
-	result.attr({ zIndex: -paths[3] });
-
-	return result;
-};
-
-
-Highcharts.SVGRenderer.prototype.cuboidPath = function (shapeArgs) {
-	var x = shapeArgs.x,
-		y = shapeArgs.y,
-		z = shapeArgs.z,
-		h = shapeArgs.height,
-		w = shapeArgs.width,
-		d = shapeArgs.depth,
-		alpha = shapeArgs.alpha,
-		beta = shapeArgs.beta,
-		origin = shapeArgs.origin;
-
-	var pArr = [
-		{x: x, y: y, z: z},
-		{x: x + w, y: y, z: z},
-		{x: x + w, y: y + h, z: z},
-		{x: x, y: y + h, z: z},
-		{x: x, y: y + h, z: z + d},
-		{x: x + w, y: y + h, z: z + d},
-		{x: x + w, y: y, z: z + d},
-		{x: x, y: y, z: z + d}
-	];
-
-	pArr = perspective(pArr, alpha, beta, origin);
-
-	var path1, // FRONT
-		path2, // TOP OR BOTTOM
-		path3; // LEFT OR RIGHT
-
-	// front	
-	path1 = [
-	'M', pArr[0].x, pArr[0].y,
-	'L', pArr[1].x, pArr[1].y,
-	'L', pArr[2].x, pArr[2].y,
-	'L', pArr[3].x, pArr[3].y,
-	'Z'
-	];
-	var z1 = (pArr[0].z + pArr[1].z + pArr[2].z + pArr[3].z) / 4;
-
-	// top or bottom
-	var top = [
-	'M', pArr[0].x, pArr[0].y,
-	'L', pArr[7].x, pArr[7].y,
-	'L', pArr[6].x, pArr[6].y,
-	'L', pArr[1].x, pArr[1].y,
-	'Z'
-	];
-	var bottom = [
-	'M', pArr[3].x, pArr[3].y,
-	'L', pArr[2].x, pArr[2].y,
-	'L', pArr[5].x, pArr[5].y,
-	'L', pArr[4].x, pArr[4].y,
-	'Z'
-	];
-	if (pArr[7].y < pArr[1].y) {
-		path2 = top;
-	} else if (pArr[4].y > pArr[2].y) {
-		path2 = bottom;
-	} else {
-		path2 = [];
-	}
-	var z2 = (beta > 0 ? (pArr[0].z + pArr[7].z + pArr[6].z + pArr[1].z) / 4 : (pArr[3].z + pArr[2].z + pArr[5].z + pArr[4].z) / 4);
-
-	// side
-	var right = [
-	'M', pArr[1].x, pArr[1].y,
-	'L', pArr[2].x, pArr[2].y,
-	'L', pArr[5].x, pArr[5].y,
-	'L', pArr[6].x, pArr[6].y,
-	'Z'
-	];
-	var left = [
-	'M', pArr[0].x, pArr[0].y,
-	'L', pArr[7].x, pArr[7].y,
-	'L', pArr[4].x, pArr[4].y,
-	'L', pArr[3].x, pArr[3].y,
-	'Z'
-	];	
-	if (pArr[6].x > pArr[1].x) {
-		path3 = right;
-	} else if (pArr[7].x < pArr[0].x) {
-		path3 = left;
-	} else {
-		path3 = [];
-	}
-	var z3 = (alpha > 0 ? (pArr[1].z + pArr[2].z + pArr[5].z + pArr[6].z) / 4 : (pArr[0].z + pArr[7].z + pArr[4].z + pArr[3].z) / 4);
-
-	return [path1, path2, path3, z1, z2, z3];
-};
-
-////// SECTORS //////
-Highcharts.SVGRenderer.prototype.arc3d = function (shapeArgs) {
-
-	shapeArgs.alpha *= deg2rad;
-	shapeArgs.beta *= deg2rad;
-	var result = this.g(),
-		paths = this.arc3dPath(shapeArgs),
-		renderer = result.renderer;
-
-	var zIndex = paths.zAll * 100;
-
-	result.shapeArgs = shapeArgs;	// Store for later use
-
-	result.side1 = renderer.path(paths.side2).attr({zIndex: paths.zSide2}).add(result);
-	result.side2 = renderer.path(paths.side1).attr({zIndex: paths.zSide1}).add(result);
-	result.inn = renderer.path(paths.inn).attr({zIndex: paths.zInn}).add(result);
-	result.out = renderer.path(paths.out).attr({zIndex: paths.zOut}).add(result);
-	result.top = renderer.path(paths.top).attr({zIndex: paths.zTop}).add(result);
-
-	result.fillSetter = function (color) {
-		this.color = color;
-
-		var c0 = color,
-		c2 = Highcharts.Color(color).brighten(-0.1).get();
-		
-		this.side1.attr({fill: c2});
-		this.side2.attr({fill: c2});
-		this.inn.attr({fill: c2});
-		this.out.attr({fill: c2});
-		this.top.attr({fill: c0});
-		return this;
-	};
-		
-	result.animate = function (args, duration, complete) {	
-		Highcharts.SVGElement.prototype.animate.call(this, args, duration, complete);
-		
-		if (args.x && args.y) {
-
-			// Recreate
-			var result = this,
-				renderer = this.renderer,
-				shapeArgs = Highcharts.splat(args)[0];
-
-			shapeArgs.alpha *= deg2rad;
-			shapeArgs.beta *= deg2rad;
-
-			var paths = renderer.arc3dPath(shapeArgs);
-
-			result.shapeArgs = shapeArgs;	// Store for later use
-
-			result.inn.attr({d: paths.inn, zIndex: paths.zInn});
-			result.out.attr({d: paths.out, zIndex: paths.zOut});
-			result.side1.attr({d: paths.side1, zIndex: paths.zSide2});
-			result.side2.attr({d: paths.side2, zIndex: paths.zSide1});
-			result.top.attr({d: paths.top, zIndex: paths.zTop});
-
-			result.attr({fill: result.color});
-			result.attr({zIndex: paths.zAll * 100});
-		}
-		
-		return this;
-	};
-
-	result.zIndex = zIndex;
-	result.attr({zIndex: zIndex});
-	return result;
-};
-
-
-Highcharts.SVGRenderer.prototype.arc3dPath = function (shapeArgs) {
-	var cx = shapeArgs.x,
-		cy = shapeArgs.y,
-		start = shapeArgs.start,
-		end = shapeArgs.end - 0.00001,
-		r = shapeArgs.r,
-		ir = shapeArgs.innerR,
-		d = shapeArgs.depth,
-		alpha = shapeArgs.alpha,
-		beta = shapeArgs.beta;
-
-	// Some Variables
-	var cs = cos(start),
-		ss = sin(start),
-		ce = cos(end),
-		se = sin(end),
-		rx = r * cos(beta),
-		ry = r * cos(alpha),
-		irx = ir * cos(beta),
-		iry = ir * cos(alpha),
-		dx = d * sin(beta),
-		dy = d * sin(alpha);
-
-	// TOP	
-	var top = ['M', cx + (rx * cs), cy + (ry * ss)];
-	top = top.concat(curveTo(cx, cy, rx, ry, start, end, 0, 0));
-	top = top.concat([
-		'L', cx + (irx * ce), cy + (iry * se)
-	]);
-	top = top.concat(curveTo(cx, cy, irx, iry, end, start, 0, 0));
-	top = top.concat(['Z']);
-
-	var midAngle = ((shapeArgs.start + shapeArgs.end) / 2);
-	var zIndex = ((sin(beta) * cos(midAngle)) + (sin(-alpha) * sin(-midAngle)));
-
-	// OUTSIDE
-	var b = (beta > 0 ? PI / 2 : 0),
-		a = (alpha > 0 ? 0 : PI / 2);
-
-	var start2 = start > -b ? start : (end > -b ? -b : start),
-		end2 = end < PI - a ? end : (start < PI - a ? PI - a : end);
-	
-	var out = ['M', cx + (rx * cos(start2)), cy + (ry * sin(start2))];
-	out = out.concat(curveTo(cx, cy, rx, ry, start2, end2, 0, 0));
-	out = out.concat([
-		'L', cx + (rx * cos(end2)) + dx, cy + (ry * sin(end2)) + dy
-	]);
-	out = out.concat(curveTo(cx, cy, rx, ry, end2, start2, dx, dy));
-	out = out.concat(['Z']);
-
-	// INSIDE
-	var inn = ['M', cx + (irx * cs), cy + (iry * ss)];
-	inn = inn.concat(curveTo(cx, cy, irx, iry, start, end, 0, 0));
-	inn = inn.concat([
-		'L', cx + (irx * cos(end)) + dx, cy + (iry * sin(end)) + dy
-	]);
-	inn = inn.concat(curveTo(cx, cy, irx, iry, end, start, dx, dy));
-	inn = inn.concat(['Z']);
-
-	// SIDES
-	var side1 = [
-		'M', cx + (rx * cs), cy + (ry * ss),
-		'L', cx + (rx * cs) + dx, cy + (ry * ss) + dy,
-		'L', cx + (irx * cs) + dx, cy + (iry * ss) + dy,
-		'L', cx + (irx * cs), cy + (iry * ss),
-		'Z'
-	];
-	var side2 = [
-		'M', cx + (rx * ce), cy + (ry * se),
-		'L', cx + (rx * ce) + dx, cy + (ry * se) + dy,
-		'L', cx + (irx * ce) + dx, cy + (iry * se) + dy,
-		'L', cx + (irx * ce), cy + (iry * se),
-		'Z'
-	];
-
-	var mr = ir + ((r - ir) / 2);
-
-	var zTop = Math.abs(zIndex * 2 * mr),
-		zOut = zIndex * r,
-		zInn = zIndex * ir,
-		zSide1 = ((sin(beta) * cos(start)) + (sin(-alpha) * sin(-start))) * mr,
-		zSide2 = ((sin(beta) * cos(end)) + (sin(-alpha) * sin(-end))) * mr;
-
-	return {
-		top: top,
-		zTop: zTop * 100,
-		out: out,
-		zOut: zOut * 100,
-		inn: inn,
-		zInn: zInn * 100,
-		side1: side1,
-		zSide1: zSide1 * 100,
-		side2: side2,
-		zSide2: zSide2 * 100,
-		zAll: zIndex
-	};
-};
-/*** 
-	EXTENSION FOR 3D CHARTS
-***/
-// Shorthand to check the is3d flag
-Highcharts.Chart.prototype.is3d = function () {
-	return this.options.chart.options3d && this.options.chart.options3d.enabled;
-};
-
-Highcharts.wrap(Highcharts.Chart.prototype, 'isInsidePlot', function (proceed) {
-	if (this.is3d()) {
-		return true;
-	} else {
-		return proceed.apply(this, [].slice.call(arguments, 1));
-	}
-});
-
-Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed) {	
-	var args = arguments;
-	args[1] = Highcharts.merge({ 
-		chart: {
-			options3d: {
-				enabled: false,
-				alpha: 0,
-				beta: 0,
-				depth: 100,
-				viewDistance: 25,
-
-				frame: {
-					bottom: { size: 1, color: 'rgba(255,255,255,0)' },
-					side: { size: 1, color: 'rgba(255,255,255,0)' },
-					back: { size: 1, color: 'rgba(255,255,255,0)' }
-				}
-			}
-		}
-	}, args[1]);
-
-	proceed.apply(this, [].slice.call(args, 1));
-});
-
-Highcharts.wrap(Highcharts.Chart.prototype, 'setChartSize', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	if (this.is3d()) {
-		var inverted = this.inverted,
-			clipBox = this.clipBox,
-			margin = this.margin,
-			x = inverted ? 'y' : 'x',
-			y = inverted ? 'x' : 'y',
-			w = inverted ? 'height' : 'width',
-			h = inverted ? 'width' : 'height';
-
-		clipBox[x] = -(margin[3] || 0);
-		clipBox[y] = -(margin[0] || 0);
-		clipBox[w] = this.chartWidth + (margin[3] || 0) + (margin[1] || 0);
-		clipBox[h] = this.chartHeight + (margin[0] || 0) + (margin[2] || 0);
-	}
-});
-
-Highcharts.wrap(Highcharts.Chart.prototype, 'redraw', function (proceed) {
-	if (this.is3d()) {
-		// Set to force a redraw of all elements
-		this.isDirtyBox = true;
-	}
-	proceed.apply(this, [].slice.call(arguments, 1));	
-});
-
-Highcharts.Chart.prototype.retrieveStacks = function () {
-	var stacks = {},
-		type = this.options.chart.type,
-		typeOptions = this.options.plotOptions[type],
-		stacking = typeOptions.stacking,
-		grouping = typeOptions.grouping,
-		i = 1;
-
-	if (grouping || !stacking) { return this.series; }
-
-	Highcharts.each(this.series, function (S) {
-		if (!stacks[S.options.stack || 0]) {
-			stacks[S.options.stack || 0] = { series: [S], position: i};
-			i++;
-		} else {
-			stacks[S.options.stack || 0].series.push(S);
-		}
-	});
-	stacks.totalStacks = i + 1;
-	return stacks;
-};
-
-/*** 
-	EXTENSION TO THE AXIS
-***/
-Highcharts.wrap(Highcharts.Axis.prototype, 'init', function (proceed) {
-	var args = arguments;
-	if (args[1].is3d()) {
-		args[2].tickWidth = Highcharts.pick(args[2].tickWidth, 0);
-		args[2].gridLineWidth = Highcharts.pick(args[2].gridLineWidth, 1);
-	}
-
-	proceed.apply(this, [].slice.call(arguments, 1));
-});	
-Highcharts.wrap(Highcharts.Axis.prototype, 'render', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {
-		return;
-	}
-
-	var chart = this.chart,
-		renderer = chart.renderer,
-		options3d = chart.options.chart.options3d,
-		alpha = options3d.alpha,
-		beta = options3d.beta * (chart.yAxis[0].opposite ? -1 : 1),
-		frame = options3d.frame,
-		fbottom = frame.bottom,
-		fback = frame.back,
-		fside = frame.side,
-		depth = options3d.depth,
-		height = this.height,
-		width = this.width,
-		left = this.left,
-		top = this.top;
-
-	var origin = {
-		x: chart.plotLeft + (chart.plotWidth / 2),
-		y: chart.plotTop + (chart.plotHeight / 2),
-		z: depth,
-		vd: options3d.viewDistance
-	};
-	if (this.horiz) {
-		/// BOTTOM
-		if (this.axisLine) {
-			this.axisLine.hide();
-		}
-		var bottomShape = {
-			x: left,
-			y: top + (chart.yAxis[0].reversed ? -fbottom.size : height),
-			z: 0,
-			width: width,
-			height: fbottom.size,
-			depth: depth,
-			alpha: alpha,
-			beta: beta,
-			origin: origin
-		};
-		if (!this.bottomFrame) {
-			this.bottomFrame = renderer.cuboid(bottomShape).attr({fill: fbottom.color, zIndex: (chart.yAxis[0].reversed && alpha > 0 ? 4 : -1)}).css({stroke: fbottom.color}).add();
-		} else {
-			this.bottomFrame.animate(bottomShape);
-		}
-	} else {
-		// BACK
-		var backShape = {
-			x: left,
-			y: top,
-			z: depth + 1,
-			width: width,
-			height: height + fbottom.size,
-			depth: fback.size,
-			alpha: alpha,
-			beta: beta,
-			origin: origin
-		};
-		if (!this.backFrame) {
-			this.backFrame = renderer.cuboid(backShape).attr({fill: fback.color, zIndex: -3}).css({stroke: fback.color}).add();
-		} else {
-			this.backFrame.animate(backShape);
-		}
-		// SIDE
-		if (this.axisLine) {
-			this.axisLine.hide();
-		}
-		var sideShape = {
-			x: (chart.yAxis[0].opposite ? width : 0) + left - fside.size,
-			y: top,
-			z: 0,
-			width: fside.size,
-			height: height + fbottom.size,
-			depth: depth + fback.size,
-			alpha: alpha,
-			beta: beta,
-			origin: origin
-		};
-		if (!this.sideFrame) {
-			this.sideFrame = renderer.cuboid(sideShape).attr({fill: fside.color, zIndex: -2}).css({stroke: fside.color}).add();
-		} else {
-			this.sideFrame.animate(sideShape);
-		}
-	}
-});
-
-Highcharts.wrap(Highcharts.Axis.prototype, 'getPlotLinePath', function (proceed) {
-	var path = proceed.apply(this, [].slice.call(arguments, 1));
-	
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {
-		return path;
-	}
-
-	if (path === null) { return path; }
-
-	var chart = this.chart,
-		options3d = chart.options.chart.options3d;
-
-	var d = options3d.depth;
-
-	options3d.origin = {
-		x: chart.plotLeft + (chart.plotWidth / 2),
-		y: chart.plotTop + (chart.plotHeight / 2),
-		z: d,
-		vd: options3d.viewDistance
-	};
-
-	var pArr = [
-		{ x: path[1], y: path[2], z : (this.horiz || this.opposite ? d : 0)},
-		{ x: path[1], y: path[2], z : d },
-		{ x: path[4], y: path[5], z : d },
-		{ x: path[4], y: path[5], z : (this.horiz || this.opposite ? 0 : d)}
-	];
-
-	var alpha = chart.options.inverted ? options3d.beta : options3d.alpha,
-		beta = chart.options.inverted ? options3d.alpha : options3d.beta;
-
-	beta *= (chart.yAxis[0].opposite ? -1 : 1);
-
-	pArr = perspective(pArr, alpha, beta, options3d.origin);
-	path = this.chart.renderer.toLinePath(pArr, false);
-
-	return path;
-});
-
-/*** 
-	EXTENSION TO THE TICKS
-***/
-
-Highcharts.wrap(Highcharts.Tick.prototype, 'getMarkPath', function (proceed) {
-	var path = proceed.apply(this, [].slice.call(arguments, 1));	
-
-	// Do not do this if the chart is not 3D
-	if (!this.axis.chart.is3d()) {
-		return path;
-	}
-
-	var chart = this.axis.chart,
-		options3d = chart.options.chart.options3d;
-
-	var origin = {
-		x: chart.plotLeft + (chart.plotWidth / 2),
-		y: chart.plotTop + (chart.plotHeight / 2),
-		z: options3d.depth,
-		vd: options3d.viewDistance
-	};
-
-	var pArr = [
-		{x: path[1], y: path[2], z: 0},
-		{x: path[4], y: path[5], z: 0}
-	];
-	
-	var alpha = chart.inverted ? options3d.beta : options3d.alpha,
-		beta = chart.inverted ? options3d.alpha : options3d.beta;
-
-	beta *= (chart.yAxis[0].opposite ? -1 : 1);
-
-	pArr = perspective(pArr, alpha, beta, origin);
-	path = [
-		'M', pArr[0].x, pArr[0].y,
-		'L', pArr[1].x, pArr[1].y
-		];
-	return path;
-});
-
-Highcharts.wrap(Highcharts.Tick.prototype, 'getLabelPosition', function (proceed) {
-	var pos = proceed.apply(this, [].slice.call(arguments, 1));
-	
-	// Do not do this if the chart is not 3D
-	if (!this.axis.chart.is3d()) {
-		return pos;
-	}	
-
-	var chart = this.axis.chart,
-		options3d = chart.options.chart.options3d;
-
-	var origin = {
-		x: chart.plotLeft + (chart.plotWidth / 2),
-		y: chart.plotTop + (chart.plotHeight / 2),
-		z: options3d.depth,
-		vd: options3d.viewDistance
-	};
-	
-	var alpha = chart.inverted ? options3d.beta : options3d.alpha,
-		beta = chart.inverted ? options3d.alpha : options3d.beta;
-
-	beta *= (chart.yAxis[0].opposite ? -1 : 1);
-
-	pos = perspective([{x: pos.x, y: pos.y, z: 0}], alpha, beta, origin)[0];
-	return pos;
-});
-
-Highcharts.wrap(Highcharts.Axis.prototype, 'drawCrosshair', function (proceed) {
-	var args = arguments;
-	if (this.chart.is3d()) {
-		if (args[2]) {
-			args[2] = {
-				plotX: args[2].plotXold || args[2].plotX,
-				plotY: args[2].plotYold || args[2].plotY
-			};
-		}
-	}
-	proceed.apply(this, [].slice.call(args, 1));
-});/*** 
-	EXTENSION FOR 3D COLUMNS
-***/
-Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'translate', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {  
-		return;
-	}	
-
-	var type = this.chart.options.chart.type,
-		series = this,
-		chart = series.chart,
-		options = chart.options,
-		typeOptions = options.plotOptions[type],		
-		options3d = options.chart.options3d,
-
-		depth = typeOptions.depth || 25,
-		origin = {
-			x: chart.plotWidth / 2,
-			y: chart.plotHeight / 2, 
-			z: options3d.depth,
-			vd: options3d.viewDistance
-		},
-		alpha = options3d.alpha,
-		beta = options3d.beta * (chart.yAxis[0].opposite ? -1 : 1);
-
-	var stack = typeOptions.stacking ? (this.options.stack || 0) : series._i; 
-	var z = stack * (depth + (typeOptions.groupZPadding || 1));
-
-	if (typeOptions.grouping !== false) { z = 0; }
-
-	z += (typeOptions.groupZPadding || 1);
-
-	Highcharts.each(series.data, function (point) {
-		var shapeArgs = point.shapeArgs,
-			tooltipPos = point.tooltipPos;
-
-		point.shapeType = 'cuboid';
-		shapeArgs.alpha = alpha;
-		shapeArgs.beta = beta; 
-		shapeArgs.z = z;
-		shapeArgs.origin = origin;
-		shapeArgs.depth = depth;
-
-		// Translate the tooltip position in 3d space
-		tooltipPos = perspective([{ x: tooltipPos[0], y: tooltipPos[1], z: z }], alpha, beta, origin)[0];
-		point.tooltipPos = [tooltipPos.x, tooltipPos.y];
-
-	});	    
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'animate', function (proceed) {
-	if (!this.chart.is3d()) {
-		proceed.apply(this, [].slice.call(arguments, 1));
-	} else {
-		var args = arguments,
-			init = args[1],
-			yAxis = this.yAxis,
-			series = this,
-			reversed = this.yAxis.reversed;
-
-		if (Highcharts.svg) { // VML is too slow anyway
-			if (init) {
-				Highcharts.each(series.data, function (point) {
-					point.height = point.shapeArgs.height;					
-					point.shapeArgs.height = 1;
-					if (!reversed) {
-						if (point.stackY) {
-							point.shapeArgs.y = point.plotY + yAxis.translate(point.stackY);
-						} else {
-							point.shapeArgs.y = point.plotY + (point.negative ? -point.height : point.height);
-						}
-					}
-				});
-
-			} else { // run the animation				
-				Highcharts.each(series.data, function (point) {					
-					point.shapeArgs.height = point.height;
-					if (!reversed) {
-						point.shapeArgs.y = point.plotY - (point.negative ? point.height : 0);
-					}
-					// null value do not have a graphic
-					if (point.graphic) {
-						point.graphic.animate(point.shapeArgs, series.options.animation);					
-					}
-				});
-
-				// delete this function to allow it only once
-				series.animate = null;
-			}
-		}
-	}
-});
-
-
-Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'init', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	if (this.chart.is3d()) {
-	var grouping = this.chart.options.plotOptions.column.grouping,
-		stacking = this.chart.options.plotOptions.column.stacking,
-		z = this.options.zIndex;
-		if (!z) {		
-			if (!(grouping !== undefined && !grouping) && stacking) {
-				var stacks = this.chart.retrieveStacks(),
-					stack = this.options.stack || 0,
-					i; // position within the stack
-				for (i = 0; i < stacks[stack].series.length; i++) {
-					if (stacks[stack].series[i] === this) {
-						break;
-					}
-				}
-				z = (stacks.totalStacks * 10) - (10 * (stacks.totalStacks - stacks[stack].position)) - i;
-				
-				this.options.zIndex = z;
-			}
-		}
-	}
-});
-function draw3DPoints(proceed) {
-	// Do not do this if the chart is not 3D
-	if (this.chart.is3d()) {		
-		var grouping = this.chart.options.plotOptions.column.grouping;
-		if (grouping !== undefined && !grouping && this.group.zIndex !== undefined) {
-			this.group.attr({zIndex : (this.group.zIndex * 10)});
-		} 
-		if (this.userOptions.borderColor === undefined) {
-			this.options.borderColor = this.color;
-		}
-
-		// Set the border color to the fill color to provide a smooth edge
-		Highcharts.each(this.data, function (point) {
-			var c = point.options.borderColor || point.color || point.series.userOptions.borderColor;
-			point.options.borderColor = c;
-			point.borderColor = c;
-			point.pointAttr[''].stroke = c;
-			// same bordercolor on hover and select
-			point.pointAttr.hover.stroke = c;
-			point.pointAttr.select.stroke = c;
-		});	
-	}
-
-	proceed.apply(this, [].slice.call(arguments, 1));
-}
-
-if (Highcharts.seriesTypes.columnrange) {
-	Highcharts.wrap(Highcharts.seriesTypes.columnrange.prototype, 'drawPoints', draw3DPoints);
-}
-
-Highcharts.wrap(Highcharts.seriesTypes.column.prototype, 'drawPoints', draw3DPoints);
-
-/*** 
-	EXTENSION FOR 3D CYLINDRICAL COLUMNS
-	Not supported
-***/
-var defaultOptions = Highcharts.getOptions();
-defaultOptions.plotOptions.cylinder = Highcharts.merge(defaultOptions.plotOptions.column);
-var CylinderSeries = Highcharts.extendClass(Highcharts.seriesTypes.column, {
-	type: 'cylinder'
-});
-Highcharts.seriesTypes.cylinder = CylinderSeries;
-
-Highcharts.wrap(Highcharts.seriesTypes.cylinder.prototype, 'translate', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {
-		return;
-	}	
-
-	var series = this,
-		chart = series.chart,
-		options = chart.options,
-		cylOptions = options.plotOptions.cylinder,
-		options3d = options.chart.options3d,
-		depth = cylOptions.depth || 0,
-		origin = {
-			x: chart.inverted ? chart.plotHeight / 2 : chart.plotWidth / 2,
-			y: chart.inverted ? chart.plotWidth / 2 : chart.plotHeight / 2, 
-			z: options3d.depth,
-			vd: options3d.viewDistance
-		},
-		alpha = options3d.alpha;
-
-	var z = cylOptions.stacking ? (this.options.stack || 0) * depth : series._i * depth;
-	z += depth / 2;
-
-	if (cylOptions.grouping !== false) { z = 0; }
-
-	Highcharts.each(series.data, function (point) {
-		var shapeArgs = point.shapeArgs;
-		point.shapeType = 'arc3d';
-		shapeArgs.x += depth / 2;
-		shapeArgs.z = z;
-		shapeArgs.start = 0;
-		shapeArgs.end = 2 * PI;
-		shapeArgs.r = depth * 0.95;
-		shapeArgs.innerR = 0;
-		shapeArgs.depth = shapeArgs.height * (1 / sin((90 - alpha) * deg2rad)) - z;
-		shapeArgs.alpha = 90 - alpha;
-		shapeArgs.beta = 0;
-		shapeArgs.origin = origin;	
-	});
-});
-/*** 
-	EXTENSION FOR 3D PIES
-***/
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'translate', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {
-		return;
-	}	
-	
-	var series = this,
-		chart = series.chart,
-		options = chart.options,
-		pieOptions = options.plotOptions.pie,
-		depth = pieOptions.depth || 0,
-		options3d = options.chart.options3d,
-		origin = {
-			x: chart.plotWidth / 2,
-			y: chart.plotHeight / 2,
-			z: options3d.depth
-		},
-		alpha = options3d.alpha,
-		beta = options3d.beta;
-
-	var z = pieOptions.stacking ? (this.options.stack || 0) * depth : series._i * depth;
-	z += depth / 2;
-
-	if (pieOptions.grouping !== false) { z = 0; }
-
-	Highcharts.each(series.data, function (point) {
-		point.shapeType = 'arc3d';
-		var shapeArgs = point.shapeArgs;
-
-		shapeArgs.z = z;
-		shapeArgs.depth = depth * 0.75;
-		shapeArgs.origin = origin;
-		shapeArgs.alpha = alpha;
-		shapeArgs.beta = beta;
-	
-		var angle = (shapeArgs.end + shapeArgs.start) / 2;
-
-		point.slicedTranslation = {
-			translateX : round(cos(angle) * series.options.slicedOffset * cos(alpha * deg2rad)),
-			translateY : round(sin(angle) * series.options.slicedOffset * cos(alpha * deg2rad))
-		};
-	});
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype.pointClass.prototype, 'haloPath', function (proceed) {
-	return this.series.chart.is3d() ? [] : proceed.call(this);
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'drawPoints', function (proceed) {
-	// Do not do this if the chart is not 3D
-	if (this.chart.is3d()) {
-		// Set the border color to the fill color to provide a smooth edge
-		Highcharts.each(this.data, function (point) {
-			var c = point.options.borderColor || point.color || point.series.userOptions.borderColor || point.series.color;
-			point.options.borderColor = c;
-			point.borderColor = c;
-			point.pointAttr[''].stroke = c;
-			// same bordercolor on hover and select
-			point.pointAttr.hover.stroke = c;
-			point.pointAttr.select.stroke = c;
-		});	
-	}
-
-	proceed.apply(this, [].slice.call(arguments, 1));
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'drawDataLabels', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-	// Do not do this if the chart is not 3D
-	if (!this.chart.is3d()) {
-		return;
-	}	
-
-	var series = this;
-	Highcharts.each(series.data, function (point) {
-		var shapeArgs = point.shapeArgs;
-		var r = shapeArgs.r,
-			d = shapeArgs.depth,
-			a1 = shapeArgs.alpha * deg2rad,
-			b1 = shapeArgs.beta * deg2rad,
-			a2 = (shapeArgs.start + shapeArgs.end) / 2; 
-
-		if (point.connector) {
-			point.connector.translate(
-				(-r * (1 - cos(b1)) * cos(a2)) + (cos(a2) > 0 ? sin(b1) * d : 0),
-				(-r * (1 - cos(a1)) * sin(a2)) + (sin(a2) > 0 ? sin(a1) * d : 0)
-			);
-		}
-		if (point.dataLabel) {
-			point.dataLabel.attr({
-				x: point.dataLabel.connX + (-r * (1 - cos(b1)) * cos(a2)) + (cos(a2) > 0 ? cos(b1) * d : 0) - (point.dataLabel.width / 2),
-				y: point.dataLabel.connY + (-r * (1 - cos(a1)) * sin(a2)) + (sin(a2) > 0 ? sin(a1) * d : 0) - (point.dataLabel.height / 2)
-			});
-		}
-	});
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'addPoint', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));	
-	if (this.chart.is3d()) {
-		// destroy (and rebuild) everything!!!
-		this.update();
-	}
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'animate', function (proceed) {
-	if (!this.chart.is3d()) {
-		proceed.apply(this, [].slice.call(arguments, 1));
-	} else {
-		var args = arguments,
-			init = args[1],
-			animation = this.options.animation,
-			attribs,
-			center = this.center,
-			group = this.group,
-			markerGroup = this.markerGroup;
-
-		if (Highcharts.svg) { // VML is too slow anyway
-				
-				if (animation === true) {
-					animation = {};
-				}
-				// Initialize the animation
-				if (init) {
-				
-					// Scale down the group and place it in the center
-					this.oldtranslateX = group.translateX;
-					this.oldtranslateY = group.translateY;
-					attribs = {
-						translateX: center[0],
-						translateY: center[1],
-						scaleX: 0.001, // #1499
-						scaleY: 0.001
-					};
-					
-					group.attr(attribs);
-					if (markerGroup) {
-						markerGroup.attrSetters = group.attrSetters;
-						markerGroup.attr(attribs);
-					}
-				
-				// Run the animation
-				} else {
-					attribs = {
-						translateX: this.oldtranslateX,
-						translateY: this.oldtranslateY,
-						scaleX: 1,
-						scaleY: 1
-					};
-					group.animate(attribs, animation);
-					if (markerGroup) {
-						markerGroup.animate(attribs, animation);
-					}
-				
-					// Delete this function to allow it only once
-					this.animate = null;
-				}
-				
-		}
-	}
-});/*** 
-	EXTENSION FOR 3D SCATTER CHART
-***/
-Highcharts.wrap(Highcharts.seriesTypes.scatter.prototype, 'translate', function (proceed) {
-//function translate3d(proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-	
-	if (!this.chart.is3d()) {
-		return;
-	}	
-
-	var series = this,
-		chart = series.chart,
-		options3d = series.chart.options.chart.options3d,
-		alpha = options3d.alpha,
-		beta = options3d.beta,
-		origin = {
-			x: chart.inverted ? chart.plotHeight / 2 : chart.plotWidth / 2,
-			y: chart.inverted ? chart.plotWidth / 2 : chart.plotHeight / 2, 
-			z: options3d.depth,
-			vd: options3d.viewDistance
-		},
-		depth = options3d.depth,
-		zAxis = chart.options.zAxis || { min : 0, max: depth };
-	
-	var rangeModifier = depth / (zAxis.max - zAxis.min);
-	
-	Highcharts.each(series.data, function (point) {
-		var pCo = { 
-			x: point.plotX,
-			y: point.plotY,
-			z: (point.z - zAxis.min) * rangeModifier
-		};
-
-		pCo = perspective([pCo], alpha, beta, origin)[0];		
-
-		point.plotXold = point.plotX;
-		point.plotYold = point.plotY;
-		
-		point.plotX = pCo.x;
-		point.plotY = pCo.y;
-		point.plotZ = pCo.z;
-	});	  
-});
-
-Highcharts.wrap(Highcharts.seriesTypes.scatter.prototype, 'init', function (proceed) {
-	var result = proceed.apply(this, [].slice.call(arguments, 1));
-
-	if (this.chart.is3d()) {
-		// Add a third coordinate
-		this.pointArrayMap = ['x', 'y', 'z'];
-
-		// Set a new default tooltip formatter
-		var default3dScatterTooltip = 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>';
-		if (this.userOptions.tooltip) {
-			this.tooltipOptions.pointFormat = this.userOptions.tooltip.pointFormat || default3dScatterTooltip;
-		} else {
-			this.tooltipOptions.pointFormat = default3dScatterTooltip;
-		}
-	}
-	return result;
-});/**
- *	Extension to the VML Renderer
- */
-if (Highcharts.VMLRenderer) {
-
-Highcharts.setOptions({animate: false});
-
-Highcharts.VMLRenderer.prototype.cuboid = Highcharts.SVGRenderer.prototype.cuboid;
-Highcharts.VMLRenderer.prototype.cuboidPath = Highcharts.SVGRenderer.prototype.cuboidPath;
-
-Highcharts.VMLRenderer.prototype.toLinePath = Highcharts.SVGRenderer.prototype.toLinePath;
-
-Highcharts.VMLRenderer.prototype.createElement3D = Highcharts.SVGRenderer.prototype.createElement3D;
-
-Highcharts.VMLRenderer.prototype.arc3d = function (shapeArgs) { 
-	var result = Highcharts.SVGRenderer.prototype.arc3d.call(this, shapeArgs);
-	result.css({zIndex: result.zIndex});
-	return result;
-};
-
-Highcharts.VMLRenderer.prototype.arc3dPath = Highcharts.SVGRenderer.prototype.arc3dPath;
-
-// Draw the series in the reverse order
-Highcharts.Chart.prototype.renderSeries = function () {
-	var serie,
-		i = this.series.length;
-	while (i--) {		
-		serie = this.series[i];
-		serie.translate();
-		if (serie.setTooltipPoints) {
-			serie.setTooltipPoints();
-		}
-		serie.render();	
-	}
-};
-
-Highcharts.wrap(Highcharts.Axis.prototype, 'render', function (proceed) {
-	proceed.apply(this, [].slice.call(arguments, 1));
-	// VML doesn't support a negative z-index
-	if (this.sideFrame) {
-		this.sideFrame.css({zIndex: 0});
-		this.sideFrame.front.attr({fill: this.sideFrame.color});
-	}
-	if (this.bottomFrame) {
-		this.bottomFrame.css({zIndex: 1});
-		this.bottomFrame.front.attr({fill: this.bottomFrame.color});
-	}	
-	if (this.backFrame) {
-		this.backFrame.css({zIndex: 0});
-		this.backFrame.front.attr({fill: this.backFrame.color});
-	}		
-});
-
-}
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/highcharts-all.js b/apps/static/js/plugins/highcharts/highcharts-all.js
deleted file mode 100644
index b2159174c..000000000
--- a/apps/static/js/plugins/highcharts/highcharts-all.js
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- Standalone Highcharts Framework
-
- License: MIT License
-*/
-var HighchartsAdapter=function(){function o(c){function b(b,a,d){b.removeEventListener(a,d,!1)}function d(b,a,d){d=b.HCProxiedMethods[d.toString()];b.detachEvent("on"+a,d)}function a(a,c){var f=a.HCEvents,i,g,k,j;if(a.removeEventListener)i=b;else if(a.attachEvent)i=d;else return;c?(g={},g[c]=!0):g=f;for(j in g)if(f[j])for(k=f[j].length;k--;)i(a,j,f[j][k])}c.HCExtended||Highcharts.extend(c,{HCExtended:!0,HCEvents:{},bind:function(b,a){var d=this,c=this.HCEvents,g;if(d.addEventListener)d.addEventListener(b,
-a,!1);else if(d.attachEvent){g=function(b){b.target=b.srcElement||window;a.call(d,b)};if(!d.HCProxiedMethods)d.HCProxiedMethods={};d.HCProxiedMethods[a.toString()]=g;d.attachEvent("on"+b,g)}c[b]===r&&(c[b]=[]);c[b].push(a)},unbind:function(c,h){var f,i;c?(f=this.HCEvents[c]||[],h?(i=HighchartsAdapter.inArray(h,f),i>-1&&(f.splice(i,1),this.HCEvents[c]=f),this.removeEventListener?b(this,c,h):this.attachEvent&&d(this,c,h)):(a(this,c),this.HCEvents[c]=[])):(a(this),this.HCEvents={})},trigger:function(b,
-a){var d=this.HCEvents[b]||[],c=d.length,g,k,j;k=function(){a.defaultPrevented=!0};for(g=0;g<c;g++){j=d[g];if(a.stopped)break;a.preventDefault=k;a.target=this;if(!a.type)a.type=b;j.call(this,a)===!1&&a.preventDefault()}}});return c}var r,l=document,p=[],m=[],q,n;Math.easeInOutSine=function(c,b,d,a){return-d/2*(Math.cos(Math.PI*c/a)-1)+b};return{init:function(c){if(!l.defaultView)this._getStyle=function(b,d){var a;return b.style[d]?b.style[d]:(d==="opacity"&&(d="filter"),a=b.currentStyle[d.replace(/\-(\w)/g,
-function(a,b){return b.toUpperCase()})],d==="filter"&&(a=a.replace(/alpha\(opacity=([0-9]+)\)/,function(b,a){return a/100})),a===""?1:a)},this.adapterRun=function(b,d){var a={width:"clientWidth",height:"clientHeight"}[d];if(a)return b.style.zoom=1,b[a]-2*parseInt(HighchartsAdapter._getStyle(b,"padding"),10)};if(!Array.prototype.forEach)this.each=function(b,d){for(var a=0,c=b.length;a<c;a++)if(d.call(b[a],b[a],a,b)===!1)return a};if(!Array.prototype.indexOf)this.inArray=function(b,d){var a,c=0;if(d)for(a=
-d.length;c<a;c++)if(d[c]===b)return c;return-1};if(!Array.prototype.filter)this.grep=function(b,d){for(var a=[],c=0,h=b.length;c<h;c++)d(b[c],c)&&a.push(b[c]);return a};n=function(b,c,a){this.options=c;this.elem=b;this.prop=a};n.prototype={update:function(){var b;b=this.paths;var d=this.elem,a=d.element;b&&a?d.attr("d",c.step(b[0],b[1],this.now,this.toD)):d.attr?a&&d.attr(this.prop,this.now):(b={},b[this.prop]=this.now+this.unit,Highcharts.css(d,b));this.options.step&&this.options.step.call(this.elem,
-this.now,this)},custom:function(b,c,a){var e=this,h=function(a){return e.step(a)},f;this.startTime=+new Date;this.start=b;this.end=c;this.unit=a;this.now=this.start;this.pos=this.state=0;h.elem=this.elem;h()&&m.push(h)===1&&(q=setInterval(function(){for(f=0;f<m.length;f++)m[f]()||m.splice(f--,1);m.length||clearInterval(q)},13))},step:function(b){var c=+new Date,a;a=this.options;var e=this.elem,h;if(e.stopAnimation||e.attr&&!e.element)a=!1;else if(b||c>=a.duration+this.startTime){this.now=this.end;
-this.pos=this.state=1;this.update();b=this.options.curAnim[this.prop]=!0;for(h in a.curAnim)a.curAnim[h]!==!0&&(b=!1);b&&a.complete&&a.complete.call(e);a=!1}else e=c-this.startTime,this.state=e/a.duration,this.pos=a.easing(e,0,1,a.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update(),a=!0;return a}};this.animate=function(b,d,a){var e,h="",f,i,g;b.stopAnimation=!1;if(typeof a!=="object"||a===null)e=arguments,a={duration:e[2],easing:e[3],complete:e[4]};if(typeof a.duration!=="number")a.duration=
-400;a.easing=Math[a.easing]||Math.easeInOutSine;a.curAnim=Highcharts.extend({},d);for(g in d)i=new n(b,a,g),f=null,g==="d"?(i.paths=c.init(b,b.d,d.d),i.toD=d.d,e=0,f=1):b.attr?e=b.attr(g):(e=parseFloat(HighchartsAdapter._getStyle(b,g))||0,g!=="opacity"&&(h="px")),f||(f=parseFloat(d[g])),i.custom(e,f,h)}},_getStyle:function(c,b){return window.getComputedStyle(c,void 0).getPropertyValue(b)},getScript:function(c,b){var d=l.getElementsByTagName("head")[0],a=l.createElement("script");a.type="text/javascript";
-a.src=c;a.onload=b;d.appendChild(a)},inArray:function(c,b){return b.indexOf?b.indexOf(c):p.indexOf.call(b,c)},adapterRun:function(c,b){return parseInt(HighchartsAdapter._getStyle(c,b),10)},grep:function(c,b){return p.filter.call(c,b)},map:function(c,b){for(var d=[],a=0,e=c.length;a<e;a++)d[a]=b.call(c[a],c[a],a,c);return d},offset:function(c){var b=document.documentElement,c=c.getBoundingClientRect();return{top:c.top+(window.pageYOffset||b.scrollTop)-(b.clientTop||0),left:c.left+(window.pageXOffset||
-b.scrollLeft)-(b.clientLeft||0)}},addEvent:function(c,b,d){o(c).bind(b,d)},removeEvent:function(c,b,d){o(c).unbind(b,d)},fireEvent:function(c,b,d,a){var e;l.createEvent&&(c.dispatchEvent||c.fireEvent)?(e=l.createEvent("Events"),e.initEvent(b,!0,!0),e.target=c,Highcharts.extend(e,d),c.dispatchEvent?c.dispatchEvent(e):c.fireEvent(b,e)):c.HCExtended===!0&&(d=d||{},c.trigger(b,d));d&&d.defaultPrevented&&(a=null);a&&a(d)},washMouseEvent:function(c){return c},stop:function(c){c.stopAnimation=!0},each:function(c,
-b){return Array.prototype.forEach.call(c,b)}}}();
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(){function q(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function w(){var a,b=arguments,c,d={},e=function(a,b){var c,d;typeof a!=="object"&&(a={});for(d in b)b.hasOwnProperty(d)&&(c=b[d],a[d]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&d!=="renderTo"&&typeof c.nodeType!=="number"?e(a[d]||{},c):b[d]);return a};b[0]===!0&&(d=b[1],b=Array.prototype.slice.call(b,2));c=b.length;for(a=0;a<c;a++)d=e(d,b[a]);return d}function z(a,b){return parseInt(a,b||
-10)}function Fa(a){return typeof a==="string"}function ca(a){return typeof a==="object"}function La(a){return Object.prototype.toString.call(a)==="[object Array]"}function ha(a){return typeof a==="number"}function za(a){return U.log(a)/U.LN10}function ia(a){return U.pow(10,a)}function ja(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function r(a){return a!==t&&a!==null}function H(a,b,c){var d,e;if(Fa(b))r(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(r(b)&&
-ca(b))for(d in b)a.setAttribute(d,b[d]);return e}function qa(a){return La(a)?a:[a]}function m(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function G(a,b){if(Aa&&!aa&&b&&b.opacity!==t)b.filter="alpha(opacity="+b.opacity*100+")";q(a.style,b)}function Y(a,b,c,d,e){a=y.createElement(a);b&&q(a,b);e&&G(a,{padding:0,border:Q,margin:0});c&&G(a,c);d&&d.appendChild(a);return a}function ka(a,b){var c=function(){};c.prototype=new a;q(c.prototype,b);return c}
-function Ga(a,b,c,d){var e=E.lang,a=+a||0,f=b===-1?(a.toString().split(".")[1]||"").length:isNaN(b=M(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(z(a=M(a).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+M(a-c).toFixed(f).slice(2):"")}function Ha(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function Ma(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);
-a.unshift(d);return c.apply(this,a)}}function Ia(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=E.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e!==null&&(e=Ga(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:""))):e=cb(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function mb(a){return U.pow(10,T(U.log(a)/
-U.LN10))}function nb(a,b,c,d){var e,c=m(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Bb(){this.symbol=this.color=0}function ob(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}function Na(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function Ba(a){for(var b=
-a.length,c=a[0];b--;)a[b]>c&&(c=a[b]);return c}function Oa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Pa(a){db||(db=Y(Ja));a&&db.appendChild(a);db.innerHTML=""}function ra(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else I.console&&console.log(c)}function da(a){return parseFloat(a.toPrecision(14))}function Qa(a,b){va=m(a,b.animation)}function Cb(){var a=E.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Ra=(a&&E.global.timezoneOffset||
-0)*6E4;eb=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,m(c,1),m(g,0),m(h,0),m(i,0))).getTime()};pb=b+"Minutes";qb=b+"Hours";rb=b+"Day";Xa=b+"Date";fb=b+"Month";gb=b+"FullYear";Db=c+"Minutes";Eb=c+"Hours";sb=c+"Date";Fb=c+"Month";Gb=c+"FullYear"}function P(){}function Sa(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function la(){this.init.apply(this,arguments)}function Ya(){this.init.apply(this,arguments)}function Hb(a,b,c,d,e){var f=a.chart.inverted;
-this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.total=null;this.points={};this.stack=e;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom":"top"),y:m(b.y,f?4:c?14:-6),x:m(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign||(f?c?"right":"left":"center")}var t,y=document,I=window,U=Math,u=U.round,T=U.floor,Ka=U.ceil,v=U.max,C=U.min,M=U.abs,Z=U.cos,ea=U.sin,ma=U.PI,Ca=ma*2/360,wa=navigator.userAgent,Ib=I.opera,Aa=/msie/i.test(wa)&&
-!Ib,hb=y.documentMode===8,ib=/AppleWebKit/.test(wa),Ta=/Firefox/.test(wa),Jb=/(Mobile|Android|Windows Phone)/.test(wa),xa="http://www.w3.org/2000/svg",aa=!!y.createElementNS&&!!y.createElementNS(xa,"svg").createSVGRect,Nb=Ta&&parseInt(wa.split("Firefox/")[1],10)<4,fa=!aa&&!Aa&&!!y.createElement("canvas").getContext,Za,$a,Kb={},tb=0,db,E,cb,va,ub,A,sa=function(){},V=[],ab=0,Ja="div",Q="none",Ob=/^[0-9]+$/,Pb="stroke-width",eb,Ra,pb,qb,rb,Xa,fb,gb,Db,Eb,sb,Fb,Gb,F={},R=I.Highcharts=I.Highcharts?ra(16,
-!0):{};cb=function(a,b,c){if(!r(b)||isNaN(b))return"Invalid date";var a=m(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b-Ra),e,f=d[qb](),g=d[rb](),h=d[Xa](),i=d[fb](),j=d[gb](),k=E.lang,l=k.weekdays,d=q({a:l[g].substr(0,3),A:l[g],d:Ha(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ha(i+1),y:j.toString().substr(2,2),Y:j,H:Ha(f),I:Ha(f%12||12),l:f%12||12,M:Ha(d[pb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:Ha(d.getSeconds()),L:Ha(u(b%1E3),3)},R.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]===
-"function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Bb.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};A=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}("millisecond",1,"second",1E3,"minute",6E4,"hour",36E5,"day",864E5,"week",6048E5,"month",26784E5,"year",31556952E3);ub={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),
-h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f&&b.length===c.length)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f,f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===
-b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){I.HighchartsAdapter=I.HighchartsAdapter||a&&{init:function(b){var c=a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur","_default","width","height","opacity"],function(a,b){var e=d,k;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){var d,c=
-a?c:this;if(c.prop!=="align")return d=c.elem,d.attr?d.attr(c.prop,b==="cur"?t:c.now):k.apply(this,arguments)})});Ma(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}:d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],
-a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;if(this[0]){Fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==t)c.chart=c.chart||{},c.chart.renderTo=this[0],new R[a](c,b[1]),d=this;c===t&&(d=V[H(this[0],"data-highcharts-chart")])}return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b,c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},
-addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=y.removeEventListener?"removeEventListener":"detachEvent";y[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,c,d,e){var f=a.Event(c),g="detached"+c,h;!Aa&&d&&(delete d.layerX,delete d.layerY,delete d.returnValue);q(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault","stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);
-b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===t)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style={};if(c.d)b.toD=c.d,c.d=1;e.stop();c.opacity!==t&&b.attr&&(c.opacity+="px");e.animate(c,d)},stop:function(b){a(b).stop()}}})(I.jQuery);var S=I.HighchartsAdapter,N=S||{};S&&S.init.call(S,ub);var jb=N.adapterRun,Qb=N.getScript,Da=N.inArray,p=N.each,vb=N.grep,Rb=N.offset,Ua=
-N.map,K=N.addEvent,W=N.removeEvent,D=N.fireEvent,Sb=N.washMouseEvent,kb=N.animate,bb=N.stop,N={enabled:!0,x:0,y:15,style:{color:"#606060",cursor:"default",fontSize:"11px"}};E={colors:"#7cb5ec,#434348,#90ed7d,#f7a35c,#8085e9,#f15c80,#e4d354,#8085e8,#8d4653,#91e8e1".split(","),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),
-weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,canvasToolsURL:"http://code.highcharts.com/4.0.1/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/4.0.1/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:0,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacing:[10,10,15,
-10],backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",margin:15,style:{color:"#333333",fontSize:"18px"}},subtitle:{text:"",align:"center",style:{color:"#555555"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},lineWidth:2,marker:{lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",
-lineWidth:2}}},point:{events:{}},dataLabels:w(N,{align:"center",enabled:!1,formatter:function(){return this.y===null?"":Ga(this.y,-1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,states:{hover:{marker:{},halo:{size:10,opacity:0.25}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3}},labels:{style:{position:"absolute",color:"#3E576F"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderColor:"#909090",borderRadius:0,navigation:{activeColor:"#274b6d",
-inactiveColor:"#CCC"},shadow:!1,itemStyle:{color:"#333333",fontSize:"12px",fontWeight:"bold"},itemHoverStyle:{color:"#000"},itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:aa,backgroundColor:"rgba(249, 249, 249, .85)",
-borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>',pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.y}</b><br/>',shadow:!0,snap:Jb?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",
-whiteSpace:"nowrap"}},credits:{enabled:0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var ba=E.plotOptions,S=ba.line;Cb();var Tb=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,Ub=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,Vb=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,ya=function(a){var b=[],c,
-d;(function(a){a&&a.stops?d=Ua(a.stops,function(a){return ya(a[1])}):(c=Tb.exec(a))?b=[z(c[1]),z(c[2]),z(c[3]),parseFloat(c[4],10)]:(c=Ub.exec(a))?b=[z(c[1],16),z(c[2],16),z(c[3],16),1]:(c=Vb.exec(a))&&(b=[z(c[1]),z(c[2]),z(c[3]),1])})(a);return{get:function(c){var f;d?(f=w(a),f.stops=[].concat(f.stops),p(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)p(d,
-function(b){b.brighten(a)});else if(ha(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=z(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};P.prototype={init:function(a,b){this.element=b==="span"?Y(b):y.createElementNS(xa,b);this.renderer=a},opacity:1,animate:function(a,b,c){b=m(b,va,!0);bb(this);if(b){b=w(b,{});if(c)b.complete=c;kb(this,a,b)}else this.attr(a),c&&c()},colorGradient:function(a,b,c){var d=this.renderer,e,f,g,h,i,j,k,l,o,n,s=[];a.linearGradient?
-f="linearGradient":a.radialGradient&&(f="radialGradient");if(f){g=a[f];h=d.gradients;j=a.stops;o=c.radialReference;La(g)&&(a[f]=g={x1:g[0],y1:g[1],x2:g[2],y2:g[3],gradientUnits:"userSpaceOnUse"});f==="radialGradient"&&o&&!r(g.gradientUnits)&&(g=w(g,{cx:o[0]-o[2]/2+g.cx*o[2],cy:o[1]-o[2]/2+g.cy*o[2],r:g.r*o[2],gradientUnits:"userSpaceOnUse"}));for(n in g)n!=="id"&&s.push(n,g[n]);for(n in j)s.push(j[n]);s=s.join(",");h[s]?a=h[s].attr("id"):(g.id=a="highcharts-"+tb++,h[s]=i=d.createElement(f).attr(g).add(d.defs),
-i.stops=[],p(j,function(a){a[1].indexOf("rgba")===0?(e=ya(a[1]),k=e.get("rgb"),l=e.get("a")):(k=a[1],l=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":l}).add(i);i.stops.push(a)}));c.setAttribute(b,"url("+d.url+"#"+a+")")}},attr:function(a,b){var c,d,e=this.element,f,g=this,h;typeof a==="string"&&b!==t&&(c=a,a={},a[c]=b);if(typeof a==="string")g=(this[a+"Getter"]||this._defaultGetter).call(this,a,e);else{for(c in a){d=a[c];h=!1;this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&&
-(f||(this.symbolAttr(a),f=!0),h=!0);if(this.rotation&&(c==="x"||c==="y"))this.doTransform=!0;h||(this[c+"Setter"]||this._defaultSetter).call(this,d,c,e);this.shadows&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(c)&&this.updateShadows(c,d)}if(this.doTransform)this.updateTransform(),this.doTransform=!1}return g},updateShadows:function(a,b){for(var c=this.shadows,d=c.length;d--;)c[d].setAttribute(a,a==="height"?v(b-(c[d].cutHeight||0),0):a==="d"?this.d:b)},addClass:function(a){var b=this.element,
-c=H(b,"class")||"";c.indexOf(a)===-1&&H(b,"class",c+" "+a);return this},symbolAttr:function(a){var b=this;p("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=m(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+a.id+")":Q)},crisp:function(a){var b,c={},d,e=a.strokeWidth||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;d=u(e)%2/2;a.x=T(a.x||this.x||
-0)+d;a.y=T(a.y||this.y||0)+d;a.width=T((a.width||this.width||0)-2*d);a.height=T((a.height||this.height||0)-2*d);a.strokeWidth=e;for(b in a)this[b]!==a[b]&&(this[b]=c[b]=a[b]);return c},css:function(a){var b=this.styles,c={},d=this.element,e,f,g="";e=!b;if(a&&a.color)a.fill=a.color;if(b)for(f in a)a[f]!==b[f]&&(c[f]=a[f],e=!0);if(e){e=this.textWidth=a&&a.width&&d.nodeName.toLowerCase()==="text"&&z(a.width);b&&(a=q(b,c));this.styles=a;e&&(fa||!aa&&this.renderer.forExport)&&delete a.width;if(Aa&&!aa)G(this.element,
-a);else{b=function(a,b){return"-"+b.toLowerCase()};for(f in a)g+=f.replace(/([A-Z])/g,b)+":"+a[f]+";";H(d,"style",g)}e&&this.added&&this.renderer.buildText(this)}return this},on:function(a,b){var c=this,d=c.element;$a&&a==="click"?(d.ontouchstart=function(a){c.touchEventFired=Date.now();a.preventDefault();b.call(d,a)},d.onclick=function(a){(wa.indexOf("Android")===-1||Date.now()-(c.touchEventFired||0)>1100)&&b.call(d,a)}):d["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=
-a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation,g=this.element;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(g.getAttribute("x")||0)+" "+(g.getAttribute("y")||0)+")");
-(r(c)||r(d))&&a.push("scale("+m(c,1)+" "+m(d,1)+")");a.length&&g.setAttribute("transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||Fa(c))this.alignTo=d=c||"renderer",ja(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=m(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||
-0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=u(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=u(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d,e=this.rotation;c=this.element;var f=this.styles,g=e*Ca;d=this.textStr;var h;if(d===""||Ob.test(d))h="num."+d.toString().length+
-(f?"|"+f.fontSize+"|"+f.fontFamily:"");h&&(a=b.cache[h]);if(!a){if(c.namespaceURI===xa||b.forExport){try{a=c.getBBox?q({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(i){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){c=a.width;d=a.height;if(Aa&&f&&f.fontSize==="11px"&&d.toPrecision(3)==="16.9")a.height=d=14;if(e)a.width=M(d*ea(g))+M(c*Z(g)),a.height=M(d*Z(g))+M(c*ea(g))}this.bBox=a;h&&(b.cache[h]=a)}return a},show:function(a){return a&&this.element.namespaceURI===
-xa?(this.element.removeAttribute("visibility"),this):this.attr({visibility:a?"inherit":"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=this.element,f=this.zIndex,g,h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(f)c.handleZ=!0,f=z(f);if(c.handleZ){a=d.childNodes;
-for(g=0;g<a.length;g++)if(b=a[g],c=H(b,"zIndex"),b!==e&&(z(c)>f||!r(f)&&r(c))){d.insertBefore(e,b);h=!0;break}}h||d.appendChild(e);this.added=!0;if(this.onAdd)this.onAdd();return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.renderer.isSVG&&b.nodeName==="SPAN"&&a.parentGroup,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;bb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=
-0;f<a.stops.length;f++)a.stops[f]=a.stops[f].destroy();a.stops=null}a.safeRemoveChild(b);for(c&&p(c,function(b){a.safeRemoveChild(b)});d&&d.div.childNodes.length===0;)b=d.parentGroup,a.safeRemoveChild(d.div),delete d.div,d=b;a.alignTo&&ja(a.renderer.alignedObjects,a);for(e in a)delete a[e];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=m(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted?"(-1,-1)":"("+m(a.offsetX,1)+", "+m(a.offsetY,1)+")";for(e=1;e<=i;e++){f=
-g.cloneNode(0);h=i*2+1-2*e;H(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:Q});if(c)H(f,"height",v(H(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this},xGetter:function(a){this.element.nodeName==="circle"&&(a={x:"cx",y:"cy"}[a]||a);return this._defaultGetter(a)},_defaultGetter:function(a){a=m(this[a],this.element?this.element.getAttribute(a):null,0);/^[0-9\.]+$/.test(a)&&
-(a=parseFloat(a));return a},dSetter:function(a,b,c){a&&a.join&&(a=a.join(" "));/(NaN| {2}|^$)/.test(a)&&(a="M 0 0");c.setAttribute(b,a);this[b]=a},dashstyleSetter:function(a){var b;if(a=a&&a.toLowerCase()){a=a.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(b=a.length;b--;)a[b]=z(a[b])*this.element.getAttribute("stroke-width");
-a=a.join(",");this.element.setAttribute("stroke-dasharray",a)}},alignSetter:function(a){this.element.setAttribute("text-anchor",{left:"start",center:"middle",right:"end"}[a])},opacitySetter:function(a,b,c){this[b]=a;c.setAttribute(b,a)},"stroke-widthSetter":function(a,b,c){a===0&&(a=1.0E-5);this.strokeWidth=a;c.setAttribute(b,a)},titleSetter:function(a){var b=this.element.getElementsByTagName("title")[0];b||(b=y.createElementNS(xa,"title"),this.element.appendChild(b));b.textContent=a},textSetter:function(a){if(a!==
-this.textStr)delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this)},fillSetter:function(a,b,c){typeof a==="string"?c.setAttribute(b,a):a&&this.colorGradient(a,b,c)},zIndexSetter:function(a,b,c){c.setAttribute(b,a);this[b]=a},_defaultSetter:function(a,b,c){c.setAttribute(b,a)}};P.prototype.yGetter=P.prototype.xGetter;P.prototype.translateXSetter=P.prototype.translateYSetter=P.prototype.rotationSetter=P.prototype.verticalAlignSetter=P.prototype.scaleXSetter=P.prototype.scaleYSetter=
-function(a,b){this[b]=a;this.doTransform=!0};P.prototype.strokeSetter=P.prototype.fillSetter;var ta=function(){this.init.apply(this,arguments)};ta.prototype={Element:P,init:function(a,b,c,d,e){var f=location,g,d=this.createElement("svg").attr({version:"1.1"}).css(this.getStyle(d));g=d.element;a.appendChild(g);a.innerHTML.indexOf("xmlns")===-1&&H(g,"xmlns",xa);this.isSVG=!0;this.box=g;this.boxWrapper=d;this.alignedObjects=[];this.url=(Ta||ib)&&y.getElementsByTagName("base").length?f.href.replace(/#.*?$/,
-"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(y.createTextNode("Created with Highcharts 4.0.1"));this.defs=this.createElement("defs").add();this.forExport=e;this.gradients={};this.cache={};this.setSize(b,c,!1);var h;if(Ta&&a.getBoundingClientRect)this.subPixelFix=b=function(){G(a,{left:0,top:0});h=a.getBoundingClientRect();G(a,{left:Ka(h.left)-h.left+"px",top:Ka(h.top)-h.top+"px"})},b(),K(I,"resize",b)},getStyle:function(a){return this.style=
-q({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Oa(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&W(I,"resize",this.subPixelFix);return this.alignedObjects=null},createElement:function(a){var b=new this.Element;b.init(this,a);return b},draw:function(){},
-buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=m(a.textStr,"").toString(),f=e.indexOf("<")!==-1,g=b.childNodes,h,i,j=H(b,"x"),k=a.styles,l=a.textWidth,o=k&&k.lineHeight,n=g.length,s=function(a){return o?z(o):c.fontMetrics(/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:k&&k.fontSize||c.style.fontSize||12).h};n--;)b.removeChild(g[n]);!f&&e.indexOf(" ")===-1?b.appendChild(y.createTextNode(e)):(h=/<.*style="([^"]+)".*>/,i=/<.*href="(http[^"]+)".*>/,l&&!a.added&&this.box.appendChild(b),
-e=f?e.replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g):[e],e[e.length-1]===""&&e.pop(),p(e,function(e,f){var g,n=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");g=e.split("|||");p(g,function(e){if(e!==""||g.length===1){var o={},m=y.createElementNS(xa,"tspan"),p;h.test(e)&&(p=e.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),
-H(m,"style",p));i.test(e)&&!d&&(H(m,"onclick",'location.href="'+e.match(i)[1]+'"'),G(m,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/&lt;/g,"<").replace(/&gt;/g,">");if(e!==" "){m.appendChild(y.createTextNode(e));if(n)o.dx=0;else if(f&&j!==null)o.x=j;H(m,o);!n&&f&&(!aa&&d&&G(m,{display:"block"}),H(m,"dy",s(m),ib&&m.offsetHeight));b.appendChild(m);n++;if(l)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),o=e.length>1&&k.whiteSpace!=="nowrap",$,r,B=a._clipHeight,q=[],v=s(),t=
-1;o&&(e.length||q.length);)delete a.bBox,$=a.getBBox(),r=$.width,!aa&&c.forExport&&(r=c.measureSpanWidth(m.firstChild.data,a.styles)),$=r>l,!$||e.length===1?(e=q,q=[],e.length&&(t++,B&&t*v>B?(e=["..."],a.attr("title",a.textStr)):(m=y.createElementNS(xa,"tspan"),H(m,{dy:v,x:j}),p&&H(m,"style",p),b.appendChild(m),r>l&&(l=r)))):(m.removeChild(m.firstChild),q.unshift(e.pop())),e.length&&m.appendChild(y.createTextNode(e.join(" ").replace(/- /g,"-")))}}})}))},button:function(a,b,c,d,e,f,g,h,i){var j=this.label(a,
-b,c,i,null,null,null,null,"button"),k=0,l,o,n,s,m,p,a={x1:0,y1:0,x2:0,y2:1},e=w({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);n=e.style;delete e.style;f=w(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);s=f.style;delete f.style;g=w(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);m=g.style;delete g.style;h=w(e,{style:{color:"#CCC"}},h);p=h.style;delete h.style;
-K(j.element,Aa?"mouseover":"mouseenter",function(){k!==3&&j.attr(f).css(s)});K(j.element,Aa?"mouseout":"mouseleave",function(){k!==3&&(l=[e,f,g][k],o=[n,s,m][k],j.attr(l).css(o))});j.setState=function(a){(j.state=k=a)?a===2?j.attr(g).css(m):a===3&&j.attr(h).css(p):j.attr(e).css(n)};return j.on("click",function(){k!==3&&d.call(j)}).attr(e).css(q({cursor:"default"},n))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=u(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=u(a[2])+b%2/2);return a},path:function(a){var b=
-{fill:Q};La(a)?b.d=a:ca(a)&&q(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=ca(a)?a:{x:a,y:b,r:c};b=this.createElement("circle");b.xSetter=function(a){this.element.setAttribute("cx",a)};b.ySetter=function(a){this.element.setAttribute("cy",a)};return b.attr(a)},arc:function(a,b,c,d,e,f){if(ca(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;a=this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0});a.r=c;return a},rect:function(a,b,c,d,e,f){var e=ca(a)?a.r:
-e,g=this.createElement("rect"),a=ca(a)?a:a===t?{}:{x:a,y:b,width:v(c,0),height:v(d,0)};if(f!==t)a.strokeWidth=f,a=g.crisp(a);if(e)a.r=e;g.rSetter=function(a){H(this.element,{rx:a,ry:a})};return g.attr(a)},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[m(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return r(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f=
-{preserveAspectRatio:Q};arguments.length>1&&q(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(u(b),u(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),q(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&q(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),
-a.alignByTranslate||a.translate(u((d-b[0])/2),u((e-b[1])/2)))},j=a.match(i)[1],a=Kb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),Y("img",{onload:function(){k(g,Kb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",
-a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=Z(f),j=ea(f),k=Z(g),g=ea(g),e=e.end-f<ma?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e,1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]},callout:function(a,b,c,d,e){var f=C(e&&e.r||0,c,d),g=f+6,h=e&&e.anchorX,i=e&&e.anchorY,
-e=u(e.strokeWidth||0)%2/2;a+=e;b+=e;e=["M",a+f,b,"L",a+c-f,b,"C",a+c,b,a+c,b,a+c,b+f,"L",a+c,b+d-f,"C",a+c,b+d,a+c,b+d,a+c-f,b+d,"L",a+f,b+d,"C",a,b+d,a,b+d,a,b+d-f,"L",a,b+f,"C",a,b,a,b,a+f,b];h&&h>c&&i>b+g&&i<b+d-g?e.splice(13,3,"L",a+c,i-6,a+c+6,i,a+c,i+6,a+c,b+d-f):h&&h<0&&i>b+g&&i<b+d-g?e.splice(33,3,"L",a,i+6,a-6,i,a,i-6,a,b+f):i&&i>d&&h>a+g&&h<a+c-g?e.splice(23,3,"L",h+6,b+d,h,b+d+6,h-6,b+d,a+f,b+d):i&&i<0&&h>a+g&&h<a+c-g&&e.splice(3,3,"L",h-6,b,h,b-6,h+6,b,c-f,b);return e}},clipRect:function(a,
-b,c,d){var e="highcharts-"+tb++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},text:function(a,b,c,d){var e=fa||!aa&&this.forExport,f={};if(d&&!this.forExport)return this.html(a,b,c);f.x=Math.round(b||0);if(c)f.y=Math.round(c);if(a||a===0)f.text=a;a=this.createElement("text").attr(f);e&&a.css({position:"absolute"});if(!d)a.xSetter=function(a,b,c){var d=c.childNodes,e,f;for(f=1;f<d.length;f++)e=d[f],e.getAttribute("x")===c.getAttribute("x")&&
-e.setAttribute("x",a);c.setAttribute(b,a)};return a},fontMetrics:function(a){var a=a||this.style.fontSize,a=/px/.test(a)?z(a):/em/.test(a)?parseFloat(a)*12:12,a=a<24?a+4:u(a*1.2),b=u(a*0.8);return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=s.element.style;J=(Va===void 0||wb===void 0||n.styles.textAlign)&&s.textStr&&s.getBBox();n.width=(Va||J.width||0)+2*x+v;n.height=(wb||J.height||0)+2*x;na=x+o.fontMetrics(a&&a.fontSize).b;if(z){if(!m)a=u(-L*x),b=h?-na:0,n.box=m=d?o.symbol(d,
-a,b,n.width,n.height,B):o.rect(a,b,n.width,n.height,0,B[Pb]),m.attr("fill",Q).add(n);m.isImg||m.attr(q({width:u(n.width),height:u(n.height)},B));B=null}}function k(){var a=n.styles,a=a&&a.textAlign,b=v+x*(1-L),c;c=h?0:na;if(r(Va)&&J&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Va-J.width);if(b!==s.x||c!==s.y)s.attr("x",b),c!==t&&s.attr("y",c);s.x=b;s.y=c}function l(a,b){m?m.attr(a,b):B[a]=b}var o=this,n=o.g(i),s=o.text("",0,0,g).attr({zIndex:1}),m,J,L=0,x=3,v=0,Va,wb,xb,yb,y=0,B={},na,
-z;n.onAdd=function(){s.add(n);n.attr({text:a||"",x:b,y:c});m&&r(e)&&n.attr({anchorX:e,anchorY:f})};n.widthSetter=function(a){Va=a};n.heightSetter=function(a){wb=a};n.paddingSetter=function(a){r(a)&&a!==x&&(x=a,k())};n.paddingLeftSetter=function(a){r(a)&&a!==v&&(v=a,k())};n.alignSetter=function(a){L={left:0,center:0.5,right:1}[a]};n.textSetter=function(a){a!==t&&s.textSetter(a);j();k()};n["stroke-widthSetter"]=function(a,b){a&&(z=!0);y=a%2/2;l(b,a)};n.strokeSetter=n.fillSetter=n.rSetter=function(a,
-b){b==="fill"&&a&&(z=!0);l(b,a)};n.anchorXSetter=function(a,b){e=a;l(b,a+y-xb)};n.anchorYSetter=function(a,b){f=a;l(b,a-yb)};n.xSetter=function(a){n.x=a;L&&(a-=L*((Va||J.width)+x));xb=u(a);n.attr("translateX",xb)};n.ySetter=function(a){yb=n.y=u(a);n.attr("translateY",yb)};var A=n.css;return q(n,{css:function(a){if(a){var b={},a=w(a);p("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration,textShadow".split(","),function(c){a[c]!==t&&(b[c]=a[c],delete a[c])});s.css(b)}return A.call(n,
-a)},getBBox:function(){return{width:J.width+2*x,height:J.height+2*x,x:J.x-x,y:J.y-x}},shadow:function(a){m&&m.shadow(a);return n},destroy:function(){W(n.element,"mouseenter");W(n.element,"mouseleave");s&&(s=s.destroy());m&&(m=m.destroy());P.prototype.destroy.call(n);n=o=j=k=l=null}})}};Za=ta;q(P.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=q(this.styles,a);G(this.element,a);return this},htmlGetBBox:function(){var a=
-this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=this.shadows;G(b,{marginLeft:c,marginTop:d});i&&p(i,function(a){G(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&
-p(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var j=this.rotation,k,l=z(this.textWidth),o=[j,g,b.innerHTML,this.textWidth].join(",");if(o!==this.cTT){k=a.fontMetrics(b.style.fontSize).b;r(j)&&this.setSpanRotation(j,h,k);i=m(this.elemWidth,b.offsetWidth);if(i>l&&/[ \-]/.test(b.textContent||b.innerText))G(b,{width:l+"px",display:"block",whiteSpace:"normal"}),i=l;this.getSpanCorrection(i,k,h,j,g)}G(b,{left:e+(this.xCorr||0)+"px",top:f+(this.yCorr||0)+"px"});if(ib)k=b.offsetHeight;
-this.cTT=o}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,c){var d={},e=Aa?"-ms-transform":ib?"-webkit-transform":Ta?"MozTransform":Ib?"-o-transform":"";d[e]=d.transform="rotate("+a+"deg)";d[e+(Ta?"Origin":"-origin")]=d.transformOrigin=b*100+"% "+c+"px";G(this.element,d)},getSpanCorrection:function(a,b,c){this.xCorr=-a*c;this.yCorr=-b}});q(ta.prototype,{html:function(a,b,c){var d=this.createElement("span"),e=d.element,f=d.renderer;d.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;
-e.innerHTML=this.textStr=a};d.xSetter=d.ySetter=d.alignSetter=d.rotationSetter=function(a,b){b==="align"&&(b="textAlign");d[b]=a;d.htmlUpdateTransform()};d.attr({text:a,x:u(b),y:u(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:this.style.fontFamily,fontSize:this.style.fontSize});d.css=d.htmlCss;if(f.isSVG)d.add=function(a){var b,c=f.box.parentNode,j=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)j.push(a),a=a.parentGroup;p(j.reverse(),function(a){var d;b=a.div=a.div||Y(Ja,{className:H(a.element,
-"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;q(a,{translateXSetter:function(b,c){d.left=b+"px";a[c]=b;a.doTransform=!0},translateYSetter:function(b,c){d.top=b+"px";a[c]=b;a.doTransform=!0},visibilitySetter:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(e);d.added=!0;d.alignOnAdd&&d.htmlUpdateTransform();return d};return d}});var X;if(!aa&&!fa){R.VMLElement=X={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",
-";"],e=b===Ja;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=Y(c);this.renderer=a},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();if(this.onAdd)this.onAdd();return this},updateTransform:P.prototype.htmlUpdateTransform,
-setSpanRotation:function(){var a=this.rotation,b=Z(a*Ca),c=ea(a*Ca);G(this.element,{filter:a?["progid:DXImageTransform.Microsoft.Matrix(M11=",b,", M12=",-c,", M21=",c,", M22=",b,", sizingMethod='auto expand')"].join(""):Q})},getSpanCorrection:function(a,b,c,d,e){var f=d?Z(d*Ca):1,g=d?ea(d*Ca):0,h=m(this.elemHeight,this.element.offsetHeight),i;this.xCorr=f<0&&-a;this.yCorr=g<0&&-h;i=f*g<0;this.xCorr+=g*b*(i?1-c:c);this.yCorr-=f*b*(d?i?c:1-c:1);e&&e!=="left"&&(this.xCorr-=a*c*(f<0?-1:1),d&&(this.yCorr-=
-h*c*(g<0?-1:1)),G(this.element,{textAlign:e}))},pathToVML:function(a){for(var b=a.length,c=[];b--;)if(ha(a[b]))c[b]=u(a[b]*10)-5;else if(a[b]==="Z")c[b]="x";else if(c[b]=a[b],a.isArc&&(a[b]==="wa"||a[b]==="at"))c[b+5]===c[b+7]&&(c[b+7]+=a[b+7]>a[b+5]?1:-1),c[b+6]===c[b+8]&&(c[b+8]+=a[b+8]>a[b+6]?1:-1);return c.join(" ")||"x"},clip:function(a){var b=this,c;a?(c=a.members,ja(c,b),c.push(b),b.destroyClip=function(){ja(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:hb?"inherit":"rect(auto)"});
-return b.css(a)},css:P.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Pa(a)},destroy:function(){this.destroyClip&&this.destroyClip();return P.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=I.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=z(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,
-l,o,n,s;k&&typeof k.value!=="string"&&(k="x");o=k;if(a){n=m(a.width,3);s=(a.opacity||0.15)/n;for(e=1;e<=3;e++){l=n*2+1-2*e;c&&(o=this.cutOffPath(k.value,l+0.5));j=['<shape isShadow="true" strokeweight="',l,'" filled="false" path="',o,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=Y(g.prepVML(j),null,{left:z(i.left)+m(a.offsetX,1),top:z(i.top)+m(a.offsetY,1)});if(c)h.cutOff=l+1;j=['<stroke color="',a.color||"black",'" opacity="',s*e,'"/>'];Y(g.prepVML(j),null,null,h);b?b.element.appendChild(h):
-f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this},updateShadows:sa,setAttr:function(a,b){hb?this.element[a]=b:this.element.setAttribute(a,b)},classSetter:function(a){this.element.className=a},dashstyleSetter:function(a,b,c){(c.getElementsByTagName("stroke")[0]||Y(this.renderer.prepVML(["<stroke/>"]),null,null,c))[b]=a||"solid";this[b]=a},dSetter:function(a,b,c){var d=this.shadows,a=a||[];this.d=a.join(" ");c.path=a=this.pathToVML(a);if(d)for(c=d.length;c--;)d[c].path=d[c].cutOff?
-this.cutOffPath(a,d[c].cutOff):a;this.setAttr(b,a)},fillSetter:function(a,b,c){var d=c.nodeName;if(d==="SPAN")c.style.color=a;else if(d!=="IMG")c.filled=a!==Q,this.setAttr("fillcolor",this.renderer.color(a,c,b,this))},opacitySetter:sa,rotationSetter:function(a,b,c){c=c.style;this[b]=c[b]=a;c.left=-u(ea(a*Ca)+1)+"px";c.top=u(Z(a*Ca))+"px"},strokeSetter:function(a,b,c){this.setAttr("strokecolor",this.renderer.color(a,c,b))},"stroke-widthSetter":function(a,b,c){c.stroked=!!a;this[b]=a;ha(a)&&(a+="px");
-this.setAttr("strokeweight",a)},titleSetter:function(a,b){this.setAttr(b,a)},visibilitySetter:function(a,b,c){a==="inherit"&&(a="visible");this.shadows&&p(this.shadows,function(c){c.style[b]=a});c.nodeName==="DIV"&&(a=a==="hidden"?"-999em":0,hb||(c.style[b]=a?"visible":"hidden"),b="top");c.style[b]=a},xSetter:function(a,b,c){this[b]=a;b==="x"?b="left":b==="y"&&(b="top");this.updateClipping?(this[b]=a,this.updateClipping()):c.style[b]=a},zIndexSetter:function(a,b,c){c.style[b]=a}};X=ka(P,X);X.prototype.ySetter=
-X.prototype.widthSetter=X.prototype.heightSetter=X.prototype.xSetter;var ga={Element:X,isIE8:wa.indexOf("MSIE 8.0")>-1,init:function(a,b,c,d){var e;this.alignedObjects=[];d=this.createElement(Ja).css(q(this.getStyle(d),{position:"relative"}));e=d.element;a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.cache={};this.setSize(b,c,!1);if(!y.namespaces.hcv){y.namespaces.add("hcv","urn:schemas-microsoft-com:vml");try{y.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}catch(f){y.styleSheets[0].cssText+=
-"hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}}},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=ca(a);return q(e,{members:[],left:(f?a.x:a)+1,top:(f?a.y:b)+1,width:(f?a.width:c)-1,height:(f?a.height:d)-1,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+u(a?e:d)+"px,"+u(a?
-f:b)+"px,"+u(a?b:f)+"px,"+u(a?d:e)+"px)"};!a&&hb&&c==="DIV"&&q(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){p(e.members,function(a){a.element&&a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=Q;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,l,o=a.linearGradient||a.radialGradient,n,s,m,J,L,x="",a=a.stops,r,v=[],q=function(){h=['<fill colors="'+v.join(",")+'" opacity="',m,'" o:opacity2="',s,'" type="',i,'" ',x,'focus="100%" method="any" />'];
-Y(e.prepVML(h),null,null,b)};n=a[0];r=a[a.length-1];n[0]>0&&a.unshift([0,n[1]]);r[0]<1&&a.push([1,r[1]]);p(a,function(a,b){g.test(a[1])?(f=ya(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);v.push(a[0]*100+"% "+k);b?(m=l,J=k):(s=l,L=k)});if(c==="fill")if(i==="gradient")c=o.x1||o[0]||0,a=o.y1||o[1]||0,n=o.x2||o[2]||0,o=o.y2||o[3]||0,x='angle="'+(90-U.atan((o-a)/(n-c))*180/ma)+'"',q();else{var j=o.r,t=j*2,u=j*2,y=o.cx,B=o.cy,na=b.radialReference,w,j=function(){na&&(w=d.getBBox(),y+=(na[0]-w.x)/w.width-
-0.5,B+=(na[1]-w.y)/w.height-0.5,t*=na[2]/w.width,u*=na[2]/w.height);x='src="'+E.global.VMLRadialGradientURL+'" size="'+t+","+u+'" origin="0.5,0.5" position="'+y+","+B+'" color2="'+L+'" ';q()};d.added?j():d.onAdd=j;j=J}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ya(a),h=["<",c,' opacity="',f.get("a"),'"/>'],Y(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?
-(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:ta.prototype.html,path:function(a){var b={coordsize:"10 10"};La(a)?b.d=a:ca(a)&&q(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(ca(a))c=a.r,b=a.y,a=a.x;d.isCircle=
-!0;d.r=c;return d.attr({x:a,y:b})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(Ja).attr(b)},image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},createElement:function(a){return a==="rect"?this.symbol(a):ta.prototype.createElement.call(this,a)},invertChild:function(a,b){var c=this,d=b.style,e=a.tagName==="IMG"&&a.style;G(a,{flip:"x",left:z(d.width)-(e?z(e.top):
-1),top:z(d.height)-(e?z(e.left):1),rotation:-90});p(a.childNodes,function(b){c.invertChild(b,a)})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=Z(f),i=ea(f),j=Z(g),k=ea(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&(c=d=2*e.r);e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+
-c,b+d/2,"e"]},rect:function(a,b,c,d,e){return ta.prototype.symbols[!r(e)||!e.r?"square":"callout"].call(0,a,b,c,d,e)}}};R.VMLRenderer=X=function(){this.init.apply(this,arguments)};X.prototype=w(ta.prototype,ga);Za=X}ta.prototype.measureSpanWidth=function(a,b){var c=y.createElement("span"),d;d=y.createTextNode(a);c.appendChild(d);G(c,b);this.box.appendChild(c);d=c.offsetWidth;Pa(c);return d};var Lb;if(fa)R.CanVGRenderer=X=function(){xa="http://www.w3.org/1999/xhtml"},X.prototype.symbols={},Lb=function(){function a(){var a=
-b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Qb(d,a);b.push(c)}}}(),Za=X;Sa.prototype={addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.margin[3]||c.chartWidth*0.33),j=g===i[0],k=g===i[i.length-1],l,f=e?m(e[g],f[g],g):g,e=this.label,o=i.info;a.isDatetimeAxis&&o&&(l=b.dateTimeLabelFormats[o.higherRanks[g]||
-o.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,isLast:k,dateTimeLabelFormat:l,value:a.isLog?da(ia(f)):f});g=d&&{width:v(1,u(d-2*(h.padding||10)))+"px"};g=q(g,h.style);if(r(e))e&&e.attr({text:b}).css(g);else{l={align:a.labelAlign};if(ha(h.rotation))l.rotation=h.rotation;if(d&&h.ellipsis)l._clipHeight=a.len/i.length;this.label=r(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(l).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,
-b=this.axis;return a?a.getBBox()[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.label.getBBox(),b=this.axis,c=b.horiz,d=b.options.labels,a=c?a.width:a.height,b=c?d.x-a*{left:0,center:0.5,right:1}[b.labelAlign]:0;return[b,c?a+b:a]},handleOverflow:function(a,b){var c=!0,d=this.axis,e=this.isFirst,f=this.isLast,g=d.horiz?b.x:b.y,h=d.reversed,i=d.tickPositions,j=this.getLabelSides(),k=j[0],j=j[1],l,o,n,s=this.label.line||0;l=d.labelEdge;o=d.justifyLabels&&(e||f);l[s]===t||g+k>l[s]?l[s]=
-g+j:o||(c=!1);if(o){l=(o=d.justifyToPlot)?d.pos:0;o=o?l+d.len:d.chart.chartWidth;do a+=e?1:-1,n=d.ticks[i[a]];while(i[a]&&(!n||n.label.line!==s));d=n&&n.label.xy&&n.label.xy.x+n.getLabelSides()[e?0:1];e&&!h||f&&h?g+k<l&&(g=l-k,n&&g+j>d&&(c=!1)):g+j>o&&(g=o-j,n&&g+k<d&&(c=!1));b.x=g}return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-
-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,l=i.staggerLines,o=i.chart.renderer.fontMetrics(e.style.fontSize).b,n=e.rotation,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);n&&i.side===2&&(b-=o-o*Z(n*Ca));!r(e.y)&&!n&&(b+=o-c.getBBox().height/2);if(l)c.line=g/(h||1)%l,b+=c.line*(i.labelOffset/l);return{x:a,y:b}},getMarkPath:function(a,b,c,d,e,f){return f.crispLine(["M",
-a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,l=this.gridLine,o=h?h+"Grid":"grid",n=h?h+"Tick":"tick",s=e[o+"LineWidth"],p=e[o+"LineColor"],J=e[o+"LineDashStyle"],L=e[n+"Length"],o=e[n+"Width"]||0,x=e[n+"Color"],r=e[n+"Position"],n=this.mark,v=k.step,q=!0,u=d.tickmarkOffset,w=this.getPosition(g,j,u,b),y=w.x,w=w.y,B=g&&y===d.pos+d.len||!g&&w===d.pos?-1:1;this.isActive=!0;if(s){j=d.getPlotLinePath(j+
-u,s*B,b,!0);if(l===t){l={stroke:p,"stroke-width":s};if(J)l.dashstyle=J;if(!h)l.zIndex=1;if(b)l.opacity=0;this.gridLine=l=s?f.path(j).attr(l).add(d.gridGroup):null}if(!b&&l&&j)l[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(o&&L)r==="inside"&&(L=-L),d.opposite&&(L=-L),h=this.getMarkPath(y,w,L,o*B,g,f),n?n.animate({d:h,opacity:c}):this.mark=f.path(h).attr({stroke:x,"stroke-width":o,opacity:c}).add(d.axisGroup);if(i&&!isNaN(y))i.xy=w=this.getLabelPosition(y,w,i,g,k,u,a,v),this.isFirst&&!this.isLast&&
-!m(e.showFirstLabel,1)||this.isLast&&!this.isFirst&&!m(e.showLastLabel,1)?q=!1:!d.isRadial&&!k.step&&!k.rotation&&!b&&c!==0&&(q=this.handleOverflow(a,w)),v&&a%v&&(q=!1),q&&!isNaN(w.y)?(w.opacity=c,i[this.isNew?"attr":"animate"](w),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Oa(this,this.axis)}};R.PlotLineOrBand=function(a,b){this.axis=a;if(b)this.options=b,this.id=b.id};R.PlotLineOrBand.prototype={render:function(){var a=this,b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,
-g=a.label,h=e.width,i=e.to,j=e.from,k=r(j)&&r(i),l=e.value,o=e.dashStyle,n=a.svgElem,s=[],p,J=e.color,L=e.zIndex,x=e.events,q={},t=b.chart.renderer;b.isLog&&(j=za(j),i=za(i),l=za(l));if(h){if(s=b.getPlotLinePath(l,h),q={stroke:J,"stroke-width":h},o)q.dashstyle=o}else if(k){j=v(j,b.min-d);i=C(i,b.max+d);s=b.getPlotBandPath(j,i,e);if(J)q.fill=J;if(e.borderWidth)q.stroke=e.borderColor,q["stroke-width"]=e.borderWidth}else return;if(r(L))q.zIndex=L;if(n)if(s)n.animate({d:s},null,n.onGetPath);else{if(n.hide(),
-n.onGetPath=function(){n.show()},g)a.label=g=g.destroy()}else if(s&&s.length&&(a.svgElem=n=t.path(s).attr(q).add(),x))for(p in d=function(b){n.on(b,function(c){x[b].apply(a,[c])})},x)d(p);if(f&&r(f.text)&&s&&s.length&&b.width>0&&b.height>0){f=w({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g){q={align:f.textAlign||f.align,rotation:f.rotation};if(r(L))q.zIndex=L;a.label=g=t.text(f.text,0,0,f.useHTML).attr(q).css(f.style).add()}b=[s[1],
-s[4],m(s[6],s[1])];s=[s[2],s[5],m(s[7],s[2])];c=Na(b);k=Na(s);g.align(f,!1,{x:c,y:k,width:Ba(b)-c,height:Ba(s)-k});g.show()}else g&&g.hide();return a},destroy:function(){ja(this.axis.plotLinesAndBands,this);delete this.axis;Oa(this)}};la.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:N,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,
-maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#707070"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,
-tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return Ga(this.total,-1)},style:N.style}},defaultLeftAxisOptions:{labels:{x:-15,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{x:0,y:20},title:{rotation:0}},defaultTopAxisOptions:{labels:{x:0,y:-15},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.coll=(this.isXAxis=c)?"xAxis":"yAxis";this.opposite=
-b.opposite;this.side=b.side||(this.horiz?this.opposite?0:2:this.opposite?1:3);this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.names=[];this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=r(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement===
-"between"?0.5:0;this.ticks={};this.labelEdge=[];this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.oldStacks={};this.min=this.max=null;this.crosshair=m(d.crosshair,qa(a.options.tooltip.crosshairs)[c?0:1],!1);var f,d=this.options.events;Da(this,a.axes)===-1&&(c&&!this.isColorAxis?a.axes.splice(a.xAxis.length,0,this):a.axes.push(this),a[this.coll].push(this));
-this.series=this.series||[];if(a.inverted&&c&&this.reversed===t)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)K(this,f,d[f]);if(this.isLog)this.val2lin=za,this.lin2val=ia},setOptions:function(a){this.options=w(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],w(E[this.coll],a))},defaultLabelFormatter:function(){var a=
-this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=E.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=Ia(h,this);else if(c)g=b;else if(d)g=cb(d,b);else if(f&&a>=1E3)for(;f--&&g===t;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=Ga(b/c,-1)+e[f]);g===t&&(g=M(b)>=1E4?Ga(b,0):Ga(b,-1,t,""));return g},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;a.buildStacks&&a.buildStacks();p(a.series,function(c){if(c.visible||
-!b.options.chart.ignoreHiddenSeries){var d;d=c.options.threshold;var e;a.hasVisibleSeries=!0;a.isLog&&d<=0&&(d=null);if(a.isXAxis){if(d=c.xData,d.length)a.dataMin=C(m(a.dataMin,d[0]),Na(d)),a.dataMax=v(m(a.dataMax,d[0]),Ba(d))}else{c.getExtremes();e=c.dataMax;c=c.dataMin;if(r(c)&&r(e))a.dataMin=C(m(a.dataMin,c),c),a.dataMax=v(m(a.dataMax,e),e);if(r(d))if(a.dataMin>=d)a.dataMin=d,a.ignoreMinPadding=!0;else if(a.dataMax<d)a.dataMax=d,a.ignoreMaxPadding=!0}}})},translate:function(a,b,c,d,e,f){var g=
-1,h=0,i=d?this.oldTransA:this.transA,d=d?this.oldMin:this.min,j=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!i)i=this.transA;if(c)g*=-1,h=this.len;this.reversed&&(g*=-1,h-=g*(this.sector||this.len));b?(a=a*g+h,a-=j,a=a/i+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),f==="between"&&(f=0.5),a=g*(a-d)*i+h+g*j+(ha(f)?i*f*this.pointRange:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-
-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,e){var f=this.chart,g=this.left,h=this.top,i,j,k=c&&f.oldChartHeight||f.chartHeight,l=c&&f.oldChartWidth||f.chartWidth,o;i=this.transB;e=m(e,this.translate(a,null,null,c));a=c=u(e+i);i=j=u(k-e-i);if(isNaN(e))o=!0;else if(this.horiz){if(i=h,j=k-this.bottom,a<g||a>g+this.width)o=!0}else if(a=g,c=l-this.right,i<h||i>h+this.height)o=!0;return o&&!d?null:f.renderer.crispLine(["M",a,i,"L",c,j],b||1)},getLinearTickPositions:function(a,
-b,c){var d,e=da(T(b/a)*a),f=da(Ka(c/a)*a),g=[];if(b===c&&ha(b))return[b];for(b=e;b<=f;){g.push(b);b=da(b+a);if(b===d)break;d=b}return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(this.getTimeTicks(this.normalizeTimeTickInterval(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&
-d.shift();else for(b=this.min+(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===t&&!this.isLog)r(a.min)||r(a.max)?this.minRange=null:(p(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===t||h<f)f=h}),this.minRange=C(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=
-(k-c+b)/2;d=[b-d,m(a.min,b-d)];if(e)d[2]=this.dataMin;b=Ba(d);c=[b+k,m(a.max,b+k)];if(e)c[2]=this.dataMax;c=Na(c);c-b<k&&(d[0]=c-k,d[1]=m(a.min,c-k),b=Ba(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this,c=b.max-b.min,d=b.axisPointRange||0,e,f=0,g=0,h=b.linkedParent,i=!!b.categories,j=b.transA;if(b.isXAxis||i||d)h?(f=h.minPointOffset,g=h.pointRangePadding):p(b.series,function(a){var h=i?1:b.isXAxis?a.pointRange:b.axisPointRange||0,j=a.options.pointPlacement,n=a.closestPointRange;
-h>c&&(h=0);d=v(d,h);f=v(f,Fa(j)?0:h/2);g=v(g,j==="on"?0:h);!a.noSharedTooltip&&r(n)&&(e=r(e)?C(e,n):n)}),h=b.ordinalSlope&&e?b.ordinalSlope/e:1,b.minPointOffset=f*=h,b.pointRangePadding=g*=h,b.pointRange=C(d,c),b.closestPointRange=e;if(a)b.oldTransA=j;b.translationSlope=b.transA=j=b.len/(c+g||1);b.transB=b.horiz?b.left:b.bottom;b.minPixelPadding=j*f},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,
-k=d.minPadding,l=d.tickInterval,o=d.minTickInterval,n=d.tickPixelInterval,s,$=b.categories;h?(b.linkedParent=c[b.coll][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=m(c.min,c.dataMin),b.max=m(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&ra(11,1)):(b.min=m(b.userMin,d.min,b.dataMin),b.max=m(b.userMax,d.max,b.dataMax));if(e)!a&&C(b.min,m(b.dataMin,b.min))<=0&&ra(10,1),b.min=da(za(b.min)),b.max=da(za(b.max));if(b.range&&r(b.max))b.userMin=b.min=v(b.min,b.max-b.range),b.userMax=b.max,b.range=
-null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!$&&!b.axisPointRange&&!b.usePercentage&&!h&&r(b.min)&&r(b.max)&&(c=b.max-b.min)){if(!r(d.min)&&!r(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!r(d.max)&&!r(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}if(ha(d.floor))b.min=v(b.min,d.floor);if(ha(d.ceiling))b.max=C(b.max,d.ceiling);b.min===b.max||b.min===void 0||b.max===void 0?b.tickInterval=1:h&&!l&&n===b.linkedParent.options.tickPixelInterval?b.tickInterval=
-b.linkedParent.tickInterval:(b.tickInterval=m(l,$?1:(b.max-b.min)*n/v(b.len,n)),!r(l)&&b.len<n&&!this.isRadial&&!this.isLog&&!$&&d.startOnTick&&d.endOnTick&&(s=!0,b.tickInterval/=4));g&&!a&&p(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(b.pointRange)b.tickInterval=v(b.pointRange,b.tickInterval);if(!l&&b.tickInterval<
-o)b.tickInterval=o;if(!f&&!e&&!l)b.tickInterval=nb(b.tickInterval,null,mb(b.tickInterval),d);b.minorTickInterval=d.minorTickInterval==="auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=a=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!a)!b.ordinalPositions&&(b.max-b.min)/b.tickInterval>v(2*b.len,200)&&ra(19,!0),a=f?b.getTimeTicks(b.normalizeTimeTickInterval(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,
-!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),s&&a.splice(1,a.length-2),b.tickPositions=a;if(!h)e=a[0],f=a[a.length-1],h=b.minPointOffset||0,d.startOnTick?b.min=e:b.min-h>e&&a.shift(),d.endOnTick?b.max=f:b.max+h<f&&a.pop(),a.length===1&&(d=M(b.max)>1E13?1:0.001,b.min-=d,b.max+=d)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.coll,this.pos,this.len].join("-");if(!this.isLinked&&
-!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1&&this.min!==t){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(da(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(r(d)&&a!==d)this.isDirty=
-!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!==this.oldAxisLength;p(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=!0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax){if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].total=null,a[b][c].cum=0;this.forceRedraw=!1;this.getSeriesExtremes();this.setTickPositions();this.oldUserMin=
-this.userMin;this.oldUserMax=this.userMax;if(!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax}else if(!this.isXAxis){if(this.oldStacks)a=this.stacks=this.oldStacks;for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total}this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=m(c,!0),e=q(e,{min:a,max:b});D(f,"setExtremes",e,function(){f.userMin=a;f.userMax=b;f.eventArgs=e;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){var c=this.dataMin,d=this.dataMax,
-e=this.options;this.allowZoomOutside||(r(c)&&a<=C(c,m(e.min,c))&&(a=t),r(d)&&b>=v(d,m(e.max,d))&&(b=t));this.displayBtn=a!==t||b!==t;this.setExtremes(a,b,!1,t,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=this.horiz,e=m(b.width,a.plotWidth-c+(b.offsetRight||0)),f=m(b.height,a.plotHeight),g=m(b.top,a.plotTop),b=m(b.left,a.plotLeft+c),c=/%$/;c.test(f)&&(f=parseInt(f,10)/100*a.plotHeight);c.test(g)&&(g=parseInt(g,10)/100*a.plotHeight+a.plotTop);
-this.left=b;this.top=g;this.width=e;this.height=f;this.bottom=a.chartHeight-f-g;this.right=a.chartWidth-e-b;this.len=v(d?e:f,0);this.pos=d?b:g},getExtremes:function(){var a=this.isLog;return{min:a?da(ia(this.min)):this.min,max:a?da(ia(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?ia(this.min):this.min,b=b?ia(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},autoLabelAlign:function(a){a=
-(m(a,0)-this.side*90+720)%360;return a>15&&a<165?"right":a>195&&a<345?"left":"center"},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,l,o=0,n=d.title,s=d.labels,$=0,J=b.axisOffset,L=b.clipOffset,x=[-1,1,1,-1][h],q,u=1,w=m(s.maxStaggerLines,5),y,z,A,B,na=h===2?c.fontMetrics(s.style.fontSize).b:0;a.hasData=j=a.hasVisibleSeries||r(a.min)&&r(a.max)&&!!e;a.showAxis=b=j||m(d.showEmpty,!0);a.staggerLines=
-a.horiz&&s.staggerLines;if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:s.zIndex||7}).addClass("highcharts-"+a.coll.toLowerCase()+"-labels").add();if(j||a.isLinked){a.labelAlign=m(s.align||a.autoLabelAlign(s.rotation));p(e,function(b){f[b]?f[b].addLabel():f[b]=new Sa(a,b)});if(a.horiz&&!a.staggerLines&&w&&!s.rotation){for(q=a.reversed?[].concat(e).reverse():e;u<w;){j=
-[];y=!1;for(s=0;s<q.length;s++)z=q[s],A=(A=f[z].label&&f[z].label.getBBox())?A.width:0,B=s%u,A&&(z=a.translate(z),j[B]!==t&&z<j[B]&&(y=!0),j[B]=z+A);if(y)u++;else break}if(u>1)a.staggerLines=u}p(e,function(b){if(h===0||h===2||{1:"left",3:"right"}[h]===a.labelAlign)$=v(f[b].getLabelSize(),$)});if(a.staggerLines)$*=a.staggerLines,a.labelOffset=$}else for(q in f)f[q].destroy(),delete f[q];if(n&&n.text&&n.enabled!==!1){if(!a.axisTitle)a.axisTitle=c.text(n.text,0,0,n.useHTML).attr({zIndex:7,rotation:n.rotation||
-0,align:n.textAlign||{low:"left",middle:"center",high:"right"}[n.align]}).addClass("highcharts-"+this.coll.toLowerCase()+"-title").css(n.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(b)k=a.axisTitle.getBBox()[g?"height":"width"],o=m(n.margin,g?5:10),l=n.offset;a.axisTitle[b?"show":"hide"]()}a.offset=x*m(d.offset,J[h]);a.axisTitleMargin=m(l,$+o+($&&x*d.labels[g?"y":"x"]-na));J[h]=v(J[h],a.axisTitleMargin+k+x*a.offset);L[i]=v(L[i],T(d.lineWidth/2)*2)},getLinePath:function(a){var b=this.chart,c=this.opposite,
-d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d,d=b.chartHeight-this.bottom-(c?this.height:0)+d;c&&(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=z(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+d/2,high:f+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*
-this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.horiz,c=a.reversed,d=a.chart,e=d.renderer,f=a.options,g=a.isLog,h=a.isLinked,i=a.tickPositions,j,k=a.axisTitle,l=a.ticks,o=a.minorTicks,n=a.alternateBands,s=f.stackLabels,m=f.alternateGridColor,J=a.tickmarkOffset,L=f.lineWidth,x=d.hasRendered&&r(a.oldMin)&&!isNaN(a.oldMin),q=a.hasData,v=a.showAxis,u,w=f.labels.overflow,y=a.justifyLabels=b&&w!==
-!1,z;a.labelEdge.length=0;a.justifyToPlot=w==="justify";p([l,o,n],function(a){for(var b in a)a[b].isActive=!1});if(q||h)if(a.minorTickInterval&&!a.categories&&p(a.getMinorTickPositions(),function(b){o[b]||(o[b]=new Sa(a,b,"minor"));x&&o[b].isNew&&o[b].render(null,!0);o[b].render(null,!1,1)}),i.length&&(j=i.slice(),(b&&c||!b&&!c)&&j.reverse(),y&&(j=j.slice(1).concat([j[0]])),p(j,function(b,c){y&&(c=c===j.length-1?0:c+1);if(!h||b>=a.min&&b<=a.max)l[b]||(l[b]=new Sa(a,b)),x&&l[b].isNew&&l[b].render(c,
-!0,0.1),l[b].render(c,!1,1)}),J&&a.min===0&&(l[-1]||(l[-1]=new Sa(a,-1,null,!0)),l[-1].render(-1))),m&&p(i,function(b,c){if(c%2===0&&b<a.max)n[b]||(n[b]=new R.PlotLineOrBand(a)),u=b+J,z=i[c+1]!==t?i[c+1]+J:a.max,n[b].options={from:g?ia(u):u,to:g?ia(z):z,color:m},n[b].render(),n[b].isActive=!0}),!a._addedPlotLB)p((f.plotLines||[]).concat(f.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;p([l,o,n],function(a){var b,c,e=[],f=va?va.duration||500:0,g=function(){for(c=e.length;c--;)a[e[c]]&&
-!a[e[c]].isActive&&(a[e[c]].destroy(),delete a[e[c]])};for(b in a)if(!a[b].isActive)a[b].render(b,!1,0),a[b].isActive=!1,e.push(b);a===n||!d.hasRendered||!f?g():f&&setTimeout(g,f)});if(L)b=a.getLinePath(L),a.axisLine?a.axisLine.animate({d:b}):a.axisLine=e.path(b).attr({stroke:f.lineColor,"stroke-width":L,zIndex:7}).add(a.axisGroup),a.axisLine[v?"show":"hide"]();if(k&&v)k[k.isNew?"attr":"animate"](a.getTitlePosition()),k.isNew=!1;s&&s.enabled&&a.renderStackTotals();a.isDirty=!1},redraw:function(){var a=
-this.chart.pointer;a&&a.reset(!0);this.render();p(this.plotLinesAndBands,function(a){a.render()});p(this.series,function(a){a.isDirty=!0})},destroy:function(a){var b=this,c=b.stacks,d,e=b.plotLinesAndBands;a||W(b);for(d in c)Oa(c[d]),c[d]=null;p([b.ticks,b.minorTicks,b.alternateBands],function(a){Oa(a)});for(a=e.length;a--;)e[a].destroy();p("stackTotalGroup,axisLine,axisTitle,axisGroup,cross,gridGroup,labelGroup".split(","),function(a){b[a]&&(b[a]=b[a].destroy())});this.cross&&this.cross.destroy()},
-drawCrosshair:function(a,b){if(this.crosshair)if((r(b)||!m(this.crosshair.snap,!0))===!1)this.hideCrosshair();else{var c,d=this.crosshair,e=d.animation;m(d.snap,!0)?r(b)&&(c=this.chart.inverted!=this.horiz?b.plotX:this.len-b.plotY):c=this.horiz?a.chartX-this.pos:this.len-a.chartY+this.pos;c=this.isRadial?this.getPlotLinePath(this.isXAxis?b.x:m(b.stackY,b.y)):this.getPlotLinePath(null,null,null,null,c);if(c===null)this.hideCrosshair();else if(this.cross)this.cross.attr({visibility:"visible"})[e?"animate":
-"attr"]({d:c},e);else{e={"stroke-width":d.width||1,stroke:d.color||"#C0C0C0",zIndex:d.zIndex||2};if(d.dashStyle)e.dashstyle=d.dashStyle;this.cross=this.chart.renderer.path(c).attr(e).add()}}},hideCrosshair:function(){this.cross&&this.cross.hide()}};q(la.prototype,{getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},addPlotLine:function(a){this.addPlotBandOrLine(a,
-"plotLines")},addPlotBandOrLine:function(a,b){var c=(new R.PlotLineOrBand(this,a)).render(),d=this.userOptions;c&&(b&&(d[b]=d[b]||[],d[b].push(a)),this.plotLinesAndBands.push(c));return c},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=this.options,d=this.userOptions,e=b.length;e--;)b[e].id===a&&b[e].destroy();p([c.plotLines||[],d.plotLines||[],c.plotBands||[],d.plotBands||[]],function(b){for(e=b.length;e--;)b[e].id===a&&ja(b,b[e])})}});la.prototype.getTimeTicks=function(a,b,
-c,d){var e=[],f={},g=E.global.useUTC,h,i=new Date(b-Ra),j=a.unitRange,k=a.count;if(r(b)){j>=A.second&&(i.setMilliseconds(0),i.setSeconds(j>=A.minute?0:k*T(i.getSeconds()/k)));if(j>=A.minute)i[Db](j>=A.hour?0:k*T(i[pb]()/k));if(j>=A.hour)i[Eb](j>=A.day?0:k*T(i[qb]()/k));if(j>=A.day)i[sb](j>=A.month?1:k*T(i[Xa]()/k));j>=A.month&&(i[Fb](j>=A.year?0:k*T(i[fb]()/k)),h=i[gb]());j>=A.year&&(h-=h%k,i[Gb](h));if(j===A.week)i[sb](i[Xa]()-i[rb]()+m(d,1));b=1;Ra&&(i=new Date(i.getTime()+Ra));h=i[gb]();for(var d=
-i.getTime(),l=i[fb](),o=i[Xa](),n=g?Ra:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===A.year?d=eb(h+b*k,0):j===A.month?d=eb(h,l+b*k):!g&&(j===A.day||j===A.week)?d=eb(h,l,o+b*k*(j===A.day?1:7)):d+=j*k,b++;e.push(d);p(vb(e,function(a){return j<=A.hour&&a%A.day===n}),function(a){f[a]="day"})}e.info=q(a,{higherRanks:f,totalRange:j*k});return e};la.prototype.normalizeTimeTickInterval=function(a,b){var c=b||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",
-[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]],d=c[c.length-1],e=A[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=A[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+A[c[g+1][0]])/2)break;e===A.year&&a<5*e&&(f=[1,2,5]);c=nb(a/e,f,d[0]==="year"?v(mb(a/e),1):1);return{unitRange:e,count:c,unitName:d[0]}};la.prototype.getLogTickPositions=function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=u(a),g=this.getLinearTickPositions(a,
-b,c);else if(a>=0.08)for(var f=T(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!l;f++){i=e.length;for(h=0;h<i&&!l;h++)j=za(ia(f)*e[h]),j>b&&(!d||k<=c)&&g.push(k),k>c&&(l=!0),k=j}else if(b=ia(b),c=ia(c),a=e[d?"minorTickInterval":"tickInterval"],a=m(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=nb(a,null,mb(a)),g=Ua(this.getLinearTickPositions(a,b,c),za),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=
-a;return g};var Mb=R.Tooltip=function(){this.init.apply(this,arguments)};Mb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=z(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape||"callout",null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).add().attr({y:-9999});fa||this.label.shadow(b.shadow);this.shared=b.shared},
-destroy:function(){if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden,h=e.followPointer||e.len>1;q(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:h?t:g?(2*f.anchorX+c)/3:c,anchorY:h?t:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(M(a-f.x)>1||M(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=
-this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},m(this.options.hideDelay,500)),b&&p(b,function(a){a.setState()}),this.chart.hoverPoints=null},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=d.plotTop,g=0,h=0,i,a=qa(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===t&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(p(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?
-(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Ua(c,u)},getPosition:function(a,b,c){var d=this.chart,e=this.distance,f={},g,h=["y",d.chartHeight,b,c.plotY+d.plotTop],i=["x",d.chartWidth,a,c.plotX+d.plotLeft],j=c.ttBelow||d.inverted&&!c.negative||!d.inverted&&c.negative,k=function(a,b,c,d){var g=c<d-e,b=d+e+c<b,c=d-e-c;d+=e;if(j&&b)f[a]=d;else if(!j&&g)f[a]=c;else if(g)f[a]=c;else if(b)f[a]=
-d;else return!1},l=function(a,b,c,d){if(d<e||d>b-e)return!1;else f[a]=d<c/2?1:d>b-c/2?b-c-2:d-c/2},o=function(a){var b=h;h=i;i=b;g=a},n=function(){k.apply(0,h)!==!1?l.apply(0,i)===!1&&!g&&(o(!0),n()):g?f.x=f.y=0:(o(!0),n())};(d.inverted||this.len>1)&&o();n();return f},defaultFormatter:function(a){var b=this.points||qa(this),c=b[0].series,d;d=[a.tooltipHeaderFormatter(b[0])];p(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});
-d.push(a.options.footerFormat||"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h={},i,j=[];i=e.formatter||this.defaultFormatter;var h=c.hoverPoints,k,l=this.shared;clearTimeout(this.hideTimer);this.followPointer=qa(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];l&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,h&&p(h,function(a){a.setState()}),p(a,function(a){a.setState("hover");j.push(a.getLabelConfig())}),h={x:a[0].category,
-y:a[0].y},h.points=j,this.len=j.length,a=a[0]):h=a.getLabelConfig();i=i.call(h,this);h=a.series;this.distance=m(h.tooltipOptions.distance,16);i===!1?this.hide():(this.isHidden&&(bb(d),d.attr("opacity",1).show()),d.attr({text:i}),k=e.borderColor||a.color||h.color||"#606060",d.attr({stroke:k}),this.updatePosition({plotX:f,plotY:g,negative:a.negative,ttBelow:a.ttBelow}),this.isHidden=!1);D(c,"tooltipRefresh",{text:i,x:f+c.plotLeft,y:g+c.plotTop,borderColor:k})},updatePosition:function(a){var b=this.chart,
-c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(u(c.x),u(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)},tooltipHeaderFormatter:function(a){var b=a.series,c=b.tooltipOptions,d=c.dateTimeLabelFormats,e=c.xDateFormat,f=b.xAxis,g=f&&f.options.type==="datetime"&&ha(a.key),c=c.headerFormat,f=f&&f.closestPointRange,h;if(g&&!e){if(f)for(h in A){if(A[h]>=f||A[h]<=A.day&&a.key%A[h]>0){e=d[h];break}}else e=d.day;e=e||d.year}g&&e&&(c=c.replace("{point.key}","{point.key:"+
-e+"}"));return Ia(c,{point:a,series:b})}};var oa;$a=y.documentElement.ontouchstart!==t;var Wa=R.Pointer=function(a,b){this.init(a,b)};Wa.prototype={init:function(a,b){var c=b.chart,d=c.events,e=fa?"":c.zoomType,c=a.inverted,f;this.options=b;this.chart=a;this.zoomX=f=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=f&&!c||e&&c;this.zoomVert=e&&!c||f&&c;this.hasZoom=f||e;this.runChartClick=d&&!!d.click;this.pinchDown=[];this.lastValidTouch={};if(R.Tooltip&&b.tooltip.enabled)a.tooltip=new Mb(a,b.tooltip),
-this.followTouchMove=b.tooltip.followTouchMove;this.setDOMEvents()},normalize:function(a,b){var c,d,a=a||window.event,a=Sb(a);if(!a.target)a.target=a.srcElement;d=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;if(!b)this.chartPosition=b=Rb(this.chart.container);d.pageX===t?(c=v(a.x,a.clientX-b.left),d=a.y):(c=d.pageX-b.left,d=d.pageY-b.top);return q(a,{chartX:u(c),chartY:u(d)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};p(this.chart.axes,function(c){b[c.isXAxis?"xAxis":
-"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f,g=b.hoverPoint,h=b.hoverSeries,i,j,k=b.chartWidth,l=this.getIndex(a);if(d&&this.options.tooltip.shared&&(!h||!h.noSharedTooltip)){f=[];i=c.length;for(j=0;j<i;j++)if(c[j].visible&&c[j].options.enableMouseTracking!==!1&&!c[j].noSharedTooltip&&
-c[j].singularTooltips!==!0&&c[j].tooltipPoints.length&&(e=c[j].tooltipPoints[l])&&e.series)e._dist=M(l-e.clientX),k=C(k,e._dist),f.push(e);for(i=f.length;i--;)f[i]._dist>k&&f.splice(i,1);if(f.length&&f[0].clientX!==this.hoverX)d.refresh(f,a),this.hoverX=f[0].clientX}c=h&&h.tooltipOptions.followPointer;if(h&&h.tracker&&!c){if((e=h.tooltipPoints[l])&&e!==g)e.onMouseOver(a)}else d&&c&&!d.isHidden&&(h=d.getAnchor([{}],a),d.updatePosition({plotX:h[0],plotY:h[1]}));if(d&&!this._onDocumentMouseMove)this._onDocumentMouseMove=
-function(a){if(V[oa])V[oa].pointer.onDocumentMouseMove(a)},K(y,"mousemove",this._onDocumentMouseMove);p(b.axes,function(b){b.drawCrosshair(a,m(e,g))})},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,f=e&&e.shared?b.hoverPoints:d;(a=a&&e&&f)&&qa(f)[0].plotX===t&&(a=!1);if(a)e.refresh(f),d&&d.setState(d.state,!0);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&e.hide();if(this._onDocumentMouseMove)W(y,"mousemove",this._onDocumentMouseMove),this._onDocumentMouseMove=null;
-p(b.axes,function(a){a.hideCrosshair()});this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart,d;p(c.series,function(e){d=a||e.getPlotBox();e.xAxis&&e.xAxis.zoomEnabled&&(e.group.attr(d),e.markerGroup&&(e.markerGroup.attr(d),e.markerGroup.clip(b?c.clipRect:null)),e.dataLabelsGroup&&e.dataLabelsGroup.attr(d))});c.clipRect.attr(b||c.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},
-drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,f=this.zoomHor,g=this.zoomVert,h=b.plotLeft,i=b.plotTop,j=b.plotWidth,k=b.plotHeight,l,o=this.mouseDownX,n=this.mouseDownY;d<h?d=h:d>h+j&&(d=h+j);e<i?e=i:e>i+k&&(e=i+k);this.hasDragged=Math.sqrt(Math.pow(o-d,2)+Math.pow(n-e,2));if(this.hasDragged>10){l=b.isInsidePlot(o-h,n-i);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(h,i,f?1:j,g?1:k,0).attr({fill:c.selectionMarkerFill||
-"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&f&&(d-=o,this.selectionMarker.attr({width:M(d),x:(d>0?0:d)+o}));this.selectionMarker&&g&&(d=e-n,this.selectionMarker.attr({height:M(d),y:(d>0?0:d)+n}));l&&!this.selectionMarker&&c.panning&&b.pan(a,c.panning)}},drop:function(a){var b=this.chart,c=this.hasPinched;if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},a=this.selectionMarker,e=a.attr?a.attr("x"):a.x,f=a.attr?a.attr("y"):a.y,g=a.attr?a.attr("width"):
-a.width,h=a.attr?a.attr("height"):a.height,i;if(this.hasDragged||c)p(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?e:f),b=a.toValue(b?e+g:f+h);!isNaN(c)&&!isNaN(b)&&(d[a.coll].push({axis:a,min:C(c,b),max:v(c,b)}),i=!0)}}),i&&D(b,"selection",d,function(a){b.zoom(q(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups()}if(b)G(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=
-!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){V[oa]&&V[oa].pointer.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=this.normalize(a,c);c&&d&&!this.inClass(a.target,"highcharts-tracker")&&!b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&this.reset()},onContainerMouseLeave:function(){var a=V[oa];if(a)a.pointer.reset(),a.pointer.chartPosition=
-null},onContainerMouseMove:function(a){var b=this.chart;oa=b.index;a=this.normalize(a);b.mouseIsDown==="mousedown"&&this.drag(a);(this.inClass(a.target,"highcharts-tracker")||b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop))&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=H(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries,c=(a=a.relatedTarget||
-a.toElement)&&a.point&&a.point.series;if(b&&!b.options.stickyTracking&&!this.inClass(a,"highcharts-tooltip")&&c!==b)b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,a=this.normalize(a);a.cancelBubble=!0;b.cancelClick||(c&&this.inClass(a.target,"highcharts-tracker")?(D(c.series,"click",q(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(q(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&D(b,"click",a)))},setDOMEvents:function(){var a=
-this,b=a.chart.container;b.onmousedown=function(b){a.onContainerMouseDown(b)};b.onmousemove=function(b){a.onContainerMouseMove(b)};b.onclick=function(b){a.onContainerClick(b)};K(b,"mouseleave",a.onContainerMouseLeave);ab===1&&K(y,"mouseup",a.onDocumentMouseUp);if($a)b.ontouchstart=function(b){a.onContainerTouchStart(b)},b.ontouchmove=function(b){a.onContainerTouchMove(b)},ab===1&&K(y,"touchend",a.onDocumentTouchEnd)},destroy:function(){var a;W(this.chart.container,"mouseleave",this.onContainerMouseLeave);
-ab||(W(y,"mouseup",this.onDocumentMouseUp),W(y,"touchend",this.onDocumentTouchEnd));clearInterval(this.tooltipTimeout);for(a in this)this[a]=null}};q(R.Pointer.prototype,{pinchTranslate:function(a,b,c,d,e,f){(this.zoomHor||this.pinchHor)&&this.pinchTranslateDirection(!0,a,b,c,d,e,f);(this.zoomVert||this.pinchVert)&&this.pinchTranslateDirection(!1,a,b,c,d,e,f)},pinchTranslateDirection:function(a,b,c,d,e,f,g,h){var i=this.chart,j=a?"x":"y",k=a?"X":"Y",l="chart"+k,o=a?"width":"height",n=i["plot"+(a?
-"Left":"Top")],s,m,p=h||1,q=i.inverted,x=i.bounds[a?"h":"v"],r=b.length===1,v=b[0][l],u=c[0][l],t=!r&&b[1][l],w=!r&&c[1][l],y,c=function(){!r&&M(v-t)>20&&(p=h||M(u-w)/M(v-t));m=(n-u)/p+v;s=i["plot"+(a?"Width":"Height")]/p};c();b=m;b<x.min?(b=x.min,y=!0):b+s>x.max&&(b=x.max-s,y=!0);y?(u-=0.8*(u-g[j][0]),r||(w-=0.8*(w-g[j][1])),c()):g[j]=[u,w];q||(f[j]=m-n,f[o]=s);f=q?1/p:p;e[o]=s;e[j]=b;d[q?a?"scaleY":"scaleX":"scale"+k]=p;d["translate"+k]=f*n+(u-f*v)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,
-e=b.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.hasZoom,j=b.selectionMarker,k={},l=g===1&&(b.inClass(a.target,"highcharts-tracker")&&c.runTrackerClick||c.runChartClick),o={};(i||e)&&!l&&a.preventDefault();Ua(f,function(a){return b.normalize(a)});if(a.type==="touchstart")p(f,function(a,b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],p(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,
-e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=C(e,f),e=v(e,f);b.min=C(a.pos,g-d);b.max=v(a.pos+a.len,e+d)}});else if(d.length){if(!j)b.selectionMarker=j=q({destroy:sa},c.plotBox);b.pinchTranslate(d,f,k,j,o,h);b.hasPinched=i;b.scaleGroups(k,o);!i&&e&&g===1&&this.runPointActions(b.normalize(a))}},onContainerTouchStart:function(a){var b=this.chart;oa=b.index;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)?(this.runPointActions(a),this.pinch(a)):this.reset()):
-a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){V[oa]&&V[oa].pointer.drop(a)}});if(I.PointerEvent||I.MSPointerEvent){var ua={},zb=!!I.PointerEvent,Wb=function(){var a,b=[];b.item=function(a){return this[a]};for(a in ua)ua.hasOwnProperty(a)&&b.push({pageX:ua[a].pageX,pageY:ua[a].pageY,target:ua[a].target});return b},Ab=function(a,b,c,d){a=a.originalEvent||a;if((a.pointerType==="touch"||
-a.pointerType===a.MSPOINTER_TYPE_TOUCH)&&V[oa])d(a),d=V[oa].pointer,d[b]({type:c,target:a.currentTarget,preventDefault:sa,touches:Wb()})};q(Wa.prototype,{onContainerPointerDown:function(a){Ab(a,"onContainerTouchStart","touchstart",function(a){ua[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}})},onContainerPointerMove:function(a){Ab(a,"onContainerTouchMove","touchmove",function(a){ua[a.pointerId]={pageX:a.pageX,pageY:a.pageY};if(!ua[a.pointerId].target)ua[a.pointerId].target=a.currentTarget})},
-onDocumentPointerUp:function(a){Ab(a,"onContainerTouchEnd","touchend",function(a){delete ua[a.pointerId]})},batchMSEvents:function(a){a(this.chart.container,zb?"pointerdown":"MSPointerDown",this.onContainerPointerDown);a(this.chart.container,zb?"pointermove":"MSPointerMove",this.onContainerPointerMove);a(y,zb?"pointerup":"MSPointerUp",this.onDocumentPointerUp)}});Ma(Wa.prototype,"init",function(a,b,c){a.call(this,b,c);(this.hasZoom||this.followTouchMove)&&G(b.container,{"-ms-touch-action":Q,"touch-action":Q})});
-Ma(Wa.prototype,"setDOMEvents",function(a){a.apply(this);(this.hasZoom||this.followTouchMove)&&this.batchMSEvents(K)});Ma(Wa.prototype,"destroy",function(a){this.batchMSEvents(W);a.call(this)})}var lb=R.Legend=function(a,b){this.init(a,b)};lb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=m(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=z(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=w(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=
-e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight=0,c.lastLineHeight=0,c.symbolWidth=m(b.symbolWidth,16),c.pages=[],c.render(),K(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.legendColor||a.color||"#CCC":g,g=a.options&&a.options.marker,i={fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g&&f.isMarker)for(j in i.stroke=h,g=a.convertAttribs(g),
-g)d=g[j],d!==t&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;p(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&Pa(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},
-positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY,p(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,G(f,{left:b.translateX+e.checkboxOffset+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":Q}))})},renderTitle:function(){var a=this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);
-a=this.title.getBBox();c=a.height;this.offsetWidth=a.width;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var b=this.chart,c=b.renderer,d=this.options,e=d.layout==="horizontal",f=this.symbolWidth,g=d.symbolPadding,h=this.itemStyle,i=this.itemHiddenStyle,j=this.padding,k=e?m(d.itemDistance,20):0,l=!d.rtl,o=d.width,n=d.itemMarginBottom||0,s=this.itemMarginTop,p=this.initialItemX,q=a.legendItem,r=a.series&&a.series.drawLegendSymbol?a.series:a,x=r.options,x=this.createCheckboxForItem&&
-x&&x.showCheckbox,t=d.useHTML;if(!q)a.legendGroup=c.g("legend-item").attr({zIndex:1}).add(this.scrollGroup),r.drawLegendSymbol(this,a),a.legendItem=q=c.text(d.labelFormat?Ia(d.labelFormat,a):d.labelFormatter.call(a),l?f+g:-g,this.baseline,t).css(w(a.visible?h:i)).attr({align:l?"left":"right",zIndex:2}).add(a.legendGroup),this.setItemEvents&&this.setItemEvents(a,q,t,h,i),this.colorizeItem(a,a.visible),x&&this.createCheckboxForItem(a);c=q.getBBox();f=a.checkboxOffset=d.itemWidth||a.legendItemWidth||
-f+g+c.width+k+(x?20:0);this.itemHeight=g=u(a.legendItemHeight||c.height);if(e&&this.itemX-p+f>(o||b.chartWidth-2*j-p-d.x))this.itemX=p,this.itemY+=s+this.lastLineHeight+n,this.lastLineHeight=0;this.maxItemWidth=v(this.maxItemWidth,f);this.lastItemY=s+this.itemY+n;this.lastLineHeight=v(g,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=f:(this.itemY+=s+g+n,this.lastLineHeight=g);this.offsetWidth=o||v((e?this.itemX-p-k:f)+j,this.offsetWidth)},getAllItems:function(){var a=
-[];p(this.chart.series,function(b){var c=b.options;if(m(c.showInLegend,!r(c.linkedTo)?t:!1,!0))a=a.concat(b.legendItems||(c.legendType==="point"?b.data:b))});return a},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,l=j.borderWidth,o=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);
-a.renderTitle();e=a.getAllItems();ob(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;p(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(l||o){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp({width:g,height:h})),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,
-"stroke-width":l||0,fill:o||Q}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;p(e,function(b){a.positionItem(b)});f&&d.align(q({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h,i=this.clipRect,j=e.navigation,k=m(j.animation,!0),l=j.arrowSize||12,o=this.nav,n=this.pages,
-s,q=this.allItems;e.layout==="horizontal"&&(f/=2);g&&(f=C(f,g));n.length=0;if(a>f&&!e.useHTML){this.clipHeight=h=f-20-this.titleHeight-this.padding;this.currentPage=m(this.currentPage,1);this.fullHeight=a;p(q,function(a,b){var c=a._legendItemPos[1],d=u(a.legendItem.getBBox().height),e=n.length;if(!e||c-n[e-1]>h&&(s||c)!==n[e-1])n.push(s||c),e++;b===q.length-1&&c+d-n[e-1]>h&&n.push(c);c!==s&&(s=c)});if(!i)i=b.clipRect=d.clipRect(0,this.padding,9999,0),b.contentGroup.clip(i);i.attr({height:h});if(!o)this.nav=
-o=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,l,l).on("click",function(){b.scroll(-1,k)}).add(o),this.pager=d.text("",15,10).css(j.style).add(o),this.down=d.symbol("triangle-down",0,0,l,l).on("click",function(){b.scroll(1,k)}).add(o);b.scroll(0);a=f}else if(o)i.attr({height:c.chartHeight}),o.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pages,d=c.length,e=this.currentPage+a,f=this.clipHeight,g=this.options.navigation,
-h=g.activeColor,g=g.inactiveColor,i=this.pager,j=this.padding;e>d&&(e=d);if(e>0)b!==t&&Qa(b,this.chart),this.nav.attr({translateX:j,translateY:f+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:e===1?g:h}).css({cursor:e===1?"default":"pointer"}),i.attr({text:e+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,fill:e===d?g:h}).css({cursor:e===d?"default":"pointer"}),c=-c[e-1]+this.initialItemY,this.scrollGroup.animate({translateY:c}),this.currentPage=e,this.positionCheckboxes(c)}};
-N=R.LegendSymbolMixin={drawRectangle:function(a,b){var c=a.options.symbolHeight||12;b.legendSymbol=this.chart.renderer.rect(0,a.baseline-5-c/2,a.symbolWidth,c,a.options.symbolRadius||0).attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b=this.options,c=b.marker,d;d=a.symbolWidth;var e=this.chart.renderer,f=this.legendGroup,a=a.baseline-u(e.fontMetrics(a.options.itemStyle.fontSize).b*0.3),g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=
-e.path(["M",0,a,"L",d,a]).attr(g).add(f)}if(c&&c.enabled!==!1)b=c.radius,this.legendSymbol=d=e.symbol(this.symbol,d/2-b,a-b,2*b,2*b).add(f),d.isMarker=!0}};(/Trident\/7\.0/.test(wa)||Ta)&&Ma(lb.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c,b)};d();setTimeout(d)});Ya.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=w(E,a);c.series=a.series=d;this.userOptions=a;d=c.chart;this.margin=this.splashArray("margin",d);this.spacing=this.splashArray("spacing",
-d);var e=d.events;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=V.length;V.push(f);ab++;d.reflow!==!1&&K(f,"load",function(){f.initReflow()});if(e)for(g in e)K(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=fa?!1:m(d.animation,!0);f.pointCount=0;f.counters=new Bb;f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=F[a.type||b.type||b.defaultSeriesType])||ra(17,!0);b=new b;b.init(this,
-a);return b},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&p(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h,i=this.isDirtyBox,j=c.length,k=j,l=this.renderer,o=l.isHidden(),n=[];Qa(a,this);o&&this.cloneRenderTo();for(this.layOutTitles();k--;)if(a=c[k],a.options.stacking&&
-(g=!0,a.isDirty)){h=!0;break}if(h)for(k=j;k--;)if(a=c[k],a.options.stacking)a.isDirty=!0;p(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;g&&this.getStacks();if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,p(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();p(b,function(a){a.isDirty&&(i=!0)});p(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,n.push(function(){D(a,"afterSetExtremes",
-q(a.eventArgs,a.getExtremes()));delete a.eventArgs});(i||g)&&a.redraw()})}i&&this.drawChartBox();p(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset(!0);l.draw();D(this,"redraw");o&&this.cloneRenderTo(!0);p(n,function(a){a.call()})},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id===a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||[];for(b=0;b<e.length;b++)if(e[b].id===
-a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=qa(b.xAxis||{}),b=b.yAxis=qa(b.yAxis||{});p(c,function(a,b){a.index=b;a.isX=!0});p(b,function(a,b){a.index=b});c=c.concat(b);p(c,function(b){new la(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];p(this.series,function(b){a=a.concat(vb(b.points||[],function(a){return a.selected}))});return a},getSelectedSeries:function(){return vb(this.series,function(a){return a.selected})},getStacks:function(){var a=
-this;p(a.yAxis,function(a){if(a.stacks&&a.hasVisibleSeries)a.oldStacks=a.stacks});p(a.series,function(b){if(b.options.stacking&&(b.visible===!0||a.options.chart.ignoreHiddenSeries===!1))b.stackKey=b.type+m(b.options.stack,"")})},setTitle:function(a,b,c){var g;var d=this,e=d.options,f;f=e.title=w(e.title,a);g=e.subtitle=w(e.subtitle,b),e=g;p([["title",a,f],["subtitle",b,e]],function(a){var b=a[0],c=d[b],e=a[1],a=a[2];c&&e&&(d[b]=c=c.destroy());a&&a.text&&!c&&(d[b]=d.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,
-"class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add())});d.layOutTitles(c)},layOutTitles:function(a){var b=0,c=this.title,d=this.subtitle,e=this.options,f=e.title,e=e.subtitle,g=this.spacingBox.width-44;if(c&&(c.css({width:(f.width||g)+"px"}).align(q({y:15},f),!1,"spacingBox"),!f.floating&&!f.verticalAlign))b=c.getBBox().height;d&&(d.css({width:(e.width||g)+"px"}).align(q({y:b+f.margin},e),!1,"spacingBox"),!e.floating&&!e.verticalAlign&&(b=Ka(b+d.getBBox().height)));c=this.titleOffset!==
-b;this.titleOffset=b;if(!this.isDirtyBox&&c)this.isDirtyBox=c,this.hasRendered&&m(a,!0)&&this.isDirtyBox&&this.redraw()},getChartSize:function(){var a=this.options.chart,b=a.width,a=a.height,c=this.renderToClone||this.renderTo;if(!r(b))this.containerWidth=jb(c,"width");if(!r(a))this.containerHeight=jb(c,"height");this.chartWidth=v(0,b||this.containerWidth||600);this.chartHeight=v(0,m(a,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;
-a?b&&(this.renderTo.appendChild(c),Pa(b),delete this.renderToClone):(c&&c.parentNode===this.renderTo&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),G(b,{position:"absolute",top:"-9999px",display:"block"}),b.style.setProperty&&b.style.setProperty("display","block","important"),y.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+tb++;if(Fa(a))this.renderTo=a=y.getElementById(a);
-a||ra(13,!0);c=z(H(a,"data-highcharts-chart"));!isNaN(c)&&V[c]&&V[c].hasRendered&&V[c].destroy();H(a,"data-highcharts-chart",this.index);a.innerHTML="";!b.skipClone&&!a.offsetWidth&&this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=Y(Ja,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},q({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},
-b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new ta(a,c,d,b.style,!0):new Za(a,c,d,b.style);fa&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.spacing,b,c=this.legend,d=this.margin,e=this.options.legend,f=m(e.margin,20),g=e.x,h=e.y,i=e.align,j=e.verticalAlign,k=this.titleOffset;this.resetMargins();b=this.axisOffset;if(k&&!r(d[0]))this.plotTop=v(this.plotTop,k+this.options.title.margin+a[0]);if(c.display&&!e.floating)if(i==="right"){if(!r(d[1]))this.marginRight=
-v(this.marginRight,c.legendWidth-g+f+a[1])}else if(i==="left"){if(!r(d[3]))this.plotLeft=v(this.plotLeft,c.legendWidth+g+f+a[3])}else if(j==="top"){if(!r(d[0]))this.plotTop=v(this.plotTop,c.legendHeight+h+f+a[0])}else if(j==="bottom"&&!r(d[2]))this.marginBottom=v(this.marginBottom,c.legendHeight-h+f+a[2]);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&p(this.axes,function(a){a.getOffset()});r(d[3])||
-(this.plotLeft+=b[3]);r(d[0])||(this.plotTop+=b[0]);r(d[2])||(this.marginBottom+=b[2]);r(d[1])||(this.marginRight+=b[1]);this.setChartSize()},reflow:function(a){var b=this,c=b.options.chart,d=b.renderTo,e=c.width||jb(d,"width"),f=c.height||jb(d,"height"),c=a?a.target:I,d=function(){if(b.container)b.setSize(e,f,!1),b.hasUserSize=null};if(!b.hasUserSize&&e&&f&&(c===I||c===y)){if(e!==b.containerWidth||f!==b.containerHeight)clearTimeout(b.reflowTimeout),a?b.reflowTimeout=setTimeout(d,100):d();b.containerWidth=
-e;b.containerHeight=f}},initReflow:function(){var a=this,b=function(b){a.reflow(b)};K(I,"resize",b);K(a,"destroy",function(){W(I,"resize",b)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&D(d,"endResize",null,function(){d.isResizing-=1})};Qa(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(r(a))d.chartWidth=e=v(0,u(a)),d.hasUserSize=!!e;if(r(b))d.chartHeight=f=v(0,u(b));(va?kb:G)(d.container,{width:e+"px",height:f+"px"},va);d.setChartSize(!0);d.renderer.setSize(e,
-f,c);d.maxTicks=null;p(d.axes,function(a){a.isDirty=!0;a.setScale()});p(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.layOutTitles();d.getMargins();d.redraw(c);d.oldChartHeight=null;D(d,"resize");va===!1?g():setTimeout(g,va&&va.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=this.spacing,h=this.clipOffset,i,j,k,l;this.plotLeft=i=u(this.plotLeft);this.plotTop=j=u(this.plotTop);this.plotWidth=
-k=v(0,u(d-i-this.marginRight));this.plotHeight=l=v(0,u(e-j-this.marginBottom));this.plotSizeX=b?l:k;this.plotSizeY=b?k:l;this.plotBorderWidth=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:e-g[0]-g[2]};this.plotBox=c.plotBox={x:i,y:j,width:k,height:l};d=2*T(this.plotBorderWidth/2);b=Ka(v(d,h[3])/2);c=Ka(v(d,h[0])/2);this.clipBox={x:b,y:c,width:T(this.plotSizeX-v(d,h[1])/2-b),height:T(this.plotSizeY-v(d,h[2])/2-c)};a||p(this.axes,function(a){a.setAxisSize();
-a.setAxisTranslation()})},resetMargins:function(){var a=this.spacing,b=this.margin;this.plotTop=m(b[0],a[0]);this.marginRight=m(b[1],a[1]);this.marginBottom=m(b[2],a[2]);this.plotLeft=m(b[3],a[3]);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,
-o=a.plotBorderWidth||0,n,s=this.plotLeft,m=this.plotTop,p=this.plotWidth,q=this.plotHeight,r=this.plotBox,v=this.clipRect,u=this.clipBox;n=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp({width:c-n,height:d-n}));else{e={fill:j||Q};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(n/2,n/2,c-n,d-n,a.borderRadius,i).attr(e).addClass("highcharts-background").add().shadow(a.shadow)}if(k)f?f.animate(r):this.plotBackground=b.rect(s,m,p,q,0).attr({fill:k}).add().shadow(a.plotShadow);
-if(l)h?h.animate(r):this.plotBGImage=b.image(l,s,m,p,q).add();v?v.animate({width:u.width,height:u.height}):this.clipRect=b.clipRect(u);if(o)g?g.animate(g.crisp({x:s,y:m,width:p,height:q})):this.plotBorder=b.rect(s,m,p,q,0,-o).attr({stroke:a.plotBorderColor,"stroke-width":o,fill:Q,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;p(["inverted","angular","polar"],function(g){c=F[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];
-for(e=d&&d.length;!f&&e--;)(c=F[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;p(b,function(a){a.linkedSeries.length=0});p(b,function(b){var d=b.options.linkedTo;if(Fa(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),b.linkedParent=d})},renderSeries:function(){p(this.series,function(a){a.translate();a.setTooltipPoints&&a.setTooltipPoints();a.render()})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=
-d.credits,g;a.setTitle();a.legend=new lb(a,d.legend);a.getStacks();p(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;p(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&p(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();a.renderSeries();e.items&&p(e.items,function(b){var d=q(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;
-c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;D(a,"destroy");V[a.index]=t;ab--;a.renderTo.removeAttribute("data-highcharts-chart");W(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();
-p("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",W(d),f&&Pa(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!aa&&I==I.top&&y.readyState!=="complete"||fa&&!I.canvg?(fa?Lb.push(function(){a.firstRender()},a.options.global.canvasToolsURL):y.attachEvent("onreadystatechange",
-function(){y.detachEvent("onreadystatechange",a.firstRender);y.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender()){a.getContainer();D(a,"init");a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();p(b.series||[],function(b){a.initSeries(b)});a.linkSeries();D(a,"beforeRender");if(R.Pointer)a.pointer=new Wa(a,b);a.render();a.renderer.draw();c&&c.apply(a,[a]);p(a.callbacks,function(b){b.apply(a,[a])});a.cloneRenderTo(!0);
-D(a,"load")}},splashArray:function(a,b){var c=b[a],c=ca(c)?c:[c,c,c,c];return[m(b[a+"Top"],c[0]),m(b[a+"Right"],c[1]),m(b[a+"Bottom"],c[2]),m(b[a+"Left"],c[3])]}};Ya.prototype.callbacks=[];X=R.CenteredSeriesMixin={getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-2*c,b=a.center,a=[m(b[0],"50%"),m(b[1],"50%"),a.size||"100%",a.innerSize||0],g=C(e,f),h;return Ua(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*z(a)/100:
-a)+(d?c:0)})}};var Ea=function(){};Ea.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ea.prototype.optionsToObject.call(this,a);q(this,a);this.options=this.options?q(this.options,a):a;if(d)this.y=this[d];if(this.x===
-t&&c)this.x=b===t?c.autoIncrement():b;return this},optionsToObject:function(a){var b={},c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b[d[0]]=a;else if(La(a)){if(a.length>e){c=typeof a[0];if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;
-if(b&&(this.setState(),ja(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)W(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,
-point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=m(c.valueDecimals,""),e=c.valuePrefix||"",f=c.valueSuffix||"";p(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return Ia(a,{point:this,series:this.series})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&
-this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});D(this,a,b,c)}};var O=function(){};O.prototype={isCartesian:!0,type:"line",pointClass:Ea,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],init:function(a,b){var c=this,d,e,f=a.series,g=function(a,b){return m(a.options.index,a._i)-m(b.options.index,
-b._i)};c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();q(c,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if(fa)b.animation=!1;e=b.events;for(d in e)K(c,d,e[d]);if(e&&e.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor();c.getSymbol();p(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);if(c.isCartesian)a.hasCartesianSeries=!0;f.push(c);c._i=f.length-1;ob(f,g);this.yAxis&&
-ob(this.yAxis.series,g);p(f,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)})},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;p(a.axisTypes||[],function(e){p(c[e],function(c){d=c.options;if(b[e]===d.index||b[e]!==t&&b[e]===d.id||b[e]===t&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});!a[e]&&a.optionalAxis!==e&&ra(18,!0)})},updateParallelArrays:function(a,b){var c=a.series,d=arguments;p(c.parallelArrays,typeof b==="number"?function(d){var f=d==="y"&&c.toYData?c.toYData(a):a[d];
-c[d+"Data"][b]=f}:function(a){Array.prototype[b].apply(c[a+"Data"],Array.prototype.slice.call(d,2))})},autoIncrement:function(){var a=this.options,b=this.xIncrement,b=m(b,a.pointStart,0);this.pointInterval=m(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else p(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+
-1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart,c=b.options.plotOptions,b=b.userOptions||{},d=b.plotOptions||{},e=c[this.type];this.userOptions=a;c=w(e,c.series,a);this.tooltipOptions=w(E.tooltip,E.plotOptions[this.type].tooltip,b.tooltip,d.series&&d.series.tooltip,d[this.type]&&d[this.type].tooltip,a.tooltip);e.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,
-e;e=a.color||ba[this.type].color;if(!e&&!a.colorByPoint)r(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,c=c.counters;this.symbol=b.symbol;if(!this.symbol)r(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:N.drawLineMarker,
-setData:function(a,b,c,d){var e=this,f=e.points,g=f&&f.length||0,h,i=e.options,j=e.chart,k=null,l=e.xAxis,o=l&&!!l.categories,n=e.tooltipPoints,s=i.turboThreshold,q=this.xData,r=this.yData,v=(h=e.pointArrayMap)&&h.length,a=a||[];h=a.length;b=m(b,!0);if(d!==!1&&h&&g===h&&!e.cropped&&!e.hasGroupedData)p(a,function(a,b){f[b].update(a,!1)});else{e.xIncrement=null;e.pointRange=o?1:i.pointRange;e.colorCounter=0;p(this.parallelArrays,function(a){e[a+"Data"].length=0});if(s&&h>s){for(c=0;k===null&&c<h;)k=
-a[c],c++;if(ha(k)){o=m(i.pointStart,0);i=m(i.pointInterval,1);for(c=0;c<h;c++)q[c]=o,r[c]=a[c],o+=i;e.xIncrement=o}else if(La(k))if(v)for(c=0;c<h;c++)i=a[c],q[c]=i[0],r[c]=i.slice(1,v+1);else for(c=0;c<h;c++)i=a[c],q[c]=i[0],r[c]=i[1];else ra(12)}else for(c=0;c<h;c++)if(a[c]!==t&&(i={series:e},e.pointClass.prototype.applyOptions.apply(i,[a[c]]),e.updateParallelArrays(i,c),o&&i.name))l.names[i.x]=i.name;Fa(r[0])&&ra(14,!0);e.data=[];e.options.data=a;for(c=g;c--;)f[c]&&f[c].destroy&&f[c].destroy();
-if(n)n.length=0;if(l)l.minRange=l.userMinRange;e.isDirty=e.isDirtyData=j.isDirtyBox=!0;c=!1}b&&j.redraw(c)},processData:function(a){var b=this.xData,c=this.yData,d=b.length,e;e=0;var f,g,h=this.xAxis,i=this.options,j=i.cropThreshold,k=0,l=this.isCartesian,o,n;if(l&&!this.isDirty&&!h.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(l&&this.sorted&&(!j||d>j||this.forceCrop))if(o=h.min,n=h.max,b[d-1]<o||b[0]>n)b=[],c=[];else if(b[0]<o||b[d-1]>n)e=this.cropData(this.xData,this.yData,o,n),b=e.xData,c=e.yData,
-e=e.start,f=!0,k=b.length;for(d=b.length-1;d>=0;d--)a=b[d]-b[d-1],!f&&b[d]>o&&b[d]<n&&k++,a>0&&(g===t||a<g)?g=a:a<0&&this.requireSorting&&ra(15);this.cropped=f;this.cropStart=e;this.processedXData=b;this.processedYData=c;this.activePointCount=k;if(i.pointRange===null)this.pointRange=g||1;this.closestPointRange=g},cropData:function(a,b,c,d){var e=a.length,f=0,g=e,h=m(this.cropShoulder,1),i;for(i=0;i<e;i++)if(a[i]>=c){f=v(0,i-h);break}for(;i<e;i++)if(a[i]>d){g=i+h;break}return{xData:a.slice(f,g),yData:b.slice(f,
-g),start:f,end:g}},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,l=[],o;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(o=0;o<g;o++)i=h+o,j?l[o]=(new f).init(this,[d[o]].concat(qa(e[o]))):(b[i]?k=b[i]:a[i]!==t&&(b[i]=k=(new f).init(this,a[i],d[o])),l[o]=k);if(b&&(g!==(c=b.length)||j))for(o=0;o<c;o++)if(o===h&&!j&&(o+=g),b[o])b[o].destroyElements(),b[o].plotX=
-t;this.data=b;this.points=l},getExtremes:function(a){var b=this.yAxis,c=this.processedXData,d,e=[],f=0;d=this.xAxis.getExtremes();var g=d.min,h=d.max,i,j,k,l,a=a||this.stackedYData||this.processedYData;d=a.length;for(l=0;l<d;l++)if(j=c[l],k=a[l],i=k!==null&&k!==t&&(!b.isLog||k.length||k>0),j=this.getExtremesFromAll||this.cropped||(c[l+1]||j)>=g&&(c[l-1]||j)<=h,i&&j)if(i=k.length)for(;i--;)k[i]!==null&&(e[f++]=k[i]);else e[f++]=k;this.dataMin=m(void 0,Na(e));this.dataMax=m(void 0,Ba(e))},translate:function(){this.processedXData||
-this.processData();this.generatePoints();for(var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i=a.pointPlacement,j=i==="between"||ha(i),k=a.threshold,a=0;a<g;a++){var l=f[a],o=l.x,n=l.y,s=l.low,p=b&&e.stacks[(this.negStacks&&n<k?"-":"")+this.stackKey];if(e.isLog&&n<=0)l.y=n=null;l.plotX=c.translate(o,0,0,0,1,i,this.type==="flags");if(b&&this.visible&&p&&p[o])p=p[o],n=p.points[this.index+","+a],s=n[0],n=n[1],s===0&&(s=m(k,e.min)),
-e.isLog&&s<=0&&(s=null),l.total=l.stackTotal=p.total,l.percentage=p.total&&l.y/p.total*100,l.stackY=n,p.setOffset(this.pointXOffset||0,this.barW||0);l.yBottom=r(s)?e.translate(s,0,1,0,1):null;h&&(n=this.modifyValue(n,l));l.plotY=typeof n==="number"&&n!==Infinity?e.translate(n,0,1,0,1):t;l.clientX=j?c.translate(o,0,0,0,1):l.plotX;l.negative=l.y<(k||0);l.category=d&&d[l.x]!==t?d[l.x]:l.x}this.getSegments()},animate:function(a){var b=this.chart,c=b.renderer,d;d=this.options.animation;var e=this.clipBox||
-b.clipBox,f=b.inverted,g;if(d&&!ca(d))d=ba[this.type].animation;g=["_sharedClip",d.duration,d.easing,e.height].join(",");a?(a=b[g],d=b[g+"m"],a||(b[g]=a=c.clipRect(q(e,{width:0})),b[g+"m"]=d=c.clipRect(-99,f?-b.plotLeft:-b.plotTop,99,f?b.chartWidth:b.chartHeight)),this.group.clip(a),this.markerGroup.clip(d),this.sharedClipKey=g):((a=b[g])&&a.animate({width:b.plotSizeX},d),b[g+"m"]&&b[g+"m"].animate({width:b.plotSizeX+99},d),this.animate=null)},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,
-c=this.group,d=this.clipBox;if(c&&this.options.clip!==!1){if(!b||!d)c.clip(d?a.renderer.clipRect(d):a.clipRect);this.markerGroup.clip()}D(this,"afterAnimate");setTimeout(function(){b&&a[b]&&(d||(a[b]=a[b].destroy()),a[b+"m"]&&(a[b+"m"]=a[b+"m"].destroy()))},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k;d=this.options.marker;var l=this.pointAttr[""],o,n=this.markerGroup,s=m(d.enabled,this.activePointCount<0.5*this.xAxis.len/d.radius);if(d.enabled!==!1||this._hasPointMarkers)for(f=
-b.length;f--;)if(g=b[f],d=T(g.plotX),e=g.plotY,k=g.graphic,i=g.marker||{},a=s&&i.enabled===t||i.enabled,o=c.isInsidePlot(u(d),e,c.inverted),a&&e!==t&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""]||l,h=a.r,i=m(i.symbol,this.symbol),j=i.indexOf("url")===0,k)k[o?"show":"hide"](!0).animate(q({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(o&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(n)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,
-b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=m(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=ba[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color;f={stroke:g,fill:g};var h=a.points||[],i,j=[],k,l=a.pointAttrToOptions;k=a.hasPointSpecificOptions;var o=b.negativeColor,n=c.lineColor,s=c.fillColor;i=b.turboThreshold;var m;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=
-e.color||ya(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,f);p(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;g=h.length;if(!i||g<i||k)for(;g--;){i=h[g];if((c=i.options&&i.options.marker||i.options)&&c.enabled===!1)c.radius=0;if(i.negative&&o)i.color=i.fillColor=o;k=b.colorByPoint||i.color;if(i.options)for(m in l)r(c[l[m]])&&(k=!0);if(k){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=f.color||!i.options.color&&e.color||
-ya(i.color).brighten(f.brightness||e.brightness).get();f={color:i.color};if(!s)f.fillColor=i.color;if(!n)f.lineColor=i.color;k[""]=a.convertAttribs(q(f,c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""])}else k=j;i.pointAttr=k}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(wa),d,e,f=a.data||[],g,h,i;D(a,"destroy");W(a);p(a.axisTypes||[],function(b){if(i=a[b])ja(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);
-for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);p("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ja(b.series,a);for(h in a)delete a[h]},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;p(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,
-e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},getGraphPath:function(){var a=this,b=[],c,d=[];p(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=b.linecap!=="square",g=this.getGraphPath(),
-h=b.negativeColor;h&&c.push(["graphNeg",h]);p(c,function(c,h){var k=c[0],l=a[k];if(l)bb(l),l.animate({d:g});else if(d&&g.length)l={stroke:c[1],"stroke-width":d,fill:Q,zIndex:1},e?l.dashstyle=e:f&&(l["stroke-linecap"]=l["stroke-linejoin"]="round"),a[k]=a.chart.renderer.path(g).attr(l).add(a.group).shadow(!h&&b.shadow)})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor||a.negativeFillColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=
-b.chartHeight,k=v(e,j),l=this.yAxis;if(d&&(f||g)){d=u(l.toPixels(a.threshold||0,!0));d<0&&(k-=d);a={x:0,y:0,width:k,height:d};k={x:0,y:d,width:k,height:k};if(b.inverted)a.height=k.y=b.plotWidth-d,c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,height:e});l.reversed?(b=k,e=a):(b=a,e=k);h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&this.graphNeg&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))}},
-invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};p(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)K(c,"resize",a),K(b,"destroy",function(){W(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=!f;g&&(this[a]=f=this.chart.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"](this.getPlotBox());return f},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;
-if(a.inverted)b=c,c=this.xAxis;return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=(c=d.animation)&&!!a.animate&&b.renderer.isSVG&&m(c.duration,500)||0,f=a.visible?"visible":"hidden",g=d.zIndex,h=a.hasRendered,i=b.seriesGroup;c=a.plotGroup("group","series",f,g,i);a.markerGroup=a.plotGroup("markerGroup","markers",f,g,i);e&&a.animate(!0);a.getAttribs();c.inverted=a.isCartesian?b.inverted:!1;a.drawGraph&&(a.drawGraph(),
-a.clipNeg());a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&a.options.enableMouseTracking!==!1&&a.drawTracker();b.inverted&&a.invertGroups();d.clip!==!1&&!a.sharedClipKey&&!h&&c.clip(b.clipRect);e&&a.animate();if(!h)e?a.animationTimeout=setTimeout(function(){a.afterAnimate()},e):a.afterAnimate();a.isDirty=a.isDirtyData=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,
-height:a.plotHeight}),c.animate({translateX:m(d&&d.left,a.plotLeft),translateY:m(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints&&this.setTooltipPoints(!0);this.render();b&&D(this,"updatedData")}};Hb.prototype={destroy:function(){Oa(this,this.axis)},render:function(a){var b=this.options,c=b.format,c=c?Ia(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,null,null,b.useHTML).css(b.style).attr({align:this.textAlign,
-rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(c.usePercentage?100:this.total,0,0,0,1),c=c.translate(0),c=M(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e[this.options.crop===!1||d.isInsidePlot(f.x,f.y)?"show":"hide"](!0)}};la.prototype.buildStacks=function(){var a=
-this.series,b=m(this.options.reversedStacks,!0),c=a.length;if(!this.isXAxis){for(this.usePercentage=!1;c--;)a[b?c:a.length-c-1].setStackedPoints();if(this.usePercentage)for(c=0;c<a.length;c++)a[c].setPercentStacks()}};la.prototype.renderStackTotals=function(){var a=this.chart,b=a.renderer,c=this.stacks,d,e,f=this.stackTotalGroup;if(!f)this.stackTotalGroup=f=b.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();f.translate(a.plotLeft,a.plotTop);for(d in c)for(e in a=c[d],a)a[e].render(f)};
-O.prototype.setStackedPoints=function(){if(this.options.stacking&&!(this.visible!==!0&&this.chart.options.chart.ignoreHiddenSeries!==!1)){var a=this.processedXData,b=this.processedYData,c=[],d=b.length,e=this.options,f=e.threshold,g=e.stack,e=e.stacking,h=this.stackKey,i="-"+h,j=this.negStacks,k=this.yAxis,l=k.stacks,o=k.oldStacks,n,m,p,q,r,u;for(q=0;q<d;q++){r=a[q];u=b[q];p=this.index+","+q;m=(n=j&&u<f)?i:h;l[m]||(l[m]={});if(!l[m][r])o[m]&&o[m][r]?(l[m][r]=o[m][r],l[m][r].total=null):l[m][r]=new Hb(k,
-k.options.stackLabels,n,r,g);m=l[m][r];m.points[p]=[m.cum||0];e==="percent"?(n=n?h:i,j&&l[n]&&l[n][r]?(n=l[n][r],m.total=n.total=v(n.total,m.total)+M(u)||0):m.total=da(m.total+(M(u)||0))):m.total=da(m.total+(u||0));m.cum=(m.cum||0)+(u||0);m.points[p].push(m.cum);c[q]=m.cum}if(e==="percent")k.usePercentage=!0;this.stackedYData=c;k.oldStacks={}}};O.prototype.setPercentStacks=function(){var a=this,b=a.stackKey,c=a.yAxis.stacks,d=a.processedXData;p([b,"-"+b],function(b){var e;for(var f=d.length,g,h;f--;)if(g=
-d[f],e=(h=c[b]&&c[b][g])&&h.points[a.index+","+f],g=e)h=h.total?100/h.total:0,g[0]=da(g[0]*h),g[1]=da(g[1]*h),a.stackedYData[f]=g[1]})};q(Ya.prototype,{addSeries:function(a,b,c){var d,e=this;a&&(b=m(b,!0),D(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;e.linkSeries();b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var e=b?"xAxis":"yAxis",f=this.options;new la(this,w(a,{index:this[e].length,isX:b}));f[e]=qa(f[e]||{});f[e].push(a);m(c,!0)&&this.redraw(d)},showLoading:function(a){var b=
-this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=Y(Ja,{className:"highcharts-loading"},q(d.style,{zIndex:10,display:Q}),this.container),this.loadingSpan=Y("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)G(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),kb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=
-this.options,b=this.loadingDiv;b&&kb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){G(b,{display:Q})}});this.loadingShown=!1}});q(Ea.prototype,{update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=e.chart,j=e.options,b=m(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);if(ca(a)){e.getAttribs();if(f)a&&a.marker&&a.marker.symbol?d.graphic=f.destroy():f.attr(d.pointAttr[d.state||""]);if(a&&a.dataLabels&&d.dataLabel)d.dataLabel=d.dataLabel.destroy()}g=
-Da(d,h);e.updateParallelArrays(d,g);j.data[g]=d.options;e.isDirty=e.isDirtyData=!0;if(!e.fixedBox&&e.hasCartesianSeries)i.isDirtyBox=!0;j.legendType==="point"&&i.legend.destroyItem(d);b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.points,f=d.chart,g,h=d.data;Qa(b,f);a=m(a,!0);c.firePointEvent("remove",null,function(){g=Da(c,h);h.length===e.length&&e.splice(g,1);h.splice(g,1);d.options.data.splice(g,1);d.updateParallelArrays(c,"splice",g,1);c.destroy();d.isDirty=!0;d.isDirtyData=
-!0;a&&f.redraw()})}});q(O.prototype,{addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xAxis&&this.xAxis.names,k=g&&g.shift||0,l=e.data,o,n=this.xData;Qa(d,i);c&&p([g,h,this.graphNeg,this.areaNeg],function(a){if(a)a.shift=k+1});if(h)h.isArea=!0;b=m(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);g=d.x;h=n.length;if(this.requireSorting&&g<n[h-1])for(o=!0;h&&n[h-1]>g;)h--;this.updateParallelArrays(d,"splice",h,0,0);this.updateParallelArrays(d,
-h);if(j)j[g]=d.name;l.splice(h,0,a);o&&(this.data.splice(h,0,null),this.processData());e.legendType==="point"&&this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),this.updateParallelArrays(d,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&(this.getAttribs(),i.redraw())},remove:function(a,b){var c=this,d=c.chart,a=m(a,!0);if(!c.isRemoving)c.isRemoving=!0,D(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;d.linkSeries();a&&d.redraw(b)});c.isRemoving=
-!1},update:function(a,b){var c=this.chart,d=this.type,e=F[d].prototype,f,a=w(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},{data:this.options.data},a);this.remove(!1);for(f in e)e.hasOwnProperty(f)&&(this[f]=t);q(this,F[a.type||d].prototype);this.init(c,a);m(b,!0)&&c.redraw(!1)}});q(la.prototype,{update:function(a,b){var c=this.chart,a=c.options[this.coll][this.options.index]=w(this.userOptions,a);this.destroy(!0);this._addedPlotLB=t;this.init(c,q(a,{events:t}));c.isDirtyBox=
-!0;m(b,!0)&&c.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,d=this.series,e=d.length;e--;)d[e]&&d[e].remove(!1);ja(b.axes,this);ja(b[c],this);b.options[c].splice(this.options.index,1);p(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;m(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}});ga=ka(O);F.line=ga;ba.area=w(S,{threshold:0});var pa=ka(O,{type:"area",getSegments:function(){var a=
-[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k=this.options.connectNulls,l,o,n;if(this.options.stacking&&!this.cropped){for(o=0;o<j.length;o++)g[j[o].x]=j[o];for(n in f)f[n].total!==null&&c.push(+n);c.sort(function(a,b){return a-b});p(c,function(a){if(!k||g[a]&&g[a].y!==null)g[a]?b.push(g[a]):(h=d.translate(a),l=f[a].percent?f[a].total?f[a].cum*100/f[a].total:0:f[a].cum,i=e.toPixels(l,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:sa}))});
-b.length&&a.push(b)}else O.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=O.prototype.getSegmentPath.call(this,a),c=[].concat(b),d,e=this.options;d=b.length;var f=this.yAxis.getThreshold(e.threshold),g;d===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)g=m(a[d].yBottom,f),d<a.length-1&&e.step&&c.push(a[d+1].plotX,g),c.push(a[d].plotX,g);else this.closeSegment(c,a,f);this.areaPath=this.areaPath.concat(c);return b},
-closeSegment:function(a,b,c){a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];O.prototype.drawGraph.apply(this);var a=this,b=this.areaPath,c=this.options,d=c.negativeColor,e=c.negativeFillColor,f=[["area",this.color,c.fillColor]];(d||e)&&f.push(["areaNeg",d,e]);p(f,function(d){var e=d[0],f=a[e];f?f.animate({d:b}):a[e]=a.chart.renderer.path(b).attr({fill:m(d[2],ya(d[1]).setOpacity(m(c.fillOpacity,0.75)).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:N.drawRectangle});
-F.area=pa;ba.spline=w(S);ga=ka(O,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j=g.plotX;var g=g.plotY,l;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;l=(k-i)*(j-d)/(j-h)+e-k;i+=l;k+=l;i>a&&i>e?(i=v(a,e),k=2*e-i):i<a&&i<e&&(i=C(a,e),k=2*e-i);k>g&&k>e?(k=v(g,e),i=2*e-k):k<g&&k<e&&(k=C(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?(b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=
-f.rightContY=null):b=["M",d,e];return b}});F.spline=ga;ba.areaspline=w(ba.area);pa=pa.prototype;ga=ka(ga,{type:"areaspline",closedStacks:!0,getSegmentPath:pa.getSegmentPath,closeSegment:pa.closeSegment,drawGraph:pa.drawGraph,drawLegendSymbol:N.drawRectangle});F.areaspline=ga;ba.column=w(S,{borderColor:"#FFFFFF",borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,shadow:!1,halo:!1},select:{color:"#C0C0C0",borderColor:"#000000",
-shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,tooltip:{distance:6},threshold:0});ga=ka(O,{type:"column",pointAttrToOptions:{stroke:"borderColor",fill:"color",r:"borderRadius"},cropShoulder:0,trackerGroups:["group","dataLabelsGroup"],negStacks:!0,init:function(){O.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},getColumnMetrics:function(){var a=this,b=a.options,c=a.xAxis,d=a.yAxis,e=
-c.reversed,f,g={},h,i=0;b.grouping===!1?i=1:p(a.chart.series,function(b){var c=b.options,e=b.yAxis;if(b.type===a.type&&b.visible&&d.len===e.len&&d.pos===e.pos)c.stacking?(f=b.stackKey,g[f]===t&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var c=C(M(c.transA)*(c.ordinalSlope||b.pointRange||c.closestPointRange||c.tickInterval||1),c.len),j=c*b.groupPadding,k=(c-2*j)/i,l=b.pointWidth,b=r(l)?(k-l)/2:k*b.pointPadding,l=m(l,k-2*b);return a.columnMetrics={width:l,offset:b+(j+((e?i-(a.columnIndex||
-0):a.columnIndex)||0)*k-c/2)*(e?-1:1)}},translate:function(){var a=this,b=a.chart,c=a.options,d=a.borderWidth=m(c.borderWidth,a.activePointCount>0.5*a.xAxis.len?0:1),e=a.yAxis,f=a.translatedThreshold=e.getThreshold(c.threshold),g=m(c.minPointLength,5),c=a.getColumnMetrics(),h=c.width,i=a.barW=Ka(v(h,1+2*d)),j=a.pointXOffset=c.offset,k=-(d%2?0.5:0),l=d%2?0.5:1;b.renderer.isVML&&b.inverted&&(l+=1);O.prototype.translate.apply(a);p(a.points,function(c){var d=m(c.yBottom,f),p=C(v(-999-d,c.plotY),e.len+
-999+d),q=c.plotX+j,r=i,t=C(p,d),x;x=v(p,d)-t;M(x)<g&&g&&(x=g,t=u(M(t-f)>g?d-g:f-(e.translate(c.y,0,1,0,1)<=f?g:0)));c.barX=q;c.pointWidth=h;c.tooltipPos=b.inverted?[e.len-p,a.xAxis.len-q-r/2]:[q+r/2,p];d=M(q)<0.5;r=u(q+r)+k;q=u(q)+k;r-=q;p=M(t)<0.5;x=u(t+x)+l;t=u(t)+l;x-=t;d&&(q+=1,r-=1);p&&(t-=1,x+=1);c.shapeType="rect";c.shapeArgs={x:q,y:t,width:r,height:x}})},getSymbol:sa,drawLegendSymbol:N.drawRectangle,drawGraph:sa,drawPoints:function(){var a=this,b=this.chart,c=a.options,d=b.renderer,e=c.animationLimit||
-250,f,g,h;p(a.points,function(i){var j=i.plotY,k=i.graphic;if(j!==t&&!isNaN(j)&&i.y!==null)f=i.shapeArgs,h=r(a.borderWidth)?{"stroke-width":a.borderWidth}:{},g=i.pointAttr[i.selected?"select":""]||a.pointAttr[""],k?(bb(k),k.attr(h)[b.pointCount<e?"animate":"attr"](w(f))):i.graphic=d[i.shapeType](f).attr(g).attr(h).add(a.group).shadow(c.shadow,null,c.stacking&&!c.borderRadius);else if(k)i.graphic=k.destroy()})},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(aa)a?
-(e.scaleY=0.001,a=C(b.pos+b.len,v(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},remove:function(){var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0});O.prototype.remove.apply(a,arguments)}});F.column=ga;ba.bar=w(ba.column);pa=ka(ga,{type:"bar",inverted:!0});F.bar=pa;ba.scatter=w(S,{lineWidth:0,tooltip:{headerFormat:'<span style="color:{series.color}">●</span> <span style="font-size: 10px;"> {series.name}</span><br/>',
-pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>"},stickyTracking:!1});pa=ka(O,{type:"scatter",sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["markerGroup"],takeOrdinalPosition:!1,singularTooltips:!0,drawGraph:function(){this.options.lineWidth&&O.prototype.drawGraph.call(this)}});F.scatter=pa;ba.pie=w(S,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,
-legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});S={type:"pie",isCartesian:!1,pointClass:ka(Ea,{init:function(){Ea.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;q(a,{visible:a.visible!==!1,name:m(a.name,"Slice")});b=function(b){a.slice(b.type==="select")};K(a,"select",b);K(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart;b.visible=b.options.visible=
-a=a===t?!b.visible:a;c.options.data[Da(b,c.data)]=b.options;p(["graphic","dataLabel","connector","shadowGroup"],function(c){if(b[c])b[c][a?"show":"hide"](!0)});b.legendItem&&d.legend.colorizeItem(b,a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Qa(c,d.chart);m(b,!0);this.sliced=this.options.sliced=a=r(a)?a:!this.sliced;d.options.data[Da(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);
-this.shadowGroup&&this.shadowGroup.animate(a)},haloPath:function(a){var b=this.shapeArgs,c=this.series.chart;return this.series.chart.renderer.symbols.arc(c.plotLeft+b.x,c.plotTop+b.y,b.r+a,b.r+a,{innerR:this.shapeArgs.r,start:b.start,end:b.end})}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},singularTooltips:!0,getColor:sa,animate:function(a){var b=this,c=b.points,d=
-b.startAngleRad;if(!a)p(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b,c,d){O.prototype.setData.call(this,a,!1,c,d);this.processData();this.generatePoints();m(b,!0)&&this.chart.redraw(c)},generatePoints:function(){var a,b=0,c,d,e,f=this.options.ignoreHiddenPoint;O.prototype.generatePoints.call(this);c=this.points;d=c.length;for(a=0;a<d;a++)e=c[a],b+=f&&!e.visible?
-0:e.y;this.total=b;for(a=0;a<d;a++)e=c[a],e.percentage=b>0?e.y/b*100:0,e.total=b},translate:function(a){this.generatePoints();var b=0,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g,h,i=c.startAngle||0,j=this.startAngleRad=ma/180*(i-90),i=(this.endAngleRad=ma/180*(m(c.endAngle,i+360)-90))-j,k=this.points,l=c.dataLabels.distance,c=c.ignoreHiddenPoint,o,n=k.length,p;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){h=U.asin(C((b-a[1])/(a[2]/2+l),1));return a[0]+(c?-1:1)*Z(h)*(a[2]/
-2+l)};for(o=0;o<n;o++){p=k[o];f=j+b*i;if(!c||p.visible)b+=p.percentage/100;g=j+b*i;p.shapeType="arc";p.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:u(f*1E3)/1E3,end:u(g*1E3)/1E3};h=(g+f)/2;h>1.5*ma?h-=2*ma:h<-ma/2&&(h+=2*ma);p.slicedTranslation={translateX:u(Z(h)*d),translateY:u(ea(h)*d)};f=Z(h)*a[2]/2;g=ea(h)*a[2]/2;p.tooltipPos=[a[0]+f*0.7,a[1]+g*0.7];p.half=h<-ma/2||h>ma/2?1:0;p.angle=h;e=C(e,l/2);p.labelPos=[a[0]+f+Z(h)*l,a[1]+g+ea(h)*l,a[0]+f+Z(h)*e,a[1]+g+ea(h)*e,a[0]+f,a[1]+g,l<0?
-"center":p.half?"right":"left",h]}},drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);p(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};f&&f.attr(c);d?d.animate(q(g,c)):h.graphic=d=b[h.shapeType](g).setRadialReference(a.center).attr(h.pointAttr[h.selected?"select":
-""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible!==void 0&&h.setVisible(h.visible)})},sortByAngle:function(a,b){a.sort(function(a,d){return a.angle!==void 0&&(d.angle-a.angle)*b})},drawLegendSymbol:N.drawRectangle,getCenter:X.getCenter,getSymbol:sa};S=ka(O,S);F.pie=S;O.prototype.drawDataLabels=function(){var a=this,b=a.options,c=b.cursor,d=b.dataLabels,e=a.points,f,g,h,i;if(d.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(d),i=a.plotGroup("dataLabelsGroup",
-"data-labels","hidden",d.zIndex||6),!a.hasRendered&&m(d.defer,!0)&&(i.attr({opacity:0}),K(a,"afterAnimate",function(){a.dataLabelsGroup.show()[b.animation?"animate":"attr"]({opacity:1},{duration:200})})),g=d,p(e,function(b){var e,l=b.dataLabel,o,n,p=b.connector,u=!0;f=b.options&&b.options.dataLabels;e=m(f&&f.enabled,g.enabled);if(l&&!e)b.dataLabel=l.destroy();else if(e){d=w(g,f);e=d.rotation;o=b.getLabelConfig();h=d.format?Ia(d.format,o):d.formatter.call(o,d);d.style.color=m(d.color,d.style.color,
-a.color,"black");if(l)if(r(h))l.attr({text:h}),u=!1;else{if(b.dataLabel=l=l.destroy(),p)b.connector=p.destroy()}else if(r(h)){l={fill:d.backgroundColor,stroke:d.borderColor,"stroke-width":d.borderWidth,r:d.borderRadius||0,rotation:e,padding:d.padding,zIndex:1};for(n in l)l[n]===t&&delete l[n];l=b.dataLabel=a.chart.renderer[e?"text":"label"](h,0,-999,null,null,null,d.useHTML).attr(l).css(q(d.style,c&&{cursor:c})).add(i).shadow(d.shadow)}l&&a.alignDataLabel(b,l,d,null,u)}})};O.prototype.alignDataLabel=
-function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=m(a.plotX,-999),i=m(a.plotY,-999),j=b.getBBox();if(a=this.visible&&(a.series.forceDL||f.isInsidePlot(h,u(i),g)||d&&f.isInsidePlot(h,g?d.x+1:d.y+d.height-1,g)))d=q({x:g?f.plotWidth-i:h,y:u(g?f.plotHeight-h:i),width:0,height:0},d),q(c,{width:j.width,height:j.height}),c.rotation?(g={align:c.align,x:d.x+c.x+d.width/2,y:d.y+c.y+d.height/2},b[e?"attr":"animate"](g)):(b.align(c,null,d),g=b.alignAttr,m(c.overflow,"justify")==="justify"?this.justifyDataLabel(b,
-c,g,j,d,e):m(c.crop,!0)&&(a=f.isInsidePlot(g.x,g.y)&&f.isInsidePlot(g.x+j.width,g.y+j.height)));if(!a)b.attr({y:-999}),b.placed=!1};O.prototype.justifyDataLabel=function(a,b,c,d,e,f){var g=this.chart,h=b.align,i=b.verticalAlign,j,k;j=c.x;if(j<0)h==="right"?b.align="left":b.x=-j,k=!0;j=c.x+d.width;if(j>g.plotWidth)h==="left"?b.align="right":b.x=g.plotWidth-j,k=!0;j=c.y;if(j<0)i==="bottom"?b.verticalAlign="top":b.y=-j,k=!0;j=c.y+d.height;if(j>g.plotHeight)i==="top"?b.verticalAlign="bottom":b.y=g.plotHeight-
-j,k=!0;if(k)a.placed=!f,a.align(b,null,e)};if(F.pie)F.pie.prototype.drawDataLabels=function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=m(e.connectorPadding,10),g=m(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=m(e.softConnector,!0),l=e.distance,o=a.center,n=o[2]/2,q=o[1],r=l>0,t,w,x,y,z=[[],[]],A,C,G,D,B,F=[0,0,0,0],N=function(a,b){return b.y-a.y};if(a.visible&&(e.enabled||a._hasPointLabels)){O.prototype.drawDataLabels.apply(a);p(b,function(a){a.dataLabel&&a.visible&&z[a.half].push(a)});
-for(D=0;!y&&b[D];)y=b[D]&&b[D].dataLabel&&(b[D].dataLabel.getBBox().height||21),D++;for(D=2;D--;){var b=[],K=[],H=z[D],I=H.length,E;a.sortByAngle(H,D-0.5);if(l>0){for(B=q-n-l;B<=q+n+l;B+=y)b.push(B);w=b.length;if(I>w){c=[].concat(H);c.sort(N);for(B=I;B--;)c[B].rank=B;for(B=I;B--;)H[B].rank>=w&&H.splice(B,1);I=H.length}for(B=0;B<I;B++){c=H[B];x=c.labelPos;c=9999;var Q,P;for(P=0;P<w;P++)Q=M(b[P]-x[1]),Q<c&&(c=Q,E=P);if(E<B&&b[B]!==null)E=B;else for(w<I-B+E&&b[B]!==null&&(E=w-I+B);b[E]===null;)E++;K.push({i:E,
-y:b[E]});b[E]=null}K.sort(N)}for(B=0;B<I;B++){c=H[B];x=c.labelPos;t=c.dataLabel;G=c.visible===!1?"hidden":"visible";c=x[1];if(l>0){if(w=K.pop(),E=w.i,C=w.y,c>C&&b[E+1]!==null||c<C&&b[E-1]!==null)C=c}else C=c;A=e.justify?o[0]+(D?-1:1)*(n+l):a.getX(E===0||E===b.length-1?c:C,D);t._attr={visibility:G,align:x[6]};t._pos={x:A+e.x+({left:f,right:-f}[x[6]]||0),y:C+e.y-10};t.connX=A;t.connY=C;if(this.options.size===null)w=t.width,A-w<f?F[3]=v(u(w-A+f),F[3]):A+w>h-f&&(F[1]=v(u(A+w-h+f),F[1])),C-y/2<0?F[0]=
-v(u(-C+y/2),F[0]):C+y/2>d&&(F[2]=v(u(C+y/2-d),F[2]))}}if(Ba(F)===0||this.verifyDataLabelOverflow(F))this.placeDataLabels(),r&&g&&p(this.points,function(b){i=b.connector;x=b.labelPos;if((t=b.dataLabel)&&t._pos)G=t._attr.visibility,A=t.connX,C=t.connY,j=k?["M",A+(x[6]==="left"?5:-5),C,"C",A,C,2*x[2]-x[4],2*x[3]-x[5],x[2],x[3],"L",x[4],x[5]]:["M",A+(x[6]==="left"?5:-5),C,"L",x[2],x[3],"L",x[4],x[5]],i?(i.animate({d:j}),i.attr("visibility",G)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,
-stroke:e.connectorColor||b.color||"#606060",visibility:G}).add(a.dataLabelsGroup);else if(i)b.connector=i.destroy()})}},F.pie.prototype.placeDataLabels=function(){p(this.points,function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},F.pie.prototype.alignDataLabel=sa,F.pie.prototype.verifyDataLabelOverflow=function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=v(b[2]-v(a[1],a[3]),c):(e=v(b[2]-
-a[1]-a[3],c),b[0]+=(a[3]-a[1])/2);d[1]!==null?e=v(C(e,b[2]-v(a[0],a[2])),c):(e=v(C(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),p(this.points,function(a){if(a.dataLabel)a.dataLabel._pos=null}),this.drawDataLabels&&this.drawDataLabels()):f=!0;return f};if(F.column)F.column.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>m(this.translatedThreshold,f.plotSizeY),j=m(c.inside,!!this.options.stacking);if(h&&
-(d=w(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:0,d.height=0);c.align=m(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=m(c.verticalAlign,g||j?"middle":i?"top":"bottom");O.prototype.alignDataLabel.call(this,a,b,c,d,e)};S=R.TrackerMixin={drawTrackerPoint:function(){var a=this,b=a.chart,c=b.pointer,d=a.options.cursor,e=d&&{cursor:d},f=function(c){var d=c.target,e;if(b.hoverSeries!==a)a.onMouseOver();
-for(;d&&!e;)e=d.point,d=d.parentNode;if(e!==t&&e!==b.hoverPoint)e.onMouseOver(c)};p(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});if(!a._hasTracking)p(a.trackerGroups,function(b){if(a[b]&&(a[b].addClass("highcharts-tracker").on("mouseover",f).on("mouseout",function(a){c.onTrackerMouseOut(a)}).css(e),$a))a[b].on("touchstart",f)}),a._hasTracking=!0},drawTrackerGraph:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:
-a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,l=k&&{cursor:k},k=a.singlePoints,m,n=function(){if(f.hoverSeries!==a)a.onMouseOver()},q="rgba(192,192,192,"+(aa?1.0E-4:0.002)+")";if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-i,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+i,d[m-1]);for(m=0;m<k.length;m++)e=k[m],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);j?j.attr({d:d}):(a.tracker=h.path(d).attr({"stroke-linejoin":"round",
-visibility:a.visible?"visible":"hidden",stroke:q,fill:c?q:Q,"stroke-width":b.lineWidth+(c?0:2*i),zIndex:2}).add(a.group),p([a.tracker,a.markerGroup],function(a){a.addClass("highcharts-tracker").on("mouseover",n).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(l);if($a)a.on("touchstart",n)}))}};if(F.column)ga.prototype.drawTracker=S.drawTrackerPoint;if(F.pie)F.pie.prototype.drawTracker=S.drawTrackerPoint;if(F.scatter)pa.prototype.drawTracker=S.drawTrackerPoint;q(lb.prototype,{setItemEvents:function(a,
-b,c,d,e){var f=this;(c?b:a.legendGroup).on("mouseover",function(){a.setState("hover");b.css(f.options.itemHoverStyle)}).on("mouseout",function(){b.css(a.visible?d:e);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):D(a,"legendItemClick",b,c)})},createCheckboxForItem:function(a){a.checkbox=Y("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},this.options.itemCheckboxStyle,this.chart.container);
-K(a.checkbox,"click",function(b){D(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})})}});E.legend.itemStyle.cursor="pointer";q(Ya.prototype,{showResetZoom:function(){var a=this,b=E.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo==="chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=
-this;D(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?p(this.axes,function(a){b=a.zoom()}):p(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b=e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&ca(e))this.resetZoomButton=e.destroy();b&&this.redraw(m(this.options.chart.animation,a&&a.animation,this.pointCount<
-100))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&p(d,function(a){a.setState()});p(b==="xy"?[1,0]:[1],function(b){var d=a[b?"chartX":"chartY"],h=c[b?"xAxis":"yAxis"][0],i=c[b?"mouseDownX":"mouseDownY"],j=(h.pointRange||0)/2,k=h.getExtremes(),l=h.toValue(i-d,!0)+j,i=h.toValue(i+c[b?"plotWidth":"plotHeight"]-d,!0)-j;h.series.length&&l>C(k.dataMin,k.min)&&i<v(k.dataMax,k.max)&&(h.setExtremes(l,i,!1,!1,{trigger:"pan"}),e=!0);c[b?"mouseDownX":"mouseDownY"]=d});e&&c.redraw(!1);G(c.container,{cursor:"move"})}});
-q(Ea.prototype,{select:function(a,b){var c=this,d=c.series,e=d.chart,a=m(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=c.options.selected=a;d.options.data[Da(c,d.data)]=c.options;c.setState(a&&"select");b||p(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[Da(a,d.data)]=a.options,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;
-if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||Da(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},importEvents:function(){if(!this.hasImportedEvents){var a=w(this.series.options.point,this.options).events,b;this.events=a;for(b in a)K(this,b,a[b]);this.hasImportedEvents=!0}},setState:function(a,
-b){var c=this.plotX,d=this.plotY,e=this.series,f=e.options.states,g=ba[e.type].marker&&e.options.marker,h=g&&!g.enabled,i=g&&g.states[a],j=i&&i.enabled===!1,k=e.stateMarkerGraphic,l=this.marker||{},m=e.chart,n=e.halo,p,a=a||"";p=this.pointAttr[a]||e.pointAttr[a];if(!(a===this.state&&!b||this.selected&&a!=="select"||f[a]&&f[a].enabled===!1||a&&(j||h&&i.enabled===!1)||a&&l.states&&l.states[a]&&l.states[a].enabled===!1)){if(this.graphic)g=g&&this.graphic.symbolName&&p.r,this.graphic.attr(w(p,g?{x:c-
-g,y:d-g,width:2*g,height:2*g}:{})),k&&k.hide();else{if(a&&i)if(g=i.radius,l=l.symbol||e.symbol,k&&k.currentSymbol!==l&&(k=k.destroy()),k)k[b?"animate":"attr"]({x:c-g,y:d-g});else if(l)e.stateMarkerGraphic=k=m.renderer.symbol(l,c-g,d-g,2*g,2*g).attr(p).add(e.markerGroup),k.currentSymbol=l;if(k)k[a&&m.isInsidePlot(c,d,m.inverted)?"show":"hide"]()}if((c=f[a]&&f[a].halo)&&c.size){if(!n)e.halo=n=m.renderer.path().add(e.seriesGroup);n.attr(q({fill:ya(this.color||e.color).setOpacity(c.opacity).get()},c.attributes))[b?
-"animate":"attr"]({d:this.haloPath(c.size)})}else n&&n.attr({d:[]});this.state=a}},haloPath:function(a){var b=this.series,c=b.chart,d=b.getPlotBox(),e=c.inverted;return c.renderer.symbols.circle(d.translateX+(e?b.yAxis.len-this.plotY:this.plotX)-a,d.translateY+(e?b.xAxis.len-this.plotX:this.plotY)-a,a*2,a*2)}});q(O.prototype,{onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&D(this,"mouseOver");this.setState("hover");a.hoverSeries=
-this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&D(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},
-setVisible:function(a,b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===t?!h:a)?"show":"hide";p(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&p(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});p(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;
-b!==!1&&d.redraw();D(c,f)},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||e.len:this.chart.plotSizeX,h,i,j=[];if(!(this.options.enableMouseTracking===!1||this.singularTooltips)){if(a)this.tooltipPoints=null;p(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(i=0;i<a;i++)if(e=b[i],c=e.x,c>=f.min&&c<=f.max){h=b[i+1];c=d===t?0:d+1;for(d=b[i+1]?C(v(0,T((e.clientX+
-(h?h.wrappedClientX||h.clientX:g))/2)),g):g;c>=0&&c<=d;)j[c++]=e}this.tooltipPoints=j}},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===t?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;D(this,a?"select":"unselect")},drawTracker:S.drawTrackerGraph});q(R,{Axis:la,Chart:Ya,Color:ya,Point:Ea,Tick:Sa,Renderer:Za,Series:O,SVGElement:P,SVGRenderer:ta,arrayMin:Na,arrayMax:Ba,charts:V,dateFormat:cb,format:Ia,pathAnim:ub,getOptions:function(){return E},
-hasBidiBug:Nb,isTouchDevice:Jb,numberFormat:Ga,seriesTypes:F,setOptions:function(a){E=w(!0,E,a);Cb();return E},addEvent:K,removeEvent:W,createElement:Y,discardElement:Pa,css:G,each:p,extend:q,map:Ua,merge:w,pick:m,splat:qa,extendClass:ka,pInt:z,wrap:Ma,svg:aa,canvas:fa,vml:!aa&&!fa,product:"Highcharts",version:"4.0.1"})})();
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(m,C){function K(a,b,c){this.init.call(this,a,b,c)}var O=m.arrayMin,P=m.arrayMax,s=m.each,F=m.extend,o=m.merge,Q=m.map,q=m.pick,x=m.pInt,p=m.getOptions().plotOptions,h=m.seriesTypes,u=m.extendClass,L=m.splat,r=m.wrap,M=m.Axis,y=m.Tick,H=m.Point,R=m.Pointer,S=m.CenteredSeriesMixin,z=m.TrackerMixin,t=m.Series,v=Math,D=v.round,A=v.floor,T=v.max,U=m.Color,w=function(){};F(K.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background={};d.options=a=o(e,a);
-(a=a.background)&&s([].concat(L(a)).reverse(),function(a){var g=a.backgroundColor,a=o(d.defaultBackgroundOptions,a);if(g)a.backgroundColor=g;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"}});
-var G=M.prototype,y=y.prototype,V={getOffset:w,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:w,setCategories:w,setTitle:w},N={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,distance:15,x:0,y:null},
-maxPadding:0,minPadding:0,showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){a=this.options=o(this.defaultOptions,this.defaultRadialOptions,a);if(!a.plotBands)a.plotBands=[]},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=S.getCenter.call(this.pane)},getLinePath:function(a,b){var c=this.center,b=q(b,
-c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center)this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset:0},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&
-1||this.pointRange||this.closestPointRange||0)},setAxisSize:function(){G.setAxisSize.call(this);if(this.isRadial){this.center=this.pane.center=m.CenteredSeriesMixin.getCenter.call(this.pane);if(this.isCircular)this.sector=this.endAngleRad-this.startAngleRad;this.len=this.width=this.height=this.center[2]*q(this.sector,1)/2}},getPosition:function(a,b){return this.postTranslate(this.isCircular?this.translate(a):0,q(this.isCircular?b:this.translate(a),this.center[2]/2)-this.offset)},postTranslate:function(a,
-b){var c=this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,f=d[2]/2,g=[q(c.outerRadius,"100%"),c.innerRadius,q(c.thickness,10)],k=/%$/,l,n=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(n||(g[0]=this.translate(a),g[1]=this.translate(b)),g=Q(g,function(a){k.test(a)&&(a=x(a,10)*f/100);
-return a}),c.shape==="circle"||!n?(a=-Math.PI/2,b=Math.PI*1.5,l=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],g[0],g[0],{start:a,end:b,innerR:q(g[1],g[0]-g[2]),open:l}));return d},getPlotLinePath:function(a,b){var c=this,d=c.center,e=c.chart,f=c.getPosition(a),g,k,l;c.isCircular?l=["M",d[0]+e.plotLeft,d[1]+e.plotTop,"L",f.x,f.y]:c.options.gridLineInterpolation==="circle"?(a=c.translate(a))&&(l=c.getLinePath(0,a)):(s(e.xAxis,function(a){a.pane===
-c.pane&&(g=a)}),l=[],a=c.translate(a),d=g.tickPositions,g.autoConnect&&(d=d.concat([d[0]])),b&&(d=[].concat(d).reverse()),s(d,function(f,c){k=g.getPosition(f,a);l.push(c?"L":"M",k.x,k.y)}));return l},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};r(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,f=c.isX,g=d&&f,k,l;l=b.options;var n=c.pane||0;if(d){if(F(this,
-g?V:N),k=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)F(this,N),this.defaultRadialOptions=(k=f)?this.defaultRadialXOptions:o(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!g&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[n]=b.panes[n]||new K(L(l.pane)[n],b,this),n=i);n=n.options;b.inverted=!1;l.chart.zoomType=null;this.startAngleRad=b=(n.startAngle-90)*Math.PI/180;this.endAngleRad=l=(q(n.endAngle,n.startAngle+360)-90)*Math.PI/
-180;this.offset=a.offset||0;if((this.isCircular=k)&&c.max===C&&l-b===2*Math.PI)this.autoConnect=!0}});r(y,"getPosition",function(a,b,c,d,e){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,e)});r(y,"getLabelPosition",function(a,b,c,d,e,f,g,k,l){var n=this.axis,j=f.y,i=f.align,h=(n.translate(this.pos)+n.startAngleRad+Math.PI/2)/Math.PI*180%360;n.isRadial?(a=n.getPosition(this.pos,n.center[2]/2+q(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:h}):j===null&&(j=n.chart.renderer.fontMetrics(d.styles.fontSize).b-
-d.getBBox().height/2),i===null&&(i=n.isCircular?h>20&&h<160?"left":h>200&&h<340?"right":"center":"center",d.attr({align:i})),a.x+=f.x,a.y+=j):a=a.call(this,b,c,d,e,f,g,k,l);return a});r(y,"getMarkPath",function(a,b,c,d,e,f,g){var k=this.axis;k.isRadial?(a=k.getPosition(this.pos,k.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,g);return b});p.arearange=o(p.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
-trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0},states:{hover:{halo:!1}}});h.arearange=u(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;s(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});t.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);
-s(this.points,function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.translate(d,0,1,0,1)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.translate(d,0,1,0,1))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=t.prototype.getSegmentPath,f,g;g=this.options;var k=g.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=e.call(this,
-b);if(k)k===!0&&(k="left"),g.step={left:"right",center:"center",right:"left"}[k];c=e.call(this,c);g.step=k;g=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return g},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=t.prototype,f=this.options.dataLabels,g,k=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)g=a[c],g.y=g.high,g._plotY=g.plotY,g.plotY=g.plotHigh,d[c]=g.dataLabel,g.dataLabel=g.dataLabelUpper,g.below=!1,k?(f.align="left",f.x=f.xHigh):f.y=
-f.yHigh;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments);for(c=b;c--;)g=a[c],g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g._plotY,g.below=!0,k?(f.align="right",f.x=f.xLow):f.y=f.yLow;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments)}},alignDataLabel:function(){h.column.prototype.alignDataLabel.apply(this,arguments)},getSymbol:h.column.prototype.getSymbol,drawPoints:w});p.areasplinerange=o(p.arearange);h.areasplinerange=u(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});
-(function(){var a=h.column.prototype;p.columnrange=o(p.column,p.arearange,{lineWidth:1,pointRange:null});h.columnrange=u(h.arearange,{type:"columnrange",translate:function(){var b=this,c=b.yAxis,d;a.translate.apply(b);s(b.points,function(a){var f=a.shapeArgs,g=b.options.minPointLength,k;a.tooltipPos=null;a.plotHigh=d=c.translate(a.high,0,1,0,1);a.plotLow=a.plotY;k=d;a=a.plotY-d;a<g&&(g-=a,a+=g,k-=g/2);f.height=a;f.y=k})},trackerGroups:["group","dataLabels"],drawGraph:w,pointAttrToOptions:a.pointAttrToOptions,
-drawPoints:a.drawPoints,drawTracker:a.drawTracker,animate:a.animate,getColumnMetrics:a.getColumnMetrics})})();p.gauge=o(p.line,{dataLabels:{enabled:!0,defer:!1,y:15,borderWidth:1,borderColor:"silver",borderRadius:3,crop:!1,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});z={type:"gauge",pointClass:u(H,{setState:function(a){this.state=a}}),angular:!0,drawGraph:w,fixedBox:!0,forceDL:!0,trackerGroups:["group","dataLabels"],translate:function(){var a=
-this.yAxis,b=this.options,c=a.center;this.generatePoints();s(this.points,function(d){var e=o(b.dial,d.dial),f=x(q(e.radius,80))*c[2]/200,g=x(q(e.baseLength,70))*f/100,k=x(q(e.rearLength,10))*f/100,l=e.baseWidth||3,n=e.topWidth||1,j=b.overshoot,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);j&&typeof j==="number"?(j=j/180*Math.PI,i=Math.max(a.startAngleRad-j,Math.min(a.endAngleRad+j,i))):b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs=
-{d:e.path||["M",-k,-l/2,"L",g,-l/2,f,-n/2,f,n/2,g,l/2,-k,l/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,f=a.chart.renderer;s(a.points,function(c){var b=c.graphic,l=c.shapeArgs,e=l.d,j=o(d.dial,c.dial);b?(b.animate(l),l.d=e):c.graphic=f[c.shapeType](l).attr({stroke:j.borderColor||"none","stroke-width":j.borderWidth||0,fill:j.backgroundColor||"black",rotation:l.rotation}).add(a.group)});
-c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,q(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)s(b.points,function(a){var d=a.graphic;d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?
-"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);t.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:function(a,b){t.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();q(b,!0)&&this.chart.redraw()},drawTracker:z&&z.drawTrackerPoint};h.gauge=u(h.line,z);p.boxplot=o(p.column,{fillColor:"#FFFFFF",lineWidth:1,medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">●</span> <b> {series.name}</b><br/>Maximum: {point.high}<br/>Upper quartile: {point.q3}<br/>Median: {point.median}<br/>Lower quartile: {point.q1}<br/>Minimum: {point.low}<br/>'},
-whiskerLength:"50%",whiskerWidth:2});h.boxplot=u(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:w,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);s(this.points,function(c){s(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=
-this,b=a.points,c=a.options,d=a.chart.renderer,e,f,g,k,l,n,j,i,h,m,p,I,r,o,J,u,w,t,v,x,z,y,E=a.doQuartiles!==!1,B=parseInt(a.options.whiskerLength,10)/100;s(b,function(b){h=b.graphic;z=b.shapeArgs;p={};o={};u={};y=b.color||a.color;if(b.plotY!==C)if(e=b.pointAttr[b.selected?"selected":""],w=z.width,t=A(z.x),v=t+w,x=D(w/2),f=A(E?b.q1Plot:b.lowPlot),g=A(E?b.q3Plot:b.lowPlot),k=A(b.highPlot),l=A(b.lowPlot),p.stroke=b.stemColor||c.stemColor||y,p["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),p.dashstyle=
-b.stemDashStyle||c.stemDashStyle,o.stroke=b.whiskerColor||c.whiskerColor||y,o["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||y,u["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),u["stroke-linecap"]="round",j=p["stroke-width"]%2/2,i=t+x+j,m=["M",i,g,"L",i,k,"M",i,f,"L",i,l],E&&(j=e["stroke-width"]%2/2,i=A(i)+j,f=A(f)+j,g=A(g)+j,t+=j,v+=j,I=["M",t,g,"L",t,f,"L",v,f,"L",v,g,"L",t,g,"z"]),B&&(j=o["stroke-width"]%2/2,k+=j,l+=j,r=["M",i-x*B,
-k,"L",i+x*B,k,"M",i-x*B,l,"L",i+x*B,l]),j=u["stroke-width"]%2/2,n=D(b.medianPlot)+j,J=["M",t,n,"L",v,n],h)b.stem.animate({d:m}),B&&b.whiskers.animate({d:r}),E&&b.box.animate({d:I}),b.medianShape.animate({d:J});else{b.graphic=h=d.g().add(a.group);b.stem=d.path(m).attr(p).add(h);if(B)b.whiskers=d.path(r).attr(o).add(h);if(E)b.box=d.path(I).attr(e).add(h);b.medianShape=d.path(J).attr(u).add(h)}})}});p.errorbar=o(p.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
-whiskerWidth:null});h.errorbar=u(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,drawDataLabels:h.arearange?h.arearange.prototype.drawDataLabels:w,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p.waterfall=o(p.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=u(h.column,{type:"waterfall",upColorProp:"fill",
-pointArrayMap:["low","y"],pointValKey:"y",init:function(a,b){b.stacking=!0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.yAxis,b,c,d,e,f,g,k,l,n;b=this.options.threshold;h.column.prototype.translate.apply(this);l=b;d=this.points;for(c=0,b=d.length;c<b;c++){e=d[c];f=e.shapeArgs;g=this.getStack(c);n=g.points[this.index+","+c];if(isNaN(e.y))e.y=this.yData[c];k=T(l,l+e.y)+n[0];f.y=a.translate(k,0,1);e.isSum||e.isIntermediateSum?(f.y=a.translate(n[1],0,1),f.height=a.translate(n[0],
-0,1)-f.y):l+=g.total;f.height<0&&(f.y+=f.height,f.height*=-1);e.plotY=f.y=D(f.y)-this.borderWidth%2/2;f.height=D(f.height);e.yBottom=f.y+f.height}},processData:function(a){var b=this.yData,c=this.points,d,e=b.length,f=this.options.threshold||0,g,k,l,n,j,i;k=g=l=n=f;for(i=0;i<e;i++)j=b[i],d=c&&c[i]?c[i]:{},j==="sum"||d.isSum?b[i]=k:j==="intermediateSum"||d.isIntermediateSum?(b[i]=g,g=f):(k+=j,g+=j),l=Math.min(k,l),n=Math.max(k,n);t.prototype.processData.call(this,a);this.dataMin=l;this.dataMax=n},
-toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum";return a.y},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=m.Color(c).brighten(0.1).get(),d=o(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;s(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,
-c=D(this.options.lineWidth+this.borderWidth)%2/2,d=[],e,f,g;for(g=1;g<b;g++)f=a[g].shapeArgs,e=a[g-1].shapeArgs,f=["M",e.x+e.width,e.y+c,"L",f.x,e.y+c],a[g-1].y<0&&(f[2]+=e.height,f[5]+=e.height),d=d.concat(f);return d},getExtremes:w,getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},drawGraph:t.prototype.drawGraph});p.bubble=o(p.scatter,{dataLabels:{format:"{point.z}",inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},
-verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0});z=u(H,{haloPath:function(){return H.prototype.haloPath.call(this,this.shapeArgs.r+this.series.options.states.hover.halo.size)}});h.bubble=u(h.scatter,{type:"bubble",pointClass:z,pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],bubblePadding:!0,
-pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},applyOpacity:function(a){var b=this.options.marker,c=q(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=U(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=t.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,f,g,k=this.zData,l=[],n=this.options.sizeBy!=="width";for(f=0,e=k.length;f<e;f++)g=b-a,g=g>0?(k[f]-a)/(b-
-a):0.5,n&&g>=0&&(g=Math.sqrt(g)),l.push(v.ceil(c+g*(d-c))/2);this.radii=l},animate:function(a){var b=this.options.animation;if(!a)s(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,
-r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=C},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});M.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,g=f?"xData":"yData",k=this.min,l={},
-n=v.min(c.plotWidth,c.plotHeight),j=Number.MAX_VALUE,i=-Number.MAX_VALUE,h=this.max-k,m=b/h,p=[];this.tickPositions&&(s(this.series,function(b){var g=b.options;if(b.bubblePadding&&(b.visible||!c.options.chart.ignoreHiddenSeries))if(a.allowZoomOutside=!0,p.push(b),f)s(["minSize","maxSize"],function(a){var b=g[a],f=/%$/.test(b),b=x(b);l[a]=f?n*b/100:b}),b.minPxSize=l.minSize,b=b.zData,b.length&&(j=v.min(j,v.max(O(b),g.displayNegative===!1?g.zThreshold:-Number.MAX_VALUE)),i=v.max(i,P(b)))}),s(p,function(a){var b=
-a[g],c=b.length,n;f&&a.getRadii(j,i,l.minSize,l.maxSize);if(h>0)for(;c--;)typeof b[c]==="number"&&(n=a.radii[c],d=Math.min((b[c]-k)*m-n,d),e=Math.max((b[c]-k)*m+n,e))}),p.length&&h>0&&q(this.options.min,this.userMin)===C&&q(this.options.max,this.userMax)===C&&(e-=b,m*=(b+d-e)/b,this.min+=d/m,this.max+=e/m))};(function(){function a(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b=this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function b(a,b){var c=this.chart,
-d=this.options.animation,e=this.group,j=this.markerGroup,i=this.xAxis.center,h=c.plotLeft,m=c.plotTop;if(c.polar){if(c.renderer.isSVG)d===!0&&(d={}),b?(c={translateX:i[0]+h,translateY:i[1]+m,scaleX:0.001,scaleY:0.001},e.attr(c),j&&j.attr(c)):(c={translateX:h,translateY:m,scaleX:1,scaleY:1},e.animate(c,d),j&&j.animate(c,d),this.animate=null)}else a.call(this,b)}var c=t.prototype,d=R.prototype,e;c.toXY=function(a){var b,c=this.chart,d=a.plotX;b=a.plotY;a.rectPlotX=d;a.rectPlotY=b;d=(d/Math.PI*180+this.xAxis.pane.options.startAngle)%
-360;d<0&&(d+=360);a.clientX=d;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-b);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};c.orderTooltipPoints=function(a){if(this.chart.polar&&(a.sort(function(a,b){return a.clientX-b.clientX}),a[0]))a[0].wrappedClientX=a[0].clientX+360,a.push(a[0])};h.area&&r(h.area.prototype,"init",a);h.areaspline&&r(h.areaspline.prototype,"init",a);h.spline&&r(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,j,i,h,m,p,o;if(this.chart.polar){e=
-c.plotX;j=c.plotY;a=b[d-1];i=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),i||(i=b[1]));if(a&&i)h=a.plotX,m=a.plotY,b=i.plotX,p=i.plotY,h=(1.5*e+h)/2.5,m=(1.5*j+m)/2.5,i=(1.5*e+b)/2.5,o=(1.5*j+p)/2.5,b=Math.sqrt(Math.pow(h-e,2)+Math.pow(m-j,2)),p=Math.sqrt(Math.pow(i-e,2)+Math.pow(o-j,2)),h=Math.atan2(m-j,h-e),m=Math.atan2(o-j,i-e),o=Math.PI/2+(h+m)/2,Math.abs(h-o)>Math.PI/2&&(o-=Math.PI),h=e+Math.cos(o)*b,m=j+Math.sin(o)*b,i=e+Math.cos(Math.PI+o)*p,o=j+Math.sin(Math.PI+o)*p,c.rightContX=i,c.rightContY=
-o;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,h||e,m||j,e,j],a.rightContX=a.rightContY=null):c=["M",e,j]}else c=a.call(this,b,c,d);return c});r(c,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});r(c,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,
-b)});r(c,"animate",b);r(c,"setTooltipPoints",function(a,b){this.chart.polar&&F(this.xAxis,{tooltipLen:360});return a.call(this,b)});if(h.column)e=h.column.prototype,r(e,"animate",b),r(e,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,h=this.chart.renderer,i,m;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(m=b.length;m--;)i=b[m],a=i.barX+e,i.shapeType="path",i.shapeArgs={d:h.symbols.arc(d[0],d[1],c-i.plotY,null,{start:a,end:a+i.pointWidth,
-innerR:c-q(i.yBottom,c)})},this.toXY(i),i.tooltipPos=[i.plotX,i.plotY],i.ttBelow=i.plotY>d[1]}}),r(e,"alignDataLabel",function(a,b,d,e,h,j){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(e.align===null)e.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(e.verticalAlign===null)e.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";c.alignDataLabel.call(this,b,d,e,h,j)}else a.call(this,b,d,e,h,j)});r(d,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=
-b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});r(d,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?s(c.axes,function(a){var e=a.isXAxis,f=a.center,h=b.chartX-f[0]-c.plotLeft,f=b.chartY-f[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(h,f):Math.sqrt(Math.pow(h,2)+Math.pow(f,2)),!0)})}):d=a.call(this,b);return d})})()})(Highcharts);
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2013 Torstein Hønsi
-
- License: www.highcharts.com/license
-*/
-(function(c){function x(e,a,b,d){var f,g,h;b*=n;a*=n;var i=[],j,o,t;b*=-1;j=d.x;o=d.y;t=(d.z===0?1.0E-4:d.z)*(d.vd||25);var y=k(b),v=l(b),m=k(a),q=l(a),r,u,s;c.each(e,function(a){r=a.x-j;u=a.y-o;s=a.z||0;f=v*r-y*s;g=-y*m*r-v*m*s+q*u;h=y*q*r+v*q*s+m*u;f=f*((t-h)/t)+j;g=g*((t-h)/t)+o;i.push({x:C(f),y:C(g),z:C(h)})});return i}function z(e,a,b,d,f,c,h,i){var j=[];return c>f&&c-f>m/2+1.0E-4?(j=j.concat(z(e,a,b,d,f,f+m/2,h,i)),j=j.concat(z(e,a,b,d,f+m/2,c,h,i))):c<f&&f-c>m/2+1.0E-4?(j=j.concat(z(e,a,b,
-d,f,f-m/2,h,i)),j=j.concat(z(e,a,b,d,f-m/2,c,h,i))):(j=c-f,["C",e+b*l(f)-b*D*j*k(f)+h,a+d*k(f)+d*D*j*l(f)+i,e+b*l(c)+b*D*j*k(c)+h,a+d*k(c)-d*D*j*l(c)+i,e+b*l(c)+h,a+d*k(c)+i])}function F(e){if(this.chart.is3d()){var a=this.chart.options.plotOptions.column.grouping;a!==void 0&&!a&&this.group.zIndex!==void 0&&this.group.attr({zIndex:this.group.zIndex*10});if(this.userOptions.borderColor===void 0)this.options.borderColor=this.color;c.each(this.data,function(a){var d=a.options.borderColor||a.color||a.series.userOptions.borderColor;
-a.options.borderColor=d;a.borderColor=d;a.pointAttr[""].stroke=d;a.pointAttr.hover.stroke=d;a.pointAttr.select.stroke=d})}e.apply(this,[].slice.call(arguments,1))}var m=Math.PI,n=m/180,k=Math.sin,l=Math.cos,C=Math.round,D=4*(Math.sqrt(2)-1)/3/(m/2);c.SVGRenderer.prototype.toLinePath=function(e,a){var b=[];c.each(e,function(a){b.push("L",a.x,a.y)});b[0]="M";a&&b.push("Z");return b};c.SVGRenderer.prototype.cuboid=function(e){var a=this.g(),e=this.cuboidPath(e);a.front=this.path(e[0]).attr({zIndex:e[3],
-"stroke-linejoin":"round"}).add(a);a.top=this.path(e[1]).attr({zIndex:e[4],"stroke-linejoin":"round"}).add(a);a.side=this.path(e[2]).attr({zIndex:e[5],"stroke-linejoin":"round"}).add(a);a.fillSetter=function(a){var d=c.Color(a).brighten(0.1).get(),e=c.Color(a).brighten(-0.1).get();this.front.attr({fill:a});this.top.attr({fill:d});this.side.attr({fill:e});this.color=a;return this};a.opacitySetter=function(a){this.front.attr({opacity:a});this.top.attr({opacity:a});this.side.attr({opacity:a});return this};
-a.attr=function(a){a.shapeArgs||a.x?(a=this.renderer.cuboidPath(a.shapeArgs||a),this.front.attr({d:a[0],zIndex:a[3]}),this.top.attr({d:a[1],zIndex:a[4]}),this.side.attr({d:a[2],zIndex:a[5]})):c.SVGElement.prototype.attr.call(this,a);return this};a.animate=function(a,d,e){a.x&&a.y?(a=this.renderer.cuboidPath(a),this.front.attr({zIndex:a[3]}).animate({d:a[0]},d,e),this.top.attr({zIndex:a[4]}).animate({d:a[1]},d,e),this.side.attr({zIndex:a[5]}).animate({d:a[2]},d,e)):a.opacity?(this.front.animate(a,
-d,e),this.top.animate(a,d,e),this.side.animate(a,d,e)):c.SVGElement.prototype.animate.call(this,a,d,e);return this};a.destroy=function(){this.front.destroy();this.top.destroy();this.side.destroy();return null};a.attr({zIndex:-e[3]});return a};c.SVGRenderer.prototype.cuboidPath=function(e){var a=e.x,b=e.y,d=e.z,c=e.height,g=e.width,h=e.depth,i=e.alpha,j=e.beta,a=[{x:a,y:b,z:d},{x:a+g,y:b,z:d},{x:a+g,y:b+c,z:d},{x:a,y:b+c,z:d},{x:a,y:b+c,z:d+h},{x:a+g,y:b+c,z:d+h},{x:a+g,y:b,z:d+h},{x:a,y:b,z:d+h}],
-a=x(a,i,j,e.origin),e=["M",a[0].x,a[0].y,"L",a[7].x,a[7].y,"L",a[6].x,a[6].y,"L",a[1].x,a[1].y,"Z"],b=["M",a[3].x,a[3].y,"L",a[2].x,a[2].y,"L",a[5].x,a[5].y,"L",a[4].x,a[4].y,"Z"],d=["M",a[1].x,a[1].y,"L",a[2].x,a[2].y,"L",a[5].x,a[5].y,"L",a[6].x,a[6].y,"Z"],c=["M",a[0].x,a[0].y,"L",a[7].x,a[7].y,"L",a[4].x,a[4].y,"L",a[3].x,a[3].y,"Z"];return[["M",a[0].x,a[0].y,"L",a[1].x,a[1].y,"L",a[2].x,a[2].y,"L",a[3].x,a[3].y,"Z"],a[7].y<a[1].y?e:a[4].y>a[2].y?b:[],a[6].x>a[1].x?d:a[7].x<a[0].x?c:[],(a[0].z+
-a[1].z+a[2].z+a[3].z)/4,j>0?(a[0].z+a[7].z+a[6].z+a[1].z)/4:(a[3].z+a[2].z+a[5].z+a[4].z)/4,i>0?(a[1].z+a[2].z+a[5].z+a[6].z)/4:(a[0].z+a[7].z+a[4].z+a[3].z)/4]};c.SVGRenderer.prototype.arc3d=function(e){e.alpha*=n;e.beta*=n;var a=this.g(),b=this.arc3dPath(e),d=a.renderer,f=b.zAll*100;a.shapeArgs=e;a.side1=d.path(b.side2).attr({zIndex:b.zSide2}).add(a);a.side2=d.path(b.side1).attr({zIndex:b.zSide1}).add(a);a.inn=d.path(b.inn).attr({zIndex:b.zInn}).add(a);a.out=d.path(b.out).attr({zIndex:b.zOut}).add(a);
-a.top=d.path(b.top).attr({zIndex:b.zTop}).add(a);a.fillSetter=function(a){this.color=a;var b=c.Color(a).brighten(-0.1).get();this.side1.attr({fill:b});this.side2.attr({fill:b});this.inn.attr({fill:b});this.out.attr({fill:b});this.top.attr({fill:a});return this};a.animate=function(a,b,d){c.SVGElement.prototype.animate.call(this,a,b,d);if(a.x&&a.y)b=this.renderer,a=c.splat(a)[0],a.alpha*=n,a.beta*=n,b=b.arc3dPath(a),this.shapeArgs=a,this.inn.attr({d:b.inn,zIndex:b.zInn}),this.out.attr({d:b.out,zIndex:b.zOut}),
-this.side1.attr({d:b.side1,zIndex:b.zSide2}),this.side2.attr({d:b.side2,zIndex:b.zSide1}),this.top.attr({d:b.top,zIndex:b.zTop}),this.attr({fill:this.color}),this.attr({zIndex:b.zAll*100});return this};a.zIndex=f;a.attr({zIndex:f});return a};c.SVGRenderer.prototype.arc3dPath=function(e){var a=e.x,b=e.y,d=e.start,c=e.end-1.0E-5,g=e.r,h=e.innerR,i=e.depth,j=e.alpha,o=e.beta,t=l(d),y=k(d),v=l(c),n=k(c),q=g*l(o),r=g*l(j),u=h*l(o),s=h*l(j),A=i*k(o),B=i*k(j),i=["M",a+q*t,b+r*y],i=i.concat(z(a,b,q,r,d,c,
-0,0)),i=i.concat(["L",a+u*v,b+s*n]),i=i.concat(z(a,b,u,s,c,d,0,0)),i=i.concat(["Z"]),e=(e.start+e.end)/2,e=k(o)*l(e)+k(-j)*k(-e),p=o>0?m/2:0,w=j>0?0:m/2,p=d>-p?d:c>-p?-p:d,x=c<m-w?c:d<m-w?m-w:c,w=["M",a+q*l(p),b+r*k(p)],w=w.concat(z(a,b,q,r,p,x,0,0)),w=w.concat(["L",a+q*l(x)+A,b+r*k(x)+B]),w=w.concat(z(a,b,q,r,x,p,A,B)),w=w.concat(["Z"]),p=["M",a+u*t,b+s*y],p=p.concat(z(a,b,u,s,d,c,0,0)),p=p.concat(["L",a+u*l(c)+A,b+s*k(c)+B]),p=p.concat(z(a,b,u,s,c,d,A,B)),p=p.concat(["Z"]),t=["M",a+q*t,b+r*y,"L",
-a+q*t+A,b+r*y+B,"L",a+u*t+A,b+s*y+B,"L",a+u*t,b+s*y,"Z"],a=["M",a+q*v,b+r*n,"L",a+q*v+A,b+r*n+B,"L",a+u*v+A,b+s*n+B,"L",a+u*v,b+s*n,"Z"],v=h+(g-h)/2,b=Math.abs(e*2*v);g*=e;h*=e;d=(k(o)*l(d)+k(-j)*k(-d))*v;c=(k(o)*l(c)+k(-j)*k(-c))*v;return{top:i,zTop:b*100,out:w,zOut:g*100,inn:p,zInn:h*100,side1:t,zSide1:d*100,side2:a,zSide2:c*100,zAll:e}};c.Chart.prototype.is3d=function(){return this.options.chart.options3d&&this.options.chart.options3d.enabled};c.wrap(c.Chart.prototype,"isInsidePlot",function(c){return this.is3d()?
-!0:c.apply(this,[].slice.call(arguments,1))});c.wrap(c.Chart.prototype,"init",function(e){var a=arguments;a[1]=c.merge({chart:{options3d:{enabled:!1,alpha:0,beta:0,depth:100,viewDistance:25,frame:{bottom:{size:1,color:"rgba(255,255,255,0)"},side:{size:1,color:"rgba(255,255,255,0)"},back:{size:1,color:"rgba(255,255,255,0)"}}}}},a[1]);e.apply(this,[].slice.call(a,1))});c.wrap(c.Chart.prototype,"setChartSize",function(c){c.apply(this,[].slice.call(arguments,1));if(this.is3d()){var a=this.inverted,b=
-this.clipBox,d=this.margin;b[a?"y":"x"]=-(d[3]||0);b[a?"x":"y"]=-(d[0]||0);b[a?"height":"width"]=this.chartWidth+(d[3]||0)+(d[1]||0);b[a?"width":"height"]=this.chartHeight+(d[0]||0)+(d[2]||0)}});c.wrap(c.Chart.prototype,"redraw",function(c){if(this.is3d())this.isDirtyBox=!0;c.apply(this,[].slice.call(arguments,1))});c.Chart.prototype.retrieveStacks=function(){var e={},a=this.options.plotOptions[this.options.chart.type],b=a.stacking,d=1;if(a.grouping||!b)return this.series;c.each(this.series,function(a){e[a.options.stack||
-0]?e[a.options.stack||0].series.push(a):(e[a.options.stack||0]={series:[a],position:d},d++)});e.totalStacks=d+1;return e};c.wrap(c.Axis.prototype,"init",function(e){var a=arguments;if(a[1].is3d())a[2].tickWidth=c.pick(a[2].tickWidth,0),a[2].gridLineWidth=c.pick(a[2].gridLineWidth,1);e.apply(this,[].slice.call(arguments,1))});c.wrap(c.Axis.prototype,"render",function(c){c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.renderer,d=a.options.chart.options3d,f=d.alpha,
-g=d.beta*(a.yAxis[0].opposite?-1:1),h=d.frame,i=h.bottom,j=h.back,h=h.side,o=d.depth,k=this.height,l=this.width,m=this.left,n=this.top,d={x:a.plotLeft+a.plotWidth/2,y:a.plotTop+a.plotHeight/2,z:o,vd:d.viewDistance};if(this.horiz)this.axisLine&&this.axisLine.hide(),g={x:m,y:n+(a.yAxis[0].reversed?-i.size:k),z:0,width:l,height:i.size,depth:o,alpha:f,beta:g,origin:d},this.bottomFrame?this.bottomFrame.animate(g):this.bottomFrame=b.cuboid(g).attr({fill:i.color,zIndex:a.yAxis[0].reversed&&f>0?4:-1}).css({stroke:i.color}).add();
-else{var q={x:m,y:n,z:o+1,width:l,height:k+i.size,depth:j.size,alpha:f,beta:g,origin:d};this.backFrame?this.backFrame.animate(q):this.backFrame=b.cuboid(q).attr({fill:j.color,zIndex:-3}).css({stroke:j.color}).add();this.axisLine&&this.axisLine.hide();a={x:(a.yAxis[0].opposite?l:0)+m-h.size,y:n,z:0,width:h.size,height:k+i.size,depth:o+j.size,alpha:f,beta:g,origin:d};this.sideFrame?this.sideFrame.animate(a):this.sideFrame=b.cuboid(a).attr({fill:h.color,zIndex:-2}).css({stroke:h.color}).add()}}});c.wrap(c.Axis.prototype,
-"getPlotLinePath",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.chart.is3d())return a;if(a===null)return a;var b=this.chart,d=b.options.chart.options3d,f=d.depth;d.origin={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:f,vd:d.viewDistance};var a=[{x:a[1],y:a[2],z:this.horiz||this.opposite?f:0},{x:a[1],y:a[2],z:f},{x:a[4],y:a[5],z:f},{x:a[4],y:a[5],z:this.horiz||this.opposite?0:f}],f=b.options.inverted?d.beta:d.alpha,g=b.options.inverted?d.alpha:d.beta;g*=b.yAxis[0].opposite?
--1:1;a=x(a,f,g,d.origin);return a=this.chart.renderer.toLinePath(a,!1)});c.wrap(c.Tick.prototype,"getMarkPath",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.axis.chart.is3d())return a;var b=this.axis.chart,d=b.options.chart.options3d,f={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:d.depth,vd:d.viewDistance},a=[{x:a[1],y:a[2],z:0},{x:a[4],y:a[5],z:0}],g=b.inverted?d.beta:d.alpha,d=b.inverted?d.alpha:d.beta;d*=b.yAxis[0].opposite?-1:1;a=x(a,g,d,f);return a=["M",a[0].x,
-a[0].y,"L",a[1].x,a[1].y]});c.wrap(c.Tick.prototype,"getLabelPosition",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(!this.axis.chart.is3d())return a;var b=this.axis.chart,d=b.options.chart.options3d,f={x:b.plotLeft+b.plotWidth/2,y:b.plotTop+b.plotHeight/2,z:d.depth,vd:d.viewDistance},g=b.inverted?d.beta:d.alpha,d=b.inverted?d.alpha:d.beta;d*=b.yAxis[0].opposite?-1:1;return a=x([{x:a.x,y:a.y,z:0}],g,d,f)[0]});c.wrap(c.Axis.prototype,"drawCrosshair",function(c){var a=arguments;this.chart.is3d()&&
-a[2]&&(a[2]={plotX:a[2].plotXold||a[2].plotX,plotY:a[2].plotYold||a[2].plotY});c.apply(this,[].slice.call(a,1))});c.wrap(c.seriesTypes.column.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.options,d=b.plotOptions[this.chart.options.chart.type],b=b.chart.options3d,f=d.depth||25,g={x:a.plotWidth/2,y:a.plotHeight/2,z:b.depth,vd:b.viewDistance},h=b.alpha,i=b.beta*(a.yAxis[0].opposite?-1:1),j=(d.stacking?this.options.stack||0:this._i)*
-(f+(d.groupZPadding||1));d.grouping!==!1&&(j=0);j+=d.groupZPadding||1;c.each(this.data,function(a){var b=a.shapeArgs,c=a.tooltipPos;a.shapeType="cuboid";b.alpha=h;b.beta=i;b.z=j;b.origin=g;b.depth=f;c=x([{x:c[0],y:c[1],z:j}],h,i,g)[0];a.tooltipPos=[c.x,c.y]})}});c.wrap(c.seriesTypes.column.prototype,"animate",function(e){if(this.chart.is3d()){var a=arguments[1],b=this.yAxis,d=this,f=this.yAxis.reversed;if(c.svg)a?c.each(d.data,function(a){a.height=a.shapeArgs.height;a.shapeArgs.height=1;if(!f)a.shapeArgs.y=
-a.stackY?a.plotY+b.translate(a.stackY):a.plotY+(a.negative?-a.height:a.height)}):(c.each(d.data,function(a){a.shapeArgs.height=a.height;if(!f)a.shapeArgs.y=a.plotY-(a.negative?a.height:0);a.graphic&&a.graphic.animate(a.shapeArgs,d.options.animation)}),d.animate=null)}else e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.column.prototype,"init",function(c){c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart.options.plotOptions.column.grouping,b=this.chart.options.plotOptions.column.stacking,
-d=this.options.zIndex;if(!d&&(a===void 0||a)&&b){a=this.chart.retrieveStacks();b=this.options.stack||0;for(d=0;d<a[b].series.length;d++)if(a[b].series[d]===this)break;d=a.totalStacks*10-10*(a.totalStacks-a[b].position)-d;this.options.zIndex=d}}});c.seriesTypes.columnrange&&c.wrap(c.seriesTypes.columnrange.prototype,"drawPoints",F);c.wrap(c.seriesTypes.column.prototype,"drawPoints",F);var E=c.getOptions();E.plotOptions.cylinder=c.merge(E.plotOptions.column);E=c.extendClass(c.seriesTypes.column,{type:"cylinder"});
-c.seriesTypes.cylinder=E;c.wrap(c.seriesTypes.cylinder.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=a.options,d=b.plotOptions.cylinder,b=b.chart.options3d,f=d.depth||0,g={x:a.inverted?a.plotHeight/2:a.plotWidth/2,y:a.inverted?a.plotWidth/2:a.plotHeight/2,z:b.depth,vd:b.viewDistance},h=b.alpha,i=d.stacking?(this.options.stack||0)*f:this._i*f;i+=f/2;d.grouping!==!1&&(i=0);c.each(this.data,function(a){var b=a.shapeArgs;a.shapeType=
-"arc3d";b.x+=f/2;b.z=i;b.start=0;b.end=2*m;b.r=f*0.95;b.innerR=0;b.depth=b.height*(1/k((90-h)*n))-i;b.alpha=90-h;b.beta=0;b.origin=g})}});c.wrap(c.seriesTypes.pie.prototype,"translate",function(e){e.apply(this,[].slice.call(arguments,1));if(this.chart.is3d()){var a=this,b=a.chart,d=b.options,f=d.plotOptions.pie,g=f.depth||0,d=d.chart.options3d,h={x:b.plotWidth/2,y:b.plotHeight/2,z:d.depth},i=d.alpha,j=d.beta,o=f.stacking?(this.options.stack||0)*g:a._i*g;o+=g/2;f.grouping!==!1&&(o=0);c.each(a.data,
-function(b){b.shapeType="arc3d";var c=b.shapeArgs;c.z=o;c.depth=g*0.75;c.origin=h;c.alpha=i;c.beta=j;c=(c.end+c.start)/2;b.slicedTranslation={translateX:C(l(c)*a.options.slicedOffset*l(i*n)),translateY:C(k(c)*a.options.slicedOffset*l(i*n))}})}});c.wrap(c.seriesTypes.pie.prototype.pointClass.prototype,"haloPath",function(c){return this.series.chart.is3d()?[]:c.call(this)});c.wrap(c.seriesTypes.pie.prototype,"drawPoints",function(e){this.chart.is3d()&&c.each(this.data,function(a){var b=a.options.borderColor||
-a.color||a.series.userOptions.borderColor||a.series.color;a.options.borderColor=b;a.borderColor=b;a.pointAttr[""].stroke=b;a.pointAttr.hover.stroke=b;a.pointAttr.select.stroke=b});e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.pie.prototype,"drawDataLabels",function(e){e.apply(this,[].slice.call(arguments,1));this.chart.is3d()&&c.each(this.data,function(a){var b=a.shapeArgs,c=b.r,e=b.depth,g=b.alpha*n,h=b.beta*n,b=(b.start+b.end)/2;a.connector&&a.connector.translate(-c*(1-l(h))*l(b)+
-(l(b)>0?k(h)*e:0),-c*(1-l(g))*k(b)+(k(b)>0?k(g)*e:0));a.dataLabel&&a.dataLabel.attr({x:a.dataLabel.connX+-c*(1-l(h))*l(b)+(l(b)>0?l(h)*e:0)-a.dataLabel.width/2,y:a.dataLabel.connY+-c*(1-l(g))*k(b)+(k(b)>0?k(g)*e:0)-a.dataLabel.height/2})})});c.wrap(c.seriesTypes.pie.prototype,"addPoint",function(c){c.apply(this,[].slice.call(arguments,1));this.chart.is3d()&&this.update()});c.wrap(c.seriesTypes.pie.prototype,"animate",function(e){if(this.chart.is3d()){var a=arguments[1],b=this.options.animation,d=
-this.center,f=this.group,g=this.markerGroup;if(c.svg)if(b===!0&&(b={}),a){if(this.oldtranslateX=f.translateX,this.oldtranslateY=f.translateY,a={translateX:d[0],translateY:d[1],scaleX:0.001,scaleY:0.001},f.attr(a),g)g.attrSetters=f.attrSetters,g.attr(a)}else a={translateX:this.oldtranslateX,translateY:this.oldtranslateY,scaleX:1,scaleY:1},f.animate(a,b),g&&g.animate(a,b),this.animate=null}else e.apply(this,[].slice.call(arguments,1))});c.wrap(c.seriesTypes.scatter.prototype,"translate",function(e){e.apply(this,
-[].slice.call(arguments,1));if(this.chart.is3d()){var a=this.chart,b=this.chart.options.chart.options3d,d=b.alpha,f=b.beta,g={x:a.inverted?a.plotHeight/2:a.plotWidth/2,y:a.inverted?a.plotWidth/2:a.plotHeight/2,z:b.depth,vd:b.viewDistance},b=b.depth,h=a.options.zAxis||{min:0,max:b},i=b/(h.max-h.min);c.each(this.data,function(a){var b={x:a.plotX,y:a.plotY,z:(a.z-h.min)*i},b=x([b],d,f,g)[0];a.plotXold=a.plotX;a.plotYold=a.plotY;a.plotX=b.x;a.plotY=b.y;a.plotZ=b.z})}});c.wrap(c.seriesTypes.scatter.prototype,
-"init",function(c){var a=c.apply(this,[].slice.call(arguments,1));if(this.chart.is3d())this.pointArrayMap=["x","y","z"],this.tooltipOptions.pointFormat=this.userOptions.tooltip?this.userOptions.tooltip.pointFormat||"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>":"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>";return a});if(c.VMLRenderer)c.setOptions({animate:!1}),c.VMLRenderer.prototype.cuboid=c.SVGRenderer.prototype.cuboid,c.VMLRenderer.prototype.cuboidPath=
-c.SVGRenderer.prototype.cuboidPath,c.VMLRenderer.prototype.toLinePath=c.SVGRenderer.prototype.toLinePath,c.VMLRenderer.prototype.createElement3D=c.SVGRenderer.prototype.createElement3D,c.VMLRenderer.prototype.arc3d=function(e){e=c.SVGRenderer.prototype.arc3d.call(this,e);e.css({zIndex:e.zIndex});return e},c.VMLRenderer.prototype.arc3dPath=c.SVGRenderer.prototype.arc3dPath,c.Chart.prototype.renderSeries=function(){for(var c,a=this.series.length;a--;)c=this.series[a],c.translate(),c.setTooltipPoints&&
-c.setTooltipPoints(),c.render()},c.wrap(c.Axis.prototype,"render",function(c){c.apply(this,[].slice.call(arguments,1));this.sideFrame&&(this.sideFrame.css({zIndex:0}),this.sideFrame.front.attr({fill:this.sideFrame.color}));this.bottomFrame&&(this.bottomFrame.css({zIndex:1}),this.bottomFrame.front.attr({fill:this.bottomFrame.color}));this.backFrame&&(this.backFrame.css({zIndex:0}),this.backFrame.front.attr({fill:this.backFrame.color}))})})(Highcharts);
-/*
- Highcharts JS v4.0.1 (2014-04-24)
- Exporting module
-
- (c) 2010-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(f){var A=f.Chart,t=f.addEvent,B=f.removeEvent,l=f.createElement,o=f.discardElement,v=f.css,k=f.merge,r=f.each,p=f.extend,D=Math.max,j=document,C=window,E=f.isTouchDevice,F=f.Renderer.prototype.symbols,s=f.getOptions(),y;p(s.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});s.navigation={menuStyle:{border:"1px solid #A0A0A0",
-background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:E?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};s.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{menuClassName:"highcharts-contextmenu",
-symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"applications/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(b,a,d){var c,b=l("form",k({method:"post",
-action:b,enctype:"multipart/form-data"},d),{display:"none"},j.body);for(c in a)l("input",{type:"hidden",name:c,value:a[c]},null,b);b.submit();o(b)};p(A.prototype,{getSVG:function(b){var a=this,d,c,z,h,g=k(a.options,b);if(!j.createElementNS)j.createElementNS=function(a,b){return j.createElement(b)};b=l("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},j.body);c=a.renderTo.style.width;h=a.renderTo.style.height;c=g.exporting.sourceWidth||g.chart.width||
-/px$/.test(c)&&parseInt(c,10)||600;h=g.exporting.sourceHeight||g.chart.height||/px$/.test(h)&&parseInt(h,10)||400;p(g.chart,{animation:!1,renderTo:b,forExport:!0,width:c,height:h});g.exporting.enabled=!1;g.series=[];r(a.series,function(a){z=k(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});z.isInternal||g.series.push(z)});d=new f.Chart(g,a.callback);r(["xAxis","yAxis"],function(b){r(a[b],function(a,c){var g=d[b][c],f=a.getExtremes(),h=f.userMin,f=f.userMax;g&&(h!==void 0||f!==void 0)&&
-g.setExtremes(h,f,!0,!1)})});c=d.container.innerHTML;g=null;d.destroy();o(b);c=c.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/&nbsp;/g," ").replace(/&shy;/g,"­").replace(/<IMG /g,"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g,
-'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});return c=c.replace(/(url\(#highcharts-[0-9]+)&quot;/g,"$1").replace(/&quot;/g,"'")},exportChart:function(b,a){var b=b||{},d=this.options.exporting,d=this.getSVG(k({chart:{borderRadius:0}},d.chartOptions,a,{exporting:{sourceWidth:b.sourceWidth||
-d.sourceWidth,sourceHeight:b.sourceHeight||d.sourceHeight}})),b=k(this.options.exporting,b);f.post(b.url,{filename:b.filename||"chart",type:b.type,width:b.width||0,scale:b.scale||2,svg:d},b.formAttributes)},print:function(){var b=this,a=b.container,d=[],c=a.parentNode,f=j.body,h=f.childNodes;if(!b.isPrinting)b.isPrinting=!0,r(h,function(a,b){if(a.nodeType===1)d[b]=a.style.display,a.style.display="none"}),f.appendChild(a),C.focus(),C.print(),setTimeout(function(){c.appendChild(a);r(h,function(a,b){if(a.nodeType===
-1)a.style.display=d[b]});b.isPrinting=!1},1E3)},contextMenu:function(b,a,d,c,f,h,g){var e=this,k=e.options.navigation,q=k.menuItemStyle,m=e.chartWidth,n=e.chartHeight,j="cache-"+b,i=e[j],u=D(f,h),w,x,o,s=function(a){e.pointer.inClass(a.target,b)||x()};if(!i)e[j]=i=l("div",{className:b},{position:"absolute",zIndex:1E3,padding:u+"px"},e.container),w=l("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},k.menuStyle),i),x=function(){v(i,{display:"none"});
-g&&g.setState(0);e.openMenu=!1},t(i,"mouseleave",function(){o=setTimeout(x,500)}),t(i,"mouseenter",function(){clearTimeout(o)}),t(document,"mouseup",s),t(e,"destroy",function(){B(document,"mouseup",s)}),r(a,function(a){if(a){var b=a.separator?l("hr",null,null,w):l("div",{onmouseover:function(){v(this,k.menuItemHoverStyle)},onmouseout:function(){v(this,q)},onclick:function(){x();a.onclick.apply(e,arguments)},innerHTML:a.text||e.options.lang[a.textKey]},p({cursor:"pointer"},q),w);e.exportDivElements.push(b)}}),
-e.exportDivElements.push(w,i),e.exportMenuWidth=i.offsetWidth,e.exportMenuHeight=i.offsetHeight;a={display:"block"};d+e.exportMenuWidth>m?a.right=m-d-f-u+"px":a.left=d-u+"px";c+h+e.exportMenuHeight>n&&g.alignOptions.verticalAlign!=="top"?a.bottom=n-c-u+"px":a.top=c+h-u+"px";v(i,a);e.openMenu=!0},addButton:function(b){var a=this,d=a.renderer,c=k(a.options.navigation.buttonOptions,b),j=c.onclick,h=c.menuItems,g,e,l={stroke:c.symbolStroke,fill:c.symbolFill},q=c.symbolSize||12;if(!a.btnCount)a.btnCount=
-0;if(!a.exportDivElements)a.exportDivElements=[],a.exportSVGElements=[];if(c.enabled!==!1){var m=c.theme,n=m.states,o=n&&n.hover,n=n&&n.select,i;delete m.states;j?i=function(){j.apply(a,arguments)}:h&&(i=function(){a.contextMenu(e.menuClassName,h,e.translateX,e.translateY,e.width,e.height,e);e.setState(2)});c.text&&c.symbol?m.paddingLeft=f.pick(m.paddingLeft,25):c.text||p(m,{width:c.width,height:c.height,padding:0});e=d.button(c.text,0,0,i,m,o,n).attr({title:a.options.lang[c._titleKey],"stroke-linecap":"round"});
-e.menuClassName=b.menuClassName||"highcharts-menu-"+a.btnCount++;c.symbol&&(g=d.symbol(c.symbol,c.symbolX-q/2,c.symbolY-q/2,q,q).attr(p(l,{"stroke-width":c.symbolStrokeWidth||1,zIndex:1})).add(e));e.add().align(p(c,{width:e.width,x:f.pick(c.x,y)}),!0,"spacingBox");y+=(e.width+c.buttonSpacing)*(c.align==="right"?-1:1);a.exportSVGElements.push(e,g)}},destroyExport:function(b){var b=b.target,a,d;for(a=0;a<b.exportSVGElements.length;a++)if(d=b.exportSVGElements[a])d.onclick=d.ontouchstart=null,b.exportSVGElements[a]=
-d.destroy();for(a=0;a<b.exportDivElements.length;a++)d=b.exportDivElements[a],B(d,"mouseleave"),b.exportDivElements[a]=d.onmouseout=d.onmouseover=d.ontouchstart=d.onclick=null,o(d)}});F.menu=function(b,a,d,c){return["M",b,a+2.5,"L",b+d,a+2.5,"M",b,a+c/2+0.5,"L",b+d,a+c/2+0.5,"M",b,a+c-1.5,"L",b+d,a+c-1.5]};A.prototype.callbacks.push(function(b){var a,d=b.options.exporting,c=d.buttons;y=0;if(d.enabled!==!1){for(a in c)b.addButton(c[a]);t(b,"destroy",b.destroyExport)}})})(Highcharts);
-/*
- Data plugin for Highcharts
-
- (c) 2012-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(j){var m=j.each,n=function(a,b){this.init(a,b)};j.extend(n.prototype,{init:function(a,b){this.options=a;this.chartOptions=b;this.columns=a.columns||this.rowsToColumns(a.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},getColumnDistribution:function(){var a=this.chartOptions,b=a&&a.chart&&a.chart.type,c=[];m(a&&a.series||[],function(a){c.push((j.seriesTypes[a.type||b||"line"].prototype.pointArrayMap||[0]).length)});this.valueCount=
-{global:(j.seriesTypes[b||"line"].prototype.pointArrayMap||[0]).length,individual:c}},dataFound:function(){if(this.options.switchRowsAndColumns)this.columns=this.rowsToColumns(this.columns);this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this,b=this.options,c=b.csv,d=this.columns,e=b.startRow||0,h=b.endRow||Number.MAX_VALUE,i=b.startColumn||0,g=b.endColumn||Number.MAX_VALUE,f,k,o=0;c&&(k=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(b.lineDelimiter||
-"\n"),f=b.itemDelimiter||(c.indexOf("\t")!==-1?"\t":","),m(k,function(b,c){var k=a.trim(b),j=k.indexOf("#")===0;c>=e&&c<=h&&!j&&k!==""&&(k=b.split(f),m(k,function(b,a){a>=i&&a<=g&&(d[a-i]||(d[a-i]=[]),d[a-i][o]=b)}),o+=1)}),this.dataFound())},parseTable:function(){var a=this.options,b=a.table,c=this.columns,d=a.startRow||0,e=a.endRow||Number.MAX_VALUE,h=a.startColumn||0,i=a.endColumn||Number.MAX_VALUE;b&&(typeof b==="string"&&(b=document.getElementById(b)),m(b.getElementsByTagName("tr"),function(a,
-b){b>=d&&b<=e&&m(a.children,function(a,e){if((a.tagName==="TD"||a.tagName==="TH")&&e>=h&&e<=i)c[e-h]||(c[e-h]=[]),c[e-h][b-d]=a.innerHTML})}),this.dataFound())},parseGoogleSpreadsheet:function(){var a=this,b=this.options,c=b.googleSpreadsheetKey,d=this.columns,e=b.startRow||0,h=b.endRow||Number.MAX_VALUE,i=b.startColumn||0,g=b.endColumn||Number.MAX_VALUE,f,k;c&&jQuery.ajax({dataType:"json",url:"https://spreadsheets.google.com/feeds/cells/"+c+"/"+(b.googleSpreadsheetWorksheet||"od6")+"/public/values?alt=json-in-script&callback=?",
-error:b.error,success:function(b){var b=b.feed.entry,c,j=b.length,m=0,n=0,l;for(l=0;l<j;l++)c=b[l],m=Math.max(m,c.gs$cell.col),n=Math.max(n,c.gs$cell.row);for(l=0;l<m;l++)if(l>=i&&l<=g)d[l-i]=[],d[l-i].length=Math.min(n,h-e);for(l=0;l<j;l++)if(c=b[l],f=c.gs$cell.row-1,k=c.gs$cell.col-1,k>=i&&k<=g&&f>=e&&f<=h)d[k-i][f-e]=c.content.$t;a.dataFound()}})},findHeaderRow:function(){m(this.columns,function(){});this.headerRow=0},trim:function(a){return typeof a==="string"?a.replace(/^\s+|\s+$/g,""):a},parseTypes:function(){for(var a=
-this.columns,b=a.length,c,d,e,h;b--;)for(c=a[b].length;c--;)d=a[b][c],e=parseFloat(d),h=this.trim(d),h==e?(a[b][c]=e,e>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(d=this.parseDate(d),b===0&&typeof d==="number"&&!isNaN(d)?(a[b][c]=d,a[b].isDatetime=!0):a[b][c]=h===""?null:h)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(a){return Date.UTC(+a[1],a[2]-1,+a[3])}}},parseDate:function(a){var b=this.options.parseDate,c,d,e;b&&(c=b(a));if(typeof a==="string")for(d in this.dateFormats)b=
-this.dateFormats[d],(e=a.match(b.regex))&&(c=b.parser(e));return c},rowsToColumns:function(a){var b,c,d,e,h;if(a){h=[];c=a.length;for(b=0;b<c;b++){e=a[b].length;for(d=0;d<e;d++)h[d]||(h[d]=[]),h[d][b]=a[b][d]}}return h},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var a=this.columns,b,c,d=this.options,e,h,i,g,f,k;if(d.complete){this.getColumnDistribution();a.length>1&&(b=a.shift(),this.headerRow===0&&b.shift(),b.isDatetime?c="datetime":b.isNumeric||
-(c="category"));for(g=0;g<a.length;g++)if(this.headerRow===0)a[g].name=a[g].shift();h=[];for(g=0,k=0;g<a.length;k++){e=j.pick(this.valueCount.individual[k],this.valueCount.global);i=[];if(g+e<=a.length)for(f=0;f<a[g].length;f++)i[f]=[b[f],a[g][f]!==void 0?a[g][f]:null],e>1&&i[f].push(a[g+1][f]!==void 0?a[g+1][f]:null),e>2&&i[f].push(a[g+2][f]!==void 0?a[g+2][f]:null),e>3&&i[f].push(a[g+3][f]!==void 0?a[g+3][f]:null),e>4&&i[f].push(a[g+4][f]!==void 0?a[g+4][f]:null);h[k]={name:a[g].name,data:i};g+=
-e}d.complete({xAxis:{type:c},series:h})}}});j.Data=n;j.data=function(a,b){return new n(a,b)};j.wrap(j.Chart.prototype,"init",function(a,b,c){var d=this;b&&b.data?j.data(j.extend(b.data,{complete:function(e){b.hasOwnProperty("series")&&(typeof b.series==="object"?m(b.series,function(a,c){b.series[c]=j.merge(a,e.series[c])}):delete b.series);b=j.merge(e,b);a.call(d,b,c)}}),b):a.call(d,b,c)})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/highcharts-more.js b/apps/static/js/plugins/highcharts/highcharts-more.js
deleted file mode 100644
index 3cc51350f..000000000
--- a/apps/static/js/plugins/highcharts/highcharts-more.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(m,C){function K(a,b,c){this.init.call(this,a,b,c)}var O=m.arrayMin,P=m.arrayMax,s=m.each,F=m.extend,o=m.merge,Q=m.map,q=m.pick,x=m.pInt,p=m.getOptions().plotOptions,h=m.seriesTypes,u=m.extendClass,L=m.splat,r=m.wrap,M=m.Axis,y=m.Tick,H=m.Point,R=m.Pointer,S=m.CenteredSeriesMixin,z=m.TrackerMixin,t=m.Series,v=Math,D=v.round,A=v.floor,T=v.max,U=m.Color,w=function(){};F(K.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background={};d.options=a=o(e,a);
-(a=a.background)&&s([].concat(L(a)).reverse(),function(a){var g=a.backgroundColor,a=o(d.defaultBackgroundOptions,a);if(g)a.backgroundColor=g;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE,outerRadius:"105%"}});
-var G=M.prototype,y=y.prototype,V={getOffset:w,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:w,setCategories:w,setTitle:w},N={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null,distance:15,x:0,y:null},
-maxPadding:0,minPadding:0,showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){a=this.options=o(this.defaultOptions,this.defaultRadialOptions,a);if(!a.plotBands)a.plotBands=[]},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=S.getCenter.call(this.pane)},getLinePath:function(a,b){var c=this.center,b=q(b,
-c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center)this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.minPixelPadding=this.isXAxis?this.transA*this.minPointOffset:0},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&
-1||this.pointRange||this.closestPointRange||0)},setAxisSize:function(){G.setAxisSize.call(this);if(this.isRadial){this.center=this.pane.center=m.CenteredSeriesMixin.getCenter.call(this.pane);if(this.isCircular)this.sector=this.endAngleRad-this.startAngleRad;this.len=this.width=this.height=this.center[2]*q(this.sector,1)/2}},getPosition:function(a,b){return this.postTranslate(this.isCircular?this.translate(a):0,q(this.isCircular?b:this.translate(a),this.center[2]/2)-this.offset)},postTranslate:function(a,
-b){var c=this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,f=d[2]/2,g=[q(c.outerRadius,"100%"),c.innerRadius,q(c.thickness,10)],k=/%$/,l,n=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(n||(g[0]=this.translate(a),g[1]=this.translate(b)),g=Q(g,function(a){k.test(a)&&(a=x(a,10)*f/100);
-return a}),c.shape==="circle"||!n?(a=-Math.PI/2,b=Math.PI*1.5,l=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],g[0],g[0],{start:a,end:b,innerR:q(g[1],g[0]-g[2]),open:l}));return d},getPlotLinePath:function(a,b){var c=this,d=c.center,e=c.chart,f=c.getPosition(a),g,k,l;c.isCircular?l=["M",d[0]+e.plotLeft,d[1]+e.plotTop,"L",f.x,f.y]:c.options.gridLineInterpolation==="circle"?(a=c.translate(a))&&(l=c.getLinePath(0,a)):(s(e.xAxis,function(a){a.pane===
-c.pane&&(g=a)}),l=[],a=c.translate(a),d=g.tickPositions,g.autoConnect&&(d=d.concat([d[0]])),b&&(d=[].concat(d).reverse()),s(d,function(f,c){k=g.getPosition(f,a);l.push(c?"L":"M",k.x,k.y)}));return l},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};r(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,f=c.isX,g=d&&f,k,l;l=b.options;var n=c.pane||0;if(d){if(F(this,
-g?V:N),k=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)F(this,N),this.defaultRadialOptions=(k=f)?this.defaultRadialXOptions:o(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!g&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[n]=b.panes[n]||new K(L(l.pane)[n],b,this),n=i);n=n.options;b.inverted=!1;l.chart.zoomType=null;this.startAngleRad=b=(n.startAngle-90)*Math.PI/180;this.endAngleRad=l=(q(n.endAngle,n.startAngle+360)-90)*Math.PI/
-180;this.offset=a.offset||0;if((this.isCircular=k)&&c.max===C&&l-b===2*Math.PI)this.autoConnect=!0}});r(y,"getPosition",function(a,b,c,d,e){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,e)});r(y,"getLabelPosition",function(a,b,c,d,e,f,g,k,l){var n=this.axis,j=f.y,i=f.align,h=(n.translate(this.pos)+n.startAngleRad+Math.PI/2)/Math.PI*180%360;n.isRadial?(a=n.getPosition(this.pos,n.center[2]/2+q(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:h}):j===null&&(j=n.chart.renderer.fontMetrics(d.styles.fontSize).b-
-d.getBBox().height/2),i===null&&(i=n.isCircular?h>20&&h<160?"left":h>200&&h<340?"right":"center":"center",d.attr({align:i})),a.x+=f.x,a.y+=j):a=a.call(this,b,c,d,e,f,g,k,l);return a});r(y,"getMarkPath",function(a,b,c,d,e,f,g){var k=this.axis;k.isRadial?(a=k.getPosition(this.pos,k.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,g);return b});p.arearange=o(p.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
-trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0},states:{hover:{halo:!1}}});h.arearange=u(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;s(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});t.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this);
-s(this.points,function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.translate(d,0,1,0,1)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.translate(d,0,1,0,1))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=t.prototype.getSegmentPath,f,g;g=this.options;var k=g.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=e.call(this,
-b);if(k)k===!0&&(k="left"),g.step={left:"right",center:"center",right:"left"}[k];c=e.call(this,c);g.step=k;g=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return g},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=t.prototype,f=this.options.dataLabels,g,k=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)g=a[c],g.y=g.high,g._plotY=g.plotY,g.plotY=g.plotHigh,d[c]=g.dataLabel,g.dataLabel=g.dataLabelUpper,g.below=!1,k?(f.align="left",f.x=f.xHigh):f.y=
-f.yHigh;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments);for(c=b;c--;)g=a[c],g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g._plotY,g.below=!0,k?(f.align="right",f.x=f.xLow):f.y=f.yLow;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments)}},alignDataLabel:function(){h.column.prototype.alignDataLabel.apply(this,arguments)},getSymbol:h.column.prototype.getSymbol,drawPoints:w});p.areasplinerange=o(p.arearange);h.areasplinerange=u(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});
-(function(){var a=h.column.prototype;p.columnrange=o(p.column,p.arearange,{lineWidth:1,pointRange:null});h.columnrange=u(h.arearange,{type:"columnrange",translate:function(){var b=this,c=b.yAxis,d;a.translate.apply(b);s(b.points,function(a){var f=a.shapeArgs,g=b.options.minPointLength,k;a.tooltipPos=null;a.plotHigh=d=c.translate(a.high,0,1,0,1);a.plotLow=a.plotY;k=d;a=a.plotY-d;a<g&&(g-=a,a+=g,k-=g/2);f.height=a;f.y=k})},trackerGroups:["group","dataLabels"],drawGraph:w,pointAttrToOptions:a.pointAttrToOptions,
-drawPoints:a.drawPoints,drawTracker:a.drawTracker,animate:a.animate,getColumnMetrics:a.getColumnMetrics})})();p.gauge=o(p.line,{dataLabels:{enabled:!0,defer:!1,y:15,borderWidth:1,borderColor:"silver",borderRadius:3,crop:!1,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});z={type:"gauge",pointClass:u(H,{setState:function(a){this.state=a}}),angular:!0,drawGraph:w,fixedBox:!0,forceDL:!0,trackerGroups:["group","dataLabels"],translate:function(){var a=
-this.yAxis,b=this.options,c=a.center;this.generatePoints();s(this.points,function(d){var e=o(b.dial,d.dial),f=x(q(e.radius,80))*c[2]/200,g=x(q(e.baseLength,70))*f/100,k=x(q(e.rearLength,10))*f/100,l=e.baseWidth||3,n=e.topWidth||1,j=b.overshoot,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);j&&typeof j==="number"?(j=j/180*Math.PI,i=Math.max(a.startAngleRad-j,Math.min(a.endAngleRad+j,i))):b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs=
-{d:e.path||["M",-k,-l/2,"L",g,-l/2,f,-n/2,f,n/2,g,l/2,-k,l/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,f=a.chart.renderer;s(a.points,function(c){var b=c.graphic,l=c.shapeArgs,e=l.d,j=o(d.dial,c.dial);b?(b.animate(l),l.d=e):c.graphic=f[c.shapeType](l).attr({stroke:j.borderColor||"none","stroke-width":j.borderWidth||0,fill:j.backgroundColor||"black",rotation:l.rotation}).add(a.group)});
-c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,q(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)s(b.points,function(a){var d=a.graphic;d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?
-"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);t.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:function(a,b){t.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();q(b,!0)&&this.chart.redraw()},drawTracker:z&&z.drawTrackerPoint};h.gauge=u(h.line,z);p.boxplot=o(p.column,{fillColor:"#FFFFFF",lineWidth:1,medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'<span style="color:{series.color}">●</span> <b> {series.name}</b><br/>Maximum: {point.high}<br/>Upper quartile: {point.q3}<br/>Median: {point.median}<br/>Lower quartile: {point.q1}<br/>Minimum: {point.low}<br/>'},
-whiskerLength:"50%",whiskerWidth:2});h.boxplot=u(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:w,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);s(this.points,function(c){s(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=
-this,b=a.points,c=a.options,d=a.chart.renderer,e,f,g,k,l,n,j,i,h,m,p,I,r,o,J,u,w,t,v,x,z,y,E=a.doQuartiles!==!1,B=parseInt(a.options.whiskerLength,10)/100;s(b,function(b){h=b.graphic;z=b.shapeArgs;p={};o={};u={};y=b.color||a.color;if(b.plotY!==C)if(e=b.pointAttr[b.selected?"selected":""],w=z.width,t=A(z.x),v=t+w,x=D(w/2),f=A(E?b.q1Plot:b.lowPlot),g=A(E?b.q3Plot:b.lowPlot),k=A(b.highPlot),l=A(b.lowPlot),p.stroke=b.stemColor||c.stemColor||y,p["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),p.dashstyle=
-b.stemDashStyle||c.stemDashStyle,o.stroke=b.whiskerColor||c.whiskerColor||y,o["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor||y,u["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),u["stroke-linecap"]="round",j=p["stroke-width"]%2/2,i=t+x+j,m=["M",i,g,"L",i,k,"M",i,f,"L",i,l],E&&(j=e["stroke-width"]%2/2,i=A(i)+j,f=A(f)+j,g=A(g)+j,t+=j,v+=j,I=["M",t,g,"L",t,f,"L",v,f,"L",v,g,"L",t,g,"z"]),B&&(j=o["stroke-width"]%2/2,k+=j,l+=j,r=["M",i-x*B,
-k,"L",i+x*B,k,"M",i-x*B,l,"L",i+x*B,l]),j=u["stroke-width"]%2/2,n=D(b.medianPlot)+j,J=["M",t,n,"L",v,n],h)b.stem.animate({d:m}),B&&b.whiskers.animate({d:r}),E&&b.box.animate({d:I}),b.medianShape.animate({d:J});else{b.graphic=h=d.g().add(a.group);b.stem=d.path(m).attr(p).add(h);if(B)b.whiskers=d.path(r).attr(o).add(h);if(E)b.box=d.path(I).attr(e).add(h);b.medianShape=d.path(J).attr(u).add(h)}})}});p.errorbar=o(p.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'},
-whiskerWidth:null});h.errorbar=u(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,drawDataLabels:h.arearange?h.arearange.prototype.drawDataLabels:w,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p.waterfall=o(p.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333"});h.waterfall=u(h.column,{type:"waterfall",upColorProp:"fill",
-pointArrayMap:["low","y"],pointValKey:"y",init:function(a,b){b.stacking=!0;h.column.prototype.init.call(this,a,b)},translate:function(){var a=this.yAxis,b,c,d,e,f,g,k,l,n;b=this.options.threshold;h.column.prototype.translate.apply(this);l=b;d=this.points;for(c=0,b=d.length;c<b;c++){e=d[c];f=e.shapeArgs;g=this.getStack(c);n=g.points[this.index+","+c];if(isNaN(e.y))e.y=this.yData[c];k=T(l,l+e.y)+n[0];f.y=a.translate(k,0,1);e.isSum||e.isIntermediateSum?(f.y=a.translate(n[1],0,1),f.height=a.translate(n[0],
-0,1)-f.y):l+=g.total;f.height<0&&(f.y+=f.height,f.height*=-1);e.plotY=f.y=D(f.y)-this.borderWidth%2/2;f.height=D(f.height);e.yBottom=f.y+f.height}},processData:function(a){var b=this.yData,c=this.points,d,e=b.length,f=this.options.threshold||0,g,k,l,n,j,i;k=g=l=n=f;for(i=0;i<e;i++)j=b[i],d=c&&c[i]?c[i]:{},j==="sum"||d.isSum?b[i]=k:j==="intermediateSum"||d.isIntermediateSum?(b[i]=g,g=f):(k+=j,g+=j),l=Math.min(k,l),n=Math.max(k,n);t.prototype.processData.call(this,a);this.dataMin=l;this.dataMax=n},
-toYData:function(a){if(a.isSum)return"sum";else if(a.isIntermediateSum)return"intermediateSum";return a.y},getAttribs:function(){h.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,c=a.upColor||this.color,a=m.Color(c).brighten(0.1).get(),d=o(this.pointAttr),e=this.upColorProp;d[""][e]=c;d.hover[e]=b.hover.upColor||a;d.select[e]=b.select.upColor||c;s(this.points,function(a){if(a.y>0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,
-c=D(this.options.lineWidth+this.borderWidth)%2/2,d=[],e,f,g;for(g=1;g<b;g++)f=a[g].shapeArgs,e=a[g-1].shapeArgs,f=["M",e.x+e.width,e.y+c,"L",f.x,e.y+c],a[g-1].y<0&&(f[2]+=e.height,f[5]+=e.height),d=d.concat(f);return d},getExtremes:w,getStack:function(a){var b=this.yAxis.stacks,c=this.stackKey;this.processedYData[a]<this.options.threshold&&(c="-"+c);return b[c][a]},drawGraph:t.prototype.drawGraph});p.bubble=o(p.scatter,{dataLabels:{format:"{point.z}",inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},
-verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",states:{hover:{halo:{size:5}}},tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},turboThreshold:0,zThreshold:0});z=u(H,{haloPath:function(){return H.prototype.haloPath.call(this,this.shapeArgs.r+this.series.options.states.hover.halo.size)}});h.bubble=u(h.scatter,{type:"bubble",pointClass:z,pointArrayMap:["y","z"],parallelArrays:["x","y","z"],trackerGroups:["group","dataLabelsGroup"],bubblePadding:!0,
-pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"},applyOpacity:function(a){var b=this.options.marker,c=q(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=U(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=t.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,f,g,k=this.zData,l=[],n=this.options.sizeBy!=="width";for(f=0,e=k.length;f<e;f++)g=b-a,g=g>0?(k[f]-a)/(b-
-a):0.5,n&&g>=0&&(g=Math.sqrt(g)),l.push(v.ceil(c+g*(d-c))/2);this.radii=l},animate:function(a){var b=this.options.animation;if(!a)s(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,
-r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=C},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});M.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,g=f?"xData":"yData",k=this.min,l={},
-n=v.min(c.plotWidth,c.plotHeight),j=Number.MAX_VALUE,i=-Number.MAX_VALUE,h=this.max-k,m=b/h,p=[];this.tickPositions&&(s(this.series,function(b){var g=b.options;if(b.bubblePadding&&(b.visible||!c.options.chart.ignoreHiddenSeries))if(a.allowZoomOutside=!0,p.push(b),f)s(["minSize","maxSize"],function(a){var b=g[a],f=/%$/.test(b),b=x(b);l[a]=f?n*b/100:b}),b.minPxSize=l.minSize,b=b.zData,b.length&&(j=v.min(j,v.max(O(b),g.displayNegative===!1?g.zThreshold:-Number.MAX_VALUE)),i=v.max(i,P(b)))}),s(p,function(a){var b=
-a[g],c=b.length,n;f&&a.getRadii(j,i,l.minSize,l.maxSize);if(h>0)for(;c--;)typeof b[c]==="number"&&(n=a.radii[c],d=Math.min((b[c]-k)*m-n,d),e=Math.max((b[c]-k)*m+n,e))}),p.length&&h>0&&q(this.options.min,this.userMin)===C&&q(this.options.max,this.userMax)===C&&(e-=b,m*=(b+d-e)/b,this.min+=d/m,this.max+=e/m))};(function(){function a(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b=this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function b(a,b){var c=this.chart,
-d=this.options.animation,e=this.group,j=this.markerGroup,i=this.xAxis.center,h=c.plotLeft,m=c.plotTop;if(c.polar){if(c.renderer.isSVG)d===!0&&(d={}),b?(c={translateX:i[0]+h,translateY:i[1]+m,scaleX:0.001,scaleY:0.001},e.attr(c),j&&j.attr(c)):(c={translateX:h,translateY:m,scaleX:1,scaleY:1},e.animate(c,d),j&&j.animate(c,d),this.animate=null)}else a.call(this,b)}var c=t.prototype,d=R.prototype,e;c.toXY=function(a){var b,c=this.chart,d=a.plotX;b=a.plotY;a.rectPlotX=d;a.rectPlotY=b;d=(d/Math.PI*180+this.xAxis.pane.options.startAngle)%
-360;d<0&&(d+=360);a.clientX=d;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-b);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};c.orderTooltipPoints=function(a){if(this.chart.polar&&(a.sort(function(a,b){return a.clientX-b.clientX}),a[0]))a[0].wrappedClientX=a[0].clientX+360,a.push(a[0])};h.area&&r(h.area.prototype,"init",a);h.areaspline&&r(h.areaspline.prototype,"init",a);h.spline&&r(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,j,i,h,m,p,o;if(this.chart.polar){e=
-c.plotX;j=c.plotY;a=b[d-1];i=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),i||(i=b[1]));if(a&&i)h=a.plotX,m=a.plotY,b=i.plotX,p=i.plotY,h=(1.5*e+h)/2.5,m=(1.5*j+m)/2.5,i=(1.5*e+b)/2.5,o=(1.5*j+p)/2.5,b=Math.sqrt(Math.pow(h-e,2)+Math.pow(m-j,2)),p=Math.sqrt(Math.pow(i-e,2)+Math.pow(o-j,2)),h=Math.atan2(m-j,h-e),m=Math.atan2(o-j,i-e),o=Math.PI/2+(h+m)/2,Math.abs(h-o)>Math.PI/2&&(o-=Math.PI),h=e+Math.cos(o)*b,m=j+Math.sin(o)*b,i=e+Math.cos(Math.PI+o)*p,o=j+Math.sin(Math.PI+o)*p,c.rightContX=i,c.rightContY=
-o;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,h||e,m||j,e,j],a.rightContX=a.rightContY=null):c=["M",e,j]}else c=a.call(this,b,c,d);return c});r(c,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});r(c,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,
-b)});r(c,"animate",b);r(c,"setTooltipPoints",function(a,b){this.chart.polar&&F(this.xAxis,{tooltipLen:360});return a.call(this,b)});if(h.column)e=h.column.prototype,r(e,"animate",b),r(e,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,h=this.chart.renderer,i,m;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(m=b.length;m--;)i=b[m],a=i.barX+e,i.shapeType="path",i.shapeArgs={d:h.symbols.arc(d[0],d[1],c-i.plotY,null,{start:a,end:a+i.pointWidth,
-innerR:c-q(i.yBottom,c)})},this.toXY(i),i.tooltipPos=[i.plotX,i.plotY],i.ttBelow=i.plotY>d[1]}}),r(e,"alignDataLabel",function(a,b,d,e,h,j){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(e.align===null)e.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(e.verticalAlign===null)e.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";c.alignDataLabel.call(this,b,d,e,h,j)}else a.call(this,b,d,e,h,j)});r(d,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=
-b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c,d)/Math.PI*180)):c=a.call(this,b);return c});r(d,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?s(c.axes,function(a){var e=a.isXAxis,f=a.center,h=b.chartX-f[0]-c.plotLeft,f=b.chartY-f[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(h,f):Math.sqrt(Math.pow(h,2)+Math.pow(f,2)),!0)})}):d=a.call(this,b);return d})})()})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/highcharts-more.src.js b/apps/static/js/plugins/highcharts/highcharts-more.src.js
deleted file mode 100644
index 3db8ab419..000000000
--- a/apps/static/js/plugins/highcharts/highcharts-more.src.js
+++ /dev/null
@@ -1,2546 +0,0 @@
-// ==ClosureCompiler==
-// @compilation_level SIMPLE_OPTIMIZATIONS
-
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- *
- * (c) 2009-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-// JSLint options:
-/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
-
-(function (Highcharts, UNDEFINED) {
-var arrayMin = Highcharts.arrayMin,
-	arrayMax = Highcharts.arrayMax,
-	each = Highcharts.each,
-	extend = Highcharts.extend,
-	merge = Highcharts.merge,
-	map = Highcharts.map,
-	pick = Highcharts.pick,
-	pInt = Highcharts.pInt,
-	defaultPlotOptions = Highcharts.getOptions().plotOptions,
-	seriesTypes = Highcharts.seriesTypes,
-	extendClass = Highcharts.extendClass,
-	splat = Highcharts.splat,
-	wrap = Highcharts.wrap,
-	Axis = Highcharts.Axis,
-	Tick = Highcharts.Tick,
-	Point = Highcharts.Point,
-	Pointer = Highcharts.Pointer,
-	CenteredSeriesMixin = Highcharts.CenteredSeriesMixin,
-	TrackerMixin = Highcharts.TrackerMixin,
-	Series = Highcharts.Series,
-	math = Math,
-	mathRound = math.round,
-	mathFloor = math.floor,
-	mathMax = math.max,
-	Color = Highcharts.Color,
-	noop = function () {};/**
- * The Pane object allows options that are common to a set of X and Y axes.
- * 
- * In the future, this can be extended to basic Highcharts and Highstock.
- */
-function Pane(options, chart, firstAxis) {
-	this.init.call(this, options, chart, firstAxis);
-}
-
-// Extend the Pane prototype
-extend(Pane.prototype, {
-	
-	/**
-	 * Initiate the Pane object
-	 */
-	init: function (options, chart, firstAxis) {
-		var pane = this,
-			backgroundOption,
-			defaultOptions = pane.defaultOptions;
-		
-		pane.chart = chart;
-		
-		// Set options
-		if (chart.angular) { // gauges
-			defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
-		}
-		pane.options = options = merge(defaultOptions, options);
-		
-		backgroundOption = options.background;
-		
-		// To avoid having weighty logic to place, update and remove the backgrounds,
-		// push them to the first axis' plot bands and borrow the existing logic there.
-		if (backgroundOption) {
-			each([].concat(splat(backgroundOption)).reverse(), function (config) {
-				var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
-				config = merge(pane.defaultBackgroundOptions, config);
-				if (backgroundColor) {
-					config.backgroundColor = backgroundColor;
-				}
-				config.color = config.backgroundColor; // due to naming in plotBands
-				firstAxis.options.plotBands.unshift(config);
-			});
-		}
-	},
-	
-	/**
-	 * The default options object
-	 */
-	defaultOptions: {
-		// background: {conditional},
-		center: ['50%', '50%'],
-		size: '85%',
-		startAngle: 0
-		//endAngle: startAngle + 360
-	},	
-	
-	/**
-	 * The default background options
-	 */
-	defaultBackgroundOptions: {
-		shape: 'circle',
-		borderWidth: 1,
-		borderColor: 'silver',
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, '#FFF'],
-				[1, '#DDD']
-			]
-		},
-		from: Number.MIN_VALUE, // corrected to axis min
-		innerRadius: 0,
-		to: Number.MAX_VALUE, // corrected to axis max
-		outerRadius: '105%'
-	}
-	
-});
-var axisProto = Axis.prototype,
-	tickProto = Tick.prototype;
-	
-/**
- * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
- */
-var hiddenAxisMixin = {
-	getOffset: noop,
-	redraw: function () {
-		this.isDirty = false; // prevent setting Y axis dirty
-	},
-	render: function () {
-		this.isDirty = false; // prevent setting Y axis dirty
-	},
-	setScale: noop,
-	setCategories: noop,
-	setTitle: noop
-};
-
-/**
- * Augmented methods for the value axis
- */
-/*jslint unparam: true*/
-var radialAxisMixin = {
-	isRadial: true,
-	
-	/**
-	 * The default options extend defaultYAxisOptions
-	 */
-	defaultRadialGaugeOptions: {
-		labels: {
-			align: 'center',
-			x: 0,
-			y: null // auto
-		},
-		minorGridLineWidth: 0,
-		minorTickInterval: 'auto',
-		minorTickLength: 10,
-		minorTickPosition: 'inside',
-		minorTickWidth: 1,
-		tickLength: 10,
-		tickPosition: 'inside',
-		tickWidth: 2,
-		title: {
-			rotation: 0
-		},
-		zIndex: 2 // behind dials, points in the series group
-	},
-	
-	// Circular axis around the perimeter of a polar chart
-	defaultRadialXOptions: {
-		gridLineWidth: 1, // spokes
-		labels: {
-			align: null, // auto
-			distance: 15,
-			x: 0,
-			y: null // auto
-		},
-		maxPadding: 0,
-		minPadding: 0,
-		showLastLabel: false, 
-		tickLength: 0
-	},
-	
-	// Radial axis, like a spoke in a polar chart
-	defaultRadialYOptions: {
-		gridLineInterpolation: 'circle',
-		labels: {
-			align: 'right',
-			x: -3,
-			y: -2
-		},
-		showLastLabel: false,
-		title: {
-			x: 4,
-			text: null,
-			rotation: 90
-		}
-	},
-	
-	/**
-	 * Merge and set options
-	 */
-	setOptions: function (userOptions) {
-		
-		var options = this.options = merge(
-			this.defaultOptions,
-			this.defaultRadialOptions,
-			userOptions
-		);
-
-		// Make sure the plotBands array is instanciated for each Axis (#2649)
-		if (!options.plotBands) {
-			options.plotBands = [];
-		}
-		
-	},
-	
-	/**
-	 * Wrap the getOffset method to return zero offset for title or labels in a radial 
-	 * axis
-	 */
-	getOffset: function () {
-		// Call the Axis prototype method (the method we're in now is on the instance)
-		axisProto.getOffset.call(this);
-		
-		// Title or label offsets are not counted
-		this.chart.axisOffset[this.side] = 0;
-		
-		// Set the center array
-		this.center = this.pane.center = CenteredSeriesMixin.getCenter.call(this.pane);
-	},
-
-
-	/**
-	 * Get the path for the axis line. This method is also referenced in the getPlotLinePath
-	 * method.
-	 */
-	getLinePath: function (lineWidth, radius) {
-		var center = this.center;
-		radius = pick(radius, center[2] / 2 - this.offset);
-		
-		return this.chart.renderer.symbols.arc(
-			this.left + center[0],
-			this.top + center[1],
-			radius,
-			radius, 
-			{
-				start: this.startAngleRad,
-				end: this.endAngleRad,
-				open: true,
-				innerR: 0
-			}
-		);
-	},
-
-	/**
-	 * Override setAxisTranslation by setting the translation to the difference
-	 * in rotation. This allows the translate method to return angle for 
-	 * any given value.
-	 */
-	setAxisTranslation: function () {
-		
-		// Call uber method		
-		axisProto.setAxisTranslation.call(this);
-			
-		// Set transA and minPixelPadding
-		if (this.center) { // it's not defined the first time
-			if (this.isCircular) {
-				
-				this.transA = (this.endAngleRad - this.startAngleRad) / 
-					((this.max - this.min) || 1);
-					
-				
-			} else { 
-				this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
-			}
-			
-			if (this.isXAxis) {
-				this.minPixelPadding = this.transA * this.minPointOffset;
-			} else {
-				// This is a workaround for regression #2593, but categories still don't position correctly.
-				// TODO: Implement true handling of Y axis categories on gauges.
-				this.minPixelPadding = 0; 
-			}
-		}
-	},
-	
-	/**
-	 * In case of auto connect, add one closestPointRange to the max value right before
-	 * tickPositions are computed, so that ticks will extend passed the real max.
-	 */
-	beforeSetTickPositions: function () {
-		if (this.autoConnect) {
-			this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260
-		}
-	},
-	
-	/**
-	 * Override the setAxisSize method to use the arc's circumference as length. This
-	 * allows tickPixelInterval to apply to pixel lengths along the perimeter
-	 */
-	setAxisSize: function () {
-		
-		axisProto.setAxisSize.call(this);
-
-		if (this.isRadial) {
-
-			// Set the center array
-			this.center = this.pane.center = Highcharts.CenteredSeriesMixin.getCenter.call(this.pane);
-
-			// The sector is used in Axis.translate to compute the translation of reversed axis points (#2570)
-			if (this.isCircular) {
-				this.sector = this.endAngleRad - this.startAngleRad;	
-			}
-			
-			// Axis len is used to lay out the ticks
-			this.len = this.width = this.height = this.center[2] * pick(this.sector, 1) / 2;
-
-
-		}
-	},
-	
-	/**
-	 * Returns the x, y coordinate of a point given by a value and a pixel distance
-	 * from center
-	 */
-	getPosition: function (value, length) {
-		return this.postTranslate(
-			this.isCircular ? this.translate(value) : 0, // #2848
-			pick(this.isCircular ? length : this.translate(value), this.center[2] / 2) - this.offset
-		);		
-	},
-	
-	/**
-	 * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates. 
-	 */
-	postTranslate: function (angle, radius) {
-		
-		var chart = this.chart,
-			center = this.center;
-			
-		angle = this.startAngleRad + angle;
-
-		return {
-			x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
-			y: chart.plotTop + center[1] + Math.sin(angle) * radius
-		}; 
-		
-	},
-	
-	/**
-	 * Find the path for plot bands along the radial axis
-	 */
-	getPlotBandPath: function (from, to, options) {
-		var center = this.center,
-			startAngleRad = this.startAngleRad,
-			fullRadius = center[2] / 2,
-			radii = [
-				pick(options.outerRadius, '100%'),
-				options.innerRadius,
-				pick(options.thickness, 10)
-			],
-			percentRegex = /%$/,
-			start,
-			end,
-			open,
-			isCircular = this.isCircular, // X axis in a polar chart
-			ret;
-			
-		// Polygonal plot bands
-		if (this.options.gridLineInterpolation === 'polygon') {
-			ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
-		
-		// Circular grid bands
-		} else {
-			
-			// Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
-			if (!isCircular) {
-				radii[0] = this.translate(from);
-				radii[1] = this.translate(to);
-			}
-			
-			// Convert percentages to pixel values
-			radii = map(radii, function (radius) {
-				if (percentRegex.test(radius)) {
-					radius = (pInt(radius, 10) * fullRadius) / 100;
-				}
-				return radius;
-			});
-			
-			// Handle full circle
-			if (options.shape === 'circle' || !isCircular) {
-				start = -Math.PI / 2;
-				end = Math.PI * 1.5;
-				open = true;
-			} else {
-				start = startAngleRad + this.translate(from);
-				end = startAngleRad + this.translate(to);
-			}
-		
-		
-			ret = this.chart.renderer.symbols.arc(
-				this.left + center[0],
-				this.top + center[1],
-				radii[0],
-				radii[0],
-				{
-					start: start,
-					end: end,
-					innerR: pick(radii[1], radii[0] - radii[2]),
-					open: open
-				}
-			);
-		}
-		 
-		return ret;
-	},
-	
-	/**
-	 * Find the path for plot lines perpendicular to the radial axis.
-	 */
-	getPlotLinePath: function (value, reverse) {
-		var axis = this,
-			center = axis.center,
-			chart = axis.chart,
-			end = axis.getPosition(value),
-			xAxis,
-			xy,
-			tickPositions,
-			ret;
-		
-		// Spokes
-		if (axis.isCircular) {
-			ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
-		
-		// Concentric circles			
-		} else if (axis.options.gridLineInterpolation === 'circle') {
-			value = axis.translate(value);
-			if (value) { // a value of 0 is in the center
-				ret = axis.getLinePath(0, value);
-			}
-		// Concentric polygons 
-		} else {
-			// Find the X axis in the same pane
-			each(chart.xAxis, function (a) {
-				if (a.pane === axis.pane) {
-					xAxis = a;
-				}
-			});
-			ret = [];
-			value = axis.translate(value);
-			tickPositions = xAxis.tickPositions;
-			if (xAxis.autoConnect) {
-				tickPositions = tickPositions.concat([tickPositions[0]]);
-			}
-			// Reverse the positions for concatenation of polygonal plot bands
-			if (reverse) {
-				tickPositions = [].concat(tickPositions).reverse();
-			}
-				
-			each(tickPositions, function (pos, i) {
-				xy = xAxis.getPosition(pos, value);
-				ret.push(i ? 'L' : 'M', xy.x, xy.y);
-			});
-			
-		}
-		return ret;
-	},
-	
-	/**
-	 * Find the position for the axis title, by default inside the gauge
-	 */
-	getTitlePosition: function () {
-		var center = this.center,
-			chart = this.chart,
-			titleOptions = this.options.title;
-		
-		return { 
-			x: chart.plotLeft + center[0] + (titleOptions.x || 0), 
-			y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] * 
-				center[2]) + (titleOptions.y || 0)  
-		};
-	}
-	
-};
-/*jslint unparam: false*/
-
-/**
- * Override axisProto.init to mix in special axis instance functions and function overrides
- */
-wrap(axisProto, 'init', function (proceed, chart, userOptions) {
-	var axis = this,
-		angular = chart.angular,
-		polar = chart.polar,
-		isX = userOptions.isX,
-		isHidden = angular && isX,
-		isCircular,
-		startAngleRad,
-		endAngleRad,
-		options,
-		chartOptions = chart.options,
-		paneIndex = userOptions.pane || 0,
-		pane,
-		paneOptions;
-		
-	// Before prototype.init
-	if (angular) {
-		extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
-		isCircular =  !isX;
-		if (isCircular) {
-			this.defaultRadialOptions = this.defaultRadialGaugeOptions;
-		}
-		
-	} else if (polar) {
-		//extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
-		extend(this, radialAxisMixin);
-		isCircular = isX;
-		this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
-		
-	}
-	
-	// Run prototype.init
-	proceed.call(this, chart, userOptions);
-	
-	if (!isHidden && (angular || polar)) {
-		options = this.options;
-		
-		// Create the pane and set the pane options.
-		if (!chart.panes) {
-			chart.panes = [];
-		}
-		this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane(
-			splat(chartOptions.pane)[paneIndex],
-			chart,
-			axis
-		);
-		paneOptions = pane.options;
-		
-			
-		// Disable certain features on angular and polar axes
-		chart.inverted = false;
-		chartOptions.chart.zoomType = null;
-		
-		// Start and end angle options are
-		// given in degrees relative to top, while internal computations are
-		// in radians relative to right (like SVG).
-		this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
-		this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360)  - 90) * Math.PI / 180;
-		this.offset = options.offset || 0;
-		
-		this.isCircular = isCircular;
-		
-		// Automatically connect grid lines?
-		if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
-			this.autoConnect = true;
-		}
-	}
-	
-});
-
-/**
- * Add special cases within the Tick class' methods for radial axes.
- */	
-wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
-	var axis = this.axis;
-	
-	return axis.getPosition ? 
-		axis.getPosition(pos) :
-		proceed.call(this, horiz, pos, tickmarkOffset, old);	
-});
-
-/**
- * Wrap the getLabelPosition function to find the center position of the label
- * based on the distance option
- */	
-wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
-	var axis = this.axis,
-		optionsY = labelOptions.y,
-		ret,
-		align = labelOptions.align,
-		angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360;
-
-	if (axis.isRadial) {
-		ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
-		
-		// Automatically rotated
-		if (labelOptions.rotation === 'auto') {
-			label.attr({ 
-				rotation: angle
-			});
-		
-		// Vertically centered
-		} else if (optionsY === null) {
-			optionsY = axis.chart.renderer.fontMetrics(label.styles.fontSize).b - label.getBBox().height / 2;
-		}
-		
-		// Automatic alignment
-		if (align === null) {
-			if (axis.isCircular) {
-				if (angle > 20 && angle < 160) {
-					align = 'left'; // right hemisphere
-				} else if (angle > 200 && angle < 340) {
-					align = 'right'; // left hemisphere
-				} else {
-					align = 'center'; // top or bottom
-				}
-			} else {
-				align = 'center';
-			}
-			label.attr({
-				align: align
-			});
-		}
-		
-		ret.x += labelOptions.x;
-		ret.y += optionsY;
-		
-	} else {
-		ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
-	}
-	return ret;
-});
-
-/**
- * Wrap the getMarkPath function to return the path of the radial marker
- */
-wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
-	var axis = this.axis,
-		endPoint,
-		ret;
-		
-	if (axis.isRadial) {
-		endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
-		ret = [
-			'M',
-			x,
-			y,
-			'L',
-			endPoint.x,
-			endPoint.y
-		];
-	} else {
-		ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
-	}
-	return ret;
-});/* 
- * The AreaRangeSeries class
- * 
- */
-
-/**
- * Extend the default options with map options
- */
-defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
-	lineWidth: 1,
-	marker: null,
-	threshold: null,
-	tooltip: {
-		pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
-	},
-	trackByArea: true,
-	dataLabels: {
-		verticalAlign: null,
-		xLow: 0,
-		xHigh: 0,
-		yLow: 0,
-		yHigh: 0	
-	},
-	states: {
-		hover: {
-			halo: false
-		}
-	}
-});
-
-/**
- * Add the series type
- */
-seriesTypes.arearange = extendClass(seriesTypes.area, {
-	type: 'arearange',
-	pointArrayMap: ['low', 'high'],
-	toYData: function (point) {
-		return [point.low, point.high];
-	},
-	pointValKey: 'low',
-	
-	/**
-	 * Extend getSegments to force null points if the higher value is null. #1703.
-	 */
-	getSegments: function () {
-		var series = this;
-
-		each(series.points, function (point) {
-			if (!series.options.connectNulls && (point.low === null || point.high === null)) {
-				point.y = null;
-			} else if (point.low === null && point.high !== null) {
-				point.y = point.high;
-			}
-		});
-		Series.prototype.getSegments.call(this);
-	},
-	
-	/**
-	 * Translate data points from raw values x and y to plotX and plotY
-	 */
-	translate: function () {
-		var series = this,
-			yAxis = series.yAxis;
-
-		seriesTypes.area.prototype.translate.apply(series);
-
-		// Set plotLow and plotHigh
-		each(series.points, function (point) {
-
-			var low = point.low,
-				high = point.high,
-				plotY = point.plotY;
-
-			if (high === null && low === null) {
-				point.y = null;
-			} else if (low === null) {
-				point.plotLow = point.plotY = null;
-				point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
-			} else if (high === null) {
-				point.plotLow = plotY;
-				point.plotHigh = null;
-			} else {
-				point.plotLow = plotY;
-				point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
-			}
-		});
-	},
-	
-	/**
-	 * Extend the line series' getSegmentPath method by applying the segment
-	 * path to both lower and higher values of the range
-	 */
-	getSegmentPath: function (segment) {
-		
-		var lowSegment,
-			highSegment = [],
-			i = segment.length,
-			baseGetSegmentPath = Series.prototype.getSegmentPath,
-			point,
-			linePath,
-			lowerPath,
-			options = this.options,
-			step = options.step,
-			higherPath;
-			
-		// Remove nulls from low segment
-		lowSegment = HighchartsAdapter.grep(segment, function (point) {
-			return point.plotLow !== null;
-		});
-		
-		// Make a segment with plotX and plotY for the top values
-		while (i--) {
-			point = segment[i];
-			if (point.plotHigh !== null) {
-				highSegment.push({
-					plotX: point.plotX,
-					plotY: point.plotHigh
-				});
-			}
-		}
-		
-		// Get the paths
-		lowerPath = baseGetSegmentPath.call(this, lowSegment);
-		if (step) {
-			if (step === true) {
-				step = 'left';
-			}
-			options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
-		}
-		higherPath = baseGetSegmentPath.call(this, highSegment);
-		options.step = step;
-		
-		// Create a line on both top and bottom of the range
-		linePath = [].concat(lowerPath, higherPath);
-		
-		// For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
-		higherPath[0] = 'L'; // this probably doesn't work for spline			
-		this.areaPath = this.areaPath.concat(lowerPath, higherPath);
-		
-		return linePath;
-	},
-	
-	/**
-	 * Extend the basic drawDataLabels method by running it for both lower and higher
-	 * values.
-	 */
-	drawDataLabels: function () {
-		
-		var data = this.data,
-			length = data.length,
-			i,
-			originalDataLabels = [],
-			seriesProto = Series.prototype,
-			dataLabelOptions = this.options.dataLabels,
-			point,
-			inverted = this.chart.inverted;
-			
-		if (dataLabelOptions.enabled || this._hasPointLabels) {
-			
-			// Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
-			i = length;
-			while (i--) {
-				point = data[i];
-				
-				// Set preliminary values
-				point.y = point.high;
-				point._plotY = point.plotY;
-				point.plotY = point.plotHigh;
-				
-				// Store original data labels and set preliminary label objects to be picked up 
-				// in the uber method
-				originalDataLabels[i] = point.dataLabel;
-				point.dataLabel = point.dataLabelUpper;
-				
-				// Set the default offset
-				point.below = false;
-				if (inverted) {
-					dataLabelOptions.align = 'left';
-					dataLabelOptions.x = dataLabelOptions.xHigh;								
-				} else {
-					dataLabelOptions.y = dataLabelOptions.yHigh;
-				}
-			}
-			
-			if (seriesProto.drawDataLabels) {
-				seriesProto.drawDataLabels.apply(this, arguments); // #1209
-			}
-			
-			// Step 2: reorganize and handle data labels for the lower values
-			i = length;
-			while (i--) {
-				point = data[i];
-				
-				// Move the generated labels from step 1, and reassign the original data labels
-				point.dataLabelUpper = point.dataLabel;
-				point.dataLabel = originalDataLabels[i];
-				
-				// Reset values
-				point.y = point.low;
-				point.plotY = point._plotY;
-				
-				// Set the default offset
-				point.below = true;
-				if (inverted) {
-					dataLabelOptions.align = 'right';
-					dataLabelOptions.x = dataLabelOptions.xLow;
-				} else {
-					dataLabelOptions.y = dataLabelOptions.yLow;
-				}
-			}
-			if (seriesProto.drawDataLabels) {
-				seriesProto.drawDataLabels.apply(this, arguments);
-			}
-		}
-	
-	},
-	
-	alignDataLabel: function () {
-		seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
-	},
-	
-	getSymbol: seriesTypes.column.prototype.getSymbol,
-	
-	drawPoints: noop
-});/**
- * The AreaSplineRangeSeries class
- */
-
-defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
-
-/**
- * AreaSplineRangeSeries object
- */
-seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
-	type: 'areasplinerange',
-	getPointSpline: seriesTypes.spline.prototype.getPointSpline
-});
-
-(function () {
-	
-	var colProto = seriesTypes.column.prototype;
-
-	/**
-	 * The ColumnRangeSeries class
-	 */
-	defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
-		lineWidth: 1,
-		pointRange: null
-	});
-
-	/**
-	 * ColumnRangeSeries object
-	 */
-	seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
-		type: 'columnrange',
-		/**
-		 * Translate data points from raw values x and y to plotX and plotY
-		 */
-		translate: function () {
-			var series = this,
-				yAxis = series.yAxis,
-				plotHigh;
-
-			colProto.translate.apply(series);
-
-			// Set plotLow and plotHigh
-			each(series.points, function (point) {
-				var shapeArgs = point.shapeArgs,
-					minPointLength = series.options.minPointLength,
-					heightDifference,
-					height,
-					y;
-
-				point.tooltipPos = null; // don't inherit from column
-				point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
-				point.plotLow = point.plotY;
-
-				// adjust shape
-				y = plotHigh;
-				height = point.plotY - plotHigh;
-
-				if (height < minPointLength) {
-					heightDifference = (minPointLength - height);
-					height += heightDifference;
-					y -= heightDifference / 2;
-				}
-				shapeArgs.height = height;
-				shapeArgs.y = y;
-			});
-		},
-		trackerGroups: ['group', 'dataLabels'],
-		drawGraph: noop,
-		pointAttrToOptions: colProto.pointAttrToOptions,
-		drawPoints: colProto.drawPoints,
-		drawTracker: colProto.drawTracker,
-		animate: colProto.animate,
-		getColumnMetrics: colProto.getColumnMetrics
-	});
-}());
-
-/* 
- * The GaugeSeries class
- */
-
-
-
-/**
- * Extend the default options
- */
-defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
-	dataLabels: {
-		enabled: true,
-		defer: false,
-		y: 15,
-		borderWidth: 1,
-		borderColor: 'silver',
-		borderRadius: 3,
-		crop: false,
-		style: {
-			fontWeight: 'bold'
-		},
-		verticalAlign: 'top',
-		zIndex: 2
-	},
-	dial: {
-		// radius: '80%',
-		// backgroundColor: 'black',
-		// borderColor: 'silver',
-		// borderWidth: 0,
-		// baseWidth: 3,
-		// topWidth: 1,
-		// baseLength: '70%' // of radius
-		// rearLength: '10%'
-	},
-	pivot: {
-		//radius: 5,
-		//borderWidth: 0
-		//borderColor: 'silver',
-		//backgroundColor: 'black'
-	},
-	tooltip: {
-		headerFormat: ''
-	},
-	showInLegend: false
-});
-
-/**
- * Extend the point object
- */
-var GaugePoint = extendClass(Point, {
-	/**
-	 * Don't do any hover colors or anything
-	 */
-	setState: function (state) {
-		this.state = state;
-	}
-});
-
-
-/**
- * Add the series type
- */
-var GaugeSeries = {
-	type: 'gauge',
-	pointClass: GaugePoint,
-	
-	// chart.angular will be set to true when a gauge series is present, and this will
-	// be used on the axes
-	angular: true, 
-	drawGraph: noop,
-	fixedBox: true,
-	forceDL: true,
-	trackerGroups: ['group', 'dataLabels'],
-	
-	/**
-	 * Calculate paths etc
-	 */
-	translate: function () {
-		
-		var series = this,
-			yAxis = series.yAxis,
-			options = series.options,
-			center = yAxis.center;
-			
-		series.generatePoints();
-		
-		each(series.points, function (point) {
-			
-			var dialOptions = merge(options.dial, point.dial),
-				radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
-				baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
-				rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
-				baseWidth = dialOptions.baseWidth || 3,
-				topWidth = dialOptions.topWidth || 1,
-				overshoot = options.overshoot,
-				rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
-
-			// Handle the wrap and overshoot options
-			if (overshoot && typeof overshoot === 'number') {
-				overshoot = overshoot / 180 * Math.PI;
-				rotation = Math.max(yAxis.startAngleRad - overshoot, Math.min(yAxis.endAngleRad + overshoot, rotation));			
-			
-			} else if (options.wrap === false) {
-				rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
-			}
-
-			rotation = rotation * 180 / Math.PI;
-				
-			point.shapeType = 'path';
-			point.shapeArgs = {
-				d: dialOptions.path || [
-					'M', 
-					-rearLength, -baseWidth / 2, 
-					'L', 
-					baseLength, -baseWidth / 2,
-					radius, -topWidth / 2,
-					radius, topWidth / 2,
-					baseLength, baseWidth / 2,
-					-rearLength, baseWidth / 2,
-					'z'
-				],
-				translateX: center[0],
-				translateY: center[1],
-				rotation: rotation
-			};
-			
-			// Positions for data label
-			point.plotX = center[0];
-			point.plotY = center[1];
-		});
-	},
-	
-	/**
-	 * Draw the points where each point is one needle
-	 */
-	drawPoints: function () {
-		
-		var series = this,
-			center = series.yAxis.center,
-			pivot = series.pivot,
-			options = series.options,
-			pivotOptions = options.pivot,
-			renderer = series.chart.renderer;
-		
-		each(series.points, function (point) {
-			
-			var graphic = point.graphic,
-				shapeArgs = point.shapeArgs,
-				d = shapeArgs.d,
-				dialOptions = merge(options.dial, point.dial); // #1233
-			
-			if (graphic) {
-				graphic.animate(shapeArgs);
-				shapeArgs.d = d; // animate alters it
-			} else {
-				point.graphic = renderer[point.shapeType](shapeArgs)
-					.attr({
-						stroke: dialOptions.borderColor || 'none',
-						'stroke-width': dialOptions.borderWidth || 0,
-						fill: dialOptions.backgroundColor || 'black',
-						rotation: shapeArgs.rotation // required by VML when animation is false
-					})
-					.add(series.group);
-			}
-		});
-		
-		// Add or move the pivot
-		if (pivot) {
-			pivot.animate({ // #1235
-				translateX: center[0],
-				translateY: center[1]
-			});
-		} else {
-			series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
-				.attr({
-					'stroke-width': pivotOptions.borderWidth || 0,
-					stroke: pivotOptions.borderColor || 'silver',
-					fill: pivotOptions.backgroundColor || 'black'
-				})
-				.translate(center[0], center[1])
-				.add(series.group);
-		}
-	},
-	
-	/**
-	 * Animate the arrow up from startAngle
-	 */
-	animate: function (init) {
-		var series = this;
-
-		if (!init) {
-			each(series.points, function (point) {
-				var graphic = point.graphic;
-
-				if (graphic) {
-					// start value
-					graphic.attr({
-						rotation: series.yAxis.startAngleRad * 180 / Math.PI
-					});
-
-					// animate
-					graphic.animate({
-						rotation: point.shapeArgs.rotation
-					}, series.options.animation);
-				}
-			});
-
-			// delete this function to allow it only once
-			series.animate = null;
-		}
-	},
-	
-	render: function () {
-		this.group = this.plotGroup(
-			'group', 
-			'series', 
-			this.visible ? 'visible' : 'hidden', 
-			this.options.zIndex, 
-			this.chart.seriesGroup
-		);
-		Series.prototype.render.call(this);
-		this.group.clip(this.chart.clipRect);
-	},
-	
-	/**
-	 * Extend the basic setData method by running processData and generatePoints immediately,
-	 * in order to access the points from the legend.
-	 */
-	setData: function (data, redraw) {
-		Series.prototype.setData.call(this, data, false);
-		this.processData();
-		this.generatePoints();
-		if (pick(redraw, true)) {
-			this.chart.redraw();
-		}
-	},
-
-	/**
-	 * If the tracking module is loaded, add the point tracker
-	 */
-	drawTracker: TrackerMixin && TrackerMixin.drawTrackerPoint
-};
-seriesTypes.gauge = extendClass(seriesTypes.line, GaugeSeries);
-
-/* ****************************************************************************
- * Start Box plot series code											      *
- *****************************************************************************/
-
-// Set default options
-defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
-	fillColor: '#FFFFFF',
-	lineWidth: 1,
-	//medianColor: null,
-	medianWidth: 2,
-	states: {
-		hover: {
-			brightness: -0.3
-		}
-	},
-	//stemColor: null,
-	//stemDashStyle: 'solid'
-	//stemWidth: null,
-	threshold: null,
-	tooltip: {
-		pointFormat: '<span style="color:{series.color}">\u25CF</span> <b> {series.name}</b><br/>' +
-			'Maximum: {point.high}<br/>' +
-			'Upper quartile: {point.q3}<br/>' +
-			'Median: {point.median}<br/>' +
-			'Lower quartile: {point.q1}<br/>' +
-			'Minimum: {point.low}<br/>'
-			
-	},
-	//whiskerColor: null,
-	whiskerLength: '50%',
-	whiskerWidth: 2
-});
-
-// Create the series object
-seriesTypes.boxplot = extendClass(seriesTypes.column, {
-	type: 'boxplot',
-	pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
-	toYData: function (point) { // return a plain array for speedy calculation
-		return [point.low, point.q1, point.median, point.q3, point.high];
-	},
-	pointValKey: 'high', // defines the top of the tracker
-	
-	/**
-	 * One-to-one mapping from options to SVG attributes
-	 */
-	pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
-		fill: 'fillColor',
-		stroke: 'color',
-		'stroke-width': 'lineWidth'
-	},
-	
-	/**
-	 * Disable data labels for box plot
-	 */
-	drawDataLabels: noop,
-
-	/**
-	 * Translate data points from raw values x and y to plotX and plotY
-	 */
-	translate: function () {
-		var series = this,
-			yAxis = series.yAxis,
-			pointArrayMap = series.pointArrayMap;
-
-		seriesTypes.column.prototype.translate.apply(series);
-
-		// do the translation on each point dimension
-		each(series.points, function (point) {
-			each(pointArrayMap, function (key) {
-				if (point[key] !== null) {
-					point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
-				}
-			});
-		});
-	},
-
-	/**
-	 * Draw the data points
-	 */
-	drawPoints: function () {
-		var series = this,  //state = series.state,
-			points = series.points,
-			options = series.options,
-			chart = series.chart,
-			renderer = chart.renderer,
-			pointAttr,
-			q1Plot,
-			q3Plot,
-			highPlot,
-			lowPlot,
-			medianPlot,
-			crispCorr,
-			crispX,
-			graphic,
-			stemPath,
-			stemAttr,
-			boxPath,
-			whiskersPath,
-			whiskersAttr,
-			medianPath,
-			medianAttr,
-			width,
-			left,
-			right,
-			halfWidth,
-			shapeArgs,
-			color,
-			doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
-			whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
-
-
-		each(points, function (point) {
-
-			graphic = point.graphic;
-			shapeArgs = point.shapeArgs; // the box
-			stemAttr = {};
-			whiskersAttr = {};
-			medianAttr = {};
-			color = point.color || series.color;
-			
-			if (point.plotY !== UNDEFINED) {
-
-				pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
-
-				// crisp vector coordinates
-				width = shapeArgs.width;
-				left = mathFloor(shapeArgs.x);
-				right = left + width;
-				halfWidth = mathRound(width / 2);
-				//crispX = mathRound(left + halfWidth) + crispCorr;
-				q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
-				q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
-				highPlot = mathFloor(point.highPlot);// + crispCorr;
-				lowPlot = mathFloor(point.lowPlot);// + crispCorr;
-				
-				// Stem attributes
-				stemAttr.stroke = point.stemColor || options.stemColor || color;
-				stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
-				stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
-				
-				// Whiskers attributes
-				whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
-				whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
-				
-				// Median attributes
-				medianAttr.stroke = point.medianColor || options.medianColor || color;
-				medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
-				medianAttr['stroke-linecap'] = 'round'; // #1638
-				
-				
-				// The stem
-				crispCorr = (stemAttr['stroke-width'] % 2) / 2;
-				crispX = left + halfWidth + crispCorr;				
-				stemPath = [
-					// stem up
-					'M',
-					crispX, q3Plot,
-					'L',
-					crispX, highPlot,
-					
-					// stem down
-					'M',
-					crispX, q1Plot,
-					'L',
-					crispX, lowPlot
-				];
-				
-				// The box
-				if (doQuartiles) {
-					crispCorr = (pointAttr['stroke-width'] % 2) / 2;
-					crispX = mathFloor(crispX) + crispCorr;
-					q1Plot = mathFloor(q1Plot) + crispCorr;
-					q3Plot = mathFloor(q3Plot) + crispCorr;
-					left += crispCorr;
-					right += crispCorr;
-					boxPath = [
-						'M',
-						left, q3Plot,
-						'L',
-						left, q1Plot,
-						'L',
-						right, q1Plot,
-						'L',
-						right, q3Plot,
-						'L',
-						left, q3Plot,
-						'z'
-					];
-				}
-				
-				// The whiskers
-				if (whiskerLength) {
-					crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
-					highPlot = highPlot + crispCorr;
-					lowPlot = lowPlot + crispCorr;
-					whiskersPath = [
-						// High whisker
-						'M',
-						crispX - halfWidth * whiskerLength, 
-						highPlot,
-						'L',
-						crispX + halfWidth * whiskerLength, 
-						highPlot,
-						
-						// Low whisker
-						'M',
-						crispX - halfWidth * whiskerLength, 
-						lowPlot,
-						'L',
-						crispX + halfWidth * whiskerLength, 
-						lowPlot
-					];
-				}
-				
-				// The median
-				crispCorr = (medianAttr['stroke-width'] % 2) / 2;				
-				medianPlot = mathRound(point.medianPlot) + crispCorr;
-				medianPath = [
-					'M',
-					left, 
-					medianPlot,
-					'L',
-					right, 
-					medianPlot
-				];
-				
-				// Create or update the graphics
-				if (graphic) { // update
-					
-					point.stem.animate({ d: stemPath });
-					if (whiskerLength) {
-						point.whiskers.animate({ d: whiskersPath });
-					}
-					if (doQuartiles) {
-						point.box.animate({ d: boxPath });
-					}
-					point.medianShape.animate({ d: medianPath });
-					
-				} else { // create new
-					point.graphic = graphic = renderer.g()
-						.add(series.group);
-					
-					point.stem = renderer.path(stemPath)
-						.attr(stemAttr)
-						.add(graphic);
-						
-					if (whiskerLength) {
-						point.whiskers = renderer.path(whiskersPath) 
-							.attr(whiskersAttr)
-							.add(graphic);
-					}
-					if (doQuartiles) {
-						point.box = renderer.path(boxPath)
-							.attr(pointAttr)
-							.add(graphic);
-					}	
-					point.medianShape = renderer.path(medianPath)
-						.attr(medianAttr)
-						.add(graphic);
-				}
-			}
-		});
-
-	}
-
-
-});
-
-/* ****************************************************************************
- * End Box plot series code												*
- *****************************************************************************/
-/* ****************************************************************************
- * Start error bar series code                                                *
- *****************************************************************************/
-
-// 1 - set default options
-defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
-	color: '#000000',
-	grouping: false,
-	linkedTo: ':previous',
-	tooltip: {
-		pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
-	},
-	whiskerWidth: null
-});
-
-// 2 - Create the series object
-seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
-	type: 'errorbar',
-	pointArrayMap: ['low', 'high'], // array point configs are mapped to this
-	toYData: function (point) { // return a plain array for speedy calculation
-		return [point.low, point.high];
-	},
-	pointValKey: 'high', // defines the top of the tracker
-	doQuartiles: false,
-	drawDataLabels: seriesTypes.arearange ? seriesTypes.arearange.prototype.drawDataLabels : noop,
-
-	/**
-	 * Get the width and X offset, either on top of the linked series column
-	 * or standalone
-	 */
-	getColumnMetrics: function () {
-		return (this.linkedParent && this.linkedParent.columnMetrics) || 
-			seriesTypes.column.prototype.getColumnMetrics.call(this);
-	}
-});
-
-/* ****************************************************************************
- * End error bar series code                                                  *
- *****************************************************************************/
-/* ****************************************************************************
- * Start Waterfall series code                                                *
- *****************************************************************************/
-
-// 1 - set default options
-defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
-	lineWidth: 1,
-	lineColor: '#333',
-	dashStyle: 'dot',
-	borderColor: '#333'
-});
-
-
-// 2 - Create the series object
-seriesTypes.waterfall = extendClass(seriesTypes.column, {
-	type: 'waterfall',
-
-	upColorProp: 'fill',
-
-	pointArrayMap: ['low', 'y'],
-
-	pointValKey: 'y',
-
-	/**
-	 * Init waterfall series, force stacking
-	 */
-	init: function (chart, options) {
-		// force stacking
-		options.stacking = true;
-
-		seriesTypes.column.prototype.init.call(this, chart, options);
-	},
-
-
-	/**
-	 * Translate data points from raw values
-	 */
-	translate: function () {
-		var series = this,
-			options = series.options,
-			axis = series.yAxis,
-			len,
-			i,
-			points,
-			point,
-			shapeArgs,
-			stack,
-			y,
-			previousY,
-			stackPoint,
-			threshold = options.threshold;
-
-		// run column series translate
-		seriesTypes.column.prototype.translate.apply(this);
-
-		previousY = threshold;
-		points = series.points;
-
-		for (i = 0, len = points.length; i < len; i++) {
-			// cache current point object
-			point = points[i];
-			shapeArgs = point.shapeArgs;
-
-			// get current stack
-			stack = series.getStack(i);
-			stackPoint = stack.points[series.index + ',' + i];
-
-			// override point value for sums
-			if (isNaN(point.y)) {
-				point.y = series.yData[i];
-			}
-
-			// up points
-			y = mathMax(previousY, previousY + point.y) + stackPoint[0];
-			shapeArgs.y = axis.translate(y, 0, 1);
-
-
-			// sum points
-			if (point.isSum || point.isIntermediateSum) {
-				shapeArgs.y = axis.translate(stackPoint[1], 0, 1);
-				shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
-
-			// if it's not the sum point, update previous stack end position
-			} else {
-				previousY += stack.total;
-			}
-
-			// negative points
-			if (shapeArgs.height < 0) {
-				shapeArgs.y += shapeArgs.height;
-				shapeArgs.height *= -1;
-			}
-
-			point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - (series.borderWidth % 2) / 2;
-			shapeArgs.height = mathRound(shapeArgs.height);
-			point.yBottom = shapeArgs.y + shapeArgs.height;
-		}
-	},
-
-	/**
-	 * Call default processData then override yData to reflect waterfall's extremes on yAxis
-	 */
-	processData: function (force) {
-		var series = this,
-			options = series.options,
-			yData = series.yData,
-			points = series.points,
-			point,
-			dataLength = yData.length,
-			threshold = options.threshold || 0,
-			subSum,
-			sum,
-			dataMin,
-			dataMax,
-			y,
-			i;
-
-		sum = subSum = dataMin = dataMax = threshold;
-
-		for (i = 0; i < dataLength; i++) {
-			y = yData[i];
-			point = points && points[i] ? points[i] : {};
-
-			if (y === "sum" || point.isSum) {
-				yData[i] = sum;
-			} else if (y === "intermediateSum" || point.isIntermediateSum) {
-				yData[i] = subSum;
-				subSum = threshold;
-			} else {
-				sum += y;
-				subSum += y;
-			}
-			dataMin = Math.min(sum, dataMin);
-			dataMax = Math.max(sum, dataMax);
-		}
-
-		Series.prototype.processData.call(this, force);
-
-		// Record extremes
-		series.dataMin = dataMin;
-		series.dataMax = dataMax;
-	},
-
-	/**
-	 * Return y value or string if point is sum
-	 */
-	toYData: function (pt) {
-		if (pt.isSum) {
-			return "sum";
-		} else if (pt.isIntermediateSum) {
-			return "intermediateSum";
-		}
-
-		return pt.y;
-	},
-
-	/**
-	 * Postprocess mapping between options and SVG attributes
-	 */
-	getAttribs: function () {
-		seriesTypes.column.prototype.getAttribs.apply(this, arguments);
-
-		var series = this,
-			options = series.options,
-			stateOptions = options.states,
-			upColor = options.upColor || series.color,
-			hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
-			seriesDownPointAttr = merge(series.pointAttr),
-			upColorProp = series.upColorProp;
-
-		seriesDownPointAttr[''][upColorProp] = upColor;
-		seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
-		seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
-
-		each(series.points, function (point) {
-			if (point.y > 0 && !point.color) {
-				point.pointAttr = seriesDownPointAttr;
-				point.color = upColor;
-			}
-		});
-	},
-
-	/**
-	 * Draw columns' connector lines
-	 */
-	getGraphPath: function () {
-
-		var data = this.data,
-			length = data.length,
-			lineWidth = this.options.lineWidth + this.borderWidth,
-			normalizer = mathRound(lineWidth) % 2 / 2,
-			path = [],
-			M = 'M',
-			L = 'L',
-			prevArgs,
-			pointArgs,
-			i,
-			d;
-
-		for (i = 1; i < length; i++) {
-			pointArgs = data[i].shapeArgs;
-			prevArgs = data[i - 1].shapeArgs;
-
-			d = [
-				M,
-				prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
-				L,
-				pointArgs.x, prevArgs.y + normalizer
-			];
-
-			if (data[i - 1].y < 0) {
-				d[2] += prevArgs.height;
-				d[5] += prevArgs.height;
-			}
-
-			path = path.concat(d);
-		}
-
-		return path;
-	},
-
-	/**
-	 * Extremes are recorded in processData
-	 */
-	getExtremes: noop,
-
-	/**
-	 * Return stack for given index
-	 */
-	getStack: function (i) {
-		var axis = this.yAxis,
-			stacks = axis.stacks,
-			key = this.stackKey;
-
-		if (this.processedYData[i] < this.options.threshold) {
-			key = '-' + key;
-		}
-
-		return stacks[key][i];
-	},
-
-	drawGraph: Series.prototype.drawGraph
-});
-
-/* ****************************************************************************
- * End Waterfall series code                                                  *
- *****************************************************************************/
-/* ****************************************************************************
- * Start Bubble series code											          *
- *****************************************************************************/
-
-// 1 - set default options
-defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
-	dataLabels: {
-		format: '{point.z}',
-		inside: true,
-		style: {
-			color: 'white',
-			textShadow: '0px 0px 3px black'
-		},
-		verticalAlign: 'middle'
-	},
-	// displayNegative: true,
-	marker: {
-		// fillOpacity: 0.5,
-		lineColor: null, // inherit from series.color
-		lineWidth: 1
-	},
-	minSize: 8,
-	maxSize: '20%',
-	// negativeColor: null,
-	// sizeBy: 'area'
-	states: {
-		hover: {
-			halo: {
-				size: 5
-			}
-		}
-	},
-	tooltip: {
-		pointFormat: '({point.x}, {point.y}), Size: {point.z}'
-	},
-	turboThreshold: 0,
-	zThreshold: 0
-});
-
-var BubblePoint = extendClass(Point, {
-	haloPath: function () {
-		return Point.prototype.haloPath.call(this, this.shapeArgs.r + this.series.options.states.hover.halo.size);
-	}
-});
-
-// 2 - Create the series object
-seriesTypes.bubble = extendClass(seriesTypes.scatter, {
-	type: 'bubble',
-	pointClass: BubblePoint,
-	pointArrayMap: ['y', 'z'],
-	parallelArrays: ['x', 'y', 'z'],
-	trackerGroups: ['group', 'dataLabelsGroup'],
-	bubblePadding: true,
-	
-	/**
-	 * Mapping between SVG attributes and the corresponding options
-	 */
-	pointAttrToOptions: { 
-		stroke: 'lineColor',
-		'stroke-width': 'lineWidth',
-		fill: 'fillColor'
-	},
-	
-	/**
-	 * Apply the fillOpacity to all fill positions
-	 */
-	applyOpacity: function (fill) {
-		var markerOptions = this.options.marker,
-			fillOpacity = pick(markerOptions.fillOpacity, 0.5);
-		
-		// When called from Legend.colorizeItem, the fill isn't predefined
-		fill = fill || markerOptions.fillColor || this.color; 
-		
-		if (fillOpacity !== 1) {
-			fill = Color(fill).setOpacity(fillOpacity).get('rgba');
-		}
-		return fill;
-	},
-	
-	/**
-	 * Extend the convertAttribs method by applying opacity to the fill
-	 */
-	convertAttribs: function () {
-		var obj = Series.prototype.convertAttribs.apply(this, arguments);
-		
-		obj.fill = this.applyOpacity(obj.fill);
-		
-		return obj;
-	},
-
-	/**
-	 * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
-	 * must be done prior to Series.translate because the axis needs to add padding in 
-	 * accordance with the point sizes.
-	 */
-	getRadii: function (zMin, zMax, minSize, maxSize) {
-		var len,
-			i,
-			pos,
-			zData = this.zData,
-			radii = [],
-			sizeByArea = this.options.sizeBy !== 'width',
-			zRange;
-		
-		// Set the shape type and arguments to be picked up in drawPoints
-		for (i = 0, len = zData.length; i < len; i++) {
-			zRange = zMax - zMin;
-			pos = zRange > 0 ? // relative size, a number between 0 and 1
-				(zData[i] - zMin) / (zMax - zMin) : 
-				0.5;
-			if (sizeByArea && pos >= 0) {
-				pos = Math.sqrt(pos);
-			}
-			radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
-		}
-		this.radii = radii;
-	},
-	
-	/**
-	 * Perform animation on the bubbles
-	 */
-	animate: function (init) {
-		var animation = this.options.animation;
-		
-		if (!init) { // run the animation
-			each(this.points, function (point) {
-				var graphic = point.graphic,
-					shapeArgs = point.shapeArgs;
-
-				if (graphic && shapeArgs) {
-					// start values
-					graphic.attr('r', 1);
-
-					// animate
-					graphic.animate({
-						r: shapeArgs.r
-					}, animation);
-				}
-			});
-
-			// delete this function to allow it only once
-			this.animate = null;
-		}
-	},
-	
-	/**
-	 * Extend the base translate method to handle bubble size
-	 */
-	translate: function () {
-		
-		var i,
-			data = this.data,
-			point,
-			radius,
-			radii = this.radii;
-		
-		// Run the parent method
-		seriesTypes.scatter.prototype.translate.call(this);
-		
-		// Set the shape type and arguments to be picked up in drawPoints
-		i = data.length;
-		
-		while (i--) {
-			point = data[i];
-			radius = radii ? radii[i] : 0; // #1737
-
-			// Flag for negativeColor to be applied in Series.js
-			point.negative = point.z < (this.options.zThreshold || 0);
-			
-			if (radius >= this.minPxSize / 2) {
-				// Shape arguments
-				point.shapeType = 'circle';
-				point.shapeArgs = {
-					x: point.plotX,
-					y: point.plotY,
-					r: radius
-				};
-				
-				// Alignment box for the data label
-				point.dlBox = {
-					x: point.plotX - radius,
-					y: point.plotY - radius,
-					width: 2 * radius,
-					height: 2 * radius
-				};
-			} else { // below zThreshold
-				point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
-			}
-		}
-	},
-	
-	/**
-	 * Get the series' symbol in the legend
-	 * 
-	 * @param {Object} legend The legend object
-	 * @param {Object} item The series (this) or point
-	 */
-	drawLegendSymbol: function (legend, item) {
-		var radius = pInt(legend.itemStyle.fontSize) / 2;
-		
-		item.legendSymbol = this.chart.renderer.circle(
-			radius,
-			legend.baseline - radius,
-			radius
-		).attr({
-			zIndex: 3
-		}).add(item.legendGroup);
-		item.legendSymbol.isMarker = true;	
-		
-	},
-	
-	drawPoints: seriesTypes.column.prototype.drawPoints,
-	alignDataLabel: seriesTypes.column.prototype.alignDataLabel
-});
-
-/**
- * Add logic to pad each axis with the amount of pixels
- * necessary to avoid the bubbles to overflow.
- */
-Axis.prototype.beforePadding = function () {
-	var axis = this,
-		axisLength = this.len,
-		chart = this.chart,
-		pxMin = 0, 
-		pxMax = axisLength,
-		isXAxis = this.isXAxis,
-		dataKey = isXAxis ? 'xData' : 'yData',
-		min = this.min,
-		extremes = {},
-		smallestSize = math.min(chart.plotWidth, chart.plotHeight),
-		zMin = Number.MAX_VALUE,
-		zMax = -Number.MAX_VALUE,
-		range = this.max - min,
-		transA = axisLength / range,
-		activeSeries = [];
-
-	// Handle padding on the second pass, or on redraw
-	if (this.tickPositions) {
-		each(this.series, function (series) {
-
-			var seriesOptions = series.options,
-				zData;
-
-			if (series.bubblePadding && (series.visible || !chart.options.chart.ignoreHiddenSeries)) {
-
-				// Correction for #1673
-				axis.allowZoomOutside = true;
-
-				// Cache it
-				activeSeries.push(series);
-
-				if (isXAxis) { // because X axis is evaluated first
-				
-					// For each series, translate the size extremes to pixel values
-					each(['minSize', 'maxSize'], function (prop) {
-						var length = seriesOptions[prop],
-							isPercent = /%$/.test(length);
-						
-						length = pInt(length);
-						extremes[prop] = isPercent ?
-							smallestSize * length / 100 :
-							length;
-						
-					});
-					series.minPxSize = extremes.minSize;
-					
-					// Find the min and max Z
-					zData = series.zData;
-					if (zData.length) { // #1735
-						zMin = math.min(
-							zMin,
-							math.max(
-								arrayMin(zData), 
-								seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
-							)
-						);
-						zMax = math.max(zMax, arrayMax(zData));
-					}
-				}
-			}
-		});
-
-		each(activeSeries, function (series) {
-
-			var data = series[dataKey],
-				i = data.length,
-				radius;
-
-			if (isXAxis) {
-				series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
-			}
-			
-			if (range > 0) {
-				while (i--) {
-					if (typeof data[i] === 'number') {
-						radius = series.radii[i];
-						pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
-						pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
-					}
-				}
-			}
-		});
-		
-		if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
-			pxMax -= axisLength;
-			transA *= (axisLength + pxMin - pxMax) / axisLength;
-			this.min += pxMin / transA;
-			this.max += pxMax / transA;
-		}
-	}
-};
-
-/* ****************************************************************************
- * End Bubble series code                                                     *
- *****************************************************************************/
-
-(function () {
-
-	/**
-	 * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
-	 * gathered in RadialAxes.js.
-	 * 
-	 */
-
-	var seriesProto = Series.prototype,
-		pointerProto = Pointer.prototype,
-		colProto;
-
-	/**
-	 * Translate a point's plotX and plotY from the internal angle and radius measures to 
-	 * true plotX, plotY coordinates
-	 */
-	seriesProto.toXY = function (point) {
-		var xy,
-			chart = this.chart,
-			plotX = point.plotX,
-			plotY = point.plotY,
-			clientX;
-	
-		// Save rectangular plotX, plotY for later computation
-		point.rectPlotX = plotX;
-		point.rectPlotY = plotY;
-	
-		// Record the angle in degrees for use in tooltip
-		clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360;
-		if (clientX < 0) { // #2665
-			clientX += 360;
-		}
-		point.clientX = clientX;
-
-	
-		// Find the polar plotX and plotY
-		xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
-		point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
-		point.plotY = point.polarPlotY = xy.y - chart.plotTop;
-	};
-
-	/** 
-	 * Order the tooltip points to get the mouse capture ranges correct. #1915. 
-	 */
-	seriesProto.orderTooltipPoints = function (points) {
-		if (this.chart.polar) {
-			points.sort(function (a, b) {
-				return a.clientX - b.clientX;
-			});
-
-			// Wrap mouse tracking around to capture movement on the segment to the left
-			// of the north point (#1469, #2093).
-			if (points[0]) {
-				points[0].wrappedClientX = points[0].clientX + 360;
-				points.push(points[0]);
-			}
-		}
-	};
-
-
-	/**
-	 * Add some special init logic to areas and areasplines
-	 */
-	function initArea(proceed, chart, options) {
-		proceed.call(this, chart, options);
-		if (this.chart.polar) {
-		
-			/**
-			 * Overridden method to close a segment path. While in a cartesian plane the area 
-			 * goes down to the threshold, in the polar chart it goes to the center.
-			 */
-			this.closeSegment = function (path) {
-				var center = this.xAxis.center;
-				path.push(
-					'L',
-					center[0],
-					center[1]
-				);			
-			};
-		
-			// Instead of complicated logic to draw an area around the inner area in a stack,
-			// just draw it behind
-			this.closedStacks = true;
-		}
-	}
-
-	if (seriesTypes.area) {		
-		wrap(seriesTypes.area.prototype, 'init', initArea);	
-	}
-	if (seriesTypes.areaspline) {		
-		wrap(seriesTypes.areaspline.prototype, 'init', initArea);			
-	}	
-
-	if (seriesTypes.spline) {
-		/**
-		 * Overridden method for calculating a spline from one point to the next
-		 */
-		wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
-	
-			var ret,
-				smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
-				denom = smoothing + 1,
-				plotX, 
-				plotY,
-				lastPoint,
-				nextPoint,
-				lastX,
-				lastY,
-				nextX,
-				nextY,
-				leftContX,
-				leftContY,
-				rightContX,
-				rightContY,
-				distanceLeftControlPoint,
-				distanceRightControlPoint,
-				leftContAngle,
-				rightContAngle,
-				jointAngle;
-		
-		
-			if (this.chart.polar) {
-		
-				plotX = point.plotX;
-				plotY = point.plotY;
-				lastPoint = segment[i - 1];
-				nextPoint = segment[i + 1];
-			
-				// Connect ends
-				if (this.connectEnds) {
-					if (!lastPoint) {
-						lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
-					}
-					if (!nextPoint) {
-						nextPoint = segment[1];
-					}	
-				}
-
-				// find control points
-				if (lastPoint && nextPoint) {
-		
-					lastX = lastPoint.plotX;
-					lastY = lastPoint.plotY;
-					nextX = nextPoint.plotX;
-					nextY = nextPoint.plotY;
-					leftContX = (smoothing * plotX + lastX) / denom;
-					leftContY = (smoothing * plotY + lastY) / denom;
-					rightContX = (smoothing * plotX + nextX) / denom;
-					rightContY = (smoothing * plotY + nextY) / denom;
-					distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
-					distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
-					leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
-					rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
-					jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
-				
-				
-					// Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
-					if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
-						jointAngle -= Math.PI;
-					}
-			
-					// Find the corrected control points for a spline straight through the point
-					leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
-					leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
-					rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
-					rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
-			
-					// Record for drawing in next point
-					point.rightContX = rightContX;
-					point.rightContY = rightContY;
-
-				}
-		
-		
-				// moveTo or lineTo
-				if (!i) {
-					ret = ['M', plotX, plotY];
-				} else { // curve from last point to this
-					ret = [
-						'C',
-						lastPoint.rightContX || lastPoint.plotX,
-						lastPoint.rightContY || lastPoint.plotY,
-						leftContX || plotX,
-						leftContY || plotY,
-						plotX,
-						plotY
-					];
-					lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
-				}
-		
-		
-			} else {
-				ret = proceed.call(this, segment, point, i);
-			}
-			return ret;
-		});
-	}
-
-	/**
-	 * Extend translate. The plotX and plotY values are computed as if the polar chart were a
-	 * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
-	 * center. 
-	 */
-	wrap(seriesProto, 'translate', function (proceed) {
-		
-		// Run uber method
-		proceed.call(this);
-	
-		// Postprocess plot coordinates
-		if (this.chart.polar && !this.preventPostTranslate) {
-			var points = this.points,
-				i = points.length;
-			while (i--) {
-				// Translate plotX, plotY from angle and radius to true plot coordinates
-				this.toXY(points[i]);
-			}
-		}
-	});
-
-	/** 
-	 * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in 
-	 * line-like series.
-	 */
-	wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
-		
-		var points = this.points;
-	
-		// Connect the path
-		if (this.chart.polar && this.options.connectEnds !== false && 
-				segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
-			this.connectEnds = true; // re-used in splines
-			segment = [].concat(segment, [points[0]]);
-		}
-	
-		// Run uber method
-		return proceed.call(this, segment);
-	
-	});
-
-
-	function polarAnimate(proceed, init) {
-		var chart = this.chart,
-			animation = this.options.animation,
-			group = this.group,
-			markerGroup = this.markerGroup,
-			center = this.xAxis.center,
-			plotLeft = chart.plotLeft,
-			plotTop = chart.plotTop,
-			attribs;
-
-		// Specific animation for polar charts
-		if (chart.polar) {
-		
-			// Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
-			// would be so slow it would't matter.
-			if (chart.renderer.isSVG) {
-
-				if (animation === true) {
-					animation = {};
-				}
-	
-				// Initialize the animation
-				if (init) {
-				
-					// Scale down the group and place it in the center
-					attribs = {
-						translateX: center[0] + plotLeft,
-						translateY: center[1] + plotTop,
-						scaleX: 0.001, // #1499
-						scaleY: 0.001
-					};
-					
-					group.attr(attribs);
-					if (markerGroup) {
-						//markerGroup.attrSetters = group.attrSetters;
-						markerGroup.attr(attribs);
-					}
-				
-				// Run the animation
-				} else {
-					attribs = {
-						translateX: plotLeft,
-						translateY: plotTop,
-						scaleX: 1,
-						scaleY: 1
-					};
-					group.animate(attribs, animation);
-					if (markerGroup) {
-						markerGroup.animate(attribs, animation);
-					}
-				
-					// Delete this function to allow it only once
-					this.animate = null;
-				}
-			}
-	
-		// For non-polar charts, revert to the basic animation
-		} else {
-			proceed.call(this, init);
-		} 
-	}
-
-	// Define the animate method for regular series
-	wrap(seriesProto, 'animate', polarAnimate);
-
-	/**
-	 * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
-	 * in degrees (0-360), not plot pixel width.
-	 */
-	wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
-		
-		if (this.chart.polar) {
-			extend(this.xAxis, {
-				tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
-			});	
-		}
-		// Run uber method
-		return proceed.call(this, renew);
-	});
-
-
-	if (seriesTypes.column) {
-
-		colProto = seriesTypes.column.prototype;
-		/**
-		* Define the animate method for columnseries
-		*/
-		wrap(colProto, 'animate', polarAnimate);
-
-
-		/**
-		 * Extend the column prototype's translate method
-		 */
-		wrap(colProto, 'translate', function (proceed) {
-		
-			var xAxis = this.xAxis,
-				len = this.yAxis.len,
-				center = xAxis.center,
-				startAngleRad = xAxis.startAngleRad,
-				renderer = this.chart.renderer,
-				start,
-				points,
-				point,
-				i;
-	
-			this.preventPostTranslate = true;
-	
-			// Run uber method
-			proceed.call(this);
-	
-			// Postprocess plot coordinates
-			if (xAxis.isRadial) {
-				points = this.points;
-				i = points.length;
-				while (i--) {
-					point = points[i];
-					start = point.barX + startAngleRad;
-					point.shapeType = 'path';
-					point.shapeArgs = {
-						d: renderer.symbols.arc(
-							center[0],
-							center[1],
-							len - point.plotY,
-							null, 
-							{
-								start: start,
-								end: start + point.pointWidth,
-								innerR: len - pick(point.yBottom, len)
-							}
-						)
-					};
-					// Provide correct plotX, plotY for tooltip
-					this.toXY(point); 
-					point.tooltipPos = [point.plotX, point.plotY];
-					point.ttBelow = point.plotY > center[1];
-				}
-			}
-		});
-
-
-		/**
-		 * Align column data labels outside the columns. #1199.
-		 */
-		wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
-	
-			if (this.chart.polar) {
-				var angle = point.rectPlotX / Math.PI * 180,
-					align,
-					verticalAlign;
-		
-				// Align nicely outside the perimeter of the columns
-				if (options.align === null) {
-					if (angle > 20 && angle < 160) {
-						align = 'left'; // right hemisphere
-					} else if (angle > 200 && angle < 340) {
-						align = 'right'; // left hemisphere
-					} else {
-						align = 'center'; // top or bottom
-					}
-					options.align = align;
-				}
-				if (options.verticalAlign === null) {
-					if (angle < 45 || angle > 315) {
-						verticalAlign = 'bottom'; // top part
-					} else if (angle > 135 && angle < 225) {
-						verticalAlign = 'top'; // bottom part
-					} else {
-						verticalAlign = 'middle'; // left or right
-					}
-					options.verticalAlign = verticalAlign;
-				}
-		
-				seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
-			} else {
-				proceed.call(this, point, dataLabel, options, alignTo, isNew);
-			}
-	
-		});		
-	}
-
-
-	/**
-	 * Extend the mouse tracker to return the tooltip position index in terms of
-	 * degrees rather than pixels
-	 */
-	wrap(pointerProto, 'getIndex', function (proceed, e) {
-		var ret,
-			chart = this.chart,
-			center,
-			x,
-			y;
-	
-		if (chart.polar) {
-			center = chart.xAxis[0].center;
-			x = e.chartX - center[0] - chart.plotLeft;
-			y = e.chartY - center[1] - chart.plotTop;
-		
-			ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
-	
-		} else {
-	
-			// Run uber method
-			ret = proceed.call(this, e);
-		}
-		return ret;
-	});
-
-	/**
-	 * Extend getCoordinates to prepare for polar axis values
-	 */
-	wrap(pointerProto, 'getCoordinates', function (proceed, e) {
-		var chart = this.chart,
-			ret = {
-				xAxis: [],
-				yAxis: []
-			};
-	
-		if (chart.polar) {	
-
-			each(chart.axes, function (axis) {
-				var isXAxis = axis.isXAxis,
-					center = axis.center,
-					x = e.chartX - center[0] - chart.plotLeft,
-					y = e.chartY - center[1] - chart.plotTop;
-			
-				ret[isXAxis ? 'xAxis' : 'yAxis'].push({
-					axis: axis,
-					value: axis.translate(
-						isXAxis ?
-							Math.PI - Math.atan2(x, y) : // angle 
-							Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
-						true
-					)
-				});
-			});
-		
-		} else {
-			ret = proceed.call(this, e);
-		}
-	
-		return ret;
-	});
-
-}());
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/highcharts.js b/apps/static/js/plugins/highcharts/highcharts.js
deleted file mode 100644
index 0f4336dbb..000000000
--- a/apps/static/js/plugins/highcharts/highcharts.js
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2009-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(){function q(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function w(){var a,b=arguments,c,d={},e=function(a,b){var c,d;typeof a!=="object"&&(a={});for(d in b)b.hasOwnProperty(d)&&(c=b[d],a[d]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&d!=="renderTo"&&typeof c.nodeType!=="number"?e(a[d]||{},c):b[d]);return a};b[0]===!0&&(d=b[1],b=Array.prototype.slice.call(b,2));c=b.length;for(a=0;a<c;a++)d=e(d,b[a]);return d}function z(a,b){return parseInt(a,b||
-10)}function Fa(a){return typeof a==="string"}function ca(a){return typeof a==="object"}function La(a){return Object.prototype.toString.call(a)==="[object Array]"}function ha(a){return typeof a==="number"}function za(a){return U.log(a)/U.LN10}function ia(a){return U.pow(10,a)}function ja(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function r(a){return a!==t&&a!==null}function H(a,b,c){var d,e;if(Fa(b))r(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(r(b)&&
-ca(b))for(d in b)a.setAttribute(d,b[d]);return e}function qa(a){return La(a)?a:[a]}function m(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function G(a,b){if(Aa&&!aa&&b&&b.opacity!==t)b.filter="alpha(opacity="+b.opacity*100+")";q(a.style,b)}function Y(a,b,c,d,e){a=y.createElement(a);b&&q(a,b);e&&G(a,{padding:0,border:Q,margin:0});c&&G(a,c);d&&d.appendChild(a);return a}function ka(a,b){var c=function(){};c.prototype=new a;q(c.prototype,b);return c}
-function Ga(a,b,c,d){var e=E.lang,a=+a||0,f=b===-1?(a.toString().split(".")[1]||"").length:isNaN(b=M(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(z(a=M(a).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+M(a-c).toFixed(f).slice(2):"")}function Ha(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function Ma(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments);
-a.unshift(d);return c.apply(this,a)}}function Ia(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h<i;h++)e=e[g[h]];if(f.length)f=f.join(":"),g=/\.([0-9])/,h=E.lang,i=void 0,/f$/.test(f)?(i=(i=f.match(g))?i[1]:-1,e!==null&&(e=Ga(e,i,h.decimalPoint,f.indexOf(",")>-1?h.thousandsSep:""))):e=cb(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function mb(a){return U.pow(10,T(U.log(a)/
-U.LN10))}function nb(a,b,c,d){var e,c=m(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function Bb(){this.symbol=this.color=0}function ob(a,b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}function Na(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function Ba(a){for(var b=
-a.length,c=a[0];b--;)a[b]>c&&(c=a[b]);return c}function Oa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Pa(a){db||(db=Y(Ja));a&&db.appendChild(a);db.innerHTML=""}function ra(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else I.console&&console.log(c)}function da(a){return parseFloat(a.toPrecision(14))}function Qa(a,b){va=m(a,b.animation)}function Cb(){var a=E.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Ra=(a&&E.global.timezoneOffset||
-0)*6E4;eb=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,m(c,1),m(g,0),m(h,0),m(i,0))).getTime()};pb=b+"Minutes";qb=b+"Hours";rb=b+"Day";Xa=b+"Date";fb=b+"Month";gb=b+"FullYear";Db=c+"Minutes";Eb=c+"Hours";sb=c+"Date";Fb=c+"Month";Gb=c+"FullYear"}function P(){}function Sa(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function la(){this.init.apply(this,arguments)}function Ya(){this.init.apply(this,arguments)}function Hb(a,b,c,d,e){var f=a.chart.inverted;
-this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.total=null;this.points={};this.stack=e;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom":"top"),y:m(b.y,f?4:c?14:-6),x:m(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign||(f?c?"right":"left":"center")}var t,y=document,I=window,U=Math,u=U.round,T=U.floor,Ka=U.ceil,v=U.max,C=U.min,M=U.abs,Z=U.cos,ea=U.sin,ma=U.PI,Ca=ma*2/360,wa=navigator.userAgent,Ib=I.opera,Aa=/msie/i.test(wa)&&
-!Ib,hb=y.documentMode===8,ib=/AppleWebKit/.test(wa),Ta=/Firefox/.test(wa),Jb=/(Mobile|Android|Windows Phone)/.test(wa),xa="http://www.w3.org/2000/svg",aa=!!y.createElementNS&&!!y.createElementNS(xa,"svg").createSVGRect,Nb=Ta&&parseInt(wa.split("Firefox/")[1],10)<4,fa=!aa&&!Aa&&!!y.createElement("canvas").getContext,Za,$a,Kb={},tb=0,db,E,cb,va,ub,A,sa=function(){},V=[],ab=0,Ja="div",Q="none",Ob=/^[0-9]+$/,Pb="stroke-width",eb,Ra,pb,qb,rb,Xa,fb,gb,Db,Eb,sb,Fb,Gb,F={},R=I.Highcharts=I.Highcharts?ra(16,
-!0):{};cb=function(a,b,c){if(!r(b)||isNaN(b))return"Invalid date";var a=m(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b-Ra),e,f=d[qb](),g=d[rb](),h=d[Xa](),i=d[fb](),j=d[gb](),k=E.lang,l=k.weekdays,d=q({a:l[g].substr(0,3),A:l[g],d:Ha(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ha(i+1),y:j.toString().substr(2,2),Y:j,H:Ha(f),I:Ha(f%12||12),l:f%12||12,M:Ha(d[pb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:Ha(d.getSeconds()),L:Ha(u(b%1E3),3)},R.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]===
-"function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Bb.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};A=function(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}("millisecond",1,"second",1E3,"minute",6E4,"hour",36E5,"day",864E5,"week",6048E5,"month",26784E5,"year",31556952E3);ub={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),
-h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f&&b.length===c.length)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length<a;)d=[].concat(b).splice(b.length-f,f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(i));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===
-b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};(function(a){I.HighchartsAdapter=I.HighchartsAdapter||a&&{init:function(b){var c=a.fx,d=c.step,e,f=a.Tween,g=f&&f.propHooks;e=a.cssHooks.opacity;a.extend(a.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});a.each(["cur","_default","width","height","opacity"],function(a,b){var e=d,k;b==="cur"?e=c.prototype:b==="_default"&&f&&(e=g[b],b="set");(k=e[b])&&(e[b]=function(c){var d,c=
-a?c:this;if(c.prop!=="align")return d=c.elem,d.attr?d.attr(c.prop,b==="cur"?t:c.now):k.apply(this,arguments)})});Ma(e,"get",function(a,b,c){return b.attr?b.opacity||0:a.call(this,b,c)});e=function(a){var c=a.elem,d;if(!a.started)d=b.init(c,c.d,c.toD),a.start=d[0],a.end=d[1],a.started=!0;c.attr("d",b.step(a.start,a.end,a.pos,c.toD))};f?g.d={set:e}:d.d=e;this.each=Array.prototype.forEach?function(a,b){return Array.prototype.forEach.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],
-a[c],c,a)===!1)return c};a.fn.highcharts=function(){var a="Chart",b=arguments,c,d;if(this[0]){Fa(b[0])&&(a=b[0],b=Array.prototype.slice.call(b,1));c=b[0];if(c!==t)c.chart=c.chart||{},c.chart.renderTo=this[0],new R[a](c,b[1]),d=this;c===t&&(d=V[H(this[0],"data-highcharts-chart")])}return d}},getScript:a.getScript,inArray:a.inArray,adapterRun:function(b,c){return a(b)[c]()},grep:a.grep,map:function(a,c){for(var d=[],e=0,f=a.length;e<f;e++)d[e]=c.call(a[e],a[e],e,a);return d},offset:function(b){return a(b).offset()},
-addEvent:function(b,c,d){a(b).bind(c,d)},removeEvent:function(b,c,d){var e=y.removeEventListener?"removeEventListener":"detachEvent";y[e]&&b&&!b[e]&&(b[e]=function(){});a(b).unbind(c,d)},fireEvent:function(b,c,d,e){var f=a.Event(c),g="detached"+c,h;!Aa&&d&&(delete d.layerX,delete d.layerY,delete d.returnValue);q(f,d);b[c]&&(b[g]=b[c],b[c]=null);a.each(["preventDefault","stopPropagation"],function(a,b){var c=f[b];f[b]=function(){try{c.call(f)}catch(a){b==="preventDefault"&&(h=!0)}}});a(b).trigger(f);
-b[g]&&(b[c]=b[g],b[g]=null);e&&!f.isDefaultPrevented()&&!h&&e(f)},washMouseEvent:function(a){var c=a.originalEvent||a;if(c.pageX===t)c.pageX=a.pageX,c.pageY=a.pageY;return c},animate:function(b,c,d){var e=a(b);if(!b.style)b.style={};if(c.d)b.toD=c.d,c.d=1;e.stop();c.opacity!==t&&b.attr&&(c.opacity+="px");e.animate(c,d)},stop:function(b){a(b).stop()}}})(I.jQuery);var S=I.HighchartsAdapter,N=S||{};S&&S.init.call(S,ub);var jb=N.adapterRun,Qb=N.getScript,Da=N.inArray,p=N.each,vb=N.grep,Rb=N.offset,Ua=
-N.map,K=N.addEvent,W=N.removeEvent,D=N.fireEvent,Sb=N.washMouseEvent,kb=N.animate,bb=N.stop,N={enabled:!0,x:0,y:15,style:{color:"#606060",cursor:"default",fontSize:"11px"}};E={colors:"#7cb5ec,#434348,#90ed7d,#f7a35c,#8085e9,#f15c80,#e4d354,#8085e8,#8d4653,#91e8e1".split(","),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),shortMonths:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),
-weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",numericSymbols:"k,M,G,T,P,E".split(","),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0,canvasToolsURL:"http://code.highcharts.com/4.0.1/modules/canvas-tools.js",VMLRadialGradientURL:"http://code.highcharts.com/4.0.1/gfx/vml-radial-gradient.png"},chart:{borderColor:"#4572A7",borderRadius:0,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacing:[10,10,15,
-10],backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0",resetZoomButton:{theme:{zIndex:20},position:{align:"right",x:-10,y:10}}},title:{text:"Chart title",align:"center",margin:15,style:{color:"#333333",fontSize:"18px"}},subtitle:{text:"",align:"center",style:{color:"#555555"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},lineWidth:2,marker:{lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{enabled:!0},select:{fillColor:"#FFFFFF",lineColor:"#000000",
-lineWidth:2}}},point:{events:{}},dataLabels:w(N,{align:"center",enabled:!1,formatter:function(){return this.y===null?"":Ga(this.y,-1)},verticalAlign:"bottom",y:0}),cropThreshold:300,pointRange:0,states:{hover:{marker:{},halo:{size:10,opacity:0.25}},select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3}},labels:{style:{position:"absolute",color:"#3E576F"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderColor:"#909090",borderRadius:0,navigation:{activeColor:"#274b6d",
-inactiveColor:"#CCC"},shadow:!1,itemStyle:{color:"#333333",fontSize:"12px",fontWeight:"bold"},itemHoverStyle:{color:"#000"},itemHiddenStyle:{color:"#CCC"},itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"1em"},style:{position:"absolute",backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,animation:aa,backgroundColor:"rgba(249, 249, 249, .85)",
-borderWidth:1,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},headerFormat:'<span style="font-size: 10px">{point.key}</span><br/>',pointFormat:'<span style="color:{series.color}">●</span> {series.name}: <b>{point.y}</b><br/>',shadow:!0,snap:Jb?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",
-whiteSpace:"nowrap"}},credits:{enabled:0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"9px"}}};var ba=E.plotOptions,S=ba.line;Cb();var Tb=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,Ub=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,Vb=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,ya=function(a){var b=[],c,
-d;(function(a){a&&a.stops?d=Ua(a.stops,function(a){return ya(a[1])}):(c=Tb.exec(a))?b=[z(c[1]),z(c[2]),z(c[3]),parseFloat(c[4],10)]:(c=Ub.exec(a))?b=[z(c[1],16),z(c[2],16),z(c[3],16),1]:(c=Vb.exec(a))&&(b=[z(c[1]),z(c[2]),z(c[3]),1])})(a);return{get:function(c){var f;d?(f=w(a),f.stops=[].concat(f.stops),p(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)p(d,
-function(b){b.brighten(a)});else if(ha(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=z(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};P.prototype={init:function(a,b){this.element=b==="span"?Y(b):y.createElementNS(xa,b);this.renderer=a},opacity:1,animate:function(a,b,c){b=m(b,va,!0);bb(this);if(b){b=w(b,{});if(c)b.complete=c;kb(this,a,b)}else this.attr(a),c&&c()},colorGradient:function(a,b,c){var d=this.renderer,e,f,g,h,i,j,k,l,o,n,s=[];a.linearGradient?
-f="linearGradient":a.radialGradient&&(f="radialGradient");if(f){g=a[f];h=d.gradients;j=a.stops;o=c.radialReference;La(g)&&(a[f]=g={x1:g[0],y1:g[1],x2:g[2],y2:g[3],gradientUnits:"userSpaceOnUse"});f==="radialGradient"&&o&&!r(g.gradientUnits)&&(g=w(g,{cx:o[0]-o[2]/2+g.cx*o[2],cy:o[1]-o[2]/2+g.cy*o[2],r:g.r*o[2],gradientUnits:"userSpaceOnUse"}));for(n in g)n!=="id"&&s.push(n,g[n]);for(n in j)s.push(j[n]);s=s.join(",");h[s]?a=h[s].attr("id"):(g.id=a="highcharts-"+tb++,h[s]=i=d.createElement(f).attr(g).add(d.defs),
-i.stops=[],p(j,function(a){a[1].indexOf("rgba")===0?(e=ya(a[1]),k=e.get("rgb"),l=e.get("a")):(k=a[1],l=1);a=d.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":l}).add(i);i.stops.push(a)}));c.setAttribute(b,"url("+d.url+"#"+a+")")}},attr:function(a,b){var c,d,e=this.element,f,g=this,h;typeof a==="string"&&b!==t&&(c=a,a={},a[c]=b);if(typeof a==="string")g=(this[a+"Getter"]||this._defaultGetter).call(this,a,e);else{for(c in a){d=a[c];h=!1;this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(c)&&
-(f||(this.symbolAttr(a),f=!0),h=!0);if(this.rotation&&(c==="x"||c==="y"))this.doTransform=!0;h||(this[c+"Setter"]||this._defaultSetter).call(this,d,c,e);this.shadows&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(c)&&this.updateShadows(c,d)}if(this.doTransform)this.updateTransform(),this.doTransform=!1}return g},updateShadows:function(a,b){for(var c=this.shadows,d=c.length;d--;)c[d].setAttribute(a,a==="height"?v(b-(c[d].cutHeight||0),0):a==="d"?this.d:b)},addClass:function(a){var b=this.element,
-c=H(b,"class")||"";c.indexOf(a)===-1&&H(b,"class",c+" "+a);return this},symbolAttr:function(a){var b=this;p("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),function(c){b[c]=m(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+a.id+")":Q)},crisp:function(a){var b,c={},d,e=a.strokeWidth||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;d=u(e)%2/2;a.x=T(a.x||this.x||
-0)+d;a.y=T(a.y||this.y||0)+d;a.width=T((a.width||this.width||0)-2*d);a.height=T((a.height||this.height||0)-2*d);a.strokeWidth=e;for(b in a)this[b]!==a[b]&&(this[b]=c[b]=a[b]);return c},css:function(a){var b=this.styles,c={},d=this.element,e,f,g="";e=!b;if(a&&a.color)a.fill=a.color;if(b)for(f in a)a[f]!==b[f]&&(c[f]=a[f],e=!0);if(e){e=this.textWidth=a&&a.width&&d.nodeName.toLowerCase()==="text"&&z(a.width);b&&(a=q(b,c));this.styles=a;e&&(fa||!aa&&this.renderer.forExport)&&delete a.width;if(Aa&&!aa)G(this.element,
-a);else{b=function(a,b){return"-"+b.toLowerCase()};for(f in a)g+=f.replace(/([A-Z])/g,b)+":"+a[f]+";";H(d,"style",g)}e&&this.added&&this.renderer.buildText(this)}return this},on:function(a,b){var c=this,d=c.element;$a&&a==="click"?(d.ontouchstart=function(a){c.touchEventFired=Date.now();a.preventDefault();b.call(d,a)},d.onclick=function(a){(wa.indexOf("Android")===-1||Date.now()-(c.touchEventFired||0)>1100)&&b.call(d,a)}):d["on"+a]=b;return this},setRadialReference:function(a){this.element.radialReference=
-a;return this},translate:function(a,b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation,g=this.element;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(g.getAttribute("x")||0)+" "+(g.getAttribute("y")||0)+")");
-(r(c)||r(d))&&a.push("scale("+m(c,1)+" "+m(d,1)+")");a.length&&g.setAttribute("transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||Fa(c))this.alignTo=d=c||"renderer",ja(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=m(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x||
-0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=u(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=u(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d,e=this.rotation;c=this.element;var f=this.styles,g=e*Ca;d=this.textStr;var h;if(d===""||Ob.test(d))h="num."+d.toString().length+
-(f?"|"+f.fontSize+"|"+f.fontFamily:"");h&&(a=b.cache[h]);if(!a){if(c.namespaceURI===xa||b.forExport){try{a=c.getBBox?q({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(i){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){c=a.width;d=a.height;if(Aa&&f&&f.fontSize==="11px"&&d.toPrecision(3)==="16.9")a.height=d=14;if(e)a.width=M(d*ea(g))+M(c*Z(g)),a.height=M(d*Z(g))+M(c*ea(g))}this.bBox=a;h&&(b.cache[h]=a)}return a},show:function(a){return a&&this.element.namespaceURI===
-xa?(this.element.removeAttribute("visibility"),this):this.attr({visibility:a?"inherit":"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=this.element,f=this.zIndex,g,h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(f)c.handleZ=!0,f=z(f);if(c.handleZ){a=d.childNodes;
-for(g=0;g<a.length;g++)if(b=a[g],c=H(b,"zIndex"),b!==e&&(z(c)>f||!r(f)&&r(c))){d.insertBefore(e,b);h=!0;break}}h||d.appendChild(e);this.added=!0;if(this.onAdd)this.onAdd();return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.renderer.isSVG&&b.nodeName==="SPAN"&&a.parentGroup,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;bb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=
-0;f<a.stops.length;f++)a.stops[f]=a.stops[f].destroy();a.stops=null}a.safeRemoveChild(b);for(c&&p(c,function(b){a.safeRemoveChild(b)});d&&d.div.childNodes.length===0;)b=d.parentGroup,a.safeRemoveChild(d.div),delete d.div,d=b;a.alignTo&&ja(a.renderer.alignedObjects,a);for(e in a)delete a[e];return null},shadow:function(a,b,c){var d=[],e,f,g=this.element,h,i,j,k;if(a){i=m(a.width,3);j=(a.opacity||0.15)/i;k=this.parentInverted?"(-1,-1)":"("+m(a.offsetX,1)+", "+m(a.offsetY,1)+")";for(e=1;e<=i;e++){f=
-g.cloneNode(0);h=i*2+1-2*e;H(f,{isShadow:"true",stroke:a.color||"black","stroke-opacity":j*e,"stroke-width":h,transform:"translate"+k,fill:Q});if(c)H(f,"height",v(H(f,"height")-h,0)),f.cutHeight=h;b?b.element.appendChild(f):g.parentNode.insertBefore(f,g);d.push(f)}this.shadows=d}return this},xGetter:function(a){this.element.nodeName==="circle"&&(a={x:"cx",y:"cy"}[a]||a);return this._defaultGetter(a)},_defaultGetter:function(a){a=m(this[a],this.element?this.element.getAttribute(a):null,0);/^[0-9\.]+$/.test(a)&&
-(a=parseFloat(a));return a},dSetter:function(a,b,c){a&&a.join&&(a=a.join(" "));/(NaN| {2}|^$)/.test(a)&&(a="M 0 0");c.setAttribute(b,a);this[b]=a},dashstyleSetter:function(a){var b;if(a=a&&a.toLowerCase()){a=a.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(b=a.length;b--;)a[b]=z(a[b])*this.element.getAttribute("stroke-width");
-a=a.join(",");this.element.setAttribute("stroke-dasharray",a)}},alignSetter:function(a){this.element.setAttribute("text-anchor",{left:"start",center:"middle",right:"end"}[a])},opacitySetter:function(a,b,c){this[b]=a;c.setAttribute(b,a)},"stroke-widthSetter":function(a,b,c){a===0&&(a=1.0E-5);this.strokeWidth=a;c.setAttribute(b,a)},titleSetter:function(a){var b=this.element.getElementsByTagName("title")[0];b||(b=y.createElementNS(xa,"title"),this.element.appendChild(b));b.textContent=a},textSetter:function(a){if(a!==
-this.textStr)delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this)},fillSetter:function(a,b,c){typeof a==="string"?c.setAttribute(b,a):a&&this.colorGradient(a,b,c)},zIndexSetter:function(a,b,c){c.setAttribute(b,a);this[b]=a},_defaultSetter:function(a,b,c){c.setAttribute(b,a)}};P.prototype.yGetter=P.prototype.xGetter;P.prototype.translateXSetter=P.prototype.translateYSetter=P.prototype.rotationSetter=P.prototype.verticalAlignSetter=P.prototype.scaleXSetter=P.prototype.scaleYSetter=
-function(a,b){this[b]=a;this.doTransform=!0};P.prototype.strokeSetter=P.prototype.fillSetter;var ta=function(){this.init.apply(this,arguments)};ta.prototype={Element:P,init:function(a,b,c,d,e){var f=location,g,d=this.createElement("svg").attr({version:"1.1"}).css(this.getStyle(d));g=d.element;a.appendChild(g);a.innerHTML.indexOf("xmlns")===-1&&H(g,"xmlns",xa);this.isSVG=!0;this.box=g;this.boxWrapper=d;this.alignedObjects=[];this.url=(Ta||ib)&&y.getElementsByTagName("base").length?f.href.replace(/#.*?$/,
-"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(y.createTextNode("Created with Highcharts 4.0.1"));this.defs=this.createElement("defs").add();this.forExport=e;this.gradients={};this.cache={};this.setSize(b,c,!1);var h;if(Ta&&a.getBoundingClientRect)this.subPixelFix=b=function(){G(a,{left:0,top:0});h=a.getBoundingClientRect();G(a,{left:Ka(h.left)-h.left+"px",top:Ka(h.top)-h.top+"px"})},b(),K(I,"resize",b)},getStyle:function(a){return this.style=
-q({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},a)},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();Oa(this.gradients||{});this.gradients=null;if(a)this.defs=a.destroy();this.subPixelFix&&W(I,"resize",this.subPixelFix);return this.alignedObjects=null},createElement:function(a){var b=new this.Element;b.init(this,a);return b},draw:function(){},
-buildText:function(a){for(var b=a.element,c=this,d=c.forExport,e=m(a.textStr,"").toString(),f=e.indexOf("<")!==-1,g=b.childNodes,h,i,j=H(b,"x"),k=a.styles,l=a.textWidth,o=k&&k.lineHeight,n=g.length,s=function(a){return o?z(o):c.fontMetrics(/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:k&&k.fontSize||c.style.fontSize||12).h};n--;)b.removeChild(g[n]);!f&&e.indexOf(" ")===-1?b.appendChild(y.createTextNode(e)):(h=/<.*style="([^"]+)".*>/,i=/<.*href="(http[^"]+)".*>/,l&&!a.added&&this.box.appendChild(b),
-e=f?e.replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g):[e],e[e.length-1]===""&&e.pop(),p(e,function(e,f){var g,n=0,e=e.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");g=e.split("|||");p(g,function(e){if(e!==""||g.length===1){var o={},m=y.createElementNS(xa,"tspan"),p;h.test(e)&&(p=e.match(h)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),
-H(m,"style",p));i.test(e)&&!d&&(H(m,"onclick",'location.href="'+e.match(i)[1]+'"'),G(m,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/&lt;/g,"<").replace(/&gt;/g,">");if(e!==" "){m.appendChild(y.createTextNode(e));if(n)o.dx=0;else if(f&&j!==null)o.x=j;H(m,o);!n&&f&&(!aa&&d&&G(m,{display:"block"}),H(m,"dy",s(m),ib&&m.offsetHeight));b.appendChild(m);n++;if(l)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),o=e.length>1&&k.whiteSpace!=="nowrap",$,r,B=a._clipHeight,q=[],v=s(),t=
-1;o&&(e.length||q.length);)delete a.bBox,$=a.getBBox(),r=$.width,!aa&&c.forExport&&(r=c.measureSpanWidth(m.firstChild.data,a.styles)),$=r>l,!$||e.length===1?(e=q,q=[],e.length&&(t++,B&&t*v>B?(e=["..."],a.attr("title",a.textStr)):(m=y.createElementNS(xa,"tspan"),H(m,{dy:v,x:j}),p&&H(m,"style",p),b.appendChild(m),r>l&&(l=r)))):(m.removeChild(m.firstChild),q.unshift(e.pop())),e.length&&m.appendChild(y.createTextNode(e.join(" ").replace(/- /g,"-")))}}})}))},button:function(a,b,c,d,e,f,g,h,i){var j=this.label(a,
-b,c,i,null,null,null,null,"button"),k=0,l,o,n,s,m,p,a={x1:0,y1:0,x2:0,y2:1},e=w({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);n=e.style;delete e.style;f=w(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);s=f.style;delete f.style;g=w(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#9BD"],[1,"#CDF"]]}},g);m=g.style;delete g.style;h=w(e,{style:{color:"#CCC"}},h);p=h.style;delete h.style;
-K(j.element,Aa?"mouseover":"mouseenter",function(){k!==3&&j.attr(f).css(s)});K(j.element,Aa?"mouseout":"mouseleave",function(){k!==3&&(l=[e,f,g][k],o=[n,s,m][k],j.attr(l).css(o))});j.setState=function(a){(j.state=k=a)?a===2?j.attr(g).css(m):a===3&&j.attr(h).css(p):j.attr(e).css(n)};return j.on("click",function(){k!==3&&d.call(j)}).attr(e).css(q({cursor:"default"},n))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=u(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=u(a[2])+b%2/2);return a},path:function(a){var b=
-{fill:Q};La(a)?b.d=a:ca(a)&&q(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=ca(a)?a:{x:a,y:b,r:c};b=this.createElement("circle");b.xSetter=function(a){this.element.setAttribute("cx",a)};b.ySetter=function(a){this.element.setAttribute("cy",a)};return b.attr(a)},arc:function(a,b,c,d,e,f){if(ca(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;a=this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0});a.r=c;return a},rect:function(a,b,c,d,e,f){var e=ca(a)?a.r:
-e,g=this.createElement("rect"),a=ca(a)?a:a===t?{}:{x:a,y:b,width:v(c,0),height:v(d,0)};if(f!==t)a.strokeWidth=f,a=g.crisp(a);if(e)a.r=e;g.rSetter=function(a){H(this.element,{rx:a,ry:a})};return g.attr(a)},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[m(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return r(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f=
-{preserveAspectRatio:Q};arguments.length>1&&q(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(u(b),u(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),q(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&q(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),
-a.alignByTranslate||a.translate(u((d-b[0])/2),u((e-b[1])/2)))},j=a.match(i)[1],a=Kb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),Y("img",{onload:function(){k(g,Kb[j]=[this.width,this.height])},src:j}));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",
-a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR,h=e.open,i=Z(f),j=ea(f),k=Z(g),g=ea(g),e=e.end-f<ma?0:1;return["M",a+c*i,b+c*j,"A",c,c,0,e,1,a+c*k,b+c*g,h?"M":"L",a+d*k,b+d*g,"A",d,d,0,e,0,a+d*i,b+d*j,h?"":"Z"]},callout:function(a,b,c,d,e){var f=C(e&&e.r||0,c,d),g=f+6,h=e&&e.anchorX,i=e&&e.anchorY,
-e=u(e.strokeWidth||0)%2/2;a+=e;b+=e;e=["M",a+f,b,"L",a+c-f,b,"C",a+c,b,a+c,b,a+c,b+f,"L",a+c,b+d-f,"C",a+c,b+d,a+c,b+d,a+c-f,b+d,"L",a+f,b+d,"C",a,b+d,a,b+d,a,b+d-f,"L",a,b+f,"C",a,b,a,b,a+f,b];h&&h>c&&i>b+g&&i<b+d-g?e.splice(13,3,"L",a+c,i-6,a+c+6,i,a+c,i+6,a+c,b+d-f):h&&h<0&&i>b+g&&i<b+d-g?e.splice(33,3,"L",a,i+6,a-6,i,a,i-6,a,b+f):i&&i>d&&h>a+g&&h<a+c-g?e.splice(23,3,"L",h+6,b+d,h,b+d+6,h-6,b+d,a+f,b+d):i&&i<0&&h>a+g&&h<a+c-g&&e.splice(3,3,"L",h-6,b,h,b-6,h+6,b,c-f,b);return e}},clipRect:function(a,
-b,c,d){var e="highcharts-"+tb++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},text:function(a,b,c,d){var e=fa||!aa&&this.forExport,f={};if(d&&!this.forExport)return this.html(a,b,c);f.x=Math.round(b||0);if(c)f.y=Math.round(c);if(a||a===0)f.text=a;a=this.createElement("text").attr(f);e&&a.css({position:"absolute"});if(!d)a.xSetter=function(a,b,c){var d=c.childNodes,e,f;for(f=1;f<d.length;f++)e=d[f],e.getAttribute("x")===c.getAttribute("x")&&
-e.setAttribute("x",a);c.setAttribute(b,a)};return a},fontMetrics:function(a){var a=a||this.style.fontSize,a=/px/.test(a)?z(a):/em/.test(a)?parseFloat(a)*12:12,a=a<24?a+4:u(a*1.2),b=u(a*0.8);return{h:a,b:b}},label:function(a,b,c,d,e,f,g,h,i){function j(){var a,b;a=s.element.style;J=(Va===void 0||wb===void 0||n.styles.textAlign)&&s.textStr&&s.getBBox();n.width=(Va||J.width||0)+2*x+v;n.height=(wb||J.height||0)+2*x;na=x+o.fontMetrics(a&&a.fontSize).b;if(z){if(!m)a=u(-L*x),b=h?-na:0,n.box=m=d?o.symbol(d,
-a,b,n.width,n.height,B):o.rect(a,b,n.width,n.height,0,B[Pb]),m.attr("fill",Q).add(n);m.isImg||m.attr(q({width:u(n.width),height:u(n.height)},B));B=null}}function k(){var a=n.styles,a=a&&a.textAlign,b=v+x*(1-L),c;c=h?0:na;if(r(Va)&&J&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(Va-J.width);if(b!==s.x||c!==s.y)s.attr("x",b),c!==t&&s.attr("y",c);s.x=b;s.y=c}function l(a,b){m?m.attr(a,b):B[a]=b}var o=this,n=o.g(i),s=o.text("",0,0,g).attr({zIndex:1}),m,J,L=0,x=3,v=0,Va,wb,xb,yb,y=0,B={},na,
-z;n.onAdd=function(){s.add(n);n.attr({text:a||"",x:b,y:c});m&&r(e)&&n.attr({anchorX:e,anchorY:f})};n.widthSetter=function(a){Va=a};n.heightSetter=function(a){wb=a};n.paddingSetter=function(a){r(a)&&a!==x&&(x=a,k())};n.paddingLeftSetter=function(a){r(a)&&a!==v&&(v=a,k())};n.alignSetter=function(a){L={left:0,center:0.5,right:1}[a]};n.textSetter=function(a){a!==t&&s.textSetter(a);j();k()};n["stroke-widthSetter"]=function(a,b){a&&(z=!0);y=a%2/2;l(b,a)};n.strokeSetter=n.fillSetter=n.rSetter=function(a,
-b){b==="fill"&&a&&(z=!0);l(b,a)};n.anchorXSetter=function(a,b){e=a;l(b,a+y-xb)};n.anchorYSetter=function(a,b){f=a;l(b,a-yb)};n.xSetter=function(a){n.x=a;L&&(a-=L*((Va||J.width)+x));xb=u(a);n.attr("translateX",xb)};n.ySetter=function(a){yb=n.y=u(a);n.attr("translateY",yb)};var A=n.css;return q(n,{css:function(a){if(a){var b={},a=w(a);p("fontSize,fontWeight,fontFamily,color,lineHeight,width,textDecoration,textShadow".split(","),function(c){a[c]!==t&&(b[c]=a[c],delete a[c])});s.css(b)}return A.call(n,
-a)},getBBox:function(){return{width:J.width+2*x,height:J.height+2*x,x:J.x-x,y:J.y-x}},shadow:function(a){m&&m.shadow(a);return n},destroy:function(){W(n.element,"mouseenter");W(n.element,"mouseleave");s&&(s=s.destroy());m&&(m=m.destroy());P.prototype.destroy.call(n);n=o=j=k=l=null}})}};Za=ta;q(P.prototype,{htmlCss:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=q(this.styles,a);G(this.element,a);return this},htmlGetBBox:function(){var a=
-this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position="absolute";b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b},htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,c=this.translateX||0,d=this.translateY||0,e=this.x||0,f=this.y||0,g=this.textAlign||"left",h={left:0,center:0.5,right:1}[g],i=this.shadows;G(b,{marginLeft:c,marginTop:d});i&&p(i,function(a){G(a,{marginLeft:c+1,marginTop:d+1})});this.inverted&&
-p(b.childNodes,function(c){a.invertChild(c,b)});if(b.tagName==="SPAN"){var j=this.rotation,k,l=z(this.textWidth),o=[j,g,b.innerHTML,this.textWidth].join(",");if(o!==this.cTT){k=a.fontMetrics(b.style.fontSize).b;r(j)&&this.setSpanRotation(j,h,k);i=m(this.elemWidth,b.offsetWidth);if(i>l&&/[ \-]/.test(b.textContent||b.innerText))G(b,{width:l+"px",display:"block",whiteSpace:"normal"}),i=l;this.getSpanCorrection(i,k,h,j,g)}G(b,{left:e+(this.xCorr||0)+"px",top:f+(this.yCorr||0)+"px"});if(ib)k=b.offsetHeight;
-this.cTT=o}}else this.alignOnAdd=!0},setSpanRotation:function(a,b,c){var d={},e=Aa?"-ms-transform":ib?"-webkit-transform":Ta?"MozTransform":Ib?"-o-transform":"";d[e]=d.transform="rotate("+a+"deg)";d[e+(Ta?"Origin":"-origin")]=d.transformOrigin=b*100+"% "+c+"px";G(this.element,d)},getSpanCorrection:function(a,b,c){this.xCorr=-a*c;this.yCorr=-b}});q(ta.prototype,{html:function(a,b,c){var d=this.createElement("span"),e=d.element,f=d.renderer;d.textSetter=function(a){a!==e.innerHTML&&delete this.bBox;
-e.innerHTML=this.textStr=a};d.xSetter=d.ySetter=d.alignSetter=d.rotationSetter=function(a,b){b==="align"&&(b="textAlign");d[b]=a;d.htmlUpdateTransform()};d.attr({text:a,x:u(b),y:u(c)}).css({position:"absolute",whiteSpace:"nowrap",fontFamily:this.style.fontFamily,fontSize:this.style.fontSize});d.css=d.htmlCss;if(f.isSVG)d.add=function(a){var b,c=f.box.parentNode,j=[];if(this.parentGroup=a){if(b=a.div,!b){for(;a;)j.push(a),a=a.parentGroup;p(j.reverse(),function(a){var d;b=a.div=a.div||Y(Ja,{className:H(a.element,
-"class")},{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px"},b||c);d=b.style;q(a,{translateXSetter:function(b,c){d.left=b+"px";a[c]=b;a.doTransform=!0},translateYSetter:function(b,c){d.top=b+"px";a[c]=b;a.doTransform=!0},visibilitySetter:function(a,b){d[b]=a}})})}}else b=c;b.appendChild(e);d.added=!0;d.alignOnAdd&&d.htmlUpdateTransform();return d};return d}});var X;if(!aa&&!fa){R.VMLElement=X={init:function(a,b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ","absolute",
-";"],e=b===Ja;(b==="shape"||e)&&d.push("left:0;top:0;width:1px;height:1px;");d.push("visibility: ",e?"hidden":"visible");c.push(' style="',d.join(""),'"/>');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=Y(c);this.renderer=a},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();if(this.onAdd)this.onAdd();return this},updateTransform:P.prototype.htmlUpdateTransform,
-setSpanRotation:function(){var a=this.rotation,b=Z(a*Ca),c=ea(a*Ca);G(this.element,{filter:a?["progid:DXImageTransform.Microsoft.Matrix(M11=",b,", M12=",-c,", M21=",c,", M22=",b,", sizingMethod='auto expand')"].join(""):Q})},getSpanCorrection:function(a,b,c,d,e){var f=d?Z(d*Ca):1,g=d?ea(d*Ca):0,h=m(this.elemHeight,this.element.offsetHeight),i;this.xCorr=f<0&&-a;this.yCorr=g<0&&-h;i=f*g<0;this.xCorr+=g*b*(i?1-c:c);this.yCorr-=f*b*(d?i?c:1-c:1);e&&e!=="left"&&(this.xCorr-=a*c*(f<0?-1:1),d&&(this.yCorr-=
-h*c*(g<0?-1:1)),G(this.element,{textAlign:e}))},pathToVML:function(a){for(var b=a.length,c=[];b--;)if(ha(a[b]))c[b]=u(a[b]*10)-5;else if(a[b]==="Z")c[b]="x";else if(c[b]=a[b],a.isArc&&(a[b]==="wa"||a[b]==="at"))c[b+5]===c[b+7]&&(c[b+7]+=a[b+7]>a[b+5]?1:-1),c[b+6]===c[b+8]&&(c[b+8]+=a[b+8]>a[b+6]?1:-1);return c.join(" ")||"x"},clip:function(a){var b=this,c;a?(c=a.members,ja(c,b),c.push(b),b.destroyClip=function(){ja(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:hb?"inherit":"rect(auto)"});
-return b.css(a)},css:P.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Pa(a)},destroy:function(){this.destroyClip&&this.destroyClip();return P.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=I.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=z(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,
-l,o,n,s;k&&typeof k.value!=="string"&&(k="x");o=k;if(a){n=m(a.width,3);s=(a.opacity||0.15)/n;for(e=1;e<=3;e++){l=n*2+1-2*e;c&&(o=this.cutOffPath(k.value,l+0.5));j=['<shape isShadow="true" strokeweight="',l,'" filled="false" path="',o,'" coordsize="10 10" style="',f.style.cssText,'" />'];h=Y(g.prepVML(j),null,{left:z(i.left)+m(a.offsetX,1),top:z(i.top)+m(a.offsetY,1)});if(c)h.cutOff=l+1;j=['<stroke color="',a.color||"black",'" opacity="',s*e,'"/>'];Y(g.prepVML(j),null,null,h);b?b.element.appendChild(h):
-f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this},updateShadows:sa,setAttr:function(a,b){hb?this.element[a]=b:this.element.setAttribute(a,b)},classSetter:function(a){this.element.className=a},dashstyleSetter:function(a,b,c){(c.getElementsByTagName("stroke")[0]||Y(this.renderer.prepVML(["<stroke/>"]),null,null,c))[b]=a||"solid";this[b]=a},dSetter:function(a,b,c){var d=this.shadows,a=a||[];this.d=a.join(" ");c.path=a=this.pathToVML(a);if(d)for(c=d.length;c--;)d[c].path=d[c].cutOff?
-this.cutOffPath(a,d[c].cutOff):a;this.setAttr(b,a)},fillSetter:function(a,b,c){var d=c.nodeName;if(d==="SPAN")c.style.color=a;else if(d!=="IMG")c.filled=a!==Q,this.setAttr("fillcolor",this.renderer.color(a,c,b,this))},opacitySetter:sa,rotationSetter:function(a,b,c){c=c.style;this[b]=c[b]=a;c.left=-u(ea(a*Ca)+1)+"px";c.top=u(Z(a*Ca))+"px"},strokeSetter:function(a,b,c){this.setAttr("strokecolor",this.renderer.color(a,c,b))},"stroke-widthSetter":function(a,b,c){c.stroked=!!a;this[b]=a;ha(a)&&(a+="px");
-this.setAttr("strokeweight",a)},titleSetter:function(a,b){this.setAttr(b,a)},visibilitySetter:function(a,b,c){a==="inherit"&&(a="visible");this.shadows&&p(this.shadows,function(c){c.style[b]=a});c.nodeName==="DIV"&&(a=a==="hidden"?"-999em":0,hb||(c.style[b]=a?"visible":"hidden"),b="top");c.style[b]=a},xSetter:function(a,b,c){this[b]=a;b==="x"?b="left":b==="y"&&(b="top");this.updateClipping?(this[b]=a,this.updateClipping()):c.style[b]=a},zIndexSetter:function(a,b,c){c.style[b]=a}};X=ka(P,X);X.prototype.ySetter=
-X.prototype.widthSetter=X.prototype.heightSetter=X.prototype.xSetter;var ga={Element:X,isIE8:wa.indexOf("MSIE 8.0")>-1,init:function(a,b,c,d){var e;this.alignedObjects=[];d=this.createElement(Ja).css(q(this.getStyle(d),{position:"relative"}));e=d.element;a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.cache={};this.setSize(b,c,!1);if(!y.namespaces.hcv){y.namespaces.add("hcv","urn:schemas-microsoft-com:vml");try{y.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}catch(f){y.styleSheets[0].cssText+=
-"hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}}},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(),f=ca(a);return q(e,{members:[],left:(f?a.x:a)+1,top:(f?a.y:b)+1,width:(f?a.width:c)-1,height:(f?a.height:d)-1,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+u(a?e:d)+"px,"+u(a?
-f:b)+"px,"+u(a?b:f)+"px,"+u(a?d:e)+"px)"};!a&&hb&&c==="DIV"&&q(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){p(e.members,function(a){a.element&&a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=Q;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,l,o=a.linearGradient||a.radialGradient,n,s,m,J,L,x="",a=a.stops,r,v=[],q=function(){h=['<fill colors="'+v.join(",")+'" opacity="',m,'" o:opacity2="',s,'" type="',i,'" ',x,'focus="100%" method="any" />'];
-Y(e.prepVML(h),null,null,b)};n=a[0];r=a[a.length-1];n[0]>0&&a.unshift([0,n[1]]);r[0]<1&&a.push([1,r[1]]);p(a,function(a,b){g.test(a[1])?(f=ya(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);v.push(a[0]*100+"% "+k);b?(m=l,J=k):(s=l,L=k)});if(c==="fill")if(i==="gradient")c=o.x1||o[0]||0,a=o.y1||o[1]||0,n=o.x2||o[2]||0,o=o.y2||o[3]||0,x='angle="'+(90-U.atan((o-a)/(n-c))*180/ma)+'"',q();else{var j=o.r,t=j*2,u=j*2,y=o.cx,B=o.cy,na=b.radialReference,w,j=function(){na&&(w=d.getBBox(),y+=(na[0]-w.x)/w.width-
-0.5,B+=(na[1]-w.y)/w.height-0.5,t*=na[2]/w.width,u*=na[2]/w.height);x='src="'+E.global.VMLRadialGradientURL+'" size="'+t+","+u+'" origin="0.5,0.5" position="'+y+","+B+'" color2="'+L+'" ';q()};d.added?j():d.onAdd=j;j=J}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=ya(a),h=["<",c,' opacity="',f.get("a"),'"/>'],Y(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?
-(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:ta.prototype.html,path:function(a){var b={coordsize:"10 10"};La(a)?b.d=a:ca(a)&&q(b,a);return this.createElement("shape").attr(b)},circle:function(a,b,c){var d=this.symbol("circle");if(ca(a))c=a.r,b=a.y,a=a.x;d.isCircle=
-!0;d.r=c;return d.attr({x:a,y:b})},g:function(a){var b;a&&(b={className:"highcharts-"+a,"class":"highcharts-"+a});return this.createElement(Ja).attr(b)},image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.attr({x:b,y:c,width:d,height:e});return f},createElement:function(a){return a==="rect"?this.symbol(a):ta.prototype.createElement.call(this,a)},invertChild:function(a,b){var c=this,d=b.style,e=a.tagName==="IMG"&&a.style;G(a,{flip:"x",left:z(d.width)-(e?z(e.top):
-1),top:z(d.height)-(e?z(e.left):1),rotation:-90});p(a.childNodes,function(b){c.invertChild(b,a)})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=Z(f),i=ea(f),j=Z(g),k=ea(g);if(g-f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&(c=d=2*e.r);e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+
-c,b+d/2,"e"]},rect:function(a,b,c,d,e){return ta.prototype.symbols[!r(e)||!e.r?"square":"callout"].call(0,a,b,c,d,e)}}};R.VMLRenderer=X=function(){this.init.apply(this,arguments)};X.prototype=w(ta.prototype,ga);Za=X}ta.prototype.measureSpanWidth=function(a,b){var c=y.createElement("span"),d;d=y.createTextNode(a);c.appendChild(d);G(c,b);this.box.appendChild(c);d=c.offsetWidth;Pa(c);return d};var Lb;if(fa)R.CanVGRenderer=X=function(){xa="http://www.w3.org/1999/xhtml"},X.prototype.symbols={},Lb=function(){function a(){var a=
-b.length,d;for(d=0;d<a;d++)b[d]();b=[]}var b=[];return{push:function(c,d){b.length===0&&Qb(d,a);b.push(c)}}}(),Za=X;Sa.prototype={addLabel:function(){var a=this.axis,b=a.options,c=a.chart,d=a.horiz,e=a.categories,f=a.names,g=this.pos,h=b.labels,i=a.tickPositions,d=d&&e&&!h.step&&!h.staggerLines&&!h.rotation&&c.plotWidth/i.length||!d&&(c.margin[3]||c.chartWidth*0.33),j=g===i[0],k=g===i[i.length-1],l,f=e?m(e[g],f[g],g):g,e=this.label,o=i.info;a.isDatetimeAxis&&o&&(l=b.dateTimeLabelFormats[o.higherRanks[g]||
-o.unitName]);this.isFirst=j;this.isLast=k;b=a.labelFormatter.call({axis:a,chart:c,isFirst:j,isLast:k,dateTimeLabelFormat:l,value:a.isLog?da(ia(f)):f});g=d&&{width:v(1,u(d-2*(h.padding||10)))+"px"};g=q(g,h.style);if(r(e))e&&e.attr({text:b}).css(g);else{l={align:a.labelAlign};if(ha(h.rotation))l.rotation=h.rotation;if(d&&h.ellipsis)l._clipHeight=a.len/i.length;this.label=r(b)&&h.enabled?c.renderer.text(b,0,0,h.useHTML).attr(l).css(g).add(a.labelGroup):null}},getLabelSize:function(){var a=this.label,
-b=this.axis;return a?a.getBBox()[b.horiz?"height":"width"]:0},getLabelSides:function(){var a=this.label.getBBox(),b=this.axis,c=b.horiz,d=b.options.labels,a=c?a.width:a.height,b=c?d.x-a*{left:0,center:0.5,right:1}[b.labelAlign]:0;return[b,c?a+b:a]},handleOverflow:function(a,b){var c=!0,d=this.axis,e=this.isFirst,f=this.isLast,g=d.horiz?b.x:b.y,h=d.reversed,i=d.tickPositions,j=this.getLabelSides(),k=j[0],j=j[1],l,o,n,s=this.label.line||0;l=d.labelEdge;o=d.justifyLabels&&(e||f);l[s]===t||g+k>l[s]?l[s]=
-g+j:o||(c=!1);if(o){l=(o=d.justifyToPlot)?d.pos:0;o=o?l+d.len:d.chart.chartWidth;do a+=e?1:-1,n=d.ticks[i[a]];while(i[a]&&(!n||n.label.line!==s));d=n&&n.label.xy&&n.label.xy.x+n.getLabelSides()[e?0:1];e&&!h||f&&h?g+k<l&&(g=l-k,n&&g+j>d&&(c=!1)):g+j>o&&(g=o-j,n&&g+k<d&&(c=!1));b.x=g}return c},getPosition:function(a,b,c,d){var e=this.axis,f=e.chart,g=d&&f.oldChartHeight||f.chartHeight;return{x:a?e.translate(b+c,null,null,d)+e.transB:e.left+e.offset+(e.opposite?(d&&f.oldChartWidth||f.chartWidth)-e.right-
-e.left:0),y:a?g-e.bottom+e.offset-(e.opposite?e.height:0):g-e.translate(b+c,null,null,d)-e.transB}},getLabelPosition:function(a,b,c,d,e,f,g,h){var i=this.axis,j=i.transA,k=i.reversed,l=i.staggerLines,o=i.chart.renderer.fontMetrics(e.style.fontSize).b,n=e.rotation,a=a+e.x-(f&&d?f*j*(k?-1:1):0),b=b+e.y-(f&&!d?f*j*(k?1:-1):0);n&&i.side===2&&(b-=o-o*Z(n*Ca));!r(e.y)&&!n&&(b+=o-c.getBBox().height/2);if(l)c.line=g/(h||1)%l,b+=c.line*(i.labelOffset/l);return{x:a,y:b}},getMarkPath:function(a,b,c,d,e,f){return f.crispLine(["M",
-a,b,"L",a+(e?0:-c),b+(e?c:0)],d)},render:function(a,b,c){var d=this.axis,e=d.options,f=d.chart.renderer,g=d.horiz,h=this.type,i=this.label,j=this.pos,k=e.labels,l=this.gridLine,o=h?h+"Grid":"grid",n=h?h+"Tick":"tick",s=e[o+"LineWidth"],p=e[o+"LineColor"],J=e[o+"LineDashStyle"],L=e[n+"Length"],o=e[n+"Width"]||0,x=e[n+"Color"],r=e[n+"Position"],n=this.mark,v=k.step,q=!0,u=d.tickmarkOffset,w=this.getPosition(g,j,u,b),y=w.x,w=w.y,B=g&&y===d.pos+d.len||!g&&w===d.pos?-1:1;this.isActive=!0;if(s){j=d.getPlotLinePath(j+
-u,s*B,b,!0);if(l===t){l={stroke:p,"stroke-width":s};if(J)l.dashstyle=J;if(!h)l.zIndex=1;if(b)l.opacity=0;this.gridLine=l=s?f.path(j).attr(l).add(d.gridGroup):null}if(!b&&l&&j)l[this.isNew?"attr":"animate"]({d:j,opacity:c})}if(o&&L)r==="inside"&&(L=-L),d.opposite&&(L=-L),h=this.getMarkPath(y,w,L,o*B,g,f),n?n.animate({d:h,opacity:c}):this.mark=f.path(h).attr({stroke:x,"stroke-width":o,opacity:c}).add(d.axisGroup);if(i&&!isNaN(y))i.xy=w=this.getLabelPosition(y,w,i,g,k,u,a,v),this.isFirst&&!this.isLast&&
-!m(e.showFirstLabel,1)||this.isLast&&!this.isFirst&&!m(e.showLastLabel,1)?q=!1:!d.isRadial&&!k.step&&!k.rotation&&!b&&c!==0&&(q=this.handleOverflow(a,w)),v&&a%v&&(q=!1),q&&!isNaN(w.y)?(w.opacity=c,i[this.isNew?"attr":"animate"](w),this.isNew=!1):i.attr("y",-9999)},destroy:function(){Oa(this,this.axis)}};R.PlotLineOrBand=function(a,b){this.axis=a;if(b)this.options=b,this.id=b.id};R.PlotLineOrBand.prototype={render:function(){var a=this,b=a.axis,c=b.horiz,d=(b.pointRange||0)/2,e=a.options,f=e.label,
-g=a.label,h=e.width,i=e.to,j=e.from,k=r(j)&&r(i),l=e.value,o=e.dashStyle,n=a.svgElem,s=[],p,J=e.color,L=e.zIndex,x=e.events,q={},t=b.chart.renderer;b.isLog&&(j=za(j),i=za(i),l=za(l));if(h){if(s=b.getPlotLinePath(l,h),q={stroke:J,"stroke-width":h},o)q.dashstyle=o}else if(k){j=v(j,b.min-d);i=C(i,b.max+d);s=b.getPlotBandPath(j,i,e);if(J)q.fill=J;if(e.borderWidth)q.stroke=e.borderColor,q["stroke-width"]=e.borderWidth}else return;if(r(L))q.zIndex=L;if(n)if(s)n.animate({d:s},null,n.onGetPath);else{if(n.hide(),
-n.onGetPath=function(){n.show()},g)a.label=g=g.destroy()}else if(s&&s.length&&(a.svgElem=n=t.path(s).attr(q).add(),x))for(p in d=function(b){n.on(b,function(c){x[b].apply(a,[c])})},x)d(p);if(f&&r(f.text)&&s&&s.length&&b.width>0&&b.height>0){f=w({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g){q={align:f.textAlign||f.align,rotation:f.rotation};if(r(L))q.zIndex=L;a.label=g=t.text(f.text,0,0,f.useHTML).attr(q).css(f.style).add()}b=[s[1],
-s[4],m(s[6],s[1])];s=[s[2],s[5],m(s[7],s[2])];c=Na(b);k=Na(s);g.align(f,!1,{x:c,y:k,width:Ba(b)-c,height:Ba(s)-k});g.show()}else g&&g.hide();return a},destroy:function(){ja(this.axis.plotLinesAndBands,this);delete this.axis;Oa(this)}};la.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:N,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,
-maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#707070"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,
-tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return Ga(this.total,-1)},style:N.style}},defaultLeftAxisOptions:{labels:{x:-15,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{x:15,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{x:0,y:20},title:{rotation:0}},defaultTopAxisOptions:{labels:{x:0,y:-15},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.coll=(this.isXAxis=c)?"xAxis":"yAxis";this.opposite=
-b.opposite;this.side=b.side||(this.horiz?this.opposite?0:2:this.opposite?1:3);this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.names=[];this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=r(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement===
-"between"?0.5:0;this.ticks={};this.labelEdge=[];this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.oldStacks={};this.min=this.max=null;this.crosshair=m(d.crosshair,qa(a.options.tooltip.crosshairs)[c?0:1],!1);var f,d=this.options.events;Da(this,a.axes)===-1&&(c&&!this.isColorAxis?a.axes.splice(a.xAxis.length,0,this):a.axes.push(this),a[this.coll].push(this));
-this.series=this.series||[];if(a.inverted&&c&&this.reversed===t)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)K(this,f,d[f]);if(this.isLog)this.val2lin=za,this.lin2val=ia},setOptions:function(a){this.options=w(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],w(E[this.coll],a))},defaultLabelFormatter:function(){var a=
-this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=E.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=Ia(h,this);else if(c)g=b;else if(d)g=cb(d,b);else if(f&&a>=1E3)for(;f--&&g===t;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=Ga(b/c,-1)+e[f]);g===t&&(g=M(b)>=1E4?Ga(b,0):Ga(b,-1,t,""));return g},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;a.buildStacks&&a.buildStacks();p(a.series,function(c){if(c.visible||
-!b.options.chart.ignoreHiddenSeries){var d;d=c.options.threshold;var e;a.hasVisibleSeries=!0;a.isLog&&d<=0&&(d=null);if(a.isXAxis){if(d=c.xData,d.length)a.dataMin=C(m(a.dataMin,d[0]),Na(d)),a.dataMax=v(m(a.dataMax,d[0]),Ba(d))}else{c.getExtremes();e=c.dataMax;c=c.dataMin;if(r(c)&&r(e))a.dataMin=C(m(a.dataMin,c),c),a.dataMax=v(m(a.dataMax,e),e);if(r(d))if(a.dataMin>=d)a.dataMin=d,a.ignoreMinPadding=!0;else if(a.dataMax<d)a.dataMax=d,a.ignoreMaxPadding=!0}}})},translate:function(a,b,c,d,e,f){var g=
-1,h=0,i=d?this.oldTransA:this.transA,d=d?this.oldMin:this.min,j=this.minPixelPadding,e=(this.options.ordinal||this.isLog&&e)&&this.lin2val;if(!i)i=this.transA;if(c)g*=-1,h=this.len;this.reversed&&(g*=-1,h-=g*(this.sector||this.len));b?(a=a*g+h,a-=j,a=a/i+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),f==="between"&&(f=0.5),a=g*(a-d)*i+h+g*j+(ha(f)?i*f*this.pointRange:0));return a},toPixels:function(a,b){return this.translate(a,!1,!this.horiz,null,!0)+(b?0:this.pos)},toValue:function(a,b){return this.translate(a-
-(b?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,b,c,d,e){var f=this.chart,g=this.left,h=this.top,i,j,k=c&&f.oldChartHeight||f.chartHeight,l=c&&f.oldChartWidth||f.chartWidth,o;i=this.transB;e=m(e,this.translate(a,null,null,c));a=c=u(e+i);i=j=u(k-e-i);if(isNaN(e))o=!0;else if(this.horiz){if(i=h,j=k-this.bottom,a<g||a>g+this.width)o=!0}else if(a=g,c=l-this.right,i<h||i>h+this.height)o=!0;return o&&!d?null:f.renderer.crispLine(["M",a,i,"L",c,j],b||1)},getLinearTickPositions:function(a,
-b,c){var d,e=da(T(b/a)*a),f=da(Ka(c/a)*a),g=[];if(b===c&&ha(b))return[b];for(b=e;b<=f;){g.push(b);b=da(b+a);if(b===d)break;d=b}return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length;for(a=1;a<e;a++)d=d.concat(this.getLogTickPositions(c,b[a-1],b[a],!0))}else if(this.isDatetimeAxis&&a.minorTickInterval==="auto")d=d.concat(this.getTimeTicks(this.normalizeTimeTickInterval(c),this.min,this.max,a.startOfWeek)),d[0]<this.min&&
-d.shift();else for(b=this.min+(b[0]-this.min)%c;b<=this.max;b+=c)d.push(b);return d},adjustForMinRange:function(){var a=this.options,b=this.min,c=this.max,d,e=this.dataMax-this.dataMin>=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===t&&!this.isLog)r(a.min)||r(a.max)?this.minRange=null:(p(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===t||h<f)f=h}),this.minRange=C(f*5,this.dataMax-this.dataMin));if(c-b<this.minRange){var k=this.minRange;d=
-(k-c+b)/2;d=[b-d,m(a.min,b-d)];if(e)d[2]=this.dataMin;b=Ba(d);c=[b+k,m(a.max,b+k)];if(e)c[2]=this.dataMax;c=Na(c);c-b<k&&(d[0]=c-k,d[1]=m(a.min,c-k),b=Ba(d))}this.min=b;this.max=c},setAxisTranslation:function(a){var b=this,c=b.max-b.min,d=b.axisPointRange||0,e,f=0,g=0,h=b.linkedParent,i=!!b.categories,j=b.transA;if(b.isXAxis||i||d)h?(f=h.minPointOffset,g=h.pointRangePadding):p(b.series,function(a){var h=i?1:b.isXAxis?a.pointRange:b.axisPointRange||0,j=a.options.pointPlacement,n=a.closestPointRange;
-h>c&&(h=0);d=v(d,h);f=v(f,Fa(j)?0:h/2);g=v(g,j==="on"?0:h);!a.noSharedTooltip&&r(n)&&(e=r(e)?C(e,n):n)}),h=b.ordinalSlope&&e?b.ordinalSlope/e:1,b.minPointOffset=f*=h,b.pointRangePadding=g*=h,b.pointRange=C(d,c),b.closestPointRange=e;if(a)b.oldTransA=j;b.translationSlope=b.transA=j=b.len/(c+g||1);b.transB=b.horiz?b.left:b.bottom;b.minPixelPadding=j*f},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,
-k=d.minPadding,l=d.tickInterval,o=d.minTickInterval,n=d.tickPixelInterval,s,$=b.categories;h?(b.linkedParent=c[b.coll][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=m(c.min,c.dataMin),b.max=m(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&ra(11,1)):(b.min=m(b.userMin,d.min,b.dataMin),b.max=m(b.userMax,d.max,b.dataMax));if(e)!a&&C(b.min,m(b.dataMin,b.min))<=0&&ra(10,1),b.min=da(za(b.min)),b.max=da(za(b.max));if(b.range&&r(b.max))b.userMin=b.min=v(b.min,b.max-b.range),b.userMax=b.max,b.range=
-null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!$&&!b.axisPointRange&&!b.usePercentage&&!h&&r(b.min)&&r(b.max)&&(c=b.max-b.min)){if(!r(d.min)&&!r(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!r(d.max)&&!r(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}if(ha(d.floor))b.min=v(b.min,d.floor);if(ha(d.ceiling))b.max=C(b.max,d.ceiling);b.min===b.max||b.min===void 0||b.max===void 0?b.tickInterval=1:h&&!l&&n===b.linkedParent.options.tickPixelInterval?b.tickInterval=
-b.linkedParent.tickInterval:(b.tickInterval=m(l,$?1:(b.max-b.min)*n/v(b.len,n)),!r(l)&&b.len<n&&!this.isRadial&&!this.isLog&&!$&&d.startOnTick&&d.endOnTick&&(s=!0,b.tickInterval/=4));g&&!a&&p(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(b.pointRange)b.tickInterval=v(b.pointRange,b.tickInterval);if(!l&&b.tickInterval<
-o)b.tickInterval=o;if(!f&&!e&&!l)b.tickInterval=nb(b.tickInterval,null,mb(b.tickInterval),d);b.minorTickInterval=d.minorTickInterval==="auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=a=d.tickPositions?[].concat(d.tickPositions):i&&i.apply(b,[b.min,b.max]);if(!a)!b.ordinalPositions&&(b.max-b.min)/b.tickInterval>v(2*b.len,200)&&ra(19,!0),a=f?b.getTimeTicks(b.normalizeTimeTickInterval(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,
-!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),s&&a.splice(1,a.length-2),b.tickPositions=a;if(!h)e=a[0],f=a[a.length-1],h=b.minPointOffset||0,d.startOnTick?b.min=e:b.min-h>e&&a.shift(),d.endOnTick?b.max=f:b.max+h<f&&a.pop(),a.length===1&&(d=M(b.max)>1E13?1:0.001,b.min-=d,b.max+=d)},setMaxTicks:function(){var a=this.chart,b=a.maxTicks||{},c=this.tickPositions,d=this._maxTicksKey=[this.coll,this.pos,this.len].join("-");if(!this.isLinked&&
-!this.isDatetimeAxis&&c&&c.length>(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1&&this.min!==t){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e<a){for(;b.length<a;)b.push(da(b[b.length-1]+this.tickInterval));this.transA*=(e-1)/(a-1);this.max=b[b.length-1]}if(r(d)&&a!==d)this.isDirty=
-!0}},setScale:function(){var a=this.stacks,b,c,d,e;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();e=this.len!==this.oldAxisLength;p(this.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)d=!0});if(e||d||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax){if(!this.isXAxis)for(b in a)for(c in a[b])a[b][c].total=null,a[b][c].cum=0;this.forceRedraw=!1;this.getSeriesExtremes();this.setTickPositions();this.oldUserMin=
-this.userMin;this.oldUserMax=this.userMax;if(!this.isDirty)this.isDirty=e||this.min!==this.oldMin||this.max!==this.oldMax}else if(!this.isXAxis){if(this.oldStacks)a=this.stacks=this.oldStacks;for(b in a)for(c in a[b])a[b][c].cum=a[b][c].total}this.setMaxTicks()},setExtremes:function(a,b,c,d,e){var f=this,g=f.chart,c=m(c,!0),e=q(e,{min:a,max:b});D(f,"setExtremes",e,function(){f.userMin=a;f.userMax=b;f.eventArgs=e;f.isDirtyExtremes=!0;c&&g.redraw(d)})},zoom:function(a,b){var c=this.dataMin,d=this.dataMax,
-e=this.options;this.allowZoomOutside||(r(c)&&a<=C(c,m(e.min,c))&&(a=t),r(d)&&b>=v(d,m(e.max,d))&&(b=t));this.displayBtn=a!==t||b!==t;this.setExtremes(a,b,!1,t,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=this.horiz,e=m(b.width,a.plotWidth-c+(b.offsetRight||0)),f=m(b.height,a.plotHeight),g=m(b.top,a.plotTop),b=m(b.left,a.plotLeft+c),c=/%$/;c.test(f)&&(f=parseInt(f,10)/100*a.plotHeight);c.test(g)&&(g=parseInt(g,10)/100*a.plotHeight+a.plotTop);
-this.left=b;this.top=g;this.width=e;this.height=f;this.bottom=a.chartHeight-f-g;this.right=a.chartWidth-e-b;this.len=v(d?e:f,0);this.pos=d?b:g},getExtremes:function(){var a=this.isLog;return{min:a?da(ia(this.min)):this.min,max:a?da(ia(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?ia(this.min):this.min,b=b?ia(this.max):this.max;c>a||a===null?a=c:b<a&&(a=b);return this.translate(a,0,1,0,1)},autoLabelAlign:function(a){a=
-(m(a,0)-this.side*90+720)%360;return a>15&&a<165?"right":a>195&&a<345?"left":"center"},getOffset:function(){var a=this,b=a.chart,c=b.renderer,d=a.options,e=a.tickPositions,f=a.ticks,g=a.horiz,h=a.side,i=b.inverted?[1,0,3,2][h]:h,j,k=0,l,o=0,n=d.title,s=d.labels,$=0,J=b.axisOffset,L=b.clipOffset,x=[-1,1,1,-1][h],q,u=1,w=m(s.maxStaggerLines,5),y,z,A,B,na=h===2?c.fontMetrics(s.style.fontSize).b:0;a.hasData=j=a.hasVisibleSeries||r(a.min)&&r(a.max)&&!!e;a.showAxis=b=j||m(d.showEmpty,!0);a.staggerLines=
-a.horiz&&s.staggerLines;if(!a.axisGroup)a.gridGroup=c.g("grid").attr({zIndex:d.gridZIndex||1}).add(),a.axisGroup=c.g("axis").attr({zIndex:d.zIndex||2}).add(),a.labelGroup=c.g("axis-labels").attr({zIndex:s.zIndex||7}).addClass("highcharts-"+a.coll.toLowerCase()+"-labels").add();if(j||a.isLinked){a.labelAlign=m(s.align||a.autoLabelAlign(s.rotation));p(e,function(b){f[b]?f[b].addLabel():f[b]=new Sa(a,b)});if(a.horiz&&!a.staggerLines&&w&&!s.rotation){for(q=a.reversed?[].concat(e).reverse():e;u<w;){j=
-[];y=!1;for(s=0;s<q.length;s++)z=q[s],A=(A=f[z].label&&f[z].label.getBBox())?A.width:0,B=s%u,A&&(z=a.translate(z),j[B]!==t&&z<j[B]&&(y=!0),j[B]=z+A);if(y)u++;else break}if(u>1)a.staggerLines=u}p(e,function(b){if(h===0||h===2||{1:"left",3:"right"}[h]===a.labelAlign)$=v(f[b].getLabelSize(),$)});if(a.staggerLines)$*=a.staggerLines,a.labelOffset=$}else for(q in f)f[q].destroy(),delete f[q];if(n&&n.text&&n.enabled!==!1){if(!a.axisTitle)a.axisTitle=c.text(n.text,0,0,n.useHTML).attr({zIndex:7,rotation:n.rotation||
-0,align:n.textAlign||{low:"left",middle:"center",high:"right"}[n.align]}).addClass("highcharts-"+this.coll.toLowerCase()+"-title").css(n.style).add(a.axisGroup),a.axisTitle.isNew=!0;if(b)k=a.axisTitle.getBBox()[g?"height":"width"],o=m(n.margin,g?5:10),l=n.offset;a.axisTitle[b?"show":"hide"]()}a.offset=x*m(d.offset,J[h]);a.axisTitleMargin=m(l,$+o+($&&x*d.labels[g?"y":"x"]-na));J[h]=v(J[h],a.axisTitleMargin+k+x*a.offset);L[i]=v(L[i],T(d.lineWidth/2)*2)},getLinePath:function(a){var b=this.chart,c=this.opposite,
-d=this.offset,e=this.horiz,f=this.left+(c?this.width:0)+d,d=b.chartHeight-this.bottom-(c?this.height:0)+d;c&&(a*=-1);return b.renderer.crispLine(["M",e?this.left:f,e?d:this.top,"L",e?b.chartWidth-this.right:f,e?d:b.chartHeight-this.bottom],a)},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,f=a?b:c,g=this.opposite,h=this.offset,i=z(e.style.fontSize||12),d={low:f+(a?0:d),middle:f+d/2,high:f+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(g?-1:1)*
-this.axisTitleMargin+(this.side===2?i:0);return{x:a?d:b+(g?this.width:0)+h+(e.x||0),y:a?b-(g?this.height:0)+h:d+(e.y||0)}},render:function(){var a=this,b=a.horiz,c=a.reversed,d=a.chart,e=d.renderer,f=a.options,g=a.isLog,h=a.isLinked,i=a.tickPositions,j,k=a.axisTitle,l=a.ticks,o=a.minorTicks,n=a.alternateBands,s=f.stackLabels,m=f.alternateGridColor,J=a.tickmarkOffset,L=f.lineWidth,x=d.hasRendered&&r(a.oldMin)&&!isNaN(a.oldMin),q=a.hasData,v=a.showAxis,u,w=f.labels.overflow,y=a.justifyLabels=b&&w!==
-!1,z;a.labelEdge.length=0;a.justifyToPlot=w==="justify";p([l,o,n],function(a){for(var b in a)a[b].isActive=!1});if(q||h)if(a.minorTickInterval&&!a.categories&&p(a.getMinorTickPositions(),function(b){o[b]||(o[b]=new Sa(a,b,"minor"));x&&o[b].isNew&&o[b].render(null,!0);o[b].render(null,!1,1)}),i.length&&(j=i.slice(),(b&&c||!b&&!c)&&j.reverse(),y&&(j=j.slice(1).concat([j[0]])),p(j,function(b,c){y&&(c=c===j.length-1?0:c+1);if(!h||b>=a.min&&b<=a.max)l[b]||(l[b]=new Sa(a,b)),x&&l[b].isNew&&l[b].render(c,
-!0,0.1),l[b].render(c,!1,1)}),J&&a.min===0&&(l[-1]||(l[-1]=new Sa(a,-1,null,!0)),l[-1].render(-1))),m&&p(i,function(b,c){if(c%2===0&&b<a.max)n[b]||(n[b]=new R.PlotLineOrBand(a)),u=b+J,z=i[c+1]!==t?i[c+1]+J:a.max,n[b].options={from:g?ia(u):u,to:g?ia(z):z,color:m},n[b].render(),n[b].isActive=!0}),!a._addedPlotLB)p((f.plotLines||[]).concat(f.plotBands||[]),function(b){a.addPlotBandOrLine(b)}),a._addedPlotLB=!0;p([l,o,n],function(a){var b,c,e=[],f=va?va.duration||500:0,g=function(){for(c=e.length;c--;)a[e[c]]&&
-!a[e[c]].isActive&&(a[e[c]].destroy(),delete a[e[c]])};for(b in a)if(!a[b].isActive)a[b].render(b,!1,0),a[b].isActive=!1,e.push(b);a===n||!d.hasRendered||!f?g():f&&setTimeout(g,f)});if(L)b=a.getLinePath(L),a.axisLine?a.axisLine.animate({d:b}):a.axisLine=e.path(b).attr({stroke:f.lineColor,"stroke-width":L,zIndex:7}).add(a.axisGroup),a.axisLine[v?"show":"hide"]();if(k&&v)k[k.isNew?"attr":"animate"](a.getTitlePosition()),k.isNew=!1;s&&s.enabled&&a.renderStackTotals();a.isDirty=!1},redraw:function(){var a=
-this.chart.pointer;a&&a.reset(!0);this.render();p(this.plotLinesAndBands,function(a){a.render()});p(this.series,function(a){a.isDirty=!0})},destroy:function(a){var b=this,c=b.stacks,d,e=b.plotLinesAndBands;a||W(b);for(d in c)Oa(c[d]),c[d]=null;p([b.ticks,b.minorTicks,b.alternateBands],function(a){Oa(a)});for(a=e.length;a--;)e[a].destroy();p("stackTotalGroup,axisLine,axisTitle,axisGroup,cross,gridGroup,labelGroup".split(","),function(a){b[a]&&(b[a]=b[a].destroy())});this.cross&&this.cross.destroy()},
-drawCrosshair:function(a,b){if(this.crosshair)if((r(b)||!m(this.crosshair.snap,!0))===!1)this.hideCrosshair();else{var c,d=this.crosshair,e=d.animation;m(d.snap,!0)?r(b)&&(c=this.chart.inverted!=this.horiz?b.plotX:this.len-b.plotY):c=this.horiz?a.chartX-this.pos:this.len-a.chartY+this.pos;c=this.isRadial?this.getPlotLinePath(this.isXAxis?b.x:m(b.stackY,b.y)):this.getPlotLinePath(null,null,null,null,c);if(c===null)this.hideCrosshair();else if(this.cross)this.cross.attr({visibility:"visible"})[e?"animate":
-"attr"]({d:c},e);else{e={"stroke-width":d.width||1,stroke:d.color||"#C0C0C0",zIndex:d.zIndex||2};if(d.dashStyle)e.dashstyle=d.dashStyle;this.cross=this.chart.renderer.path(c).attr(e).add()}}},hideCrosshair:function(){this.cross&&this.cross.hide()}};q(la.prototype,{getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},addPlotBand:function(a){this.addPlotBandOrLine(a,"plotBands")},addPlotLine:function(a){this.addPlotBandOrLine(a,
-"plotLines")},addPlotBandOrLine:function(a,b){var c=(new R.PlotLineOrBand(this,a)).render(),d=this.userOptions;c&&(b&&(d[b]=d[b]||[],d[b].push(a)),this.plotLinesAndBands.push(c));return c},removePlotBandOrLine:function(a){for(var b=this.plotLinesAndBands,c=this.options,d=this.userOptions,e=b.length;e--;)b[e].id===a&&b[e].destroy();p([c.plotLines||[],d.plotLines||[],c.plotBands||[],d.plotBands||[]],function(b){for(e=b.length;e--;)b[e].id===a&&ja(b,b[e])})}});la.prototype.getTimeTicks=function(a,b,
-c,d){var e=[],f={},g=E.global.useUTC,h,i=new Date(b-Ra),j=a.unitRange,k=a.count;if(r(b)){j>=A.second&&(i.setMilliseconds(0),i.setSeconds(j>=A.minute?0:k*T(i.getSeconds()/k)));if(j>=A.minute)i[Db](j>=A.hour?0:k*T(i[pb]()/k));if(j>=A.hour)i[Eb](j>=A.day?0:k*T(i[qb]()/k));if(j>=A.day)i[sb](j>=A.month?1:k*T(i[Xa]()/k));j>=A.month&&(i[Fb](j>=A.year?0:k*T(i[fb]()/k)),h=i[gb]());j>=A.year&&(h-=h%k,i[Gb](h));if(j===A.week)i[sb](i[Xa]()-i[rb]()+m(d,1));b=1;Ra&&(i=new Date(i.getTime()+Ra));h=i[gb]();for(var d=
-i.getTime(),l=i[fb](),o=i[Xa](),n=g?Ra:(864E5+i.getTimezoneOffset()*6E4)%864E5;d<c;)e.push(d),j===A.year?d=eb(h+b*k,0):j===A.month?d=eb(h,l+b*k):!g&&(j===A.day||j===A.week)?d=eb(h,l,o+b*k*(j===A.day?1:7)):d+=j*k,b++;e.push(d);p(vb(e,function(a){return j<=A.hour&&a%A.day===n}),function(a){f[a]="day"})}e.info=q(a,{higherRanks:f,totalRange:j*k});return e};la.prototype.normalizeTimeTickInterval=function(a,b){var c=b||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",
-[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]],d=c[c.length-1],e=A[d[0]],f=d[1],g;for(g=0;g<c.length;g++)if(d=c[g],e=A[d[0]],f=d[1],c[g+1]&&a<=(e*f[f.length-1]+A[c[g+1][0]])/2)break;e===A.year&&a<5*e&&(f=[1,2,5]);c=nb(a/e,f,d[0]==="year"?v(mb(a/e),1):1);return{unitRange:e,count:c,unitName:d[0]}};la.prototype.getLogTickPositions=function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=u(a),g=this.getLinearTickPositions(a,
-b,c);else if(a>=0.08)for(var f=T(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];f<c+1&&!l;f++){i=e.length;for(h=0;h<i&&!l;h++)j=za(ia(f)*e[h]),j>b&&(!d||k<=c)&&g.push(k),k>c&&(l=!0),k=j}else if(b=ia(b),c=ia(c),a=e[d?"minorTickInterval":"tickInterval"],a=m(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=nb(a,null,mb(a)),g=Ua(this.getLinearTickPositions(a,b,c),za),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=
-a;return g};var Mb=R.Tooltip=function(){this.init.apply(this,arguments)};Mb.prototype={init:function(a,b){var c=b.borderWidth,d=b.style,e=z(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape||"callout",null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).add().attr({y:-9999});fa||this.label.shadow(b.shadow);this.shared=b.shared},
-destroy:function(){if(this.label)this.label=this.label.destroy();clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,c,d){var e=this,f=e.now,g=e.options.animation!==!1&&!e.isHidden,h=e.followPointer||e.len>1;q(f,{x:g?(2*f.x+a)/3:a,y:g?(f.y+b)/2:b,anchorX:h?t:g?(2*f.anchorX+c)/3:c,anchorY:h?t:g?(f.anchorY+d)/2:d});e.label.attr(f);if(g&&(M(a-f.x)>1||M(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=
-this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},m(this.options.hideDelay,500)),b&&p(b,function(a){a.setState()}),this.chart.hoverPoints=null},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=d.plotTop,g=0,h=0,i,a=qa(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===t&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(p(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?
-(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Ua(c,u)},getPosition:function(a,b,c){var d=this.chart,e=this.distance,f={},g,h=["y",d.chartHeight,b,c.plotY+d.plotTop],i=["x",d.chartWidth,a,c.plotX+d.plotLeft],j=c.ttBelow||d.inverted&&!c.negative||!d.inverted&&c.negative,k=function(a,b,c,d){var g=c<d-e,b=d+e+c<b,c=d-e-c;d+=e;if(j&&b)f[a]=d;else if(!j&&g)f[a]=c;else if(g)f[a]=c;else if(b)f[a]=
-d;else return!1},l=function(a,b,c,d){if(d<e||d>b-e)return!1;else f[a]=d<c/2?1:d>b-c/2?b-c-2:d-c/2},o=function(a){var b=h;h=i;i=b;g=a},n=function(){k.apply(0,h)!==!1?l.apply(0,i)===!1&&!g&&(o(!0),n()):g?f.x=f.y=0:(o(!0),n())};(d.inverted||this.len>1)&&o();n();return f},defaultFormatter:function(a){var b=this.points||qa(this),c=b[0].series,d;d=[a.tooltipHeaderFormatter(b[0])];p(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});
-d.push(a.options.footerFormat||"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h={},i,j=[];i=e.formatter||this.defaultFormatter;var h=c.hoverPoints,k,l=this.shared;clearTimeout(this.hideTimer);this.followPointer=qa(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];l&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,h&&p(h,function(a){a.setState()}),p(a,function(a){a.setState("hover");j.push(a.getLabelConfig())}),h={x:a[0].category,
-y:a[0].y},h.points=j,this.len=j.length,a=a[0]):h=a.getLabelConfig();i=i.call(h,this);h=a.series;this.distance=m(h.tooltipOptions.distance,16);i===!1?this.hide():(this.isHidden&&(bb(d),d.attr("opacity",1).show()),d.attr({text:i}),k=e.borderColor||a.color||h.color||"#606060",d.attr({stroke:k}),this.updatePosition({plotX:f,plotY:g,negative:a.negative,ttBelow:a.ttBelow}),this.isHidden=!1);D(c,"tooltipRefresh",{text:i,x:f+c.plotLeft,y:g+c.plotTop,borderColor:k})},updatePosition:function(a){var b=this.chart,
-c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(u(c.x),u(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)},tooltipHeaderFormatter:function(a){var b=a.series,c=b.tooltipOptions,d=c.dateTimeLabelFormats,e=c.xDateFormat,f=b.xAxis,g=f&&f.options.type==="datetime"&&ha(a.key),c=c.headerFormat,f=f&&f.closestPointRange,h;if(g&&!e){if(f)for(h in A){if(A[h]>=f||A[h]<=A.day&&a.key%A[h]>0){e=d[h];break}}else e=d.day;e=e||d.year}g&&e&&(c=c.replace("{point.key}","{point.key:"+
-e+"}"));return Ia(c,{point:a,series:b})}};var oa;$a=y.documentElement.ontouchstart!==t;var Wa=R.Pointer=function(a,b){this.init(a,b)};Wa.prototype={init:function(a,b){var c=b.chart,d=c.events,e=fa?"":c.zoomType,c=a.inverted,f;this.options=b;this.chart=a;this.zoomX=f=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=f&&!c||e&&c;this.zoomVert=e&&!c||f&&c;this.hasZoom=f||e;this.runChartClick=d&&!!d.click;this.pinchDown=[];this.lastValidTouch={};if(R.Tooltip&&b.tooltip.enabled)a.tooltip=new Mb(a,b.tooltip),
-this.followTouchMove=b.tooltip.followTouchMove;this.setDOMEvents()},normalize:function(a,b){var c,d,a=a||window.event,a=Sb(a);if(!a.target)a.target=a.srcElement;d=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;if(!b)this.chartPosition=b=Rb(this.chart.container);d.pageX===t?(c=v(a.x,a.clientX-b.left),d=a.y):(c=d.pageX-b.left,d=d.pageY-b.top);return q(a,{chartX:u(c),chartY:u(d)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};p(this.chart.axes,function(c){b[c.isXAxis?"xAxis":
-"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f,g=b.hoverPoint,h=b.hoverSeries,i,j,k=b.chartWidth,l=this.getIndex(a);if(d&&this.options.tooltip.shared&&(!h||!h.noSharedTooltip)){f=[];i=c.length;for(j=0;j<i;j++)if(c[j].visible&&c[j].options.enableMouseTracking!==!1&&!c[j].noSharedTooltip&&
-c[j].singularTooltips!==!0&&c[j].tooltipPoints.length&&(e=c[j].tooltipPoints[l])&&e.series)e._dist=M(l-e.clientX),k=C(k,e._dist),f.push(e);for(i=f.length;i--;)f[i]._dist>k&&f.splice(i,1);if(f.length&&f[0].clientX!==this.hoverX)d.refresh(f,a),this.hoverX=f[0].clientX}c=h&&h.tooltipOptions.followPointer;if(h&&h.tracker&&!c){if((e=h.tooltipPoints[l])&&e!==g)e.onMouseOver(a)}else d&&c&&!d.isHidden&&(h=d.getAnchor([{}],a),d.updatePosition({plotX:h[0],plotY:h[1]}));if(d&&!this._onDocumentMouseMove)this._onDocumentMouseMove=
-function(a){if(V[oa])V[oa].pointer.onDocumentMouseMove(a)},K(y,"mousemove",this._onDocumentMouseMove);p(b.axes,function(b){b.drawCrosshair(a,m(e,g))})},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,f=e&&e.shared?b.hoverPoints:d;(a=a&&e&&f)&&qa(f)[0].plotX===t&&(a=!1);if(a)e.refresh(f),d&&d.setState(d.state,!0);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&e.hide();if(this._onDocumentMouseMove)W(y,"mousemove",this._onDocumentMouseMove),this._onDocumentMouseMove=null;
-p(b.axes,function(a){a.hideCrosshair()});this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart,d;p(c.series,function(e){d=a||e.getPlotBox();e.xAxis&&e.xAxis.zoomEnabled&&(e.group.attr(d),e.markerGroup&&(e.markerGroup.attr(d),e.markerGroup.clip(b?c.clipRect:null)),e.dataLabelsGroup&&e.dataLabelsGroup.attr(d))});c.clipRect.attr(b||c.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},
-drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,f=this.zoomHor,g=this.zoomVert,h=b.plotLeft,i=b.plotTop,j=b.plotWidth,k=b.plotHeight,l,o=this.mouseDownX,n=this.mouseDownY;d<h?d=h:d>h+j&&(d=h+j);e<i?e=i:e>i+k&&(e=i+k);this.hasDragged=Math.sqrt(Math.pow(o-d,2)+Math.pow(n-e,2));if(this.hasDragged>10){l=b.isInsidePlot(o-h,n-i);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(h,i,f?1:j,g?1:k,0).attr({fill:c.selectionMarkerFill||
-"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&f&&(d-=o,this.selectionMarker.attr({width:M(d),x:(d>0?0:d)+o}));this.selectionMarker&&g&&(d=e-n,this.selectionMarker.attr({height:M(d),y:(d>0?0:d)+n}));l&&!this.selectionMarker&&c.panning&&b.pan(a,c.panning)}},drop:function(a){var b=this.chart,c=this.hasPinched;if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},a=this.selectionMarker,e=a.attr?a.attr("x"):a.x,f=a.attr?a.attr("y"):a.y,g=a.attr?a.attr("width"):
-a.width,h=a.attr?a.attr("height"):a.height,i;if(this.hasDragged||c)p(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?e:f),b=a.toValue(b?e+g:f+h);!isNaN(c)&&!isNaN(b)&&(d[a.coll].push({axis:a,min:C(c,b),max:v(c,b)}),i=!0)}}),i&&D(b,"selection",d,function(a){b.zoom(q(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups()}if(b)G(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=
-!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){V[oa]&&V[oa].pointer.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=this.normalize(a,c);c&&d&&!this.inClass(a.target,"highcharts-tracker")&&!b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&this.reset()},onContainerMouseLeave:function(){var a=V[oa];if(a)a.pointer.reset(),a.pointer.chartPosition=
-null},onContainerMouseMove:function(a){var b=this.chart;oa=b.index;a=this.normalize(a);b.mouseIsDown==="mousedown"&&this.drag(a);(this.inClass(a.target,"highcharts-tracker")||b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop))&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=H(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries,c=(a=a.relatedTarget||
-a.toElement)&&a.point&&a.point.series;if(b&&!b.options.stickyTracking&&!this.inClass(a,"highcharts-tooltip")&&c!==b)b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,a=this.normalize(a);a.cancelBubble=!0;b.cancelClick||(c&&this.inClass(a.target,"highcharts-tracker")?(D(c.series,"click",q(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(q(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&D(b,"click",a)))},setDOMEvents:function(){var a=
-this,b=a.chart.container;b.onmousedown=function(b){a.onContainerMouseDown(b)};b.onmousemove=function(b){a.onContainerMouseMove(b)};b.onclick=function(b){a.onContainerClick(b)};K(b,"mouseleave",a.onContainerMouseLeave);ab===1&&K(y,"mouseup",a.onDocumentMouseUp);if($a)b.ontouchstart=function(b){a.onContainerTouchStart(b)},b.ontouchmove=function(b){a.onContainerTouchMove(b)},ab===1&&K(y,"touchend",a.onDocumentTouchEnd)},destroy:function(){var a;W(this.chart.container,"mouseleave",this.onContainerMouseLeave);
-ab||(W(y,"mouseup",this.onDocumentMouseUp),W(y,"touchend",this.onDocumentTouchEnd));clearInterval(this.tooltipTimeout);for(a in this)this[a]=null}};q(R.Pointer.prototype,{pinchTranslate:function(a,b,c,d,e,f){(this.zoomHor||this.pinchHor)&&this.pinchTranslateDirection(!0,a,b,c,d,e,f);(this.zoomVert||this.pinchVert)&&this.pinchTranslateDirection(!1,a,b,c,d,e,f)},pinchTranslateDirection:function(a,b,c,d,e,f,g,h){var i=this.chart,j=a?"x":"y",k=a?"X":"Y",l="chart"+k,o=a?"width":"height",n=i["plot"+(a?
-"Left":"Top")],s,m,p=h||1,q=i.inverted,x=i.bounds[a?"h":"v"],r=b.length===1,v=b[0][l],u=c[0][l],t=!r&&b[1][l],w=!r&&c[1][l],y,c=function(){!r&&M(v-t)>20&&(p=h||M(u-w)/M(v-t));m=(n-u)/p+v;s=i["plot"+(a?"Width":"Height")]/p};c();b=m;b<x.min?(b=x.min,y=!0):b+s>x.max&&(b=x.max-s,y=!0);y?(u-=0.8*(u-g[j][0]),r||(w-=0.8*(w-g[j][1])),c()):g[j]=[u,w];q||(f[j]=m-n,f[o]=s);f=q?1/p:p;e[o]=s;e[j]=b;d[q?a?"scaleY":"scaleX":"scale"+k]=p;d["translate"+k]=f*n+(u-f*v)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,
-e=b.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.hasZoom,j=b.selectionMarker,k={},l=g===1&&(b.inClass(a.target,"highcharts-tracker")&&c.runTrackerClick||c.runChartClick),o={};(i||e)&&!l&&a.preventDefault();Ua(f,function(a){return b.normalize(a)});if(a.type==="touchstart")p(f,function(a,b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],p(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,
-e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=C(e,f),e=v(e,f);b.min=C(a.pos,g-d);b.max=v(a.pos+a.len,e+d)}});else if(d.length){if(!j)b.selectionMarker=j=q({destroy:sa},c.plotBox);b.pinchTranslate(d,f,k,j,o,h);b.hasPinched=i;b.scaleGroups(k,o);!i&&e&&g===1&&this.runPointActions(b.normalize(a))}},onContainerTouchStart:function(a){var b=this.chart;oa=b.index;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)?(this.runPointActions(a),this.pinch(a)):this.reset()):
-a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){V[oa]&&V[oa].pointer.drop(a)}});if(I.PointerEvent||I.MSPointerEvent){var ua={},zb=!!I.PointerEvent,Wb=function(){var a,b=[];b.item=function(a){return this[a]};for(a in ua)ua.hasOwnProperty(a)&&b.push({pageX:ua[a].pageX,pageY:ua[a].pageY,target:ua[a].target});return b},Ab=function(a,b,c,d){a=a.originalEvent||a;if((a.pointerType==="touch"||
-a.pointerType===a.MSPOINTER_TYPE_TOUCH)&&V[oa])d(a),d=V[oa].pointer,d[b]({type:c,target:a.currentTarget,preventDefault:sa,touches:Wb()})};q(Wa.prototype,{onContainerPointerDown:function(a){Ab(a,"onContainerTouchStart","touchstart",function(a){ua[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}})},onContainerPointerMove:function(a){Ab(a,"onContainerTouchMove","touchmove",function(a){ua[a.pointerId]={pageX:a.pageX,pageY:a.pageY};if(!ua[a.pointerId].target)ua[a.pointerId].target=a.currentTarget})},
-onDocumentPointerUp:function(a){Ab(a,"onContainerTouchEnd","touchend",function(a){delete ua[a.pointerId]})},batchMSEvents:function(a){a(this.chart.container,zb?"pointerdown":"MSPointerDown",this.onContainerPointerDown);a(this.chart.container,zb?"pointermove":"MSPointerMove",this.onContainerPointerMove);a(y,zb?"pointerup":"MSPointerUp",this.onDocumentPointerUp)}});Ma(Wa.prototype,"init",function(a,b,c){a.call(this,b,c);(this.hasZoom||this.followTouchMove)&&G(b.container,{"-ms-touch-action":Q,"touch-action":Q})});
-Ma(Wa.prototype,"setDOMEvents",function(a){a.apply(this);(this.hasZoom||this.followTouchMove)&&this.batchMSEvents(K)});Ma(Wa.prototype,"destroy",function(a){this.batchMSEvents(W);a.call(this)})}var lb=R.Legend=function(a,b){this.init(a,b)};lb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=m(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=z(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=w(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=
-e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight=0,c.lastLineHeight=0,c.symbolWidth=m(b.symbolWidth,16),c.pages=[],c.render(),K(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.legendColor||a.color||"#CCC":g,g=a.options&&a.options.marker,i={fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g&&f.isMarker)for(j in i.stroke=h,g=a.convertAttribs(g),
-g)d=g[j],d!==t&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;p(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&Pa(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},
-positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY,p(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,G(f,{left:b.translateX+e.checkboxOffset+f.x-20+"px",top:g+"px",display:g>c-6&&g<c+d-6?"":Q}))})},renderTitle:function(){var a=this.padding,b=this.options.title,c=0;if(b.text){if(!this.title)this.title=this.chart.renderer.label(b.text,a-3,a-4,null,null,null,null,null,"legend-title").attr({zIndex:1}).css(b.style).add(this.group);
-a=this.title.getBBox();c=a.height;this.offsetWidth=a.width;this.contentGroup.attr({translateY:c})}this.titleHeight=c},renderItem:function(a){var b=this.chart,c=b.renderer,d=this.options,e=d.layout==="horizontal",f=this.symbolWidth,g=d.symbolPadding,h=this.itemStyle,i=this.itemHiddenStyle,j=this.padding,k=e?m(d.itemDistance,20):0,l=!d.rtl,o=d.width,n=d.itemMarginBottom||0,s=this.itemMarginTop,p=this.initialItemX,q=a.legendItem,r=a.series&&a.series.drawLegendSymbol?a.series:a,x=r.options,x=this.createCheckboxForItem&&
-x&&x.showCheckbox,t=d.useHTML;if(!q)a.legendGroup=c.g("legend-item").attr({zIndex:1}).add(this.scrollGroup),r.drawLegendSymbol(this,a),a.legendItem=q=c.text(d.labelFormat?Ia(d.labelFormat,a):d.labelFormatter.call(a),l?f+g:-g,this.baseline,t).css(w(a.visible?h:i)).attr({align:l?"left":"right",zIndex:2}).add(a.legendGroup),this.setItemEvents&&this.setItemEvents(a,q,t,h,i),this.colorizeItem(a,a.visible),x&&this.createCheckboxForItem(a);c=q.getBBox();f=a.checkboxOffset=d.itemWidth||a.legendItemWidth||
-f+g+c.width+k+(x?20:0);this.itemHeight=g=u(a.legendItemHeight||c.height);if(e&&this.itemX-p+f>(o||b.chartWidth-2*j-p-d.x))this.itemX=p,this.itemY+=s+this.lastLineHeight+n,this.lastLineHeight=0;this.maxItemWidth=v(this.maxItemWidth,f);this.lastItemY=s+this.itemY+n;this.lastLineHeight=v(g,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];e?this.itemX+=f:(this.itemY+=s+g+n,this.lastLineHeight=g);this.offsetWidth=o||v((e?this.itemX-p-k:f)+j,this.offsetWidth)},getAllItems:function(){var a=
-[];p(this.chart.series,function(b){var c=b.options;if(m(c.showInLegend,!r(c.linkedTo)?t:!1,!0))a=a.concat(b.legendItems||(c.legendType==="point"?b.data:b))});return a},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,l=j.borderWidth,o=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);
-a.renderTitle();e=a.getAllItems();ob(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;p(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(l||o){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp({width:g,height:h})),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,
-"stroke-width":l||0,fill:o||Q}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;p(e,function(b){a.positionItem(b)});f&&d.align(q({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h,i=this.clipRect,j=e.navigation,k=m(j.animation,!0),l=j.arrowSize||12,o=this.nav,n=this.pages,
-s,q=this.allItems;e.layout==="horizontal"&&(f/=2);g&&(f=C(f,g));n.length=0;if(a>f&&!e.useHTML){this.clipHeight=h=f-20-this.titleHeight-this.padding;this.currentPage=m(this.currentPage,1);this.fullHeight=a;p(q,function(a,b){var c=a._legendItemPos[1],d=u(a.legendItem.getBBox().height),e=n.length;if(!e||c-n[e-1]>h&&(s||c)!==n[e-1])n.push(s||c),e++;b===q.length-1&&c+d-n[e-1]>h&&n.push(c);c!==s&&(s=c)});if(!i)i=b.clipRect=d.clipRect(0,this.padding,9999,0),b.contentGroup.clip(i);i.attr({height:h});if(!o)this.nav=
-o=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,l,l).on("click",function(){b.scroll(-1,k)}).add(o),this.pager=d.text("",15,10).css(j.style).add(o),this.down=d.symbol("triangle-down",0,0,l,l).on("click",function(){b.scroll(1,k)}).add(o);b.scroll(0);a=f}else if(o)i.attr({height:c.chartHeight}),o.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pages,d=c.length,e=this.currentPage+a,f=this.clipHeight,g=this.options.navigation,
-h=g.activeColor,g=g.inactiveColor,i=this.pager,j=this.padding;e>d&&(e=d);if(e>0)b!==t&&Qa(b,this.chart),this.nav.attr({translateX:j,translateY:f+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:e===1?g:h}).css({cursor:e===1?"default":"pointer"}),i.attr({text:e+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,fill:e===d?g:h}).css({cursor:e===d?"default":"pointer"}),c=-c[e-1]+this.initialItemY,this.scrollGroup.animate({translateY:c}),this.currentPage=e,this.positionCheckboxes(c)}};
-N=R.LegendSymbolMixin={drawRectangle:function(a,b){var c=a.options.symbolHeight||12;b.legendSymbol=this.chart.renderer.rect(0,a.baseline-5-c/2,a.symbolWidth,c,a.options.symbolRadius||0).attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b=this.options,c=b.marker,d;d=a.symbolWidth;var e=this.chart.renderer,f=this.legendGroup,a=a.baseline-u(e.fontMetrics(a.options.itemStyle.fontSize).b*0.3),g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=
-e.path(["M",0,a,"L",d,a]).attr(g).add(f)}if(c&&c.enabled!==!1)b=c.radius,this.legendSymbol=d=e.symbol(this.symbol,d/2-b,a-b,2*b,2*b).add(f),d.isMarker=!0}};(/Trident\/7\.0/.test(wa)||Ta)&&Ma(lb.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c,b)};d();setTimeout(d)});Ya.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=w(E,a);c.series=a.series=d;this.userOptions=a;d=c.chart;this.margin=this.splashArray("margin",d);this.spacing=this.splashArray("spacing",
-d);var e=d.events;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=V.length;V.push(f);ab++;d.reflow!==!1&&K(f,"load",function(){f.initReflow()});if(e)for(g in e)K(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=fa?!1:m(d.animation,!0);f.pointCount=0;f.counters=new Bb;f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=F[a.type||b.type||b.defaultSeriesType])||ra(17,!0);b=new b;b.init(this,
-a);return b},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&p(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h,i=this.isDirtyBox,j=c.length,k=j,l=this.renderer,o=l.isHidden(),n=[];Qa(a,this);o&&this.cloneRenderTo();for(this.layOutTitles();k--;)if(a=c[k],a.options.stacking&&
-(g=!0,a.isDirty)){h=!0;break}if(h)for(k=j;k--;)if(a=c[k],a.options.stacking)a.isDirty=!0;p(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;g&&this.getStacks();if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,p(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();p(b,function(a){a.isDirty&&(i=!0)});p(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,n.push(function(){D(a,"afterSetExtremes",
-q(a.eventArgs,a.getExtremes()));delete a.eventArgs});(i||g)&&a.redraw()})}i&&this.drawChartBox();p(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset(!0);l.draw();D(this,"redraw");o&&this.cloneRenderTo(!0);p(n,function(a){a.call()})},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;d<b.length;d++)if(b[d].options.id===a)return b[d];for(d=0;d<c.length;d++)if(c[d].options.id===a)return c[d];for(d=0;d<c.length;d++){e=c[d].points||[];for(b=0;b<e.length;b++)if(e[b].id===
-a)return e[b]}return null},getAxes:function(){var a=this,b=this.options,c=b.xAxis=qa(b.xAxis||{}),b=b.yAxis=qa(b.yAxis||{});p(c,function(a,b){a.index=b;a.isX=!0});p(b,function(a,b){a.index=b});c=c.concat(b);p(c,function(b){new la(a,b)});a.adjustTickAmounts()},getSelectedPoints:function(){var a=[];p(this.series,function(b){a=a.concat(vb(b.points||[],function(a){return a.selected}))});return a},getSelectedSeries:function(){return vb(this.series,function(a){return a.selected})},getStacks:function(){var a=
-this;p(a.yAxis,function(a){if(a.stacks&&a.hasVisibleSeries)a.oldStacks=a.stacks});p(a.series,function(b){if(b.options.stacking&&(b.visible===!0||a.options.chart.ignoreHiddenSeries===!1))b.stackKey=b.type+m(b.options.stack,"")})},setTitle:function(a,b,c){var g;var d=this,e=d.options,f;f=e.title=w(e.title,a);g=e.subtitle=w(e.subtitle,b),e=g;p([["title",a,f],["subtitle",b,e]],function(a){var b=a[0],c=d[b],e=a[1],a=a[2];c&&e&&(d[b]=c=c.destroy());a&&a.text&&!c&&(d[b]=d.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,
-"class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add())});d.layOutTitles(c)},layOutTitles:function(a){var b=0,c=this.title,d=this.subtitle,e=this.options,f=e.title,e=e.subtitle,g=this.spacingBox.width-44;if(c&&(c.css({width:(f.width||g)+"px"}).align(q({y:15},f),!1,"spacingBox"),!f.floating&&!f.verticalAlign))b=c.getBBox().height;d&&(d.css({width:(e.width||g)+"px"}).align(q({y:b+f.margin},e),!1,"spacingBox"),!e.floating&&!e.verticalAlign&&(b=Ka(b+d.getBBox().height)));c=this.titleOffset!==
-b;this.titleOffset=b;if(!this.isDirtyBox&&c)this.isDirtyBox=c,this.hasRendered&&m(a,!0)&&this.isDirtyBox&&this.redraw()},getChartSize:function(){var a=this.options.chart,b=a.width,a=a.height,c=this.renderToClone||this.renderTo;if(!r(b))this.containerWidth=jb(c,"width");if(!r(a))this.containerHeight=jb(c,"height");this.chartWidth=v(0,b||this.containerWidth||600);this.chartHeight=v(0,m(a,this.containerHeight>19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;
-a?b&&(this.renderTo.appendChild(c),Pa(b),delete this.renderToClone):(c&&c.parentNode===this.renderTo&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),G(b,{position:"absolute",top:"-9999px",display:"block"}),b.style.setProperty&&b.style.setProperty("display","block","important"),y.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+tb++;if(Fa(a))this.renderTo=a=y.getElementById(a);
-a||ra(13,!0);c=z(H(a,"data-highcharts-chart"));!isNaN(c)&&V[c]&&V[c].hasRendered&&V[c].destroy();H(a,"data-highcharts-chart",this.index);a.innerHTML="";!b.skipClone&&!a.offsetWidth&&this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=Y(Ja,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},q({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},
-b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new ta(a,c,d,b.style,!0):new Za(a,c,d,b.style);fa&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.spacing,b,c=this.legend,d=this.margin,e=this.options.legend,f=m(e.margin,20),g=e.x,h=e.y,i=e.align,j=e.verticalAlign,k=this.titleOffset;this.resetMargins();b=this.axisOffset;if(k&&!r(d[0]))this.plotTop=v(this.plotTop,k+this.options.title.margin+a[0]);if(c.display&&!e.floating)if(i==="right"){if(!r(d[1]))this.marginRight=
-v(this.marginRight,c.legendWidth-g+f+a[1])}else if(i==="left"){if(!r(d[3]))this.plotLeft=v(this.plotLeft,c.legendWidth+g+f+a[3])}else if(j==="top"){if(!r(d[0]))this.plotTop=v(this.plotTop,c.legendHeight+h+f+a[0])}else if(j==="bottom"&&!r(d[2]))this.marginBottom=v(this.marginBottom,c.legendHeight-h+f+a[2]);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&p(this.axes,function(a){a.getOffset()});r(d[3])||
-(this.plotLeft+=b[3]);r(d[0])||(this.plotTop+=b[0]);r(d[2])||(this.marginBottom+=b[2]);r(d[1])||(this.marginRight+=b[1]);this.setChartSize()},reflow:function(a){var b=this,c=b.options.chart,d=b.renderTo,e=c.width||jb(d,"width"),f=c.height||jb(d,"height"),c=a?a.target:I,d=function(){if(b.container)b.setSize(e,f,!1),b.hasUserSize=null};if(!b.hasUserSize&&e&&f&&(c===I||c===y)){if(e!==b.containerWidth||f!==b.containerHeight)clearTimeout(b.reflowTimeout),a?b.reflowTimeout=setTimeout(d,100):d();b.containerWidth=
-e;b.containerHeight=f}},initReflow:function(){var a=this,b=function(b){a.reflow(b)};K(I,"resize",b);K(a,"destroy",function(){W(I,"resize",b)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&D(d,"endResize",null,function(){d.isResizing-=1})};Qa(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(r(a))d.chartWidth=e=v(0,u(a)),d.hasUserSize=!!e;if(r(b))d.chartHeight=f=v(0,u(b));(va?kb:G)(d.container,{width:e+"px",height:f+"px"},va);d.setChartSize(!0);d.renderer.setSize(e,
-f,c);d.maxTicks=null;p(d.axes,function(a){a.isDirty=!0;a.setScale()});p(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.layOutTitles();d.getMargins();d.redraw(c);d.oldChartHeight=null;D(d,"resize");va===!1?g():setTimeout(g,va&&va.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=this.spacing,h=this.clipOffset,i,j,k,l;this.plotLeft=i=u(this.plotLeft);this.plotTop=j=u(this.plotTop);this.plotWidth=
-k=v(0,u(d-i-this.marginRight));this.plotHeight=l=v(0,u(e-j-this.marginBottom));this.plotSizeX=b?l:k;this.plotSizeY=b?k:l;this.plotBorderWidth=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:g[3],y:g[0],width:d-g[3]-g[1],height:e-g[0]-g[2]};this.plotBox=c.plotBox={x:i,y:j,width:k,height:l};d=2*T(this.plotBorderWidth/2);b=Ka(v(d,h[3])/2);c=Ka(v(d,h[0])/2);this.clipBox={x:b,y:c,width:T(this.plotSizeX-v(d,h[1])/2-b),height:T(this.plotSizeY-v(d,h[2])/2-c)};a||p(this.axes,function(a){a.setAxisSize();
-a.setAxisTranslation()})},resetMargins:function(){var a=this.spacing,b=this.margin;this.plotTop=m(b[0],a[0]);this.marginRight=m(b[1],a[1]);this.marginBottom=m(b[2],a[2]);this.plotLeft=m(b[3],a[3]);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,
-o=a.plotBorderWidth||0,n,s=this.plotLeft,m=this.plotTop,p=this.plotWidth,q=this.plotHeight,r=this.plotBox,v=this.clipRect,u=this.clipBox;n=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp({width:c-n,height:d-n}));else{e={fill:j||Q};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(n/2,n/2,c-n,d-n,a.borderRadius,i).attr(e).addClass("highcharts-background").add().shadow(a.shadow)}if(k)f?f.animate(r):this.plotBackground=b.rect(s,m,p,q,0).attr({fill:k}).add().shadow(a.plotShadow);
-if(l)h?h.animate(r):this.plotBGImage=b.image(l,s,m,p,q).add();v?v.animate({width:u.width,height:u.height}):this.clipRect=b.clipRect(u);if(o)g?g.animate(g.crisp({x:s,y:m,width:p,height:q})):this.plotBorder=b.rect(s,m,p,q,0,-o).attr({stroke:a.plotBorderColor,"stroke-width":o,fill:Q,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;p(["inverted","angular","polar"],function(g){c=F[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];
-for(e=d&&d.length;!f&&e--;)(c=F[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;p(b,function(a){a.linkedSeries.length=0});p(b,function(b){var d=b.options.linkedTo;if(Fa(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),b.linkedParent=d})},renderSeries:function(){p(this.series,function(a){a.translate();a.setTooltipPoints&&a.setTooltipPoints();a.render()})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=
-d.credits,g;a.setTitle();a.legend=new lb(a,d.legend);a.getStacks();p(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;p(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&p(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();a.renderSeries();e.items&&p(e.items,function(b){var d=q(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;
-c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;D(a,"destroy");V[a.index]=t;ab--;a.renderTo.removeAttribute("data-highcharts-chart");W(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();
-p("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",W(d),f&&Pa(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!aa&&I==I.top&&y.readyState!=="complete"||fa&&!I.canvg?(fa?Lb.push(function(){a.firstRender()},a.options.global.canvasToolsURL):y.attachEvent("onreadystatechange",
-function(){y.detachEvent("onreadystatechange",a.firstRender);y.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender()){a.getContainer();D(a,"init");a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();p(b.series||[],function(b){a.initSeries(b)});a.linkSeries();D(a,"beforeRender");if(R.Pointer)a.pointer=new Wa(a,b);a.render();a.renderer.draw();c&&c.apply(a,[a]);p(a.callbacks,function(b){b.apply(a,[a])});a.cloneRenderTo(!0);
-D(a,"load")}},splashArray:function(a,b){var c=b[a],c=ca(c)?c:[c,c,c,c];return[m(b[a+"Top"],c[0]),m(b[a+"Right"],c[1]),m(b[a+"Bottom"],c[2]),m(b[a+"Left"],c[3])]}};Ya.prototype.callbacks=[];X=R.CenteredSeriesMixin={getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-2*c,b=a.center,a=[m(b[0],"50%"),m(b[1],"50%"),a.size||"100%",a.innerSize||0],g=C(e,f),h;return Ua(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*z(a)/100:
-a)+(d?c:0)})}};var Ea=function(){};Ea.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++],a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ea.prototype.optionsToObject.call(this,a);q(this,a);this.options=this.options?q(this.options,a):a;if(d)this.y=this[d];if(this.x===
-t&&c)this.x=b===t?c.autoIncrement():b;return this},optionsToObject:function(a){var b={},c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b[d[0]]=a;else if(La(a)){if(a.length>e){c=typeof a[0];if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;g<e;)b[d[g++]]=a[f++]}else if(typeof a==="object"){b=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}return b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;
-if(b&&(this.setState(),ja(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)W(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,dataLabel,dataLabelUpper,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,
-point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=m(c.valueDecimals,""),e=c.valuePrefix||"",f=c.valueSuffix||"";p(b.pointArrayMap||["y"],function(b){b="{point."+b;if(e||f)a=a.replace(b+"}",e+b+"}"+f);a=a.replace(b+"}",b+":,."+d+"f}")});return Ia(a,{point:this,series:this.series})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&
-this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});D(this,a,b,c)}};var O=function(){};O.prototype={isCartesian:!0,type:"line",pointClass:Ea,sorted:!0,requireSorting:!0,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],init:function(a,b){var c=this,d,e,f=a.series,g=function(a,b){return m(a.options.index,a._i)-m(b.options.index,
-b._i)};c.chart=a;c.options=b=c.setOptions(b);c.linkedSeries=[];c.bindAxes();q(c,{name:b.name,state:"",pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});if(fa)b.animation=!1;e=b.events;for(d in e)K(c,d,e[d]);if(e&&e.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;c.getColor();c.getSymbol();p(c.parallelArrays,function(a){c[a+"Data"]=[]});c.setData(b.data,!1);if(c.isCartesian)a.hasCartesianSeries=!0;f.push(c);c._i=f.length-1;ob(f,g);this.yAxis&&
-ob(this.yAxis.series,g);p(f,function(a,b){a.index=b;a.name=a.name||"Series "+(b+1)})},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;p(a.axisTypes||[],function(e){p(c[e],function(c){d=c.options;if(b[e]===d.index||b[e]!==t&&b[e]===d.id||b[e]===t&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0});!a[e]&&a.optionalAxis!==e&&ra(18,!0)})},updateParallelArrays:function(a,b){var c=a.series,d=arguments;p(c.parallelArrays,typeof b==="number"?function(d){var f=d==="y"&&c.toYData?c.toYData(a):a[d];
-c[d+"Data"][b]=f}:function(a){Array.prototype[b].apply(c[a+"Data"],Array.prototype.slice.call(d,2))})},autoIncrement:function(){var a=this.options,b=this.xIncrement,b=m(b,a.pointStart,0);this.pointInterval=m(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);d.length&&(b=[d])}else p(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+
-1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart,c=b.options.plotOptions,b=b.userOptions||{},d=b.plotOptions||{},e=c[this.type];this.userOptions=a;c=w(e,c.series,a);this.tooltipOptions=w(E.tooltip,E.plotOptions[this.type].tooltip,b.tooltip,d.series&&d.series.tooltip,d[this.type]&&d[this.type].tooltip,a.tooltip);e.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,
-e;e=a.color||ba[this.type].color;if(!e&&!a.colorByPoint)r(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols,c=c.counters;this.symbol=b.symbol;if(!this.symbol)r(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:N.drawLineMarker,
-setData:function(a,b,c,d){var e=this,f=e.points,g=f&&f.length||0,h,i=e.options,j=e.chart,k=null,l=e.xAxis,o=l&&!!l.categories,n=e.tooltipPoints,s=i.turboThreshold,q=this.xData,r=this.yData,v=(h=e.pointArrayMap)&&h.length,a=a||[];h=a.length;b=m(b,!0);if(d!==!1&&h&&g===h&&!e.cropped&&!e.hasGroupedData)p(a,function(a,b){f[b].update(a,!1)});else{e.xIncrement=null;e.pointRange=o?1:i.pointRange;e.colorCounter=0;p(this.parallelArrays,function(a){e[a+"Data"].length=0});if(s&&h>s){for(c=0;k===null&&c<h;)k=
-a[c],c++;if(ha(k)){o=m(i.pointStart,0);i=m(i.pointInterval,1);for(c=0;c<h;c++)q[c]=o,r[c]=a[c],o+=i;e.xIncrement=o}else if(La(k))if(v)for(c=0;c<h;c++)i=a[c],q[c]=i[0],r[c]=i.slice(1,v+1);else for(c=0;c<h;c++)i=a[c],q[c]=i[0],r[c]=i[1];else ra(12)}else for(c=0;c<h;c++)if(a[c]!==t&&(i={series:e},e.pointClass.prototype.applyOptions.apply(i,[a[c]]),e.updateParallelArrays(i,c),o&&i.name))l.names[i.x]=i.name;Fa(r[0])&&ra(14,!0);e.data=[];e.options.data=a;for(c=g;c--;)f[c]&&f[c].destroy&&f[c].destroy();
-if(n)n.length=0;if(l)l.minRange=l.userMinRange;e.isDirty=e.isDirtyData=j.isDirtyBox=!0;c=!1}b&&j.redraw(c)},processData:function(a){var b=this.xData,c=this.yData,d=b.length,e;e=0;var f,g,h=this.xAxis,i=this.options,j=i.cropThreshold,k=0,l=this.isCartesian,o,n;if(l&&!this.isDirty&&!h.isDirty&&!this.yAxis.isDirty&&!a)return!1;if(l&&this.sorted&&(!j||d>j||this.forceCrop))if(o=h.min,n=h.max,b[d-1]<o||b[0]>n)b=[],c=[];else if(b[0]<o||b[d-1]>n)e=this.cropData(this.xData,this.yData,o,n),b=e.xData,c=e.yData,
-e=e.start,f=!0,k=b.length;for(d=b.length-1;d>=0;d--)a=b[d]-b[d-1],!f&&b[d]>o&&b[d]<n&&k++,a>0&&(g===t||a<g)?g=a:a<0&&this.requireSorting&&ra(15);this.cropped=f;this.cropStart=e;this.processedXData=b;this.processedYData=c;this.activePointCount=k;if(i.pointRange===null)this.pointRange=g||1;this.closestPointRange=g},cropData:function(a,b,c,d){var e=a.length,f=0,g=e,h=m(this.cropShoulder,1),i;for(i=0;i<e;i++)if(a[i]>=c){f=v(0,i-h);break}for(;i<e;i++)if(a[i]>d){g=i+h;break}return{xData:a.slice(f,g),yData:b.slice(f,
-g),start:f,end:g}},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,i,j=this.hasGroupedData,k,l=[],o;if(!b&&!j)b=[],b.length=a.length,b=this.data=b;for(o=0;o<g;o++)i=h+o,j?l[o]=(new f).init(this,[d[o]].concat(qa(e[o]))):(b[i]?k=b[i]:a[i]!==t&&(b[i]=k=(new f).init(this,a[i],d[o])),l[o]=k);if(b&&(g!==(c=b.length)||j))for(o=0;o<c;o++)if(o===h&&!j&&(o+=g),b[o])b[o].destroyElements(),b[o].plotX=
-t;this.data=b;this.points=l},getExtremes:function(a){var b=this.yAxis,c=this.processedXData,d,e=[],f=0;d=this.xAxis.getExtremes();var g=d.min,h=d.max,i,j,k,l,a=a||this.stackedYData||this.processedYData;d=a.length;for(l=0;l<d;l++)if(j=c[l],k=a[l],i=k!==null&&k!==t&&(!b.isLog||k.length||k>0),j=this.getExtremesFromAll||this.cropped||(c[l+1]||j)>=g&&(c[l-1]||j)<=h,i&&j)if(i=k.length)for(;i--;)k[i]!==null&&(e[f++]=k[i]);else e[f++]=k;this.dataMin=m(void 0,Na(e));this.dataMax=m(void 0,Ba(e))},translate:function(){this.processedXData||
-this.processData();this.generatePoints();for(var a=this.options,b=a.stacking,c=this.xAxis,d=c.categories,e=this.yAxis,f=this.points,g=f.length,h=!!this.modifyValue,i=a.pointPlacement,j=i==="between"||ha(i),k=a.threshold,a=0;a<g;a++){var l=f[a],o=l.x,n=l.y,s=l.low,p=b&&e.stacks[(this.negStacks&&n<k?"-":"")+this.stackKey];if(e.isLog&&n<=0)l.y=n=null;l.plotX=c.translate(o,0,0,0,1,i,this.type==="flags");if(b&&this.visible&&p&&p[o])p=p[o],n=p.points[this.index+","+a],s=n[0],n=n[1],s===0&&(s=m(k,e.min)),
-e.isLog&&s<=0&&(s=null),l.total=l.stackTotal=p.total,l.percentage=p.total&&l.y/p.total*100,l.stackY=n,p.setOffset(this.pointXOffset||0,this.barW||0);l.yBottom=r(s)?e.translate(s,0,1,0,1):null;h&&(n=this.modifyValue(n,l));l.plotY=typeof n==="number"&&n!==Infinity?e.translate(n,0,1,0,1):t;l.clientX=j?c.translate(o,0,0,0,1):l.plotX;l.negative=l.y<(k||0);l.category=d&&d[l.x]!==t?d[l.x]:l.x}this.getSegments()},animate:function(a){var b=this.chart,c=b.renderer,d;d=this.options.animation;var e=this.clipBox||
-b.clipBox,f=b.inverted,g;if(d&&!ca(d))d=ba[this.type].animation;g=["_sharedClip",d.duration,d.easing,e.height].join(",");a?(a=b[g],d=b[g+"m"],a||(b[g]=a=c.clipRect(q(e,{width:0})),b[g+"m"]=d=c.clipRect(-99,f?-b.plotLeft:-b.plotTop,99,f?b.chartWidth:b.chartHeight)),this.group.clip(a),this.markerGroup.clip(d),this.sharedClipKey=g):((a=b[g])&&a.animate({width:b.plotSizeX},d),b[g+"m"]&&b[g+"m"].animate({width:b.plotSizeX+99},d),this.animate=null)},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,
-c=this.group,d=this.clipBox;if(c&&this.options.clip!==!1){if(!b||!d)c.clip(d?a.renderer.clipRect(d):a.clipRect);this.markerGroup.clip()}D(this,"afterAnimate");setTimeout(function(){b&&a[b]&&(d||(a[b]=a[b].destroy()),a[b+"m"]&&(a[b+"m"]=a[b+"m"].destroy()))},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k;d=this.options.marker;var l=this.pointAttr[""],o,n=this.markerGroup,s=m(d.enabled,this.activePointCount<0.5*this.xAxis.len/d.radius);if(d.enabled!==!1||this._hasPointMarkers)for(f=
-b.length;f--;)if(g=b[f],d=T(g.plotX),e=g.plotY,k=g.graphic,i=g.marker||{},a=s&&i.enabled===t||i.enabled,o=c.isInsidePlot(u(d),e,c.inverted),a&&e!==t&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""]||l,h=a.r,i=m(i.symbol,this.symbol),j=i.indexOf("url")===0,k)k[o?"show":"hide"](!0).animate(q({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(o&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(n)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,
-b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=m(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=ba[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color;f={stroke:g,fill:g};var h=a.points||[],i,j=[],k,l=a.pointAttrToOptions;k=a.hasPointSpecificOptions;var o=b.negativeColor,n=c.lineColor,s=c.fillColor;i=b.turboThreshold;var m;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=
-e.color||ya(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,f);p(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;g=h.length;if(!i||g<i||k)for(;g--;){i=h[g];if((c=i.options&&i.options.marker||i.options)&&c.enabled===!1)c.radius=0;if(i.negative&&o)i.color=i.fillColor=o;k=b.colorByPoint||i.color;if(i.options)for(m in l)r(c[l[m]])&&(k=!0);if(k){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=f.color||!i.options.color&&e.color||
-ya(i.color).brighten(f.brightness||e.brightness).get();f={color:i.color};if(!s)f.fillColor=i.color;if(!n)f.lineColor=i.color;k[""]=a.convertAttribs(q(f,c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""])}else k=j;i.pointAttr=k}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(wa),d,e,f=a.data||[],g,h,i;D(a,"destroy");W(a);p(a.axisTypes||[],function(b){if(i=a[b])ja(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);
-for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout);p("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;ja(b.series,a);for(h in a)delete a[h]},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;p(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,
-e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c},getGraphPath:function(){var a=this,b=[],c,d=[];p(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=b.linecap!=="square",g=this.getGraphPath(),
-h=b.negativeColor;h&&c.push(["graphNeg",h]);p(c,function(c,h){var k=c[0],l=a[k];if(l)bb(l),l.animate({d:g});else if(d&&g.length)l={stroke:c[1],"stroke-width":d,fill:Q,zIndex:1},e?l.dashstyle=e:f&&(l["stroke-linecap"]=l["stroke-linejoin"]="round"),a[k]=a.chart.renderer.path(g).attr(l).add(a.group).shadow(!h&&b.shadow)})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor||a.negativeFillColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=
-b.chartHeight,k=v(e,j),l=this.yAxis;if(d&&(f||g)){d=u(l.toPixels(a.threshold||0,!0));d<0&&(k-=d);a={x:0,y:0,width:k,height:d};k={x:0,y:d,width:k,height:k};if(b.inverted)a.height=k.y=b.plotWidth-d,c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d,height:e});l.reversed?(b=k,e=a):(b=a,e=k);h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&this.graphNeg&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))}},
-invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};p(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)K(c,"resize",a),K(b,"destroy",function(){W(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=!f;g&&(this[a]=f=this.chart.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"](this.getPlotBox());return f},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;
-if(a.inverted)b=c,c=this.xAxis;return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,b=a.chart,c,d=a.options,e=(c=d.animation)&&!!a.animate&&b.renderer.isSVG&&m(c.duration,500)||0,f=a.visible?"visible":"hidden",g=d.zIndex,h=a.hasRendered,i=b.seriesGroup;c=a.plotGroup("group","series",f,g,i);a.markerGroup=a.plotGroup("markerGroup","markers",f,g,i);e&&a.animate(!0);a.getAttribs();c.inverted=a.isCartesian?b.inverted:!1;a.drawGraph&&(a.drawGraph(),
-a.clipNeg());a.drawDataLabels&&a.drawDataLabels();a.visible&&a.drawPoints();a.drawTracker&&a.options.enableMouseTracking!==!1&&a.drawTracker();b.inverted&&a.invertGroups();d.clip!==!1&&!a.sharedClipKey&&!h&&c.clip(b.clipRect);e&&a.animate();if(!h)e?a.animationTimeout=setTimeout(function(){a.afterAnimate()},e):a.afterAnimate();a.isDirty=a.isDirtyData=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis,e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,
-height:a.plotHeight}),c.animate({translateX:m(d&&d.left,a.plotLeft),translateY:m(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints&&this.setTooltipPoints(!0);this.render();b&&D(this,"updatedData")}};Hb.prototype={destroy:function(){Oa(this,this.axis)},render:function(a){var b=this.options,c=b.format,c=c?Ia(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,null,null,b.useHTML).css(b.style).attr({align:this.textAlign,
-rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(c.usePercentage?100:this.total,0,0,0,1),c=c.translate(0),c=M(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e[this.options.crop===!1||d.isInsidePlot(f.x,f.y)?"show":"hide"](!0)}};la.prototype.buildStacks=function(){var a=
-this.series,b=m(this.options.reversedStacks,!0),c=a.length;if(!this.isXAxis){for(this.usePercentage=!1;c--;)a[b?c:a.length-c-1].setStackedPoints();if(this.usePercentage)for(c=0;c<a.length;c++)a[c].setPercentStacks()}};la.prototype.renderStackTotals=function(){var a=this.chart,b=a.renderer,c=this.stacks,d,e,f=this.stackTotalGroup;if(!f)this.stackTotalGroup=f=b.g("stack-labels").attr({visibility:"visible",zIndex:6}).add();f.translate(a.plotLeft,a.plotTop);for(d in c)for(e in a=c[d],a)a[e].render(f)};
-O.prototype.setStackedPoints=function(){if(this.options.stacking&&!(this.visible!==!0&&this.chart.options.chart.ignoreHiddenSeries!==!1)){var a=this.processedXData,b=this.processedYData,c=[],d=b.length,e=this.options,f=e.threshold,g=e.stack,e=e.stacking,h=this.stackKey,i="-"+h,j=this.negStacks,k=this.yAxis,l=k.stacks,o=k.oldStacks,n,m,p,q,r,u;for(q=0;q<d;q++){r=a[q];u=b[q];p=this.index+","+q;m=(n=j&&u<f)?i:h;l[m]||(l[m]={});if(!l[m][r])o[m]&&o[m][r]?(l[m][r]=o[m][r],l[m][r].total=null):l[m][r]=new Hb(k,
-k.options.stackLabels,n,r,g);m=l[m][r];m.points[p]=[m.cum||0];e==="percent"?(n=n?h:i,j&&l[n]&&l[n][r]?(n=l[n][r],m.total=n.total=v(n.total,m.total)+M(u)||0):m.total=da(m.total+(M(u)||0))):m.total=da(m.total+(u||0));m.cum=(m.cum||0)+(u||0);m.points[p].push(m.cum);c[q]=m.cum}if(e==="percent")k.usePercentage=!0;this.stackedYData=c;k.oldStacks={}}};O.prototype.setPercentStacks=function(){var a=this,b=a.stackKey,c=a.yAxis.stacks,d=a.processedXData;p([b,"-"+b],function(b){var e;for(var f=d.length,g,h;f--;)if(g=
-d[f],e=(h=c[b]&&c[b][g])&&h.points[a.index+","+f],g=e)h=h.total?100/h.total:0,g[0]=da(g[0]*h),g[1]=da(g[1]*h),a.stackedYData[f]=g[1]})};q(Ya.prototype,{addSeries:function(a,b,c){var d,e=this;a&&(b=m(b,!0),D(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;e.linkSeries();b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var e=b?"xAxis":"yAxis",f=this.options;new la(this,w(a,{index:this[e].length,isX:b}));f[e]=qa(f[e]||{});f[e].push(a);m(c,!0)&&this.redraw(d)},showLoading:function(a){var b=
-this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=Y(Ja,{className:"highcharts-loading"},q(d.style,{zIndex:10,display:Q}),this.container),this.loadingSpan=Y("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)G(c,{opacity:0,display:"",left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),kb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=
-this.options,b=this.loadingDiv;b&&kb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){G(b,{display:Q})}});this.loadingShown=!1}});q(Ea.prototype,{update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=e.chart,j=e.options,b=m(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);if(ca(a)){e.getAttribs();if(f)a&&a.marker&&a.marker.symbol?d.graphic=f.destroy():f.attr(d.pointAttr[d.state||""]);if(a&&a.dataLabels&&d.dataLabel)d.dataLabel=d.dataLabel.destroy()}g=
-Da(d,h);e.updateParallelArrays(d,g);j.data[g]=d.options;e.isDirty=e.isDirtyData=!0;if(!e.fixedBox&&e.hasCartesianSeries)i.isDirtyBox=!0;j.legendType==="point"&&i.legend.destroyItem(d);b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.points,f=d.chart,g,h=d.data;Qa(b,f);a=m(a,!0);c.firePointEvent("remove",null,function(){g=Da(c,h);h.length===e.length&&e.splice(g,1);h.splice(g,1);d.options.data.splice(g,1);d.updateParallelArrays(c,"splice",g,1);c.destroy();d.isDirty=!0;d.isDirtyData=
-!0;a&&f.redraw()})}});q(O.prototype,{addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xAxis&&this.xAxis.names,k=g&&g.shift||0,l=e.data,o,n=this.xData;Qa(d,i);c&&p([g,h,this.graphNeg,this.areaNeg],function(a){if(a)a.shift=k+1});if(h)h.isArea=!0;b=m(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);g=d.x;h=n.length;if(this.requireSorting&&g<n[h-1])for(o=!0;h&&n[h-1]>g;)h--;this.updateParallelArrays(d,"splice",h,0,0);this.updateParallelArrays(d,
-h);if(j)j[g]=d.name;l.splice(h,0,a);o&&(this.data.splice(h,0,null),this.processData());e.legendType==="point"&&this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),this.updateParallelArrays(d,"shift"),l.shift()));this.isDirtyData=this.isDirty=!0;b&&(this.getAttribs(),i.redraw())},remove:function(a,b){var c=this,d=c.chart,a=m(a,!0);if(!c.isRemoving)c.isRemoving=!0,D(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=!0;d.linkSeries();a&&d.redraw(b)});c.isRemoving=
-!1},update:function(a,b){var c=this.chart,d=this.type,e=F[d].prototype,f,a=w(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},{data:this.options.data},a);this.remove(!1);for(f in e)e.hasOwnProperty(f)&&(this[f]=t);q(this,F[a.type||d].prototype);this.init(c,a);m(b,!0)&&c.redraw(!1)}});q(la.prototype,{update:function(a,b){var c=this.chart,a=c.options[this.coll][this.options.index]=w(this.userOptions,a);this.destroy(!0);this._addedPlotLB=t;this.init(c,q(a,{events:t}));c.isDirtyBox=
-!0;m(b,!0)&&c.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,d=this.series,e=d.length;e--;)d[e]&&d[e].remove(!1);ja(b.axes,this);ja(b[c],this);b.options[c].splice(this.options.index,1);p(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;m(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}});ga=ka(O);F.line=ga;ba.area=w(S,{threshold:0});var pa=ka(O,{type:"area",getSegments:function(){var a=
-[],b=[],c=[],d=this.xAxis,e=this.yAxis,f=e.stacks[this.stackKey],g={},h,i,j=this.points,k=this.options.connectNulls,l,o,n;if(this.options.stacking&&!this.cropped){for(o=0;o<j.length;o++)g[j[o].x]=j[o];for(n in f)f[n].total!==null&&c.push(+n);c.sort(function(a,b){return a-b});p(c,function(a){if(!k||g[a]&&g[a].y!==null)g[a]?b.push(g[a]):(h=d.translate(a),l=f[a].percent?f[a].total?f[a].cum*100/f[a].total:0:f[a].cum,i=e.toPixels(l,!0),b.push({y:null,plotX:h,clientX:h,plotY:i,yBottom:i,onMouseOver:sa}))});
-b.length&&a.push(b)}else O.prototype.getSegments.call(this),a=this.segments;this.segments=a},getSegmentPath:function(a){var b=O.prototype.getSegmentPath.call(this,a),c=[].concat(b),d,e=this.options;d=b.length;var f=this.yAxis.getThreshold(e.threshold),g;d===3&&c.push("L",b[1],b[2]);if(e.stacking&&!this.closedStacks)for(d=a.length-1;d>=0;d--)g=m(a[d].yBottom,f),d<a.length-1&&e.step&&c.push(a[d+1].plotX,g),c.push(a[d].plotX,g);else this.closeSegment(c,a,f);this.areaPath=this.areaPath.concat(c);return b},
-closeSegment:function(a,b,c){a.push("L",b[b.length-1].plotX,c,"L",b[0].plotX,c)},drawGraph:function(){this.areaPath=[];O.prototype.drawGraph.apply(this);var a=this,b=this.areaPath,c=this.options,d=c.negativeColor,e=c.negativeFillColor,f=[["area",this.color,c.fillColor]];(d||e)&&f.push(["areaNeg",d,e]);p(f,function(d){var e=d[0],f=a[e];f?f.animate({d:b}):a[e]=a.chart.renderer.path(b).attr({fill:m(d[2],ya(d[1]).setOpacity(m(c.fillOpacity,0.75)).get()),zIndex:0}).add(a.group)})},drawLegendSymbol:N.drawRectangle});
-F.area=pa;ba.spline=w(S);ga=ka(O,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,i,j,k;if(f&&g){a=f.plotY;j=g.plotX;var g=g.plotY,l;h=(1.5*d+f.plotX)/2.5;i=(1.5*e+a)/2.5;j=(1.5*d+j)/2.5;k=(1.5*e+g)/2.5;l=(k-i)*(j-d)/(j-h)+e-k;i+=l;k+=l;i>a&&i>e?(i=v(a,e),k=2*e-i):i<a&&i<e&&(i=C(a,e),k=2*e-i);k>g&&k>e?(k=v(g,e),i=2*e-k):k<g&&k<e&&(k=C(g,e),i=2*e-k);b.rightContX=j;b.rightContY=k}c?(b=["C",f.rightContX||f.plotX,f.rightContY||f.plotY,h||d,i||e,d,e],f.rightContX=
-f.rightContY=null):b=["M",d,e];return b}});F.spline=ga;ba.areaspline=w(ba.area);pa=pa.prototype;ga=ka(ga,{type:"areaspline",closedStacks:!0,getSegmentPath:pa.getSegmentPath,closeSegment:pa.closeSegment,drawGraph:pa.drawGraph,drawLegendSymbol:N.drawRectangle});F.areaspline=ga;ba.column=w(S,{borderColor:"#FFFFFF",borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,shadow:!1,halo:!1},select:{color:"#C0C0C0",borderColor:"#000000",
-shadow:!1}},dataLabels:{align:null,verticalAlign:null,y:null},stickyTracking:!1,tooltip:{distance:6},threshold:0});ga=ka(O,{type:"column",pointAttrToOptions:{stroke:"borderColor",fill:"color",r:"borderRadius"},cropShoulder:0,trackerGroups:["group","dataLabelsGroup"],negStacks:!0,init:function(){O.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},getColumnMetrics:function(){var a=this,b=a.options,c=a.xAxis,d=a.yAxis,e=
-c.reversed,f,g={},h,i=0;b.grouping===!1?i=1:p(a.chart.series,function(b){var c=b.options,e=b.yAxis;if(b.type===a.type&&b.visible&&d.len===e.len&&d.pos===e.pos)c.stacking?(f=b.stackKey,g[f]===t&&(g[f]=i++),h=g[f]):c.grouping!==!1&&(h=i++),b.columnIndex=h});var c=C(M(c.transA)*(c.ordinalSlope||b.pointRange||c.closestPointRange||c.tickInterval||1),c.len),j=c*b.groupPadding,k=(c-2*j)/i,l=b.pointWidth,b=r(l)?(k-l)/2:k*b.pointPadding,l=m(l,k-2*b);return a.columnMetrics={width:l,offset:b+(j+((e?i-(a.columnIndex||
-0):a.columnIndex)||0)*k-c/2)*(e?-1:1)}},translate:function(){var a=this,b=a.chart,c=a.options,d=a.borderWidth=m(c.borderWidth,a.activePointCount>0.5*a.xAxis.len?0:1),e=a.yAxis,f=a.translatedThreshold=e.getThreshold(c.threshold),g=m(c.minPointLength,5),c=a.getColumnMetrics(),h=c.width,i=a.barW=Ka(v(h,1+2*d)),j=a.pointXOffset=c.offset,k=-(d%2?0.5:0),l=d%2?0.5:1;b.renderer.isVML&&b.inverted&&(l+=1);O.prototype.translate.apply(a);p(a.points,function(c){var d=m(c.yBottom,f),p=C(v(-999-d,c.plotY),e.len+
-999+d),q=c.plotX+j,r=i,t=C(p,d),x;x=v(p,d)-t;M(x)<g&&g&&(x=g,t=u(M(t-f)>g?d-g:f-(e.translate(c.y,0,1,0,1)<=f?g:0)));c.barX=q;c.pointWidth=h;c.tooltipPos=b.inverted?[e.len-p,a.xAxis.len-q-r/2]:[q+r/2,p];d=M(q)<0.5;r=u(q+r)+k;q=u(q)+k;r-=q;p=M(t)<0.5;x=u(t+x)+l;t=u(t)+l;x-=t;d&&(q+=1,r-=1);p&&(t-=1,x+=1);c.shapeType="rect";c.shapeArgs={x:q,y:t,width:r,height:x}})},getSymbol:sa,drawLegendSymbol:N.drawRectangle,drawGraph:sa,drawPoints:function(){var a=this,b=this.chart,c=a.options,d=b.renderer,e=c.animationLimit||
-250,f,g,h;p(a.points,function(i){var j=i.plotY,k=i.graphic;if(j!==t&&!isNaN(j)&&i.y!==null)f=i.shapeArgs,h=r(a.borderWidth)?{"stroke-width":a.borderWidth}:{},g=i.pointAttr[i.selected?"select":""]||a.pointAttr[""],k?(bb(k),k.attr(h)[b.pointCount<e?"animate":"attr"](w(f))):i.graphic=d[i.shapeType](f).attr(g).attr(h).add(a.group).shadow(c.shadow,null,c.stacking&&!c.borderRadius);else if(k)i.graphic=k.destroy()})},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(aa)a?
-(e.scaleY=0.001,a=C(b.pos+b.len,v(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)},remove:function(){var a=this,b=a.chart;b.hasRendered&&p(b.series,function(b){if(b.type===a.type)b.isDirty=!0});O.prototype.remove.apply(a,arguments)}});F.column=ga;ba.bar=w(ba.column);pa=ka(ga,{type:"bar",inverted:!0});F.bar=pa;ba.scatter=w(S,{lineWidth:0,tooltip:{headerFormat:'<span style="color:{series.color}">●</span> <span style="font-size: 10px;"> {series.name}</span><br/>',
-pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>"},stickyTracking:!1});pa=ka(O,{type:"scatter",sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["markerGroup"],takeOrdinalPosition:!1,singularTooltips:!0,drawGraph:function(){this.options.lineWidth&&O.prototype.drawGraph.call(this)}});F.scatter=pa;ba.pie=w(S,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,
-legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});S={type:"pie",isCartesian:!1,pointClass:ka(Ea,{init:function(){Ea.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;q(a,{visible:a.visible!==!1,name:m(a.name,"Slice")});b=function(b){a.slice(b.type==="select")};K(a,"select",b);K(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart;b.visible=b.options.visible=
-a=a===t?!b.visible:a;c.options.data[Da(b,c.data)]=b.options;p(["graphic","dataLabel","connector","shadowGroup"],function(c){if(b[c])b[c][a?"show":"hide"](!0)});b.legendItem&&d.legend.colorizeItem(b,a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;Qa(c,d.chart);m(b,!0);this.sliced=this.options.sliced=a=r(a)?a:!this.sliced;d.options.data[Da(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);
-this.shadowGroup&&this.shadowGroup.animate(a)},haloPath:function(a){var b=this.shapeArgs,c=this.series.chart;return this.series.chart.renderer.symbols.arc(c.plotLeft+b.x,c.plotTop+b.y,b.r+a,b.r+a,{innerR:this.shapeArgs.r,start:b.start,end:b.end})}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},singularTooltips:!0,getColor:sa,animate:function(a){var b=this,c=b.points,d=
-b.startAngleRad;if(!a)p(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b,c,d){O.prototype.setData.call(this,a,!1,c,d);this.processData();this.generatePoints();m(b,!0)&&this.chart.redraw(c)},generatePoints:function(){var a,b=0,c,d,e,f=this.options.ignoreHiddenPoint;O.prototype.generatePoints.call(this);c=this.points;d=c.length;for(a=0;a<d;a++)e=c[a],b+=f&&!e.visible?
-0:e.y;this.total=b;for(a=0;a<d;a++)e=c[a],e.percentage=b>0?e.y/b*100:0,e.total=b},translate:function(a){this.generatePoints();var b=0,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g,h,i=c.startAngle||0,j=this.startAngleRad=ma/180*(i-90),i=(this.endAngleRad=ma/180*(m(c.endAngle,i+360)-90))-j,k=this.points,l=c.dataLabels.distance,c=c.ignoreHiddenPoint,o,n=k.length,p;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){h=U.asin(C((b-a[1])/(a[2]/2+l),1));return a[0]+(c?-1:1)*Z(h)*(a[2]/
-2+l)};for(o=0;o<n;o++){p=k[o];f=j+b*i;if(!c||p.visible)b+=p.percentage/100;g=j+b*i;p.shapeType="arc";p.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:u(f*1E3)/1E3,end:u(g*1E3)/1E3};h=(g+f)/2;h>1.5*ma?h-=2*ma:h<-ma/2&&(h+=2*ma);p.slicedTranslation={translateX:u(Z(h)*d),translateY:u(ea(h)*d)};f=Z(h)*a[2]/2;g=ea(h)*a[2]/2;p.tooltipPos=[a[0]+f*0.7,a[1]+g*0.7];p.half=h<-ma/2||h>ma/2?1:0;p.angle=h;e=C(e,l/2);p.labelPos=[a[0]+f+Z(h)*l,a[1]+g+ea(h)*l,a[0]+f+Z(h)*e,a[1]+g+ea(h)*e,a[0]+f,a[1]+g,l<0?
-"center":p.half?"right":"left",h]}},drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,d,e=a.options.shadow,f,g;if(e&&!a.shadowGroup)a.shadowGroup=b.g("shadow").add(a.group);p(a.points,function(h){d=h.graphic;g=h.shapeArgs;f=h.shadowGroup;if(e&&!f)f=h.shadowGroup=b.g("shadow").add(a.shadowGroup);c=h.sliced?h.slicedTranslation:{translateX:0,translateY:0};f&&f.attr(c);d?d.animate(q(g,c)):h.graphic=d=b[h.shapeType](g).setRadialReference(a.center).attr(h.pointAttr[h.selected?"select":
-""]).attr({"stroke-linejoin":"round"}).attr(c).add(a.group).shadow(e,f);h.visible!==void 0&&h.setVisible(h.visible)})},sortByAngle:function(a,b){a.sort(function(a,d){return a.angle!==void 0&&(d.angle-a.angle)*b})},drawLegendSymbol:N.drawRectangle,getCenter:X.getCenter,getSymbol:sa};S=ka(O,S);F.pie=S;O.prototype.drawDataLabels=function(){var a=this,b=a.options,c=b.cursor,d=b.dataLabels,e=a.points,f,g,h,i;if(d.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(d),i=a.plotGroup("dataLabelsGroup",
-"data-labels","hidden",d.zIndex||6),!a.hasRendered&&m(d.defer,!0)&&(i.attr({opacity:0}),K(a,"afterAnimate",function(){a.dataLabelsGroup.show()[b.animation?"animate":"attr"]({opacity:1},{duration:200})})),g=d,p(e,function(b){var e,l=b.dataLabel,o,n,p=b.connector,u=!0;f=b.options&&b.options.dataLabels;e=m(f&&f.enabled,g.enabled);if(l&&!e)b.dataLabel=l.destroy();else if(e){d=w(g,f);e=d.rotation;o=b.getLabelConfig();h=d.format?Ia(d.format,o):d.formatter.call(o,d);d.style.color=m(d.color,d.style.color,
-a.color,"black");if(l)if(r(h))l.attr({text:h}),u=!1;else{if(b.dataLabel=l=l.destroy(),p)b.connector=p.destroy()}else if(r(h)){l={fill:d.backgroundColor,stroke:d.borderColor,"stroke-width":d.borderWidth,r:d.borderRadius||0,rotation:e,padding:d.padding,zIndex:1};for(n in l)l[n]===t&&delete l[n];l=b.dataLabel=a.chart.renderer[e?"text":"label"](h,0,-999,null,null,null,d.useHTML).attr(l).css(q(d.style,c&&{cursor:c})).add(i).shadow(d.shadow)}l&&a.alignDataLabel(b,l,d,null,u)}})};O.prototype.alignDataLabel=
-function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=m(a.plotX,-999),i=m(a.plotY,-999),j=b.getBBox();if(a=this.visible&&(a.series.forceDL||f.isInsidePlot(h,u(i),g)||d&&f.isInsidePlot(h,g?d.x+1:d.y+d.height-1,g)))d=q({x:g?f.plotWidth-i:h,y:u(g?f.plotHeight-h:i),width:0,height:0},d),q(c,{width:j.width,height:j.height}),c.rotation?(g={align:c.align,x:d.x+c.x+d.width/2,y:d.y+c.y+d.height/2},b[e?"attr":"animate"](g)):(b.align(c,null,d),g=b.alignAttr,m(c.overflow,"justify")==="justify"?this.justifyDataLabel(b,
-c,g,j,d,e):m(c.crop,!0)&&(a=f.isInsidePlot(g.x,g.y)&&f.isInsidePlot(g.x+j.width,g.y+j.height)));if(!a)b.attr({y:-999}),b.placed=!1};O.prototype.justifyDataLabel=function(a,b,c,d,e,f){var g=this.chart,h=b.align,i=b.verticalAlign,j,k;j=c.x;if(j<0)h==="right"?b.align="left":b.x=-j,k=!0;j=c.x+d.width;if(j>g.plotWidth)h==="left"?b.align="right":b.x=g.plotWidth-j,k=!0;j=c.y;if(j<0)i==="bottom"?b.verticalAlign="top":b.y=-j,k=!0;j=c.y+d.height;if(j>g.plotHeight)i==="top"?b.verticalAlign="bottom":b.y=g.plotHeight-
-j,k=!0;if(k)a.placed=!f,a.align(b,null,e)};if(F.pie)F.pie.prototype.drawDataLabels=function(){var a=this,b=a.data,c,d=a.chart,e=a.options.dataLabels,f=m(e.connectorPadding,10),g=m(e.connectorWidth,1),h=d.plotWidth,d=d.plotHeight,i,j,k=m(e.softConnector,!0),l=e.distance,o=a.center,n=o[2]/2,q=o[1],r=l>0,t,w,x,y,z=[[],[]],A,C,G,D,B,F=[0,0,0,0],N=function(a,b){return b.y-a.y};if(a.visible&&(e.enabled||a._hasPointLabels)){O.prototype.drawDataLabels.apply(a);p(b,function(a){a.dataLabel&&a.visible&&z[a.half].push(a)});
-for(D=0;!y&&b[D];)y=b[D]&&b[D].dataLabel&&(b[D].dataLabel.getBBox().height||21),D++;for(D=2;D--;){var b=[],K=[],H=z[D],I=H.length,E;a.sortByAngle(H,D-0.5);if(l>0){for(B=q-n-l;B<=q+n+l;B+=y)b.push(B);w=b.length;if(I>w){c=[].concat(H);c.sort(N);for(B=I;B--;)c[B].rank=B;for(B=I;B--;)H[B].rank>=w&&H.splice(B,1);I=H.length}for(B=0;B<I;B++){c=H[B];x=c.labelPos;c=9999;var Q,P;for(P=0;P<w;P++)Q=M(b[P]-x[1]),Q<c&&(c=Q,E=P);if(E<B&&b[B]!==null)E=B;else for(w<I-B+E&&b[B]!==null&&(E=w-I+B);b[E]===null;)E++;K.push({i:E,
-y:b[E]});b[E]=null}K.sort(N)}for(B=0;B<I;B++){c=H[B];x=c.labelPos;t=c.dataLabel;G=c.visible===!1?"hidden":"visible";c=x[1];if(l>0){if(w=K.pop(),E=w.i,C=w.y,c>C&&b[E+1]!==null||c<C&&b[E-1]!==null)C=c}else C=c;A=e.justify?o[0]+(D?-1:1)*(n+l):a.getX(E===0||E===b.length-1?c:C,D);t._attr={visibility:G,align:x[6]};t._pos={x:A+e.x+({left:f,right:-f}[x[6]]||0),y:C+e.y-10};t.connX=A;t.connY=C;if(this.options.size===null)w=t.width,A-w<f?F[3]=v(u(w-A+f),F[3]):A+w>h-f&&(F[1]=v(u(A+w-h+f),F[1])),C-y/2<0?F[0]=
-v(u(-C+y/2),F[0]):C+y/2>d&&(F[2]=v(u(C+y/2-d),F[2]))}}if(Ba(F)===0||this.verifyDataLabelOverflow(F))this.placeDataLabels(),r&&g&&p(this.points,function(b){i=b.connector;x=b.labelPos;if((t=b.dataLabel)&&t._pos)G=t._attr.visibility,A=t.connX,C=t.connY,j=k?["M",A+(x[6]==="left"?5:-5),C,"C",A,C,2*x[2]-x[4],2*x[3]-x[5],x[2],x[3],"L",x[4],x[5]]:["M",A+(x[6]==="left"?5:-5),C,"L",x[2],x[3],"L",x[4],x[5]],i?(i.animate({d:j}),i.attr("visibility",G)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,
-stroke:e.connectorColor||b.color||"#606060",visibility:G}).add(a.dataLabelsGroup);else if(i)b.connector=i.destroy()})}},F.pie.prototype.placeDataLabels=function(){p(this.points,function(a){var a=a.dataLabel,b;if(a)(b=a._pos)?(a.attr(a._attr),a[a.moved?"animate":"attr"](b),a.moved=!0):a&&a.attr({y:-999})})},F.pie.prototype.alignDataLabel=sa,F.pie.prototype.verifyDataLabelOverflow=function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=v(b[2]-v(a[1],a[3]),c):(e=v(b[2]-
-a[1]-a[3],c),b[0]+=(a[3]-a[1])/2);d[1]!==null?e=v(C(e,b[2]-v(a[0],a[2])),c):(e=v(C(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e<b[2]?(b[2]=e,this.translate(b),p(this.points,function(a){if(a.dataLabel)a.dataLabel._pos=null}),this.drawDataLabels&&this.drawDataLabels()):f=!0;return f};if(F.column)F.column.prototype.alignDataLabel=function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>m(this.translatedThreshold,f.plotSizeY),j=m(c.inside,!!this.options.stacking);if(h&&
-(d=w(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height:0,d.height=0);c.align=m(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=m(c.verticalAlign,g||j?"middle":i?"top":"bottom");O.prototype.alignDataLabel.call(this,a,b,c,d,e)};S=R.TrackerMixin={drawTrackerPoint:function(){var a=this,b=a.chart,c=b.pointer,d=a.options.cursor,e=d&&{cursor:d},f=function(c){var d=c.target,e;if(b.hoverSeries!==a)a.onMouseOver();
-for(;d&&!e;)e=d.point,d=d.parentNode;if(e!==t&&e!==b.hoverPoint)e.onMouseOver(c)};p(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});if(!a._hasTracking)p(a.trackerGroups,function(b){if(a[b]&&(a[b].addClass("highcharts-tracker").on("mouseover",f).on("mouseout",function(a){c.onTrackerMouseOut(a)}).css(e),$a))a[b].on("touchstart",f)}),a._hasTracking=!0},drawTrackerGraph:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:
-a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,l=k&&{cursor:k},k=a.singlePoints,m,n=function(){if(f.hoverSeries!==a)a.onMouseOver()},q="rgba(192,192,192,"+(aa?1.0E-4:0.002)+")";if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-i,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+i,d[m-1]);for(m=0;m<k.length;m++)e=k[m],d.push("M",e.plotX-i,e.plotY,"L",e.plotX+i,e.plotY);j?j.attr({d:d}):(a.tracker=h.path(d).attr({"stroke-linejoin":"round",
-visibility:a.visible?"visible":"hidden",stroke:q,fill:c?q:Q,"stroke-width":b.lineWidth+(c?0:2*i),zIndex:2}).add(a.group),p([a.tracker,a.markerGroup],function(a){a.addClass("highcharts-tracker").on("mouseover",n).on("mouseout",function(a){g.onTrackerMouseOut(a)}).css(l);if($a)a.on("touchstart",n)}))}};if(F.column)ga.prototype.drawTracker=S.drawTrackerPoint;if(F.pie)F.pie.prototype.drawTracker=S.drawTrackerPoint;if(F.scatter)pa.prototype.drawTracker=S.drawTrackerPoint;q(lb.prototype,{setItemEvents:function(a,
-b,c,d,e){var f=this;(c?b:a.legendGroup).on("mouseover",function(){a.setState("hover");b.css(f.options.itemHoverStyle)}).on("mouseout",function(){b.css(a.visible?d:e);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):D(a,"legendItemClick",b,c)})},createCheckboxForItem:function(a){a.checkbox=Y("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},this.options.itemCheckboxStyle,this.chart.container);
-K(a.checkbox,"click",function(b){D(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})})}});E.legend.itemStyle.cursor="pointer";q(Ya.prototype,{showResetZoom:function(){var a=this,b=E.lang,c=a.options.chart.resetZoomButton,d=c.theme,e=d.states,f=c.relativeTo==="chart"?null:"plotBox";this.resetZoomButton=a.renderer.button(b.resetZoom,null,null,function(){a.zoomOut()},d,e&&e.hover).attr({align:c.position.align,title:b.resetZoomTitle}).add().align(c.position,!1,f)},zoomOut:function(){var a=
-this;D(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var b,c=this.pointer,d=!1,e;!a||a.resetSelection?p(this.axes,function(a){b=a.zoom()}):p(a.xAxis.concat(a.yAxis),function(a){var e=a.axis,h=e.isXAxis;if(c[h?"zoomX":"zoomY"]||c[h?"pinchX":"pinchY"])b=e.zoom(a.min,a.max),e.displayBtn&&(d=!0)});e=this.resetZoomButton;if(d&&!e)this.showResetZoom();else if(!d&&ca(e))this.resetZoomButton=e.destroy();b&&this.redraw(m(this.options.chart.animation,a&&a.animation,this.pointCount<
-100))},pan:function(a,b){var c=this,d=c.hoverPoints,e;d&&p(d,function(a){a.setState()});p(b==="xy"?[1,0]:[1],function(b){var d=a[b?"chartX":"chartY"],h=c[b?"xAxis":"yAxis"][0],i=c[b?"mouseDownX":"mouseDownY"],j=(h.pointRange||0)/2,k=h.getExtremes(),l=h.toValue(i-d,!0)+j,i=h.toValue(i+c[b?"plotWidth":"plotHeight"]-d,!0)-j;h.series.length&&l>C(k.dataMin,k.min)&&i<v(k.dataMax,k.max)&&(h.setExtremes(l,i,!1,!1,{trigger:"pan"}),e=!0);c[b?"mouseDownX":"mouseDownY"]=d});e&&c.redraw(!1);G(c.container,{cursor:"move"})}});
-q(Ea.prototype,{select:function(a,b){var c=this,d=c.series,e=d.chart,a=m(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=c.options.selected=a;d.options.data[Da(c,d.data)]=c.options;c.setState(a&&"select");b||p(e.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=a.options.selected=!1,d.options.data[Da(a,d.data)]=a.options,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(a){var b=this.series,c=b.chart,d=c.tooltip,e=c.hoverPoint;
-if(e&&e!==this)e.onMouseOut();this.firePointEvent("mouseOver");d&&(!d.shared||b.noSharedTooltip)&&d.refresh(this,a);this.setState("hover");c.hoverPoint=this},onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||Da(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},importEvents:function(){if(!this.hasImportedEvents){var a=w(this.series.options.point,this.options).events,b;this.events=a;for(b in a)K(this,b,a[b]);this.hasImportedEvents=!0}},setState:function(a,
-b){var c=this.plotX,d=this.plotY,e=this.series,f=e.options.states,g=ba[e.type].marker&&e.options.marker,h=g&&!g.enabled,i=g&&g.states[a],j=i&&i.enabled===!1,k=e.stateMarkerGraphic,l=this.marker||{},m=e.chart,n=e.halo,p,a=a||"";p=this.pointAttr[a]||e.pointAttr[a];if(!(a===this.state&&!b||this.selected&&a!=="select"||f[a]&&f[a].enabled===!1||a&&(j||h&&i.enabled===!1)||a&&l.states&&l.states[a]&&l.states[a].enabled===!1)){if(this.graphic)g=g&&this.graphic.symbolName&&p.r,this.graphic.attr(w(p,g?{x:c-
-g,y:d-g,width:2*g,height:2*g}:{})),k&&k.hide();else{if(a&&i)if(g=i.radius,l=l.symbol||e.symbol,k&&k.currentSymbol!==l&&(k=k.destroy()),k)k[b?"animate":"attr"]({x:c-g,y:d-g});else if(l)e.stateMarkerGraphic=k=m.renderer.symbol(l,c-g,d-g,2*g,2*g).attr(p).add(e.markerGroup),k.currentSymbol=l;if(k)k[a&&m.isInsidePlot(c,d,m.inverted)?"show":"hide"]()}if((c=f[a]&&f[a].halo)&&c.size){if(!n)e.halo=n=m.renderer.path().add(e.seriesGroup);n.attr(q({fill:ya(this.color||e.color).setOpacity(c.opacity).get()},c.attributes))[b?
-"animate":"attr"]({d:this.haloPath(c.size)})}else n&&n.attr({d:[]});this.state=a}},haloPath:function(a){var b=this.series,c=b.chart,d=b.getPlotBox(),e=c.inverted;return c.renderer.symbols.circle(d.translateX+(e?b.yAxis.len-this.plotY:this.plotX)-a,d.translateY+(e?b.xAxis.len-this.plotX:this.plotY)-a,a*2,a*2)}});q(O.prototype,{onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&D(this,"mouseOver");this.setState("hover");a.hoverSeries=
-this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&D(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},
-setVisible:function(a,b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===t?!h:a)?"show":"hide";p(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&p(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});p(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;
-b!==!1&&d.redraw();D(c,f)},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||e.len:this.chart.plotSizeX,h,i,j=[];if(!(this.options.enableMouseTracking===!1||this.singularTooltips)){if(a)this.tooltipPoints=null;p(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(i=0;i<a;i++)if(e=b[i],c=e.x,c>=f.min&&c<=f.max){h=b[i+1];c=d===t?0:d+1;for(d=b[i+1]?C(v(0,T((e.clientX+
-(h?h.wrappedClientX||h.clientX:g))/2)),g):g;c>=0&&c<=d;)j[c++]=e}this.tooltipPoints=j}},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===t?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;D(this,a?"select":"unselect")},drawTracker:S.drawTrackerGraph});q(R,{Axis:la,Chart:Ya,Color:ya,Point:Ea,Tick:Sa,Renderer:Za,Series:O,SVGElement:P,SVGRenderer:ta,arrayMin:Na,arrayMax:Ba,charts:V,dateFormat:cb,format:Ia,pathAnim:ub,getOptions:function(){return E},
-hasBidiBug:Nb,isTouchDevice:Jb,numberFormat:Ga,seriesTypes:F,setOptions:function(a){E=w(!0,E,a);Cb();return E},addEvent:K,removeEvent:W,createElement:Y,discardElement:Pa,css:G,each:p,extend:q,map:Ua,merge:w,pick:m,splat:qa,extendClass:ka,pInt:z,wrap:Ma,svg:aa,canvas:fa,vml:!aa&&!fa,product:"Highcharts",version:"4.0.1"})})();
diff --git a/apps/static/js/plugins/highcharts/highcharts.src.js b/apps/static/js/plugins/highcharts/highcharts.src.js
deleted file mode 100644
index c544e7fd7..000000000
--- a/apps/static/js/plugins/highcharts/highcharts.src.js
+++ /dev/null
@@ -1,17672 +0,0 @@
-// ==ClosureCompiler==
-// @compilation_level SIMPLE_OPTIMIZATIONS
-
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- *
- * (c) 2009-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-// JSLint options:
-/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
-
-(function () {
-// encapsulated variables
-var UNDEFINED,
-	doc = document,
-	win = window,
-	math = Math,
-	mathRound = math.round,
-	mathFloor = math.floor,
-	mathCeil = math.ceil,
-	mathMax = math.max,
-	mathMin = math.min,
-	mathAbs = math.abs,
-	mathCos = math.cos,
-	mathSin = math.sin,
-	mathPI = math.PI,
-	deg2rad = mathPI * 2 / 360,
-
-
-	// some variables
-	userAgent = navigator.userAgent,
-	isOpera = win.opera,
-	isIE = /msie/i.test(userAgent) && !isOpera,
-	docMode8 = doc.documentMode === 8,
-	isWebKit = /AppleWebKit/.test(userAgent),
-	isFirefox = /Firefox/.test(userAgent),
-	isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent),
-	SVG_NS = 'http://www.w3.org/2000/svg',
-	hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
-	hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
-	useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext,
-	Renderer,
-	hasTouch,
-	symbolSizes = {},
-	idCounter = 0,
-	garbageBin,
-	defaultOptions,
-	dateFormat, // function
-	globalAnimation,
-	pathAnim,
-	timeUnits,
-	noop = function () {},
-	charts = [],
-	chartCount = 0,
-	PRODUCT = 'Highcharts',
-	VERSION = '4.0.1',
-
-	// some constants for frequently used strings
-	DIV = 'div',
-	ABSOLUTE = 'absolute',
-	RELATIVE = 'relative',
-	HIDDEN = 'hidden',
-	PREFIX = 'highcharts-',
-	VISIBLE = 'visible',
-	PX = 'px',
-	NONE = 'none',
-	M = 'M',
-	L = 'L',
-	numRegex = /^[0-9]+$/,
-	NORMAL_STATE = '',
-	HOVER_STATE = 'hover',
-	SELECT_STATE = 'select',
-	MILLISECOND = 'millisecond',
-	SECOND = 'second',
-	MINUTE = 'minute',
-	HOUR = 'hour',
-	DAY = 'day',
-	WEEK = 'week',
-	MONTH = 'month',
-	YEAR = 'year',
-	
-	// Object for extending Axis
-	AxisPlotLineOrBandExtension,
-
-	// constants for attributes
-	STROKE_WIDTH = 'stroke-width',
-
-	// time methods, changed based on whether or not UTC is used
-	makeTime,
-	timezoneOffset,
-	getMinutes,
-	getHours,
-	getDay,
-	getDate,
-	getMonth,
-	getFullYear,
-	setMinutes,
-	setHours,
-	setDate,
-	setMonth,
-	setFullYear,
-
-
-	// lookup over the types and the associated classes
-	seriesTypes = {};
-
-// The Highcharts namespace
-var Highcharts = win.Highcharts = win.Highcharts ? error(16, true) : {};
-
-/**
- * Extend an object with the members of another
- * @param {Object} a The object to be extended
- * @param {Object} b The object to add to the first one
- */
-function extend(a, b) {
-	var n;
-	if (!a) {
-		a = {};
-	}
-	for (n in b) {
-		a[n] = b[n];
-	}
-	return a;
-}
-	
-/**
- * Deep merge two or more objects and return a third object. If the first argument is
- * true, the contents of the second object is copied into the first object.
- * Previously this function redirected to jQuery.extend(true), but this had two limitations.
- * First, it deep merged arrays, which lead to workarounds in Highcharts. Second,
- * it copied properties from extended prototypes. 
- */
-function merge() {
-	var i,
-		args = arguments,
-		len,
-		ret = {},
-		doCopy = function (copy, original) {
-			var value, key;
-
-			// An object is replacing a primitive
-			if (typeof copy !== 'object') {
-				copy = {};
-			}
-
-			for (key in original) {
-				if (original.hasOwnProperty(key)) {
-					value = original[key];
-
-					// Copy the contents of objects, but not arrays or DOM nodes
-					if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]'
-							&& key !== 'renderTo' && typeof value.nodeType !== 'number') {
-						copy[key] = doCopy(copy[key] || {}, value);
-				
-					// Primitives and arrays are copied over directly
-					} else {
-						copy[key] = original[key];
-					}
-				}
-			}
-			return copy;
-		};
-
-	// If first argument is true, copy into the existing object. Used in setOptions.
-	if (args[0] === true) {
-		ret = args[1];
-		args = Array.prototype.slice.call(args, 2);
-	}
-
-	// For each argument, extend the return
-	len = args.length;
-	for (i = 0; i < len; i++) {
-		ret = doCopy(ret, args[i]);
-	}
-
-	return ret;
-}
-
-/**
- * Take an array and turn into a hash with even number arguments as role_keys and odd numbers as
- * values. Allows creating constants for commonly used style properties, attributes etc.
- * Avoid it in performance critical situations like looping
- */
-function hash() {
-	var i = 0,
-		args = arguments,
-		length = args.length,
-		obj = {};
-	for (; i < length; i++) {
-		obj[args[i++]] = args[i];
-	}
-	return obj;
-}
-
-/**
- * Shortcut for parseInt
- * @param {Object} s
- * @param {Number} mag Magnitude
- */
-function pInt(s, mag) {
-	return parseInt(s, mag || 10);
-}
-
-/**
- * Check for string
- * @param {Object} s
- */
-function isString(s) {
-	return typeof s === 'string';
-}
-
-/**
- * Check for object
- * @param {Object} obj
- */
-function isObject(obj) {
-	return typeof obj === 'object';
-}
-
-/**
- * Check for array
- * @param {Object} obj
- */
-function isArray(obj) {
-	return Object.prototype.toString.call(obj) === '[object Array]';
-}
-
-/**
- * Check for number
- * @param {Object} n
- */
-function isNumber(n) {
-	return typeof n === 'number';
-}
-
-function log2lin(num) {
-	return math.log(num) / math.LN10;
-}
-function lin2log(num) {
-	return math.pow(10, num);
-}
-
-/**
- * Remove last occurence of an item from an array
- * @param {Array} arr
- * @param {Mixed} item
- */
-function erase(arr, item) {
-	var i = arr.length;
-	while (i--) {
-		if (arr[i] === item) {
-			arr.splice(i, 1);
-			break;
-		}
-	}
-	//return arr;
-}
-
-/**
- * Returns true if the object is not null or undefined. Like MooTools' $.defined.
- * @param {Object} obj
- */
-function defined(obj) {
-	return obj !== UNDEFINED && obj !== null;
-}
-
-/**
- * Set or get an attribute or an object of attributes. Can't use jQuery attr because
- * it attempts to set expando properties on the SVG element, which is not allowed.
- *
- * @param {Object} elem The DOM element to receive the attribute(s)
- * @param {String|Object} prop The property or an abject of key-value pairs
- * @param {String} value The value if a single property is set
- */
-function attr(elem, prop, value) {
-	var key,
-		ret;
-
-	// if the prop is a string
-	if (isString(prop)) {
-		// set the value
-		if (defined(value)) {
-			elem.setAttribute(prop, value);
-
-		// get the value
-		} else if (elem && elem.getAttribute) { // elem not defined when printing pie demo...
-			ret = elem.getAttribute(prop);
-		}
-
-	// else if prop is defined, it is a hash of key/value pairs
-	} else if (defined(prop) && isObject(prop)) {
-		for (key in prop) {
-			elem.setAttribute(key, prop[key]);
-		}
-	}
-	return ret;
-}
-/**
- * Check if an element is an array, and if not, make it into an array. Like
- * MooTools' $.splat.
- */
-function splat(obj) {
-	return isArray(obj) ? obj : [obj];
-}
-
-
-/**
- * Return the first value that is defined. Like MooTools' $.pick.
- */
-function pick() {
-	var args = arguments,
-		i,
-		arg,
-		length = args.length;
-	for (i = 0; i < length; i++) {
-		arg = args[i];
-		if (typeof arg !== 'undefined' && arg !== null) {
-			return arg;
-		}
-	}
-}
-
-/**
- * Set CSS on a given element
- * @param {Object} el
- * @param {Object} styles Style object with camel case property names
- */
-function css(el, styles) {
-	if (isIE && !hasSVG) { // #2686
-		if (styles && styles.opacity !== UNDEFINED) {
-			styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
-		}
-	}
-	extend(el.style, styles);
-}
-
-/**
- * Utility function to create element with attributes and styles
- * @param {Object} tag
- * @param {Object} attribs
- * @param {Object} styles
- * @param {Object} parent
- * @param {Object} nopad
- */
-function createElement(tag, attribs, styles, parent, nopad) {
-	var el = doc.createElement(tag);
-	if (attribs) {
-		extend(el, attribs);
-	}
-	if (nopad) {
-		css(el, {padding: 0, border: NONE, margin: 0});
-	}
-	if (styles) {
-		css(el, styles);
-	}
-	if (parent) {
-		parent.appendChild(el);
-	}
-	return el;
-}
-
-/**
- * Extend a prototyped class by new members
- * @param {Object} parent
- * @param {Object} members
- */
-function extendClass(parent, members) {
-	var object = function () {};
-	object.prototype = new parent();
-	extend(object.prototype, members);
-	return object;
-}
-
-/**
- * Format a number and return a string based on input settings
- * @param {Number} number The input number to format
- * @param {Number} decimals The amount of decimals
- * @param {String} decPoint The decimal point, defaults to the one given in the lang options
- * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options
- */
-function numberFormat(number, decimals, decPoint, thousandsSep) {
-	var lang = defaultOptions.lang,
-		// http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
-		n = +number || 0,
-		c = decimals === -1 ?
-			(n.toString().split('.')[1] || '').length : // preserve decimals
-			(isNaN(decimals = mathAbs(decimals)) ? 2 : decimals),
-		d = decPoint === undefined ? lang.decimalPoint : decPoint,
-		t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep,
-		s = n < 0 ? "-" : "",
-		i = String(pInt(n = mathAbs(n).toFixed(c))),
-		j = i.length > 3 ? i.length % 3 : 0;
-
-	return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
-		(c ? d + mathAbs(n - i).toFixed(c).slice(2) : "");
-}
-
-/**
- * Pad a string to a given length by adding 0 to the beginning
- * @param {Number} number
- * @param {Number} length
- */
-function pad(number, length) {
-	// Create an array of the remaining length +1 and join it with 0's
-	return new Array((length || 2) + 1 - String(number).length).join(0) + number;
-}
-
-/**
- * Wrap a method with extended functionality, preserving the original function
- * @param {Object} obj The context object that the method belongs to 
- * @param {String} method The name of the method to extend
- * @param {Function} func A wrapper function callback. This function is called with the same arguments
- * as the original function, except that the original function is unshifted and passed as the first 
- * argument. 
- */
-function wrap(obj, method, func) {
-	var proceed = obj[method];
-	obj[method] = function () {
-		var args = Array.prototype.slice.call(arguments);
-		args.unshift(proceed);
-		return func.apply(this, args);
-	};
-}
-
-/**
- * Based on http://www.php.net/manual/en/function.strftime.php
- * @param {String} format
- * @param {Number} timestamp
- * @param {Boolean} capitalize
- */
-dateFormat = function (format, timestamp, capitalize) {
-	if (!defined(timestamp) || isNaN(timestamp)) {
-		return 'Invalid date';
-	}
-	format = pick(format, '%Y-%m-%d %H:%M:%S');
-
-	var date = new Date(timestamp - timezoneOffset),
-		key, // used in for constuct below
-		// get the basic time values
-		hours = date[getHours](),
-		day = date[getDay](),
-		dayOfMonth = date[getDate](),
-		month = date[getMonth](),
-		fullYear = date[getFullYear](),
-		lang = defaultOptions.lang,
-		langWeekdays = lang.weekdays,
-
-		// List all format role_keys. Custom formats can be added from the outside.
-		replacements = extend({
-
-			// Day
-			'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
-			'A': langWeekdays[day], // Long weekday, like 'Monday'
-			'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31
-			'e': dayOfMonth, // Day of the month, 1 through 31
-
-			// Week (none implemented)
-			//'W': weekNumber(),
-
-			// Month
-			'b': lang.shortMonths[month], // Short month, like 'Jan'
-			'B': lang.months[month], // Long month, like 'January'
-			'm': pad(month + 1), // Two digit month number, 01 through 12
-
-			// Year
-			'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009
-			'Y': fullYear, // Four digits year, like 2009
-
-			// Time
-			'H': pad(hours), // Two digits hours in 24h format, 00 through 23
-			'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11
-			'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12
-			'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59
-			'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM
-			'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM
-			'S': pad(date.getSeconds()), // Two digits seconds, 00 through  59
-			'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby)
-		}, Highcharts.dateFormats);
-
-
-	// do the replaces
-	for (key in replacements) {
-		while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster
-			format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]);
-		}
-	}
-
-	// Optionally capitalize the string and return
-	return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format;
-};
-
-/** 
- * Format a single variable. Similar to sprintf, without the % prefix.
- */
-function formatSingle(format, val) {
-	var floatRegex = /f$/,
-		decRegex = /\.([0-9])/,
-		lang = defaultOptions.lang,
-		decimals;
-
-	if (floatRegex.test(format)) { // float
-		decimals = format.match(decRegex);
-		decimals = decimals ? decimals[1] : -1;
-		if (val !== null) {
-			val = numberFormat(
-				val,
-				decimals,
-				lang.decimalPoint,
-				format.indexOf(',') > -1 ? lang.thousandsSep : ''
-			);
-		}
-	} else {
-		val = dateFormat(format, val);
-	}
-	return val;
-}
-
-/**
- * Format a string according to a subset of the rules of Python's String.format method.
- */
-function format(str, ctx) {
-	var splitter = '{',
-		isInside = false,
-		segment,
-		valueAndFormat,
-		path,
-		i,
-		len,
-		ret = [],
-		val,
-		index;
-	
-	while ((index = str.indexOf(splitter)) !== -1) {
-		
-		segment = str.slice(0, index);
-		if (isInside) { // we're on the closing bracket looking back
-			
-			valueAndFormat = segment.split(':');
-			path = valueAndFormat.shift().split('.'); // get first and leave format
-			len = path.length;
-			val = ctx;
-
-			// Assign deeper paths
-			for (i = 0; i < len; i++) {
-				val = val[path[i]];
-			}
-
-			// Format the replacement
-			if (valueAndFormat.length) {
-				val = formatSingle(valueAndFormat.join(':'), val);
-			}
-
-			// Push the result and advance the cursor
-			ret.push(val);
-			
-		} else {
-			ret.push(segment);
-			
-		}
-		str = str.slice(index + 1); // the rest
-		isInside = !isInside; // toggle
-		splitter = isInside ? '}' : '{'; // now look for next matching bracket
-	}
-	ret.push(str);
-	return ret.join('');
-}
-
-/**
- * Get the magnitude of a number
- */
-function getMagnitude(num) {
-	return math.pow(10, mathFloor(math.log(num) / math.LN10));
-}
-
-/**
- * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
- * @param {Number} interval
- * @param {Array} multiples
- * @param {Number} magnitude
- * @param {Object} options
- */
-function normalizeTickInterval(interval, multiples, magnitude, options) {
-	var normalized, i;
-
-	// round to a tenfold of 1, 2, 2.5 or 5
-	magnitude = pick(magnitude, 1);
-	normalized = interval / magnitude;
-
-	// multiples for a linear scale
-	if (!multiples) {
-		multiples = [1, 2, 2.5, 5, 10];
-
-		// the allowDecimals option
-		if (options && options.allowDecimals === false) {
-			if (magnitude === 1) {
-				multiples = [1, 2, 5, 10];
-			} else if (magnitude <= 0.1) {
-				multiples = [1 / magnitude];
-			}
-		}
-	}
-
-	// normalize the interval to the nearest multiple
-	for (i = 0; i < multiples.length; i++) {
-		interval = multiples[i];
-		if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) {
-			break;
-		}
-	}
-
-	// multiply back to the correct magnitude
-	interval *= magnitude;
-
-	return interval;
-}
-
-
-/**
- * Helper class that contains variuos counters that are local to the chart.
- */
-function ChartCounters() {
-	this.color = 0;
-	this.symbol = 0;
-}
-
-ChartCounters.prototype =  {
-	/**
-	 * Wraps the color counter if it reaches the specified length.
-	 */
-	wrapColor: function (length) {
-		if (this.color >= length) {
-			this.color = 0;
-		}
-	},
-
-	/**
-	 * Wraps the symbol counter if it reaches the specified length.
-	 */
-	wrapSymbol: function (length) {
-		if (this.symbol >= length) {
-			this.symbol = 0;
-		}
-	}
-};
-
-
-/**
- * Utility method that sorts an object array and keeping the order of equal items.
- * ECMA script standard does not specify the behaviour when items are equal.
- */
-function stableSort(arr, sortFunction) {
-	var length = arr.length,
-		sortValue,
-		i;
-
-	// Add index to each item
-	for (i = 0; i < length; i++) {
-		arr[i].ss_i = i; // stable sort index
-	}
-
-	arr.sort(function (a, b) {
-		sortValue = sortFunction(a, b);
-		return sortValue === 0 ? a.ss_i - b.ss_i : sortValue;
-	});
-
-	// Remove index from items
-	for (i = 0; i < length; i++) {
-		delete arr[i].ss_i; // stable sort index
-	}
-}
-
-/**
- * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
- * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
- * method is slightly slower, but safe.
- */
-function arrayMin(data) {
-	var i = data.length,
-		min = data[0];
-
-	while (i--) {
-		if (data[i] < min) {
-			min = data[i];
-		}
-	}
-	return min;
-}
-
-/**
- * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
- * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
- * method is slightly slower, but safe.
- */
-function arrayMax(data) {
-	var i = data.length,
-		max = data[0];
-
-	while (i--) {
-		if (data[i] > max) {
-			max = data[i];
-		}
-	}
-	return max;
-}
-
-/**
- * Utility method that destroys any SVGElement or VMLElement that are properties on the given object.
- * It loops all properties and invokes destroy if there is a destroy method. The property is
- * then delete'ed.
- * @param {Object} The object to destroy properties on
- * @param {Object} Exception, do not destroy this property, only delete it.
- */
-function destroyObjectProperties(obj, except) {
-	var n;
-	for (n in obj) {
-		// If the object is non-null and destroy is defined
-		if (obj[n] && obj[n] !== except && obj[n].destroy) {
-			// Invoke the destroy
-			obj[n].destroy();
-		}
-
-		// Delete the property from the object.
-		delete obj[n];
-	}
-}
-
-
-/**
- * Discard an element by moving it to the bin and delete
- * @param {Object} The HTML node to discard
- */
-function discardElement(element) {
-	// create a garbage bin element, not part of the DOM
-	if (!garbageBin) {
-		garbageBin = createElement(DIV);
-	}
-
-	// move the node and empty bin
-	if (element) {
-		garbageBin.appendChild(element);
-	}
-	garbageBin.innerHTML = '';
-}
-
-/**
- * Provide error messages for debugging, with links to online explanation 
- */
-function error(code, stop) {
-	var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
-	if (stop) {
-		throw msg;
-	} else if (win.console) {
-		console.log(msg);
-	}
-}
-
-/**
- * Fix JS round off float errors
- * @param {Number} num
- */
-function correctFloat(num) {
-	return parseFloat(
-		num.toPrecision(14)
-	);
-}
-
-/**
- * Set the global animation to either a given value, or fall back to the
- * given chart's animation option
- * @param {Object} animation
- * @param {Object} chart
- */
-function setAnimation(animation, chart) {
-	globalAnimation = pick(animation, chart.animation);
-}
-
-/**
- * The time unit lookup
- */
-/*jslint white: true*/
-timeUnits = hash(
-	MILLISECOND, 1,
-	SECOND, 1000,
-	MINUTE, 60000,
-	HOUR, 3600000,
-	DAY, 24 * 3600000,
-	WEEK, 7 * 24 * 3600000,
-	MONTH, 31 * 24 * 3600000,
-	YEAR, 31556952000
-);
-/*jslint white: false*/
-/**
- * Path interpolation algorithm used across adapters
- */
-pathAnim = {
-	/**
-	 * Prepare start and end values so that the path can be animated one to one
-	 */
-	init: function (elem, fromD, toD) {
-		fromD = fromD || '';
-		var shift = elem.shift,
-			bezier = fromD.indexOf('C') > -1,
-			numParams = bezier ? 7 : 3,
-			endLength,
-			slice,
-			i,
-			start = fromD.split(' '),
-			end = [].concat(toD), // copy
-			startBaseLine,
-			endBaseLine,
-			sixify = function (arr) { // in splines make move points have six parameters like bezier curves
-				i = arr.length;
-				while (i--) {
-					if (arr[i] === M) {
-						arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]);
-					}
-				}
-			};
-
-		if (bezier) {
-			sixify(start);
-			sixify(end);
-		}
-
-		// pull out the base lines before padding
-		if (elem.isArea) {
-			startBaseLine = start.splice(start.length - 6, 6);
-			endBaseLine = end.splice(end.length - 6, 6);
-		}
-
-		// if shifting points, prepend a dummy point to the end path
-		if (shift <= end.length / numParams && start.length === end.length) {
-			while (shift--) {
-				end = [].concat(end).splice(0, numParams).concat(end);
-			}
-		}
-		elem.shift = 0; // reset for following animations
-
-		// copy and append last point until the length matches the end length
-		if (start.length) {
-			endLength = end.length;
-			while (start.length < endLength) {
-
-				//bezier && sixify(start);
-				slice = [].concat(start).splice(start.length - numParams, numParams);
-				if (bezier) { // disable first control point
-					slice[numParams - 6] = slice[numParams - 2];
-					slice[numParams - 5] = slice[numParams - 1];
-				}
-				start = start.concat(slice);
-			}
-		}
-
-		if (startBaseLine) { // append the base lines for areas
-			start = start.concat(startBaseLine);
-			end = end.concat(endBaseLine);
-		}
-		return [start, end];
-	},
-
-	/**
-	 * Interpolate each value of the path and return the array
-	 */
-	step: function (start, end, pos, complete) {
-		var ret = [],
-			i = start.length,
-			startVal;
-
-		if (pos === 1) { // land on the final path without adjustment points appended in the ends
-			ret = complete;
-
-		} else if (i === end.length && pos < 1) {
-			while (i--) {
-				startVal = parseFloat(start[i]);
-				ret[i] =
-					isNaN(startVal) ? // a letter instruction like M or L
-						start[i] :
-						pos * (parseFloat(end[i] - startVal)) + startVal;
-
-			}
-		} else { // if animation is finished or length not matching, land on right value
-			ret = end;
-		}
-		return ret;
-	}
-};
-
-(function ($) {
-	/**
-	 * The default HighchartsAdapter for jQuery
-	 */
-	win.HighchartsAdapter = win.HighchartsAdapter || ($ && {
-		
-		/**
-		 * Initialize the adapter by applying some extensions to jQuery
-		 */
-		init: function (pathAnim) {
-			
-			// extend the animate function to allow SVG animations
-			var Fx = $.fx,
-				Step = Fx.step,
-				dSetter,
-				Tween = $.Tween,
-				propHooks = Tween && Tween.propHooks,
-				opacityHook = $.cssHooks.opacity;
-			
-			/*jslint unparam: true*//* allow unused param x in this function */
-			$.extend($.easing, {
-				easeOutQuad: function (x, t, b, c, d) {
-					return -c * (t /= d) * (t - 2) + b;
-				}
-			});
-			/*jslint unparam: false*/
-		
-			// extend some methods to check for elem.attr, which means it is a Highcharts SVG object
-			$.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) {
-				var obj = Step,
-					base;
-					
-				// Handle different parent objects
-				if (fn === 'cur') {
-					obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype
-				
-				} else if (fn === '_default' && Tween) { // jQuery 1.8 model
-					obj = propHooks[fn];
-					fn = 'set';
-				}
-		
-				// Overwrite the method
-				base = obj[fn];
-				if (base) { // step.width and step.height don't exist in jQuery < 1.7
-		
-					// create the extended function replacement
-					obj[fn] = function (fx) {
-
-						var elem;
-						
-						// Fx.prototype.cur does not use fx argument
-						fx = i ? fx : this;
-
-						// Don't run animations on textual properties like align (#1821)
-						if (fx.prop === 'align') {
-							return;
-						}
-		
-						// shortcut
-						elem = fx.elem;
-		
-						// Fx.prototype.cur returns the current value. The other ones are setters
-						// and returning a value has no effect.
-						return elem.attr ? // is SVG element wrapper
-							elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method
-							base.apply(this, arguments); // use jQuery's built-in method
-					};
-				}
-			});
-
-			// Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+
-			wrap(opacityHook, 'get', function (proceed, elem, computed) {
-				return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed);
-			});
-			
-			
-			// Define the setter function for d (path definitions)
-			dSetter = function (fx) {
-				var elem = fx.elem,
-					ends;
-		
-				// Normally start and end should be set in state == 0, but sometimes,
-				// for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped
-				// in these cases
-				if (!fx.started) {
-					ends = pathAnim.init(elem, elem.d, elem.toD);
-					fx.start = ends[0];
-					fx.end = ends[1];
-					fx.started = true;
-				}
-		
-		
-				// interpolate each value of the path
-				elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD));
-			};
-			
-			// jQuery 1.8 style
-			if (Tween) {
-				propHooks.d = {
-					set: dSetter
-				};
-			// pre 1.8
-			} else {
-				// animate paths
-				Step.d = dSetter;
-			}
-			
-			/**
-			 * Utility for iterating over an array. Parameters are reversed compared to jQuery.
-			 * @param {Array} arr
-			 * @param {Function} fn
-			 */
-			this.each = Array.prototype.forEach ?
-				function (arr, fn) { // modern browsers
-					return Array.prototype.forEach.call(arr, fn);
-					
-				} : 
-				function (arr, fn) { // legacy
-					var i = 0, 
-						len = arr.length;
-					for (; i < len; i++) {
-						if (fn.call(arr[i], arr[i], i, arr) === false) {
-							return i;
-						}
-					}
-				};
-			
-			/**
-			 * Register Highcharts as a plugin in the respective framework
-			 */
-			$.fn.highcharts = function () {
-				var constr = 'Chart', // default constructor
-					args = arguments,
-					options,
-					ret,
-					chart;
-
-				if (this[0]) {
-
-					if (isString(args[0])) {
-						constr = args[0];
-						args = Array.prototype.slice.call(args, 1); 
-					}
-					options = args[0];
-
-					// Create the chart
-					if (options !== UNDEFINED) {
-						/*jslint unused:false*/
-						options.chart = options.chart || {};
-						options.chart.renderTo = this[0];
-						chart = new Highcharts[constr](options, args[1]);
-						ret = this;
-						/*jslint unused:true*/
-					}
-
-					// When called without parameters or with the return argument, get a predefined chart
-					if (options === UNDEFINED) {
-						ret = charts[attr(this[0], 'data-highcharts-chart')];
-					}
-				}
-				
-				return ret;
-			};
-
-		},
-
-		
-		/**
-		 * Downloads a script and executes a callback when done.
-		 * @param {String} scriptLocation
-		 * @param {Function} callback
-		 */
-		getScript: $.getScript,
-		
-		/**
-		 * Return the index of an item in an array, or -1 if not found
-		 */
-		inArray: $.inArray,
-		
-		/**
-		 * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method.
-		 * @param {Object} elem The HTML element
-		 * @param {String} method Which method to run on the wrapped element
-		 */
-		adapterRun: function (elem, method) {
-			return $(elem)[method]();
-		},
-	
-		/**
-		 * Filter an array
-		 */
-		grep: $.grep,
-	
-		/**
-		 * Map an array
-		 * @param {Array} arr
-		 * @param {Function} fn
-		 */
-		map: function (arr, fn) {
-			//return jQuery.map(arr, fn);
-			var results = [],
-				i = 0,
-				len = arr.length;
-			for (; i < len; i++) {
-				results[i] = fn.call(arr[i], arr[i], i, arr);
-			}
-			return results;
-	
-		},
-	
-		/**
-		 * Get the position of an element relative to the top left of the page
-		 */
-		offset: function (el) {
-			return $(el).offset();
-		},
-	
-		/**
-		 * Add an event listener
-		 * @param {Object} el A HTML element or custom object
-		 * @param {String} event The event type
-		 * @param {Function} fn The event handler
-		 */
-		addEvent: function (el, event, fn) {
-			$(el).bind(event, fn);
-		},
-	
-		/**
-		 * Remove event added with addEvent
-		 * @param {Object} el The object
-		 * @param {String} eventType The event type. Leave blank to remove all events.
-		 * @param {Function} handler The function to remove
-		 */
-		removeEvent: function (el, eventType, handler) {
-			// workaround for jQuery issue with unbinding custom events:
-			// http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2
-			var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent';
-			if (doc[func] && el && !el[func]) {
-				el[func] = function () {};
-			}
-	
-			$(el).unbind(eventType, handler);
-		},
-	
-		/**
-		 * Fire an event on a custom object
-		 * @param {Object} el
-		 * @param {String} type
-		 * @param {Object} eventArguments
-		 * @param {Function} defaultFunction
-		 */
-		fireEvent: function (el, type, eventArguments, defaultFunction) {
-			var event = $.Event(type),
-				detachedType = 'detached' + type,
-				defaultPrevented;
-	
-			// Remove warnings in Chrome when accessing returnValue (#2790), layerX and layerY. Although Highcharts
-			// never uses these properties, Chrome includes them in the default click event and
-			// raises the warning when they are copied over in the extend statement below.
-			//
-			// To avoid problems in IE (see #1010) where we cannot delete the properties and avoid
-			// testing if they are there (warning in chrome) the only option is to test if running IE.
-			if (!isIE && eventArguments) {
-				delete eventArguments.layerX;
-				delete eventArguments.layerY;
-				delete eventArguments.returnValue;
-			}
-	
-			extend(event, eventArguments);
-	
-			// Prevent jQuery from triggering the object method that is named the
-			// same as the event. For example, if the event is 'select', jQuery
-			// attempts calling el.select and it goes into a loop.
-			if (el[type]) {
-				el[detachedType] = el[type];
-				el[type] = null;
-			}
-	
-			// Wrap preventDefault and stopPropagation in try/catch blocks in
-			// order to prevent JS errors when cancelling events on non-DOM
-			// objects. #615.
-			/*jslint unparam: true*/
-			$.each(['preventDefault', 'stopPropagation'], function (i, fn) {
-				var base = event[fn];
-				event[fn] = function () {
-					try {
-						base.call(event);
-					} catch (e) {
-						if (fn === 'preventDefault') {
-							defaultPrevented = true;
-						}
-					}
-				};
-			});
-			/*jslint unparam: false*/
-	
-			// trigger it
-			$(el).trigger(event);
-	
-			// attach the method
-			if (el[detachedType]) {
-				el[type] = el[detachedType];
-				el[detachedType] = null;
-			}
-	
-			if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) {
-				defaultFunction(event);
-			}
-		},
-		
-		/**
-		 * Extension method needed for MooTools
-		 */
-		washMouseEvent: function (e) {
-			var ret = e.originalEvent || e;
-			
-			// computed by jQuery, needed by IE8
-			if (ret.pageX === UNDEFINED) { // #1236
-				ret.pageX = e.pageX;
-				ret.pageY = e.pageY;
-			}
-			
-			return ret;
-		},
-	
-		/**
-		 * Animate a HTML element or SVG element wrapper
-		 * @param {Object} el
-		 * @param {Object} params
-		 * @param {Object} options jQuery-like animation options: duration, easing, callback
-		 */
-		animate: function (el, params, options) {
-			var $el = $(el);
-			if (!el.style) {
-				el.style = {}; // #1881
-			}
-			if (params.d) {
-				el.toD = params.d; // keep the array form for paths, used in $.fx.step.d
-				params.d = 1; // because in jQuery, animating to an array has a different meaning
-			}
-	
-			$el.stop();
-			if (params.opacity !== UNDEFINED && el.attr) {
-				params.opacity += 'px'; // force jQuery to use same logic as width and height (#2161)
-			}
-			$el.animate(params, options);
-	
-		},
-		/**
-		 * Stop running animation
-		 */
-		stop: function (el) {
-			$(el).stop();
-		}
-	});
-}(win.jQuery));
-
-
-// check for a custom HighchartsAdapter defined prior to this file
-var globalAdapter = win.HighchartsAdapter,
-	adapter = globalAdapter || {};
-	
-// Initialize the adapter
-if (globalAdapter) {
-	globalAdapter.init.call(globalAdapter, pathAnim);
-}
-
-
-// Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object
-// and all the utility functions will be null. In that case they are populated by the
-// default adapters below.
-var adapterRun = adapter.adapterRun,
-	getScript = adapter.getScript,
-	inArray = adapter.inArray,
-	each = adapter.each,
-	grep = adapter.grep,
-	offset = adapter.offset,
-	map = adapter.map,
-	addEvent = adapter.addEvent,
-	removeEvent = adapter.removeEvent,
-	fireEvent = adapter.fireEvent,
-	washMouseEvent = adapter.washMouseEvent,
-	animate = adapter.animate,
-	stop = adapter.stop;
-
-
-
-/* ****************************************************************************
- * Handle the options                                                         *
- *****************************************************************************/
-var
-
-defaultLabelOptions = {
-	enabled: true,
-	// rotation: 0,
-	// align: 'center',
-	x: 0,
-	y: 15,
-	/*formatter: function () {
-		return this.value;
-	},*/
-	style: {
-		color: '#606060',
-		cursor: 'default',
-		fontSize: '11px'
-	}
-};
-
-defaultOptions = {
-	colors: ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c', 
-		    '#8085e9', '#f15c80', '#e4d354', '#8085e8', '#8d4653', '#91e8e1'], // docs
-	symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
-	lang: {
-		loading: 'Loading...',
-		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
-				'August', 'September', 'October', 'November', 'December'],
-		shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
-		weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
-		decimalPoint: '.',
-		numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels
-		resetZoom: 'Reset zoom',
-		resetZoomTitle: 'Reset zoom level 1:1',
-		thousandsSep: ','
-	},
-	global: {
-		useUTC: true,
-		//timezoneOffset: 0,
-		canvasToolsURL: 'http://code.highcharts.com/4.0.1/modules/canvas-tools.js',
-		VMLRadialGradientURL: 'http://code.highcharts.com/4.0.1/gfx/vml-radial-gradient.png'
-	},
-	chart: {
-		//animation: true,
-		//alignTicks: false,
-		//reflow: true,
-		//className: null,
-		//events: { load, selection },
-		//margin: [null],
-		//marginTop: null,
-		//marginRight: null,
-		//marginBottom: null,
-		//marginLeft: null,
-		borderColor: '#4572A7',
-		//borderWidth: 0,
-		borderRadius: 0,
-		defaultSeriesType: 'line',
-		ignoreHiddenSeries: true,
-		//inverted: false,
-		//shadow: false,
-		spacing: [10, 10, 15, 10],
-		//spacingTop: 10,
-		//spacingRight: 10,
-		//spacingBottom: 15,
-		//spacingLeft: 10,
-		//style: {
-		//	fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font
-		//	fontSize: '12px'
-		//},
-		backgroundColor: '#FFFFFF',
-		//plotBackgroundColor: null,
-		plotBorderColor: '#C0C0C0',
-		//plotBorderWidth: 0,
-		//plotShadow: false,
-		//zoomType: ''
-		resetZoomButton: {
-			theme: {
-				zIndex: 20
-			},
-			position: {
-				align: 'right',
-				x: -10,
-				//verticalAlign: 'top',
-				y: 10
-			}
-			// relativeTo: 'plot'
-		}
-	},
-	title: {
-		text: 'Chart title',
-		align: 'center',
-		// floating: false,
-		margin: 15,
-		// x: 0,
-		// verticalAlign: 'top',
-		// y: null,
-		style: {
-			color: '#333333', // docs
-			fontSize: '18px'
-		}
-
-	},
-	subtitle: {
-		text: '',
-		align: 'center',
-		// floating: false
-		// x: 0,
-		// verticalAlign: 'top',
-		// y: null,
-		style: {
-			color: '#555555' // docs
-		}
-	},
-
-	plotOptions: {
-		line: { // base series options
-			allowPointSelect: false,
-			showCheckbox: false,
-			animation: {
-				duration: 1000
-			},
-			//connectNulls: false,
-			//cursor: 'default',
-			//clip: true,
-			//dashStyle: null,
-			//enableMouseTracking: true,
-			events: {},
-			//legendIndex: 0,
-			//linecap: 'round',
-			lineWidth: 2,
-			//shadow: false,
-			// stacking: null,
-			marker: {
-				//enabled: true,
-				//symbol: null,
-				lineWidth: 0,
-				radius: 4,
-				lineColor: '#FFFFFF',
-				//fillColor: null,
-				states: { // states for a single point
-					hover: {
-						enabled: true
-						//radius: base + 2
-					},
-					select: {
-						fillColor: '#FFFFFF',
-						lineColor: '#000000',
-						lineWidth: 2
-					}
-				}
-			},
-			point: {
-				events: {}
-			},
-			dataLabels: merge(defaultLabelOptions, {
-				align: 'center',
-				//defer: true,
-				enabled: false,
-				formatter: function () {
-					return this.y === null ? '' : numberFormat(this.y, -1);
-				},
-				verticalAlign: 'bottom', // above singular point
-				y: 0
-				// backgroundColor: undefined,
-				// borderColor: undefined,
-				// borderRadius: undefined,
-				// borderWidth: undefined,
-				// padding: 3,
-				// shadow: false
-			}),
-			cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
-			pointRange: 0,
-			//pointStart: 0,
-			//pointInterval: 1,
-			//showInLegend: null, // auto: true for standalone series, false for linked series
-			states: { // states for the entire series
-				hover: {
-					//enabled: false,
-					//lineWidth: base + 1,
-					marker: {
-						// lineWidth: base + 1,
-						// radius: base + 1
-					},
-					halo: {
-						size: 10,
-						opacity: 0.25
-					}
-				},
-				select: {
-					marker: {}
-				}
-			},
-			stickyTracking: true,
-			//tooltip: {
-				//pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.y}</b>'
-				//valueDecimals: null,
-				//xDateFormat: '%A, %b %e, %Y',
-				//valuePrefix: '',
-				//ySuffix: ''				
-			//}
-			turboThreshold: 1000
-			// zIndex: null
-		}
-	},
-	labels: {
-		//items: [],
-		style: {
-			//font: defaultFont,
-			position: ABSOLUTE,
-			color: '#3E576F'
-		}
-	},
-	legend: {
-		enabled: true,
-		align: 'center',
-		//floating: false,
-		layout: 'horizontal',
-		labelFormatter: function () {
-			return this.name;
-		},
-		//borderWidth: 0,
-		borderColor: '#909090',
-		borderRadius: 0, // docs
-		navigation: {
-			// animation: true,
-			activeColor: '#274b6d',
-			// arrowSize: 12
-			inactiveColor: '#CCC'
-			// style: {} // text styles
-		},
-		// margin: 20,
-		// reversed: false,
-		shadow: false,
-		// backgroundColor: null,
-		/*style: {
-			padding: '5px'
-		},*/
-		itemStyle: {			
-			color: '#333333', // docs
-			fontSize: '12px',
-			fontWeight: 'bold' // docs
-		},
-		itemHoverStyle: {
-			//cursor: 'pointer', removed as of #601
-			color: '#000'
-		},
-		itemHiddenStyle: {
-			color: '#CCC'
-		},
-		itemCheckboxStyle: {
-			position: ABSOLUTE,
-			width: '13px', // for IE precision
-			height: '13px'
-		},
-		// itemWidth: undefined,
-		// symbolRadius: 0,
-		// symbolWidth: 16,
-		symbolPadding: 5,
-		verticalAlign: 'bottom',
-		// width: undefined,
-		x: 0,
-		y: 0,
-		title: {
-			//text: null,
-			style: {
-				fontWeight: 'bold'
-			}
-		}			
-	},
-
-	loading: {
-		// hideDuration: 100,
-		labelStyle: {
-			fontWeight: 'bold',
-			position: RELATIVE,
-			top: '1em'
-		},
-		// showDuration: 0,
-		style: {
-			position: ABSOLUTE,
-			backgroundColor: 'white',
-			opacity: 0.5,
-			textAlign: 'center'
-		}
-	},
-
-	tooltip: {
-		enabled: true,
-		animation: hasSVG,
-		//crosshairs: null,
-		backgroundColor: 'rgba(249, 249, 249, .85)',
-		borderWidth: 1,
-		borderRadius: 3,
-		dateTimeLabelFormats: { 
-			millisecond: '%A, %b %e, %H:%M:%S.%L',
-			second: '%A, %b %e, %H:%M:%S',
-			minute: '%A, %b %e, %H:%M',
-			hour: '%A, %b %e, %H:%M',
-			day: '%A, %b %e, %Y',
-			week: 'Week from %A, %b %e, %Y',
-			month: '%B %Y',
-			year: '%Y'
-		},
-		//formatter: defaultFormatter,
-		headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
-		pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>', // docs
-		shadow: true,
-		//shape: 'calout',
-		//shared: false,
-		snap: isTouchDevice ? 25 : 10,
-		style: {
-			color: '#333333',
-			cursor: 'default',
-			fontSize: '12px',
-			padding: '8px',
-			whiteSpace: 'nowrap'
-		}
-		//xDateFormat: '%A, %b %e, %Y',
-		//valueDecimals: null,
-		//valuePrefix: '',
-		//valueSuffix: ''
-	},
-
-	credits: {
-		enabled: true,
-		text: 'Highcharts.com',
-		href: 'http://www.highcharts.com',
-		position: {
-			align: 'right',
-			x: -10,
-			verticalAlign: 'bottom',
-			y: -5
-		},
-		style: {
-			cursor: 'pointer',
-			color: '#909090',
-			fontSize: '9px'
-		}
-	}
-};
-
-
-
-
-// Series defaults
-var defaultPlotOptions = defaultOptions.plotOptions,
-	defaultSeriesOptions = defaultPlotOptions.line;
-
-// set the default time methods
-setTimeMethods();
-
-
-
-/**
- * Set the time methods globally based on the useUTC option. Time method can be either
- * local time or UTC (default).
- */
-function setTimeMethods() {
-	var useUTC = defaultOptions.global.useUTC,
-		GET = useUTC ? 'getUTC' : 'get',
-		SET = useUTC ? 'setUTC' : 'set';
-
-
-	timezoneOffset = ((useUTC && defaultOptions.global.timezoneOffset) || 0) * 60000;
-	makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) {
-		return new Date(
-			year,
-			month,
-			pick(date, 1),
-			pick(hours, 0),
-			pick(minutes, 0),
-			pick(seconds, 0)
-		).getTime();
-	};
-	getMinutes =  GET + 'Minutes';
-	getHours =    GET + 'Hours';
-	getDay =      GET + 'Day';
-	getDate =     GET + 'Date';
-	getMonth =    GET + 'Month';
-	getFullYear = GET + 'FullYear';
-	setMinutes =  SET + 'Minutes';
-	setHours =    SET + 'Hours';
-	setDate =     SET + 'Date';
-	setMonth =    SET + 'Month';
-	setFullYear = SET + 'FullYear';
-
-}
-
-/**
- * Merge the default options with custom options and return the new options structure
- * @param {Object} options The new custom options
- */
-function setOptions(options) {
-	
-	// Copy in the default options
-	defaultOptions = merge(true, defaultOptions, options);
-	
-	// Apply UTC
-	setTimeMethods();
-
-	return defaultOptions;
-}
-
-/**
- * Get the updated default options. Until 3.0.7, merely exposing defaultOptions for outside modules
- * wasn't enough because the setOptions method created a new object.
- */
-function getOptions() {
-	return defaultOptions;
-}
-
-
-/**
- * Handle color operations. The object methods are chainable.
- * @param {String} input The input color in either rbga or hex format
- */
-var rgbaRegEx = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,
-	hexRegEx = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
-	rgbRegEx = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/;
-
-var Color = function (input) {
-	// declare variables
-	var rgba = [], result, stops;
-
-	/**
-	 * Parse the input color to rgba array
-	 * @param {String} input
-	 */
-	function init(input) {
-
-		// Gradients
-		if (input && input.stops) {
-			stops = map(input.stops, function (stop) {
-				return Color(stop[1]);
-			});
-
-		// Solid colors
-		} else {
-			// rgba
-			result = rgbaRegEx.exec(input);
-			if (result) {
-				rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)];
-			} else { 
-				// hex
-				result = hexRegEx.exec(input);
-				if (result) {
-					rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1];
-				} else {
-					// rgb
-					result = rgbRegEx.exec(input);
-					if (result) {
-						rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
-					}
-				}
-			}
-		}		
-
-	}
-	/**
-	 * Return the color a specified format
-	 * @param {String} format
-	 */
-	function get(format) {
-		var ret;
-
-		if (stops) {
-			ret = merge(input);
-			ret.stops = [].concat(ret.stops);
-			each(stops, function (stop, i) {
-				ret.stops[i] = [ret.stops[i][0], stop.get(format)];
-			});
-
-		// it's NaN if gradient colors on a column chart
-		} else if (rgba && !isNaN(rgba[0])) {
-			if (format === 'rgb') {
-				ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
-			} else if (format === 'a') {
-				ret = rgba[3];
-			} else {
-				ret = 'rgba(' + rgba.join(',') + ')';
-			}
-		} else {
-			ret = input;
-		}
-		return ret;
-	}
-
-	/**
-	 * Brighten the color
-	 * @param {Number} alpha
-	 */
-	function brighten(alpha) {
-		if (stops) {
-			each(stops, function (stop) {
-				stop.brighten(alpha);
-			});
-		
-		} else if (isNumber(alpha) && alpha !== 0) {
-			var i;
-			for (i = 0; i < 3; i++) {
-				rgba[i] += pInt(alpha * 255);
-
-				if (rgba[i] < 0) {
-					rgba[i] = 0;
-				}
-				if (rgba[i] > 255) {
-					rgba[i] = 255;
-				}
-			}
-		}
-		return this;
-	}
-	/**
-	 * Set the color's opacity to a given alpha value
-	 * @param {Number} alpha
-	 */
-	function setOpacity(alpha) {
-		rgba[3] = alpha;
-		return this;
-	}
-
-	// initialize: parse the input
-	init(input);
-
-	// public methods
-	return {
-		get: get,
-		brighten: brighten,
-		rgba: rgba,
-		setOpacity: setOpacity
-	};
-};
-
-
-/**
- * A wrapper object for SVG elements
- */
-function SVGElement() {}
-
-SVGElement.prototype = {
-	/**
-	 * Initialize the SVG renderer
-	 * @param {Object} renderer
-	 * @param {String} nodeName
-	 */
-	init: function (renderer, nodeName) {
-		var wrapper = this;
-		wrapper.element = nodeName === 'span' ?
-			createElement(nodeName) :
-			doc.createElementNS(SVG_NS, nodeName);
-		wrapper.renderer = renderer;
-	},
-	/**
-	 * Default base for animation
-	 */
-	opacity: 1,
-	/**
-	 * Animate a given attribute
-	 * @param {Object} params
-	 * @param {Number} options The same options as in jQuery animation
-	 * @param {Function} complete Function to perform at the end of animation
-	 */
-	animate: function (params, options, complete) {
-		var animOptions = pick(options, globalAnimation, true);
-		stop(this); // stop regardless of animation actually running, or reverting to .attr (#607)
-		if (animOptions) {
-			animOptions = merge(animOptions, {}); //#2625
-			if (complete) { // allows using a callback with the global animation without overwriting it
-				animOptions.complete = complete;
-			}
-			animate(this, params, animOptions);
-		} else {
-			this.attr(params);
-			if (complete) {
-				complete();
-			}
-		}
-	},
-
-	/**
-	 * Build an SVG gradient out of a common JavaScript configuration object
-	 */
-	colorGradient: function (color, prop, elem) {
-		var renderer = this.renderer,
-			colorObject,
-			gradName,
-			gradAttr,
-			gradients,
-			gradientObject,
-			stops,
-			stopColor,
-			stopOpacity,
-			radialReference,
-			n,
-			id,
-			key = [];
-
-		// Apply linear or radial gradients
-		if (color.linearGradient) {
-			gradName = 'linearGradient';
-		} else if (color.radialGradient) {
-			gradName = 'radialGradient';
-		}
-
-		if (gradName) {
-			gradAttr = color[gradName];
-			gradients = renderer.gradients;
-			stops = color.stops;
-			radialReference = elem.radialReference;
-
-			// Keep < 2.2 kompatibility
-			if (isArray(gradAttr)) {
-				color[gradName] = gradAttr = {
-					x1: gradAttr[0],
-					y1: gradAttr[1],
-					x2: gradAttr[2],
-					y2: gradAttr[3],
-					gradientUnits: 'userSpaceOnUse'
-				};
-			}
-
-			// Correct the radial gradient for the radial reference system
-			if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) {
-				gradAttr = merge(gradAttr, {
-					cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2],
-					cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2],
-					r: gradAttr.r * radialReference[2],
-					gradientUnits: 'userSpaceOnUse'
-				});
-			}
-
-			// Build the unique key to detect whether we need to create a new element (#1282)
-			for (n in gradAttr) {
-				if (n !== 'id') {
-					key.push(n, gradAttr[n]);
-				}
-			}
-			for (n in stops) {
-				key.push(stops[n]);
-			}
-			key = key.join(',');
-
-			// Check if a gradient object with the same config object is created within this renderer
-			if (gradients[key]) {
-				id = gradients[key].attr('id');
-
-			} else {
-
-				// Set the id and create the element
-				gradAttr.id = id = PREFIX + idCounter++;
-				gradients[key] = gradientObject = renderer.createElement(gradName)
-					.attr(gradAttr)
-					.add(renderer.defs);
-
-
-				// The gradient needs to keep a list of stops to be able to destroy them
-				gradientObject.stops = [];
-				each(stops, function (stop) {
-					var stopObject;
-					if (stop[1].indexOf('rgba') === 0) {
-						colorObject = Color(stop[1]);
-						stopColor = colorObject.get('rgb');
-						stopOpacity = colorObject.get('a');
-					} else {
-						stopColor = stop[1];
-						stopOpacity = 1;
-					}
-					stopObject = renderer.createElement('stop').attr({
-						offset: stop[0],
-						'stop-color': stopColor,
-						'stop-opacity': stopOpacity
-					}).add(gradientObject);
-
-					// Add the stop element to the gradient
-					gradientObject.stops.push(stopObject);
-				});
-			}
-
-			// Set the reference to the gradient object
-			elem.setAttribute(prop, 'url(' + renderer.url + '#' + id + ')');
-		} 
-	},
-
-	/**
-	 * Set or get a given attribute
-	 * @param {Object|String} hash
-	 * @param {Mixed|Undefined} val
-	 */
-	attr: function (hash, val) {
-		var key,
-			value,
-			element = this.element,
-			hasSetSymbolSize,
-			ret = this,
-			skipAttr;
-
-		// single key-value pair
-		if (typeof hash === 'string' && val !== UNDEFINED) {
-			key = hash;
-			hash = {};
-			hash[key] = val;
-		}
-
-		// used as a getter: first argument is a string, second is undefined
-		if (typeof hash === 'string') {
-			ret = (this[hash + 'Getter'] || this._defaultGetter).call(this, hash, element);
-		
-		// setter
-		} else {
-
-			for (key in hash) {
-				value = hash[key];
-				skipAttr = false;
-
-
-
-				if (this.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
-					if (!hasSetSymbolSize) {
-						this.symbolAttr(hash);
-						hasSetSymbolSize = true;
-					}
-					skipAttr = true;
-				}
-
-				if (this.rotation && (key === 'x' || key === 'y')) {
-					this.doTransform = true;
-				}
-				
-				if (!skipAttr) {
-					(this[key + 'Setter'] || this._defaultSetter).call(this, value, key, element);
-				}
-
-				// Let the shadow follow the main element
-				if (this.shadows && /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(key)) {
-					this.updateShadows(key, value);
-				}
-			}
-
-			// Update transform. Do this outside the loop to prevent redundant updating for batch setting
-			// of attributes.
-			if (this.doTransform) {
-				this.updateTransform();
-				this.doTransform = false;
-			}
-
-		}
-
-		return ret;
-	},
-
-	updateShadows: function (key, value) {
-		var shadows = this.shadows,
-			i = shadows.length;
-		while (i--) {
-			shadows[i].setAttribute(
-				key,
-				key === 'height' ?
-					mathMax(value - (shadows[i].cutHeight || 0), 0) :
-					key === 'd' ? this.d : value
-			);
-		}
-	},
-
-	/**
-	 * Add a class name to an element
-	 */
-	addClass: function (className) {
-		var element = this.element,
-			currentClassName = attr(element, 'class') || '';
-
-		if (currentClassName.indexOf(className) === -1) {
-			attr(element, 'class', currentClassName + ' ' + className);
-		}
-		return this;
-	},
-	/* hasClass and removeClass are not (yet) needed
-	hasClass: function (className) {
-		return attr(this.element, 'class').indexOf(className) !== -1;
-	},
-	removeClass: function (className) {
-		attr(this.element, 'class', attr(this.element, 'class').replace(className, ''));
-		return this;
-	},
-	*/
-
-	/**
-	 * If one of the symbol size affecting parameters are changed,
-	 * check all the others only once for each call to an element's
-	 * .attr() method
-	 * @param {Object} hash
-	 */
-	symbolAttr: function (hash) {
-		var wrapper = this;
-
-		each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) {
-			wrapper[key] = pick(hash[key], wrapper[key]);
-		});
-
-		wrapper.attr({
-			d: wrapper.renderer.symbols[wrapper.symbolName](
-				wrapper.x,
-				wrapper.y,
-				wrapper.width,
-				wrapper.height,
-				wrapper
-			)
-		});
-	},
-
-	/**
-	 * Apply a clipping path to this object
-	 * @param {String} id
-	 */
-	clip: function (clipRect) {
-		return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE);
-	},
-
-	/**
-	 * Calculate the coordinates needed for drawing a rectangle crisply and return the
-	 * calculated attributes
-	 * @param {Number} strokeWidth
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	crisp: function (rect) {
-
-		var wrapper = this,
-			key,
-			attribs = {},
-			normalizer,
-			strokeWidth = rect.strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
-
-		normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
-
-		// normalize for crisp edges
-		rect.x = mathFloor(rect.x || wrapper.x || 0) + normalizer;
-		rect.y = mathFloor(rect.y || wrapper.y || 0) + normalizer;
-		rect.width = mathFloor((rect.width || wrapper.width || 0) - 2 * normalizer);
-		rect.height = mathFloor((rect.height || wrapper.height || 0) - 2 * normalizer);
-		rect.strokeWidth = strokeWidth;
-
-		for (key in rect) {
-			if (wrapper[key] !== rect[key]) { // only set attribute if changed
-				wrapper[key] = attribs[key] = rect[key];
-			}
-		}
-
-		return attribs;
-	},
-
-	/**
-	 * Set styles for the element
-	 * @param {Object} styles
-	 */
-	css: function (styles) {
-		var elemWrapper = this,
-			oldStyles = elemWrapper.styles,
-			newStyles = {},
-			elem = elemWrapper.element,
-			textWidth,
-			n,
-			serializedCss = '',
-			hyphenate,
-			hasNew = !oldStyles;
-
-		// convert legacy
-		if (styles && styles.color) {
-			styles.fill = styles.color;
-		}
-
-		// Filter out existing styles to increase performance (#2640)
-		if (oldStyles) {
-			for (n in styles) {
-				if (styles[n] !== oldStyles[n]) {
-					newStyles[n] = styles[n];
-					hasNew = true;
-				}
-			}
-		}
-		if (hasNew) {
-			textWidth = elemWrapper.textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text' && pInt(styles.width);
-
-			// Merge the new styles with the old ones
-			if (oldStyles) {
-				styles = extend(
-					oldStyles,
-					newStyles
-				);
-			}		
-
-			// store object
-			elemWrapper.styles = styles;
-
-			if (textWidth && (useCanVG || (!hasSVG && elemWrapper.renderer.forExport))) {
-				delete styles.width;
-			}
-
-			// serialize and set style attribute
-			if (isIE && !hasSVG) {
-				css(elemWrapper.element, styles);
-			} else {
-				/*jslint unparam: true*/
-				hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
-				/*jslint unparam: false*/
-				for (n in styles) {
-					serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';';
-				}
-				attr(elem, 'style', serializedCss); // #1881
-			}
-
-
-			// re-build text
-			if (textWidth && elemWrapper.added) {
-				elemWrapper.renderer.buildText(elemWrapper);
-			}
-		}
-
-		return elemWrapper;
-	},
-
-	/**
-	 * Add an event listener
-	 * @param {String} eventType
-	 * @param {Function} handler
-	 */
-	on: function (eventType, handler) {
-		var svgElement = this,
-			element = svgElement.element;
-		
-		// touch
-		if (hasTouch && eventType === 'click') {
-			element.ontouchstart = function (e) {			
-				svgElement.touchEventFired = Date.now();				
-				e.preventDefault();
-				handler.call(element, e);
-			};
-			element.onclick = function (e) {												
-				if (userAgent.indexOf('Android') === -1 || Date.now() - (svgElement.touchEventFired || 0) > 1100) { // #2269
-					handler.call(element, e);
-				}
-			};			
-		} else {
-			// simplest possible event model for internal use
-			element['on' + eventType] = handler;
-		}
-		return this;
-	},
-
-	/**
-	 * Set the coordinates needed to draw a consistent radial gradient across
-	 * pie slices regardless of positioning inside the chart. The format is
-	 * [centerX, centerY, diameter] in pixels.
-	 */
-	setRadialReference: function (coordinates) {
-		this.element.radialReference = coordinates;
-		return this;
-	},
-
-	/**
-	 * Move an object and its children by x and y values
-	 * @param {Number} x
-	 * @param {Number} y
-	 */
-	translate: function (x, y) {
-		return this.attr({
-			translateX: x,
-			translateY: y
-		});
-	},
-
-	/**
-	 * Invert a group, rotate and flip
-	 */
-	invert: function () {
-		var wrapper = this;
-		wrapper.inverted = true;
-		wrapper.updateTransform();
-		return wrapper;
-	},
-
-	/**
-	 * Private method to update the transform attribute based on internal
-	 * properties
-	 */
-	updateTransform: function () {
-		var wrapper = this,
-			translateX = wrapper.translateX || 0,
-			translateY = wrapper.translateY || 0,
-			scaleX = wrapper.scaleX,
-			scaleY = wrapper.scaleY,
-			inverted = wrapper.inverted,
-			rotation = wrapper.rotation,
-			element = wrapper.element,
-			transform;
-
-		// flipping affects translate as adjustment for flipping around the group's axis
-		if (inverted) {
-			translateX += wrapper.attr('width');
-			translateY += wrapper.attr('height');
-		}
-
-		// Apply translate. Nearly all transformed elements have translation, so instead
-		// of checking for translate = 0, do it always (#1767, #1846).
-		transform = ['translate(' + translateX + ',' + translateY + ')'];
-
-		// apply rotation
-		if (inverted) {
-			transform.push('rotate(90) scale(-1,1)');
-		} else if (rotation) { // text rotation
-			transform.push('rotate(' + rotation + ' ' + (element.getAttribute('x') || 0) + ' ' + (element.getAttribute('y') || 0) + ')');
-		}
-
-		// apply scale
-		if (defined(scaleX) || defined(scaleY)) {
-			transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
-		}
-
-		if (transform.length) {
-			element.setAttribute('transform', transform.join(' '));
-		}
-	},
-	/**
-	 * Bring the element to the front
-	 */
-	toFront: function () {
-		var element = this.element;
-		element.parentNode.appendChild(element);
-		return this;
-	},
-
-
-	/**
-	 * Break down alignment options like align, verticalAlign, x and y
-	 * to x and y relative to the chart.
-	 *
-	 * @param {Object} alignOptions
-	 * @param {Boolean} alignByTranslate
-	 * @param {String[Object} box The box to align to, needs a width and height. When the
-	 *        box is a string, it refers to an object in the Renderer. For example, when
-	 *        box is 'spacingBox', it refers to Renderer.spacingBox which holds width, height
-	 *        x and y properties.
-	 *
-	 */
-	align: function (alignOptions, alignByTranslate, box) {
-		var align,
-			vAlign,
-			x,
-			y,
-			attribs = {},
-			alignTo,
-			renderer = this.renderer,
-			alignedObjects = renderer.alignedObjects;
-
-		// First call on instanciate
-		if (alignOptions) {
-			this.alignOptions = alignOptions;
-			this.alignByTranslate = alignByTranslate;
-			if (!box || isString(box)) { // boxes other than renderer handle this internally
-				this.alignTo = alignTo = box || 'renderer';
-				erase(alignedObjects, this); // prevent duplicates, like legendGroup after resize
-				alignedObjects.push(this);
-				box = null; // reassign it below
-			}
-
-		// When called on resize, no arguments are supplied
-		} else {
-			alignOptions = this.alignOptions;
-			alignByTranslate = this.alignByTranslate;
-			alignTo = this.alignTo;
-		}
-
-		box = pick(box, renderer[alignTo], renderer);
-
-		// Assign variables
-		align = alignOptions.align;
-		vAlign = alignOptions.verticalAlign;
-		x = (box.x || 0) + (alignOptions.x || 0); // default: left align
-		y = (box.y || 0) + (alignOptions.y || 0); // default: top align
-
-		// Align
-		if (align === 'right' || align === 'center') {
-			x += (box.width - (alignOptions.width || 0)) /
-					{ right: 1, center: 2 }[align];
-		}
-		attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x);
-
-
-		// Vertical align
-		if (vAlign === 'bottom' || vAlign === 'middle') {
-			y += (box.height - (alignOptions.height || 0)) /
-					({ bottom: 1, middle: 2 }[vAlign] || 1);
-
-		}
-		attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y);
-
-		// Animate only if already placed
-		this[this.placed ? 'animate' : 'attr'](attribs);
-		this.placed = true;
-		this.alignAttr = attribs;
-
-		return this;
-	},
-
-	/**
-	 * Get the bounding box (width, height, x and y) for the element
-	 */
-	getBBox: function () {
-		var wrapper = this,
-			bBox = wrapper.bBox,
-			renderer = wrapper.renderer,
-			width,
-			height,
-			rotation = wrapper.rotation,
-			element = wrapper.element,
-			styles = wrapper.styles,
-			rad = rotation * deg2rad,
-			textStr = wrapper.textStr,
-			cacheKey;
-
-		// Since numbers are monospaced, and numerical labels appear a lot in a chart,
-		// we assume that a label of n characters has the same bounding box as others 
-		// of the same length.
-		if (textStr === '' || numRegex.test(textStr)) {
-			cacheKey = 'num.' + textStr.toString().length + (styles ? ('|' + styles.fontSize + '|' + styles.fontFamily) : '');
-
-		} //else { // This code block made demo/waterfall fail, related to buildText
-			// Caching all strings reduces rendering time by 4-5%. 
-			// TODO: Check how this affects places where bBox is found on the element
-			//cacheKey = textStr + (styles ? ('|' + styles.fontSize + '|' + styles.fontFamily) : '');
-		//}
-		if (cacheKey) {
-			bBox = renderer.cache[cacheKey];
-		}
-
-		// No cache found
-		if (!bBox) {
-
-			// SVG elements
-			if (element.namespaceURI === SVG_NS || renderer.forExport) {
-				try { // Fails in Firefox if the container has display: none.
-
-					bBox = element.getBBox ?
-						// SVG: use extend because IE9 is not allowed to change width and height in case
-						// of rotation (below)
-						extend({}, element.getBBox()) :
-						// Canvas renderer and legacy IE in export mode
-						{
-							width: element.offsetWidth,
-							height: element.offsetHeight
-						};
-				} catch (e) {}
-
-				// If the bBox is not set, the try-catch block above failed. The other condition
-				// is for Opera that returns a width of -Infinity on hidden elements.
-				if (!bBox || bBox.width < 0) {
-					bBox = { width: 0, height: 0 };
-				}
-
-
-			// VML Renderer or useHTML within SVG
-			} else {
-
-				bBox = wrapper.htmlGetBBox();
-
-			}
-
-			// True SVG elements as well as HTML elements in modern browsers using the .useHTML option
-			// need to compensated for rotation
-			if (renderer.isSVG) {
-				width = bBox.width;
-				height = bBox.height;
-
-				// Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669, #2568)
-				if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '16.9') {
-					bBox.height = height = 14;
-				}
-
-				// Adjust for rotated text
-				if (rotation) {
-					bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad));
-					bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad));
-				}
-			}
-
-			// Cache it
-			wrapper.bBox = bBox;
-			if (cacheKey) {
-				renderer.cache[cacheKey] = bBox;
-			}
-		}
-		return bBox;
-	},
-
-	/**
-	 * Show the element
-	 */
-	show: function (inherit) {
-		// IE9-11 doesn't handle visibilty:inherit well, so we remove the attribute instead (#2881)
-		if (inherit && this.element.namespaceURI === SVG_NS) {
-			this.element.removeAttribute('visibility');
-			return this;
-		} else {
-			return this.attr({ visibility: inherit ? 'inherit' : VISIBLE });
-		}
-	},
-
-	/**
-	 * Hide the element
-	 */
-	hide: function () {
-		return this.attr({ visibility: HIDDEN });
-	},
-
-	fadeOut: function (duration) {
-		var elemWrapper = this;
-		elemWrapper.animate({
-			opacity: 0
-		}, {
-			duration: duration || 150,
-			complete: function () {
-				elemWrapper.hide();
-			}
-		});
-	},
-
-	/**
-	 * Add the element
-	 * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined
-	 *    to append the element to the renderer.box.
-	 */
-	add: function (parent) {
-
-		var renderer = this.renderer,
-			parentWrapper = parent || renderer,
-			parentNode = parentWrapper.element || renderer.box,
-			childNodes,
-			element = this.element,
-			zIndex = this.zIndex,
-			otherElement,
-			otherZIndex,
-			i,
-			inserted;
-
-		if (parent) {
-			this.parentGroup = parent;
-		}
-
-		// mark as inverted
-		this.parentInverted = parent && parent.inverted;
-
-		// build formatted text
-		if (this.textStr !== undefined) {
-			renderer.buildText(this);
-		}
-
-		// mark the container as having z indexed children
-		if (zIndex) {
-			parentWrapper.handleZ = true;
-			zIndex = pInt(zIndex);
-		}
-
-		// insert according to this and other elements' zIndex
-		if (parentWrapper.handleZ) { // this element or any of its siblings has a z index
-			childNodes = parentNode.childNodes;
-			for (i = 0; i < childNodes.length; i++) {
-				otherElement = childNodes[i];
-				otherZIndex = attr(otherElement, 'zIndex');
-				if (otherElement !== element && (
-						// insert before the first element with a higher zIndex
-						pInt(otherZIndex) > zIndex ||
-						// if no zIndex given, insert before the first element with a zIndex
-						(!defined(zIndex) && defined(otherZIndex))
-
-						)) {
-					parentNode.insertBefore(element, otherElement);
-					inserted = true;
-					break;
-				}
-			}
-		}
-
-		// default: append at the end
-		if (!inserted) {
-			parentNode.appendChild(element);
-		}
-
-		// mark as added
-		this.added = true;
-
-		// fire an event for internal hooks
-		if (this.onAdd) {
-			this.onAdd();
-		}
-
-		return this;
-	},
-
-	/**
-	 * Removes a child either by removeChild or move to garbageBin.
-	 * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
-	 */
-	safeRemoveChild: function (element) {
-		var parentNode = element.parentNode;
-		if (parentNode) {
-			parentNode.removeChild(element);
-		}
-	},
-
-	/**
-	 * Destroy the element and element wrapper
-	 */
-	destroy: function () {
-		var wrapper = this,
-			element = wrapper.element || {},
-			shadows = wrapper.shadows,
-			parentToClean = wrapper.renderer.isSVG && element.nodeName === 'SPAN' && wrapper.parentGroup,
-			grandParent,
-			key,
-			i;
-
-		// remove events
-		element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null;
-		stop(wrapper); // stop running animations
-
-		if (wrapper.clipPath) {
-			wrapper.clipPath = wrapper.clipPath.destroy();
-		}
-
-		// Destroy stops in case this is a gradient object
-		if (wrapper.stops) {
-			for (i = 0; i < wrapper.stops.length; i++) {
-				wrapper.stops[i] = wrapper.stops[i].destroy();
-			}
-			wrapper.stops = null;
-		}
-
-		// remove element
-		wrapper.safeRemoveChild(element);
-
-		// destroy shadows
-		if (shadows) {
-			each(shadows, function (shadow) {
-				wrapper.safeRemoveChild(shadow);
-			});
-		}
-
-		// In case of useHTML, clean up empty containers emulating SVG groups (#1960, #2393).
-		while (parentToClean && parentToClean.div.childNodes.length === 0) {
-			grandParent = parentToClean.parentGroup;
-			wrapper.safeRemoveChild(parentToClean.div);
-			delete parentToClean.div;
-			parentToClean = grandParent;
-		}
-
-		// remove from alignObjects
-		if (wrapper.alignTo) {
-			erase(wrapper.renderer.alignedObjects, wrapper);
-		}
-
-		for (key in wrapper) {
-			delete wrapper[key];
-		}
-
-		return null;
-	},
-
-	/**
-	 * Add a shadow to the element. Must be done after the element is added to the DOM
-	 * @param {Boolean|Object} shadowOptions
-	 */
-	shadow: function (shadowOptions, group, cutOff) {
-		var shadows = [],
-			i,
-			shadow,
-			element = this.element,
-			strokeWidth,
-			shadowWidth,
-			shadowElementOpacity,
-
-			// compensate for inverted plot area
-			transform;
-
-
-		if (shadowOptions) {
-			shadowWidth = pick(shadowOptions.width, 3);
-			shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
-			transform = this.parentInverted ?
-				'(-1,-1)' :
-				'(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')';
-			for (i = 1; i <= shadowWidth; i++) {
-				shadow = element.cloneNode(0);
-				strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
-				attr(shadow, {
-					'isShadow': 'true',
-					'stroke': shadowOptions.color || 'black',
-					'stroke-opacity': shadowElementOpacity * i,
-					'stroke-width': strokeWidth,
-					'transform': 'translate' + transform,
-					'fill': NONE
-				});
-				if (cutOff) {
-					attr(shadow, 'height', mathMax(attr(shadow, 'height') - strokeWidth, 0));
-					shadow.cutHeight = strokeWidth;
-				}
-
-				if (group) {
-					group.element.appendChild(shadow);
-				} else {
-					element.parentNode.insertBefore(shadow, element);
-				}
-
-				shadows.push(shadow);
-			}
-
-			this.shadows = shadows;
-		}
-		return this;
-
-	},
-
-	xGetter: function (key) {
-		if (this.element.nodeName === 'circle') {
-			key = { x: 'cx', y: 'cy' }[key] || key;
-		}
-		return this._defaultGetter(key);
-	},
-
-	/** 
-	 * Get the current value of an attribute or pseudo attribute, used mainly
-	 * for animation.
-	 */
-	_defaultGetter: function (key) {
-		var ret = pick(this[key], this.element ? this.element.getAttribute(key) : null, 0);
-
-		if (/^[0-9\.]+$/.test(ret)) { // is numerical
-			ret = parseFloat(ret);
-		}
-		return ret;
-	},
-
-
-	dSetter: function (value, key, element) {
-		if (value && value.join) { // join path
-			value = value.join(' ');
-		}
-		if (/(NaN| {2}|^$)/.test(value)) {
-			value = 'M 0 0';
-		}
-		element.setAttribute(key, value);
-
-		this[key] = value;
-	},
-	dashstyleSetter: function (value) {
-		var i;
-		value = value && value.toLowerCase();
-		if (value) {
-			value = value
-				.replace('shortdashdotdot', '3,1,1,1,1,1,')
-				.replace('shortdashdot', '3,1,1,1')
-				.replace('shortdot', '1,1,')
-				.replace('shortdash', '3,1,')
-				.replace('longdash', '8,3,')
-				.replace(/dot/g, '1,3,')
-				.replace('dash', '4,3,')
-				.replace(/,$/, '')
-				.split(','); // ending comma
-
-			i = value.length;
-			while (i--) {
-				value[i] = pInt(value[i]) * this.element.getAttribute('stroke-width');
-			}
-			value = value.join(',');
-			this.element.setAttribute('stroke-dasharray', value);
-		}
-	},
-	alignSetter: function (value) {
-		this.element.setAttribute('text-anchor', { left: 'start', center: 'middle', right: 'end' }[value]);
-	},
-	opacitySetter: function (value, key, element) {
-		this[key] = value;
-		element.setAttribute(key, value);
-	},
-	// In Chrome/Win < 6 as well as Batik and PhantomJS as of 1.9.7, the stroke attribute can't be set when the stroke-
-	// width is 0. #1369
-	'stroke-widthSetter': function (value, key, element) {
-		if (value === 0) {
-			value = 0.00001;
-		}
-		this.strokeWidth = value; // read in symbol paths like 'callout'
-		element.setAttribute(key, value);
-	},
-	titleSetter: function (value) {
-		var titleNode = this.element.getElementsByTagName('title')[0];
-		if (!titleNode) {
-			titleNode = doc.createElementNS(SVG_NS, 'title');
-			this.element.appendChild(titleNode);
-		}
-		titleNode.textContent = value;
-	},
-	textSetter: function (value) {
-		if (value !== this.textStr) {
-			// Delete bBox memo when the text changes
-			delete this.bBox;
-		
-			this.textStr = value;
-			if (this.added) {
-				this.renderer.buildText(this);
-			}
-		}
-	},
-	fillSetter: function (value, key, element) {
-
-		if (typeof value === 'string') {
-			element.setAttribute(key, value);
-		} else if (value) {
-			this.colorGradient(value, key, element);
-		}
-	},
-	zIndexSetter: function (value, key, element) {
-		element.setAttribute(key, value);
-		this[key] = value;
-	},
-	_defaultSetter: function (value, key, element) {
-		element.setAttribute(key, value);
-	}
-};
-
-// Some shared setters and getters
-SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
-SVGElement.prototype.translateXSetter = SVGElement.prototype.translateYSetter = 
-		SVGElement.prototype.rotationSetter = SVGElement.prototype.verticalAlignSetter = 
-		SVGElement.prototype.scaleXSetter = SVGElement.prototype.scaleYSetter = function (value, key) {
-	this[key] = value;
-	this.doTransform = true;
-};
-SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
-
-
-
-// In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke-
-// width is 0. #1369
-/*SVGElement.prototype['stroke-widthSetter'] = SVGElement.prototype.strokeSetter = function (value, key) {
-	this[key] = value;
-	// Only apply the stroke attribute if the stroke width is defined and larger than 0
-	if (this.stroke && this['stroke-width']) {
-		this.element.setAttribute('stroke', this.stroke);
-		this.element.setAttribute('stroke-width', this['stroke-width']);
-		this.hasStroke = true;
-	} else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
-		this.element.removeAttribute('stroke');
-		this.hasStroke = false;
-	}
-};*/
-
-
-/**
- * The default SVG renderer
- */
-var SVGRenderer = function () {
-	this.init.apply(this, arguments);
-};
-SVGRenderer.prototype = {
-	Element: SVGElement,
-
-	/**
-	 * Initialize the SVGRenderer
-	 * @param {Object} container
-	 * @param {Number} width
-	 * @param {Number} height
-	 * @param {Boolean} forExport
-	 */
-	init: function (container, width, height, style, forExport) {
-		var renderer = this,
-			loc = location,
-			boxWrapper,
-			element,
-			desc;
-
-		boxWrapper = renderer.createElement('svg')
-			.attr({
-				version: '1.1'
-			})
-			.css(this.getStyle(style));
-		element = boxWrapper.element;
-		container.appendChild(element);
-
-		// For browsers other than IE, add the namespace attribute (#1978)
-		if (container.innerHTML.indexOf('xmlns') === -1) {
-			attr(element, 'xmlns', SVG_NS);
-		}
-
-		// object properties
-		renderer.isSVG = true;
-		renderer.box = element;
-		renderer.boxWrapper = boxWrapper;
-		renderer.alignedObjects = [];
-
-		// Page url used for internal references. #24, #672, #1070
-		renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ?
-			loc.href
-				.replace(/#.*?$/, '') // remove the hash
-				.replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes
-				.replace(/ /g, '%20') : // replace spaces (needed for Safari only)
-			'';
-
-		// Add description
-		desc = this.createElement('desc').add();
-		desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION));
-
-
-		renderer.defs = this.createElement('defs').add();
-		renderer.forExport = forExport;
-		renderer.gradients = {}; // Object where gradient SvgElements are stored
-		renderer.cache = {}; // Cache for numerical bounding boxes
-
-		renderer.setSize(width, height, false);
-
-
-
-		// Issue 110 workaround:
-		// In Firefox, if a div is positioned by percentage, its pixel position may land
-		// between pixels. The container itself doesn't display this, but an SVG element
-		// inside this container will be drawn at subpixel precision. In order to draw
-		// sharp lines, this must be compensated for. This doesn't seem to work inside
-		// iframes though (like in jsFiddle).
-		var subPixelFix, rect;
-		if (isFirefox && container.getBoundingClientRect) {
-			renderer.subPixelFix = subPixelFix = function () {
-				css(container, { left: 0, top: 0 });
-				rect = container.getBoundingClientRect();
-				css(container, {
-					left: (mathCeil(rect.left) - rect.left) + PX,
-					top: (mathCeil(rect.top) - rect.top) + PX
-				});
-			};
-
-			// run the fix now
-			subPixelFix();
-
-			// run it on resize
-			addEvent(win, 'resize', subPixelFix);
-		}
-	},
-
-	getStyle: function (style) {
-		return (this.style = extend({
-			fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', // default font
-			fontSize: '12px'
-		}, style));
-	},
-
-	/**
-	 * Detect whether the renderer is hidden. This happens when one of the parent elements
-	 * has display: none. #608.
-	 */
-	isHidden: function () {
-		return !this.boxWrapper.getBBox().width;
-	},
-
-	/**
-	 * Destroys the renderer and its allocated members.
-	 */
-	destroy: function () {
-		var renderer = this,
-			rendererDefs = renderer.defs;
-		renderer.box = null;
-		renderer.boxWrapper = renderer.boxWrapper.destroy();
-
-		// Call destroy on all gradient elements
-		destroyObjectProperties(renderer.gradients || {});
-		renderer.gradients = null;
-
-		// Defs are null in VMLRenderer
-		// Otherwise, destroy them here.
-		if (rendererDefs) {
-			renderer.defs = rendererDefs.destroy();
-		}
-
-		// Remove sub pixel fix handler
-		// We need to check that there is a handler, otherwise all functions that are registered for event 'resize' are removed
-		// See issue #982
-		if (renderer.subPixelFix) {
-			removeEvent(win, 'resize', renderer.subPixelFix);
-		}
-
-		renderer.alignedObjects = null;
-
-		return null;
-	},
-
-	/**
-	 * Create a wrapper for an SVG element
-	 * @param {Object} nodeName
-	 */
-	createElement: function (nodeName) {
-		var wrapper = new this.Element();
-		wrapper.init(this, nodeName);
-		return wrapper;
-	},
-
-	/**
-	 * Dummy function for use in canvas renderer
-	 */
-	draw: function () {},
-
-	/**
-	 * Parse a simple HTML string into SVG tspans
-	 *
-	 * @param {Object} textNode The parent text SVG node
-	 */
-	buildText: function (wrapper) {
-		var textNode = wrapper.element,
-			renderer = this,
-			forExport = renderer.forExport,
-			textStr = pick(wrapper.textStr, '').toString(),
-			hasMarkup = textStr.indexOf('<') !== -1,
-			lines,
-			childNodes = textNode.childNodes,
-			styleRegex,
-			hrefRegex,
-			parentX = attr(textNode, 'x'),
-			textStyles = wrapper.styles,
-			width = wrapper.textWidth,
-			textLineHeight = textStyles && textStyles.lineHeight,
-			i = childNodes.length,
-			getLineHeight = function (tspan) {
-				return textLineHeight ? 
-					pInt(textLineHeight) :
-					renderer.fontMetrics(
-						/(px|em)$/.test(tspan && tspan.style.fontSize) ?
-							tspan.style.fontSize :
-							((textStyles && textStyles.fontSize) || renderer.style.fontSize || 12)
-					).h;
-			};
-
-		/// remove old text
-		while (i--) {
-			textNode.removeChild(childNodes[i]);
-		}
-
-		// Skip tspans, add text directly to text node
-		if (!hasMarkup && textStr.indexOf(' ') === -1) {
-			textNode.appendChild(doc.createTextNode(textStr));
-			return;
-
-		// Complex strings, add more logic
-		} else {
-
-			styleRegex = /<.*style="([^"]+)".*>/;
-			hrefRegex = /<.*href="(http[^"]+)".*>/;
-
-			if (width && !wrapper.added) {
-				this.box.appendChild(textNode); // attach it to the DOM to read offset width
-			}
-
-			if (hasMarkup) {
-				lines = textStr
-					.replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
-					.replace(/<(i|em)>/g, '<span style="font-style:italic">')
-					.replace(/<a/g, '<span')
-					.replace(/<\/(b|strong|i|em|a)>/g, '</span>')
-					.split(/<br.*?>/g);
-
-			} else {
-				lines = [textStr];
-			}
-
-
-			// remove empty line at end
-			if (lines[lines.length - 1] === '') {
-				lines.pop();
-			}
-
-			
-			// build the lines
-			each(lines, function (line, lineNo) {
-				var spans, spanNo = 0;
-
-				line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
-				spans = line.split('|||');
-
-				each(spans, function (span) {
-					if (span !== '' || spans.length === 1) {
-						var attributes = {},
-							tspan = doc.createElementNS(SVG_NS, 'tspan'),
-							spanStyle; // #390
-						if (styleRegex.test(span)) {
-							spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2');
-							attr(tspan, 'style', spanStyle);
-						}
-						if (hrefRegex.test(span) && !forExport) { // Not for export - #1529
-							attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
-							css(tspan, { cursor: 'pointer' });
-						}
-
-						span = (span.replace(/<(.|\n)*?>/g, '') || ' ')
-							.replace(/&lt;/g, '<')
-							.replace(/&gt;/g, '>');
-
-						// Nested tags aren't supported, and cause crash in Safari (#1596)
-						if (span !== ' ') {
-
-							// add the text node
-							tspan.appendChild(doc.createTextNode(span));
-
-							if (!spanNo) { // first span in a line, align it to the left
-								if (lineNo && parentX !== null) {
-									attributes.x = parentX;
-								}
-							} else {
-								attributes.dx = 0; // #16
-							}
-
-							// add attributes
-							attr(tspan, attributes);
-
-							// first span on subsequent line, add the line height
-							if (!spanNo && lineNo) {
-
-								// allow getting the right offset height in exporting in IE
-								if (!hasSVG && forExport) {
-									css(tspan, { display: 'block' });
-								}
-
-								// Set the line height based on the font size of either
-								// the text element or the tspan element
-								attr(
-									tspan,
-									'dy',
-									getLineHeight(tspan),
-									// Safari 6.0.2 - too optimized for its own good (#1539)
-									// TODO: revisit this with future versions of Safari
-									isWebKit && tspan.offsetHeight
-								);
-							}
-
-							// Append it
-							textNode.appendChild(tspan);
-
-							spanNo++;
-
-							// check width and apply soft breaks
-							if (width) {
-								var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
-									hasWhiteSpace = words.length > 1 && textStyles.whiteSpace !== 'nowrap',
-									tooLong,
-									actualWidth,
-									clipHeight = wrapper._clipHeight,
-									rest = [],
-									dy = getLineHeight(),
-									softLineNo = 1,
-									bBox;
-
-								while (hasWhiteSpace && (words.length || rest.length)) {
-									delete wrapper.bBox; // delete cache
-									bBox = wrapper.getBBox();
-									actualWidth = bBox.width;
-
-									// Old IE cannot measure the actualWidth for SVG elements (#2314)
-									if (!hasSVG && renderer.forExport) {
-										actualWidth = renderer.measureSpanWidth(tspan.firstChild.data, wrapper.styles);
-									}
-
-									tooLong = actualWidth > width;
-									if (!tooLong || words.length === 1) { // new line needed
-										words = rest;
-										rest = [];
-										if (words.length) {
-											softLineNo++;
-
-											if (clipHeight && softLineNo * dy > clipHeight) {
-												words = ['...'];
-												wrapper.attr('title', wrapper.textStr);
-											} else {
-
-												tspan = doc.createElementNS(SVG_NS, 'tspan');
-												attr(tspan, {
-													dy: dy,
-													x: parentX
-												});
-												if (spanStyle) { // #390
-													attr(tspan, 'style', spanStyle);
-												}
-												textNode.appendChild(tspan);
-
-												if (actualWidth > width) { // a single word is pressing it out
-													width = actualWidth;
-												}
-											}
-										}
-									} else { // append to existing line tspan
-										tspan.removeChild(tspan.firstChild);
-										rest.unshift(words.pop());
-									}
-									if (words.length) {
-										tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-')));
-									}
-								}
-							}
-						}
-					}
-				});
-			});
-		}
-	},
-
-	/**
-	 * Create a button with preset states
-	 * @param {String} text
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Function} callback
-	 * @param {Object} normalState
-	 * @param {Object} hoverState
-	 * @param {Object} pressedState
-	 */
-	button: function (text, x, y, callback, normalState, hoverState, pressedState, disabledState, shape) {
-		var label = this.label(text, x, y, shape, null, null, null, null, 'button'),
-			curState = 0,
-			stateOptions,
-			stateStyle,
-			normalStyle,
-			hoverStyle,
-			pressedStyle,
-			disabledStyle,
-			verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 };
-
-		// Normal state - prepare the attributes
-		normalState = merge({
-			'stroke-width': 1,
-			stroke: '#CCCCCC',
-			fill: {
-				linearGradient: verticalGradient,
-				stops: [
-					[0, '#FEFEFE'],
-					[1, '#F6F6F6']
-				]
-			},
-			r: 2,
-			padding: 5,
-			style: {
-				color: 'black'
-			}
-		}, normalState);
-		normalStyle = normalState.style;
-		delete normalState.style;
-
-		// Hover state
-		hoverState = merge(normalState, {
-			stroke: '#68A',
-			fill: {
-				linearGradient: verticalGradient,
-				stops: [
-					[0, '#FFF'],
-					[1, '#ACF']
-				]
-			}
-		}, hoverState);
-		hoverStyle = hoverState.style;
-		delete hoverState.style;
-
-		// Pressed state
-		pressedState = merge(normalState, {
-			stroke: '#68A',
-			fill: {
-				linearGradient: verticalGradient,
-				stops: [
-					[0, '#9BD'],
-					[1, '#CDF']
-				]
-			}
-		}, pressedState);
-		pressedStyle = pressedState.style;
-		delete pressedState.style;
-
-		// Disabled state
-		disabledState = merge(normalState, {
-			style: {
-				color: '#CCC'
-			}
-		}, disabledState);
-		disabledStyle = disabledState.style;
-		delete disabledState.style;
-
-		// Add the events. IE9 and IE10 need mouseover and mouseout to funciton (#667).
-		addEvent(label.element, isIE ? 'mouseover' : 'mouseenter', function () {
-			if (curState !== 3) {
-				label.attr(hoverState)
-					.css(hoverStyle);
-			}
-		});
-		addEvent(label.element, isIE ? 'mouseout' : 'mouseleave', function () {
-			if (curState !== 3) {
-				stateOptions = [normalState, hoverState, pressedState][curState];
-				stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
-				label.attr(stateOptions)
-					.css(stateStyle);
-			}
-		});
-
-		label.setState = function (state) {
-			label.state = curState = state;
-			if (!state) {
-				label.attr(normalState)
-					.css(normalStyle);
-			} else if (state === 2) {
-				label.attr(pressedState)
-					.css(pressedStyle);
-			} else if (state === 3) {
-				label.attr(disabledState)
-					.css(disabledStyle);
-			}
-		};
-
-		return label
-			.on('click', function () {
-				if (curState !== 3) {
-					callback.call(label);
-				}
-			})
-			.attr(normalState)
-			.css(extend({ cursor: 'default' }, normalStyle));
-	},
-
-	/**
-	 * Make a straight line crisper by not spilling out to neighbour pixels
-	 * @param {Array} points
-	 * @param {Number} width
-	 */
-	crispLine: function (points, width) {
-		// points format: [M, 0, 0, L, 100, 0]
-		// normalize to a crisp line
-		if (points[1] === points[4]) {
-			// Substract due to #1129. Now bottom and left axis gridlines behave the same.
-			points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2);
-		}
-		if (points[2] === points[5]) {
-			points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2);
-		}
-		return points;
-	},
-
-
-	/**
-	 * Draw a path
-	 * @param {Array} path An SVG path in array form
-	 */
-	path: function (path) {
-		var attr = {
-			fill: NONE
-		};
-		if (isArray(path)) {
-			attr.d = path;
-		} else if (isObject(path)) { // attributes
-			extend(attr, path);
-		}
-		return this.createElement('path').attr(attr);
-	},
-
-	/**
-	 * Draw and return an SVG circle
-	 * @param {Number} x The x position
-	 * @param {Number} y The y position
-	 * @param {Number} r The radius
-	 */
-	circle: function (x, y, r) {
-		var attr = isObject(x) ?
-			x :
-			{
-				x: x,
-				y: y,
-				r: r
-			},
-			wrapper = this.createElement('circle');
-
-		wrapper.xSetter = function (value) {
-			this.element.setAttribute('cx', value);
-		};
-		wrapper.ySetter = function (value) {
-			this.element.setAttribute('cy', value);
-		};
-		return wrapper.attr(attr);
-	},
-
-	/**
-	 * Draw and return an arc
-	 * @param {Number} x X position
-	 * @param {Number} y Y position
-	 * @param {Number} r Radius
-	 * @param {Number} innerR Inner radius like used in donut charts
-	 * @param {Number} start Starting angle
-	 * @param {Number} end Ending angle
-	 */
-	arc: function (x, y, r, innerR, start, end) {
-		var arc;
-
-		if (isObject(x)) {
-			y = x.y;
-			r = x.r;
-			innerR = x.innerR;
-			start = x.start;
-			end = x.end;
-			x = x.x;
-		}
-
-		// Arcs are defined as symbols for the ability to set
-		// attributes in attr and animate
-		arc = this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
-			innerR: innerR || 0,
-			start: start || 0,
-			end: end || 0
-		});
-		arc.r = r; // #959
-		return arc;
-	},
-
-	/**
-	 * Draw and return a rectangle
-	 * @param {Number} x Left position
-	 * @param {Number} y Top position
-	 * @param {Number} width
-	 * @param {Number} height
-	 * @param {Number} r Border corner radius
-	 * @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing
-	 */
-	rect: function (x, y, width, height, r, strokeWidth) {
-
-		r = isObject(x) ? x.r : r;
-
-		var wrapper = this.createElement('rect'),
-			attribs = isObject(x) ? x : x === UNDEFINED ? {} : {
-				x: x,
-				y: y,
-				width: mathMax(width, 0),
-				height: mathMax(height, 0)
-			};
-
-		if (strokeWidth !== UNDEFINED) {
-			attribs.strokeWidth = strokeWidth;
-			attribs = wrapper.crisp(attribs);
-		}
-
-		if (r) {
-			attribs.r = r;
-		}
-
-		wrapper.rSetter = function (value) {
-			attr(this.element, {
-				rx: value,
-				ry: value
-			});
-		};
-		
-		return wrapper.attr(attribs);
-	},
-
-	/**
-	 * Resize the box and re-align all aligned elements
-	 * @param {Object} width
-	 * @param {Object} height
-	 * @param {Boolean} animate
-	 *
-	 */
-	setSize: function (width, height, animate) {
-		var renderer = this,
-			alignedObjects = renderer.alignedObjects,
-			i = alignedObjects.length;
-
-		renderer.width = width;
-		renderer.height = height;
-
-		renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({
-			width: width,
-			height: height
-		});
-
-		while (i--) {
-			alignedObjects[i].align();
-		}
-	},
-
-	/**
-	 * Create a group
-	 * @param {String} name The group will be given a class name of 'highcharts-{name}'.
-	 *     This can be used for styling and scripting.
-	 */
-	g: function (name) {
-		var elem = this.createElement('g');
-		return defined(name) ? elem.attr({ 'class': PREFIX + name }) : elem;
-	},
-
-	/**
-	 * Display an image
-	 * @param {String} src
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	image: function (src, x, y, width, height) {
-		var attribs = {
-				preserveAspectRatio: NONE
-			},
-			elemWrapper;
-
-		// optional properties
-		if (arguments.length > 1) {
-			extend(attribs, {
-				x: x,
-				y: y,
-				width: width,
-				height: height
-			});
-		}
-
-		elemWrapper = this.createElement('image').attr(attribs);
-
-		// set the href in the xlink namespace
-		if (elemWrapper.element.setAttributeNS) {
-			elemWrapper.element.setAttributeNS('http://www.w3.org/1999/xlink',
-				'href', src);
-		} else {
-			// could be exporting in IE
-			// using href throws "not supported" in ie7 and under, requries regex shim to fix later
-			elemWrapper.element.setAttribute('hc-svg-href', src);
-	}
-
-		return elemWrapper;
-	},
-
-	/**
-	 * Draw a symbol out of pre-defined shape paths from the namespace 'symbol' object.
-	 *
-	 * @param {Object} symbol
-	 * @param {Object} x
-	 * @param {Object} y
-	 * @param {Object} radius
-	 * @param {Object} options
-	 */
-	symbol: function (symbol, x, y, width, height, options) {
-
-		var obj,
-
-			// get the symbol definition function
-			symbolFn = this.symbols[symbol],
-
-			// check if there's a path defined for this symbol
-			path = symbolFn && symbolFn(
-				mathRound(x),
-				mathRound(y),
-				width,
-				height,
-				options
-			),
-
-			imageElement,
-			imageRegex = /^url\((.*?)\)$/,
-			imageSrc,
-			imageSize,
-			centerImage;
-
-		if (path) {
-
-			obj = this.path(path);
-			// expando properties for use in animate and attr
-			extend(obj, {
-				symbolName: symbol,
-				x: x,
-				y: y,
-				width: width,
-				height: height
-			});
-			if (options) {
-				extend(obj, options);
-			}
-
-
-		// image symbols
-		} else if (imageRegex.test(symbol)) {
-
-			// On image load, set the size and position
-			centerImage = function (img, size) {
-				if (img.element) { // it may be destroyed in the meantime (#1390)
-					img.attr({
-						width: size[0],
-						height: size[1]
-					});
-
-					if (!img.alignByTranslate) { // #185
-						img.translate(
-							mathRound((width - size[0]) / 2), // #1378
-							mathRound((height - size[1]) / 2)
-						);
-					}
-				}
-			};
-
-			imageSrc = symbol.match(imageRegex)[1];
-			imageSize = symbolSizes[imageSrc];
-
-			// Ireate the image synchronously, add attribs async
-			obj = this.image(imageSrc)
-				.attr({
-					x: x,
-					y: y
-				});
-			obj.isImg = true;
-
-			if (imageSize) {
-				centerImage(obj, imageSize);
-			} else {
-				// Initialize image to be 0 size so export will still function if there's no cached sizes.
-				//
-				obj.attr({ width: 0, height: 0 });
-
-				// Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8,
-				// the created element must be assigned to a variable in order to load (#292).
-				imageElement = createElement('img', {
-					onload: function () {
-						centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]);
-					},
-					src: imageSrc
-				});
-			}
-		}
-
-		return obj;
-	},
-
-	/**
-	 * An extendable collection of functions for defining symbol paths.
-	 */
-	symbols: {
-		'circle': function (x, y, w, h) {
-			var cpw = 0.166 * w;
-			return [
-				M, x + w / 2, y,
-				'C', x + w + cpw, y, x + w + cpw, y + h, x + w / 2, y + h,
-				'C', x - cpw, y + h, x - cpw, y, x + w / 2, y,
-				'Z'
-			];
-		},
-
-		'square': function (x, y, w, h) {
-			return [
-				M, x, y,
-				L, x + w, y,
-				x + w, y + h,
-				x, y + h,
-				'Z'
-			];
-		},
-
-		'triangle': function (x, y, w, h) {
-			return [
-				M, x + w / 2, y,
-				L, x + w, y + h,
-				x, y + h,
-				'Z'
-			];
-		},
-
-		'triangle-down': function (x, y, w, h) {
-			return [
-				M, x, y,
-				L, x + w, y,
-				x + w / 2, y + h,
-				'Z'
-			];
-		},
-		'diamond': function (x, y, w, h) {
-			return [
-				M, x + w / 2, y,
-				L, x + w, y + h / 2,
-				x + w / 2, y + h,
-				x, y + h / 2,
-				'Z'
-			];
-		},
-		'arc': function (x, y, w, h, options) {
-			var start = options.start,
-				radius = options.r || w || h,
-				end = options.end - 0.001, // to prevent cos and sin of start and end from becoming equal on 360 arcs (related: #1561)
-				innerRadius = options.innerR,
-				open = options.open,
-				cosStart = mathCos(start),
-				sinStart = mathSin(start),
-				cosEnd = mathCos(end),
-				sinEnd = mathSin(end),
-				longArc = options.end - start < mathPI ? 0 : 1;
-
-			return [
-				M,
-				x + radius * cosStart,
-				y + radius * sinStart,
-				'A', // arcTo
-				radius, // x radius
-				radius, // y radius
-				0, // slanting
-				longArc, // long or short arc
-				1, // clockwise
-				x + radius * cosEnd,
-				y + radius * sinEnd,
-				open ? M : L,
-				x + innerRadius * cosEnd,
-				y + innerRadius * sinEnd,
-				'A', // arcTo
-				innerRadius, // x radius
-				innerRadius, // y radius
-				0, // slanting
-				longArc, // long or short arc
-				0, // clockwise
-				x + innerRadius * cosStart,
-				y + innerRadius * sinStart,
-
-				open ? '' : 'Z' // close
-			];
-		},
-
-		/**
-		 * Callout shape used for default tooltips, also used for rounded rectangles in VML
-		 */
-		callout: function (x, y, w, h, options) {
-			var arrowLength = 6,
-				halfDistance = 6,
-				r = mathMin((options && options.r) || 0, w, h),
-				safeDistance = r + halfDistance,
-				anchorX = options && options.anchorX,
-				anchorY = options && options.anchorY,
-				path,
-				normalizer = mathRound(options.strokeWidth || 0) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors;
-
-			x += normalizer;
-			y += normalizer;
-			path = [
-				'M', x + r, y, 
-				'L', x + w - r, y, // top side
-				'C', x + w, y, x + w, y, x + w, y + r, // top-right corner
-				'L', x + w, y + h - r, // right side
-				'C', x + w, y + h, x + w, y + h, x + w - r, y + h, // bottom-right corner
-				'L', x + r, y + h, // bottom side
-				'C', x, y + h, x, y + h, x, y + h - r, // bottom-left corner
-				'L', x, y + r, // left side
-				'C', x, y, x, y, x + r, y // top-right corner
-			];
-			
-			if (anchorX && anchorX > w && anchorY > y + safeDistance && anchorY < y + h - safeDistance) { // replace right side
-				path.splice(13, 3,
-					'L', x + w, anchorY - halfDistance, 
-					x + w + arrowLength, anchorY,
-					x + w, anchorY + halfDistance,
-					x + w, y + h - r
-				);
-			} else if (anchorX && anchorX < 0 && anchorY > y + safeDistance && anchorY < y + h - safeDistance) { // replace left side
-				path.splice(33, 3, 
-					'L', x, anchorY + halfDistance, 
-					x - arrowLength, anchorY,
-					x, anchorY - halfDistance,
-					x, y + r
-				);
-			} else if (anchorY && anchorY > h && anchorX > x + safeDistance && anchorX < x + w - safeDistance) { // replace bottom
-				path.splice(23, 3,
-					'L', anchorX + halfDistance, y + h,
-					anchorX, y + h + arrowLength,
-					anchorX - halfDistance, y + h,
-					x + r, y + h
-				);
-			} else if (anchorY && anchorY < 0 && anchorX > x + safeDistance && anchorX < x + w - safeDistance) { // replace top
-				path.splice(3, 3,
-					'L', anchorX - halfDistance, y,
-					anchorX, y - arrowLength,
-					anchorX + halfDistance, y,
-					w - r, y
-				);
-			}
-			return path;
-		}
-	},
-
-	/**
-	 * Define a clipping rectangle
-	 * @param {String} id
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	clipRect: function (x, y, width, height) {
-		var wrapper,
-			id = PREFIX + idCounter++,
-
-			clipPath = this.createElement('clipPath').attr({
-				id: id
-			}).add(this.defs);
-
-		wrapper = this.rect(x, y, width, height, 0).add(clipPath);
-		wrapper.id = id;
-		wrapper.clipPath = clipPath;
-
-		return wrapper;
-	},
-
-
-	
-
-
-	/**
-	 * Add text to the SVG object
-	 * @param {String} str
-	 * @param {Number} x Left position
-	 * @param {Number} y Top position
-	 * @param {Boolean} useHTML Use HTML to render the text
-	 */
-	text: function (str, x, y, useHTML) {
-
-		// declare variables
-		var renderer = this,
-			fakeSVG = useCanVG || (!hasSVG && renderer.forExport),
-			wrapper,
-			attr = {};
-
-		if (useHTML && !renderer.forExport) {
-			return renderer.html(str, x, y);
-		}
-
-		attr.x = Math.round(x || 0); // X is always needed for line-wrap logic
-		if (y) {
-			attr.y = Math.round(y);
-		}
-		if (str || str === 0) {
-			attr.text = str;
-		}
-
-		wrapper = renderer.createElement('text')
-			.attr(attr);
-
-		// Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063)
-		if (fakeSVG) {
-			wrapper.css({
-				position: ABSOLUTE
-			});
-		}
-
-		if (!useHTML) {
-			wrapper.xSetter = function (value, key, element) {
-				var childNodes = element.childNodes,
-					child,
-					i;
-				for (i = 1; i < childNodes.length; i++) {
-					child = childNodes[i];
-					// if the x values are equal, the tspan represents a linebreak
-					if (child.getAttribute('x') === element.getAttribute('x')) {
-						child.setAttribute('x', value);
-					}
-				}
-				element.setAttribute(key, value);
-			};
-		}
-		
-		return wrapper;
-	},
-
-	/**
-	 * Utility to return the baseline offset and total line height from the font size
-	 */
-	fontMetrics: function (fontSize) {
-		fontSize = fontSize || this.style.fontSize;
-		fontSize = /px/.test(fontSize) ? pInt(fontSize) : /em/.test(fontSize) ? parseFloat(fontSize) * 12 : 12;
-
-		// Empirical values found by comparing font size and bounding box height.
-		// Applies to the default font family. http://jsfiddle.net/highcharts/7xvn7/
-		var lineHeight = fontSize < 24 ? fontSize + 4 : mathRound(fontSize * 1.2),
-			baseline = mathRound(lineHeight * 0.8);
-
-		return {
-			h: lineHeight,
-			b: baseline
-		};
-	},
-
-	/**
-	 * Add a label, a text item that can hold a colored or gradient background
-	 * as well as a border and shadow.
-	 * @param {string} str
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {String} shape
-	 * @param {Number} anchorX In case the shape has a pointer, like a flag, this is the
-	 *    coordinates it should be pinned to
-	 * @param {Number} anchorY
-	 * @param {Boolean} baseline Whether to position the label relative to the text baseline,
-	 *    like renderer.text, or to the upper border of the rectangle.
-	 * @param {String} className Class name for the group
-	 */
-	label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
-
-		var renderer = this,
-			wrapper = renderer.g(className),
-			text = renderer.text('', 0, 0, useHTML)
-				.attr({
-					zIndex: 1
-				}),
-				//.add(wrapper),
-			box,
-			bBox,
-			alignFactor = 0,
-			padding = 3,
-			paddingLeft = 0,
-			width,
-			height,
-			wrapperX,
-			wrapperY,
-			crispAdjust = 0,
-			deferredAttr = {},
-			baselineOffset,
-			needsBox;
-
-		/**
-		 * This function runs after the label is added to the DOM (when the bounding box is
-		 * available), and after the text of the label is updated to detect the new bounding
-		 * box and reflect it in the border box.
-		 */
-		function updateBoxSize() {
-			var boxX,
-				boxY,
-				style = text.element.style;
-
-			bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) && text.textStr && 
-				text.getBBox();
-			wrapper.width = (width || bBox.width || 0) + 2 * padding + paddingLeft;
-			wrapper.height = (height || bBox.height || 0) + 2 * padding;
-
-			// update the label-scoped y offset
-			baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b;
-
-			
-			if (needsBox) {
-
-				// create the border box if it is not already present
-				if (!box) {
-					boxX = mathRound(-alignFactor * padding);
-					boxY = baseline ? -baselineOffset : 0;
-
-					wrapper.box = box = shape ?
-						renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height, deferredAttr) :
-						renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
-					box.attr('fill', NONE).add(wrapper);
-				}
-
-				// apply the box attributes
-				if (!box.isImg) { // #1630
-					box.attr(extend({
-						width: mathRound(wrapper.width),
-						height: mathRound(wrapper.height)
-					}, deferredAttr));
-				}
-				deferredAttr = null;
-			}
-		}
-
-		/**
-		 * This function runs after setting text or padding, but only if padding is changed
-		 */
-		function updateTextPadding() {
-			var styles = wrapper.styles,
-				textAlign = styles && styles.textAlign,
-				x = paddingLeft + padding * (1 - alignFactor),
-				y;
-
-			// determin y based on the baseline
-			y = baseline ? 0 : baselineOffset;
-
-			// compensate for alignment
-			if (defined(width) && bBox && (textAlign === 'center' || textAlign === 'right')) {
-				x += { center: 0.5, right: 1 }[textAlign] * (width - bBox.width);
-			}
-
-			// update if anything changed
-			if (x !== text.x || y !== text.y) {
-				text.attr('x', x);
-				if (y !== UNDEFINED) {
-					text.attr('y', y);
-				}
-			}
-
-			// record current values
-			text.x = x;
-			text.y = y;
-		}
-
-		/**
-		 * Set a box attribute, or defer it if the box is not yet created
-		 * @param {Object} key
-		 * @param {Object} value
-		 */
-		function boxAttr(key, value) {
-			if (box) {
-				box.attr(key, value);
-			} else {
-				deferredAttr[key] = value;
-			}
-		}
-
-		/**
-		 * After the text element is added, get the desired size of the border box
-		 * and add it before the text in the DOM.
-		 */
-		wrapper.onAdd = function () {
-			text.add(wrapper);
-			wrapper.attr({
-				text: str || '', // alignment is available now
-				x: x,
-				y: y
-			});
-
-			if (box && defined(anchorX)) {
-				wrapper.attr({
-					anchorX: anchorX,
-					anchorY: anchorY
-				});
-			}
-		};
-
-		/*
-		 * Add specific attribute setters.
-		 */
-
-		// only change local variables
-		wrapper.widthSetter = function (value) {
-			width = value;
-		};
-		wrapper.heightSetter = function (value) {
-			height = value;
-		};
-		wrapper.paddingSetter =  function (value) {
-			if (defined(value) && value !== padding) {
-				padding = value;
-				updateTextPadding();
-			}
-		};
-		wrapper.paddingLeftSetter =  function (value) {
-			if (defined(value) && value !== paddingLeft) {
-				paddingLeft = value;
-				updateTextPadding();
-			}
-		};
-
-
-		// change local variable and prevent setting attribute on the group
-		wrapper.alignSetter = function (value) {
-			alignFactor = { left: 0, center: 0.5, right: 1 }[value];
-		};
-
-		// apply these to the box and the text alike
-		wrapper.textSetter = function (value) {
-			if (value !== UNDEFINED) {
-				text.textSetter(value);
-			}
-			updateBoxSize();
-			updateTextPadding();
-		};
-
-		// apply these to the box but not to the text
-		wrapper['stroke-widthSetter'] = function (value, key) {
-			if (value) {
-				needsBox = true;
-			}
-			crispAdjust = value % 2 / 2;
-			boxAttr(key, value);
-		};
-		wrapper.strokeSetter = wrapper.fillSetter = wrapper.rSetter = function (value, key) {
-			if (key === 'fill' && value) {
-				needsBox = true;
-			}
-			boxAttr(key, value);
-		};
-		wrapper.anchorXSetter = function (value, key) {
-			anchorX = value;
-			boxAttr(key, value + crispAdjust - wrapperX);
-		};
-		wrapper.anchorYSetter = function (value, key) {
-			anchorY = value;
-			boxAttr(key, value - wrapperY);
-		};
-
-		// rename attributes
-		wrapper.xSetter = function (value) {
-			wrapper.x = value; // for animation getter
-			if (alignFactor) {
-				value -= alignFactor * ((width || bBox.width) + padding);
-			}
-			wrapperX = mathRound(value);
-			wrapper.attr('translateX', wrapperX);
-		};
-		wrapper.ySetter = function (value) {
-			wrapperY = wrapper.y = mathRound(value);
-			wrapper.attr('translateY', wrapperY);
-		};
-
-		// Redirect certain methods to either the box or the text
-		var baseCss = wrapper.css;
-		return extend(wrapper, {
-			/**
-			 * Pick up some properties and apply them to the text instead of the wrapper
-			 */
-			css: function (styles) {
-				if (styles) {
-					var textStyles = {};
-					styles = merge(styles); // create a copy to avoid altering the original object (#537)
-					each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration', 'textShadow'], function (prop) {
-						if (styles[prop] !== UNDEFINED) {
-							textStyles[prop] = styles[prop];
-							delete styles[prop];
-						}
-					});
-					text.css(textStyles);
-				}
-				return baseCss.call(wrapper, styles);
-			},
-			/**
-			 * Return the bounding box of the box, not the group
-			 */
-			getBBox: function () {
-				return {
-					width: bBox.width + 2 * padding,
-					height: bBox.height + 2 * padding,
-					x: bBox.x - padding,
-					y: bBox.y - padding
-				};
-			},
-			/**
-			 * Apply the shadow to the box
-			 */
-			shadow: function (b) {
-				if (box) {
-					box.shadow(b);
-				}
-				return wrapper;
-			},
-			/**
-			 * Destroy and release memory.
-			 */
-			destroy: function () {
-
-				// Added by button implementation
-				removeEvent(wrapper.element, 'mouseenter');
-				removeEvent(wrapper.element, 'mouseleave');
-
-				if (text) {
-					text = text.destroy();
-				}
-				if (box) {
-					box = box.destroy();
-				}
-				// Call base implementation to destroy the rest
-				SVGElement.prototype.destroy.call(wrapper);
-
-				// Release local pointers (#1298)
-				wrapper = renderer = updateBoxSize = updateTextPadding = boxAttr = null;
-			}
-		});
-	}
-}; // end SVGRenderer
-
-
-// general renderer
-Renderer = SVGRenderer;
-// extend SvgElement for useHTML option
-extend(SVGElement.prototype, {
-	/**
-	 * Apply CSS to HTML elements. This is used in text within SVG rendering and
-	 * by the VML renderer
-	 */
-	htmlCss: function (styles) {
-		var wrapper = this,
-			element = wrapper.element,
-			textWidth = styles && element.tagName === 'SPAN' && styles.width;
-
-		if (textWidth) {
-			delete styles.width;
-			wrapper.textWidth = textWidth;
-			wrapper.updateTransform();
-		}
-
-		wrapper.styles = extend(wrapper.styles, styles);
-		css(wrapper.element, styles);
-
-		return wrapper;
-	},
-
-	/**
-	 * VML and useHTML method for calculating the bounding box based on offsets
-	 * @param {Boolean} refresh Whether to force a fresh value from the DOM or to
-	 * use the cached value
-	 *
-	 * @return {Object} A hash containing values for x, y, width and height
-	 */
-
-	htmlGetBBox: function () {
-		var wrapper = this,
-			element = wrapper.element,
-			bBox = wrapper.bBox;
-
-		// faking getBBox in exported SVG in legacy IE
-		if (!bBox) {
-			// faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?)
-			if (element.nodeName === 'text') {
-				element.style.position = ABSOLUTE;
-			}
-
-			bBox = wrapper.bBox = {
-				x: element.offsetLeft,
-				y: element.offsetTop,
-				width: element.offsetWidth,
-				height: element.offsetHeight
-			};
-		}
-
-		return bBox;
-	},
-
-	/**
-	 * VML override private method to update elements based on internal
-	 * properties based on SVG transform
-	 */
-	htmlUpdateTransform: function () {
-		// aligning non added elements is expensive
-		if (!this.added) {
-			this.alignOnAdd = true;
-			return;
-		}
-
-		var wrapper = this,
-			renderer = wrapper.renderer,
-			elem = wrapper.element,
-			translateX = wrapper.translateX || 0,
-			translateY = wrapper.translateY || 0,
-			x = wrapper.x || 0,
-			y = wrapper.y || 0,
-			align = wrapper.textAlign || 'left',
-			alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
-			shadows = wrapper.shadows;
-
-		// apply translate
-		css(elem, {
-			marginLeft: translateX,
-			marginTop: translateY
-		});
-		if (shadows) { // used in labels/tooltip
-			each(shadows, function (shadow) {
-				css(shadow, {
-					marginLeft: translateX + 1,
-					marginTop: translateY + 1
-				});
-			});
-		}
-
-		// apply inversion
-		if (wrapper.inverted) { // wrapper is a group
-			each(elem.childNodes, function (child) {
-				renderer.invertChild(child, elem);
-			});
-		}
-
-		if (elem.tagName === 'SPAN') {
-
-			var width,
-				rotation = wrapper.rotation,
-				baseline,
-				textWidth = pInt(wrapper.textWidth),
-				currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(',');
-
-			if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
-
-
-				baseline = renderer.fontMetrics(elem.style.fontSize).b;
-
-				// Renderer specific handling of span rotation
-				if (defined(rotation)) {
-					wrapper.setSpanRotation(rotation, alignCorrection, baseline);
-				}
-
-				width = pick(wrapper.elemWidth, elem.offsetWidth);
-
-				// Update textWidth
-				if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254
-					css(elem, {
-						width: textWidth + PX,
-						display: 'block',
-						whiteSpace: 'normal'
-					});
-					width = textWidth;
-				}
-
-				wrapper.getSpanCorrection(width, baseline, alignCorrection, rotation, align);
-			}
-
-			// apply position with correction
-			css(elem, {
-				left: (x + (wrapper.xCorr || 0)) + PX,
-				top: (y + (wrapper.yCorr || 0)) + PX
-			});
-
-			// force reflow in webkit to apply the left and top on useHTML element (#1249)
-			if (isWebKit) {
-				baseline = elem.offsetHeight; // assigned to baseline for JSLint purpose
-			}
-
-			// record current text transform
-			wrapper.cTT = currentTextTransform;
-		}
-	},
-
-	/**
-	 * Set the rotation of an individual HTML span
-	 */
-	setSpanRotation: function (rotation, alignCorrection, baseline) {
-		var rotationStyle = {},
-			cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
-
-		rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
-		rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] = rotationStyle.transformOrigin = (alignCorrection * 100) + '% ' + baseline + 'px';
-		css(this.element, rotationStyle);
-	},
-
-	/**
-	 * Get the correction in X and Y positioning as the element is rotated.
-	 */
-	getSpanCorrection: function (width, baseline, alignCorrection) {
-		this.xCorr = -width * alignCorrection;
-		this.yCorr = -baseline;
-	}
-});
-
-// Extend SvgRenderer for useHTML option.
-extend(SVGRenderer.prototype, {
-	/**
-	 * Create HTML text node. This is used by the VML renderer as well as the SVG
-	 * renderer through the useHTML option.
-	 *
-	 * @param {String} str
-	 * @param {Number} x
-	 * @param {Number} y
-	 */
-	html: function (str, x, y) {
-		var wrapper = this.createElement('span'),
-			element = wrapper.element,
-			renderer = wrapper.renderer;
-
-		// Text setter
-		wrapper.textSetter = function (value) {
-			if (value !== element.innerHTML) {
-				delete this.bBox;
-			}
-			element.innerHTML = this.textStr = value;
-		};
-
-		// Various setters which rely on update transform
-		wrapper.xSetter = wrapper.ySetter = wrapper.alignSetter = wrapper.rotationSetter = function (value, key) {
-			if (key === 'align') {
-				key = 'textAlign'; // Do not overwrite the SVGElement.align method. Same as VML.
-			}
-			wrapper[key] = value;
-			wrapper.htmlUpdateTransform();
-		};
-
-		// Set the default attributes
-		wrapper.attr({
-				text: str,
-				x: mathRound(x),
-				y: mathRound(y)
-			})
-			.css({
-				position: ABSOLUTE,
-				whiteSpace: 'nowrap',
-				fontFamily: this.style.fontFamily,
-				fontSize: this.style.fontSize
-			});
-
-		// Use the HTML specific .css method
-		wrapper.css = wrapper.htmlCss;
-
-		// This is specific for HTML within SVG
-		if (renderer.isSVG) {
-			wrapper.add = function (svgGroupWrapper) {
-
-				var htmlGroup,
-					container = renderer.box.parentNode,
-					parentGroup,
-					parents = [];
-
-				this.parentGroup = svgGroupWrapper;
-
-				// Create a mock group to hold the HTML elements
-				if (svgGroupWrapper) {
-					htmlGroup = svgGroupWrapper.div;
-					if (!htmlGroup) {
-
-						// Read the parent chain into an array and read from top down
-						parentGroup = svgGroupWrapper;
-						while (parentGroup) {
-
-							parents.push(parentGroup);
-
-							// Move up to the next parent group
-							parentGroup = parentGroup.parentGroup;
-						}
-
-						// Ensure dynamically updating position when any parent is translated
-						each(parents.reverse(), function (parentGroup) {
-							var htmlGroupStyle;
-
-							// Create a HTML div and append it to the parent div to emulate
-							// the SVG group structure
-							htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, {
-								className: attr(parentGroup.element, 'class')
-							}, {
-								position: ABSOLUTE,
-								left: (parentGroup.translateX || 0) + PX,
-								top: (parentGroup.translateY || 0) + PX
-							}, htmlGroup || container); // the top group is appended to container
-
-							// Shortcut
-							htmlGroupStyle = htmlGroup.style;
-
-							// Set listeners to update the HTML div's position whenever the SVG group
-							// position is changed
-							extend(parentGroup, {
-								translateXSetter: function (value, key) {
-									htmlGroupStyle.left = value + PX;
-									parentGroup[key] = value;
-									parentGroup.doTransform = true;
-								},
-								translateYSetter: function (value, key) {
-									htmlGroupStyle.top = value + PX;
-									parentGroup[key] = value;
-									parentGroup.doTransform = true;
-								},
-								visibilitySetter: function (value, key) {
-									htmlGroupStyle[key] = value;
-								}
-							});
-						});
-
-					}
-				} else {
-					htmlGroup = container;
-				}
-
-				htmlGroup.appendChild(element);
-
-				// Shared with VML:
-				wrapper.added = true;
-				if (wrapper.alignOnAdd) {
-					wrapper.htmlUpdateTransform();
-				}
-
-				return wrapper;
-			};
-		}
-		return wrapper;
-	}
-});
-
-/* ****************************************************************************
- *                                                                            *
- * START OF INTERNET EXPLORER <= 8 SPECIFIC CODE                              *
- *                                                                            *
- * For applications and websites that don't need IE support, like platform    *
- * targeted mobile applications and web applications, this code can be removed.               *
- *                                                                            *
- *****************************************************************************/
-
-/**
- * @constructor
- */
-var VMLRenderer, VMLElement;
-if (!hasSVG && !useCanVG) {
-
-/**
- * The VML element wrapper.
- */
-Highcharts.VMLElement = VMLElement = {
-
-	/**
-	 * Initialize a new VML element wrapper. It builds the markup as a string
-	 * to minimize DOM traffic.
-	 * @param {Object} renderer
-	 * @param {Object} nodeName
-	 */
-	init: function (renderer, nodeName) {
-		var wrapper = this,
-			markup =  ['<', nodeName, ' filled="f" stroked="f"'],
-			style = ['position: ', ABSOLUTE, ';'],
-			isDiv = nodeName === DIV;
-
-		// divs and shapes need size
-		if (nodeName === 'shape' || isDiv) {
-			style.push('left:0;top:0;width:1px;height:1px;');
-		}
-		style.push('visibility: ', isDiv ? HIDDEN : VISIBLE);
-
-		markup.push(' style="', style.join(''), '"/>');
-
-		// create element with default attributes and style
-		if (nodeName) {
-			markup = isDiv || nodeName === 'span' || nodeName === 'img' ?
-				markup.join('')
-				: renderer.prepVML(markup);
-			wrapper.element = createElement(markup);
-		}
-
-		wrapper.renderer = renderer;
-	},
-
-	/**
-	 * Add the node to the given parent
-	 * @param {Object} parent
-	 */
-	add: function (parent) {
-		var wrapper = this,
-			renderer = wrapper.renderer,
-			element = wrapper.element,
-			box = renderer.box,
-			inverted = parent && parent.inverted,
-
-			// get the parent node
-			parentNode = parent ?
-				parent.element || parent :
-				box;
-
-
-		// if the parent group is inverted, apply inversion on all children
-		if (inverted) { // only on groups
-			renderer.invertChild(element, parentNode);
-		}
-
-		// append it
-		parentNode.appendChild(element);
-
-		// align text after adding to be able to read offset
-		wrapper.added = true;
-		if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) {
-			wrapper.updateTransform();
-		}
-
-		// fire an event for internal hooks
-		if (wrapper.onAdd) {
-			wrapper.onAdd();
-		}
-
-		return wrapper;
-	},
-
-	/**
-	 * VML always uses htmlUpdateTransform
-	 */
-	updateTransform: SVGElement.prototype.htmlUpdateTransform,
-
-	/**
-	 * Set the rotation of a span with oldIE's filter
-	 */
-	setSpanRotation: function () {
-		// Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented
-		// but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+
-		// has support for CSS3 transform. The getBBox method also needs to be updated
-		// to compensate for the rotation, like it currently does for SVG.
-		// Test case: http://jsfiddle.net/highcharts/Ybt44/
-
-		var rotation = this.rotation,
-			costheta = mathCos(rotation * deg2rad),
-			sintheta = mathSin(rotation * deg2rad);
-					
-		css(this.element, {
-			filter: rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
-				', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
-				', sizingMethod=\'auto expand\')'].join('') : NONE
-		});
-	},
-
-	/**
-	 * Get the positioning correction for the span after rotating. 
-	 */
-	getSpanCorrection: function (width, baseline, alignCorrection, rotation, align) {
-
-		var costheta = rotation ? mathCos(rotation * deg2rad) : 1,
-			sintheta = rotation ? mathSin(rotation * deg2rad) : 0,
-			height = pick(this.elemHeight, this.element.offsetHeight),
-			quad,
-			nonLeft = align && align !== 'left';
-
-		// correct x and y
-		this.xCorr = costheta < 0 && -width;
-		this.yCorr = sintheta < 0 && -height;
-
-		// correct for baseline and corners spilling out after rotation
-		quad = costheta * sintheta < 0;
-		this.xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection);
-		this.yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
-		// correct for the length/height of the text
-		if (nonLeft) {
-			this.xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
-			if (rotation) {
-				this.yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
-			}
-			css(this.element, {
-				textAlign: align
-			});
-		}
-	},
-
-	/**
-	 * Converts a subset of an SVG path definition to its VML counterpart. Takes an array
-	 * as the parameter and returns a string.
-	 */
-	pathToVML: function (value) {
-		// convert paths
-		var i = value.length,
-			path = [];
-
-		while (i--) {
-
-			// Multiply by 10 to allow subpixel precision.
-			// Substracting half a pixel seems to make the coordinates
-			// align with SVG, but this hasn't been tested thoroughly
-			if (isNumber(value[i])) {
-				path[i] = mathRound(value[i] * 10) - 5;
-			} else if (value[i] === 'Z') { // close the path
-				path[i] = 'x';
-			} else {
-				path[i] = value[i];
-
-				// When the start X and end X coordinates of an arc are too close,
-				// they are rounded to the same value above. In this case, substract or 
-				// add 1 from the end X and Y positions. #186, #760, #1371, #1410.
-				if (value.isArc && (value[i] === 'wa' || value[i] === 'at')) {
-					// Start and end X
-					if (path[i + 5] === path[i + 7]) {
-						path[i + 7] += value[i + 7] > value[i + 5] ? 1 : -1;
-					}
-					// Start and end Y
-					if (path[i + 6] === path[i + 8]) {
-						path[i + 8] += value[i + 8] > value[i + 6] ? 1 : -1;
-					}
-				}
-			}
-		}
-
-		
-		// Loop up again to handle path shortcuts (#2132)
-		/*while (i++ < path.length) {
-			if (path[i] === 'H') { // horizontal line to
-				path[i] = 'L';
-				path.splice(i + 2, 0, path[i - 1]);
-			} else if (path[i] === 'V') { // vertical line to
-				path[i] = 'L';
-				path.splice(i + 1, 0, path[i - 2]);
-			}
-		}*/
-		return path.join(' ') || 'x';
-	},
-
-	/**
-	 * Set the element's clipping to a predefined rectangle
-	 *
-	 * @param {String} id The id of the clip rectangle
-	 */
-	clip: function (clipRect) {
-		var wrapper = this,
-			clipMembers,
-			cssRet;
-
-		if (clipRect) {
-			clipMembers = clipRect.members;
-			erase(clipMembers, wrapper); // Ensure unique list of elements (#1258)
-			clipMembers.push(wrapper);
-			wrapper.destroyClip = function () {
-				erase(clipMembers, wrapper);
-			};
-			cssRet = clipRect.getCSS(wrapper);
-
-		} else {
-			if (wrapper.destroyClip) {
-				wrapper.destroyClip();
-			}
-			cssRet = { clip: docMode8 ? 'inherit' : 'rect(auto)' }; // #1214
-		}
-
-		return wrapper.css(cssRet);
-
-	},
-
-	/**
-	 * Set styles for the element
-	 * @param {Object} styles
-	 */
-	css: SVGElement.prototype.htmlCss,
-
-	/**
-	 * Removes a child either by removeChild or move to garbageBin.
-	 * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
-	 */
-	safeRemoveChild: function (element) {
-		// discardElement will detach the node from its parent before attaching it
-		// to the garbage bin. Therefore it is important that the node is attached and have parent.
-		if (element.parentNode) {
-			discardElement(element);
-		}
-	},
-
-	/**
-	 * Extend element.destroy by removing it from the clip members array
-	 */
-	destroy: function () {
-		if (this.destroyClip) {
-			this.destroyClip();
-		}
-
-		return SVGElement.prototype.destroy.apply(this);
-	},
-
-	/**
-	 * Add an event listener. VML override for normalizing event parameters.
-	 * @param {String} eventType
-	 * @param {Function} handler
-	 */
-	on: function (eventType, handler) {
-		// simplest possible event model for internal use
-		this.element['on' + eventType] = function () {
-			var evt = win.event;
-			evt.target = evt.srcElement;
-			handler(evt);
-		};
-		return this;
-	},
-
-	/**
-	 * In stacked columns, cut off the shadows so that they don't overlap
-	 */
-	cutOffPath: function (path, length) {
-
-		var len;
-
-		path = path.split(/[ ,]/);
-		len = path.length;
-
-		if (len === 9 || len === 11) {
-			path[len - 4] = path[len - 2] = pInt(path[len - 2]) - 10 * length;
-		}
-		return path.join(' ');
-	},
-
-	/**
-	 * Apply a drop shadow by copying elements and giving them different strokes
-	 * @param {Boolean|Object} shadowOptions
-	 */
-	shadow: function (shadowOptions, group, cutOff) {
-		var shadows = [],
-			i,
-			element = this.element,
-			renderer = this.renderer,
-			shadow,
-			elemStyle = element.style,
-			markup,
-			path = element.path,
-			strokeWidth,
-			modifiedPath,
-			shadowWidth,
-			shadowElementOpacity;
-
-		// some times empty paths are not strings
-		if (path && typeof path.value !== 'string') {
-			path = 'x';
-		}
-		modifiedPath = path;
-
-		if (shadowOptions) {
-			shadowWidth = pick(shadowOptions.width, 3);
-			shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
-			for (i = 1; i <= 3; i++) {
-
-				strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
-
-				// Cut off shadows for stacked column items
-				if (cutOff) {
-					modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5);
-				}
-
-				markup = ['<shape isShadow="true" strokeweight="', strokeWidth,
-					'" filled="false" path="', modifiedPath,
-					'" coordsize="10 10" style="', element.style.cssText, '" />'];
-
-				shadow = createElement(renderer.prepVML(markup),
-					null, {
-						left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1),
-						top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1)
-					}
-				);
-				if (cutOff) {
-					shadow.cutOff = strokeWidth + 1;
-				}
-
-				// apply the opacity
-				markup = ['<stroke color="', shadowOptions.color || 'black', '" opacity="', shadowElementOpacity * i, '"/>'];
-				createElement(renderer.prepVML(markup), null, null, shadow);
-
-
-				// insert it
-				if (group) {
-					group.element.appendChild(shadow);
-				} else {
-					element.parentNode.insertBefore(shadow, element);
-				}
-
-				// record it
-				shadows.push(shadow);
-
-			}
-
-			this.shadows = shadows;
-		}
-		return this;
-	},
-	updateShadows: noop, // Used in SVG only
-
-	setAttr: function (key, value) {
-		if (docMode8) { // IE8 setAttribute bug
-			this.element[key] = value;
-		} else {
-			this.element.setAttribute(key, value);
-		}
-	},
-	classSetter: function (value) {
-		// IE8 Standards mode has problems retrieving the className unless set like this
-		this.element.className = value;
-	},
-	dashstyleSetter: function (value, key, element) {
-		var strokeElem = element.getElementsByTagName('stroke')[0] ||
-			createElement(this.renderer.prepVML(['<stroke/>']), null, null, element);
-		strokeElem[key] = value || 'solid';
-		this[key] = value; /* because changing stroke-width will change the dash length
-			and cause an epileptic effect */
-	},
-	dSetter: function (value, key, element) {
-		var i,
-			shadows = this.shadows;
-		value = value || [];
-		this.d = value.join(' '); // used in getter for animation
-
-		element.path = value = this.pathToVML(value);
-
-		// update shadows
-		if (shadows) {
-			i = shadows.length;
-			while (i--) {
-				shadows[i].path = shadows[i].cutOff ? this.cutOffPath(value, shadows[i].cutOff) : value;
-			}
-		}
-		this.setAttr(key, value);
-	},
-	fillSetter: function (value, key, element) {
-		var nodeName = element.nodeName;
-		if (nodeName === 'SPAN') { // text color
-			element.style.color = value;
-		} else if (nodeName !== 'IMG') { // #1336
-			element.filled = value !== NONE;
-			this.setAttr('fillcolor', this.renderer.color(value, element, key, this));
-		}
-	},
-	opacitySetter: noop, // Don't bother - animation is too slow and filters introduce artifacts
-	rotationSetter: function (value, key, element) {
-		var style = element.style;
-		this[key] = style[key] = value; // style is for #1873
-
-		// Correction for the 1x1 size of the shape container. Used in gauge needles.
-		style.left = -mathRound(mathSin(value * deg2rad) + 1) + PX;
-		style.top = mathRound(mathCos(value * deg2rad)) + PX;
-	},
-	strokeSetter: function (value, key, element) {
-		this.setAttr('strokecolor', this.renderer.color(value, element, key));
-	},
-	'stroke-widthSetter': function (value, key, element) {
-		element.stroked = !!value; // VML "stroked" attribute
-		this[key] = value; // used in getter, issue #113
-		if (isNumber(value)) {
-			value += PX;
-		}
-		this.setAttr('strokeweight', value);
-	},
-	titleSetter: function (value, key) {
-		this.setAttr(key, value);
-	},
-	visibilitySetter: function (value, key, element) {
-
-		// Handle inherited visibility
-		if (value === 'inherit') {
-			value = VISIBLE;
-		}
-		
-		// Let the shadow follow the main element
-		if (this.shadows) {
-			each(this.shadows, function (shadow) {
-				shadow.style[key] = value;
-			});
-		}
-
-		// Instead of toggling the visibility CSS property, move the div out of the viewport.
-		// This works around #61 and #586
-		if (element.nodeName === 'DIV') {
-			value = value === HIDDEN ? '-999em' : 0;
-
-			// In order to redraw, IE7 needs the div to be visible when tucked away
-			// outside the viewport. So the visibility is actually opposite of
-			// the expected value. This applies to the tooltip only.
-			if (!docMode8) {
-				element.style[key] = value ? VISIBLE : HIDDEN;
-			}
-			key = 'top';
-		}
-		element.style[key] = value;
-	},
-	xSetter: function (value, key, element) {
-		this[key] = value; // used in getter
-
-		if (key === 'x') {
-			key = 'left';
-		} else if (key === 'y') {
-			key = 'top';
-		}/* else {
-			value = mathMax(0, value); // don't set width or height below zero (#311)
-		}*/
-
-		// clipping rectangle special
-		if (this.updateClipping) {
-			this[key] = value; // the key is now 'left' or 'top' for 'x' and 'y'
-			this.updateClipping();
-		} else {
-			// normal
-			element.style[key] = value;
-		}
-	},
-	zIndexSetter: function (value, key, element) {
-		element.style[key] = value;
-	}
-};
-VMLElement = extendClass(SVGElement, VMLElement);
-
-// Some shared setters
-VMLElement.prototype.ySetter =
-	VMLElement.prototype.widthSetter = 
-	VMLElement.prototype.heightSetter = 
-	VMLElement.prototype.xSetter;
-
-
-/**
- * The VML renderer
- */
-var VMLRendererExtension = { // inherit SVGRenderer
-
-	Element: VMLElement,
-	isIE8: userAgent.indexOf('MSIE 8.0') > -1,
-
-
-	/**
-	 * Initialize the VMLRenderer
-	 * @param {Object} container
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	init: function (container, width, height, style) {
-		var renderer = this,
-			boxWrapper,
-			box,
-			css;
-
-		renderer.alignedObjects = [];
-
-		boxWrapper = renderer.createElement(DIV)
-			.css(extend(this.getStyle(style), { position: RELATIVE}));
-		box = boxWrapper.element;
-		container.appendChild(boxWrapper.element);
-
-
-		// generate the containing box
-		renderer.isVML = true;
-		renderer.box = box;
-		renderer.boxWrapper = boxWrapper;
-		renderer.cache = {};
-
-
-		renderer.setSize(width, height, false);
-
-		// The only way to make IE6 and IE7 print is to use a global namespace. However,
-		// with IE8 the only way to make the dynamic shapes visible in screen and print mode
-		// seems to be to add the xmlns attribute and the behaviour style inline.
-		if (!doc.namespaces.hcv) {
-
-			doc.namespaces.add('hcv', 'urn:schemas-microsoft-com:vml');
-
-			// Setup default CSS (#2153, #2368, #2384)
-			css = 'hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke' +
-				'{ behavior:url(#default#VML); display: inline-block; } ';
-			try {
-				doc.createStyleSheet().cssText = css;
-			} catch (e) {
-				doc.styleSheets[0].cssText += css;
-			}
-
-		}
-	},
-
-
-	/**
-	 * Detect whether the renderer is hidden. This happens when one of the parent elements
-	 * has display: none
-	 */
-	isHidden: function () {
-		return !this.box.offsetWidth;
-	},
-
-	/**
-	 * Define a clipping rectangle. In VML it is accomplished by storing the values
-	 * for setting the CSS style to all associated members.
-	 *
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	clipRect: function (x, y, width, height) {
-
-		// create a dummy element
-		var clipRect = this.createElement(),
-			isObj = isObject(x);
-
-		// mimic a rectangle with its style object for automatic updating in attr
-		return extend(clipRect, {
-			members: [],
-			left: (isObj ? x.x : x) + 1,
-			top: (isObj ? x.y : y) + 1,
-			width: (isObj ? x.width : width) - 1,
-			height: (isObj ? x.height : height) - 1,
-			getCSS: function (wrapper) {
-				var element = wrapper.element,
-					nodeName = element.nodeName,
-					isShape = nodeName === 'shape',
-					inverted = wrapper.inverted,
-					rect = this,
-					top = rect.top - (isShape ? element.offsetTop : 0),
-					left = rect.left,
-					right = left + rect.width,
-					bottom = top + rect.height,
-					ret = {
-						clip: 'rect(' +
-							mathRound(inverted ? left : top) + 'px,' +
-							mathRound(inverted ? bottom : right) + 'px,' +
-							mathRound(inverted ? right : bottom) + 'px,' +
-							mathRound(inverted ? top : left) + 'px)'
-					};
-
-				// issue 74 workaround
-				if (!inverted && docMode8 && nodeName === 'DIV') {
-					extend(ret, {
-						width: right + PX,
-						height: bottom + PX
-					});
-				}
-				return ret;
-			},
-
-			// used in attr and animation to update the clipping of all members
-			updateClipping: function () {
-				each(clipRect.members, function (member) {
-					if (member.element) { // Deleted series, like in stock/members/series-remove demo. Should be removed from members, but this will do.
-						member.css(clipRect.getCSS(member));
-					}
-				});
-			}
-		});
-
-	},
-
-
-	/**
-	 * Take a color and return it if it's a string, make it a gradient if it's a
-	 * gradient configuration object, and apply opacity.
-	 *
-	 * @param {Object} color The color or config object
-	 */
-	color: function (color, elem, prop, wrapper) {
-		var renderer = this,
-			colorObject,
-			regexRgba = /^rgba/,
-			markup,
-			fillType,
-			ret = NONE;
-
-		// Check for linear or radial gradient
-		if (color && color.linearGradient) {
-			fillType = 'gradient';
-		} else if (color && color.radialGradient) {
-			fillType = 'pattern';
-		}
-
-
-		if (fillType) {
-
-			var stopColor,
-				stopOpacity,
-				gradient = color.linearGradient || color.radialGradient,
-				x1,
-				y1,
-				x2,
-				y2,
-				opacity1,
-				opacity2,
-				color1,
-				color2,
-				fillAttr = '',
-				stops = color.stops,
-				firstStop,
-				lastStop,
-				colors = [],
-				addFillNode = function () {
-					// Add the fill subnode. When colors attribute is used, the meanings of opacity and o:opacity2
-					// are reversed.
-					markup = ['<fill colors="' + colors.join(',') + '" opacity="', opacity2, '" o:opacity2="', opacity1,
-						'" type="', fillType, '" ', fillAttr, 'focus="100%" method="any" />'];
-					createElement(renderer.prepVML(markup), null, null, elem);
-				};
-
-			// Extend from 0 to 1
-			firstStop = stops[0];
-			lastStop = stops[stops.length - 1];
-			if (firstStop[0] > 0) {
-				stops.unshift([
-					0,
-					firstStop[1]
-				]);
-			}
-			if (lastStop[0] < 1) {
-				stops.push([
-					1,
-					lastStop[1]
-				]);
-			}
-
-			// Compute the stops
-			each(stops, function (stop, i) {
-				if (regexRgba.test(stop[1])) {
-					colorObject = Color(stop[1]);
-					stopColor = colorObject.get('rgb');
-					stopOpacity = colorObject.get('a');
-				} else {
-					stopColor = stop[1];
-					stopOpacity = 1;
-				}
-
-				// Build the color attribute
-				colors.push((stop[0] * 100) + '% ' + stopColor);
-
-				// Only start and end opacities are allowed, so we use the first and the last
-				if (!i) {
-					opacity1 = stopOpacity;
-					color2 = stopColor;
-				} else {
-					opacity2 = stopOpacity;
-					color1 = stopColor;
-				}
-			});
-
-			// Apply the gradient to fills only.
-			if (prop === 'fill') {
-
-				// Handle linear gradient angle
-				if (fillType === 'gradient') {
-					x1 = gradient.x1 || gradient[0] || 0;
-					y1 = gradient.y1 || gradient[1] || 0;
-					x2 = gradient.x2 || gradient[2] || 0;
-					y2 = gradient.y2 || gradient[3] || 0;
-					fillAttr = 'angle="' + (90  - math.atan(
-						(y2 - y1) / // y vector
-						(x2 - x1) // x vector
-						) * 180 / mathPI) + '"';
-
-					addFillNode();
-
-				// Radial (circular) gradient
-				} else {
-
-					var r = gradient.r,
-						sizex = r * 2,
-						sizey = r * 2,
-						cx = gradient.cx,
-						cy = gradient.cy,
-						radialReference = elem.radialReference,
-						bBox,
-						applyRadialGradient = function () {
-							if (radialReference) {
-								bBox = wrapper.getBBox();
-								cx += (radialReference[0] - bBox.x) / bBox.width - 0.5;
-								cy += (radialReference[1] - bBox.y) / bBox.height - 0.5;
-								sizex *= radialReference[2] / bBox.width;
-								sizey *= radialReference[2] / bBox.height;
-							}
-							fillAttr = 'src="' + defaultOptions.global.VMLRadialGradientURL + '" ' +
-								'size="' + sizex + ',' + sizey + '" ' +
-								'origin="0.5,0.5" ' +
-								'position="' + cx + ',' + cy + '" ' +
-								'color2="' + color2 + '" ';
-
-							addFillNode();
-						};
-
-					// Apply radial gradient
-					if (wrapper.added) {
-						applyRadialGradient();
-					} else {
-						// We need to know the bounding box to get the size and position right
-						wrapper.onAdd = applyRadialGradient;
-					}
-
-					// The fill element's color attribute is broken in IE8 standards mode, so we
-					// need to set the parent shape's fillcolor attribute instead.
-					ret = color1;
-				}
-
-			// Gradients are not supported for VML stroke, return the first color. #722.
-			} else {
-				ret = stopColor;
-			}
-
-		// if the color is an rgba color, split it and add a fill node
-		// to hold the opacity component
-		} else if (regexRgba.test(color) && elem.tagName !== 'IMG') {
-
-			colorObject = Color(color);
-
-			markup = ['<', prop, ' opacity="', colorObject.get('a'), '"/>'];
-			createElement(this.prepVML(markup), null, null, elem);
-
-			ret = colorObject.get('rgb');
-
-
-		} else {
-			var propNodes = elem.getElementsByTagName(prop); // 'stroke' or 'fill' node
-			if (propNodes.length) {
-				propNodes[0].opacity = 1;
-				propNodes[0].type = 'solid';
-			}
-			ret = color;
-		}
-
-		return ret;
-	},
-
-	/**
-	 * Take a VML string and prepare it for either IE8 or IE6/IE7.
-	 * @param {Array} markup A string array of the VML markup to prepare
-	 */
-	prepVML: function (markup) {
-		var vmlStyle = 'display:inline-block;behavior:url(#default#VML);',
-			isIE8 = this.isIE8;
-
-		markup = markup.join('');
-
-		if (isIE8) { // add xmlns and style inline
-			markup = markup.replace('/>', ' xmlns="urn:schemas-microsoft-com:vml" />');
-			if (markup.indexOf('style="') === -1) {
-				markup = markup.replace('/>', ' style="' + vmlStyle + '" />');
-			} else {
-				markup = markup.replace('style="', 'style="' + vmlStyle);
-			}
-
-		} else { // add namespace
-			markup = markup.replace('<', '<hcv:');
-		}
-
-		return markup;
-	},
-
-	/**
-	 * Create rotated and aligned text
-	 * @param {String} str
-	 * @param {Number} x
-	 * @param {Number} y
-	 */
-	text: SVGRenderer.prototype.html,
-
-	/**
-	 * Create and return a path element
-	 * @param {Array} path
-	 */
-	path: function (path) {
-		var attr = {
-			// subpixel precision down to 0.1 (width and height = 1px)
-			coordsize: '10 10'
-		};
-		if (isArray(path)) {
-			attr.d = path;
-		} else if (isObject(path)) { // attributes
-			extend(attr, path);
-		}
-		// create the shape
-		return this.createElement('shape').attr(attr);
-	},
-
-	/**
-	 * Create and return a circle element. In VML circles are implemented as
-	 * shapes, which is faster than v:oval
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} r
-	 */
-	circle: function (x, y, r) {
-		var circle = this.symbol('circle');
-		if (isObject(x)) {
-			r = x.r;
-			y = x.y;
-			x = x.x;
-		}
-		circle.isCircle = true; // Causes x and y to mean center (#1682)
-		circle.r = r;
-		return circle.attr({ x: x, y: y });
-	},
-
-	/**
-	 * Create a group using an outer div and an inner v:group to allow rotating
-	 * and flipping. A simple v:group would have problems with positioning
-	 * child HTML elements and CSS clip.
-	 *
-	 * @param {String} name The name of the group
-	 */
-	g: function (name) {
-		var wrapper,
-			attribs;
-
-		// set the class name
-		if (name) {
-			attribs = { 'className': PREFIX + name, 'class': PREFIX + name };
-		}
-
-		// the div to hold HTML and clipping
-		wrapper = this.createElement(DIV).attr(attribs);
-
-		return wrapper;
-	},
-
-	/**
-	 * VML override to create a regular HTML image
-	 * @param {String} src
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @param {Number} width
-	 * @param {Number} height
-	 */
-	image: function (src, x, y, width, height) {
-		var obj = this.createElement('img')
-			.attr({ src: src });
-
-		if (arguments.length > 1) {
-			obj.attr({
-				x: x,
-				y: y,
-				width: width,
-				height: height
-			});
-		}
-		return obj;
-	},
-
-	/**
-	 * For rectangles, VML uses a shape for rect to overcome bugs and rotation problems
-	 */
-	createElement: function (nodeName) {
-		return nodeName === 'rect' ? this.symbol(nodeName) : SVGRenderer.prototype.createElement.call(this, nodeName);	
-	},
-
-	/**
-	 * In the VML renderer, each child of an inverted div (group) is inverted
-	 * @param {Object} element
-	 * @param {Object} parentNode
-	 */
-	invertChild: function (element, parentNode) {
-		var ren = this,
-			parentStyle = parentNode.style,
-			imgStyle = element.tagName === 'IMG' && element.style; // #1111
-
-		css(element, {
-			flip: 'x',
-			left: pInt(parentStyle.width) - (imgStyle ? pInt(imgStyle.top) : 1),
-			top: pInt(parentStyle.height) - (imgStyle ? pInt(imgStyle.left) : 1),
-			rotation: -90
-		});
-
-		// Recursively invert child elements, needed for nested composite shapes like box plots and error bars. #1680, #1806.
-		each(element.childNodes, function (child) {
-			ren.invertChild(child, element);
-		});
-	},
-
-	/**
-	 * Symbol definitions that override the parent SVG renderer's symbols
-	 *
-	 */
-	symbols: {
-		// VML specific arc function
-		arc: function (x, y, w, h, options) {
-			var start = options.start,
-				end = options.end,
-				radius = options.r || w || h,
-				innerRadius = options.innerR,
-				cosStart = mathCos(start),
-				sinStart = mathSin(start),
-				cosEnd = mathCos(end),
-				sinEnd = mathSin(end),
-				ret;
-
-			if (end - start === 0) { // no angle, don't show it.
-				return ['x'];
-			}
-
-			ret = [
-				'wa', // clockwise arc to
-				x - radius, // left
-				y - radius, // top
-				x + radius, // right
-				y + radius, // bottom
-				x + radius * cosStart, // start x
-				y + radius * sinStart, // start y
-				x + radius * cosEnd, // end x
-				y + radius * sinEnd  // end y
-			];
-
-			if (options.open && !innerRadius) {
-				ret.push(
-					'e',
-					M,
-					x,// - innerRadius,
-					y// - innerRadius
-				);
-			}
-
-			ret.push(
-				'at', // anti clockwise arc to
-				x - innerRadius, // left
-				y - innerRadius, // top
-				x + innerRadius, // right
-				y + innerRadius, // bottom
-				x + innerRadius * cosEnd, // start x
-				y + innerRadius * sinEnd, // start y
-				x + innerRadius * cosStart, // end x
-				y + innerRadius * sinStart, // end y
-				'x', // finish path
-				'e' // close
-			);
-
-			ret.isArc = true;
-			return ret;
-
-		},
-		// Add circle symbol path. This performs significantly faster than v:oval.
-		circle: function (x, y, w, h, wrapper) {
-
-			if (wrapper) {
-				w = h = 2 * wrapper.r;
-			}
-
-			// Center correction, #1682
-			if (wrapper && wrapper.isCircle) {
-				x -= w / 2;
-				y -= h / 2;
-			}
-
-			// Return the path
-			return [
-				'wa', // clockwisearcto
-				x, // left
-				y, // top
-				x + w, // right
-				y + h, // bottom
-				x + w, // start x
-				y + h / 2,     // start y
-				x + w, // end x
-				y + h / 2,     // end y
-				//'x', // finish path
-				'e' // close
-			];
-		},
-		/**
-		 * Add rectangle symbol path which eases rotation and omits arcsize problems
-		 * compared to the built-in VML roundrect shape. When borders are not rounded,
-		 * use the simpler square path, else use the callout path without the arrow.
-		 */
-		rect: function (x, y, w, h, options) {
-			return SVGRenderer.prototype.symbols[
-				!defined(options) || !options.r ? 'square' : 'callout'
-			].call(0, x, y, w, h, options);
-		}
-	}
-};
-Highcharts.VMLRenderer = VMLRenderer = function () {
-	this.init.apply(this, arguments);
-};
-VMLRenderer.prototype = merge(SVGRenderer.prototype, VMLRendererExtension);
-
-	// general renderer
-	Renderer = VMLRenderer;
-}
-
-// This method is used with exporting in old IE, when emulating SVG (see #2314)
-SVGRenderer.prototype.measureSpanWidth = function (text, styles) {
-	var measuringSpan = doc.createElement('span'),
-		offsetWidth,
-	textNode = doc.createTextNode(text);
-
-	measuringSpan.appendChild(textNode);
-	css(measuringSpan, styles);
-	this.box.appendChild(measuringSpan);
-	offsetWidth = measuringSpan.offsetWidth;
-	discardElement(measuringSpan); // #2463
-	return offsetWidth;
-};
-
-
-/* ****************************************************************************
- *                                                                            *
- * END OF INTERNET EXPLORER <= 8 SPECIFIC CODE                                *
- *                                                                            *
- *****************************************************************************/
-/* ****************************************************************************
- *                                                                            *
- * START OF ANDROID < 3 SPECIFIC CODE. THIS CAN BE REMOVED IF YOU'RE NOT      *
- * TARGETING THAT SYSTEM.                                                     *
- *                                                                            *
- *****************************************************************************/
-var CanVGRenderer,
-	CanVGController;
-
-if (useCanVG) {
-	/**
-	 * The CanVGRenderer is empty from start to keep the source footprint small.
-	 * When requested, the CanVGController downloads the rest of the source packaged
-	 * together with the canvg library.
-	 */
-	Highcharts.CanVGRenderer = CanVGRenderer = function () {
-		// Override the global SVG namespace to fake SVG/HTML that accepts CSS
-		SVG_NS = 'http://www.w3.org/1999/xhtml';
-	};
-
-	/**
-	 * Start with an empty symbols object. This is needed when exporting is used (exporting.src.js will add a few symbols), but 
-	 * the implementation from SvgRenderer will not be merged in until first render.
-	 */
-	CanVGRenderer.prototype.symbols = {};
-
-	/**
-	 * Handles on demand download of canvg rendering support.
-	 */
-	CanVGController = (function () {
-		// List of renderering calls
-		var deferredRenderCalls = [];
-
-		/**
-		 * When downloaded, we are ready to draw deferred charts.
-		 */
-		function drawDeferred() {
-			var callLength = deferredRenderCalls.length,
-				callIndex;
-
-			// Draw all pending render calls
-			for (callIndex = 0; callIndex < callLength; callIndex++) {
-				deferredRenderCalls[callIndex]();
-			}
-			// Clear the list
-			deferredRenderCalls = [];
-		}
-
-		return {
-			push: function (func, scriptLocation) {
-				// Only get the script once
-				if (deferredRenderCalls.length === 0) {
-					getScript(scriptLocation, drawDeferred);
-				}
-				// Register render call
-				deferredRenderCalls.push(func);
-			}
-		};
-	}());
-
-	Renderer = CanVGRenderer;
-} // end CanVGRenderer
-
-/* ****************************************************************************
- *                                                                            *
- * END OF ANDROID < 3 SPECIFIC CODE                                           *
- *                                                                            *
- *****************************************************************************/
-
-/**
- * The Tick class
- */
-function Tick(axis, pos, type, noLabel) {
-	this.axis = axis;
-	this.pos = pos;
-	this.type = type || '';
-	this.isNew = true;
-
-	if (!type && !noLabel) {
-		this.addLabel();
-	}
-}
-
-Tick.prototype = {
-	/**
-	 * Write the tick label
-	 */
-	addLabel: function () {
-		var tick = this,
-			axis = tick.axis,
-			options = axis.options,
-			chart = axis.chart,
-			horiz = axis.horiz,
-			categories = axis.categories,
-			names = axis.names,
-			pos = tick.pos,
-			labelOptions = options.labels,
-			str,
-			tickPositions = axis.tickPositions,
-			width = (horiz && categories &&
-				!labelOptions.step && !labelOptions.staggerLines &&
-				!labelOptions.rotation &&
-				chart.plotWidth / tickPositions.length) ||
-				(!horiz && (chart.margin[3] || chart.chartWidth * 0.33)), // #1580, #1931
-			isFirst = pos === tickPositions[0],
-			isLast = pos === tickPositions[tickPositions.length - 1],
-			css,
-			attr,
-			value = categories ?
-				pick(categories[pos], names[pos], pos) :
-				pos,
-			label = tick.label,
-			tickPositionInfo = tickPositions.info,
-			dateTimeLabelFormat;
-
-		// Set the datetime label format. If a higher rank is set for this position, use that. If not,
-		// use the general format.
-		if (axis.isDatetimeAxis && tickPositionInfo) {
-			dateTimeLabelFormat = options.dateTimeLabelFormats[tickPositionInfo.higherRanks[pos] || tickPositionInfo.unitName];
-		}
-		// set properties for access in render method
-		tick.isFirst = isFirst;
-		tick.isLast = isLast;
-
-		// get the string
-		str = axis.labelFormatter.call({
-			axis: axis,
-			chart: chart,
-			isFirst: isFirst,
-			isLast: isLast,
-			dateTimeLabelFormat: dateTimeLabelFormat,
-			value: axis.isLog ? correctFloat(lin2log(value)) : value
-		});
-
-		// prepare CSS
-		css = width && { width: mathMax(1, mathRound(width - 2 * (labelOptions.padding || 10))) + PX };
-		css = extend(css, labelOptions.style);
-
-		// first call
-		if (!defined(label)) {
-			attr = {
-				align: axis.labelAlign
-			};
-			if (isNumber(labelOptions.rotation)) {
-				attr.rotation = labelOptions.rotation;
-			}
-			if (width && labelOptions.ellipsis) {
-				attr._clipHeight = axis.len / tickPositions.length;
-			}
-
-			tick.label =
-				defined(str) && labelOptions.enabled ?
-					chart.renderer.text(
-							str,
-							0,
-							0,
-							labelOptions.useHTML
-						)
-						.attr(attr)
-						// without position absolute, IE export sometimes is wrong
-						.css(css)
-						.add(axis.labelGroup) :
-					null;
-
-		// update
-		} else if (label) {
-			label.attr({
-					text: str
-				})
-				.css(css);
-		}
-	},
-
-	/**
-	 * Get the offset height or width of the label
-	 */
-	getLabelSize: function () {
-		var label = this.label,
-			axis = this.axis;
-		return label ?
-			label.getBBox()[axis.horiz ? 'height' : 'width'] :
-			0;
-	},
-
-	/**
-	 * Find how far the labels extend to the right and left of the tick's x position. Used for anti-collision
-	 * detection with overflow logic.
-	 */
-	getLabelSides: function () {
-		var bBox = this.label.getBBox(),
-			axis = this.axis,
-			horiz = axis.horiz,
-			options = axis.options,
-			labelOptions = options.labels,
-			size = horiz ? bBox.width : bBox.height,
-			leftSide = horiz ?
-				labelOptions.x - size * { left: 0, center: 0.5, right: 1 }[axis.labelAlign] :
-				0,
-			rightSide = horiz ?
-				size + leftSide :
-				size;
-
-		return [leftSide, rightSide];
-	},
-
-	/**
-	 * Handle the label overflow by adjusting the labels to the left and right edge, or
-	 * hide them if they collide into the neighbour label.
-	 */
-	handleOverflow: function (index, xy) {
-		var show = true,
-			axis = this.axis,
-			isFirst = this.isFirst,
-			isLast = this.isLast,
-			horiz = axis.horiz,
-			pxPos = horiz ? xy.x : xy.y,
-			reversed = axis.reversed,
-			tickPositions = axis.tickPositions,
-			sides = this.getLabelSides(),
-			leftSide = sides[0],
-			rightSide = sides[1],
-			axisLeft,
-			axisRight,
-			neighbour,
-			neighbourEdge,
-			line = this.label.line || 0,
-			labelEdge = axis.labelEdge,
-			justifyLabel = axis.justifyLabels && (isFirst || isLast),
-			justifyToPlot;
-
-		// Hide it if it now overlaps the neighbour label
-		if (labelEdge[line] === UNDEFINED || pxPos + leftSide > labelEdge[line]) {
-			labelEdge[line] = pxPos + rightSide;
-
-		} else if (!justifyLabel) {
-			show = false;
-		}
-
-		if (justifyLabel) {
-			justifyToPlot = axis.justifyToPlot;
-			axisLeft = justifyToPlot ? axis.pos : 0;
-			axisRight = justifyToPlot ? axisLeft + axis.len : axis.chart.chartWidth;
-
-			// Find the firsth neighbour on the same line
-			do {
-				index += (isFirst ? 1 : -1);
-				neighbour = axis.ticks[tickPositions[index]];
-			} while (tickPositions[index] && (!neighbour || neighbour.label.line !== line));
-
-			neighbourEdge = neighbour && neighbour.label.xy && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1];
-
-			if ((isFirst && !reversed) || (isLast && reversed)) {
-				// Is the label spilling out to the left of the plot area?
-				if (pxPos + leftSide < axisLeft) {
-
-					// Align it to plot left
-					pxPos = axisLeft - leftSide;
-
-					// Hide it if it now overlaps the neighbour label
-					if (neighbour && pxPos + rightSide > neighbourEdge) {
-						show = false;
-					}
-				}
-
-			} else {
-				// Is the label spilling out to the right of the plot area?
-				if (pxPos + rightSide > axisRight) {
-
-					// Align it to plot right
-					pxPos = axisRight - rightSide;
-
-					// Hide it if it now overlaps the neighbour label
-					if (neighbour && pxPos + leftSide < neighbourEdge) {
-						show = false;
-					}
-
-				}
-			}
-
-			// Set the modified x position of the label
-			xy.x = pxPos;
-		}
-		return show;
-	},
-
-	/**
-	 * Get the x and y position for ticks and labels
-	 */
-	getPosition: function (horiz, pos, tickmarkOffset, old) {
-		var axis = this.axis,
-			chart = axis.chart,
-			cHeight = (old && chart.oldChartHeight) || chart.chartHeight;
-
-		return {
-			x: horiz ?
-				axis.translate(pos + tickmarkOffset, null, null, old) + axis.transB :
-				axis.left + axis.offset + (axis.opposite ? ((old && chart.oldChartWidth) || chart.chartWidth) - axis.right - axis.left : 0),
-
-			y: horiz ?
-				cHeight - axis.bottom + axis.offset - (axis.opposite ? axis.height : 0) :
-				cHeight - axis.translate(pos + tickmarkOffset, null, null, old) - axis.transB
-		};
-
-	},
-
-	/**
-	 * Get the x, y position of the tick label
-	 */
-	getLabelPosition: function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
-		var axis = this.axis,
-			transA = axis.transA,
-			reversed = axis.reversed,
-			staggerLines = axis.staggerLines,
-			baseline = axis.chart.renderer.fontMetrics(labelOptions.style.fontSize).b,
-			rotation = labelOptions.rotation;
-
-		x = x + labelOptions.x - (tickmarkOffset && horiz ?
-			tickmarkOffset * transA * (reversed ? -1 : 1) : 0);
-		y = y + labelOptions.y - (tickmarkOffset && !horiz ?
-			tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
-
-		// Correct for rotation (#1764)
-		if (rotation && axis.side === 2) {
-			y -= baseline - baseline * mathCos(rotation * deg2rad);
-		}
-
-		// Vertically centered
-		if (!defined(labelOptions.y) && !rotation) { // #1951
-			y += baseline - label.getBBox().height / 2;
-		}
-
-		// Correct for staggered labels
-		if (staggerLines) {
-			label.line = (index / (step || 1) % staggerLines);
-			y += label.line * (axis.labelOffset / staggerLines);
-		}
-
-		return {
-			x: x,
-			y: y
-		};
-	},
-
-	/**
-	 * Extendible method to return the path of the marker
-	 */
-	getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) {
-		return renderer.crispLine([
-				M,
-				x,
-				y,
-				L,
-				x + (horiz ? 0 : -tickLength),
-				y + (horiz ? tickLength : 0)
-			], tickWidth);
-	},
-
-	/**
-	 * Put everything in place
-	 *
-	 * @param index {Number}
-	 * @param old {Boolean} Use old coordinates to prepare an animation into new position
-	 */
-	render: function (index, old, opacity) {
-		var tick = this,
-			axis = tick.axis,
-			options = axis.options,
-			chart = axis.chart,
-			renderer = chart.renderer,
-			horiz = axis.horiz,
-			type = tick.type,
-			label = tick.label,
-			pos = tick.pos,
-			labelOptions = options.labels,
-			gridLine = tick.gridLine,
-			gridPrefix = type ? type + 'Grid' : 'grid',
-			tickPrefix = type ? type + 'Tick' : 'tick',
-			gridLineWidth = options[gridPrefix + 'LineWidth'],
-			gridLineColor = options[gridPrefix + 'LineColor'],
-			dashStyle = options[gridPrefix + 'LineDashStyle'],
-			tickLength = options[tickPrefix + 'Length'],
-			tickWidth = options[tickPrefix + 'Width'] || 0,
-			tickColor = options[tickPrefix + 'Color'],
-			tickPosition = options[tickPrefix + 'Position'],
-			gridLinePath,
-			mark = tick.mark,
-			markPath,
-			step = labelOptions.step,
-			attribs,
-			show = true,
-			tickmarkOffset = axis.tickmarkOffset,
-			xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
-			x = xy.x,
-			y = xy.y,
-			reverseCrisp = ((horiz && x === axis.pos + axis.len) || (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
-
-		this.isActive = true;
-
-		// create the grid line
-		if (gridLineWidth) {
-			gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLineWidth * reverseCrisp, old, true);
-
-			if (gridLine === UNDEFINED) {
-				attribs = {
-					stroke: gridLineColor,
-					'stroke-width': gridLineWidth
-				};
-				if (dashStyle) {
-					attribs.dashstyle = dashStyle;
-				}
-				if (!type) {
-					attribs.zIndex = 1;
-				}
-				if (old) {
-					attribs.opacity = 0;
-				}
-				tick.gridLine = gridLine =
-					gridLineWidth ?
-						renderer.path(gridLinePath)
-							.attr(attribs).add(axis.gridGroup) :
-						null;
-			}
-
-			// If the parameter 'old' is set, the current call will be followed
-			// by another call, therefore do not do any animations this time
-			if (!old && gridLine && gridLinePath) {
-				gridLine[tick.isNew ? 'attr' : 'animate']({
-					d: gridLinePath,
-					opacity: opacity
-				});
-			}
-		}
-
-		// create the tick mark
-		if (tickWidth && tickLength) {
-
-			// negate the length
-			if (tickPosition === 'inside') {
-				tickLength = -tickLength;
-			}
-			if (axis.opposite) {
-				tickLength = -tickLength;
-			}
-
-			markPath = tick.getMarkPath(x, y, tickLength, tickWidth * reverseCrisp, horiz, renderer);
-			if (mark) { // updating
-				mark.animate({
-					d: markPath,
-					opacity: opacity
-				});
-			} else { // first time
-				tick.mark = renderer.path(
-					markPath
-				).attr({
-					stroke: tickColor,
-					'stroke-width': tickWidth,
-					opacity: opacity
-				}).add(axis.axisGroup);
-			}
-		}
-
-		// the label is created on init - now move it into place
-		if (label && !isNaN(x)) {
-			label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
-
-			// Apply show first and show last. If the tick is both first and last, it is
-			// a single centered tick, in which case we show the label anyway (#2100).
-			if ((tick.isFirst && !tick.isLast && !pick(options.showFirstLabel, 1)) ||
-					(tick.isLast && !tick.isFirst && !pick(options.showLastLabel, 1))) {
-				show = false;
-
-			// Handle label overflow and show or hide accordingly
-			} else if (!axis.isRadial && !labelOptions.step && !labelOptions.rotation && !old && opacity !== 0) {
-				show = tick.handleOverflow(index, xy);
-			}
-
-			// apply step
-			if (step && index % step) {
-				// show those indices dividable by step
-				show = false;
-			}
-
-			// Set the new position, and show or hide
-			if (show && !isNaN(xy.y)) {
-				xy.opacity = opacity;
-				label[tick.isNew ? 'attr' : 'animate'](xy);
-				tick.isNew = false;
-			} else {
-				label.attr('y', -9999); // #1338
-			}
-		}
-	},
-
-	/**
-	 * Destructor for the tick prototype
-	 */
-	destroy: function () {
-		destroyObjectProperties(this, this.axis);
-	}
-};
-
-/**
- * The object wrapper for plot lines and plot bands
- * @param {Object} options
- */
-Highcharts.PlotLineOrBand = function (axis, options) {
-	this.axis = axis;
-
-	if (options) {
-		this.options = options;
-		this.id = options.id;
-	}
-};
-
-Highcharts.PlotLineOrBand.prototype = {
-	
-	/**
-	 * Render the plot line or plot band. If it is already existing,
-	 * move it.
-	 */
-	render: function () {
-		var plotLine = this,
-			axis = plotLine.axis,
-			horiz = axis.horiz,
-			halfPointRange = (axis.pointRange || 0) / 2,
-			options = plotLine.options,
-			optionsLabel = options.label,
-			label = plotLine.label,
-			width = options.width,
-			to = options.to,
-			from = options.from,
-			isBand = defined(from) && defined(to),
-			value = options.value,
-			dashStyle = options.dashStyle,
-			svgElem = plotLine.svgElem,
-			path = [],
-			addEvent,
-			eventType,
-			xs,
-			ys,
-			x,
-			y,
-			color = options.color,
-			zIndex = options.zIndex,
-			events = options.events,
-			attribs = {},
-			renderer = axis.chart.renderer;
-
-		// logarithmic conversion
-		if (axis.isLog) {
-			from = log2lin(from);
-			to = log2lin(to);
-			value = log2lin(value);
-		}
-
-		// plot line
-		if (width) {
-			path = axis.getPlotLinePath(value, width);
-			attribs = {
-				stroke: color,
-				'stroke-width': width
-			};
-			if (dashStyle) {
-				attribs.dashstyle = dashStyle;
-			}
-		} else if (isBand) { // plot band
-			
-			// keep within plot area
-			from = mathMax(from, axis.min - halfPointRange);
-			to = mathMin(to, axis.max + halfPointRange);
-			
-			path = axis.getPlotBandPath(from, to, options);
-			if (color) {
-				attribs.fill = color;
-			}
-			if (options.borderWidth) {
-				attribs.stroke = options.borderColor;
-				attribs['stroke-width'] = options.borderWidth;
-			}
-		} else {
-			return;
-		}
-		// zIndex
-		if (defined(zIndex)) {
-			attribs.zIndex = zIndex;
-		}
-
-		// common for lines and bands
-		if (svgElem) {
-			if (path) {
-				svgElem.animate({
-					d: path
-				}, null, svgElem.onGetPath);
-			} else {
-				svgElem.hide();
-				svgElem.onGetPath = function () {
-					svgElem.show();
-				};
-				if (label) {
-					plotLine.label = label = label.destroy();
-				}
-			}
-		} else if (path && path.length) {
-			plotLine.svgElem = svgElem = renderer.path(path)
-				.attr(attribs).add();
-
-			// events
-			if (events) {
-				addEvent = function (eventType) {
-					svgElem.on(eventType, function (e) {
-						events[eventType].apply(plotLine, [e]);
-					});
-				};
-				for (eventType in events) {
-					addEvent(eventType);
-				}
-			}
-		}
-
-		// the plot band/line label
-		if (optionsLabel && defined(optionsLabel.text) && path && path.length && axis.width > 0 && axis.height > 0) {
-			// apply defaults
-			optionsLabel = merge({
-				align: horiz && isBand && 'center',
-				x: horiz ? !isBand && 4 : 10,
-				verticalAlign : !horiz && isBand && 'middle',
-				y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
-				rotation: horiz && !isBand && 90
-			}, optionsLabel);
-
-			// add the SVG element
-			if (!label) {
-				attribs = {
-					align: optionsLabel.textAlign || optionsLabel.align,
-					rotation: optionsLabel.rotation
-				};
-				if (defined(zIndex)) {
-					attribs.zIndex = zIndex;
-				}
-				plotLine.label = label = renderer.text(
-						optionsLabel.text,
-						0,
-						0,
-						optionsLabel.useHTML
-					)
-					.attr(attribs)
-					.css(optionsLabel.style)
-					.add();
-			}
-
-			// get the bounding box and align the label
-			xs = [path[1], path[4], pick(path[6], path[1])];
-			ys = [path[2], path[5], pick(path[7], path[2])];
-			x = arrayMin(xs);
-			y = arrayMin(ys);
-
-			label.align(optionsLabel, false, {
-				x: x,
-				y: y,
-				width: arrayMax(xs) - x,
-				height: arrayMax(ys) - y
-			});
-			label.show();
-
-		} else if (label) { // move out of sight
-			label.hide();
-		}
-
-		// chainable
-		return plotLine;
-	},
-
-	/**
-	 * Remove the plot line or band
-	 */
-	destroy: function () {
-		// remove it from the lookup
-		erase(this.axis.plotLinesAndBands, this);
-		
-		delete this.axis;
-		destroyObjectProperties(this);
-	}
-};
-
-/**
- * Object with members for extending the Axis prototype
- */
-
-AxisPlotLineOrBandExtension = {
-
-	/**
-	 * Create the path for a plot band
-	 */ 
-	getPlotBandPath: function (from, to) {
-		var toPath = this.getPlotLinePath(to),
-			path = this.getPlotLinePath(from);
-
-		if (path && toPath) {
-			path.push(
-				toPath[4],
-				toPath[5],
-				toPath[1],
-				toPath[2]
-			);
-		} else { // outside the axis area
-			path = null;
-		}
-		
-		return path;
-	},
-
-	addPlotBand: function (options) {
-		this.addPlotBandOrLine(options, 'plotBands');
-	},
-	
-	addPlotLine: function (options) {
-			this.addPlotBandOrLine(options, 'plotLines');
-	},
-
-	/**
-	 * Add a plot band or plot line after render time
-	 *
-	 * @param options {Object} The plotBand or plotLine configuration object
-	 */
-	addPlotBandOrLine: function (options, coll) {
-		var obj = new Highcharts.PlotLineOrBand(this, options).render(),
-			userOptions = this.userOptions;
-
-		if (obj) { // #2189
-			// Add it to the user options for exporting and Axis.update
-			if (coll) {
-				userOptions[coll] = userOptions[coll] || [];
-				userOptions[coll].push(options); 
-			}
-			this.plotLinesAndBands.push(obj); 
-		}
-		
-		return obj;
-	},
-
-	/**
-	 * Remove a plot band or plot line from the chart by id
-	 * @param {Object} id
-	 */
-	removePlotBandOrLine: function (id) {
-		var plotLinesAndBands = this.plotLinesAndBands,
-			options = this.options,
-			userOptions = this.userOptions,
-			i = plotLinesAndBands.length;
-		while (i--) {
-			if (plotLinesAndBands[i].id === id) {
-				plotLinesAndBands[i].destroy();
-			}
-		}
-		each([options.plotLines || [], userOptions.plotLines || [], options.plotBands || [], userOptions.plotBands || []], function (arr) {
-			i = arr.length;
-			while (i--) {
-				if (arr[i].id === id) {
-					erase(arr, arr[i]);
-				}
-			}
-		});
-	}
-};
-
-/**
- * Create a new axis object
- * @param {Object} chart
- * @param {Object} options
- */
-function Axis() {
-	this.init.apply(this, arguments);
-}
-
-Axis.prototype = {
-
-	/**
-	 * Default options for the X axis - the Y axis has extended defaults
-	 */
-	defaultOptions: {
-		// allowDecimals: null,
-		// alternateGridColor: null,
-		// categories: [],
-		dateTimeLabelFormats: {
-			millisecond: '%H:%M:%S.%L',
-			second: '%H:%M:%S',
-			minute: '%H:%M',
-			hour: '%H:%M',
-			day: '%e. %b',
-			week: '%e. %b',
-			month: '%b \'%y',
-			year: '%Y'
-		},
-		endOnTick: false,
-		gridLineColor: '#C0C0C0',
-		// gridLineDashStyle: 'solid',
-		// gridLineWidth: 0,
-		// reversed: false,
-
-		labels: defaultLabelOptions,
-			// { step: null },
-		lineColor: '#C0D0E0',
-		lineWidth: 1,
-		//linkedTo: null,
-		//max: undefined,
-		//min: undefined,
-		minPadding: 0.01,
-		maxPadding: 0.01,
-		//minRange: null,
-		minorGridLineColor: '#E0E0E0',
-		// minorGridLineDashStyle: null,
-		minorGridLineWidth: 1,
-		minorTickColor: '#A0A0A0',
-		//minorTickInterval: null,
-		minorTickLength: 2,
-		minorTickPosition: 'outside', // inside or outside
-		//minorTickWidth: 0,
-		//opposite: false,
-		//offset: 0,
-		//plotBands: [{
-		//	events: {},
-		//	zIndex: 1,
-		//	labels: { align, x, verticalAlign, y, style, rotation, textAlign }
-		//}],
-		//plotLines: [{
-		//	events: {}
-		//  dashStyle: {}
-		//	zIndex:
-		//	labels: { align, x, verticalAlign, y, style, rotation, textAlign }
-		//}],
-		//reversed: false,
-		// showFirstLabel: true,
-		// showLastLabel: true,
-		startOfWeek: 1,
-		startOnTick: false,
-		tickColor: '#C0D0E0',
-		//tickInterval: null,
-		tickLength: 10,
-		tickmarkPlacement: 'between', // on or between
-		tickPixelInterval: 100,
-		tickPosition: 'outside',
-		tickWidth: 1,
-		title: {
-			//text: null,
-			align: 'middle', // low, middle or high
-			//margin: 0 for horizontal, 10 for vertical axes,
-			//rotation: 0,
-			//side: 'outside',
-			style: {
-				color: '#707070'
-			}
-			//x: 0,
-			//y: 0
-		},
-		type: 'linear' // linear, logarithmic or datetime
-	},
-
-	/**
-	 * This options set extends the defaultOptions for Y axes
-	 */
-	defaultYAxisOptions: {
-		endOnTick: true,
-		gridLineWidth: 1,
-		tickPixelInterval: 72,
-		showLastLabel: true,
-		labels: {
-			x: -8,
-			y: 3
-		},
-		lineWidth: 0,
-		maxPadding: 0.05,
-		minPadding: 0.05,
-		startOnTick: true,
-		tickWidth: 0,
-		title: {
-			rotation: 270,
-			text: 'Values'
-		},
-		stackLabels: {
-			enabled: false,
-			//align: dynamic,
-			//y: dynamic,
-			//x: dynamic,
-			//verticalAlign: dynamic,
-			//textAlign: dynamic,
-			//rotation: 0,
-			formatter: function () {
-				return numberFormat(this.total, -1);
-			},
-			style: defaultLabelOptions.style
-		}
-	},
-
-	/**
-	 * These options extend the defaultOptions for left axes
-	 */
-	defaultLeftAxisOptions: {
-		labels: {
-			x: -15,
-			y: null
-		},
-		title: {
-			rotation: 270
-		}
-	},
-
-	/**
-	 * These options extend the defaultOptions for right axes
-	 */
-	defaultRightAxisOptions: {
-		labels: {
-			x: 15,
-			y: null
-		},
-		title: {
-			rotation: 90
-		}
-	},
-
-	/**
-	 * These options extend the defaultOptions for bottom axes
-	 */
-	defaultBottomAxisOptions: {
-		labels: {
-			x: 0,
-			y: 20
-			// overflow: undefined,
-			// staggerLines: null
-		},
-		title: {
-			rotation: 0
-		}
-	},
-	/**
-	 * These options extend the defaultOptions for left axes
-	 */
-	defaultTopAxisOptions: {
-		labels: {
-			x: 0,
-			y: -15
-			// overflow: undefined
-			// staggerLines: null
-		},
-		title: {
-			rotation: 0
-		}
-	},
-
-	/**
-	 * Initialize the axis
-	 */
-	init: function (chart, userOptions) {
-
-
-		var isXAxis = userOptions.isX,
-			axis = this;
-
-		// Flag, is the axis horizontal
-		axis.horiz = chart.inverted ? !isXAxis : isXAxis;
-
-		// Flag, isXAxis
-		axis.isXAxis = isXAxis;
-		axis.coll = isXAxis ? 'xAxis' : 'yAxis';
-
-		axis.opposite = userOptions.opposite; // needed in setOptions
-		axis.side = userOptions.side || (axis.horiz ?
-				(axis.opposite ? 0 : 2) : // top : bottom
-				(axis.opposite ? 1 : 3));  // right : left
-
-		axis.setOptions(userOptions);
-
-
-		var options = this.options,
-			type = options.type,
-			isDatetimeAxis = type === 'datetime';
-
-		axis.labelFormatter = options.labels.formatter || axis.defaultLabelFormatter; // can be overwritten by dynamic format
-
-
-		// Flag, stagger lines or not
-		axis.userOptions = userOptions;
-
-		//axis.axisTitleMargin = UNDEFINED,// = options.title.margin,
-		axis.minPixelPadding = 0;
-		//axis.ignoreMinPadding = UNDEFINED; // can be set to true by a column or bar series
-		//axis.ignoreMaxPadding = UNDEFINED;
-
-		axis.chart = chart;
-		axis.reversed = options.reversed;
-		axis.zoomEnabled = options.zoomEnabled !== false;
-
-		// Initial categories
-		axis.categories = options.categories || type === 'category';
-		axis.names = [];
-
-		// Elements
-		//axis.axisGroup = UNDEFINED;
-		//axis.gridGroup = UNDEFINED;
-		//axis.axisTitle = UNDEFINED;
-		//axis.axisLine = UNDEFINED;
-
-		// Shorthand types
-		axis.isLog = type === 'logarithmic';
-		axis.isDatetimeAxis = isDatetimeAxis;
-
-		// Flag, if axis is linked to another axis
-		axis.isLinked = defined(options.linkedTo);
-		// Linked axis.
-		//axis.linkedParent = UNDEFINED;
-
-		// Tick positions
-		//axis.tickPositions = UNDEFINED; // array containing predefined positions
-		// Tick intervals
-		//axis.tickInterval = UNDEFINED;
-		//axis.minorTickInterval = UNDEFINED;
-
-		axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0;
-
-		// Major ticks
-		axis.ticks = {};
-		axis.labelEdge = [];
-		// Minor ticks
-		axis.minorTicks = {};
-		//axis.tickAmount = UNDEFINED;
-
-		// List of plotLines/Bands
-		axis.plotLinesAndBands = [];
-
-		// Alternate bands
-		axis.alternateBands = {};
-
-		// Axis metrics
-		//axis.left = UNDEFINED;
-		//axis.top = UNDEFINED;
-		//axis.width = UNDEFINED;
-		//axis.height = UNDEFINED;
-		//axis.bottom = UNDEFINED;
-		//axis.right = UNDEFINED;
-		//axis.transA = UNDEFINED;
-		//axis.transB = UNDEFINED;
-		//axis.oldTransA = UNDEFINED;
-		axis.len = 0;
-		//axis.oldMin = UNDEFINED;
-		//axis.oldMax = UNDEFINED;
-		//axis.oldUserMin = UNDEFINED;
-		//axis.oldUserMax = UNDEFINED;
-		//axis.oldAxisLength = UNDEFINED;
-		axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
-		axis.range = options.range;
-		axis.offset = options.offset || 0;
-
-
-		// Dictionary for stacks
-		axis.stacks = {};
-		axis.oldStacks = {};
-		
-		// Min and max in the data
-		//axis.dataMin = UNDEFINED,
-		//axis.dataMax = UNDEFINED,
-
-		// The axis range
-		axis.max = null;
-		axis.min = null;
-
-		// User set min and max
-		//axis.userMin = UNDEFINED,
-		//axis.userMax = UNDEFINED,
-
-		// Crosshair options
-		axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false);
-		// Run Axis
-
-		var eventType,
-			events = axis.options.events;
-
-		// Register
-		if (inArray(axis, chart.axes) === -1) { // don't add it again on Axis.update()
-			if (isXAxis && !this.isColorAxis) { // #2713
-				chart.axes.splice(chart.xAxis.length, 0, axis);
-			} else {
-				chart.axes.push(axis);
-			}
-
-			chart[axis.coll].push(axis);
-		}
-
-		axis.series = axis.series || []; // populated by Series
-
-		// inverted charts have reversed xAxes as default
-		if (chart.inverted && isXAxis && axis.reversed === UNDEFINED) {
-			axis.reversed = true;
-		}
-
-		axis.removePlotBand = axis.removePlotBandOrLine;
-		axis.removePlotLine = axis.removePlotBandOrLine;
-
-
-		// register event listeners
-		for (eventType in events) {
-			addEvent(axis, eventType, events[eventType]);
-		}
-
-		// extend logarithmic axis
-		if (axis.isLog) {
-			axis.val2lin = log2lin;
-			axis.lin2val = lin2log;
-		}
-	},
-
-	/**
-	 * Merge and set options
-	 */
-	setOptions: function (userOptions) {
-		this.options = merge(
-			this.defaultOptions,
-			this.isXAxis ? {} : this.defaultYAxisOptions,
-			[this.defaultTopAxisOptions, this.defaultRightAxisOptions,
-				this.defaultBottomAxisOptions, this.defaultLeftAxisOptions][this.side],
-			merge(
-				defaultOptions[this.coll], // if set in setOptions (#1053)
-				userOptions
-			)
-		);
-	},
-
-	/**
-	 * The default label formatter. The context is a special config object for the label.
-	 */
-	defaultLabelFormatter: function () {
-		var axis = this.axis,
-			value = this.value,
-			categories = axis.categories,
-			dateTimeLabelFormat = this.dateTimeLabelFormat,
-			numericSymbols = defaultOptions.lang.numericSymbols,
-			i = numericSymbols && numericSymbols.length,
-			multi,
-			ret,
-			formatOption = axis.options.labels.format,
-
-			// make sure the same symbol is added for all labels on a linear axis
-			numericSymbolDetector = axis.isLog ? value : axis.tickInterval;
-
-		if (formatOption) {
-			ret = format(formatOption, this);
-
-		} else if (categories) {
-			ret = value;
-
-		} else if (dateTimeLabelFormat) { // datetime axis
-			ret = dateFormat(dateTimeLabelFormat, value);
-
-		} else if (i && numericSymbolDetector >= 1000) {
-			// Decide whether we should add a numeric symbol like k (thousands) or M (millions).
-			// If we are to enable this in tooltip or other places as well, we can move this
-			// logic to the numberFormatter and enable it by a parameter.
-			while (i-- && ret === UNDEFINED) {
-				multi = Math.pow(1000, i + 1);
-				if (numericSymbolDetector >= multi && numericSymbols[i] !== null) {
-					ret = numberFormat(value / multi, -1) + numericSymbols[i];
-				}
-			}
-		}
-
-		if (ret === UNDEFINED) {
-			if (mathAbs(value) >= 10000) { // add thousands separators
-				ret = numberFormat(value, 0);
-
-			} else { // small numbers
-				ret = numberFormat(value, -1, UNDEFINED, ''); // #2466
-			}
-		}
-
-		return ret;
-	},
-
-	/**
-	 * Get the minimum and maximum for the series of each axis
-	 */
-	getSeriesExtremes: function () {
-		var axis = this,
-			chart = axis.chart;
-
-		axis.hasVisibleSeries = false;
-
-		// reset dataMin and dataMax in case we're redrawing
-		axis.dataMin = axis.dataMax = null;
-		
-		if (axis.buildStacks) {
-			axis.buildStacks();
-		}
-
-		// loop through this axis' series
-		each(axis.series, function (series) {
-
-			if (series.visible || !chart.options.chart.ignoreHiddenSeries) {
-
-				var seriesOptions = series.options,
-					xData,
-					threshold = seriesOptions.threshold,
-					seriesDataMin,
-					seriesDataMax;
-
-				axis.hasVisibleSeries = true;
-
-				// Validate threshold in logarithmic axes
-				if (axis.isLog && threshold <= 0) {
-					threshold = null;
-				}
-
-				// Get dataMin and dataMax for X axes
-				if (axis.isXAxis) {
-					xData = series.xData;
-					if (xData.length) {
-						axis.dataMin = mathMin(pick(axis.dataMin, xData[0]), arrayMin(xData));
-						axis.dataMax = mathMax(pick(axis.dataMax, xData[0]), arrayMax(xData));
-					}
-
-				// Get dataMin and dataMax for Y axes, as well as handle stacking and processed data
-				} else {
-
-					// Get this particular series extremes
-					series.getExtremes();
-					seriesDataMax = series.dataMax;
-					seriesDataMin = series.dataMin;
-
-					// Get the dataMin and dataMax so far. If percentage is used, the min and max are
-					// always 0 and 100. If seriesDataMin and seriesDataMax is null, then series
-					// doesn't have active y data, we continue with nulls
-					if (defined(seriesDataMin) && defined(seriesDataMax)) {
-						axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin);
-						axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax);
-					}
-
-					// Adjust to threshold
-					if (defined(threshold)) {
-						if (axis.dataMin >= threshold) {
-							axis.dataMin = threshold;
-							axis.ignoreMinPadding = true;
-						} else if (axis.dataMax < threshold) {
-							axis.dataMax = threshold;
-							axis.ignoreMaxPadding = true;
-						}
-					}
-				}
-			}
-		});
-	},
-
-	/**
-	 * Translate from axis value to pixel position on the chart, or back
-	 *
-	 */
-	translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
-		var axis = this,
-			sign = 1,
-			cvsOffset = 0,
-			localA = old ? axis.oldTransA : axis.transA,
-			localMin = old ? axis.oldMin : axis.min,
-			returnValue,
-			minPixelPadding = axis.minPixelPadding,
-			postTranslate = (axis.options.ordinal || (axis.isLog && handleLog)) && axis.lin2val;
-
-		if (!localA) {
-			localA = axis.transA;
-		}
-
-		// In vertical axes, the canvas coordinates start from 0 at the top like in
-		// SVG.
-		if (cvsCoord) {
-			sign *= -1; // canvas coordinates inverts the value
-			cvsOffset = axis.len;
-		}
-
-		// Handle reversed axis
-		if (axis.reversed) {
-			sign *= -1;
-			cvsOffset -= sign * (axis.sector || axis.len);
-		}
-
-		// From pixels to value
-		if (backwards) { // reverse translation
-
-			val = val * sign + cvsOffset;
-			val -= minPixelPadding;
-			returnValue = val / localA + localMin; // from chart pixel to value
-			if (postTranslate) { // log and ordinal axes
-				returnValue = axis.lin2val(returnValue);
-			}
-
-		// From value to pixels
-		} else {
-			if (postTranslate) { // log and ordinal axes
-				val = axis.val2lin(val);
-			}
-			if (pointPlacement === 'between') {
-				pointPlacement = 0.5;
-			}
-			returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * minPixelPadding) +
-				(isNumber(pointPlacement) ? localA * pointPlacement * axis.pointRange : 0);
-		}
-
-		return returnValue;
-	},
-
-	/**
-	 * Utility method to translate an axis value to pixel position.
-	 * @param {Number} value A value in terms of axis units
-	 * @param {Boolean} paneCoordinates Whether to return the pixel coordinate relative to the chart
-	 *        or just the axis/pane itself.
-	 */
-	toPixels: function (value, paneCoordinates) {
-		return this.translate(value, false, !this.horiz, null, true) + (paneCoordinates ? 0 : this.pos);
-	},
-
-	/*
-	 * Utility method to translate a pixel position in to an axis value
-	 * @param {Number} pixel The pixel value coordinate
-	 * @param {Boolean} paneCoordiantes Whether the input pixel is relative to the chart or just the
-	 *        axis/pane itself.
-	 */
-	toValue: function (pixel, paneCoordinates) {
-		return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
-	},
-
-	/**
-	 * Create the path for a plot line that goes from the given value on
-	 * this axis, across the plot to the opposite side
-	 * @param {Number} value
-	 * @param {Number} lineWidth Used for calculation crisp line
-	 * @param {Number] old Use old coordinates (for resizing and rescaling)
-	 */
-	getPlotLinePath: function (value, lineWidth, old, force, translatedValue) {
-		var axis = this,
-			chart = axis.chart,
-			axisLeft = axis.left,
-			axisTop = axis.top,
-			x1,
-			y1,
-			x2,
-			y2,
-			cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
-			cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
-			skip,
-			transB = axis.transB;
-
-		translatedValue = pick(translatedValue, axis.translate(value, null, null, old));
-		x1 = x2 = mathRound(translatedValue + transB);
-		y1 = y2 = mathRound(cHeight - translatedValue - transB);
-
-		if (isNaN(translatedValue)) { // no min or max
-			skip = true;
-
-		} else if (axis.horiz) {
-			y1 = axisTop;
-			y2 = cHeight - axis.bottom;
-			if (x1 < axisLeft || x1 > axisLeft + axis.width) {
-				skip = true;
-			}
-		} else {
-			x1 = axisLeft;
-			x2 = cWidth - axis.right;
-
-			if (y1 < axisTop || y1 > axisTop + axis.height) {
-				skip = true;
-			}
-		}
-		return skip && !force ?
-			null :
-			chart.renderer.crispLine([M, x1, y1, L, x2, y2], lineWidth || 1);
-	},
-
-	/**
-	 * Set the tick positions of a linear axis to round values like whole tens or every five.
-	 */
-	getLinearTickPositions: function (tickInterval, min, max) {
-		var pos,
-			lastPos,
-			roundedMin = correctFloat(mathFloor(min / tickInterval) * tickInterval),
-			roundedMax = correctFloat(mathCeil(max / tickInterval) * tickInterval),
-			tickPositions = [];
-
-		// For single points, add a tick regardless of the relative position (#2662)
-		if (min === max && isNumber(min)) {
-			return [min];
-		}
-
-		// Populate the intermediate values
-		pos = roundedMin;
-		while (pos <= roundedMax) {
-
-			// Place the tick on the rounded value
-			tickPositions.push(pos);
-
-			// Always add the raw tickInterval, not the corrected one.
-			pos = correctFloat(pos + tickInterval);
-
-			// If the interval is not big enough in the current min - max range to actually increase
-			// the loop variable, we need to break out to prevent endless loop. Issue #619
-			if (pos === lastPos) {
-				break;
-			}
-
-			// Record the last value
-			lastPos = pos;
-		}
-		return tickPositions;
-	},
-
-	/**
-	 * Return the minor tick positions. For logarithmic axes, reuse the same logic
-	 * as for major ticks.
-	 */
-	getMinorTickPositions: function () {
-		var axis = this,
-			options = axis.options,
-			tickPositions = axis.tickPositions,
-			minorTickInterval = axis.minorTickInterval,
-			minorTickPositions = [],
-			pos,
-			i,
-			len;
-
-		if (axis.isLog) {
-			len = tickPositions.length;
-			for (i = 1; i < len; i++) {
-				minorTickPositions = minorTickPositions.concat(
-					axis.getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true)
-				);
-			}
-		} else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314
-			minorTickPositions = minorTickPositions.concat(
-				axis.getTimeTicks(
-					axis.normalizeTimeTickInterval(minorTickInterval),
-					axis.min,
-					axis.max,
-					options.startOfWeek
-				)
-			);
-			if (minorTickPositions[0] < axis.min) {
-				minorTickPositions.shift();
-			}
-		} else {
-			for (pos = axis.min + (tickPositions[0] - axis.min) % minorTickInterval; pos <= axis.max; pos += minorTickInterval) {
-				minorTickPositions.push(pos);
-			}
-		}
-		return minorTickPositions;
-	},
-
-	/**
-	 * Adjust the min and max for the minimum range. Keep in mind that the series data is
-	 * not yet processed, so we don't have information on data cropping and grouping, or
-	 * updated axis.pointRange or series.pointRange. The data can't be processed until
-	 * we have finally established min and max.
-	 */
-	adjustForMinRange: function () {
-		var axis = this,
-			options = axis.options,
-			min = axis.min,
-			max = axis.max,
-			zoomOffset,
-			spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange,
-			closestDataRange,
-			i,
-			distance,
-			xData,
-			loopLength,
-			minArgs,
-			maxArgs;
-
-		// Set the automatic minimum range based on the closest point distance
-		if (axis.isXAxis && axis.minRange === UNDEFINED && !axis.isLog) {
-
-			if (defined(options.min) || defined(options.max)) {
-				axis.minRange = null; // don't do this again
-
-			} else {
-
-				// Find the closest distance between raw data points, as opposed to
-				// closestPointRange that applies to processed points (cropped and grouped)
-				each(axis.series, function (series) {
-					xData = series.xData;
-					loopLength = series.xIncrement ? 1 : xData.length - 1;
-					for (i = loopLength; i > 0; i--) {
-						distance = xData[i] - xData[i - 1];
-						if (closestDataRange === UNDEFINED || distance < closestDataRange) {
-							closestDataRange = distance;
-						}
-					}
-				});
-				axis.minRange = mathMin(closestDataRange * 5, axis.dataMax - axis.dataMin);
-			}
-		}
-
-		// if minRange is exceeded, adjust
-		if (max - min < axis.minRange) {
-			var minRange = axis.minRange;
-			zoomOffset = (minRange - max + min) / 2;
-
-			// if min and max options have been set, don't go beyond it
-			minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
-			if (spaceAvailable) { // if space is available, stay within the data range
-				minArgs[2] = axis.dataMin;
-			}
-			min = arrayMax(minArgs);
-
-			maxArgs = [min + minRange, pick(options.max, min + minRange)];
-			if (spaceAvailable) { // if space is availabe, stay within the data range
-				maxArgs[2] = axis.dataMax;
-			}
-
-			max = arrayMin(maxArgs);
-
-			// now if the max is adjusted, adjust the min back
-			if (max - min < minRange) {
-				minArgs[0] = max - minRange;
-				minArgs[1] = pick(options.min, max - minRange);
-				min = arrayMax(minArgs);
-			}
-		}
-
-		// Record modified extremes
-		axis.min = min;
-		axis.max = max;
-	},
-
-	/**
-	 * Update translation information
-	 */
-	setAxisTranslation: function (saveOld) {
-		var axis = this,
-			range = axis.max - axis.min,
-			pointRange = axis.axisPointRange || 0,
-			closestPointRange,
-			minPointOffset = 0,
-			pointRangePadding = 0,
-			linkedParent = axis.linkedParent,
-			ordinalCorrection,
-			hasCategories = !!axis.categories,
-			transA = axis.transA;
-
-		// Adjust translation for padding. Y axis with categories need to go through the same (#1784).
-		if (axis.isXAxis || hasCategories || pointRange) {
-			if (linkedParent) {
-				minPointOffset = linkedParent.minPointOffset;
-				pointRangePadding = linkedParent.pointRangePadding;
-
-			} else {
-				each(axis.series, function (series) {
-					var seriesPointRange = hasCategories ? 1 : (axis.isXAxis ? series.pointRange : (axis.axisPointRange || 0)), // #2806
-						pointPlacement = series.options.pointPlacement,
-						seriesClosestPointRange = series.closestPointRange;
-
-					if (seriesPointRange > range) { // #1446
-						seriesPointRange = 0;
-					}
-					pointRange = mathMax(pointRange, seriesPointRange);
-
-					// minPointOffset is the value padding to the left of the axis in order to make
-					// room for points with a pointRange, typically columns. When the pointPlacement option
-					// is 'between' or 'on', this padding does not apply.
-					minPointOffset = mathMax(
-						minPointOffset,
-						isString(pointPlacement) ? 0 : seriesPointRange / 2
-					);
-
-					// Determine the total padding needed to the length of the axis to make room for the
-					// pointRange. If the series' pointPlacement is 'on', no padding is added.
-					pointRangePadding = mathMax(
-						pointRangePadding,
-						pointPlacement === 'on' ? 0 : seriesPointRange
-					);
-
-					// Set the closestPointRange
-					if (!series.noSharedTooltip && defined(seriesClosestPointRange)) {
-						closestPointRange = defined(closestPointRange) ?
-							mathMin(closestPointRange, seriesClosestPointRange) :
-							seriesClosestPointRange;
-					}
-				});
-			}
-
-			// Record minPointOffset and pointRangePadding
-			ordinalCorrection = axis.ordinalSlope && closestPointRange ? axis.ordinalSlope / closestPointRange : 1; // #988, #1853
-			axis.minPointOffset = minPointOffset = minPointOffset * ordinalCorrection;
-			axis.pointRangePadding = pointRangePadding = pointRangePadding * ordinalCorrection;
-
-			// pointRange means the width reserved for each point, like in a column chart
-			axis.pointRange = mathMin(pointRange, range);
-
-			// closestPointRange means the closest distance between points. In columns
-			// it is mostly equal to pointRange, but in lines pointRange is 0 while closestPointRange
-			// is some other value
-			axis.closestPointRange = closestPointRange;
-		}
-
-		// Secondary values
-		if (saveOld) {
-			axis.oldTransA = transA;
-		}
-		axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRangePadding) || 1);
-		axis.transB = axis.horiz ? axis.left : axis.bottom; // translation addend
-		axis.minPixelPadding = transA * minPointOffset;
-	},
-
-	/**
-	 * Set the tick positions to round values and optionally extend the extremes
-	 * to the nearest tick
-	 */
-	setTickPositions: function (secondPass) {
-		var axis = this,
-			chart = axis.chart,
-			options = axis.options,
-			isLog = axis.isLog,
-			isDatetimeAxis = axis.isDatetimeAxis,
-			isXAxis = axis.isXAxis,
-			isLinked = axis.isLinked,
-			tickPositioner = axis.options.tickPositioner,
-			maxPadding = options.maxPadding,
-			minPadding = options.minPadding,
-			length,
-			linkedParentExtremes,
-			tickIntervalOption = options.tickInterval,
-			minTickIntervalOption = options.minTickInterval,
-			tickPixelIntervalOption = options.tickPixelInterval,
-			tickPositions,
-			keepTwoTicksOnly,
-			categories = axis.categories;
-
-		// linked axis gets the extremes from the parent axis
-		if (isLinked) {
-			axis.linkedParent = chart[axis.coll][options.linkedTo];
-			linkedParentExtremes = axis.linkedParent.getExtremes();
-			axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
-			axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
-			if (options.type !== axis.linkedParent.options.type) {
-				error(11, 1); // Can't link axes of different type
-			}
-		} else { // initial min and max from the extreme data values
-			axis.min = pick(axis.userMin, options.min, axis.dataMin);
-			axis.max = pick(axis.userMax, options.max, axis.dataMax);
-		}
-
-		if (isLog) {
-			if (!secondPass && mathMin(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
-				error(10, 1); // Can't plot negative values on log axis
-			}
-			axis.min = correctFloat(log2lin(axis.min)); // correctFloat cures #934
-			axis.max = correctFloat(log2lin(axis.max));
-		}
-
-		// handle zoomed range
-		if (axis.range && defined(axis.max)) {
-			axis.userMin = axis.min = mathMax(axis.min, axis.max - axis.range); // #618
-			axis.userMax = axis.max;
-
-			axis.range = null;  // don't use it when running setExtremes
-		}
-
-		// Hook for adjusting this.min and this.max. Used by bubble series.
-		if (axis.beforePadding) {
-			axis.beforePadding();
-		}
-
-		// adjust min and max for the minimum range
-		axis.adjustForMinRange();
-
-		// Pad the values to get clear of the chart's edges. To avoid tickInterval taking the padding
-		// into account, we do this after computing tick interval (#1337).
-		if (!categories && !axis.axisPointRange && !axis.usePercentage && !isLinked && defined(axis.min) && defined(axis.max)) {
-			length = axis.max - axis.min;
-			if (length) {
-				if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) {
-					axis.min -= length * minPadding;
-				}
-				if (!defined(options.max) && !defined(axis.userMax)  && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) {
-					axis.max += length * maxPadding;
-				}
-			}
-		}
-
-		// Stay within floor and ceiling
-		if (isNumber(options.floor)) {
-			axis.min = mathMax(axis.min, options.floor);
-		}
-		if (isNumber(options.ceiling)) {
-			axis.max = mathMin(axis.max, options.ceiling);
-		}
-
-		// get tickInterval
-		if (axis.min === axis.max || axis.min === undefined || axis.max === undefined) {
-			axis.tickInterval = 1;
-		} else if (isLinked && !tickIntervalOption &&
-				tickPixelIntervalOption === axis.linkedParent.options.tickPixelInterval) {
-			axis.tickInterval = axis.linkedParent.tickInterval;
-		} else {
-			axis.tickInterval = pick(
-				tickIntervalOption,
-				categories ? // for categoried axis, 1 is default, for linear axis use tickPix
-					1 :
-					// don't let it be more than the data range
-					(axis.max - axis.min) * tickPixelIntervalOption / mathMax(axis.len, tickPixelIntervalOption)
-			);
-			// For squished axes, set only two ticks
-			if (!defined(tickIntervalOption) && axis.len < tickPixelIntervalOption && !this.isRadial &&
-					!this.isLog && !categories && options.startOnTick && options.endOnTick) {
-				keepTwoTicksOnly = true;
-				axis.tickInterval /= 4; // tick extremes closer to the real values
-			}
-		}
-
-		// Now we're finished detecting min and max, crop and group series data. This
-		// is in turn needed in order to find tick positions in ordinal axes.
-		if (isXAxis && !secondPass) {
-			each(axis.series, function (series) {
-				series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax);
-			});
-		}
-
-		// set the translation factor used in translate function
-		axis.setAxisTranslation(true);
-
-		// hook for ordinal axes and radial axes
-		if (axis.beforeSetTickPositions) {
-			axis.beforeSetTickPositions();
-		}
-
-		// hook for extensions, used in Highstock ordinal axes
-		if (axis.postProcessTickInterval) {
-			axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval);
-		}
-
-		// In column-like charts, don't cramp in more ticks than there are points (#1943)
-		if (axis.pointRange) {
-			axis.tickInterval = mathMax(axis.pointRange, axis.tickInterval);
-		}
-
-		// Before normalizing the tick interval, handle minimum tick interval. This applies only if tickInterval is not defined.
-		if (!tickIntervalOption && axis.tickInterval < minTickIntervalOption) {
-			axis.tickInterval = minTickIntervalOption;
-		}
-
-		// for linear axes, get magnitude and normalize the interval
-		if (!isDatetimeAxis && !isLog) { // linear
-			if (!tickIntervalOption) {
-				axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, getMagnitude(axis.tickInterval), options);
-			}
-		}
-
-		// get minorTickInterval
-		axis.minorTickInterval = options.minorTickInterval === 'auto' && axis.tickInterval ?
-				axis.tickInterval / 5 : options.minorTickInterval;
-
-		// find the tick positions
-		axis.tickPositions = tickPositions = options.tickPositions ?
-			[].concat(options.tickPositions) : // Work on a copy (#1565)
-			(tickPositioner && tickPositioner.apply(axis, [axis.min, axis.max]));
-		if (!tickPositions) {
-
-			// Too many ticks
-			if (!axis.ordinalPositions && (axis.max - axis.min) / axis.tickInterval > mathMax(2 * axis.len, 200)) {
-				error(19, true);
-			}
-
-			if (isDatetimeAxis) {
-				tickPositions = axis.getTimeTicks(
-					axis.normalizeTimeTickInterval(axis.tickInterval, options.units),
-					axis.min,
-					axis.max,
-					options.startOfWeek,
-					axis.ordinalPositions,
-					axis.closestPointRange,
-					true
-				);
-			} else if (isLog) {
-				tickPositions = axis.getLogTickPositions(axis.tickInterval, axis.min, axis.max);
-			} else {
-				tickPositions = axis.getLinearTickPositions(axis.tickInterval, axis.min, axis.max);
-			}
-
-			if (keepTwoTicksOnly) {
-				tickPositions.splice(1, tickPositions.length - 2);
-			}
-
-			axis.tickPositions = tickPositions;
-		}
-
-		if (!isLinked) {
-
-			// reset min/max or remove extremes based on start/end on tick
-			var roundedMin = tickPositions[0],
-				roundedMax = tickPositions[tickPositions.length - 1],
-				minPointOffset = axis.minPointOffset || 0,
-				singlePad;
-
-			if (options.startOnTick) {
-				axis.min = roundedMin;
-			} else if (axis.min - minPointOffset > roundedMin) {
-				tickPositions.shift();
-			}
-
-			if (options.endOnTick) {
-				axis.max = roundedMax;
-			} else if (axis.max + minPointOffset < roundedMax) {
-				tickPositions.pop();
-			}
-
-			// When there is only one point, or all points have the same value on this axis, then min
-			// and max are equal and tickPositions.length is 0 or 1. In this case, add some padding
-			// in order to center the point, but leave it with one tick. #1337.
-			if (tickPositions.length === 1) {
-				singlePad = mathAbs(axis.max) > 10e12 ? 1 : 0.001; // The lowest possible number to avoid extra padding on columns (#2619, #2846)
-				axis.min -= singlePad;
-				axis.max += singlePad;
-			}
-		}
-	},
-
-	/**
-	 * Set the max ticks of either the x and y axis collection
-	 */
-	setMaxTicks: function () {
-
-		var chart = this.chart,
-			maxTicks = chart.maxTicks || {},
-			tickPositions = this.tickPositions,
-			key = this._maxTicksKey = [this.coll, this.pos, this.len].join('-');
-
-		if (!this.isLinked && !this.isDatetimeAxis && tickPositions && tickPositions.length > (maxTicks[key] || 0) && this.options.alignTicks !== false) {
-			maxTicks[key] = tickPositions.length;
-		}
-		chart.maxTicks = maxTicks;
-	},
-
-	/**
-	 * When using multiple axes, adjust the number of ticks to match the highest
-	 * number of ticks in that group
-	 */
-	adjustTickAmount: function () {
-		var axis = this,
-			chart = axis.chart,
-			key = axis._maxTicksKey,
-			tickPositions = axis.tickPositions,
-			maxTicks = chart.maxTicks;
-
-		if (maxTicks && maxTicks[key] && !axis.isDatetimeAxis && !axis.categories && !axis.isLinked &&
-				axis.options.alignTicks !== false && this.min !== UNDEFINED) {
-			var oldTickAmount = axis.tickAmount,
-				calculatedTickAmount = tickPositions.length,
-				tickAmount;
-
-			// set the axis-level tickAmount to use below
-			axis.tickAmount = tickAmount = maxTicks[key];
-
-			if (calculatedTickAmount < tickAmount) {
-				while (tickPositions.length < tickAmount) {
-					tickPositions.push(correctFloat(
-						tickPositions[tickPositions.length - 1] + axis.tickInterval
-					));
-				}
-				axis.transA *= (calculatedTickAmount - 1) / (tickAmount - 1);
-				axis.max = tickPositions[tickPositions.length - 1];
-
-			}
-			if (defined(oldTickAmount) && tickAmount !== oldTickAmount) {
-				axis.isDirty = true;
-			}
-		}
-	},
-
-	/**
-	 * Set the scale based on data min and max, user set min and max or options
-	 *
-	 */
-	setScale: function () {
-		var axis = this,
-			stacks = axis.stacks,
-			type,
-			i,
-			isDirtyData,
-			isDirtyAxisLength;
-
-		axis.oldMin = axis.min;
-		axis.oldMax = axis.max;
-		axis.oldAxisLength = axis.len;
-
-		// set the new axisLength
-		axis.setAxisSize();
-		//axisLength = horiz ? axisWidth : axisHeight;
-		isDirtyAxisLength = axis.len !== axis.oldAxisLength;
-
-		// is there new data?
-		each(axis.series, function (series) {
-			if (series.isDirtyData || series.isDirty ||
-					series.xAxis.isDirty) { // when x axis is dirty, we need new data extremes for y as well
-				isDirtyData = true;
-			}
-		});
-
-		// do we really need to go through all this?
-		if (isDirtyAxisLength || isDirtyData || axis.isLinked || axis.forceRedraw ||
-			axis.userMin !== axis.oldUserMin || axis.userMax !== axis.oldUserMax) {
-
-			// reset stacks
-			if (!axis.isXAxis) {
-				for (type in stacks) {
-					for (i in stacks[type]) {
-						stacks[type][i].total = null;
-						stacks[type][i].cum = 0;
-					}
-				}
-			}
-
-			axis.forceRedraw = false;
-
-			// get data extremes if needed
-			axis.getSeriesExtremes();
-
-			// get fixed positions based on tickInterval
-			axis.setTickPositions();
-
-			// record old values to decide whether a rescale is necessary later on (#540)
-			axis.oldUserMin = axis.userMin;
-			axis.oldUserMax = axis.userMax;
-
-			// Mark as dirty if it is not already set to dirty and extremes have changed. #595.
-			if (!axis.isDirty) {
-				axis.isDirty = isDirtyAxisLength || axis.min !== axis.oldMin || axis.max !== axis.oldMax;
-			}
-		} else if (!axis.isXAxis) {
-			if (axis.oldStacks) {
-				stacks = axis.stacks = axis.oldStacks;
-			}
-
-			// reset stacks
-			for (type in stacks) {
-				for (i in stacks[type]) {
-					stacks[type][i].cum = stacks[type][i].total;
-				}
-			}
-		}
-
-		// Set the maximum tick amount
-		axis.setMaxTicks();
-	},
-
-	/**
-	 * Set the extremes and optionally redraw
-	 * @param {Number} newMin
-	 * @param {Number} newMax
-	 * @param {Boolean} redraw
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 * @param {Object} eventArguments
-	 *
-	 */
-	setExtremes: function (newMin, newMax, redraw, animation, eventArguments) {
-		var axis = this,
-			chart = axis.chart;
-
-		redraw = pick(redraw, true); // defaults to true
-
-		// Extend the arguments with min and max
-		eventArguments = extend(eventArguments, {
-			min: newMin,
-			max: newMax
-		});
-
-		// Fire the event
-		fireEvent(axis, 'setExtremes', eventArguments, function () { // the default event handler
-
-			axis.userMin = newMin;
-			axis.userMax = newMax;
-			axis.eventArgs = eventArguments;
-
-			// Mark for running afterSetExtremes
-			axis.isDirtyExtremes = true;
-
-			// redraw
-			if (redraw) {
-				chart.redraw(animation);
-			}
-		});
-	},
-
-	/**
-	 * Overridable method for zooming chart. Pulled out in a separate method to allow overriding
-	 * in stock charts.
-	 */
-	zoom: function (newMin, newMax) {
-		var dataMin = this.dataMin,
-			dataMax = this.dataMax,
-			options = this.options;
-
-		// Prevent pinch zooming out of range. Check for defined is for #1946. #1734.
-		if (!this.allowZoomOutside) {
-			if (defined(dataMin) && newMin <= mathMin(dataMin, pick(options.min, dataMin))) {
-				newMin = UNDEFINED;
-			}
-			if (defined(dataMax) && newMax >= mathMax(dataMax, pick(options.max, dataMax))) {
-				newMax = UNDEFINED;
-			}
-		}
-
-		// In full view, displaying the reset zoom button is not required
-		this.displayBtn = newMin !== UNDEFINED || newMax !== UNDEFINED;
-
-		// Do it
-		this.setExtremes(
-			newMin,
-			newMax,
-			false,
-			UNDEFINED,
-			{ trigger: 'zoom' }
-		);
-		return true;
-	},
-
-	/**
-	 * Update the axis metrics
-	 */
-	setAxisSize: function () {
-		var chart = this.chart,
-			options = this.options,
-			offsetLeft = options.offsetLeft || 0,
-			offsetRight = options.offsetRight || 0,
-			horiz = this.horiz,
-			width = pick(options.width, chart.plotWidth - offsetLeft + offsetRight),
-			height = pick(options.height, chart.plotHeight),
-			top = pick(options.top, chart.plotTop),
-			left = pick(options.left, chart.plotLeft + offsetLeft),
-			percentRegex = /%$/; // docs
-
-		// Check for percentage based input values
-		if (percentRegex.test(height)) {
-			height = parseInt(height, 10) / 100 * chart.plotHeight;
-		}
-		if (percentRegex.test(top)) {
-			top = parseInt(top, 10) / 100 * chart.plotHeight + chart.plotTop;
-		}
-
-		// Expose basic values to use in Series object and navigator
-		this.left = left;
-		this.top = top;
-		this.width = width;
-		this.height = height;
-		this.bottom = chart.chartHeight - height - top;
-		this.right = chart.chartWidth - width - left;
-
-		// Direction agnostic properties
-		this.len = mathMax(horiz ? width : height, 0); // mathMax fixes #905
-		this.pos = horiz ? left : top; // distance from SVG origin
-	},
-
-	/**
-	 * Get the actual axis extremes
-	 */
-	getExtremes: function () {
-		var axis = this,
-			isLog = axis.isLog;
-
-		return {
-			min: isLog ? correctFloat(lin2log(axis.min)) : axis.min,
-			max: isLog ? correctFloat(lin2log(axis.max)) : axis.max,
-			dataMin: axis.dataMin,
-			dataMax: axis.dataMax,
-			userMin: axis.userMin,
-			userMax: axis.userMax
-		};
-	},
-
-	/**
-	 * Get the zero plane either based on zero or on the min or max value.
-	 * Used in bar and area plots
-	 */
-	getThreshold: function (threshold) {
-		var axis = this,
-			isLog = axis.isLog;
-
-		var realMin = isLog ? lin2log(axis.min) : axis.min,
-			realMax = isLog ? lin2log(axis.max) : axis.max;
-
-		if (realMin > threshold || threshold === null) {
-			threshold = realMin;
-		} else if (realMax < threshold) {
-			threshold = realMax;
-		}
-
-		return axis.translate(threshold, 0, 1, 0, 1);
-	},
-
-	/**
-	 * Compute auto alignment for the axis label based on which side the axis is on
-	 * and the given rotation for the label
-	 */
-	autoLabelAlign: function (rotation) {
-		var ret,
-			angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360;
-
-		if (angle > 15 && angle < 165) {
-			ret = 'right';
-		} else if (angle > 195 && angle < 345) {
-			ret = 'left';
-		} else {
-			ret = 'center';
-		}
-		return ret;
-	},
-
-	/**
-	 * Render the tick labels to a preliminary position to get their sizes
-	 */
-	getOffset: function () {
-		var axis = this,
-			chart = axis.chart,
-			renderer = chart.renderer,
-			options = axis.options,
-			tickPositions = axis.tickPositions,
-			ticks = axis.ticks,
-			horiz = axis.horiz,
-			side = axis.side,
-			invertedSide = chart.inverted ? [1, 0, 3, 2][side] : side,
-			hasData,
-			showAxis,
-			titleOffset = 0,
-			titleOffsetOption,
-			titleMargin = 0,
-			axisTitleOptions = options.title,
-			labelOptions = options.labels,
-			labelOffset = 0, // reset
-			axisOffset = chart.axisOffset,
-			clipOffset = chart.clipOffset,
-			directionFactor = [-1, 1, 1, -1][side],
-			n,
-			i,
-			autoStaggerLines = 1,
-			maxStaggerLines = pick(labelOptions.maxStaggerLines, 5),
-			sortedPositions,
-			lastRight,
-			overlap,
-			pos,
-			bBox,
-			x,
-			w,
-			lineNo,
-			lineHeightCorrection = side === 2 ? renderer.fontMetrics(labelOptions.style.fontSize).b : 0;
-
-		// For reuse in Axis.render
-		axis.hasData = hasData = (axis.hasVisibleSeries || (defined(axis.min) && defined(axis.max) && !!tickPositions));
-		axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
-
-		// Set/reset staggerLines
-		axis.staggerLines = axis.horiz && labelOptions.staggerLines;
-
-		// Create the axisGroup and gridGroup elements on first iteration
-		if (!axis.axisGroup) {
-			axis.gridGroup = renderer.g('grid')
-				.attr({ zIndex: options.gridZIndex || 1 })
-				.add();
-			axis.axisGroup = renderer.g('axis')
-				.attr({ zIndex: options.zIndex || 2 })
-				.add();
-			axis.labelGroup = renderer.g('axis-labels')
-				.attr({ zIndex: labelOptions.zIndex || 7 })
-				.addClass(PREFIX + axis.coll.toLowerCase() + '-labels')
-				.add();
-		}
-
-		if (hasData || axis.isLinked) {
-
-			// Set the explicit or automatic label alignment
-			axis.labelAlign = pick(labelOptions.align || axis.autoLabelAlign(labelOptions.rotation));
-
-			// Generate ticks
-			each(tickPositions, function (pos) {
-				if (!ticks[pos]) {
-					ticks[pos] = new Tick(axis, pos);
-				} else {
-					ticks[pos].addLabel(); // update labels depending on tick interval
-				}
-			});
-
-			// Handle automatic stagger lines
-			if (axis.horiz && !axis.staggerLines && maxStaggerLines && !labelOptions.rotation) {
-				sortedPositions = axis.reversed ? [].concat(tickPositions).reverse() : tickPositions;
-				while (autoStaggerLines < maxStaggerLines) {
-					lastRight = [];
-					overlap = false;
-
-					for (i = 0; i < sortedPositions.length; i++) {
-						pos = sortedPositions[i];
-						bBox = ticks[pos].label && ticks[pos].label.getBBox();
-						w = bBox ? bBox.width : 0;
-						lineNo = i % autoStaggerLines;
-
-						if (w) {
-							x = axis.translate(pos); // don't handle log
-							if (lastRight[lineNo] !== UNDEFINED && x < lastRight[lineNo]) {
-								overlap = true;
-							}
-							lastRight[lineNo] = x + w;
-						}
-					}
-					if (overlap) {
-						autoStaggerLines++;
-					} else {
-						break;
-					}
-				}
-
-				if (autoStaggerLines > 1) {
-					axis.staggerLines = autoStaggerLines;
-				}
-			}
-
-
-			each(tickPositions, function (pos) {
-				// left side must be align: right and right side must have align: left for labels
-				if (side === 0 || side === 2 || { 1: 'left', 3: 'right' }[side] === axis.labelAlign) {
-
-					// get the highest offset
-					labelOffset = mathMax(
-						ticks[pos].getLabelSize(),
-						labelOffset
-					);
-				}
-
-			});
-			if (axis.staggerLines) {
-				labelOffset *= axis.staggerLines;
-				axis.labelOffset = labelOffset;
-			}
-
-
-		} else { // doesn't have data
-			for (n in ticks) {
-				ticks[n].destroy();
-				delete ticks[n];
-			}
-		}
-
-		if (axisTitleOptions && axisTitleOptions.text && axisTitleOptions.enabled !== false) {
-			if (!axis.axisTitle) {
-				axis.axisTitle = renderer.text(
-					axisTitleOptions.text,
-					0,
-					0,
-					axisTitleOptions.useHTML
-				)
-				.attr({
-					zIndex: 7,
-					rotation: axisTitleOptions.rotation || 0,
-					align:
-						axisTitleOptions.textAlign ||
-						{ low: 'left', middle: 'center', high: 'right' }[axisTitleOptions.align]
-				})
-				.addClass(PREFIX + this.coll.toLowerCase() + '-title')
-				.css(axisTitleOptions.style)
-				.add(axis.axisGroup);
-				axis.axisTitle.isNew = true;
-			}
-
-			if (showAxis) {
-				titleOffset = axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
-				titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10);
-				titleOffsetOption = axisTitleOptions.offset;
-			}
-
-			// hide or show the title depending on whether showEmpty is set
-			axis.axisTitle[showAxis ? 'show' : 'hide']();
-		}
-
-		// handle automatic or user set offset
-		axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
-
-		axis.axisTitleMargin =
-			pick(titleOffsetOption,
-				labelOffset + titleMargin +
-				(labelOffset && (directionFactor * options.labels[horiz ? 'y' : 'x'] - lineHeightCorrection))
-			);
-
-		axisOffset[side] = mathMax(
-			axisOffset[side],
-			axis.axisTitleMargin + titleOffset + directionFactor * axis.offset
-		);
-		clipOffset[invertedSide] = mathMax(clipOffset[invertedSide], mathFloor(options.lineWidth / 2) * 2);
-	},
-
-	/**
-	 * Get the path for the axis line
-	 */
-	getLinePath: function (lineWidth) {
-		var chart = this.chart,
-			opposite = this.opposite,
-			offset = this.offset,
-			horiz = this.horiz,
-			lineLeft = this.left + (opposite ? this.width : 0) + offset,
-			lineTop = chart.chartHeight - this.bottom - (opposite ? this.height : 0) + offset;
-
-		if (opposite) {
-			lineWidth *= -1; // crispify the other way - #1480, #1687
-		}
-
-		return chart.renderer.crispLine([
-				M,
-				horiz ?
-					this.left :
-					lineLeft,
-				horiz ?
-					lineTop :
-					this.top,
-				L,
-				horiz ?
-					chart.chartWidth - this.right :
-					lineLeft,
-				horiz ?
-					lineTop :
-					chart.chartHeight - this.bottom
-			], lineWidth);
-	},
-
-	/**
-	 * Position the title
-	 */
-	getTitlePosition: function () {
-		// compute anchor points for each of the title align options
-		var horiz = this.horiz,
-			axisLeft = this.left,
-			axisTop = this.top,
-			axisLength = this.len,
-			axisTitleOptions = this.options.title,
-			margin = horiz ? axisLeft : axisTop,
-			opposite = this.opposite,
-			offset = this.offset,
-			fontSize = pInt(axisTitleOptions.style.fontSize || 12),
-
-			// the position in the length direction of the axis
-			alongAxis = {
-				low: margin + (horiz ? 0 : axisLength),
-				middle: margin + axisLength / 2,
-				high: margin + (horiz ? axisLength : 0)
-			}[axisTitleOptions.align],
-
-			// the position in the perpendicular direction of the axis
-			offAxis = (horiz ? axisTop + this.height : axisLeft) +
-				(horiz ? 1 : -1) * // horizontal axis reverses the margin
-				(opposite ? -1 : 1) * // so does opposite axes
-				this.axisTitleMargin +
-				(this.side === 2 ? fontSize : 0);
-
-		return {
-			x: horiz ?
-				alongAxis :
-				offAxis + (opposite ? this.width : 0) + offset +
-					(axisTitleOptions.x || 0), // x
-			y: horiz ?
-				offAxis - (opposite ? this.height : 0) + offset :
-				alongAxis + (axisTitleOptions.y || 0) // y
-		};
-	},
-
-	/**
-	 * Render the axis
-	 */
-	render: function () {
-		var axis = this,
-			horiz = axis.horiz,
-			reversed = axis.reversed,
-			chart = axis.chart,
-			renderer = chart.renderer,
-			options = axis.options,
-			isLog = axis.isLog,
-			isLinked = axis.isLinked,
-			tickPositions = axis.tickPositions,
-			sortedPositions,
-			axisTitle = axis.axisTitle,			
-			ticks = axis.ticks,
-			minorTicks = axis.minorTicks,
-			alternateBands = axis.alternateBands,
-			stackLabelOptions = options.stackLabels,
-			alternateGridColor = options.alternateGridColor,
-			tickmarkOffset = axis.tickmarkOffset,
-			lineWidth = options.lineWidth,
-			linePath,
-			hasRendered = chart.hasRendered,
-			slideInTicks = hasRendered && defined(axis.oldMin) && !isNaN(axis.oldMin),
-			hasData = axis.hasData,
-			showAxis = axis.showAxis,
-			from,
-			overflow = options.labels.overflow,
-			justifyLabels = axis.justifyLabels = horiz && overflow !== false,
-			to;
-
-		// Reset
-		axis.labelEdge.length = 0;
-		axis.justifyToPlot = overflow === 'justify';
-
-		// Mark all elements inActive before we go over and mark the active ones
-		each([ticks, minorTicks, alternateBands], function (coll) {
-			var pos;
-			for (pos in coll) {
-				coll[pos].isActive = false;
-			}
-		});
-
-		// If the series has data draw the ticks. Else only the line and title
-		if (hasData || isLinked) {
-
-			// minor ticks
-			if (axis.minorTickInterval && !axis.categories) {
-				each(axis.getMinorTickPositions(), function (pos) {
-					if (!minorTicks[pos]) {
-						minorTicks[pos] = new Tick(axis, pos, 'minor');
-					}
-
-					// render new ticks in old position
-					if (slideInTicks && minorTicks[pos].isNew) {
-						minorTicks[pos].render(null, true);
-					}
-
-					minorTicks[pos].render(null, false, 1);
-				});
-			}
-
-			// Major ticks. Pull out the first item and render it last so that
-			// we can get the position of the neighbour label. #808.
-			if (tickPositions.length) { // #1300
-				sortedPositions = tickPositions.slice();
-				if ((horiz && reversed) || (!horiz && !reversed)) {
-					sortedPositions.reverse();
-				}
-				if (justifyLabels) {
-					sortedPositions = sortedPositions.slice(1).concat([sortedPositions[0]]);
-				}
-				each(sortedPositions, function (pos, i) {
-
-					// Reorganize the indices
-					if (justifyLabels) {
-						i = (i === sortedPositions.length - 1) ? 0 : i + 1;
-					}
-
-					// linked axes need an extra check to find out if
-					if (!isLinked || (pos >= axis.min && pos <= axis.max)) {
-
-						if (!ticks[pos]) {
-							ticks[pos] = new Tick(axis, pos);
-						}
-
-						// render new ticks in old position
-						if (slideInTicks && ticks[pos].isNew) {
-							ticks[pos].render(i, true, 0.1);
-						}
-
-						ticks[pos].render(i, false, 1);
-					}
-
-				});
-				// In a categorized axis, the tick marks are displayed between labels. So
-				// we need to add a tick mark and grid line at the left edge of the X axis.
-				if (tickmarkOffset && axis.min === 0) {
-					if (!ticks[-1]) {
-						ticks[-1] = new Tick(axis, -1, null, true);
-					}
-					ticks[-1].render(-1);
-				}
-
-			}
-
-			// alternate grid color
-			if (alternateGridColor) {
-				each(tickPositions, function (pos, i) {
-					if (i % 2 === 0 && pos < axis.max) {
-						if (!alternateBands[pos]) {
-							alternateBands[pos] = new Highcharts.PlotLineOrBand(axis);
-						}
-						from = pos + tickmarkOffset; // #949
-						to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max;
-						alternateBands[pos].options = {
-							from: isLog ? lin2log(from) : from,
-							to: isLog ? lin2log(to) : to,
-							color: alternateGridColor
-						};
-						alternateBands[pos].render();
-						alternateBands[pos].isActive = true;
-					}
-				});
-			}
-
-			// custom plot lines and bands
-			if (!axis._addedPlotLB) { // only first time
-				each((options.plotLines || []).concat(options.plotBands || []), function (plotLineOptions) {
-					axis.addPlotBandOrLine(plotLineOptions);
-				});
-				axis._addedPlotLB = true;
-			}
-
-		} // end if hasData
-
-		// Remove inactive ticks
-		each([ticks, minorTicks, alternateBands], function (coll) {
-			var pos,
-				i,
-				forDestruction = [],
-				delay = globalAnimation ? globalAnimation.duration || 500 : 0,
-				destroyInactiveItems = function () {
-					i = forDestruction.length;
-					while (i--) {
-						// When resizing rapidly, the same items may be destroyed in different timeouts,
-						// or the may be reactivated
-						if (coll[forDestruction[i]] && !coll[forDestruction[i]].isActive) {
-							coll[forDestruction[i]].destroy();
-							delete coll[forDestruction[i]];
-						}
-					}
-
-				};
-
-			for (pos in coll) {
-
-				if (!coll[pos].isActive) {
-					// Render to zero opacity
-					coll[pos].render(pos, false, 0);
-					coll[pos].isActive = false;
-					forDestruction.push(pos);
-				}
-			}
-
-			// When the objects are finished fading out, destroy them
-			if (coll === alternateBands || !chart.hasRendered || !delay) {
-				destroyInactiveItems();
-			} else if (delay) {
-				setTimeout(destroyInactiveItems, delay);
-			}
-		});
-
-		// Static items. As the axis group is cleared on subsequent calls
-		// to render, these items are added outside the group.
-		// axis line
-		if (lineWidth) {
-			linePath = axis.getLinePath(lineWidth);
-			if (!axis.axisLine) {
-				axis.axisLine = renderer.path(linePath)
-					.attr({
-						stroke: options.lineColor,
-						'stroke-width': lineWidth,
-						zIndex: 7
-					})
-					.add(axis.axisGroup);
-			} else {
-				axis.axisLine.animate({ d: linePath });
-			}
-
-			// show or hide the line depending on options.showEmpty
-			axis.axisLine[showAxis ? 'show' : 'hide']();
-		}
-
-		if (axisTitle && showAxis) {
-
-			axisTitle[axisTitle.isNew ? 'attr' : 'animate'](
-				axis.getTitlePosition()
-			);
-			axisTitle.isNew = false;
-		}
-
-		// Stacked totals:
-		if (stackLabelOptions && stackLabelOptions.enabled) {
-			axis.renderStackTotals();
-		}
-		// End stacked totals
-
-		axis.isDirty = false;
-	},
-
-	/**
-	 * Redraw the axis to reflect changes in the data or axis extremes
-	 */
-	redraw: function () {
-		var axis = this,
-			chart = axis.chart,
-			pointer = chart.pointer;
-
-		// hide tooltip and hover states
-		if (pointer) {
-			pointer.reset(true);
-		}
-
-		// render the axis
-		axis.render();
-
-		// move plot lines and bands
-		each(axis.plotLinesAndBands, function (plotLine) {
-			plotLine.render();
-		});
-
-		// mark associated series as dirty and ready for redraw
-		each(axis.series, function (series) {
-			series.isDirty = true;
-		});
-
-	},
-
-	/**
-	 * Destroys an Axis instance.
-	 */
-	destroy: function (keepEvents) {
-		var axis = this,
-			stacks = axis.stacks,
-			stackKey,
-			plotLinesAndBands = axis.plotLinesAndBands,
-			i;
-
-		// Remove the events
-		if (!keepEvents) {
-			removeEvent(axis);
-		}
-
-		// Destroy each stack total
-		for (stackKey in stacks) {
-			destroyObjectProperties(stacks[stackKey]);
-
-			stacks[stackKey] = null;
-		}
-
-		// Destroy collections
-		each([axis.ticks, axis.minorTicks, axis.alternateBands], function (coll) {
-			destroyObjectProperties(coll);
-		});
-		i = plotLinesAndBands.length;
-		while (i--) { // #1975
-			plotLinesAndBands[i].destroy();
-		}
-
-		// Destroy local variables
-		each(['stackTotalGroup', 'axisLine', 'axisTitle', 'axisGroup', 'cross', 'gridGroup', 'labelGroup'], function (prop) {
-			if (axis[prop]) {
-				axis[prop] = axis[prop].destroy();
-			}
-		});
-
-		// Destroy crosshair
-		if (this.cross) {
-			this.cross.destroy();
-		}
-	},
-
-	/**
-	 * Draw the crosshair
-	 */
-	drawCrosshair: function (e, point) {
-		if (!this.crosshair) { return; }// Do not draw crosshairs if you don't have too.
-
-		if ((defined(point) || !pick(this.crosshair.snap, true)) === false) {
-			this.hideCrosshair();
-			return;
-		}
-
-		var path,
-			options = this.crosshair,
-			animation = options.animation,
-			pos;
-
-		// Get the path
-		if (!pick(options.snap, true)) {
-			pos = (this.horiz ? e.chartX - this.pos : this.len - e.chartY + this.pos);
-		} else if (defined(point)) {
-			/*jslint eqeq: true*/
-			pos = (this.chart.inverted != this.horiz) ? point.plotX : this.len - point.plotY;
-			/*jslint eqeq: false*/
-		}
-
-		if (this.isRadial) {
-			path = this.getPlotLinePath(this.isXAxis ? point.x : pick(point.stackY, point.y));
-		} else {
-			path = this.getPlotLinePath(null, null, null, null, pos);
-		}
-
-		if (path === null) {
-			this.hideCrosshair();
-			return;
-		}
-
-		// Draw the cross
-		if (this.cross) {
-			this.cross
-				.attr({ visibility: VISIBLE })[animation ? 'animate' : 'attr']({ d: path }, animation);
-		} else {
-			var attribs = {
-				'stroke-width': options.width || 1,
-				stroke: options.color || '#C0C0C0',
-				zIndex: options.zIndex || 2
-			};
-			if (options.dashStyle) {
-				attribs.dashstyle = options.dashStyle;
-			}
-			this.cross = this.chart.renderer.path(path).attr(attribs).add();
-		}
-	},
-
-	/**
-	 *	Hide the crosshair.
-	 */
-	hideCrosshair: function () {
-		if (this.cross) {
-			this.cross.hide();
-		}
-	}
-}; // end Axis
-
-extend(Axis.prototype, AxisPlotLineOrBandExtension);
-
-/**
- * Set the tick positions to a time unit that makes sense, for example
- * on the first of each month or on every Monday. Return an array
- * with the time positions. Used in datetime axes as well as for grouping
- * data on a datetime axis.
- *
- * @param {Object} normalizedInterval The interval in axis values (ms) and the count
- * @param {Number} min The minimum in axis values
- * @param {Number} max The maximum in axis values
- * @param {Number} startOfWeek
- */
-Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek) {
-	var tickPositions = [],
-		i,
-		higherRanks = {},
-		useUTC = defaultOptions.global.useUTC,
-		minYear, // used in months and years as a basis for Date.UTC()
-		minDate = new Date(min - timezoneOffset),
-		interval = normalizedInterval.unitRange,
-		count = normalizedInterval.count;
-
-	if (defined(min)) { // #1300
-		if (interval >= timeUnits[SECOND]) { // second
-			minDate.setMilliseconds(0);
-			minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
-				count * mathFloor(minDate.getSeconds() / count));
-		}
-	
-		if (interval >= timeUnits[MINUTE]) { // minute
-			minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
-				count * mathFloor(minDate[getMinutes]() / count));
-		}
-	
-		if (interval >= timeUnits[HOUR]) { // hour
-			minDate[setHours](interval >= timeUnits[DAY] ? 0 :
-				count * mathFloor(minDate[getHours]() / count));
-		}
-	
-		if (interval >= timeUnits[DAY]) { // day
-			minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
-				count * mathFloor(minDate[getDate]() / count));
-		}
-	
-		if (interval >= timeUnits[MONTH]) { // month
-			minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
-				count * mathFloor(minDate[getMonth]() / count));
-			minYear = minDate[getFullYear]();
-		}
-	
-		if (interval >= timeUnits[YEAR]) { // year
-			minYear -= minYear % count;
-			minDate[setFullYear](minYear);
-		}
-	
-		// week is a special case that runs outside the hierarchy
-		if (interval === timeUnits[WEEK]) {
-			// get start of current week, independent of count
-			minDate[setDate](minDate[getDate]() - minDate[getDay]() +
-				pick(startOfWeek, 1));
-		}
-	
-	
-		// get tick positions
-		i = 1;
-		if (timezoneOffset) {
-			minDate = new Date(minDate.getTime() + timezoneOffset);
-		}
-		minYear = minDate[getFullYear]();
-		var time = minDate.getTime(),
-			minMonth = minDate[getMonth](),
-			minDateDate = minDate[getDate](),
-			localTimezoneOffset = useUTC ? 
-				timezoneOffset : 
-				(24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950
-	
-		// iterate and add tick positions at appropriate values
-		while (time < max) {
-			tickPositions.push(time);
-	
-			// if the interval is years, use Date.UTC to increase years
-			if (interval === timeUnits[YEAR]) {
-				time = makeTime(minYear + i * count, 0);
-	
-			// if the interval is months, use Date.UTC to increase months
-			} else if (interval === timeUnits[MONTH]) {
-				time = makeTime(minYear, minMonth + i * count);
-	
-			// if we're using global time, the interval is not fixed as it jumps
-			// one hour at the DST crossover
-			} else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) {
-				time = makeTime(minYear, minMonth, minDateDate +
-					i * count * (interval === timeUnits[DAY] ? 1 : 7));
-	
-			// else, the interval is fixed and we use simple addition
-			} else {
-				time += interval * count;
-			}
-	
-			i++;
-		}
-	
-		// push the last time
-		tickPositions.push(time);
-
-
-		// mark new days if the time is dividible by day (#1649, #1760)
-		each(grep(tickPositions, function (time) {
-			return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === localTimezoneOffset;
-		}), function (time) {
-			higherRanks[time] = DAY;
-		});
-	}
-
-
-	// record information on the chosen unit - for dynamic label formatter
-	tickPositions.info = extend(normalizedInterval, {
-		higherRanks: higherRanks,
-		totalRange: interval * count
-	});
-
-	return tickPositions;
-};
-
-/**
- * Get a normalized tick interval for dates. Returns a configuration object with
- * unit range (interval), count and name. Used to prepare data for getTimeTicks. 
- * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
- * of segments in stock charts, the normalizing logic was extracted in order to 
- * prevent it for running over again for each segment having the same interval. 
- * #662, #697.
- */
-Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
-	var units = unitsOption || [[
-				MILLISECOND, // unit name
-				[1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
-			], [
-				SECOND,
-				[1, 2, 5, 10, 15, 30]
-			], [
-				MINUTE,
-				[1, 2, 5, 10, 15, 30]
-			], [
-				HOUR,
-				[1, 2, 3, 4, 6, 8, 12]
-			], [
-				DAY,
-				[1, 2]
-			], [
-				WEEK,
-				[1, 2]
-			], [
-				MONTH,
-				[1, 2, 3, 4, 6]
-			], [
-				YEAR,
-				null
-			]],
-		unit = units[units.length - 1], // default unit is years
-		interval = timeUnits[unit[0]],
-		multiples = unit[1],
-		count,
-		i;
-		
-	// loop through the units to find the one that best fits the tickInterval
-	for (i = 0; i < units.length; i++) {
-		unit = units[i];
-		interval = timeUnits[unit[0]];
-		multiples = unit[1];
-
-
-		if (units[i + 1]) {
-			// lessThan is in the middle between the highest multiple and the next unit.
-			var lessThan = (interval * multiples[multiples.length - 1] +
-						timeUnits[units[i + 1][0]]) / 2;
-
-			// break and keep the current unit
-			if (tickInterval <= lessThan) {
-				break;
-			}
-		}
-	}
-
-	// prevent 2.5 years intervals, though 25, 250 etc. are allowed
-	if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
-		multiples = [1, 2, 5];
-	}
-
-	// get the count
-	count = normalizeTickInterval(
-		tickInterval / interval, 
-		multiples,
-		unit[0] === YEAR ? mathMax(getMagnitude(tickInterval / interval), 1) : 1 // #1913, #2360
-	);
-	
-	return {
-		unitRange: interval,
-		count: count,
-		unitName: unit[0]
-	};
-};/**
- * Methods defined on the Axis prototype
- */
-
-/**
- * Set the tick positions of a logarithmic axis
- */
-Axis.prototype.getLogTickPositions = function (interval, min, max, minor) {
-	var axis = this,
-		options = axis.options,
-		axisLength = axis.len,
-		// Since we use this method for both major and minor ticks,
-		// use a local variable and return the result
-		positions = []; 
-	
-	// Reset
-	if (!minor) {
-		axis._minorAutoInterval = null;
-	}
-	
-	// First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
-	if (interval >= 0.5) {
-		interval = mathRound(interval);
-		positions = axis.getLinearTickPositions(interval, min, max);
-		
-	// Second case: We need intermediary ticks. For example 
-	// 1, 2, 4, 6, 8, 10, 20, 40 etc. 
-	} else if (interval >= 0.08) {
-		var roundedMin = mathFloor(min),
-			intermediate,
-			i,
-			j,
-			len,
-			pos,
-			lastPos,
-			break2;
-			
-		if (interval > 0.3) {
-			intermediate = [1, 2, 4];
-		} else if (interval > 0.15) { // 0.2 equals five minor ticks per 1, 10, 100 etc
-			intermediate = [1, 2, 4, 6, 8];
-		} else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
-			intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
-		}
-		
-		for (i = roundedMin; i < max + 1 && !break2; i++) {
-			len = intermediate.length;
-			for (j = 0; j < len && !break2; j++) {
-				pos = log2lin(lin2log(i) * intermediate[j]);
-				
-				if (pos > min && (!minor || lastPos <= max)) { // #1670
-					positions.push(lastPos);
-				}
-				
-				if (lastPos > max) {
-					break2 = true;
-				}
-				lastPos = pos;
-			}
-		}
-		
-	// Third case: We are so deep in between whole logarithmic values that
-	// we might as well handle the tick positions like a linear axis. For
-	// example 1.01, 1.02, 1.03, 1.04.
-	} else {
-		var realMin = lin2log(min),
-			realMax = lin2log(max),
-			tickIntervalOption = options[minor ? 'minorTickInterval' : 'tickInterval'],
-			filteredTickIntervalOption = tickIntervalOption === 'auto' ? null : tickIntervalOption,
-			tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1),
-			totalPixelLength = minor ? axisLength / axis.tickPositions.length : axisLength;
-		
-		interval = pick(
-			filteredTickIntervalOption,
-			axis._minorAutoInterval,
-			(realMax - realMin) * tickPixelIntervalOption / (totalPixelLength || 1)
-		);
-		
-		interval = normalizeTickInterval(
-			interval, 
-			null, 
-			getMagnitude(interval)
-		);
-		
-		positions = map(axis.getLinearTickPositions(
-			interval, 
-			realMin,
-			realMax	
-		), log2lin);
-		
-		if (!minor) {
-			axis._minorAutoInterval = interval / 5;
-		}
-	}
-	
-	// Set the axis-level tickInterval variable 
-	if (!minor) {
-		axis.tickInterval = interval;
-	}
-	return positions;
-};/**
- * The tooltip object
- * @param {Object} chart The chart instance
- * @param {Object} options Tooltip options
- */
-var Tooltip = Highcharts.Tooltip = function () {
-	this.init.apply(this, arguments);
-};
-
-Tooltip.prototype = {
-
-	init: function (chart, options) {
-
-		var borderWidth = options.borderWidth,
-			style = options.style,
-			padding = pInt(style.padding);
-
-		// Save the chart and options
-		this.chart = chart;
-		this.options = options;
-
-		// Keep track of the current series
-		//this.currentSeries = UNDEFINED;
-
-		// List of crosshairs
-		this.crosshairs = [];
-
-		// Current values of x and y when animating
-		this.now = { x: 0, y: 0 };
-
-		// The tooltip is initially hidden
-		this.isHidden = true;
-
-
-		// create the label
-		this.label = chart.renderer.label('', 0, 0, options.shape || 'callout', null, null, options.useHTML, null, 'tooltip')
-			.attr({
-				padding: padding,
-				fill: options.backgroundColor,
-				'stroke-width': borderWidth,
-				r: options.borderRadius,
-				zIndex: 8
-			})
-			.css(style)
-			.css({ padding: 0 }) // Remove it from VML, the padding is applied as an attribute instead (#1117)
-			.add()
-			.attr({ y: -9999 }); // #2301, #2657
-
-		// When using canVG the shadow shows up as a gray circle
-		// even if the tooltip is hidden.
-		if (!useCanVG) {
-			this.label.shadow(options.shadow);
-		}
-
-		// Public property for getting the shared state.
-		this.shared = options.shared;
-	},
-
-	/**
-	 * Destroy the tooltip and its elements.
-	 */
-	destroy: function () {
-		// Destroy and clear local variables
-		if (this.label) {
-			this.label = this.label.destroy();
-		}
-		clearTimeout(this.hideTimer);
-		clearTimeout(this.tooltipTimeout);
-	},
-
-	/**
-	 * Provide a soft movement for the tooltip
-	 *
-	 * @param {Number} x
-	 * @param {Number} y
-	 * @private
-	 */
-	move: function (x, y, anchorX, anchorY) {
-		var tooltip = this,
-			now = tooltip.now,
-			animate = tooltip.options.animation !== false && !tooltip.isHidden,
-			skipAnchor = tooltip.followPointer || tooltip.len > 1;
-
-		// get intermediate values for animation
-		extend(now, {
-			x: animate ? (2 * now.x + x) / 3 : x,
-			y: animate ? (now.y + y) / 2 : y,
-			anchorX: skipAnchor ? UNDEFINED : animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
-			anchorY: skipAnchor ? UNDEFINED : animate ? (now.anchorY + anchorY) / 2 : anchorY
-		});
-
-		// move to the intermediate value
-		tooltip.label.attr(now);
-
-		
-		// run on next tick of the mouse tracker
-		if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) {
-		
-			// never allow two timeouts
-			clearTimeout(this.tooltipTimeout);
-			
-			// set the fixed interval ticking for the smooth tooltip
-			this.tooltipTimeout = setTimeout(function () {
-				// The interval function may still be running during destroy, so check that the chart is really there before calling.
-				if (tooltip) {
-					tooltip.move(x, y, anchorX, anchorY);
-				}
-			}, 32);
-			
-		}
-	},
-
-	/**
-	 * Hide the tooltip
-	 */
-	hide: function () {
-		var tooltip = this,
-			hoverPoints;
-		
-		clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766)
-		if (!this.isHidden) {
-			hoverPoints = this.chart.hoverPoints;
-
-			this.hideTimer = setTimeout(function () {
-				tooltip.label.fadeOut();
-				tooltip.isHidden = true;
-			}, pick(this.options.hideDelay, 500));
-
-			// hide previous hoverPoints and set new
-			if (hoverPoints) {
-				each(hoverPoints, function (point) {
-					point.setState();
-				});
-			}
-
-			this.chart.hoverPoints = null;
-		}
-	},
-	
-	/** 
-	 * Extendable method to get the anchor position of the tooltip
-	 * from a point or set of points
-	 */
-	getAnchor: function (points, mouseEvent) {
-		var ret,
-			chart = this.chart,
-			inverted = chart.inverted,
-			plotTop = chart.plotTop,
-			plotX = 0,
-			plotY = 0,
-			yAxis;
-		
-		points = splat(points);
-		
-		// Pie uses a special tooltipPos
-		ret = points[0].tooltipPos;
-		
-		// When tooltip follows mouse, relate the position to the mouse
-		if (this.followPointer && mouseEvent) {
-			if (mouseEvent.chartX === UNDEFINED) {
-				mouseEvent = chart.pointer.normalize(mouseEvent);
-			}
-			ret = [
-				mouseEvent.chartX - chart.plotLeft,
-				mouseEvent.chartY - plotTop
-			];
-		}
-		// When shared, use the average position
-		if (!ret) {
-			each(points, function (point) {
-				yAxis = point.series.yAxis;
-				plotX += point.plotX;
-				plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) +
-					(!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
-			});
-			
-			plotX /= points.length;
-			plotY /= points.length;
-			
-			ret = [
-				inverted ? chart.plotWidth - plotY : plotX,
-				this.shared && !inverted && points.length > 1 && mouseEvent ? 
-					mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424)
-					inverted ? chart.plotHeight - plotX : plotY
-			];
-		}
-
-		return map(ret, mathRound);
-	},
-	
-	/**
-	 * Place the tooltip in a chart without spilling over
-	 * and not covering the point it self.
-	 */
-	getPosition: function (boxWidth, boxHeight, point) {
-		
-		var chart = this.chart,
-			distance = this.distance,
-			ret = {},
-			swapped,
-			first = ['y', chart.chartHeight, boxHeight, point.plotY + chart.plotTop],
-			second = ['x', chart.chartWidth, boxWidth, point.plotX + chart.plotLeft],
-			// The far side is right or bottom
-			preferFarSide = point.ttBelow || (chart.inverted && !point.negative) || (!chart.inverted && point.negative),
-			/**
-			 * Handle the preferred dimension. When the preferred dimension is tooltip
-			 * on top or bottom of the point, it will look for space there.
-			 */
-			firstDimension = function (dim, outerSize, innerSize, point) {
-				var roomLeft = innerSize < point - distance,
-					roomRight = point + distance + innerSize < outerSize,
-					alignedLeft = point - distance - innerSize,
-					alignedRight = point + distance;
-
-				if (preferFarSide && roomRight) {
-					ret[dim] = alignedRight;
-				} else if (!preferFarSide && roomLeft) {
-					ret[dim] = alignedLeft;
-				} else if (roomLeft) {
-					ret[dim] = alignedLeft;
-				} else if (roomRight) {
-					ret[dim] = alignedRight;
-				} else {
-					return false;
-				}
-			},
-			/**
-			 * Handle the secondary dimension. If the preferred dimension is tooltip
-			 * on top or bottom of the point, the second dimension is to align the tooltip
-			 * above the point, trying to align center but allowing left or right
-			 * align within the chart box.
-			 */
-			secondDimension = function (dim, outerSize, innerSize, point) {
-				// Too close to the edge, return false and swap dimensions
-				if (point < distance || point > outerSize - distance) {
-					return false;
-				
-				// Align left/top
-				} else if (point < innerSize / 2) {
-					ret[dim] = 1;
-				// Align right/bottom
-				} else if (point > outerSize - innerSize / 2) {
-					ret[dim] = outerSize - innerSize - 2;
-				// Align center
-				} else {
-					ret[dim] = point - innerSize / 2;
-				}
-			},
-			/**
-			 * Swap the dimensions 
-			 */
-			swap = function (count) {
-				var temp = first;
-				first = second;
-				second = temp;
-				swapped = count;
-			},
-			run = function () {
-				if (firstDimension.apply(0, first) !== false) {
-					if (secondDimension.apply(0, second) === false && !swapped) {
-						swap(true);
-						run();
-					}
-				} else if (!swapped) {
-					swap(true);
-					run();
-				} else {
-					ret.x = ret.y = 0;
-				}
-			};
-
-		// Under these conditions, prefer the tooltip on the side of the point
-		if (chart.inverted || this.len > 1) {
-			swap();
-		}
-		run();
-
-		return ret;
-	
-	},
-
-	/**
-	 * In case no user defined formatter is given, this will be used. Note that the context
-	 * here is an object holding point, series, x, y etc.
-	 */
-	defaultFormatter: function (tooltip) {
-		var items = this.points || splat(this),
-			series = items[0].series,
-			s;
-
-		// build the header
-		s = [tooltip.tooltipHeaderFormatter(items[0])];
-
-		// build the values
-		each(items, function (item) {
-			series = item.series;
-			s.push((series.tooltipFormatter && series.tooltipFormatter(item)) ||
-				item.point.tooltipFormatter(series.tooltipOptions.pointFormat));
-		});
-
-		// footer
-		s.push(tooltip.options.footerFormat || '');
-
-		return s.join('');
-	},
-
-	/**
-	 * Refresh the tooltip's text and position.
-	 * @param {Object} point
-	 */
-	refresh: function (point, mouseEvent) {
-		var tooltip = this,
-			chart = tooltip.chart,
-			label = tooltip.label,
-			options = tooltip.options,
-			x,
-			y,
-			anchor,
-			textConfig = {},
-			text,
-			pointConfig = [],
-			formatter = options.formatter || tooltip.defaultFormatter,
-			hoverPoints = chart.hoverPoints,
-			borderColor,
-			shared = tooltip.shared,
-			currentSeries;
-			
-		clearTimeout(this.hideTimer);
-		
-		// get the reference point coordinates (pie charts use tooltipPos)
-		tooltip.followPointer = splat(point)[0].series.tooltipOptions.followPointer;
-		anchor = tooltip.getAnchor(point, mouseEvent);
-		x = anchor[0];
-		y = anchor[1];
-
-		// shared tooltip, array is sent over
-		if (shared && !(point.series && point.series.noSharedTooltip)) {
-			
-			// hide previous hoverPoints and set new
-			
-			chart.hoverPoints = point;
-			if (hoverPoints) {
-				each(hoverPoints, function (point) {
-					point.setState();
-				});
-			}
-
-			each(point, function (item) {
-				item.setState(HOVER_STATE);
-
-				pointConfig.push(item.getLabelConfig());
-			});
-
-			textConfig = {
-				x: point[0].category,
-				y: point[0].y
-			};
-			textConfig.points = pointConfig;
-			this.len = pointConfig.length;
-			point = point[0];
-
-		// single point tooltip
-		} else {
-			textConfig = point.getLabelConfig();
-		}
-		text = formatter.call(textConfig, tooltip);
-
-		// register the current series
-		currentSeries = point.series;
-		this.distance = pick(currentSeries.tooltipOptions.distance, 16);
-
-		// update the inner HTML
-		if (text === false) {
-			this.hide();
-		} else {
-
-			// show it
-			if (tooltip.isHidden) {
-				stop(label);
-				label.attr('opacity', 1).show();
-			}
-
-			// update text
-			label.attr({
-				text: text
-			});
-
-			// set the stroke color of the box
-			borderColor = options.borderColor || point.color || currentSeries.color || '#606060';
-			label.attr({
-				stroke: borderColor
-			});
-			
-			tooltip.updatePosition({ plotX: x, plotY: y, negative: point.negative, ttBelow: point.ttBelow });
-		
-			this.isHidden = false;
-		}
-		fireEvent(chart, 'tooltipRefresh', {
-				text: text,
-				x: x + chart.plotLeft,
-				y: y + chart.plotTop,
-				borderColor: borderColor
-			});
-	},
-	
-	/**
-	 * Find the new position and perform the move
-	 */
-	updatePosition: function (point) {
-		var chart = this.chart,
-			label = this.label, 
-			pos = (this.options.positioner || this.getPosition).call(
-				this,
-				label.width,
-				label.height,
-				point
-			);
-
-		// do the move
-		this.move(
-			mathRound(pos.x), 
-			mathRound(pos.y), 
-			point.plotX + chart.plotLeft, 
-			point.plotY + chart.plotTop
-		);
-	},
-
-
-	/**
-	 * Format the header of the tooltip
-	 */
-	tooltipHeaderFormatter: function (point) {
-		var series = point.series,
-			tooltipOptions = series.tooltipOptions,
-			dateTimeLabelFormats = tooltipOptions.dateTimeLabelFormats,
-			xDateFormat = tooltipOptions.xDateFormat,
-			xAxis = series.xAxis,
-			isDateTime = xAxis && xAxis.options.type === 'datetime' && isNumber(point.key),
-			headerFormat = tooltipOptions.headerFormat,
-			closestPointRange = xAxis && xAxis.closestPointRange,
-			n;
-
-		// Guess the best date format based on the closest point distance (#568)
-		if (isDateTime && !xDateFormat) {
-			if (closestPointRange) {
-				for (n in timeUnits) {
-					if (timeUnits[n] >= closestPointRange || 
-							// If the point is placed every day at 23:59, we need to show
-							// the minutes as well. This logic only works for time units less than 
-							// a day, since all higher time units are dividable by those. #2637.
-							(timeUnits[n] <= timeUnits[DAY] && point.key % timeUnits[n] > 0)) {
-						xDateFormat = dateTimeLabelFormats[n];
-						break;
-					}
-				}
-			} else {
-				xDateFormat = dateTimeLabelFormats.day;
-			}
-
-			xDateFormat = xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
-
-		}
-
-		// Insert the header date format if any
-		if (isDateTime && xDateFormat) {
-			headerFormat = headerFormat.replace('{point.key}', '{point.key:' + xDateFormat + '}');
-		}
-
-		return format(headerFormat, {
-			point: point,
-			series: series
-		});
-	}
-};
-
-var hoverChartIndex;
-
-// Global flag for touch support
-hasTouch = doc.documentElement.ontouchstart !== UNDEFINED;
-
-/**
- * The mouse tracker object. All methods starting with "on" are primary DOM event handlers. 
- * Subsequent methods should be named differently from what they are doing.
- * @param {Object} chart The Chart instance
- * @param {Object} options The root options object
- */
-var Pointer = Highcharts.Pointer = function (chart, options) {
-	this.init(chart, options);
-};
-
-Pointer.prototype = {
-	/**
-	 * Initialize Pointer
-	 */
-	init: function (chart, options) {
-		
-		var chartOptions = options.chart,
-			chartEvents = chartOptions.events,
-			zoomType = useCanVG ? '' : chartOptions.zoomType,
-			inverted = chart.inverted,
-			zoomX,
-			zoomY;
-
-		// Store references
-		this.options = options;
-		this.chart = chart;
-		
-		// Zoom status
-		this.zoomX = zoomX = /x/.test(zoomType);
-		this.zoomY = zoomY = /y/.test(zoomType);
-		this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
-		this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
-		this.hasZoom = zoomX || zoomY;
-
-		// Do we need to handle click on a touch device?
-		this.runChartClick = chartEvents && !!chartEvents.click;
-
-		this.pinchDown = [];
-		this.lastValidTouch = {};
-
-		if (Highcharts.Tooltip && options.tooltip.enabled) {
-			chart.tooltip = new Tooltip(chart, options.tooltip);
-			this.followTouchMove = options.tooltip.followTouchMove;
-		}
-
-		this.setDOMEvents();
-	}, 
-
-	/**
-	 * Add crossbrowser support for chartX and chartY
-	 * @param {Object} e The event object in standard browsers
-	 */
-	normalize: function (e, chartPosition) {
-		var chartX,
-			chartY,
-			ePos;
-
-		// common IE normalizing
-		e = e || window.event;
-
-		// Framework specific normalizing (#1165)
-		e = washMouseEvent(e);
-
-		// More IE normalizing, needs to go after washMouseEvent
-		if (!e.target) {
-			e.target = e.srcElement;
-		}
-		
-		// iOS (#2757)
-		ePos = e.touches ?  (e.touches.length ? e.touches.item(0) : e.changedTouches[0]) : e;
-
-		// Get mouse position
-		if (!chartPosition) {
-			this.chartPosition = chartPosition = offset(this.chart.container);
-		}
-
-		// chartX and chartY
-		if (ePos.pageX === UNDEFINED) { // IE < 9. #886.
-			chartX = mathMax(e.x, e.clientX - chartPosition.left); // #2005, #2129: the second case is 
-				// for IE10 quirks mode within framesets
-			chartY = e.y;
-		} else {
-			chartX = ePos.pageX - chartPosition.left;
-			chartY = ePos.pageY - chartPosition.top;
-		}
-
-		return extend(e, {
-			chartX: mathRound(chartX),
-			chartY: mathRound(chartY)
-		});
-	},
-
-	/**
-	 * Get the click position in terms of axis values.
-	 *
-	 * @param {Object} e A pointer event
-	 */
-	getCoordinates: function (e) {
-		var coordinates = {
-				xAxis: [],
-				yAxis: []
-			};
-
-		each(this.chart.axes, function (axis) {
-			coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
-				axis: axis,
-				value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
-			});
-		});
-		return coordinates;
-	},
-	
-	/**
-	 * Return the index in the tooltipPoints array, corresponding to pixel position in 
-	 * the plot area.
-	 */
-	getIndex: function (e) {
-		var chart = this.chart;
-		return chart.inverted ? 
-			chart.plotHeight + chart.plotTop - e.chartY : 
-			e.chartX - chart.plotLeft;
-	},
-
-	/**
-	 * With line type charts with a single tracker, get the point closest to the mouse.
-	 * Run Point.onMouseOver and display tooltip for the point or points.
-	 */
-	runPointActions: function (e) {
-		var pointer = this,
-			chart = pointer.chart,
-			series = chart.series,
-			tooltip = chart.tooltip,
-			followPointer,
-			point,
-			points,
-			hoverPoint = chart.hoverPoint,
-			hoverSeries = chart.hoverSeries,
-			i,
-			j,
-			distance = chart.chartWidth,
-			index = pointer.getIndex(e),
-			anchor;
-
-		// shared tooltip
-		if (tooltip && pointer.options.tooltip.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) {
-			points = [];
-
-			// loop over all series and find the ones with points closest to the mouse
-			i = series.length;
-			for (j = 0; j < i; j++) {
-				if (series[j].visible &&
-						series[j].options.enableMouseTracking !== false &&
-						!series[j].noSharedTooltip && series[j].singularTooltips !== true && series[j].tooltipPoints.length) {
-					point = series[j].tooltipPoints[index];
-					if (point && point.series) { // not a dummy point, #1544
-						point._dist = mathAbs(index - point.clientX);
-						distance = mathMin(distance, point._dist);
-						points.push(point);
-					}
-				}
-			}
-			// remove furthest points
-			i = points.length;
-			while (i--) {
-				if (points[i]._dist > distance) {
-					points.splice(i, 1);
-				}
-			}
-			// refresh the tooltip if necessary
-			if (points.length && (points[0].clientX !== pointer.hoverX)) {
-				tooltip.refresh(points, e);
-				pointer.hoverX = points[0].clientX;
-			}
-		}
-
-		// Separate tooltip and general mouse events
-		followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
-		if (hoverSeries && hoverSeries.tracker && !followPointer) { // #2584, #2830
-
-			// get the point
-			point = hoverSeries.tooltipPoints[index];
-
-			// a new point is hovered, refresh the tooltip
-			if (point && point !== hoverPoint) {
-
-				// trigger the events
-				point.onMouseOver(e);
-
-			}
-			
-		} else if (tooltip && followPointer && !tooltip.isHidden) {
-			anchor = tooltip.getAnchor([{}], e);
-			tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
-		}
-
-		// Start the event listener to pick up the tooltip 
-		if (tooltip && !pointer._onDocumentMouseMove) {
-			pointer._onDocumentMouseMove = function (e) {
-				if (charts[hoverChartIndex]) {
-					charts[hoverChartIndex].pointer.onDocumentMouseMove(e);
-				}
-			};
-			addEvent(doc, 'mousemove', pointer._onDocumentMouseMove);
-		}
-
-		// Draw independent crosshairs
-		each(chart.axes, function (axis) {
-			axis.drawCrosshair(e, pick(point, hoverPoint));
-		});
-	},
-
-
-
-	/**
-	 * Reset the tracking by hiding the tooltip, the hover series state and the hover point
-	 * 
-	 * @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible
-	 */
-	reset: function (allowMove) {
-		var pointer = this,
-			chart = pointer.chart,
-			hoverSeries = chart.hoverSeries,
-			hoverPoint = chart.hoverPoint,
-			tooltip = chart.tooltip,
-			tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint;
-			
-		// Narrow in allowMove
-		allowMove = allowMove && tooltip && tooltipPoints;
-			
-		// Check if the points have moved outside the plot area, #1003
-		if (allowMove && splat(tooltipPoints)[0].plotX === UNDEFINED) {
-			allowMove = false;
-		}	
-
-		// Just move the tooltip, #349
-		if (allowMove) {
-			tooltip.refresh(tooltipPoints);
-			if (hoverPoint) { // #2500
-				hoverPoint.setState(hoverPoint.state, true);
-			}
-
-		// Full reset
-		} else {
-
-			if (hoverPoint) {
-				hoverPoint.onMouseOut();
-			}
-
-			if (hoverSeries) {
-				hoverSeries.onMouseOut();
-			}
-
-			if (tooltip) {
-				tooltip.hide();
-			}
-
-			if (pointer._onDocumentMouseMove) {
-				removeEvent(doc, 'mousemove', pointer._onDocumentMouseMove);
-				pointer._onDocumentMouseMove = null;
-			}
-
-			// Remove crosshairs
-			each(chart.axes, function (axis) {
-				axis.hideCrosshair();
-			});
-			
-			pointer.hoverX = null;
-
-		}
-	},
-
-	/**
-	 * Scale series groups to a certain scale and translation
-	 */
-	scaleGroups: function (attribs, clip) {
-
-		var chart = this.chart,
-			seriesAttribs;
-
-		// Scale each series
-		each(chart.series, function (series) {
-			seriesAttribs = attribs || series.getPlotBox(); // #1701
-			if (series.xAxis && series.xAxis.zoomEnabled) {
-				series.group.attr(seriesAttribs);
-				if (series.markerGroup) {
-					series.markerGroup.attr(seriesAttribs);
-					series.markerGroup.clip(clip ? chart.clipRect : null);
-				}
-				if (series.dataLabelsGroup) {
-					series.dataLabelsGroup.attr(seriesAttribs);
-				}
-			}
-		});
-		
-		// Clip
-		chart.clipRect.attr(clip || chart.clipBox);
-	},
-
-	/**
-	 * Start a drag operation
-	 */
-	dragStart: function (e) {
-		var chart = this.chart;
-
-		// Record the start position
-		chart.mouseIsDown = e.type;
-		chart.cancelClick = false;
-		chart.mouseDownX = this.mouseDownX = e.chartX;
-		chart.mouseDownY = this.mouseDownY = e.chartY;
-	},
-
-	/**
-	 * Perform a drag operation in response to a mousemove event while the mouse is down
-	 */
-	drag: function (e) {
-
-		var chart = this.chart,
-			chartOptions = chart.options.chart,
-			chartX = e.chartX,
-			chartY = e.chartY,
-			zoomHor = this.zoomHor,
-			zoomVert = this.zoomVert,
-			plotLeft = chart.plotLeft,
-			plotTop = chart.plotTop,
-			plotWidth = chart.plotWidth,
-			plotHeight = chart.plotHeight,
-			clickedInside,
-			size,
-			mouseDownX = this.mouseDownX,
-			mouseDownY = this.mouseDownY;
-
-		// If the mouse is outside the plot area, adjust to cooordinates
-		// inside to prevent the selection marker from going outside
-		if (chartX < plotLeft) {
-			chartX = plotLeft;
-		} else if (chartX > plotLeft + plotWidth) {
-			chartX = plotLeft + plotWidth;
-		}
-
-		if (chartY < plotTop) {
-			chartY = plotTop;
-		} else if (chartY > plotTop + plotHeight) {
-			chartY = plotTop + plotHeight;
-		}
-		
-		// determine if the mouse has moved more than 10px
-		this.hasDragged = Math.sqrt(
-			Math.pow(mouseDownX - chartX, 2) +
-			Math.pow(mouseDownY - chartY, 2)
-		);
-		
-		if (this.hasDragged > 10) {
-			clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
-
-			// make a selection
-			if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside) {
-				if (!this.selectionMarker) {
-					this.selectionMarker = chart.renderer.rect(
-						plotLeft,
-						plotTop,
-						zoomHor ? 1 : plotWidth,
-						zoomVert ? 1 : plotHeight,
-						0
-					)
-					.attr({
-						fill: chartOptions.selectionMarkerFill || 'rgba(69,114,167,0.25)',
-						zIndex: 7
-					})
-					.add();
-				}
-			}
-
-			// adjust the width of the selection marker
-			if (this.selectionMarker && zoomHor) {
-				size = chartX - mouseDownX;
-				this.selectionMarker.attr({
-					width: mathAbs(size),
-					x: (size > 0 ? 0 : size) + mouseDownX
-				});
-			}
-			// adjust the height of the selection marker
-			if (this.selectionMarker && zoomVert) {
-				size = chartY - mouseDownY;
-				this.selectionMarker.attr({
-					height: mathAbs(size),
-					y: (size > 0 ? 0 : size) + mouseDownY
-				});
-			}
-
-			// panning
-			if (clickedInside && !this.selectionMarker && chartOptions.panning) {
-				chart.pan(e, chartOptions.panning);
-			}
-		}
-	},
-
-	/**
-	 * On mouse up or touch end across the entire document, drop the selection.
-	 */
-	drop: function (e) {
-		var chart = this.chart,
-			hasPinched = this.hasPinched;
-
-		if (this.selectionMarker) {
-			var selectionData = {
-					xAxis: [],
-					yAxis: [],
-					originalEvent: e.originalEvent || e
-				},
-				selectionBox = this.selectionMarker,
-				selectionLeft = selectionBox.attr ? selectionBox.attr('x') : selectionBox.x,
-				selectionTop = selectionBox.attr ? selectionBox.attr('y') : selectionBox.y,
-				selectionWidth = selectionBox.attr ? selectionBox.attr('width') : selectionBox.width,
-				selectionHeight = selectionBox.attr ? selectionBox.attr('height') : selectionBox.height,
-				runZoom;
-
-			// a selection has been made
-			if (this.hasDragged || hasPinched) {
-
-				// record each axis' min and max
-				each(chart.axes, function (axis) {
-					if (axis.zoomEnabled) {
-						var horiz = axis.horiz,
-							selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)),
-							selectionMax = axis.toValue((horiz ? selectionLeft + selectionWidth : selectionTop + selectionHeight));
-
-						if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859
-							selectionData[axis.coll].push({
-								axis: axis,
-								min: mathMin(selectionMin, selectionMax), // for reversed axes,
-								max: mathMax(selectionMin, selectionMax)
-							});
-							runZoom = true;
-						}
-					}
-				});
-				if (runZoom) {
-					fireEvent(chart, 'selection', selectionData, function (args) { 
-						chart.zoom(extend(args, hasPinched ? { animation: false } : null)); 
-					});
-				}
-
-			}
-			this.selectionMarker = this.selectionMarker.destroy();
-
-			// Reset scaling preview
-			if (hasPinched) {
-				this.scaleGroups();
-			}
-		}
-
-		// Reset all
-		if (chart) { // it may be destroyed on mouse up - #877
-			css(chart.container, { cursor: chart._cursor });
-			chart.cancelClick = this.hasDragged > 10; // #370
-			chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
-			this.pinchDown = [];
-		}
-	},
-
-	onContainerMouseDown: function (e) {
-
-		e = this.normalize(e);
-
-		// issue #295, dragging not always working in Firefox
-		if (e.preventDefault) {
-			e.preventDefault();
-		}
-		
-		this.dragStart(e);
-	},
-
-	
-
-	onDocumentMouseUp: function (e) {
-		if (charts[hoverChartIndex]) {
-			charts[hoverChartIndex].pointer.drop(e);
-		}
-	},
-
-	/**
-	 * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea.
-	 * Issue #149 workaround. The mouseleave event does not always fire. 
-	 */
-	onDocumentMouseMove: function (e) {
-		var chart = this.chart,
-			chartPosition = this.chartPosition,
-			hoverSeries = chart.hoverSeries;
-
-		e = this.normalize(e, chartPosition);
-
-		// If we're outside, hide the tooltip
-		if (chartPosition && hoverSeries && !this.inClass(e.target, 'highcharts-tracker') &&
-				!chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
-			this.reset();
-		}
-	},
-
-	/**
-	 * When mouse leaves the container, hide the tooltip.
-	 */
-	onContainerMouseLeave: function () {
-		var chart = charts[hoverChartIndex];
-		if (chart) {
-			chart.pointer.reset();
-			chart.pointer.chartPosition = null; // also reset the chart position, used in #149 fix
-		}
-	},
-
-	// The mousemove, touchmove and touchstart event handler
-	onContainerMouseMove: function (e) {
-
-		var chart = this.chart;
-
-		hoverChartIndex = chart.index;
-
-		// normalize
-		e = this.normalize(e);		
-		
-		if (chart.mouseIsDown === 'mousedown') {
-			this.drag(e);
-		} 
-		
-		// Show the tooltip and run mouse over events (#977)
-		if ((this.inClass(e.target, 'highcharts-tracker') || 
-				chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) && !chart.openMenu) {
-			this.runPointActions(e);
-		}
-	},
-
-	/**
-	 * Utility to detect whether an element has, or has a parent with, a specific
-	 * class name. Used on detection of tracker objects and on deciding whether
-	 * hovering the tooltip should cause the active series to mouse out.
-	 */
-	inClass: function (element, className) {
-		var elemClassName;
-		while (element) {
-			elemClassName = attr(element, 'class');
-			if (elemClassName) {
-				if (elemClassName.indexOf(className) !== -1) {
-					return true;
-				} else if (elemClassName.indexOf(PREFIX + 'container') !== -1) {
-					return false;
-				}
-			}
-			element = element.parentNode;
-		}		
-	},
-
-	onTrackerMouseOut: function (e) {
-		var series = this.chart.hoverSeries,
-			relatedTarget = e.relatedTarget || e.toElement,
-			relatedSeries = relatedTarget && relatedTarget.point && relatedTarget.point.series; // #2499
-		
-		if (series && !series.options.stickyTracking && !this.inClass(relatedTarget, PREFIX + 'tooltip') &&
-				relatedSeries !== series) {
-			series.onMouseOut();
-		}
-	},
-
-	onContainerClick: function (e) {
-		var chart = this.chart,
-			hoverPoint = chart.hoverPoint, 
-			plotLeft = chart.plotLeft,
-			plotTop = chart.plotTop;
-		
-		e = this.normalize(e);
-		e.cancelBubble = true; // IE specific
-
-		if (!chart.cancelClick) {
-			
-			// On tracker click, fire the series and point events. #783, #1583
-			if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) {
-
-				// the series click event
-				fireEvent(hoverPoint.series, 'click', extend(e, {
-					point: hoverPoint
-				}));
-
-				// the point click event
-				if (chart.hoverPoint) { // it may be destroyed (#1844)
-					hoverPoint.firePointEvent('click', e);
-				}
-
-			// When clicking outside a tracker, fire a chart event
-			} else {
-				extend(e, this.getCoordinates(e));
-
-				// fire a click event in the chart
-				if (chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) {
-					fireEvent(chart, 'click', e);
-				}
-			}
-
-
-		}
-	},
-
-	/**
-	 * Set the JS DOM events on the container and document. This method should contain
-	 * a one-to-one assignment between methods and their handlers. Any advanced logic should
-	 * be moved to the handler reflecting the event's name.
-	 */
-	setDOMEvents: function () {
-
-		var pointer = this,
-			container = pointer.chart.container;
-
-		container.onmousedown = function (e) {
-			pointer.onContainerMouseDown(e);
-		};
-		container.onmousemove = function (e) {
-			pointer.onContainerMouseMove(e);
-		};
-		container.onclick = function (e) {
-			pointer.onContainerClick(e);
-		};
-		addEvent(container, 'mouseleave', pointer.onContainerMouseLeave);
-		if (chartCount === 1) {
-			addEvent(doc, 'mouseup', pointer.onDocumentMouseUp);
-		}
-		if (hasTouch) {
-			container.ontouchstart = function (e) {
-				pointer.onContainerTouchStart(e);
-			};
-			container.ontouchmove = function (e) {
-				pointer.onContainerTouchMove(e);
-			};
-			if (chartCount === 1) {
-				addEvent(doc, 'touchend', pointer.onDocumentTouchEnd);
-			}
-		}
-		
-	},
-
-	/**
-	 * Destroys the Pointer object and disconnects DOM events.
-	 */
-	destroy: function () {
-		var prop;
-
-		removeEvent(this.chart.container, 'mouseleave', this.onContainerMouseLeave);
-		if (!chartCount) {
-			removeEvent(doc, 'mouseup', this.onDocumentMouseUp);
-			removeEvent(doc, 'touchend', this.onDocumentTouchEnd);
-		}
-
-		// memory and CPU leak
-		clearInterval(this.tooltipTimeout);
-
-		for (prop in this) {
-			this[prop] = null;
-		}
-	}
-};
-
-
-/* Support for touch devices */
-extend(Highcharts.Pointer.prototype, {
-
-	/**
-	 * Run translation operations
-	 */
-	pinchTranslate: function (pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
-		if (this.zoomHor || this.pinchHor) {
-			this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
-		}
-		if (this.zoomVert || this.pinchVert) {
-			this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
-		}
-	},
-
-	/**
-	 * Run translation operations for each direction (horizontal and vertical) independently
-	 */
-	pinchTranslateDirection: function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
-		var chart = this.chart,
-			xy = horiz ? 'x' : 'y',
-			XY = horiz ? 'X' : 'Y',
-			sChartXY = 'chart' + XY,
-			wh = horiz ? 'width' : 'height',
-			plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')],
-			selectionWH,
-			selectionXY,
-			clipXY,
-			scale = forcedScale || 1,
-			inverted = chart.inverted,
-			bounds = chart.bounds[horiz ? 'h' : 'v'],
-			singleTouch = pinchDown.length === 1,
-			touch0Start = pinchDown[0][sChartXY],
-			touch0Now = touches[0][sChartXY],
-			touch1Start = !singleTouch && pinchDown[1][sChartXY],
-			touch1Now = !singleTouch && touches[1][sChartXY],
-			outOfBounds,
-			transformScale,
-			scaleKey,
-			setScale = function () {
-				if (!singleTouch && mathAbs(touch0Start - touch1Start) > 20) { // Don't zoom if fingers are too close on this axis
-					scale = forcedScale || mathAbs(touch0Now - touch1Now) / mathAbs(touch0Start - touch1Start); 
-				}
-				
-				clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
-				selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
-			};
-
-		// Set the scale, first pass
-		setScale();
-
-		selectionXY = clipXY; // the clip position (x or y) is altered if out of bounds, the selection position is not
-
-		// Out of bounds
-		if (selectionXY < bounds.min) {
-			selectionXY = bounds.min;
-			outOfBounds = true;
-		} else if (selectionXY + selectionWH > bounds.max) {
-			selectionXY = bounds.max - selectionWH;
-			outOfBounds = true;
-		}
-		
-		// Is the chart dragged off its bounds, determined by dataMin and dataMax?
-		if (outOfBounds) {
-
-			// Modify the touchNow position in order to create an elastic drag movement. This indicates
-			// to the user that the chart is responsive but can't be dragged further.
-			touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
-			if (!singleTouch) {
-				touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
-			}
-
-			// Set the scale, second pass to adapt to the modified touchNow positions
-			setScale();
-
-		} else {
-			lastValidTouch[xy] = [touch0Now, touch1Now];
-		}
-
-		// Set geometry for clipping, selection and transformation
-		if (!inverted) { // TODO: implement clipping for inverted charts
-			clip[xy] = clipXY - plotLeftTop;
-			clip[wh] = selectionWH;
-		}
-		scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
-		transformScale = inverted ? 1 / scale : scale;
-
-		selectionMarker[wh] = selectionWH;
-		selectionMarker[xy] = selectionXY;
-		transform[scaleKey] = scale;
-		transform['translate' + XY] = (transformScale * plotLeftTop) + (touch0Now - (transformScale * touch0Start));
-	},
-	
-	/**
-	 * Handle touch events with two touches
-	 */
-	pinch: function (e) {
-
-		var self = this,
-			chart = self.chart,
-			pinchDown = self.pinchDown,
-			followTouchMove = self.followTouchMove,
-			touches = e.touches,
-			touchesLength = touches.length,
-			lastValidTouch = self.lastValidTouch,
-			hasZoom = self.hasZoom,
-			selectionMarker = self.selectionMarker,
-			transform = {},
-			fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, PREFIX + 'tracker') && 
-				chart.runTrackerClick) || chart.runChartClick),
-			clip = {};
-
-		// On touch devices, only proceed to trigger click if a handler is defined
-		if ((hasZoom || followTouchMove) && !fireClickEvent) {
-			e.preventDefault();
-		}
-		
-		// Normalize each touch
-		map(touches, function (e) {
-			return self.normalize(e);
-		});
-		
-		// Register the touch start position
-		if (e.type === 'touchstart') {
-			each(touches, function (e, i) {
-				pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
-			});
-			lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] && pinchDown[1].chartX];
-			lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] && pinchDown[1].chartY];
-
-			// Identify the data bounds in pixels
-			each(chart.axes, function (axis) {
-				if (axis.zoomEnabled) {
-					var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
-						minPixelPadding = axis.minPixelPadding,
-						min = axis.toPixels(axis.dataMin),
-						max = axis.toPixels(axis.dataMax),
-						absMin = mathMin(min, max),
-						absMax = mathMax(min, max);
-
-					// Store the bounds for use in the touchmove handler
-					bounds.min = mathMin(axis.pos, absMin - minPixelPadding);
-					bounds.max = mathMax(axis.pos + axis.len, absMax + minPixelPadding);
-				}
-			});
-		
-		// Event type is touchmove, handle panning and pinching
-		} else if (pinchDown.length) { // can be 0 when releasing, if touchend fires first
-			
-
-			// Set the marker
-			if (!selectionMarker) {
-				self.selectionMarker = selectionMarker = extend({
-					destroy: noop
-				}, chart.plotBox);
-			}
-			
-			self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
-
-			self.hasPinched = hasZoom;
-
-			// Scale and translate the groups to provide visual feedback during pinching
-			self.scaleGroups(transform, clip);
-			
-			// Optionally move the tooltip on touchmove
-			if (!hasZoom && followTouchMove && touchesLength === 1) {
-				this.runPointActions(self.normalize(e));
-			}
-		}
-	},
-
-	onContainerTouchStart: function (e) {
-		var chart = this.chart;
-
-		hoverChartIndex = chart.index;
-
-		if (e.touches.length === 1) {
-
-			e = this.normalize(e);
-
-			if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
-
-				// Run mouse events and display tooltip etc
-				this.runPointActions(e);
-
-				this.pinch(e);
-
-			} else {
-				// Hide the tooltip on touching outside the plot area (#1203)
-				this.reset();
-			}
-
-		} else if (e.touches.length === 2) {
-			this.pinch(e);
-		}   
-	},
-
-	onContainerTouchMove: function (e) {
-		if (e.touches.length === 1 || e.touches.length === 2) {
-			this.pinch(e);
-		}
-	},
-
-	onDocumentTouchEnd: function (e) {
-		if (charts[hoverChartIndex]) {
-			charts[hoverChartIndex].pointer.drop(e);
-		}
-	}
-
-});
-if (win.PointerEvent || win.MSPointerEvent) {
-	
-	// The touches object keeps track of the points being touched at all times
-	var touches = {},
-		hasPointerEvent = !!win.PointerEvent,
-		getWebkitTouches = function () {
-			var key, fake = [];
-			fake.item = function (i) { return this[i]; };
-			for (key in touches) {
-				if (touches.hasOwnProperty(key)) {
-					fake.push({
-						pageX: touches[key].pageX,
-						pageY: touches[key].pageY,
-						target: touches[key].target
-					});
-				}
-			}
-			return fake;
-		},
-		translateMSPointer = function (e, method, wktype, callback) {
-			var p;
-			e = e.originalEvent || e;
-			if ((e.pointerType === 'touch' || e.pointerType === e.MSPOINTER_TYPE_TOUCH) && charts[hoverChartIndex]) {
-				callback(e);
-				p = charts[hoverChartIndex].pointer;
-				p[method]({
-					type: wktype,
-					target: e.currentTarget,
-					preventDefault: noop,
-					touches: getWebkitTouches()
-				});				
-			}
-		};
-
-	/**
-	 * Extend the Pointer prototype with methods for each event handler and more
-	 */
-	extend(Pointer.prototype, {
-		onContainerPointerDown: function (e) {
-			translateMSPointer(e, 'onContainerTouchStart', 'touchstart', function (e) {
-				touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY, target: e.currentTarget };
-			});
-		},
-		onContainerPointerMove: function (e) {
-			translateMSPointer(e, 'onContainerTouchMove', 'touchmove', function (e) {
-				touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY };
-				if (!touches[e.pointerId].target) {
-					touches[e.pointerId].target = e.currentTarget;
-				}
-			});
-		},
-		onDocumentPointerUp: function (e) {
-			translateMSPointer(e, 'onContainerTouchEnd', 'touchend', function (e) {
-				delete touches[e.pointerId];
-			});
-		},
-
-		/**
-		 * Add or remove the MS Pointer specific events
-		 */
-		batchMSEvents: function (fn) {
-			fn(this.chart.container, hasPointerEvent ? 'pointerdown' : 'MSPointerDown', this.onContainerPointerDown);
-			fn(this.chart.container, hasPointerEvent ? 'pointermove' : 'MSPointerMove', this.onContainerPointerMove);
-			fn(doc, hasPointerEvent ? 'pointerup' : 'MSPointerUp', this.onDocumentPointerUp);
-		}
-	});
-
-	// Disable default IE actions for pinch and such on chart element
-	wrap(Pointer.prototype, 'init', function (proceed, chart, options) {
-		proceed.call(this, chart, options);
-		if (this.hasZoom || this.followTouchMove) {
-			css(chart.container, {
-				'-ms-touch-action': NONE,
-				'touch-action': NONE
-			});
-		}
-	});
-
-	// Add IE specific touch events to chart
-	wrap(Pointer.prototype, 'setDOMEvents', function (proceed) {
-		proceed.apply(this);
-		if (this.hasZoom || this.followTouchMove) {
-			this.batchMSEvents(addEvent);
-		}
-	});
-	// Destroy MS events also
-	wrap(Pointer.prototype, 'destroy', function (proceed) {
-		this.batchMSEvents(removeEvent);
-		proceed.call(this);
-	});
-}
-/**
- * The overview of the chart's series
- */
-var Legend = Highcharts.Legend = function (chart, options) {
-	this.init(chart, options);
-};
-
-Legend.prototype = {
-	
-	/**
-	 * Initialize the legend
-	 */
-	init: function (chart, options) {
-		
-		var legend = this,
-			itemStyle = options.itemStyle,
-			padding = pick(options.padding, 8),
-			itemMarginTop = options.itemMarginTop || 0;
-	
-		this.options = options;
-
-		if (!options.enabled) {
-			return;
-		}
-	
-		legend.baseline = pInt(itemStyle.fontSize) + 3 + itemMarginTop; // used in Series prototype
-		legend.itemStyle = itemStyle;
-		legend.itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle);
-		legend.itemMarginTop = itemMarginTop;
-		legend.padding = padding;
-		legend.initialItemX = padding;
-		legend.initialItemY = padding - 5; // 5 is the number of pixels above the text
-		legend.maxItemWidth = 0;
-		legend.chart = chart;
-		legend.itemHeight = 0;
-		legend.lastLineHeight = 0;
-		legend.symbolWidth = pick(options.symbolWidth, 16);
-		legend.pages = [];
-
-
-		// Render it
-		legend.render();
-
-		// move checkboxes
-		addEvent(legend.chart, 'endResize', function () { 
-			legend.positionCheckboxes();
-		});
-
-	},
-
-	/**
-	 * Set the colors for the legend item
-	 * @param {Object} item A Series or Point instance
-	 * @param {Object} visible Dimmed or colored
-	 */
-	colorizeItem: function (item, visible) {
-		var legend = this,
-			options = legend.options,
-			legendItem = item.legendItem,
-			legendLine = item.legendLine,
-			legendSymbol = item.legendSymbol,
-			hiddenColor = legend.itemHiddenStyle.color,
-			textColor = visible ? options.itemStyle.color : hiddenColor,
-			symbolColor = visible ? (item.legendColor || item.color || '#CCC') : hiddenColor,
-			markerOptions = item.options && item.options.marker,
-			symbolAttr = { fill: symbolColor },
-			key,
-			val;
-		
-		if (legendItem) {
-			legendItem.css({ fill: textColor, color: textColor }); // color for #1553, oldIE
-		}
-		if (legendLine) {
-			legendLine.attr({ stroke: symbolColor });
-		}
-		
-		if (legendSymbol) {
-			
-			// Apply marker options
-			if (markerOptions && legendSymbol.isMarker) { // #585
-				symbolAttr.stroke = symbolColor;
-				markerOptions = item.convertAttribs(markerOptions);
-				for (key in markerOptions) {
-					val = markerOptions[key];
-					if (val !== UNDEFINED) {
-						symbolAttr[key] = val;
-					}
-				}
-			}
-
-			legendSymbol.attr(symbolAttr);
-		}
-	},
-
-	/**
-	 * Position the legend item
-	 * @param {Object} item A Series or Point instance
-	 */
-	positionItem: function (item) {
-		var legend = this,
-			options = legend.options,
-			symbolPadding = options.symbolPadding,
-			ltr = !options.rtl,
-			legendItemPos = item._legendItemPos,
-			itemX = legendItemPos[0],
-			itemY = legendItemPos[1],
-			checkbox = item.checkbox;
-
-		if (item.legendGroup) {
-			item.legendGroup.translate(
-				ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4,
-				itemY
-			);
-		}
-
-		if (checkbox) {
-			checkbox.x = itemX;
-			checkbox.y = itemY;
-		}
-	},
-
-	/**
-	 * Destroy a single legend item
-	 * @param {Object} item The series or point
-	 */
-	destroyItem: function (item) {
-		var checkbox = item.checkbox;
-
-		// destroy SVG elements
-		each(['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'], function (key) {
-			if (item[key]) {
-				item[key] = item[key].destroy();
-			}
-		});
-
-		if (checkbox) {
-			discardElement(item.checkbox);
-		}
-	},
-
-	/**
-	 * Destroys the legend.
-	 */
-	destroy: function () {
-		var legend = this,
-			legendGroup = legend.group,
-			box = legend.box;
-
-		if (box) {
-			legend.box = box.destroy();
-		}
-
-		if (legendGroup) {
-			legend.group = legendGroup.destroy();
-		}
-	},
-
-	/**
-	 * Position the checkboxes after the width is determined
-	 */
-	positionCheckboxes: function (scrollOffset) {
-		var alignAttr = this.group.alignAttr,
-			translateY,
-			clipHeight = this.clipHeight || this.legendHeight;
-
-		if (alignAttr) {
-			translateY = alignAttr.translateY;
-			each(this.allItems, function (item) {
-				var checkbox = item.checkbox,
-					top;
-				
-				if (checkbox) {
-					top = (translateY + checkbox.y + (scrollOffset || 0) + 3);
-					css(checkbox, {
-						left: (alignAttr.translateX + item.checkboxOffset + checkbox.x - 20) + PX,
-						top: top + PX,
-						display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : NONE
-					});
-				}
-			});
-		}
-	},
-	
-	/**
-	 * Render the legend title on top of the legend
-	 */
-	renderTitle: function () {
-		var options = this.options,
-			padding = this.padding,
-			titleOptions = options.title,
-			titleHeight = 0,
-			bBox;
-		
-		if (titleOptions.text) {
-			if (!this.title) {
-				this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title')
-					.attr({ zIndex: 1 })
-					.css(titleOptions.style)
-					.add(this.group);
-			}
-			bBox = this.title.getBBox();
-			titleHeight = bBox.height;
-			this.offsetWidth = bBox.width; // #1717
-			this.contentGroup.attr({ translateY: titleHeight });
-		}
-		this.titleHeight = titleHeight;
-	},
-
-	/**
-	 * Render a single specific legend item
-	 * @param {Object} item A series or point
-	 */
-	renderItem: function (item) {
-		var legend = this,
-			chart = legend.chart,
-			renderer = chart.renderer,
-			options = legend.options,
-			horizontal = options.layout === 'horizontal',
-			symbolWidth = legend.symbolWidth,
-			symbolPadding = options.symbolPadding,
-			itemStyle = legend.itemStyle,
-			itemHiddenStyle = legend.itemHiddenStyle,
-			padding = legend.padding,
-			itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, // docs
-			ltr = !options.rtl,
-			itemHeight,
-			widthOption = options.width,
-			itemMarginBottom = options.itemMarginBottom || 0,
-			itemMarginTop = legend.itemMarginTop,
-			initialItemX = legend.initialItemX,
-			bBox,
-			itemWidth,
-			li = item.legendItem,
-			series = item.series && item.series.drawLegendSymbol ? item.series : item,
-			seriesOptions = series.options,
-			showCheckbox = legend.createCheckboxForItem && seriesOptions && seriesOptions.showCheckbox,
-			useHTML = options.useHTML;
-
-		if (!li) { // generate it once, later move it
-
-			// Generate the group box
-			// A group to hold the symbol and text. Text is to be appended in Legend class.
-			item.legendGroup = renderer.g('legend-item')
-				.attr({ zIndex: 1 })
-				.add(legend.scrollGroup);
-
-			// Draw the legend symbol inside the group box
-			series.drawLegendSymbol(legend, item);
-
-			// Generate the list item text and add it to the group
-			item.legendItem = li = renderer.text(
-					options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item),
-					ltr ? symbolWidth + symbolPadding : -symbolPadding,
-					legend.baseline,
-					useHTML
-				)
-				.css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
-				.attr({
-					align: ltr ? 'left' : 'right',
-					zIndex: 2
-				})
-				.add(item.legendGroup);
-
-			if (legend.setItemEvents) {
-				legend.setItemEvents(item, li, useHTML, itemStyle, itemHiddenStyle);
-			}			
-
-			// Colorize the items
-			legend.colorizeItem(item, item.visible);
-
-			// add the HTML checkbox on top
-			if (showCheckbox) {
-				legend.createCheckboxForItem(item);				
-			}
-		}
-
-		// calculate the positions for the next line
-		bBox = li.getBBox();
-
-		itemWidth = item.checkboxOffset = 
-			options.itemWidth || 
-			item.legendItemWidth || 
-			symbolWidth + symbolPadding + bBox.width + itemDistance + (showCheckbox ? 20 : 0);
-		legend.itemHeight = itemHeight = mathRound(item.legendItemHeight || bBox.height);
-
-		// if the item exceeds the width, start a new line
-		if (horizontal && legend.itemX - initialItemX + itemWidth >
-				(widthOption || (chart.chartWidth - 2 * padding - initialItemX - options.x))) {
-			legend.itemX = initialItemX;
-			legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
-			legend.lastLineHeight = 0; // reset for next line
-		}
-
-		// If the item exceeds the height, start a new column
-		/*if (!horizontal && legend.itemY + options.y + itemHeight > chart.chartHeight - spacingTop - spacingBottom) {
-			legend.itemY = legend.initialItemY;
-			legend.itemX += legend.maxItemWidth;
-			legend.maxItemWidth = 0;
-		}*/
-
-		// Set the edge positions
-		legend.maxItemWidth = mathMax(legend.maxItemWidth, itemWidth);
-		legend.lastItemY = itemMarginTop + legend.itemY + itemMarginBottom;
-		legend.lastLineHeight = mathMax(itemHeight, legend.lastLineHeight); // #915
-
-		// cache the position of the newly generated or reordered items
-		item._legendItemPos = [legend.itemX, legend.itemY];
-
-		// advance
-		if (horizontal) {
-			legend.itemX += itemWidth;
-
-		} else {
-			legend.itemY += itemMarginTop + itemHeight + itemMarginBottom;
-			legend.lastLineHeight = itemHeight;
-		}
-
-		// the width of the widest item
-		legend.offsetWidth = widthOption || mathMax(
-			(horizontal ? legend.itemX - initialItemX - itemDistance : itemWidth) + padding,
-			legend.offsetWidth
-		);
-	},
-
-	/**
-	 * Get all items, which is one item per series for normal series and one item per point
-	 * for pie series.
-	 */
-	getAllItems: function () {
-		var allItems = [];
-		each(this.chart.series, function (series) {
-			var seriesOptions = series.options;
-
-			// Handle showInLegend. If the series is linked to another series, defaults to false.
-			if (!pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? UNDEFINED : false, true)) {
-				return;
-			}
-
-			// use points or series for the legend item depending on legendType
-			allItems = allItems.concat(
-					series.legendItems ||
-					(seriesOptions.legendType === 'point' ?
-							series.data :
-							series)
-			);
-		});
-		return allItems;
-	},
-
-	/**
-	 * Render the legend. This method can be called both before and after
-	 * chart.render. If called after, it will only rearrange items instead
-	 * of creating new ones.
-	 */
-	render: function () {
-		var legend = this,
-			chart = legend.chart,
-			renderer = chart.renderer,
-			legendGroup = legend.group,
-			allItems,
-			display,
-			legendWidth,
-			legendHeight,
-			box = legend.box,
-			options = legend.options,
-			padding = legend.padding,
-			legendBorderWidth = options.borderWidth,
-			legendBackgroundColor = options.backgroundColor;
-
-		legend.itemX = legend.initialItemX;
-		legend.itemY = legend.initialItemY;
-		legend.offsetWidth = 0;
-		legend.lastItemY = 0;
-
-		if (!legendGroup) {
-			legend.group = legendGroup = renderer.g('legend')
-				.attr({ zIndex: 7 }) 
-				.add();
-			legend.contentGroup = renderer.g()
-				.attr({ zIndex: 1 }) // above background
-				.add(legendGroup);
-			legend.scrollGroup = renderer.g()
-				.add(legend.contentGroup);
-		}
-		
-		legend.renderTitle();
-
-		// add each series or point
-		allItems = legend.getAllItems();
-
-		// sort by legendIndex
-		stableSort(allItems, function (a, b) {
-			return ((a.options && a.options.legendIndex) || 0) - ((b.options && b.options.legendIndex) || 0);
-		});
-
-		// reversed legend
-		if (options.reversed) {
-			allItems.reverse();
-		}
-
-		legend.allItems = allItems;
-		legend.display = display = !!allItems.length;
-
-		// render the items
-		each(allItems, function (item) {
-			legend.renderItem(item); 
-		});
-
-		// Draw the border
-		legendWidth = options.width || legend.offsetWidth;
-		legendHeight = legend.lastItemY + legend.lastLineHeight + legend.titleHeight;
-		
-		
-		legendHeight = legend.handleOverflow(legendHeight);
-
-		if (legendBorderWidth || legendBackgroundColor) {
-			legendWidth += padding;
-			legendHeight += padding;
-
-			if (!box) {
-				legend.box = box = renderer.rect(
-					0,
-					0,
-					legendWidth,
-					legendHeight,
-					options.borderRadius,
-					legendBorderWidth || 0
-				).attr({
-					stroke: options.borderColor,
-					'stroke-width': legendBorderWidth || 0,
-					fill: legendBackgroundColor || NONE
-				})
-				.add(legendGroup)
-				.shadow(options.shadow);
-				box.isNew = true;
-
-			} else if (legendWidth > 0 && legendHeight > 0) {
-				box[box.isNew ? 'attr' : 'animate'](
-					box.crisp({ width: legendWidth, height: legendHeight })
-				);
-				box.isNew = false;
-			}
-
-			// hide the border if no items
-			box[display ? 'show' : 'hide']();
-		}
-		
-		legend.legendWidth = legendWidth;
-		legend.legendHeight = legendHeight;
-
-		// Now that the legend width and height are established, put the items in the 
-		// final position
-		each(allItems, function (item) {
-			legend.positionItem(item);
-		});
-
-		// 1.x compatibility: positioning based on style
-		/*var props = ['left', 'right', 'top', 'bottom'],
-			prop,
-			i = 4;
-		while (i--) {
-			prop = props[i];
-			if (options.style[prop] && options.style[prop] !== 'auto') {
-				options[i < 2 ? 'align' : 'verticalAlign'] = prop;
-				options[i < 2 ? 'x' : 'y'] = pInt(options.style[prop]) * (i % 2 ? -1 : 1);
-			}
-		}*/
-
-		if (display) {
-			legendGroup.align(extend({
-				width: legendWidth,
-				height: legendHeight
-			}, options), true, 'spacingBox');
-		}
-
-		if (!chart.isResizing) {
-			this.positionCheckboxes();
-		}
-	},
-	
-	/**
-	 * Set up the overflow handling by adding navigation with up and down arrows below the
-	 * legend.
-	 */
-	handleOverflow: function (legendHeight) {
-		var legend = this,
-			chart = this.chart,
-			renderer = chart.renderer,
-			options = this.options,
-			optionsY = options.y,
-			alignTop = options.verticalAlign === 'top',
-			spaceHeight = chart.spacingBox.height + (alignTop ? -optionsY : optionsY) - this.padding,
-			maxHeight = options.maxHeight,
-			clipHeight,
-			clipRect = this.clipRect,
-			navOptions = options.navigation,
-			animation = pick(navOptions.animation, true),
-			arrowSize = navOptions.arrowSize || 12,
-			nav = this.nav,
-			pages = this.pages,
-			lastY,
-			allItems = this.allItems;
-			
-		// Adjust the height
-		if (options.layout === 'horizontal') {
-			spaceHeight /= 2;
-		}
-		if (maxHeight) {
-			spaceHeight = mathMin(spaceHeight, maxHeight);
-		}
-		
-		// Reset the legend height and adjust the clipping rectangle
-		pages.length = 0;
-		if (legendHeight > spaceHeight && !options.useHTML) {
-
-			this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight - this.padding;
-			this.currentPage = pick(this.currentPage, 1);
-			this.fullHeight = legendHeight;
-			
-			// Fill pages with Y positions so that the top of each a legend item defines
-			// the scroll top for each page (#2098)
-			each(allItems, function (item, i) {
-				var y = item._legendItemPos[1],
-					h = mathRound(item.legendItem.getBBox().height),
-					len = pages.length;
-				
-				if (!len || (y - pages[len - 1] > clipHeight && (lastY || y) !== pages[len - 1])) {
-					pages.push(lastY || y);
-					len++;
-				}
-				
-				if (i === allItems.length - 1 && y + h - pages[len - 1] > clipHeight) {
-					pages.push(y);
-				}
-				if (y !== lastY) {
-					lastY = y;
-				}
-			});
-
-			// Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787)
-			if (!clipRect) {
-				clipRect = legend.clipRect = renderer.clipRect(0, this.padding, 9999, 0);
-				legend.contentGroup.clip(clipRect);
-			}
-			clipRect.attr({
-				height: clipHeight
-			});
-			
-			// Add navigation elements
-			if (!nav) {
-				this.nav = nav = renderer.g().attr({ zIndex: 1 }).add(this.group);
-				this.up = renderer.symbol('triangle', 0, 0, arrowSize, arrowSize)
-					.on('click', function () {
-						legend.scroll(-1, animation);
-					})
-					.add(nav);
-				this.pager = renderer.text('', 15, 10)
-					.css(navOptions.style)
-					.add(nav);
-				this.down = renderer.symbol('triangle-down', 0, 0, arrowSize, arrowSize)
-					.on('click', function () {
-						legend.scroll(1, animation);
-					})
-					.add(nav);
-			}
-			
-			// Set initial position
-			legend.scroll(0);
-			
-			legendHeight = spaceHeight;
-			
-		} else if (nav) {
-			clipRect.attr({
-				height: chart.chartHeight
-			});
-			nav.hide();
-			this.scrollGroup.attr({
-				translateY: 1
-			});
-			this.clipHeight = 0; // #1379
-		}
-		
-		return legendHeight;
-	},
-	
-	/**
-	 * Scroll the legend by a number of pages
-	 * @param {Object} scrollBy
-	 * @param {Object} animation
-	 */
-	scroll: function (scrollBy, animation) {
-		var pages = this.pages,
-			pageCount = pages.length,
-			currentPage = this.currentPage + scrollBy,
-			clipHeight = this.clipHeight,
-			navOptions = this.options.navigation,
-			activeColor = navOptions.activeColor,
-			inactiveColor = navOptions.inactiveColor,
-			pager = this.pager,
-			padding = this.padding,
-			scrollOffset;
-		
-		// When resizing while looking at the last page
-		if (currentPage > pageCount) {
-			currentPage = pageCount;
-		}
-		
-		if (currentPage > 0) {
-			
-			if (animation !== UNDEFINED) {
-				setAnimation(animation, this.chart);
-			}
-			
-			this.nav.attr({
-				translateX: padding,
-				translateY: clipHeight + this.padding + 7 + this.titleHeight,
-				visibility: VISIBLE
-			});
-			this.up.attr({
-					fill: currentPage === 1 ? inactiveColor : activeColor
-				})
-				.css({
-					cursor: currentPage === 1 ? 'default' : 'pointer'
-				});
-			pager.attr({
-				text: currentPage + '/' + pageCount
-			});
-			this.down.attr({
-					x: 18 + this.pager.getBBox().width, // adjust to text width
-					fill: currentPage === pageCount ? inactiveColor : activeColor
-				})
-				.css({
-					cursor: currentPage === pageCount ? 'default' : 'pointer'
-				});
-			
-			scrollOffset = -pages[currentPage - 1] + this.initialItemY;
-
-			this.scrollGroup.animate({
-				translateY: scrollOffset
-			});			
-			
-			this.currentPage = currentPage;
-			this.positionCheckboxes(scrollOffset);
-		}
-			
-	}
-	
-};
-
-/*
- * LegendSymbolMixin
- */ 
-
-var LegendSymbolMixin = Highcharts.LegendSymbolMixin = {
-
-	/**
-	 * Get the series' symbol in the legend
-	 * 
-	 * @param {Object} legend The legend object
-	 * @param {Object} item The series (this) or point
-	 */
-	drawRectangle: function (legend, item) {
-		var symbolHeight = legend.options.symbolHeight || 12;
-		
-		item.legendSymbol = this.chart.renderer.rect(
-			0,
-			legend.baseline - 5 - (symbolHeight / 2),
-			legend.symbolWidth,
-			symbolHeight,
-			legend.options.symbolRadius || 0
-		).attr({
-			zIndex: 3
-		}).add(item.legendGroup);		
-		
-	},
-
-	/**
-	 * Get the series' symbol in the legend. This method should be overridable to create custom 
-	 * symbols through Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
-	 * 
-	 * @param {Object} legend The legend object
-	 */
-	drawLineMarker: function (legend) {
-
-		var options = this.options,
-			markerOptions = options.marker,
-			radius,
-			legendOptions = legend.options,
-			legendSymbol,
-			symbolWidth = legend.symbolWidth,
-			renderer = this.chart.renderer,
-			legendItemGroup = this.legendGroup,
-			verticalCenter = legend.baseline - mathRound(renderer.fontMetrics(legendOptions.itemStyle.fontSize).b * 0.3),
-			attr;
-			
-		// Draw the line
-		if (options.lineWidth) {
-			attr = {
-				'stroke-width': options.lineWidth
-			};
-			if (options.dashStyle) {
-				attr.dashstyle = options.dashStyle;
-			}
-			this.legendLine = renderer.path([
-				M,
-				0,
-				verticalCenter,
-				L,
-				symbolWidth,
-				verticalCenter
-			])
-			.attr(attr)
-			.add(legendItemGroup);
-		}
-		
-		// Draw the marker
-		if (markerOptions && markerOptions.enabled !== false) {
-			radius = markerOptions.radius;
-			this.legendSymbol = legendSymbol = renderer.symbol(
-				this.symbol,
-				(symbolWidth / 2) - radius,
-				verticalCenter - radius,
-				2 * radius,
-				2 * radius
-			)
-			.add(legendItemGroup);
-			legendSymbol.isMarker = true;
-		}
-	}
-};
-
-// Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
-// and for #2580, a similar drawing flaw in Firefox 26.
-// TODO: Explore if there's a general cause for this. The problem may be related 
-// to nested group elements, as the legend item texts are within 4 group elements.
-if (/Trident\/7\.0/.test(userAgent) || isFirefox) {
-	wrap(Legend.prototype, 'positionItem', function (proceed, item) {
-		var legend = this,
-			runPositionItem = function () { // If chart destroyed in sync, this is undefined (#2030)
-				if (item._legendItemPos) {
-					proceed.call(legend, item);
-				}
-			};
-
-		// Do it now, for export and to get checkbox placement
-		runPositionItem();
-		
-		// Do it after to work around the core issue
-		setTimeout(runPositionItem);
-	});
-}
-/**
- * The chart class
- * @param {Object} options
- * @param {Function} callback Function to run when the chart has loaded
- */
-function Chart() {
-	this.init.apply(this, arguments);
-}
-
-Chart.prototype = {
-
-	/**
-	 * Initialize the chart
-	 */
-	init: function (userOptions, callback) {
-
-		// Handle regular options
-		var options,
-			seriesOptions = userOptions.series; // skip merging data points to increase performance
-
-		userOptions.series = null;
-		options = merge(defaultOptions, userOptions); // do the merge
-		options.series = userOptions.series = seriesOptions; // set back the series data
-		this.userOptions = userOptions;
-
-		var optionsChart = options.chart;
-		
-		// Create margin & spacing array
-		this.margin = this.splashArray('margin', optionsChart);
-		this.spacing = this.splashArray('spacing', optionsChart);
-
-		var chartEvents = optionsChart.events;
-
-		//this.runChartClick = chartEvents && !!chartEvents.click;
-		this.bounds = { h: {}, v: {} }; // Pixel data bounds for touch zoom
-
-		this.callback = callback;
-		this.isResizing = 0;
-		this.options = options;
-		//chartTitleOptions = UNDEFINED;
-		//chartSubtitleOptions = UNDEFINED;
-
-		this.axes = [];
-		this.series = [];
-		this.hasCartesianSeries = optionsChart.showAxes;
-		//this.axisOffset = UNDEFINED;
-		//this.maxTicks = UNDEFINED; // handle the greatest amount of ticks on grouped axes
-		//this.inverted = UNDEFINED;
-		//this.loadingShown = UNDEFINED;
-		//this.container = UNDEFINED;
-		//this.chartWidth = UNDEFINED;
-		//this.chartHeight = UNDEFINED;
-		//this.marginRight = UNDEFINED;
-		//this.marginBottom = UNDEFINED;
-		//this.containerWidth = UNDEFINED;
-		//this.containerHeight = UNDEFINED;
-		//this.oldChartWidth = UNDEFINED;
-		//this.oldChartHeight = UNDEFINED;
-
-		//this.renderTo = UNDEFINED;
-		//this.renderToClone = UNDEFINED;
-
-		//this.spacingBox = UNDEFINED
-
-		//this.legend = UNDEFINED;
-
-		// Elements
-		//this.chartBackground = UNDEFINED;
-		//this.plotBackground = UNDEFINED;
-		//this.plotBGImage = UNDEFINED;
-		//this.plotBorder = UNDEFINED;
-		//this.loadingDiv = UNDEFINED;
-		//this.loadingSpan = UNDEFINED;
-
-		var chart = this,
-			eventType;
-
-		// Add the chart to the global lookup
-		chart.index = charts.length;
-		charts.push(chart);
-		chartCount++;
-
-		// Set up auto resize
-		if (optionsChart.reflow !== false) {
-			addEvent(chart, 'load', function () {
-				chart.initReflow();
-			});
-		}
-
-		// Chart event handlers
-		if (chartEvents) {
-			for (eventType in chartEvents) {
-				addEvent(chart, eventType, chartEvents[eventType]);
-			}
-		}
-
-		chart.xAxis = [];
-		chart.yAxis = [];
-
-		// Expose methods and variables
-		chart.animation = useCanVG ? false : pick(optionsChart.animation, true);
-		chart.pointCount = 0;
-		chart.counters = new ChartCounters();
-
-		chart.firstRender();
-	},
-
-	/**
-	 * Initialize an individual series, called internally before render time
-	 */
-	initSeries: function (options) {
-		var chart = this,
-			optionsChart = chart.options.chart,
-			type = options.type || optionsChart.type || optionsChart.defaultSeriesType,
-			series,
-			constr = seriesTypes[type];
-
-		// No such series type
-		if (!constr) {
-			error(17, true);
-		}
-
-		series = new constr();
-		series.init(this, options);
-		return series;
-	},
-
-	/**
-	 * Check whether a given point is within the plot area
-	 *
-	 * @param {Number} plotX Pixel x relative to the plot area
-	 * @param {Number} plotY Pixel y relative to the plot area
-	 * @param {Boolean} inverted Whether the chart is inverted
-	 */
-	isInsidePlot: function (plotX, plotY, inverted) {
-		var x = inverted ? plotY : plotX,
-			y = inverted ? plotX : plotY;
-			
-		return x >= 0 &&
-			x <= this.plotWidth &&
-			y >= 0 &&
-			y <= this.plotHeight;
-	},
-
-	/**
-	 * Adjust all axes tick amounts
-	 */
-	adjustTickAmounts: function () {
-		if (this.options.chart.alignTicks !== false) {
-			each(this.axes, function (axis) {
-				axis.adjustTickAmount();
-			});
-		}
-		this.maxTicks = null;
-	},
-
-	/**
-	 * Redraw legend, axes or series based on updated data
-	 *
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 */
-	redraw: function (animation) {
-		var chart = this,
-			axes = chart.axes,
-			series = chart.series,
-			pointer = chart.pointer,
-			legend = chart.legend,
-			redrawLegend = chart.isDirtyLegend,
-			hasStackedSeries,
-			hasDirtyStacks,
-			isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed?
-			seriesLength = series.length,
-			i = seriesLength,
-			serie,
-			renderer = chart.renderer,
-			isHiddenChart = renderer.isHidden(),
-			afterRedraw = [];
-			
-		setAnimation(animation, chart);
-		
-		if (isHiddenChart) {
-			chart.cloneRenderTo();
-		}
-
-		// Adjust title layout (reflow multiline text)
-		chart.layOutTitles();
-
-		// link stacked series
-		while (i--) {
-			serie = series[i];
-
-			if (serie.options.stacking) {
-				hasStackedSeries = true;
-				
-				if (serie.isDirty) {
-					hasDirtyStacks = true;
-					break;
-				}
-			}
-		}
-		if (hasDirtyStacks) { // mark others as dirty
-			i = seriesLength;
-			while (i--) {
-				serie = series[i];
-				if (serie.options.stacking) {
-					serie.isDirty = true;
-				}
-			}
-		}
-
-		// handle updated data in the series
-		each(series, function (serie) {
-			if (serie.isDirty) { // prepare the data so axis can read it
-				if (serie.options.legendType === 'point') {
-					redrawLegend = true;
-				}
-			}
-		});
-
-		// handle added or removed series
-		if (redrawLegend && legend.options.enabled) { // series or pie points are added or removed
-			// draw legend graphics
-			legend.render();
-
-			chart.isDirtyLegend = false;
-		}
-
-		// reset stacks
-		if (hasStackedSeries) {
-			chart.getStacks();
-		}
-
-
-		if (chart.hasCartesianSeries) {
-			if (!chart.isResizing) {
-
-				// reset maxTicks
-				chart.maxTicks = null;
-
-				// set axes scales
-				each(axes, function (axis) {
-					axis.setScale();
-				});
-			}
-
-			chart.adjustTickAmounts();
-			chart.getMargins();
-
-			// If one axis is dirty, all axes must be redrawn (#792, #2169)
-			each(axes, function (axis) {
-				if (axis.isDirty) {
-					isDirtyBox = true;
-				}
-			});
-
-			// redraw axes
-			each(axes, function (axis) {
-				
-				// Fire 'afterSetExtremes' only if extremes are set
-				if (axis.isDirtyExtremes) { // #821
-					axis.isDirtyExtremes = false;
-					afterRedraw.push(function () { // prevent a recursive call to chart.redraw() (#1119)
-						fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
-						delete axis.eventArgs;
-					});
-				}
-				
-				if (isDirtyBox || hasStackedSeries) {
-					axis.redraw();
-				}
-			});
-
-
-		}
-		// the plot areas size has changed
-		if (isDirtyBox) {
-			chart.drawChartBox();
-		}
-
-
-		// redraw affected series
-		each(series, function (serie) {
-			if (serie.isDirty && serie.visible &&
-					(!serie.isCartesian || serie.xAxis)) { // issue #153
-				serie.redraw();
-			}
-		});
-
-		// move tooltip or reset
-		if (pointer) {
-			pointer.reset(true);
-		}
-
-		// redraw if canvas
-		renderer.draw();
-
-		// fire the event
-		fireEvent(chart, 'redraw'); // jQuery breaks this when calling it from addEvent. Overwrites chart.redraw
-		
-		if (isHiddenChart) {
-			chart.cloneRenderTo(true);
-		}
-		
-		// Fire callbacks that are put on hold until after the redraw
-		each(afterRedraw, function (callback) {
-			callback.call();
-		});
-	},
-
-	/**
-	 * Get an axis, series or point object by id.
-	 * @param id {String} The id as given in the configuration options
-	 */
-	get: function (id) {
-		var chart = this,
-			axes = chart.axes,
-			series = chart.series;
-
-		var i,
-			j,
-			points;
-
-		// search axes
-		for (i = 0; i < axes.length; i++) {
-			if (axes[i].options.id === id) {
-				return axes[i];
-			}
-		}
-
-		// search series
-		for (i = 0; i < series.length; i++) {
-			if (series[i].options.id === id) {
-				return series[i];
-			}
-		}
-
-		// search points
-		for (i = 0; i < series.length; i++) {
-			points = series[i].points || [];
-			for (j = 0; j < points.length; j++) {
-				if (points[j].id === id) {
-					return points[j];
-				}
-			}
-		}
-		return null;
-	},
-
-	/**
-	 * Create the Axis instances based on the config options
-	 */
-	getAxes: function () {
-		var chart = this,
-			options = this.options,
-			xAxisOptions = options.xAxis = splat(options.xAxis || {}),
-			yAxisOptions = options.yAxis = splat(options.yAxis || {}),
-			optionsArray,
-			axis;
-
-		// make sure the options are arrays and add some members
-		each(xAxisOptions, function (axis, i) {
-			axis.index = i;
-			axis.isX = true;
-		});
-
-		each(yAxisOptions, function (axis, i) {
-			axis.index = i;
-		});
-
-		// concatenate all axis options into one array
-		optionsArray = xAxisOptions.concat(yAxisOptions);
-
-		each(optionsArray, function (axisOptions) {
-			axis = new Axis(chart, axisOptions);
-		});
-
-		chart.adjustTickAmounts();
-	},
-
-
-	/**
-	 * Get the currently selected points from all series
-	 */
-	getSelectedPoints: function () {
-		var points = [];
-		each(this.series, function (serie) {
-			points = points.concat(grep(serie.points || [], function (point) {
-				return point.selected;
-			}));
-		});
-		return points;
-	},
-
-	/**
-	 * Get the currently selected series
-	 */
-	getSelectedSeries: function () {
-		return grep(this.series, function (serie) {
-			return serie.selected;
-		});
-	},
-
-	/**
-	 * Generate stacks for each series and calculate stacks total values
-	 */
-	getStacks: function () {
-		var chart = this;
-
-		// reset stacks for each yAxis
-		each(chart.yAxis, function (axis) {
-			if (axis.stacks && axis.hasVisibleSeries) {
-				axis.oldStacks = axis.stacks;
-			}
-		});
-
-		each(chart.series, function (series) {
-			if (series.options.stacking && (series.visible === true || chart.options.chart.ignoreHiddenSeries === false)) {
-				series.stackKey = series.type + pick(series.options.stack, '');
-			}
-		});
-	},	
-
-	/**
-	 * Show the title and subtitle of the chart
-	 *
-	 * @param titleOptions {Object} New title options
-	 * @param subtitleOptions {Object} New subtitle options
-	 *
-	 */
-	setTitle: function (titleOptions, subtitleOptions, redraw) {
-		var chart = this,
-			options = chart.options,
-			chartTitleOptions,
-			chartSubtitleOptions;
-
-		chartTitleOptions = options.title = merge(options.title, titleOptions);
-		chartSubtitleOptions = options.subtitle = merge(options.subtitle, subtitleOptions);
-
-		// add title and subtitle
-		each([
-			['title', titleOptions, chartTitleOptions],
-			['subtitle', subtitleOptions, chartSubtitleOptions]
-		], function (arr) {
-			var name = arr[0],
-				title = chart[name],
-				titleOptions = arr[1],
-				chartTitleOptions = arr[2];
-
-			if (title && titleOptions) {
-				chart[name] = title = title.destroy(); // remove old
-			}
-			
-			if (chartTitleOptions && chartTitleOptions.text && !title) {
-				chart[name] = chart.renderer.text(
-					chartTitleOptions.text,
-					0,
-					0,
-					chartTitleOptions.useHTML
-				)
-				.attr({
-					align: chartTitleOptions.align,
-					'class': PREFIX + name,
-					zIndex: chartTitleOptions.zIndex || 4
-				})
-				.css(chartTitleOptions.style)
-				.add();
-			}	
-		});
-		chart.layOutTitles(redraw);
-	},
-
-	/**
-	 * Lay out the chart titles and cache the full offset height for use in getMargins
-	 */
-	layOutTitles: function (redraw) {
-		var titleOffset = 0,
-			title = this.title,
-			subtitle = this.subtitle,
-			options = this.options,
-			titleOptions = options.title,
-			subtitleOptions = options.subtitle,
-			requiresDirtyBox,
-			autoWidth = this.spacingBox.width - 44; // 44 makes room for default context button
-
-		if (title) {
-			title
-				.css({ width: (titleOptions.width || autoWidth) + PX })
-				.align(extend({ y: 15 }, titleOptions), false, 'spacingBox');
-			
-			if (!titleOptions.floating && !titleOptions.verticalAlign) {
-				titleOffset = title.getBBox().height;
-			}
-		}
-		if (subtitle) {
-			subtitle
-				.css({ width: (subtitleOptions.width || autoWidth) + PX })
-				.align(extend({ y: titleOffset + titleOptions.margin }, subtitleOptions), false, 'spacingBox');
-			
-			if (!subtitleOptions.floating && !subtitleOptions.verticalAlign) {
-				titleOffset = mathCeil(titleOffset + subtitle.getBBox().height);
-			}
-		}
-
-		requiresDirtyBox = this.titleOffset !== titleOffset;				
-		this.titleOffset = titleOffset; // used in getMargins
-
-		if (!this.isDirtyBox && requiresDirtyBox) {
-			this.isDirtyBox = requiresDirtyBox;
-			// Redraw if necessary (#2719, #2744)		
-			if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
-				this.redraw();
-			}
-		}
-	},
-
-	/**
-	 * Get chart width and height according to options and container size
-	 */
-	getChartSize: function () {
-		var chart = this,
-			optionsChart = chart.options.chart,
-			widthOption = optionsChart.width,
-			heightOption = optionsChart.height,
-			renderTo = chart.renderToClone || chart.renderTo;
-
-		// get inner width and height from jQuery (#824)
-		if (!defined(widthOption)) {
-			chart.containerWidth = adapterRun(renderTo, 'width');
-		}
-		if (!defined(heightOption)) {
-			chart.containerHeight = adapterRun(renderTo, 'height');
-		}
-		
-		chart.chartWidth = mathMax(0, widthOption || chart.containerWidth || 600); // #1393, 1460
-		chart.chartHeight = mathMax(0, pick(heightOption,
-			// the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7:
-			chart.containerHeight > 19 ? chart.containerHeight : 400));
-	},
-
-	/**
-	 * Create a clone of the chart's renderTo div and place it outside the viewport to allow
-	 * size computation on chart.render and chart.redraw
-	 */
-	cloneRenderTo: function (revert) {
-		var clone = this.renderToClone,
-			container = this.container;
-		
-		// Destroy the clone and bring the container back to the real renderTo div
-		if (revert) {
-			if (clone) {
-				this.renderTo.appendChild(container);
-				discardElement(clone);
-				delete this.renderToClone;
-			}
-		
-		// Set up the clone
-		} else {
-			if (container && container.parentNode === this.renderTo) {
-				this.renderTo.removeChild(container); // do not clone this
-			}
-			this.renderToClone = clone = this.renderTo.cloneNode(0);
-			css(clone, {
-				position: ABSOLUTE,
-				top: '-9999px',
-				display: 'block' // #833
-			});
-			if (clone.style.setProperty) { // #2631
-				clone.style.setProperty('display', 'block', 'important');
-			}
-			doc.body.appendChild(clone);
-			if (container) {
-				clone.appendChild(container);
-			}
-		}
-	},
-
-	/**
-	 * Get the containing element, determine the size and create the inner container
-	 * div to hold the chart
-	 */
-	getContainer: function () {
-		var chart = this,
-			container,
-			optionsChart = chart.options.chart,
-			chartWidth,
-			chartHeight,
-			renderTo,
-			indexAttrName = 'data-highcharts-chart',
-			oldChartIndex,
-			containerId;
-
-		chart.renderTo = renderTo = optionsChart.renderTo;
-		containerId = PREFIX + idCounter++;
-
-		if (isString(renderTo)) {
-			chart.renderTo = renderTo = doc.getElementById(renderTo);
-		}
-		
-		// Display an error if the renderTo is wrong
-		if (!renderTo) {
-			error(13, true);
-		}
-		
-		// If the container already holds a chart, destroy it. The check for hasRendered is there
-		// because web pages that are saved to disk from the browser, will preserve the data-highcharts-chart
-		// attribute and the SVG contents, but not an interactive chart. So in this case,
-		// charts[oldChartIndex] will point to the wrong chart if any (#2609).
-		oldChartIndex = pInt(attr(renderTo, indexAttrName));
-		if (!isNaN(oldChartIndex) && charts[oldChartIndex] && charts[oldChartIndex].hasRendered) {
-			charts[oldChartIndex].destroy();
-		}		
-		
-		// Make a reference to the chart from the div
-		attr(renderTo, indexAttrName, chart.index);
-
-		// remove previous chart
-		renderTo.innerHTML = '';
-
-		// If the container doesn't have an offsetWidth, it has or is a child of a node
-		// that has display:none. We need to temporarily move it out to a visible
-		// state to determine the size, else the legend and tooltips won't render
-		// properly. The allowClone option is used in sparklines as a micro optimization,
-		// saving about 1-2 ms each chart.
-		if (!optionsChart.skipClone && !renderTo.offsetWidth) {
-			chart.cloneRenderTo();
-		}
-
-		// get the width and height
-		chart.getChartSize();
-		chartWidth = chart.chartWidth;
-		chartHeight = chart.chartHeight;
-
-		// create the inner container
-		chart.container = container = createElement(DIV, {
-				className: PREFIX + 'container' +
-					(optionsChart.className ? ' ' + optionsChart.className : ''),
-				id: containerId
-			}, extend({
-				position: RELATIVE,
-				overflow: HIDDEN, // needed for context menu (avoid scrollbars) and
-					// content overflow in IE
-				width: chartWidth + PX,
-				height: chartHeight + PX,
-				textAlign: 'left',
-				lineHeight: 'normal', // #427
-				zIndex: 0, // #1072
-				'-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
-			}, optionsChart.style),
-			chart.renderToClone || renderTo
-		);
-
-		// cache the cursor (#1650)
-		chart._cursor = container.style.cursor;
-
-		// Initialize the renderer
-		chart.renderer =
-			optionsChart.forExport ? // force SVG, used for SVG export
-				new SVGRenderer(container, chartWidth, chartHeight, optionsChart.style, true) :
-				new Renderer(container, chartWidth, chartHeight, optionsChart.style);
-
-		if (useCanVG) {
-			// If we need canvg library, extend and configure the renderer
-			// to get the tracker for translating mouse events
-			chart.renderer.create(chart, container, chartWidth, chartHeight);
-		}
-	},
-
-	/**
-	 * Calculate margins by rendering axis labels in a preliminary position. Title,
-	 * subtitle and legend have already been rendered at this stage, but will be
-	 * moved into their final positions
-	 */
-	getMargins: function () {
-		var chart = this,
-			spacing = chart.spacing,
-			axisOffset,
-			legend = chart.legend,
-			margin = chart.margin,
-			legendOptions = chart.options.legend,
-			legendMargin = pick(legendOptions.margin, 20),
-			legendX = legendOptions.x,
-			legendY = legendOptions.y,
-			align = legendOptions.align,
-			verticalAlign = legendOptions.verticalAlign,
-			titleOffset = chart.titleOffset;
-
-		chart.resetMargins();
-		axisOffset = chart.axisOffset;
-
-		// Adjust for title and subtitle
-		if (titleOffset && !defined(margin[0])) {
-			chart.plotTop = mathMax(chart.plotTop, titleOffset + chart.options.title.margin + spacing[0]);
-		}
-		
-		// Adjust for legend
-		if (legend.display && !legendOptions.floating) {
-			if (align === 'right') { // horizontal alignment handled first
-				if (!defined(margin[1])) {
-					chart.marginRight = mathMax(
-						chart.marginRight,
-						legend.legendWidth - legendX + legendMargin + spacing[1]
-					);
-				}
-			} else if (align === 'left') {
-				if (!defined(margin[3])) {
-					chart.plotLeft = mathMax(
-						chart.plotLeft,
-						legend.legendWidth + legendX + legendMargin + spacing[3]
-					);
-				}
-
-			} else if (verticalAlign === 'top') {
-				if (!defined(margin[0])) {
-					chart.plotTop = mathMax(
-						chart.plotTop,
-						legend.legendHeight + legendY + legendMargin + spacing[0]
-					);
-				}
-
-			} else if (verticalAlign === 'bottom') {
-				if (!defined(margin[2])) {
-					chart.marginBottom = mathMax(
-						chart.marginBottom,
-						legend.legendHeight - legendY + legendMargin + spacing[2]
-					);
-				}
-			}
-		}
-
-		// adjust for scroller
-		if (chart.extraBottomMargin) {
-			chart.marginBottom += chart.extraBottomMargin;
-		}
-		if (chart.extraTopMargin) {
-			chart.plotTop += chart.extraTopMargin;
-		}
-
-		// pre-render axes to get labels offset width
-		if (chart.hasCartesianSeries) {
-			each(chart.axes, function (axis) {
-				axis.getOffset();
-			});
-		}
-		
-		if (!defined(margin[3])) {
-			chart.plotLeft += axisOffset[3];
-		}
-		if (!defined(margin[0])) {
-			chart.plotTop += axisOffset[0];
-		}
-		if (!defined(margin[2])) {
-			chart.marginBottom += axisOffset[2];
-		}
-		if (!defined(margin[1])) {
-			chart.marginRight += axisOffset[1];
-		}
-
-		chart.setChartSize();
-
-	},
-
-	/**
-	 * Resize the chart to its container if size is not explicitly set
-	 */
-	reflow: function (e) {
-		var chart = this,
-			optionsChart = chart.options.chart,
-			renderTo = chart.renderTo,
-			width = optionsChart.width || adapterRun(renderTo, 'width'),
-			height = optionsChart.height || adapterRun(renderTo, 'height'),
-			target = e ? e.target : win, // #805 - MooTools doesn't supply e
-			doReflow = function () {
-				if (chart.container) { // It may have been destroyed in the meantime (#1257)
-					chart.setSize(width, height, false);
-					chart.hasUserSize = null;
-				}
-			};
-			
-		// Width and height checks for display:none. Target is doc in IE8 and Opera,
-		// win in Firefox, Chrome and IE9.
-		if (!chart.hasUserSize && width && height && (target === win || target === doc)) {
-			if (width !== chart.containerWidth || height !== chart.containerHeight) {
-				clearTimeout(chart.reflowTimeout);
-				if (e) { // Called from window.resize
-					chart.reflowTimeout = setTimeout(doReflow, 100);
-				} else { // Called directly (#2224)
-					doReflow();
-				}
-			}
-			chart.containerWidth = width;
-			chart.containerHeight = height;
-		}
-	},
-
-	/**
-	 * Add the event handlers necessary for auto resizing
-	 */
-	initReflow: function () {
-		var chart = this,
-			reflow = function (e) {
-				chart.reflow(e);
-			};
-			
-		
-		addEvent(win, 'resize', reflow);
-		addEvent(chart, 'destroy', function () {
-			removeEvent(win, 'resize', reflow);
-		});
-	},
-
-	/**
-	 * Resize the chart to a given width and height
-	 * @param {Number} width
-	 * @param {Number} height
-	 * @param {Object|Boolean} animation
-	 */
-	setSize: function (width, height, animation) {
-		var chart = this,
-			chartWidth,
-			chartHeight,
-			fireEndResize;
-
-		// Handle the isResizing counter
-		chart.isResizing += 1;
-		fireEndResize = function () {
-			if (chart) {
-				fireEvent(chart, 'endResize', null, function () {
-					chart.isResizing -= 1;
-				});
-			}
-		};
-
-		// set the animation for the current process
-		setAnimation(animation, chart);
-
-		chart.oldChartHeight = chart.chartHeight;
-		chart.oldChartWidth = chart.chartWidth;
-		if (defined(width)) {
-			chart.chartWidth = chartWidth = mathMax(0, mathRound(width));
-			chart.hasUserSize = !!chartWidth;
-		}
-		if (defined(height)) {
-			chart.chartHeight = chartHeight = mathMax(0, mathRound(height));
-		}
-
-		// Resize the container with the global animation applied if enabled (#2503)
-		(globalAnimation ? animate : css)(chart.container, {
-			width: chartWidth + PX,
-			height: chartHeight + PX
-		}, globalAnimation);
-
-		chart.setChartSize(true);
-		chart.renderer.setSize(chartWidth, chartHeight, animation);
-
-		// handle axes
-		chart.maxTicks = null;
-		each(chart.axes, function (axis) {
-			axis.isDirty = true;
-			axis.setScale();
-		});
-
-		// make sure non-cartesian series are also handled
-		each(chart.series, function (serie) {
-			serie.isDirty = true;
-		});
-
-		chart.isDirtyLegend = true; // force legend redraw
-		chart.isDirtyBox = true; // force redraw of plot and chart border
-
-		chart.layOutTitles(); // #2857
-		chart.getMargins();
-
-		chart.redraw(animation);
-
-
-		chart.oldChartHeight = null;
-		fireEvent(chart, 'resize');
-
-		// fire endResize and set isResizing back
-		// If animation is disabled, fire without delay
-		if (globalAnimation === false) {
-			fireEndResize();
-		} else { // else set a timeout with the animation duration
-			setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500);
-		}
-	},
-
-	/**
-	 * Set the public chart properties. This is done before and after the pre-render
-	 * to determine margin sizes
-	 */
-	setChartSize: function (skipAxes) {
-		var chart = this,
-			inverted = chart.inverted,
-			renderer = chart.renderer,
-			chartWidth = chart.chartWidth,
-			chartHeight = chart.chartHeight,
-			optionsChart = chart.options.chart,
-			spacing = chart.spacing,
-			clipOffset = chart.clipOffset,
-			clipX,
-			clipY,
-			plotLeft,
-			plotTop,
-			plotWidth,
-			plotHeight,
-			plotBorderWidth;
-
-		chart.plotLeft = plotLeft = mathRound(chart.plotLeft);
-		chart.plotTop = plotTop = mathRound(chart.plotTop);
-		chart.plotWidth = plotWidth = mathMax(0, mathRound(chartWidth - plotLeft - chart.marginRight));
-		chart.plotHeight = plotHeight = mathMax(0, mathRound(chartHeight - plotTop - chart.marginBottom));
-
-		chart.plotSizeX = inverted ? plotHeight : plotWidth;
-		chart.plotSizeY = inverted ? plotWidth : plotHeight;
-		
-		chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
-
-		// Set boxes used for alignment
-		chart.spacingBox = renderer.spacingBox = {
-			x: spacing[3],
-			y: spacing[0],
-			width: chartWidth - spacing[3] - spacing[1],
-			height: chartHeight - spacing[0] - spacing[2]
-		};
-		chart.plotBox = renderer.plotBox = {
-			x: plotLeft,
-			y: plotTop,
-			width: plotWidth,
-			height: plotHeight
-		};
-
-		plotBorderWidth = 2 * mathFloor(chart.plotBorderWidth / 2);
-		clipX = mathCeil(mathMax(plotBorderWidth, clipOffset[3]) / 2);
-		clipY = mathCeil(mathMax(plotBorderWidth, clipOffset[0]) / 2);
-		chart.clipBox = {
-			x: clipX, 
-			y: clipY, 
-			width: mathFloor(chart.plotSizeX - mathMax(plotBorderWidth, clipOffset[1]) / 2 - clipX), 
-			height: mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY)
-		};
-
-		if (!skipAxes) {
-			each(chart.axes, function (axis) {
-				axis.setAxisSize();
-				axis.setAxisTranslation();
-			});
-		}
-	},
-
-	/**
-	 * Initial margins before auto size margins are applied
-	 */
-	resetMargins: function () {
-		var chart = this,
-			spacing = chart.spacing,
-			margin = chart.margin;
-
-		chart.plotTop = pick(margin[0], spacing[0]);
-		chart.marginRight = pick(margin[1], spacing[1]);
-		chart.marginBottom = pick(margin[2], spacing[2]);
-		chart.plotLeft = pick(margin[3], spacing[3]);
-		chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
-		chart.clipOffset = [0, 0, 0, 0];
-	},
-
-	/**
-	 * Draw the borders and backgrounds for chart and plot area
-	 */
-	drawChartBox: function () {
-		var chart = this,
-			optionsChart = chart.options.chart,
-			renderer = chart.renderer,
-			chartWidth = chart.chartWidth,
-			chartHeight = chart.chartHeight,
-			chartBackground = chart.chartBackground,
-			plotBackground = chart.plotBackground,
-			plotBorder = chart.plotBorder,
-			plotBGImage = chart.plotBGImage,
-			chartBorderWidth = optionsChart.borderWidth || 0,
-			chartBackgroundColor = optionsChart.backgroundColor,
-			plotBackgroundColor = optionsChart.plotBackgroundColor,
-			plotBackgroundImage = optionsChart.plotBackgroundImage,
-			plotBorderWidth = optionsChart.plotBorderWidth || 0,
-			mgn,
-			bgAttr,
-			plotLeft = chart.plotLeft,
-			plotTop = chart.plotTop,
-			plotWidth = chart.plotWidth,
-			plotHeight = chart.plotHeight,
-			plotBox = chart.plotBox,
-			clipRect = chart.clipRect,
-			clipBox = chart.clipBox;
-
-		// Chart area
-		mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
-
-		if (chartBorderWidth || chartBackgroundColor) {
-			if (!chartBackground) {
-				
-				bgAttr = {
-					fill: chartBackgroundColor || NONE
-				};
-				if (chartBorderWidth) { // #980
-					bgAttr.stroke = optionsChart.borderColor;
-					bgAttr['stroke-width'] = chartBorderWidth;
-				}
-				chart.chartBackground = renderer.rect(mgn / 2, mgn / 2, chartWidth - mgn, chartHeight - mgn,
-						optionsChart.borderRadius, chartBorderWidth)
-					.attr(bgAttr)
-					.addClass(PREFIX + 'background')
-					.add()
-					.shadow(optionsChart.shadow);
-
-			} else { // resize
-				chartBackground.animate(
-					chartBackground.crisp({ width: chartWidth - mgn, height: chartHeight - mgn })
-				);
-			}
-		}
-
-
-		// Plot background
-		if (plotBackgroundColor) {
-			if (!plotBackground) {
-				chart.plotBackground = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0)
-					.attr({
-						fill: plotBackgroundColor
-					})
-					.add()
-					.shadow(optionsChart.plotShadow);
-			} else {
-				plotBackground.animate(plotBox);
-			}
-		}
-		if (plotBackgroundImage) {
-			if (!plotBGImage) {
-				chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight)
-					.add();
-			} else {
-				plotBGImage.animate(plotBox);
-			}
-		}
-		
-		// Plot clip
-		if (!clipRect) {
-			chart.clipRect = renderer.clipRect(clipBox);
-		} else {
-			clipRect.animate({
-				width: clipBox.width,
-				height: clipBox.height
-			});
-		}
-
-		// Plot area border
-		if (plotBorderWidth) {
-			if (!plotBorder) {
-				chart.plotBorder = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0, -plotBorderWidth)
-					.attr({
-						stroke: optionsChart.plotBorderColor,
-						'stroke-width': plotBorderWidth,
-						fill: NONE,
-						zIndex: 1
-					})
-					.add();
-			} else {
-				plotBorder.animate(
-					plotBorder.crisp({ x: plotLeft, y: plotTop, width: plotWidth, height: plotHeight })
-				);
-			}
-		}
-
-		// reset
-		chart.isDirtyBox = false;
-	},
-
-	/**
-	 * Detect whether a certain chart property is needed based on inspecting its options
-	 * and series. This mainly applies to the chart.invert property, and in extensions to 
-	 * the chart.angular and chart.polar properties.
-	 */
-	propFromSeries: function () {
-		var chart = this,
-			optionsChart = chart.options.chart,
-			klass,
-			seriesOptions = chart.options.series,
-			i,
-			value;
-			
-			
-		each(['inverted', 'angular', 'polar'], function (key) {
-			
-			// The default series type's class
-			klass = seriesTypes[optionsChart.type || optionsChart.defaultSeriesType];
-			
-			// Get the value from available chart-wide properties
-			value = (
-				chart[key] || // 1. it is set before
-				optionsChart[key] || // 2. it is set in the options
-				(klass && klass.prototype[key]) // 3. it's default series class requires it
-			);
-	
-			// 4. Check if any the chart's series require it
-			i = seriesOptions && seriesOptions.length;
-			while (!value && i--) {
-				klass = seriesTypes[seriesOptions[i].type];
-				if (klass && klass.prototype[key]) {
-					value = true;
-				}
-			}
-	
-			// Set the chart property
-			chart[key] = value;	
-		});
-		
-	},
-
-	/**
-	 * Link two or more series together. This is done initially from Chart.render,
-	 * and after Chart.addSeries and Series.remove.
-	 */
-	linkSeries: function () {
-		var chart = this,
-			chartSeries = chart.series;
-
-		// Reset links
-		each(chartSeries, function (series) {
-			series.linkedSeries.length = 0;
-		});
-
-		// Apply new links
-		each(chartSeries, function (series) {
-			var linkedTo = series.options.linkedTo;
-			if (isString(linkedTo)) {
-				if (linkedTo === ':previous') {
-					linkedTo = chart.series[series.index - 1];
-				} else {
-					linkedTo = chart.get(linkedTo);
-				}
-				if (linkedTo) {
-					linkedTo.linkedSeries.push(series);
-					series.linkedParent = linkedTo;
-				}
-			}
-		});
-	},
-
-	/**
-	 * Render series for the chart
-	 */
-	renderSeries: function () {
-		each(this.series, function (serie) {
-			serie.translate();
-			if (serie.setTooltipPoints) {
-				serie.setTooltipPoints();
-			}
-			serie.render();
-		});
-	},
-
-	/**
-	 * Render all graphics for the chart
-	 */
-	render: function () {
-		var chart = this,
-			axes = chart.axes,
-			renderer = chart.renderer,
-			options = chart.options;
-
-		var labels = options.labels,
-			credits = options.credits,
-			creditsHref;
-
-		// Title
-		chart.setTitle();
-
-
-		// Legend
-		chart.legend = new Legend(chart, options.legend);
-
-		chart.getStacks(); // render stacks
-
-		// Get margins by pre-rendering axes
-		// set axes scales
-		each(axes, function (axis) {
-			axis.setScale();
-		});
-
-		chart.getMargins();
-
-		chart.maxTicks = null; // reset for second pass
-		each(axes, function (axis) {
-			axis.setTickPositions(true); // update to reflect the new margins
-			axis.setMaxTicks();
-		});
-		chart.adjustTickAmounts();
-		chart.getMargins(); // second pass to check for new labels
-
-
-		// Draw the borders and backgrounds
-		chart.drawChartBox();		
-
-
-		// Axes
-		if (chart.hasCartesianSeries) {
-			each(axes, function (axis) {
-				axis.render();
-			});
-		}
-
-		// The series
-		if (!chart.seriesGroup) {
-			chart.seriesGroup = renderer.g('series-group')
-				.attr({ zIndex: 3 })
-				.add();
-		}
-		chart.renderSeries();
-
-		// Labels
-		if (labels.items) {
-			each(labels.items, function (label) {
-				var style = extend(labels.style, label.style),
-					x = pInt(style.left) + chart.plotLeft,
-					y = pInt(style.top) + chart.plotTop + 12;
-
-				// delete to prevent rewriting in IE
-				delete style.left;
-				delete style.top;
-
-				renderer.text(
-					label.html,
-					x,
-					y
-				)
-				.attr({ zIndex: 2 })
-				.css(style)
-				.add();
-
-			});
-		}
-
-		// Credits
-		if (credits.enabled && !chart.credits) {
-			creditsHref = credits.href;
-			chart.credits = renderer.text(
-				credits.text,
-				0,
-				0
-			)
-			.on('click', function () {
-				if (creditsHref) {
-					location.href = creditsHref;
-				}
-			})
-			.attr({
-				align: credits.position.align,
-				zIndex: 8
-			})
-			.css(credits.style)
-			.add()
-			.align(credits.position);
-		}
-
-		// Set flag
-		chart.hasRendered = true;
-
-	},
-
-	/**
-	 * Clean up memory usage
-	 */
-	destroy: function () {
-		var chart = this,
-			axes = chart.axes,
-			series = chart.series,
-			container = chart.container,
-			i,
-			parentNode = container && container.parentNode;
-			
-		// fire the chart.destoy event
-		fireEvent(chart, 'destroy');
-		
-		// Delete the chart from charts lookup array
-		charts[chart.index] = UNDEFINED;
-		chartCount--;
-		chart.renderTo.removeAttribute('data-highcharts-chart');
-
-		// remove events
-		removeEvent(chart);
-
-		// ==== Destroy collections:
-		// Destroy axes
-		i = axes.length;
-		while (i--) {
-			axes[i] = axes[i].destroy();
-		}
-
-		// Destroy each series
-		i = series.length;
-		while (i--) {
-			series[i] = series[i].destroy();
-		}
-
-		// ==== Destroy chart properties:
-		each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage', 
-				'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'pointer', 'scroller', 
-				'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) {
-			var prop = chart[name];
-
-			if (prop && prop.destroy) {
-				chart[name] = prop.destroy();
-			}
-		});
-
-		// remove container and all SVG
-		if (container) { // can break in IE when destroyed before finished loading
-			container.innerHTML = '';
-			removeEvent(container);
-			if (parentNode) {
-				discardElement(container);
-			}
-
-		}
-
-		// clean it all up
-		for (i in chart) {
-			delete chart[i];
-		}
-
-	},
-
-
-	/**
-	 * VML namespaces can't be added until after complete. Listening
-	 * for Perini's doScroll hack is not enough.
-	 */
-	isReadyToRender: function () {
-		var chart = this;
-
-		// Note: in spite of JSLint's complaints, win == win.top is required
-		/*jslint eqeq: true*/
-		if ((!hasSVG && (win == win.top && doc.readyState !== 'complete')) || (useCanVG && !win.canvg)) {
-		/*jslint eqeq: false*/
-			if (useCanVG) {
-				// Delay rendering until canvg library is downloaded and ready
-				CanVGController.push(function () { chart.firstRender(); }, chart.options.global.canvasToolsURL);
-			} else {
-				doc.attachEvent('onreadystatechange', function () {
-					doc.detachEvent('onreadystatechange', chart.firstRender);
-					if (doc.readyState === 'complete') {
-						chart.firstRender();
-					}
-				});
-			}
-			return false;
-		}
-		return true;
-	},
-
-	/**
-	 * Prepare for first rendering after all data are loaded
-	 */
-	firstRender: function () {
-		var chart = this,
-			options = chart.options,
-			callback = chart.callback;
-
-		// Check whether the chart is ready to render
-		if (!chart.isReadyToRender()) {
-			return;
-		}
-
-		// Create the container
-		chart.getContainer();
-
-		// Run an early event after the container and renderer are established
-		fireEvent(chart, 'init');
-
-		
-		chart.resetMargins();
-		chart.setChartSize();
-
-		// Set the common chart properties (mainly invert) from the given series
-		chart.propFromSeries();
-
-		// get axes
-		chart.getAxes();
-
-		// Initialize the series
-		each(options.series || [], function (serieOptions) {
-			chart.initSeries(serieOptions);
-		});
-
-		chart.linkSeries();
-
-		// Run an event after axes and series are initialized, but before render. At this stage,
-		// the series data is indexed and cached in the xData and yData arrays, so we can access
-		// those before rendering. Used in Highstock. 
-		fireEvent(chart, 'beforeRender'); 
-
-		// depends on inverted and on margins being set
-		if (Highcharts.Pointer) {
-			chart.pointer = new Pointer(chart, options);
-		}
-
-		chart.render();
-
-		// add canvas
-		chart.renderer.draw();
-		// run callbacks
-		if (callback) {
-			callback.apply(chart, [chart]);
-		}
-		each(chart.callbacks, function (fn) {
-			fn.apply(chart, [chart]);
-		});
-		
-		
-		// If the chart was rendered outside the top container, put it back in
-		chart.cloneRenderTo(true);
-		
-		fireEvent(chart, 'load');
-
-	},
-
-	/**
-	* Creates arrays for spacing and margin from given options.
-	*/
-	splashArray: function (target, options) {
-		var oVar = options[target],
-			tArray = isObject(oVar) ? oVar : [oVar, oVar, oVar, oVar];
-
-		return [pick(options[target + 'Top'], tArray[0]),
-				pick(options[target + 'Right'], tArray[1]),
-				pick(options[target + 'Bottom'], tArray[2]),
-				pick(options[target + 'Left'], tArray[3])];
-	}
-}; // end Chart
-
-// Hook for exporting module
-Chart.prototype.callbacks = [];
-
-var CenteredSeriesMixin = Highcharts.CenteredSeriesMixin = {
-	/**
-	 * Get the center of the pie based on the size and center options relative to the  
-	 * plot area. Borrowed by the polar and gauge series types.
-	 */
-	getCenter: function () {
-		
-		var options = this.options,
-			chart = this.chart,
-			slicingRoom = 2 * (options.slicedOffset || 0),
-			handleSlicingRoom,
-			plotWidth = chart.plotWidth - 2 * slicingRoom,
-			plotHeight = chart.plotHeight - 2 * slicingRoom,
-			centerOption = options.center,
-			positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0],
-			smallestSize = mathMin(plotWidth, plotHeight),
-			isPercent;
-		
-		return map(positions, function (length, i) {
-			isPercent = /%$/.test(length);
-			handleSlicingRoom = i < 2 || (i === 2 && isPercent);
-			return (isPercent ?
-				// i == 0: centerX, relative to width
-				// i == 1: centerY, relative to height
-				// i == 2: size, relative to smallestSize
-				// i == 4: innerSize, relative to smallestSize
-				[plotWidth, plotHeight, smallestSize, smallestSize][i] *
-					pInt(length) / 100 :
-				length) + (handleSlicingRoom ? slicingRoom : 0);
-		});
-	}
-};
-
-/**
- * The Point object and prototype. Inheritable and used as base for PiePoint
- */
-var Point = function () {};
-Point.prototype = {
-
-	/**
-	 * Initialize the point
-	 * @param {Object} series The series object containing this point
-	 * @param {Object} options The data in either number, array or object format
-	 */
-	init: function (series, options, x) {
-
-		var point = this,
-			colors;
-		point.series = series;
-		point.applyOptions(options, x);
-		point.pointAttr = {};
-
-		if (series.options.colorByPoint) {
-			colors = series.options.colors || series.chart.options.colors;
-			point.color = point.color || colors[series.colorCounter++];
-			// loop back to zero
-			if (series.colorCounter === colors.length) {
-				series.colorCounter = 0;
-			}
-		}
-
-		series.chart.pointCount++;
-		return point;
-	},
-	/**
-	 * Apply the options containing the x and y data and possible some extra properties.
-	 * This is called on point init or from point.update.
-	 *
-	 * @param {Object} options
-	 */
-	applyOptions: function (options, x) {
-		var point = this,
-			series = point.series,
-			pointValKey = series.pointValKey;
-
-		options = Point.prototype.optionsToObject.call(this, options);
-
-		// copy options directly to point
-		extend(point, options);
-		point.options = point.options ? extend(point.options, options) : options;
-
-		// For higher dimension series types. For instance, for ranges, point.y is mapped to point.low.
-		if (pointValKey) {
-			point.y = point[pointValKey];
-		}
-
-		// If no x is set by now, get auto incremented value. All points must have an
-		// x value, however the y value can be null to create a gap in the series
-		if (point.x === UNDEFINED && series) {
-			point.x = x === UNDEFINED ? series.autoIncrement() : x;
-		}
-
-		return point;
-	},
-
-	/**
-	 * Transform number or array configs into objects
-	 */
-	optionsToObject: function (options) {
-		var ret = {},
-			series = this.series,
-			pointArrayMap = series.pointArrayMap || ['y'],
-			valueCount = pointArrayMap.length,
-			firstItemType,
-			i = 0,
-			j = 0;
-
-		if (typeof options === 'number' || options === null) {
-			ret[pointArrayMap[0]] = options;
-
-		} else if (isArray(options)) {
-			// with leading x value
-			if (options.length > valueCount) {
-				firstItemType = typeof options[0];
-				if (firstItemType === 'string') {
-					ret.name = options[0];
-				} else if (firstItemType === 'number') {
-					ret.x = options[0];
-				}
-				i++;
-			}
-			while (j < valueCount) {
-				ret[pointArrayMap[j++]] = options[i++];
-			}
-		} else if (typeof options === 'object') {
-			ret = options;
-
-			// This is the fastest way to detect if there are individual point dataLabels that need
-			// to be considered in drawDataLabels. These can only occur in object configs.
-			if (options.dataLabels) {
-				series._hasPointLabels = true;
-			}
-
-			// Same approach as above for markers
-			if (options.marker) {
-				series._hasPointMarkers = true;
-			}
-		}
-		return ret;
-	},
-
-	/**
-	 * Destroy a point to clear memory. Its reference still stays in series.data.
-	 */
-	destroy: function () {
-		var point = this,
-			series = point.series,
-			chart = series.chart,
-			hoverPoints = chart.hoverPoints,
-			prop;
-
-		chart.pointCount--;
-
-		if (hoverPoints) {
-			point.setState();
-			erase(hoverPoints, point);
-			if (!hoverPoints.length) {
-				chart.hoverPoints = null;
-			}
-
-		}
-		if (point === chart.hoverPoint) {
-			point.onMouseOut();
-		}
-
-		// remove all events
-		if (point.graphic || point.dataLabel) { // removeEvent and destroyElements are performance expensive
-			removeEvent(point);
-			point.destroyElements();
-		}
-
-		if (point.legendItem) { // pies have legend items
-			chart.legend.destroyItem(point);
-		}
-
-		for (prop in point) {
-			point[prop] = null;
-		}
-
-
-	},
-
-	/**
-	 * Destroy SVG elements associated with the point
-	 */
-	destroyElements: function () {
-		var point = this,
-			props = ['graphic', 'dataLabel', 'dataLabelUpper', 'group', 'connector', 'shadowGroup'],
-			prop,
-			i = 6;
-		while (i--) {
-			prop = props[i];
-			if (point[prop]) {
-				point[prop] = point[prop].destroy();
-			}
-		}
-	},
-
-	/**
-	 * Return the configuration hash needed for the data label and tooltip formatters
-	 */
-	getLabelConfig: function () {
-		var point = this;
-		return {
-			x: point.category,
-			y: point.y,
-			key: point.name || point.category,
-			series: point.series,
-			point: point,
-			percentage: point.percentage,
-			total: point.total || point.stackTotal
-		};
-	},	
-
-	/**
-	 * Extendable method for formatting each point's tooltip line
-	 *
-	 * @return {String} A string to be concatenated in to the common tooltip text
-	 */
-	tooltipFormatter: function (pointFormat) {
-
-		// Insert options for valueDecimals, valuePrefix, and valueSuffix
-		var series = this.series,
-			seriesTooltipOptions = series.tooltipOptions,
-			valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''),
-			valuePrefix = seriesTooltipOptions.valuePrefix || '',
-			valueSuffix = seriesTooltipOptions.valueSuffix || '';
-
-		// Loop over the point array map and replace unformatted values with sprintf formatting markup
-		each(series.pointArrayMap || ['y'], function (key) {
-			key = '{point.' + key; // without the closing bracket
-			if (valuePrefix || valueSuffix) {
-				pointFormat = pointFormat.replace(key + '}', valuePrefix + key + '}' + valueSuffix);
-			}
-			pointFormat = pointFormat.replace(key + '}', key + ':,.' + valueDecimals + 'f}');
-		});
-
-		return format(pointFormat, {
-			point: this,
-			series: this.series
-		});
-	},
-
-	/**
-	 * Fire an event on the Point object. Must not be renamed to fireEvent, as this
-	 * causes a name clash in MooTools
-	 * @param {String} eventType
-	 * @param {Object} eventArgs Additional event arguments
-	 * @param {Function} defaultFunction Default event handler
-	 */
-	firePointEvent: function (eventType, eventArgs, defaultFunction) {
-		var point = this,
-			series = this.series,
-			seriesOptions = series.options;
-
-		// load event handlers on demand to save time on mouseover/out
-		if (seriesOptions.point.events[eventType] || (point.options && point.options.events && point.options.events[eventType])) {
-			this.importEvents();
-		}
-
-		// add default handler if in selection mode
-		if (eventType === 'click' && seriesOptions.allowPointSelect) {
-			defaultFunction = function (event) {
-				// Control key is for Windows, meta (= Cmd key) for Mac, Shift for Opera
-				point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
-			};
-		}
-
-		fireEvent(this, eventType, eventArgs, defaultFunction);
-	}
-};/**
- * @classDescription The base function which all other series types inherit from. The data in the series is stored
- * in various arrays.
- *
- * - First, series.options.data contains all the original config options for
- * each point whether added by options or methods like series.addPoint.
- * - Next, series.data contains those values converted to points, but in case the series data length
- * exceeds the cropThreshold, or if the data is grouped, series.data doesn't contain all the points. It
- * only contains the points that have been created on demand.
- * - Then there's series.points that contains all currently visible point objects. In case of cropping,
- * the cropped-away points are not part of this array. The series.points array starts at series.cropStart
- * compared to series.data and series.options.data. If however the series data is grouped, these can't
- * be correlated one to one.
- * - series.xData and series.processedXData contain clean x values, equivalent to series.data and series.points.
- * - series.yData and series.processedYData contain clean x values, equivalent to series.data and series.points.
- *
- * @param {Object} chart
- * @param {Object} options
- */
-var Series = function () {};
-
-Series.prototype = {
-
-	isCartesian: true,
-	type: 'line',
-	pointClass: Point,
-	sorted: true, // requires the data to be sorted
-	requireSorting: true,
-	pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
-		stroke: 'lineColor',
-		'stroke-width': 'lineWidth',
-		fill: 'fillColor',
-		r: 'radius'
-	},
-	axisTypes: ['xAxis', 'yAxis'],
-	colorCounter: 0,
-	parallelArrays: ['x', 'y'], // each point's x and y values are stored in this.xData and this.yData
-	init: function (chart, options) {
-		var series = this,
-			eventType,
-			events,
-			chartSeries = chart.series,
-			sortByIndex = function (a, b) {
-				return pick(a.options.index, a._i) - pick(b.options.index, b._i);
-			};
-
-		series.chart = chart;
-		series.options = options = series.setOptions(options); // merge with plotOptions
-		series.linkedSeries = [];
-
-		// bind the axes
-		series.bindAxes();
-
-		// set some variables
-		extend(series, {
-			name: options.name,
-			state: NORMAL_STATE,
-			pointAttr: {},
-			visible: options.visible !== false, // true by default
-			selected: options.selected === true // false by default
-		});
-
-		// special
-		if (useCanVG) {
-			options.animation = false;
-		}
-
-		// register event listeners
-		events = options.events;
-		for (eventType in events) {
-			addEvent(series, eventType, events[eventType]);
-		}
-		if (
-			(events && events.click) ||
-			(options.point && options.point.events && options.point.events.click) ||
-			options.allowPointSelect
-		) {
-			chart.runTrackerClick = true;
-		}
-
-		series.getColor();
-		series.getSymbol();
-
-		// Set the data
-		each(series.parallelArrays, function (key) {
-			series[key + 'Data'] = [];
-		});
-		series.setData(options.data, false);
-
-		// Mark cartesian
-		if (series.isCartesian) {
-			chart.hasCartesianSeries = true;
-		}
-
-		// Register it in the chart
-		chartSeries.push(series);
-		series._i = chartSeries.length - 1;
-
-		// Sort series according to index option (#248, #1123, #2456)
-		stableSort(chartSeries, sortByIndex);
-		if (this.yAxis) {
-			stableSort(this.yAxis.series, sortByIndex);
-		}
-
-		each(chartSeries, function (series, i) {
-			series.index = i;
-			series.name = series.name || 'Series ' + (i + 1);
-		});
-
-	},
-
-	/**
-	 * Set the xAxis and yAxis properties of cartesian series, and register the series
-	 * in the axis.series array
-	 */
-	bindAxes: function () {
-		var series = this,
-			seriesOptions = series.options,
-			chart = series.chart,
-			axisOptions;
-
-		each(series.axisTypes || [], function (AXIS) { // repeat for xAxis and yAxis
-
-			each(chart[AXIS], function (axis) { // loop through the chart's axis objects
-				axisOptions = axis.options;
-
-				// apply if the series xAxis or yAxis option mathches the number of the
-				// axis, or if undefined, use the first axis
-				if ((seriesOptions[AXIS] === axisOptions.index) ||
-						(seriesOptions[AXIS] !== UNDEFINED && seriesOptions[AXIS] === axisOptions.id) ||
-						(seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) {
-
-					// register this series in the axis.series lookup
-					axis.series.push(series);
-
-					// set this series.xAxis or series.yAxis reference
-					series[AXIS] = axis;
-
-					// mark dirty for redraw
-					axis.isDirty = true;
-				}
-			});
-
-			// The series needs an X and an Y axis
-			if (!series[AXIS] && series.optionalAxis !== AXIS) {
-				error(18, true);
-			}
-
-		});
-	},
-
-	/**
-	 * For simple series types like line and column, the data values are held in arrays like
-	 * xData and yData for quick lookup to find extremes and more. For multidimensional series
-	 * like bubble and map, this can be extended with arrays like zData and valueData by
-	 * adding to the series.parallelArrays array.
-	 */
-	updateParallelArrays: function (point, i) {
-		var series = point.series,
-			args = arguments,
-			fn = typeof i === 'number' ?
-				 // Insert the value in the given position
-				function (key) {
-					var val = key === 'y' && series.toYData ? series.toYData(point) : point[key];
-					series[key + 'Data'][i] = val;
-				} :
-				// Apply the method specified in i with the following arguments as arguments
-				function (key) {
-					Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
-				};
-
-		each(series.parallelArrays, fn);
-	},
-
-	/**
-	 * Return an auto incremented x value based on the pointStart and pointInterval options.
-	 * This is only used if an x value is not given for the point that calls autoIncrement.
-	 */
-	autoIncrement: function () {
-		var series = this,
-			options = series.options,
-			xIncrement = series.xIncrement;
-
-		xIncrement = pick(xIncrement, options.pointStart, 0);
-
-		series.pointInterval = pick(series.pointInterval, options.pointInterval, 1);
-
-		series.xIncrement = xIncrement + series.pointInterval;
-		return xIncrement;
-	},
-
-	/**
-	 * Divide the series data into segments divided by null values.
-	 */
-	getSegments: function () {
-		var series = this,
-			lastNull = -1,
-			segments = [],
-			i,
-			points = series.points,
-			pointsLength = points.length;
-
-		if (pointsLength) { // no action required for []
-
-			// if connect nulls, just remove null points
-			if (series.options.connectNulls) {
-				i = pointsLength;
-				while (i--) {
-					if (points[i].y === null) {
-						points.splice(i, 1);
-					}
-				}
-				if (points.length) {
-					segments = [points];
-				}
-
-			// else, split on null points
-			} else {
-				each(points, function (point, i) {
-					if (point.y === null) {
-						if (i > lastNull + 1) {
-							segments.push(points.slice(lastNull + 1, i));
-						}
-						lastNull = i;
-					} else if (i === pointsLength - 1) { // last value
-						segments.push(points.slice(lastNull + 1, i + 1));
-					}
-				});
-			}
-		}
-
-		// register it
-		series.segments = segments;
-	},
-
-	/**
-	 * Set the series options by merging from the options tree
-	 * @param {Object} itemOptions
-	 */
-	setOptions: function (itemOptions) {
-		var chart = this.chart,
-			chartOptions = chart.options,
-			plotOptions = chartOptions.plotOptions,
-			userOptions = chart.userOptions || {},
-			userPlotOptions = userOptions.plotOptions || {},
-			typeOptions = plotOptions[this.type],
-			options;
-
-		this.userOptions = itemOptions;
-
-		options = merge(
-			typeOptions,
-			plotOptions.series,
-			itemOptions
-		);
-
-		// The tooltip options are merged between global and series specific options
-		this.tooltipOptions = merge(
-			defaultOptions.tooltip,
-			defaultOptions.plotOptions[this.type].tooltip,
-			userOptions.tooltip,
-			userPlotOptions.series && userPlotOptions.series.tooltip,
-			userPlotOptions[this.type] && userPlotOptions[this.type].tooltip,
-			itemOptions.tooltip
-		);
-
-		// Delete marker object if not allowed (#1125)
-		if (typeOptions.marker === null) {
-			delete options.marker;
-		}
-
-		return options;
-
-	},
-	/**
-	 * Get the series' color
-	 */
-	getColor: function () {
-		var options = this.options,
-			userOptions = this.userOptions,
-			defaultColors = this.chart.options.colors,
-			counters = this.chart.counters,
-			color,
-			colorIndex;
-
-		color = options.color || defaultPlotOptions[this.type].color;
-
-		if (!color && !options.colorByPoint) {
-			if (defined(userOptions._colorIndex)) { // after Series.update()
-				colorIndex = userOptions._colorIndex;
-			} else {
-				userOptions._colorIndex = counters.color;
-				colorIndex = counters.color++;
-			}
-			color = defaultColors[colorIndex];
-		}
-
-		this.color = color;
-		counters.wrapColor(defaultColors.length);
-	},
-	/**
-	 * Get the series' symbol
-	 */
-	getSymbol: function () {
-		var series = this,
-			userOptions = series.userOptions,
-			seriesMarkerOption = series.options.marker,
-			chart = series.chart,
-			defaultSymbols = chart.options.symbols,
-			counters = chart.counters,
-			symbolIndex;
-
-		series.symbol = seriesMarkerOption.symbol;
-		if (!series.symbol) {
-			if (defined(userOptions._symbolIndex)) { // after Series.update()
-				symbolIndex = userOptions._symbolIndex;
-			} else {
-				userOptions._symbolIndex = counters.symbol;
-				symbolIndex = counters.symbol++;
-			}
-			series.symbol = defaultSymbols[symbolIndex];
-		}
-
-		// don't substract radius in image symbols (#604)
-		if (/^url/.test(series.symbol)) {
-			seriesMarkerOption.radius = 0;
-		}
-		counters.wrapSymbol(defaultSymbols.length);
-	},
-
-	drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
-
-	/**
-	 * Replace the series data with a new set of data
-	 * @param {Object} data
-	 * @param {Object} redraw
-	 */
-	setData: function (data, redraw, animation, updatePoints) {
-		var series = this,
-			oldData = series.points,
-			oldDataLength = (oldData && oldData.length) || 0,
-			dataLength,
-			options = series.options,
-			chart = series.chart,
-			firstPoint = null,
-			xAxis = series.xAxis,
-			hasCategories = xAxis && !!xAxis.categories,
-			tooltipPoints = series.tooltipPoints,
-			i,
-			turboThreshold = options.turboThreshold,
-			pt,
-			xData = this.xData,
-			yData = this.yData,
-			pointArrayMap = series.pointArrayMap,
-			valueCount = pointArrayMap && pointArrayMap.length;
-
-		data = data || [];
-		dataLength = data.length;
-		redraw = pick(redraw, true);
-
-		// If the point count is the same as is was, just run Point.update which is
-		// cheaper, allows animation, and keeps references to points.
-		if (updatePoints !== false && dataLength && oldDataLength === dataLength && !series.cropped && !series.hasGroupedData) {
-			each(data, function (point, i) {
-				oldData[i].update(point, false);
-			});
-
-		} else {
-
-			// Reset properties
-			series.xIncrement = null;
-			series.pointRange = hasCategories ? 1 : options.pointRange;
-
-			series.colorCounter = 0; // for series with colorByPoint (#1547)
-			
-			// Update parallel arrays
-			each(this.parallelArrays, function (key) {
-				series[key + 'Data'].length = 0;
-			});
-
-			// In turbo mode, only one- or twodimensional arrays of numbers are allowed. The
-			// first value is tested, and we assume that all the rest are defined the same
-			// way. Although the 'for' loops are similar, they are repeated inside each
-			// if-else conditional for max performance.
-			if (turboThreshold && dataLength > turboThreshold) {
-
-				// find the first non-null point
-				i = 0;
-				while (firstPoint === null && i < dataLength) {
-					firstPoint = data[i];
-					i++;
-				}
-
-
-				if (isNumber(firstPoint)) { // assume all points are numbers
-					var x = pick(options.pointStart, 0),
-						pointInterval = pick(options.pointInterval, 1);
-
-					for (i = 0; i < dataLength; i++) {
-						xData[i] = x;
-						yData[i] = data[i];
-						x += pointInterval;
-					}
-					series.xIncrement = x;
-				} else if (isArray(firstPoint)) { // assume all points are arrays
-					if (valueCount) { // [x, low, high] or [x, o, h, l, c]
-						for (i = 0; i < dataLength; i++) {
-							pt = data[i];
-							xData[i] = pt[0];
-							yData[i] = pt.slice(1, valueCount + 1);
-						}
-					} else { // [x, y]
-						for (i = 0; i < dataLength; i++) {
-							pt = data[i];
-							xData[i] = pt[0];
-							yData[i] = pt[1];
-						}
-					}
-				} else {
-					error(12); // Highcharts expects configs to be numbers or arrays in turbo mode
-				}
-			} else {
-				for (i = 0; i < dataLength; i++) {
-					if (data[i] !== UNDEFINED) { // stray commas in oldIE
-						pt = { series: series };
-						series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
-						series.updateParallelArrays(pt, i);
-						if (hasCategories && pt.name) {
-							xAxis.names[pt.x] = pt.name; // #2046
-						}
-					}
-				}
-			}
-
-			// Forgetting to cast strings to numbers is a common caveat when handling CSV or JSON
-			if (isString(yData[0])) {
-				error(14, true);
-			}
-
-			series.data = [];
-			series.options.data = data;
-			//series.zData = zData;
-
-			// destroy old points
-			i = oldDataLength;
-			while (i--) {
-				if (oldData[i] && oldData[i].destroy) {
-					oldData[i].destroy();
-				}
-			}
-			if (tooltipPoints) { // #2594
-				tooltipPoints.length = 0;
-			}
-
-			// reset minRange (#878)
-			if (xAxis) {
-				xAxis.minRange = xAxis.userMinRange;
-			}
-
-			// redraw
-			series.isDirty = series.isDirtyData = chart.isDirtyBox = true;
-			animation = false;
-		}
-
-		if (redraw) {
-			chart.redraw(animation);
-		}
-	},
-
-	/**
-	 * Process the data by cropping away unused data points if the series is longer
-	 * than the crop threshold. This saves computing time for lage series.
-	 */
-	processData: function (force) {
-		var series = this,
-			processedXData = series.xData, // copied during slice operation below
-			processedYData = series.yData,
-			dataLength = processedXData.length,
-			croppedData,
-			cropStart = 0,
-			cropped,
-			distance,
-			closestPointRange,
-			xAxis = series.xAxis,
-			i, // loop variable
-			options = series.options,
-			cropThreshold = options.cropThreshold,
-			activePointCount = 0,
-			isCartesian = series.isCartesian,
-			min,
-			max;
-
-		// If the series data or axes haven't changed, don't go through this. Return false to pass
-		// the message on to override methods like in data grouping.
-		if (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) {
-			return false;
-		}
-
-
-		// optionally filter out points outside the plot area
-		if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
-			
-			min = xAxis.min;
-			max = xAxis.max;
-
-			// it's outside current extremes
-			if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
-				processedXData = [];
-				processedYData = [];
-
-			// only crop if it's actually spilling out
-			} else if (processedXData[0] < min || processedXData[dataLength - 1] > max) {
-				croppedData = this.cropData(series.xData, series.yData, min, max);
-				processedXData = croppedData.xData;
-				processedYData = croppedData.yData;
-				cropStart = croppedData.start;
-				cropped = true;
-				activePointCount = processedXData.length;
-			}
-		}
-
-
-		// Find the closest distance between processed points
-		for (i = processedXData.length - 1; i >= 0; i--) {
-			distance = processedXData[i] - processedXData[i - 1];
-			
-			if (!cropped && processedXData[i] > min && processedXData[i] < max) {
-				activePointCount++;
-			}
-			if (distance > 0 && (closestPointRange === UNDEFINED || distance < closestPointRange)) {
-				closestPointRange = distance;
-
-			// Unsorted data is not supported by the line tooltip, as well as data grouping and
-			// navigation in Stock charts (#725) and width calculation of columns (#1900)
-			} else if (distance < 0 && series.requireSorting) {
-				error(15);
-			}
-		}
-
-		// Record the properties
-		series.cropped = cropped; // undefined or true
-		series.cropStart = cropStart;
-		series.processedXData = processedXData;
-		series.processedYData = processedYData;
-		series.activePointCount = activePointCount;
-
-		if (options.pointRange === null) { // null means auto, as for columns, candlesticks and OHLC
-			series.pointRange = closestPointRange || 1;
-		}
-		series.closestPointRange = closestPointRange;
-
-	},
-
-	/**
-	 * Iterate over xData and crop values between min and max. Returns object containing crop start/end
-	 * cropped xData with corresponding part of yData, dataMin and dataMax within the cropped range
-	 */
-	cropData: function (xData, yData, min, max) {
-		var dataLength = xData.length,
-			cropStart = 0,
-			cropEnd = dataLength,
-			cropShoulder = pick(this.cropShoulder, 1), // line-type series need one point outside
-			i;
-
-		// iterate up to find slice start
-		for (i = 0; i < dataLength; i++) {
-			if (xData[i] >= min) {
-				cropStart = mathMax(0, i - cropShoulder);
-				break;
-			}
-		}
-
-		// proceed to find slice end
-		for (; i < dataLength; i++) {
-			if (xData[i] > max) {
-				cropEnd = i + cropShoulder;
-				break;
-			}
-		}
-
-		return {
-			xData: xData.slice(cropStart, cropEnd),
-			yData: yData.slice(cropStart, cropEnd),
-			start: cropStart,
-			end: cropEnd
-		};
-	},
-
-
-	/**
-	 * Generate the data point after the data has been processed by cropping away
-	 * unused points and optionally grouped in Highcharts Stock.
-	 */
-	generatePoints: function () {
-		var series = this,
-			options = series.options,
-			dataOptions = options.data,
-			data = series.data,
-			dataLength,
-			processedXData = series.processedXData,
-			processedYData = series.processedYData,
-			pointClass = series.pointClass,
-			processedDataLength = processedXData.length,
-			cropStart = series.cropStart || 0,
-			cursor,
-			hasGroupedData = series.hasGroupedData,
-			point,
-			points = [],
-			i;
-
-		if (!data && !hasGroupedData) {
-			var arr = [];
-			arr.length = dataOptions.length;
-			data = series.data = arr;
-		}
-
-		for (i = 0; i < processedDataLength; i++) {
-			cursor = cropStart + i;
-			if (!hasGroupedData) {
-				if (data[cursor]) {
-					point = data[cursor];
-				} else if (dataOptions[cursor] !== UNDEFINED) { // #970
-					data[cursor] = point = (new pointClass()).init(series, dataOptions[cursor], processedXData[i]);
-				}
-				points[i] = point;
-			} else {
-				// splat the y data in case of ohlc data array
-				points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
-			}
-		}
-
-		// Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when
-		// swithching view from non-grouped data to grouped data (#637)
-		if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) {
-			for (i = 0; i < dataLength; i++) {
-				if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points
-					i += processedDataLength;
-				}
-				if (data[i]) {
-					data[i].destroyElements();
-					data[i].plotX = UNDEFINED; // #1003
-				}
-			}
-		}
-
-		series.data = data;
-		series.points = points;
-	},
-
-	/**
-	 * Calculate Y extremes for visible data
-	 */
-	getExtremes: function (yData) {
-		var xAxis = this.xAxis,
-			yAxis = this.yAxis,
-			xData = this.processedXData,
-			yDataLength,
-			activeYData = [],
-			activeCounter = 0,
-			xExtremes = xAxis.getExtremes(), // #2117, need to compensate for log X axis
-			xMin = xExtremes.min,
-			xMax = xExtremes.max,
-			validValue,
-			withinRange,
-			dataMin,
-			dataMax,
-			x,
-			y,
-			i,
-			j;
-
-		yData = yData || this.stackedYData || this.processedYData;
-		yDataLength = yData.length;
-
-		for (i = 0; i < yDataLength; i++) {
-
-			x = xData[i];
-			y = yData[i];
-
-			// For points within the visible range, including the first point outside the
-			// visible range, consider y extremes
-			validValue = y !== null && y !== UNDEFINED && (!yAxis.isLog || (y.length || y > 0));
-			withinRange = this.getExtremesFromAll || this.cropped || ((xData[i + 1] || x) >= xMin &&
-				(xData[i - 1] || x) <= xMax);
-
-			if (validValue && withinRange) {
-
-				j = y.length;
-				if (j) { // array, like ohlc or range data
-					while (j--) {
-						if (y[j] !== null) {
-							activeYData[activeCounter++] = y[j];
-						}
-					}
-				} else {
-					activeYData[activeCounter++] = y;
-				}
-			}
-		}
-		this.dataMin = pick(dataMin, arrayMin(activeYData));
-		this.dataMax = pick(dataMax, arrayMax(activeYData));
-	},
-
-	/**
-	 * Translate data points from raw data values to chart specific positioning data
-	 * needed later in drawPoints, drawGraph and drawTracker.
-	 */
-	translate: function () {
-		if (!this.processedXData) { // hidden series
-			this.processData();
-		}
-		this.generatePoints();
-		var series = this,
-			options = series.options,
-			stacking = options.stacking,
-			xAxis = series.xAxis,
-			categories = xAxis.categories,
-			yAxis = series.yAxis,
-			points = series.points,
-			dataLength = points.length,
-			hasModifyValue = !!series.modifyValue,
-			i,
-			pointPlacement = options.pointPlacement,
-			dynamicallyPlaced = pointPlacement === 'between' || isNumber(pointPlacement),
-			threshold = options.threshold;
-
-		// Translate each point
-		for (i = 0; i < dataLength; i++) {
-			var point = points[i],
-				xValue = point.x,
-				yValue = point.y,
-				yBottom = point.low,
-				stack = stacking && yAxis.stacks[(series.negStacks && yValue < threshold ? '-' : '') + series.stackKey],
-				pointStack,
-				stackValues;
-
-			// Discard disallowed y values for log axes
-			if (yAxis.isLog && yValue <= 0) {
-				point.y = yValue = null;
-			}
-
-			// Get the plotX translation
-			point.plotX = xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags'); // Math.round fixes #591
-
-
-			// Calculate the bottom y value for stacked series
-			if (stacking && series.visible && stack && stack[xValue]) {
-
-				pointStack = stack[xValue];
-				stackValues = pointStack.points[series.index + ',' + i];
-				yBottom = stackValues[0];
-				yValue = stackValues[1];
-
-				if (yBottom === 0) {
-					yBottom = pick(threshold, yAxis.min);
-				}
-				if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
-					yBottom = null;
-				}
-
-				point.total = point.stackTotal = pointStack.total;
-				point.percentage = pointStack.total && (point.y / pointStack.total * 100);
-				point.stackY = yValue;
-
-				// Place the stack label
-				pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
-
-			}
-
-			// Set translated yBottom or remove it
-			point.yBottom = defined(yBottom) ?
-				yAxis.translate(yBottom, 0, 1, 0, 1) :
-				null;
-
-			// general hook, used for Highstock compare mode
-			if (hasModifyValue) {
-				yValue = series.modifyValue(yValue, point);
-			}
-
-			// Set the the plotY value, reset it for redraws
-			point.plotY = (typeof yValue === 'number' && yValue !== Infinity) ?
-				//mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10 : // Math.round fixes #591
-				yAxis.translate(yValue, 0, 1, 0, 1) :
-				UNDEFINED;
-
-			// Set client related positions for mouse tracking
-			point.clientX = dynamicallyPlaced ? xAxis.translate(xValue, 0, 0, 0, 1) : point.plotX; // #1514
-
-			point.negative = point.y < (threshold || 0);
-
-			// some API data
-			point.category = categories && categories[point.x] !== UNDEFINED ?
-				categories[point.x] : point.x;
-
-		}
-
-		// now that we have the cropped data, build the segments
-		series.getSegments();
-	},
-
-	/**
-	 * Animate in the series
-	 */
-	animate: function (init) {
-		var series = this,
-			chart = series.chart,
-			renderer = chart.renderer,
-			clipRect,
-			markerClipRect,
-			animation = series.options.animation,
-			clipBox = series.clipBox || chart.clipBox,
-			inverted = chart.inverted,
-			sharedClipKey;
-
-		// Animation option is set to true
-		if (animation && !isObject(animation)) {
-			animation = defaultPlotOptions[series.type].animation;
-		}
-		sharedClipKey = ['_sharedClip', animation.duration, animation.easing, clipBox.height].join(',');
-
-		// Initialize the animation. Set up the clipping rectangle.
-		if (init) {
-
-			// If a clipping rectangle with the same properties is currently present in the chart, use that.
-			clipRect = chart[sharedClipKey];
-			markerClipRect = chart[sharedClipKey + 'm'];
-			if (!clipRect) {
-				chart[sharedClipKey] = clipRect = renderer.clipRect(
-					extend(clipBox, { width: 0 })
-				);
-
-				chart[sharedClipKey + 'm'] = markerClipRect = renderer.clipRect(
-					-99, // include the width of the first marker
-					inverted ? -chart.plotLeft : -chart.plotTop,
-					99,
-					inverted ? chart.chartWidth : chart.chartHeight
-				);
-			}
-			series.group.clip(clipRect);
-			series.markerGroup.clip(markerClipRect);
-			series.sharedClipKey = sharedClipKey;
-
-		// Run the animation
-		} else {
-			clipRect = chart[sharedClipKey];
-			if (clipRect) {
-				clipRect.animate({
-					width: chart.plotSizeX
-				}, animation);
-			}
-			if (chart[sharedClipKey + 'm']) {
-				chart[sharedClipKey + 'm'].animate({
-					width: chart.plotSizeX + 99
-				}, animation);
-			}
-
-			// Delete this function to allow it only once
-			series.animate = null;
- 
-		}
-	},
-
-	/**
-	 * This runs after animation to land on the final plot clipping
-	 */
-	afterAnimate: function () {
-		var chart = this.chart,
-			sharedClipKey = this.sharedClipKey,
-			group = this.group,
-			clipBox = this.clipBox;
-
-		if (group && this.options.clip !== false) {
-			if (!sharedClipKey || !clipBox) {
-				group.clip(clipBox ? chart.renderer.clipRect(clipBox) : chart.clipRect);
-			}
-			this.markerGroup.clip(); // no clip
-		}
-
-		fireEvent(this, 'afterAnimate');
-
-		// Remove the shared clipping rectancgle when all series are shown
-		setTimeout(function () {
-			if (sharedClipKey && chart[sharedClipKey]) {
-				if (!clipBox) {
-					chart[sharedClipKey] = chart[sharedClipKey].destroy();
-				}
-				if (chart[sharedClipKey + 'm']) {
-					chart[sharedClipKey + 'm'] = chart[sharedClipKey + 'm'].destroy();
-				}
-			}
-		}, 100);
-	},
-
-	/**
-	 * Draw the markers
-	 */
-	drawPoints: function () {
-		var series = this,
-			pointAttr,
-			points = series.points,
-			chart = series.chart,
-			plotX,
-			plotY,
-			i,
-			point,
-			radius,
-			symbol,
-			isImage,
-			graphic,
-			options = series.options,
-			seriesMarkerOptions = options.marker,
-			seriesPointAttr = series.pointAttr[''],
-			pointMarkerOptions,
-			enabled,
-			isInside,
-			markerGroup = series.markerGroup,
-			globallyEnabled = pick(
-				seriesMarkerOptions.enabled, 
-				series.activePointCount < (0.5 * series.xAxis.len / seriesMarkerOptions.radius)
-			);
-
-		if (seriesMarkerOptions.enabled !== false || series._hasPointMarkers) {
-
-			i = points.length;
-			while (i--) {
-				point = points[i];
-				plotX = mathFloor(point.plotX); // #1843
-				plotY = point.plotY;
-				graphic = point.graphic;
-				pointMarkerOptions = point.marker || {};
-				enabled = (globallyEnabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled;
-				isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858
-
-				// only draw the point if y is defined
-				if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
-
-					// shortcuts
-					pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] || seriesPointAttr;
-					radius = pointAttr.r;
-					symbol = pick(pointMarkerOptions.symbol, series.symbol);
-					isImage = symbol.indexOf('url') === 0;
-
-					if (graphic) { // update
-						graphic[isInside ? 'show' : 'hide'](true) // Since the marker group isn't clipped, each individual marker must be toggled
-							.animate(extend({
-								x: plotX - radius,
-								y: plotY - radius
-							}, graphic.symbolName ? { // don't apply to image symbols #507
-								width: 2 * radius,
-								height: 2 * radius
-							} : {}));
-					} else if (isInside && (radius > 0 || isImage)) {
-						point.graphic = graphic = chart.renderer.symbol(
-							symbol,
-							plotX - radius,
-							plotY - radius,
-							2 * radius,
-							2 * radius
-						)
-						.attr(pointAttr)
-						.add(markerGroup);
-					}
-
-				} else if (graphic) {
-					point.graphic = graphic.destroy(); // #1269
-				}
-			}
-		}
-
-	},
-
-	/**
-	 * Convert state properties from API naming conventions to SVG attributes
-	 *
-	 * @param {Object} options API options object
-	 * @param {Object} base1 SVG attribute object to inherit from
-	 * @param {Object} base2 Second level SVG attribute object to inherit from
-	 */
-	convertAttribs: function (options, base1, base2, base3) {
-		var conversion = this.pointAttrToOptions,
-			attr,
-			option,
-			obj = {};
-
-		options = options || {};
-		base1 = base1 || {};
-		base2 = base2 || {};
-		base3 = base3 || {};
-
-		for (attr in conversion) {
-			option = conversion[attr];
-			obj[attr] = pick(options[option], base1[attr], base2[attr], base3[attr]);
-		}
-		return obj;
-	},
-
-	/**
-	 * Get the state attributes. Each series type has its own set of attributes
-	 * that are allowed to change on a point's state change. Series wide attributes are stored for
-	 * all series, and additionally point specific attributes are stored for all
-	 * points with individual marker options. If such options are not defined for the point,
-	 * a reference to the series wide attributes is stored in point.pointAttr.
-	 */
-	getAttribs: function () {
-		var series = this,
-			seriesOptions = series.options,
-			normalOptions = defaultPlotOptions[series.type].marker ? seriesOptions.marker : seriesOptions,
-			stateOptions = normalOptions.states,
-			stateOptionsHover = stateOptions[HOVER_STATE],
-			pointStateOptionsHover,
-			seriesColor = series.color,
-			normalDefaults = {
-				stroke: seriesColor,
-				fill: seriesColor
-			},
-			points = series.points || [], // #927
-			i,
-			point,
-			seriesPointAttr = [],
-			pointAttr,
-			pointAttrToOptions = series.pointAttrToOptions,
-			hasPointSpecificOptions = series.hasPointSpecificOptions,
-			negativeColor = seriesOptions.negativeColor,
-			defaultLineColor = normalOptions.lineColor,
-			defaultFillColor = normalOptions.fillColor,
-			turboThreshold = seriesOptions.turboThreshold,
-			attr,
-			key;
-
-		// series type specific modifications
-		if (seriesOptions.marker) { // line, spline, area, areaspline, scatter
-
-			// if no hover radius is given, default to normal radius + 2
-			stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2;
-			stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1;
-
-		} else { // column, bar, pie
-
-			// if no hover color is given, brighten the normal color
-			stateOptionsHover.color = stateOptionsHover.color ||
-				Color(stateOptionsHover.color || seriesColor)
-					.brighten(stateOptionsHover.brightness).get();
-		}
-
-		// general point attributes for the series normal state
-		seriesPointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, normalDefaults);
-
-		// HOVER_STATE and SELECT_STATE states inherit from normal state except the default radius
-		each([HOVER_STATE, SELECT_STATE], function (state) {
-			seriesPointAttr[state] =
-					series.convertAttribs(stateOptions[state], seriesPointAttr[NORMAL_STATE]);
-		});
-
-		// set it
-		series.pointAttr = seriesPointAttr;
-
-
-		// Generate the point-specific attribute collections if specific point
-		// options are given. If not, create a referance to the series wide point
-		// attributes
-		i = points.length;
-		if (!turboThreshold || i < turboThreshold || hasPointSpecificOptions) {
-			while (i--) {
-				point = points[i];
-				normalOptions = (point.options && point.options.marker) || point.options;
-				if (normalOptions && normalOptions.enabled === false) {
-					normalOptions.radius = 0;
-				}
-
-				if (point.negative && negativeColor) {
-					point.color = point.fillColor = negativeColor;
-				}
-
-				hasPointSpecificOptions = seriesOptions.colorByPoint || point.color; // #868
-
-				// check if the point has specific visual options
-				if (point.options) {
-					for (key in pointAttrToOptions) {
-						if (defined(normalOptions[pointAttrToOptions[key]])) {
-							hasPointSpecificOptions = true;
-						}
-					}
-				}
-
-				// a specific marker config object is defined for the individual point:
-				// create it's own attribute collection
-				if (hasPointSpecificOptions) {
-					normalOptions = normalOptions || {};
-					pointAttr = [];
-					stateOptions = normalOptions.states || {}; // reassign for individual point
-					pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {};
-
-					// Handle colors for column and pies
-					if (!seriesOptions.marker) { // column, bar, point
-						// If no hover color is given, brighten the normal color. #1619, #2579
-						pointStateOptionsHover.color = pointStateOptionsHover.color || (!point.options.color && stateOptionsHover.color) ||
-							Color(point.color)
-								.brighten(pointStateOptionsHover.brightness || stateOptionsHover.brightness)
-								.get();
-					}
-
-					// normal point state inherits series wide normal state
-					attr = { color: point.color }; // #868
-					if (!defaultFillColor) { // Individual point color or negative color markers (#2219)
-						attr.fillColor = point.color;
-					}
-					if (!defaultLineColor) {
-						attr.lineColor = point.color; // Bubbles take point color, line markers use white
-					}
-					pointAttr[NORMAL_STATE] = series.convertAttribs(extend(attr, normalOptions), seriesPointAttr[NORMAL_STATE]);
-
-					// inherit from point normal and series hover
-					pointAttr[HOVER_STATE] = series.convertAttribs(
-						stateOptions[HOVER_STATE],
-						seriesPointAttr[HOVER_STATE],
-						pointAttr[NORMAL_STATE]
-					);
-
-					// inherit from point normal and series hover
-					pointAttr[SELECT_STATE] = series.convertAttribs(
-						stateOptions[SELECT_STATE],
-						seriesPointAttr[SELECT_STATE],
-						pointAttr[NORMAL_STATE]
-					);
-
-
-				// no marker config object is created: copy a reference to the series-wide
-				// attribute collection
-				} else {
-					pointAttr = seriesPointAttr;
-				}
-
-				point.pointAttr = pointAttr;
-			}
-		}
-	},
-
-	/**
-	 * Clear DOM objects and free up memory
-	 */
-	destroy: function () {
-		var series = this,
-			chart = series.chart,
-			issue134 = /AppleWebKit\/533/.test(userAgent),
-			destroy,
-			i,
-			data = series.data || [],
-			point,
-			prop,
-			axis;
-
-		// add event hook
-		fireEvent(series, 'destroy');
-
-		// remove all events
-		removeEvent(series);
-
-		// erase from axes
-		each(series.axisTypes || [], function (AXIS) {
-			axis = series[AXIS];
-			if (axis) {
-				erase(axis.series, series);
-				axis.isDirty = axis.forceRedraw = true;
-			}
-		});
-
-		// remove legend items
-		if (series.legendItem) {
-			series.chart.legend.destroyItem(series);
-		}
-
-		// destroy all points with their elements
-		i = data.length;
-		while (i--) {
-			point = data[i];
-			if (point && point.destroy) {
-				point.destroy();
-			}
-		}
-		series.points = null;
-
-		// Clear the animation timeout if we are destroying the series during initial animation
-		clearTimeout(series.animationTimeout);
-
-		// destroy all SVGElements associated to the series
-		each(['area', 'graph', 'dataLabelsGroup', 'group', 'markerGroup', 'tracker',
-				'graphNeg', 'areaNeg', 'posClip', 'negClip'], function (prop) {
-			if (series[prop]) {
-
-				// issue 134 workaround
-				destroy = issue134 && prop === 'group' ?
-					'hide' :
-					'destroy';
-
-				series[prop][destroy]();
-			}
-		});
-
-		// remove from hoverSeries
-		if (chart.hoverSeries === series) {
-			chart.hoverSeries = null;
-		}
-		erase(chart.series, series);
-
-		// clear all members
-		for (prop in series) {
-			delete series[prop];
-		}
-	},
-
-	/**
-	 * Return the graph path of a segment
-	 */
-	getSegmentPath: function (segment) {
-		var series = this,
-			segmentPath = [],
-			step = series.options.step;
-
-		// build the segment line
-		each(segment, function (point, i) {
-
-			var plotX = point.plotX,
-				plotY = point.plotY,
-				lastPoint;
-
-			if (series.getPointSpline) { // generate the spline as defined in the SplineSeries object
-				segmentPath.push.apply(segmentPath, series.getPointSpline(segment, point, i));
-
-			} else {
-
-				// moveTo or lineTo
-				segmentPath.push(i ? L : M);
-
-				// step line?
-				if (step && i) {
-					lastPoint = segment[i - 1];
-					if (step === 'right') {
-						segmentPath.push(
-							lastPoint.plotX,
-							plotY
-						);
-
-					} else if (step === 'center') {
-						segmentPath.push(
-							(lastPoint.plotX + plotX) / 2,
-							lastPoint.plotY,
-							(lastPoint.plotX + plotX) / 2,
-							plotY
-						);
-
-					} else {
-						segmentPath.push(
-							plotX,
-							lastPoint.plotY
-						);
-					}
-				}
-
-				// normal line to next point
-				segmentPath.push(
-					point.plotX,
-					point.plotY
-				);
-			}
-		});
-
-		return segmentPath;
-	},
-
-	/**
-	 * Get the graph path
-	 */
-	getGraphPath: function () {
-		var series = this,
-			graphPath = [],
-			segmentPath,
-			singlePoints = []; // used in drawTracker
-
-		// Divide into segments and build graph and area paths
-		each(series.segments, function (segment) {
-
-			segmentPath = series.getSegmentPath(segment);
-
-			// add the segment to the graph, or a single point for tracking
-			if (segment.length > 1) {
-				graphPath = graphPath.concat(segmentPath);
-			} else {
-				singlePoints.push(segment[0]);
-			}
-		});
-
-		// Record it for use in drawGraph and drawTracker, and return graphPath
-		series.singlePoints = singlePoints;
-		series.graphPath = graphPath;
-
-		return graphPath;
-
-	},
-
-	/**
-	 * Draw the actual graph
-	 */
-	drawGraph: function () {
-		var series = this,
-			options = this.options,
-			props = [['graph', options.lineColor || this.color]],
-			lineWidth = options.lineWidth,
-			dashStyle =  options.dashStyle,
-			roundCap = options.linecap !== 'square',
-			graphPath = this.getGraphPath(),
-			negativeColor = options.negativeColor;
-
-		if (negativeColor) {
-			props.push(['graphNeg', negativeColor]);
-		}
-
-		// draw the graph
-		each(props, function (prop, i) {
-			var graphKey = prop[0],
-				graph = series[graphKey],
-				attribs;
-
-			if (graph) {
-				stop(graph); // cancel running animations, #459
-				graph.animate({ d: graphPath });
-
-			} else if (lineWidth && graphPath.length) { // #1487
-				attribs = {
-					stroke: prop[1],
-					'stroke-width': lineWidth,
-					fill: NONE,
-					zIndex: 1 // #1069
-				};
-				if (dashStyle) {
-					attribs.dashstyle = dashStyle;
-				} else if (roundCap) {
-					attribs['stroke-linecap'] = attribs['stroke-linejoin'] = 'round';
-				}
-
-				series[graphKey] = series.chart.renderer.path(graphPath)
-					.attr(attribs)
-					.add(series.group)
-					.shadow(!i && options.shadow);
-			}
-		});
-	},
-
-	/**
-	 * Clip the graphs into the positive and negative coloured graphs
-	 */
-	clipNeg: function () {
-		var options = this.options,
-			chart = this.chart,
-			renderer = chart.renderer,
-			negativeColor = options.negativeColor || options.negativeFillColor,
-			translatedThreshold,
-			posAttr,
-			negAttr,
-			graph = this.graph,
-			area = this.area,
-			posClip = this.posClip,
-			negClip = this.negClip,
-			chartWidth = chart.chartWidth,
-			chartHeight = chart.chartHeight,
-			chartSizeMax = mathMax(chartWidth, chartHeight),
-			yAxis = this.yAxis,
-			above,
-			below;
-
-		if (negativeColor && (graph || area)) {
-			translatedThreshold = mathRound(yAxis.toPixels(options.threshold || 0, true));
-			if (translatedThreshold < 0) {
-				chartSizeMax -= translatedThreshold; // #2534
-			}
-			above = {
-				x: 0,
-				y: 0,
-				width: chartSizeMax,
-				height: translatedThreshold
-			};
-			below = {
-				x: 0,
-				y: translatedThreshold,
-				width: chartSizeMax,
-				height: chartSizeMax
-			};
-
-			if (chart.inverted) {
-
-				above.height = below.y = chart.plotWidth - translatedThreshold;
-				if (renderer.isVML) {
-					above = {
-						x: chart.plotWidth - translatedThreshold - chart.plotLeft,
-						y: 0,
-						width: chartWidth,
-						height: chartHeight
-					};
-					below = {
-						x: translatedThreshold + chart.plotLeft - chartWidth,
-						y: 0,
-						width: chart.plotLeft + translatedThreshold,
-						height: chartWidth
-					};
-				}
-			}
-
-			if (yAxis.reversed) {
-				posAttr = below;
-				negAttr = above;
-			} else {
-				posAttr = above;
-				negAttr = below;
-			}
-
-			if (posClip) { // update
-				posClip.animate(posAttr);
-				negClip.animate(negAttr);
-			} else {
-
-				this.posClip = posClip = renderer.clipRect(posAttr);
-				this.negClip = negClip = renderer.clipRect(negAttr);
-
-				if (graph && this.graphNeg) {
-					graph.clip(posClip);
-					this.graphNeg.clip(negClip);
-				}
-
-				if (area) {
-					area.clip(posClip);
-					this.areaNeg.clip(negClip);
-				}
-			}
-		}
-	},
-
-	/**
-	 * Initialize and perform group inversion on series.group and series.markerGroup
-	 */
-	invertGroups: function () {
-		var series = this,
-			chart = series.chart;
-
-		// Pie, go away (#1736)
-		if (!series.xAxis) {
-			return;
-		}
-
-		// A fixed size is needed for inversion to work
-		function setInvert() {
-			var size = {
-				width: series.yAxis.len,
-				height: series.xAxis.len
-			};
-
-			each(['group', 'markerGroup'], function (groupName) {
-				if (series[groupName]) {
-					series[groupName].attr(size).invert();
-				}
-			});
-		}
-
-		addEvent(chart, 'resize', setInvert); // do it on resize
-		addEvent(series, 'destroy', function () {
-			removeEvent(chart, 'resize', setInvert);
-		});
-
-		// Do it now
-		setInvert(); // do it now
-
-		// On subsequent render and redraw, just do setInvert without setting up events again
-		series.invertGroups = setInvert;
-	},
-
-	/**
-	 * General abstraction for creating plot groups like series.group, series.dataLabelsGroup and
-	 * series.markerGroup. On subsequent calls, the group will only be adjusted to the updated plot size.
-	 */
-	plotGroup: function (prop, name, visibility, zIndex, parent) {
-		var group = this[prop],
-			isNew = !group;
-
-		// Generate it on first call
-		if (isNew) {
-			this[prop] = group = this.chart.renderer.g(name)
-				.attr({
-					visibility: visibility,
-					zIndex: zIndex || 0.1 // IE8 needs this
-				})
-				.add(parent);
-		}
-		// Place it on first and subsequent (redraw) calls
-		group[isNew ? 'attr' : 'animate'](this.getPlotBox());
-		return group;
-	},
-
-	/**
-	 * Get the translation and scale for the plot area of this series
-	 */
-	getPlotBox: function () {
-		var chart = this.chart,
-			xAxis = this.xAxis,
-			yAxis = this.yAxis;
-
-		// Swap axes for inverted (#2339)
-		if (chart.inverted) {
-			xAxis = yAxis;
-			yAxis = this.xAxis;
-		}
-		return {
-			translateX: xAxis ? xAxis.left : chart.plotLeft,
-			translateY: yAxis ? yAxis.top : chart.plotTop,
-			scaleX: 1, // #1623
-			scaleY: 1
-		};
-	},
-
-	/**
-	 * Render the graph and markers
-	 */
-	render: function () {
-		var series = this,
-			chart = series.chart,
-			group,
-			options = series.options,
-			animation = options.animation,
-			// Animation doesn't work in IE8 quirks when the group div is hidden,
-			// and looks bad in other oldIE
-			animDuration = (animation && !!series.animate && chart.renderer.isSVG && pick(animation.duration, 500)) || 0,
-			visibility = series.visible ? VISIBLE : HIDDEN,
-			zIndex = options.zIndex,
-			hasRendered = series.hasRendered,
-			chartSeriesGroup = chart.seriesGroup;
-
-		// the group
-		group = series.plotGroup(
-			'group',
-			'series',
-			visibility,
-			zIndex,
-			chartSeriesGroup
-		);
-
-		series.markerGroup = series.plotGroup(
-			'markerGroup',
-			'markers',
-			visibility,
-			zIndex,
-			chartSeriesGroup
-		);
-
-		// initiate the animation
-		if (animDuration) {
-			series.animate(true);
-		}
-
-		// cache attributes for shapes
-		series.getAttribs();
-
-		// SVGRenderer needs to know this before drawing elements (#1089, #1795)
-		group.inverted = series.isCartesian ? chart.inverted : false;
-
-		// draw the graph if any
-		if (series.drawGraph) {
-			series.drawGraph();
-			series.clipNeg();
-		}
-
-		// draw the data labels (inn pies they go before the points)
-		if (series.drawDataLabels) {
-			series.drawDataLabels();
-		}
-
-		// draw the points
-		if (series.visible) {
-			series.drawPoints();
-		}
-
-
-		// draw the mouse tracking area
-		if (series.drawTracker && series.options.enableMouseTracking !== false) {
-			series.drawTracker();
-		}
-
-		// Handle inverted series and tracker groups
-		if (chart.inverted) {
-			series.invertGroups();
-		}
-
-		// Initial clipping, must be defined after inverting groups for VML
-		if (options.clip !== false && !series.sharedClipKey && !hasRendered) {
-			group.clip(chart.clipRect);
-		}
-
-		// Run the animation
-		if (animDuration) {
-			series.animate();
-		} 
-
-		// Call the afterAnimate function on animation complete (but don't overwrite the animation.complete option
-		// which should be available to the user).
-		if (!hasRendered) {
-			if (animDuration) {
-				series.animationTimeout = setTimeout(function () {
-					series.afterAnimate();
-				}, animDuration);
-			} else {
-				series.afterAnimate();
-			}
-		}
-
-		series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
-		// (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
-		series.hasRendered = true;
-	},
-
-	/**
-	 * Redraw the series after an update in the axes.
-	 */
-	redraw: function () {
-		var series = this,
-			chart = series.chart,
-			wasDirtyData = series.isDirtyData, // cache it here as it is set to false in render, but used after
-			group = series.group,
-			xAxis = series.xAxis,
-			yAxis = series.yAxis;
-
-		// reposition on resize
-		if (group) {
-			if (chart.inverted) {
-				group.attr({
-					width: chart.plotWidth,
-					height: chart.plotHeight
-				});
-			}
-
-			group.animate({
-				translateX: pick(xAxis && xAxis.left, chart.plotLeft),
-				translateY: pick(yAxis && yAxis.top, chart.plotTop)
-			});
-		}
-
-		series.translate();
-		if (series.setTooltipPoints) {
-			series.setTooltipPoints(true);
-		}
-		series.render();
-
-		if (wasDirtyData) {
-			fireEvent(series, 'updatedData');
-		}
-	}
-}; // end Series prototype
-
-/**
- * The class for stack items
- */
-function StackItem(axis, options, isNegative, x, stackOption) {
-	
-	var inverted = axis.chart.inverted;
-
-	this.axis = axis;
-
-	// Tells if the stack is negative
-	this.isNegative = isNegative;
-
-	// Save the options to be able to style the label
-	this.options = options;
-
-	// Save the x value to be able to position the label later
-	this.x = x;
-
-	// Initialize total value
-	this.total = null;
-
-	// This will keep each points' extremes stored by series.index and point index
-	this.points = {};
-
-	// Save the stack option on the series configuration object, and whether to treat it as percent
-	this.stack = stackOption;
-
-	// The align options and text align varies on whether the stack is negative and
-	// if the chart is inverted or not.
-	// First test the user supplied value, then use the dynamic.
-	this.alignOptions = {
-		align: options.align || (inverted ? (isNegative ? 'left' : 'right') : 'center'),
-		verticalAlign: options.verticalAlign || (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
-		y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)),
-		x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0)
-	};
-
-	this.textAlign = options.textAlign || (inverted ? (isNegative ? 'right' : 'left') : 'center');
-}
-
-StackItem.prototype = {
-	destroy: function () {
-		destroyObjectProperties(this, this.axis);
-	},
-
-	/**
-	 * Renders the stack total label and adds it to the stack label group.
-	 */
-	render: function (group) {
-		var options = this.options,
-			formatOption = options.format,
-			str = formatOption ?
-				format(formatOption, this) : 
-				options.formatter.call(this);  // format the text in the label
-
-		// Change the text to reflect the new total and set visibility to hidden in case the serie is hidden
-		if (this.label) {
-			this.label.attr({text: str, visibility: HIDDEN});
-		// Create new label
-		} else {
-			this.label =
-				this.axis.chart.renderer.text(str, null, null, options.useHTML)		// dummy positions, actual position updated with setOffset method in columnseries
-					.css(options.style)				// apply style
-					.attr({
-						align: this.textAlign,				// fix the text-anchor
-						rotation: options.rotation,	// rotation
-						visibility: HIDDEN					// hidden until setOffset is called
-					})				
-					.add(group);							// add to the labels-group
-		}
-	},
-
-	/**
-	 * Sets the offset that the stack has from the x value and repositions the label.
-	 */
-	setOffset: function (xOffset, xWidth) {
-		var stackItem = this,
-			axis = stackItem.axis,
-			chart = axis.chart,
-			inverted = chart.inverted,
-			neg = this.isNegative,							// special treatment is needed for negative stacks
-			y = axis.translate(axis.usePercentage ? 100 : this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates
-			yZero = axis.translate(0),						// stack origin
-			h = mathAbs(y - yZero),							// stack height
-			x = chart.xAxis[0].translate(this.x) + xOffset,	// stack x position
-			plotHeight = chart.plotHeight,
-			stackBox = {	// this is the box for the complete stack
-				x: inverted ? (neg ? y : y - h) : x,
-				y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y),
-				width: inverted ? h : xWidth,
-				height: inverted ? xWidth : h
-			},
-			label = this.label,
-			alignAttr;
-		
-		if (label) {
-			label.align(this.alignOptions, null, stackBox);	// align the label to the box
-				
-			// Set visibility (#678)
-			alignAttr = label.alignAttr;
-			label[this.options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) ? 'show' : 'hide'](true);
-		}
-	}
-};
-
-
-// Stacking methods defined on the Axis prototype
-
-/**
- * Build the stacks from top down
- */
-Axis.prototype.buildStacks = function () {
-	var series = this.series,
-		reversedStacks = pick(this.options.reversedStacks, true),
-		i = series.length;
-	if (!this.isXAxis) {
-		this.usePercentage = false;
-		while (i--) {
-			series[reversedStacks ? i : series.length - i - 1].setStackedPoints();
-		}
-		// Loop up again to compute percent stack
-		if (this.usePercentage) {
-			for (i = 0; i < series.length; i++) {
-				series[i].setPercentStacks();
-			}
-		}
-	}
-};
-
-Axis.prototype.renderStackTotals = function () {
-	var axis = this,
-		chart = axis.chart,
-		renderer = chart.renderer,
-		stacks = axis.stacks,
-		stackKey, 
-		oneStack, 
-		stackCategory,
-		stackTotalGroup = axis.stackTotalGroup;
-
-	// Create a separate group for the stack total labels
-	if (!stackTotalGroup) {
-		axis.stackTotalGroup = stackTotalGroup =
-			renderer.g('stack-labels')
-				.attr({
-					visibility: VISIBLE,
-					zIndex: 6
-				})
-				.add();
-	}
-
-	// plotLeft/Top will change when y axis gets wider so we need to translate the
-	// stackTotalGroup at every render call. See bug #506 and #516
-	stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
-
-	// Render each stack total
-	for (stackKey in stacks) {
-		oneStack = stacks[stackKey];
-		for (stackCategory in oneStack) {
-			oneStack[stackCategory].render(stackTotalGroup);
-		}
-	}
-};
-
-
-// Stacking methods defnied for Series prototype
-
-/**
- * Adds series' points value to corresponding stack
- */
-Series.prototype.setStackedPoints = function () {
-	if (!this.options.stacking || (this.visible !== true && this.chart.options.chart.ignoreHiddenSeries !== false)) {
-		return;
-	}
-
-	var series = this,
-		xData = series.processedXData,
-		yData = series.processedYData,
-		stackedYData = [],
-		yDataLength = yData.length,
-		seriesOptions = series.options,
-		threshold = seriesOptions.threshold,
-		stackOption = seriesOptions.stack,
-		stacking = seriesOptions.stacking,
-		stackKey = series.stackKey,
-		negKey = '-' + stackKey,
-		negStacks = series.negStacks,
-		yAxis = series.yAxis,
-		stacks = yAxis.stacks,
-		oldStacks = yAxis.oldStacks,
-		isNegative,
-		stack,
-		other,
-		key,
-		pointKey,
-		i,
-		x,
-		y;
-
-	// loop over the non-null y values and read them into a local array
-	for (i = 0; i < yDataLength; i++) {
-		x = xData[i];
-		y = yData[i];
-		pointKey = series.index + ',' + i;
-
-		// Read stacked values into a stack based on the x value,
-		// the sign of y and the stack key. Stacking is also handled for null values (#739)
-		isNegative = negStacks && y < threshold;
-		key = isNegative ? negKey : stackKey;
-
-		// Create empty object for this stack if it doesn't exist yet
-		if (!stacks[key]) {
-			stacks[key] = {};
-		}
-
-		// Initialize StackItem for this x
-		if (!stacks[key][x]) {
-			if (oldStacks[key] && oldStacks[key][x]) {
-				stacks[key][x] = oldStacks[key][x];
-				stacks[key][x].total = null;
-			} else {
-				stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, isNegative, x, stackOption);
-			}
-		}
-
-		// If the StackItem doesn't exist, create it first
-		stack = stacks[key][x];
-		stack.points[pointKey] = [stack.cum || 0];
-
-		// Add value to the stack total
-		if (stacking === 'percent') {
-
-			// Percent stacked column, totals are the same for the positive and negative stacks
-			other = isNegative ? stackKey : negKey;
-			if (negStacks && stacks[other] && stacks[other][x]) {
-				other = stacks[other][x];
-				stack.total = other.total = mathMax(other.total, stack.total) + mathAbs(y) || 0;
-
-			// Percent stacked areas
-			} else {
-				stack.total = correctFloat(stack.total + (mathAbs(y) || 0));
-			}
-		} else {
-			stack.total = correctFloat(stack.total + (y || 0));
-		}
-
-		stack.cum = (stack.cum || 0) + (y || 0);
-
-		stack.points[pointKey].push(stack.cum);
-		stackedYData[i] = stack.cum;
-
-	}
-
-	if (stacking === 'percent') {
-		yAxis.usePercentage = true;
-	}
-
-	this.stackedYData = stackedYData; // To be used in getExtremes
-
-	// Reset old stacks
-	yAxis.oldStacks = {};
-};
-
-/**
- * Iterate over all stacks and compute the absolute values to percent
- */
-Series.prototype.setPercentStacks = function () {
-	var series = this,
-		stackKey = series.stackKey,
-		stacks = series.yAxis.stacks,
-		processedXData = series.processedXData;
-
-	each([stackKey, '-' + stackKey], function (key) {
-		var i = processedXData.length,
-			x,
-			stack,
-			pointExtremes,
-			totalFactor;
-
-		while (i--) {
-			x = processedXData[i];
-			stack = stacks[key] && stacks[key][x];
-			pointExtremes = stack && stack.points[series.index + ',' + i];
-			if (pointExtremes) {
-				totalFactor = stack.total ? 100 / stack.total : 0;
-				pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor); // Y bottom value
-				pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor); // Y value
-				series.stackedYData[i] = pointExtremes[1];
-			}
-		}
-	});
-};
-
-// Extend the Chart prototype for dynamic methods
-extend(Chart.prototype, {
-
-	/**
-	 * Add a series dynamically after  time
-	 *
-	 * @param {Object} options The config options
-	 * @param {Boolean} redraw Whether to redraw the chart after adding. Defaults to true.
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 *
-	 * @return {Object} series The newly created series object
-	 */
-	addSeries: function (options, redraw, animation) {
-		var series,
-			chart = this;
-
-		if (options) {
-			redraw = pick(redraw, true); // defaults to true
-
-			fireEvent(chart, 'addSeries', { options: options }, function () {
-				series = chart.initSeries(options);
-
-				chart.isDirtyLegend = true; // the series array is out of sync with the display
-				chart.linkSeries();
-				if (redraw) {
-					chart.redraw(animation);
-				}
-			});
-		}
-
-		return series;
-	},
-
-	/**
-     * Add an axis to the chart
-     * @param {Object} options The axis option
-     * @param {Boolean} isX Whether it is an X axis or a value axis
-     */
-	addAxis: function (options, isX, redraw, animation) {
-		var key = isX ? 'xAxis' : 'yAxis',
-			chartOptions = this.options,
-			axis;
-
-		/*jslint unused: false*/
-		axis = new Axis(this, merge(options, {
-			index: this[key].length,
-			isX: isX
-		}));
-		/*jslint unused: true*/
-
-		// Push the new axis options to the chart options
-		chartOptions[key] = splat(chartOptions[key] || {});
-		chartOptions[key].push(options);
-
-		if (pick(redraw, true)) {
-			this.redraw(animation);
-		}
-	},
-
-	/**
-	 * Dim the chart and show a loading text or symbol
-	 * @param {String} str An optional text to show in the loading label instead of the default one
-	 */
-	showLoading: function (str) {
-		var chart = this,
-			options = chart.options,
-			loadingDiv = chart.loadingDiv;
-
-		var loadingOptions = options.loading;
-
-		// create the layer at the first call
-		if (!loadingDiv) {
-			chart.loadingDiv = loadingDiv = createElement(DIV, {
-				className: PREFIX + 'loading'
-			}, extend(loadingOptions.style, {
-				zIndex: 10,
-				display: NONE
-			}), chart.container);
-
-			chart.loadingSpan = createElement(
-				'span',
-				null,
-				loadingOptions.labelStyle,
-				loadingDiv
-			);
-
-		}
-
-		// update text
-		chart.loadingSpan.innerHTML = str || options.lang.loading;
-
-		// show it
-		if (!chart.loadingShown) {
-			css(loadingDiv, {
-				opacity: 0,
-				display: '',
-				left: chart.plotLeft + PX,
-				top: chart.plotTop + PX,
-				width: chart.plotWidth + PX,
-				height: chart.plotHeight + PX
-			});
-			animate(loadingDiv, {
-				opacity: loadingOptions.style.opacity
-			}, {
-				duration: loadingOptions.showDuration || 0
-			});
-			chart.loadingShown = true;
-		}
-	},
-
-	/**
-	 * Hide the loading layer
-	 */
-	hideLoading: function () {
-		var options = this.options,
-			loadingDiv = this.loadingDiv;
-
-		if (loadingDiv) {
-			animate(loadingDiv, {
-				opacity: 0
-			}, {
-				duration: options.loading.hideDuration || 100,
-				complete: function () {
-					css(loadingDiv, { display: NONE });
-				}
-			});
-		}
-		this.loadingShown = false;
-	}
-});
-
-// extend the Point prototype for dynamic methods
-extend(Point.prototype, {
-	/**
-	 * Update the point with new options (typically x/y data) and optionally redraw the series.
-	 *
-	 * @param {Object} options Point options as defined in the series.data array
-	 * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 *
-	 */
-	update: function (options, redraw, animation) {
-		var point = this,
-			series = point.series,
-			graphic = point.graphic,
-			i,
-			data = series.data,
-			chart = series.chart,
-			seriesOptions = series.options;
-
-		redraw = pick(redraw, true);
-
-		// fire the event with a default handler of doing the update
-		point.firePointEvent('update', { options: options }, function () {
-
-			point.applyOptions(options);
-
-			// update visuals
-			if (isObject(options)) {
-				series.getAttribs();
-				if (graphic) {
-					if (options && options.marker && options.marker.symbol) {
-						point.graphic = graphic.destroy();
-					} else {
-						graphic.attr(point.pointAttr[point.state || '']);
-					}
-				}
-				if (options && options.dataLabels && point.dataLabel) { // #2468
-					point.dataLabel = point.dataLabel.destroy();
-				}
-			}
-
-			// record changes in the parallel arrays
-			i = inArray(point, data);
-			series.updateParallelArrays(point, i);
-
-			seriesOptions.data[i] = point.options;
-
-			// redraw
-			series.isDirty = series.isDirtyData = true;
-			if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
-				chart.isDirtyBox = true;
-			}
-
-			if (seriesOptions.legendType === 'point') { // #1831, #1885
-				chart.legend.destroyItem(point);
-			}
-			if (redraw) {
-				chart.redraw(animation);
-			}
-		});
-	},
-
-	/**
-	 * Remove a point and optionally redraw the series and if necessary the axes
-	 * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 */
-	remove: function (redraw, animation) {
-		var point = this,
-			series = point.series,
-			points = series.points,
-			chart = series.chart,
-			i,
-			data = series.data;
-
-		setAnimation(animation, chart);
-		redraw = pick(redraw, true);
-
-		// fire the event with a default handler of removing the point
-		point.firePointEvent('remove', null, function () {
-
-			// splice all the parallel arrays
-			i = inArray(point, data);
-			if (data.length === points.length) {
-				points.splice(i, 1);
-			}
-			data.splice(i, 1);
-			series.options.data.splice(i, 1);
-			series.updateParallelArrays(point, 'splice', i, 1);
-
-			point.destroy();
-
-			// redraw
-			series.isDirty = true;
-			series.isDirtyData = true;
-			if (redraw) {
-				chart.redraw();
-			}
-		});
-	}
-});
-
-// Extend the series prototype for dynamic methods
-extend(Series.prototype, {
-	/**
-	 * Add a point dynamically after chart load time
-	 * @param {Object} options Point options as given in series.data
-	 * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
-	 * @param {Boolean} shift If shift is true, a point is shifted off the start
-	 *    of the series as one is appended to the end.
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 */
-	addPoint: function (options, redraw, shift, animation) {
-		var series = this,
-			seriesOptions = series.options,
-			data = series.data,
-			graph = series.graph,
-			area = series.area,
-			chart = series.chart,
-			names = series.xAxis && series.xAxis.names,
-			currentShift = (graph && graph.shift) || 0,
-			dataOptions = seriesOptions.data,
-			point,
-			isInTheMiddle,
-			xData = series.xData,
-			x,
-			i;
-
-		setAnimation(animation, chart);
-
-		// Make graph animate sideways
-		if (shift) {
-			each([graph, area, series.graphNeg, series.areaNeg], function (shape) {
-				if (shape) {
-					shape.shift = currentShift + 1;
-				}
-			});
-		}
-		if (area) {
-			area.isArea = true; // needed in animation, both with and without shift
-		}
-
-		// Optional redraw, defaults to true
-		redraw = pick(redraw, true);
-
-		// Get options and push the point to xData, yData and series.options. In series.generatePoints
-		// the Point instance will be created on demand and pushed to the series.data array.
-		point = { series: series };
-		series.pointClass.prototype.applyOptions.apply(point, [options]);
-		x = point.x;
-
-		// Get the insertion point
-		i = xData.length;
-		if (series.requireSorting && x < xData[i - 1]) {
-			isInTheMiddle = true;
-			while (i && xData[i - 1] > x) {
-				i--;
-			}
-		}
-
-		series.updateParallelArrays(point, 'splice', i, 0, 0); // insert undefined item
-		series.updateParallelArrays(point, i); // update it
-
-		if (names) {
-			names[x] = point.name;
-		}
-		dataOptions.splice(i, 0, options);
-
-		if (isInTheMiddle) {
-			series.data.splice(i, 0, null);
-			series.processData();
-		}
-
-		// Generate points to be added to the legend (#1329)
-		if (seriesOptions.legendType === 'point') {
-			series.generatePoints();
-		}
-
-		// Shift the first point off the parallel arrays
-		// todo: consider series.removePoint(i) method
-		if (shift) {
-			if (data[0] && data[0].remove) {
-				data[0].remove(false);
-			} else {
-				data.shift();
-				series.updateParallelArrays(point, 'shift');
-
-				dataOptions.shift();
-			}
-		}
-
-		// redraw
-		series.isDirty = true;
-		series.isDirtyData = true;
-		if (redraw) {
-			series.getAttribs(); // #1937
-			chart.redraw();
-		}
-	},
-
-	/**
-	 * Remove a series and optionally redraw the chart
-	 *
-	 * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
-	 * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
-	 *    configuration
-	 */
-
-	remove: function (redraw, animation) {
-		var series = this,
-			chart = series.chart;
-		redraw = pick(redraw, true);
-
-		if (!series.isRemoving) {  /* prevent triggering native event in jQuery
-				(calling the remove function from the remove event) */
-			series.isRemoving = true;
-
-			// fire the event with a default handler of removing the point
-			fireEvent(series, 'remove', null, function () {
-
-
-				// destroy elements
-				series.destroy();
-
-
-				// redraw
-				chart.isDirtyLegend = chart.isDirtyBox = true;
-				chart.linkSeries();
-
-				if (redraw) {
-					chart.redraw(animation);
-				}
-			});
-
-		}
-		series.isRemoving = false;
-	},
-
-	/**
-	 * Update the series with a new set of options
-	 */
-	update: function (newOptions, redraw) {
-		var chart = this.chart,
-			// must use user options when changing type because this.options is merged
-			// in with type specific plotOptions
-			oldOptions = this.userOptions,
-			oldType = this.type,
-			proto = seriesTypes[oldType].prototype,
-			n;
-
-		// Do the merge, with some forced options
-		newOptions = merge(oldOptions, {
-			animation: false,
-			index: this.index,
-			pointStart: this.xData[0] // when updating after addPoint
-		}, { data: this.options.data }, newOptions);
-
-		// Destroy the series and reinsert methods from the type prototype
-		this.remove(false);
-		for (n in proto) { // Overwrite series-type specific methods (#2270)
-			if (proto.hasOwnProperty(n)) {
-				this[n] = UNDEFINED;
-			}
-		}
-		extend(this, seriesTypes[newOptions.type || oldType].prototype);
-
-
-		this.init(chart, newOptions);
-		if (pick(redraw, true)) {
-			chart.redraw(false);
-		}
-	}
-});
-
-// Extend the Axis.prototype for dynamic methods
-extend(Axis.prototype, {
-
-	/**
-	 * Update the axis with a new options structure
-	 */
-	update: function (newOptions, redraw) {
-		var chart = this.chart;
-
-		newOptions = chart.options[this.coll][this.options.index] = merge(this.userOptions, newOptions);
-
-		this.destroy(true);
-		this._addedPlotLB = UNDEFINED; // #1611, #2887
-
-		this.init(chart, extend(newOptions, { events: UNDEFINED }));
-
-		chart.isDirtyBox = true;
-		if (pick(redraw, true)) {
-			chart.redraw();
-		}
-	},
-
-	/**
-     * Remove the axis from the chart
-     */
-	remove: function (redraw) {
-		var chart = this.chart,
-			key = this.coll, // xAxis or yAxis
-			axisSeries = this.series,
-			i = axisSeries.length;
-
-		// Remove associated series (#2687)
-		while (i--) {
-			if (axisSeries[i]) {
-				axisSeries[i].remove(false);
-			}
-		}
-
-		// Remove the axis
-		erase(chart.axes, this);
-		erase(chart[key], this);
-		chart.options[key].splice(this.options.index, 1);
-		each(chart[key], function (axis, i) { // Re-index, #1706
-			axis.options.index = i;
-		});
-		this.destroy();
-		chart.isDirtyBox = true;
-
-		if (pick(redraw, true)) {
-			chart.redraw();
-		}
-	},
-
-	/**
-	 * Update the axis title by options
-	 */
-	setTitle: function (newTitleOptions, redraw) {
-		this.update({ title: newTitleOptions }, redraw);
-	},
-
-	/**
-	 * Set new axis categories and optionally redraw
-	 * @param {Array} categories
-	 * @param {Boolean} redraw
-	 */
-	setCategories: function (categories, redraw) {
-		this.update({ categories: categories }, redraw);
-	}
-
-});
-
-
-/**
- * LineSeries object
- */
-var LineSeries = extendClass(Series);
-seriesTypes.line = LineSeries;
-
-/**
- * Set the default options for area
- */
-defaultPlotOptions.area = merge(defaultSeriesOptions, {
-	threshold: 0
-	// trackByArea: false,
-	// lineColor: null, // overrides color, but lets fillColor be unaltered
-	// fillOpacity: 0.75,
-	// fillColor: null
-});
-
-/**
- * AreaSeries object
- */
-var AreaSeries = extendClass(Series, {
-	type: 'area',
-	/**
-	 * For stacks, don't split segments on null values. Instead, draw null values with 
-	 * no marker. Also insert dummy points for any X position that exists in other series
-	 * in the stack.
-	 */ 
-	getSegments: function () {
-		var segments = [],
-			segment = [],
-			keys = [],
-			xAxis = this.xAxis,
-			yAxis = this.yAxis,
-			stack = yAxis.stacks[this.stackKey],
-			pointMap = {},
-			plotX,
-			plotY,
-			points = this.points,
-			connectNulls = this.options.connectNulls,
-			val,
-			i,
-			x;
-
-		if (this.options.stacking && !this.cropped) { // cropped causes artefacts in Stock, and perf issue
-			// Create a map where we can quickly look up the points by their X value.
-			for (i = 0; i < points.length; i++) {
-				pointMap[points[i].x] = points[i];
-			}
-
-			// Sort the role_keys (#1651)
-			for (x in stack) {
-				if (stack[x].total !== null) { // nulled after switching between grouping and not (#1651, #2336)
-					keys.push(+x);
-				}
-			}
-			keys.sort(function (a, b) {
-				return a - b;
-			});
-
-			each(keys, function (x) {
-				if (connectNulls && (!pointMap[x] || pointMap[x].y === null)) { // #1836
-					return;
-
-				// The point exists, push it to the segment
-				} else if (pointMap[x]) {
-					segment.push(pointMap[x]);
-
-				// There is no point for this X value in this series, so we 
-				// insert a dummy point in order for the areas to be drawn
-				// correctly.
-				} else {
-					plotX = xAxis.translate(x);
-					val = stack[x].percent ? (stack[x].total ? stack[x].cum * 100 / stack[x].total : 0) : stack[x].cum; // #1991
-					plotY = yAxis.toPixels(val, true);
-					segment.push({ 
-						y: null, 
-						plotX: plotX,
-						clientX: plotX, 
-						plotY: plotY, 
-						yBottom: plotY,
-						onMouseOver: noop
-					});
-				}
-			});
-
-			if (segment.length) {
-				segments.push(segment);
-			}
-
-		} else {
-			Series.prototype.getSegments.call(this);
-			segments = this.segments;
-		}
-
-		this.segments = segments;
-	},
-	
-	/**
-	 * Extend the base Series getSegmentPath method by adding the path for the area.
-	 * This path is pushed to the series.areaPath property.
-	 */
-	getSegmentPath: function (segment) {
-		
-		var segmentPath = Series.prototype.getSegmentPath.call(this, segment), // call base method
-			areaSegmentPath = [].concat(segmentPath), // work on a copy for the area path
-			i,
-			options = this.options,
-			segLength = segmentPath.length,
-			translatedThreshold = this.yAxis.getThreshold(options.threshold), // #2181
-			yBottom;
-		
-		if (segLength === 3) { // for animation from 1 to two points
-			areaSegmentPath.push(L, segmentPath[1], segmentPath[2]);
-		}
-		if (options.stacking && !this.closedStacks) {
-			
-			// Follow stack back. Todo: implement areaspline. A general solution could be to 
-			// reverse the entire graphPath of the previous series, though may be hard with
-			// splines and with series with different extremes
-			for (i = segment.length - 1; i >= 0; i--) {
-
-				yBottom = pick(segment[i].yBottom, translatedThreshold);
-			
-				// step line?
-				if (i < segment.length - 1 && options.step) {
-					areaSegmentPath.push(segment[i + 1].plotX, yBottom);
-				}
-				
-				areaSegmentPath.push(segment[i].plotX, yBottom);
-			}
-
-		} else { // follow zero line back
-			this.closeSegment(areaSegmentPath, segment, translatedThreshold);
-		}
-		this.areaPath = this.areaPath.concat(areaSegmentPath);
-		return segmentPath;
-	},
-	
-	/**
-	 * Extendable method to close the segment path of an area. This is overridden in polar 
-	 * charts.
-	 */
-	closeSegment: function (path, segment, translatedThreshold) {
-		path.push(
-			L,
-			segment[segment.length - 1].plotX,
-			translatedThreshold,
-			L,
-			segment[0].plotX,
-			translatedThreshold
-		);
-	},
-	
-	/**
-	 * Draw the graph and the underlying area. This method calls the Series base
-	 * function and adds the area. The areaPath is calculated in the getSegmentPath
-	 * method called from Series.prototype.drawGraph.
-	 */
-	drawGraph: function () {
-		
-		// Define or reset areaPath
-		this.areaPath = [];
-		
-		// Call the base method
-		Series.prototype.drawGraph.apply(this);
-		
-		// Define local variables
-		var series = this,
-			areaPath = this.areaPath,
-			options = this.options,
-			negativeColor = options.negativeColor,
-			negativeFillColor = options.negativeFillColor,
-			props = [['area', this.color, options.fillColor]]; // area name, main color, fill color
-		
-		if (negativeColor || negativeFillColor) {
-			props.push(['areaNeg', negativeColor, negativeFillColor]);
-		}
-		
-		each(props, function (prop) {
-			var areaKey = prop[0],
-				area = series[areaKey];
-				
-			// Create or update the area
-			if (area) { // update
-				area.animate({ d: areaPath });
-	
-			} else { // create
-				series[areaKey] = series.chart.renderer.path(areaPath)
-					.attr({
-						fill: pick(
-							prop[2],
-							Color(prop[1]).setOpacity(pick(options.fillOpacity, 0.75)).get()
-						),
-						zIndex: 0 // #1069
-					}).add(series.group);
-			}
-		});
-	},
-
-	drawLegendSymbol: LegendSymbolMixin.drawRectangle
-});
-
-seriesTypes.area = AreaSeries;
-/**
- * Set the default options for spline
- */
-defaultPlotOptions.spline = merge(defaultSeriesOptions);
-
-/**
- * SplineSeries object
- */
-var SplineSeries = extendClass(Series, {
-	type: 'spline',
-
-	/**
-	 * Get the spline segment from a given point's previous neighbour to the given point
-	 */
-	getPointSpline: function (segment, point, i) {
-		var smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc
-			denom = smoothing + 1,
-			plotX = point.plotX,
-			plotY = point.plotY,
-			lastPoint = segment[i - 1],
-			nextPoint = segment[i + 1],
-			leftContX,
-			leftContY,
-			rightContX,
-			rightContY,
-			ret;
-
-		// find control points
-		if (lastPoint && nextPoint) {
-		
-			var lastX = lastPoint.plotX,
-				lastY = lastPoint.plotY,
-				nextX = nextPoint.plotX,
-				nextY = nextPoint.plotY,
-				correction;
-
-			leftContX = (smoothing * plotX + lastX) / denom;
-			leftContY = (smoothing * plotY + lastY) / denom;
-			rightContX = (smoothing * plotX + nextX) / denom;
-			rightContY = (smoothing * plotY + nextY) / denom;
-
-			// have the two control points make a straight line through main point
-			correction = ((rightContY - leftContY) * (rightContX - plotX)) /
-				(rightContX - leftContX) + plotY - rightContY;
-
-			leftContY += correction;
-			rightContY += correction;
-
-			// to prevent false extremes, check that control points are between
-			// neighbouring points' y values
-			if (leftContY > lastY && leftContY > plotY) {
-				leftContY = mathMax(lastY, plotY);
-				rightContY = 2 * plotY - leftContY; // mirror of left control point
-			} else if (leftContY < lastY && leftContY < plotY) {
-				leftContY = mathMin(lastY, plotY);
-				rightContY = 2 * plotY - leftContY;
-			}
-			if (rightContY > nextY && rightContY > plotY) {
-				rightContY = mathMax(nextY, plotY);
-				leftContY = 2 * plotY - rightContY;
-			} else if (rightContY < nextY && rightContY < plotY) {
-				rightContY = mathMin(nextY, plotY);
-				leftContY = 2 * plotY - rightContY;
-			}
-
-			// record for drawing in next point
-			point.rightContX = rightContX;
-			point.rightContY = rightContY;
-
-		}
-		
-		// Visualize control points for debugging
-		/*
-		if (leftContX) {
-			this.chart.renderer.circle(leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, 2)
-				.attr({
-					stroke: 'red',
-					'stroke-width': 1,
-					fill: 'none'
-				})
-				.add();
-			this.chart.renderer.path(['M', leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop,
-				'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
-				.attr({
-					stroke: 'red',
-					'stroke-width': 1
-				})
-				.add();
-			this.chart.renderer.circle(rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, 2)
-				.attr({
-					stroke: 'green',
-					'stroke-width': 1,
-					fill: 'none'
-				})
-				.add();
-			this.chart.renderer.path(['M', rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop,
-				'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
-				.attr({
-					stroke: 'green',
-					'stroke-width': 1
-				})
-				.add();
-		}
-		*/
-
-		// moveTo or lineTo
-		if (!i) {
-			ret = [M, plotX, plotY];
-		} else { // curve from last point to this
-			ret = [
-				'C',
-				lastPoint.rightContX || lastPoint.plotX,
-				lastPoint.rightContY || lastPoint.plotY,
-				leftContX || plotX,
-				leftContY || plotY,
-				plotX,
-				plotY
-			];
-			lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
-		}
-		return ret;
-	}
-});
-seriesTypes.spline = SplineSeries;
-
-/**
- * Set the default options for areaspline
- */
-defaultPlotOptions.areaspline = merge(defaultPlotOptions.area);
-
-/**
- * AreaSplineSeries object
- */
-var areaProto = AreaSeries.prototype,
-	AreaSplineSeries = extendClass(SplineSeries, {
-		type: 'areaspline',
-		closedStacks: true, // instead of following the previous graph back, follow the threshold back
-		
-		// Mix in methods from the area series
-		getSegmentPath: areaProto.getSegmentPath,
-		closeSegment: areaProto.closeSegment,
-		drawGraph: areaProto.drawGraph,
-		drawLegendSymbol: LegendSymbolMixin.drawRectangle
-	});
-
-seriesTypes.areaspline = AreaSplineSeries;
-
-/**
- * Set the default options for column
- */
-defaultPlotOptions.column = merge(defaultSeriesOptions, {
-	borderColor: '#FFFFFF',
-	//borderWidth: 1,
-	borderRadius: 0,
-	//colorByPoint: undefined,
-	groupPadding: 0.2,
-	//grouping: true,
-	marker: null, // point options are specified in the base options
-	pointPadding: 0.1,
-	//pointWidth: null,
-	minPointLength: 0,
-	cropThreshold: 50, // when there are more points, they will not animate out of the chart on xAxis.setExtremes
-	pointRange: null, // null means auto, meaning 1 in a categorized axis and least distance between points if not categories
-	states: {
-		hover: {
-			brightness: 0.1,
-			shadow: false,
-			halo: false
-		},
-		select: {
-			color: '#C0C0C0',
-			borderColor: '#000000',
-			shadow: false
-		}
-	},
-	dataLabels: {
-		align: null, // auto
-		verticalAlign: null, // auto
-		y: null
-	},
-	stickyTracking: false,
-	tooltip: {
-		distance: 6
-	},
-	threshold: 0
-});
-
-/**
- * ColumnSeries object
- */
-var ColumnSeries = extendClass(Series, {
-	type: 'column',
-	pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
-		stroke: 'borderColor',
-		fill: 'color',
-		r: 'borderRadius'
-	},
-	cropShoulder: 0,
-	trackerGroups: ['group', 'dataLabelsGroup'],
-	negStacks: true, // use separate negative stacks, unlike area stacks where a negative 
-		// point is substracted from previous (#1910)
-	
-	/**
-	 * Initialize the series
-	 */
-	init: function () {
-		Series.prototype.init.apply(this, arguments);
-
-		var series = this,
-			chart = series.chart;
-
-		// if the series is added dynamically, force redraw of other
-		// series affected by a new column
-		if (chart.hasRendered) {
-			each(chart.series, function (otherSeries) {
-				if (otherSeries.type === series.type) {
-					otherSeries.isDirty = true;
-				}
-			});
-		}
-	},
-
-	/**
-	 * Return the width and x offset of the columns adjusted for grouping, groupPadding, pointPadding,
-	 * pointWidth etc. 
-	 */
-	getColumnMetrics: function () {
-
-		var series = this,
-			options = series.options,
-			xAxis = series.xAxis,
-			yAxis = series.yAxis,
-			reversedXAxis = xAxis.reversed,
-			stackKey,
-			stackGroups = {},
-			columnIndex,
-			columnCount = 0;
-
-		// Get the total number of column type series.
-		// This is called on every series. Consider moving this logic to a
-		// chart.orderStacks() function and call it on init, addSeries and removeSeries
-		if (options.grouping === false) {
-			columnCount = 1;
-		} else {
-			each(series.chart.series, function (otherSeries) {
-				var otherOptions = otherSeries.options,
-					otherYAxis = otherSeries.yAxis;
-				if (otherSeries.type === series.type && otherSeries.visible &&
-						yAxis.len === otherYAxis.len && yAxis.pos === otherYAxis.pos) {  // #642, #2086
-					if (otherOptions.stacking) {
-						stackKey = otherSeries.stackKey;
-						if (stackGroups[stackKey] === UNDEFINED) {
-							stackGroups[stackKey] = columnCount++;
-						}
-						columnIndex = stackGroups[stackKey];
-					} else if (otherOptions.grouping !== false) { // #1162
-						columnIndex = columnCount++;
-					}
-					otherSeries.columnIndex = columnIndex;
-				}
-			});
-		}
-
-		var categoryWidth = mathMin(
-				mathAbs(xAxis.transA) * (xAxis.ordinalSlope || options.pointRange || xAxis.closestPointRange || xAxis.tickInterval || 1), // #2610
-				xAxis.len // #1535
-			),
-			groupPadding = categoryWidth * options.groupPadding,
-			groupWidth = categoryWidth - 2 * groupPadding,
-			pointOffsetWidth = groupWidth / columnCount,
-			optionPointWidth = options.pointWidth,
-			pointPadding = defined(optionPointWidth) ? (pointOffsetWidth - optionPointWidth) / 2 :
-				pointOffsetWidth * options.pointPadding,
-			pointWidth = pick(optionPointWidth, pointOffsetWidth - 2 * pointPadding), // exact point width, used in polar charts
-			colIndex = (reversedXAxis ? 
-				columnCount - (series.columnIndex || 0) : // #1251
-				series.columnIndex) || 0,
-			pointXOffset = pointPadding + (groupPadding + colIndex *
-				pointOffsetWidth - (categoryWidth / 2)) *
-				(reversedXAxis ? -1 : 1);
-
-		// Save it for reading in linked series (Error bars particularly)
-		return (series.columnMetrics = { 
-			width: pointWidth, 
-			offset: pointXOffset 
-		});
-			
-	},
-
-	/**
-	 * Translate each point to the plot area coordinate system and find shape positions
-	 */
-	translate: function () {
-		var series = this,
-			chart = series.chart,
-			options = series.options,
-			borderWidth = series.borderWidth = pick(
-				options.borderWidth, 
-				series.activePointCount > 0.5 * series.xAxis.len ? 0 : 1
-			),
-			yAxis = series.yAxis,
-			threshold = options.threshold,
-			translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold),
-			minPointLength = pick(options.minPointLength, 5),
-			metrics = series.getColumnMetrics(),
-			pointWidth = metrics.width,
-			seriesBarW = series.barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width
-			pointXOffset = series.pointXOffset = metrics.offset,
-			xCrisp = -(borderWidth % 2 ? 0.5 : 0),
-			yCrisp = borderWidth % 2 ? 0.5 : 1;
-
-		if (chart.renderer.isVML && chart.inverted) {
-			yCrisp += 1;
-		}
-
-		Series.prototype.translate.apply(series);
-
-		// record the new values
-		each(series.points, function (point) {
-			var yBottom = pick(point.yBottom, translatedThreshold),
-				plotY = mathMin(mathMax(-999 - yBottom, point.plotY), yAxis.len + 999 + yBottom), // Don't draw too far outside plot area (#1303, #2241)
-				barX = point.plotX + pointXOffset,
-				barW = seriesBarW,
-				barY = mathMin(plotY, yBottom),
-				right,
-				bottom,
-				fromTop,
-				fromLeft,
-				barH = mathMax(plotY, yBottom) - barY;
-
-			// Handle options.minPointLength
-			if (mathAbs(barH) < minPointLength) {
-				if (minPointLength) {
-					barH = minPointLength;
-					barY =
-						mathRound(mathAbs(barY - translatedThreshold) > minPointLength ? // stacked
-							yBottom - minPointLength : // keep position
-							translatedThreshold - (yAxis.translate(point.y, 0, 1, 0, 1) <= translatedThreshold ? minPointLength : 0)); // use exact yAxis.translation (#1485)
-				}
-			}
-
-			// Cache for access in polar
-			point.barX = barX;
-			point.pointWidth = pointWidth;
-
-			// Fix the tooltip on center of grouped columns (#1216)
-			point.tooltipPos = chart.inverted ? [yAxis.len - plotY, series.xAxis.len - barX - barW / 2] : [barX + barW / 2, plotY];
-
-			// Round off to obtain crisp edges
-			fromLeft = mathAbs(barX) < 0.5;
-			right = mathRound(barX + barW) + xCrisp;
-			barX = mathRound(barX) + xCrisp;
-			barW = right - barX;
-
-			fromTop = mathAbs(barY) < 0.5;
-			bottom = mathRound(barY + barH) + yCrisp;
-			barY = mathRound(barY) + yCrisp;
-			barH = bottom - barY;
-
-			// Top and left edges are exceptions
-			if (fromLeft) {
-				barX += 1;
-				barW -= 1;
-			}
-			if (fromTop) {
-				barY -= 1;
-				barH += 1;
-			}
-
-			// Register shape type and arguments to be used in drawPoints
-			point.shapeType = 'rect';
-			point.shapeArgs = {
-				x: barX,
-				y: barY,
-				width: barW,
-				height: barH
-			};
-		});
-
-	},
-
-	getSymbol: noop,
-	
-	/**
-	 * Use a solid rectangle like the area series types
-	 */
-	drawLegendSymbol: LegendSymbolMixin.drawRectangle,
-	
-	
-	/**
-	 * Columns have no graph
-	 */
-	drawGraph: noop,
-
-	/**
-	 * Draw the columns. For bars, the series.group is rotated, so the same coordinates
-	 * apply for columns and bars. This method is inherited by scatter series.
-	 *
-	 */
-	drawPoints: function () {
-		var series = this,
-			chart = this.chart,
-			options = series.options,
-			renderer = chart.renderer,
-			animationLimit = options.animationLimit || 250,
-			shapeArgs,
-			pointAttr,
-			borderAttr;
-
-		// draw the columns
-		each(series.points, function (point) {
-			var plotY = point.plotY,
-				graphic = point.graphic;
-
-			if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
-				shapeArgs = point.shapeArgs;
-				borderAttr = defined(series.borderWidth) ? {
-					'stroke-width': series.borderWidth
-				} : {};
-				pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] || series.pointAttr[NORMAL_STATE];
-				if (graphic) { // update
-					stop(graphic);
-					graphic.attr(borderAttr)[chart.pointCount < animationLimit ? 'animate' : 'attr'](merge(shapeArgs));
-
-				} else {
-					point.graphic = graphic = renderer[point.shapeType](shapeArgs)
-						.attr(pointAttr)
-						.attr(borderAttr)
-						.add(series.group)
-						.shadow(options.shadow, null, options.stacking && !options.borderRadius);
-				}
-
-			} else if (graphic) {
-				point.graphic = graphic.destroy(); // #1269
-			}
-		});
-	},
-
-	/**
-	 * Animate the column heights one by one from zero
-	 * @param {Boolean} init Whether to initialize the animation or run it
-	 */
-	animate: function (init) {
-		var series = this,
-			yAxis = this.yAxis,
-			options = series.options,
-			inverted = this.chart.inverted,
-			attr = {},
-			translatedThreshold;
-
-		if (hasSVG) { // VML is too slow anyway
-			if (init) {
-				attr.scaleY = 0.001;
-				translatedThreshold = mathMin(yAxis.pos + yAxis.len, mathMax(yAxis.pos, yAxis.toPixels(options.threshold)));
-				if (inverted) {
-					attr.translateX = translatedThreshold - yAxis.len;
-				} else {
-					attr.translateY = translatedThreshold;
-				}
-				series.group.attr(attr);
-
-			} else { // run the animation
-				
-				attr.scaleY = 1;
-				attr[inverted ? 'translateX' : 'translateY'] = yAxis.pos;
-				series.group.animate(attr, series.options.animation);
-
-				// delete this function to allow it only once
-				series.animate = null;
-			}
-		}
-	},
-	
-	/**
-	 * Remove this series from the chart
-	 */
-	remove: function () {
-		var series = this,
-			chart = series.chart;
-
-		// column and bar series affects other series of the same type
-		// as they are either stacked or grouped
-		if (chart.hasRendered) {
-			each(chart.series, function (otherSeries) {
-				if (otherSeries.type === series.type) {
-					otherSeries.isDirty = true;
-				}
-			});
-		}
-
-		Series.prototype.remove.apply(series, arguments);
-	}
-});
-seriesTypes.column = ColumnSeries;
-/**
- * Set the default options for bar
- */
-defaultPlotOptions.bar = merge(defaultPlotOptions.column);
-/**
- * The Bar series class
- */
-var BarSeries = extendClass(ColumnSeries, {
-	type: 'bar',
-	inverted: true
-});
-seriesTypes.bar = BarSeries;
-
-/**
- * Set the default options for scatter
- */
-defaultPlotOptions.scatter = merge(defaultSeriesOptions, {
-	lineWidth: 0,
-	tooltip: {
-		headerFormat: '<span style="color:{series.color}">\u25CF</span> <span style="font-size: 10px;"> {series.name}</span><br/>', // docs
-		pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
-	},
-	stickyTracking: false
-});
-
-/**
- * The scatter series class
- */
-var ScatterSeries = extendClass(Series, {
-	type: 'scatter',
-	sorted: false,
-	requireSorting: false,
-	noSharedTooltip: true,
-	trackerGroups: ['markerGroup'],
-	takeOrdinalPosition: false, // #2342
-	singularTooltips: true,
-	drawGraph: function () {
-		if (this.options.lineWidth) {
-			Series.prototype.drawGraph.call(this);
-		}
-	}
-});
-
-seriesTypes.scatter = ScatterSeries;
-
-/**
- * Set the default options for pie
- */
-defaultPlotOptions.pie = merge(defaultSeriesOptions, {
-	borderColor: '#FFFFFF',
-	borderWidth: 1,
-	center: [null, null],
-	clip: false,
-	colorByPoint: true, // always true for pies
-	dataLabels: {
-		// align: null,
-		// connectorWidth: 1,
-		// connectorColor: point.color,
-		// connectorPadding: 5,
-		distance: 30,
-		enabled: true,
-		formatter: function () {
-			return this.point.name;
-		}
-		// softConnector: true,
-		//y: 0
-	},
-	ignoreHiddenPoint: true,
-	//innerSize: 0,
-	legendType: 'point',
-	marker: null, // point options are specified in the base options
-	size: null,
-	showInLegend: false,
-	slicedOffset: 10,
-	states: {
-		hover: {
-			brightness: 0.1,
-			shadow: false
-		}
-	},
-	stickyTracking: false,
-	tooltip: {
-		followPointer: true
-	}
-});
-
-/**
- * Extended point object for pies
- */
-var PiePoint = extendClass(Point, {
-	/**
-	 * Initiate the pie slice
-	 */
-	init: function () {
-
-		Point.prototype.init.apply(this, arguments);
-
-		var point = this,
-			toggleSlice;
-
-		// Disallow negative values (#1530)
-		if (point.y < 0) {
-			point.y = null;
-		}
-
-		//visible: options.visible !== false,
-		extend(point, {
-			visible: point.visible !== false,
-			name: pick(point.name, 'Slice')
-		});
-
-		// add event listener for select
-		toggleSlice = function (e) {
-			point.slice(e.type === 'select');
-		};
-		addEvent(point, 'select', toggleSlice);
-		addEvent(point, 'unselect', toggleSlice);
-
-		return point;
-	},
-
-	/**
-	 * Toggle the visibility of the pie slice
-	 * @param {Boolean} vis Whether to show the slice or not. If undefined, the
-	 *    visibility is toggled
-	 */
-	setVisible: function (vis) {
-		var point = this,
-			series = point.series,
-			chart = series.chart;
-
-		// if called without an argument, toggle visibility
-		point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis;
-		series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
-
-		// Show and hide associated elements
-		each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) {
-			if (point[key]) {
-				point[key][vis ? 'show' : 'hide'](true);
-			}
-		});
-
-		if (point.legendItem) {
-			chart.legend.colorizeItem(point, vis);
-		}
-		
-		// Handle ignore hidden slices
-		if (!series.isDirty && series.options.ignoreHiddenPoint) {
-			series.isDirty = true;
-			chart.redraw();
-		}
-	},
-
-	/**
-	 * Set or toggle whether the slice is cut out from the pie
-	 * @param {Boolean} sliced When undefined, the slice state is toggled
-	 * @param {Boolean} redraw Whether to redraw the chart. True by default.
-	 */
-	slice: function (sliced, redraw, animation) {
-		var point = this,
-			series = point.series,
-			chart = series.chart,
-			translation;
-
-		setAnimation(animation, chart);
-
-		// redraw is true by default
-		redraw = pick(redraw, true);
-
-		// if called without an argument, toggle
-		point.sliced = point.options.sliced = sliced = defined(sliced) ? sliced : !point.sliced;
-		series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
-
-		translation = sliced ? point.slicedTranslation : {
-			translateX: 0,
-			translateY: 0
-		};
-
-		point.graphic.animate(translation);
-		
-		if (point.shadowGroup) {
-			point.shadowGroup.animate(translation);
-		}
-
-	},
-
-	haloPath: function (size) {
-		var shapeArgs = this.shapeArgs,
-			chart = this.series.chart;
-
-		return this.series.chart.renderer.symbols.arc(chart.plotLeft + shapeArgs.x, chart.plotTop + shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
-			innerR: this.shapeArgs.r,
-			start: shapeArgs.start,
-			end: shapeArgs.end
-		});
-	}
-});
-
-/**
- * The Pie series class
- */
-var PieSeries = {
-	type: 'pie',
-	isCartesian: false,
-	pointClass: PiePoint,
-	requireSorting: false,
-	noSharedTooltip: true,
-	trackerGroups: ['group', 'dataLabelsGroup'],
-	axisTypes: [],
-	pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
-		stroke: 'borderColor',
-		'stroke-width': 'borderWidth',
-		fill: 'color'
-	},
-	singularTooltips: true,
-
-	/**
-	 * Pies have one color each point
-	 */
-	getColor: noop,
-
-	/**
-	 * Animate the pies in
-	 */
-	animate: function (init) {
-		var series = this,
-			points = series.points,
-			startAngleRad = series.startAngleRad;
-
-		if (!init) {
-			each(points, function (point) {
-				var graphic = point.graphic,
-					args = point.shapeArgs;
-
-				if (graphic) {
-					// start values
-					graphic.attr({
-						r: series.center[3] / 2, // animate from inner radius (#779)
-						start: startAngleRad,
-						end: startAngleRad
-					});
-
-					// animate
-					graphic.animate({
-						r: args.r,
-						start: args.start,
-						end: args.end
-					}, series.options.animation);
-				}
-			});
-
-			// delete this function to allow it only once
-			series.animate = null;
-		}
-	},
-
-	/**
-	 * Extend the basic setData method by running processData and generatePoints immediately,
-	 * in order to access the points from the legend.
-	 */
-	setData: function (data, redraw, animation, updatePoints) {
-		Series.prototype.setData.call(this, data, false, animation, updatePoints);
-		this.processData();
-		this.generatePoints();
-		if (pick(redraw, true)) {
-			this.chart.redraw(animation);
-		} 
-	},
-
-	/**
-	 * Extend the generatePoints method by adding total and percentage properties to each point
-	 */
-	generatePoints: function () {
-		var i,
-			total = 0,
-			points,
-			len,
-			point,
-			ignoreHiddenPoint = this.options.ignoreHiddenPoint;
-
-		Series.prototype.generatePoints.call(this);
-
-		// Populate local vars
-		points = this.points;
-		len = points.length;
-		
-		// Get the total sum
-		for (i = 0; i < len; i++) {
-			point = points[i];
-			total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
-		}
-		this.total = total;
-
-		// Set each point's properties
-		for (i = 0; i < len; i++) {
-			point = points[i];
-			point.percentage = total > 0 ? (point.y / total) * 100 : 0;
-			point.total = total;
-		}
-		
-	},
-	
-	/**
-	 * Do translation for pie slices
-	 */
-	translate: function (positions) {
-		this.generatePoints();
-		
-		var series = this,
-			cumulative = 0,
-			precision = 1000, // issue #172
-			options = series.options,
-			slicedOffset = options.slicedOffset,
-			connectorOffset = slicedOffset + options.borderWidth,
-			start,
-			end,
-			angle,
-			startAngle = options.startAngle || 0,
-			startAngleRad = series.startAngleRad = mathPI / 180 * (startAngle - 90),
-			endAngleRad = series.endAngleRad = mathPI / 180 * ((pick(options.endAngle, startAngle + 360)) - 90),
-			circ = endAngleRad - startAngleRad, //2 * mathPI,
-			points = series.points,
-			radiusX, // the x component of the radius vector for a given point
-			radiusY,
-			labelDistance = options.dataLabels.distance,
-			ignoreHiddenPoint = options.ignoreHiddenPoint,
-			i,
-			len = points.length,
-			point;
-
-		// Get positions - either an integer or a percentage string must be given.
-		// If positions are passed as a parameter, we're in a recursive loop for adjusting
-		// space for data labels.
-		if (!positions) {
-			series.center = positions = series.getCenter();
-		}
-
-		// utility for getting the x value from a given y, used for anticollision logic in data labels
-		series.getX = function (y, left) {
-
-			angle = math.asin(mathMin((y - positions[1]) / (positions[2] / 2 + labelDistance), 1));
-
-			return positions[0] +
-				(left ? -1 : 1) *
-				(mathCos(angle) * (positions[2] / 2 + labelDistance));
-		};
-
-		// Calculate the geometry for each point
-		for (i = 0; i < len; i++) {
-			
-			point = points[i];
-			
-			// set start and end angle
-			start = startAngleRad + (cumulative * circ);
-			if (!ignoreHiddenPoint || point.visible) {
-				cumulative += point.percentage / 100;
-			}
-			end = startAngleRad + (cumulative * circ);
-
-			// set the shape
-			point.shapeType = 'arc';
-			point.shapeArgs = {
-				x: positions[0],
-				y: positions[1],
-				r: positions[2] / 2,
-				innerR: positions[3] / 2,
-				start: mathRound(start * precision) / precision,
-				end: mathRound(end * precision) / precision
-			};
-
-			// The angle must stay within -90 and 270 (#2645)
-			angle = (end + start) / 2;
-			if (angle > 1.5 * mathPI) {
-				angle -= 2 * mathPI;
-			} else if (angle < -mathPI / 2) {
-				angle += 2 * mathPI;
-			}
-
-			// Center for the sliced out slice
-			point.slicedTranslation = {
-				translateX: mathRound(mathCos(angle) * slicedOffset),
-				translateY: mathRound(mathSin(angle) * slicedOffset)
-			};
-
-			// set the anchor point for tooltips
-			radiusX = mathCos(angle) * positions[2] / 2;
-			radiusY = mathSin(angle) * positions[2] / 2;
-			point.tooltipPos = [
-				positions[0] + radiusX * 0.7,
-				positions[1] + radiusY * 0.7
-			];
-			
-			point.half = angle < -mathPI / 2 || angle > mathPI / 2 ? 1 : 0;
-			point.angle = angle;
-
-			// set the anchor point for data labels
-			connectorOffset = mathMin(connectorOffset, labelDistance / 2); // #1678
-			point.labelPos = [
-				positions[0] + radiusX + mathCos(angle) * labelDistance, // first break of connector
-				positions[1] + radiusY + mathSin(angle) * labelDistance, // a/a
-				positions[0] + radiusX + mathCos(angle) * connectorOffset, // second break, right outside pie
-				positions[1] + radiusY + mathSin(angle) * connectorOffset, // a/a
-				positions[0] + radiusX, // landing point for connector
-				positions[1] + radiusY, // a/a
-				labelDistance < 0 ? // alignment
-					'center' :
-					point.half ? 'right' : 'left', // alignment
-				angle // center angle
-			];
-
-		}
-	},
-	
-	drawGraph: null,
-
-	/**
-	 * Draw the data points
-	 */
-	drawPoints: function () {
-		var series = this,
-			chart = series.chart,
-			renderer = chart.renderer,
-			groupTranslation,
-			//center,
-			graphic,
-			//group,
-			shadow = series.options.shadow,
-			shadowGroup,
-			shapeArgs;
-
-		if (shadow && !series.shadowGroup) {
-			series.shadowGroup = renderer.g('shadow')
-				.add(series.group);
-		}
-
-		// draw the slices
-		each(series.points, function (point) {
-			graphic = point.graphic;
-			shapeArgs = point.shapeArgs;
-			shadowGroup = point.shadowGroup;
-
-			// put the shadow behind all points
-			if (shadow && !shadowGroup) {
-				shadowGroup = point.shadowGroup = renderer.g('shadow')
-					.add(series.shadowGroup);
-			}
-
-			// if the point is sliced, use special translation, else use plot area traslation
-			groupTranslation = point.sliced ? point.slicedTranslation : {
-				translateX: 0,
-				translateY: 0
-			};
-
-			//group.translate(groupTranslation[0], groupTranslation[1]);
-			if (shadowGroup) {
-				shadowGroup.attr(groupTranslation);
-			}
-
-			// draw the slice
-			if (graphic) {
-				graphic.animate(extend(shapeArgs, groupTranslation));
-			} else {
-				point.graphic = graphic = renderer[point.shapeType](shapeArgs)
-					.setRadialReference(series.center)
-					.attr(
-						point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]
-					)
-					.attr({ 
-						'stroke-linejoin': 'round'
-						//zIndex: 1 // #2722 (reversed)
-					})
-					.attr(groupTranslation)
-					.add(series.group)
-					.shadow(shadow, shadowGroup);	
-			}
-
-			// detect point specific visibility (#2430)
-			if (point.visible !== undefined) {
-				point.setVisible(point.visible);
-			}
-
-		});
-
-	},
-
-	/**
-	 * Utility for sorting data labels
-	 */
-	sortByAngle: function (points, sign) {
-		points.sort(function (a, b) {
-			return a.angle !== undefined && (b.angle - a.angle) * sign;
-		});
-	},		
-
-	/**
-	 * Use a simple symbol from LegendSymbolMixin
-	 */
-	drawLegendSymbol: LegendSymbolMixin.drawRectangle,
-
-	/**
-	 * Use the getCenter method from drawLegendSymbol
-	 */
-	getCenter: CenteredSeriesMixin.getCenter,
-
-	/**
-	 * Pies don't have point marker symbols
-	 */
-	getSymbol: noop
-
-};
-PieSeries = extendClass(Series, PieSeries);
-seriesTypes.pie = PieSeries;
-
-/**
- * Draw the data labels
- */
-Series.prototype.drawDataLabels = function () {
-
-	var series = this,
-		seriesOptions = series.options,
-		cursor = seriesOptions.cursor,
-		options = seriesOptions.dataLabels,
-		points = series.points,
-		pointOptions,
-		generalOptions,
-		str,
-		dataLabelsGroup;
-
-	if (options.enabled || series._hasPointLabels) {
-
-		// Process default alignment of data labels for columns
-		if (series.dlProcessOptions) {
-			series.dlProcessOptions(options);
-		}
-
-		// Create a separate group for the data labels to avoid rotation
-		dataLabelsGroup = series.plotGroup(
-			'dataLabelsGroup',
-			'data-labels',
-			HIDDEN,
-			options.zIndex || 6
-		);
-
-		if (!series.hasRendered && pick(options.defer, true)) {
-			dataLabelsGroup.attr({ opacity: 0 });
-			addEvent(series, 'afterAnimate', function () {
-				series.dataLabelsGroup.show()[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: 200 });
-			});
-		}
-
-		// Make the labels for each point
-		generalOptions = options;
-		each(points, function (point) {
-
-			var enabled,
-				dataLabel = point.dataLabel,
-				labelConfig,
-				attr,
-				name,
-				rotation,
-				connector = point.connector,
-				isNew = true;
-
-			// Determine if each data label is enabled
-			pointOptions = point.options && point.options.dataLabels;
-			enabled = pick(pointOptions && pointOptions.enabled, generalOptions.enabled); // #2282
-
-
-			// If the point is outside the plot area, destroy it. #678, #820
-			if (dataLabel && !enabled) {
-				point.dataLabel = dataLabel.destroy();
-
-			// Individual labels are disabled if the are explicitly disabled
-			// in the point options, or if they fall outside the plot area.
-			} else if (enabled) {
-
-				// Create individual options structure that can be extended without
-				// affecting others
-				options = merge(generalOptions, pointOptions);
-
-				rotation = options.rotation;
-
-				// Get the string
-				labelConfig = point.getLabelConfig();
-				str = options.format ?
-					format(options.format, labelConfig) :
-					options.formatter.call(labelConfig, options);
-
-				// Determine the color
-				options.style.color = pick(options.color, options.style.color, series.color, 'black');
-
-
-				// update existing label
-				if (dataLabel) {
-
-					if (defined(str)) {
-						dataLabel
-							.attr({
-								text: str
-							});
-						isNew = false;
-
-					} else { // #1437 - the label is shown conditionally
-						point.dataLabel = dataLabel = dataLabel.destroy();
-						if (connector) {
-							point.connector = connector.destroy();
-						}
-					}
-
-				// create new label
-				} else if (defined(str)) {
-					attr = {
-						//align: align,
-						fill: options.backgroundColor,
-						stroke: options.borderColor,
-						'stroke-width': options.borderWidth,
-						r: options.borderRadius || 0,
-						rotation: rotation,
-						padding: options.padding,
-						zIndex: 1
-					};
-					// Remove unused attributes (#947)
-					for (name in attr) {
-						if (attr[name] === UNDEFINED) {
-							delete attr[name];
-						}
-					}
-
-					dataLabel = point.dataLabel = series.chart.renderer[rotation ? 'text' : 'label']( // labels don't support rotation
-						str,
-						0,
-						-999,
-						null,
-						null,
-						null,
-						options.useHTML
-					)
-					.attr(attr)
-					.css(extend(options.style, cursor && { cursor: cursor }))
-					.add(dataLabelsGroup)
-					.shadow(options.shadow);
-
-				}
-
-				if (dataLabel) {
-					// Now the data label is created and placed at 0,0, so we need to align it
-					series.alignDataLabel(point, dataLabel, options, null, isNew);
-				}
-			}
-		});
-	}
-};
-
-/**
- * Align each individual data label
- */
-Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
-	var chart = this.chart,
-		inverted = chart.inverted,
-		plotX = pick(point.plotX, -999),
-		plotY = pick(point.plotY, -999),
-		bBox = dataLabel.getBBox(),
-		// Math.round for rounding errors (#2683), alignTo to allow column labels (#2700)
-		visible = this.visible && (point.series.forceDL || chart.isInsidePlot(plotX, mathRound(plotY), inverted) ||
-			(alignTo && chart.isInsidePlot(plotX, inverted ? alignTo.x + 1 : alignTo.y + alignTo.height - 1, inverted))),
-		alignAttr; // the final position;
-
-	if (visible) {
-
-		// The alignment box is a singular point
-		alignTo = extend({
-			x: inverted ? chart.plotWidth - plotY : plotX,
-			y: mathRound(inverted ? chart.plotHeight - plotX : plotY),
-			width: 0,
-			height: 0
-		}, alignTo);
-
-		// Add the text size for alignment calculation
-		extend(options, {
-			width: bBox.width,
-			height: bBox.height
-		});
-
-		// Allow a hook for changing alignment in the last moment, then do the alignment
-		if (options.rotation) { // Fancy box alignment isn't supported for rotated text
-			alignAttr = {
-				align: options.align,
-				x: alignTo.x + options.x + alignTo.width / 2,
-				y: alignTo.y + options.y + alignTo.height / 2
-			};
-			dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
-		} else {
-			dataLabel.align(options, null, alignTo);
-			alignAttr = dataLabel.alignAttr;
-
-			// Handle justify or crop
-			if (pick(options.overflow, 'justify') === 'justify') {
-				this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
-
-			} else if (pick(options.crop, true)) {
-				// Now check that the data label is within the plot area
-				visible = chart.isInsidePlot(alignAttr.x, alignAttr.y) && chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height);
-
-			}
-		}
-	}
-
-	// Show or hide based on the final aligned position
-	if (!visible) {
-		dataLabel.attr({ y: -999 });
-		dataLabel.placed = false; // don't animate back in
-	}
-
-};
-
-/**
- * If data labels fall partly outside the plot area, align them back in, in a way that
- * doesn't hide the point.
- */
-Series.prototype.justifyDataLabel = function (dataLabel, options, alignAttr, bBox, alignTo, isNew) {
-	var chart = this.chart,
-		align = options.align,
-		verticalAlign = options.verticalAlign,
-		off,
-		justified;
-
-	// Off left
-	off = alignAttr.x;
-	if (off < 0) {
-		if (align === 'right') {
-			options.align = 'left';
-		} else {
-			options.x = -off;
-		}
-		justified = true;
-	}
-
-	// Off right
-	off = alignAttr.x + bBox.width;
-	if (off > chart.plotWidth) {
-		if (align === 'left') {
-			options.align = 'right';
-		} else {
-			options.x = chart.plotWidth - off;
-		}
-		justified = true;
-	}
-
-	// Off top
-	off = alignAttr.y;
-	if (off < 0) {
-		if (verticalAlign === 'bottom') {
-			options.verticalAlign = 'top';
-		} else {
-			options.y = -off;
-		}
-		justified = true;
-	}
-
-	// Off bottom
-	off = alignAttr.y + bBox.height;
-	if (off > chart.plotHeight) {
-		if (verticalAlign === 'top') {
-			options.verticalAlign = 'bottom';
-		} else {
-			options.y = chart.plotHeight - off;
-		}
-		justified = true;
-	}
-
-	if (justified) {
-		dataLabel.placed = !isNew;
-		dataLabel.align(options, null, alignTo);
-	}
-};
-
-/**
- * Override the base drawDataLabels method by pie specific functionality
- */
-if (seriesTypes.pie) {
-	seriesTypes.pie.prototype.drawDataLabels = function () {
-		var series = this,
-			data = series.data,
-			point,
-			chart = series.chart,
-			options = series.options.dataLabels,
-			connectorPadding = pick(options.connectorPadding, 10),
-			connectorWidth = pick(options.connectorWidth, 1),
-			plotWidth = chart.plotWidth,
-			plotHeight = chart.plotHeight,
-			connector,
-			connectorPath,
-			softConnector = pick(options.softConnector, true),
-			distanceOption = options.distance,
-			seriesCenter = series.center,
-			radius = seriesCenter[2] / 2,
-			centerY = seriesCenter[1],
-			outside = distanceOption > 0,
-			dataLabel,
-			dataLabelWidth,
-			labelPos,
-			labelHeight,
-			halves = [// divide the points into right and left halves for anti collision
-				[], // right
-				[]  // left
-			],
-			x,
-			y,
-			visibility,
-			rankArr,
-			i,
-			j,
-			overflow = [0, 0, 0, 0], // top, right, bottom, left
-			sort = function (a, b) {
-				return b.y - a.y;
-			};
-
-		// get out if not enabled
-		if (!series.visible || (!options.enabled && !series._hasPointLabels)) {
-			return;
-		}
-
-		// run parent method
-		Series.prototype.drawDataLabels.apply(series);
-
-		// arrange points for detection collision
-		each(data, function (point) {
-			if (point.dataLabel && point.visible) { // #407, #2510
-				halves[point.half].push(point);
-			}
-		});
-
-		// assume equal label heights
-		i = 0;
-		while (!labelHeight && data[i]) { // #1569
-			labelHeight = data[i] && data[i].dataLabel && (data[i].dataLabel.getBBox().height || 21); // 21 is for #968
-			i++;
-		}
-
-		/* Loop over the points in each half, starting from the top and bottom
-		 * of the pie to detect overlapping labels.
-		 */
-		i = 2;
-		while (i--) {
-
-			var slots = [],
-				slotsLength,
-				usedSlots = [],
-				points = halves[i],
-				pos,
-				length = points.length,
-				slotIndex;
-
-			// Sort by angle
-			series.sortByAngle(points, i - 0.5);
-
-			// Only do anti-collision when we are outside the pie and have connectors (#856)
-			if (distanceOption > 0) {
-
-				// build the slots
-				for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
-					slots.push(pos);
-
-					// visualize the slot
-					/*
-					var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
-						slotY = pos + chart.plotTop;
-					if (!isNaN(slotX)) {
-						chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1)
-							.attr({
-								'stroke-width': 1,
-								stroke: 'silver'
-							})
-							.add();
-						chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
-							.attr({
-								fill: 'silver'
-							}).add();
-					}
-					*/
-				}
-				slotsLength = slots.length;
-
-				// if there are more values than available slots, remove lowest values
-				if (length > slotsLength) {
-					// create an array for sorting and ranking the points within each quarter
-					rankArr = [].concat(points);
-					rankArr.sort(sort);
-					j = length;
-					while (j--) {
-						rankArr[j].rank = j;
-					}
-					j = length;
-					while (j--) {
-						if (points[j].rank >= slotsLength) {
-							points.splice(j, 1);
-						}
-					}
-					length = points.length;
-				}
-
-				// The label goes to the nearest open slot, but not closer to the edge than
-				// the label's index.
-				for (j = 0; j < length; j++) {
-
-					point = points[j];
-					labelPos = point.labelPos;
-
-					var closest = 9999,
-						distance,
-						slotI;
-
-					// find the closest slot index
-					for (slotI = 0; slotI < slotsLength; slotI++) {
-						distance = mathAbs(slots[slotI] - labelPos[1]);
-						if (distance < closest) {
-							closest = distance;
-							slotIndex = slotI;
-						}
-					}
-
-					// if that slot index is closer to the edges of the slots, move it
-					// to the closest appropriate slot
-					if (slotIndex < j && slots[j] !== null) { // cluster at the top
-						slotIndex = j;
-					} else if (slotsLength  < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
-						slotIndex = slotsLength - length + j;
-						while (slots[slotIndex] === null) { // make sure it is not taken
-							slotIndex++;
-						}
-					} else {
-						// Slot is taken, find next free slot below. In the next run, the next slice will find the
-						// slot above these, because it is the closest one
-						while (slots[slotIndex] === null) { // make sure it is not taken
-							slotIndex++;
-						}
-					}
-
-					usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
-					slots[slotIndex] = null; // mark as taken
-				}
-				// sort them in order to fill in from the top
-				usedSlots.sort(sort);
-			}
-
-			// now the used slots are sorted, fill them up sequentially
-			for (j = 0; j < length; j++) {
-
-				var slot, naturalY;
-
-				point = points[j];
-				labelPos = point.labelPos;
-				dataLabel = point.dataLabel;
-				visibility = point.visible === false ? HIDDEN : VISIBLE;
-				naturalY = labelPos[1];
-
-				if (distanceOption > 0) {
-					slot = usedSlots.pop();
-					slotIndex = slot.i;
-
-					// if the slot next to currrent slot is free, the y value is allowed
-					// to fall back to the natural position
-					y = slot.y;
-					if ((naturalY > y && slots[slotIndex + 1] !== null) ||
-							(naturalY < y &&  slots[slotIndex - 1] !== null)) {
-						y = naturalY;
-					}
-
-				} else {
-					y = naturalY;
-				}
-
-				// get the x - use the natural x position for first and last slot, to prevent the top
-				// and botton slice connectors from touching each other on either side
-				x = options.justify ?
-					seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
-					series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
-
-
-				// Record the placement and visibility
-				dataLabel._attr = {
-					visibility: visibility,
-					align: labelPos[6]
-				};
-				dataLabel._pos = {
-					x: x + options.x +
-						({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0),
-					y: y + options.y - 10 // 10 is for the baseline (label vs text)
-				};
-				dataLabel.connX = x;
-				dataLabel.connY = y;
-
-
-				// Detect overflowing data labels
-				if (this.options.size === null) {
-					dataLabelWidth = dataLabel.width;
-					// Overflow left
-					if (x - dataLabelWidth < connectorPadding) {
-						overflow[3] = mathMax(mathRound(dataLabelWidth - x + connectorPadding), overflow[3]);
-
-					// Overflow right
-					} else if (x + dataLabelWidth > plotWidth - connectorPadding) {
-						overflow[1] = mathMax(mathRound(x + dataLabelWidth - plotWidth + connectorPadding), overflow[1]);
-					}
-
-					// Overflow top
-					if (y - labelHeight / 2 < 0) {
-						overflow[0] = mathMax(mathRound(-y + labelHeight / 2), overflow[0]);
-
-					// Overflow left
-					} else if (y + labelHeight / 2 > plotHeight) {
-						overflow[2] = mathMax(mathRound(y + labelHeight / 2 - plotHeight), overflow[2]);
-					}
-				}
-			} // for each point
-		} // for each half
-
-		// Do not apply the final placement and draw the connectors until we have verified
-		// that labels are not spilling over.
-		if (arrayMax(overflow) === 0 || this.verifyDataLabelOverflow(overflow)) {
-
-			// Place the labels in the final position
-			this.placeDataLabels();
-
-			// Draw the connectors
-			if (outside && connectorWidth) {
-				each(this.points, function (point) {
-					connector = point.connector;
-					labelPos = point.labelPos;
-					dataLabel = point.dataLabel;
-
-					if (dataLabel && dataLabel._pos) {
-						visibility = dataLabel._attr.visibility;
-						x = dataLabel.connX;
-						y = dataLabel.connY;
-						connectorPath = softConnector ? [
-							M,
-							x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
-							'C',
-							x, y, // first break, next to the label
-							2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
-							labelPos[2], labelPos[3], // second break
-							L,
-							labelPos[4], labelPos[5] // base
-						] : [
-							M,
-							x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
-							L,
-							labelPos[2], labelPos[3], // second break
-							L,
-							labelPos[4], labelPos[5] // base
-						];
-
-						if (connector) {
-							connector.animate({ d: connectorPath });
-							connector.attr('visibility', visibility);
-
-						} else {
-							point.connector = connector = series.chart.renderer.path(connectorPath).attr({
-								'stroke-width': connectorWidth,
-								stroke: options.connectorColor || point.color || '#606060',
-								visibility: visibility
-								//zIndex: 0 // #2722 (reversed)
-							})
-							.add(series.dataLabelsGroup);
-						}
-					} else if (connector) {
-						point.connector = connector.destroy();
-					}
-				});
-			}
-		}
-	};
-	/**
-	 * Perform the final placement of the data labels after we have verified that they
-	 * fall within the plot area.
-	 */
-	seriesTypes.pie.prototype.placeDataLabels = function () {
-		each(this.points, function (point) {
-			var dataLabel = point.dataLabel,
-				_pos;
-
-			if (dataLabel) {
-				_pos = dataLabel._pos;
-				if (_pos) {
-					dataLabel.attr(dataLabel._attr);
-					dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
-					dataLabel.moved = true;
-				} else if (dataLabel) {
-					dataLabel.attr({ y: -999 });
-				}
-			}
-		});
-	};
-
-	seriesTypes.pie.prototype.alignDataLabel =  noop;
-
-	/**
-	 * Verify whether the data labels are allowed to draw, or we should run more translation and data
-	 * label positioning to keep them inside the plot area. Returns true when data labels are ready
-	 * to draw.
-	 */
-	seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
-
-		var center = this.center,
-			options = this.options,
-			centerOption = options.center,
-			minSize = options.minSize || 80,
-			newSize = minSize,
-			ret;
-
-		// Handle horizontal size and center
-		if (centerOption[0] !== null) { // Fixed center
-			newSize = mathMax(center[2] - mathMax(overflow[1], overflow[3]), minSize);
-
-		} else { // Auto center
-			newSize = mathMax(
-				center[2] - overflow[1] - overflow[3], // horizontal overflow
-				minSize
-			);
-			center[0] += (overflow[3] - overflow[1]) / 2; // horizontal center
-		}
-
-		// Handle vertical size and center
-		if (centerOption[1] !== null) { // Fixed center
-			newSize = mathMax(mathMin(newSize, center[2] - mathMax(overflow[0], overflow[2])), minSize);
-
-		} else { // Auto center
-			newSize = mathMax(
-				mathMin(
-					newSize,
-					center[2] - overflow[0] - overflow[2] // vertical overflow
-				),
-				minSize
-			);
-			center[1] += (overflow[0] - overflow[2]) / 2; // vertical center
-		}
-
-		// If the size must be decreased, we need to run translate and drawDataLabels again
-		if (newSize < center[2]) {
-			center[2] = newSize;
-			this.translate(center);
-			each(this.points, function (point) {
-				if (point.dataLabel) {
-					point.dataLabel._pos = null; // reset
-				}
-			});
-
-			if (this.drawDataLabels) {
-				this.drawDataLabels();
-			}
-		// Else, return true to indicate that the pie and its labels is within the plot area
-		} else {
-			ret = true;
-		}
-		return ret;
-	};
-}
-
-if (seriesTypes.column) {
-
-	/**
-	 * Override the basic data label alignment by adjusting for the position of the column
-	 */
-	seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options,  alignTo, isNew) {
-		var chart = this.chart,
-			inverted = chart.inverted,
-			dlBox = point.dlBox || point.shapeArgs, // data label box for alignment
-			below = point.below || (point.plotY > pick(this.translatedThreshold, chart.plotSizeY)),
-			inside = pick(options.inside, !!this.options.stacking); // draw it inside the box?
-
-		// Align to the column itself, or the top of it
-		if (dlBox) { // Area range uses this method but not alignTo
-			alignTo = merge(dlBox);
-
-			if (inverted) {
-				alignTo = {
-					x: chart.plotWidth - alignTo.y - alignTo.height,
-					y: chart.plotHeight - alignTo.x - alignTo.width,
-					width: alignTo.height,
-					height: alignTo.width
-				};
-			}
-
-			// Compute the alignment box
-			if (!inside) {
-				if (inverted) {
-					alignTo.x += below ? 0 : alignTo.width;
-					alignTo.width = 0;
-				} else {
-					alignTo.y += below ? alignTo.height : 0;
-					alignTo.height = 0;
-				}
-			}
-		}
-
-
-		// When alignment is undefined (typically columns and bars), display the individual
-		// point below or above the point depending on the threshold
-		options.align = pick(
-			options.align,
-			!inverted || inside ? 'center' : below ? 'right' : 'left'
-		);
-		options.verticalAlign = pick(
-			options.verticalAlign,
-			inverted || inside ? 'middle' : below ? 'top' : 'bottom'
-		);
-
-		// Call the parent method
-		Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
-	};
-}
-
-
-
-/**
- * TrackerMixin for points and graphs
- */
-
-var TrackerMixin = Highcharts.TrackerMixin = {
-
-	drawTrackerPoint: function () {
-		var series = this,
-			chart = series.chart,
-			pointer = chart.pointer,
-			cursor = series.options.cursor,
-			css = cursor && { cursor: cursor },
-			onMouseOver = function (e) {
-				var target = e.target,
-				point;
-
-				if (chart.hoverSeries !== series) {
-					series.onMouseOver();
-				}
-
-				while (target && !point) {
-					point = target.point;
-					target = target.parentNode;
-				}
-
-				if (point !== UNDEFINED && point !== chart.hoverPoint) { // undefined on graph in scatterchart
-					point.onMouseOver(e);
-				}
-			};
-
-		// Add reference to the point
-		each(series.points, function (point) {
-			if (point.graphic) {
-				point.graphic.element.point = point;
-			}
-			if (point.dataLabel) {
-				point.dataLabel.element.point = point;
-			}
-		});
-
-		// Add the event listeners, we need to do this only once
-		if (!series._hasTracking) {
-			each(series.trackerGroups, function (key) {
-				if (series[key]) { // we don't always have dataLabelsGroup
-					series[key]
-						.addClass(PREFIX + 'tracker')
-						.on('mouseover', onMouseOver)
-						.on('mouseout', function (e) { pointer.onTrackerMouseOut(e); })
-						.css(css);
-					if (hasTouch) {
-						series[key].on('touchstart', onMouseOver);
-					}
-				}
-			});
-			series._hasTracking = true;
-		}
-	},
-
-	/**
-	 * Draw the tracker object that sits above all data labels and markers to
-	 * track mouse events on the graph or points. For the line type charts
-	 * the tracker uses the same graphPath, but with a greater stroke width
-	 * for better control.
-	 */
-	drawTrackerGraph: function () {
-		var series = this,
-			options = series.options,
-			trackByArea = options.trackByArea,
-			trackerPath = [].concat(trackByArea ? series.areaPath : series.graphPath),
-			trackerPathLength = trackerPath.length,
-			chart = series.chart,
-			pointer = chart.pointer,
-			renderer = chart.renderer,
-			snap = chart.options.tooltip.snap,
-			tracker = series.tracker,
-			cursor = options.cursor,
-			css = cursor && { cursor: cursor },
-			singlePoints = series.singlePoints,
-			singlePoint,
-			i,
-			onMouseOver = function () {
-				if (chart.hoverSeries !== series) {
-					series.onMouseOver();
-				}
-			},
-			/*
-			 * Empirical lowest possible opacities for TRACKER_FILL for an element to stay invisible but clickable
-			 * IE6: 0.002
-			 * IE7: 0.002
-			 * IE8: 0.002
-			 * IE9: 0.00000000001 (unlimited)
-			 * IE10: 0.0001 (exporting only)
-			 * FF: 0.00000000001 (unlimited)
-			 * Chrome: 0.000001
-			 * Safari: 0.000001
-			 * Opera: 0.00000000001 (unlimited)
-			 */
-			TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')';
-
-		// Extend end points. A better way would be to use round linecaps,
-		// but those are not clickable in VML.
-		if (trackerPathLength && !trackByArea) {
-			i = trackerPathLength + 1;
-			while (i--) {
-				if (trackerPath[i] === M) { // extend left side
-					trackerPath.splice(i + 1, 0, trackerPath[i + 1] - snap, trackerPath[i + 2], L);
-				}
-				if ((i && trackerPath[i] === M) || i === trackerPathLength) { // extend right side
-					trackerPath.splice(i, 0, L, trackerPath[i - 2] + snap, trackerPath[i - 1]);
-				}
-			}
-		}
-
-		// handle single points
-		for (i = 0; i < singlePoints.length; i++) {
-			singlePoint = singlePoints[i];
-			trackerPath.push(M, singlePoint.plotX - snap, singlePoint.plotY,
-			L, singlePoint.plotX + snap, singlePoint.plotY);
-		}
-
-		// draw the tracker
-		if (tracker) {
-			tracker.attr({ d: trackerPath });
-		} else { // create
-
-			series.tracker = renderer.path(trackerPath)
-			.attr({
-				'stroke-linejoin': 'round', // #1225
-				visibility: series.visible ? VISIBLE : HIDDEN,
-				stroke: TRACKER_FILL,
-				fill: trackByArea ? TRACKER_FILL : NONE,
-				'stroke-width' : options.lineWidth + (trackByArea ? 0 : 2 * snap),
-				zIndex: 2
-			})
-			.add(series.group);
-
-			// The tracker is added to the series group, which is clipped, but is covered
-			// by the marker group. So the marker group also needs to capture events.
-			each([series.tracker, series.markerGroup], function (tracker) {
-				tracker.addClass(PREFIX + 'tracker')
-					.on('mouseover', onMouseOver)
-					.on('mouseout', function (e) { pointer.onTrackerMouseOut(e); })
-					.css(css);
-
-				if (hasTouch) {
-					tracker.on('touchstart', onMouseOver);
-				}
-			});
-		}
-	}
-};
-/* End TrackerMixin */
-
-
-/**
- * Add tracking event listener to the series group, so the point graphics
- * themselves act as trackers
- */ 
-
-if (seriesTypes.column) {
-	ColumnSeries.prototype.drawTracker = TrackerMixin.drawTrackerPoint;	
-}
-
-if (seriesTypes.pie) {
-	seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
-}
-
-if (seriesTypes.scatter) {
-	ScatterSeries.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
-}
-
-/* 
- * Extend Legend for item events 
- */ 
-extend(Legend.prototype, {
-
-	setItemEvents: function (item, legendItem, useHTML, itemStyle, itemHiddenStyle) {
-	var legend = this;
-	// Set the events on the item group, or in case of useHTML, the item itself (#1249)
-	(useHTML ? legendItem : item.legendGroup).on('mouseover', function () {
-			item.setState(HOVER_STATE);
-			legendItem.css(legend.options.itemHoverStyle);
-		})
-		.on('mouseout', function () {
-			legendItem.css(item.visible ? itemStyle : itemHiddenStyle);
-			item.setState();
-		})
-		.on('click', function (event) {
-			var strLegendItemClick = 'legendItemClick',
-				fnLegendItemClick = function () {
-					item.setVisible();
-				};
-				
-			// Pass over the click/touch event. #4.
-			event = {
-				browserEvent: event
-			};
-
-			// click the name or symbol
-			if (item.firePointEvent) { // point
-				item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
-			} else {
-				fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
-			}
-		});
-	},
-
-	createCheckboxForItem: function (item) {
-		var legend = this;
-
-		item.checkbox = createElement('input', {
-			type: 'checkbox',
-			checked: item.selected,
-			defaultChecked: item.selected // required by IE7
-		}, legend.options.itemCheckboxStyle, legend.chart.container);
-
-		addEvent(item.checkbox, 'click', function (event) {
-			var target = event.target;
-			fireEvent(item, 'checkboxClick', {
-					checked: target.checked
-				},
-				function () {
-					item.select();
-				}
-			);
-		});
-	}	
-});
-
-/* 
- * Add pointer cursor to legend itemstyle in defaultOptions
- */
-defaultOptions.legend.itemStyle.cursor = 'pointer';
-
-
-/* 
- * Extend the Chart object with interaction
- */
-
-extend(Chart.prototype, {
-	/**
-	 * Display the zoom button
-	 */
-	showResetZoom: function () {
-		var chart = this,
-			lang = defaultOptions.lang,
-			btnOptions = chart.options.chart.resetZoomButton,
-			theme = btnOptions.theme,
-			states = theme.states,
-			alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
-			
-		this.resetZoomButton = chart.renderer.button(lang.resetZoom, null, null, function () { chart.zoomOut(); }, theme, states && states.hover)
-			.attr({
-				align: btnOptions.position.align,
-				title: lang.resetZoomTitle
-			})
-			.add()
-			.align(btnOptions.position, false, alignTo);
-			
-	},
-
-	/**
-	 * Zoom out to 1:1
-	 */
-	zoomOut: function () {
-		var chart = this;
-		fireEvent(chart, 'selection', { resetSelection: true }, function () { 
-			chart.zoom();
-		});
-	},
-
-	/**
-	 * Zoom into a given portion of the chart given by axis coordinates
-	 * @param {Object} event
-	 */
-	zoom: function (event) {
-		var chart = this,
-			hasZoomed,
-			pointer = chart.pointer,
-			displayButton = false,
-			resetZoomButton;
-
-		// If zoom is called with no arguments, reset the axes
-		if (!event || event.resetSelection) {
-			each(chart.axes, function (axis) {
-				hasZoomed = axis.zoom();
-			});
-		} else { // else, zoom in on all axes
-			each(event.xAxis.concat(event.yAxis), function (axisData) {
-				var axis = axisData.axis,
-					isXAxis = axis.isXAxis;
-
-				// don't zoom more than minRange
-				if (pointer[isXAxis ? 'zoomX' : 'zoomY'] || pointer[isXAxis ? 'pinchX' : 'pinchY']) {
-					hasZoomed = axis.zoom(axisData.min, axisData.max);
-					if (axis.displayBtn) {
-						displayButton = true;
-					}
-				}
-			});
-		}
-		
-		// Show or hide the Reset zoom button
-		resetZoomButton = chart.resetZoomButton;
-		if (displayButton && !resetZoomButton) {
-			chart.showResetZoom();
-		} else if (!displayButton && isObject(resetZoomButton)) {
-			chart.resetZoomButton = resetZoomButton.destroy();
-		}
-		
-
-		// Redraw
-		if (hasZoomed) {
-			chart.redraw(
-				pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100) // animation
-			);
-		}
-	},
-
-	/**
-	 * Pan the chart by dragging the mouse across the pane. This function is called
-	 * on mouse move, and the distance to pan is computed from chartX compared to
-	 * the first chartX position in the dragging operation.
-	 */
-	pan: function (e, panning) {
-
-		var chart = this,
-			hoverPoints = chart.hoverPoints,
-			doRedraw;
-
-		// remove active points for shared tooltip
-		if (hoverPoints) {
-			each(hoverPoints, function (point) {
-				point.setState();
-			});
-		}
-
-		each(panning === 'xy' ? [1, 0] : [1], function (isX) { // xy is used in maps
-			var mousePos = e[isX ? 'chartX' : 'chartY'],
-				axis = chart[isX ? 'xAxis' : 'yAxis'][0],
-				startPos = chart[isX ? 'mouseDownX' : 'mouseDownY'],
-				halfPointRange = (axis.pointRange || 0) / 2,
-				extremes = axis.getExtremes(),
-				newMin = axis.toValue(startPos - mousePos, true) + halfPointRange,
-				newMax = axis.toValue(startPos + chart[isX ? 'plotWidth' : 'plotHeight'] - mousePos, true) - halfPointRange;
-
-			if (axis.series.length && newMin > mathMin(extremes.dataMin, extremes.min) && newMax < mathMax(extremes.dataMax, extremes.max)) {
-				axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
-				doRedraw = true;
-			}
-
-			chart[isX ? 'mouseDownX' : 'mouseDownY'] = mousePos; // set new reference for next run
-		});
-
-		if (doRedraw) {
-			chart.redraw(false);
-		}
-		css(chart.container, { cursor: 'move' });
-	}
-});
-
-/*
- * Extend the Point object with interaction
- */
-extend(Point.prototype, {
-	/**
-	 * Toggle the selection status of a point
-	 * @param {Boolean} selected Whether to select or unselect the point.
-	 * @param {Boolean} accumulate Whether to add to the previous selection. By default,
-	 *		 this happens if the control key (Cmd on Mac) was pressed during clicking.
-	 */
-	select: function (selected, accumulate) {
-		var point = this,
-			series = point.series,
-			chart = series.chart;
-
-		selected = pick(selected, !point.selected);
-
-		// fire the event with the defalut handler
-		point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
-			point.selected = point.options.selected = selected;
-			series.options.data[inArray(point, series.data)] = point.options;
-
-			point.setState(selected && SELECT_STATE);
-
-			// unselect all other points unless Ctrl or Cmd + click
-			if (!accumulate) {
-				each(chart.getSelectedPoints(), function (loopPoint) {
-					if (loopPoint.selected && loopPoint !== point) {
-						loopPoint.selected = loopPoint.options.selected = false;
-						series.options.data[inArray(loopPoint, series.data)] = loopPoint.options;
-						loopPoint.setState(NORMAL_STATE);
-							loopPoint.firePointEvent('unselect');
-					}
-				});
-			}
-		});
-	},
-
-	/**
-	 * Runs on mouse over the point
-	 */
-	onMouseOver: function (e) {
-		var point = this,
-			series = point.series,
-			chart = series.chart,
-			tooltip = chart.tooltip,
-			hoverPoint = chart.hoverPoint;
-
-		// set normal state to previous series
-		if (hoverPoint && hoverPoint !== point) {
-			hoverPoint.onMouseOut();
-		}
-
-		// trigger the event
-		point.firePointEvent('mouseOver');
-
-		// update the tooltip
-		if (tooltip && (!tooltip.shared || series.noSharedTooltip)) {
-			tooltip.refresh(point, e);
-		}
-
-		// hover this
-		point.setState(HOVER_STATE);
-		chart.hoverPoint = point;
-	},
-
-	/**
-	 * Runs on mouse out from the point
-	 */
-	onMouseOut: function () {
-		var chart = this.series.chart,
-			hoverPoints = chart.hoverPoints;
-
-		if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887
-			this.firePointEvent('mouseOut');
-
-			this.setState();
-			chart.hoverPoint = null;
-		}
-	},
-
-	/**
-	 * Import events from the series' and point's options. Only do it on
-	 * demand, to save processing time on hovering.
-	 */
-	importEvents: function () {
-		if (!this.hasImportedEvents) {
-			var point = this,
-				options = merge(point.series.options.point, point.options),
-				events = options.events,
-				eventType;
-
-			point.events = events;
-
-			for (eventType in events) {
-				addEvent(point, eventType, events[eventType]);
-			}
-			this.hasImportedEvents = true;
-
-		}
-	},
-
-	/**
-	 * Set the point's state
-	 * @param {String} state
-	 */
-	setState: function (state, move) {
-		var point = this,
-			plotX = point.plotX,
-			plotY = point.plotY,
-			series = point.series,
-			stateOptions = series.options.states,
-			markerOptions = defaultPlotOptions[series.type].marker && series.options.marker,
-			normalDisabled = markerOptions && !markerOptions.enabled,
-			markerStateOptions = markerOptions && markerOptions.states[state],
-			stateDisabled = markerStateOptions && markerStateOptions.enabled === false,
-			stateMarkerGraphic = series.stateMarkerGraphic,
-			pointMarker = point.marker || {},
-			chart = series.chart,
-			radius,
-			halo = series.halo,
-			haloOptions,
-			newSymbol,
-			pointAttr;
-
-		state = state || NORMAL_STATE; // empty string
-		pointAttr = point.pointAttr[state] || series.pointAttr[state];
-
-		if (
-				// already has this state
-				(state === point.state && !move) ||
-				// selected points don't respond to hover
-				(point.selected && state !== SELECT_STATE) ||
-				// series' state options is disabled
-				(stateOptions[state] && stateOptions[state].enabled === false) ||
-				// general point marker's state options is disabled
-				(state && (stateDisabled || (normalDisabled && markerStateOptions.enabled === false))) ||
-				// individual point marker's state options is disabled
-				(state && pointMarker.states && pointMarker.states[state] && pointMarker.states[state].enabled === false) // #1610
-
-			) {
-			return;
-		}
-
-		// apply hover styles to the existing point
-		if (point.graphic) {
-			radius = markerOptions && point.graphic.symbolName && pointAttr.r;
-			point.graphic.attr(merge(
-				pointAttr,
-				radius ? { // new symbol attributes (#507, #612)
-					x: plotX - radius,
-					y: plotY - radius,
-					width: 2 * radius,
-					height: 2 * radius
-				} : {}
-			));
-
-			// Zooming in from a range with no markers to a range with markers
-			if (stateMarkerGraphic) {
-				stateMarkerGraphic.hide();
-			}
-		} else {
-			// if a graphic is not applied to each point in the normal state, create a shared
-			// graphic for the hover state
-			if (state && markerStateOptions) {
-				radius = markerStateOptions.radius;
-				newSymbol = pointMarker.symbol || series.symbol;
-
-				// If the point has another symbol than the previous one, throw away the
-				// state marker graphic and force a new one (#1459)
-				if (stateMarkerGraphic && stateMarkerGraphic.currentSymbol !== newSymbol) {
-					stateMarkerGraphic = stateMarkerGraphic.destroy();
-				}
-
-				// Add a new state marker graphic
-				if (!stateMarkerGraphic) {
-					if (newSymbol) {
-						series.stateMarkerGraphic = stateMarkerGraphic = chart.renderer.symbol(
-							newSymbol,
-							plotX - radius,
-							plotY - radius,
-							2 * radius,
-							2 * radius
-						)
-						.attr(pointAttr)
-						.add(series.markerGroup);
-						stateMarkerGraphic.currentSymbol = newSymbol;
-					}
-
-				// Move the existing graphic
-				} else {
-					stateMarkerGraphic[move ? 'animate' : 'attr']({ // #1054
-						x: plotX - radius,
-						y: plotY - radius
-					});
-				}
-			}
-
-			if (stateMarkerGraphic) {
-				stateMarkerGraphic[state && chart.isInsidePlot(plotX, plotY, chart.inverted) ? 'show' : 'hide'](); // #2450
-			}
-		}
-
-		// Show me your halo
-		haloOptions = stateOptions[state] && stateOptions[state].halo;
-		if (haloOptions && haloOptions.size) {
-			if (!halo) {
-				series.halo = halo = chart.renderer.path()
-					.add(series.seriesGroup);
-			}
-			halo.attr(extend({
-				fill: Color(point.color || series.color).setOpacity(haloOptions.opacity).get()
-			}, haloOptions.attributes))[move ? 'animate' : 'attr']({
-				d: point.haloPath(haloOptions.size)
-			});
-		} else if (halo) {
-			halo.attr({ d: [] });
-		}
-
-		point.state = state;
-	},
-
-	haloPath: function (size) {
-		var series = this.series,
-			chart = series.chart,
-			plotBox = series.getPlotBox(),
-			inverted = chart.inverted;
-
-		return chart.renderer.symbols.circle(
-			plotBox.translateX + (inverted ? series.yAxis.len - this.plotY : this.plotX) - size, 
-			plotBox.translateY + (inverted ? series.xAxis.len - this.plotX : this.plotY) - size, 
-			size * 2, 
-			size * 2
-		);
-	}
-});
-
-/*
- * Extend the Series object with interaction
- */
-
-extend(Series.prototype, {
-	/**
-	 * Series mouse over handler
-	 */
-	onMouseOver: function () {
-		var series = this,
-			chart = series.chart,
-			hoverSeries = chart.hoverSeries;
-
-		// set normal state to previous series
-		if (hoverSeries && hoverSeries !== series) {
-			hoverSeries.onMouseOut();
-		}
-
-		// trigger the event, but to save processing time,
-		// only if defined
-		if (series.options.events.mouseOver) {
-			fireEvent(series, 'mouseOver');
-		}
-
-		// hover this
-		series.setState(HOVER_STATE);
-		chart.hoverSeries = series;
-	},
-
-	/**
-	 * Series mouse out handler
-	 */
-	onMouseOut: function () {
-		// trigger the event only if listeners exist
-		var series = this,
-			options = series.options,
-			chart = series.chart,
-			tooltip = chart.tooltip,
-			hoverPoint = chart.hoverPoint;
-
-		// trigger mouse out on the point, which must be in this series
-		if (hoverPoint) {
-			hoverPoint.onMouseOut();
-		}
-
-		// fire the mouse out event
-		if (series && options.events.mouseOut) {
-			fireEvent(series, 'mouseOut');
-		}
-
-
-		// hide the tooltip
-		if (tooltip && !options.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) {
-			tooltip.hide();
-		}
-
-		// set normal state
-		series.setState();
-		chart.hoverSeries = null;
-	},
-
-	/**
-	 * Set the state of the graph
-	 */
-	setState: function (state) {
-		var series = this,
-			options = series.options,
-			graph = series.graph,
-			graphNeg = series.graphNeg,
-			stateOptions = options.states,
-			lineWidth = options.lineWidth,
-			attribs;
-
-		state = state || NORMAL_STATE;
-
-		if (series.state !== state) {
-			series.state = state;
-
-			if (stateOptions[state] && stateOptions[state].enabled === false) {
-				return;
-			}
-
-			if (state) {
-				lineWidth = stateOptions[state].lineWidth || lineWidth + 1;
-			}
-
-			if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML
-				attribs = {
-					'stroke-width': lineWidth
-				};
-				// use attr because animate will cause any other animation on the graph to stop
-				graph.attr(attribs);
-				if (graphNeg) {
-					graphNeg.attr(attribs);
-				}
-			}
-		}
-	},
-
-	/**
-	 * Set the visibility of the graph
-	 *
-	 * @param vis {Boolean} True to show the series, false to hide. If UNDEFINED,
-	 *				the visibility is toggled.
-	 */
-	setVisible: function (vis, redraw) {
-		var series = this,
-			chart = series.chart,
-			legendItem = series.legendItem,
-			showOrHide,
-			ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
-			oldVisibility = series.visible;
-
-		// if called without an argument, toggle visibility
-		series.visible = vis = series.userOptions.visible = vis === UNDEFINED ? !oldVisibility : vis;
-		showOrHide = vis ? 'show' : 'hide';
-
-		// show or hide elements
-		each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) {
-			if (series[key]) {
-				series[key][showOrHide]();
-			}
-		});
-
-
-		// hide tooltip (#1361)
-		if (chart.hoverSeries === series) {
-			series.onMouseOut();
-		}
-
-
-		if (legendItem) {
-			chart.legend.colorizeItem(series, vis);
-		}
-
-
-		// rescale or adapt to resized chart
-		series.isDirty = true;
-		// in a stack, all other series are affected
-		if (series.options.stacking) {
-			each(chart.series, function (otherSeries) {
-				if (otherSeries.options.stacking && otherSeries.visible) {
-					otherSeries.isDirty = true;
-				}
-			});
-		}
-
-		// show or hide linked series
-		each(series.linkedSeries, function (otherSeries) {
-			otherSeries.setVisible(vis, false);
-		});
-
-		if (ignoreHiddenSeries) {
-			chart.isDirtyBox = true;
-		}
-		if (redraw !== false) {
-			chart.redraw();
-		}
-
-		fireEvent(series, showOrHide);
-	},
-
-	/**
-	 * Memorize tooltip texts and positions
-	 */
-	setTooltipPoints: function (renew) {
-		var series = this,
-			points = [],
-			pointsLength,
-			low,
-			high,
-			xAxis = series.xAxis,
-			xExtremes = xAxis && xAxis.getExtremes(),
-			axisLength = xAxis ? (xAxis.tooltipLen || xAxis.len) : series.chart.plotSizeX, // tooltipLen and tooltipPosName used in polar
-			point,
-			pointX,
-			nextPoint,
-			i,
-			tooltipPoints = []; // a lookup array for each pixel in the x dimension
-
-		// don't waste resources if tracker is disabled
-		if (series.options.enableMouseTracking === false || series.singularTooltips) {
-			return;
-		}
-
-		// renew
-		if (renew) {
-			series.tooltipPoints = null;
-		}
-
-		// concat segments to overcome null values
-		each(series.segments || series.points, function (segment) {
-			points = points.concat(segment);
-		});
-
-		// Reverse the points in case the X axis is reversed
-		if (xAxis && xAxis.reversed) {
-			points = points.reverse();
-		}
-
-		// Polar needs additional shaping
-		if (series.orderTooltipPoints) {
-			series.orderTooltipPoints(points);
-		}
-
-		// Assign each pixel position to the nearest point
-		pointsLength = points.length;
-		for (i = 0; i < pointsLength; i++) {
-			point = points[i];
-			pointX = point.x;
-			if (pointX >= xExtremes.min && pointX <= xExtremes.max) { // #1149
-				nextPoint = points[i + 1];
-
-				// Set this range's low to the last range's high plus one
-				low = high === UNDEFINED ? 0 : high + 1;
-				// Now find the new high
-				high = points[i + 1] ?
-					mathMin(mathMax(0, mathFloor( // #2070
-						(point.clientX + (nextPoint ? (nextPoint.wrappedClientX || nextPoint.clientX) : axisLength)) / 2
-					)), axisLength) :
-					axisLength;
-
-				while (low >= 0 && low <= high) {
-					tooltipPoints[low++] = point;
-				}
-			}
-		}
-		series.tooltipPoints = tooltipPoints;
-	},
-
-	/**
-	 * Show the graph
-	 */
-	show: function () {
-		this.setVisible(true);
-	},
-
-	/**
-	 * Hide the graph
-	 */
-	hide: function () {
-		this.setVisible(false);
-	},
-
-
-	/**
-	 * Set the selected state of the graph
-	 *
-	 * @param selected {Boolean} True to select the series, false to unselect. If
-	 *				UNDEFINED, the selection state is toggled.
-	 */
-	select: function (selected) {
-		var series = this;
-		// if called without an argument, toggle
-		series.selected = selected = (selected === UNDEFINED) ? !series.selected : selected;
-
-		if (series.checkbox) {
-			series.checkbox.checked = selected;
-		}
-
-		fireEvent(series, selected ? 'select' : 'unselect');
-	},
-
-	drawTracker: TrackerMixin.drawTrackerGraph
-});
-// global variables
-extend(Highcharts, {
-	
-	// Constructors
-	Axis: Axis,
-	Chart: Chart,
-	Color: Color,
-	Point: Point,
-	Tick: Tick,	
-	Renderer: Renderer,
-	Series: Series,
-	SVGElement: SVGElement,
-	SVGRenderer: SVGRenderer,
-	
-	// Various
-	arrayMin: arrayMin,
-	arrayMax: arrayMax,
-	charts: charts,
-	dateFormat: dateFormat,
-	format: format,
-	pathAnim: pathAnim,
-	getOptions: getOptions,
-	hasBidiBug: hasBidiBug,
-	isTouchDevice: isTouchDevice,
-	numberFormat: numberFormat,
-	seriesTypes: seriesTypes,
-	setOptions: setOptions,
-	addEvent: addEvent,
-	removeEvent: removeEvent,
-	createElement: createElement,
-	discardElement: discardElement,
-	css: css,
-	each: each,
-	extend: extend,
-	map: map,
-	merge: merge,
-	pick: pick,
-	splat: splat,
-	extendClass: extendClass,
-	pInt: pInt,
-	wrap: wrap,
-	svg: hasSVG,
-	canvas: useCanVG,
-	vml: !hasSVG && !useCanVG,
-	product: PRODUCT,
-	version: VERSION
-});
-
-}());
diff --git a/apps/static/js/plugins/highcharts/modules/canvas-tools.js b/apps/static/js/plugins/highcharts/modules/canvas-tools.js
deleted file mode 100644
index d9229716d..000000000
--- a/apps/static/js/plugins/highcharts/modules/canvas-tools.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- A class to parse color values
- @author Stoyan Stefanov <sstoo@gmail.com>
- @link   http://www.phpied.com/rgb-color-parser-in-javascript/
- Use it if you like it
-
- canvg.js - Javascript SVG parser and renderer on Canvas
- MIT Licensed 
- Gabe Lerner (gabelerner@gmail.com)
- http://code.google.com/p/canvg/
-
- Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
-
- Highcharts JS v4.0.1 (2014-04-24)
- CanVGRenderer Extension module
-
- (c) 2011-2012 Torstein Honsi, Erik Olsson
-
- License: www.highcharts.com/license
-*/
-function RGBColor(m){this.ok=!1;m.charAt(0)=="#"&&(m=m.substr(1,6));var m=m.replace(/ /g,""),m=m.toLowerCase(),a={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",
-darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",
-gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",
-lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",
-oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",
-slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"},c;for(c in a)m==c&&(m=a[c]);var d=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(b){return[parseInt(b[1]),parseInt(b[2]),parseInt(b[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/,
-example:["#00ff00","336699"],process:function(b){return[parseInt(b[1],16),parseInt(b[2],16),parseInt(b[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(b){return[parseInt(b[1]+b[1],16),parseInt(b[2]+b[2],16),parseInt(b[3]+b[3],16)]}}];for(c=0;c<d.length;c++){var b=d[c].process,k=d[c].re.exec(m);if(k)channels=b(k),this.r=channels[0],this.g=channels[1],this.b=channels[2],this.ok=!0}this.r=this.r<0||isNaN(this.r)?0:this.r>255?255:this.r;this.g=this.g<0||isNaN(this.g)?0:
-this.g>255?255:this.g;this.b=this.b<0||isNaN(this.b)?0:this.b>255?255:this.b;this.toRGB=function(){return"rgb("+this.r+", "+this.g+", "+this.b+")"};this.toHex=function(){var b=this.r.toString(16),a=this.g.toString(16),d=this.b.toString(16);b.length==1&&(b="0"+b);a.length==1&&(a="0"+a);d.length==1&&(d="0"+d);return"#"+b+a+d};this.getHelpXML=function(){for(var b=[],k=0;k<d.length;k++)for(var c=d[k].example,j=0;j<c.length;j++)b[b.length]=c[j];for(var h in a)b[b.length]=h;c=document.createElement("ul");
-c.setAttribute("id","rgbcolor-examples");for(k=0;k<b.length;k++)try{var l=document.createElement("li"),o=new RGBColor(b[k]),n=document.createElement("div");n.style.cssText="margin: 3px; border: 1px solid black; background:"+o.toHex()+"; color:"+o.toHex();n.appendChild(document.createTextNode("test"));var q=document.createTextNode(" "+b[k]+" -> "+o.toRGB()+" -> "+o.toHex());l.appendChild(n);l.appendChild(q);c.appendChild(l)}catch(p){}return c}}
-if(!window.console)window.console={},window.console.log=function(){},window.console.dir=function(){};if(!Array.prototype.indexOf)Array.prototype.indexOf=function(m){for(var a=0;a<this.length;a++)if(this[a]==m)return a;return-1};
-(function(){function m(){var a={FRAMERATE:30,MAX_VIRTUAL_PIXELS:3E4};a.init=function(c){a.Definitions={};a.Styles={};a.Animations=[];a.Images=[];a.ctx=c;a.ViewPort=new function(){this.viewPorts=[];this.Clear=function(){this.viewPorts=[]};this.SetCurrent=function(a,b){this.viewPorts.push({width:a,height:b})};this.RemoveCurrent=function(){this.viewPorts.pop()};this.Current=function(){return this.viewPorts[this.viewPorts.length-1]};this.width=function(){return this.Current().width};this.height=function(){return this.Current().height};
-this.ComputeSize=function(a){return a!=null&&typeof a=="number"?a:a=="x"?this.width():a=="y"?this.height():Math.sqrt(Math.pow(this.width(),2)+Math.pow(this.height(),2))/Math.sqrt(2)}}};a.init();a.ImagesLoaded=function(){for(var c=0;c<a.Images.length;c++)if(!a.Images[c].loaded)return!1;return!0};a.trim=function(a){return a.replace(/^\s+|\s+$/g,"")};a.compressSpaces=function(a){return a.replace(/[\s\r\t\n]+/gm," ")};a.ajax=function(a){var d;return(d=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"))?
-(d.open("GET",a,!1),d.send(null),d.responseText):null};a.parseXml=function(a){if(window.DOMParser)return(new DOMParser).parseFromString(a,"text/xml");else{var a=a.replace(/<!DOCTYPE svg[^>]*>/,""),d=new ActiveXObject("Microsoft.XMLDOM");d.async="false";d.loadXML(a);return d}};a.Property=function(c,d){this.name=c;this.value=d;this.hasValue=function(){return this.value!=null&&this.value!==""};this.numValue=function(){if(!this.hasValue())return 0;var b=parseFloat(this.value);(this.value+"").match(/%$/)&&
-(b/=100);return b};this.valueOrDefault=function(b){return this.hasValue()?this.value:b};this.numValueOrDefault=function(b){return this.hasValue()?this.numValue():b};var b=this;this.Color={addOpacity:function(d){var c=b.value;if(d!=null&&d!=""){var f=new RGBColor(b.value);f.ok&&(c="rgba("+f.r+", "+f.g+", "+f.b+", "+d+")")}return new a.Property(b.name,c)}};this.Definition={getDefinition:function(){var d=b.value.replace(/^(url\()?#([^\)]+)\)?$/,"$2");return a.Definitions[d]},isUrl:function(){return b.value.indexOf("url(")==
-0},getFillStyle:function(b){var d=this.getDefinition();return d!=null&&d.createGradient?d.createGradient(a.ctx,b):d!=null&&d.createPattern?d.createPattern(a.ctx,b):null}};this.Length={DPI:function(){return 96},EM:function(b){var d=12,c=new a.Property("fontSize",a.Font.Parse(a.ctx.font).fontSize);c.hasValue()&&(d=c.Length.toPixels(b));return d},toPixels:function(d){if(!b.hasValue())return 0;var c=b.value+"";return c.match(/em$/)?b.numValue()*this.EM(d):c.match(/ex$/)?b.numValue()*this.EM(d)/2:c.match(/px$/)?
-b.numValue():c.match(/pt$/)?b.numValue()*1.25:c.match(/pc$/)?b.numValue()*15:c.match(/cm$/)?b.numValue()*this.DPI(d)/2.54:c.match(/mm$/)?b.numValue()*this.DPI(d)/25.4:c.match(/in$/)?b.numValue()*this.DPI(d):c.match(/%$/)?b.numValue()*a.ViewPort.ComputeSize(d):b.numValue()}};this.Time={toMilliseconds:function(){if(!b.hasValue())return 0;var a=b.value+"";if(a.match(/s$/))return b.numValue()*1E3;a.match(/ms$/);return b.numValue()}};this.Angle={toRadians:function(){if(!b.hasValue())return 0;var a=b.value+
-"";return a.match(/deg$/)?b.numValue()*(Math.PI/180):a.match(/grad$/)?b.numValue()*(Math.PI/200):a.match(/rad$/)?b.numValue():b.numValue()*(Math.PI/180)}}};a.Font=new function(){this.Styles=["normal","italic","oblique","inherit"];this.Variants=["normal","small-caps","inherit"];this.Weights="normal,bold,bolder,lighter,100,200,300,400,500,600,700,800,900,inherit".split(",");this.CreateFont=function(d,b,c,e,f,g){g=g!=null?this.Parse(g):this.CreateFont("","","","","",a.ctx.font);return{fontFamily:f||
-g.fontFamily,fontSize:e||g.fontSize,fontStyle:d||g.fontStyle,fontWeight:c||g.fontWeight,fontVariant:b||g.fontVariant,toString:function(){return[this.fontStyle,this.fontVariant,this.fontWeight,this.fontSize,this.fontFamily].join(" ")}}};var c=this;this.Parse=function(d){for(var b={},d=a.trim(a.compressSpaces(d||"")).split(" "),k=!1,e=!1,f=!1,g=!1,j="",h=0;h<d.length;h++)if(!e&&c.Styles.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontStyle=d[h];e=!0}else if(!g&&c.Variants.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontVariant=
-d[h];e=g=!0}else if(!f&&c.Weights.indexOf(d[h])!=-1){if(d[h]!="inherit")b.fontWeight=d[h];e=g=f=!0}else if(k)d[h]!="inherit"&&(j+=d[h]);else{if(d[h]!="inherit")b.fontSize=d[h].split("/")[0];e=g=f=k=!0}if(j!="")b.fontFamily=j;return b}};a.ToNumberArray=function(c){for(var c=a.trim(a.compressSpaces((c||"").replace(/,/g," "))).split(" "),d=0;d<c.length;d++)c[d]=parseFloat(c[d]);return c};a.Point=function(a,d){this.x=a;this.y=d;this.angleTo=function(b){return Math.atan2(b.y-this.y,b.x-this.x)};this.applyTransform=
-function(b){var a=this.x*b[1]+this.y*b[3]+b[5];this.x=this.x*b[0]+this.y*b[2]+b[4];this.y=a}};a.CreatePoint=function(c){c=a.ToNumberArray(c);return new a.Point(c[0],c[1])};a.CreatePath=function(c){for(var c=a.ToNumberArray(c),d=[],b=0;b<c.length;b+=2)d.push(new a.Point(c[b],c[b+1]));return d};a.BoundingBox=function(a,d,b,k){this.y2=this.x2=this.y1=this.x1=Number.NaN;this.x=function(){return this.x1};this.y=function(){return this.y1};this.width=function(){return this.x2-this.x1};this.height=function(){return this.y2-
-this.y1};this.addPoint=function(b,a){if(b!=null){if(isNaN(this.x1)||isNaN(this.x2))this.x2=this.x1=b;if(b<this.x1)this.x1=b;if(b>this.x2)this.x2=b}if(a!=null){if(isNaN(this.y1)||isNaN(this.y2))this.y2=this.y1=a;if(a<this.y1)this.y1=a;if(a>this.y2)this.y2=a}};this.addX=function(b){this.addPoint(b,null)};this.addY=function(b){this.addPoint(null,b)};this.addBoundingBox=function(b){this.addPoint(b.x1,b.y1);this.addPoint(b.x2,b.y2)};this.addQuadraticCurve=function(b,a,d,c,k,l){d=b+2/3*(d-b);c=a+2/3*(c-
-a);this.addBezierCurve(b,a,d,d+1/3*(k-b),c,c+1/3*(l-a),k,l)};this.addBezierCurve=function(b,a,d,c,k,l,o,n){var q=[b,a],p=[d,c],t=[k,l],m=[o,n];this.addPoint(q[0],q[1]);this.addPoint(m[0],m[1]);for(i=0;i<=1;i++)b=function(b){return Math.pow(1-b,3)*q[i]+3*Math.pow(1-b,2)*b*p[i]+3*(1-b)*Math.pow(b,2)*t[i]+Math.pow(b,3)*m[i]},a=6*q[i]-12*p[i]+6*t[i],d=-3*q[i]+9*p[i]-9*t[i]+3*m[i],c=3*p[i]-3*q[i],d==0?a!=0&&(a=-c/a,0<a&&a<1&&(i==0&&this.addX(b(a)),i==1&&this.addY(b(a)))):(c=Math.pow(a,2)-4*c*d,c<0||(k=
-(-a+Math.sqrt(c))/(2*d),0<k&&k<1&&(i==0&&this.addX(b(k)),i==1&&this.addY(b(k))),a=(-a-Math.sqrt(c))/(2*d),0<a&&a<1&&(i==0&&this.addX(b(a)),i==1&&this.addY(b(a)))))};this.isPointInBox=function(b,a){return this.x1<=b&&b<=this.x2&&this.y1<=a&&a<=this.y2};this.addPoint(a,d);this.addPoint(b,k)};a.Transform=function(c){var d=this;this.Type={};this.Type.translate=function(b){this.p=a.CreatePoint(b);this.apply=function(b){b.translate(this.p.x||0,this.p.y||0)};this.applyToPoint=function(b){b.applyTransform([1,
-0,0,1,this.p.x||0,this.p.y||0])}};this.Type.rotate=function(b){b=a.ToNumberArray(b);this.angle=new a.Property("angle",b[0]);this.cx=b[1]||0;this.cy=b[2]||0;this.apply=function(b){b.translate(this.cx,this.cy);b.rotate(this.angle.Angle.toRadians());b.translate(-this.cx,-this.cy)};this.applyToPoint=function(b){var a=this.angle.Angle.toRadians();b.applyTransform([1,0,0,1,this.p.x||0,this.p.y||0]);b.applyTransform([Math.cos(a),Math.sin(a),-Math.sin(a),Math.cos(a),0,0]);b.applyTransform([1,0,0,1,-this.p.x||
-0,-this.p.y||0])}};this.Type.scale=function(b){this.p=a.CreatePoint(b);this.apply=function(b){b.scale(this.p.x||1,this.p.y||this.p.x||1)};this.applyToPoint=function(b){b.applyTransform([this.p.x||0,0,0,this.p.y||0,0,0])}};this.Type.matrix=function(b){this.m=a.ToNumberArray(b);this.apply=function(b){b.transform(this.m[0],this.m[1],this.m[2],this.m[3],this.m[4],this.m[5])};this.applyToPoint=function(b){b.applyTransform(this.m)}};this.Type.SkewBase=function(b){this.base=d.Type.matrix;this.base(b);this.angle=
-new a.Property("angle",b)};this.Type.SkewBase.prototype=new this.Type.matrix;this.Type.skewX=function(b){this.base=d.Type.SkewBase;this.base(b);this.m=[1,0,Math.tan(this.angle.Angle.toRadians()),1,0,0]};this.Type.skewX.prototype=new this.Type.SkewBase;this.Type.skewY=function(b){this.base=d.Type.SkewBase;this.base(b);this.m=[1,Math.tan(this.angle.Angle.toRadians()),0,1,0,0]};this.Type.skewY.prototype=new this.Type.SkewBase;this.transforms=[];this.apply=function(b){for(var a=0;a<this.transforms.length;a++)this.transforms[a].apply(b)};
-this.applyToPoint=function(b){for(var a=0;a<this.transforms.length;a++)this.transforms[a].applyToPoint(b)};for(var c=a.trim(a.compressSpaces(c)).split(/\s(?=[a-z])/),b=0;b<c.length;b++){var k=c[b].split("(")[0],e=c[b].split("(")[1].replace(")","");this.transforms.push(new this.Type[k](e))}};a.AspectRatio=function(c,d,b,k,e,f,g,j,h,l){var d=a.compressSpaces(d),d=d.replace(/^defer\s/,""),o=d.split(" ")[0]||"xMidYMid",d=d.split(" ")[1]||"meet",n=b/k,q=e/f,p=Math.min(n,q),m=Math.max(n,q);d=="meet"&&(k*=
-p,f*=p);d=="slice"&&(k*=m,f*=m);h=new a.Property("refX",h);l=new a.Property("refY",l);h.hasValue()&&l.hasValue()?c.translate(-p*h.Length.toPixels("x"),-p*l.Length.toPixels("y")):(o.match(/^xMid/)&&(d=="meet"&&p==q||d=="slice"&&m==q)&&c.translate(b/2-k/2,0),o.match(/YMid$/)&&(d=="meet"&&p==n||d=="slice"&&m==n)&&c.translate(0,e/2-f/2),o.match(/^xMax/)&&(d=="meet"&&p==q||d=="slice"&&m==q)&&c.translate(b-k,0),o.match(/YMax$/)&&(d=="meet"&&p==n||d=="slice"&&m==n)&&c.translate(0,e-f));o=="none"?c.scale(n,
-q):d=="meet"?c.scale(p,p):d=="slice"&&c.scale(m,m);c.translate(g==null?0:-g,j==null?0:-j)};a.Element={};a.Element.ElementBase=function(c){this.attributes={};this.styles={};this.children=[];this.attribute=function(b,d){var c=this.attributes[b];if(c!=null)return c;c=new a.Property(b,"");d==!0&&(this.attributes[b]=c);return c};this.style=function(b,d){var c=this.styles[b];if(c!=null)return c;c=this.attribute(b);if(c!=null&&c.hasValue())return c;c=this.parent;if(c!=null&&(c=c.style(b),c!=null&&c.hasValue()))return c;
-c=new a.Property(b,"");d==!0&&(this.styles[b]=c);return c};this.render=function(b){if(this.style("display").value!="none"&&this.attribute("visibility").value!="hidden"){b.save();this.setContext(b);if(this.attribute("mask").hasValue()){var a=this.attribute("mask").Definition.getDefinition();a!=null&&a.apply(b,this)}else this.style("filter").hasValue()?(a=this.style("filter").Definition.getDefinition(),a!=null&&a.apply(b,this)):this.renderChildren(b);this.clearContext(b);b.restore()}};this.setContext=
-function(){};this.clearContext=function(){};this.renderChildren=function(b){for(var a=0;a<this.children.length;a++)this.children[a].render(b)};this.addChild=function(b,d){var c=b;d&&(c=a.CreateElement(b));c.parent=this;this.children.push(c)};if(c!=null&&c.nodeType==1){for(var d=0;d<c.childNodes.length;d++){var b=c.childNodes[d];b.nodeType==1&&this.addChild(b,!0)}for(d=0;d<c.attributes.length;d++)b=c.attributes[d],this.attributes[b.nodeName]=new a.Property(b.nodeName,b.nodeValue);b=a.Styles[c.nodeName];
-if(b!=null)for(var k in b)this.styles[k]=b[k];if(this.attribute("class").hasValue())for(var d=a.compressSpaces(this.attribute("class").value).split(" "),e=0;e<d.length;e++){b=a.Styles["."+d[e]];if(b!=null)for(k in b)this.styles[k]=b[k];b=a.Styles[c.nodeName+"."+d[e]];if(b!=null)for(k in b)this.styles[k]=b[k]}if(this.attribute("style").hasValue()){b=this.attribute("style").value.split(";");for(d=0;d<b.length;d++)a.trim(b[d])!=""&&(c=b[d].split(":"),k=a.trim(c[0]),c=a.trim(c[1]),this.styles[k]=new a.Property(k,
-c))}this.attribute("id").hasValue()&&a.Definitions[this.attribute("id").value]==null&&(a.Definitions[this.attribute("id").value]=this)}};a.Element.RenderedElementBase=function(c){this.base=a.Element.ElementBase;this.base(c);this.setContext=function(d){if(this.style("fill").Definition.isUrl()){var b=this.style("fill").Definition.getFillStyle(this);if(b!=null)d.fillStyle=b}else if(this.style("fill").hasValue())b=this.style("fill"),this.style("fill-opacity").hasValue()&&(b=b.Color.addOpacity(this.style("fill-opacity").value)),
-d.fillStyle=b.value=="none"?"rgba(0,0,0,0)":b.value;if(this.style("stroke").Definition.isUrl()){if(b=this.style("stroke").Definition.getFillStyle(this),b!=null)d.strokeStyle=b}else if(this.style("stroke").hasValue())b=this.style("stroke"),this.style("stroke-opacity").hasValue()&&(b=b.Color.addOpacity(this.style("stroke-opacity").value)),d.strokeStyle=b.value=="none"?"rgba(0,0,0,0)":b.value;if(this.style("stroke-width").hasValue())d.lineWidth=this.style("stroke-width").Length.toPixels();if(this.style("stroke-linecap").hasValue())d.lineCap=
-this.style("stroke-linecap").value;if(this.style("stroke-linejoin").hasValue())d.lineJoin=this.style("stroke-linejoin").value;if(this.style("stroke-miterlimit").hasValue())d.miterLimit=this.style("stroke-miterlimit").value;if(typeof d.font!="undefined")d.font=a.Font.CreateFont(this.style("font-style").value,this.style("font-variant").value,this.style("font-weight").value,this.style("font-size").hasValue()?this.style("font-size").Length.toPixels()+"px":"",this.style("font-family").value).toString();
-this.attribute("transform").hasValue()&&(new a.Transform(this.attribute("transform").value)).apply(d);this.attribute("clip-path").hasValue()&&(b=this.attribute("clip-path").Definition.getDefinition(),b!=null&&b.apply(d));if(this.style("opacity").hasValue())d.globalAlpha=this.style("opacity").numValue()}};a.Element.RenderedElementBase.prototype=new a.Element.ElementBase;a.Element.PathElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.path=function(d){d!=null&&d.beginPath();
-return new a.BoundingBox};this.renderChildren=function(d){this.path(d);a.Mouse.checkPath(this,d);d.fillStyle!=""&&d.fill();d.strokeStyle!=""&&d.stroke();var b=this.getMarkers();if(b!=null){if(this.style("marker-start").Definition.isUrl()){var c=this.style("marker-start").Definition.getDefinition();c.render(d,b[0][0],b[0][1])}if(this.style("marker-mid").Definition.isUrl())for(var c=this.style("marker-mid").Definition.getDefinition(),e=1;e<b.length-1;e++)c.render(d,b[e][0],b[e][1]);this.style("marker-end").Definition.isUrl()&&
-(c=this.style("marker-end").Definition.getDefinition(),c.render(d,b[b.length-1][0],b[b.length-1][1]))}};this.getBoundingBox=function(){return this.path()};this.getMarkers=function(){return null}};a.Element.PathElementBase.prototype=new a.Element.RenderedElementBase;a.Element.svg=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseClearContext=this.clearContext;this.clearContext=function(d){this.baseClearContext(d);a.ViewPort.RemoveCurrent()};this.baseSetContext=this.setContext;
-this.setContext=function(d){d.strokeStyle="rgba(0,0,0,0)";d.lineCap="butt";d.lineJoin="miter";d.miterLimit=4;this.baseSetContext(d);this.attribute("x").hasValue()&&this.attribute("y").hasValue()&&d.translate(this.attribute("x").Length.toPixels("x"),this.attribute("y").Length.toPixels("y"));var b=a.ViewPort.width(),c=a.ViewPort.height();if(typeof this.root=="undefined"&&this.attribute("width").hasValue()&&this.attribute("height").hasValue()){var b=this.attribute("width").Length.toPixels("x"),c=this.attribute("height").Length.toPixels("y"),
-e=0,f=0;this.attribute("refX").hasValue()&&this.attribute("refY").hasValue()&&(e=-this.attribute("refX").Length.toPixels("x"),f=-this.attribute("refY").Length.toPixels("y"));d.beginPath();d.moveTo(e,f);d.lineTo(b,f);d.lineTo(b,c);d.lineTo(e,c);d.closePath();d.clip()}a.ViewPort.SetCurrent(b,c);if(this.attribute("viewBox").hasValue()){var e=a.ToNumberArray(this.attribute("viewBox").value),f=e[0],g=e[1],b=e[2],c=e[3];a.AspectRatio(d,this.attribute("preserveAspectRatio").value,a.ViewPort.width(),b,a.ViewPort.height(),
-c,f,g,this.attribute("refX").value,this.attribute("refY").value);a.ViewPort.RemoveCurrent();a.ViewPort.SetCurrent(e[2],e[3])}}};a.Element.svg.prototype=new a.Element.RenderedElementBase;a.Element.rect=function(c){this.base=a.Element.PathElementBase;this.base(c);this.path=function(d){var b=this.attribute("x").Length.toPixels("x"),c=this.attribute("y").Length.toPixels("y"),e=this.attribute("width").Length.toPixels("x"),f=this.attribute("height").Length.toPixels("y"),g=this.attribute("rx").Length.toPixels("x"),
-j=this.attribute("ry").Length.toPixels("y");this.attribute("rx").hasValue()&&!this.attribute("ry").hasValue()&&(j=g);this.attribute("ry").hasValue()&&!this.attribute("rx").hasValue()&&(g=j);d!=null&&(d.beginPath(),d.moveTo(b+g,c),d.lineTo(b+e-g,c),d.quadraticCurveTo(b+e,c,b+e,c+j),d.lineTo(b+e,c+f-j),d.quadraticCurveTo(b+e,c+f,b+e-g,c+f),d.lineTo(b+g,c+f),d.quadraticCurveTo(b,c+f,b,c+f-j),d.lineTo(b,c+j),d.quadraticCurveTo(b,c,b+g,c),d.closePath());return new a.BoundingBox(b,c,b+e,c+f)}};a.Element.rect.prototype=
-new a.Element.PathElementBase;a.Element.circle=function(c){this.base=a.Element.PathElementBase;this.base(c);this.path=function(d){var b=this.attribute("cx").Length.toPixels("x"),c=this.attribute("cy").Length.toPixels("y"),e=this.attribute("r").Length.toPixels();d!=null&&(d.beginPath(),d.arc(b,c,e,0,Math.PI*2,!0),d.closePath());return new a.BoundingBox(b-e,c-e,b+e,c+e)}};a.Element.circle.prototype=new a.Element.PathElementBase;a.Element.ellipse=function(c){this.base=a.Element.PathElementBase;this.base(c);
-this.path=function(d){var b=4*((Math.sqrt(2)-1)/3),c=this.attribute("rx").Length.toPixels("x"),e=this.attribute("ry").Length.toPixels("y"),f=this.attribute("cx").Length.toPixels("x"),g=this.attribute("cy").Length.toPixels("y");d!=null&&(d.beginPath(),d.moveTo(f,g-e),d.bezierCurveTo(f+b*c,g-e,f+c,g-b*e,f+c,g),d.bezierCurveTo(f+c,g+b*e,f+b*c,g+e,f,g+e),d.bezierCurveTo(f-b*c,g+e,f-c,g+b*e,f-c,g),d.bezierCurveTo(f-c,g-b*e,f-b*c,g-e,f,g-e),d.closePath());return new a.BoundingBox(f-c,g-e,f+c,g+e)}};a.Element.ellipse.prototype=
-new a.Element.PathElementBase;a.Element.line=function(c){this.base=a.Element.PathElementBase;this.base(c);this.getPoints=function(){return[new a.Point(this.attribute("x1").Length.toPixels("x"),this.attribute("y1").Length.toPixels("y")),new a.Point(this.attribute("x2").Length.toPixels("x"),this.attribute("y2").Length.toPixels("y"))]};this.path=function(d){var b=this.getPoints();d!=null&&(d.beginPath(),d.moveTo(b[0].x,b[0].y),d.lineTo(b[1].x,b[1].y));return new a.BoundingBox(b[0].x,b[0].y,b[1].x,b[1].y)};
-this.getMarkers=function(){var a=this.getPoints(),b=a[0].angleTo(a[1]);return[[a[0],b],[a[1],b]]}};a.Element.line.prototype=new a.Element.PathElementBase;a.Element.polyline=function(c){this.base=a.Element.PathElementBase;this.base(c);this.points=a.CreatePath(this.attribute("points").value);this.path=function(d){var b=new a.BoundingBox(this.points[0].x,this.points[0].y);d!=null&&(d.beginPath(),d.moveTo(this.points[0].x,this.points[0].y));for(var c=1;c<this.points.length;c++)b.addPoint(this.points[c].x,
-this.points[c].y),d!=null&&d.lineTo(this.points[c].x,this.points[c].y);return b};this.getMarkers=function(){for(var a=[],b=0;b<this.points.length-1;b++)a.push([this.points[b],this.points[b].angleTo(this.points[b+1])]);a.push([this.points[this.points.length-1],a[a.length-1][1]]);return a}};a.Element.polyline.prototype=new a.Element.PathElementBase;a.Element.polygon=function(c){this.base=a.Element.polyline;this.base(c);this.basePath=this.path;this.path=function(a){var b=this.basePath(a);a!=null&&(a.lineTo(this.points[0].x,
-this.points[0].y),a.closePath());return b}};a.Element.polygon.prototype=new a.Element.polyline;a.Element.path=function(c){this.base=a.Element.PathElementBase;this.base(c);c=this.attribute("d").value;c=c.replace(/,/gm," ");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,"$1 $2");c=c.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,"$1 $2");c=c.replace(/([0-9])([+\-])/gm,
-"$1 $2");c=c.replace(/(\.[0-9]*)(\.)/gm,"$1 $2");c=c.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,"$1 $3 $4 ");c=a.compressSpaces(c);c=a.trim(c);this.PathParser=new function(d){this.tokens=d.split(" ");this.reset=function(){this.i=-1;this.previousCommand=this.command="";this.start=new a.Point(0,0);this.control=new a.Point(0,0);this.current=new a.Point(0,0);this.points=[];this.angles=[]};this.isEnd=function(){return this.i>=this.tokens.length-1};this.isCommandOrEnd=function(){return this.isEnd()?
-!0:this.tokens[this.i+1].match(/^[A-Za-z]$/)!=null};this.isRelativeCommand=function(){return this.command==this.command.toLowerCase()};this.getToken=function(){this.i+=1;return this.tokens[this.i]};this.getScalar=function(){return parseFloat(this.getToken())};this.nextCommand=function(){this.previousCommand=this.command;this.command=this.getToken()};this.getPoint=function(){return this.makeAbsolute(new a.Point(this.getScalar(),this.getScalar()))};this.getAsControlPoint=function(){var b=this.getPoint();
-return this.control=b};this.getAsCurrentPoint=function(){var b=this.getPoint();return this.current=b};this.getReflectedControlPoint=function(){return this.previousCommand.toLowerCase()!="c"&&this.previousCommand.toLowerCase()!="s"?this.current:new a.Point(2*this.current.x-this.control.x,2*this.current.y-this.control.y)};this.makeAbsolute=function(b){if(this.isRelativeCommand())b.x=this.current.x+b.x,b.y=this.current.y+b.y;return b};this.addMarker=function(b,a,d){d!=null&&this.angles.length>0&&this.angles[this.angles.length-
-1]==null&&(this.angles[this.angles.length-1]=this.points[this.points.length-1].angleTo(d));this.addMarkerAngle(b,a==null?null:a.angleTo(b))};this.addMarkerAngle=function(b,a){this.points.push(b);this.angles.push(a)};this.getMarkerPoints=function(){return this.points};this.getMarkerAngles=function(){for(var b=0;b<this.angles.length;b++)if(this.angles[b]==null)for(var a=b+1;a<this.angles.length;a++)if(this.angles[a]!=null){this.angles[b]=this.angles[a];break}return this.angles}}(c);this.path=function(d){var b=
-this.PathParser;b.reset();var c=new a.BoundingBox;for(d!=null&&d.beginPath();!b.isEnd();)switch(b.nextCommand(),b.command.toUpperCase()){case "M":var e=b.getAsCurrentPoint();b.addMarker(e);c.addPoint(e.x,e.y);d!=null&&d.moveTo(e.x,e.y);for(b.start=b.current;!b.isCommandOrEnd();)e=b.getAsCurrentPoint(),b.addMarker(e,b.start),c.addPoint(e.x,e.y),d!=null&&d.lineTo(e.x,e.y);break;case "L":for(;!b.isCommandOrEnd();){var f=b.current,e=b.getAsCurrentPoint();b.addMarker(e,f);c.addPoint(e.x,e.y);d!=null&&
-d.lineTo(e.x,e.y)}break;case "H":for(;!b.isCommandOrEnd();)e=new a.Point((b.isRelativeCommand()?b.current.x:0)+b.getScalar(),b.current.y),b.addMarker(e,b.current),b.current=e,c.addPoint(b.current.x,b.current.y),d!=null&&d.lineTo(b.current.x,b.current.y);break;case "V":for(;!b.isCommandOrEnd();)e=new a.Point(b.current.x,(b.isRelativeCommand()?b.current.y:0)+b.getScalar()),b.addMarker(e,b.current),b.current=e,c.addPoint(b.current.x,b.current.y),d!=null&&d.lineTo(b.current.x,b.current.y);break;case "C":for(;!b.isCommandOrEnd();){var g=
-b.current,f=b.getPoint(),j=b.getAsControlPoint(),e=b.getAsCurrentPoint();b.addMarker(e,j,f);c.addBezierCurve(g.x,g.y,f.x,f.y,j.x,j.y,e.x,e.y);d!=null&&d.bezierCurveTo(f.x,f.y,j.x,j.y,e.x,e.y)}break;case "S":for(;!b.isCommandOrEnd();)g=b.current,f=b.getReflectedControlPoint(),j=b.getAsControlPoint(),e=b.getAsCurrentPoint(),b.addMarker(e,j,f),c.addBezierCurve(g.x,g.y,f.x,f.y,j.x,j.y,e.x,e.y),d!=null&&d.bezierCurveTo(f.x,f.y,j.x,j.y,e.x,e.y);break;case "Q":for(;!b.isCommandOrEnd();)g=b.current,j=b.getAsControlPoint(),
-e=b.getAsCurrentPoint(),b.addMarker(e,j,j),c.addQuadraticCurve(g.x,g.y,j.x,j.y,e.x,e.y),d!=null&&d.quadraticCurveTo(j.x,j.y,e.x,e.y);break;case "T":for(;!b.isCommandOrEnd();)g=b.current,j=b.getReflectedControlPoint(),b.control=j,e=b.getAsCurrentPoint(),b.addMarker(e,j,j),c.addQuadraticCurve(g.x,g.y,j.x,j.y,e.x,e.y),d!=null&&d.quadraticCurveTo(j.x,j.y,e.x,e.y);break;case "A":for(;!b.isCommandOrEnd();){var g=b.current,h=b.getScalar(),l=b.getScalar(),f=b.getScalar()*(Math.PI/180),o=b.getScalar(),j=b.getScalar(),
-e=b.getAsCurrentPoint(),n=new a.Point(Math.cos(f)*(g.x-e.x)/2+Math.sin(f)*(g.y-e.y)/2,-Math.sin(f)*(g.x-e.x)/2+Math.cos(f)*(g.y-e.y)/2),q=Math.pow(n.x,2)/Math.pow(h,2)+Math.pow(n.y,2)/Math.pow(l,2);q>1&&(h*=Math.sqrt(q),l*=Math.sqrt(q));o=(o==j?-1:1)*Math.sqrt((Math.pow(h,2)*Math.pow(l,2)-Math.pow(h,2)*Math.pow(n.y,2)-Math.pow(l,2)*Math.pow(n.x,2))/(Math.pow(h,2)*Math.pow(n.y,2)+Math.pow(l,2)*Math.pow(n.x,2)));isNaN(o)&&(o=0);var p=new a.Point(o*h*n.y/l,o*-l*n.x/h),g=new a.Point((g.x+e.x)/2+Math.cos(f)*
-p.x-Math.sin(f)*p.y,(g.y+e.y)/2+Math.sin(f)*p.x+Math.cos(f)*p.y),m=function(b,a){return(b[0]*a[0]+b[1]*a[1])/(Math.sqrt(Math.pow(b[0],2)+Math.pow(b[1],2))*Math.sqrt(Math.pow(a[0],2)+Math.pow(a[1],2)))},s=function(b,a){return(b[0]*a[1]<b[1]*a[0]?-1:1)*Math.acos(m(b,a))},o=s([1,0],[(n.x-p.x)/h,(n.y-p.y)/l]),q=[(n.x-p.x)/h,(n.y-p.y)/l],p=[(-n.x-p.x)/h,(-n.y-p.y)/l],n=s(q,p);if(m(q,p)<=-1)n=Math.PI;m(q,p)>=1&&(n=0);j==0&&n>0&&(n-=2*Math.PI);j==1&&n<0&&(n+=2*Math.PI);q=new a.Point(g.x-h*Math.cos((o+n)/
-2),g.y-l*Math.sin((o+n)/2));b.addMarkerAngle(q,(o+n)/2+(j==0?1:-1)*Math.PI/2);b.addMarkerAngle(e,n+(j==0?1:-1)*Math.PI/2);c.addPoint(e.x,e.y);d!=null&&(m=h>l?h:l,e=h>l?1:h/l,h=h>l?l/h:1,d.translate(g.x,g.y),d.rotate(f),d.scale(e,h),d.arc(0,0,m,o,o+n,1-j),d.scale(1/e,1/h),d.rotate(-f),d.translate(-g.x,-g.y))}break;case "Z":d!=null&&d.closePath(),b.current=b.start}return c};this.getMarkers=function(){for(var a=this.PathParser.getMarkerPoints(),b=this.PathParser.getMarkerAngles(),c=[],e=0;e<a.length;e++)c.push([a[e],
-b[e]]);return c}};a.Element.path.prototype=new a.Element.PathElementBase;a.Element.pattern=function(c){this.base=a.Element.ElementBase;this.base(c);this.createPattern=function(d){var b=new a.Element.svg;b.attributes.viewBox=new a.Property("viewBox",this.attribute("viewBox").value);b.attributes.x=new a.Property("x",this.attribute("x").value);b.attributes.y=new a.Property("y",this.attribute("y").value);b.attributes.width=new a.Property("width",this.attribute("width").value);b.attributes.height=new a.Property("height",
-this.attribute("height").value);b.children=this.children;var c=document.createElement("canvas");c.width=this.attribute("width").Length.toPixels("x");c.height=this.attribute("height").Length.toPixels("y");b.render(c.getContext("2d"));return d.createPattern(c,"repeat")}};a.Element.pattern.prototype=new a.Element.ElementBase;a.Element.marker=function(c){this.base=a.Element.ElementBase;this.base(c);this.baseRender=this.render;this.render=function(d,b,c){d.translate(b.x,b.y);this.attribute("orient").valueOrDefault("auto")==
-"auto"&&d.rotate(c);this.attribute("markerUnits").valueOrDefault("strokeWidth")=="strokeWidth"&&d.scale(d.lineWidth,d.lineWidth);d.save();var e=new a.Element.svg;e.attributes.viewBox=new a.Property("viewBox",this.attribute("viewBox").value);e.attributes.refX=new a.Property("refX",this.attribute("refX").value);e.attributes.refY=new a.Property("refY",this.attribute("refY").value);e.attributes.width=new a.Property("width",this.attribute("markerWidth").value);e.attributes.height=new a.Property("height",
-this.attribute("markerHeight").value);e.attributes.fill=new a.Property("fill",this.attribute("fill").valueOrDefault("black"));e.attributes.stroke=new a.Property("stroke",this.attribute("stroke").valueOrDefault("none"));e.children=this.children;e.render(d);d.restore();this.attribute("markerUnits").valueOrDefault("strokeWidth")=="strokeWidth"&&d.scale(1/d.lineWidth,1/d.lineWidth);this.attribute("orient").valueOrDefault("auto")=="auto"&&d.rotate(-c);d.translate(-b.x,-b.y)}};a.Element.marker.prototype=
-new a.Element.ElementBase;a.Element.defs=function(c){this.base=a.Element.ElementBase;this.base(c);this.render=function(){}};a.Element.defs.prototype=new a.Element.ElementBase;a.Element.GradientBase=function(c){this.base=a.Element.ElementBase;this.base(c);this.gradientUnits=this.attribute("gradientUnits").valueOrDefault("objectBoundingBox");this.stops=[];for(c=0;c<this.children.length;c++)this.stops.push(this.children[c]);this.getGradient=function(){};this.createGradient=function(d,b){var c=this;this.attribute("xlink:href").hasValue()&&
-(c=this.attribute("xlink:href").Definition.getDefinition());for(var e=this.getGradient(d,b),f=0;f<c.stops.length;f++)e.addColorStop(c.stops[f].offset,c.stops[f].color);if(this.attribute("gradientTransform").hasValue()){c=a.ViewPort.viewPorts[0];f=new a.Element.rect;f.attributes.x=new a.Property("x",-a.MAX_VIRTUAL_PIXELS/3);f.attributes.y=new a.Property("y",-a.MAX_VIRTUAL_PIXELS/3);f.attributes.width=new a.Property("width",a.MAX_VIRTUAL_PIXELS);f.attributes.height=new a.Property("height",a.MAX_VIRTUAL_PIXELS);
-var g=new a.Element.g;g.attributes.transform=new a.Property("transform",this.attribute("gradientTransform").value);g.children=[f];f=new a.Element.svg;f.attributes.x=new a.Property("x",0);f.attributes.y=new a.Property("y",0);f.attributes.width=new a.Property("width",c.width);f.attributes.height=new a.Property("height",c.height);f.children=[g];g=document.createElement("canvas");g.width=c.width;g.height=c.height;c=g.getContext("2d");c.fillStyle=e;f.render(c);return c.createPattern(g,"no-repeat")}return e}};
-a.Element.GradientBase.prototype=new a.Element.ElementBase;a.Element.linearGradient=function(c){this.base=a.Element.GradientBase;this.base(c);this.getGradient=function(a,b){var c=b.getBoundingBox(),e=this.gradientUnits=="objectBoundingBox"?c.x()+c.width()*this.attribute("x1").numValue():this.attribute("x1").Length.toPixels("x"),f=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("y1").numValue():this.attribute("y1").Length.toPixels("y"),g=this.gradientUnits=="objectBoundingBox"?
-c.x()+c.width()*this.attribute("x2").numValue():this.attribute("x2").Length.toPixels("x"),c=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("y2").numValue():this.attribute("y2").Length.toPixels("y");return a.createLinearGradient(e,f,g,c)}};a.Element.linearGradient.prototype=new a.Element.GradientBase;a.Element.radialGradient=function(c){this.base=a.Element.GradientBase;this.base(c);this.getGradient=function(a,b){var c=b.getBoundingBox(),e=this.gradientUnits=="objectBoundingBox"?
-c.x()+c.width()*this.attribute("cx").numValue():this.attribute("cx").Length.toPixels("x"),f=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("cy").numValue():this.attribute("cy").Length.toPixels("y"),g=e,j=f;this.attribute("fx").hasValue()&&(g=this.gradientUnits=="objectBoundingBox"?c.x()+c.width()*this.attribute("fx").numValue():this.attribute("fx").Length.toPixels("x"));this.attribute("fy").hasValue()&&(j=this.gradientUnits=="objectBoundingBox"?c.y()+c.height()*this.attribute("fy").numValue():
-this.attribute("fy").Length.toPixels("y"));c=this.gradientUnits=="objectBoundingBox"?(c.width()+c.height())/2*this.attribute("r").numValue():this.attribute("r").Length.toPixels();return a.createRadialGradient(g,j,0,e,f,c)}};a.Element.radialGradient.prototype=new a.Element.GradientBase;a.Element.stop=function(c){this.base=a.Element.ElementBase;this.base(c);this.offset=this.attribute("offset").numValue();c=this.style("stop-color");this.style("stop-opacity").hasValue()&&(c=c.Color.addOpacity(this.style("stop-opacity").value));
-this.color=c.value};a.Element.stop.prototype=new a.Element.ElementBase;a.Element.AnimateBase=function(c){this.base=a.Element.ElementBase;this.base(c);a.Animations.push(this);this.duration=0;this.begin=this.attribute("begin").Time.toMilliseconds();this.maxDuration=this.begin+this.attribute("dur").Time.toMilliseconds();this.getProperty=function(){var a=this.attribute("attributeType").value,b=this.attribute("attributeName").value;return a=="CSS"?this.parent.style(b,!0):this.parent.attribute(b,!0)};this.initialValue=
-null;this.removed=!1;this.calcValue=function(){return""};this.update=function(a){if(this.initialValue==null)this.initialValue=this.getProperty().value;if(this.duration>this.maxDuration)if(this.attribute("repeatCount").value=="indefinite")this.duration=0;else return this.attribute("fill").valueOrDefault("remove")=="remove"&&!this.removed?(this.removed=!0,this.getProperty().value=this.initialValue,!0):!1;this.duration+=a;a=!1;if(this.begin<this.duration)a=this.calcValue(),this.attribute("type").hasValue()&&
-(a=this.attribute("type").value+"("+a+")"),this.getProperty().value=a,a=!0;return a};this.progress=function(){return(this.duration-this.begin)/(this.maxDuration-this.begin)}};a.Element.AnimateBase.prototype=new a.Element.ElementBase;a.Element.animate=function(c){this.base=a.Element.AnimateBase;this.base(c);this.calcValue=function(){var a=this.attribute("from").numValue(),b=this.attribute("to").numValue();return a+(b-a)*this.progress()}};a.Element.animate.prototype=new a.Element.AnimateBase;a.Element.animateColor=
-function(c){this.base=a.Element.AnimateBase;this.base(c);this.calcValue=function(){var a=new RGBColor(this.attribute("from").value),b=new RGBColor(this.attribute("to").value);if(a.ok&&b.ok){var c=a.r+(b.r-a.r)*this.progress(),e=a.g+(b.g-a.g)*this.progress(),a=a.b+(b.b-a.b)*this.progress();return"rgb("+parseInt(c,10)+","+parseInt(e,10)+","+parseInt(a,10)+")"}return this.attribute("from").value}};a.Element.animateColor.prototype=new a.Element.AnimateBase;a.Element.animateTransform=function(c){this.base=
-a.Element.animate;this.base(c)};a.Element.animateTransform.prototype=new a.Element.animate;a.Element.font=function(c){this.base=a.Element.ElementBase;this.base(c);this.horizAdvX=this.attribute("horiz-adv-x").numValue();this.isArabic=this.isRTL=!1;this.missingGlyph=this.fontFace=null;this.glyphs=[];for(c=0;c<this.children.length;c++){var d=this.children[c];if(d.type=="font-face")this.fontFace=d,d.style("font-family").hasValue()&&(a.Definitions[d.style("font-family").value]=this);else if(d.type=="missing-glyph")this.missingGlyph=
-d;else if(d.type=="glyph")d.arabicForm!=""?(this.isArabic=this.isRTL=!0,typeof this.glyphs[d.unicode]=="undefined"&&(this.glyphs[d.unicode]=[]),this.glyphs[d.unicode][d.arabicForm]=d):this.glyphs[d.unicode]=d}};a.Element.font.prototype=new a.Element.ElementBase;a.Element.fontface=function(c){this.base=a.Element.ElementBase;this.base(c);this.ascent=this.attribute("ascent").value;this.descent=this.attribute("descent").value;this.unitsPerEm=this.attribute("units-per-em").numValue()};a.Element.fontface.prototype=
-new a.Element.ElementBase;a.Element.missingglyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=0};a.Element.missingglyph.prototype=new a.Element.path;a.Element.glyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=this.attribute("horiz-adv-x").numValue();this.unicode=this.attribute("unicode").value;this.arabicForm=this.attribute("arabic-form").value};a.Element.glyph.prototype=new a.Element.path;a.Element.text=function(c){this.base=a.Element.RenderedElementBase;
-this.base(c);if(c!=null){this.children=[];for(var d=0;d<c.childNodes.length;d++){var b=c.childNodes[d];b.nodeType==1?this.addChild(b,!0):b.nodeType==3&&this.addChild(new a.Element.tspan(b),!1)}}this.baseSetContext=this.setContext;this.setContext=function(b){this.baseSetContext(b);if(this.style("dominant-baseline").hasValue())b.textBaseline=this.style("dominant-baseline").value;if(this.style("alignment-baseline").hasValue())b.textBaseline=this.style("alignment-baseline").value};this.renderChildren=
-function(b){for(var a=this.style("text-anchor").valueOrDefault("start"),c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),j=0;j<this.children.length;j++){var h=this.children[j];h.attribute("x").hasValue()?h.x=h.attribute("x").Length.toPixels("x"):(h.attribute("dx").hasValue()&&(c+=h.attribute("dx").Length.toPixels("x")),h.x=c);c=h.measureText(b);if(a!="start"&&(j==0||h.attribute("x").hasValue())){for(var l=c,o=j+1;o<this.children.length;o++){var n=this.children[o];
-if(n.attribute("x").hasValue())break;l+=n.measureText(b)}h.x-=a=="end"?l:l/2}c=h.x+c;h.attribute("y").hasValue()?h.y=h.attribute("y").Length.toPixels("y"):(h.attribute("dy").hasValue()&&(d+=h.attribute("dy").Length.toPixels("y")),h.y=d);d=h.y;h.render(b)}}};a.Element.text.prototype=new a.Element.RenderedElementBase;a.Element.TextElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getGlyph=function(a,b,c){var e=b[c],f=null;if(a.isArabic){var g="isolated";if((c==0||b[c-
-1]==" ")&&c<b.length-2&&b[c+1]!=" ")g="terminal";c>0&&b[c-1]!=" "&&c<b.length-2&&b[c+1]!=" "&&(g="medial");if(c>0&&b[c-1]!=" "&&(c==b.length-1||b[c+1]==" "))g="initial";typeof a.glyphs[e]!="undefined"&&(f=a.glyphs[e][g],f==null&&a.glyphs[e].type=="glyph"&&(f=a.glyphs[e]))}else f=a.glyphs[e];if(f==null)f=a.missingGlyph;return f};this.renderChildren=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var k=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize),
-e=this.parent.style("font-style").valueOrDefault(a.Font.Parse(a.ctx.font).fontStyle),f=this.getText();b.isRTL&&(f=f.split("").reverse().join(""));for(var g=a.ToNumberArray(this.parent.attribute("dx").value),j=0;j<f.length;j++){var h=this.getGlyph(b,f,j),l=k/b.fontFace.unitsPerEm;c.translate(this.x,this.y);c.scale(l,-l);var o=c.lineWidth;c.lineWidth=c.lineWidth*b.fontFace.unitsPerEm/k;e=="italic"&&c.transform(1,0,0.4,1,0,0);h.render(c);e=="italic"&&c.transform(1,0,-0.4,1,0,0);c.lineWidth=o;c.scale(1/
-l,-1/l);c.translate(-this.x,-this.y);this.x+=k*(h.horizAdvX||b.horizAdvX)/b.fontFace.unitsPerEm;typeof g[j]!="undefined"&&!isNaN(g[j])&&(this.x+=g[j])}}else c.strokeStyle!=""&&c.strokeText(a.compressSpaces(this.getText()),this.x,this.y),c.fillStyle!=""&&c.fillText(a.compressSpaces(this.getText()),this.x,this.y)};this.getText=function(){};this.measureText=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var c=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize),
-k=0,e=this.getText();b.isRTL&&(e=e.split("").reverse().join(""));for(var f=a.ToNumberArray(this.parent.attribute("dx").value),g=0;g<e.length;g++){var j=this.getGlyph(b,e,g);k+=(j.horizAdvX||b.horizAdvX)*c/b.fontFace.unitsPerEm;typeof f[g]!="undefined"&&!isNaN(f[g])&&(k+=f[g])}return k}b=a.compressSpaces(this.getText());if(!c.measureText)return b.length*10;c.save();this.setContext(c);b=c.measureText(b).width;c.restore();return b}};a.Element.TextElementBase.prototype=new a.Element.RenderedElementBase;
-a.Element.tspan=function(c){this.base=a.Element.TextElementBase;this.base(c);this.text=c.nodeType==3?c.nodeValue:c.childNodes.length>0?c.childNodes[0].nodeValue:c.text;this.getText=function(){return this.text}};a.Element.tspan.prototype=new a.Element.TextElementBase;a.Element.tref=function(c){this.base=a.Element.TextElementBase;this.base(c);this.getText=function(){var a=this.attribute("xlink:href").Definition.getDefinition();if(a!=null)return a.children[0].getText()}};a.Element.tref.prototype=new a.Element.TextElementBase;
-a.Element.a=function(c){this.base=a.Element.TextElementBase;this.base(c);this.hasText=!0;for(var d=0;d<c.childNodes.length;d++)if(c.childNodes[d].nodeType!=3)this.hasText=!1;this.text=this.hasText?c.childNodes[0].nodeValue:"";this.getText=function(){return this.text};this.baseRenderChildren=this.renderChildren;this.renderChildren=function(b){if(this.hasText){this.baseRenderChildren(b);var c=new a.Property("fontSize",a.Font.Parse(a.ctx.font).fontSize);a.Mouse.checkBoundingBox(this,new a.BoundingBox(this.x,
-this.y-c.Length.toPixels("y"),this.x+this.measureText(b),this.y))}else c=new a.Element.g,c.children=this.children,c.parent=this,c.render(b)};this.onclick=function(){window.open(this.attribute("xlink:href").value)};this.onmousemove=function(){a.ctx.canvas.style.cursor="pointer"}};a.Element.a.prototype=new a.Element.TextElementBase;a.Element.image=function(c){this.base=a.Element.RenderedElementBase;this.base(c);a.Images.push(this);this.img=document.createElement("img");this.loaded=!1;var d=this;this.img.onload=
-function(){d.loaded=!0};this.img.src=this.attribute("xlink:href").value;this.renderChildren=function(b){var c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),f=this.attribute("width").Length.toPixels("x"),g=this.attribute("height").Length.toPixels("y");f==0||g==0||(b.save(),b.translate(c,d),a.AspectRatio(b,this.attribute("preserveAspectRatio").value,f,this.img.width,g,this.img.height,0,0),b.drawImage(this.img,0,0),b.restore())}};a.Element.image.prototype=new a.Element.RenderedElementBase;
-a.Element.g=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getBoundingBox=function(){for(var c=new a.BoundingBox,b=0;b<this.children.length;b++)c.addBoundingBox(this.children[b].getBoundingBox());return c}};a.Element.g.prototype=new a.Element.RenderedElementBase;a.Element.symbol=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseSetContext=this.setContext;this.setContext=function(c){this.baseSetContext(c);if(this.attribute("viewBox").hasValue()){var b=
-a.ToNumberArray(this.attribute("viewBox").value),k=b[0],e=b[1];width=b[2];height=b[3];a.AspectRatio(c,this.attribute("preserveAspectRatio").value,this.attribute("width").Length.toPixels("x"),width,this.attribute("height").Length.toPixels("y"),height,k,e);a.ViewPort.SetCurrent(b[2],b[3])}}};a.Element.symbol.prototype=new a.Element.RenderedElementBase;a.Element.style=function(c){this.base=a.Element.ElementBase;this.base(c);for(var c=c.childNodes[0].nodeValue+(c.childNodes.length>1?c.childNodes[1].nodeValue:
-""),c=c.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm,""),c=a.compressSpaces(c),c=c.split("}"),d=0;d<c.length;d++)if(a.trim(c[d])!="")for(var b=c[d].split("{"),k=b[0].split(","),b=b[1].split(";"),e=0;e<k.length;e++){var f=a.trim(k[e]);if(f!=""){for(var g={},j=0;j<b.length;j++){var h=b[j].indexOf(":"),l=b[j].substr(0,h),h=b[j].substr(h+1,b[j].length-h);l!=null&&h!=null&&(g[a.trim(l)]=new a.Property(a.trim(l),a.trim(h)))}a.Styles[f]=g;if(f=="@font-face"){f=g["font-family"].value.replace(/"/g,
-"");g=g.src.value.split(",");for(j=0;j<g.length;j++)if(g[j].indexOf('format("svg")')>0){l=g[j].indexOf("url");h=g[j].indexOf(")",l);l=g[j].substr(l+5,h-l-6);l=a.parseXml(a.ajax(l)).getElementsByTagName("font");for(h=0;h<l.length;h++){var o=a.CreateElement(l[h]);a.Definitions[f]=o}}}}}};a.Element.style.prototype=new a.Element.ElementBase;a.Element.use=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.baseSetContext=this.setContext;this.setContext=function(a){this.baseSetContext(a);
-this.attribute("x").hasValue()&&a.translate(this.attribute("x").Length.toPixels("x"),0);this.attribute("y").hasValue()&&a.translate(0,this.attribute("y").Length.toPixels("y"))};this.getDefinition=function(){var a=this.attribute("xlink:href").Definition.getDefinition();if(this.attribute("width").hasValue())a.attribute("width",!0).value=this.attribute("width").value;if(this.attribute("height").hasValue())a.attribute("height",!0).value=this.attribute("height").value;return a};this.path=function(a){var b=
-this.getDefinition();b!=null&&b.path(a)};this.renderChildren=function(a){var b=this.getDefinition();b!=null&&b.render(a)}};a.Element.use.prototype=new a.Element.RenderedElementBase;a.Element.mask=function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,b){var c=this.attribute("x").Length.toPixels("x"),e=this.attribute("y").Length.toPixels("y"),f=this.attribute("width").Length.toPixels("x"),g=this.attribute("height").Length.toPixels("y"),j=b.attribute("mask").value;b.attribute("mask").value=
-"";var h=document.createElement("canvas");h.width=c+f;h.height=e+g;var l=h.getContext("2d");this.renderChildren(l);var o=document.createElement("canvas");o.width=c+f;o.height=e+g;var n=o.getContext("2d");b.render(n);n.globalCompositeOperation="destination-in";n.fillStyle=l.createPattern(h,"no-repeat");n.fillRect(0,0,c+f,e+g);a.fillStyle=n.createPattern(o,"no-repeat");a.fillRect(0,0,c+f,e+g);b.attribute("mask").value=j};this.render=function(){}};a.Element.mask.prototype=new a.Element.ElementBase;a.Element.clipPath=
-function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a){for(var b=0;b<this.children.length;b++)this.children[b].path&&(this.children[b].path(a),a.clip())};this.render=function(){}};a.Element.clipPath.prototype=new a.Element.ElementBase;a.Element.filter=function(c){this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,b){var c=b.getBoundingBox(),e=this.attribute("x").Length.toPixels("x"),f=this.attribute("y").Length.toPixels("y");if(e==0||f==0)e=c.x1,f=c.y1;var g=
-this.attribute("width").Length.toPixels("x"),j=this.attribute("height").Length.toPixels("y");if(g==0||j==0)g=c.width(),j=c.height();c=b.style("filter").value;b.style("filter").value="";var h=0.2*g,l=0.2*j,o=document.createElement("canvas");o.width=g+2*h;o.height=j+2*l;var n=o.getContext("2d");n.translate(-e+h,-f+l);b.render(n);for(var q=0;q<this.children.length;q++)this.children[q].apply(n,0,0,g+2*h,j+2*l);a.drawImage(o,0,0,g+2*h,j+2*l,e-h,f-l,g+2*h,j+2*l);b.style("filter",!0).value=c};this.render=
-function(){}};a.Element.filter.prototype=new a.Element.ElementBase;a.Element.feGaussianBlur=function(c){function d(a,c,d,f,g){for(var j=0;j<g;j++)for(var h=0;h<f;h++)for(var l=a[j*f*4+h*4+3]/255,o=0;o<4;o++){for(var n=d[0]*(l==0?255:a[j*f*4+h*4+o])*(l==0||o==3?1:l),q=1;q<d.length;q++){var p=Math.max(h-q,0),m=a[j*f*4+p*4+3]/255,p=Math.min(h+q,f-1),p=a[j*f*4+p*4+3]/255,s=d[q],r;m==0?r=255:(r=Math.max(h-q,0),r=a[j*f*4+r*4+o]);m=r*(m==0||o==3?1:m);p==0?r=255:(r=Math.min(h+q,f-1),r=a[j*f*4+r*4+o]);n+=
-s*(m+r*(p==0||o==3?1:p))}c[h*g*4+j*4+o]=n}}this.base=a.Element.ElementBase;this.base(c);this.apply=function(a,c,e,f,g){var e=this.attribute("stdDeviation").numValue(),c=a.getImageData(0,0,f,g),e=Math.max(e,0.01),j=Math.ceil(e*4)+1;mask=[];for(var h=0;h<j;h++)mask[h]=Math.exp(-0.5*(h/e)*(h/e));e=mask;j=0;for(h=1;h<e.length;h++)j+=Math.abs(e[h]);j=2*j+Math.abs(e[0]);for(h=0;h<e.length;h++)e[h]/=j;tmp=[];d(c.data,tmp,e,f,g);d(tmp,c.data,e,g,f);a.clearRect(0,0,f,g);a.putImageData(c,0,0)}};a.Element.filter.prototype=
-new a.Element.feGaussianBlur;a.Element.title=function(){};a.Element.title.prototype=new a.Element.ElementBase;a.Element.desc=function(){};a.Element.desc.prototype=new a.Element.ElementBase;a.Element.MISSING=function(a){console.log("ERROR: Element '"+a.nodeName+"' not yet implemented.")};a.Element.MISSING.prototype=new a.Element.ElementBase;a.CreateElement=function(c){var d=c.nodeName.replace(/^[^:]+:/,""),d=d.replace(/\-/g,""),b=null,b=typeof a.Element[d]!="undefined"?new a.Element[d](c):new a.Element.MISSING(c);
-b.type=c.nodeName;return b};a.load=function(c,d){a.loadXml(c,a.ajax(d))};a.loadXml=function(c,d){a.loadXmlDoc(c,a.parseXml(d))};a.loadXmlDoc=function(c,d){a.init(c);var b=function(a){for(var b=c.canvas;b;)a.x-=b.offsetLeft,a.y-=b.offsetTop,b=b.offsetParent;window.scrollX&&(a.x+=window.scrollX);window.scrollY&&(a.y+=window.scrollY);return a};if(a.opts.ignoreMouse!=!0)c.canvas.onclick=function(c){c=b(new a.Point(c!=null?c.clientX:event.clientX,c!=null?c.clientY:event.clientY));a.Mouse.onclick(c.x,c.y)},
-c.canvas.onmousemove=function(c){c=b(new a.Point(c!=null?c.clientX:event.clientX,c!=null?c.clientY:event.clientY));a.Mouse.onmousemove(c.x,c.y)};var k=a.CreateElement(d.documentElement),e=k.root=!0,f=function(){a.ViewPort.Clear();c.canvas.parentNode&&a.ViewPort.SetCurrent(c.canvas.parentNode.clientWidth,c.canvas.parentNode.clientHeight);if(a.opts.ignoreDimensions!=!0){if(k.style("width").hasValue())c.canvas.width=k.style("width").Length.toPixels("x"),c.canvas.style.width=c.canvas.width+"px";if(k.style("height").hasValue())c.canvas.height=
-k.style("height").Length.toPixels("y"),c.canvas.style.height=c.canvas.height+"px"}var b=c.canvas.clientWidth||c.canvas.width,d=c.canvas.clientHeight||c.canvas.height;a.ViewPort.SetCurrent(b,d);if(a.opts!=null&&a.opts.offsetX!=null)k.attribute("x",!0).value=a.opts.offsetX;if(a.opts!=null&&a.opts.offsetY!=null)k.attribute("y",!0).value=a.opts.offsetY;if(a.opts!=null&&a.opts.scaleWidth!=null&&a.opts.scaleHeight!=null){var f=1,g=1;k.attribute("width").hasValue()&&(f=k.attribute("width").Length.toPixels("x")/
-a.opts.scaleWidth);k.attribute("height").hasValue()&&(g=k.attribute("height").Length.toPixels("y")/a.opts.scaleHeight);k.attribute("width",!0).value=a.opts.scaleWidth;k.attribute("height",!0).value=a.opts.scaleHeight;k.attribute("viewBox",!0).value="0 0 "+b*f+" "+d*g;k.attribute("preserveAspectRatio",!0).value="none"}a.opts.ignoreClear!=!0&&c.clearRect(0,0,b,d);k.render(c);e&&(e=!1,a.opts!=null&&typeof a.opts.renderCallback=="function"&&a.opts.renderCallback())},g=!0;a.ImagesLoaded()&&(g=!1,f());
-a.intervalID=setInterval(function(){var b=!1;g&&a.ImagesLoaded()&&(g=!1,b=!0);a.opts.ignoreMouse!=!0&&(b|=a.Mouse.hasEvents());if(a.opts.ignoreAnimation!=!0)for(var c=0;c<a.Animations.length;c++)b|=a.Animations[c].update(1E3/a.FRAMERATE);a.opts!=null&&typeof a.opts.forceRedraw=="function"&&a.opts.forceRedraw()==!0&&(b=!0);b&&(f(),a.Mouse.runEvents())},1E3/a.FRAMERATE)};a.stop=function(){a.intervalID&&clearInterval(a.intervalID)};a.Mouse=new function(){this.events=[];this.hasEvents=function(){return this.events.length!=
-0};this.onclick=function(a,d){this.events.push({type:"onclick",x:a,y:d,run:function(a){if(a.onclick)a.onclick()}})};this.onmousemove=function(a,d){this.events.push({type:"onmousemove",x:a,y:d,run:function(a){if(a.onmousemove)a.onmousemove()}})};this.eventElements=[];this.checkPath=function(a,d){for(var b=0;b<this.events.length;b++){var k=this.events[b];d.isPointInPath&&d.isPointInPath(k.x,k.y)&&(this.eventElements[b]=a)}};this.checkBoundingBox=function(a,d){for(var b=0;b<this.events.length;b++){var k=
-this.events[b];d.isPointInBox(k.x,k.y)&&(this.eventElements[b]=a)}};this.runEvents=function(){a.ctx.canvas.style.cursor="";for(var c=0;c<this.events.length;c++)for(var d=this.events[c],b=this.eventElements[c];b;)d.run(b),b=b.parent;this.events=[];this.eventElements=[]}};return a}this.canvg=function(a,c,d){if(a==null&&c==null&&d==null)for(var c=document.getElementsByTagName("svg"),b=0;b<c.length;b++){a=c[b];d=document.createElement("canvas");d.width=a.clientWidth;d.height=a.clientHeight;a.parentNode.insertBefore(d,
-a);a.parentNode.removeChild(a);var k=document.createElement("div");k.appendChild(a);canvg(d,k.innerHTML)}else d=d||{},typeof a=="string"&&(a=document.getElementById(a)),a.svg==null?(b=m(),a.svg=b):(b=a.svg,b.stop()),b.opts=d,a=a.getContext("2d"),typeof c.documentElement!="undefined"?b.loadXmlDoc(a,c):c.substr(0,1)=="<"?b.loadXml(a,c):b.load(a,c)}})();
-if(CanvasRenderingContext2D)CanvasRenderingContext2D.prototype.drawSvg=function(m,a,c,d,b){canvg(this.canvas,m,{ignoreMouse:!0,ignoreAnimation:!0,ignoreDimensions:!0,ignoreClear:!0,offsetX:a,offsetY:c,scaleWidth:d,scaleHeight:b})};
-(function(m){var a=m.css,c=m.CanVGRenderer,d=m.SVGRenderer,b=m.extend,k=m.merge,e=m.addEvent,f=m.createElement,g=m.discardElement;b(c.prototype,d.prototype);b(c.prototype,{create:function(a,b,c,d){this.setContainer(b,c,d);this.configure(a)},setContainer:function(a,b,c){var d=a.style,e=a.parentNode,g=d.left,d=d.top,k=a.offsetWidth,m=a.offsetHeight,s={visibility:"hidden",position:"absolute"};this.init.apply(this,[a,b,c]);this.canvas=f("canvas",{width:k,height:m},{position:"relative",left:g,top:d},a);
-this.ttLine=f("div",null,s,e);this.ttDiv=f("div",null,s,e);this.ttTimer=void 0;this.hiddenSvg=a=f("div",{width:k,height:m},{visibility:"hidden",left:g,top:d},e);a.appendChild(this.box)},configure:function(b){var c=this,d=b.options.tooltip,f=d.borderWidth,g=c.ttDiv,m=d.style,p=c.ttLine,t=parseInt(m.padding,10),m=k(m,{padding:t+"px","background-color":d.backgroundColor,"border-style":"solid","border-width":f+"px","border-radius":d.borderRadius+"px"});d.shadow&&(m=k(m,{"box-shadow":"1px 1px 3px gray",
-"-webkit-box-shadow":"1px 1px 3px gray"}));a(g,m);a(p,{"border-left":"1px solid darkgray"});e(b,"tooltipRefresh",function(d){var e=b.container,f=e.offsetLeft,e=e.offsetTop,k;g.innerHTML=d.text;k=b.tooltip.getPosition(g.offsetWidth,g.offsetHeight,{plotX:d.x,plotY:d.y});a(g,{visibility:"visible",left:k.x+"px",top:k.y+"px","border-color":d.borderColor});a(p,{visibility:"visible",left:f+d.x+"px",top:e+b.plotTop+"px",height:b.plotHeight+"px"});c.ttTimer!==void 0&&clearTimeout(c.ttTimer);c.ttTimer=setTimeout(function(){a(g,
-{visibility:"hidden"});a(p,{visibility:"hidden"})},3E3)})},destroy:function(){g(this.canvas);this.ttTimer!==void 0&&clearTimeout(this.ttTimer);g(this.ttLine);g(this.ttDiv);g(this.hiddenSvg);return d.prototype.destroy.apply(this)},color:function(a,b,c){a&&a.linearGradient&&(a=a.stops[a.stops.length-1][1]);return d.prototype.color.call(this,a,b,c)},draw:function(){window.canvg(this.canvas,this.hiddenSvg.innerHTML)}})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/canvas-tools.src.js b/apps/static/js/plugins/highcharts/modules/canvas-tools.src.js
deleted file mode 100644
index 2d3f8ea60..000000000
--- a/apps/static/js/plugins/highcharts/modules/canvas-tools.src.js
+++ /dev/null
@@ -1,3113 +0,0 @@
-/**
- * @license A class to parse color values
- * @author Stoyan Stefanov <sstoo@gmail.com>
- * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
- * Use it if you like it
- *
- */
-function RGBColor(color_string)
-{
-    this.ok = false;
-
-    // strip any leading #
-    if (color_string.charAt(0) == '#') { // remove # if any
-        color_string = color_string.substr(1,6);
-    }
-
-    color_string = color_string.replace(/ /g,'');
-    color_string = color_string.toLowerCase();
-
-    // before getting into regexps, try simple matches
-    // and overwrite the input
-    var simple_colors = {
-        aliceblue: 'f0f8ff',
-        antiquewhite: 'faebd7',
-        aqua: '00ffff',
-        aquamarine: '7fffd4',
-        azure: 'f0ffff',
-        beige: 'f5f5dc',
-        bisque: 'ffe4c4',
-        black: '000000',
-        blanchedalmond: 'ffebcd',
-        blue: '0000ff',
-        blueviolet: '8a2be2',
-        brown: 'a52a2a',
-        burlywood: 'deb887',
-        cadetblue: '5f9ea0',
-        chartreuse: '7fff00',
-        chocolate: 'd2691e',
-        coral: 'ff7f50',
-        cornflowerblue: '6495ed',
-        cornsilk: 'fff8dc',
-        crimson: 'dc143c',
-        cyan: '00ffff',
-        darkblue: '00008b',
-        darkcyan: '008b8b',
-        darkgoldenrod: 'b8860b',
-        darkgray: 'a9a9a9',
-        darkgreen: '006400',
-        darkkhaki: 'bdb76b',
-        darkmagenta: '8b008b',
-        darkolivegreen: '556b2f',
-        darkorange: 'ff8c00',
-        darkorchid: '9932cc',
-        darkred: '8b0000',
-        darksalmon: 'e9967a',
-        darkseagreen: '8fbc8f',
-        darkslateblue: '483d8b',
-        darkslategray: '2f4f4f',
-        darkturquoise: '00ced1',
-        darkviolet: '9400d3',
-        deeppink: 'ff1493',
-        deepskyblue: '00bfff',
-        dimgray: '696969',
-        dodgerblue: '1e90ff',
-        feldspar: 'd19275',
-        firebrick: 'b22222',
-        floralwhite: 'fffaf0',
-        forestgreen: '228b22',
-        fuchsia: 'ff00ff',
-        gainsboro: 'dcdcdc',
-        ghostwhite: 'f8f8ff',
-        gold: 'ffd700',
-        goldenrod: 'daa520',
-        gray: '808080',
-        green: '008000',
-        greenyellow: 'adff2f',
-        honeydew: 'f0fff0',
-        hotpink: 'ff69b4',
-        indianred : 'cd5c5c',
-        indigo : '4b0082',
-        ivory: 'fffff0',
-        khaki: 'f0e68c',
-        lavender: 'e6e6fa',
-        lavenderblush: 'fff0f5',
-        lawngreen: '7cfc00',
-        lemonchiffon: 'fffacd',
-        lightblue: 'add8e6',
-        lightcoral: 'f08080',
-        lightcyan: 'e0ffff',
-        lightgoldenrodyellow: 'fafad2',
-        lightgrey: 'd3d3d3',
-        lightgreen: '90ee90',
-        lightpink: 'ffb6c1',
-        lightsalmon: 'ffa07a',
-        lightseagreen: '20b2aa',
-        lightskyblue: '87cefa',
-        lightslateblue: '8470ff',
-        lightslategray: '778899',
-        lightsteelblue: 'b0c4de',
-        lightyellow: 'ffffe0',
-        lime: '00ff00',
-        limegreen: '32cd32',
-        linen: 'faf0e6',
-        magenta: 'ff00ff',
-        maroon: '800000',
-        mediumaquamarine: '66cdaa',
-        mediumblue: '0000cd',
-        mediumorchid: 'ba55d3',
-        mediumpurple: '9370d8',
-        mediumseagreen: '3cb371',
-        mediumslateblue: '7b68ee',
-        mediumspringgreen: '00fa9a',
-        mediumturquoise: '48d1cc',
-        mediumvioletred: 'c71585',
-        midnightblue: '191970',
-        mintcream: 'f5fffa',
-        mistyrose: 'ffe4e1',
-        moccasin: 'ffe4b5',
-        navajowhite: 'ffdead',
-        navy: '000080',
-        oldlace: 'fdf5e6',
-        olive: '808000',
-        olivedrab: '6b8e23',
-        orange: 'ffa500',
-        orangered: 'ff4500',
-        orchid: 'da70d6',
-        palegoldenrod: 'eee8aa',
-        palegreen: '98fb98',
-        paleturquoise: 'afeeee',
-        palevioletred: 'd87093',
-        papayawhip: 'ffefd5',
-        peachpuff: 'ffdab9',
-        peru: 'cd853f',
-        pink: 'ffc0cb',
-        plum: 'dda0dd',
-        powderblue: 'b0e0e6',
-        purple: '800080',
-        red: 'ff0000',
-        rosybrown: 'bc8f8f',
-        royalblue: '4169e1',
-        saddlebrown: '8b4513',
-        salmon: 'fa8072',
-        sandybrown: 'f4a460',
-        seagreen: '2e8b57',
-        seashell: 'fff5ee',
-        sienna: 'a0522d',
-        silver: 'c0c0c0',
-        skyblue: '87ceeb',
-        slateblue: '6a5acd',
-        slategray: '708090',
-        snow: 'fffafa',
-        springgreen: '00ff7f',
-        steelblue: '4682b4',
-        tan: 'd2b48c',
-        teal: '008080',
-        thistle: 'd8bfd8',
-        tomato: 'ff6347',
-        turquoise: '40e0d0',
-        violet: 'ee82ee',
-        violetred: 'd02090',
-        wheat: 'f5deb3',
-        white: 'ffffff',
-        whitesmoke: 'f5f5f5',
-        yellow: 'ffff00',
-        yellowgreen: '9acd32'
-    };
-    for (var key in simple_colors) {
-        if (color_string == key) {
-            color_string = simple_colors[key];
-        }
-    }
-    // emd of simple type-in colors
-
-    // array of color definition objects
-    var color_defs = [
-        {
-            re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
-            example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1]),
-                    parseInt(bits[2]),
-                    parseInt(bits[3])
-                ];
-            }
-        },
-        {
-            re: /^(\w{2})(\w{2})(\w{2})$/,
-            example: ['#00ff00', '336699'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1], 16),
-                    parseInt(bits[2], 16),
-                    parseInt(bits[3], 16)
-                ];
-            }
-        },
-        {
-            re: /^(\w{1})(\w{1})(\w{1})$/,
-            example: ['#fb0', 'f0f'],
-            process: function (bits){
-                return [
-                    parseInt(bits[1] + bits[1], 16),
-                    parseInt(bits[2] + bits[2], 16),
-                    parseInt(bits[3] + bits[3], 16)
-                ];
-            }
-        }
-    ];
-
-    // search through the definitions to find a match
-    for (var i = 0; i < color_defs.length; i++) {
-        var re = color_defs[i].re;
-        var processor = color_defs[i].process;
-        var bits = re.exec(color_string);
-        if (bits) {
-            channels = processor(bits);
-            this.r = channels[0];
-            this.g = channels[1];
-            this.b = channels[2];
-            this.ok = true;
-        }
-
-    }
-
-    // validate/cleanup values
-    this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
-    this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
-    this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
-
-    // some getters
-    this.toRGB = function () {
-        return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
-    }
-    this.toHex = function () {
-        var r = this.r.toString(16);
-        var g = this.g.toString(16);
-        var b = this.b.toString(16);
-        if (r.length == 1) r = '0' + r;
-        if (g.length == 1) g = '0' + g;
-        if (b.length == 1) b = '0' + b;
-        return '#' + r + g + b;
-    }
-
-    // help
-    this.getHelpXML = function () {
-
-        var examples = new Array();
-        // add regexps
-        for (var i = 0; i < color_defs.length; i++) {
-            var example = color_defs[i].example;
-            for (var j = 0; j < example.length; j++) {
-                examples[examples.length] = example[j];
-            }
-        }
-        // add type-in colors
-        for (var sc in simple_colors) {
-            examples[examples.length] = sc;
-        }
-
-        var xml = document.createElement('ul');
-        xml.setAttribute('id', 'rgbcolor-examples');
-        for (var i = 0; i < examples.length; i++) {
-            try {
-                var list_item = document.createElement('li');
-                var list_color = new RGBColor(examples[i]);
-                var example_div = document.createElement('div');
-                example_div.style.cssText =
-                        'margin: 3px; '
-                        + 'border: 1px solid black; '
-                        + 'background:' + list_color.toHex() + '; '
-                        + 'color:' + list_color.toHex()
-                ;
-                example_div.appendChild(document.createTextNode('test'));
-                var list_item_value = document.createTextNode(
-                    ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
-                );
-                list_item.appendChild(example_div);
-                list_item.appendChild(list_item_value);
-                xml.appendChild(list_item);
-
-            } catch(e){}
-        }
-        return xml;
-
-    }
-
-}
-
-/**
- * @license canvg.js - Javascript SVG parser and renderer on Canvas
- * MIT Licensed 
- * Gabe Lerner (gabelerner@gmail.com)
- * http://code.google.com/p/canvg/
- *
- * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
- *
- */
-if(!window.console) {
-	window.console = {};
-	window.console.log = function(str) {};
-	window.console.dir = function(str) {};
-}
-
-if(!Array.prototype.indexOf){
-	Array.prototype.indexOf = function(obj){
-		for(var i=0; i<this.length; i++){
-			if(this[i]==obj){
-				return i;
-			}
-		}
-		return -1;
-	}
-}
-
-(function(){
-	// canvg(target, s)
-	// empty parameters: replace all 'svg' elements on page with 'canvas' elements
-	// target: canvas element or the id of a canvas element
-	// s: svg string, url to svg file, or xml document
-	// opts: optional hash of options
-	//		 ignoreMouse: true => ignore mouse events
-	//		 ignoreAnimation: true => ignore animations
-	//		 ignoreDimensions: true => does not try to resize canvas
-	//		 ignoreClear: true => does not clear canvas
-	//		 offsetX: int => draws at a x offset
-	//		 offsetY: int => draws at a y offset
-	//		 scaleWidth: int => scales horizontally to width
-	//		 scaleHeight: int => scales vertically to height
-	//		 renderCallback: function => will call the function after the first render is completed
-	//		 forceRedraw: function => will call the function on every frame, if it returns true, will redraw
-	this.canvg = function (target, s, opts) {
-		// no parameters
-		if (target == null && s == null && opts == null) {
-			var svgTags = document.getElementsByTagName('svg');
-			for (var i=0; i<svgTags.length; i++) {
-				var svgTag = svgTags[i];
-				var c = document.createElement('canvas');
-				c.width = svgTag.clientWidth;
-				c.height = svgTag.clientHeight;
-				svgTag.parentNode.insertBefore(c, svgTag);
-				svgTag.parentNode.removeChild(svgTag);
-				var div = document.createElement('div');
-				div.appendChild(svgTag);
-				canvg(c, div.innerHTML);
-			}
-			return;
-		}	
-		opts = opts || {};
-	
-		if (typeof target == 'string') {
-			target = document.getElementById(target);
-		}
-		
-		// reuse class per canvas
-		var svg;
-		if (target.svg == null) {
-			svg = build();
-			target.svg = svg;
-		}
-		else {
-			svg = target.svg;
-			svg.stop();
-		}
-		svg.opts = opts;
-		
-		var ctx = target.getContext('2d');
-		if (typeof(s.documentElement) != 'undefined') {
-			// load from xml doc
-			svg.loadXmlDoc(ctx, s);
-		}
-		else if (s.substr(0,1) == '<') {
-			// load from xml string
-			svg.loadXml(ctx, s);
-		}
-		else {
-			// load from url
-			svg.load(ctx, s);
-		}
-	}
-
-	function build() {
-		var svg = { };
-		
-		svg.FRAMERATE = 30;
-		svg.MAX_VIRTUAL_PIXELS = 30000;
-		
-		// globals
-		svg.init = function(ctx) {
-			svg.Definitions = {};
-			svg.Styles = {};
-			svg.Animations = [];
-			svg.Images = [];
-			svg.ctx = ctx;
-			svg.ViewPort = new (function () {
-				this.viewPorts = [];
-				this.Clear = function() { this.viewPorts = []; }
-				this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
-				this.RemoveCurrent = function() { this.viewPorts.pop(); }
-				this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
-				this.width = function() { return this.Current().width; }
-				this.height = function() { return this.Current().height; }
-				this.ComputeSize = function(d) {
-					if (d != null && typeof(d) == 'number') return d;
-					if (d == 'x') return this.width();
-					if (d == 'y') return this.height();
-					return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);			
-				}
-			});
-		}
-		svg.init();
-		
-		// images loaded
-		svg.ImagesLoaded = function() { 
-			for (var i=0; i<svg.Images.length; i++) {
-				if (!svg.Images[i].loaded) return false;
-			}
-			return true;
-		}
-
-		// trim
-		svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
-		
-		// compress spaces
-		svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
-		
-		// ajax
-		svg.ajax = function(url) {
-			var AJAX;
-			if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
-			else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
-			if(AJAX){
-			   AJAX.open('GET',url,false);
-			   AJAX.send(null);
-			   return AJAX.responseText;
-			}
-			return null;
-		} 
-		
-		// parse xml
-		svg.parseXml = function(xml) {
-			if (window.DOMParser)
-			{
-				var parser = new DOMParser();
-				return parser.parseFromString(xml, 'text/xml');
-			}
-			else 
-			{
-				xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
-				var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
-				xmlDoc.async = 'false';
-				xmlDoc.loadXML(xml); 
-				return xmlDoc;
-			}		
-		}
-		
-		svg.Property = function(name, value) {
-			this.name = name;
-			this.value = value;
-			
-			this.hasValue = function() {
-				return (this.value != null && this.value !== '');
-			}
-							
-			// return the numerical value of the property
-			this.numValue = function() {
-				if (!this.hasValue()) return 0;
-				
-				var n = parseFloat(this.value);
-				if ((this.value + '').match(/%$/)) {
-					n = n / 100.0;
-				}
-				return n;
-			}
-			
-			this.valueOrDefault = function(def) {
-				if (this.hasValue()) return this.value;
-				return def;
-			}
-			
-			this.numValueOrDefault = function(def) {
-				if (this.hasValue()) return this.numValue();
-				return def;
-			}
-			
-			/* EXTENSIONS */
-			var that = this;
-			
-			// color extensions
-			this.Color = {
-				// augment the current color value with the opacity
-				addOpacity: function(opacity) {
-					var newValue = that.value;
-					if (opacity != null && opacity != '') {
-						var color = new RGBColor(that.value);
-						if (color.ok) {
-							newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
-						}
-					}
-					return new svg.Property(that.name, newValue);
-				}
-			}
-			
-			// definition extensions
-			this.Definition = {
-				// get the definition from the definitions table
-				getDefinition: function() {
-					var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
-					return svg.Definitions[name];
-				},
-				
-				isUrl: function() {
-					return that.value.indexOf('url(') == 0
-				},
-				
-				getFillStyle: function(e) {
-					var def = this.getDefinition();
-					
-					// gradient
-					if (def != null && def.createGradient) {
-						return def.createGradient(svg.ctx, e);
-					}
-					
-					// pattern
-					if (def != null && def.createPattern) {
-						return def.createPattern(svg.ctx, e);
-					}
-					
-					return null;
-				}
-			}
-			
-			// length extensions
-			this.Length = {
-				DPI: function(viewPort) {
-					return 96.0; // TODO: compute?
-				},
-				
-				EM: function(viewPort) {
-					var em = 12;
-					
-					var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
-					if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
-					
-					return em;
-				},
-			
-				// get the length as pixels
-				toPixels: function(viewPort) {
-					if (!that.hasValue()) return 0;
-					var s = that.value+'';
-					if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
-					if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
-					if (s.match(/px$/)) return that.numValue();
-					if (s.match(/pt$/)) return that.numValue() * 1.25;
-					if (s.match(/pc$/)) return that.numValue() * 15;
-					if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
-					if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
-					if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
-					if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
-					return that.numValue();
-				}
-			}
-			
-			// time extensions
-			this.Time = {
-				// get the time as milliseconds
-				toMilliseconds: function() {
-					if (!that.hasValue()) return 0;
-					var s = that.value+'';
-					if (s.match(/s$/)) return that.numValue() * 1000;
-					if (s.match(/ms$/)) return that.numValue();
-					return that.numValue();
-				}
-			}
-			
-			// angle extensions
-			this.Angle = {
-				// get the angle as radians
-				toRadians: function() {
-					if (!that.hasValue()) return 0;
-					var s = that.value+'';
-					if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
-					if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
-					if (s.match(/rad$/)) return that.numValue();
-					return that.numValue() * (Math.PI / 180.0);
-				}
-			}
-		}
-		
-		// fonts
-		svg.Font = new (function() {
-			this.Styles = ['normal','italic','oblique','inherit'];
-			this.Variants = ['normal','small-caps','inherit'];
-			this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
-			
-			this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) { 
-				var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
-				return { 
-					fontFamily: fontFamily || f.fontFamily, 
-					fontSize: fontSize || f.fontSize, 
-					fontStyle: fontStyle || f.fontStyle, 
-					fontWeight: fontWeight || f.fontWeight, 
-					fontVariant: fontVariant || f.fontVariant,
-					toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') } 
-				} 
-			}
-			
-			var that = this;
-			this.Parse = function(s) {
-				var f = {};
-				var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
-				var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
-				var ff = '';
-				for (var i=0; i<d.length; i++) {
-					if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
-					else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true;	}
-					else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) {	if (d[i] != 'inherit') f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; }
-					else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
-					else { if (d[i] != 'inherit') ff += d[i]; }
-				} if (ff != '') f.fontFamily = ff;
-				return f;
-			}
-		});
-		
-		// points and paths
-		svg.ToNumberArray = function(s) {
-			var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
-			for (var i=0; i<a.length; i++) {
-				a[i] = parseFloat(a[i]);
-			}
-			return a;
-		}		
-		svg.Point = function(x, y) {
-			this.x = x;
-			this.y = y;
-			
-			this.angleTo = function(p) {
-				return Math.atan2(p.y - this.y, p.x - this.x);
-			}
-			
-			this.applyTransform = function(v) {
-				var xp = this.x * v[0] + this.y * v[2] + v[4];
-				var yp = this.x * v[1] + this.y * v[3] + v[5];
-				this.x = xp;
-				this.y = yp;
-			}
-		}
-		svg.CreatePoint = function(s) {
-			var a = svg.ToNumberArray(s);
-			return new svg.Point(a[0], a[1]);
-		}
-		svg.CreatePath = function(s) {
-			var a = svg.ToNumberArray(s);
-			var path = [];
-			for (var i=0; i<a.length; i+=2) {
-				path.push(new svg.Point(a[i], a[i+1]));
-			}
-			return path;
-		}
-		
-		// bounding box
-		svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
-			this.x1 = Number.NaN;
-			this.y1 = Number.NaN;
-			this.x2 = Number.NaN;
-			this.y2 = Number.NaN;
-			
-			this.x = function() { return this.x1; }
-			this.y = function() { return this.y1; }
-			this.width = function() { return this.x2 - this.x1; }
-			this.height = function() { return this.y2 - this.y1; }
-			
-			this.addPoint = function(x, y) {	
-				if (x != null) {
-					if (isNaN(this.x1) || isNaN(this.x2)) {
-						this.x1 = x;
-						this.x2 = x;
-					}
-					if (x < this.x1) this.x1 = x;
-					if (x > this.x2) this.x2 = x;
-				}
-			
-				if (y != null) {
-					if (isNaN(this.y1) || isNaN(this.y2)) {
-						this.y1 = y;
-						this.y2 = y;
-					}
-					if (y < this.y1) this.y1 = y;
-					if (y > this.y2) this.y2 = y;
-				}
-			}			
-			this.addX = function(x) { this.addPoint(x, null); }
-			this.addY = function(y) { this.addPoint(null, y); }
-			
-			this.addBoundingBox = function(bb) {
-				this.addPoint(bb.x1, bb.y1);
-				this.addPoint(bb.x2, bb.y2);
-			}
-			
-			this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
-				var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
-				var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
-				var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
-				var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
-				this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y,	cp2y, p2x, p2y);
-			}
-			
-			this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
-				// from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
-				var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
-				this.addPoint(p0[0], p0[1]);
-				this.addPoint(p3[0], p3[1]);
-				
-				for (i=0; i<=1; i++) {
-					var f = function(t) { 
-						return Math.pow(1-t, 3) * p0[i]
-						+ 3 * Math.pow(1-t, 2) * t * p1[i]
-						+ 3 * (1-t) * Math.pow(t, 2) * p2[i]
-						+ Math.pow(t, 3) * p3[i];
-					}
-					
-					var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
-					var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
-					var c = 3 * p1[i] - 3 * p0[i];
-					
-					if (a == 0) {
-						if (b == 0) continue;
-						var t = -c / b;
-						if (0 < t && t < 1) {
-							if (i == 0) this.addX(f(t));
-							if (i == 1) this.addY(f(t));
-						}
-						continue;
-					}
-					
-					var b2ac = Math.pow(b, 2) - 4 * c * a;
-					if (b2ac < 0) continue;
-					var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
-					if (0 < t1 && t1 < 1) {
-						if (i == 0) this.addX(f(t1));
-						if (i == 1) this.addY(f(t1));
-					}
-					var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
-					if (0 < t2 && t2 < 1) {
-						if (i == 0) this.addX(f(t2));
-						if (i == 1) this.addY(f(t2));
-					}
-				}
-			}
-			
-			this.isPointInBox = function(x, y) {
-				return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
-			}
-			
-			this.addPoint(x1, y1);
-			this.addPoint(x2, y2);
-		}
-		
-		// transforms
-		svg.Transform = function(v) {	
-			var that = this;
-			this.Type = {}
-		
-			// translate
-			this.Type.translate = function(s) {
-				this.p = svg.CreatePoint(s);			
-				this.apply = function(ctx) {
-					ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
-				}
-				this.applyToPoint = function(p) {
-					p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
-				}
-			}
-			
-			// rotate
-			this.Type.rotate = function(s) {
-				var a = svg.ToNumberArray(s);
-				this.angle = new svg.Property('angle', a[0]);
-				this.cx = a[1] || 0;
-				this.cy = a[2] || 0;
-				this.apply = function(ctx) {
-					ctx.translate(this.cx, this.cy);
-					ctx.rotate(this.angle.Angle.toRadians());
-					ctx.translate(-this.cx, -this.cy);
-				}
-				this.applyToPoint = function(p) {
-					var a = this.angle.Angle.toRadians();
-					p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
-					p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
-					p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
-				}			
-			}
-			
-			this.Type.scale = function(s) {
-				this.p = svg.CreatePoint(s);
-				this.apply = function(ctx) {
-					ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
-				}
-				this.applyToPoint = function(p) {
-					p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
-				}				
-			}
-			
-			this.Type.matrix = function(s) {
-				this.m = svg.ToNumberArray(s);
-				this.apply = function(ctx) {
-					ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
-				}
-				this.applyToPoint = function(p) {
-					p.applyTransform(this.m);
-				}					
-			}
-			
-			this.Type.SkewBase = function(s) {
-				this.base = that.Type.matrix;
-				this.base(s);
-				this.angle = new svg.Property('angle', s);
-			}
-			this.Type.SkewBase.prototype = new this.Type.matrix;
-			
-			this.Type.skewX = function(s) {
-				this.base = that.Type.SkewBase;
-				this.base(s);
-				this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
-			}
-			this.Type.skewX.prototype = new this.Type.SkewBase;
-			
-			this.Type.skewY = function(s) {
-				this.base = that.Type.SkewBase;
-				this.base(s);
-				this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
-			}
-			this.Type.skewY.prototype = new this.Type.SkewBase;
-		
-			this.transforms = [];
-			
-			this.apply = function(ctx) {
-				for (var i=0; i<this.transforms.length; i++) {
-					this.transforms[i].apply(ctx);
-				}
-			}
-			
-			this.applyToPoint = function(p) {
-				for (var i=0; i<this.transforms.length; i++) {
-					this.transforms[i].applyToPoint(p);
-				}
-			}
-			
-			var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
-			for (var i=0; i<data.length; i++) {
-				var type = data[i].split('(')[0];
-				var s = data[i].split('(')[1].replace(')','');
-				var transform = new this.Type[type](s);
-				this.transforms.push(transform);
-			}
-		}
-		
-		// aspect ratio
-		svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
-			// aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
-			aspectRatio = svg.compressSpaces(aspectRatio);
-			aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
-			var align = aspectRatio.split(' ')[0] || 'xMidYMid';
-			var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';					
-	
-			// calculate scale
-			var scaleX = width / desiredWidth;
-			var scaleY = height / desiredHeight;
-			var scaleMin = Math.min(scaleX, scaleY);
-			var scaleMax = Math.max(scaleX, scaleY);
-			if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
-			if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }	
-			
-			refX = new svg.Property('refX', refX);
-			refY = new svg.Property('refY', refY);
-			if (refX.hasValue() && refY.hasValue()) {				
-				ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
-			} 
-			else {					
-				// align
-				if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0); 
-				if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0); 
-				if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0); 
-				if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight); 
-			}
-			
-			// scale
-			if (align == 'none') ctx.scale(scaleX, scaleY);
-			else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin); 
-			else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax); 	
-			
-			// translate
-			ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);			
-		}
-		
-		// elements
-		svg.Element = {}
-		
-		svg.Element.ElementBase = function(node) {	
-			this.attributes = {};
-			this.styles = {};
-			this.children = [];
-			
-			// get or create attribute
-			this.attribute = function(name, createIfNotExists) {
-				var a = this.attributes[name];
-				if (a != null) return a;
-							
-				a = new svg.Property(name, '');
-				if (createIfNotExists == true) this.attributes[name] = a;
-				return a;
-			}
-			
-			// get or create style, crawls up node tree
-			this.style = function(name, createIfNotExists) {
-				var s = this.styles[name];
-				if (s != null) return s;
-				
-				var a = this.attribute(name);
-				if (a != null && a.hasValue()) {
-					return a;
-				}
-				
-				var p = this.parent;
-				if (p != null) {
-					var ps = p.style(name);
-					if (ps != null && ps.hasValue()) {
-						return ps;
-					}
-				}
-					
-				s = new svg.Property(name, '');
-				if (createIfNotExists == true) this.styles[name] = s;
-				return s;
-			}
-			
-			// base render
-			this.render = function(ctx) {
-				// don't render display=none
-				if (this.style('display').value == 'none') return;
-				
-				// don't render visibility=hidden
-				if (this.attribute('visibility').value == 'hidden') return;
-			
-				ctx.save();
-					this.setContext(ctx);
-						// mask
-						if (this.attribute('mask').hasValue()) {
-							var mask = this.attribute('mask').Definition.getDefinition();
-							if (mask != null) mask.apply(ctx, this);
-						}
-						else if (this.style('filter').hasValue()) {
-							var filter = this.style('filter').Definition.getDefinition();
-							if (filter != null) filter.apply(ctx, this);
-						}
-						else this.renderChildren(ctx);				
-					this.clearContext(ctx);
-				ctx.restore();
-			}
-			
-			// base set context
-			this.setContext = function(ctx) {
-				// OVERRIDE ME!
-			}
-			
-			// base clear context
-			this.clearContext = function(ctx) {
-				// OVERRIDE ME!
-			}			
-			
-			// base render children
-			this.renderChildren = function(ctx) {
-				for (var i=0; i<this.children.length; i++) {
-					this.children[i].render(ctx);
-				}
-			}
-			
-			this.addChild = function(childNode, create) {
-				var child = childNode;
-				if (create) child = svg.CreateElement(childNode);
-				child.parent = this;
-				this.children.push(child);			
-			}
-				
-			if (node != null && node.nodeType == 1) { //ELEMENT_NODE
-				// add children
-				for (var i=0; i<node.childNodes.length; i++) {
-					var childNode = node.childNodes[i];
-					if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
-				}
-				
-				// add attributes
-				for (var i=0; i<node.attributes.length; i++) {
-					var attribute = node.attributes[i];
-					this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
-				}
-										
-				// add tag styles
-				var styles = svg.Styles[node.nodeName];
-				if (styles != null) {
-					for (var name in styles) {
-						this.styles[name] = styles[name];
-					}
-				}					
-				
-				// add class styles
-				if (this.attribute('class').hasValue()) {
-					var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
-					for (var j=0; j<classes.length; j++) {
-						styles = svg.Styles['.'+classes[j]];
-						if (styles != null) {
-							for (var name in styles) {
-								this.styles[name] = styles[name];
-							}
-						}
-						styles = svg.Styles[node.nodeName+'.'+classes[j]];
-						if (styles != null) {
-							for (var name in styles) {
-								this.styles[name] = styles[name];
-							}
-						}
-					}
-				}
-				
-				// add inline styles
-				if (this.attribute('style').hasValue()) {
-					var styles = this.attribute('style').value.split(';');
-					for (var i=0; i<styles.length; i++) {
-						if (svg.trim(styles[i]) != '') {
-							var style = styles[i].split(':');
-							var name = svg.trim(style[0]);
-							var value = svg.trim(style[1]);
-							this.styles[name] = new svg.Property(name, value);
-						}
-					}
-				}	
-
-				// add id
-				if (this.attribute('id').hasValue()) {
-					if (svg.Definitions[this.attribute('id').value] == null) {
-						svg.Definitions[this.attribute('id').value] = this;
-					}
-				}
-			}
-		}
-		
-		svg.Element.RenderedElementBase = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.setContext = function(ctx) {
-				// fill
-				if (this.style('fill').Definition.isUrl()) {
-					var fs = this.style('fill').Definition.getFillStyle(this);
-					if (fs != null) ctx.fillStyle = fs;
-				}
-				else if (this.style('fill').hasValue()) {
-					var fillStyle = this.style('fill');
-					if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
-					ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
-				}
-									
-				// stroke
-				if (this.style('stroke').Definition.isUrl()) {
-					var fs = this.style('stroke').Definition.getFillStyle(this);
-					if (fs != null) ctx.strokeStyle = fs;
-				}
-				else if (this.style('stroke').hasValue()) {
-					var strokeStyle = this.style('stroke');
-					if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
-					ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
-				}
-				if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
-				if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
-				if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
-				if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
-
-				// font
-				if (typeof(ctx.font) != 'undefined') {
-					ctx.font = svg.Font.CreateFont( 
-						this.style('font-style').value, 
-						this.style('font-variant').value, 
-						this.style('font-weight').value, 
-						this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '', 
-						this.style('font-family').value).toString();
-				}
-				
-				// transform
-				if (this.attribute('transform').hasValue()) { 
-					var transform = new svg.Transform(this.attribute('transform').value);
-					transform.apply(ctx);
-				}
-				
-				// clip
-				if (this.attribute('clip-path').hasValue()) {
-					var clip = this.attribute('clip-path').Definition.getDefinition();
-					if (clip != null) clip.apply(ctx);
-				}
-				
-				// opacity
-				if (this.style('opacity').hasValue()) {
-					ctx.globalAlpha = this.style('opacity').numValue();
-				}
-			}		
-		}
-		svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
-		
-		svg.Element.PathElementBase = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.path = function(ctx) {
-				if (ctx != null) ctx.beginPath();
-				return new svg.BoundingBox();
-			}
-			
-			this.renderChildren = function(ctx) {
-				this.path(ctx);
-				svg.Mouse.checkPath(this, ctx);
-				if (ctx.fillStyle != '') ctx.fill();
-				if (ctx.strokeStyle != '') ctx.stroke();
-				
-				var markers = this.getMarkers();
-				if (markers != null) {
-					if (this.style('marker-start').Definition.isUrl()) {
-						var marker = this.style('marker-start').Definition.getDefinition();
-						marker.render(ctx, markers[0][0], markers[0][1]);
-					}
-					if (this.style('marker-mid').Definition.isUrl()) {
-						var marker = this.style('marker-mid').Definition.getDefinition();
-						for (var i=1;i<markers.length-1;i++) {
-							marker.render(ctx, markers[i][0], markers[i][1]);
-						}
-					}
-					if (this.style('marker-end').Definition.isUrl()) {
-						var marker = this.style('marker-end').Definition.getDefinition();
-						marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
-					}
-				}					
-			}
-			
-			this.getBoundingBox = function() {
-				return this.path();
-			}
-			
-			this.getMarkers = function() {
-				return null;
-			}
-		}
-		svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
-		
-		// svg element
-		svg.Element.svg = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.baseClearContext = this.clearContext;
-			this.clearContext = function(ctx) {
-				this.baseClearContext(ctx);
-				svg.ViewPort.RemoveCurrent();
-			}
-			
-			this.baseSetContext = this.setContext;
-			this.setContext = function(ctx) {
-				// initial values
-				ctx.strokeStyle = 'rgba(0,0,0,0)';
-				ctx.lineCap = 'butt';
-				ctx.lineJoin = 'miter';
-				ctx.miterLimit = 4;			
-			
-				this.baseSetContext(ctx);
-				
-				// create new view port
-				if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
-					ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
-				}
-				
-				var width = svg.ViewPort.width();
-				var height = svg.ViewPort.height();
-				if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
-					width = this.attribute('width').Length.toPixels('x');
-					height = this.attribute('height').Length.toPixels('y');
-					
-					var x = 0;
-					var y = 0;
-					if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
-						x = -this.attribute('refX').Length.toPixels('x');
-						y = -this.attribute('refY').Length.toPixels('y');
-					}
-					
-					ctx.beginPath();
-					ctx.moveTo(x, y);
-					ctx.lineTo(width, y);
-					ctx.lineTo(width, height);
-					ctx.lineTo(x, height);
-					ctx.closePath();
-					ctx.clip();
-				}
-				svg.ViewPort.SetCurrent(width, height);	
-						
-				// viewbox
-				if (this.attribute('viewBox').hasValue()) {				
-					var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
-					var minX = viewBox[0];
-					var minY = viewBox[1];
-					width = viewBox[2];
-					height = viewBox[3];
-					
-					svg.AspectRatio(ctx,
-									this.attribute('preserveAspectRatio').value, 
-									svg.ViewPort.width(), 
-									width,
-									svg.ViewPort.height(),
-									height,
-									minX,
-									minY,
-									this.attribute('refX').value,
-									this.attribute('refY').value);
-										
-					svg.ViewPort.RemoveCurrent();	
-					svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);						
-				}				
-			}
-		}
-		svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
-
-		// rect element
-		svg.Element.rect = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-			
-			this.path = function(ctx) {
-				var x = this.attribute('x').Length.toPixels('x');
-				var y = this.attribute('y').Length.toPixels('y');
-				var width = this.attribute('width').Length.toPixels('x');
-				var height = this.attribute('height').Length.toPixels('y');
-				var rx = this.attribute('rx').Length.toPixels('x');
-				var ry = this.attribute('ry').Length.toPixels('y');
-				if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
-				if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
-				
-				if (ctx != null) {
-					ctx.beginPath();
-					ctx.moveTo(x + rx, y);
-					ctx.lineTo(x + width - rx, y);
-					ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
-					ctx.lineTo(x + width, y + height - ry);
-					ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
-					ctx.lineTo(x + rx, y + height);
-					ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
-					ctx.lineTo(x, y + ry);
-					ctx.quadraticCurveTo(x, y, x + rx, y)
-					ctx.closePath();
-				}
-				
-				return new svg.BoundingBox(x, y, x + width, y + height);
-			}
-		}
-		svg.Element.rect.prototype = new svg.Element.PathElementBase;
-		
-		// circle element
-		svg.Element.circle = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-			
-			this.path = function(ctx) {
-				var cx = this.attribute('cx').Length.toPixels('x');
-				var cy = this.attribute('cy').Length.toPixels('y');
-				var r = this.attribute('r').Length.toPixels();
-			
-				if (ctx != null) {
-					ctx.beginPath();
-					ctx.arc(cx, cy, r, 0, Math.PI * 2, true); 
-					ctx.closePath();
-				}
-				
-				return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
-			}
-		}
-		svg.Element.circle.prototype = new svg.Element.PathElementBase;	
-
-		// ellipse element
-		svg.Element.ellipse = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-			
-			this.path = function(ctx) {
-				var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
-				var rx = this.attribute('rx').Length.toPixels('x');
-				var ry = this.attribute('ry').Length.toPixels('y');
-				var cx = this.attribute('cx').Length.toPixels('x');
-				var cy = this.attribute('cy').Length.toPixels('y');
-				
-				if (ctx != null) {
-					ctx.beginPath();
-					ctx.moveTo(cx, cy - ry);
-					ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry,  cx + rx, cy - (KAPPA * ry), cx + rx, cy);
-					ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
-					ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
-					ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
-					ctx.closePath();
-				}
-				
-				return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
-			}
-		}
-		svg.Element.ellipse.prototype = new svg.Element.PathElementBase;			
-		
-		// line element
-		svg.Element.line = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-			
-			this.getPoints = function() {
-				return [
-					new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
-					new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
-			}
-								
-			this.path = function(ctx) {
-				var points = this.getPoints();
-				
-				if (ctx != null) {
-					ctx.beginPath();
-					ctx.moveTo(points[0].x, points[0].y);
-					ctx.lineTo(points[1].x, points[1].y);
-				}
-				
-				return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
-			}
-			
-			this.getMarkers = function() {
-				var points = this.getPoints();	
-				var a = points[0].angleTo(points[1]);
-				return [[points[0], a], [points[1], a]];
-			}
-		}
-		svg.Element.line.prototype = new svg.Element.PathElementBase;		
-				
-		// polyline element
-		svg.Element.polyline = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-			
-			this.points = svg.CreatePath(this.attribute('points').value);
-			this.path = function(ctx) {
-				var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
-				if (ctx != null) {
-					ctx.beginPath();
-					ctx.moveTo(this.points[0].x, this.points[0].y);
-				}
-				for (var i=1; i<this.points.length; i++) {
-					bb.addPoint(this.points[i].x, this.points[i].y);
-					if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
-				}
-				return bb;
-			}
-			
-			this.getMarkers = function() {
-				var markers = [];
-				for (var i=0; i<this.points.length - 1; i++) {
-					markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
-				}
-				markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
-				return markers;
-			}			
-		}
-		svg.Element.polyline.prototype = new svg.Element.PathElementBase;				
-				
-		// polygon element
-		svg.Element.polygon = function(node) {
-			this.base = svg.Element.polyline;
-			this.base(node);
-			
-			this.basePath = this.path;
-			this.path = function(ctx) {
-				var bb = this.basePath(ctx);
-				if (ctx != null) {
-					ctx.lineTo(this.points[0].x, this.points[0].y);
-					ctx.closePath();
-				}
-				return bb;
-			}
-		}
-		svg.Element.polygon.prototype = new svg.Element.polyline;
-
-		// path element
-		svg.Element.path = function(node) {
-			this.base = svg.Element.PathElementBase;
-			this.base(node);
-					
-			var d = this.attribute('d').value;
-			// TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
-			d = d.replace(/,/gm,' '); // get rid of all commas
-			d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
-			d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
-			d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
-			d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
-			d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
-			d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
-			d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
-			d = svg.compressSpaces(d); // compress multiple spaces
-			d = svg.trim(d);
-			this.PathParser = new (function(d) {
-				this.tokens = d.split(' ');
-				
-				this.reset = function() {
-					this.i = -1;
-					this.command = '';
-					this.previousCommand = '';
-					this.start = new svg.Point(0, 0);
-					this.control = new svg.Point(0, 0);
-					this.current = new svg.Point(0, 0);
-					this.points = [];
-					this.angles = [];
-				}
-								
-				this.isEnd = function() {
-					return this.i >= this.tokens.length - 1;
-				}
-				
-				this.isCommandOrEnd = function() {
-					if (this.isEnd()) return true;
-					return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
-				}
-				
-				this.isRelativeCommand = function() {
-					return this.command == this.command.toLowerCase();
-				}
-							
-				this.getToken = function() {
-					this.i = this.i + 1;
-					return this.tokens[this.i];
-				}
-				
-				this.getScalar = function() {
-					return parseFloat(this.getToken());
-				}
-				
-				this.nextCommand = function() {
-					this.previousCommand = this.command;
-					this.command = this.getToken();
-				}				
-				
-				this.getPoint = function() {
-					var p = new svg.Point(this.getScalar(), this.getScalar());
-					return this.makeAbsolute(p);
-				}
-				
-				this.getAsControlPoint = function() {
-					var p = this.getPoint();
-					this.control = p;
-					return p;
-				}
-				
-				this.getAsCurrentPoint = function() {
-					var p = this.getPoint();
-					this.current = p;
-					return p;	
-				}
-				
-				this.getReflectedControlPoint = function() {
-					if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
-						return this.current;
-					}
-					
-					// reflect point
-					var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);					
-					return p;
-				}
-				
-				this.makeAbsolute = function(p) {
-					if (this.isRelativeCommand()) {
-						p.x = this.current.x + p.x;
-						p.y = this.current.y + p.y;
-					}
-					return p;
-				}
-				
-				this.addMarker = function(p, from, priorTo) {
-					// if the last angle isn't filled in because we didn't have this point yet ...
-					if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
-						this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
-					}
-					this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
-				}
-				
-				this.addMarkerAngle = function(p, a) {
-					this.points.push(p);
-					this.angles.push(a);
-				}				
-				
-				this.getMarkerPoints = function() { return this.points; }
-				this.getMarkerAngles = function() {
-					for (var i=0; i<this.angles.length; i++) {
-						if (this.angles[i] == null) {
-							for (var j=i+1; j<this.angles.length; j++) {
-								if (this.angles[j] != null) {
-									this.angles[i] = this.angles[j];
-									break;
-								}
-							}
-						}
-					}
-					return this.angles;
-				}
-			})(d);
-
-			this.path = function(ctx) {
-				var pp = this.PathParser;
-				pp.reset();
-
-				var bb = new svg.BoundingBox();
-				if (ctx != null) ctx.beginPath();
-				while (!pp.isEnd()) {
-					pp.nextCommand();
-					switch (pp.command.toUpperCase()) {
-					case 'M':
-						var p = pp.getAsCurrentPoint();
-						pp.addMarker(p);
-						bb.addPoint(p.x, p.y);
-						if (ctx != null) ctx.moveTo(p.x, p.y);
-						pp.start = pp.current;
-						while (!pp.isCommandOrEnd()) {
-							var p = pp.getAsCurrentPoint();
-							pp.addMarker(p, pp.start);
-							bb.addPoint(p.x, p.y);
-							if (ctx != null) ctx.lineTo(p.x, p.y);
-						}
-						break;
-					case 'L':
-						while (!pp.isCommandOrEnd()) {
-							var c = pp.current;
-							var p = pp.getAsCurrentPoint();
-							pp.addMarker(p, c);
-							bb.addPoint(p.x, p.y);
-							if (ctx != null) ctx.lineTo(p.x, p.y);
-						}
-						break;
-					case 'H':
-						while (!pp.isCommandOrEnd()) {
-							var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
-							pp.addMarker(newP, pp.current);
-							pp.current = newP;
-							bb.addPoint(pp.current.x, pp.current.y);
-							if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
-						}
-						break;
-					case 'V':
-						while (!pp.isCommandOrEnd()) {
-							var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
-							pp.addMarker(newP, pp.current);
-							pp.current = newP;
-							bb.addPoint(pp.current.x, pp.current.y);
-							if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
-						}
-						break;
-					case 'C':
-						while (!pp.isCommandOrEnd()) {
-							var curr = pp.current;
-							var p1 = pp.getPoint();
-							var cntrl = pp.getAsControlPoint();
-							var cp = pp.getAsCurrentPoint();
-							pp.addMarker(cp, cntrl, p1);
-							bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
-							if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
-						}
-						break;
-					case 'S':
-						while (!pp.isCommandOrEnd()) {
-							var curr = pp.current;
-							var p1 = pp.getReflectedControlPoint();
-							var cntrl = pp.getAsControlPoint();
-							var cp = pp.getAsCurrentPoint();
-							pp.addMarker(cp, cntrl, p1);
-							bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
-							if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
-						}
-						break;
-					case 'Q':
-						while (!pp.isCommandOrEnd()) {
-							var curr = pp.current;
-							var cntrl = pp.getAsControlPoint();
-							var cp = pp.getAsCurrentPoint();
-							pp.addMarker(cp, cntrl, cntrl);
-							bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
-							if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
-						}
-						break;
-					case 'T':
-						while (!pp.isCommandOrEnd()) {
-							var curr = pp.current;
-							var cntrl = pp.getReflectedControlPoint();
-							pp.control = cntrl;
-							var cp = pp.getAsCurrentPoint();
-							pp.addMarker(cp, cntrl, cntrl);
-							bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
-							if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
-						}
-						break;
-					case 'A':
-						while (!pp.isCommandOrEnd()) {
-						    var curr = pp.current;
-							var rx = pp.getScalar();
-							var ry = pp.getScalar();
-							var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
-							var largeArcFlag = pp.getScalar();
-							var sweepFlag = pp.getScalar();
-							var cp = pp.getAsCurrentPoint();
-
-							// Conversion from endpoint to center parameterization
-							// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
-							// x1', y1'
-							var currp = new svg.Point(
-								Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
-								-Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
-							);
-							// adjust radii
-							var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
-							if (l > 1) {
-								rx *= Math.sqrt(l);
-								ry *= Math.sqrt(l);
-							}
-							// cx', cy'
-							var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
-								((Math.pow(rx,2)*Math.pow(ry,2))-(Math.pow(rx,2)*Math.pow(currp.y,2))-(Math.pow(ry,2)*Math.pow(currp.x,2))) /
-								(Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
-							);
-							if (isNaN(s)) s = 0;
-							var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
-							// cx, cy
-							var centp = new svg.Point(
-								(curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
-								(curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
-							);
-							// vector magnitude
-							var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
-							// ratio between two vectors
-							var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
-							// angle between two vectors
-							var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
-							// initial angle
-							var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
-							// angle delta
-							var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
-							var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
-							var ad = a(u, v);
-							if (r(u,v) <= -1) ad = Math.PI;
-							if (r(u,v) >= 1) ad = 0;
-
-							if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI;
-							if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI;
-
-							// for markers
-							var halfWay = new svg.Point(
-								centp.x - rx * Math.cos((a1 + ad) / 2),
-								centp.y - ry * Math.sin((a1 + ad) / 2)
-							);
-							pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
-							pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
-
-							bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
-							if (ctx != null) {
-								var r = rx > ry ? rx : ry;
-								var sx = rx > ry ? 1 : rx / ry;
-								var sy = rx > ry ? ry / rx : 1;
-
-								ctx.translate(centp.x, centp.y);
-								ctx.rotate(xAxisRotation);
-								ctx.scale(sx, sy);
-								ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
-								ctx.scale(1/sx, 1/sy);
-								ctx.rotate(-xAxisRotation);
-								ctx.translate(-centp.x, -centp.y);
-							}
-						}
-						break;
-					case 'Z':
-						if (ctx != null) ctx.closePath();
-						pp.current = pp.start;
-					}
-				}
-
-				return bb;
-			}
-
-			this.getMarkers = function() {
-				var points = this.PathParser.getMarkerPoints();
-				var angles = this.PathParser.getMarkerAngles();
-				
-				var markers = [];
-				for (var i=0; i<points.length; i++) {
-					markers.push([points[i], angles[i]]);
-				}
-				return markers;
-			}
-		}
-		svg.Element.path.prototype = new svg.Element.PathElementBase;
-		
-		// pattern element
-		svg.Element.pattern = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.createPattern = function(ctx, element) {
-				// render me using a temporary svg element
-				var tempSvg = new svg.Element.svg();
-				tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
-				tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value);
-				tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value);
-				tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
-				tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
-				tempSvg.children = this.children;
-				
-				var c = document.createElement('canvas');
-				c.width = this.attribute('width').Length.toPixels('x');
-				c.height = this.attribute('height').Length.toPixels('y');
-				tempSvg.render(c.getContext('2d'));		
-				return ctx.createPattern(c, 'repeat');
-			}
-		}
-		svg.Element.pattern.prototype = new svg.Element.ElementBase;
-		
-		// marker element
-		svg.Element.marker = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.baseRender = this.render;
-			this.render = function(ctx, point, angle) {
-				ctx.translate(point.x, point.y);
-				if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
-				if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
-				ctx.save();
-							
-				// render me using a temporary svg element
-				var tempSvg = new svg.Element.svg();
-				tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
-				tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
-				tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
-				tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
-				tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
-				tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
-				tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
-				tempSvg.children = this.children;
-				tempSvg.render(ctx);
-				
-				ctx.restore();
-				if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
-				if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
-				ctx.translate(-point.x, -point.y);
-			}
-		}
-		svg.Element.marker.prototype = new svg.Element.ElementBase;
-		
-		// definitions element
-		svg.Element.defs = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);	
-			
-			this.render = function(ctx) {
-				// NOOP
-			}
-		}
-		svg.Element.defs.prototype = new svg.Element.ElementBase;
-		
-		// base for gradients
-		svg.Element.GradientBase = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
-			
-			this.stops = [];			
-			for (var i=0; i<this.children.length; i++) {
-				var child = this.children[i];
-				this.stops.push(child);
-			}	
-			
-			this.getGradient = function() {
-				// OVERRIDE ME!
-			}			
-
-			this.createGradient = function(ctx, element) {
-				var stopsContainer = this;
-				if (this.attribute('xlink:href').hasValue()) {
-					stopsContainer = this.attribute('xlink:href').Definition.getDefinition();
-				}
-			
-				var g = this.getGradient(ctx, element);
-				for (var i=0; i<stopsContainer.stops.length; i++) {
-					g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color);
-				}
-				
-				if (this.attribute('gradientTransform').hasValue()) {
-					// render as transformed pattern on temporary canvas
-					var rootView = svg.ViewPort.viewPorts[0];
-					
-					var rect = new svg.Element.rect();
-					rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
-					rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
-					rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
-					rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
-					
-					var group = new svg.Element.g();
-					group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
-					group.children = [ rect ];
-					
-					var tempSvg = new svg.Element.svg();
-					tempSvg.attributes['x'] = new svg.Property('x', 0);
-					tempSvg.attributes['y'] = new svg.Property('y', 0);
-					tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
-					tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
-					tempSvg.children = [ group ];
-					
-					var c = document.createElement('canvas');
-					c.width = rootView.width;
-					c.height = rootView.height;
-					var tempCtx = c.getContext('2d');
-					tempCtx.fillStyle = g;
-					tempSvg.render(tempCtx);		
-					return tempCtx.createPattern(c, 'no-repeat');
-				}
-				
-				return g;				
-			}
-		}
-		svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
-		
-		// linear gradient element
-		svg.Element.linearGradient = function(node) {
-			this.base = svg.Element.GradientBase;
-			this.base(node);
-			
-			this.getGradient = function(ctx, element) {
-				var bb = element.getBoundingBox();
-				
-				var x1 = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.x() + bb.width() * this.attribute('x1').numValue() 
-					: this.attribute('x1').Length.toPixels('x'));
-				var y1 = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.y() + bb.height() * this.attribute('y1').numValue()
-					: this.attribute('y1').Length.toPixels('y'));
-				var x2 = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.x() + bb.width() * this.attribute('x2').numValue()
-					: this.attribute('x2').Length.toPixels('x'));
-				var y2 = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.y() + bb.height() * this.attribute('y2').numValue()
-					: this.attribute('y2').Length.toPixels('y'));
-
-				return ctx.createLinearGradient(x1, y1, x2, y2);
-			}
-		}
-		svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
-		
-		// radial gradient element
-		svg.Element.radialGradient = function(node) {
-			this.base = svg.Element.GradientBase;
-			this.base(node);
-			
-			this.getGradient = function(ctx, element) {
-				var bb = element.getBoundingBox();
-				
-				var cx = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.x() + bb.width() * this.attribute('cx').numValue() 
-					: this.attribute('cx').Length.toPixels('x'));
-				var cy = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.y() + bb.height() * this.attribute('cy').numValue() 
-					: this.attribute('cy').Length.toPixels('y'));
-				
-				var fx = cx;
-				var fy = cy;
-				if (this.attribute('fx').hasValue()) {
-					fx = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.x() + bb.width() * this.attribute('fx').numValue() 
-					: this.attribute('fx').Length.toPixels('x'));
-				}
-				if (this.attribute('fy').hasValue()) {
-					fy = (this.gradientUnits == 'objectBoundingBox' 
-					? bb.y() + bb.height() * this.attribute('fy').numValue() 
-					: this.attribute('fy').Length.toPixels('y'));
-				}
-				
-				var r = (this.gradientUnits == 'objectBoundingBox' 
-					? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
-					: this.attribute('r').Length.toPixels());
-				
-				return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
-			}
-		}
-		svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
-		
-		// gradient stop element
-		svg.Element.stop = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.offset = this.attribute('offset').numValue();
-			
-			var stopColor = this.style('stop-color');
-			if (this.style('stop-opacity').hasValue()) stopColor = stopColor.Color.addOpacity(this.style('stop-opacity').value);
-			this.color = stopColor.value;
-		}
-		svg.Element.stop.prototype = new svg.Element.ElementBase;
-		
-		// animation base element
-		svg.Element.AnimateBase = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			svg.Animations.push(this);
-			
-			this.duration = 0.0;
-			this.begin = this.attribute('begin').Time.toMilliseconds();
-			this.maxDuration = this.begin + this.attribute('dur').Time.toMilliseconds();
-			
-			this.getProperty = function() {
-				var attributeType = this.attribute('attributeType').value;
-				var attributeName = this.attribute('attributeName').value;
-				
-				if (attributeType == 'CSS') {
-					return this.parent.style(attributeName, true);
-				}
-				return this.parent.attribute(attributeName, true);			
-			};
-			
-			this.initialValue = null;
-			this.removed = false;			
-
-			this.calcValue = function() {
-				// OVERRIDE ME!
-				return '';
-			}
-			
-			this.update = function(delta) {	
-				// set initial value
-				if (this.initialValue == null) {
-					this.initialValue = this.getProperty().value;
-				}
-			
-				// if we're past the end time
-				if (this.duration > this.maxDuration) {
-					// loop for indefinitely repeating animations
-					if (this.attribute('repeatCount').value == 'indefinite') {
-						this.duration = 0.0
-					}
-					else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
-						this.removed = true;
-						this.getProperty().value = this.initialValue;
-						return true;
-					}
-					else {
-						return false; // no updates made
-					}
-				}			
-				this.duration = this.duration + delta;
-			
-				// if we're past the begin time
-				var updated = false;
-				if (this.begin < this.duration) {
-					var newValue = this.calcValue(); // tween
-					
-					if (this.attribute('type').hasValue()) {
-						// for transform, etc.
-						var type = this.attribute('type').value;
-						newValue = type + '(' + newValue + ')';
-					}
-					
-					this.getProperty().value = newValue;
-					updated = true;
-				}
-				
-				return updated;
-			}
-			
-			// fraction of duration we've covered
-			this.progress = function() {
-				return ((this.duration - this.begin) / (this.maxDuration - this.begin));
-			}			
-		}
-		svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
-		
-		// animate element
-		svg.Element.animate = function(node) {
-			this.base = svg.Element.AnimateBase;
-			this.base(node);
-			
-			this.calcValue = function() {
-				var from = this.attribute('from').numValue();
-				var to = this.attribute('to').numValue();
-				
-				// tween value linearly
-				return from + (to - from) * this.progress(); 
-			};
-		}
-		svg.Element.animate.prototype = new svg.Element.AnimateBase;
-			
-		// animate color element
-		svg.Element.animateColor = function(node) {
-			this.base = svg.Element.AnimateBase;
-			this.base(node);
-
-			this.calcValue = function() {
-				var from = new RGBColor(this.attribute('from').value);
-				var to = new RGBColor(this.attribute('to').value);
-				
-				if (from.ok && to.ok) {
-					// tween color linearly
-					var r = from.r + (to.r - from.r) * this.progress();
-					var g = from.g + (to.g - from.g) * this.progress();
-					var b = from.b + (to.b - from.b) * this.progress();
-					return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
-				}
-				return this.attribute('from').value;
-			};
-		}
-		svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
-		
-		// animate transform element
-		svg.Element.animateTransform = function(node) {
-			this.base = svg.Element.animate;
-			this.base(node);
-		}
-		svg.Element.animateTransform.prototype = new svg.Element.animate;
-		
-		// font element
-		svg.Element.font = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-
-			this.horizAdvX = this.attribute('horiz-adv-x').numValue();			
-			
-			this.isRTL = false;
-			this.isArabic = false;
-			this.fontFace = null;
-			this.missingGlyph = null;
-			this.glyphs = [];			
-			for (var i=0; i<this.children.length; i++) {
-				var child = this.children[i];
-				if (child.type == 'font-face') {
-					this.fontFace = child;
-					if (child.style('font-family').hasValue()) {
-						svg.Definitions[child.style('font-family').value] = this;
-					}
-				}
-				else if (child.type == 'missing-glyph') this.missingGlyph = child;
-				else if (child.type == 'glyph') {
-					if (child.arabicForm != '') {
-						this.isRTL = true;
-						this.isArabic = true;
-						if (typeof(this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = [];
-						this.glyphs[child.unicode][child.arabicForm] = child;
-					}
-					else {
-						this.glyphs[child.unicode] = child;
-					}
-				}
-			}	
-		}
-		svg.Element.font.prototype = new svg.Element.ElementBase;
-		
-		// font-face element
-		svg.Element.fontface = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);	
-			
-			this.ascent = this.attribute('ascent').value;
-			this.descent = this.attribute('descent').value;
-			this.unitsPerEm = this.attribute('units-per-em').numValue();				
-		}
-		svg.Element.fontface.prototype = new svg.Element.ElementBase;
-		
-		// missing-glyph element
-		svg.Element.missingglyph = function(node) {
-			this.base = svg.Element.path;
-			this.base(node);	
-			
-			this.horizAdvX = 0;
-		}
-		svg.Element.missingglyph.prototype = new svg.Element.path;
-		
-		// glyph element
-		svg.Element.glyph = function(node) {
-			this.base = svg.Element.path;
-			this.base(node);	
-			
-			this.horizAdvX = this.attribute('horiz-adv-x').numValue();
-			this.unicode = this.attribute('unicode').value;
-			this.arabicForm = this.attribute('arabic-form').value;
-		}
-		svg.Element.glyph.prototype = new svg.Element.path;
-		
-		// text element
-		svg.Element.text = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			if (node != null) {
-				// add children
-				this.children = [];
-				for (var i=0; i<node.childNodes.length; i++) {
-					var childNode = node.childNodes[i];
-					if (childNode.nodeType == 1) { // capture tspan and tref nodes
-						this.addChild(childNode, true);
-					}
-					else if (childNode.nodeType == 3) { // capture text
-						this.addChild(new svg.Element.tspan(childNode), false);
-					}
-				}
-			}
-			
-			this.baseSetContext = this.setContext;
-			this.setContext = function(ctx) {
-				this.baseSetContext(ctx);
-				if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value;
-				if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value;
-			}
-			
-			this.renderChildren = function(ctx) {
-				var textAnchor = this.style('text-anchor').valueOrDefault('start');
-				var x = this.attribute('x').Length.toPixels('x');
-				var y = this.attribute('y').Length.toPixels('y');
-				for (var i=0; i<this.children.length; i++) {
-					var child = this.children[i];
-				
-					if (child.attribute('x').hasValue()) {
-						child.x = child.attribute('x').Length.toPixels('x');
-					}
-					else {
-						if (child.attribute('dx').hasValue()) x += child.attribute('dx').Length.toPixels('x');
-						child.x = x;
-					}
-					
-					var childLength = child.measureText(ctx);
-					if (textAnchor != 'start' && (i==0 || child.attribute('x').hasValue())) { // new group?
-						// loop through rest of children
-						var groupLength = childLength;
-						for (var j=i+1; j<this.children.length; j++) {
-							var childInGroup = this.children[j];
-							if (childInGroup.attribute('x').hasValue()) break; // new group
-							groupLength += childInGroup.measureText(ctx);
-						}
-						child.x -= (textAnchor == 'end' ? groupLength : groupLength / 2.0);
-					}
-					x = child.x + childLength;
-					
-					if (child.attribute('y').hasValue()) {
-						child.y = child.attribute('y').Length.toPixels('y');
-					}
-					else {
-						if (child.attribute('dy').hasValue()) y += child.attribute('dy').Length.toPixels('y');
-						child.y = y;
-					}	
-					y = child.y;
-					
-					child.render(ctx);
-				}
-			}
-		}
-		svg.Element.text.prototype = new svg.Element.RenderedElementBase;
-		
-		// text base
-		svg.Element.TextElementBase = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.getGlyph = function(font, text, i) {
-				var c = text[i];
-				var glyph = null;
-				if (font.isArabic) {
-					var arabicForm = 'isolated';
-					if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal'; 
-					if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial';
-					if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
-					if (typeof(font.glyphs[c]) != 'undefined') {
-						glyph = font.glyphs[c][arabicForm];
-						if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
-					}
-				}
-				else {
-					glyph = font.glyphs[c];
-				}
-				if (glyph == null) glyph = font.missingGlyph;
-				return glyph;
-			}
-			
-			this.renderChildren = function(ctx) {
-				var customFont = this.parent.style('font-family').Definition.getDefinition();
-				if (customFont != null) {
-					var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
-					var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
-					var text = this.getText();
-					if (customFont.isRTL) text = text.split("").reverse().join("");
-					
-					var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
-					for (var i=0; i<text.length; i++) {
-						var glyph = this.getGlyph(customFont, text, i);
-						var scale = fontSize / customFont.fontFace.unitsPerEm;
-						ctx.translate(this.x, this.y);
-						ctx.scale(scale, -scale);
-						var lw = ctx.lineWidth;
-						ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
-						if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
-						glyph.render(ctx);
-						if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
-						ctx.lineWidth = lw;
-						ctx.scale(1/scale, -1/scale);
-						ctx.translate(-this.x, -this.y);	
-						
-						this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
-						if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
-							this.x += dx[i];
-						}
-					}
-					return;
-				}
-			
-				if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
-				if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
-			}
-			
-			this.getText = function() {
-				// OVERRIDE ME
-			}
-			
-			this.measureText = function(ctx) {
-				var customFont = this.parent.style('font-family').Definition.getDefinition();
-				if (customFont != null) {
-					var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
-					var measure = 0;
-					var text = this.getText();
-					if (customFont.isRTL) text = text.split("").reverse().join("");
-					var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
-					for (var i=0; i<text.length; i++) {
-						var glyph = this.getGlyph(customFont, text, i);
-						measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
-						if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
-							measure += dx[i];
-						}
-					}
-					return measure;
-				}
-			
-				var textToMeasure = svg.compressSpaces(this.getText());
-				if (!ctx.measureText) return textToMeasure.length * 10;
-				
-				ctx.save();
-				this.setContext(ctx);
-				var width = ctx.measureText(textToMeasure).width;
-				ctx.restore();
-				return width;
-			}
-		}
-		svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
-		
-		// tspan 
-		svg.Element.tspan = function(node) {
-			this.base = svg.Element.TextElementBase;
-			this.base(node);
-			
-			this.text = node.nodeType == 3 ? node.nodeValue : // text
-						node.childNodes.length > 0 ? node.childNodes[0].nodeValue : // element
-						node.text;
-			this.getText = function() {
-				return this.text;
-			}
-		}
-		svg.Element.tspan.prototype = new svg.Element.TextElementBase;
-		
-		// tref
-		svg.Element.tref = function(node) {
-			this.base = svg.Element.TextElementBase;
-			this.base(node);
-			
-			this.getText = function() {
-				var element = this.attribute('xlink:href').Definition.getDefinition();
-				if (element != null) return element.children[0].getText();
-			}
-		}
-		svg.Element.tref.prototype = new svg.Element.TextElementBase;		
-		
-		// a element
-		svg.Element.a = function(node) {
-			this.base = svg.Element.TextElementBase;
-			this.base(node);
-			
-			this.hasText = true;
-			for (var i=0; i<node.childNodes.length; i++) {
-				if (node.childNodes[i].nodeType != 3) this.hasText = false;
-			}
-			
-			// this might contain text
-			this.text = this.hasText ? node.childNodes[0].nodeValue : '';
-			this.getText = function() {
-				return this.text;
-			}		
-
-			this.baseRenderChildren = this.renderChildren;
-			this.renderChildren = function(ctx) {
-				if (this.hasText) {
-					// render as text element
-					this.baseRenderChildren(ctx);
-					var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
-					svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.Length.toPixels('y'), this.x + this.measureText(ctx), this.y));					
-				}
-				else {
-					// render as temporary group
-					var g = new svg.Element.g();
-					g.children = this.children;
-					g.parent = this;
-					g.render(ctx);
-				}
-			}
-			
-			this.onclick = function() {
-				window.open(this.attribute('xlink:href').value);
-			}
-			
-			this.onmousemove = function() {
-				svg.ctx.canvas.style.cursor = 'pointer';
-			}
-		}
-		svg.Element.a.prototype = new svg.Element.TextElementBase;		
-		
-		// image element
-		svg.Element.image = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			svg.Images.push(this);
-			this.img = document.createElement('img');
-			this.loaded = false;
-			var that = this;
-			this.img.onload = function() { that.loaded = true; }
-			this.img.src = this.attribute('xlink:href').value;
-			
-			this.renderChildren = function(ctx) {
-				var x = this.attribute('x').Length.toPixels('x');
-				var y = this.attribute('y').Length.toPixels('y');
-				
-				var width = this.attribute('width').Length.toPixels('x');
-				var height = this.attribute('height').Length.toPixels('y');			
-				if (width == 0 || height == 0) return;
-			
-				ctx.save();
-				ctx.translate(x, y);
-				svg.AspectRatio(ctx,
-								this.attribute('preserveAspectRatio').value,
-								width,
-								this.img.width,
-								height,
-								this.img.height,
-								0,
-								0);	
-				ctx.drawImage(this.img, 0, 0);			
-				ctx.restore();
-			}
-		}
-		svg.Element.image.prototype = new svg.Element.RenderedElementBase;
-		
-		// group element
-		svg.Element.g = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.getBoundingBox = function() {
-				var bb = new svg.BoundingBox();
-				for (var i=0; i<this.children.length; i++) {
-					bb.addBoundingBox(this.children[i].getBoundingBox());
-				}
-				return bb;
-			};
-		}
-		svg.Element.g.prototype = new svg.Element.RenderedElementBase;
-
-		// symbol element
-		svg.Element.symbol = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.baseSetContext = this.setContext;
-			this.setContext = function(ctx) {		
-				this.baseSetContext(ctx);
-				
-				// viewbox
-				if (this.attribute('viewBox').hasValue()) {				
-					var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
-					var minX = viewBox[0];
-					var minY = viewBox[1];
-					width = viewBox[2];
-					height = viewBox[3];
-					
-					svg.AspectRatio(ctx,
-									this.attribute('preserveAspectRatio').value, 
-									this.attribute('width').Length.toPixels('x'),
-									width,
-									this.attribute('height').Length.toPixels('y'),
-									height,
-									minX,
-									minY);
-
-					svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);						
-				}
-			}			
-		}
-		svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;		
-			
-		// style element
-		svg.Element.style = function(node) { 
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			// text, or spaces then CDATA
-			var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : '');
-			css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
-			css = svg.compressSpaces(css); // replace whitespace
-			var cssDefs = css.split('}');
-			for (var i=0; i<cssDefs.length; i++) {
-				if (svg.trim(cssDefs[i]) != '') {
-					var cssDef = cssDefs[i].split('{');
-					var cssClasses = cssDef[0].split(',');
-					var cssProps = cssDef[1].split(';');
-					for (var j=0; j<cssClasses.length; j++) {
-						var cssClass = svg.trim(cssClasses[j]);
-						if (cssClass != '') {
-							var props = {};
-							for (var k=0; k<cssProps.length; k++) {
-								var prop = cssProps[k].indexOf(':');
-								var name = cssProps[k].substr(0, prop);
-								var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
-								if (name != null && value != null) {
-									props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
-								}
-							}
-							svg.Styles[cssClass] = props;
-							if (cssClass == '@font-face') {
-								var fontFamily = props['font-family'].value.replace(/"/g,'');
-								var srcs = props['src'].value.split(',');
-								for (var s=0; s<srcs.length; s++) {
-									if (srcs[s].indexOf('format("svg")') > 0) {
-										var urlStart = srcs[s].indexOf('url');
-										var urlEnd = srcs[s].indexOf(')', urlStart);
-										var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
-										var doc = svg.parseXml(svg.ajax(url));
-										var fonts = doc.getElementsByTagName('font');
-										for (var f=0; f<fonts.length; f++) {
-											var font = svg.CreateElement(fonts[f]);
-											svg.Definitions[fontFamily] = font;
-										}
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-		svg.Element.style.prototype = new svg.Element.ElementBase;
-		
-		// use element 
-		svg.Element.use = function(node) {
-			this.base = svg.Element.RenderedElementBase;
-			this.base(node);
-			
-			this.baseSetContext = this.setContext;
-			this.setContext = function(ctx) {
-				this.baseSetContext(ctx);
-				if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0);
-				if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y'));
-			}
-			
-			this.getDefinition = function() {
-				var element = this.attribute('xlink:href').Definition.getDefinition();
-				if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
-				if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
-				return element;
-			}
-			
-			this.path = function(ctx) {
-				var element = this.getDefinition();
-				if (element != null) element.path(ctx);
-			}
-			
-			this.renderChildren = function(ctx) {
-				var element = this.getDefinition();
-				if (element != null) element.render(ctx);
-			}
-		}
-		svg.Element.use.prototype = new svg.Element.RenderedElementBase;
-		
-		// mask element
-		svg.Element.mask = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-						
-			this.apply = function(ctx, element) {
-				// render as temp svg	
-				var x = this.attribute('x').Length.toPixels('x');
-				var y = this.attribute('y').Length.toPixels('y');
-				var width = this.attribute('width').Length.toPixels('x');
-				var height = this.attribute('height').Length.toPixels('y');
-				
-				// temporarily remove mask to avoid recursion
-				var mask = element.attribute('mask').value;
-				element.attribute('mask').value = '';
-				
-					var cMask = document.createElement('canvas');
-					cMask.width = x + width;
-					cMask.height = y + height;
-					var maskCtx = cMask.getContext('2d');
-					this.renderChildren(maskCtx);
-				
-					var c = document.createElement('canvas');
-					c.width = x + width;
-					c.height = y + height;
-					var tempCtx = c.getContext('2d');
-					element.render(tempCtx);
-					tempCtx.globalCompositeOperation = 'destination-in';
-					tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
-					tempCtx.fillRect(0, 0, x + width, y + height);
-					
-					ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
-					ctx.fillRect(0, 0, x + width, y + height);
-					
-				// reassign mask
-				element.attribute('mask').value = mask;	
-			}
-			
-			this.render = function(ctx) {
-				// NO RENDER
-			}
-		}
-		svg.Element.mask.prototype = new svg.Element.ElementBase;
-		
-		// clip element
-		svg.Element.clipPath = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-			
-			this.apply = function(ctx) {
-				for (var i=0; i<this.children.length; i++) {
-					if (this.children[i].path) {
-						this.children[i].path(ctx);
-						ctx.clip();
-					}
-				}
-			}
-			
-			this.render = function(ctx) {
-				// NO RENDER
-			}
-		}
-		svg.Element.clipPath.prototype = new svg.Element.ElementBase;
-
-		// filters
-		svg.Element.filter = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);
-						
-			this.apply = function(ctx, element) {
-				// render as temp svg	
-				var bb = element.getBoundingBox();
-				var x = this.attribute('x').Length.toPixels('x');
-				var y = this.attribute('y').Length.toPixels('y');
-				if (x == 0 || y == 0) {
-					x = bb.x1;
-					y = bb.y1;
-				}
-				var width = this.attribute('width').Length.toPixels('x');
-				var height = this.attribute('height').Length.toPixels('y');
-				if (width == 0 || height == 0) {
-					width = bb.width();
-					height = bb.height();
-				}
-				
-				// temporarily remove filter to avoid recursion
-				var filter = element.style('filter').value;
-				element.style('filter').value = '';
-				
-				// max filter distance
-				var extraPercent = .20;
-				var px = extraPercent * width;
-				var py = extraPercent * height;
-				
-				var c = document.createElement('canvas');
-				c.width = width + 2*px;
-				c.height = height + 2*py;
-				var tempCtx = c.getContext('2d');
-				tempCtx.translate(-x + px, -y + py);
-				element.render(tempCtx);
-			
-				// apply filters
-				for (var i=0; i<this.children.length; i++) {
-					this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
-				}
-				
-				// render on me
-				ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
-				
-				// reassign filter
-				element.style('filter', true).value = filter;	
-			}
-			
-			this.render = function(ctx) {
-				// NO RENDER
-			}		
-		}
-		svg.Element.filter.prototype = new svg.Element.ElementBase;
-		
-		svg.Element.feGaussianBlur = function(node) {
-			this.base = svg.Element.ElementBase;
-			this.base(node);	
-			
-			function make_fgauss(sigma) {
-				sigma = Math.max(sigma, 0.01);			      
-				var len = Math.ceil(sigma * 4.0) + 1;                     
-				mask = [];                               
-				for (var i = 0; i < len; i++) {                             
-					mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma));                                           
-				}                                                           
-				return mask; 
-			}
-			
-			function normalize(mask) {
-				var sum = 0;
-				for (var i = 1; i < mask.length; i++) {
-					sum += Math.abs(mask[i]);
-				}
-				sum = 2 * sum + Math.abs(mask[0]);
-				for (var i = 0; i < mask.length; i++) {
-					mask[i] /= sum;
-				}
-				return mask;
-			}
-			
-			function convolve_even(src, dst, mask, width, height) {
-			  for (var y = 0; y < height; y++) {
-				for (var x = 0; x < width; x++) {
-				  var a = imGet(src, x, y, width, height, 3)/255;
-				  for (var rgba = 0; rgba < 4; rgba++) {					  
-					  var sum = mask[0] * (a==0?255:imGet(src, x, y, width, height, rgba)) * (a==0||rgba==3?1:a);
-					  for (var i = 1; i < mask.length; i++) {
-						var a1 = imGet(src, Math.max(x-i,0), y, width, height, 3)/255;
-					    var a2 = imGet(src, Math.min(x+i, width-1), y, width, height, 3)/255;
-						sum += mask[i] * 
-						  ((a1==0?255:imGet(src, Math.max(x-i,0), y, width, height, rgba)) * (a1==0||rgba==3?1:a1) + 
-						   (a2==0?255:imGet(src, Math.min(x+i, width-1), y, width, height, rgba)) * (a2==0||rgba==3?1:a2));
-					  }
-					  imSet(dst, y, x, height, width, rgba, sum);
-				  }			  
-				}
-			  }
-			}		
-
-			function imGet(img, x, y, width, height, rgba) {
-				return img[y*width*4 + x*4 + rgba];
-			}
-			
-			function imSet(img, x, y, width, height, rgba, val) {
-				img[y*width*4 + x*4 + rgba] = val;
-			}
-						
-			function blur(ctx, width, height, sigma)
-			{
-				var srcData = ctx.getImageData(0, 0, width, height);
-				var mask = make_fgauss(sigma);
-				mask = normalize(mask);
-				tmp = [];
-				convolve_even(srcData.data, tmp, mask, width, height);
-				convolve_even(tmp, srcData.data, mask, height, width);
-				ctx.clearRect(0, 0, width, height);
-				ctx.putImageData(srcData, 0, 0);
-			}			
-		
-			this.apply = function(ctx, x, y, width, height) {
-				// assuming x==0 && y==0 for now
-				blur(ctx, width, height, this.attribute('stdDeviation').numValue());
-			}
-		}
-		svg.Element.filter.prototype = new svg.Element.feGaussianBlur;
-		
-		// title element, do nothing
-		svg.Element.title = function(node) {
-		}
-		svg.Element.title.prototype = new svg.Element.ElementBase;
-
-		// desc element, do nothing
-		svg.Element.desc = function(node) {
-		}
-		svg.Element.desc.prototype = new svg.Element.ElementBase;		
-		
-		svg.Element.MISSING = function(node) {
-			console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.');
-		}
-		svg.Element.MISSING.prototype = new svg.Element.ElementBase;
-		
-		// element factory
-		svg.CreateElement = function(node) {	
-			var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
-			className = className.replace(/\-/g,''); // remove dashes
-			var e = null;
-			if (typeof(svg.Element[className]) != 'undefined') {
-				e = new svg.Element[className](node);
-			}
-			else {
-				e = new svg.Element.MISSING(node);
-			}
-
-			e.type = node.nodeName;
-			return e;
-		}
-				
-		// load from url
-		svg.load = function(ctx, url) {
-			svg.loadXml(ctx, svg.ajax(url));
-		}
-		
-		// load from xml
-		svg.loadXml = function(ctx, xml) {
-			svg.loadXmlDoc(ctx, svg.parseXml(xml));
-		}
-		
-		svg.loadXmlDoc = function(ctx, dom) {
-			svg.init(ctx);
-			
-			var mapXY = function(p) {
-				var e = ctx.canvas;
-				while (e) {
-					p.x -= e.offsetLeft;
-					p.y -= e.offsetTop;
-					e = e.offsetParent;
-				}
-				if (window.scrollX) p.x += window.scrollX;
-				if (window.scrollY) p.y += window.scrollY;
-				return p;
-			}
-			
-			// bind mouse
-			if (svg.opts['ignoreMouse'] != true) {
-				ctx.canvas.onclick = function(e) {
-					var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
-					svg.Mouse.onclick(p.x, p.y);
-				};
-				ctx.canvas.onmousemove = function(e) {
-					var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
-					svg.Mouse.onmousemove(p.x, p.y);
-				};
-			}
-		
-			var e = svg.CreateElement(dom.documentElement);
-			e.root = true;
-					
-			// render loop
-			var isFirstRender = true;
-			var draw = function() {
-				svg.ViewPort.Clear();
-				if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
-			
-				if (svg.opts['ignoreDimensions'] != true) {
-					// set canvas size
-					if (e.style('width').hasValue()) {
-						ctx.canvas.width = e.style('width').Length.toPixels('x');
-						ctx.canvas.style.width = ctx.canvas.width + 'px';
-					}
-					if (e.style('height').hasValue()) {
-						ctx.canvas.height = e.style('height').Length.toPixels('y');
-						ctx.canvas.style.height = ctx.canvas.height + 'px';
-					}
-				}
-				var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
-				var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
-				svg.ViewPort.SetCurrent(cWidth, cHeight);		
-				
-				if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
-				if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
-				if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
-					var xRatio = 1, yRatio = 1;
-					if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth'];
-					if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight'];
-				
-					e.attribute('width', true).value = svg.opts['scaleWidth'];
-					e.attribute('height', true).value = svg.opts['scaleHeight'];			
-					e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
-					e.attribute('preserveAspectRatio', true).value = 'none';
-				}
-			
-				// clear and render
-				if (svg.opts['ignoreClear'] != true) {
-					ctx.clearRect(0, 0, cWidth, cHeight);
-				}
-				e.render(ctx);
-				if (isFirstRender) {
-					isFirstRender = false;
-					if (svg.opts != null && typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback']();
-				}			
-			}
-			
-			var waitingForImages = true;
-			if (svg.ImagesLoaded()) {
-				waitingForImages = false;
-				draw();
-			}
-			svg.intervalID = setInterval(function() { 
-				var needUpdate = false;
-				
-				if (waitingForImages && svg.ImagesLoaded()) {
-					waitingForImages = false;
-					needUpdate = true;
-				}
-			
-				// need update from mouse events?
-				if (svg.opts['ignoreMouse'] != true) {
-					needUpdate = needUpdate | svg.Mouse.hasEvents();
-				}
-			
-				// need update from animations?
-				if (svg.opts['ignoreAnimation'] != true) {
-					for (var i=0; i<svg.Animations.length; i++) {
-						needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
-					}
-				}
-				
-				// need update from redraw?
-				if (svg.opts != null && typeof(svg.opts['forceRedraw']) == 'function') {
-					if (svg.opts['forceRedraw']() == true) needUpdate = true;
-				}
-				
-				// render if needed
-				if (needUpdate) {
-					draw();				
-					svg.Mouse.runEvents(); // run and clear our events
-				}
-			}, 1000 / svg.FRAMERATE);
-		}
-		
-		svg.stop = function() {
-			if (svg.intervalID) {
-				clearInterval(svg.intervalID);
-			}
-		}
-		
-		svg.Mouse = new (function() {
-			this.events = [];
-			this.hasEvents = function() { return this.events.length != 0; }
-		
-			this.onclick = function(x, y) {
-				this.events.push({ type: 'onclick', x: x, y: y, 
-					run: function(e) { if (e.onclick) e.onclick(); }
-				});
-			}
-			
-			this.onmousemove = function(x, y) {
-				this.events.push({ type: 'onmousemove', x: x, y: y,
-					run: function(e) { if (e.onmousemove) e.onmousemove(); }
-				});
-			}			
-			
-			this.eventElements = [];
-			
-			this.checkPath = function(element, ctx) {
-				for (var i=0; i<this.events.length; i++) {
-					var e = this.events[i];
-					if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
-				}
-			}
-			
-			this.checkBoundingBox = function(element, bb) {
-				for (var i=0; i<this.events.length; i++) {
-					var e = this.events[i];
-					if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
-				}			
-			}
-			
-			this.runEvents = function() {
-				svg.ctx.canvas.style.cursor = '';
-				
-				for (var i=0; i<this.events.length; i++) {
-					var e = this.events[i];
-					var element = this.eventElements[i];
-					while (element) {
-						e.run(element);
-						element = element.parent;
-					}
-				}		
-			
-				// done running, clear
-				this.events = []; 
-				this.eventElements = [];
-			}
-		});
-		
-		return svg;
-	}
-})();
-
-if (CanvasRenderingContext2D) {
-	CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) {
-		canvg(this.canvas, s, { 
-			ignoreMouse: true, 
-			ignoreAnimation: true, 
-			ignoreDimensions: true, 
-			ignoreClear: true, 
-			offsetX: dx, 
-			offsetY: dy, 
-			scaleWidth: dw, 
-			scaleHeight: dh
-		});
-	}
-}/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- * CanVGRenderer Extension module
- *
- * (c) 2011-2012 Torstein Honsi, Erik Olsson
- *
- * License: www.highcharts.com/license
- */
-
-// JSLint options:
-/*global Highcharts */
-
-(function (Highcharts) { // encapsulate
-	var UNDEFINED,
-		DIV = 'div',
-		ABSOLUTE = 'absolute',
-		RELATIVE = 'relative',
-		HIDDEN = 'hidden',
-		VISIBLE = 'visible',
-		PX = 'px',
-		css = Highcharts.css,
-		CanVGRenderer = Highcharts.CanVGRenderer,
-		SVGRenderer = Highcharts.SVGRenderer,
-		extend = Highcharts.extend,
-		merge = Highcharts.merge,
-		addEvent = Highcharts.addEvent,
-		createElement = Highcharts.createElement,
-		discardElement = Highcharts.discardElement;
-
-	// Extend CanVG renderer on demand, inherit from SVGRenderer
-	extend(CanVGRenderer.prototype, SVGRenderer.prototype);
-
-	// Add additional functionality:
-	extend(CanVGRenderer.prototype, {
-		create: function (chart, container, chartWidth, chartHeight) {
-			this.setContainer(container, chartWidth, chartHeight);
-			this.configure(chart);
-		},
-		setContainer: function (container, chartWidth, chartHeight) {
-			var containerStyle = container.style,
-				containerParent = container.parentNode,
-				containerLeft = containerStyle.left,
-				containerTop = containerStyle.top,
-				containerOffsetWidth = container.offsetWidth,
-				containerOffsetHeight = container.offsetHeight,
-				canvas,
-				initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };
-
-			this.init.apply(this, [container, chartWidth, chartHeight]);
-
-			// add the canvas above it
-			canvas = createElement('canvas', {
-				width: containerOffsetWidth,
-				height: containerOffsetHeight
-			}, {
-				position: RELATIVE,
-				left: containerLeft,
-				top: containerTop
-			}, container);
-			this.canvas = canvas;
-
-			// Create the tooltip line and div, they are placed as siblings to
-			// the container (and as direct childs to the div specified in the html page)
-			this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent);
-			this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent);
-			this.ttTimer = UNDEFINED;
-
-			// Move away the svg node to a new div inside the container's parent so we can hide it.
-			var hiddenSvg = createElement(DIV, {
-				width: containerOffsetWidth,
-				height: containerOffsetHeight
-			}, {
-				visibility: HIDDEN,
-				left: containerLeft,
-				top: containerTop
-			}, containerParent);
-			this.hiddenSvg = hiddenSvg;
-			hiddenSvg.appendChild(this.box);
-		},
-
-		/**
-		 * Configures the renderer with the chart. Attach a listener to the event tooltipRefresh.
-		 **/
-		configure: function (chart) {
-			var renderer = this,
-				options = chart.options.tooltip,
-				borderWidth = options.borderWidth,
-				tooltipDiv = renderer.ttDiv,
-				tooltipDivStyle = options.style,
-				tooltipLine = renderer.ttLine,
-				padding = parseInt(tooltipDivStyle.padding, 10);
-
-			// Add border styling from options to the style
-			tooltipDivStyle = merge(tooltipDivStyle, {
-				padding: padding + PX,
-				'background-color': options.backgroundColor,
-				'border-style': 'solid',
-				'border-width': borderWidth + PX,
-				'border-radius': options.borderRadius + PX
-			});
-
-			// Optionally add shadow
-			if (options.shadow) {
-				tooltipDivStyle = merge(tooltipDivStyle, {
-					'box-shadow': '1px 1px 3px gray', // w3c
-					'-webkit-box-shadow': '1px 1px 3px gray' // webkit
-				});
-			}
-			css(tooltipDiv, tooltipDivStyle);
-
-			// Set simple style on the line
-			css(tooltipLine, {
-				'border-left': '1px solid darkgray'
-			});
-
-			// This event is triggered when a new tooltip should be shown
-			addEvent(chart, 'tooltipRefresh', function (args) {
-				var chartContainer = chart.container,
-					offsetLeft = chartContainer.offsetLeft,
-					offsetTop = chartContainer.offsetTop,
-					position;
-
-				// Set the content of the tooltip
-				tooltipDiv.innerHTML = args.text;
-
-				// Compute the best position for the tooltip based on the divs size and container size.
-				position = chart.tooltip.getPosition(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, {plotX: args.x, plotY: args.y});
-
-				css(tooltipDiv, {
-					visibility: VISIBLE,
-					left: position.x + PX,
-					top: position.y + PX,
-					'border-color': args.borderColor
-				});
-
-				// Position the tooltip line
-				css(tooltipLine, {
-					visibility: VISIBLE,
-					left: offsetLeft + args.x + PX,
-					top: offsetTop + chart.plotTop + PX,
-					height: chart.plotHeight  + PX
-				});
-
-				// This timeout hides the tooltip after 3 seconds
-				// First clear any existing timer
-				if (renderer.ttTimer !== UNDEFINED) {
-					clearTimeout(renderer.ttTimer);
-				}
-
-				// Start a new timer that hides tooltip and line
-				renderer.ttTimer = setTimeout(function () {
-					css(tooltipDiv, { visibility: HIDDEN });
-					css(tooltipLine, { visibility: HIDDEN });
-				}, 3000);
-			});
-		},
-
-		/**
-		 * Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer.
-		 */
-		destroy: function () {
-			var renderer = this;
-
-			// Remove the canvas
-			discardElement(renderer.canvas);
-
-			// Kill the timer
-			if (renderer.ttTimer !== UNDEFINED) {
-				clearTimeout(renderer.ttTimer);
-			}
-
-			// Remove the divs for tooltip and line
-			discardElement(renderer.ttLine);
-			discardElement(renderer.ttDiv);
-			discardElement(renderer.hiddenSvg);
-
-			// Continue with base class
-			return SVGRenderer.prototype.destroy.apply(renderer);
-		},
-
-		/**
-		 * Take a color and return it if it's a string, do not make it a gradient even if it is a
-		 * gradient. Currently canvg cannot render gradients (turns out black),
-		 * see: http://code.google.com/p/canvg/issues/detail?id=104
-		 *
-		 * @param {Object} color The color or config object
-		 */
-		color: function (color, elem, prop) {
-			if (color && color.linearGradient) {
-				// Pick the end color and forward to base implementation
-				color = color.stops[color.stops.length - 1][1];
-			}
-			return SVGRenderer.prototype.color.call(this, color, elem, prop);
-		},
-
-		/**
-		 * Draws the SVG on the canvas or adds a draw invokation to the deferred list.
-		 */
-		draw: function () {
-			var renderer = this;
-			window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML);
-		}
-	});
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/data.js b/apps/static/js/plugins/highcharts/modules/data.js
deleted file mode 100644
index 93e8b8f1d..000000000
--- a/apps/static/js/plugins/highcharts/modules/data.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- Data plugin for Highcharts
-
- (c) 2012-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(j){var m=j.each,n=function(a,b){this.init(a,b)};j.extend(n.prototype,{init:function(a,b){this.options=a;this.chartOptions=b;this.columns=a.columns||this.rowsToColumns(a.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},getColumnDistribution:function(){var a=this.chartOptions,b=a&&a.chart&&a.chart.type,c=[];m(a&&a.series||[],function(a){c.push((j.seriesTypes[a.type||b||"line"].prototype.pointArrayMap||[0]).length)});this.valueCount=
-{global:(j.seriesTypes[b||"line"].prototype.pointArrayMap||[0]).length,individual:c}},dataFound:function(){if(this.options.switchRowsAndColumns)this.columns=this.rowsToColumns(this.columns);this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this,b=this.options,c=b.csv,d=this.columns,e=b.startRow||0,h=b.endRow||Number.MAX_VALUE,i=b.startColumn||0,g=b.endColumn||Number.MAX_VALUE,f,k,o=0;c&&(k=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(b.lineDelimiter||
-"\n"),f=b.itemDelimiter||(c.indexOf("\t")!==-1?"\t":","),m(k,function(b,c){var k=a.trim(b),j=k.indexOf("#")===0;c>=e&&c<=h&&!j&&k!==""&&(k=b.split(f),m(k,function(b,a){a>=i&&a<=g&&(d[a-i]||(d[a-i]=[]),d[a-i][o]=b)}),o+=1)}),this.dataFound())},parseTable:function(){var a=this.options,b=a.table,c=this.columns,d=a.startRow||0,e=a.endRow||Number.MAX_VALUE,h=a.startColumn||0,i=a.endColumn||Number.MAX_VALUE;b&&(typeof b==="string"&&(b=document.getElementById(b)),m(b.getElementsByTagName("tr"),function(a,
-b){b>=d&&b<=e&&m(a.children,function(a,e){if((a.tagName==="TD"||a.tagName==="TH")&&e>=h&&e<=i)c[e-h]||(c[e-h]=[]),c[e-h][b-d]=a.innerHTML})}),this.dataFound())},parseGoogleSpreadsheet:function(){var a=this,b=this.options,c=b.googleSpreadsheetKey,d=this.columns,e=b.startRow||0,h=b.endRow||Number.MAX_VALUE,i=b.startColumn||0,g=b.endColumn||Number.MAX_VALUE,f,k;c&&jQuery.ajax({dataType:"json",url:"https://spreadsheets.google.com/feeds/cells/"+c+"/"+(b.googleSpreadsheetWorksheet||"od6")+"/public/values?alt=json-in-script&callback=?",
-error:b.error,success:function(b){var b=b.feed.entry,c,j=b.length,m=0,n=0,l;for(l=0;l<j;l++)c=b[l],m=Math.max(m,c.gs$cell.col),n=Math.max(n,c.gs$cell.row);for(l=0;l<m;l++)if(l>=i&&l<=g)d[l-i]=[],d[l-i].length=Math.min(n,h-e);for(l=0;l<j;l++)if(c=b[l],f=c.gs$cell.row-1,k=c.gs$cell.col-1,k>=i&&k<=g&&f>=e&&f<=h)d[k-i][f-e]=c.content.$t;a.dataFound()}})},findHeaderRow:function(){m(this.columns,function(){});this.headerRow=0},trim:function(a){return typeof a==="string"?a.replace(/^\s+|\s+$/g,""):a},parseTypes:function(){for(var a=
-this.columns,b=a.length,c,d,e,h;b--;)for(c=a[b].length;c--;)d=a[b][c],e=parseFloat(d),h=this.trim(d),h==e?(a[b][c]=e,e>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(d=this.parseDate(d),b===0&&typeof d==="number"&&!isNaN(d)?(a[b][c]=d,a[b].isDatetime=!0):a[b][c]=h===""?null:h)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(a){return Date.UTC(+a[1],a[2]-1,+a[3])}}},parseDate:function(a){var b=this.options.parseDate,c,d,e;b&&(c=b(a));if(typeof a==="string")for(d in this.dateFormats)b=
-this.dateFormats[d],(e=a.match(b.regex))&&(c=b.parser(e));return c},rowsToColumns:function(a){var b,c,d,e,h;if(a){h=[];c=a.length;for(b=0;b<c;b++){e=a[b].length;for(d=0;d<e;d++)h[d]||(h[d]=[]),h[d][b]=a[b][d]}}return h},parsed:function(){this.options.parsed&&this.options.parsed.call(this,this.columns)},complete:function(){var a=this.columns,b,c,d=this.options,e,h,i,g,f,k;if(d.complete){this.getColumnDistribution();a.length>1&&(b=a.shift(),this.headerRow===0&&b.shift(),b.isDatetime?c="datetime":b.isNumeric||
-(c="category"));for(g=0;g<a.length;g++)if(this.headerRow===0)a[g].name=a[g].shift();h=[];for(g=0,k=0;g<a.length;k++){e=j.pick(this.valueCount.individual[k],this.valueCount.global);i=[];if(g+e<=a.length)for(f=0;f<a[g].length;f++)i[f]=[b[f],a[g][f]!==void 0?a[g][f]:null],e>1&&i[f].push(a[g+1][f]!==void 0?a[g+1][f]:null),e>2&&i[f].push(a[g+2][f]!==void 0?a[g+2][f]:null),e>3&&i[f].push(a[g+3][f]!==void 0?a[g+3][f]:null),e>4&&i[f].push(a[g+4][f]!==void 0?a[g+4][f]:null);h[k]={name:a[g].name,data:i};g+=
-e}d.complete({xAxis:{type:c},series:h})}}});j.Data=n;j.data=function(a,b){return new n(a,b)};j.wrap(j.Chart.prototype,"init",function(a,b,c){var d=this;b&&b.data?j.data(j.extend(b.data,{complete:function(e){b.hasOwnProperty("series")&&(typeof b.series==="object"?m(b.series,function(a,c){b.series[c]=j.merge(a,e.series[c])}):delete b.series);b=j.merge(e,b);a.call(d,b,c)}}),b):a.call(d,b,c)})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/data.src.js b/apps/static/js/plugins/highcharts/modules/data.src.js
deleted file mode 100644
index 1be448e0f..000000000
--- a/apps/static/js/plugins/highcharts/modules/data.src.js
+++ /dev/null
@@ -1,607 +0,0 @@
-/**
- * @license Data plugin for Highcharts
- *
- * (c) 2012-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-/*
- * The Highcharts Data plugin is a utility to ease parsing of input sources like
- * CSV, HTML tables or grid views into basic configuration options for use 
- * directly in the Highcharts constructor.
- *
- * Demo: http://jsfiddle.net/highcharts/SnLFj/
- *
- * --- OPTIONS ---
- *
- * - columns : Array<Array<Mixed>>
- * A two-dimensional array representing the input data on tabular form. This input can
- * be used when the data is already parsed, for example from a grid view component.
- * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns
- * are interpreted as series. See also the rows option.
- *
- * - complete : Function(chartOptions)
- * The callback that is evaluated when the data is finished loading, optionally from an 
- * external source, and parsed. The first argument passed is a finished chart options
- * object, containing series and an xAxis with categories if applicable. Thise options
- * can be extended with additional options and passed directly to the chart constructor.
- *
- * - csv : String
- * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn
- * and endColumn to delimit what part of the table is used. The lineDelimiter and 
- * itemDelimiter options define the CSV delimiter formats.
- * 
- * - endColumn : Integer
- * In tabular input data, the first row (indexed by 0) to use. Defaults to the last 
- * column containing data.
- *
- * - endRow : Integer
- * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row
- * containing data.
- *
- * - googleSpreadsheetKey : String 
- * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample
- * for general information on GS.
- *
- * - googleSpreadsheetWorksheet : String 
- * The Google Spreadsheet worksheet. The available id's can be read from 
- * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic
- *
- * - itemDelimiter : String
- * Item or cell delimiter for parsing CSV. Defaults to the tab character "\t" if a tab character
- * is found in the CSV string, if not it defaults to ",".
- *
- * - lineDelimiter : String
- * Line delimiter for parsing CSV. Defaults to "\n".
- *
- * - parsed : Function
- * A callback function to access the parsed columns, the two-dimentional input data
- * array directly, before they are interpreted into series data and categories.
- *
- * - parseDate : Function
- * A callback function to parse string representations of dates into JavaScript timestamps.
- * Return an integer on success.
- *
- * - rows : Array<Array<Mixed>>
- * The same as the columns input option, but defining rows intead of columns.
- *
- * - startColumn : Integer
- * In tabular input data, the first column (indexed by 0) to use. 
- *
- * - startRow : Integer
- * In tabular input data, the first row (indexed by 0) to use.
- *
- * - switchRowsAndColumns : Boolean
- * Switch rows and columns of the input data, so that this.columns effectively becomes the
- * rows of the data set, and the rows are interpreted as series.
- *
- * - table : String|HTMLElement
- * A HTML table or the id of such to be parsed as input data. Related options ara startRow,
- * endRow, startColumn and endColumn to delimit what part of the table is used.
- */
-
-// JSLint options:
-/*global jQuery */
-
-(function (Highcharts) {	
-	
-	// Utilities
-	var each = Highcharts.each;
-	
-	
-	// The Data constructor
-	var Data = function (dataOptions, chartOptions) {
-		this.init(dataOptions, chartOptions);
-	};
-	
-	// Set the prototype properties
-	Highcharts.extend(Data.prototype, {
-		
-	/**
-	 * Initialize the Data object with the given options
-	 */
-	init: function (options, chartOptions) {
-		this.options = options;
-		this.chartOptions = chartOptions;
-		this.columns = options.columns || this.rowsToColumns(options.rows) || [];
-
-		// No need to parse or interpret anything
-		if (this.columns.length) {
-			this.dataFound();
-
-		// Parse and interpret
-		} else {
-
-			// Parse a CSV string if options.csv is given
-			this.parseCSV();
-			
-			// Parse a HTML table if options.table is given
-			this.parseTable();
-
-			// Parse a Google Spreadsheet 
-			this.parseGoogleSpreadsheet();	
-		}
-
-	},
-
-	/**
-	 * Get the column distribution. For example, a line series takes a single column for 
-	 * Y values. A range series takes two columns for low and high values respectively,
-	 * and an OHLC series takes four columns.
-	 */
-	getColumnDistribution: function () {
-		var chartOptions = this.chartOptions,
-			getValueCount = function (type) {
-				return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
-			},
-			globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
-			individualCounts = [];
-
-		each((chartOptions && chartOptions.series) || [], function (series) {
-			individualCounts.push(getValueCount(series.type || globalType));
-		});
-
-		this.valueCount = {
-			global: getValueCount(globalType),
-			individual: individualCounts
-		};
-	},
-
-	/**
-	 * When the data is parsed into columns, either by CSV, table, GS or direct input,
-	 * continue with other operations.
-	 */
-	dataFound: function () {
-		
-		if (this.options.switchRowsAndColumns) {
-			this.columns = this.rowsToColumns(this.columns);
-		}
-
-		// Interpret the values into right types
-		this.parseTypes();
-		
-		// Use first row for series names?
-		this.findHeaderRow();
-		
-		// Handle columns if a handleColumns callback is given
-		this.parsed();
-		
-		// Complete if a complete callback is given
-		this.complete();
-		
-	},
-	
-	/**
-	 * Parse a CSV input string
-	 */
-	parseCSV: function () {
-		var self = this,
-			options = this.options,
-			csv = options.csv,
-			columns = this.columns,
-			startRow = options.startRow || 0,
-			endRow = options.endRow || Number.MAX_VALUE,
-			startColumn = options.startColumn || 0,
-			endColumn = options.endColumn || Number.MAX_VALUE,
-			itemDelimiter,
-			lines,
-			activeRowNo = 0;
-			
-		if (csv) {
-			
-			lines = csv
-				.replace(/\r\n/g, "\n") // Unix
-				.replace(/\r/g, "\n") // Mac
-				.split(options.lineDelimiter || "\n");
-
-			itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
-			
-			each(lines, function (line, rowNo) {
-				var trimmed = self.trim(line),
-					isComment = trimmed.indexOf('#') === 0,
-					isBlank = trimmed === '',
-					items;
-				
-				if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
-					items = line.split(itemDelimiter);
-					each(items, function (item, colNo) {
-						if (colNo >= startColumn && colNo <= endColumn) {
-							if (!columns[colNo - startColumn]) {
-								columns[colNo - startColumn] = [];					
-							}
-							
-							columns[colNo - startColumn][activeRowNo] = item;
-						}
-					});
-					activeRowNo += 1;
-				}
-			});
-
-			this.dataFound();
-		}
-	},
-	
-	/**
-	 * Parse a HTML table
-	 */
-	parseTable: function () {
-		var options = this.options,
-			table = options.table,
-			columns = this.columns,
-			startRow = options.startRow || 0,
-			endRow = options.endRow || Number.MAX_VALUE,
-			startColumn = options.startColumn || 0,
-			endColumn = options.endColumn || Number.MAX_VALUE;
-
-		if (table) {
-			
-			if (typeof table === 'string') {
-				table = document.getElementById(table);
-			}
-			
-			each(table.getElementsByTagName('tr'), function (tr, rowNo) {
-				if (rowNo >= startRow && rowNo <= endRow) {
-					each(tr.children, function (item, colNo) {
-						if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
-							if (!columns[colNo - startColumn]) {
-								columns[colNo - startColumn] = [];					
-							}
-							
-							columns[colNo - startColumn][rowNo - startRow] = item.innerHTML;
-						}
-					});
-				}
-			});
-
-			this.dataFound(); // continue
-		}
-	},
-
-	/**
-	 */
-	parseGoogleSpreadsheet: function () {
-		var self = this,
-			options = this.options,
-			googleSpreadsheetKey = options.googleSpreadsheetKey,
-			columns = this.columns,
-			startRow = options.startRow || 0,
-			endRow = options.endRow || Number.MAX_VALUE,
-			startColumn = options.startColumn || 0,
-			endColumn = options.endColumn || Number.MAX_VALUE,
-			gr, // google row
-			gc; // google column
-
-		if (googleSpreadsheetKey) {
-			jQuery.ajax({
-				dataType: 'json', 
-				url: 'https://spreadsheets.google.com/feeds/cells/' + 
-				  googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
-					  '/public/values?alt=json-in-script&callback=?',
-				error: options.error,
-				success: function (json) {
-					// Prepare the data from the spreadsheat
-					var cells = json.feed.entry,
-						cell,
-						cellCount = cells.length,
-						colCount = 0,
-						rowCount = 0,
-						i;
-				
-					// First, find the total number of columns and rows that 
-					// are actually filled with data
-					for (i = 0; i < cellCount; i++) {
-						cell = cells[i];
-						colCount = Math.max(colCount, cell.gs$cell.col);
-						rowCount = Math.max(rowCount, cell.gs$cell.row);			
-					}
-				
-					// Set up arrays containing the column data
-					for (i = 0; i < colCount; i++) {
-						if (i >= startColumn && i <= endColumn) {
-							// Create new columns with the length of either end-start or rowCount
-							columns[i - startColumn] = [];
-
-							// Setting the length to avoid jslint warning
-							columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
-						}
-					}
-					
-					// Loop over the cells and assign the value to the right
-					// place in the column arrays
-					for (i = 0; i < cellCount; i++) {
-						cell = cells[i];
-						gr = cell.gs$cell.row - 1; // rows start at 1
-						gc = cell.gs$cell.col - 1; // columns start at 1
-
-						// If both row and col falls inside start and end
-						// set the transposed cell value in the newly created columns
-						if (gc >= startColumn && gc <= endColumn &&
-							gr >= startRow && gr <= endRow) {
-							columns[gc - startColumn][gr - startRow] = cell.content.$t;
-						}
-					}
-					self.dataFound();
-				}
-			});
-		}
-	},
-	
-	/**
-	 * Find the header row. For now, we just check whether the first row contains
-	 * numbers or strings. Later we could loop down and find the first row with 
-	 * numbers.
-	 */
-	findHeaderRow: function () {
-		var headerRow = 0;
-		each(this.columns, function (column) {
-			if (typeof column[0] !== 'string') {
-				headerRow = null;
-			}
-		});
-		this.headerRow = 0;			
-	},
-	
-	/**
-	 * Trim a string from whitespace
-	 */
-	trim: function (str) {
-		return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str;
-	},
-	
-	/**
-	 * Parse numeric cells in to number types and date types in to true dates.
-	 */
-	parseTypes: function () {
-		var columns = this.columns,
-			col = columns.length, 
-			row,
-			val,
-			floatVal,
-			trimVal,
-			dateVal;
-			
-		while (col--) {
-			row = columns[col].length;
-			while (row--) {
-				val = columns[col][row];
-				floatVal = parseFloat(val);
-				trimVal = this.trim(val);
-
-				/*jslint eqeq: true*/
-				if (trimVal == floatVal) { // is numeric
-				/*jslint eqeq: false*/
-					columns[col][row] = floatVal;
-					
-					// If the number is greater than milliseconds in a year, assume datetime
-					if (floatVal > 365 * 24 * 3600 * 1000) {
-						columns[col].isDatetime = true;
-					} else {
-						columns[col].isNumeric = true;
-					}					
-				
-				} else { // string, continue to determine if it is a date string or really a string
-					dateVal = this.parseDate(val);
-					
-					if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date
-						columns[col][row] = dateVal;
-						columns[col].isDatetime = true;
-					
-					} else { // string
-						columns[col][row] = trimVal === '' ? null : trimVal;
-					}
-				}
-				
-			}
-		}
-	},
-	
-	/**
-	 * A collection of available date formats, extendable from the outside to support
-	 * custom date formats.
-	 */
-	dateFormats: {
-		'YYYY-mm-dd': {
-			regex: '^([0-9]{4})-([0-9]{2})-([0-9]{2})$',
-			parser: function (match) {
-				return Date.UTC(+match[1], match[2] - 1, +match[3]);
-			}
-		}
-	},
-	
-	/**
-	 * Parse a date and return it as a number. Overridable through options.parseDate.
-	 */
-	parseDate: function (val) {
-		var parseDate = this.options.parseDate,
-			ret,
-			key,
-			format,
-			match;
-
-		if (parseDate) {
-			ret = parseDate(val);
-		}
-			
-		if (typeof val === 'string') {
-			for (key in this.dateFormats) {
-				format = this.dateFormats[key];
-				match = val.match(format.regex);
-				if (match) {
-					ret = format.parser(match);
-				}
-			}
-		}
-		return ret;
-	},
-	
-	/**
-	 * Reorganize rows into columns
-	 */
-	rowsToColumns: function (rows) {
-		var row,
-			rowsLength,
-			col,
-			colsLength,
-			columns;
-
-		if (rows) {
-			columns = [];
-			rowsLength = rows.length;
-			for (row = 0; row < rowsLength; row++) {
-				colsLength = rows[row].length;
-				for (col = 0; col < colsLength; col++) {
-					if (!columns[col]) {
-						columns[col] = [];
-					}
-					columns[col][row] = rows[row][col];
-				}
-			}
-		}
-		return columns;
-	},
-	
-	/**
-	 * A hook for working directly on the parsed columns
-	 */
-	parsed: function () {
-		if (this.options.parsed) {
-			this.options.parsed.call(this, this.columns);
-		}
-	},
-	
-	/**
-	 * If a complete callback function is provided in the options, interpret the 
-	 * columns into a Highcharts options object.
-	 */
-	complete: function () {
-		
-		var columns = this.columns,
-			firstCol,
-			type,
-			options = this.options,
-			valueCount,
-			series,
-			data,
-			i,
-			j,
-			seriesIndex;
-			
-		
-		if (options.complete) {
-
-			this.getColumnDistribution();
-			
-			// Use first column for X data or categories?
-			if (columns.length > 1) {
-				firstCol = columns.shift();
-				if (this.headerRow === 0) {
-					firstCol.shift(); // remove the first cell
-				}
-				
-				
-				if (firstCol.isDatetime) {
-					type = 'datetime';
-				} else if (!firstCol.isNumeric) {
-					type = 'category';
-				}
-			}
-
-			// Get the names and shift the top row
-			for (i = 0; i < columns.length; i++) {
-				if (this.headerRow === 0) {
-					columns[i].name = columns[i].shift();
-				}
-			}
-			
-			// Use the next columns for series
-			series = [];
-			for (i = 0, seriesIndex = 0; i < columns.length; seriesIndex++) {
-
-				// This series' value count
-				valueCount = Highcharts.pick(this.valueCount.individual[seriesIndex], this.valueCount.global);
-				
-				// Iterate down the cells of each column and add data to the series
-				data = [];
-
-				// Only loop and fill the data series if there are columns available.
-				// We need this check to avoid reading outside the array bounds.
-				if (i + valueCount <= columns.length) {
-					for (j = 0; j < columns[i].length; j++) {
-						data[j] = [
-							firstCol[j],
-							columns[i][j] !== undefined ? columns[i][j] : null
-						];
-						if (valueCount > 1) {
-							data[j].push(columns[i + 1][j] !== undefined ? columns[i + 1][j] : null);
-						}
-						if (valueCount > 2) {
-							data[j].push(columns[i + 2][j] !== undefined ? columns[i + 2][j] : null);
-						}
-						if (valueCount > 3) {
-							data[j].push(columns[i + 3][j] !== undefined ? columns[i + 3][j] : null);
-						}
-						if (valueCount > 4) {
-							data[j].push(columns[i + 4][j] !== undefined ? columns[i + 4][j] : null);
-						}
-					}
-				}
-
-				// Add the series
-				series[seriesIndex] = {
-					name: columns[i].name,
-					data: data
-				};
-
-				i += valueCount;
-			}
-			
-			// Do the callback
-			options.complete({
-				xAxis: {
-					type: type
-				},
-				series: series
-			});
-		}
-	}
-	});
-	
-	// Register the Data prototype and data function on Highcharts
-	Highcharts.Data = Data;
-	Highcharts.data = function (options, chartOptions) {
-		return new Data(options, chartOptions);
-	};
-
-	// Extend Chart.init so that the Chart constructor accepts a new configuration
-	// option group, data.
-	Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) {
-		var chart = this;
-
-		if (userOptions && userOptions.data) {
-			Highcharts.data(Highcharts.extend(userOptions.data, {
-				complete: function (dataOptions) {
-					
-					// Merge series configs
-					if (userOptions.hasOwnProperty('series')) {
-						if (typeof userOptions.series === 'object') {
-							each(userOptions.series, function (series, i) {
-								userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]);
-							});
-						} else { // Allow merging in dataOptions.series (#2856)
-							delete userOptions.series;
-						}
-					}
-
-					// Do the merge
-					userOptions = Highcharts.merge(dataOptions, userOptions);
-
-					proceed.call(chart, userOptions, callback);
-				}
-			}), userOptions);
-		} else {
-			proceed.call(chart, userOptions, callback);
-		}
-	});
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/drilldown.js b/apps/static/js/plugins/highcharts/modules/drilldown.js
deleted file mode 100644
index 6f147802c..000000000
--- a/apps/static/js/plugins/highcharts/modules/drilldown.js
+++ /dev/null
@@ -1,14 +0,0 @@
-(function(g){function s(a,b,d){return"rgba("+[Math.round(a[0]+(b[0]-a[0])*d),Math.round(a[1]+(b[1]-a[1])*d),Math.round(a[2]+(b[2]-a[2])*d),a[3]+(b[3]-a[3])*d].join(",")+")"}var t=function(){},o=g.getOptions(),i=g.each,p=g.extend,x=g.format,q=g.wrap,l=g.Chart,n=g.seriesTypes,u=n.pie,m=n.column,v=HighchartsAdapter.fireEvent,y=HighchartsAdapter.inArray;p(o.lang,{drillUpText:"◁ Back to {series.name}"});o.drilldown={activeAxisLabelStyle:{cursor:"pointer",color:"#0d233a",fontWeight:"bold",textDecoration:"underline"},
-activeDataLabelStyle:{cursor:"pointer",color:"#0d233a",fontWeight:"bold",textDecoration:"underline"},animation:{duration:500},drillUpButton:{position:{align:"right",x:-10,y:10}}};g.SVGRenderer.prototype.Element.prototype.fadeIn=function(a){this.attr({opacity:0.1,visibility:"inherit"}).animate({opacity:1},a||{duration:250})};l.prototype.addSeriesAsDrilldown=function(a,b){this.addSingleSeriesAsDrilldown(a,b);this.applyDrilldown()};l.prototype.addSingleSeriesAsDrilldown=function(a,b){var d=a.series,
-c=d.xAxis,f=d.yAxis,h;h=a.color||d.color;var e,w=[],g=[],k;k=d.levelNumber||0;b=p({color:h},b);e=y(a,d.points);i(d.chart.series,function(a){if(a.xAxis===c)w.push(a),g.push(a.userOptions),a.levelNumber=a.levelNumber||0});h={levelNumber:k,seriesOptions:d.userOptions,levelSeriesOptions:g,levelSeries:w,shapeArgs:a.shapeArgs,bBox:a.graphic.getBBox(),color:h,lowerSeriesOptions:b,pointOptions:d.options.data[e],pointIndex:e,oldExtremes:{xMin:c&&c.userMin,xMax:c&&c.userMax,yMin:f&&f.userMin,yMax:f&&f.userMax}};
-if(!this.drilldownLevels)this.drilldownLevels=[];this.drilldownLevels.push(h);h=h.lowerSeries=this.addSeries(b,!1);h.levelNumber=k+1;if(c)c.oldPos=c.pos,c.userMin=c.userMax=null,f.userMin=f.userMax=null;if(d.type===h.type)h.animate=h.animateDrilldown||t,h.options.animation=!0};l.prototype.applyDrilldown=function(){var a=this.drilldownLevels,b=a[a.length-1].levelNumber;i(this.drilldownLevels,function(a){a.levelNumber===b&&i(a.levelSeries,function(a){a.levelNumber===b&&a.remove(!1)})});this.redraw();
-this.showDrillUpButton()};l.prototype.getDrilldownBackText=function(){var a=this.drilldownLevels[this.drilldownLevels.length-1];a.series=a.seriesOptions;return x(this.options.lang.drillUpText,a)};l.prototype.showDrillUpButton=function(){var a=this,b=this.getDrilldownBackText(),d=a.options.drilldown.drillUpButton,c,f;this.drillUpButton?this.drillUpButton.attr({text:b}).align():(f=(c=d.theme)&&c.states,this.drillUpButton=this.renderer.button(b,null,null,function(){a.drillUp()},c,f&&f.hover,f&&f.select).attr({align:d.position.align,
-zIndex:9}).add().align(d.position,!1,d.relativeTo||"plotBox"))};l.prototype.drillUp=function(){for(var a=this,b=a.drilldownLevels,d=b[b.length-1].levelNumber,c=b.length,f=a.series,h=f.length,e,g,j,k,l=function(b){var c;i(f,function(a){a.userOptions===b&&(c=a)});c=c||a.addSeries(b,!1);if(c.type===g.type&&c.animateDrillupTo)c.animate=c.animateDrillupTo;b===e.seriesOptions&&(j=c)};c--;)if(e=b[c],e.levelNumber===d){b.pop();g=e.lowerSeries;if(!g.chart)for(;h--;)if(f[h].options.id===e.lowerSeriesOptions.id){g=
-f[h];break}g.xData=[];i(e.levelSeriesOptions,l);v(a,"drillup",{seriesOptions:e.seriesOptions});if(j.type===g.type)j.drilldownLevel=e,j.options.animation=a.options.drilldown.animation,g.animateDrillupFrom&&g.animateDrillupFrom(e);j.levelNumber=d;g.remove(!1);if(j.xAxis)k=e.oldExtremes,j.xAxis.setExtremes(k.xMin,k.xMax,!1),j.yAxis.setExtremes(k.yMin,k.yMax,!1)}this.redraw();this.drilldownLevels.length===0?this.drillUpButton=this.drillUpButton.destroy():this.drillUpButton.attr({text:this.getDrilldownBackText()}).align()};
-m.prototype.supportsDrilldown=!0;m.prototype.animateDrillupTo=function(a){if(!a){var b=this,d=b.drilldownLevel;i(this.points,function(a){a.graphic.hide();a.dataLabel&&a.dataLabel.hide();a.connector&&a.connector.hide()});setTimeout(function(){i(b.points,function(a,b){var h=b===(d&&d.pointIndex)?"show":"fadeIn",e=h==="show"?!0:void 0;a.graphic[h](e);if(a.dataLabel)a.dataLabel[h](e);if(a.connector)a.connector[h](e)})},Math.max(this.chart.options.drilldown.animation.duration-50,0));this.animate=t}};m.prototype.animateDrilldown=
-function(a){var b=this,d=this.chart.drilldownLevels,c=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1].shapeArgs,f=this.chart.options.drilldown.animation;if(!a)i(d,function(a){if(b.userOptions===a.lowerSeriesOptions)c=a.shapeArgs}),c.x+=this.xAxis.oldPos-this.xAxis.pos,i(this.points,function(a){a.graphic&&a.graphic.attr(c).animate(a.shapeArgs,f);a.dataLabel&&a.dataLabel.fadeIn(f)}),this.animate=null};m.prototype.animateDrillupFrom=function(a){var b=this.chart.options.drilldown.animation,
-d=this.group,c=this;i(c.trackerGroups,function(a){if(c[a])c[a].on("mouseover")});delete this.group;i(this.points,function(c){var h=c.graphic,e=g.Color(c.color).rgba,i=g.Color(a.color).rgba,j=function(){h.destroy();d&&(d=d.destroy())};h&&(delete c.graphic,b?h.animate(a.shapeArgs,g.merge(b,{step:function(a,b){b.prop==="start"&&e.length===4&&i.length===4&&this.attr({fill:s(e,i,b.pos)})},complete:j})):(h.attr(a.shapeArgs),j()))})};u&&p(u.prototype,{supportsDrilldown:!0,animateDrillupTo:m.prototype.animateDrillupTo,
-animateDrillupFrom:m.prototype.animateDrillupFrom,animateDrilldown:function(a){var b=this.chart.drilldownLevels[this.chart.drilldownLevels.length-1],d=this.chart.options.drilldown.animation,c=b.shapeArgs,f=c.start,h=(c.end-f)/this.points.length,e=g.Color(b.color).rgba;if(!a)i(this.points,function(a,b){var i=g.Color(a.color).rgba;a.graphic.attr(g.merge(c,{start:f+b*h,end:f+(b+1)*h}))[d?"animate":"attr"](a.shapeArgs,g.merge(d,{step:function(a,b){b.prop==="start"&&e.length===4&&i.length===4&&this.attr({fill:s(e,
-i,b.pos)})}}))}),this.animate=null}});g.Point.prototype.doDrilldown=function(a){for(var b=this.series.chart,d=b.options.drilldown,c=(d.series||[]).length,f;c--&&!f;)d.series[c].id===this.drilldown&&(f=d.series[c]);v(b,"drilldown",{point:this,seriesOptions:f});f&&(a?b.addSingleSeriesAsDrilldown(this,f):b.addSeriesAsDrilldown(this,f))};q(g.Point.prototype,"init",function(a,b,d,c){var f=a.call(this,b,d,c),h=b.chart,e=(a=b.xAxis&&b.xAxis.ticks[c])&&a.label;if(f.drilldown){if(g.addEvent(f,"click",function(){f.doDrilldown()}),
-e){if(!e._basicStyle)e._basicStyle=e.element.getAttribute("style");e.addClass("highcharts-drilldown-axis-label").css(h.options.drilldown.activeAxisLabelStyle).on("click",function(){i(e.ddPoints,function(a){a.doDrilldown&&a.doDrilldown(!0)});h.applyDrilldown()});if(!e.ddPoints)e.ddPoints=[];e.ddPoints.push(f)}}else e&&e._basicStyle&&e.element.setAttribute("style",e._basicStyle);return f});q(g.Series.prototype,"drawDataLabels",function(a){var b=this.chart.options.drilldown.activeDataLabelStyle;a.call(this);
-i(this.points,function(a){if(a.drilldown&&a.dataLabel)a.dataLabel.attr({"class":"highcharts-drilldown-data-label"}).css(b).on("click",function(){a.doDrilldown()})})});var r,o=function(a){a.call(this);i(this.points,function(a){a.drilldown&&a.graphic&&a.graphic.attr({"class":"highcharts-drilldown-point"}).css({cursor:"pointer"})})};for(r in n)n[r].prototype.supportsDrilldown&&q(n[r].prototype,"drawTracker",o)})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/drilldown.src.js b/apps/static/js/plugins/highcharts/modules/drilldown.src.js
deleted file mode 100644
index 9dff53f5f..000000000
--- a/apps/static/js/plugins/highcharts/modules/drilldown.src.js
+++ /dev/null
@@ -1,585 +0,0 @@
-/**
- * Highcharts Drilldown plugin
- * 
- * Author: Torstein Honsi
- * License: MIT License
- *
- * Demo: http://jsfiddle.net/highcharts/Vf3yT/
- */
-
-/*global HighchartsAdapter*/
-(function (H) {
-
-	"use strict";
-
-	var noop = function () {},
-		defaultOptions = H.getOptions(),
-		each = H.each,
-		extend = H.extend,
-		format = H.format,
-		wrap = H.wrap,
-		Chart = H.Chart,
-		seriesTypes = H.seriesTypes,
-		PieSeries = seriesTypes.pie,
-		ColumnSeries = seriesTypes.column,
-		fireEvent = HighchartsAdapter.fireEvent,
-		inArray = HighchartsAdapter.inArray;
-
-	// Utilities
-	function tweenColors(startColor, endColor, pos) {
-		var rgba = [
-				Math.round(startColor[0] + (endColor[0] - startColor[0]) * pos),
-				Math.round(startColor[1] + (endColor[1] - startColor[1]) * pos),
-				Math.round(startColor[2] + (endColor[2] - startColor[2]) * pos),
-				startColor[3] + (endColor[3] - startColor[3]) * pos
-			];
-		return 'rgba(' + rgba.join(',') + ')';
-	}
-
-	// Add language
-	extend(defaultOptions.lang, {
-		drillUpText: '◁ Back to {series.name}'
-	});
-	defaultOptions.drilldown = {
-		activeAxisLabelStyle: {
-			cursor: 'pointer',
-			color: '#0d233a',
-			fontWeight: 'bold',
-			textDecoration: 'underline'			
-		},
-		activeDataLabelStyle: {
-			cursor: 'pointer',
-			color: '#0d233a',
-			fontWeight: 'bold',
-			textDecoration: 'underline'			
-		},
-		animation: {
-			duration: 500
-		},
-		drillUpButton: {
-			position: { 
-				align: 'right',
-				x: -10,
-				y: 10
-			}
-			// relativeTo: 'plotBox'
-			// theme
-		}
-	};	
-
-	/**
-	 * A general fadeIn method
-	 */
-	H.SVGRenderer.prototype.Element.prototype.fadeIn = function (animation) {
-		this
-		.attr({
-			opacity: 0.1,
-			visibility: 'inherit'
-		})
-		.animate({
-			opacity: 1
-		}, animation || {
-			duration: 250
-		});
-	};
-
-	Chart.prototype.addSeriesAsDrilldown = function (point, ddOptions) {
-		this.addSingleSeriesAsDrilldown(point, ddOptions);
-		this.applyDrilldown();
-	};
-	Chart.prototype.addSingleSeriesAsDrilldown = function (point, ddOptions) {
-		var oldSeries = point.series,
-			xAxis = oldSeries.xAxis,
-			yAxis = oldSeries.yAxis,
-			newSeries,
-			color = point.color || oldSeries.color,
-			pointIndex,
-			levelSeries = [],
-			levelSeriesOptions = [],
-			level,
-			levelNumber;
-
-		levelNumber = oldSeries.levelNumber || 0;
-			
-		ddOptions = extend({
-			color: color
-		}, ddOptions);
-		pointIndex = inArray(point, oldSeries.points);
-
-		// Record options for all current series
-		each(oldSeries.chart.series, function (series) {
-			if (series.xAxis === xAxis) {
-				levelSeries.push(series);
-				levelSeriesOptions.push(series.userOptions);
-				series.levelNumber = series.levelNumber || 0;
-			}
-		});
-		
-		// Add a record of properties for each drilldown level
-		level = {
-			levelNumber: levelNumber,
-			seriesOptions: oldSeries.userOptions,
-			levelSeriesOptions: levelSeriesOptions,
-			levelSeries: levelSeries,
-			shapeArgs: point.shapeArgs,
-			bBox: point.graphic.getBBox(),
-			color: color,
-			lowerSeriesOptions: ddOptions,
-			pointOptions: oldSeries.options.data[pointIndex],
-			pointIndex: pointIndex,
-			oldExtremes: {
-				xMin: xAxis && xAxis.userMin,
-				xMax: xAxis && xAxis.userMax,
-				yMin: yAxis && yAxis.userMin,
-				yMax: yAxis && yAxis.userMax
-			}
-		};
-
-		// Generate and push it to a lookup array
-		if (!this.drilldownLevels) {
-			this.drilldownLevels = [];
-		}
-		this.drilldownLevels.push(level);
-
-		newSeries = level.lowerSeries = this.addSeries(ddOptions, false);
-		newSeries.levelNumber = levelNumber + 1;
-		if (xAxis) {
-			xAxis.oldPos = xAxis.pos;
-			xAxis.userMin = xAxis.userMax = null;
-			yAxis.userMin = yAxis.userMax = null;
-		}
-
-		// Run fancy cross-animation on supported and equal types
-		if (oldSeries.type === newSeries.type) {
-			newSeries.animate = newSeries.animateDrilldown || noop;
-			newSeries.options.animation = true;
-		}
-	};
-
-	Chart.prototype.applyDrilldown = function () {
-		var drilldownLevels = this.drilldownLevels, 
-			levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber;
-		
-		each(this.drilldownLevels, function (level) {
-			if (level.levelNumber === levelToRemove) {
-				each(level.levelSeries, function (series) {
-					if (series.levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown
-						series.remove(false);
-					}
-				});
-			}
-		});
-		
-		this.redraw();
-		this.showDrillUpButton();
-	};
-
-	Chart.prototype.getDrilldownBackText = function () {
-		var lastLevel = this.drilldownLevels[this.drilldownLevels.length - 1];
-		lastLevel.series = lastLevel.seriesOptions;
-		return format(this.options.lang.drillUpText, lastLevel);
-
-	};
-
-	Chart.prototype.showDrillUpButton = function () {
-		var chart = this,
-			backText = this.getDrilldownBackText(),
-			buttonOptions = chart.options.drilldown.drillUpButton,
-			attr,
-			states;
-			
-
-		if (!this.drillUpButton) {
-			attr = buttonOptions.theme;
-			states = attr && attr.states;
-						
-			this.drillUpButton = this.renderer.button(
-				backText,
-				null,
-				null,
-				function () {
-					chart.drillUp(); 
-				},
-				attr, 
-				states && states.hover,
-				states && states.select
-			)
-			.attr({
-				align: buttonOptions.position.align,
-				zIndex: 9
-			})
-			.add()
-			.align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
-		} else {
-			this.drillUpButton.attr({
-				text: backText
-			})
-			.align();
-		}
-	};
-
-	Chart.prototype.drillUp = function () {
-		var chart = this,
-			drilldownLevels = chart.drilldownLevels,
-			levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber,
-			i = drilldownLevels.length,
-			chartSeries = chart.series,
-			seriesI = chartSeries.length,
-			level,
-			oldSeries,
-			newSeries,
-			oldExtremes,
-			addSeries = function (seriesOptions) {
-				var addedSeries;
-				each(chartSeries, function (series) {
-					if (series.userOptions === seriesOptions) {
-						addedSeries = series;
-					}
-				});
-
-				addedSeries = addedSeries || chart.addSeries(seriesOptions, false);
-				if (addedSeries.type === oldSeries.type && addedSeries.animateDrillupTo) {
-					addedSeries.animate = addedSeries.animateDrillupTo;
-				}
-				if (seriesOptions === level.seriesOptions) {
-					newSeries = addedSeries;
-				}
-			};
-		
-		while (i--) {
-
-			level = drilldownLevels[i];
-			if (level.levelNumber === levelNumber) {
-				drilldownLevels.pop();
-				
-				// Get the lower series by reference or id
-				oldSeries = level.lowerSeries;
-				if (!oldSeries.chart) {  // #2786
-					while (seriesI--) {
-						if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id) {
-							oldSeries = chartSeries[seriesI];
-							break;
-						}
-					}
-				}
-				oldSeries.xData = []; // Overcome problems with minRange (#2898)
-
-				each(level.levelSeriesOptions, addSeries);
-				
-				fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions });
-
-				if (newSeries.type === oldSeries.type) {
-					newSeries.drilldownLevel = level;
-					newSeries.options.animation = chart.options.drilldown.animation;
-
-					if (oldSeries.animateDrillupFrom) {
-						oldSeries.animateDrillupFrom(level);
-					}
-				}
-				newSeries.levelNumber = levelNumber;
-				
-				oldSeries.remove(false);
-
-				// Reset the zoom level of the upper series
-				if (newSeries.xAxis) {
-					oldExtremes = level.oldExtremes;
-					newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false);
-					newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false);
-				}
-			}
-		}
-
-		this.redraw();
-
-		if (this.drilldownLevels.length === 0) {
-			this.drillUpButton = this.drillUpButton.destroy();
-		} else {
-			this.drillUpButton.attr({
-				text: this.getDrilldownBackText()
-			})
-			.align();
-		}
-	};
-
-
-	ColumnSeries.prototype.supportsDrilldown = true;
-	
-	/**
-	 * When drilling up, keep the upper series invisible until the lower series has
-	 * moved into place
-	 */
-	ColumnSeries.prototype.animateDrillupTo = function (init) {
-		if (!init) {
-			var newSeries = this,
-				level = newSeries.drilldownLevel;
-
-			each(this.points, function (point) {
-				point.graphic.hide();
-				if (point.dataLabel) {
-					point.dataLabel.hide();
-				}
-				if (point.connector) {
-					point.connector.hide();
-				}
-			});
-
-
-			// Do dummy animation on first point to get to complete
-			setTimeout(function () {
-				each(newSeries.points, function (point, i) {  
-					// Fade in other points			  
-					var verb = i === (level && level.pointIndex) ? 'show' : 'fadeIn',
-						inherit = verb === 'show' ? true : undefined;
-					point.graphic[verb](inherit);
-					if (point.dataLabel) {
-						point.dataLabel[verb](inherit);
-					}
-					if (point.connector) {
-						point.connector[verb](inherit);
-					}
-				});
-			}, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));
-
-			// Reset
-			this.animate = noop;
-		}
-
-	};
-	
-	ColumnSeries.prototype.animateDrilldown = function (init) {
-		var series = this,
-			drilldownLevels = this.chart.drilldownLevels,
-			animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs,
-			animationOptions = this.chart.options.drilldown.animation;
-			
-		if (!init) {
-			each(drilldownLevels, function (level) {
-				if (series.userOptions === level.lowerSeriesOptions) {
-					animateFrom = level.shapeArgs;
-				}
-			});
-
-			animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos);
-	
-			each(this.points, function (point) {
-				if (point.graphic) {
-					point.graphic
-						.attr(animateFrom)
-						.animate(point.shapeArgs, animationOptions);
-				}
-				if (point.dataLabel) {
-					point.dataLabel.fadeIn(animationOptions);
-				}
-			});
-			this.animate = null;
-		}
-		
-	};
-
-	/**
-	 * When drilling up, pull out the individual point graphics from the lower series
-	 * and animate them into the origin point in the upper series.
-	 */
-	ColumnSeries.prototype.animateDrillupFrom = function (level) {
-		var animationOptions = this.chart.options.drilldown.animation,
-			group = this.group,
-			series = this;
-
-		// Cancel mouse events on the series group (#2787)
-		each(series.trackerGroups, function (key) {
-			if (series[key]) { // we don't always have dataLabelsGroup
-				series[key].on('mouseover');
-			}
-		});
-			
-
-		delete this.group;
-		each(this.points, function (point) {
-			var graphic = point.graphic,
-				startColor = H.Color(point.color).rgba,
-				endColor = H.Color(level.color).rgba,
-				complete = function () {
-					graphic.destroy();
-					if (group) {
-						group = group.destroy();
-					}
-				};
-
-			if (graphic) {
-			
-				delete point.graphic;
-
-				if (animationOptions) {
-					/*jslint unparam: true*/
-					graphic.animate(level.shapeArgs, H.merge(animationOptions, {
-						step: function (val, fx) {
-							if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) {
-								this.attr({
-									fill: tweenColors(startColor, endColor, fx.pos)
-								});
-							}
-						},
-						complete: complete
-					}));
-					/*jslint unparam: false*/
-				} else {
-					graphic.attr(level.shapeArgs);
-					complete();
-				}
-			}
-		});
-	};
-
-	if (PieSeries) {
-		extend(PieSeries.prototype, {
-			supportsDrilldown: true,
-			animateDrillupTo: ColumnSeries.prototype.animateDrillupTo,
-			animateDrillupFrom: ColumnSeries.prototype.animateDrillupFrom,
-
-			animateDrilldown: function (init) {
-				var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1],
-					animationOptions = this.chart.options.drilldown.animation,
-					animateFrom = level.shapeArgs,
-					start = animateFrom.start,
-					angle = animateFrom.end - start,
-					startAngle = angle / this.points.length,
-					startColor = H.Color(level.color).rgba;
-
-				if (!init) {
-					each(this.points, function (point, i) {
-						var endColor = H.Color(point.color).rgba;
-
-						/*jslint unparam: true*/
-						point.graphic
-							.attr(H.merge(animateFrom, {
-								start: start + i * startAngle,
-								end: start + (i + 1) * startAngle
-							}))[animationOptions ? 'animate' : 'attr'](point.shapeArgs, H.merge(animationOptions, {
-								step: function (val, fx) {
-									if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) {
-										this.attr({
-											fill: tweenColors(startColor, endColor, fx.pos)
-										});
-									}
-								}
-							}));
-						/*jslint unparam: false*/
-					});
-					this.animate = null;
-				}
-			}
-		});
-	}
-	
-	H.Point.prototype.doDrilldown = function (_holdRedraw) {
-		var series = this.series,
-			chart = series.chart,
-			drilldown = chart.options.drilldown,
-			i = (drilldown.series || []).length,
-			seriesOptions;
-		
-		while (i-- && !seriesOptions) {
-			if (drilldown.series[i].id === this.drilldown) {
-				seriesOptions = drilldown.series[i];
-			}
-		}
-
-		// Fire the event. If seriesOptions is undefined, the implementer can check for 
-		// seriesOptions, and call addSeriesAsDrilldown async if necessary.
-		fireEvent(chart, 'drilldown', { 
-			point: this,
-			seriesOptions: seriesOptions
-		});
-		
-		if (seriesOptions) {
-			if (_holdRedraw) {
-				chart.addSingleSeriesAsDrilldown(this, seriesOptions);
-			} else {
-				chart.addSeriesAsDrilldown(this, seriesOptions);
-			}
-		}
-
-	};
-	
-	wrap(H.Point.prototype, 'init', function (proceed, series, options, x) {
-		var point = proceed.call(this, series, options, x),
-			chart = series.chart,
-			tick = series.xAxis && series.xAxis.ticks[x],
-			tickLabel = tick && tick.label;
-		
-		if (point.drilldown) {
-			
-			// Add the click event to the point label
-			H.addEvent(point, 'click', function () {
-				point.doDrilldown();
-			});
-			
-			// Make axis labels clickable
-			if (tickLabel) {
-				if (!tickLabel._basicStyle) {
-					tickLabel._basicStyle = tickLabel.element.getAttribute('style');
-				}
-				tickLabel
-					.addClass('highcharts-drilldown-axis-label')
-					.css(chart.options.drilldown.activeAxisLabelStyle)
-					.on('click', function () {
-						each(tickLabel.ddPoints, function (point) {
-							if (point.doDrilldown) {
-								point.doDrilldown(true);
-							}
-						});
-						chart.applyDrilldown();
-					});
-				if (!tickLabel.ddPoints) {
-					tickLabel.ddPoints = [];
-				}
-				tickLabel.ddPoints.push(point);
-					
-			}
-		} else if (tickLabel && tickLabel._basicStyle) {
-			tickLabel.element.setAttribute('style', tickLabel._basicStyle);
-		}
-		
-		return point;
-	});
-
-	wrap(H.Series.prototype, 'drawDataLabels', function (proceed) {
-		var css = this.chart.options.drilldown.activeDataLabelStyle;
-
-		proceed.call(this);
-
-		each(this.points, function (point) {
-			if (point.drilldown && point.dataLabel) {
-				point.dataLabel
-					.attr({
-						'class': 'highcharts-drilldown-data-label'
-					})
-					.css(css)
-					.on('click', function () {
-						point.doDrilldown();
-					});
-			}
-		});
-	});
-
-	// Mark the trackers with a pointer 
-	var type, 
-		drawTrackerWrapper = function (proceed) {
-			proceed.call(this);
-			each(this.points, function (point) {
-				if (point.drilldown && point.graphic) {
-					point.graphic
-						.attr({
-							'class': 'highcharts-drilldown-point'
-						})
-						.css({ cursor: 'pointer' });
-				}
-			});
-		};
-	for (type in seriesTypes) {
-		if (seriesTypes[type].prototype.supportsDrilldown) {
-			wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper);
-		}
-	}
-		
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/exporting.js b/apps/static/js/plugins/highcharts/modules/exporting.js
deleted file mode 100644
index 69974bd77..000000000
--- a/apps/static/js/plugins/highcharts/modules/exporting.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
- Exporting module
-
- (c) 2010-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(f){var A=f.Chart,t=f.addEvent,B=f.removeEvent,l=f.createElement,o=f.discardElement,v=f.css,k=f.merge,r=f.each,p=f.extend,D=Math.max,j=document,C=window,E=f.isTouchDevice,F=f.Renderer.prototype.symbols,s=f.getOptions(),y;p(s.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});s.navigation={menuStyle:{border:"1px solid #A0A0A0",
-background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:E?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};s.exporting={type:"image/png",url:"http://export.highcharts.com/",buttons:{contextButton:{menuClassName:"highcharts-contextmenu",
-symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"applications/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}};f.post=function(b,a,d){var c,b=l("form",k({method:"post",
-action:b,enctype:"multipart/form-data"},d),{display:"none"},j.body);for(c in a)l("input",{type:"hidden",name:c,value:a[c]},null,b);b.submit();o(b)};p(A.prototype,{getSVG:function(b){var a=this,d,c,z,h,g=k(a.options,b);if(!j.createElementNS)j.createElementNS=function(a,b){return j.createElement(b)};b=l("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},j.body);c=a.renderTo.style.width;h=a.renderTo.style.height;c=g.exporting.sourceWidth||g.chart.width||
-/px$/.test(c)&&parseInt(c,10)||600;h=g.exporting.sourceHeight||g.chart.height||/px$/.test(h)&&parseInt(h,10)||400;p(g.chart,{animation:!1,renderTo:b,forExport:!0,width:c,height:h});g.exporting.enabled=!1;g.series=[];r(a.series,function(a){z=k(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});z.isInternal||g.series.push(z)});d=new f.Chart(g,a.callback);r(["xAxis","yAxis"],function(b){r(a[b],function(a,c){var g=d[b][c],f=a.getExtremes(),h=f.userMin,f=f.userMax;g&&(h!==void 0||f!==void 0)&&
-g.setExtremes(h,f,!0,!1)})});c=d.container.innerHTML;g=null;d.destroy();o(b);c=c.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/<svg /,'<svg xmlns:xlink="http://www.w3.org/1999/xlink" ').replace(/ href=/g," xlink:href=").replace(/\n/," ").replace(/<\/svg>.*?$/,"</svg>").replace(/&nbsp;/g," ").replace(/&shy;/g,"­").replace(/<IMG /g,"<image ").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g,
-'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});return c=c.replace(/(url\(#highcharts-[0-9]+)&quot;/g,"$1").replace(/&quot;/g,"'")},exportChart:function(b,a){var b=b||{},d=this.options.exporting,d=this.getSVG(k({chart:{borderRadius:0}},d.chartOptions,a,{exporting:{sourceWidth:b.sourceWidth||
-d.sourceWidth,sourceHeight:b.sourceHeight||d.sourceHeight}})),b=k(this.options.exporting,b);f.post(b.url,{filename:b.filename||"chart",type:b.type,width:b.width||0,scale:b.scale||2,svg:d},b.formAttributes)},print:function(){var b=this,a=b.container,d=[],c=a.parentNode,f=j.body,h=f.childNodes;if(!b.isPrinting)b.isPrinting=!0,r(h,function(a,b){if(a.nodeType===1)d[b]=a.style.display,a.style.display="none"}),f.appendChild(a),C.focus(),C.print(),setTimeout(function(){c.appendChild(a);r(h,function(a,b){if(a.nodeType===
-1)a.style.display=d[b]});b.isPrinting=!1},1E3)},contextMenu:function(b,a,d,c,f,h,g){var e=this,k=e.options.navigation,q=k.menuItemStyle,m=e.chartWidth,n=e.chartHeight,j="cache-"+b,i=e[j],u=D(f,h),w,x,o,s=function(a){e.pointer.inClass(a.target,b)||x()};if(!i)e[j]=i=l("div",{className:b},{position:"absolute",zIndex:1E3,padding:u+"px"},e.container),w=l("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},k.menuStyle),i),x=function(){v(i,{display:"none"});
-g&&g.setState(0);e.openMenu=!1},t(i,"mouseleave",function(){o=setTimeout(x,500)}),t(i,"mouseenter",function(){clearTimeout(o)}),t(document,"mouseup",s),t(e,"destroy",function(){B(document,"mouseup",s)}),r(a,function(a){if(a){var b=a.separator?l("hr",null,null,w):l("div",{onmouseover:function(){v(this,k.menuItemHoverStyle)},onmouseout:function(){v(this,q)},onclick:function(){x();a.onclick.apply(e,arguments)},innerHTML:a.text||e.options.lang[a.textKey]},p({cursor:"pointer"},q),w);e.exportDivElements.push(b)}}),
-e.exportDivElements.push(w,i),e.exportMenuWidth=i.offsetWidth,e.exportMenuHeight=i.offsetHeight;a={display:"block"};d+e.exportMenuWidth>m?a.right=m-d-f-u+"px":a.left=d-u+"px";c+h+e.exportMenuHeight>n&&g.alignOptions.verticalAlign!=="top"?a.bottom=n-c-u+"px":a.top=c+h-u+"px";v(i,a);e.openMenu=!0},addButton:function(b){var a=this,d=a.renderer,c=k(a.options.navigation.buttonOptions,b),j=c.onclick,h=c.menuItems,g,e,l={stroke:c.symbolStroke,fill:c.symbolFill},q=c.symbolSize||12;if(!a.btnCount)a.btnCount=
-0;if(!a.exportDivElements)a.exportDivElements=[],a.exportSVGElements=[];if(c.enabled!==!1){var m=c.theme,n=m.states,o=n&&n.hover,n=n&&n.select,i;delete m.states;j?i=function(){j.apply(a,arguments)}:h&&(i=function(){a.contextMenu(e.menuClassName,h,e.translateX,e.translateY,e.width,e.height,e);e.setState(2)});c.text&&c.symbol?m.paddingLeft=f.pick(m.paddingLeft,25):c.text||p(m,{width:c.width,height:c.height,padding:0});e=d.button(c.text,0,0,i,m,o,n).attr({title:a.options.lang[c._titleKey],"stroke-linecap":"round"});
-e.menuClassName=b.menuClassName||"highcharts-menu-"+a.btnCount++;c.symbol&&(g=d.symbol(c.symbol,c.symbolX-q/2,c.symbolY-q/2,q,q).attr(p(l,{"stroke-width":c.symbolStrokeWidth||1,zIndex:1})).add(e));e.add().align(p(c,{width:e.width,x:f.pick(c.x,y)}),!0,"spacingBox");y+=(e.width+c.buttonSpacing)*(c.align==="right"?-1:1);a.exportSVGElements.push(e,g)}},destroyExport:function(b){var b=b.target,a,d;for(a=0;a<b.exportSVGElements.length;a++)if(d=b.exportSVGElements[a])d.onclick=d.ontouchstart=null,b.exportSVGElements[a]=
-d.destroy();for(a=0;a<b.exportDivElements.length;a++)d=b.exportDivElements[a],B(d,"mouseleave"),b.exportDivElements[a]=d.onmouseout=d.onmouseover=d.ontouchstart=d.onclick=null,o(d)}});F.menu=function(b,a,d,c){return["M",b,a+2.5,"L",b+d,a+2.5,"M",b,a+c/2+0.5,"L",b+d,a+c/2+0.5,"M",b,a+c-1.5,"L",b+d,a+c-1.5]};A.prototype.callbacks.push(function(b){var a,d=b.options.exporting,c=d.buttons;y=0;if(d.enabled!==!1){for(a in c)b.addButton(c[a]);t(b,"destroy",b.destroyExport)}})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/exporting.src.js b/apps/static/js/plugins/highcharts/modules/exporting.src.js
deleted file mode 100644
index 8cdafdd1b..000000000
--- a/apps/static/js/plugins/highcharts/modules/exporting.src.js
+++ /dev/null
@@ -1,715 +0,0 @@
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- * Exporting module
- *
- * (c) 2010-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-// JSLint options:
-/*global Highcharts, document, window, Math, setTimeout */
-
-(function (Highcharts) { // encapsulate
-
-// create shortcuts
-var Chart = Highcharts.Chart,
-	addEvent = Highcharts.addEvent,
-	removeEvent = Highcharts.removeEvent,
-	createElement = Highcharts.createElement,
-	discardElement = Highcharts.discardElement,
-	css = Highcharts.css,
-	merge = Highcharts.merge,
-	each = Highcharts.each,
-	extend = Highcharts.extend,
-	math = Math,
-	mathMax = math.max,
-	doc = document,
-	win = window,
-	isTouchDevice = Highcharts.isTouchDevice,
-	M = 'M',
-	L = 'L',
-	DIV = 'div',
-	HIDDEN = 'hidden',
-	NONE = 'none',
-	PREFIX = 'highcharts-',
-	ABSOLUTE = 'absolute',
-	PX = 'px',
-	UNDEFINED,
-	symbols = Highcharts.Renderer.prototype.symbols,
-	defaultOptions = Highcharts.getOptions(),
-	buttonOffset;
-
-	// Add language
-	extend(defaultOptions.lang, {
-		printChart: 'Print chart',
-		downloadPNG: 'Download PNG image',
-		downloadJPEG: 'Download JPEG image',
-		downloadPDF: 'Download PDF document',
-		downloadSVG: 'Download SVG vector image',
-		contextButtonTitle: 'Chart context menu'
-	});
-
-// Buttons and menus are collected in a separate config option set called 'navigation'.
-// This can be extended later to add control buttons like zoom and pan right click menus.
-defaultOptions.navigation = {
-	menuStyle: {
-		border: '1px solid #A0A0A0',
-		background: '#FFFFFF',
-		padding: '5px 0'
-	},
-	menuItemStyle: {
-		padding: '0 10px',
-		background: NONE,
-		color: '#303030',
-		fontSize: isTouchDevice ? '14px' : '11px'
-	},
-	menuItemHoverStyle: {
-		background: '#4572A5',
-		color: '#FFFFFF'
-	},
-
-	buttonOptions: {
-		symbolFill: '#E0E0E0',
-		symbolSize: 14,
-		symbolStroke: '#666',
-		symbolStrokeWidth: 3,
-		symbolX: 12.5,
-		symbolY: 10.5,
-		align: 'right',
-		buttonSpacing: 3,
-		height: 22,
-		// text: null,
-		theme: {
-			fill: 'white', // capture hover
-			stroke: 'none'
-		},
-		verticalAlign: 'top',
-		width: 24
-	}
-};
-
-
-
-// Add the export related options
-defaultOptions.exporting = {
-	//enabled: true,
-	//filename: 'chart',
-	type: 'image/png',
-	url: 'http://export.highcharts.com/',
-	//width: undefined,
-	//scale: 2
-	buttons: {
-		contextButton: {
-			menuClassName: PREFIX + 'contextmenu',
-			//x: -10,
-			symbol: 'menu',
-			_titleKey: 'contextButtonTitle',
-			menuItems: [{
-				textKey: 'printChart',
-				onclick: function () {
-					this.print();
-				}
-			}, {
-				separator: true
-			}, {
-				textKey: 'downloadPNG',
-				onclick: function () {
-					this.exportChart();
-				}
-			}, {
-				textKey: 'downloadJPEG',
-				onclick: function () {
-					this.exportChart({
-						type: 'image/jpeg'
-					});
-				}
-			}, {
-				textKey: 'downloadPDF',
-				onclick: function () {
-					this.exportChart({
-						type: 'applications/pdf'
-					});
-				}
-			}, {
-				textKey: 'downloadSVG',
-				onclick: function () {
-					this.exportChart({
-						type: 'image/svg+xml'
-					});
-				}
-			}
-			// Enable this block to add "View SVG" to the dropdown menu
-			/*
-			,{
-
-				text: 'View SVG',
-				onclick: function () {
-					var svg = this.getSVG()
-						.replace(/</g, '\n&lt;')
-						.replace(/>/g, '&gt;');
-
-					doc.body.innerHTML = '<pre>' + svg + '</pre>';
-				}
-			} // */
-			]
-		}
-	}
-};
-
-// Add the Highcharts.post utility
-Highcharts.post = function (url, data, formAttributes) {
-	var name,
-		form;
-
-	// create the form
-	form = createElement('form', merge({
-		method: 'post',
-		action: url,
-		enctype: 'multipart/form-data'
-	}, formAttributes), {
-		display: NONE
-	}, doc.body);
-
-	// add the data
-	for (name in data) {
-		createElement('input', {
-			type: HIDDEN,
-			name: name,
-			value: data[name]
-		}, null, form);
-	}
-
-	// submit
-	form.submit();
-
-	// clean up
-	discardElement(form);
-};
-
-extend(Chart.prototype, {
-
-	/**
-	 * Return an SVG representation of the chart
-	 *
-	 * @param additionalOptions {Object} Additional chart options for the generated SVG representation
-	 */
-	getSVG: function (additionalOptions) {
-		var chart = this,
-			chartCopy,
-			sandbox,
-			svg,
-			seriesOptions,
-			sourceWidth,
-			sourceHeight,
-			cssWidth,
-			cssHeight,
-			options = merge(chart.options, additionalOptions); // copy the options and add extra options
-
-		// IE compatibility hack for generating SVG content that it doesn't really understand
-		if (!doc.createElementNS) {
-			/*jslint unparam: true*//* allow unused parameter ns in function below */
-			doc.createElementNS = function (ns, tagName) {
-				return doc.createElement(tagName);
-			};
-			/*jslint unparam: false*/
-		}
-
-		// create a sandbox where a new chart will be generated
-		sandbox = createElement(DIV, null, {
-			position: ABSOLUTE,
-			top: '-9999em',
-			width: chart.chartWidth + PX,
-			height: chart.chartHeight + PX
-		}, doc.body);
-
-		// get the source size
-		cssWidth = chart.renderTo.style.width;
-		cssHeight = chart.renderTo.style.height;
-		sourceWidth = options.exporting.sourceWidth ||
-			options.chart.width ||
-			(/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||
-			600;
-		sourceHeight = options.exporting.sourceHeight ||
-			options.chart.height ||
-			(/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||
-			400;
-
-		// override some options
-		extend(options.chart, {
-			animation: false,
-			renderTo: sandbox,
-			forExport: true,
-			width: sourceWidth,
-			height: sourceHeight
-		});
-		options.exporting.enabled = false; // hide buttons in print
-
-		// prepare for replicating the chart
-		options.series = [];
-		each(chart.series, function (serie) {
-			seriesOptions = merge(serie.options, {
-				animation: false, // turn off animation
-				showCheckbox: false,
-				visible: serie.visible
-			});
-
-			if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
-				options.series.push(seriesOptions);
-			}
-		});
-
-		// generate the chart copy
-		chartCopy = new Highcharts.Chart(options, chart.callback);
-
-		// reflect axis extremes in the export
-		each(['xAxis', 'yAxis'], function (axisType) {
-			each(chart[axisType], function (axis, i) {
-				var axisCopy = chartCopy[axisType][i],
-					extremes = axis.getExtremes(),
-					userMin = extremes.userMin,
-					userMax = extremes.userMax;
-
-				if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) {
-					axisCopy.setExtremes(userMin, userMax, true, false);
-				}
-			});
-		});
-
-		// get the SVG from the container's innerHTML
-		svg = chartCopy.container.innerHTML;
-
-		// free up memory
-		options = null;
-		chartCopy.destroy();
-		discardElement(sandbox);
-
-		// sanitize
-		svg = svg
-			.replace(/zIndex="[^"]+"/g, '')
-			.replace(/isShadow="[^"]+"/g, '')
-			.replace(/symbolName="[^"]+"/g, '')
-			.replace(/jQuery[0-9]+="[^"]+"/g, '')
-			.replace(/url\([^#]+#/g, 'url(#')
-			.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
-			.replace(/ href=/g, ' xlink:href=')
-			.replace(/\n/, ' ')
-			.replace(/<\/svg>.*?$/, '</svg>') // any HTML added to the container after the SVG (#894)
-			/* This fails in IE < 8
-			.replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
-				return s2 +'.'+ s3[0];
-			})*/
-
-			// Replace HTML entities, issue #347
-			.replace(/&nbsp;/g, '\u00A0') // no-break space
-			.replace(/&shy;/g,  '\u00AD') // soft hyphen
-
-			// IE specific
-			.replace(/<IMG /g, '<image ')
-			.replace(/height=([^" ]+)/g, 'height="$1"')
-			.replace(/width=([^" ]+)/g, 'width="$1"')
-			.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
-			.replace(/id=([^" >]+)/g, 'id="$1"')
-			.replace(/class=([^" >]+)/g, 'class="$1"')
-			.replace(/ transform /g, ' ')
-			.replace(/:(path|rect)/g, '$1')
-			.replace(/style="([^"]+)"/g, function (s) {
-				return s.toLowerCase();
-			});
-
-		// IE9 beta bugs with innerHTML. Test again with final IE9.
-		svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
-			.replace(/&quot;/g, "'");
-
-		return svg;
-	},
-
-	/**
-	 * Submit the SVG representation of the chart to the server
-	 * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
-	 * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
-	 */
-	exportChart: function (options, chartOptions) {
-		options = options || {};
-
-		var chart = this,
-			chartExportingOptions = chart.options.exporting,
-			svg = chart.getSVG(merge(
-				{ chart: { borderRadius: 0 } },
-				chartExportingOptions.chartOptions,
-				chartOptions,
-				{
-					exporting: {
-						sourceWidth: options.sourceWidth || chartExportingOptions.sourceWidth,
-						sourceHeight: options.sourceHeight || chartExportingOptions.sourceHeight
-					}
-				}
-			));
-
-		// merge the options
-		options = merge(chart.options.exporting, options);
-
-		// do the post
-		Highcharts.post(options.url, {
-			filename: options.filename || 'chart',
-			type: options.type,
-			width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
-			scale: options.scale || 2,
-			svg: svg
-		}, options.formAttributes);
-
-	},
-
-	/**
-	 * Print the chart
-	 */
-	print: function () {
-
-		var chart = this,
-			container = chart.container,
-			origDisplay = [],
-			origParent = container.parentNode,
-			body = doc.body,
-			childNodes = body.childNodes;
-
-		if (chart.isPrinting) { // block the button while in printing mode
-			return;
-		}
-
-		chart.isPrinting = true;
-
-		// hide all body content
-		each(childNodes, function (node, i) {
-			if (node.nodeType === 1) {
-				origDisplay[i] = node.style.display;
-				node.style.display = NONE;
-			}
-		});
-
-		// pull out the chart
-		body.appendChild(container);
-
-		// print
-		win.focus(); // #1510
-		win.print();
-
-		// allow the browser to prepare before reverting
-		setTimeout(function () {
-
-			// put the chart back in
-			origParent.appendChild(container);
-
-			// restore all body content
-			each(childNodes, function (node, i) {
-				if (node.nodeType === 1) {
-					node.style.display = origDisplay[i];
-				}
-			});
-
-			chart.isPrinting = false;
-
-		}, 1000);
-
-	},
-
-	/**
-	 * Display a popup menu for choosing the export type
-	 *
-	 * @param {String} className An identifier for the menu
-	 * @param {Array} items A collection with text and onclicks for the items
-	 * @param {Number} x The x position of the opener button
-	 * @param {Number} y The y position of the opener button
-	 * @param {Number} width The width of the opener button
-	 * @param {Number} height The height of the opener button
-	 */
-	contextMenu: function (className, items, x, y, width, height, button) {
-		var chart = this,
-			navOptions = chart.options.navigation,
-			menuItemStyle = navOptions.menuItemStyle,
-			chartWidth = chart.chartWidth,
-			chartHeight = chart.chartHeight,
-			cacheName = 'cache-' + className,
-			menu = chart[cacheName],
-			menuPadding = mathMax(width, height), // for mouse leave detection
-			boxShadow = '3px 3px 10px #888',
-			innerMenu,
-			hide,
-			hideTimer,
-			menuStyle,
-			docMouseUpHandler = function (e) {
-				if (!chart.pointer.inClass(e.target, className)) {
-					hide();
-				}
-			};
-
-		// create the menu only the first time
-		if (!menu) {
-
-			// create a HTML element above the SVG
-			chart[cacheName] = menu = createElement(DIV, {
-				className: className
-			}, {
-				position: ABSOLUTE,
-				zIndex: 1000,
-				padding: menuPadding + PX
-			}, chart.container);
-
-			innerMenu = createElement(DIV, null,
-				extend({
-					MozBoxShadow: boxShadow,
-					WebkitBoxShadow: boxShadow,
-					boxShadow: boxShadow
-				}, navOptions.menuStyle), menu);
-
-			// hide on mouse out
-			hide = function () {
-				css(menu, { display: NONE });
-				if (button) {
-					button.setState(0);
-				}
-				chart.openMenu = false;
-			};
-
-			// Hide the menu some time after mouse leave (#1357)
-			addEvent(menu, 'mouseleave', function () {
-				hideTimer = setTimeout(hide, 500);
-			});
-			addEvent(menu, 'mouseenter', function () {
-				clearTimeout(hideTimer);
-			});
-
-
-			// Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
-			addEvent(document, 'mouseup', docMouseUpHandler);
-			addEvent(chart, 'destroy', function () {
-				removeEvent(document, 'mouseup', docMouseUpHandler);
-			});
-
-
-			// create the items
-			each(items, function (item) {
-				if (item) {
-					var element = item.separator ?
-						createElement('hr', null, null, innerMenu) :
-						createElement(DIV, {
-							onmouseover: function () {
-								css(this, navOptions.menuItemHoverStyle);
-							},
-							onmouseout: function () {
-								css(this, menuItemStyle);
-							},
-							onclick: function () {
-								hide();
-								item.onclick.apply(chart, arguments);
-							},
-							innerHTML: item.text || chart.options.lang[item.textKey]
-						}, extend({
-							cursor: 'pointer'
-						}, menuItemStyle), innerMenu);
-
-
-					// Keep references to menu divs to be able to destroy them
-					chart.exportDivElements.push(element);
-				}
-			});
-
-			// Keep references to menu and innerMenu div to be able to destroy them
-			chart.exportDivElements.push(innerMenu, menu);
-
-			chart.exportMenuWidth = menu.offsetWidth;
-			chart.exportMenuHeight = menu.offsetHeight;
-		}
-
-		menuStyle = { display: 'block' };
-
-		// if outside right, right align it
-		if (x + chart.exportMenuWidth > chartWidth) {
-			menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
-		} else {
-			menuStyle.left = (x - menuPadding) + PX;
-		}
-		// if outside bottom, bottom align it
-		if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {
-			menuStyle.bottom = (chartHeight - y - menuPadding)  + PX;
-		} else {
-			menuStyle.top = (y + height - menuPadding) + PX;
-		}
-
-		css(menu, menuStyle);
-		chart.openMenu = true;
-	},
-
-	/**
-	 * Add the export button to the chart
-	 */
-	addButton: function (options) {
-		var chart = this,
-			renderer = chart.renderer,
-			btnOptions = merge(chart.options.navigation.buttonOptions, options),
-			onclick = btnOptions.onclick,
-			menuItems = btnOptions.menuItems,
-			symbol,
-			button,
-			symbolAttr = {
-				stroke: btnOptions.symbolStroke,
-				fill: btnOptions.symbolFill
-			},
-			symbolSize = btnOptions.symbolSize || 12;
-		if (!chart.btnCount) {
-			chart.btnCount = 0;
-		}
-
-		// Keeps references to the button elements
-		if (!chart.exportDivElements) {
-			chart.exportDivElements = [];
-			chart.exportSVGElements = [];
-		}
-
-		if (btnOptions.enabled === false) {
-			return;
-		}
-
-
-		var attr = btnOptions.theme,
-			states = attr.states,
-			hover = states && states.hover,
-			select = states && states.select,
-			callback;
-
-		delete attr.states;
-
-		if (onclick) {
-			callback = function () {
-				onclick.apply(chart, arguments);
-			};
-
-		} else if (menuItems) {
-			callback = function () {
-				chart.contextMenu(
-					button.menuClassName,
-					menuItems,
-					button.translateX,
-					button.translateY,
-					button.width,
-					button.height,
-					button
-				);
-				button.setState(2);
-			};
-		}
-
-
-		if (btnOptions.text && btnOptions.symbol) {
-			attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);
-
-		} else if (!btnOptions.text) {
-			extend(attr, {
-				width: btnOptions.width,
-				height: btnOptions.height,
-				padding: 0
-			});
-		}
-
-		button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
-			.attr({
-				title: chart.options.lang[btnOptions._titleKey],
-				'stroke-linecap': 'round'
-			});
-		button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++;
-
-		if (btnOptions.symbol) {
-			symbol = renderer.symbol(
-					btnOptions.symbol,
-					btnOptions.symbolX - (symbolSize / 2),
-					btnOptions.symbolY - (symbolSize / 2),
-					symbolSize,
-					symbolSize
-				)
-				.attr(extend(symbolAttr, {
-					'stroke-width': btnOptions.symbolStrokeWidth || 1,
-					zIndex: 1
-				})).add(button);
-		}
-
-		button.add()
-			.align(extend(btnOptions, {
-				width: button.width,
-				x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654
-			}), true, 'spacingBox');
-
-		buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
-
-		chart.exportSVGElements.push(button, symbol);
-
-	},
-
-	/**
-	 * Destroy the buttons.
-	 */
-	destroyExport: function (e) {
-		var chart = e.target,
-			i,
-			elem;
-
-		// Destroy the extra buttons added
-		for (i = 0; i < chart.exportSVGElements.length; i++) {
-			elem = chart.exportSVGElements[i];
-
-			// Destroy and null the svg/vml elements
-			if (elem) { // #1822
-				elem.onclick = elem.ontouchstart = null;
-				chart.exportSVGElements[i] = elem.destroy();
-			}
-		}
-
-		// Destroy the divs for the menu
-		for (i = 0; i < chart.exportDivElements.length; i++) {
-			elem = chart.exportDivElements[i];
-
-			// Remove the event handler
-			removeEvent(elem, 'mouseleave');
-
-			// Remove inline events
-			chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
-
-			// Destroy the div by moving to garbage bin
-			discardElement(elem);
-		}
-	}
-});
-
-
-symbols.menu = function (x, y, width, height) {
-	var arr = [
-		M, x, y + 2.5,
-		L, x + width, y + 2.5,
-		M, x, y + height / 2 + 0.5,
-		L, x + width, y + height / 2 + 0.5,
-		M, x, y + height - 1.5,
-		L, x + width, y + height - 1.5
-	];
-	return arr;
-};
-
-// Add the buttons on chart load
-Chart.prototype.callbacks.push(function (chart) {
-	var n,
-		exportingOptions = chart.options.exporting,
-		buttons = exportingOptions.buttons;
-
-	buttonOffset = 0;
-
-	if (exportingOptions.enabled !== false) {
-
-		for (n in buttons) {
-			chart.addButton(buttons[n]);
-		}
-
-		// Destroy the export elements at chart destroy
-		addEvent(chart, 'destroy', chart.destroyExport);
-	}
-
-});
-
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/funnel.js b/apps/static/js/plugins/highcharts/modules/funnel.js
deleted file mode 100644
index 8a8a192ff..000000000
--- a/apps/static/js/plugins/highcharts/modules/funnel.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- 
- Highcharts funnel module
-
- (c) 2010-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(b){var d=b.getOptions(),v=d.plotOptions,q=b.seriesTypes,E=b.merge,D=function(){},A=b.each;v.funnel=E(v.pie,{animation:!1,center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",reversed:!1,dataLabels:{connectorWidth:1,connectorColor:"#606060"},size:!0,states:{select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}}});q.funnel=b.extendClass(q.pie,{type:"funnel",animate:D,singularTooltips:!0,translate:function(){var a=function(j,a){return/%$/.test(j)?a*parseInt(j,
-10)/100:parseInt(j,10)},B=0,f=this.chart,c=this.options,g=c.reversed,b=f.plotWidth,n=f.plotHeight,o=0,f=c.center,h=a(f[0],b),d=a(f[0],n),q=a(c.width,b),k,r,e=a(c.height,n),s=a(c.neckWidth,b),t=a(c.neckHeight,n),w=e-t,a=this.data,x,y,v=c.dataLabels.position==="left"?1:0,z,l,C,p,i,u,m;this.getWidthAt=r=function(j){return j>e-t||e===t?s:s+(q-s)*((e-t-j)/(e-t))};this.getX=function(j,a){return h+(a?-1:1)*(r(g?n-j:j)/2+c.dataLabels.distance)};this.center=[h,d,e];this.centerX=h;A(a,function(a){B+=a.y});
-A(a,function(a){m=null;y=B?a.y/B:0;l=d-e/2+o*e;i=l+y*e;k=r(l);z=h-k/2;C=z+k;k=r(i);p=h-k/2;u=p+k;l>w?(z=p=h-s/2,C=u=h+s/2):i>w&&(m=i,k=r(w),p=h-k/2,u=p+k,i=w);g&&(l=e-l,i=e-i,m=m?e-m:null);x=["M",z,l,"L",C,l,u,i];m&&x.push(u,m,p,m);x.push(p,i,"Z");a.shapeType="path";a.shapeArgs={d:x};a.percentage=y*100;a.plotX=h;a.plotY=(l+(m||i))/2;a.tooltipPos=[h,a.plotY];a.slice=D;a.half=v;o+=y})},drawPoints:function(){var a=this,b=a.options,f=a.chart.renderer;A(a.data,function(c){var g=c.graphic,d=c.shapeArgs;
-g?g.animate(d):c.graphic=f.path(d).attr({fill:c.color,stroke:b.borderColor,"stroke-width":b.borderWidth}).add(a.group)})},sortByAngle:function(a){a.sort(function(a,b){return a.plotY-b.plotY})},drawDataLabels:function(){var a=this.data,b=this.options.dataLabels.distance,f,c,g,d=a.length,n,o;for(this.center[2]-=2*b;d--;)g=a[d],c=(f=g.half)?1:-1,o=g.plotY,n=this.getX(o,f),g.labelPos=[0,o,n+(b-5)*c,o,n+b*c,o,f?"right":"left",0];q.pie.prototype.drawDataLabels.call(this)}});d.plotOptions.pyramid=b.merge(d.plotOptions.funnel,
-{neckWidth:"0%",neckHeight:"0%",reversed:!0});b.seriesTypes.pyramid=b.extendClass(b.seriesTypes.funnel,{type:"pyramid"})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/funnel.src.js b/apps/static/js/plugins/highcharts/modules/funnel.src.js
deleted file mode 100644
index eb6ba9b9a..000000000
--- a/apps/static/js/plugins/highcharts/modules/funnel.src.js
+++ /dev/null
@@ -1,310 +0,0 @@
-/**
- * @license 
- * Highcharts funnel module
- *
- * (c) 2010-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-/*global Highcharts */
-(function (Highcharts) {
-	
-'use strict';
-
-// create shortcuts
-var defaultOptions = Highcharts.getOptions(),
-	defaultPlotOptions = defaultOptions.plotOptions,
-	seriesTypes = Highcharts.seriesTypes,
-	merge = Highcharts.merge,
-	noop = function () {},
-	each = Highcharts.each;
-
-// set default options
-defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {
-	animation: false,
-	center: ['50%', '50%'],
-	width: '90%',
-	neckWidth: '30%',
-	height: '100%',
-	neckHeight: '25%',
-	reversed: false,
-	dataLabels: {
-		//position: 'right',
-		connectorWidth: 1,
-		connectorColor: '#606060'
-	},
-	size: true, // to avoid adapting to data label size in Pie.drawDataLabels
-	states: {
-		select: {
-			color: '#C0C0C0',
-			borderColor: '#000000',
-			shadow: false
-		}
-	}	
-});
-
-
-seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
-	
-	type: 'funnel',
-	animate: noop,
-	singularTooltips: true,
-
-	/**
-	 * Overrides the pie translate method
-	 */
-	translate: function () {
-		
-		var 
-			// Get positions - either an integer or a percentage string must be given
-			getLength = function (length, relativeTo) {
-				return (/%$/).test(length) ?
-					relativeTo * parseInt(length, 10) / 100 :
-					parseInt(length, 10);
-			},
-			
-			sum = 0,
-			series = this,
-			chart = series.chart,
-			options = series.options,
-			reversed = options.reversed,
-			plotWidth = chart.plotWidth,
-			plotHeight = chart.plotHeight,
-			cumulative = 0, // start at top
-			center = options.center,
-			centerX = getLength(center[0], plotWidth),
-			centerY = getLength(center[0], plotHeight),
-			width = getLength(options.width, plotWidth),
-			tempWidth,
-			getWidthAt,
-			height = getLength(options.height, plotHeight),
-			neckWidth = getLength(options.neckWidth, plotWidth),
-			neckHeight = getLength(options.neckHeight, plotHeight),
-			neckY = height - neckHeight,
-			data = series.data,
-			path,
-			fraction,
-			half = options.dataLabels.position === 'left' ? 1 : 0,
-
-			x1, 
-			y1, 
-			x2, 
-			x3, 
-			y3, 
-			x4, 
-			y5;
-
-		// Return the width at a specific y coordinate
-		series.getWidthAt = getWidthAt = function (y) {
-			return y > height - neckHeight || height === neckHeight ?
-				neckWidth :
-				neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight));
-		};
-		series.getX = function (y, half) {
-					return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? plotHeight - y : y) / 2) + options.dataLabels.distance);
-		};
-
-		// Expose
-		series.center = [centerX, centerY, height];
-		series.centerX = centerX;
-
-		/*
-		 * Individual point coordinate naming:
-		 *
-		 * x1,y1 _________________ x2,y1
-		 *  \                         /
-		 *   \                       /
-		 *    \                     /
-		 *     \                   /
-		 *      \                 /
-		 *     x3,y3 _________ x4,y3
-		 *
-		 * Additional for the base of the neck:
-		 *
-		 *       |               |
-		 *       |               |
-		 *       |               |
-		 *     x3,y5 _________ x4,y5
-		 */
-
-
-
-
-		// get the total sum
-		each(data, function (point) {
-			sum += point.y;
-		});
-
-		each(data, function (point) {
-			// set start and end positions
-			y5 = null;
-			fraction = sum ? point.y / sum : 0;
-			y1 = centerY - height / 2 + cumulative * height;
-			y3 = y1 + fraction * height;
-			//tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
-			tempWidth = getWidthAt(y1);
-			x1 = centerX - tempWidth / 2;
-			x2 = x1 + tempWidth;
-			tempWidth = getWidthAt(y3);
-			x3 = centerX - tempWidth / 2;
-			x4 = x3 + tempWidth;
-
-			// the entire point is within the neck
-			if (y1 > neckY) {
-				x1 = x3 = centerX - neckWidth / 2;
-				x2 = x4 = centerX + neckWidth / 2;
-			
-			// the base of the neck
-			} else if (y3 > neckY) {
-				y5 = y3;
-
-				tempWidth = getWidthAt(neckY);
-				x3 = centerX - tempWidth / 2;
-				x4 = x3 + tempWidth;
-
-				y3 = neckY;
-			}
-
-			if (reversed) {
-				y1 = height - y1;
-				y3 = height - y3;
-				y5 = (y5 ? height - y5 : null);
-			}
-			// save the path
-			path = [
-				'M',
-				x1, y1,
-				'L',
-				x2, y1,
-				x4, y3
-			];
-			if (y5) {
-				path.push(x4, y5, x3, y5);
-			}
-			path.push(x3, y3, 'Z');
-
-			// prepare for using shared dr
-			point.shapeType = 'path';
-			point.shapeArgs = { d: path };
-
-
-			// for tooltips and data labels
-			point.percentage = fraction * 100;
-			point.plotX = centerX;
-			point.plotY = (y1 + (y5 || y3)) / 2;
-
-			// Placement of tooltips and data labels
-			point.tooltipPos = [
-				centerX,
-				point.plotY
-			];
-
-			// Slice is a noop on funnel points
-			point.slice = noop;
-			
-			// Mimicking pie data label placement logic
-			point.half = half;
-
-			cumulative += fraction;
-		});		
-	},
-	/**
-	 * Draw a single point (wedge)
-	 * @param {Object} point The point object
-	 * @param {Object} color The color of the point
-	 * @param {Number} brightness The brightness relative to the color
-	 */
-	drawPoints: function () {
-		var series = this,
-			options = series.options,
-			chart = series.chart,
-			renderer = chart.renderer;
-
-		each(series.data, function (point) {
-			
-			var graphic = point.graphic,
-				shapeArgs = point.shapeArgs;
-
-			if (!graphic) { // Create the shapes
-				point.graphic = renderer.path(shapeArgs).
-					attr({
-						fill: point.color,
-						stroke: options.borderColor,
-						'stroke-width': options.borderWidth
-					}).
-					add(series.group);
-					
-			} else { // Update the shapes
-				graphic.animate(shapeArgs);
-			}
-		});
-	},
-
-	/**
-	 * Funnel items don't have angles (#2289)
-	 */
-	sortByAngle: function (points) {
-		points.sort(function (a, b) {
-			return a.plotY - b.plotY;
-		});
-	},
-	
-	/**
-	 * Extend the pie data label method
-	 */
-	drawDataLabels: function () {
-		var data = this.data,
-			labelDistance = this.options.dataLabels.distance,
-			leftSide,
-			sign,
-			point,
-			i = data.length,
-			x,
-			y;
-		
-		// In the original pie label anticollision logic, the slots are distributed
-		// from one labelDistance above to one labelDistance below the pie. In funnels
-		// we don't want this.
-		this.center[2] -= 2 * labelDistance;
-		
-		// Set the label position array for each point.
-		while (i--) {
-			point = data[i];
-			leftSide = point.half;
-			sign = leftSide ? 1 : -1;
-			y = point.plotY;
-			x = this.getX(y, leftSide);
-				
-			// set the anchor point for data labels
-			point.labelPos = [
-				0, // first break of connector
-				y, // a/a
-				x + (labelDistance - 5) * sign, // second break, right outside point shape
-				y, // a/a
-				x + labelDistance * sign, // landing point for connector
-				y, // a/a
-				leftSide ? 'right' : 'left', // alignment
-				0 // center angle
-			];
-		}
-		
-		seriesTypes.pie.prototype.drawDataLabels.call(this);
-	}
-
-});
-
-/** 
- * Pyramid series type.
- * A pyramid series is a special type of funnel, without neck and reversed by default.
- */
-defaultOptions.plotOptions.pyramid = Highcharts.merge(defaultOptions.plotOptions.funnel, {        
-	neckWidth: '0%',
-	neckHeight: '0%',
-	reversed: true
-});
-Highcharts.seriesTypes.pyramid = Highcharts.extendClass(Highcharts.seriesTypes.funnel, {
-	type: 'pyramid'
-});
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/heatmap.js b/apps/static/js/plugins/highcharts/modules/heatmap.js
deleted file mode 100644
index a3edef492..000000000
--- a/apps/static/js/plugins/highcharts/modules/heatmap.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
-
- (c) 2011-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(g){var j=g.Axis,x=g.Chart,o=g.Color,y=g.Legend,s=g.LegendSymbolMixin,t=g.Series,u=g.getOptions(),k=g.each,p=g.extend,z=g.extendClass,l=g.merge,q=g.pick,v=g.numberFormat,m=g.seriesTypes,w=g.wrap,n=function(){},r=g.ColorAxis=function(){this.isColorAxis=!0;this.init.apply(this,arguments)};p(r.prototype,j.prototype);p(r.prototype,{defaultColorAxisOptions:{lineWidth:0,gridLineWidth:1,tickPixelInterval:72,startOnTick:!0,endOnTick:!0,offset:0,marker:{animation:{duration:50},color:"gray",width:0.01},
-labels:{overflow:"justify"},minColor:"#EFEFFF",maxColor:"#003875",tickLength:5},init:function(b,a){var d=b.options.legend.layout!=="vertical",c;c=l(this.defaultColorAxisOptions,{side:d?2:1,reversed:!d},a,{isX:d,opposite:!d,showEmpty:!1,title:null,isColor:!0});j.prototype.init.call(this,b,c);a.dataClasses&&this.initDataClasses(a);this.initStops(a);this.isXAxis=!0;this.horiz=d;this.zoomEnabled=!1},tweenColors:function(b,a,d){var c=a.rgba[3]!==1||b.rgba[3]!==1;return(c?"rgba(":"rgb(")+Math.round(a.rgba[0]+
-(b.rgba[0]-a.rgba[0])*(1-d))+","+Math.round(a.rgba[1]+(b.rgba[1]-a.rgba[1])*(1-d))+","+Math.round(a.rgba[2]+(b.rgba[2]-a.rgba[2])*(1-d))+(c?","+(a.rgba[3]+(b.rgba[3]-a.rgba[3])*(1-d)):"")+")"},initDataClasses:function(b){var a=this,d=this.chart,c,e=0,h=this.options;this.dataClasses=c=[];k(b.dataClasses,function(f,i){var g,f=l(f);c.push(f);if(!f.color)h.dataClassColor==="category"?(g=d.options.colors,f.color=g[e++],e===g.length&&(e=0)):f.color=a.tweenColors(o(h.minColor),o(h.maxColor),i/(b.dataClasses.length-
-1))})},initStops:function(b){this.stops=b.stops||[[0,this.options.minColor],[1,this.options.maxColor]];k(this.stops,function(a){a.color=o(a[1])})},setOptions:function(b){j.prototype.setOptions.call(this,b);this.options.crosshair=this.options.marker;this.coll="colorAxis"},setAxisSize:function(){var b=this.legendSymbol,a=this.chart,d,c,e;if(b)this.left=d=b.attr("x"),this.top=c=b.attr("y"),this.width=e=b.attr("width"),this.height=b=b.attr("height"),this.right=a.chartWidth-d-e,this.bottom=a.chartHeight-
-c-b,this.len=this.horiz?e:b,this.pos=this.horiz?d:c},toColor:function(b,a){var d,c=this.stops,e,h=this.dataClasses,f,i;if(h)for(i=h.length;i--;){if(f=h[i],e=f.from,c=f.to,(e===void 0||b>=e)&&(c===void 0||b<=c)){d=f.color;if(a)a.dataClass=i;break}}else{this.isLog&&(b=this.val2lin(b));d=1-(this.max-b)/(this.max-this.min);for(i=c.length;i--;)if(d>c[i][0])break;e=c[i]||c[i+1];c=c[i+1]||e;d=1-(c[0]-d)/(c[0]-e[0]||1);d=this.tweenColors(e.color,c.color,d)}return d},getOffset:function(){var b=this.legendGroup;
-if(b&&(j.prototype.getOffset.call(this),!this.axisGroup.parentGroup))this.axisGroup.add(b),this.gridGroup.add(b),this.labelGroup.add(b),this.added=!0},setLegendColor:function(){var b,a=this.options;b=this.horiz?[0,0,1,0]:[0,0,0,1];this.legendColor={linearGradient:{x1:b[0],y1:b[1],x2:b[2],y2:b[3]},stops:a.stops||[[0,a.minColor],[1,a.maxColor]]}},drawLegendSymbol:function(b,a){var d=b.padding,c=b.options,e=this.horiz,h=q(c.symbolWidth,e?200:12),f=q(c.symbolHeight,e?12:200),c=q(c.labelPadding,e?10:30);
-this.setLegendColor();a.legendSymbol=this.chart.renderer.rect(0,b.baseline-11,h,f).attr({zIndex:1}).add(a.legendGroup);a.legendSymbol.getBBox();this.legendItemWidth=h+d+(e?0:c);this.legendItemHeight=f+d+(e?c:0)},setState:n,visible:!0,setVisible:n,getSeriesExtremes:function(){var b;if(this.series.length)b=this.series[0],this.dataMin=b.valueMin,this.dataMax=b.valueMax},drawCrosshair:function(b,a){var d=!this.cross,c=a&&a.plotX,e=a&&a.plotY,h,f=this.pos,i=this.len;if(a)h=this.toPixels(a.value),h<f?h=
-f-2:h>f+i&&(h=f+i+2),a.plotX=h,a.plotY=this.len-h,j.prototype.drawCrosshair.call(this,b,a),a.plotX=c,a.plotY=e,!d&&this.cross&&this.cross.attr({fill:this.crosshair.color}).add(this.labelGroup)},getPlotLinePath:function(b,a,d,c,e){return e?this.horiz?["M",e-4,this.top-6,"L",e+4,this.top-6,e,this.top,"Z"]:["M",this.left,e,"L",this.left-6,e+6,this.left-6,e-6,"Z"]:j.prototype.getPlotLinePath.call(this,b,a,d,c)},update:function(b,a){k(this.series,function(a){a.isDirtyData=!0});j.prototype.update.call(this,
-b,a);this.legendItem&&(this.setLegendColor(),this.chart.legend.colorizeItem(this,!0))},getDataClassLegendSymbols:function(){var b=this,a=this.chart,d=[],c=a.options.legend,e=c.valueDecimals,h=c.valueSuffix||"",f;k(this.dataClasses,function(c,g){var j=!0,l=c.from,m=c.to;f="";l===void 0?f="< ":m===void 0&&(f="> ");l!==void 0&&(f+=v(l,e)+h);l!==void 0&&m!==void 0&&(f+=" - ");m!==void 0&&(f+=v(m,e)+h);d.push(p({chart:a,name:f,options:{},drawLegendSymbol:s.drawRectangle,visible:!0,setState:n,setVisible:function(){j=
-this.visible=!j;k(b.series,function(a){k(a.points,function(a){a.dataClass===g&&a.setVisible(j)})});a.legend.colorizeItem(this,j)}},c))});return d},name:""});w(x.prototype,"getAxes",function(b){var a=this.options.colorAxis;b.call(this);this.colorAxis=[];a&&new r(this,a)});w(y.prototype,"getAllItems",function(b){var a=[],d=this.chart.colorAxis[0];d&&(d.options.dataClasses?a=a.concat(d.getDataClassLegendSymbols()):a.push(d),k(d.series,function(a){a.options.showInLegend=!1}));return a.concat(b.call(this))});
-g={pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",dashstyle:"dashStyle"},pointArrayMap:["value"],axisTypes:["xAxis","yAxis","colorAxis"],optionalAxis:"colorAxis",trackerGroups:["group","markerGroup","dataLabelsGroup"],getSymbol:n,parallelArrays:["x","y","value"],translateColors:function(){var b=this,a=this.options.nullColor,d=this.colorAxis;k(this.data,function(c){var e=c.value;if(e=e===null?a:d?d.toColor(e,c):c.color||b.color)c.color=e})}};u.plotOptions.heatmap=
-l(u.plotOptions.scatter,{animation:!1,borderWidth:0,nullColor:"#F8F8F8",dataLabels:{format:"{point.value}",verticalAlign:"middle",crop:!1,overflow:!1,style:{color:"white",fontWeight:"bold",textShadow:"0 0 5px black"}},marker:null,tooltip:{pointFormat:"{point.x}, {point.y}: {point.value}<br/>"},states:{normal:{animation:!0},hover:{brightness:0.2}}});m.heatmap=z(m.scatter,l(g,{type:"heatmap",pointArrayMap:["y","value"],hasPointSpecificOptions:!0,supportsDrilldown:!0,getExtremesFromAll:!0,init:function(){m.scatter.prototype.init.apply(this,
-arguments);this.pointRange=this.options.colsize||1;this.yAxis.axisPointRange=this.options.rowsize||1},translate:function(){var b=this.options,a=this.xAxis,d=this.yAxis;this.generatePoints();k(this.points,function(c){var e=(b.colsize||1)/2,h=(b.rowsize||1)/2,f=Math.round(a.len-a.translate(c.x-e,0,1,0,1)),e=Math.round(a.len-a.translate(c.x+e,0,1,0,1)),g=Math.round(d.translate(c.y-h,0,1,0,1)),h=Math.round(d.translate(c.y+h,0,1,0,1));c.plotX=(f+e)/2;c.plotY=(g+h)/2;c.shapeType="rect";c.shapeArgs={x:Math.min(f,
-e),y:Math.min(g,h),width:Math.abs(e-f),height:Math.abs(h-g)}});this.translateColors()},drawPoints:m.column.prototype.drawPoints,animate:n,getBox:n,drawLegendSymbol:s.drawRectangle,getExtremes:function(){t.prototype.getExtremes.call(this,this.valueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;t.prototype.getExtremes.call(this)}}))})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/heatmap.src.js b/apps/static/js/plugins/highcharts/modules/heatmap.src.js
deleted file mode 100644
index 0057e25e4..000000000
--- a/apps/static/js/plugins/highcharts/modules/heatmap.src.js
+++ /dev/null
@@ -1,606 +0,0 @@
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- *
- * (c) 2011-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-/*global HighchartsAdapter*/
-(function (Highcharts) {
-
-
-var UNDEFINED,
-	Axis = Highcharts.Axis,
-	Chart = Highcharts.Chart,
-	Color = Highcharts.Color,
-	Legend = Highcharts.Legend,
-	LegendSymbolMixin = Highcharts.LegendSymbolMixin,
-	Series = Highcharts.Series,
-	
-	defaultOptions = Highcharts.getOptions(),
-	each = Highcharts.each,
-	extend = Highcharts.extend,
-	extendClass = Highcharts.extendClass,
-	merge = Highcharts.merge,
-	pick = Highcharts.pick,
-	numberFormat = Highcharts.numberFormat,
-	seriesTypes = Highcharts.seriesTypes,
-	wrap = Highcharts.wrap,
-	noop = function () {};
-
-	
-
-
-/**
- * The ColorAxis object for inclusion in gradient legends
- */
-var ColorAxis = Highcharts.ColorAxis = function () {
-	this.isColorAxis = true;
-	this.init.apply(this, arguments);
-};
-extend(ColorAxis.prototype, Axis.prototype);
-extend(ColorAxis.prototype, {
-	defaultColorAxisOptions: {
-		lineWidth: 0,
-		gridLineWidth: 1,
-		tickPixelInterval: 72,
-		startOnTick: true,
-		endOnTick: true,
-		offset: 0,
-		marker: { // docs: use another name?
-			animation: {
-				duration: 50
-			},
-			color: 'gray',
-			width: 0.01
-		},
-		labels: {
-			overflow: 'justify'
-		},
-		minColor: '#EFEFFF',
-		maxColor: '#003875',
-		tickLength: 5
-	},
-	init: function (chart, userOptions) {
-		var horiz = chart.options.legend.layout !== 'vertical',
-			options;
-
-		// Build the options
-		options = merge(this.defaultColorAxisOptions, {
-			side: horiz ? 2 : 1,
-			reversed: !horiz
-		}, userOptions, {
-			isX: horiz,
-			opposite: !horiz,
-			showEmpty: false,
-			title: null,
-			isColor: true
-		});
-
-		Axis.prototype.init.call(this, chart, options);
-
-		// Base init() pushes it to the xAxis array, now pop it again
-		//chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
-
-		// Prepare data classes
-		if (userOptions.dataClasses) {
-			this.initDataClasses(userOptions);
-		}
-		this.initStops(userOptions);
-
-		// Override original axis properties
-		this.isXAxis = true;
-		this.horiz = horiz;
-		this.zoomEnabled = false;
-	},
-
-	/*
-	 * Return an intermediate color between two colors, according to pos where 0
-	 * is the from color and 1 is the to color
-	 */
-	tweenColors: function (from, to, pos) {
-		// Check for has alpha, because rgba colors perform worse due to lack of
-		// support in WebKit.
-		var hasAlpha = (to.rgba[3] !== 1 || from.rgba[3] !== 1);
-		return (hasAlpha ? 'rgba(' : 'rgb(') + 
-			Math.round(to.rgba[0] + (from.rgba[0] - to.rgba[0]) * (1 - pos)) + ',' + 
-			Math.round(to.rgba[1] + (from.rgba[1] - to.rgba[1]) * (1 - pos)) + ',' + 
-			Math.round(to.rgba[2] + (from.rgba[2] - to.rgba[2]) * (1 - pos)) + 
-			(hasAlpha ? (',' + (to.rgba[3] + (from.rgba[3] - to.rgba[3]) * (1 - pos))) : '') + ')';
-		},
-
-	initDataClasses: function (userOptions) {
-		var axis = this,
-			chart = this.chart,
-			dataClasses,
-			colorCounter = 0,
-			options = this.options;
-		this.dataClasses = dataClasses = [];
-
-		each(userOptions.dataClasses, function (dataClass, i) {
-			var colors;
-
-			dataClass = merge(dataClass);
-			dataClasses.push(dataClass);
-			if (!dataClass.color) {
-				if (options.dataClassColor === 'category') {
-					colors = chart.options.colors;
-					dataClass.color = colors[colorCounter++];
-					// loop back to zero
-					if (colorCounter === colors.length) {
-						colorCounter = 0;
-					}
-				} else {
-					dataClass.color = axis.tweenColors(Color(options.minColor), Color(options.maxColor), i / (userOptions.dataClasses.length - 1));
-				}
-			}
-		});
-	},
-
-	initStops: function (userOptions) {
-		this.stops = userOptions.stops || [
-			[0, this.options.minColor],
-			[1, this.options.maxColor]
-		];
-		each(this.stops, function (stop) {
-			stop.color = Color(stop[1]);
-		});
-	},
-
-	/**
-	 * Extend the setOptions method to process extreme colors and color
-	 * stops.
-	 */
-	setOptions: function (userOptions) {
-		Axis.prototype.setOptions.call(this, userOptions);
-
-		this.options.crosshair = this.options.marker;
-		this.coll = 'colorAxis';
-	},
-
-	setAxisSize: function () {
-		var symbol = this.legendSymbol,
-			chart = this.chart,
-			x,
-			y,
-			width,
-			height;
-
-		if (symbol) {
-			this.left = x = symbol.attr('x');
-			this.top = y = symbol.attr('y');
-			this.width = width = symbol.attr('width');
-			this.height = height = symbol.attr('height');
-			this.right = chart.chartWidth - x - width;
-			this.bottom = chart.chartHeight - y - height;
-
-			this.len = this.horiz ? width : height;
-			this.pos = this.horiz ? x : y;
-		}
-	},
-
-	/** 
-	 * Translate from a value to a color
-	 */
-	toColor: function (value, point) {
-		var pos,
-			stops = this.stops,
-			from,
-			to,
-			color,
-			dataClasses = this.dataClasses,
-			dataClass,
-			i;
-
-		if (dataClasses) {
-			i = dataClasses.length;
-			while (i--) {
-				dataClass = dataClasses[i];
-				from = dataClass.from;
-				to = dataClass.to;
-				if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
-					color = dataClass.color;
-					if (point) {
-						point.dataClass = i;
-					}
-					break;
-				}	
-			}
-
-		} else {
-
-			if (this.isLog) {
-				value = this.val2lin(value);
-			}
-			pos = 1 - ((this.max - value) / (this.max - this.min));
-			i = stops.length;
-			while (i--) {
-				if (pos > stops[i][0]) {
-					break;
-				}
-			}
-			from = stops[i] || stops[i + 1];
-			to = stops[i + 1] || from;
-
-			// The position within the gradient
-			pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
-			
-			color = this.tweenColors(
-				from.color, 
-				to.color,
-				pos
-			);
-		}
-		return color;
-	},
-
-	getOffset: function () {
-		var group = this.legendGroup;
-		if (group) {
-
-			Axis.prototype.getOffset.call(this);
-			
-			if (!this.axisGroup.parentGroup) {
-
-				// Move the axis elements inside the legend group
-				this.axisGroup.add(group);
-				this.gridGroup.add(group);
-				this.labelGroup.add(group);
-
-				this.added = true;
-			}
-		}
-	},
-
-	/**
-	 * Create the color gradient
-	 */
-	setLegendColor: function () {
-		var grad,
-			horiz = this.horiz,
-			options = this.options;
-
-		grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1]; 
-		this.legendColor = {
-			linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
-			stops: options.stops || [
-				[0, options.minColor],
-				[1, options.maxColor]
-			]
-		};
-	},
-
-	/**
-	 * The color axis appears inside the legend and has its own legend symbol
-	 */
-	drawLegendSymbol: function (legend, item) {
-		var padding = legend.padding,
-			legendOptions = legend.options,
-			horiz = this.horiz,
-			box,
-			width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
-			height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
-			labelPadding = pick(legendOptions.labelPadding, horiz ? 10 : 30);
-
-		this.setLegendColor();
-
-		// Create the gradient
-		item.legendSymbol = this.chart.renderer.rect(
-			0,
-			legend.baseline - 11,
-			width,
-			height
-		).attr({
-			zIndex: 1
-		}).add(item.legendGroup);
-		box = item.legendSymbol.getBBox();
-
-		// Set how much space this legend item takes up
-		this.legendItemWidth = width + padding + (horiz ? 0 : labelPadding);
-		this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
-	},
-	/**
-	 * Fool the legend
-	 */
-	setState: noop,
-	visible: true,
-	setVisible: noop,
-	getSeriesExtremes: function () {
-		var series;
-		if (this.series.length) {
-			series = this.series[0];
-			this.dataMin = series.valueMin;
-			this.dataMax = series.valueMax;
-		}
-	},
-	drawCrosshair: function (e, point) {
-		var newCross = !this.cross,
-			plotX = point && point.plotX,
-			plotY = point && point.plotY,
-			crossPos,
-			axisPos = this.pos,
-			axisLen = this.len;
-		
-		if (point) {
-			crossPos = this.toPixels(point.value);
-			if (crossPos < axisPos) {
-				crossPos = axisPos - 2;
-			} else if (crossPos > axisPos + axisLen) {
-				crossPos = axisPos + axisLen + 2;
-			}
-			
-			point.plotX = crossPos;
-			point.plotY = this.len - crossPos;
-			Axis.prototype.drawCrosshair.call(this, e, point);
-			point.plotX = plotX;
-			point.plotY = plotY;
-			
-			if (!newCross && this.cross) {
-				this.cross
-					.attr({
-						fill: this.crosshair.color
-					})
-					.add(this.labelGroup);
-			}
-		}
-	},
-	getPlotLinePath: function (a, b, c, d, pos) {
-		if (pos) { // crosshairs only
-			return this.horiz ? 
-				['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] : 
-				['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z'];
-		} else {
-			return Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
-		}
-	},
-
-	update: function (newOptions, redraw) {
-		each(this.series, function (series) {
-			series.isDirtyData = true; // Needed for Axis.update when choropleth colors change
-		});
-		Axis.prototype.update.call(this, newOptions, redraw);
-		if (this.legendItem) {
-			this.setLegendColor();
-			this.chart.legend.colorizeItem(this, true);
-		}
-	},
-
-	/**
-	 * Get the legend item symbols for data classes
-	 */
-	getDataClassLegendSymbols: function () {
-		var axis = this,
-			chart = this.chart,
-			legendItems = [],
-			legendOptions = chart.options.legend,
-			valueDecimals = legendOptions.valueDecimals,
-			valueSuffix = legendOptions.valueSuffix || '',
-			name;
-
-		each(this.dataClasses, function (dataClass, i) {
-			var vis = true,
-				from = dataClass.from,
-				to = dataClass.to;
-			
-			// Assemble the default name. This can be overridden by legend.options.labelFormatter
-			name = '';
-			if (from === UNDEFINED) {
-				name = '< ';
-			} else if (to === UNDEFINED) {
-				name = '> ';
-			}
-			if (from !== UNDEFINED) {
-				name += numberFormat(from, valueDecimals) + valueSuffix;
-			}
-			if (from !== UNDEFINED && to !== UNDEFINED) {
-				name += ' - ';
-			}
-			if (to !== UNDEFINED) {
-				name += numberFormat(to, valueDecimals) + valueSuffix;
-			}
-			
-			// Add a mock object to the legend items
-			legendItems.push(extend({
-				chart: chart,
-				name: name,
-				options: {},
-				drawLegendSymbol: LegendSymbolMixin.drawRectangle,
-				visible: true,
-				setState: noop,
-				setVisible: function () {
-					vis = this.visible = !vis;
-					each(axis.series, function (series) {
-						each(series.points, function (point) {
-							if (point.dataClass === i) {
-								point.setVisible(vis);
-							}
-						});
-					});
-					
-					chart.legend.colorizeItem(this, vis);
-				}
-			}, dataClass));
-		});
-		return legendItems;
-	},
-	name: '' // Prevents 'undefined' in legend in IE8
-});
-
-
-
-/**
- * Extend the chart getAxes method to also get the color axis
- */
-wrap(Chart.prototype, 'getAxes', function (proceed) {
-
-	var options = this.options,
-		colorAxisOptions = options.colorAxis;
-
-	proceed.call(this);
-
-	this.colorAxis = [];
-	if (colorAxisOptions) {
-		proceed = new ColorAxis(this, colorAxisOptions); // Fake assignment for jsLint
-	}
-});
-
-
-/**
- * Wrap the legend getAllItems method to add the color axis. This also removes the 
- * axis' own series to prevent them from showing up individually.
- */
-wrap(Legend.prototype, 'getAllItems', function (proceed) {
-	var allItems = [],
-		colorAxis = this.chart.colorAxis[0];
-
-	if (colorAxis) {
-
-		// Data classes
-		if (colorAxis.options.dataClasses) {
-			allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
-		// Gradient legend
-		} else {
-			// Add this axis on top
-			allItems.push(colorAxis);
-		}
-
-		// Don't add the color axis' series
-		each(colorAxis.series, function (series) {
-			series.options.showInLegend = false;
-		});
-	}
-
-	return allItems.concat(proceed.call(this));
-});/**
- * Mixin for maps and heatmaps
- */
-var colorSeriesMixin = {
-
-	pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
-		stroke: 'borderColor',
-		'stroke-width': 'borderWidth',
-		fill: 'color',
-		dashstyle: 'dashStyle'
-	},
-	pointArrayMap: ['value'],
-	axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
-	optionalAxis: 'colorAxis',
-	trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
-	getSymbol: noop,
-	parallelArrays: ['x', 'y', 'value'],
-	
-	/**
-	 * In choropleth maps, the color is a result of the value, so this needs translation too
-	 */
-	translateColors: function () {
-		var series = this,
-			nullColor = this.options.nullColor,
-			colorAxis = this.colorAxis;
-
-		each(this.data, function (point) {
-			var value = point.value,
-				color;
-
-			color = value === null ? nullColor : colorAxis ? colorAxis.toColor(value, point) : (point.color) || series.color;
-
-			if (color) {
-				point.color = color;
-			}
-		});
-	}
-};
-/**
- * Extend the default options with map options
- */
-defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
-	animation: false,
-	borderWidth: 0,
-	nullColor: '#F8F8F8',
-	dataLabels: {
-		format: '{point.value}',
-		verticalAlign: 'middle',
-		crop: false,
-		overflow: false,
-		style: {
-			color: 'white',
-			fontWeight: 'bold',
-			textShadow: '0 0 5px black'
-		}
-	},
-	marker: null,
-	tooltip: {
-		pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
-	},
-	states: {
-		normal: {
-			animation: true
-		},
-		hover: {
-			brightness: 0.2
-		}
-	}
-});
-
-// The Heatmap series type
-seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
-	type: 'heatmap',
-	pointArrayMap: ['y', 'value'],
-	hasPointSpecificOptions: true,
-	supportsDrilldown: true,
-	getExtremesFromAll: true,
-	init: function () {
-		seriesTypes.scatter.prototype.init.apply(this, arguments);
-		this.pointRange = this.options.colsize || 1;
-		this.yAxis.axisPointRange = this.options.rowsize || 1; // general point range
-	},
-	translate: function () {
-		var series = this,
-			options = series.options,
-			xAxis = series.xAxis,
-			yAxis = series.yAxis;
-
-		series.generatePoints();
-
-		each(series.points, function (point) {
-			var xPad = (options.colsize || 1) / 2,
-				yPad = (options.rowsize || 1) / 2,
-				x1 = Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)),
-				x2 = Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)),
-				y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
-				y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1));
-
-			// Set plotX and plotY for use in K-D-TreeView and more
-			point.plotX = (x1 + x2) / 2;
-			point.plotY = (y1 + y2) / 2;
-
-			point.shapeType = 'rect';
-			point.shapeArgs = {
-				x: Math.min(x1, x2),
-				y: Math.min(y1, y2),
-				width: Math.abs(x2 - x1),
-				height: Math.abs(y2 - y1)
-			};
-		});
-		
-		series.translateColors();
-	},
-	drawPoints: seriesTypes.column.prototype.drawPoints,
-	animate: noop,
-	getBox: noop,
-	drawLegendSymbol: LegendSymbolMixin.drawRectangle,
-
-	getExtremes: function () {
-		// Get the extremes from the value data
-		Series.prototype.getExtremes.call(this, this.valueData);
-		this.valueMin = this.dataMin;
-		this.valueMax = this.dataMax;
-
-		// Get the extremes from the y data
-		Series.prototype.getExtremes.call(this);
-	}
-		
-}));
-
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/no-data-to-display.js b/apps/static/js/plugins/highcharts/modules/no-data-to-display.js
deleted file mode 100644
index 31faeed98..000000000
--- a/apps/static/js/plugins/highcharts/modules/no-data-to-display.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- Highcharts JS v4.0.1 (2014-04-24)
- Plugin for displaying a message when there is no data visible in chart.
-
- (c) 2010-2014 Highsoft AS
- Author: Oystein Moseng
-
- License: www.highcharts.com/license
-*/
-(function(c){function f(){return!!this.points.length}function g(){this.hasData()?this.hideNoData():this.showNoData()}var d=c.seriesTypes,e=c.Chart.prototype,h=c.getOptions(),i=c.extend;i(h.lang,{noData:"No data to display"});h.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"},attr:{},style:{fontWeight:"bold",fontSize:"12px",color:"#60606a"}};if(d.pie)d.pie.prototype.hasData=f;if(d.gauge)d.gauge.prototype.hasData=f;if(d.waterfall)d.waterfall.prototype.hasData=f;c.Series.prototype.hasData=
-function(){return this.dataMax!==void 0&&this.dataMin!==void 0};e.showNoData=function(a){var b=this.options,a=a||b.lang.noData,b=b.noData;if(!this.noDataLabel)this.noDataLabel=this.renderer.label(a,0,0,null,null,null,null,null,"no-data").attr(b.attr).css(b.style).add(),this.noDataLabel.align(i(this.noDataLabel.getBBox(),b.position),!1,"plotBox")};e.hideNoData=function(){if(this.noDataLabel)this.noDataLabel=this.noDataLabel.destroy()};e.hasData=function(){for(var a=this.series,b=a.length;b--;)if(a[b].hasData()&&
-!a[b].options.isInternal)return!0;return!1};e.callbacks.push(function(a){c.addEvent(a,"load",g);c.addEvent(a,"redraw",g)})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/no-data-to-display.src.js b/apps/static/js/plugins/highcharts/modules/no-data-to-display.src.js
deleted file mode 100644
index 198e597bd..000000000
--- a/apps/static/js/plugins/highcharts/modules/no-data-to-display.src.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * @license Highcharts JS v4.0.1 (2014-04-24)
- * Plugin for displaying a message when there is no data visible in chart.
- *
- * (c) 2010-2014 Highsoft AS
- * Author: Oystein Moseng
- *
- * License: www.highcharts.com/license
- */
-
-(function (H) { // docs
-	
-	var seriesTypes = H.seriesTypes,
-		chartPrototype = H.Chart.prototype,
-		defaultOptions = H.getOptions(),
-		extend = H.extend;
-
-	// Add language option
-	extend(defaultOptions.lang, {
-		noData: 'No data to display'
-	});
-	
-	// Add default display options for message
-	defaultOptions.noData = {
-		position: {
-			x: 0,
-			y: 0,			
-			align: 'center',
-			verticalAlign: 'middle'
-		},
-		attr: {						
-		},
-		style: {	
-			fontWeight: 'bold',		
-			fontSize: '12px',
-			color: '#60606a'		
-		}
-	};
-
-	/**
-	 * Define hasData functions for series. These return true if there are data points on this series within the plot area
-	 */	
-	function hasDataPie() {
-		return !!this.points.length; /* != 0 */
-	}
-
-	if (seriesTypes.pie) {
-		seriesTypes.pie.prototype.hasData = hasDataPie;
-	}
-
-	if (seriesTypes.gauge) {
-		seriesTypes.gauge.prototype.hasData = hasDataPie;
-	}
-
-	if (seriesTypes.waterfall) {
-		seriesTypes.waterfall.prototype.hasData = hasDataPie;
-	}
-
-	H.Series.prototype.hasData = function () {
-		return this.dataMax !== undefined && this.dataMin !== undefined;
-	};
-	
-	/**
-	 * Display a no-data message.
-	 *
-	 * @param {String} str An optional message to show in place of the default one 
-	 */
-	chartPrototype.showNoData = function (str) {
-		var chart = this,
-			options = chart.options,
-			text = str || options.lang.noData,
-			noDataOptions = options.noData;
-
-		if (!chart.noDataLabel) {
-			chart.noDataLabel = chart.renderer.label(text, 0, 0, null, null, null, null, null, 'no-data')
-				.attr(noDataOptions.attr)
-				.css(noDataOptions.style)
-				.add();
-			chart.noDataLabel.align(extend(chart.noDataLabel.getBBox(), noDataOptions.position), false, 'plotBox');
-		}
-	};
-
-	/**
-	 * Hide no-data message	
-	 */	
-	chartPrototype.hideNoData = function () {
-		var chart = this;
-		if (chart.noDataLabel) {
-			chart.noDataLabel = chart.noDataLabel.destroy();
-		}
-	};
-
-	/**
-	 * Returns true if there are data points within the plot area now
-	 */	
-	chartPrototype.hasData = function () {
-		var chart = this,
-			series = chart.series,
-			i = series.length;
-
-		while (i--) {
-			if (series[i].hasData() && !series[i].options.isInternal) { 
-				return true;
-			}	
-		}
-
-		return false;
-	};
-
-	/**
-	 * Show no-data message if there is no data in sight. Otherwise, hide it.
-	 */
-	function handleNoData() {
-		var chart = this;
-		if (chart.hasData()) {
-			chart.hideNoData();
-		} else {
-			chart.showNoData();
-		}
-	}
-
-	/**
-	 * Add event listener to handle automatic display of no-data message
-	 */
-	chartPrototype.callbacks.push(function (chart) {
-		H.addEvent(chart, 'load', handleNoData);
-		H.addEvent(chart, 'redraw', handleNoData);
-	});
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/modules/solid-gauge.js b/apps/static/js/plugins/highcharts/modules/solid-gauge.js
deleted file mode 100644
index 8ea489d56..000000000
--- a/apps/static/js/plugins/highcharts/modules/solid-gauge.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
-  Highcharts JS v4.0.1 (2014-04-24)
- Solid angular gauge module
-
- (c) 2010-2014 Torstein Honsi
-
- License: www.highcharts.com/license
-*/
-(function(a){var l=a.getOptions().plotOptions,o=a.pInt,p=a.pick,j=a.each,m;l.solidgauge=a.merge(l.gauge,{colorByPoint:!0});m={initDataClasses:function(b){var h=this,e=this.chart,c,k=0,f=this.options;this.dataClasses=c=[];j(b.dataClasses,function(g,d){var i,g=a.merge(g);c.push(g);if(!g.color)f.dataClassColor==="category"?(i=e.options.colors,g.color=i[k++],k===i.length&&(k=0)):g.color=h.tweenColors(a.Color(f.minColor),a.Color(f.maxColor),d/(b.dataClasses.length-1))})},initStops:function(b){this.stops=
-b.stops||[[0,this.options.minColor],[1,this.options.maxColor]];j(this.stops,function(b){b.color=a.Color(b[1])})},toColor:function(b,h){var e,c=this.stops,a,f=this.dataClasses,g,d;if(f)for(d=f.length;d--;){if(g=f[d],a=g.from,c=g.to,(a===void 0||b>=a)&&(c===void 0||b<=c)){e=g.color;if(h)h.dataClass=d;break}}else{this.isLog&&(b=this.val2lin(b));e=1-(this.max-b)/(this.max-this.min);for(d=c.length;d--;)if(e>c[d][0])break;a=c[d]||c[d+1];c=c[d+1]||a;e=1-(c[0]-e)/(c[0]-a[0]||1);e=this.tweenColors(a.color,
-c.color,e)}return e},tweenColors:function(b,a,e){var c=a.rgba[3]!==1||b.rgba[3]!==1;return b.rgba.length===0||a.rgba.length===0?"none":(c?"rgba(":"rgb(")+Math.round(a.rgba[0]+(b.rgba[0]-a.rgba[0])*(1-e))+","+Math.round(a.rgba[1]+(b.rgba[1]-a.rgba[1])*(1-e))+","+Math.round(a.rgba[2]+(b.rgba[2]-a.rgba[2])*(1-e))+(c?","+(a.rgba[3]+(b.rgba[3]-a.rgba[3])*(1-e)):"")+")"}};a.seriesTypes.solidgauge=a.extendClass(a.seriesTypes.gauge,{type:"solidgauge",bindAxes:function(){var b;a.seriesTypes.gauge.prototype.bindAxes.call(this);
-b=this.yAxis;a.extend(b,m);b.options.dataClasses&&b.initDataClasses(b.options);b.initStops(b.options)},drawPoints:function(){var b=this,h=b.yAxis,e=h.center,c=b.options,k=b.chart.renderer;a.each(b.points,function(f){var g=f.graphic,d=h.startAngleRad+h.translate(f.y,null,null,null,!0),i=o(p(c.radius,100))*e[2]/200,l=o(p(c.innerRadius,60))*e[2]/200,n=h.toColor(f.y,f),j;if(n!=="none")j=f.color,f.color=n;c.wrap===!1&&(d=Math.max(h.startAngleRad,Math.min(h.endAngleRad,d)));d=d*180/Math.PI;d={x:e[0],y:e[1],
-r:i,innerR:l,start:h.startAngleRad,end:d/(180/Math.PI)};g?(i=d.d,g.attr({fill:f.color}).animate(d,{step:function(b,c){g.attr("fill",m.tweenColors(a.Color(j),a.Color(n),c.pos))}}),d.d=i):f.graphic=k.arc(d).attr({stroke:c.borderColor||"none","stroke-width":c.borderWidth||0,fill:f.color}).add(b.group)})},animate:null})})(Highcharts);
diff --git a/apps/static/js/plugins/highcharts/modules/solid-gauge.src.js b/apps/static/js/plugins/highcharts/modules/solid-gauge.src.js
deleted file mode 100644
index 56e0a5820..000000000
--- a/apps/static/js/plugins/highcharts/modules/solid-gauge.src.js
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * @license  Highcharts JS v4.0.1 (2014-04-24)
- * Solid angular gauge module
- *
- * (c) 2010-2014 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
-
-/*global Highcharts*/
-(function (H) {
-	"use strict";
-
-	var defaultPlotOptions = H.getOptions().plotOptions,
-		pInt = H.pInt,
-		pick = H.pick,
-		each = H.each,
-		colorAxisMethods,
-		UNDEFINED;
-
-	// The default options
-	defaultPlotOptions.solidgauge = H.merge(defaultPlotOptions.gauge, {
-		colorByPoint: true
-	});
-
-
-	// These methods are defined in the ColorAxis object, and copied here.
-	// If we implement an AMD system we should make ColorAxis a dependency.
-	colorAxisMethods = {
-
-
-		initDataClasses: function (userOptions) {
-			var axis = this,
-				chart = this.chart,
-				dataClasses,
-				colorCounter = 0,
-				options = this.options;
-			this.dataClasses = dataClasses = [];
-
-			each(userOptions.dataClasses, function (dataClass, i) {
-				var colors;
-
-				dataClass = H.merge(dataClass);
-				dataClasses.push(dataClass);
-				if (!dataClass.color) {
-					if (options.dataClassColor === 'category') {
-						colors = chart.options.colors;
-						dataClass.color = colors[colorCounter++];
-						// loop back to zero
-						if (colorCounter === colors.length) {
-							colorCounter = 0;
-						}
-					} else {
-						dataClass.color = axis.tweenColors(H.Color(options.minColor), H.Color(options.maxColor), i / (userOptions.dataClasses.length - 1));
-					}
-				}
-			});
-		},
-
-		initStops: function (userOptions) {
-			this.stops = userOptions.stops || [
-				[0, this.options.minColor],
-				[1, this.options.maxColor]
-			];
-			each(this.stops, function (stop) {
-				stop.color = H.Color(stop[1]);
-			});
-		},
-		/** 
-		 * Translate from a value to a color
-		 */
-		toColor: function (value, point) {
-			var pos,
-				stops = this.stops,
-				from,
-				to,
-				color,
-				dataClasses = this.dataClasses,
-				dataClass,
-				i;
-
-			if (dataClasses) {
-				i = dataClasses.length;
-				while (i--) {
-					dataClass = dataClasses[i];
-					from = dataClass.from;
-					to = dataClass.to;
-					if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
-						color = dataClass.color;
-						if (point) {
-							point.dataClass = i;
-						}
-						break;
-					}   
-				}
-
-			} else {
-
-				if (this.isLog) {
-					value = this.val2lin(value);
-				}
-				pos = 1 - ((this.max - value) / (this.max - this.min));
-				i = stops.length;
-				while (i--) {
-					if (pos > stops[i][0]) {
-						break;
-					}
-				}
-				from = stops[i] || stops[i + 1];
-				to = stops[i + 1] || from;
-
-				// The position within the gradient
-				pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
-				
-				color = this.tweenColors(
-					from.color, 
-					to.color,
-					pos
-				);
-			}
-			return color;
-		},
-		tweenColors: function (from, to, pos) {
-			// Check for has alpha, because rgba colors perform worse due to lack of
-			// support in WebKit.
-			var hasAlpha = (to.rgba[3] !== 1 || from.rgba[3] !== 1);
-
-			if (from.rgba.length === 0 || to.rgba.length === 0) {
-				return 'none';
-			}
-			return (hasAlpha ? 'rgba(' : 'rgb(') + 
-				Math.round(to.rgba[0] + (from.rgba[0] - to.rgba[0]) * (1 - pos)) + ',' + 
-				Math.round(to.rgba[1] + (from.rgba[1] - to.rgba[1]) * (1 - pos)) + ',' + 
-				Math.round(to.rgba[2] + (from.rgba[2] - to.rgba[2]) * (1 - pos)) + 
-				(hasAlpha ? (',' + (to.rgba[3] + (from.rgba[3] - to.rgba[3]) * (1 - pos))) : '') + ')';
-		}
-	};
-
-	// The series prototype
-	H.seriesTypes.solidgauge = H.extendClass(H.seriesTypes.gauge, {
-		type: 'solidgauge',
-
-		bindAxes: function () {
-			var axis;
-			H.seriesTypes.gauge.prototype.bindAxes.call(this);
-
-			axis = this.yAxis;
-			H.extend(axis, colorAxisMethods);
-
-			// Prepare data classes
-			if (axis.options.dataClasses) {
-				axis.initDataClasses(axis.options);
-			}
-			axis.initStops(axis.options);
-		},
-
-		/**
-		 * Draw the points where each point is one needle
-		 */
-		drawPoints: function () {
-			var series = this,
-				yAxis = series.yAxis,
-				center = yAxis.center,
-				options = series.options,
-				renderer = series.chart.renderer;
-
-			H.each(series.points, function (point) {
-				var graphic = point.graphic,
-					rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true),
-					radius = (pInt(pick(options.radius, 100)) * center[2]) / 200,
-					innerRadius = (pInt(pick(options.innerRadius, 60)) * center[2]) / 200,
-					shapeArgs,
-					d,
-					toColor = yAxis.toColor(point.y, point),
-					fromColor;
-
-				if (toColor !== 'none') {
-					fromColor = point.color;
-					point.color = toColor;
-				}
-
-				// Handle the wrap option
-				if (options.wrap === false) {
-					rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
-				}
-				rotation = rotation * 180 / Math.PI;
-
-				shapeArgs = {
-					x: center[0],
-					y: center[1],
-					r: radius,
-					innerR: innerRadius,
-					start: yAxis.startAngleRad,
-					end: rotation / (180 / Math.PI)
-				};
-
-				if (graphic) {
-					d = shapeArgs.d;
-
-					/*jslint unparam: true*/
-					graphic.attr({
-						fill: point.color
-					}).animate(shapeArgs, {
-						step: function (value, fx) {
-							graphic.attr('fill', colorAxisMethods.tweenColors(H.Color(fromColor), H.Color(toColor), fx.pos));
-						}
-					});
-					/*jslint unparam: false*/
-					shapeArgs.d = d; // animate alters it
-				} else {
-					point.graphic = renderer.arc(shapeArgs)
-						.attr({
-							stroke: options.borderColor || 'none',
-							'stroke-width': options.borderWidth || 0,
-							fill: point.color
-						})
-						.add(series.group);
-				}
-			});
-		},
-		animate: null
-	});
-
-}(Highcharts));
diff --git a/apps/static/js/plugins/highcharts/themes/dark-blue.js b/apps/static/js/plugins/highcharts/themes/dark-blue.js
deleted file mode 100644
index 7cf7138ab..000000000
--- a/apps/static/js/plugins/highcharts/themes/dark-blue.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * Dark blue theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-Highcharts.theme = {
-	colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 },
-			stops: [
-				[0, 'rgb(48, 48, 96)'],
-				[1, 'rgb(0, 0, 0)']
-			]
-		},
-		borderColor: '#000000',
-		borderWidth: 2,
-		className: 'dark-container',
-		plotBackgroundColor: 'rgba(255, 255, 255, .1)',
-		plotBorderColor: '#CCCCCC',
-		plotBorderWidth: 1
-	},
-	title: {
-		style: {
-			color: '#C0C0C0',
-			font: 'bold 16px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#666666',
-			font: 'bold 12px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	xAxis: {
-		gridLineColor: '#333333',
-		gridLineWidth: 1,
-		labels: {
-			style: {
-				color: '#A0A0A0'
-			}
-		},
-		lineColor: '#A0A0A0',
-		tickColor: '#A0A0A0',
-		title: {
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-
-			}
-		}
-	},
-	yAxis: {
-		gridLineColor: '#333333',
-		labels: {
-			style: {
-				color: '#A0A0A0'
-			}
-		},
-		lineColor: '#A0A0A0',
-		minorTickInterval: null,
-		tickColor: '#A0A0A0',
-		tickWidth: 1,
-		title: {
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-			}
-		}
-	},
-	tooltip: {
-		backgroundColor: 'rgba(0, 0, 0, 0.75)',
-		style: {
-			color: '#F0F0F0'
-		}
-	},
-	toolbar: {
-		itemStyle: {
-			color: 'silver'
-		}
-	},
-	plotOptions: {
-		line: {
-			dataLabels: {
-				color: '#CCC'
-			},
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		spline: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		scatter: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		candlestick: {
-			lineColor: 'white'
-		}
-	},
-	legend: {
-		itemStyle: {
-			font: '9pt Trebuchet MS, Verdana, sans-serif',
-			color: '#A0A0A0'
-		},
-		itemHoverStyle: {
-			color: '#FFF'
-		},
-		itemHiddenStyle: {
-			color: '#444'
-		}
-	},
-	credits: {
-		style: {
-			color: '#666'
-		}
-	},
-	labels: {
-		style: {
-			color: '#CCC'
-		}
-	},
-
-	navigation: {
-		buttonOptions: {
-			symbolStroke: '#DDDDDD',
-			hoverSymbolStroke: '#FFFFFF',
-			theme: {
-				fill: {
-					linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-					stops: [
-						[0.4, '#606060'],
-						[0.6, '#333333']
-					]
-				},
-				stroke: '#000000'
-			}
-		}
-	},
-
-	// scroll charts
-	rangeSelector: {
-		buttonTheme: {
-			fill: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-			stroke: '#000000',
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold'
-			},
-			states: {
-				hover: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.4, '#BBB'],
-							[0.6, '#888']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'white'
-					}
-				},
-				select: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.1, '#000'],
-							[0.3, '#333']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'yellow'
-					}
-				}
-			}
-		},
-		inputStyle: {
-			backgroundColor: '#333',
-			color: 'silver'
-		},
-		labelStyle: {
-			color: 'silver'
-		}
-	},
-
-	navigator: {
-		handles: {
-			backgroundColor: '#666',
-			borderColor: '#AAA'
-		},
-		outlineColor: '#CCC',
-		maskFill: 'rgba(16, 16, 16, 0.5)',
-		series: {
-			color: '#7798BF',
-			lineColor: '#A6C7ED'
-		}
-	},
-
-	scrollbar: {
-		barBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		barBorderColor: '#CCC',
-		buttonArrowColor: '#CCC',
-		buttonBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		buttonBorderColor: '#CCC',
-		rifleColor: '#FFF',
-		trackBackgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, '#000'],
-				[1, '#333']
-			]
-		},
-		trackBorderColor: '#666'
-	},
-
-	// special colors for some of the
-	legendBackgroundColor: 'rgba(0, 0, 0, 0.5)',
-	background2: 'rgb(35, 35, 70)',
-	dataLabelsColor: '#444',
-	textColor: '#C0C0C0',
-	maskColor: 'rgba(255,255,255,0.3)'
-};
-
-// Apply the theme
-var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/dark-green.js b/apps/static/js/plugins/highcharts/themes/dark-green.js
deleted file mode 100644
index 4a7ad586a..000000000
--- a/apps/static/js/plugins/highcharts/themes/dark-green.js
+++ /dev/null
@@ -1,255 +0,0 @@
-/**
- * Dark blue theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-Highcharts.theme = {
-	colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: {
-			linearGradient: [0, 0, 250, 500],
-			stops: [
-				[0, 'rgb(48, 96, 48)'],
-				[1, 'rgb(0, 0, 0)']
-			]
-		},
-		borderColor: '#000000',
-		borderWidth: 2,
-		className: 'dark-container',
-		plotBackgroundColor: 'rgba(255, 255, 255, .1)',
-		plotBorderColor: '#CCCCCC',
-		plotBorderWidth: 1
-	},
-	title: {
-		style: {
-			color: '#C0C0C0',
-			font: 'bold 16px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#666666',
-			font: 'bold 12px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	xAxis: {
-		gridLineColor: '#333333',
-		gridLineWidth: 1,
-		labels: {
-			style: {
-				color: '#A0A0A0'
-			}
-		},
-		lineColor: '#A0A0A0',
-		tickColor: '#A0A0A0',
-		title: {
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-
-			}
-		}
-	},
-	yAxis: {
-		gridLineColor: '#333333',
-		labels: {
-			style: {
-				color: '#A0A0A0'
-			}
-		},
-		lineColor: '#A0A0A0',
-		minorTickInterval: null,
-		tickColor: '#A0A0A0',
-		tickWidth: 1,
-		title: {
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-			}
-		}
-	},
-	tooltip: {
-		backgroundColor: 'rgba(0, 0, 0, 0.75)',
-		style: {
-			color: '#F0F0F0'
-		}
-	},
-	toolbar: {
-		itemStyle: {
-			color: 'silver'
-		}
-	},
-	plotOptions: {
-		line: {
-			dataLabels: {
-				color: '#CCC'
-			},
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		spline: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		scatter: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		candlestick: {
-			lineColor: 'white'
-		}
-	},
-	legend: {
-		itemStyle: {
-			font: '9pt Trebuchet MS, Verdana, sans-serif',
-			color: '#A0A0A0'
-		},
-		itemHoverStyle: {
-			color: '#FFF'
-		},
-		itemHiddenStyle: {
-			color: '#444'
-		}
-	},
-	credits: {
-		style: {
-			color: '#666'
-		}
-	},
-	labels: {
-		style: {
-			color: '#CCC'
-		}
-	},
-
-
-	navigation: {
-		buttonOptions: {
-			symbolStroke: '#DDDDDD',
-			hoverSymbolStroke: '#FFFFFF',
-			theme: {
-				fill: {
-					linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-					stops: [
-						[0.4, '#606060'],
-						[0.6, '#333333']
-					]
-				},
-				stroke: '#000000'
-			}
-		}
-	},
-
-	// scroll charts
-	rangeSelector: {
-		buttonTheme: {
-			fill: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-			stroke: '#000000',
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold'
-			},
-			states: {
-				hover: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.4, '#BBB'],
-							[0.6, '#888']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'white'
-					}
-				},
-				select: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.1, '#000'],
-							[0.3, '#333']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'yellow'
-					}
-				}
-			}
-		},
-		inputStyle: {
-			backgroundColor: '#333',
-			color: 'silver'
-		},
-		labelStyle: {
-			color: 'silver'
-		}
-	},
-
-	navigator: {
-		handles: {
-			backgroundColor: '#666',
-			borderColor: '#AAA'
-		},
-		outlineColor: '#CCC',
-		maskFill: 'rgba(16, 16, 16, 0.5)',
-		series: {
-			color: '#7798BF',
-			lineColor: '#A6C7ED'
-		}
-	},
-
-	scrollbar: {
-		barBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		barBorderColor: '#CCC',
-		buttonArrowColor: '#CCC',
-		buttonBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		buttonBorderColor: '#CCC',
-		rifleColor: '#FFF',
-		trackBackgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, '#000'],
-				[1, '#333']
-			]
-		},
-		trackBorderColor: '#666'
-	},
-
-	// special colors for some of the
-	legendBackgroundColor: 'rgba(0, 0, 0, 0.5)',
-	background2: 'rgb(35, 35, 70)',
-	dataLabelsColor: '#444',
-	textColor: '#C0C0C0',
-	maskColor: 'rgba(255,255,255,0.3)'
-};
-
-// Apply the theme
-var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/dark-unica.js b/apps/static/js/plugins/highcharts/themes/dark-unica.js
deleted file mode 100644
index 2a47201d0..000000000
--- a/apps/static/js/plugins/highcharts/themes/dark-unica.js
+++ /dev/null
@@ -1,213 +0,0 @@
-/**
- * Dark theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-// Load the fonts
-Highcharts.createElement('link', {
-	href: 'https://fonts.css.network/css?family=Unica+One',
-	rel: 'stylesheet',
-	type: 'text/css'
-}, null, document.getElementsByTagName('head')[0]);
-
-Highcharts.theme = {
-	colors: ["#2b908f", "#90ee7e", "#f45b5b", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 },
-			stops: [
-				[0, '#2a2a2b'],
-				[1, '#3e3e40']
-			]
-		},
-		style: {
-			fontFamily: "'Unica One', sans-serif"
-		},
-		plotBorderColor: '#606063'
-	},
-	title: {
-		style: {
-			color: '#E0E0E3',
-			textTransform: 'uppercase',
-			fontSize: '20px'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#E0E0E3',
-			textTransform: 'uppercase'
-		}
-	},
-	xAxis: {
-		gridLineColor: '#707073',
-		labels: {
-			style: {
-				color: '#E0E0E3'
-			}
-		},
-		lineColor: '#707073',
-		minorGridLineColor: '#505053',
-		tickColor: '#707073',
-		title: {
-			style: {
-				color: '#A0A0A3'
-
-			}
-		}
-	},
-	yAxis: {
-		gridLineColor: '#707073',
-		labels: {
-			style: {
-				color: '#E0E0E3'
-			}
-		},
-		lineColor: '#707073',
-		minorGridLineColor: '#505053',
-		tickColor: '#707073',
-		tickWidth: 1,
-		title: {
-			style: {
-				color: '#A0A0A3'
-			}
-		}
-	},
-	tooltip: {
-		backgroundColor: 'rgba(0, 0, 0, 0.85)',
-		style: {
-			color: '#F0F0F0'
-		}
-	},
-	plotOptions: {
-		series: {
-			dataLabels: {
-				color: '#B0B0B3'
-			},
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		boxplot: {
-			fillColor: '#505053'
-		},
-		candlestick: {
-			lineColor: 'white'
-		},
-		errorbar: {
-			color: 'white'
-		}
-	},
-	legend: {
-		itemStyle: {
-			color: '#E0E0E3'
-		},
-		itemHoverStyle: {
-			color: '#FFF'
-		},
-		itemHiddenStyle: {
-			color: '#606063'
-		}
-	},
-	credits: {
-		style: {
-			color: '#666'
-		}
-	},
-	labels: {
-		style: {
-			color: '#707073'
-		}
-	},
-
-	drilldown: {
-		activeAxisLabelStyle: {
-			color: '#F0F0F3'
-		},
-		activeDataLabelStyle: {
-			color: '#F0F0F3'
-		}
-	},
-
-	navigation: {
-		buttonOptions: {
-			symbolStroke: '#DDDDDD',
-			theme: {
-				fill: '#505053'
-			}
-		}
-	},
-
-	// scroll charts
-	rangeSelector: {
-		buttonTheme: {
-			fill: '#505053',
-			stroke: '#000000',
-			style: {
-				color: '#CCC'
-			},
-			states: {
-				hover: {
-					fill: '#707073',
-					stroke: '#000000',
-					style: {
-						color: 'white'
-					}
-				},
-				select: {
-					fill: '#000003',
-					stroke: '#000000',
-					style: {
-						color: 'white'
-					}
-				}
-			}
-		},
-		inputBoxBorderColor: '#505053',
-		inputStyle: {
-			backgroundColor: '#333',
-			color: 'silver'
-		},
-		labelStyle: {
-			color: 'silver'
-		}
-	},
-
-	navigator: {
-		handles: {
-			backgroundColor: '#666',
-			borderColor: '#AAA'
-		},
-		outlineColor: '#CCC',
-		maskFill: 'rgba(255,255,255,0.1)',
-		series: {
-			color: '#7798BF',
-			lineColor: '#A6C7ED'
-		},
-		xAxis: {
-			gridLineColor: '#505053'
-		}
-	},
-
-	scrollbar: {
-		barBackgroundColor: '#808083',
-		barBorderColor: '#808083',
-		buttonArrowColor: '#CCC',
-		buttonBackgroundColor: '#606063',
-		buttonBorderColor: '#606063',
-		rifleColor: '#FFF',
-		trackBackgroundColor: '#404043',
-		trackBorderColor: '#404043'
-	},
-
-	// special colors for some of the
-	legendBackgroundColor: 'rgba(0, 0, 0, 0.5)',
-	background2: '#505053',
-	dataLabelsColor: '#B0B0B3',
-	textColor: '#C0C0C0',
-	contrastTextColor: '#F0F0F3',
-	maskColor: 'rgba(255,255,255,0.3)'
-};
-
-// Apply the theme
-Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/gray.js b/apps/static/js/plugins/highcharts/themes/gray.js
deleted file mode 100644
index d9a40169f..000000000
--- a/apps/static/js/plugins/highcharts/themes/gray.js
+++ /dev/null
@@ -1,257 +0,0 @@
-/**
- * Gray theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-Highcharts.theme = {
-	colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, 'rgb(96, 96, 96)'],
-				[1, 'rgb(16, 16, 16)']
-			]
-		},
-		borderWidth: 0,
-		borderRadius: 0,
-		plotBackgroundColor: null,
-		plotShadow: false,
-		plotBorderWidth: 0
-	},
-	title: {
-		style: {
-			color: '#FFF',
-			font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#DDD',
-			font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-		}
-	},
-	xAxis: {
-		gridLineWidth: 0,
-		lineColor: '#999',
-		tickColor: '#999',
-		labels: {
-			style: {
-				color: '#999',
-				fontWeight: 'bold'
-			}
-		},
-		title: {
-			style: {
-				color: '#AAA',
-				font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-			}
-		}
-	},
-	yAxis: {
-		alternateGridColor: null,
-		minorTickInterval: null,
-		gridLineColor: 'rgba(255, 255, 255, .1)',
-		minorGridLineColor: 'rgba(255,255,255,0.07)',
-		lineWidth: 0,
-		tickWidth: 0,
-		labels: {
-			style: {
-				color: '#999',
-				fontWeight: 'bold'
-			}
-		},
-		title: {
-			style: {
-				color: '#AAA',
-				font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-			}
-		}
-	},
-	legend: {
-		itemStyle: {
-			color: '#CCC'
-		},
-		itemHoverStyle: {
-			color: '#FFF'
-		},
-		itemHiddenStyle: {
-			color: '#333'
-		}
-	},
-	labels: {
-		style: {
-			color: '#CCC'
-		}
-	},
-	tooltip: {
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, 'rgba(96, 96, 96, .8)'],
-				[1, 'rgba(16, 16, 16, .8)']
-			]
-		},
-		borderWidth: 0,
-		style: {
-			color: '#FFF'
-		}
-	},
-
-
-	plotOptions: {
-		series: {
-			nullColor: '#444444'
-		},
-		line: {
-			dataLabels: {
-				color: '#CCC'
-			},
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		spline: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		scatter: {
-			marker: {
-				lineColor: '#333'
-			}
-		},
-		candlestick: {
-			lineColor: 'white'
-		}
-	},
-
-	toolbar: {
-		itemStyle: {
-			color: '#CCC'
-		}
-	},
-
-	navigation: {
-		buttonOptions: {
-			symbolStroke: '#DDDDDD',
-			hoverSymbolStroke: '#FFFFFF',
-			theme: {
-				fill: {
-					linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-					stops: [
-						[0.4, '#606060'],
-						[0.6, '#333333']
-					]
-				},
-				stroke: '#000000'
-			}
-		}
-	},
-
-	// scroll charts
-	rangeSelector: {
-		buttonTheme: {
-			fill: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-			stroke: '#000000',
-			style: {
-				color: '#CCC',
-				fontWeight: 'bold'
-			},
-			states: {
-				hover: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.4, '#BBB'],
-							[0.6, '#888']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'white'
-					}
-				},
-				select: {
-					fill: {
-						linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-						stops: [
-							[0.1, '#000'],
-							[0.3, '#333']
-						]
-					},
-					stroke: '#000000',
-					style: {
-						color: 'yellow'
-					}
-				}
-			}
-		},
-		inputStyle: {
-			backgroundColor: '#333',
-			color: 'silver'
-		},
-		labelStyle: {
-			color: 'silver'
-		}
-	},
-
-	navigator: {
-		handles: {
-			backgroundColor: '#666',
-			borderColor: '#AAA'
-		},
-		outlineColor: '#CCC',
-		maskFill: 'rgba(16, 16, 16, 0.5)',
-		series: {
-			color: '#7798BF',
-			lineColor: '#A6C7ED'
-		}
-	},
-
-	scrollbar: {
-		barBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		barBorderColor: '#CCC',
-		buttonArrowColor: '#CCC',
-		buttonBackgroundColor: {
-				linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-				stops: [
-					[0.4, '#888'],
-					[0.6, '#555']
-				]
-			},
-		buttonBorderColor: '#CCC',
-		rifleColor: '#FFF',
-		trackBackgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
-			stops: [
-				[0, '#000'],
-				[1, '#333']
-			]
-		},
-		trackBorderColor: '#666'
-	},
-
-	// special colors for some of the demo examples
-	legendBackgroundColor: 'rgba(48, 48, 48, 0.8)',
-	background2: 'rgb(70, 70, 70)',
-	dataLabelsColor: '#444',
-	textColor: '#E0E0E0',
-	maskColor: 'rgba(255,255,255,0.3)'
-};
-
-// Apply the theme
-var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/grid-light.js b/apps/static/js/plugins/highcharts/themes/grid-light.js
deleted file mode 100644
index eb3dd3686..000000000
--- a/apps/static/js/plugins/highcharts/themes/grid-light.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Grid-light theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-// Load the fonts
-Highcharts.createElement('link', {
-	href: 'https://fonts.css.network/css?family=Dosis:400,600',
-	rel: 'stylesheet',
-	type: 'text/css'
-}, null, document.getElementsByTagName('head')[0]);
-
-Highcharts.theme = {
-	colors: ["#7cb5ec", "#f7a35c", "#90ee7e", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: null,
-		style: {
-			fontFamily: "Dosis, sans-serif"
-		}
-	},
-	title: {
-		style: {
-			fontSize: '16px',
-			fontWeight: 'bold',
-			textTransform: 'uppercase'
-		}
-	},
-	tooltip: {
-		borderWidth: 0,
-		backgroundColor: 'rgba(219,219,216,0.8)',
-		shadow: false
-	},
-	legend: {
-		itemStyle: {
-			fontWeight: 'bold',
-			fontSize: '13px'
-		}
-	},
-	xAxis: {
-		gridLineWidth: 1,
-		labels: {
-			style: {
-				fontSize: '12px'
-			}
-		}
-	},
-	yAxis: {
-		minorTickInterval: 'auto',
-		title: {
-			style: {
-				textTransform: 'uppercase'
-			}
-		},
-		labels: {
-			style: {
-				fontSize: '12px'
-			}
-		}
-	},
-	plotOptions: {
-		candlestick: {
-			lineColor: '#404048'
-		}
-	},
-
-
-	// General
-	background2: '#F0F0EA'
-	
-};
-
-// Apply the theme
-Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/grid.js b/apps/static/js/plugins/highcharts/themes/grid.js
deleted file mode 100644
index 70342f5ec..000000000
--- a/apps/static/js/plugins/highcharts/themes/grid.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Grid theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-Highcharts.theme = {
-	colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'],
-	chart: {
-		backgroundColor: {
-			linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 },
-			stops: [
-				[0, 'rgb(255, 255, 255)'],
-				[1, 'rgb(240, 240, 255)']
-			]
-		},
-		borderWidth: 2,
-		plotBackgroundColor: 'rgba(255, 255, 255, .9)',
-		plotShadow: true,
-		plotBorderWidth: 1
-	},
-	title: {
-		style: {
-			color: '#000',
-			font: 'bold 16px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#666666',
-			font: 'bold 12px "Trebuchet MS", Verdana, sans-serif'
-		}
-	},
-	xAxis: {
-		gridLineWidth: 1,
-		lineColor: '#000',
-		tickColor: '#000',
-		labels: {
-			style: {
-				color: '#000',
-				font: '11px Trebuchet MS, Verdana, sans-serif'
-			}
-		},
-		title: {
-			style: {
-				color: '#333',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-
-			}
-		}
-	},
-	yAxis: {
-		minorTickInterval: 'auto',
-		lineColor: '#000',
-		lineWidth: 1,
-		tickWidth: 1,
-		tickColor: '#000',
-		labels: {
-			style: {
-				color: '#000',
-				font: '11px Trebuchet MS, Verdana, sans-serif'
-			}
-		},
-		title: {
-			style: {
-				color: '#333',
-				fontWeight: 'bold',
-				fontSize: '12px',
-				fontFamily: 'Trebuchet MS, Verdana, sans-serif'
-			}
-		}
-	},
-	legend: {
-		itemStyle: {
-			font: '9pt Trebuchet MS, Verdana, sans-serif',
-			color: 'black'
-
-		},
-		itemHoverStyle: {
-			color: '#039'
-		},
-		itemHiddenStyle: {
-			color: 'gray'
-		}
-	},
-	labels: {
-		style: {
-			color: '#99b'
-		}
-	},
-
-	navigation: {
-		buttonOptions: {
-			theme: {
-				stroke: '#CCCCCC'
-			}
-		}
-	}
-};
-
-// Apply the theme
-var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/sand-signika.js b/apps/static/js/plugins/highcharts/themes/sand-signika.js
deleted file mode 100644
index bd153f934..000000000
--- a/apps/static/js/plugins/highcharts/themes/sand-signika.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * Sand-Signika theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-// Load the fonts
-Highcharts.createElement('link', {
-	href: 'https://fonts.css.network/css?family=Signika:400,700',
-	rel: 'stylesheet',
-	type: 'text/css'
-}, null, document.getElementsByTagName('head')[0]);
-
-// Add the background image to the container
-Highcharts.wrap(Highcharts.Chart.prototype, 'getContainer', function (proceed) {
-	proceed.call(this);
-	this.container.style.background = 'url(http://www.highcharts.com/samples/graphics/sand.png)';
-});
-
-
-Highcharts.theme = {
-	colors: ["#f45b5b", "#8085e9", "#8d4654", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
-		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
-	chart: {
-		backgroundColor: null,
-		style: {
-			fontFamily: "Signika, serif"
-		}
-	},
-	title: {
-		style: {
-			color: 'black',
-			fontSize: '16px',
-			fontWeight: 'bold'
-		}
-	},
-	subtitle: {
-		style: {
-			color: 'black'
-		}
-	},
-	tooltip: {
-		borderWidth: 0
-	},
-	legend: {
-		itemStyle: {
-			fontWeight: 'bold',
-			fontSize: '13px'
-		}
-	},
-	xAxis: {
-		labels: {
-			style: {
-				color: '#6e6e70'
-			}
-		}
-	},
-	yAxis: {
-		labels: {
-			style: {
-				color: '#6e6e70'
-			}
-		}
-	},
-	plotOptions: {
-		series: {
-			shadow: true
-		},
-		candlestick: {
-			lineColor: '#404048'
-		}
-	},
-
-	// Highstock specific
-	navigator: {
-		xAxis: {
-			gridLineColor: '#D0D0D8'
-		}
-	},
-	rangeSelector: {
-		buttonTheme: {
-			fill: 'white',
-			stroke: '#C0C0C8',
-			'stroke-width': 1,
-			states: {
-				select: {
-					fill: '#D0D0D8'
-				}
-			}
-		}
-	},
-	scrollbar: {
-		trackBorderColor: '#C0C0C8'
-	},
-
-	// General
-	background2: '#E0E0E8'
-	
-};
-
-// Apply the theme
-Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/highcharts/themes/skies.js b/apps/static/js/plugins/highcharts/themes/skies.js
deleted file mode 100644
index d58b1f246..000000000
--- a/apps/static/js/plugins/highcharts/themes/skies.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Skies theme for Highcharts JS
- * @author Torstein Honsi
- */
-
-Highcharts.theme = {
-	colors: ["#514F78", "#42A07B", "#9B5E4A", "#72727F", "#1F949A", "#82914E", "#86777F", "#42A07B"],
-	chart: {
-		className: 'skies',
-		borderWidth: 0,
-		plotShadow: true,
-		plotBackgroundImage: 'http://www.highcharts.com/demo/gfx/skies.jpg',
-		plotBackgroundColor: {
-			linearGradient: [0, 0, 250, 500],
-			stops: [
-				[0, 'rgba(255, 255, 255, 1)'],
-				[1, 'rgba(255, 255, 255, 0)']
-			]
-		},
-		plotBorderWidth: 1
-	},
-	title: {
-		style: {
-			color: '#3E576F',
-			font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-		}
-	},
-	subtitle: {
-		style: {
-			color: '#6D869F',
-			font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-		}
-	},
-	xAxis: {
-		gridLineWidth: 0,
-		lineColor: '#C0D0E0',
-		tickColor: '#C0D0E0',
-		labels: {
-			style: {
-				color: '#666',
-				fontWeight: 'bold'
-			}
-		},
-		title: {
-			style: {
-				color: '#666',
-				font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-			}
-		}
-	},
-	yAxis: {
-		alternateGridColor: 'rgba(255, 255, 255, .5)',
-		lineColor: '#C0D0E0',
-		tickColor: '#C0D0E0',
-		tickWidth: 1,
-		labels: {
-			style: {
-				color: '#666',
-				fontWeight: 'bold'
-			}
-		},
-		title: {
-			style: {
-				color: '#666',
-				font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
-			}
-		}
-	},
-	legend: {
-		itemStyle: {
-			font: '9pt Trebuchet MS, Verdana, sans-serif',
-			color: '#3E576F'
-		},
-		itemHoverStyle: {
-			color: 'black'
-		},
-		itemHiddenStyle: {
-			color: 'silver'
-		}
-	},
-	labels: {
-		style: {
-			color: '#3E576F'
-		}
-	}
-};
-
-// Apply the theme
-var highchartsOptions = Highcharts.setOptions(Highcharts.theme);
diff --git a/apps/static/js/plugins/iCheck/icheck.min.js b/apps/static/js/plugins/iCheck/icheck.min.js
deleted file mode 100644
index 9b826fb7e..000000000
--- a/apps/static/js/plugins/iCheck/icheck.min.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/*! iCheck v1.0.2 by Damir Sultanov, http://git.io/arlzeA, MIT Licensed */
-(function(f){function A(a,b,d){var c=a[0],g=/er/.test(d)?_indeterminate:/bl/.test(d)?n:k,e=d==_update?{checked:c[k],disabled:c[n],indeterminate:"true"==a.attr(_indeterminate)||"false"==a.attr(_determinate)}:c[g];if(/^(ch|di|in)/.test(d)&&!e)x(a,g);else if(/^(un|en|de)/.test(d)&&e)q(a,g);else if(d==_update)for(var f in e)e[f]?x(a,f,!0):q(a,f,!0);else if(!b||"toggle"==d){if(!b)a[_callback]("ifClicked");e?c[_type]!==r&&q(a,g):x(a,g)}}function x(a,b,d){var c=a[0],g=a.parent(),e=b==k,u=b==_indeterminate,
-    v=b==n,s=u?_determinate:e?y:"enabled",F=l(a,s+t(c[_type])),B=l(a,b+t(c[_type]));if(!0!==c[b]){if(!d&&b==k&&c[_type]==r&&c.name){var w=a.closest("form"),p='input[name="'+c.name+'"]',p=w.length?w.find(p):f(p);p.each(function(){this!==c&&f(this).data(m)&&q(f(this),b)})}u?(c[b]=!0,c[k]&&q(a,k,"force")):(d||(c[b]=!0),e&&c[_indeterminate]&&q(a,_indeterminate,!1));D(a,e,b,d)}c[n]&&l(a,_cursor,!0)&&g.find("."+C).css(_cursor,"default");g[_add](B||l(a,b)||"");g.attr("role")&&!u&&g.attr("aria-"+(v?n:k),"true");
-    g[_remove](F||l(a,s)||"")}function q(a,b,d){var c=a[0],g=a.parent(),e=b==k,f=b==_indeterminate,m=b==n,s=f?_determinate:e?y:"enabled",q=l(a,s+t(c[_type])),r=l(a,b+t(c[_type]));if(!1!==c[b]){if(f||!d||"force"==d)c[b]=!1;D(a,e,s,d)}!c[n]&&l(a,_cursor,!0)&&g.find("."+C).css(_cursor,"pointer");g[_remove](r||l(a,b)||"");g.attr("role")&&!f&&g.attr("aria-"+(m?n:k),"false");g[_add](q||l(a,s)||"")}function E(a,b){if(a.data(m)){a.parent().html(a.attr("style",a.data(m).s||""));if(b)a[_callback](b);a.off(".i").unwrap();
-    f(_label+'[for="'+a[0].id+'"]').add(a.closest(_label)).off(".i")}}function l(a,b,f){if(a.data(m))return a.data(m).o[b+(f?"":"Class")]}function t(a){return a.charAt(0).toUpperCase()+a.slice(1)}function D(a,b,f,c){if(!c){if(b)a[_callback]("ifToggled");a[_callback]("ifChanged")[_callback]("if"+t(f))}}var m="iCheck",C=m+"-helper",r="radio",k="checked",y="un"+k,n="disabled";_determinate="determinate";_indeterminate="in"+_determinate;_update="update";_type="type";_click="click";_touch="touchbegin.i touchend.i";
-    _add="addClass";_remove="removeClass";_callback="trigger";_label="label";_cursor="cursor";_mobile=/ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);f.fn[m]=function(a,b){var d='input[type="checkbox"], input[type="'+r+'"]',c=f(),g=function(a){a.each(function(){var a=f(this);c=a.is(d)?c.add(a):c.add(a.find(d))})};if(/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(a))return a=a.toLowerCase(),g(this),c.each(function(){var c=
-        f(this);"destroy"==a?E(c,"ifDestroyed"):A(c,!0,a);f.isFunction(b)&&b()});if("object"!=typeof a&&a)return this;var e=f.extend({checkedClass:k,disabledClass:n,indeterminateClass:_indeterminate,labelHover:!0},a),l=e.handle,v=e.hoverClass||"hover",s=e.focusClass||"focus",t=e.activeClass||"active",B=!!e.labelHover,w=e.labelHoverClass||"hover",p=(""+e.increaseArea).replace("%","")|0;if("checkbox"==l||l==r)d='input[type="'+l+'"]';-50>p&&(p=-50);g(this);return c.each(function(){var a=f(this);E(a);var c=this,
-        b=c.id,g=-p+"%",d=100+2*p+"%",d={position:"absolute",top:g,left:g,display:"block",width:d,height:d,margin:0,padding:0,background:"#fff",border:0,opacity:0},g=_mobile?{position:"absolute",visibility:"hidden"}:p?d:{position:"absolute",opacity:0},l="checkbox"==c[_type]?e.checkboxClass||"icheckbox":e.radioClass||"i"+r,z=f(_label+'[for="'+b+'"]').add(a.closest(_label)),u=!!e.aria,y=m+"-"+Math.random().toString(36).substr(2,6),h='<div class="'+l+'" '+(u?'role="'+c[_type]+'" ':"");u&&z.each(function(){h+=
-        'aria-labelledby="';this.id?h+=this.id:(this.id=y,h+=y);h+='"'});h=a.wrap(h+"/>")[_callback]("ifCreated").parent().append(e.insert);d=f('<ins class="'+C+'"/>').css(d).appendTo(h);a.data(m,{o:e,s:a.attr("style")}).css(g);e.inheritClass&&h[_add](c.className||"");e.inheritID&&b&&h.attr("id",m+"-"+b);"static"==h.css("position")&&h.css("position","relative");A(a,!0,_update);if(z.length)z.on(_click+".i mouseover.i mouseout.i "+_touch,function(b){var d=b[_type],e=f(this);if(!c[n]){if(d==_click){if(f(b.target).is("a"))return;
-        A(a,!1,!0)}else B&&(/ut|nd/.test(d)?(h[_remove](v),e[_remove](w)):(h[_add](v),e[_add](w)));if(_mobile)b.stopPropagation();else return!1}});a.on(_click+".i focus.i blur.i keyup.i keydown.i keypress.i",function(b){var d=b[_type];b=b.keyCode;if(d==_click)return!1;if("keydown"==d&&32==b)return c[_type]==r&&c[k]||(c[k]?q(a,k):x(a,k)),!1;if("keyup"==d&&c[_type]==r)!c[k]&&x(a,k);else if(/us|ur/.test(d))h["blur"==d?_remove:_add](s)});d.on(_click+" mousedown mouseup mouseover mouseout "+_touch,function(b){var d=
-        b[_type],e=/wn|up/.test(d)?t:v;if(!c[n]){if(d==_click)A(a,!1,!0);else{if(/wn|er|in/.test(d))h[_add](e);else h[_remove](e+" "+t);if(z.length&&B&&e==v)z[/ut|nd/.test(d)?_remove:_add](w)}if(_mobile)b.stopPropagation();else return!1}})})}})(window.jQuery||window.Zepto);
diff --git a/apps/static/js/plugins/jstree/jstree.min.js b/apps/static/js/plugins/jstree/jstree.min.js
deleted file mode 100755
index e19446dd9..000000000
--- a/apps/static/js/plugins/jstree/jstree.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! jsTree - v3.0.9 - 2015-01-05 - (MIT) */
-!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a,b){"use strict";if(!a.jstree){var c=0,d=!1,e=!1,f=!1,g=[],h=a("script:last").attr("src"),i=document,j=i.createElement("LI"),k,l;j.setAttribute("role","treeitem"),k=i.createElement("I"),k.className="jstree-icon jstree-ocl",k.setAttribute("role","presentation"),j.appendChild(k),k=i.createElement("A"),k.className="jstree-anchor",k.setAttribute("href","#"),k.setAttribute("tabindex","-1"),l=i.createElement("I"),l.className="jstree-icon jstree-themeicon",l.setAttribute("role","presentation"),k.appendChild(l),j.appendChild(k),k=l=null,a.jstree={version:"3.0.9",defaults:{plugins:[]},plugins:{},path:h&&-1!==h.indexOf("/")?h.replace(/\/[^\/]+$/,""):"",idregex:/[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g},a.jstree.create=function(b,d){var e=new a.jstree.core(++c),f=d;return d=a.extend(!0,{},a.jstree.defaults,d),f&&f.plugins&&(d.plugins=f.plugins),a.each(d.plugins,function(a,b){"core"!==a&&(e=e.plugin(b,d[b]))}),e.init(b,d),e},a.jstree.destroy=function(){a(".jstree:jstree").jstree("destroy"),a(document).off(".jstree")},a.jstree.core=function(a){this._id=a,this._cnt=0,this._wrk=null,this._data={core:{themes:{name:!1,dots:!1,icons:!1},selected:[],last_error:{},working:!1,worker_queue:[],focused:null}}},a.jstree.reference=function(b){var c=null,d=null;if(b&&b.id&&(b=b.id),!d||!d.length)try{d=a(b)}catch(e){}if(!d||!d.length)try{d=a("#"+b.replace(a.jstree.idregex,"\\$&"))}catch(e){}return d&&d.length&&(d=d.closest(".jstree")).length&&(d=d.data("jstree"))?c=d:a(".jstree").each(function(){var d=a(this).data("jstree");return d&&d._model.data[b]?(c=d,!1):void 0}),c},a.fn.jstree=function(c){var d="string"==typeof c,e=Array.prototype.slice.call(arguments,1),f=null;return c!==!0||this.length?(this.each(function(){var g=a.jstree.reference(this),h=d&&g?g[c]:null;return f=d&&h?h.apply(g,e):null,g||d||c!==b&&!a.isPlainObject(c)||a(this).data("jstree",new a.jstree.create(this,c)),(g&&!d||c===!0)&&(f=g||!1),null!==f&&f!==b?!1:void 0}),null!==f&&f!==b?f:this):!1},a.expr[":"].jstree=a.expr.createPseudo(function(c){return function(c){return a(c).hasClass("jstree")&&a(c).data("jstree")!==b}}),a.jstree.defaults.core={data:!1,strings:!1,check_callback:!1,error:a.noop,animation:200,multiple:!0,themes:{name:!1,url:!1,dir:!1,dots:!0,icons:!0,stripes:!1,variant:!1,responsive:!1},expand_selected_onload:!0,worker:!0,force_text:!1,dblclick_toggle:!0},a.jstree.core.prototype={plugin:function(b,c){var d=a.jstree.plugins[b];return d?(this._data[b]={},d.prototype=this,new d(c,this)):this},init:function(b,c){this._model={data:{"#":{id:"#",parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}}},changed:[],force_full_redraw:!1,redraw_timeout:!1,default_state:{loaded:!0,opened:!1,selected:!1,disabled:!1}},this.element=a(b).addClass("jstree jstree-"+this._id),this.settings=c,this._data.core.ready=!1,this._data.core.loaded=!1,this._data.core.rtl="rtl"===this.element.css("direction"),this.element[this._data.core.rtl?"addClass":"removeClass"]("jstree-rtl"),this.element.attr("role","tree"),this.settings.core.multiple&&this.element.attr("aria-multiselectable",!0),this.element.attr("tabindex")||this.element.attr("tabindex","0"),this.bind(),this.trigger("init"),this._data.core.original_container_html=this.element.find(" > ul > li").clone(!0),this._data.core.original_container_html.find("li").addBack().contents().filter(function(){return 3===this.nodeType&&(!this.nodeValue||/^\s+$/.test(this.nodeValue))}).remove(),this.element.html("<ul class='jstree-container-ul jstree-children' role='group'><li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>"+this.get_string("Loading ...")+"</a></li></ul>"),this.element.attr("aria-activedescendant","j"+this._id+"_loading"),this._data.core.li_height=this.get_container_ul().children("li").first().height()||24,this.trigger("loading"),this.load_node("#")},destroy:function(a){if(this._wrk)try{window.URL.revokeObjectURL(this._wrk),this._wrk=null}catch(b){}a||this.element.empty(),this.teardown()},teardown:function(){this.unbind(),this.element.removeClass("jstree").removeData("jstree").find("[class^='jstree']").addBack().attr("class",function(){return this.className.replace(/jstree[^ ]*|$/gi,"")}),this.element=null},bind:function(){var b="",c=null,d=0;this.element.on("dblclick.jstree",function(){if(document.selection&&document.selection.empty)document.selection.empty();else if(window.getSelection){var a=window.getSelection();try{a.removeAllRanges(),a.collapse()}catch(b){}}}).on("mousedown.jstree",a.proxy(function(a){a.target===this.element[0]&&(a.preventDefault(),d=+new Date)},this)).on("mousedown.jstree",".jstree-ocl",function(a){a.preventDefault()}).on("click.jstree",".jstree-ocl",a.proxy(function(a){this.toggle_node(a.target)},this)).on("dblclick.jstree",".jstree-anchor",a.proxy(function(a){this.settings.core.dblclick_toggle&&this.toggle_node(a.target)},this)).on("click.jstree",".jstree-anchor",a.proxy(function(b){b.preventDefault(),b.currentTarget!==document.activeElement&&a(b.currentTarget).focus(),this.activate_node(b.currentTarget,b)},this)).on("keydown.jstree",".jstree-anchor",a.proxy(function(b){if("INPUT"===b.target.tagName)return!0;var c=null;switch(this._data.core.rtl&&(37===b.which?b.which=39:39===b.which&&(b.which=37)),b.which){case 32:b.ctrlKey&&(b.type="click",a(b.currentTarget).trigger(b));break;case 13:b.type="click",a(b.currentTarget).trigger(b);break;case 37:b.preventDefault(),this.is_open(b.currentTarget)?this.close_node(b.currentTarget):(c=this.get_parent(b.currentTarget),c&&"#"!==c.id&&this.get_node(c,!0).children(".jstree-anchor").focus());break;case 38:b.preventDefault(),c=this.get_prev_dom(b.currentTarget),c&&c.length&&c.children(".jstree-anchor").focus();break;case 39:b.preventDefault(),this.is_closed(b.currentTarget)?this.open_node(b.currentTarget,function(a){this.get_node(a,!0).children(".jstree-anchor").focus()}):this.is_open(b.currentTarget)&&(c=this.get_node(b.currentTarget,!0).children(".jstree-children")[0],c&&a(this._firstChild(c)).children(".jstree-anchor").focus());break;case 40:b.preventDefault(),c=this.get_next_dom(b.currentTarget),c&&c.length&&c.children(".jstree-anchor").focus();break;case 106:this.open_all();break;case 36:b.preventDefault(),c=this._firstChild(this.get_container_ul()[0]),c&&a(c).children(".jstree-anchor").filter(":visible").focus();break;case 35:b.preventDefault(),this.element.find(".jstree-anchor").filter(":visible").last().focus()}},this)).on("load_node.jstree",a.proxy(function(b,c){c.status&&("#"!==c.node.id||this._data.core.loaded||(this._data.core.loaded=!0,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.trigger("loaded")),this._data.core.ready||setTimeout(a.proxy(function(){if(!this.get_container_ul().find(".jstree-loading").length){if(this._data.core.ready=!0,this._data.core.selected.length){if(this.settings.core.expand_selected_onload){var b=[],c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)b=b.concat(this._model.data[this._data.core.selected[c]].parents);for(b=a.vakata.array_unique(b),c=0,d=b.length;d>c;c++)this.open_node(b[c],!1,0)}this.trigger("changed",{action:"ready",selected:this._data.core.selected})}this.trigger("ready")}},this),0))},this)).on("keypress.jstree",a.proxy(function(d){if("INPUT"===d.target.tagName)return!0;c&&clearTimeout(c),c=setTimeout(function(){b=""},500);var e=String.fromCharCode(d.which).toLowerCase(),f=this.element.find(".jstree-anchor").filter(":visible"),g=f.index(document.activeElement)||0,h=!1;if(b+=e,b.length>1){if(f.slice(g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return}if(new RegExp("^"+e+"+$").test(b)){if(f.slice(g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return}},this)).on("init.jstree",a.proxy(function(){var a=this.settings.core.themes;this._data.core.themes.dots=a.dots,this._data.core.themes.stripes=a.stripes,this._data.core.themes.icons=a.icons,this.set_theme(a.name||"default",a.url),this.set_theme_variant(a.variant)},this)).on("loading.jstree",a.proxy(function(){this[this._data.core.themes.dots?"show_dots":"hide_dots"](),this[this._data.core.themes.icons?"show_icons":"hide_icons"](),this[this._data.core.themes.stripes?"show_stripes":"hide_stripes"]()},this)).on("blur.jstree",".jstree-anchor",a.proxy(function(b){this._data.core.focused=null,a(b.currentTarget).filter(".jstree-hovered").mouseleave(),this.element.attr("tabindex","0")},this)).on("focus.jstree",".jstree-anchor",a.proxy(function(b){var c=this.get_node(b.currentTarget);c&&c.id&&(this._data.core.focused=c.id),this.element.find(".jstree-hovered").not(b.currentTarget).mouseleave(),a(b.currentTarget).mouseenter(),this.element.attr("tabindex","-1")},this)).on("focus.jstree",a.proxy(function(){+new Date-d>500&&!this._data.core.focused&&(d=0,this.get_node(this.element.attr("aria-activedescendant"),!0).find("> .jstree-anchor").focus())},this)).on("mouseenter.jstree",".jstree-anchor",a.proxy(function(a){this.hover_node(a.currentTarget)},this)).on("mouseleave.jstree",".jstree-anchor",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},unbind:function(){this.element.off(".jstree"),a(document).off(".jstree-"+this._id)},trigger:function(a,b){b||(b={}),b.instance=this,this.element.triggerHandler(a.replace(".jstree","")+".jstree",b)},get_container:function(){return this.element},get_container_ul:function(){return this.element.children(".jstree-children").first()},get_string:function(b){var c=this.settings.core.strings;return a.isFunction(c)?c.call(this,b):c&&c[b]?c[b]:b},_firstChild:function(a){a=a?a.firstChild:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_nextSibling:function(a){a=a?a.nextSibling:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_previousSibling:function(a){a=a?a.previousSibling:null;while(null!==a&&1!==a.nodeType)a=a.previousSibling;return a},get_node:function(b,c){b&&b.id&&(b=b.id);var d;try{if(this._model.data[b])b=this._model.data[b];else if("string"==typeof b&&this._model.data[b.replace(/^#/,"")])b=this._model.data[b.replace(/^#/,"")];else if("string"==typeof b&&(d=a("#"+b.replace(a.jstree.idregex,"\\$&"),this.element)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else if((d=a(b,this.element)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else{if(!(d=a(b,this.element)).length||!d.hasClass("jstree"))return!1;b=this._model.data["#"]}return c&&(b="#"===b.id?this.element:a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)),b}catch(e){return!1}},get_path:function(a,b,c){if(a=a.parents?a:this.get_node(a),!a||"#"===a.id||!a.parents)return!1;var d,e,f=[];for(f.push(c?a.id:a.text),d=0,e=a.parents.length;e>d;d++)f.push(c?a.parents[d]:this.get_text(a.parents[d]));return f=f.reverse().slice(1),b?f.join(b):f},get_next_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this._firstChild(this.get_container_ul()[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}if(b.hasClass("jstree-open")){d=this._firstChild(b.children(".jstree-children")[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);if(null!==d)return a(d)}d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return null!==d?a(d):b.parentsUntil(".jstree",".jstree-node").next(".jstree-node:visible").first()},get_prev_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this.get_container_ul()[0].lastChild;while(d&&0===d.offsetHeight)d=this._previousSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);if(null!==d){b=a(d);while(b.hasClass("jstree-open"))b=b.children(".jstree-children").first().children(".jstree-node:visible:last");return b}return d=b[0].parentNode.parentNode,d&&d.className&&-1!==d.className.indexOf("jstree-node")?a(d):!1},get_parent:function(a){return a=this.get_node(a),a&&"#"!==a.id?a.parent:!1},get_children_dom:function(a){return a=this.get_node(a,!0),a[0]===this.element[0]?this.get_container_ul().children(".jstree-node"):a&&a.length?a.children(".jstree-children").children(".jstree-node"):!1},is_parent:function(a){return a=this.get_node(a),a&&(a.state.loaded===!1||a.children.length>0)},is_loaded:function(a){return a=this.get_node(a),a&&a.state.loaded},is_loading:function(a){return a=this.get_node(a),a&&a.state&&a.state.loading},is_open:function(a){return a=this.get_node(a),a&&a.state.opened},is_closed:function(a){return a=this.get_node(a),a&&this.is_parent(a)&&!a.state.opened},is_leaf:function(a){return!this.is_parent(a)},load_node:function(b,c){var d,e,f,g,h;if(a.isArray(b))return this._load_nodes(b.slice(),c),!0;if(b=this.get_node(b),!b)return c&&c.call(this,b,!1),!1;if(b.state.loaded){for(b.state.loaded=!1,d=0,e=b.children_d.length;e>d;d++){for(f=0,g=b.parents.length;g>f;f++)this._model.data[b.parents[f]].children_d=a.vakata.array_remove_item(this._model.data[b.parents[f]].children_d,b.children_d[d]);this._model.data[b.children_d[d]].state.selected&&(h=!0,this._data.core.selected=a.vakata.array_remove_item(this._data.core.selected,b.children_d[d])),delete this._model.data[b.children_d[d]]}b.children=[],b.children_d=[],h&&this.trigger("changed",{action:"load_node",node:b,selected:this._data.core.selected})}return b.state.loading=!0,this.get_node(b,!0).addClass("jstree-loading").attr("aria-busy",!0),this._load_node(b,a.proxy(function(a){b=this._model.data[b.id],b.state.loading=!1,b.state.loaded=a;var d=this.get_node(b,!0);b.state.loaded&&!b.children.length&&d&&d.length&&!d.hasClass("jstree-leaf")&&d.removeClass("jstree-closed jstree-open").addClass("jstree-leaf"),d.removeClass("jstree-loading").attr("aria-busy",!1),this.trigger("load_node",{node:b,status:a}),c&&c.call(this,b,a)},this)),!0},_load_nodes:function(a,b,c){var d=!0,e=function(){this._load_nodes(a,b,!0)},f=this._model.data,g,h;for(g=0,h=a.length;h>g;g++)!f[a[g]]||f[a[g]].state.loaded&&c||(this.is_loading(a[g])||this.load_node(a[g],e),d=!1);d&&b&&!b.done&&(b.call(this,a),b.done=!0)},load_all:function(a,b){if(a||(a="#"),a=this.get_node(a),!a)return!1;var c=[],d=this._model.data,e=d[a.id].children_d,f,g;for(a.state&&!a.state.loaded&&c.push(a.id),f=0,g=e.length;g>f;f++)d[e[f]]&&d[e[f]].state&&!d[e[f]].state.loaded&&c.push(e[f]);c.length?this._load_nodes(c,function(){this.load_all(a,b)}):(b&&b.call(this,a),this.trigger("load_all",{node:a}))},_load_node:function(b,c){var d=this.settings.core.data,e;return d?a.isFunction(d)?d.call(this,b,a.proxy(function(d){d===!1&&c.call(this,!1),this["string"==typeof d?"_append_html_data":"_append_json_data"](b,"string"==typeof d?a(d):d,function(a){c.call(this,a)})},this)):"object"==typeof d?d.url?(d=a.extend(!0,{},d),a.isFunction(d.url)&&(d.url=d.url.call(this,b)),a.isFunction(d.data)&&(d.data=d.data.call(this,b)),a.ajax(d).done(a.proxy(function(d,e,f){var g=f.getResponseHeader("Content-Type");return-1!==g.indexOf("json")||"object"==typeof d?this._append_json_data(b,d,function(a){c.call(this,a)}):-1!==g.indexOf("html")||"string"==typeof d?this._append_html_data(b,a(d),function(a){c.call(this,a)}):(this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:f})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))},this)).fail(a.proxy(function(a){c.call(this,!1),this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:a})},this.settings.core.error.call(this,this._data.core.last_error)},this))):(e=a.isArray(d)||a.isPlainObject(d)?JSON.parse(JSON.stringify(d)):d,"#"===b.id?this._append_json_data(b,e,function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_05",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))):"string"==typeof d?"#"===b.id?this._append_html_data(b,a(d),function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_06",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1)):c.call(this,!1):"#"===b.id?this._append_html_data(b,this._data.core.original_container_html.clone(!0),function(a){c.call(this,a)}):c.call(this,!1)},_node_changed:function(a){a=this.get_node(a),a&&this._model.changed.push(a.id)},_append_html_data:function(b,c,d){b=this.get_node(b),b.children=[],b.children_d=[];var e=c.is("ul")?c.children():c,f=b.id,g=[],h=[],i=this._model.data,j=i[f],k=this._data.core.selected.length,l,m,n;for(e.each(a.proxy(function(b,c){l=this._parse_model_from_html(a(c),f,j.parents.concat()),l&&(g.push(l),h.push(l),i[l].children_d.length&&(h=h.concat(i[l].children_d)))},this)),j.children=g,j.children_d=h,m=0,n=j.parents.length;n>m;m++)i[j.parents[m]].children_d=i[j.parents[m]].children_d.concat(h);this.trigger("model",{nodes:h,parent:f}),"#"!==f?(this._node_changed(f),this.redraw()):(this.get_container_ul().children(".jstree-initial-node").remove(),this.redraw(!0)),this._data.core.selected.length!==k&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)},_append_json_data:function(b,c,d,e){b=this.get_node(b),b.children=[],b.children_d=[],c.d&&(c=c.d,"string"==typeof c&&(c=JSON.parse(c))),a.isArray(c)||(c=[c]);var f=null,g={df:this._model.default_state,dat:c,par:b.id,m:this._model.data,t_id:this._id,t_cnt:this._cnt,sel:this._data.core.selected},h=function(a,b){a.data&&(a=a.data);var c=a.dat,d=a.par,e=[],f=[],g=[],h=a.df,i=a.t_id,j=a.t_cnt,k=a.m,l=k[d],m=a.sel,n,o,p,q,r=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f,i,j,l,m={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in h)h.hasOwnProperty(f)&&(m.state[f]=h[f]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(m.icon=a.data.jstree.icon),a&&a.data&&(m.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(m.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(m.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(m.li_attr[f]=a.li_attr[f]);if(m.li_attr.id||(m.li_attr.id=e),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(m.a_attr[f]=a.a_attr[f]);for(a&&a.children&&a.children===!0&&(m.state.loaded=!1,m.children=[],m.children_d=[]),k[m.id]=m,f=0,i=m.children.length;i>f;f++)j=r(k[m.children[f]],m.id,d),l=k[j],m.children_d.push(j),l.children_d.length&&(m.children_d=m.children_d.concat(l.children_d));return delete a.data,delete a.children,k[m.id].original=a,m.state.selected&&g.push(m.id),m.id},s=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,l,m,n,o;do e="j"+i+"_"+ ++j;while(k[e]);o={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in h)h.hasOwnProperty(f)&&(o.state[f]=h[f]);if(a&&a.id&&(o.id=a.id.toString()),a&&a.text&&(o.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(o.icon=a.data.jstree.icon),a&&a.data&&(o.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(o.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(o.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(o.li_attr[f]=a.li_attr[f]);if(o.li_attr.id&&!o.id&&(o.id=o.li_attr.id.toString()),o.id||(o.id=e),o.li_attr.id||(o.li_attr.id=o.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(o.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,l=a.children.length;l>f;f++)m=s(a.children[f],o.id,d),n=k[m],o.children.push(m),n.children_d.length&&(o.children_d=o.children_d.concat(n.children_d));o.children_d=o.children_d.concat(o.children)}return a&&a.children&&a.children===!0&&(o.state.loaded=!1,o.children=[],o.children_d=[]),delete a.data,delete a.children,o.original=a,k[o.id]=o,o.state.selected&&g.push(o.id),o.id};if(c.length&&c[0].id!==b&&c[0].parent!==b){for(o=0,p=c.length;p>o;o++)c[o].children||(c[o].children=[]),k[c[o].id.toString()]=c[o];for(o=0,p=c.length;p>o;o++)k[c[o].parent.toString()].children.push(c[o].id.toString()),l.children_d.push(c[o].id.toString());for(o=0,p=l.children.length;p>o;o++)n=r(k[l.children[o]],d,l.parents.concat()),f.push(n),k[n].children_d.length&&(f=f.concat(k[n].children_d));for(o=0,p=l.parents.length;p>o;o++)k[l.parents[o]].children_d=k[l.parents[o]].children_d.concat(f);q={cnt:j,mod:k,sel:m,par:d,dpc:f,add:g}}else{for(o=0,p=c.length;p>o;o++)n=s(c[o],d,l.parents.concat()),n&&(e.push(n),f.push(n),k[n].children_d.length&&(f=f.concat(k[n].children_d)));for(l.children=e,l.children_d=f,o=0,p=l.parents.length;p>o;o++)k[l.parents[o]].children_d=k[l.parents[o]].children_d.concat(f);q={cnt:j,mod:k,sel:m,par:d,dpc:f,add:g}}return"undefined"!=typeof window&&"undefined"!=typeof window.document?q:void postMessage(q)},i=function(b,c){if(this._cnt=b.cnt,this._model.data=b.mod,c){var e,f,g=b.add,h=b.sel,i=this._data.core.selected.slice(),j=this._model.data;if(h.length!==i.length||a.vakata.array_unique(h.concat(i)).length!==h.length){for(e=0,f=h.length;f>e;e++)-1===a.inArray(h[e],g)&&-1===a.inArray(h[e],i)&&(j[h[e]].state.selected=!1);for(e=0,f=i.length;f>e;e++)-1===a.inArray(i[e],h)&&(j[i[e]].state.selected=!0)}}b.add.length&&(this._data.core.selected=this._data.core.selected.concat(b.add)),this.trigger("model",{nodes:b.dpc,parent:b.par}),"#"!==b.par?(this._node_changed(b.par),this.redraw()):this.redraw(!0),b.add.length&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)};if(this.settings.core.worker&&window.Blob&&window.URL&&window.Worker)try{null===this._wrk&&(this._wrk=window.URL.createObjectURL(new window.Blob(["self.onmessage = "+h.toString()],{type:"text/javascript"}))),!this._data.core.working||e?(this._data.core.working=!0,f=new window.Worker(this._wrk),f.onmessage=a.proxy(function(a){i.call(this,a.data,!0);try{f.terminate(),f=null}catch(b){}this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1},this),g.par?f.postMessage(g):this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1):this._data.core.worker_queue.push([b,c,d,!0])}catch(j){i.call(this,h(g),!1),this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1}else i.call(this,h(g),!1)},_parse_model_from_html:function(b,c,d){d=d?[].concat(d):[],c&&d.unshift(c);var e,f,g=this._model.data,h={id:!1,text:!1,icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1},i,j,k;for(i in this._model.default_state)this._model.default_state.hasOwnProperty(i)&&(h.state[i]=this._model.default_state[i]);if(j=a.vakata.attributes(b,!0),a.each(j,function(b,c){return c=a.trim(c),c.length?(h.li_attr[b]=c,void("id"===b&&(h.id=c.toString()))):!0}),j=b.children("a").first(),j.length&&(j=a.vakata.attributes(j,!0),a.each(j,function(b,c){c=a.trim(c),c.length&&(h.a_attr[b]=c)})),j=b.children("a").first().length?b.children("a").first().clone():b.clone(),j.children("ins, i, ul").remove(),j=j.html(),j=a("<div />").html(j),h.text=this.settings.core.force_text?j.text():j.html(),j=b.data(),h.data=j?a.extend(!0,{},j):null,h.state.opened=b.hasClass("jstree-open"),h.state.selected=b.children("a").hasClass("jstree-clicked"),h.state.disabled=b.children("a").hasClass("jstree-disabled"),h.data&&h.data.jstree)for(i in h.data.jstree)h.data.jstree.hasOwnProperty(i)&&(h.state[i]=h.data.jstree[i]);j=b.children("a").children(".jstree-themeicon"),j.length&&(h.icon=j.hasClass("jstree-themeicon-hidden")?!1:j.attr("rel")),h.state.icon&&(h.icon=h.state.icon),j=b.children("ul").children("li");do k="j"+this._id+"_"+ ++this._cnt;while(g[k]);return h.id=h.li_attr.id?h.li_attr.id.toString():k,j.length?(j.each(a.proxy(function(b,c){e=this._parse_model_from_html(a(c),h.id,d),f=this._model.data[e],h.children.push(e),f.children_d.length&&(h.children_d=h.children_d.concat(f.children_d))},this)),h.children_d=h.children_d.concat(h.children)):b.hasClass("jstree-closed")&&(h.state.loaded=!1),h.li_attr["class"]&&(h.li_attr["class"]=h.li_attr["class"].replace("jstree-closed","").replace("jstree-open","")),h.a_attr["class"]&&(h.a_attr["class"]=h.a_attr["class"].replace("jstree-clicked","").replace("jstree-disabled","")),g[h.id]=h,h.state.selected&&this._data.core.selected.push(h.id),h.id},_parse_model_from_flat_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f=this._model.data,g=this._model.default_state,h,i,j,k,l={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(h in g)g.hasOwnProperty(h)&&(l.state[h]=g[h]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),a&&a.data&&(l.data=a.data,a.data.jstree))for(h in a.data.jstree)a.data.jstree.hasOwnProperty(h)&&(l.state[h]=a.data.jstree[h]);if(a&&"object"==typeof a.state)for(h in a.state)a.state.hasOwnProperty(h)&&(l.state[h]=a.state[h]);if(a&&"object"==typeof a.li_attr)for(h in a.li_attr)a.li_attr.hasOwnProperty(h)&&(l.li_attr[h]=a.li_attr[h]);if(l.li_attr.id||(l.li_attr.id=e),a&&"object"==typeof a.a_attr)for(h in a.a_attr)a.a_attr.hasOwnProperty(h)&&(l.a_attr[h]=a.a_attr[h]);for(a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),f[l.id]=l,h=0,i=l.children.length;i>h;h++)j=this._parse_model_from_flat_json(f[l.children[h]],l.id,d),k=f[j],l.children_d.push(j),k.children_d.length&&(l.children_d=l.children_d.concat(k.children_d));return delete a.data,delete a.children,f[l.id].original=a,l.state.selected&&this._data.core.selected.push(l.id),l.id},_parse_model_from_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,g,h,i,j=this._model.data,k=this._model.default_state,l;do e="j"+this._id+"_"+ ++this._cnt;while(j[e]);l={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in k)k.hasOwnProperty(f)&&(l.state[f]=k[f]);if(a&&a.id&&(l.id=a.id.toString()),a&&a.text&&(l.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),a&&a.data&&(l.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(l.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(l.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(l.li_attr[f]=a.li_attr[f]);if(l.li_attr.id&&!l.id&&(l.id=l.li_attr.id.toString()),l.id||(l.id=e),l.li_attr.id||(l.li_attr.id=l.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(l.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,g=a.children.length;g>f;f++)h=this._parse_model_from_json(a.children[f],l.id,d),i=j[h],l.children.push(h),i.children_d.length&&(l.children_d=l.children_d.concat(i.children_d));l.children_d=l.children_d.concat(l.children)}return a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),delete a.data,delete a.children,l.original=a,j[l.id]=l,l.state.selected&&this._data.core.selected.push(l.id),l.id},_redraw:function(){var a=this._model.force_full_redraw?this._model.data["#"].children.concat([]):this._model.changed.concat([]),b=document.createElement("UL"),c,d,e,f=this._data.core.focused;for(d=0,e=a.length;e>d;d++)c=this.redraw_node(a[d],!0,this._model.force_full_redraw),c&&this._model.force_full_redraw&&b.appendChild(c);this._model.force_full_redraw&&(b.className=this.get_container_ul()[0].className,b.setAttribute("role","group"),this.element.empty().append(b)),null!==f&&(c=this.get_node(f,!0),c&&c.length&&c.children(".jstree-anchor")[0]!==document.activeElement?c.children(".jstree-anchor").focus():this._data.core.focused=null),this._model.force_full_redraw=!1,this._model.changed=[],this.trigger("redraw",{nodes:a})},redraw:function(a){a&&(this._model.force_full_redraw=!0),this._redraw()},draw_children:function(a){var b=this.get_node(a),c=!1,d=!1,e=!1,f=document;if(!b)return!1;if("#"===b.id)return this.redraw(!0);if(a=this.get_node(a,!0),!a||!a.length)return!1;if(a.children(".jstree-children").remove(),a=a[0],b.children.length&&b.state.loaded){for(e=f.createElement("UL"),e.setAttribute("role","group"),e.className="jstree-children",c=0,d=b.children.length;d>c;c++)e.appendChild(this.redraw_node(b.children[c],!0,!0));a.appendChild(e)}},redraw_node:function(b,c,d,e){var f=this.get_node(b),g=!1,h=!1,i=!1,k=!1,l=!1,m=!1,n="",o=document,p=this._model.data,q=!1,r=!1,s=null,t=0,u=0;if(!f)return!1;if("#"===f.id)return this.redraw(!0);if(c=c||0===f.children.length,b=document.querySelector?this.element[0].querySelector("#"+(-1!=="0123456789".indexOf(f.id[0])?"\\3"+f.id[0]+" "+f.id.substr(1).replace(a.jstree.idregex,"\\$&"):f.id.replace(a.jstree.idregex,"\\$&"))):document.getElementById(f.id))b=a(b),d||(g=b.parent().parent()[0],g===this.element[0]&&(g=null),h=b.index()),c||!f.children.length||b.children(".jstree-children").length||(c=!0),c||(i=b.children(".jstree-children")[0]),q=b.children(".jstree-anchor")[0]===document.activeElement,b.remove();else if(c=!0,!d){if(g="#"!==f.parent?a("#"+f.parent.replace(a.jstree.idregex,"\\$&"),this.element)[0]:null,!(null===g||g&&p[f.parent].state.opened))return!1;h=a.inArray(f.id,null===g?p["#"].children:p[f.parent].children)}b=j.cloneNode(!0),n="jstree-node ";for(k in f.li_attr)if(f.li_attr.hasOwnProperty(k)){if("id"===k)continue;"class"!==k?b.setAttribute(k,f.li_attr[k]):n+=f.li_attr[k]}f.a_attr.id||(f.a_attr.id=f.id+"_anchor"),b.setAttribute("aria-selected",!!f.state.selected),b.setAttribute("aria-level",f.parents.length),b.setAttribute("aria-labelledby",f.a_attr.id),f.state.disabled&&b.setAttribute("aria-disabled",!0),f.state.loaded&&!f.children.length?n+=" jstree-leaf":(n+=f.state.opened&&f.state.loaded?" jstree-open":" jstree-closed",b.setAttribute("aria-expanded",f.state.opened&&f.state.loaded)),null!==f.parent&&p[f.parent].children[p[f.parent].children.length-1]===f.id&&(n+=" jstree-last"),b.id=f.id,b.className=n,n=(f.state.selected?" jstree-clicked":"")+(f.state.disabled?" jstree-disabled":"");for(l in f.a_attr)if(f.a_attr.hasOwnProperty(l)){if("href"===l&&"#"===f.a_attr[l])continue;"class"!==l?b.childNodes[1].setAttribute(l,f.a_attr[l]):n+=" "+f.a_attr[l]}if(n.length&&(b.childNodes[1].className="jstree-anchor "+n),(f.icon&&f.icon!==!0||f.icon===!1)&&(f.icon===!1?b.childNodes[1].childNodes[0].className+=" jstree-themeicon-hidden":-1===f.icon.indexOf("/")&&-1===f.icon.indexOf(".")?b.childNodes[1].childNodes[0].className+=" "+f.icon+" jstree-themeicon-custom":(b.childNodes[1].childNodes[0].style.backgroundImage="url("+f.icon+")",b.childNodes[1].childNodes[0].style.backgroundPosition="center center",b.childNodes[1].childNodes[0].style.backgroundSize="auto",b.childNodes[1].childNodes[0].className+=" jstree-themeicon-custom")),this.settings.core.force_text?b.childNodes[1].appendChild(o.createTextNode(f.text)):b.childNodes[1].innerHTML+=f.text,c&&f.children.length&&(f.state.opened||e)&&f.state.loaded){for(m=o.createElement("UL"),m.setAttribute("role","group"),m.className="jstree-children",k=0,l=f.children.length;l>k;k++)m.appendChild(this.redraw_node(f.children[k],c,!0));
-b.appendChild(m)}if(i&&b.appendChild(i),!d){for(g||(g=this.element[0]),k=0,l=g.childNodes.length;l>k;k++)if(g.childNodes[k]&&g.childNodes[k].className&&-1!==g.childNodes[k].className.indexOf("jstree-children")){s=g.childNodes[k];break}s||(s=o.createElement("UL"),s.setAttribute("role","group"),s.className="jstree-children",g.appendChild(s)),g=s,h<g.childNodes.length?g.insertBefore(b,g.childNodes[h]):g.appendChild(b),q&&(t=this.element[0].scrollTop,u=this.element[0].scrollLeft,b.childNodes[1].focus(),this.element[0].scrollTop=t,this.element[0].scrollLeft=u)}return f.state.opened&&!f.state.loaded&&(f.state.opened=!1,setTimeout(a.proxy(function(){this.open_node(f.id,!1,0)},this),0)),b},open_node:function(c,d,e){var f,g,h,i;if(a.isArray(c)){for(c=c.slice(),f=0,g=c.length;g>f;f++)this.open_node(c[f],d,e);return!0}if(c=this.get_node(c),!c||"#"===c.id)return!1;if(e=e===b?this.settings.core.animation:e,!this.is_closed(c))return d&&d.call(this,c,!1),!1;if(this.is_loaded(c))h=this.get_node(c,!0),i=this,h.length&&(e&&h.children(".jstree-children").length&&h.children(".jstree-children").stop(!0,!0),c.children.length&&!this._firstChild(h.children(".jstree-children")[0])&&this.draw_children(c),e?(this.trigger("before_open",{node:c}),h.children(".jstree-children").css("display","none").end().removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded",!0).children(".jstree-children").stop(!0,!0).slideDown(e,function(){this.style.display="",i.trigger("after_open",{node:c})})):(this.trigger("before_open",{node:c}),h[0].className=h[0].className.replace("jstree-closed","jstree-open"),h[0].setAttribute("aria-expanded",!0))),c.state.opened=!0,d&&d.call(this,c,!0),h.length||this.trigger("before_open",{node:c}),this.trigger("open_node",{node:c}),e&&h.length||this.trigger("after_open",{node:c});else{if(this.is_loading(c))return setTimeout(a.proxy(function(){this.open_node(c,d,e)},this),500);this.load_node(c,function(a,b){return b?this.open_node(a,d,e):d?d.call(this,a,!1):!1})}},_open_to:function(b){if(b=this.get_node(b),!b||"#"===b.id)return!1;var c,d,e=b.parents;for(c=0,d=e.length;d>c;c+=1)"#"!==c&&this.open_node(e[c],!1,0);return a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)},close_node:function(c,d){var e,f,g,h;if(a.isArray(c)){for(c=c.slice(),e=0,f=c.length;f>e;e++)this.close_node(c[e],d);return!0}return c=this.get_node(c),c&&"#"!==c.id?this.is_closed(c)?!1:(d=d===b?this.settings.core.animation:d,g=this,h=this.get_node(c,!0),h.length&&(d?h.children(".jstree-children").attr("style","display:block !important").end().removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded",!1).children(".jstree-children").stop(!0,!0).slideUp(d,function(){this.style.display="",h.children(".jstree-children").remove(),g.trigger("after_close",{node:c})}):(h[0].className=h[0].className.replace("jstree-open","jstree-closed"),h.attr("aria-expanded",!1).children(".jstree-children").remove())),c.state.opened=!1,this.trigger("close_node",{node:c}),void(d&&h.length||this.trigger("after_close",{node:c}))):!1},toggle_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.toggle_node(b[c]);return!0}return this.is_closed(b)?this.open_node(b):this.is_open(b)?this.close_node(b):void 0},open_all:function(a,b,c){if(a||(a="#"),a=this.get_node(a),!a)return!1;var d="#"===a.id?this.get_container_ul():this.get_node(a,!0),e,f,g;if(!d.length){for(e=0,f=a.children_d.length;f>e;e++)this.is_closed(this._model.data[a.children_d[e]])&&(this._model.data[a.children_d[e]].state.opened=!0);return this.trigger("open_all",{node:a})}c=c||d,g=this,d=this.is_closed(a)?d.find(".jstree-closed").addBack():d.find(".jstree-closed"),d.each(function(){g.open_node(this,function(a,d){d&&this.is_parent(a)&&this.open_all(a,b,c)},b||0)}),0===c.find(".jstree-closed").length&&this.trigger("open_all",{node:this.get_node(c)})},close_all:function(b,c){if(b||(b="#"),b=this.get_node(b),!b)return!1;var d="#"===b.id?this.get_container_ul():this.get_node(b,!0),e=this,f,g;if(!d.length){for(f=0,g=b.children_d.length;g>f;f++)this._model.data[b.children_d[f]].state.opened=!1;return this.trigger("close_all",{node:b})}d=this.is_open(b)?d.find(".jstree-open").addBack():d.find(".jstree-open"),a(d.get().reverse()).each(function(){e.close_node(this,c||0)}),this.trigger("close_all",{node:b})},is_disabled:function(a){return a=this.get_node(a),a&&a.state&&a.state.disabled},enable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.enable_node(b[c]);return!0}return b=this.get_node(b),b&&"#"!==b.id?(b.state.disabled=!1,this.get_node(b,!0).children(".jstree-anchor").removeClass("jstree-disabled").attr("aria-disabled",!1),void this.trigger("enable_node",{node:b})):!1},disable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.disable_node(b[c]);return!0}return b=this.get_node(b),b&&"#"!==b.id?(b.state.disabled=!0,this.get_node(b,!0).children(".jstree-anchor").addClass("jstree-disabled").attr("aria-disabled",!0),void this.trigger("disable_node",{node:b})):!1},activate_node:function(a,c){if(this.is_disabled(a))return!1;if(this._data.core.last_clicked=this._data.core.last_clicked&&this._data.core.last_clicked.id!==b?this.get_node(this._data.core.last_clicked.id):null,this._data.core.last_clicked&&!this._data.core.last_clicked.state.selected&&(this._data.core.last_clicked=null),!this._data.core.last_clicked&&this._data.core.selected.length&&(this._data.core.last_clicked=this.get_node(this._data.core.selected[this._data.core.selected.length-1])),this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&(!c.shiftKey||this._data.core.last_clicked&&this.get_parent(a)&&this.get_parent(a)===this._data.core.last_clicked.parent))if(c.shiftKey){var d=this.get_node(a).id,e=this._data.core.last_clicked.id,f=this.get_node(this._data.core.last_clicked.parent).children,g=!1,h,i;for(h=0,i=f.length;i>h;h+=1)f[h]===d&&(g=!g),f[h]===e&&(g=!g),g||f[h]===d||f[h]===e?this.select_node(f[h],!0,!1,c):this.deselect_node(f[h],!0,c);this.trigger("changed",{action:"select_node",node:this.get_node(a),selected:this._data.core.selected,event:c})}else this.is_selected(a)?this.deselect_node(a,!1,c):this.select_node(a,!1,!1,c);else!this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&this.is_selected(a)?this.deselect_node(a,!1,c):(this.deselect_all(!0),this.select_node(a,!1,!1,c),this._data.core.last_clicked=this.get_node(a));this.trigger("activate_node",{node:this.get_node(a)})},hover_node:function(a){if(a=this.get_node(a,!0),!a||!a.length||a.children(".jstree-hovered").length)return!1;var b=this.element.find(".jstree-hovered"),c=this.element;b&&b.length&&this.dehover_node(b),a.children(".jstree-anchor").addClass("jstree-hovered"),this.trigger("hover_node",{node:this.get_node(a)}),setTimeout(function(){c.attr("aria-activedescendant",a[0].id)},0)},dehover_node:function(a){return a=this.get_node(a,!0),a&&a.length&&a.children(".jstree-hovered").length?(a.children(".jstree-anchor").removeClass("jstree-hovered"),void this.trigger("dehover_node",{node:this.get_node(a)})):!1},select_node:function(b,c,d,e){var f,g,h,i;if(a.isArray(b)){for(b=b.slice(),g=0,h=b.length;h>g;g++)this.select_node(b[g],c,d,e);return!0}return b=this.get_node(b),b&&"#"!==b.id?(f=this.get_node(b,!0),void(b.state.selected||(b.state.selected=!0,this._data.core.selected.push(b.id),d||(f=this._open_to(b)),f&&f.length&&f.attr("aria-selected",!0).children(".jstree-anchor").addClass("jstree-clicked"),this.trigger("select_node",{node:b,selected:this._data.core.selected,event:e}),c||this.trigger("changed",{action:"select_node",node:b,selected:this._data.core.selected,event:e})))):!1},deselect_node:function(b,c,d){var e,f,g;if(a.isArray(b)){for(b=b.slice(),e=0,f=b.length;f>e;e++)this.deselect_node(b[e],c,d);return!0}return b=this.get_node(b),b&&"#"!==b.id?(g=this.get_node(b,!0),void(b.state.selected&&(b.state.selected=!1,this._data.core.selected=a.vakata.array_remove_item(this._data.core.selected,b.id),g.length&&g.attr("aria-selected",!1).children(".jstree-anchor").removeClass("jstree-clicked"),this.trigger("deselect_node",{node:b,selected:this._data.core.selected,event:d}),c||this.trigger("changed",{action:"deselect_node",node:b,selected:this._data.core.selected,event:d})))):!1},select_all:function(a){var b=this._data.core.selected.concat([]),c,d;for(this._data.core.selected=this._model.data["#"].children_d.concat(),c=0,d=this._data.core.selected.length;d>c;c++)this._model.data[this._data.core.selected[c]]&&(this._model.data[this._data.core.selected[c]].state.selected=!0);this.redraw(!0),this.trigger("select_all",{selected:this._data.core.selected}),a||this.trigger("changed",{action:"select_all",selected:this._data.core.selected,old_selection:b})},deselect_all:function(a){var b=this._data.core.selected.concat([]),c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)this._model.data[this._data.core.selected[c]]&&(this._model.data[this._data.core.selected[c]].state.selected=!1);this._data.core.selected=[],this.element.find(".jstree-clicked").removeClass("jstree-clicked").parent().attr("aria-selected",!1),this.trigger("deselect_all",{selected:this._data.core.selected,node:b}),a||this.trigger("changed",{action:"deselect_all",selected:this._data.core.selected,old_selection:b})},is_selected:function(a){return a=this.get_node(a),a&&"#"!==a.id?a.state.selected:!1},get_selected:function(b){return b?a.map(this._data.core.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.core.selected.slice()},get_top_selected:function(b){var c=this.get_selected(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},get_bottom_selected:function(b){var c=this.get_selected(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},get_state:function(){var a={core:{open:[],scroll:{left:this.element.scrollLeft(),top:this.element.scrollTop()},selected:[]}},b;for(b in this._model.data)this._model.data.hasOwnProperty(b)&&"#"!==b&&(this._model.data[b].state.opened&&a.core.open.push(b),this._model.data[b].state.selected&&a.core.selected.push(b));return a},set_state:function(c,d){if(c){if(c.core){var e,f,g,h;if(c.core.open)return a.isArray(c.core.open)?(e=!0,f=!1,g=this,a.each(c.core.open.concat([]),function(b,h){f=g.get_node(h),f&&(g.is_loaded(h)?(g.is_closed(h)&&g.open_node(h,!1,0),c&&c.core&&c.core.open&&a.vakata.array_remove_item(c.core.open,h)):(g.is_loading(h)||g.open_node(h,a.proxy(function(b,e){!e&&c&&c.core&&c.core.open&&a.vakata.array_remove_item(c.core.open,b.id),this.set_state(c,d)},g),0),e=!1))}),e&&(delete c.core.open,this.set_state(c,d)),!1):(delete c.core.open,this.set_state(c,d),!1);if(c.core.scroll)return c.core.scroll&&c.core.scroll.left!==b&&this.element.scrollLeft(c.core.scroll.left),c.core.scroll&&c.core.scroll.top!==b&&this.element.scrollTop(c.core.scroll.top),delete c.core.scroll,this.set_state(c,d),!1;if(c.core.selected)return h=this,this.deselect_all(),a.each(c.core.selected,function(a,b){h.select_node(b)}),delete c.core.selected,this.set_state(c,d),!1;if(a.isEmptyObject(c.core))return delete c.core,this.set_state(c,d),!1}return a.isEmptyObject(c)?(c=null,d&&d.call(this),this.trigger("set_state"),!1):!0}return!1},refresh:function(b,c){this._data.core.state=c===!0?{}:this.get_state(),c&&a.isFunction(c)&&(this._data.core.state=c.call(this,this._data.core.state)),this._cnt=0,this._model.data={"#":{id:"#",parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}}};var d=this.get_container_ul()[0].className;b||(this.element.html("<ul class='"+d+"' role='group'><li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>"+this.get_string("Loading ...")+"</a></li></ul>"),this.element.attr("aria-activedescendant","j"+this._id+"_loading")),this.load_node("#",function(b,c){c&&(this.get_container_ul()[0].className=d,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.set_state(a.extend(!0,{},this._data.core.state),function(){this.trigger("refresh")})),this._data.core.state=null})},refresh_node:function(b){if(b=this.get_node(b),!b||"#"===b.id)return!1;var c=[],d=[],e=this._data.core.selected.concat([]);d.push(b.id),b.state.opened===!0&&c.push(b.id),this.get_node(b,!0).find(".jstree-open").each(function(){c.push(this.id)}),this._load_nodes(d,a.proxy(function(a){this.open_node(c,!1,0),this.select_node(this._data.core.selected),this.trigger("refresh_node",{node:b,nodes:a})},this))},set_id:function(b,c){if(b=this.get_node(b),!b||"#"===b.id)return!1;var d,e,f=this._model.data;for(c=c.toString(),f[b.parent].children[a.inArray(b.id,f[b.parent].children)]=c,d=0,e=b.parents.length;e>d;d++)f[b.parents[d]].children_d[a.inArray(b.id,f[b.parents[d]].children_d)]=c;for(d=0,e=b.children.length;e>d;d++)f[b.children[d]].parent=c;for(d=0,e=b.children_d.length;e>d;d++)f[b.children_d[d]].parents[a.inArray(b.id,f[b.children_d[d]].parents)]=c;return d=a.inArray(b.id,this._data.core.selected),-1!==d&&(this._data.core.selected[d]=c),d=this.get_node(b.id,!0),d&&d.attr("id",c),delete f[b.id],b.id=c,f[c]=b,!0},get_text:function(a){return a=this.get_node(a),a&&"#"!==a.id?a.text:!1},set_text:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.set_text(b[d],c);return!0}return b=this.get_node(b),b&&"#"!==b.id?(b.text=c,this.get_node(b,!0).length&&this.redraw_node(b.id),this.trigger("set_text",{obj:b,text:c}),!0):!1},get_json:function(b,c,d){if(b=this.get_node(b||"#"),!b)return!1;c&&c.flat&&!d&&(d=[]);var e={id:b.id,text:b.text,icon:this.get_icon(b),li_attr:a.extend(!0,{},b.li_attr),a_attr:a.extend(!0,{},b.a_attr),state:{},data:c&&c.no_data?!1:a.extend(!0,{},b.data)},f,g;if(c&&c.flat?e.parent=b.parent:e.children=[],!c||!c.no_state)for(f in b.state)b.state.hasOwnProperty(f)&&(e.state[f]=b.state[f]);if(c&&c.no_id&&(delete e.id,e.li_attr&&e.li_attr.id&&delete e.li_attr.id,e.a_attr&&e.a_attr.id&&delete e.a_attr.id),c&&c.flat&&"#"!==b.id&&d.push(e),!c||!c.no_children)for(f=0,g=b.children.length;g>f;f++)c&&c.flat?this.get_json(b.children[f],c,d):e.children.push(this.get_json(b.children[f],c));return c&&c.flat?d:"#"===b.id?e.children:e},create_node:function(c,d,e,f,g){if(null===c&&(c="#"),c=this.get_node(c),!c)return!1;if(e=e===b?"last":e,!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(c))return this.load_node(c,function(){this.create_node(c,d,e,f,!0)});d||(d={text:this.get_string("New node")}),"string"==typeof d&&(d={text:d}),d.text===b&&(d.text=this.get_string("New node"));var h,i,j,k;switch("#"===c.id&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":h=this.get_node(c.parent),e=a.inArray(c.id,h.children),c=h;break;case"after":h=this.get_node(c.parent),e=a.inArray(c.id,h.children)+1,c=h;break;case"inside":case"first":e=0;break;case"last":e=c.children.length;break;default:e||(e=0)}if(e>c.children.length&&(e=c.children.length),d.id||(d.id=!0),!this.check("create_node",d,c,e))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(d.id===!0&&delete d.id,d=this._parse_model_from_json(d,c.id,c.parents.concat()),!d)return!1;for(h=this.get_node(d),i=[],i.push(d),i=i.concat(h.children_d),this.trigger("model",{nodes:i,parent:c.id}),c.children_d=c.children_d.concat(i),j=0,k=c.parents.length;k>j;j++)this._model.data[c.parents[j]].children_d=this._model.data[c.parents[j]].children_d.concat(i);for(d=h,h=[],j=0,k=c.children.length;k>j;j++)h[j>=e?j+1:j]=c.children[j];return h[e]=d.id,c.children=h,this.redraw_node(c,!0),f&&f.call(this,this.get_node(d)),this.trigger("create_node",{node:this.get_node(d),parent:c.id,position:e}),d.id},rename_node:function(b,c){var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.rename_node(b[d],c);return!0}return b=this.get_node(b),b&&"#"!==b.id?(f=b.text,this.check("rename_node",b,this.get_parent(b),c)?(this.set_text(b,c),this.trigger("rename_node",{node:b,text:c,old:f}),!0):(this.settings.core.error.call(this,this._data.core.last_error),!1)):!1},delete_node:function(b){var c,d,e,f,g,h,i,j,k,l;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.delete_node(b[c]);return!0}if(b=this.get_node(b),!b||"#"===b.id)return!1;if(e=this.get_node(b.parent),f=a.inArray(b.id,e.children),l=!1,!this.check("delete_node",b,e,f))return this.settings.core.error.call(this,this._data.core.last_error),!1;for(-1!==f&&(e.children=a.vakata.array_remove(e.children,f)),g=b.children_d.concat([]),g.push(b.id),j=0,k=g.length;k>j;j++){for(h=0,i=b.parents.length;i>h;h++)f=a.inArray(g[j],this._model.data[b.parents[h]].children_d),-1!==f&&(this._model.data[b.parents[h]].children_d=a.vakata.array_remove(this._model.data[b.parents[h]].children_d,f));this._model.data[g[j]].state.selected&&(l=!0,f=a.inArray(g[j],this._data.core.selected),-1!==f&&(this._data.core.selected=a.vakata.array_remove(this._data.core.selected,f)))}for(this.trigger("delete_node",{node:b,parent:e.id}),l&&this.trigger("changed",{action:"delete_node",node:b,selected:this._data.core.selected,parent:e.id}),j=0,k=g.length;k>j;j++)delete this._model.data[g[j]];return this.redraw_node(e,!0),!0},check:function(b,c,d,e,f){c=c&&c.id?c:this.get_node(c),d=d&&d.id?d:this.get_node(d);var g=b.match(/^move_node|copy_node|create_node$/i)?d:c,h=this.settings.core.check_callback;return"move_node"!==b&&"copy_node"!==b||f&&f.is_multi||c.id!==d.id&&a.inArray(c.id,d.children)!==e&&-1===a.inArray(d.id,c.children_d)?(g&&g.data&&(g=g.data),g&&g.functions&&(g.functions[b]===!1||g.functions[b]===!0)?(g.functions[b]===!1&&(this._data.core.last_error={error:"check",plugin:"core",id:"core_02",reason:"Node data prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})}),g.functions[b]):h===!1||a.isFunction(h)&&h.call(this,b,c,d,e,f)===!1||h&&h[b]===!1?(this._data.core.last_error={error:"check",plugin:"core",id:"core_03",reason:"User config for core.check_callback prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1):!0):(this._data.core.last_error={error:"check",plugin:"core",id:"core_01",reason:"Moving parent inside child",data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1)},last_error:function(){return this._data.core.last_error},move_node:function(c,d,e,f,g,h){var i,j,k,l,m,n,o,p,q,r,s,t,u,v;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.move_node(c,d,e,f,!0)});if(a.isArray(c)){for(c=c.slice(),i=0,j=c.length;j>i;i++)this.move_node(c[i],d,e,f,g,!0)&&(d=c[i],e="after");return this.redraw(),!0}if(c=c&&c.id?c:this.get_node(c),!c||"#"===c.id)return!1;if(k=(c.parent||"#").toString(),m=e.toString().match(/^(before|after)$/)&&"#"!==d.id?this.get_node(d.parent):d,n=c.instance?c.instance:this._model.data[c.id]?this:a.jstree.reference(c.id),o=!n||!n._id||this._id!==n._id,l=n&&n._id&&k&&n._model.data[k]&&n._model.data[k].children?a.inArray(c.id,n._model.data[k].children):-1,o)return this.copy_node(c,d,e,f,g)?(n&&n.delete_node(c),!0):!1;switch("#"===d.id&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,m.children);break;case"after":e=a.inArray(d.id,m.children)+1;break;case"inside":case"first":e=0;break;case"last":e=m.children.length;break;default:e||(e=0)}if(e>m.children.length&&(e=m.children.length),!this.check("move_node",c,m,e,{core:!0,is_multi:n&&n._id&&n._id!==this._id,is_foreign:!n||!n._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(c.parent===m.id){for(p=m.children.concat(),q=a.inArray(c.id,p),-1!==q&&(p=a.vakata.array_remove(p,q),e>q&&e--),q=[],r=0,s=p.length;s>r;r++)q[r>=e?r+1:r]=p[r];q[e]=c.id,m.children=q,this._node_changed(m.id),this.redraw("#"===m.id)}else{for(q=c.children_d.concat(),q.push(c.id),r=0,s=c.parents.length;s>r;r++){for(p=[],v=n._model.data[c.parents[r]].children_d,t=0,u=v.length;u>t;t++)-1===a.inArray(v[t],q)&&p.push(v[t]);n._model.data[c.parents[r]].children_d=p}for(n._model.data[k].children=a.vakata.array_remove_item(n._model.data[k].children,c.id),r=0,s=m.parents.length;s>r;r++)this._model.data[m.parents[r]].children_d=this._model.data[m.parents[r]].children_d.concat(q);for(p=[],r=0,s=m.children.length;s>r;r++)p[r>=e?r+1:r]=m.children[r];for(p[e]=c.id,m.children=p,m.children_d.push(c.id),m.children_d=m.children_d.concat(c.children_d),c.parent=m.id,q=m.parents.concat(),q.unshift(m.id),v=c.parents.length,c.parents=q,q=q.concat(),r=0,s=c.children_d.length;s>r;r++)this._model.data[c.children_d[r]].parents=this._model.data[c.children_d[r]].parents.slice(0,-1*v),Array.prototype.push.apply(this._model.data[c.children_d[r]].parents,q);("#"===k||"#"===m.id)&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||(this._node_changed(k),this._node_changed(m.id)),h||this.redraw()}return f&&f.call(this,c,m,e),this.trigger("move_node",{node:c,parent:m.id,position:e,old_parent:k,old_position:l,is_multi:n&&n._id&&n._id!==this._id,is_foreign:!n||!n._id,old_instance:n,new_instance:this}),!0},copy_node:function(c,d,e,f,g,h){var i,j,k,l,m,n,o,p,q,r,s;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.copy_node(c,d,e,f,!0)});if(a.isArray(c)){for(c=c.slice(),i=0,j=c.length;j>i;i++)l=this.copy_node(c[i],d,e,f,g,!0),l&&(d=l,e="after");return this.redraw(),!0}if(c=c&&c.id?c:this.get_node(c),!c||"#"===c.id)return!1;switch(p=(c.parent||"#").toString(),q=e.toString().match(/^(before|after)$/)&&"#"!==d.id?this.get_node(d.parent):d,r=c.instance?c.instance:this._model.data[c.id]?this:a.jstree.reference(c.id),s=!r||!r._id||this._id!==r._id,"#"===d.id&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,q.children);break;case"after":e=a.inArray(d.id,q.children)+1;break;case"inside":case"first":e=0;break;case"last":e=q.children.length;break;default:e||(e=0)}if(e>q.children.length&&(e=q.children.length),!this.check("copy_node",c,q,e,{core:!0,is_multi:r&&r._id&&r._id!==this._id,is_foreign:!r||!r._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(o=r?r.get_json(c,{no_id:!0,no_data:!0,no_state:!0}):c,!o)return!1;if(o.id===!0&&delete o.id,o=this._parse_model_from_json(o,q.id,q.parents.concat()),!o)return!1;for(l=this.get_node(o),c&&c.state&&c.state.loaded===!1&&(l.state.loaded=!1),k=[],k.push(o),k=k.concat(l.children_d),this.trigger("model",{nodes:k,parent:q.id}),m=0,n=q.parents.length;n>m;m++)this._model.data[q.parents[m]].children_d=this._model.data[q.parents[m]].children_d.concat(k);for(k=[],m=0,n=q.children.length;n>m;m++)k[m>=e?m+1:m]=q.children[m];return k[e]=l.id,q.children=k,q.children_d.push(l.id),q.children_d=q.children_d.concat(l.children_d),"#"===q.id&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||this._node_changed(q.id),h||this.redraw("#"===q.id),f&&f.call(this,l,q,e),this.trigger("copy_node",{node:l,original:c,parent:q.id,position:e,old_parent:p,old_position:r&&r._id&&p&&r._model.data[p]&&r._model.data[p].children?a.inArray(c.id,r._model.data[p].children):-1,is_multi:r&&r._id&&r._id!==this._id,is_foreign:!r||!r._id,old_instance:r,new_instance:this}),l.id},cut:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&"#"!==g.id&&c.push(g);return c.length?(d=c,f=this,e="move_node",void this.trigger("cut",{node:b})):!1},copy:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&"#"!==g.id&&c.push(g);return c.length?(d=c,f=this,e="copy_node",void this.trigger("copy",{node:b})):!1},get_buffer:function(){return{mode:e,node:d,inst:f}},can_paste:function(){return e!==!1&&d!==!1},paste:function(a,b){return a=this.get_node(a),a&&e&&e.match(/^(copy_node|move_node)$/)&&d?(this[e](d,a,b)&&this.trigger("paste",{parent:a.id,node:d,mode:e}),d=!1,e=!1,void(f=!1)):!1},clear_buffer:function(){d=!1,e=!1,f=!1,this.trigger("clear_buffer")},edit:function(b,c){if(b=this.get_node(b),!b)return!1;if(this.settings.core.check_callback===!1)return this._data.core.last_error={error:"check",plugin:"core",id:"core_07",reason:"Could not edit node because of check_callback"},this.settings.core.error.call(this,this._data.core.last_error),!1;c="string"==typeof c?c:b.text,this.set_text(b,""),b=this._open_to(b);var d=this._data.core.rtl,e=this.element.width(),f=b.children(".jstree-anchor"),g=a("<span>"),h=c,i=a("<div />",{css:{position:"absolute",top:"-200px",left:d?"0px":"-1000px",visibility:"hidden"}}).appendTo("body"),j=a("<input />",{value:h,"class":"jstree-rename-input",css:{padding:"0",border:"1px solid silver","box-sizing":"border-box",display:"inline-block",height:this._data.core.li_height+"px",lineHeight:this._data.core.li_height+"px",width:"150px"},blur:a.proxy(function(){var c=g.children(".jstree-rename-input"),d=c.val();""===d&&(d=h),i.remove(),g.replaceWith(f),g.remove(),this.set_text(b,h),this.rename_node(b,a("<div></div>").text(d)[this.settings.core.force_text?"text":"html"]())===!1&&this.set_text(b,h)},this),keydown:function(a){var b=a.which;27===b&&(this.value=h),(27===b||13===b||37===b||38===b||39===b||40===b||32===b)&&a.stopImmediatePropagation(),(27===b||13===b)&&(a.preventDefault(),this.blur())},click:function(a){a.stopImmediatePropagation()},mousedown:function(a){a.stopImmediatePropagation()},keyup:function(a){j.width(Math.min(i.text("pW"+this.value).width(),e))},keypress:function(a){return 13===a.which?!1:void 0}}),k={fontFamily:f.css("fontFamily")||"",fontSize:f.css("fontSize")||"",fontWeight:f.css("fontWeight")||"",fontStyle:f.css("fontStyle")||"",fontStretch:f.css("fontStretch")||"",fontVariant:f.css("fontVariant")||"",letterSpacing:f.css("letterSpacing")||"",wordSpacing:f.css("wordSpacing")||""};g.attr("class",f.attr("class")).append(f.contents().clone()).append(j),f.replaceWith(g),i.css(k),j.css(k).width(Math.min(i.text("pW"+j[0].value).width(),e))[0].select()},set_theme:function(b,c){if(!b)return!1;if(c===!0){var d=this.settings.core.themes.dir;d||(d=a.jstree.path+"/themes"),c=d+"/"+b+"/style.css"}c&&-1===a.inArray(c,g)&&(a("head").append('<link rel="stylesheet" href="'+c+'" type="text/css" />'),g.push(c)),this._data.core.themes.name&&this.element.removeClass("jstree-"+this._data.core.themes.name),this._data.core.themes.name=b,this.element.addClass("jstree-"+b),this.element[this.settings.core.themes.responsive?"addClass":"removeClass"]("jstree-"+b+"-responsive"),this.trigger("set_theme",{theme:b})},get_theme:function(){return this._data.core.themes.name},set_theme_variant:function(a){this._data.core.themes.variant&&this.element.removeClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant),this._data.core.themes.variant=a,a&&this.element.addClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant)},get_theme_variant:function(){return this._data.core.themes.variant},show_stripes:function(){this._data.core.themes.stripes=!0,this.get_container_ul().addClass("jstree-striped")},hide_stripes:function(){this._data.core.themes.stripes=!1,this.get_container_ul().removeClass("jstree-striped")},toggle_stripes:function(){this._data.core.themes.stripes?this.hide_stripes():this.show_stripes()},show_dots:function(){this._data.core.themes.dots=!0,this.get_container_ul().removeClass("jstree-no-dots")},hide_dots:function(){this._data.core.themes.dots=!1,this.get_container_ul().addClass("jstree-no-dots")},toggle_dots:function(){this._data.core.themes.dots?this.hide_dots():this.show_dots()},show_icons:function(){this._data.core.themes.icons=!0,this.get_container_ul().removeClass("jstree-no-icons")},hide_icons:function(){this._data.core.themes.icons=!1,this.get_container_ul().addClass("jstree-no-icons")},toggle_icons:function(){this._data.core.themes.icons?this.hide_icons():this.show_icons()},set_icon:function(b,c){var d,e,f,g;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.set_icon(b[d],c);return!0}return b=this.get_node(b),b&&"#"!==b.id?(g=b.icon,b.icon=c,f=this.get_node(b,!0).children(".jstree-anchor").children(".jstree-themeicon"),c===!1?this.hide_icon(b):c===!0?(f.removeClass("jstree-themeicon-custom "+g).css("background","").removeAttr("rel"),g===!1&&this.show_icon(b)):-1===c.indexOf("/")&&-1===c.indexOf(".")?(f.removeClass(g).css("background",""),f.addClass(c+" jstree-themeicon-custom").attr("rel",c),g===!1&&this.show_icon(b)):(f.removeClass(g).css("background",""),f.addClass("jstree-themeicon-custom").css("background","url('"+c+"') center center no-repeat").attr("rel",c),g===!1&&this.show_icon(b)),!0):!1},get_icon:function(a){return a=this.get_node(a),a&&"#"!==a.id?a.icon:!1},hide_icon:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.hide_icon(b[c]);return!0}return b=this.get_node(b),b&&"#"!==b?(b.icon=!1,this.get_node(b,!0).children(".jstree-anchor").children(".jstree-themeicon").addClass("jstree-themeicon-hidden"),!0):!1},show_icon:function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.show_icon(b[c]);return!0}return b=this.get_node(b),b&&"#"!==b?(e=this.get_node(b,!0),b.icon=e.length?e.children(".jstree-anchor").children(".jstree-themeicon").attr("rel"):!0,b.icon||(b.icon=!0),e.children(".jstree-anchor").children(".jstree-themeicon").removeClass("jstree-themeicon-hidden"),!0):!1}},a.vakata={},a.vakata.attributes=function(b,c){b=a(b)[0];var d=c?{}:[];return b&&b.attributes&&a.each(b.attributes,function(b,e){-1===a.inArray(e.name.toLowerCase(),["style","contenteditable","hasfocus","tabindex"])&&null!==e.value&&""!==a.trim(e.value)&&(c?d[e.name]=e.value:d.push(e.name))}),d},a.vakata.array_unique=function(a){var b=[],c,d,e;for(c=0,e=a.length;e>c;c++){for(d=0;c>=d;d++)if(a[c]===a[d])break;d===c&&b.push(a[c])}return b},a.vakata.array_remove=function(a,b,c){var d=a.slice((c||b)+1||a.length);return a.length=0>b?a.length+b:b,a.push.apply(a,d),a},a.vakata.array_remove_item=function(b,c){var d=a.inArray(c,b);return-1!==d?a.vakata.array_remove(b,d):b};var m=document.createElement("I");m.className="jstree-icon jstree-checkbox",m.setAttribute("role","presentation"),a.jstree.defaults.checkbox={visible:!0,three_state:!0,whole_node:!0,keep_selected_style:!0,cascade:"",tie_selection:!0},a.jstree.plugins.checkbox=function(b,c){this.bind=function(){c.bind.call(this),this._data.checkbox.uto=!1,this._data.checkbox.selected=[],this.settings.checkbox.three_state&&(this.settings.checkbox.cascade="up+down+undetermined"),this.element.on("init.jstree",a.proxy(function(){this._data.checkbox.visible=this.settings.checkbox.visible,this.settings.checkbox.keep_selected_style||this.element.addClass("jstree-checkbox-no-clicked"),this.settings.checkbox.tie_selection&&this.element.addClass("jstree-checkbox-selection")},this)).on("loading.jstree",a.proxy(function(){this[this._data.checkbox.visible?"show_checkboxes":"hide_checkboxes"]()},this)),-1!==this.settings.checkbox.cascade.indexOf("undetermined")&&this.element.on("changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree",a.proxy(function(){this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)},this)),this.settings.checkbox.tie_selection||this.element.on("model.jstree",a.proxy(function(a,b){var c=this._model.data,d=c[b.parent],e=b.nodes,f,g;
-for(f=0,g=e.length;g>f;f++)c[e[f]].state.checked=c[e[f]].original&&c[e[f]].original.state&&c[e[f]].original.state.checked,c[e[f]].state.checked&&this._data.checkbox.selected.push(e[f])},this)),(-1!==this.settings.checkbox.cascade.indexOf("up")||-1!==this.settings.checkbox.cascade.indexOf("down"))&&this.element.on("model.jstree",a.proxy(function(b,c){var d=this._model.data,e=d[c.parent],f=c.nodes,g=[],h,i,j,k,l,m,n=this.settings.checkbox.cascade,o=this.settings.checkbox.tie_selection;if(-1!==n.indexOf("down"))if(e.state[o?"selected":"checked"]){for(i=0,j=f.length;j>i;i++)d[f[i]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(f)}else for(i=0,j=f.length;j>i;i++)if(d[f[i]].state[o?"selected":"checked"]){for(k=0,l=d[f[i]].children_d.length;l>k;k++)d[d[f[i]].children_d[k]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(d[f[i]].children_d)}if(-1!==n.indexOf("up")){for(i=0,j=e.children_d.length;j>i;i++)d[e.children_d[i]].children.length||g.push(d[e.children_d[i]].parent);for(g=a.vakata.array_unique(g),k=0,l=g.length;l>k;k++){e=d[g[k]];while(e&&"#"!==e.id){for(h=0,i=0,j=e.children.length;j>i;i++)h+=d[e.children[i]].state[o?"selected":"checked"];if(h!==j)break;e.state[o?"selected":"checked"]=!0,this._data[o?"core":"checkbox"].selected.push(e.id),m=this.get_node(e,!0),m&&m.length&&m.attr("aria-selected",!0).children(".jstree-anchor").addClass(o?"jstree-clicked":"jstree-checked"),e=this.get_node(e.parent)}}}this._data[o?"core":"checkbox"].selected=a.vakata.array_unique(this._data[o?"core":"checkbox"].selected)},this)).on(this.settings.checkbox.tie_selection?"select_node.jstree":"check_node.jstree",a.proxy(function(b,c){var d=c.node,e=this._model.data,f=this.get_node(d.parent),g=this.get_node(d,!0),h,i,j,k,l=this.settings.checkbox.cascade,m=this.settings.checkbox.tie_selection;if(-1!==l.indexOf("down"))for(this._data[m?"core":"checkbox"].selected=a.vakata.array_unique(this._data[m?"core":"checkbox"].selected.concat(d.children_d)),h=0,i=d.children_d.length;i>h;h++)k=e[d.children_d[h]],k.state[m?"selected":"checked"]=!0,k&&k.original&&k.original.state&&k.original.state.undetermined&&(k.original.state.undetermined=!1);if(-1!==l.indexOf("up"))while(f&&"#"!==f.id){for(j=0,h=0,i=f.children.length;i>h;h++)j+=e[f.children[h]].state[m?"selected":"checked"];if(j!==i)break;f.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(f.id),k=this.get_node(f,!0),k&&k.length&&k.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),f=this.get_node(f.parent)}-1!==l.indexOf("down")&&g.length&&g.find(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked").parent().attr("aria-selected",!0)},this)).on(this.settings.checkbox.tie_selection?"deselect_all.jstree":"uncheck_all.jstree",a.proxy(function(a,b){var c=this.get_node("#"),d=this._model.data,e,f,g;for(e=0,f=c.children_d.length;f>e;e++)g=d[c.children_d[e]],g&&g.original&&g.original.state&&g.original.state.undetermined&&(g.original.state.undetermined=!1)},this)).on(this.settings.checkbox.tie_selection?"deselect_node.jstree":"uncheck_node.jstree",a.proxy(function(b,c){var d=c.node,e=this.get_node(d,!0),f,g,h,i=this.settings.checkbox.cascade,j=this.settings.checkbox.tie_selection;if(d&&d.original&&d.original.state&&d.original.state.undetermined&&(d.original.state.undetermined=!1),-1!==i.indexOf("down"))for(f=0,g=d.children_d.length;g>f;f++)h=this._model.data[d.children_d[f]],h.state[j?"selected":"checked"]=!1,h&&h.original&&h.original.state&&h.original.state.undetermined&&(h.original.state.undetermined=!1);if(-1!==i.indexOf("up"))for(f=0,g=d.parents.length;g>f;f++)h=this._model.data[d.parents[f]],h.state[j?"selected":"checked"]=!1,h&&h.original&&h.original.state&&h.original.state.undetermined&&(h.original.state.undetermined=!1),h=this.get_node(d.parents[f],!0),h&&h.length&&h.attr("aria-selected",!1).children(".jstree-anchor").removeClass(j?"jstree-clicked":"jstree-checked");for(h=[],f=0,g=this._data[j?"core":"checkbox"].selected.length;g>f;f++)-1!==i.indexOf("down")&&-1!==a.inArray(this._data[j?"core":"checkbox"].selected[f],d.children_d)||-1!==i.indexOf("up")&&-1!==a.inArray(this._data[j?"core":"checkbox"].selected[f],d.parents)||h.push(this._data[j?"core":"checkbox"].selected[f]);this._data[j?"core":"checkbox"].selected=a.vakata.array_unique(h),-1!==i.indexOf("down")&&e.length&&e.find(".jstree-anchor").removeClass(j?"jstree-clicked":"jstree-checked").parent().attr("aria-selected",!1)},this)),-1!==this.settings.checkbox.cascade.indexOf("up")&&this.element.on("delete_node.jstree",a.proxy(function(a,b){var c=this.get_node(b.parent),d=this._model.data,e,f,g,h,i=this.settings.checkbox.tie_selection;while(c&&"#"!==c.id){for(g=0,e=0,f=c.children.length;f>e;e++)g+=d[c.children[e]].state[i?"selected":"checked"];if(g!==f)break;c.state[i?"selected":"checked"]=!0,this._data[i?"core":"checkbox"].selected.push(c.id),h=this.get_node(c,!0),h&&h.length&&h.attr("aria-selected",!0).children(".jstree-anchor").addClass(i?"jstree-clicked":"jstree-checked"),c=this.get_node(c.parent)}},this)).on("move_node.jstree",a.proxy(function(b,c){var d=c.is_multi,e=c.old_parent,f=this.get_node(c.parent),g=this._model.data,h,i,j,k,l,m=this.settings.checkbox.tie_selection;if(!d){h=this.get_node(e);while(h&&"#"!==h.id){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(i!==k)break;h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),h=this.get_node(h.parent)}}h=f;while(h&&"#"!==h.id){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(i===k)h.state[m?"selected":"checked"]||(h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"));else{if(!h.state[m?"selected":"checked"])break;h.state[m?"selected":"checked"]=!1,this._data[m?"core":"checkbox"].selected=a.vakata.array_remove_item(this._data[m?"core":"checkbox"].selected,h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!1).children(".jstree-anchor").removeClass(m?"jstree-clicked":"jstree-checked")}h=this.get_node(h.parent)}},this))},this._undetermined=function(){var b,c,d=this._model.data,e=this.settings.checkbox.tie_selection,f=this._data[e?"core":"checkbox"].selected,g=[],h=this;for(b=0,c=f.length;c>b;b++)d[f[b]]&&d[f[b]].parents&&(g=g.concat(d[f[b]].parents));for(this.element.find(".jstree-closed").not(":has(.jstree-children)").each(function(){var a=h.get_node(this),e;if(a.state.loaded)for(b=0,c=a.children_d.length;c>b;b++)e=d[a.children_d[b]],!e.state.loaded&&e.original&&e.original.state&&e.original.state.undetermined&&e.original.state.undetermined===!0&&(g.push(e.id),g=g.concat(e.parents));else a.original&&a.original.state&&a.original.state.undetermined&&a.original.state.undetermined===!0&&(g.push(a.id),g=g.concat(a.parents))}),g=a.vakata.array_unique(g),g=a.vakata.array_remove_item(g,"#"),this.element.find(".jstree-undetermined").removeClass("jstree-undetermined"),b=0,c=g.length;c>b;b++)d[g[b]].state[e?"selected":"checked"]||(f=this.get_node(g[b],!0),f&&f.length&&f.children(".jstree-anchor").children(".jstree-checkbox").addClass("jstree-undetermined"))},this.redraw_node=function(b,d,e,f){if(b=c.redraw_node.apply(this,arguments)){var g,h,i=null;for(g=0,h=b.childNodes.length;h>g;g++)if(b.childNodes[g]&&b.childNodes[g].className&&-1!==b.childNodes[g].className.indexOf("jstree-anchor")){i=b.childNodes[g];break}i&&(!this.settings.checkbox.tie_selection&&this._model.data[b.id].state.checked&&(i.className+=" jstree-checked"),i.insertBefore(m.cloneNode(!1),i.childNodes[0]))}return e||-1===this.settings.checkbox.cascade.indexOf("undetermined")||(this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)),b},this.show_checkboxes=function(){this._data.core.themes.checkboxes=!0,this.get_container_ul().removeClass("jstree-no-checkboxes")},this.hide_checkboxes=function(){this._data.core.themes.checkboxes=!1,this.get_container_ul().addClass("jstree-no-checkboxes")},this.toggle_checkboxes=function(){this._data.core.themes.checkboxes?this.hide_checkboxes():this.show_checkboxes()},this.is_undetermined=function(b){b=this.get_node(b);var c=this.settings.checkbox.cascade,d,e,f=this.settings.checkbox.tie_selection,g=this._data[f?"core":"checkbox"].selected,h=this._model.data;if(!b||b.state[f?"selected":"checked"]===!0||-1===c.indexOf("undetermined")||-1===c.indexOf("down")&&-1===c.indexOf("up"))return!1;if(!b.state.loaded&&b.original.state.undetermined===!0)return!0;for(d=0,e=b.children_d.length;e>d;d++)if(-1!==a.inArray(b.children_d[d],g)||!h[b.children_d[d]].state.loaded&&h[b.children_d[d]].original.state.undetermined)return!0;return!1},this.activate_node=function(b,d){return this.settings.checkbox.tie_selection&&(this.settings.checkbox.whole_node||a(d.target).hasClass("jstree-checkbox"))&&(d.ctrlKey=!0),this.settings.checkbox.tie_selection||!this.settings.checkbox.whole_node&&!a(d.target).hasClass("jstree-checkbox")?c.activate_node.call(this,b,d):this.is_disabled(b)?!1:(this.is_checked(b)?this.uncheck_node(b,d):this.check_node(b,d),void this.trigger("activate_node",{node:this.get_node(b)}))},this.check_node=function(b,c){if(this.settings.checkbox.tie_selection)return this.select_node(b,!1,!0,c);var d,e,f,g;if(a.isArray(b)){for(b=b.slice(),e=0,f=b.length;f>e;e++)this.check_node(b[e],c);return!0}return b=this.get_node(b),b&&"#"!==b.id?(d=this.get_node(b,!0),void(b.state.checked||(b.state.checked=!0,this._data.checkbox.selected.push(b.id),d&&d.length&&d.children(".jstree-anchor").addClass("jstree-checked"),this.trigger("check_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.uncheck_node=function(b,c){if(this.settings.checkbox.tie_selection)return this.deselect_node(b,!1,c);var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.uncheck_node(b[d],c);return!0}return b=this.get_node(b),b&&"#"!==b.id?(f=this.get_node(b,!0),void(b.state.checked&&(b.state.checked=!1,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,b.id),f.length&&f.children(".jstree-anchor").removeClass("jstree-checked"),this.trigger("uncheck_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.check_all=function(){if(this.settings.checkbox.tie_selection)return this.select_all();var a=this._data.checkbox.selected.concat([]),b,c;for(this._data.checkbox.selected=this._model.data["#"].children_d.concat(),b=0,c=this._data.checkbox.selected.length;c>b;b++)this._model.data[this._data.checkbox.selected[b]]&&(this._model.data[this._data.checkbox.selected[b]].state.checked=!0);this.redraw(!0),this.trigger("check_all",{selected:this._data.checkbox.selected})},this.uncheck_all=function(){if(this.settings.checkbox.tie_selection)return this.deselect_all();var a=this._data.checkbox.selected.concat([]),b,c;for(b=0,c=this._data.checkbox.selected.length;c>b;b++)this._model.data[this._data.checkbox.selected[b]]&&(this._model.data[this._data.checkbox.selected[b]].state.checked=!1);this._data.checkbox.selected=[],this.element.find(".jstree-checked").removeClass("jstree-checked"),this.trigger("uncheck_all",{selected:this._data.checkbox.selected,node:a})},this.is_checked=function(a){return this.settings.checkbox.tie_selection?this.is_selected(a):(a=this.get_node(a),a&&"#"!==a.id?a.state.checked:!1)},this.get_checked=function(b){return this.settings.checkbox.tie_selection?this.get_selected(b):b?a.map(this._data.checkbox.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.checkbox.selected},this.get_top_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_top_selected(b);var c=this.get_checked(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},this.get_bottom_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_bottom_selected(b);var c=this.get_checked(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},this.load_node=function(b,d){var e,f,g,h,i,j;if(!a.isArray(b)&&!this.settings.checkbox.tie_selection&&(j=this.get_node(b),j&&j.state.loaded))for(e=0,f=j.children_d.length;f>e;e++)this._model.data[j.children_d[e]].state.checked&&(i=!0,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,j.children_d[e]));return c.load_node.apply(this,arguments)},this.get_state=function(){var a=c.get_state.apply(this,arguments);return this.settings.checkbox.tie_selection?a:(a.checkbox=this._data.checkbox.selected.slice(),a)},this.set_state=function(b,d){var e=c.set_state.apply(this,arguments);if(e&&b.checkbox){if(!this.settings.checkbox.tie_selection){this.uncheck_all();var f=this;a.each(b.checkbox,function(a,b){f.check_node(b)})}return delete b.checkbox,!1}return e}};var n=null,o,p;a.jstree.defaults.contextmenu={select_node:!0,show_at_node:!0,items:function(b,c){return{create:{separator_before:!1,separator_after:!0,_disabled:!1,label:"Create",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.create_node(d,{},"last",function(a){setTimeout(function(){c.edit(a)},0)})}},rename:{separator_before:!1,separator_after:!1,_disabled:!1,label:"Rename",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.edit(d)}},remove:{separator_before:!1,icon:!1,separator_after:!1,_disabled:!1,label:"Delete",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.delete_node(c.is_selected(d)?c.get_selected():d)}},ccp:{separator_before:!0,icon:!1,separator_after:!1,label:"Edit",action:!1,submenu:{cut:{separator_before:!1,separator_after:!1,label:"Cut",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.cut(c.is_selected(d)?c.get_selected():d)}},copy:{separator_before:!1,icon:!1,separator_after:!1,label:"Copy",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.copy(c.is_selected(d)?c.get_selected():d)}},paste:{separator_before:!1,icon:!1,_disabled:function(b){return!a.jstree.reference(b.reference).can_paste()},separator_after:!1,label:"Paste",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.paste(d)}}}}}}},a.jstree.plugins.contextmenu=function(c,d){this.bind=function(){d.bind.call(this);var b=0;this.element.on("contextmenu.jstree",".jstree-anchor",a.proxy(function(a,c){a.preventDefault(),b=a.ctrlKey?+new Date:0,(c||n)&&(b=+new Date+1e4),n&&clearTimeout(n),this.is_loading(a.currentTarget)||this.show_contextmenu(a.currentTarget,a.pageX,a.pageY,a)},this)).on("click.jstree",".jstree-anchor",a.proxy(function(c){this._data.contextmenu.visible&&(!b||+new Date-b>250)&&a.vakata.context.hide(),b=0},this)).on("touchstart.jstree",".jstree-anchor",function(b){b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(o=b.pageX,p=b.pageY,n=setTimeout(function(){a(b.currentTarget).trigger("contextmenu",!0)},750))}),a(document).on("context_hide.vakata.jstree",a.proxy(function(){this._data.contextmenu.visible=!1},this))},this.teardown=function(){this._data.contextmenu.visible&&a.vakata.context.hide(),d.teardown.call(this)},this.show_contextmenu=function(c,d,e,f){if(c=this.get_node(c),!c||"#"===c.id)return!1;var g=this.settings.contextmenu,h=this.get_node(c,!0),i=h.children(".jstree-anchor"),j=!1,k=!1;(g.show_at_node||d===b||e===b)&&(j=i.offset(),d=j.left,e=j.top+this._data.core.li_height),this.settings.contextmenu.select_node&&!this.is_selected(c)&&this.activate_node(c,f),k=g.items,a.isFunction(k)&&(k=k.call(this,c,a.proxy(function(a){this._show_contextmenu(c,d,e,a)},this))),a.isPlainObject(k)&&this._show_contextmenu(c,d,e,k)},this._show_contextmenu=function(b,c,d,e){var f=this.get_node(b,!0),g=f.children(".jstree-anchor");a(document).one("context_show.vakata.jstree",a.proxy(function(b,c){var d="jstree-contextmenu jstree-"+this.get_theme()+"-contextmenu";a(c.element).addClass(d)},this)),this._data.contextmenu.visible=!0,a.vakata.context.show(g,{x:c,y:d},e),this.trigger("show_contextmenu",{node:b,x:c,y:d})}},a(function(){a(document).on("touchmove.vakata.jstree",function(a){n&&a.originalEvent&&a.originalEvent.changedTouches&&a.originalEvent.changedTouches[0]&&(Math.abs(o-a.pageX)>50||Math.abs(p-a.pageY)>50)&&clearTimeout(n)}).on("touchend.vakata.jstree",function(a){n&&clearTimeout(n)})}),function(a){var b=!1,c={element:!1,reference:!1,position_x:0,position_y:0,items:[],html:"",is_visible:!1};a.vakata.context={settings:{hide_onmouseleave:0,icons:!0},_trigger:function(b){a(document).triggerHandler("context_"+b+".vakata",{reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}})},_execute:function(b){return b=c.items[b],b&&(!b._disabled||a.isFunction(b._disabled)&&!b._disabled({item:b,reference:c.reference,element:c.element}))&&b.action?b.action.call(null,{item:b,reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}}):!1},_parse:function(b,d){if(!b)return!1;d||(c.html="",c.items=[]);var e="",f=!1,g;return d&&(e+="<ul>"),a.each(b,function(b,d){return d?(c.items.push(d),!f&&d.separator_before&&(e+="<li class='vakata-context-separator'><a href='#' "+(a.vakata.context.settings.icons?"":'style="margin-left:0px;"')+">&#160;</a></li>"),f=!1,e+="<li class='"+(d._class||"")+(d._disabled===!0||a.isFunction(d._disabled)&&d._disabled({item:d,reference:c.reference,element:c.element})?" vakata-contextmenu-disabled ":"")+"' "+(d.shortcut?" data-shortcut='"+d.shortcut+"' ":"")+">",e+="<a href='#' rel='"+(c.items.length-1)+"'>",a.vakata.context.settings.icons&&(e+="<i ",d.icon&&(e+=-1!==d.icon.indexOf("/")||-1!==d.icon.indexOf(".")?" style='background:url(\""+d.icon+"\") center center no-repeat' ":" class='"+d.icon+"' "),e+="></i><span class='vakata-contextmenu-sep'>&#160;</span>"),e+=(a.isFunction(d.label)?d.label({item:b,reference:c.reference,element:c.element}):d.label)+(d.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+d.shortcut+'">'+(d.shortcut_label||"")+"</span>":"")+"</a>",d.submenu&&(g=a.vakata.context._parse(d.submenu,!0),g&&(e+=g)),e+="</li>",void(d.separator_after&&(e+="<li class='vakata-context-separator'><a href='#' "+(a.vakata.context.settings.icons?"":'style="margin-left:0px;"')+">&#160;</a></li>",f=!0))):!0}),e=e.replace(/<li class\='vakata-context-separator'\><\/li\>$/,""),d&&(e+="</ul>"),d||(c.html=e,a.vakata.context._trigger("parse")),e.length>10?e:!1},_show_submenu:function(c){if(c=a(c),c.length&&c.children("ul").length){var d=c.children("ul"),e=c.offset().left+c.outerWidth(),f=c.offset().top,g=d.width(),h=d.height(),i=a(window).width()+a(window).scrollLeft(),j=a(window).height()+a(window).scrollTop();b?c[e-(g+10+c.outerWidth())<0?"addClass":"removeClass"]("vakata-context-left"):c[e+g+10>i?"addClass":"removeClass"]("vakata-context-right"),f+h+10>j&&d.css("bottom","-1px"),d.show()}},show:function(d,e,f){var g,h,i,j,k,l,m,n,o=!0;switch(c.element&&c.element.length&&c.element.width(""),o){case!e&&!d:return!1;case!!e&&!!d:c.reference=d,c.position_x=e.x,c.position_y=e.y;break;case!e&&!!d:c.reference=d,g=d.offset(),c.position_x=g.left+d.outerHeight(),c.position_y=g.top;break;case!!e&&!d:c.position_x=e.x,c.position_y=e.y}d&&!f&&a(d).data("vakata_contextmenu")&&(f=a(d).data("vakata_contextmenu")),a.vakata.context._parse(f)&&c.element.html(c.html),c.items.length&&(c.element.appendTo("body"),h=c.element,i=c.position_x,j=c.position_y,k=h.width(),l=h.height(),m=a(window).width()+a(window).scrollLeft(),n=a(window).height()+a(window).scrollTop(),b&&(i-=h.outerWidth()-a(d).outerWidth(),i<a(window).scrollLeft()+20&&(i=a(window).scrollLeft()+20)),i+k+20>m&&(i=m-(k+20)),j+l+20>n&&(j=n-(l+20)),c.element.css({left:i,top:j}).show().find("a").first().focus().parent().addClass("vakata-context-hover"),c.is_visible=!0,a.vakata.context._trigger("show"))},hide:function(){c.is_visible&&(c.element.hide().find("ul").hide().end().find(":focus").blur().end().detach(),c.is_visible=!1,a.vakata.context._trigger("hide"))}},a(function(){b="rtl"===a("body").css("direction");var d=!1;c.element=a("<ul class='vakata-context'></ul>"),c.element.on("mouseenter","li",function(b){b.stopImmediatePropagation(),a.contains(this,b.relatedTarget)||(d&&clearTimeout(d),c.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(),a(this).siblings().find("ul").hide().end().end().parentsUntil(".vakata-context","li").addBack().addClass("vakata-context-hover"),a.vakata.context._show_submenu(this))}).on("mouseleave","li",function(b){a.contains(this,b.relatedTarget)||a(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover")}).on("mouseleave",function(b){a(this).find(".vakata-context-hover").removeClass("vakata-context-hover"),a.vakata.context.settings.hide_onmouseleave&&(d=setTimeout(function(b){return function(){a.vakata.context.hide()}}(this),a.vakata.context.settings.hide_onmouseleave))}).on("click","a",function(b){b.preventDefault(),a(this).blur().parent().hasClass("vakata-context-disabled")||a.vakata.context._execute(a(this).attr("rel"))===!1||a.vakata.context.hide()}).on("keydown","a",function(b){var d=null;switch(b.which){case 13:case 32:b.type="mouseup",b.preventDefault(),a(b.currentTarget).trigger(b);break;case 37:c.is_visible&&(c.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 38:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 39:c.is_visible&&(c.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 40:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 27:a.vakata.context.hide(),b.preventDefault()}}).on("keydown",function(a){a.preventDefault();var b=c.element.find(".vakata-contextmenu-shortcut-"+a.which).parent();b.parent().not(".vakata-context-disabled")&&b.click()}),a(document).on("mousedown.vakata.jstree",function(b){c.is_visible&&!a.contains(c.element[0],b.target)&&a.vakata.context.hide()}).on("context_show.vakata.jstree",function(a,d){c.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"),b&&c.element.addClass("vakata-context-rtl").css("direction","rtl"),c.element.find("ul").hide().end()})})}(a),a.jstree.defaults.dnd={copy:!0,open_timeout:500,is_draggable:!0,check_while_dragging:!0,always_copy:!1,inside_pos:0,drag_selection:!0,touch:!0},a.jstree.plugins.dnd=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("mousedown.jstree touchstart.jstree",".jstree-anchor",a.proxy(function(b){if("touchstart"===b.type&&(!this.settings.dnd.touch||"selected"===this.settings.dnd.touch&&!a(b.currentTarget).hasClass("jstree-clicked")))return!0;var c=this.get_node(b.target),d=this.is_selected(c)&&this.settings.drag_selection?this.get_selected().length:1,e=d>1?d+" "+this.get_string("nodes"):this.get_text(b.currentTarget);return this.settings.core.force_text&&(e=a("<div />").text(e).html()),c&&c.id&&"#"!==c.id&&(1===b.which||"touchstart"===b.type)&&(this.settings.dnd.is_draggable===!0||a.isFunction(this.settings.dnd.is_draggable)&&this.settings.dnd.is_draggable.call(this,d>1?this.get_selected(!0):[c]))?(this.element.trigger("mousedown.jstree"),a.vakata.dnd.start(b,{jstree:!0,origin:this,obj:this.get_node(c,!0),nodes:d>1?this.get_selected():[c.id]},'<div id="jstree-dnd" class="jstree-'+this.get_theme()+" jstree-"+this.get_theme()+"-"+this.get_theme_variant()+" "+(this.settings.core.themes.responsive?" jstree-dnd-responsive":"")+'"><i class="jstree-icon jstree-er"></i>'+e+'<ins class="jstree-copy" style="display:none;">+</ins></div>')):void 0},this))}},a(function(){var b=!1,c=!1,d=!1,e=a('<div id="jstree-marker">&#160;</div>').hide();a(document).on("dnd_start.vakata.jstree",function(a,c){b=!1,c&&c.data&&c.data.jstree&&e.appendTo("body")}).on("dnd_move.vakata.jstree",function(f,g){if(d&&clearTimeout(d),g&&g.data&&g.data.jstree&&(!g.event.target.id||"jstree-marker"!==g.event.target.id)){var h=a.jstree.reference(g.event.target),i=!1,j=!1,k=!1,l,m,n,o,p,q,r,s,t,u,v,w,x,y;if(h&&h._data&&h._data.dnd)if(e.attr("class","jstree-"+h.get_theme()+(h.settings.core.themes.responsive?" jstree-dnd-responsive":"")),g.helper.children().attr("class","jstree-"+h.get_theme()+" jstree-"+h.get_theme()+"-"+h.get_theme_variant()+" "+(h.settings.core.themes.responsive?" jstree-dnd-responsive":"")).find(".jstree-copy").first()[g.data.origin&&(g.data.origin.settings.dnd.always_copy||g.data.origin.settings.dnd.copy&&(g.event.metaKey||g.event.ctrlKey))?"show":"hide"](),g.event.target!==h.element[0]&&g.event.target!==h.get_container_ul()[0]||0!==h.get_container_ul().children().length){if(i=a(g.event.target).closest(".jstree-anchor"),i&&i.length&&i.parent().is(".jstree-closed, .jstree-open, .jstree-leaf")&&(j=i.offset(),k=g.event.pageY-j.top,n=i.height(),q=n/3>k?["b","i","a"]:k>n-n/3?["a","i","b"]:k>n/2?["i","a","b"]:["i","b","a"],a.each(q,function(f,k){switch(k){case"b":l=j.left-6,m=j.top,o=h.get_parent(i),p=i.parent().index();break;case"i":x=h.settings.dnd.inside_pos,y=h.get_node(i.parent()),l=j.left-2,m=j.top+n/2+1,o=y.id,p="first"===x?0:"last"===x?y.children.length:Math.min(x,y.children.length);break;case"a":l=j.left-6,m=j.top+n,o=h.get_parent(i),p=i.parent().index()+1}for(r=!0,s=0,t=g.data.nodes.length;t>s;s++)if(u=g.data.origin&&(g.data.origin.settings.dnd.always_copy||g.data.origin.settings.dnd.copy&&(g.event.metaKey||g.event.ctrlKey))?"copy_node":"move_node",v=p,"move_node"===u&&"a"===k&&g.data.origin&&g.data.origin===h&&o===h.get_parent(g.data.nodes[s])&&(w=h.get_node(o),v>a.inArray(g.data.nodes[s],w.children)&&(v-=1)),r=r&&(h&&h.settings&&h.settings.dnd&&h.settings.dnd.check_while_dragging===!1||h.check(u,g.data.origin&&g.data.origin!==h?g.data.origin.get_node(g.data.nodes[s]):g.data.nodes[s],o,v,{dnd:!0,ref:h.get_node(i.parent()),pos:k,is_multi:g.data.origin&&g.data.origin!==h,is_foreign:!g.data.origin})),!r){h&&h.last_error&&(c=h.last_error());break}return"i"===k&&i.parent().is(".jstree-closed")&&h.settings.dnd.open_timeout&&(d=setTimeout(function(a,b){return function(){a.open_node(b)}}(h,i),h.settings.dnd.open_timeout)),r?(b={ins:h,par:o,pos:"i"!==k||"last"!==x||0!==p||h.is_loaded(y)?p:"last"},e.css({left:l+"px",top:m+"px"}).show(),g.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok"),c={},q=!0,!1):void 0}),q===!0))return}else{for(r=!0,s=0,t=g.data.nodes.length;t>s;s++)if(r=r&&h.check(g.data.origin&&(g.data.origin.settings.dnd.always_copy||g.data.origin.settings.dnd.copy&&(g.event.metaKey||g.event.ctrlKey))?"copy_node":"move_node",g.data.origin&&g.data.origin!==h?g.data.origin.get_node(g.data.nodes[s]):g.data.nodes[s],"#","last",{dnd:!0,ref:h.get_node("#"),pos:"i",is_multi:g.data.origin&&g.data.origin!==h,is_foreign:!g.data.origin}),!r)break;if(r)return b={ins:h,par:"#",pos:"last"},e.hide(),void g.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok")}b=!1,g.helper.find(".jstree-icon").removeClass("jstree-ok").addClass("jstree-er"),e.hide()}}).on("dnd_scroll.vakata.jstree",function(a,c){c&&c.data&&c.data.jstree&&(e.hide(),b=!1,c.helper.find(".jstree-icon").first().removeClass("jstree-ok").addClass("jstree-er"))}).on("dnd_stop.vakata.jstree",function(f,g){if(d&&clearTimeout(d),g&&g.data&&g.data.jstree){e.hide().detach();var h,i,j=[];if(b){for(h=0,i=g.data.nodes.length;i>h;h++)j[h]=g.data.origin?g.data.origin.get_node(g.data.nodes[h]):g.data.nodes[h],g.data.origin&&(j[h].instance=g.data.origin);for(b.ins[g.data.origin&&(g.data.origin.settings.dnd.always_copy||g.data.origin.settings.dnd.copy&&(g.event.metaKey||g.event.ctrlKey))?"copy_node":"move_node"](j,b.par,b.pos),h=0,i=j.length;i>h;h++)j[h].instance&&(j[h].instance=null)}else h=a(g.event.target).closest(".jstree"),h.length&&c&&c.error&&"check"===c.error&&(h=h.jstree(!0),h&&h.settings.core.error.call(this,c))}}).on("keyup.jstree keydown.jstree",function(b,c){c=a.vakata.dnd._get(),c&&c.data&&c.data.jstree&&c.helper.find(".jstree-copy").first()[c.data.origin&&(c.data.origin.settings.dnd.always_copy||c.data.origin.settings.dnd.copy&&(b.metaKey||b.ctrlKey))?"show":"hide"]()})}),function(a){var b={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1};a.vakata.dnd={settings:{scroll_speed:10,scroll_proximity:20,helper_left:5,helper_top:10,threshold:5,threshold_touch:50},_trigger:function(b,c){var d=a.vakata.dnd._get();d.event=c,a(document).triggerHandler("dnd_"+b+".vakata",d)},_get:function(){return{data:b.data,element:b.element,helper:b.helper}},_clean:function(){b.helper&&b.helper.remove(),b.scroll_i&&(clearInterval(b.scroll_i),b.scroll_i=!1),b={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1},a(document).off("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(document).off("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop)},_scroll:function(c){if(!b.scroll_e||!b.scroll_l&&!b.scroll_t)return b.scroll_i&&(clearInterval(b.scroll_i),b.scroll_i=!1),!1;if(!b.scroll_i)return b.scroll_i=setInterval(a.vakata.dnd._scroll,100),!1;if(c===!0)return!1;var d=b.scroll_e.scrollTop(),e=b.scroll_e.scrollLeft();b.scroll_e.scrollTop(d+b.scroll_t*a.vakata.dnd.settings.scroll_speed),b.scroll_e.scrollLeft(e+b.scroll_l*a.vakata.dnd.settings.scroll_speed),(d!==b.scroll_e.scrollTop()||e!==b.scroll_e.scrollLeft())&&a.vakata.dnd._trigger("scroll",b.scroll_e)},start:function(c,d,e){"touchstart"===c.type&&c.originalEvent&&c.originalEvent.changedTouches&&c.originalEvent.changedTouches[0]&&(c.pageX=c.originalEvent.changedTouches[0].pageX,c.pageY=c.originalEvent.changedTouches[0].pageY,c.target=document.elementFromPoint(c.originalEvent.changedTouches[0].pageX-window.pageXOffset,c.originalEvent.changedTouches[0].pageY-window.pageYOffset)),b.is_drag&&a.vakata.dnd.stop({});try{c.currentTarget.unselectable="on",c.currentTarget.onselectstart=function(){return!1},c.currentTarget.style&&(c.currentTarget.style.MozUserSelect="none")}catch(f){}return b.init_x=c.pageX,b.init_y=c.pageY,b.data=d,b.is_down=!0,b.element=c.currentTarget,b.target=c.target,b.is_touch="touchstart"===c.type,e!==!1&&(b.helper=a("<div id='vakata-dnd'></div>").html(e).css({display:"block",margin:"0",padding:"0",position:"absolute",top:"-2000px",lineHeight:"16px",zIndex:"10000"})),a(document).on("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(document).on("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop),!1
-},drag:function(c){if("touchmove"===c.type&&c.originalEvent&&c.originalEvent.changedTouches&&c.originalEvent.changedTouches[0]&&(c.pageX=c.originalEvent.changedTouches[0].pageX,c.pageY=c.originalEvent.changedTouches[0].pageY,c.target=document.elementFromPoint(c.originalEvent.changedTouches[0].pageX-window.pageXOffset,c.originalEvent.changedTouches[0].pageY-window.pageYOffset)),b.is_down){if(!b.is_drag){if(!(Math.abs(c.pageX-b.init_x)>(b.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)||Math.abs(c.pageY-b.init_y)>(b.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)))return;b.helper&&(b.helper.appendTo("body"),b.helper_w=b.helper.outerWidth()),b.is_drag=!0,a.vakata.dnd._trigger("start",c)}var d=!1,e=!1,f=!1,g=!1,h=!1,i=!1,j=!1,k=!1,l=!1,m=!1;return b.scroll_t=0,b.scroll_l=0,b.scroll_e=!1,a(a(c.target).parentsUntil("body").addBack().get().reverse()).filter(function(){return/^auto|scroll$/.test(a(this).css("overflow"))&&(this.scrollHeight>this.offsetHeight||this.scrollWidth>this.offsetWidth)}).each(function(){var d=a(this),e=d.offset();return this.scrollHeight>this.offsetHeight&&(e.top+d.height()-c.pageY<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_t=1),c.pageY-e.top<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_t=-1)),this.scrollWidth>this.offsetWidth&&(e.left+d.width()-c.pageX<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_l=1),c.pageX-e.left<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_l=-1)),b.scroll_t||b.scroll_l?(b.scroll_e=a(this),!1):void 0}),b.scroll_e||(d=a(document),e=a(window),f=d.height(),g=e.height(),h=d.width(),i=e.width(),j=d.scrollTop(),k=d.scrollLeft(),f>g&&c.pageY-j<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_t=-1),f>g&&g-(c.pageY-j)<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_t=1),h>i&&c.pageX-k<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_l=-1),h>i&&i-(c.pageX-k)<a.vakata.dnd.settings.scroll_proximity&&(b.scroll_l=1),(b.scroll_t||b.scroll_l)&&(b.scroll_e=d)),b.scroll_e&&a.vakata.dnd._scroll(!0),b.helper&&(l=parseInt(c.pageY+a.vakata.dnd.settings.helper_top,10),m=parseInt(c.pageX+a.vakata.dnd.settings.helper_left,10),f&&l+25>f&&(l=f-50),h&&m+b.helper_w>h&&(m=h-(b.helper_w+2)),b.helper.css({left:m+"px",top:l+"px"})),a.vakata.dnd._trigger("move",c),!1}},stop:function(c){if("touchend"===c.type&&c.originalEvent&&c.originalEvent.changedTouches&&c.originalEvent.changedTouches[0]&&(c.pageX=c.originalEvent.changedTouches[0].pageX,c.pageY=c.originalEvent.changedTouches[0].pageY,c.target=document.elementFromPoint(c.originalEvent.changedTouches[0].pageX-window.pageXOffset,c.originalEvent.changedTouches[0].pageY-window.pageYOffset)),b.is_drag)a.vakata.dnd._trigger("stop",c);else if("touchend"===c.type&&c.target===b.target){var d=setTimeout(function(){a(c.target).click()},100);a(c.target).one("click",function(){d&&clearTimeout(d)})}return a.vakata.dnd._clean(),!1}}}(a),a.jstree.defaults.search={ajax:!1,fuzzy:!1,case_sensitive:!1,show_only_matches:!1,close_opened_onclear:!0,search_leaves_only:!1,search_callback:!1},a.jstree.plugins.search=function(c,d){this.bind=function(){d.bind.call(this),this._data.search.str="",this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=!1,this.element.on("before_open.jstree",a.proxy(function(b,c){var d,e,f,g=this._data.search.res,h=[],i=a();if(g&&g.length&&(this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(g,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.dom.children(".jstree-anchor").addClass("jstree-search"),this._data.search.som&&this._data.search.res.length)){for(d=0,e=g.length;e>d;d++)h=h.concat(this.get_node(g[d]).parents);h=a.vakata.array_remove_item(a.vakata.array_unique(h),"#"),i=h.length?a(this.element[0].querySelectorAll("#"+a.map(h,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))):a(),this.element.find(".jstree-node").hide().filter(".jstree-last").filter(function(){return this.nextSibling}).removeClass("jstree-last"),i=i.add(this._data.search.dom),i.parentsUntil(".jstree").addBack().show().filter(".jstree-children").each(function(){a(this).children(".jstree-node:visible").eq(-1).addClass("jstree-last")})}},this)).on("search.jstree",a.proxy(function(b,c){this._data.search.som&&c.nodes.length&&(this.element.find(".jstree-node").hide().filter(".jstree-last").filter(function(){return this.nextSibling}).removeClass("jstree-last"),c.nodes.parentsUntil(".jstree").addBack().show().filter(".jstree-children").each(function(){a(this).children(".jstree-node:visible").eq(-1).addClass("jstree-last")}))},this)).on("clear_search.jstree",a.proxy(function(a,b){this._data.search.som&&b.nodes.length&&this.element.find(".jstree-node").css("display","").filter(".jstree-last").filter(function(){return this.nextSibling}).removeClass("jstree-last")},this))},this.search=function(c,d,e){if(c===!1||""===a.trim(c.toString()))return this.clear_search();c=c.toString();var f=this.settings.search,g=f.ajax?f.ajax:!1,h=null,i=[],j=[],k,l;return this._data.search.res.length&&this.clear_search(),e===b&&(e=f.show_only_matches),d||g===!1?(this._data.search.str=c,this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=e,h=new a.vakata.search(c,!0,{caseSensitive:f.case_sensitive,fuzzy:f.fuzzy}),a.each(this._model.data,function(a,b){b.text&&(f.search_callback&&f.search_callback.call(this,c,b)||!f.search_callback&&h.search(b.text).isMatch)&&(!f.search_leaves_only||b.state.loaded&&0===b.children.length)&&(i.push(a),j=j.concat(b.parents))}),i.length&&(j=a.vakata.array_unique(j),this._search_open(j),this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(i,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.res=i,this._data.search.dom.children(".jstree-anchor").addClass("jstree-search")),void this.trigger("search",{nodes:this._data.search.dom,str:c,res:this._data.search.res,show_only_matches:e})):a.isFunction(g)?g.call(this,c,a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e)},!0)},this)):(g=a.extend({},g),g.data||(g.data={}),g.data.str=c,a.ajax(g).fail(a.proxy(function(){this._data.core.last_error={error:"ajax",plugin:"search",id:"search_01",reason:"Could not load search parents",data:JSON.stringify(g)},this.settings.core.error.call(this,this._data.core.last_error)},this)).done(a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e)},!0)},this)))},this.clear_search=function(){this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search"),this.settings.search.close_opened_onclear&&this.close_node(this._data.search.opn,0),this.trigger("clear_search",{nodes:this._data.search.dom,str:this._data.search.str,res:this._data.search.res}),this._data.search.str="",this._data.search.res=[],this._data.search.opn=[],this._data.search.dom=a()},this._search_open=function(b){var c=this;a.each(b.concat([]),function(d,e){if("#"===e)return!0;try{e=a("#"+e.replace(a.jstree.idregex,"\\$&"),c.element)}catch(f){}e&&e.length&&c.is_closed(e)&&(c._data.search.opn.push(e[0].id),c.open_node(e,function(){c._search_open(b)},0))})}},function(a){a.vakata.search=function(a,b,c){c=c||{},c.fuzzy!==!1&&(c.fuzzy=!0),a=c.caseSensitive?a:a.toLowerCase();var d=c.location||0,e=c.distance||100,f=c.threshold||.6,g=a.length,h,i,j,k;return g>32&&(c.fuzzy=!1),c.fuzzy&&(h=1<<g-1,i=function(){var b={},c=0;for(c=0;g>c;c++)b[a.charAt(c)]=0;for(c=0;g>c;c++)b[a.charAt(c)]|=1<<g-c-1;return b}(),j=function(a,b){var c=a/g,f=Math.abs(d-b);return e?c+f/e:f?1:c}),k=function(b){if(b=c.caseSensitive?b:b.toLowerCase(),a===b||-1!==b.indexOf(a))return{isMatch:!0,score:0};if(!c.fuzzy)return{isMatch:!1,score:1};var e,k,l=b.length,m=f,n=b.indexOf(a,d),o,p,q=g+l,r,s,t,u,v,w=1,x=[];for(-1!==n&&(m=Math.min(j(0,n),m),n=b.lastIndexOf(a,d+g),-1!==n&&(m=Math.min(j(0,n),m))),n=-1,e=0;g>e;e++){o=0,p=q;while(p>o)j(e,d+p)<=m?o=p:q=p,p=Math.floor((q-o)/2+o);for(q=p,s=Math.max(1,d-p+1),t=Math.min(d+p,l)+g,u=new Array(t+2),u[t+1]=(1<<e)-1,k=t;k>=s;k--)if(v=i[b.charAt(k-1)],u[k]=0===e?(u[k+1]<<1|1)&v:(u[k+1]<<1|1)&v|((r[k+1]|r[k])<<1|1)|r[k+1],u[k]&h&&(w=j(e,k-1),m>=w)){if(m=w,n=k-1,x.push(n),!(n>d))break;s=Math.max(1,2*d-n)}if(j(e+1,d)>m)break;r=u}return{isMatch:n>=0,score:w}},b===!0?{search:k}:k(b)}}(a),a.jstree.defaults.sort=function(a,b){return this.get_text(a)>this.get_text(b)?1:-1},a.jstree.plugins.sort=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("model.jstree",a.proxy(function(a,b){this.sort(b.parent,!0)},this)).on("rename_node.jstree create_node.jstree",a.proxy(function(a,b){this.sort(b.parent||b.node.parent,!1),this.redraw_node(b.parent||b.node.parent,!0)},this)).on("move_node.jstree copy_node.jstree",a.proxy(function(a,b){this.sort(b.parent,!1),this.redraw_node(b.parent,!0)},this))},this.sort=function(b,c){var d,e;if(b=this.get_node(b),b&&b.children&&b.children.length&&(b.children.sort(a.proxy(this.settings.sort,this)),c))for(d=0,e=b.children_d.length;e>d;d++)this.sort(b.children_d[d],!1)}};var q=!1;a.jstree.defaults.state={key:"jstree",events:"changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree",ttl:!1,filter:!1},a.jstree.plugins.state=function(b,c){this.bind=function(){c.bind.call(this);var b=a.proxy(function(){this.element.on(this.settings.state.events,a.proxy(function(){q&&clearTimeout(q),q=setTimeout(a.proxy(function(){this.save_state()},this),100)},this)),this.trigger("state_ready")},this);this.element.on("ready.jstree",a.proxy(function(a,c){this.element.one("restore_state.jstree",b),this.restore_state()||b()},this))},this.save_state=function(){var b={state:this.get_state(),ttl:this.settings.state.ttl,sec:+new Date};a.vakata.storage.set(this.settings.state.key,JSON.stringify(b))},this.restore_state=function(){var b=a.vakata.storage.get(this.settings.state.key);if(b)try{b=JSON.parse(b)}catch(c){return!1}return b&&b.ttl&&b.sec&&+new Date-b.sec>b.ttl?!1:(b&&b.state&&(b=b.state),b&&a.isFunction(this.settings.state.filter)&&(b=this.settings.state.filter.call(this,b)),b?(this.element.one("set_state.jstree",function(c,d){d.instance.trigger("restore_state",{state:a.extend(!0,{},b)})}),this.set_state(b),!0):!1)},this.clear_state=function(){return a.vakata.storage.del(this.settings.state.key)}},function(a,b){a.vakata.storage={set:function(a,b){return window.localStorage.setItem(a,b)},get:function(a){return window.localStorage.getItem(a)},del:function(a){return window.localStorage.removeItem(a)}}}(a),a.jstree.defaults.types={"#":{},"default":{}},a.jstree.plugins.types=function(c,d){this.init=function(a,c){var e,f;if(c&&c.types&&c.types["default"])for(e in c.types)if("default"!==e&&"#"!==e&&c.types.hasOwnProperty(e))for(f in c.types["default"])c.types["default"].hasOwnProperty(f)&&c.types[e][f]===b&&(c.types[e][f]=c.types["default"][f]);d.init.call(this,a,c),this._model.data["#"].type="#"},this.refresh=function(a,b){d.refresh.call(this,a,b),this._model.data["#"].type="#"},this.bind=function(){this.element.on("model.jstree",a.proxy(function(a,c){var d=this._model.data,e=c.nodes,f=this.settings.types,g,h,i="default";for(g=0,h=e.length;h>g;g++)i="default",d[e[g]].original&&d[e[g]].original.type&&f[d[e[g]].original.type]&&(i=d[e[g]].original.type),d[e[g]].data&&d[e[g]].data.jstree&&d[e[g]].data.jstree.type&&f[d[e[g]].data.jstree.type]&&(i=d[e[g]].data.jstree.type),d[e[g]].type=i,d[e[g]].icon===!0&&f[i].icon!==b&&(d[e[g]].icon=f[i].icon);d["#"].type="#"},this)),d.bind.call(this)},this.get_json=function(b,c,e){var f,g,h=this._model.data,i=c?a.extend(!0,{},c,{no_id:!1}):{},j=d.get_json.call(this,b,i,e);if(j===!1)return!1;if(a.isArray(j))for(f=0,g=j.length;g>f;f++)j[f].type=j[f].id&&h[j[f].id]&&h[j[f].id].type?h[j[f].id].type:"default",c&&c.no_id&&(delete j[f].id,j[f].li_attr&&j[f].li_attr.id&&delete j[f].li_attr.id,j[f].a_attr&&j[f].a_attr.id&&delete j[f].a_attr.id);else j.type=j.id&&h[j.id]&&h[j.id].type?h[j.id].type:"default",c&&c.no_id&&(j=this._delete_ids(j));return j},this._delete_ids=function(b){if(a.isArray(b)){for(var c=0,d=b.length;d>c;c++)b[c]=this._delete_ids(b[c]);return b}return delete b.id,b.li_attr&&b.li_attr.id&&delete b.li_attr.id,b.a_attr&&b.a_attr.id&&delete b.a_attr.id,b.children&&a.isArray(b.children)&&(b.children=this._delete_ids(b.children)),b},this.check=function(c,e,f,g,h){if(d.check.call(this,c,e,f,g,h)===!1)return!1;e=e&&e.id?e:this.get_node(e),f=f&&f.id?f:this.get_node(f);var i=e&&e.id?a.jstree.reference(e.id):null,j,k,l,m;switch(i=i&&i._model&&i._model.data?i._model.data:null,c){case"create_node":case"move_node":case"copy_node":if("move_node"!==c||-1===a.inArray(e.id,f.children)){if(j=this.get_rules(f),j.max_children!==b&&-1!==j.max_children&&j.max_children===f.children.length)return this._data.core.last_error={error:"check",plugin:"types",id:"types_01",reason:"max_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(j.valid_children!==b&&-1!==j.valid_children&&-1===a.inArray(e.type||"default",j.valid_children))return this._data.core.last_error={error:"check",plugin:"types",id:"types_02",reason:"valid_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(i&&e.children_d&&e.parents){for(k=0,l=0,m=e.children_d.length;m>l;l++)k=Math.max(k,i[e.children_d[l]].parents.length);k=k-e.parents.length+1}(0>=k||k===b)&&(k=1);do{if(j.max_depth!==b&&-1!==j.max_depth&&j.max_depth<k)return this._data.core.last_error={error:"check",plugin:"types",id:"types_03",reason:"max_depth prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;f=this.get_node(f.parent),j=this.get_rules(f),k++}while(f)}}return!0},this.get_rules=function(a){if(a=this.get_node(a),!a)return!1;var c=this.get_type(a,!0);return c.max_depth===b&&(c.max_depth=-1),c.max_children===b&&(c.max_children=-1),c.valid_children===b&&(c.valid_children=-1),c},this.get_type=function(b,c){return b=this.get_node(b),b?c?a.extend({type:b.type},this.settings.types[b.type]):b.type:!1},this.set_type=function(c,d){var e,f,g,h,i;if(a.isArray(c)){for(c=c.slice(),f=0,g=c.length;g>f;f++)this.set_type(c[f],d);return!0}return e=this.settings.types,c=this.get_node(c),e[d]&&c?(h=c.type,i=this.get_icon(c),c.type=d,(i===!0||e[h]&&e[h].icon!==b&&i===e[h].icon)&&this.set_icon(c,e[d].icon!==b?e[d].icon:!0),!0):!1}},a.jstree.defaults.unique={case_sensitive:!1,duplicate:function(a,b){return a+" ("+b+")"}},a.jstree.plugins.unique=function(c,d){this.check=function(b,c,e,f,g){if(d.check.call(this,b,c,e,f,g)===!1)return!1;if(c=c&&c.id?c:this.get_node(c),e=e&&e.id?e:this.get_node(e),!e||!e.children)return!0;var h="rename_node"===b?f:c.text,i=[],j=this.settings.unique.case_sensitive,k=this._model.data,l,m;for(l=0,m=e.children.length;m>l;l++)i.push(j?k[e.children[l]].text:k[e.children[l]].text.toLowerCase());switch(j||(h=h.toLowerCase()),b){case"delete_node":return!0;case"rename_node":return l=-1===a.inArray(h,i)||c.text&&c.text[j?"toString":"toLowerCase"]()===h,l||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_01",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),l;case"create_node":return l=-1===a.inArray(h,i),l||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_04",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),l;case"copy_node":return l=-1===a.inArray(h,i),l||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_02",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),l;case"move_node":return l=c.parent===e.id||-1===a.inArray(h,i),l||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_03",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),l}return!0},this.create_node=function(c,e,f,g,h){if(!e||e.text===b){if(null===c&&(c="#"),c=this.get_node(c),!c)return d.create_node.call(this,c,e,f,g,h);if(f=f===b?"last":f,!f.toString().match(/^(before|after)$/)&&!h&&!this.is_loaded(c))return d.create_node.call(this,c,e,f,g,h);e||(e={});var i,j,k,l,m,n=this._model.data,o=this.settings.unique.case_sensitive,p=this.settings.unique.duplicate;for(j=i=this.get_string("New node"),k=[],l=0,m=c.children.length;m>l;l++)k.push(o?n[c.children[l]].text:n[c.children[l]].text.toLowerCase());l=1;while(-1!==a.inArray(o?j:j.toLowerCase(),k))j=p.call(this,i,++l).toString();e.text=j}return d.create_node.call(this,c,e,f,g,h)}};var r=document.createElement("DIV");r.setAttribute("unselectable","on"),r.setAttribute("role","presentation"),r.className="jstree-wholerow",r.innerHTML="&#160;",a.jstree.plugins.wholerow=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("ready.jstree set_state.jstree",a.proxy(function(){this.hide_dots()},this)).on("init.jstree loading.jstree ready.jstree",a.proxy(function(){this.get_container_ul().addClass("jstree-wholerow-ul")},this)).on("deselect_all.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked")},this)).on("changed.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked");var c=!1,d,e;for(d=0,e=b.selected.length;e>d;d++)c=this.get_node(b.selected[d],!0),c&&c.length&&c.children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("open_node.jstree",a.proxy(function(a,b){this.get_node(b.node,!0).find(".jstree-clicked").parent().children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("hover_node.jstree dehover_node.jstree",a.proxy(function(a,b){"hover_node"===a.type&&this.is_disabled(b.node)||this.get_node(b.node,!0).children(".jstree-wholerow")["hover_node"===a.type?"addClass":"removeClass"]("jstree-wholerow-hovered")},this)).on("contextmenu.jstree",".jstree-wholerow",a.proxy(function(b){b.preventDefault();var c=a.Event("contextmenu",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey,pageX:b.pageX,pageY:b.pageY});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c)},this)).on("click.jstree",".jstree-wholerow",function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()}).on("click.jstree",".jstree-leaf > .jstree-ocl",a.proxy(function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()},this)).on("mouseover.jstree",".jstree-wholerow, .jstree-icon",a.proxy(function(a){return a.stopImmediatePropagation(),this.is_disabled(a.currentTarget)||this.hover_node(a.currentTarget),!1},this)).on("mouseleave.jstree",".jstree-node",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},this.teardown=function(){this.settings.wholerow&&this.element.find(".jstree-wholerow").remove(),c.teardown.call(this)},this.redraw_node=function(b,d,e,f){if(b=c.redraw_node.apply(this,arguments)){var g=r.cloneNode(!0);-1!==a.inArray(b.id,this._data.core.selected)&&(g.className+=" jstree-wholerow-clicked"),this._data.core.focused&&this._data.core.focused===b.id&&(g.className+=" jstree-wholerow-hovered"),b.insertBefore(g,b.childNodes[0])}return b}},function(a){if(document.registerElement&&Object&&Object.create){var b=Object.create(HTMLElement.prototype);b.createdCallback=function(){var b={core:{},plugins:[]},c;for(c in a.jstree.plugins)a.jstree.plugins.hasOwnProperty(c)&&this.attributes[c]&&(b.plugins.push(c),this.getAttribute(c)&&JSON.parse(this.getAttribute(c))&&(b[c]=JSON.parse(this.getAttribute(c))));for(c in a.jstree.defaults.core)a.jstree.defaults.core.hasOwnProperty(c)&&this.attributes[c]&&(b.core[c]=JSON.parse(this.getAttribute(c))||this.getAttribute(c));jQuery(this).jstree(b)};try{document.registerElement("vakata-jstree",{prototype:b})}catch(c){}}}(jQuery)}});
\ No newline at end of file
diff --git a/apps/static/js/plugins/ladda/ladda.jquery.min.js b/apps/static/js/plugins/ladda/ladda.jquery.min.js
deleted file mode 100755
index 74fb3ae03..000000000
--- a/apps/static/js/plugins/ladda/ladda.jquery.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * Ladda for jQuery
- * http://lab.hakim.se/ladda
- * MIT licensed
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-!function(a,b){if(void 0===b)return console.error("jQuery required for Ladda.jQuery");var c=[];b=b.extend(b,{ladda:function(b){"stopAll"===b&&a.stopAll()}}),b.fn=b.extend(b.fn,{ladda:function(d){var e=c.slice.call(arguments,1);return"bind"===d?(e.unshift(b(this).selector),a.bind.apply(a,e)):b(this).each(function(){var c,f=b(this);void 0===d?f.data("ladda",a.create(this)):(c=f.data("ladda"),c[d].apply(c,e))}),this}})}(this.Ladda,this.jQuery);
\ No newline at end of file
diff --git a/apps/static/js/plugins/ladda/ladda.min.js b/apps/static/js/plugins/ladda/ladda.min.js
deleted file mode 100755
index f7f81ec74..000000000
--- a/apps/static/js/plugins/ladda/ladda.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * Ladda 1.0.0 (2016-03-08, 09:31)
- * http://lab.hakim.se/ladda
- * MIT licensed
- *
- * Copyright (C) 2016 Hakim El Hattab, http://hakim.se
- */
-!function(a,b){"object"==typeof exports?module.exports=b(require("spin.js")):"function"==typeof define&&define.amd?define(["spin"],b):a.Ladda=b(a.Spinner)}(this,function(a){"use strict";function b(a){if("undefined"==typeof a)return void console.warn("Ladda button target must be defined.");if(/ladda-button/i.test(a.className)||(a.className+=" ladda-button"),a.hasAttribute("data-style")||a.setAttribute("data-style","expand-right"),!a.querySelector(".ladda-label")){var b=document.createElement("span");b.className="ladda-label",i(a,b)}var c,d=a.querySelector(".ladda-spinner");d||(d=document.createElement("span"),d.className="ladda-spinner"),a.appendChild(d);var e,f={start:function(){return c||(c=g(a)),a.setAttribute("disabled",""),a.setAttribute("data-loading",""),clearTimeout(e),c.spin(d),this.setProgress(0),this},startAfter:function(a){return clearTimeout(e),e=setTimeout(function(){f.start()},a),this},stop:function(){return a.removeAttribute("disabled"),a.removeAttribute("data-loading"),clearTimeout(e),c&&(e=setTimeout(function(){c.stop()},1e3)),this},toggle:function(){return this.isLoading()?this.stop():this.start(),this},setProgress:function(b){b=Math.max(Math.min(b,1),0);var c=a.querySelector(".ladda-progress");0===b&&c&&c.parentNode?c.parentNode.removeChild(c):(c||(c=document.createElement("div"),c.className="ladda-progress",a.appendChild(c)),c.style.width=(b||0)*a.offsetWidth+"px")},enable:function(){return this.stop(),this},disable:function(){return this.stop(),a.setAttribute("disabled",""),this},isLoading:function(){return a.hasAttribute("data-loading")},remove:function(){clearTimeout(e),a.removeAttribute("disabled",""),a.removeAttribute("data-loading",""),c&&(c.stop(),c=null);for(var b=0,d=j.length;d>b;b++)if(f===j[b]){j.splice(b,1);break}}};return j.push(f),f}function c(a,b){for(;a.parentNode&&a.tagName!==b;)a=a.parentNode;return b===a.tagName?a:void 0}function d(a){for(var b=["input","textarea","select"],c=[],d=0;d<b.length;d++)for(var e=a.getElementsByTagName(b[d]),f=0;f<e.length;f++)e[f].hasAttribute("required")&&c.push(e[f]);return c}function e(a,e){e=e||{};var f=[];"string"==typeof a?f=h(document.querySelectorAll(a)):"object"==typeof a&&"string"==typeof a.nodeName&&(f=[a]);for(var g=0,i=f.length;i>g;g++)!function(){var a=f[g];if("function"==typeof a.addEventListener){var h=b(a),i=-1;a.addEventListener("click",function(b){var f=!0,g=c(a,"FORM");if("undefined"!=typeof g)if("function"==typeof g.checkValidity)f=g.checkValidity();else for(var j=d(g),k=0;k<j.length;k++)""===j[k].value.replace(/^\s+|\s+$/g,"")&&(f=!1),"checkbox"!==j[k].type&&"radio"!==j[k].type||j[k].checked||(f=!1),"email"===j[k].type&&(f=/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(j[k].value));f&&(h.startAfter(1),"number"==typeof e.timeout&&(clearTimeout(i),i=setTimeout(h.stop,e.timeout)),"function"==typeof e.callback&&e.callback.apply(null,[h]))},!1)}}()}function f(){for(var a=0,b=j.length;b>a;a++)j[a].stop()}function g(b){var c,d,e=b.offsetHeight;0===e&&(e=parseFloat(window.getComputedStyle(b).height)),e>32&&(e*=.8),b.hasAttribute("data-spinner-size")&&(e=parseInt(b.getAttribute("data-spinner-size"),10)),b.hasAttribute("data-spinner-color")&&(c=b.getAttribute("data-spinner-color")),b.hasAttribute("data-spinner-lines")&&(d=parseInt(b.getAttribute("data-spinner-lines"),10));var f=.2*e,g=.6*f,h=7>f?2:3;return new a({color:c||"#fff",lines:d||12,radius:f,length:g,width:h,zIndex:"auto",top:"auto",left:"auto",className:""})}function h(a){for(var b=[],c=0;c<a.length;c++)b.push(a[c]);return b}function i(a,b){var c=document.createRange();c.selectNodeContents(a),c.surroundContents(b),a.appendChild(b)}var j=[];return{bind:e,create:b,stopAll:f}});
\ No newline at end of file
diff --git a/apps/static/js/plugins/ladda/spin.min.js b/apps/static/js/plugins/ladda/spin.min.js
deleted file mode 100755
index 4415f5b61..000000000
--- a/apps/static/js/plugins/ladda/spin.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return l[e]||(m.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",m.cssRules.length),l[e]=1),e}function d(a,b){var c,d,e=a.style;for(b=b.charAt(0).toUpperCase()+b.slice(1),d=0;d<k.length;d++)if(c=k[d]+b,void 0!==e[c])return c;return void 0!==e[b]?b:void 0}function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)void 0===a[d]&&(a[d]=c[d])}return a}function g(a,b){return"string"==typeof a?a:a[b%a.length]}function h(a){this.opts=f(a||{},h.defaults,n)}function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',c)}m.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.width,left:d.radius,top:-d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.length+d.width,k=2*j,l=2*-(d.width+d.length)+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}}var j,k=["webkit","Moz","ms","O"],l={},m=function(){var c=a("style",{type:"text/css"});return b(document.getElementsByTagName("head")[0],c),c.sheet||c.styleSheet}(),n={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",direction:1,speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"50%",left:"50%",position:"absolute"};h.defaults={},f(h.prototype,{spin:function(b){this.stop();var c=this,d=c.opts,f=c.el=e(a(0,{className:d.className}),{position:d.position,width:0,zIndex:d.zIndex});d.radius+d.length+d.width;if(e(f,{left:d.left,top:d.top}),b&&b.insertBefore(f,b.firstChild||null),f.setAttribute("role","progressbar"),c.lines(f,c.opts),!j){var g,h=0,i=(d.lines-1)*(1-d.direction)/2,k=d.fps,l=k/d.speed,m=(1-d.opacity)/(l*d.trail/100),n=l/d.lines;!function o(){h++;for(var a=0;a<d.lines;a++)g=Math.max(1-(h+(d.lines-a)*n)%l*m,d.opacity),c.opacity(f,a*d.direction+i,g,d);c.timeout=c.el&&setTimeout(o,~~(1e3/k))}()}return c},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=void 0),this},lines:function(d,f){function h(b,c){return e(a(),{position:"absolute",width:f.length+f.width+"px",height:f.width+"px",background:b,boxShadow:c,transformOrigin:"left",transform:"rotate("+~~(360/f.lines*k+f.rotate)+"deg) translate("+f.radius+"px,0)",borderRadius:(f.corners*f.width>>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k<f.lines;k++)i=e(a(),{position:"absolute",top:1+~(f.width/2)+"px",transform:f.hwaccel?"translate3d(0,0,0)":"",opacity:f.opacity,animation:j&&c(f.opacity,f.trail,l+k*f.direction,f.lines)+" "+1/f.speed+"s linear infinite"}),f.shadow&&b(i,e(h("#000","0 0 4px #000"),{top:"2px"})),b(d,b(i,h(g(f.color,k),"0 0 1px rgba(0,0,0,.1)")));return d},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}});var o=e(a("group"),{behavior:"url(#default#VML)"});return!d(o,"transform")&&o.adj?i():j=d(o,"animation"),h});
\ No newline at end of file
diff --git a/apps/static/js/plugins/layer/layer.js b/apps/static/js/plugins/layer/layer.js
deleted file mode 100644
index cb1945622..000000000
--- a/apps/static/js/plugins/layer/layer.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! layer-v2.4 弹层组件 License LGPL  http://layer.layui.com/ By 贤心 */
-;!function(a,b){"use strict";var c,d,e={getPath:function(){var a=document.scripts,b=a[a.length-1],c=b.src;if(!b.getAttribute("merge"))return c.substring(0,c.lastIndexOf("/")+1)}(),enter:function(a){13===a.keyCode&&a.preventDefault()},config:{},end:{},btn:["&#x786E;&#x5B9A;","&#x53D6;&#x6D88;"],type:["dialog","page","iframe","loading","tips"]},f={v:"2.4",ie6:!!a.ActiveXObject&&!a.XMLHttpRequest,index:0,path:e.getPath,config:function(a,b){var d=0;return a=a||{},f.cache=e.config=c.extend(e.config,a),f.path=e.config.path||f.path,"string"==typeof a.extend&&(a.extend=[a.extend]),f.use("skin/layer.css",a.extend&&a.extend.length>0?function g(){var c=a.extend;f.use(c[c[d]?d:d-1],d<c.length?function(){return++d,g}():b)}():b),this},use:function(a,b,d){var e=c("head")[0],a=a.replace(/\s/g,""),g=/\.css$/.test(a),h=document.createElement(g?"link":"script"),i="layui_layer_"+a.replace(/\.|\//g,"");return f.path?(g&&(h.rel="stylesheet"),h[g?"href":"src"]=/^http:\/\//.test(a)?a:f.path+a,h.id=i,c("#"+i)[0]||e.appendChild(h),function j(){(g?1989===parseInt(c("#"+i).css("width")):f[d||i])?function(){b&&b();try{g||e.removeChild(h)}catch(a){}}():setTimeout(j,100)}(),this):void 0},ready:function(a,b){var d="function"==typeof a;return d&&(b=a),f.config(c.extend(e.config,function(){return d?{}:{path:a}}()),b),this},alert:function(a,b,d){var e="function"==typeof b;return e&&(d=b),f.open(c.extend({content:a,yes:d},e?{}:b))},confirm:function(a,b,d,g){var h="function"==typeof b;return h&&(g=d,d=b),f.open(c.extend({content:a,btn:e.btn,yes:d,btn2:g},h?{}:b))},msg:function(a,d,g){var i="function"==typeof d,j=e.config.skin,k=(j?j+" "+j+"-msg":"")||"layui-layer-msg",l=h.anim.length-1;return i&&(g=d),f.open(c.extend({content:a,time:3e3,shade:!1,skin:k,title:!1,closeBtn:!1,btn:!1,end:g},i&&!e.config.skin?{skin:k+" layui-layer-hui",shift:l}:function(){return d=d||{},(-1===d.icon||d.icon===b&&!e.config.skin)&&(d.skin=k+" "+(d.skin||"layui-layer-hui")),d}()))},load:function(a,b){return f.open(c.extend({type:3,icon:a||0,shade:.01},b))},tips:function(a,b,d){return f.open(c.extend({type:4,content:[a,b],closeBtn:!1,time:3e3,shade:!1,fix:!1,maxWidth:210},d))}},g=function(a){var b=this;b.index=++f.index,b.config=c.extend({},b.config,e.config,a),b.creat()};g.pt=g.prototype;var h=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];h.anim=["layer-anim","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],g.pt.config={type:0,shade:.3,fix:!0,move:h[1],title:"&#x4FE1;&#x606F;",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,shift:0,icon:-1,scrollbar:!0,tips:2},g.pt.vessel=function(a,b){var c=this,d=c.index,f=c.config,g=f.zIndex+d,i="object"==typeof f.title,j=f.maxmin&&(1===f.type||2===f.type),k=f.title?'<div class="layui-layer-title" style="'+(i?f.title[1]:"")+'">'+(i?f.title[0]:f.title)+"</div>":"";return f.zIndex=g,b([f.shade?'<div class="layui-layer-shade" id="layui-layer-shade'+d+'" times="'+d+'" style="'+("z-index:"+(g-1)+"; background-color:"+(f.shade[1]||"#000")+"; opacity:"+(f.shade[0]||f.shade)+"; filter:alpha(opacity="+(100*f.shade[0]||100*f.shade)+");")+'"></div>':"",'<div class="'+h[0]+(" layui-layer-"+e.type[f.type])+(0!=f.type&&2!=f.type||f.shade?"":" layui-layer-border")+" "+(f.skin||"")+'" id="'+h[0]+d+'" type="'+e.type[f.type]+'" times="'+d+'" showtime="'+f.time+'" conType="'+(a?"object":"string")+'" style="z-index: '+g+"; width:"+f.area[0]+";height:"+f.area[1]+(f.fix?"":";position:absolute;")+'">'+(a&&2!=f.type?"":k)+'<div id="'+(f.id||"")+'" class="layui-layer-content'+(0==f.type&&-1!==f.icon?" layui-layer-padding":"")+(3==f.type?" layui-layer-loading"+f.icon:"")+'">'+(0==f.type&&-1!==f.icon?'<i class="layui-layer-ico layui-layer-ico'+f.icon+'"></i>':"")+(1==f.type&&a?"":f.content||"")+'</div><span class="layui-layer-setwin">'+function(){var a=j?'<a class="layui-layer-min" href="javascript:;"><cite></cite></a><a class="layui-layer-ico layui-layer-max" href="javascript:;"></a>':"";return f.closeBtn&&(a+='<a class="layui-layer-ico '+h[7]+" "+h[7]+(f.title?f.closeBtn:4==f.type?"1":"2")+'" href="javascript:;"></a>'),a}()+"</span>"+(f.btn?function(){var a="";"string"==typeof f.btn&&(f.btn=[f.btn]);for(var b=0,c=f.btn.length;c>b;b++)a+='<a class="'+h[6]+b+'">'+f.btn[b]+"</a>";return'<div class="'+h[6]+'">'+a+"</div>"}():"")+"</div>"],k),c},g.pt.creat=function(){var a=this,b=a.config,g=a.index,i=b.content,j="object"==typeof i;if(!c("#"+b.id)[0]){switch("string"==typeof b.area&&(b.area="auto"===b.area?["",""]:[b.area,""]),b.type){case 0:b.btn="btn"in b?b.btn:e.btn[0],f.closeAll("dialog");break;case 2:var i=b.content=j?b.content:[b.content||"http://layer.layui.com","auto"];b.content='<iframe scrolling="'+(b.content[1]||"auto")+'" allowtransparency="true" id="'+h[4]+g+'" name="'+h[4]+g+'" onload="this.className=\'\';" class="layui-layer-load" frameborder="0" src="'+b.content[0]+'"></iframe>';break;case 3:b.title=!1,b.closeBtn=!1,-1===b.icon&&0===b.icon,f.closeAll("loading");break;case 4:j||(b.content=[b.content,"body"]),b.follow=b.content[1],b.content=b.content[0]+'<i class="layui-layer-TipsG"></i>',b.title=!1,b.tips="object"==typeof b.tips?b.tips:[b.tips,!0],b.tipsMore||f.closeAll("tips")}a.vessel(j,function(d,e){c("body").append(d[0]),j?function(){2==b.type||4==b.type?function(){c("body").append(d[1])}():function(){i.parents("."+h[0])[0]||(i.show().addClass("layui-layer-wrap").wrap(d[1]),c("#"+h[0]+g).find("."+h[5]).before(e))}()}():c("body").append(d[1]),a.layero=c("#"+h[0]+g),b.scrollbar||h.html.css("overflow","hidden").attr("layer-full",g)}).auto(g),2==b.type&&f.ie6&&a.layero.find("iframe").attr("src",i[0]),c(document).off("keydown",e.enter).on("keydown",e.enter),a.layero.on("keydown",function(a){c(document).off("keydown",e.enter)}),4==b.type?a.tips():a.offset(),b.fix&&d.on("resize",function(){a.offset(),(/^\d+%$/.test(b.area[0])||/^\d+%$/.test(b.area[1]))&&a.auto(g),4==b.type&&a.tips()}),b.time<=0||setTimeout(function(){f.close(a.index)},b.time),a.move().callback(),h.anim[b.shift]&&a.layero.addClass(h.anim[b.shift])}},g.pt.auto=function(a){function b(a){a=g.find(a),a.height(i[1]-j-k-2*(0|parseFloat(a.css("padding"))))}var e=this,f=e.config,g=c("#"+h[0]+a);""===f.area[0]&&f.maxWidth>0&&(/MSIE 7/.test(navigator.userAgent)&&f.btn&&g.width(g.innerWidth()),g.outerWidth()>f.maxWidth&&g.width(f.maxWidth));var i=[g.innerWidth(),g.innerHeight()],j=g.find(h[1]).outerHeight()||0,k=g.find("."+h[6]).outerHeight()||0;switch(f.type){case 2:b("iframe");break;default:""===f.area[1]?f.fix&&i[1]>=d.height()&&(i[1]=d.height(),b("."+h[5])):b("."+h[5])}return e},g.pt.offset=function(){var a=this,b=a.config,c=a.layero,e=[c.outerWidth(),c.outerHeight()],f="object"==typeof b.offset;a.offsetTop=(d.height()-e[1])/2,a.offsetLeft=(d.width()-e[0])/2,f?(a.offsetTop=b.offset[0],a.offsetLeft=b.offset[1]||a.offsetLeft):"auto"!==b.offset&&(a.offsetTop=b.offset,"rb"===b.offset&&(a.offsetTop=d.height()-e[1],a.offsetLeft=d.width()-e[0])),b.fix||(a.offsetTop=/%$/.test(a.offsetTop)?d.height()*parseFloat(a.offsetTop)/100:parseFloat(a.offsetTop),a.offsetLeft=/%$/.test(a.offsetLeft)?d.width()*parseFloat(a.offsetLeft)/100:parseFloat(a.offsetLeft),a.offsetTop+=d.scrollTop(),a.offsetLeft+=d.scrollLeft()),c.css({top:a.offsetTop,left:a.offsetLeft})},g.pt.tips=function(){var a=this,b=a.config,e=a.layero,f=[e.outerWidth(),e.outerHeight()],g=c(b.follow);g[0]||(g=c("body"));var i={width:g.outerWidth(),height:g.outerHeight(),top:g.offset().top,left:g.offset().left},j=e.find(".layui-layer-TipsG"),k=b.tips[0];b.tips[1]||j.remove(),i.autoLeft=function(){i.left+f[0]-d.width()>0?(i.tipLeft=i.left+i.width-f[0],j.css({right:12,left:"auto"})):i.tipLeft=i.left},i.where=[function(){i.autoLeft(),i.tipTop=i.top-f[1]-10,j.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",b.tips[1])},function(){i.tipLeft=i.left+i.width+10,i.tipTop=i.top,j.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",b.tips[1])},function(){i.autoLeft(),i.tipTop=i.top+i.height+10,j.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",b.tips[1])},function(){i.tipLeft=i.left-f[0]-10,i.tipTop=i.top,j.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",b.tips[1])}],i.where[k-1](),1===k?i.top-(d.scrollTop()+f[1]+16)<0&&i.where[2]():2===k?d.width()-(i.left+i.width+f[0]+16)>0||i.where[3]():3===k?i.top-d.scrollTop()+i.height+f[1]+16-d.height()>0&&i.where[0]():4===k&&f[0]+16-i.left>0&&i.where[1](),e.find("."+h[5]).css({"background-color":b.tips[1],"padding-right":b.closeBtn?"30px":""}),e.css({left:i.tipLeft-(b.fix?d.scrollLeft():0),top:i.tipTop-(b.fix?d.scrollTop():0)})},g.pt.move=function(){var a=this,b=a.config,e={setY:0,moveLayer:function(){var a=e.layero,b=parseInt(a.css("margin-left")),c=parseInt(e.move.css("left"));0===b||(c-=b),"fixed"!==a.css("position")&&(c-=a.parent().offset().left,e.setY=0),a.css({left:c,top:parseInt(e.move.css("top"))-e.setY})}},f=a.layero.find(b.move);return b.move&&f.attr("move","ok"),f.css({cursor:b.move?"move":"auto"}),c(b.move).on("mousedown",function(a){if(a.preventDefault(),"ok"===c(this).attr("move")){e.ismove=!0,e.layero=c(this).parents("."+h[0]);var f=e.layero.offset().left,g=e.layero.offset().top,i=e.layero.outerWidth()-6,j=e.layero.outerHeight()-6;c("#layui-layer-moves")[0]||c("body").append('<div id="layui-layer-moves" class="layui-layer-moves" style="left:'+f+"px; top:"+g+"px; width:"+i+"px; height:"+j+'px; z-index:2147483584"></div>'),e.move=c("#layui-layer-moves"),b.moveType&&e.move.css({visibility:"hidden"}),e.moveX=a.pageX-e.move.position().left,e.moveY=a.pageY-e.move.position().top,"fixed"!==e.layero.css("position")||(e.setY=d.scrollTop())}}),c(document).mousemove(function(a){if(e.ismove){var c=a.pageX-e.moveX,f=a.pageY-e.moveY;if(a.preventDefault(),!b.moveOut){e.setY=d.scrollTop();var g=d.width()-e.move.outerWidth(),h=e.setY;0>c&&(c=0),c>g&&(c=g),h>f&&(f=h),f>d.height()-e.move.outerHeight()+e.setY&&(f=d.height()-e.move.outerHeight()+e.setY)}e.move.css({left:c,top:f}),b.moveType&&e.moveLayer(),c=f=g=h=null}}).mouseup(function(){try{e.ismove&&(e.moveLayer(),e.move.remove(),b.moveEnd&&b.moveEnd()),e.ismove=!1}catch(a){e.ismove=!1}}),a},g.pt.callback=function(){function a(){var a=g.cancel&&g.cancel(b.index,d);a===!1||f.close(b.index)}var b=this,d=b.layero,g=b.config;b.openLayer(),g.success&&(2==g.type?d.find("iframe").on("load",function(){g.success(d,b.index)}):g.success(d,b.index)),f.ie6&&b.IE6(d),d.find("."+h[6]).children("a").on("click",function(){var a=c(this).index();if(0===a)g.yes?g.yes(b.index,d):g.btn1?g.btn1(b.index,d):f.close(b.index);else{var e=g["btn"+(a+1)]&&g["btn"+(a+1)](b.index,d);e===!1||f.close(b.index)}}),d.find("."+h[7]).on("click",a),g.shadeClose&&c("#layui-layer-shade"+b.index).on("click",function(){f.close(b.index)}),d.find(".layui-layer-min").on("click",function(){var a=g.min&&g.min(d);a===!1||f.min(b.index,g)}),d.find(".layui-layer-max").on("click",function(){c(this).hasClass("layui-layer-maxmin")?(f.restore(b.index),g.restore&&g.restore(d)):(f.full(b.index,g),setTimeout(function(){g.full&&g.full(d)},100))}),g.end&&(e.end[b.index]=g.end)},e.reselect=function(){c.each(c("select"),function(a,b){var d=c(this);d.parents("."+h[0])[0]||1==d.attr("layer")&&c("."+h[0]).length<1&&d.removeAttr("layer").show(),d=null})},g.pt.IE6=function(a){function b(){a.css({top:f+(e.config.fix?d.scrollTop():0)})}var e=this,f=a.offset().top;b(),d.scroll(b),c("select").each(function(a,b){var d=c(this);d.parents("."+h[0])[0]||"none"===d.css("display")||d.attr({layer:"1"}).hide(),d=null})},g.pt.openLayer=function(){var a=this;f.zIndex=a.config.zIndex,f.setTop=function(a){var b=function(){f.zIndex++,a.css("z-index",f.zIndex+1)};return f.zIndex=parseInt(a[0].style.zIndex),a.on("mousedown",b),f.zIndex}},e.record=function(a){var b=[a.width(),a.height(),a.position().top,a.position().left+parseFloat(a.css("margin-left"))];a.find(".layui-layer-max").addClass("layui-layer-maxmin"),a.attr({area:b})},e.rescollbar=function(a){h.html.attr("layer-full")==a&&(h.html[0].style.removeProperty?h.html[0].style.removeProperty("overflow"):h.html[0].style.removeAttribute("overflow"),h.html.removeAttr("layer-full"))},a.layer=f,f.getChildFrame=function(a,b){return b=b||c("."+h[4]).attr("times"),c("#"+h[0]+b).find("iframe").contents().find(a)},f.getFrameIndex=function(a){return c("#"+a).parents("."+h[4]).attr("times")},f.iframeAuto=function(a){if(a){var b=f.getChildFrame("html",a).outerHeight(),d=c("#"+h[0]+a),e=d.find(h[1]).outerHeight()||0,g=d.find("."+h[6]).outerHeight()||0;d.css({height:b+e+g}),d.find("iframe").css({height:b})}},f.iframeSrc=function(a,b){c("#"+h[0]+a).find("iframe").attr("src",b)},f.style=function(a,b){var d=c("#"+h[0]+a),f=d.attr("type"),g=d.find(h[1]).outerHeight()||0,i=d.find("."+h[6]).outerHeight()||0;(f===e.type[1]||f===e.type[2])&&(d.css(b),f===e.type[2]&&d.find("iframe").css({height:parseFloat(b.height)-g-i}))},f.min=function(a,b){var d=c("#"+h[0]+a),g=d.find(h[1]).outerHeight()||0;e.record(d),f.style(a,{width:180,height:g,overflow:"hidden"}),d.find(".layui-layer-min").hide(),"page"===d.attr("type")&&d.find(h[4]).hide(),e.rescollbar(a)},f.restore=function(a){var b=c("#"+h[0]+a),d=b.attr("area").split(",");b.attr("type");f.style(a,{width:parseFloat(d[0]),height:parseFloat(d[1]),top:parseFloat(d[2]),left:parseFloat(d[3]),overflow:"visible"}),b.find(".layui-layer-max").removeClass("layui-layer-maxmin"),b.find(".layui-layer-min").show(),"page"===b.attr("type")&&b.find(h[4]).show(),e.rescollbar(a)},f.full=function(a){var b,g=c("#"+h[0]+a);e.record(g),h.html.attr("layer-full")||h.html.css("overflow","hidden").attr("layer-full",a),clearTimeout(b),b=setTimeout(function(){var b="fixed"===g.css("position");f.style(a,{top:b?0:d.scrollTop(),left:b?0:d.scrollLeft(),width:d.width(),height:d.height()}),g.find(".layui-layer-min").hide()},100)},f.title=function(a,b){var d=c("#"+h[0]+(b||f.index)).find(h[1]);d.html(a)},f.close=function(a){var b=c("#"+h[0]+a),d=b.attr("type");if(b[0]){if(d===e.type[1]&&"object"===b.attr("conType")){b.children(":not(."+h[5]+")").remove();for(var g=0;2>g;g++)b.find(".layui-layer-wrap").unwrap().hide()}else{if(d===e.type[2])try{var i=c("#"+h[4]+a)[0];i.contentWindow.document.write(""),i.contentWindow.close(),b.find("."+h[5])[0].removeChild(i)}catch(j){}b[0].innerHTML="",b.remove()}c("#layui-layer-moves, #layui-layer-shade"+a).remove(),f.ie6&&e.reselect(),e.rescollbar(a),c(document).off("keydown",e.enter),"function"==typeof e.end[a]&&e.end[a](),delete e.end[a]}},f.closeAll=function(a){c.each(c("."+h[0]),function(){var b=c(this),d=a?b.attr("type")===a:1;d&&f.close(b.attr("times")),d=null})};var i=f.cache||{},j=function(a){return i.skin?" "+i.skin+" "+i.skin+"-"+a:""};f.prompt=function(a,b){a=a||{},"function"==typeof a&&(b=a);var d,e=2==a.formType?'<textarea class="layui-layer-input">'+(a.value||"")+"</textarea>":function(){return'<input type="'+(1==a.formType?"password":"text")+'" class="layui-layer-input" value="'+(a.value||"")+'">'}();return f.open(c.extend({btn:["&#x786E;&#x5B9A;","&#x53D6;&#x6D88;"],content:e,skin:"layui-layer-prompt"+j("prompt"),success:function(a){d=a.find(".layui-layer-input"),d.focus()},yes:function(c){var e=d.val();""===e?d.focus():e.length>(a.maxlength||500)?f.tips("&#x6700;&#x591A;&#x8F93;&#x5165;"+(a.maxlength||500)+"&#x4E2A;&#x5B57;&#x6570;",d,{tips:1}):b&&b(e,c,d)}},a))},f.tab=function(a){a=a||{};var b=a.tab||{};return f.open(c.extend({type:1,skin:"layui-layer-tab"+j("tab"),title:function(){var a=b.length,c=1,d="";if(a>0)for(d='<span class="layui-layer-tabnow">'+b[0].title+"</span>";a>c;c++)d+="<span>"+b[c].title+"</span>";return d}(),content:'<ul class="layui-layer-tabmain">'+function(){var a=b.length,c=1,d="";if(a>0)for(d='<li class="layui-layer-tabli xubox_tab_layer">'+(b[0].content||"no content")+"</li>";a>c;c++)d+='<li class="layui-layer-tabli">'+(b[c].content||"no  content")+"</li>";return d}()+"</ul>",success:function(b){var d=b.find(".layui-layer-title").children(),e=b.find(".layui-layer-tabmain").children();d.on("mousedown",function(b){b.stopPropagation?b.stopPropagation():b.cancelBubble=!0;var d=c(this),f=d.index();d.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),e.eq(f).show().siblings().hide(),"function"==typeof a.change&&a.change(f)})}},a))},f.photos=function(b,d,e){function g(a,b,c){var d=new Image;return d.src=a,d.complete?b(d):(d.onload=function(){d.onload=null,b(d)},void(d.onerror=function(a){d.onerror=null,c(a)}))}var h={};if(b=b||{},b.photos){var i=b.photos.constructor===Object,k=i?b.photos:{},l=k.data||[],m=k.start||0;if(h.imgIndex=(0|m)+1,b.img=b.img||"img",i){if(0===l.length)return f.msg("&#x6CA1;&#x6709;&#x56FE;&#x7247;")}else{var n=c(b.photos),o=function(){l=[],n.find(b.img).each(function(a){var b=c(this);b.attr("layer-index",a),l.push({alt:b.attr("alt"),pid:b.attr("layer-pid"),src:b.attr("layer-src")||b.attr("src"),thumb:b.attr("src")})})};if(o(),0===l.length)return;if(d||n.on("click",b.img,function(){var a=c(this),d=a.attr("layer-index");f.photos(c.extend(b,{photos:{start:d,data:l,tab:b.tab},full:b.full}),!0),o()}),!d)return}h.imgprev=function(a){h.imgIndex--,h.imgIndex<1&&(h.imgIndex=l.length),h.tabimg(a)},h.imgnext=function(a,b){h.imgIndex++,h.imgIndex>l.length&&(h.imgIndex=1,b)||h.tabimg(a)},h.keyup=function(a){if(!h.end){var b=a.keyCode;a.preventDefault(),37===b?h.imgprev(!0):39===b?h.imgnext(!0):27===b&&f.close(h.index)}},h.tabimg=function(a){l.length<=1||(k.start=h.imgIndex-1,f.close(h.index),f.photos(b,!0,a))},h.event=function(){h.bigimg.hover(function(){h.imgsee.show()},function(){h.imgsee.hide()}),h.bigimg.find(".layui-layer-imgprev").on("click",function(a){a.preventDefault(),h.imgprev()}),h.bigimg.find(".layui-layer-imgnext").on("click",function(a){a.preventDefault(),h.imgnext()}),c(document).on("keyup",h.keyup)},h.loadi=f.load(1,{shade:"shade"in b?!1:.9,scrollbar:!1}),g(l[m].src,function(d){f.close(h.loadi),h.index=f.open(c.extend({type:1,area:function(){var e=[d.width,d.height],f=[c(a).width()-50,c(a).height()-50];return!b.full&&e[0]>f[0]&&(e[0]=f[0],e[1]=e[0]*d.height/d.width),[e[0]+"px",e[1]+"px"]}(),title:!1,shade:.9,shadeClose:!0,closeBtn:!1,move:".layui-layer-phimg img",moveType:1,scrollbar:!1,moveOut:!0,shift:5*Math.random()|0,skin:"layui-layer-photos"+j("photos"),content:'<div class="layui-layer-phimg"><img src="'+l[m].src+'" alt="'+(l[m].alt||"")+'" layer-pid="'+l[m].pid+'"><div class="layui-layer-imgsee">'+(l.length>1?'<span class="layui-layer-imguide"><a href="javascript:;" class="layui-layer-iconext layui-layer-imgprev"></a><a href="javascript:;" class="layui-layer-iconext layui-layer-imgnext"></a></span>':"")+'<div class="layui-layer-imgbar" style="display:'+(e?"block":"")+'"><span class="layui-layer-imgtit"><a href="javascript:;">'+(l[m].alt||"")+"</a><em>"+h.imgIndex+"/"+l.length+"</em></span></div></div></div>",success:function(a,c){h.bigimg=a.find(".layui-layer-phimg"),h.imgsee=a.find(".layui-layer-imguide,.layui-layer-imgbar"),h.event(a),b.tab&&b.tab(l[m],a)},end:function(){h.end=!0,c(document).off("keyup",h.keyup)}},b))},function(){f.close(h.loadi),f.msg("&#x5F53;&#x524D;&#x56FE;&#x7247;&#x5730;&#x5740;&#x5F02;&#x5E38;<br>&#x662F;&#x5426;&#x7EE7;&#x7EED;&#x67E5;&#x770B;&#x4E0B;&#x4E00;&#x5F20;&#xFF1F;",{time:3e4,btn:["&#x4E0B;&#x4E00;&#x5F20;","&#x4E0D;&#x770B;&#x4E86;"],yes:function(){l.length>1&&h.imgnext(!0,!0)}})})}},e.run=function(){c=jQuery,d=c(a),h.html=c("html"),f.open=function(a){var b=new g(a);return b.index}},"function"==typeof define?define(function(){return e.run(),f}):function(){e.run(),f.use("skin/layer.css")}()}(window);
\ No newline at end of file
diff --git a/apps/static/js/plugins/layer/skin/default/icon-ext.png b/apps/static/js/plugins/layer/skin/default/icon-ext.png
deleted file mode 100644
index bbbb669bb..000000000
Binary files a/apps/static/js/plugins/layer/skin/default/icon-ext.png and /dev/null differ
diff --git a/apps/static/js/plugins/layer/skin/default/icon.png b/apps/static/js/plugins/layer/skin/default/icon.png
deleted file mode 100644
index 3e17da8b1..000000000
Binary files a/apps/static/js/plugins/layer/skin/default/icon.png and /dev/null differ
diff --git a/apps/static/js/plugins/layer/skin/default/loading-0.gif b/apps/static/js/plugins/layer/skin/default/loading-0.gif
deleted file mode 100644
index 6f3c9539a..000000000
Binary files a/apps/static/js/plugins/layer/skin/default/loading-0.gif and /dev/null differ
diff --git a/apps/static/js/plugins/layer/skin/default/loading-1.gif b/apps/static/js/plugins/layer/skin/default/loading-1.gif
deleted file mode 100644
index db3a483e4..000000000
Binary files a/apps/static/js/plugins/layer/skin/default/loading-1.gif and /dev/null differ
diff --git a/apps/static/js/plugins/layer/skin/default/loading-2.gif b/apps/static/js/plugins/layer/skin/default/loading-2.gif
deleted file mode 100644
index 5bb90fd6a..000000000
Binary files a/apps/static/js/plugins/layer/skin/default/loading-2.gif and /dev/null differ
diff --git a/apps/static/js/plugins/layer/skin/layer.css b/apps/static/js/plugins/layer/skin/layer.css
deleted file mode 100644
index b4c094128..000000000
--- a/apps/static/js/plugins/layer/skin/layer.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/*!
- 
- @Name: layer's style
- @Author: 贤心
- @Blog: sentsin.com
- 
- */.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}*html{background-image:url(about:blank);background-attachment:fixed}html #layui_layer_skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3);border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.3);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-moves{position:absolute;border:3px solid #666;border:3px solid rgba(0,0,0,.5);cursor:move;background-color:#fff;background-color:rgba(255,255,255,.3);filter:alpha(opacity=50)}.layui-layer-load{background:url(default/loading-0.gif) center center no-repeat #fff}.layui-layer-ico{background:url(default/icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);-ms-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px solid #dedede;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.7}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe .layui-layer-content{overflow:hidden}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(default/loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(default/loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(default/loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.3);background-color:#F90;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#F90}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#F90}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#BBB5B5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}
\ No newline at end of file
diff --git a/apps/static/js/plugins/magnific/jquery.magnific-popup.min.js b/apps/static/js/plugins/magnific/jquery.magnific-popup.min.js
deleted file mode 100644
index ad353b97e..000000000
--- a/apps/static/js/plugins/magnific/jquery.magnific-popup.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! Magnific Popup - v1.0.0 - 2015-01-03
-* http://dimsemenov.com/plugins/magnific-popup/
-* Copyright (c) 2015 Dmitry Semenov; */
-!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks&&(c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]&&b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g&&b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isIE7=-1!==c.indexOf("MSIE 7."),b.isIE8=-1!==c.indexOf("MSIE 8."),b.isLowIE=b.isIE7||b.isIE8,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e<h.length;e++)if(g=h[e],g.parsed&&(g=g.el[0]),g===c.el[0]){b.index=e;break}}else b.items=a.isArray(c.items)?c.items:[c.items],b.index=c.index||0;if(b.isOpen)return void b.updateItemHTML();b.types=[],f="",b.ev=c.mainEl&&c.mainEl.length?c.mainEl.eq(0):d,c.key?(b.popupsCache[c.key]||(b.popupsCache[c.key]={}),b.currTemplate=b.popupsCache[c.key]):b.currTemplate={},b.st=a.extend(!0,{},a.magnificPopup.defaults,c),b.fixedContentPos="auto"===b.st.fixedContentPos?!b.probablyMobile:b.st.fixedContentPos,b.st.modal&&(b.st.closeOnContentClick=!1,b.st.closeOnBgClick=!1,b.st.showCloseBtn=!1,b.st.enableEscapeKey=!1),b.bgOverlay||(b.bgOverlay=x("bg").on("click"+p,function(){b.close()}),b.wrap=x("wrap").attr("tabindex",-1).on("click"+p,function(a){b._checkIfClose(a.target)&&b.close()}),b.container=x("container",b.wrap)),b.contentContainer=x("content"),b.st.preloader&&(b.preloader=x("preloader",b.container,b.st.tLoading));var i=a.magnificPopup.modules;for(e=0;e<i.length;e++){var j=i[e];j=j.charAt(0).toUpperCase()+j.slice(1),b["init"+j].call(b)}y("BeforeOpen"),b.st.showCloseBtn&&(b.st.closeBtnInside?(w(l,function(a,b,c,d){c.close_replaceWith=z(d.type)}),f+=" mfp-close-btn-in"):b.wrap.append(z())),b.st.alignTop&&(f+=" mfp-align-top"),b.wrap.css(b.fixedContentPos?{overflow:b.st.overflowY,overflowX:"hidden",overflowY:b.st.overflowY}:{top:v.scrollTop(),position:"absolute"}),(b.st.fixedBgPos===!1||"auto"===b.st.fixedBgPos&&!b.fixedContentPos)&&b.bgOverlay.css({height:d.height(),position:"absolute"}),b.st.enableEscapeKey&&d.on("keyup"+p,function(a){27===a.keyCode&&b.close()}),v.on("resize"+p,function(){b.updateSize()}),b.st.closeOnContentClick||(f+=" mfp-auto-cursor"),f&&b.wrap.addClass(f);var k=b.wH=v.height(),n={};if(b.fixedContentPos&&b._hasScrollBar(k)){var o=b._getScrollbarSize();o&&(n.marginRight=o)}b.fixedContentPos&&(b.isIE7?a("body, html").css("overflow","hidden"):n.overflow="hidden");var r=b.st.mainClass;return b.isIE7&&(r+=" mfp-ie7"),r&&b._addClassToMFP(r),b.updateItemHTML(),y("BuildControls"),a("html").css(n),b.bgOverlay.add(b.wrap).prependTo(b.st.prependTo||a(document.body)),b._lastFocusedEl=document.activeElement,setTimeout(function(){b.content?(b._addClassToMFP(q),b._setFocus()):b.bgOverlay.addClass(q),d.on("focusin"+p,b._onFocusIn)},16),b.isOpen=!0,b.updateSize(k),y(m),c},close:function(){b.isOpen&&(y(i),b.isOpen=!1,b.st.removalDelay&&!b.isLowIE&&b.supportsTransition?(b._addClassToMFP(r),setTimeout(function(){b._close()},b.st.removalDelay)):b._close())},_close:function(){y(h);var c=r+" "+q+" ";if(b.bgOverlay.detach(),b.wrap.detach(),b.container.empty(),b.st.mainClass&&(c+=b.st.mainClass+" "),b._removeClassFromMFP(c),b.fixedContentPos){var e={marginRight:""};b.isIE7?a("body, html").css("overflow",""):e.overflow="",a("html").css(e)}d.off("keyup"+p+" focusin"+p),b.ev.off(p),b.wrap.attr("class","mfp-wrap").removeAttr("style"),b.bgOverlay.attr("class","mfp-bg"),b.container.attr("class","mfp-container"),!b.st.showCloseBtn||b.st.closeBtnInside&&b.currTemplate[b.currItem.type]!==!0||b.currTemplate.closeBtn&&b.currTemplate.closeBtn.detach(),b._lastFocusedEl&&a(b._lastFocusedEl).focus(),b.currItem=null,b.content=null,b.currTemplate=null,b.prevHeight=0,y(j)},updateSize:function(a){if(b.isIOS){var c=document.documentElement.clientWidth/window.innerWidth,d=window.innerHeight*c;b.wrap.css("height",d),b.wH=d}else b.wH=a||v.height();b.fixedContentPos||b.wrap.css("height",b.wH),y("Resize")},updateItemHTML:function(){var c=b.items[b.index];b.contentContainer.detach(),b.content&&b.content.detach(),c.parsed||(c=b.parseEl(b.index));var d=c.type;if(y("BeforeChange",[b.currItem?b.currItem.type:"",d]),b.currItem=c,!b.currTemplate[d]){var f=b.st[d]?b.st[d].markup:!1;y("FirstMarkupParse",f),b.currTemplate[d]=f?a(f):!0}e&&e!==c.type&&b.container.removeClass("mfp-"+e+"-holder");var g=b["get"+d.charAt(0).toUpperCase()+d.slice(1)](c,b.currTemplate[d]);b.appendContent(g,d),c.preloaded=!0,y(n,c),e=c.type,b.container.prepend(b.contentContainer),y("AfterChange")},appendContent:function(a,c){b.content=a,a?b.st.showCloseBtn&&b.st.closeBtnInside&&b.currTemplate[c]===!0?b.content.find(".mfp-close").length||b.content.append(z()):b.content=a:b.content="",y(k),b.container.addClass("mfp-"+c+"-holder"),b.contentContainer.append(b.content)},parseEl:function(c){var d,e=b.items[c];if(e.tagName?e={el:a(e)}:(d=e.type,e={data:e,src:e.src}),e.el){for(var f=b.types,g=0;g<f.length;g++)if(e.el.hasClass("mfp-"+f[g])){d=f[g];break}e.src=e.el.attr("data-mfp-src"),e.src||(e.src=e.el.attr("href"))}return e.type=d||b.st.type||"inline",e.index=c,e.parsed=!0,b.items[c]=e,y("ElementParse",e),b.items[c]},addGroup:function(a,c){var d=function(d){d.mfpEl=this,b._openClick(d,a,c)};c||(c={});var e="click.magnificPopup";c.mainEl=a,c.items?(c.isObj=!0,a.off(e).on(e,d)):(c.isObj=!1,c.delegate?a.off(e).on(e,c.delegate,d):(c.items=a,a.off(e).on(e,d)))},_openClick:function(c,d,e){var f=void 0!==e.midClick?e.midClick:a.magnificPopup.defaults.midClick;if(f||2!==c.which&&!c.ctrlKey&&!c.metaKey){var g=void 0!==e.disableOn?e.disableOn:a.magnificPopup.defaults.disableOn;if(g)if(a.isFunction(g)){if(!g.call(b))return!0}else if(v.width()<g)return!0;c.type&&(c.preventDefault(),b.isOpen&&c.stopPropagation()),e.el=a(c.mfpEl),e.delegate&&(e.items=d.find(e.delegate)),b.open(e)}},updateStatus:function(a,d){if(b.preloader){c!==a&&b.container.removeClass("mfp-s-"+c),d||"loading"!==a||(d=b.st.tLoading);var e={status:a,text:d};y("UpdateStatus",e),a=e.status,d=e.text,b.preloader.html(d),b.preloader.find("a").on("click",function(a){a.stopImmediatePropagation()}),b.container.addClass("mfp-s-"+a),c=a}},_checkIfClose:function(c){if(!a(c).hasClass(s)){var d=b.st.closeOnContentClick,e=b.st.closeOnBgClick;if(d&&e)return!0;if(!b.content||a(c).hasClass("mfp-close")||b.preloader&&c===b.preloader[0])return!0;if(c===b.content[0]||a.contains(b.content[0],c)){if(d)return!0}else if(e&&a.contains(document,c))return!0;return!1}},_addClassToMFP:function(a){b.bgOverlay.addClass(a),b.wrap.addClass(a)},_removeClassFromMFP:function(a){this.bgOverlay.removeClass(a),b.wrap.removeClass(a)},_hasScrollBar:function(a){return(b.isIE7?d.height():document.body.scrollHeight)>(a||v.height())},_setFocus:function(){(b.st.focus?b.content.find(b.st.focus).eq(0):b.wrap).focus()},_onFocusIn:function(c){return c.target===b.wrap[0]||a.contains(b.wrap[0],c.target)?void 0:(b._setFocus(),!1)},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),y(l,[b,c,d]),a.each(c,function(a,c){if(void 0===c||c===!1)return!0;if(e=a.split("_"),e.length>1){var d=b.find(p+"-"+e[0]);if(d.length>0){var f=e[1];"replaceWith"===f?d[0]!==c[0]&&d.replaceWith(c):"img"===f?d.is("img")?d.attr("src",c):d.replaceWith('<img src="'+c+'" class="'+d.attr("class")+'" />'):d.attr(e[1],c)}}else b.find(p+"-"+a).html(c)})},_getScrollbarSize:function(){if(void 0===b.scrollbarSize){var a=document.createElement("div");a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),b.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return b.scrollbarSize}},a.magnificPopup={instance:null,proto:t.prototype,modules:[],open:function(b,c){return A(),b=b?a.extend(!0,{},b):{},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'<button title="%title%" type="button" class="mfp-close">&times;</button>',tClose:"Close (Esc)",tLoading:"Loading..."}},a.fn.magnificPopup=function(c){A();var d=a(this);if("string"==typeof c)if("open"===c){var e,f=u?d.data("magnificPopup"):d[0].magnificPopup,g=parseInt(arguments[1],10)||0;f.items?e=f.items[g]:(e=d,f.delegate&&(e=e.find(f.delegate)),e=e.eq(g)),b._openClick({mfpEl:e},d,f)}else b.isOpen&&b[c].apply(b,Array.prototype.slice.call(arguments,1));else c=a.extend(!0,{},c),u?d.data("magnificPopup",c):d[0].magnificPopup=c,b.addGroup(d,c);return d};var C,D,E,F="inline",G=function(){E&&(D.after(E.addClass(C)).detach(),E=null)};a.magnificPopup.registerModule(F,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){b.types.push(F),w(h+"."+F,function(){G()})},getInline:function(c,d){if(G(),c.src){var e=b.st.inline,f=a(c.src);if(f.length){var g=f[0].parentNode;g&&g.tagName&&(D||(C=e.hiddenClass,D=x(C),C="mfp-"+C),E=f.after(D).detach().removeClass(C)),b.updateStatus("ready")}else b.updateStatus("error",e.tNotFound),f=a("<div>");return c.inlineElement=f,f}return b.updateStatus("ready"),b._parseMarkup(d,{},c),d}}});var H,I="ajax",J=function(){H&&a(document.body).removeClass(H)},K=function(){J(),b.req&&b.req.abort()};a.magnificPopup.registerModule(I,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'<a href="%url%">The content</a> could not be loaded.'},proto:{initAjax:function(){b.types.push(I),H=b.st.ajax.cursor,w(h+"."+I,K),w("BeforeChange."+I,K)},getAjax:function(c){H&&a(document.body).addClass(H),b.updateStatus("loading");var d=a.extend({url:c.src,success:function(d,e,f){var g={data:d,xhr:f};y("ParseAjax",g),b.appendContent(a(g.data),I),c.finished=!0,J(),b._setFocus(),setTimeout(function(){b.wrap.addClass(q)},16),b.updateStatus("ready"),y("AjaxContentAdded")},error:function(){J(),c.finished=c.loadError=!0,b.updateStatus("error",b.st.ajax.tError.replace("%url%",c.src))}},b.st.ajax.settings);return b.req=a.ajax(d),""}}});var L,M=function(c){if(c.data&&void 0!==c.data.title)return c.data.title;var d=b.st.image.titleSrc;if(d){if(a.isFunction(d))return d.call(b,c);if(c.el)return c.el.attr(d)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'<div class="mfp-figure"><div class="mfp-close"></div><figure><div class="mfp-img"></div><figcaption><div class="mfp-bottom-bar"><div class="mfp-title"></div><div class="mfp-counter"></div></div></figcaption></figure></div>',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'<a href="%url%">The image</a> could not be loaded.'},proto:{initImage:function(){var c=b.st.image,d=".image";b.types.push("image"),w(m+d,function(){"image"===b.currItem.type&&c.cursor&&a(document.body).addClass(c.cursor)}),w(h+d,function(){c.cursor&&a(document.body).removeClass(c.cursor),v.off("resize"+p)}),w("Resize"+d,b.resizeImage),b.isLowIE&&w("AfterChange",b.resizeImage)},resizeImage:function(){var a=b.currItem;if(a&&a.img&&b.st.image.verticalFit){var c=0;b.isLowIE&&(c=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",b.wH-c)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,L&&clearInterval(L),a.isCheckingImgSize=!1,y("ImageHasSize",a),a.imgHidden&&(b.content&&b.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var c=0,d=a.img[0],e=function(f){L&&clearInterval(L),L=setInterval(function(){return d.naturalWidth>0?void b._onImageHasSize(a):(c>200&&clearInterval(L),c++,void(3===c?e(10):40===c?e(50):100===c&&e(500)))},f)};e(1)},getImage:function(c,d){var e=0,f=function(){c&&(c.img[0].complete?(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("ready")),c.hasSize=!0,c.loaded=!0,y("ImageLoadComplete")):(e++,200>e?setTimeout(f,100):g()))},g=function(){c&&(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("error",h.tError.replace("%url%",c.src))),c.hasSize=!0,c.loaded=!0,c.loadError=!0)},h=b.st.image,i=d.find(".mfp-img");if(i.length){var j=document.createElement("img");j.className="mfp-img",c.el&&c.el.find("img").length&&(j.alt=c.el.find("img").attr("alt")),c.img=a(j).on("load.mfploader",f).on("error.mfploader",g),j.src=c.src,i.is("img")&&(c.img=c.img.clone()),j=c.img[0],j.naturalWidth>0?c.hasSize=!0:j.width||(c.hasSize=!1)}return b._parseMarkup(d,{title:M(c),img_replaceWith:c.img},c),b.resizeImage(),c.hasSize?(L&&clearInterval(L),c.loadError?(d.addClass("mfp-loading"),b.updateStatus("error",h.tError.replace("%url%",c.src))):(d.removeClass("mfp-loading"),b.updateStatus("ready")),d):(b.updateStatus("loading"),c.loading=!0,c.hasSize||(c.imgHidden=!0,d.addClass("mfp-loading"),b.findImageSize(c)),d)}}});var N,O=function(){return void 0===N&&(N=void 0!==document.createElement("p").style.MozTransform),N};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a,c=b.st.zoom,d=".zoom";if(c.enabled&&b.supportsTransition){var e,f,g=c.duration,j=function(a){var b=a.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+c.duration/1e3+"s "+c.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,b.css(e),b},k=function(){b.content.css("visibility","visible")};w("BuildControls"+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.content.css("visibility","hidden"),a=b._getItemToZoom(),!a)return void k();f=j(a),f.css(b._getOffset()),b.wrap.append(f),e=setTimeout(function(){f.css(b._getOffset(!0)),e=setTimeout(function(){k(),setTimeout(function(){f.remove(),a=f=null,y("ZoomAnimationEnded")},16)},g)},16)}}),w(i+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.st.removalDelay=g,!a){if(a=b._getItemToZoom(),!a)return;f=j(a)}f.css(b._getOffset(!0)),b.wrap.append(f),b.content.css("visibility","hidden"),setTimeout(function(){f.css(b._getOffset())},16)}}),w(h+d,function(){b._allowZoom()&&(k(),f&&f.remove(),a=null)})}},_allowZoom:function(){return"image"===b.currItem.type},_getItemToZoom:function(){return b.currItem.hasSize?b.currItem.img:!1},_getOffset:function(c){var d;d=c?b.currItem.img:b.st.zoom.opener(b.currItem.el||b.currItem);var e=d.offset(),f=parseInt(d.css("padding-top"),10),g=parseInt(d.css("padding-bottom"),10);e.top-=a(window).scrollTop()-f;var h={width:d.width(),height:(u?d.innerHeight():d[0].offsetHeight)-g-f};return O()?h["-moz-transform"]=h.transform="translate("+e.left+"px,"+e.top+"px)":(h.left=e.left,h.top=e.top),h}}});var P="iframe",Q="//about:blank",R=function(a){if(b.currTemplate[P]){var c=b.currTemplate[P].find("iframe");c.length&&(a||(c[0].src=Q),b.isIE8&&c.css("display",a?"block":"none"))}};a.magnificPopup.registerModule(P,{options:{markup:'<div class="mfp-iframe-scaler"><div class="mfp-close"></div><iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe></div>',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){b.types.push(P),w("BeforeChange",function(a,b,c){b!==c&&(b===P?R():c===P&&R(!0))}),w(h+"."+P,function(){R()})},getIframe:function(c,d){var e=c.src,f=b.st.iframe;a.each(f.patterns,function(){return e.indexOf(this.index)>-1?(this.id&&(e="string"==typeof this.id?e.substr(e.lastIndexOf(this.id)+this.id.length,e.length):this.id.call(this,e)),e=this.src.replace("%id%",e),!1):void 0});var g={};return f.srcAction&&(g[f.srcAction]=e),b._parseMarkup(d,g,c),b.updateStatus("ready"),d}}});var S=function(a){var c=b.items.length;return a>c-1?a-c:0>a?c+a:a},T=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=b.st.gallery,e=".mfp-gallery",g=Boolean(a.fn.mfpFastClick);return b.direction=!0,c&&c.enabled?(f+=" mfp-gallery",w(m+e,function(){c.navigateByImgClick&&b.wrap.on("click"+e,".mfp-img",function(){return b.items.length>1?(b.next(),!1):void 0}),d.on("keydown"+e,function(a){37===a.keyCode?b.prev():39===a.keyCode&&b.next()})}),w("UpdateStatus"+e,function(a,c){c.text&&(c.text=T(c.text,b.currItem.index,b.items.length))}),w(l+e,function(a,d,e,f){var g=b.items.length;e.counter=g>1?T(c.tCounter,f.index,g):""}),w("BuildControls"+e,function(){if(b.items.length>1&&c.arrows&&!b.arrowLeft){var d=c.arrowMarkup,e=b.arrowLeft=a(d.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(s),f=b.arrowRight=a(d.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(s),h=g?"mfpFastClick":"click";e[h](function(){b.prev()}),f[h](function(){b.next()}),b.isIE7&&(x("b",e[0],!1,!0),x("a",e[0],!1,!0),x("b",f[0],!1,!0),x("a",f[0],!1,!0)),b.container.append(e.add(f))}}),w(n+e,function(){b._preloadTimeout&&clearTimeout(b._preloadTimeout),b._preloadTimeout=setTimeout(function(){b.preloadNearbyImages(),b._preloadTimeout=null},16)}),void w(h+e,function(){d.off(e),b.wrap.off("click"+e),b.arrowLeft&&g&&b.arrowLeft.add(b.arrowRight).destroyMfpFastClick(),b.arrowRight=b.arrowLeft=null})):!1},next:function(){b.direction=!0,b.index=S(b.index+1),b.updateItemHTML()},prev:function(){b.direction=!1,b.index=S(b.index-1),b.updateItemHTML()},goTo:function(a){b.direction=a>=b.index,b.index=a,b.updateItemHTML()},preloadNearbyImages:function(){var a,c=b.st.gallery.preload,d=Math.min(c[0],b.items.length),e=Math.min(c[1],b.items.length);for(a=1;a<=(b.direction?e:d);a++)b._preloadItem(b.index+a);for(a=1;a<=(b.direction?d:e);a++)b._preloadItem(b.index-a)},_preloadItem:function(c){if(c=S(c),!b.items[c].preloaded){var d=b.items[c];d.parsed||(d=b.parseEl(c)),y("LazyLoad",d),"image"===d.type&&(d.img=a('<img class="mfp-img" />').on("load.mfploader",function(){d.hasSize=!0}).on("error.mfploader",function(){d.hasSize=!0,d.loadError=!0,y("LazyLoadError",d)}).attr("src",d.src)),d.preloaded=!0}}}});var U="retina";a.magnificPopup.registerModule(U,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=b.st.retina,c=a.ratio;c=isNaN(c)?c():c,c>1&&(w("ImageHasSize."+U,function(a,b){b.img.css({"max-width":b.img[0].naturalWidth/c,width:"100%"})}),w("ElementParse."+U,function(b,d){d.src=a.replaceSrc(d,c)}))}}}}),function(){var b=1e3,c="ontouchstart"in window,d=function(){v.off("touchmove"+f+" touchend"+f)},e="mfpFastClick",f="."+e;a.fn.mfpFastClick=function(e){return a(this).each(function(){var g,h=a(this);if(c){var i,j,k,l,m,n;h.on("touchstart"+f,function(a){l=!1,n=1,m=a.originalEvent?a.originalEvent.touches[0]:a.touches[0],j=m.clientX,k=m.clientY,v.on("touchmove"+f,function(a){m=a.originalEvent?a.originalEvent.touches:a.touches,n=m.length,m=m[0],(Math.abs(m.clientX-j)>10||Math.abs(m.clientY-k)>10)&&(l=!0,d())}).on("touchend"+f,function(a){d(),l||n>1||(g=!0,a.preventDefault(),clearTimeout(i),i=setTimeout(function(){g=!1},b),e())})})}h.on("click"+f,function(){g||e()})})},a.fn.destroyMfpFastClick=function(){a(this).off("touchstart"+f+" click"+f),c&&v.off("touchmove"+f+" touchend"+f)}}(),A()});
\ No newline at end of file
diff --git a/apps/static/js/plugins/moment/moment.min.js b/apps/static/js/plugins/moment/moment.min.js
index 8e6866af0..5787a4085 100644
--- a/apps/static/js/plugins/moment/moment.min.js
+++ b/apps/static/js/plugins/moment/moment.min.js
@@ -1,7 +1 @@
-//! moment.js
-//! version : 2.10.6
-//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
-//! license : MIT
-//! momentjs.com
-!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return Hc.apply(null,arguments)}function b(a){Hc=a}function c(a){return"[object Array]"===Object.prototype.toString.call(a)}function d(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function e(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function f(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function g(a,b){for(var c in b)f(b,c)&&(a[c]=b[c]);return f(b,"toString")&&(a.toString=b.toString),f(b,"valueOf")&&(a.valueOf=b.valueOf),a}function h(a,b,c,d){return Ca(a,b,c,d,!0).utc()}function i(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function j(a){return null==a._pf&&(a._pf=i()),a._pf}function k(a){if(null==a._isValid){var b=j(a);a._isValid=!(isNaN(a._d.getTime())||!(b.overflow<0)||b.empty||b.invalidMonth||b.invalidWeekday||b.nullInput||b.invalidFormat||b.userInvalidated),a._strict&&(a._isValid=a._isValid&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour)}return a._isValid}function l(a){var b=h(NaN);return null!=a?g(j(b),a):j(b).userInvalidated=!0,b}function m(a,b){var c,d,e;if("undefined"!=typeof b._isAMomentObject&&(a._isAMomentObject=b._isAMomentObject),"undefined"!=typeof b._i&&(a._i=b._i),"undefined"!=typeof b._f&&(a._f=b._f),"undefined"!=typeof b._l&&(a._l=b._l),"undefined"!=typeof b._strict&&(a._strict=b._strict),"undefined"!=typeof b._tzm&&(a._tzm=b._tzm),"undefined"!=typeof b._isUTC&&(a._isUTC=b._isUTC),"undefined"!=typeof b._offset&&(a._offset=b._offset),"undefined"!=typeof b._pf&&(a._pf=j(b)),"undefined"!=typeof b._locale&&(a._locale=b._locale),Jc.length>0)for(c in Jc)d=Jc[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function n(b){m(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),Kc===!1&&(Kc=!0,a.updateOffset(this),Kc=!1)}function o(a){return a instanceof n||null!=a&&null!=a._isAMomentObject}function p(a){return 0>a?Math.ceil(a):Math.floor(a)}function q(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=p(b)),c}function r(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&&q(a[d])!==q(b[d]))&&g++;return g+f}function s(){}function t(a){return a?a.toLowerCase().replace("_","-"):a}function u(a){for(var b,c,d,e,f=0;f<a.length;){for(e=t(a[f]).split("-"),b=e.length,c=t(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=v(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&r(e,c,!0)>=b-1)break;b--}f++}return null}function v(a){var b=null;if(!Lc[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Ic._abbr,require("./locale/"+a),w(b)}catch(c){}return Lc[a]}function w(a,b){var c;return a&&(c="undefined"==typeof b?y(a):x(a,b),c&&(Ic=c)),Ic._abbr}function x(a,b){return null!==b?(b.abbr=a,Lc[a]=Lc[a]||new s,Lc[a].set(b),w(a),Lc[a]):(delete Lc[a],null)}function y(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Ic;if(!c(a)){if(b=v(a))return b;a=[a]}return u(a)}function z(a,b){var c=a.toLowerCase();Mc[c]=Mc[c+"s"]=Mc[b]=a}function A(a){return"string"==typeof a?Mc[a]||Mc[a.toLowerCase()]:void 0}function B(a){var b,c,d={};for(c in a)f(a,c)&&(b=A(c),b&&(d[b]=a[c]));return d}function C(b,c){return function(d){return null!=d?(E(this,b,d),a.updateOffset(this,c),this):D(this,b)}}function D(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function E(a,b,c){return a._d["set"+(a._isUTC?"UTC":"")+b](c)}function F(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=A(a),"function"==typeof this[a])return this[a](b);return this}function G(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function H(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Qc[a]=e),b&&(Qc[b[0]]=function(){return G(e.apply(this,arguments),b[1],b[2])}),c&&(Qc[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function I(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function J(a){var b,c,d=a.match(Nc);for(b=0,c=d.length;c>b;b++)Qc[d[b]]?d[b]=Qc[d[b]]:d[b]=I(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 K(a,b){return a.isValid()?(b=L(b,a.localeData()),Pc[b]=Pc[b]||J(b),Pc[b](a)):a.localeData().invalidDate()}function L(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Oc.lastIndex=0;d>=0&&Oc.test(a);)a=a.replace(Oc,c),Oc.lastIndex=0,d-=1;return a}function M(a){return"function"==typeof a&&"[object Function]"===Object.prototype.toString.call(a)}function N(a,b,c){dd[a]=M(b)?b:function(a){return a&&c?c:b}}function O(a,b){return f(dd,a)?dd[a](b._strict,b._locale):new RegExp(P(a))}function P(a){return a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Q(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=q(a)}),c=0;c<a.length;c++)ed[a[c]]=d}function R(a,b){Q(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function S(a,b,c){null!=b&&f(ed,a)&&ed[a](b,c._a,c,a)}function T(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function U(a){return this._months[a.month()]}function V(a){return this._monthsShort[a.month()]}function W(a,b,c){var d,e,f;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;12>d;d++){if(e=h([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function X(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),T(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function Y(b){return null!=b?(X(this,b),a.updateOffset(this,!0),this):D(this,"Month")}function Z(){return T(this.year(),this.month())}function $(a){var b,c=a._a;return c&&-2===j(a).overflow&&(b=c[gd]<0||c[gd]>11?gd:c[hd]<1||c[hd]>T(c[fd],c[gd])?hd:c[id]<0||c[id]>24||24===c[id]&&(0!==c[jd]||0!==c[kd]||0!==c[ld])?id:c[jd]<0||c[jd]>59?jd:c[kd]<0||c[kd]>59?kd:c[ld]<0||c[ld]>999?ld:-1,j(a)._overflowDayOfYear&&(fd>b||b>hd)&&(b=hd),j(a).overflow=b),a}function _(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function aa(a,b){var c=!0;return g(function(){return c&&(_(a+"\n"+(new Error).stack),c=!1),b.apply(this,arguments)},b)}function ba(a,b){od[a]||(_(b),od[a]=!0)}function ca(a){var b,c,d=a._i,e=pd.exec(d);if(e){for(j(a).iso=!0,b=0,c=qd.length;c>b;b++)if(qd[b][1].exec(d)){a._f=qd[b][0];break}for(b=0,c=rd.length;c>b;b++)if(rd[b][1].exec(d)){a._f+=(e[6]||" ")+rd[b][0];break}d.match(ad)&&(a._f+="Z"),va(a)}else a._isValid=!1}function da(b){var c=sd.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(ca(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function ea(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 fa(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function ga(a){return ha(a)?366:365}function ha(a){return a%4===0&&a%100!==0||a%400===0}function ia(){return ha(this.year())}function ja(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=Da(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ka(a){return ja(a,this._week.dow,this._week.doy).week}function la(){return this._week.dow}function ma(){return this._week.doy}function na(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function oa(a){var b=ja(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function pa(a,b,c,d,e){var f,g=6+e-d,h=fa(a,0,1+g),i=h.getUTCDay();return e>i&&(i+=7),c=null!=c?1*c:e,f=1+g+7*(b-1)-i+c,{year:f>0?a:a-1,dayOfYear:f>0?f:ga(a-1)+f}}function qa(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function ra(a,b,c){return null!=a?a:null!=b?b:c}function sa(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function ta(a){var b,c,d,e,f=[];if(!a._d){for(d=sa(a),a._w&&null==a._a[hd]&&null==a._a[gd]&&ua(a),a._dayOfYear&&(e=ra(a._a[fd],d[fd]),a._dayOfYear>ga(e)&&(j(a)._overflowDayOfYear=!0),c=fa(e,0,a._dayOfYear),a._a[gd]=c.getUTCMonth(),a._a[hd]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[id]&&0===a._a[jd]&&0===a._a[kd]&&0===a._a[ld]&&(a._nextDay=!0,a._a[id]=0),a._d=(a._useUTC?fa:ea).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[id]=24)}}function ua(a){var b,c,d,e,f,g,h;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=ra(b.GG,a._a[fd],ja(Da(),1,4).year),d=ra(b.W,1),e=ra(b.E,1)):(f=a._locale._week.dow,g=a._locale._week.doy,c=ra(b.gg,a._a[fd],ja(Da(),f,g).year),d=ra(b.w,1),null!=b.d?(e=b.d,f>e&&++d):e=null!=b.e?b.e+f:f),h=pa(c,d,e,g,f),a._a[fd]=h.year,a._dayOfYear=h.dayOfYear}function va(b){if(b._f===a.ISO_8601)return void ca(b);b._a=[],j(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,k=0;for(e=L(b._f,b._locale).match(Nc)||[],c=0;c<e.length;c++)f=e[c],d=(h.match(O(f,b))||[])[0],d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&j(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),k+=d.length),Qc[f]?(d?j(b).empty=!1:j(b).unusedTokens.push(f),S(f,d,b)):b._strict&&!d&&j(b).unusedTokens.push(f);j(b).charsLeftOver=i-k,h.length>0&&j(b).unusedInput.push(h),j(b).bigHour===!0&&b._a[id]<=12&&b._a[id]>0&&(j(b).bigHour=void 0),b._a[id]=wa(b._locale,b._a[id],b._meridiem),ta(b),$(b)}function wa(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function xa(a){var b,c,d,e,f;if(0===a._f.length)return j(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=m({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],va(b),k(b)&&(f+=j(b).charsLeftOver,f+=10*j(b).unusedTokens.length,j(b).score=f,(null==d||d>f)&&(d=f,c=b));g(a,c||b)}function ya(a){if(!a._d){var b=B(a._i);a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],ta(a)}}function za(a){var b=new n($(Aa(a)));return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function Aa(a){var b=a._i,e=a._f;return a._locale=a._locale||y(a._l),null===b||void 0===e&&""===b?l({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),o(b)?new n($(b)):(c(e)?xa(a):e?va(a):d(b)?a._d=b:Ba(a),a))}function Ba(b){var f=b._i;void 0===f?b._d=new Date:d(f)?b._d=new Date(+f):"string"==typeof f?da(b):c(f)?(b._a=e(f.slice(0),function(a){return parseInt(a,10)}),ta(b)):"object"==typeof f?ya(b):"number"==typeof f?b._d=new Date(f):a.createFromInputFallback(b)}function Ca(a,b,c,d,e){var f={};return"boolean"==typeof c&&(d=c,c=void 0),f._isAMomentObject=!0,f._useUTC=f._isUTC=e,f._l=c,f._i=a,f._f=b,f._strict=d,za(f)}function Da(a,b,c,d){return Ca(a,b,c,d,!1)}function Ea(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return Da();for(d=b[0],e=1;e<b.length;++e)(!b[e].isValid()||b[e][a](d))&&(d=b[e]);return d}function Fa(){var a=[].slice.call(arguments,0);return Ea("isBefore",a)}function Ga(){var a=[].slice.call(arguments,0);return Ea("isAfter",a)}function Ha(a){var b=B(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._locale=y(),this._bubble()}function Ia(a){return a instanceof Ha}function Ja(a,b){H(a,0,0,function(){var a=this.utcOffset(),c="+";return 0>a&&(a=-a,c="-"),c+G(~~(a/60),2)+b+G(~~a%60,2)})}function Ka(a){var b=(a||"").match(ad)||[],c=b[b.length-1]||[],d=(c+"").match(xd)||["-",0,0],e=+(60*d[1])+q(d[2]);return"+"===d[0]?e:-e}function La(b,c){var e,f;return c._isUTC?(e=c.clone(),f=(o(b)||d(b)?+b:+Da(b))-+e,e._d.setTime(+e._d+f),a.updateOffset(e,!1),e):Da(b).local()}function Ma(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Na(b,c){var d,e=this._offset||0;return null!=b?("string"==typeof b&&(b=Ka(b)),Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ma(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?bb(this,Ya(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ma(this)}function Oa(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Pa(a){return this.utcOffset(0,a)}function Qa(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ma(this),"m")),this}function Ra(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ka(this._i)),this}function Sa(a){return a=a?Da(a).utcOffset():0,(this.utcOffset()-a)%60===0}function Ta(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ua(){if("undefined"!=typeof this._isDSTShifted)return this._isDSTShifted;var a={};if(m(a,this),a=Aa(a),a._a){var b=a._isUTC?h(a._a):Da(a._a);this._isDSTShifted=this.isValid()&&r(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Va(){return!this._isUTC}function Wa(){return this._isUTC}function Xa(){return this._isUTC&&0===this._offset}function Ya(a,b){var c,d,e,g=a,h=null;return Ia(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(g={},b?g[b]=a:g.milliseconds=a):(h=yd.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:q(h[hd])*c,h:q(h[id])*c,m:q(h[jd])*c,s:q(h[kd])*c,ms:q(h[ld])*c}):(h=zd.exec(a))?(c="-"===h[1]?-1:1,g={y:Za(h[2],c),M:Za(h[3],c),d:Za(h[4],c),h:Za(h[5],c),m:Za(h[6],c),s:Za(h[7],c),w:Za(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=_a(Da(g.from),Da(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new Ha(g),Ia(a)&&f(a,"_locale")&&(d._locale=a._locale),d}function Za(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function $a(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function _a(a,b){var c;return b=La(b,a),a.isBefore(b)?c=$a(a,b):(c=$a(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function ab(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(ba(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ya(c,d),bb(this,e,a),this}}function bb(b,c,d,e){var f=c._milliseconds,g=c._days,h=c._months;e=null==e?!0:e,f&&b._d.setTime(+b._d+f*d),g&&E(b,"Date",D(b,"Date")+g*d),h&&X(b,D(b,"Month")+h*d),e&&a.updateOffset(b,g||h)}function cb(a,b){var c=a||Da(),d=La(c,this).startOf("day"),e=this.diff(d,"days",!0),f=-6>e?"sameElse":-1>e?"lastWeek":0>e?"lastDay":1>e?"sameDay":2>e?"nextDay":7>e?"nextWeek":"sameElse";return this.format(b&&b[f]||this.localeData().calendar(f,this,Da(c)))}function db(){return new n(this)}function eb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+this>+a):(c=o(a)?+a:+Da(a),c<+this.clone().startOf(b))}function fb(a,b){var c;return b=A("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+a>+this):(c=o(a)?+a:+Da(a),+this.clone().endOf(b)<c)}function gb(a,b,c){return this.isAfter(a,c)&&this.isBefore(b,c)}function hb(a,b){var c;return b=A(b||"millisecond"),"millisecond"===b?(a=o(a)?a:Da(a),+this===+a):(c=+Da(a),+this.clone().startOf(b)<=c&&c<=+this.clone().endOf(b))}function ib(a,b,c){var d,e,f=La(a,this),g=6e4*(f.utcOffset()-this.utcOffset());return b=A(b),"year"===b||"month"===b||"quarter"===b?(e=jb(this,f),"quarter"===b?e/=3:"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:p(e)}function jb(a,b){var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),f=a.clone().add(e,"months");return 0>b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function kb(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function lb(){var a=this.clone().utc();return 0<a.year()&&a.year()<=9999?"function"==typeof Date.prototype.toISOString?this.toDate().toISOString():K(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):K(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}function mb(b){var c=K(this,b||a.defaultFormat);return this.localeData().postformat(c)}function nb(a,b){return this.isValid()?Ya({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function ob(a){return this.from(Da(),a)}function pb(a,b){return this.isValid()?Ya({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function qb(a){return this.to(Da(),a)}function rb(a){var b;return void 0===a?this._locale._abbr:(b=y(a),null!=b&&(this._locale=b),this)}function sb(){return this._locale}function tb(a){switch(a=A(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}function ub(a){return a=A(a),void 0===a||"millisecond"===a?this:this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms")}function vb(){return+this._d-6e4*(this._offset||0)}function wb(){return Math.floor(+this/1e3)}function xb(){return this._offset?new Date(+this):this._d}function yb(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function zb(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function Ab(){return k(this)}function Bb(){return g({},j(this))}function Cb(){return j(this).overflow}function Db(a,b){H(0,[a,a.length],0,b)}function Eb(a,b,c){return ja(Da([a,11,31+b-c]),b,c).week}function Fb(a){var b=ja(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==a?b:this.add(a-b,"y")}function Gb(a){var b=ja(this,1,4).year;return null==a?b:this.add(a-b,"y")}function Hb(){return Eb(this.year(),1,4)}function Ib(){var a=this.localeData()._week;return Eb(this.year(),a.dow,a.doy)}function Jb(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)}function Kb(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Lb(a){return this._weekdays[a.day()]}function Mb(a){return this._weekdaysShort[a.day()]}function Nb(a){return this._weekdaysMin[a.day()]}function Ob(a){var b,c,d;for(this._weekdaysParse=this._weekdaysParse||[],b=0;7>b;b++)if(this._weekdaysParse[b]||(c=Da([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}function Pb(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Kb(a,this.localeData()),this.add(a-b,"d")):b}function Qb(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Rb(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)}function Sb(a,b){H(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Tb(a,b){return b._meridiemParse}function Ub(a){return"p"===(a+"").toLowerCase().charAt(0)}function Vb(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wb(a,b){b[ld]=q(1e3*("0."+a))}function Xb(){return this._isUTC?"UTC":""}function Yb(){return this._isUTC?"Coordinated Universal Time":""}function Zb(a){return Da(1e3*a)}function $b(){return Da.apply(null,arguments).parseZone()}function _b(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.call(b,c):d}function ac(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function bc(){return this._invalidDate}function cc(a){return this._ordinal.replace("%d",a)}function dc(a){return a}function ec(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)}function fc(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)}function gc(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function hc(a,b,c,d){var e=y(),f=h().set(d,b);return e[c](f,a)}function ic(a,b,c,d,e){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return hc(a,b,c,e);var f,g=[];for(f=0;d>f;f++)g[f]=hc(a,f,c,e);return g}function jc(a,b){return ic(a,b,"months",12,"month")}function kc(a,b){return ic(a,b,"monthsShort",12,"month")}function lc(a,b){return ic(a,b,"weekdays",7,"day")}function mc(a,b){return ic(a,b,"weekdaysShort",7,"day")}function nc(a,b){return ic(a,b,"weekdaysMin",7,"day")}function oc(){var a=this._data;return this._milliseconds=Wd(this._milliseconds),this._days=Wd(this._days),this._months=Wd(this._months),a.milliseconds=Wd(a.milliseconds),a.seconds=Wd(a.seconds),a.minutes=Wd(a.minutes),a.hours=Wd(a.hours),a.months=Wd(a.months),a.years=Wd(a.years),this}function pc(a,b,c,d){var e=Ya(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function qc(a,b){return pc(this,a,b,1)}function rc(a,b){return pc(this,a,b,-1)}function sc(a){return 0>a?Math.floor(a):Math.ceil(a)}function tc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*sc(vc(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=p(f/1e3),i.seconds=a%60,b=p(a/60),i.minutes=b%60,c=p(b/60),i.hours=c%24,g+=p(c/24),e=p(uc(g)),h+=e,g-=sc(vc(e)),d=p(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function uc(a){return 4800*a/146097}function vc(a){return 146097*a/4800}function wc(a){var b,c,d=this._milliseconds;if(a=A(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+uc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(vc(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function xc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*q(this._months/12)}function yc(a){return function(){return this.as(a)}}function zc(a){return a=A(a),this[a+"s"]()}function Ac(a){return function(){return this._data[a]}}function Bc(){return p(this.days()/7)}function Cc(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function Dc(a,b,c){var d=Ya(a).abs(),e=ke(d.as("s")),f=ke(d.as("m")),g=ke(d.as("h")),h=ke(d.as("d")),i=ke(d.as("M")),j=ke(d.as("y")),k=e<le.s&&["s",e]||1===f&&["m"]||f<le.m&&["mm",f]||1===g&&["h"]||g<le.h&&["hh",g]||1===h&&["d"]||h<le.d&&["dd",h]||1===i&&["M"]||i<le.M&&["MM",i]||1===j&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,Cc.apply(null,k)}function Ec(a,b){return void 0===le[a]?!1:void 0===b?le[a]:(le[a]=b,!0)}function Fc(a){var b=this.localeData(),c=Dc(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function Gc(){var a,b,c,d=me(this._milliseconds)/1e3,e=me(this._days),f=me(this._months);a=p(d/60),b=p(a/60),d%=60,a%=60,c=p(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var Hc,Ic,Jc=a.momentProperties=[],Kc=!1,Lc={},Mc={},Nc=/(\[[^\[]*\])|(\\)?(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,9}|x|X|zz?|ZZ?|.)/g,Oc=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Pc={},Qc={},Rc=/\d/,Sc=/\d\d/,Tc=/\d{3}/,Uc=/\d{4}/,Vc=/[+-]?\d{6}/,Wc=/\d\d?/,Xc=/\d{1,3}/,Yc=/\d{1,4}/,Zc=/[+-]?\d{1,6}/,$c=/\d+/,_c=/[+-]?\d+/,ad=/Z|[+-]\d\d:?\d\d/gi,bd=/[+-]?\d+(\.\d{1,3})?/,cd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,dd={},ed={},fd=0,gd=1,hd=2,id=3,jd=4,kd=5,ld=6;H("M",["MM",2],"Mo",function(){return this.month()+1}),H("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),H("MMMM",0,0,function(a){return this.localeData().months(this,a)}),z("month","M"),N("M",Wc),N("MM",Wc,Sc),N("MMM",cd),N("MMMM",cd),Q(["M","MM"],function(a,b){b[gd]=q(a)-1}),Q(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[gd]=e:j(c).invalidMonth=a});var md="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),nd="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),od={};a.suppressDeprecationWarnings=!1;var pd=/^\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)?)?$/,qd=[["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}/]],rd=[["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/]],sd=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=aa("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+(a._useUTC?" UTC":""))}),H(0,["YY",2],0,function(){return this.year()%100}),H(0,["YYYY",4],0,"year"),H(0,["YYYYY",5],0,"year"),H(0,["YYYYYY",6,!0],0,"year"),z("year","y"),N("Y",_c),N("YY",Wc,Sc),N("YYYY",Yc,Uc),N("YYYYY",Zc,Vc),N("YYYYYY",Zc,Vc),Q(["YYYYY","YYYYYY"],fd),Q("YYYY",function(b,c){c[fd]=2===b.length?a.parseTwoDigitYear(b):q(b)}),Q("YY",function(b,c){c[fd]=a.parseTwoDigitYear(b)}),a.parseTwoDigitYear=function(a){return q(a)+(q(a)>68?1900:2e3)};var td=C("FullYear",!1);H("w",["ww",2],"wo","week"),H("W",["WW",2],"Wo","isoWeek"),z("week","w"),z("isoWeek","W"),N("w",Wc),N("ww",Wc,Sc),N("W",Wc),N("WW",Wc,Sc),R(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=q(a)});var ud={dow:0,doy:6};H("DDD",["DDDD",3],"DDDo","dayOfYear"),z("dayOfYear","DDD"),N("DDD",Xc),N("DDDD",Tc),Q(["DDD","DDDD"],function(a,b,c){c._dayOfYear=q(a)}),a.ISO_8601=function(){};var vd=aa("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var a=Da.apply(null,arguments);return this>a?this:a}),wd=aa("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var a=Da.apply(null,arguments);return a>this?this:a});Ja("Z",":"),Ja("ZZ",""),N("Z",ad),N("ZZ",ad),Q(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ka(a)});var xd=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var yd=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,zd=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Ya.fn=Ha.prototype;var Ad=ab(1,"add"),Bd=ab(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var Cd=aa("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});H(0,["gg",2],0,function(){return this.weekYear()%100}),H(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Db("gggg","weekYear"),Db("ggggg","weekYear"),Db("GGGG","isoWeekYear"),Db("GGGGG","isoWeekYear"),z("weekYear","gg"),z("isoWeekYear","GG"),N("G",_c),N("g",_c),N("GG",Wc,Sc),N("gg",Wc,Sc),N("GGGG",Yc,Uc),N("gggg",Yc,Uc),N("GGGGG",Zc,Vc),N("ggggg",Zc,Vc),R(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=q(a)}),R(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),H("Q",0,0,"quarter"),z("quarter","Q"),N("Q",Rc),Q("Q",function(a,b){b[gd]=3*(q(a)-1)}),H("D",["DD",2],"Do","date"),z("date","D"),N("D",Wc),N("DD",Wc,Sc),N("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),Q(["D","DD"],hd),Q("Do",function(a,b){b[hd]=q(a.match(Wc)[0],10)});var Dd=C("Date",!0);H("d",0,"do","day"),H("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),H("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),H("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),H("e",0,0,"weekday"),H("E",0,0,"isoWeekday"),z("day","d"),z("weekday","e"),z("isoWeekday","E"),N("d",Wc),N("e",Wc),N("E",Wc),N("dd",cd),N("ddd",cd),N("dddd",cd),R(["dd","ddd","dddd"],function(a,b,c){var d=c._locale.weekdaysParse(a);null!=d?b.d=d:j(c).invalidWeekday=a}),R(["d","e","E"],function(a,b,c,d){b[d]=q(a)});var Ed="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Fd="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Gd="Su_Mo_Tu_We_Th_Fr_Sa".split("_");H("H",["HH",2],0,"hour"),H("h",["hh",2],0,function(){return this.hours()%12||12}),Sb("a",!0),Sb("A",!1),z("hour","h"),N("a",Tb),N("A",Tb),N("H",Wc),N("h",Wc),N("HH",Wc,Sc),N("hh",Wc,Sc),Q(["H","HH"],id),Q(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),Q(["h","hh"],function(a,b,c){b[id]=q(a),j(c).bigHour=!0});var Hd=/[ap]\.?m?\.?/i,Id=C("Hours",!0);H("m",["mm",2],0,"minute"),z("minute","m"),N("m",Wc),N("mm",Wc,Sc),Q(["m","mm"],jd);var Jd=C("Minutes",!1);H("s",["ss",2],0,"second"),z("second","s"),N("s",Wc),N("ss",Wc,Sc),Q(["s","ss"],kd);var Kd=C("Seconds",!1);H("S",0,0,function(){return~~(this.millisecond()/100)}),H(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),H(0,["SSS",3],0,"millisecond"),H(0,["SSSS",4],0,function(){return 10*this.millisecond()}),H(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),H(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),H(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),H(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),H(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),z("millisecond","ms"),N("S",Xc,Rc),N("SS",Xc,Sc),N("SSS",Xc,Tc);var Ld;for(Ld="SSSS";Ld.length<=9;Ld+="S")N(Ld,$c);for(Ld="S";Ld.length<=9;Ld+="S")Q(Ld,Wb);var Md=C("Milliseconds",!1);H("z",0,0,"zoneAbbr"),H("zz",0,0,"zoneName");var Nd=n.prototype;Nd.add=Ad,Nd.calendar=cb,Nd.clone=db,Nd.diff=ib,Nd.endOf=ub,Nd.format=mb,Nd.from=nb,Nd.fromNow=ob,Nd.to=pb,Nd.toNow=qb,Nd.get=F,Nd.invalidAt=Cb,Nd.isAfter=eb,Nd.isBefore=fb,Nd.isBetween=gb,Nd.isSame=hb,Nd.isValid=Ab,Nd.lang=Cd,Nd.locale=rb,Nd.localeData=sb,Nd.max=wd,Nd.min=vd,Nd.parsingFlags=Bb,Nd.set=F,Nd.startOf=tb,Nd.subtract=Bd,Nd.toArray=yb,Nd.toObject=zb,Nd.toDate=xb,Nd.toISOString=lb,Nd.toJSON=lb,Nd.toString=kb,Nd.unix=wb,Nd.valueOf=vb,Nd.year=td,Nd.isLeapYear=ia,Nd.weekYear=Fb,Nd.isoWeekYear=Gb,Nd.quarter=Nd.quarters=Jb,Nd.month=Y,Nd.daysInMonth=Z,Nd.week=Nd.weeks=na,Nd.isoWeek=Nd.isoWeeks=oa,Nd.weeksInYear=Ib,Nd.isoWeeksInYear=Hb,Nd.date=Dd,Nd.day=Nd.days=Pb,Nd.weekday=Qb,Nd.isoWeekday=Rb,Nd.dayOfYear=qa,Nd.hour=Nd.hours=Id,Nd.minute=Nd.minutes=Jd,Nd.second=Nd.seconds=Kd,
-Nd.millisecond=Nd.milliseconds=Md,Nd.utcOffset=Na,Nd.utc=Pa,Nd.local=Qa,Nd.parseZone=Ra,Nd.hasAlignedHourOffset=Sa,Nd.isDST=Ta,Nd.isDSTShifted=Ua,Nd.isLocal=Va,Nd.isUtcOffset=Wa,Nd.isUtc=Xa,Nd.isUTC=Xa,Nd.zoneAbbr=Xb,Nd.zoneName=Yb,Nd.dates=aa("dates accessor is deprecated. Use date instead.",Dd),Nd.months=aa("months accessor is deprecated. Use month instead",Y),Nd.years=aa("years accessor is deprecated. Use year instead",td),Nd.zone=aa("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Oa);var Od=Nd,Pd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Qd={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Rd="Invalid date",Sd="%d",Td=/\d{1,2}/,Ud={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"},Vd=s.prototype;Vd._calendar=Pd,Vd.calendar=_b,Vd._longDateFormat=Qd,Vd.longDateFormat=ac,Vd._invalidDate=Rd,Vd.invalidDate=bc,Vd._ordinal=Sd,Vd.ordinal=cc,Vd._ordinalParse=Td,Vd.preparse=dc,Vd.postformat=dc,Vd._relativeTime=Ud,Vd.relativeTime=ec,Vd.pastFuture=fc,Vd.set=gc,Vd.months=U,Vd._months=md,Vd.monthsShort=V,Vd._monthsShort=nd,Vd.monthsParse=W,Vd.week=ka,Vd._week=ud,Vd.firstDayOfYear=ma,Vd.firstDayOfWeek=la,Vd.weekdays=Lb,Vd._weekdays=Ed,Vd.weekdaysMin=Nb,Vd._weekdaysMin=Gd,Vd.weekdaysShort=Mb,Vd._weekdaysShort=Fd,Vd.weekdaysParse=Ob,Vd.isPM=Ub,Vd._meridiemParse=Hd,Vd.meridiem=Vb,w("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===q(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=aa("moment.lang is deprecated. Use moment.locale instead.",w),a.langData=aa("moment.langData is deprecated. Use moment.localeData instead.",y);var Wd=Math.abs,Xd=yc("ms"),Yd=yc("s"),Zd=yc("m"),$d=yc("h"),_d=yc("d"),ae=yc("w"),be=yc("M"),ce=yc("y"),de=Ac("milliseconds"),ee=Ac("seconds"),fe=Ac("minutes"),ge=Ac("hours"),he=Ac("days"),ie=Ac("months"),je=Ac("years"),ke=Math.round,le={s:45,m:45,h:22,d:26,M:11},me=Math.abs,ne=Ha.prototype;ne.abs=oc,ne.add=qc,ne.subtract=rc,ne.as=wc,ne.asMilliseconds=Xd,ne.asSeconds=Yd,ne.asMinutes=Zd,ne.asHours=$d,ne.asDays=_d,ne.asWeeks=ae,ne.asMonths=be,ne.asYears=ce,ne.valueOf=xc,ne._bubble=tc,ne.get=zc,ne.milliseconds=de,ne.seconds=ee,ne.minutes=fe,ne.hours=ge,ne.days=he,ne.weeks=Bc,ne.months=ie,ne.years=je,ne.humanize=Fc,ne.toISOString=Gc,ne.toString=Gc,ne.toJSON=Gc,ne.locale=rb,ne.localeData=sb,ne.toIsoString=aa("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Gc),ne.lang=Cd,H("X",0,0,"unix"),H("x",0,0,"valueOf"),N("x",_c),N("X",bd),Q("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),Q("x",function(a,b,c){c._d=new Date(q(a))}),a.version="2.10.6",b(Da),a.fn=Od,a.min=Fa,a.max=Ga,a.utc=h,a.unix=Zb,a.months=jc,a.isDate=d,a.locale=w,a.invalid=l,a.duration=Ya,a.isMoment=o,a.weekdays=lc,a.parseZone=$b,a.localeData=y,a.isDuration=Ia,a.monthsShort=kc,a.weekdaysMin=nc,a.defineLocale=x,a.weekdaysShort=mc,a.normalizeUnits=A,a.relativeTimeThreshold=Ec;var oe=a;return oe});
\ No newline at end of file
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function c(){return e.apply(null,arguments)}function o(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function u(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e){return void 0===e}function h(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,s=[];for(n=0;n<e.length;++n)s.push(t(e[n],n));return s}function m(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function _(e,t){for(var n in t)m(t,n)&&(e[n]=t[n]);return m(t,"toString")&&(e.toString=t.toString),m(t,"valueOf")&&(e.valueOf=t.valueOf),e}function y(e,t,n,s){return Tt(e,t,n,s,!0).utc()}function g(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function v(e){if(null==e._isValid){var t=g(e),n=i.call(t.parsedDateParts,function(e){return null!=e}),s=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return s;e._isValid=s}return e._isValid}function p(e){var t=y(NaN);return null!=e?_(g(t),e):g(t).userInvalidated=!0,t}i=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1};var r=c.momentProperties=[];function w(e,t){var n,s,i;if(l(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),l(t._i)||(e._i=t._i),l(t._f)||(e._f=t._f),l(t._l)||(e._l=t._l),l(t._strict)||(e._strict=t._strict),l(t._tzm)||(e._tzm=t._tzm),l(t._isUTC)||(e._isUTC=t._isUTC),l(t._offset)||(e._offset=t._offset),l(t._pf)||(e._pf=g(t)),l(t._locale)||(e._locale=t._locale),0<r.length)for(n=0;n<r.length;n++)l(i=t[s=r[n]])||(e[s]=i);return e}var t=!1;function M(e){w(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===t&&(t=!0,c.updateOffset(this),t=!1)}function k(e){return e instanceof M||null!=e&&null!=e._isAMomentObject}function S(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function D(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=S(t)),n}function a(e,t,n){var s,i=Math.min(e.length,t.length),r=Math.abs(e.length-t.length),a=0;for(s=0;s<i;s++)(n&&e[s]!==t[s]||!n&&D(e[s])!==D(t[s]))&&a++;return a+r}function Y(e){!1===c.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function n(i,r){var a=!0;return _(function(){if(null!=c.deprecationHandler&&c.deprecationHandler(null,i),a){for(var e,t=[],n=0;n<arguments.length;n++){if(e="","object"==typeof arguments[n]){for(var s in e+="\n["+n+"] ",arguments[0])e+=s+": "+arguments[0][s]+", ";e=e.slice(0,-2)}else e=arguments[n];t.push(e)}Y(i+"\nArguments: "+Array.prototype.slice.call(t).join("")+"\n"+(new Error).stack),a=!1}return r.apply(this,arguments)},r)}var s,O={};function T(e,t){null!=c.deprecationHandler&&c.deprecationHandler(e,t),O[e]||(Y(t),O[e]=!0)}function b(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function x(e,t){var n,s=_({},e);for(n in t)m(t,n)&&(u(e[n])&&u(t[n])?(s[n]={},_(s[n],e[n]),_(s[n],t[n])):null!=t[n]?s[n]=t[n]:delete s[n]);for(n in e)m(e,n)&&!m(t,n)&&u(e[n])&&(s[n]=_({},s[n]));return s}function P(e){null!=e&&this.set(e)}c.suppressDeprecationWarnings=!1,c.deprecationHandler=null,s=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)m(e,t)&&n.push(t);return n};var W={};function C(e,t){var n=e.toLowerCase();W[n]=W[n+"s"]=W[t]=e}function H(e){return"string"==typeof e?W[e]||W[e.toLowerCase()]:void 0}function R(e){var t,n,s={};for(n in e)m(e,n)&&(t=H(n))&&(s[t]=e[n]);return s}var U={};function F(e,t){U[e]=t}function L(e,t,n){var s=""+Math.abs(e),i=t-s.length;return(0<=e?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+s}var N=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,G=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,V={},E={};function I(e,t,n,s){var i=s;"string"==typeof s&&(i=function(){return this[s]()}),e&&(E[e]=i),t&&(E[t[0]]=function(){return L(i.apply(this,arguments),t[1],t[2])}),n&&(E[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function A(e,t){return e.isValid()?(t=j(t,e.localeData()),V[t]=V[t]||function(s){var e,i,t,r=s.match(N);for(e=0,i=r.length;e<i;e++)E[r[e]]?r[e]=E[r[e]]:r[e]=(t=r[e]).match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"");return function(e){var t,n="";for(t=0;t<i;t++)n+=b(r[t])?r[t].call(e,s):r[t];return n}}(t),V[t](e)):e.localeData().invalidDate()}function j(e,t){var n=5;function s(e){return t.longDateFormat(e)||e}for(G.lastIndex=0;0<=n&&G.test(e);)e=e.replace(G,s),G.lastIndex=0,n-=1;return e}var Z=/\d/,z=/\d\d/,$=/\d{3}/,q=/\d{4}/,J=/[+-]?\d{6}/,B=/\d\d?/,Q=/\d\d\d\d?/,X=/\d\d\d\d\d\d?/,K=/\d{1,3}/,ee=/\d{1,4}/,te=/[+-]?\d{1,6}/,ne=/\d+/,se=/[+-]?\d+/,ie=/Z|[+-]\d\d:?\d\d/gi,re=/Z|[+-]\d\d(?::?\d\d)?/gi,ae=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,oe={};function ue(e,n,s){oe[e]=b(n)?n:function(e,t){return e&&s?s:n}}function le(e,t){return m(oe,e)?oe[e](t._strict,t._locale):new RegExp(he(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function he(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var de={};function ce(e,n){var t,s=n;for("string"==typeof e&&(e=[e]),h(n)&&(s=function(e,t){t[n]=D(e)}),t=0;t<e.length;t++)de[e[t]]=s}function fe(e,i){ce(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}var me=0,_e=1,ye=2,ge=3,ve=4,pe=5,we=6,Me=7,ke=8;function Se(e){return De(e)?366:365}function De(e){return e%4==0&&e%100!=0||e%400==0}I("Y",0,0,function(){var e=this.year();return e<=9999?""+e:"+"+e}),I(0,["YY",2],0,function(){return this.year()%100}),I(0,["YYYY",4],0,"year"),I(0,["YYYYY",5],0,"year"),I(0,["YYYYYY",6,!0],0,"year"),C("year","y"),F("year",1),ue("Y",se),ue("YY",B,z),ue("YYYY",ee,q),ue("YYYYY",te,J),ue("YYYYYY",te,J),ce(["YYYYY","YYYYYY"],me),ce("YYYY",function(e,t){t[me]=2===e.length?c.parseTwoDigitYear(e):D(e)}),ce("YY",function(e,t){t[me]=c.parseTwoDigitYear(e)}),ce("Y",function(e,t){t[me]=parseInt(e,10)}),c.parseTwoDigitYear=function(e){return D(e)+(68<D(e)?1900:2e3)};var Ye,Oe=Te("FullYear",!0);function Te(t,n){return function(e){return null!=e?(xe(this,t,e),c.updateOffset(this,n),this):be(this,t)}}function be(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function xe(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&De(e.year())&&1===e.month()&&29===e.date()?e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Pe(n,e.month())):e._d["set"+(e._isUTC?"UTC":"")+t](n))}function Pe(e,t){if(isNaN(e)||isNaN(t))return NaN;var n,s=(t%(n=12)+n)%n;return e+=(t-s)/12,1===s?De(e)?29:28:31-s%7%2}Ye=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},I("M",["MM",2],"Mo",function(){return this.month()+1}),I("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),I("MMMM",0,0,function(e){return this.localeData().months(this,e)}),C("month","M"),F("month",8),ue("M",B),ue("MM",B,z),ue("MMM",function(e,t){return t.monthsShortRegex(e)}),ue("MMMM",function(e,t){return t.monthsRegex(e)}),ce(["M","MM"],function(e,t){t[_e]=D(e)-1}),ce(["MMM","MMMM"],function(e,t,n,s){var i=n._locale.monthsParse(e,s,n._strict);null!=i?t[_e]=i:g(n).invalidMonth=e});var We=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Ce="January_February_March_April_May_June_July_August_September_October_November_December".split("_");var He="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Re(e,t){var n;if(!e.isValid())return e;if("string"==typeof t)if(/^\d+$/.test(t))t=D(t);else if(!h(t=e.localeData().monthsParse(t)))return e;return n=Math.min(e.date(),Pe(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e}function Ue(e){return null!=e?(Re(this,e),c.updateOffset(this,!0),this):be(this,"Month")}var Fe=ae;var Le=ae;function Ne(){function e(e,t){return t.length-e.length}var t,n,s=[],i=[],r=[];for(t=0;t<12;t++)n=y([2e3,t]),s.push(this.monthsShort(n,"")),i.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(s.sort(e),i.sort(e),r.sort(e),t=0;t<12;t++)s[t]=he(s[t]),i[t]=he(i[t]);for(t=0;t<24;t++)r[t]=he(r[t]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Ge(e){var t;if(e<100&&0<=e){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function Ve(e,t,n){var s=7+t-n;return-((7+Ge(e,0,s).getUTCDay()-t)%7)+s-1}function Ee(e,t,n,s,i){var r,a,o=1+7*(t-1)+(7+n-s)%7+Ve(e,s,i);return a=o<=0?Se(r=e-1)+o:o>Se(e)?(r=e+1,o-Se(e)):(r=e,o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(Se(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),C("week","w"),C("isoWeek","W"),F("week",5),F("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=D(e)});function je(e,t){return e.slice(t,7).concat(e.slice(0,t))}I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),C("day","d"),C("weekday","e"),C("isoWeekday","E"),F("day",11),F("weekday",11),F("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=D(e)});var Ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var $e="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var qe=ae;var Je=ae;var Be=ae;function Qe(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=he(o[t]),u[t]=he(u[t]),l[t]=he(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Ke(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Xe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Ke("a",!0),Ke("A",!1),C("hour","h"),F("hour",13),ue("a",et),ue("A",et),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=D(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=D(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i))});var tt,nt=Te("Hours",!0),st={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d 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"},months:Ce,monthsShort:He,week:{dow:0,doy:6},weekdays:Ze,weekdaysMin:$e,weekdaysShort:ze,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=l(t)?ht(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,s=st;if(t.abbr=e,null!=it[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])s=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return it[e]=new P(x(s,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ht(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!o(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=at(e[r]).split("-")).length,n=(n=at(e[r+1]))?n.split("-"):null;0<t;){if(s=ot(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&a(i,n,!0)>=t-1)break;t--}r++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11<n[_e]?_e:n[ye]<1||n[ye]>Pe(n[me],n[_e])?ye:n[ge]<0||24<n[ge]||24===n[ge]&&(0!==n[ve]||0!==n[pe]||0!==n[we])?ge:n[ve]<0||59<n[ve]?ve:n[pe]<0||59<n[pe]?pe:n[we]<0||999<n[we]?we:-1,g(e)._overflowDayOfYear&&(t<me||ye<t)&&(t=ye),g(e)._overflowWeeks&&-1===t&&(t=Me),g(e)._overflowWeekday&&-1===t&&(t=ke),g(e).overflow=t),e}function ct(e,t,n){return null!=e?e:null!=t?t:n}function ft(e){var t,n,s,i,r,a=[];if(!e._d){var o,u;for(o=e,u=new Date(c.now()),s=o._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],e._w&&null==e._a[ye]&&null==e._a[_e]&&function(e){var t,n,s,i,r,a,o,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)r=1,a=4,n=ct(t.GG,e._a[me],Ie(bt(),1,4).year),s=ct(t.W,1),((i=ct(t.E,1))<1||7<i)&&(u=!0);else{r=e._locale._week.dow,a=e._locale._week.doy;var l=Ie(bt(),r,a);n=ct(t.gg,e._a[me],l.year),s=ct(t.w,l.week),null!=t.d?((i=t.d)<0||6<i)&&(u=!0):null!=t.e?(i=t.e+r,(t.e<0||6<t.e)&&(u=!0)):i=r}s<1||s>Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ct(e._a[me],s[me]),(e._dayOfYear>Se(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[pe]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var mt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,s,i,r,a,o=e._i,u=mt.exec(o)||_t.exec(o);if(u){for(g(e).iso=!0,t=0,n=gt.length;t<n;t++)if(gt[t][1].exec(u[1])){i=gt[t][0],s=!1!==gt[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=vt.length;t<n;t++)if(vt[t][1].exec(u[3])){r=(u[2]||" ")+vt[t][0];break}if(null==r)return void(e._isValid=!1)}if(!s&&null!=r)return void(e._isValid=!1);if(u[4]){if(!yt.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),Yt(e)}else e._isValid=!1}var Mt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function kt(e,t,n,s,i,r){var a=[function(e){var t=parseInt(e,10);{if(t<=49)return 2e3+t;if(t<=999)return 1900+t}return t}(e),He.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&a.push(parseInt(r,10)),a}var St={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Dt(e){var t,n,s,i=Mt.exec(e._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var r=kt(i[4],i[3],i[2],i[5],i[6],i[7]);if(t=i[1],n=r,s=e,t&&ze.indexOf(t)!==new Date(n[0],n[1],n[2]).getDay()&&(g(s).weekdayMismatch=!0,!(s._isValid=!1)))return;e._a=r,e._tzm=function(e,t,n){if(e)return St[e];if(t)return 0;var s=parseInt(n,10),i=s%100;return(s-i)/100*60+i}(i[8],i[9],i[10]),e._d=Ge.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),g(e).rfc2822=!0}else e._isValid=!1}function Yt(e){if(e._f!==c.ISO_8601)if(e._f!==c.RFC_2822){e._a=[],g(e).empty=!0;var t,n,s,i,r,a,o,u,l=""+e._i,h=l.length,d=0;for(s=j(e._f,e._locale).match(N)||[],t=0;t<s.length;t++)i=s[t],(n=(l.match(le(i,e))||[])[0])&&(0<(r=l.substr(0,l.indexOf(n))).length&&g(e).unusedInput.push(r),l=l.slice(l.indexOf(n)+n.length),d+=n.length),E[i]?(n?g(e).empty=!1:g(e).unusedTokens.push(i),a=i,u=e,null!=(o=n)&&m(de,a)&&de[a](o,u._a,u,a)):e._strict&&!n&&g(e).unusedTokens.push(i);g(e).charsLeftOver=h-d,0<l.length&&g(e).unusedInput.push(l),e._a[ge]<=12&&!0===g(e).bigHour&&0<e._a[ge]&&(g(e).bigHour=void 0),g(e).parsedDateParts=e._a.slice(0),g(e).meridiem=e._meridiem,e._a[ge]=function(e,t,n){var s;if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((s=e.isPM(n))&&t<12&&(t+=12),s||12!==t||(t=0)),t)}(e._locale,e._a[ge],e._meridiem),ft(e),dt(e)}else Dt(e);else wt(e)}function Ot(e){var t,n,s,i,r=e._i,a=e._f;return e._locale=e._locale||ht(e._l),null===r||void 0===a&&""===r?p({nullInput:!0}):("string"==typeof r&&(e._i=r=e._locale.preparse(r)),k(r)?new M(dt(r)):(d(r)?e._d=r:o(a)?function(e){var t,n,s,i,r;if(0===e._f.length)return g(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<e._f.length;i++)r=0,t=w({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],Yt(t),v(t)&&(r+=g(t).charsLeftOver,r+=10*g(t).unusedTokens.length,g(t).score=r,(null==s||r<s)&&(s=r,n=t));_(e,n||t)}(e):a?Yt(e):l(n=(t=e)._i)?t._d=new Date(c.now()):d(n)?t._d=new Date(n.valueOf()):"string"==typeof n?(s=t,null===(i=pt.exec(s._i))?(wt(s),!1===s._isValid&&(delete s._isValid,Dt(s),!1===s._isValid&&(delete s._isValid,c.createFromInputFallback(s)))):s._d=new Date(+i[1])):o(n)?(t._a=f(n.slice(0),function(e){return parseInt(e,10)}),ft(t)):u(n)?function(e){if(!e._d){var t=R(e._i);e._a=f([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),ft(e)}}(t):h(n)?t._d=new Date(n):c.createFromInputFallback(t),v(e)||(e._d=null),e))}function Tt(e,t,n,s,i){var r,a={};return!0!==n&&!1!==n||(s=n,n=void 0),(u(e)&&function(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}(e)||o(e)&&0===e.length)&&(e=void 0),a._isAMomentObject=!0,a._useUTC=a._isUTC=i,a._l=n,a._i=e,a._f=t,a._strict=s,(r=new M(dt(Ot(a))))._nextDay&&(r.add(1,"d"),r._nextDay=void 0),r}function bt(e,t,n,s){return Tt(e,t,n,s,!1)}c.createFromInputFallback=n("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),c.ISO_8601=function(){},c.RFC_2822=function(){};var xt=n("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=bt.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:p()}),Pt=n("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=bt.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:p()});function Wt(e,t){var n,s;if(1===t.length&&o(t[0])&&(t=t[0]),!t.length)return bt();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Ct=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ht(e){var t=R(e),n=t.year||0,s=t.quarter||0,i=t.month||0,r=t.week||t.isoWeek||0,a=t.day||0,o=t.hour||0,u=t.minute||0,l=t.second||0,h=t.millisecond||0;this._isValid=function(e){for(var t in e)if(-1===Ye.call(Ct,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,s=0;s<Ct.length;++s)if(e[Ct[s]]){if(n)return!1;parseFloat(e[Ct[s]])!==D(e[Ct[s]])&&(n=!0)}return!0}(t),this._milliseconds=+h+1e3*l+6e4*u+1e3*o*60*60,this._days=+a+7*r,this._months=+i+3*s+12*n,this._data={},this._locale=ht(),this._bubble()}function Rt(e){return e instanceof Ht}function Ut(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Ft(e,n){I(e,0,0,function(){var e=this.utcOffset(),t="+";return e<0&&(e=-e,t="-"),t+L(~~(e/60),2)+n+L(~~e%60,2)})}Ft("Z",":"),Ft("ZZ",""),ue("Z",re),ue("ZZ",re),ce(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Nt(re,e)});var Lt=/([\+\-]|\d\d)/gi;function Nt(e,t){var n=(t||"").match(e);if(null===n)return null;var s=((n[n.length-1]||[])+"").match(Lt)||["-",0,0],i=60*s[1]+D(s[2]);return 0===i?0:"+"===s[0]?i:-i}function Gt(e,t){var n,s;return t._isUTC?(n=t.clone(),s=(k(e)||d(e)?e.valueOf():bt(e).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+s),c.updateOffset(n,!1),n):bt(e).local()}function Vt(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Et(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}c.updateOffset=function(){};var It=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,At=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function jt(e,t){var n,s,i,r=e,a=null;return Rt(e)?r={ms:e._milliseconds,d:e._days,M:e._months}:h(e)?(r={},t?r[t]=e:r.milliseconds=e):(a=It.exec(e))?(n="-"===a[1]?-1:1,r={y:0,d:D(a[ye])*n,h:D(a[ge])*n,m:D(a[ve])*n,s:D(a[pe])*n,ms:D(Ut(1e3*a[we]))*n}):(a=At.exec(e))?(n="-"===a[1]?-1:1,r={y:Zt(a[2],n),M:Zt(a[3],n),w:Zt(a[4],n),d:Zt(a[5],n),h:Zt(a[6],n),m:Zt(a[7],n),s:Zt(a[8],n)}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(i=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=Gt(t,e),e.isBefore(t)?n=zt(e,t):((n=zt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(bt(r.from),bt(r.to)),(r={}).ms=i.milliseconds,r.M=i.months),s=new Ht(r),Rt(e)&&m(e,"_locale")&&(s._locale=e._locale),s}function Zt(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function zt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function $t(s,i){return function(e,t){var n;return null===t||isNaN(+t)||(T(i,"moment()."+i+"(period, number) is deprecated. Please use moment()."+i+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),n=e,e=t,t=n),qt(this,jt(e="string"==typeof e?+e:e,t),s),this}}function qt(e,t,n,s){var i=t._milliseconds,r=Ut(t._days),a=Ut(t._months);e.isValid()&&(s=null==s||s,a&&Re(e,be(e,"Month")+a*n),r&&xe(e,"Date",be(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s&&c.updateOffset(e,r||a))}jt.fn=Ht.prototype,jt.invalid=function(){return jt(NaN)};var Jt=$t(1,"add"),Bt=$t(-1,"subtract");function Qt(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),s=e.clone().add(n,"months");return-(n+(t-s<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(n+1,"months")-s)))||0}function Xt(e){var t;return void 0===e?this._locale._abbr:(null!=(t=ht(e))&&(this._locale=t),this)}c.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",c.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Kt=n("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function en(){return this._locale}var tn=126227808e5;function nn(e,t){return(e%t+t)%t}function sn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-tn:new Date(e,t,n).valueOf()}function rn(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-tn:Date.UTC(e,t,n)}function an(e,t){I(0,[e,e.length],0,t)}function on(e,t,n,s,i){var r;return null==e?Ie(this,s,i).year:((r=Ae(e,s,i))<t&&(t=r),function(e,t,n,s,i){var r=Ee(e,t,n,s,i),a=Ge(r.year,0,r.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}.call(this,e,t,n,s,i))}I(0,["gg",2],0,function(){return this.weekYear()%100}),I(0,["GG",2],0,function(){return this.isoWeekYear()%100}),an("gggg","weekYear"),an("ggggg","weekYear"),an("GGGG","isoWeekYear"),an("GGGGG","isoWeekYear"),C("weekYear","gg"),C("isoWeekYear","GG"),F("weekYear",1),F("isoWeekYear",1),ue("G",se),ue("g",se),ue("GG",B,z),ue("gg",B,z),ue("GGGG",ee,q),ue("gggg",ee,q),ue("GGGGG",te,J),ue("ggggg",te,J),fe(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=D(e)}),fe(["gg","GG"],function(e,t,n,s){t[s]=c.parseTwoDigitYear(e)}),I("Q",0,"Qo","quarter"),C("quarter","Q"),F("quarter",7),ue("Q",Z),ce("Q",function(e,t){t[_e]=3*(D(e)-1)}),I("D",["DD",2],"Do","date"),C("date","D"),F("date",9),ue("D",B),ue("DD",B,z),ue("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),ce(["D","DD"],ye),ce("Do",function(e,t){t[ye]=D(e.match(B)[0])});var un=Te("Date",!0);I("DDD",["DDDD",3],"DDDo","dayOfYear"),C("dayOfYear","DDD"),F("dayOfYear",4),ue("DDD",K),ue("DDDD",$),ce(["DDD","DDDD"],function(e,t,n){n._dayOfYear=D(e)}),I("m",["mm",2],0,"minute"),C("minute","m"),F("minute",14),ue("m",B),ue("mm",B,z),ce(["m","mm"],ve);var ln=Te("Minutes",!1);I("s",["ss",2],0,"second"),C("second","s"),F("second",15),ue("s",B),ue("ss",B,z),ce(["s","ss"],pe);var hn,dn=Te("Seconds",!1);for(I("S",0,0,function(){return~~(this.millisecond()/100)}),I(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),I(0,["SSS",3],0,"millisecond"),I(0,["SSSS",4],0,function(){return 10*this.millisecond()}),I(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),I(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),I(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),I(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),I(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),C("millisecond","ms"),F("millisecond",16),ue("S",K,Z),ue("SS",K,z),ue("SSS",K,$),hn="SSSS";hn.length<=9;hn+="S")ue(hn,ne);function cn(e,t){t[we]=D(1e3*("0."+e))}for(hn="S";hn.length<=9;hn+="S")ce(hn,cn);var fn=Te("Milliseconds",!1);I("z",0,0,"zoneAbbr"),I("zz",0,0,"zoneName");var mn=M.prototype;function _n(e){return e}mn.add=Jt,mn.calendar=function(e,t){var n=e||bt(),s=Gt(n,this).startOf("day"),i=c.calendarFormat(this,s)||"sameElse",r=t&&(b(t[i])?t[i].call(this,n):t[i]);return this.format(r||this.localeData().calendar(i,this,bt(n)))},mn.clone=function(){return new M(this)},mn.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Gt(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=H(t)){case"year":r=Qt(this,s)/12;break;case"month":r=Qt(this,s);break;case"quarter":r=Qt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:S(r)},mn.endOf=function(e){var t;if(void 0===(e=H(e))||"millisecond"===e||!this.isValid())return this;var n=this._isUTC?rn:sn;switch(e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf(),t+=36e5-nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":t=this._d.valueOf(),t+=6e4-nn(t,6e4)-1;break;case"second":t=this._d.valueOf(),t+=1e3-nn(t,1e3)-1;break}return this._d.setTime(t),c.updateOffset(this,!0),this},mn.format=function(e){e||(e=this.isUtc()?c.defaultFormatUtc:c.defaultFormat);var t=A(this,e);return this.localeData().postformat(t)},mn.from=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||bt(e).isValid())?jt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},mn.fromNow=function(e){return this.from(bt(),e)},mn.to=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||bt(e).isValid())?jt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},mn.toNow=function(e){return this.to(bt(),e)},mn.get=function(e){return b(this[e=H(e)])?this[e]():this},mn.invalidAt=function(){return g(this).overflow},mn.isAfter=function(e,t){var n=k(e)?e:bt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},mn.isBefore=function(e,t){var n=k(e)?e:bt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},mn.isBetween=function(e,t,n,s){var i=k(e)?e:bt(e),r=k(t)?t:bt(t);return!!(this.isValid()&&i.isValid()&&r.isValid())&&("("===(s=s||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===s[1]?this.isBefore(r,n):!this.isAfter(r,n))},mn.isSame=function(e,t){var n,s=k(e)?e:bt(e);return!(!this.isValid()||!s.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()===s.valueOf():(n=s.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},mn.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},mn.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},mn.isValid=function(){return v(this)},mn.lang=Kt,mn.locale=Xt,mn.localeData=en,mn.max=Pt,mn.min=xt,mn.parsingFlags=function(){return _({},g(this))},mn.set=function(e,t){if("object"==typeof e)for(var n=function(e){var t=[];for(var n in e)t.push({unit:n,priority:U[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}(e=R(e)),s=0;s<n.length;s++)this[n[s].unit](e[n[s].unit]);else if(b(this[e=H(e)]))return this[e](t);return this},mn.startOf=function(e){var t;if(void 0===(e=H(e))||"millisecond"===e||!this.isValid())return this;var n=this._isUTC?rn:sn;switch(e){case"year":t=n(this.year(),0,1);break;case"quarter":t=n(this.year(),this.month()-this.month()%3,1);break;case"month":t=n(this.year(),this.month(),1);break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":t=n(this.year(),this.month(),this.date());break;case"hour":t=this._d.valueOf(),t-=nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":t=this._d.valueOf(),t-=nn(t,6e4);break;case"second":t=this._d.valueOf(),t-=nn(t,1e3);break}return this._d.setTime(t),c.updateOffset(this,!0),this},mn.subtract=Bt,mn.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},mn.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},mn.toDate=function(){return new Date(this.valueOf())},mn.toISOString=function(e){if(!this.isValid())return null;var t=!0!==e,n=t?this.clone().utc():this;return n.year()<0||9999<n.year()?A(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):b(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",A(n,"Z")):A(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},mn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e="moment",t="";this.isLocal()||(e=0===this.utcOffset()?"moment.utc":"moment.parseZone",t="Z");var n="["+e+'("]',s=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=t+'[")]';return this.format(n+s+"-MM-DD[T]HH:mm:ss.SSS"+i)},mn.toJSON=function(){return this.isValid()?this.toISOString():null},mn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},mn.unix=function(){return Math.floor(this.valueOf()/1e3)},mn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},mn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},mn.year=Oe,mn.isLeapYear=function(){return De(this.year())},mn.weekYear=function(e){return on.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},mn.isoWeekYear=function(e){return on.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},mn.quarter=mn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},mn.month=Ue,mn.daysInMonth=function(){return Pe(this.year(),this.month())},mn.week=mn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},mn.isoWeek=mn.isoWeeks=function(e){var t=Ie(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},mn.weeksInYear=function(){var e=this.localeData()._week;return Ae(this.year(),e.dow,e.doy)},mn.isoWeeksInYear=function(){return Ae(this.year(),1,4)},mn.date=un,mn.day=mn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t,n,s=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(t=e,n=this.localeData(),e="string"!=typeof t?t:isNaN(t)?"number"==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10),this.add(e-s,"d")):s},mn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},mn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null==e)return this.day()||7;var t,n,s=(t=e,n=this.localeData(),"string"==typeof t?n.weekdaysParse(t)%7||7:isNaN(t)?null:t);return this.day(this.day()%7?s:s-7)},mn.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},mn.hour=mn.hours=nt,mn.minute=mn.minutes=ln,mn.second=mn.seconds=dn,mn.millisecond=mn.milliseconds=fn,mn.utcOffset=function(e,t,n){var s,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Vt(this);if("string"==typeof e){if(null===(e=Nt(re,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(s=Vt(this)),this._offset=e,this._isUTC=!0,null!=s&&this.add(s,"m"),i!==e&&(!t||this._changeInProgress?qt(this,jt(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,c.updateOffset(this,!0),this._changeInProgress=null)),this},mn.utc=function(e){return this.utcOffset(0,e)},mn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Vt(this),"m")),this},mn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=Nt(ie,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},mn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?bt(e).utcOffset():0,(this.utcOffset()-e)%60==0)},mn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},mn.isUtc=Et,mn.isUTC=Et,mn.zoneAbbr=function(){return this._isUTC?"UTC":""},mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},mn.dates=n("dates accessor is deprecated. Use date instead.",un),mn.months=n("months accessor is deprecated. Use month instead",Ue),mn.years=n("years accessor is deprecated. Use year instead",Oe),mn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),mn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Ot(e))._a){var t=e._isUTC?y(e._a):bt(e._a);this._isDSTShifted=this.isValid()&&0<a(e._a,t.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted});var yn=P.prototype;function gn(e,t,n,s){var i=ht(),r=y().set(s,t);return i[n](r,e)}function vn(e,t,n){if(h(e)&&(t=e,e=void 0),e=e||"",null!=t)return gn(e,t,n,"month");var s,i=[];for(s=0;s<12;s++)i[s]=gn(e,s,n,"month");return i}function pn(e,t,n,s){t=("boolean"==typeof e?h(t)&&(n=t,t=void 0):(t=e,e=!1,h(n=t)&&(n=t,t=void 0)),t||"");var i,r=ht(),a=e?r._week.dow:0;if(null!=n)return gn(t,(n+a)%7,s,"day");var o=[];for(i=0;i<7;i++)o[i]=gn(t,(i+a)%7,s,"day");return o}yn.calendar=function(e,t,n){var s=this._calendar[e]||this._calendar.sameElse;return b(s)?s.call(t,n):s},yn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},yn.invalidDate=function(){return this._invalidDate},yn.ordinal=function(e){return this._ordinal.replace("%d",e)},yn.preparse=_n,yn.postformat=_n,yn.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return b(i)?i(e,t,n,s):i.replace(/%d/i,e)},yn.pastFuture=function(e,t){var n=this._relativeTime[0<e?"future":"past"];return b(n)?n(t):n.replace(/%s/i,t)},yn.set=function(e){var t,n;for(n in e)b(t=e[n])?this[n]=t:this["_"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},yn.months=function(e,t){return e?o(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||We).test(t)?"format":"standalone"][e.month()]:o(this._months)?this._months:this._months.standalone},yn.monthsShort=function(e,t){return e?o(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[We.test(t)?"format":"standalone"][e.month()]:o(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},yn.monthsParse=function(e,t,n){var s,i,r;if(this._monthsParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=y([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=y([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(r="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},yn.monthsRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(m(this,"_monthsRegex")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},yn.monthsShortRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(m(this,"_monthsShortRegex")||(this._monthsShortRegex=Fe),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},yn.week=function(e){return Ie(e,this._week.dow,this._week.doy).week},yn.firstDayOfYear=function(){return this._week.doy},yn.firstDayOfWeek=function(){return this._week.dow},yn.weekdays=function(e,t){var n=o(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"];return!0===e?je(n,this._week.dow):e?n[e.day()]:n},yn.weekdaysMin=function(e){return!0===e?je(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},yn.weekdaysShort=function(e){return!0===e?je(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},yn.weekdaysParse=function(e,t,n){var s,i,r;if(this._weekdaysParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=y([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=y([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[s]||(r="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},yn.weekdaysRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(m(this,"_weekdaysRegex")||(this._weekdaysRegex=qe),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},yn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(m(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Je),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},yn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(m(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Be),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},yn.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},yn.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ut("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===D(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),c.lang=n("moment.lang is deprecated. Use moment.locale instead.",ut),c.langData=n("moment.langData is deprecated. Use moment.localeData instead.",ht);var wn=Math.abs;function Mn(e,t,n,s){var i=jt(t,n);return e._milliseconds+=s*i._milliseconds,e._days+=s*i._days,e._months+=s*i._months,e._bubble()}function kn(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Dn(e){return 146097*e/4800}function Yn(e){return function(){return this.as(e)}}var On=Yn("ms"),Tn=Yn("s"),bn=Yn("m"),xn=Yn("h"),Pn=Yn("d"),Wn=Yn("w"),Cn=Yn("M"),Hn=Yn("Q"),Rn=Yn("y");function Un(e){return function(){return this.isValid()?this._data[e]:NaN}}var Fn=Un("milliseconds"),Ln=Un("seconds"),Nn=Un("minutes"),Gn=Un("hours"),Vn=Un("days"),En=Un("months"),In=Un("years");var An=Math.round,jn={ss:44,s:45,m:45,h:22,d:26,M:11};var Zn=Math.abs;function zn(e){return(0<e)-(e<0)||+e}function $n(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=Zn(this._milliseconds)/1e3,s=Zn(this._days),i=Zn(this._months);t=S((e=S(n/60))/60),n%=60,e%=60;var r=S(i/12),a=i%=12,o=s,u=t,l=e,h=n?n.toFixed(3).replace(/\.?0+$/,""):"",d=this.asSeconds();if(!d)return"P0D";var c=d<0?"-":"",f=zn(this._months)!==zn(d)?"-":"",m=zn(this._days)!==zn(d)?"-":"",_=zn(this._milliseconds)!==zn(d)?"-":"";return c+"P"+(r?f+r+"Y":"")+(a?f+a+"M":"")+(o?m+o+"D":"")+(u||l||h?"T":"")+(u?_+u+"H":"")+(l?_+l+"M":"")+(h?_+h+"S":"")}var qn=Ht.prototype;return qn.isValid=function(){return this._isValid},qn.abs=function(){var e=this._data;return this._milliseconds=wn(this._milliseconds),this._days=wn(this._days),this._months=wn(this._months),e.milliseconds=wn(e.milliseconds),e.seconds=wn(e.seconds),e.minutes=wn(e.minutes),e.hours=wn(e.hours),e.months=wn(e.months),e.years=wn(e.years),this},qn.add=function(e,t){return Mn(this,e,t,1)},qn.subtract=function(e,t){return Mn(this,e,t,-1)},qn.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=H(e))||"quarter"===e||"year"===e)switch(t=this._days+s/864e5,n=this._months+Sn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(Dn(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},qn.asMilliseconds=On,qn.asSeconds=Tn,qn.asMinutes=bn,qn.asHours=xn,qn.asDays=Pn,qn.asWeeks=Wn,qn.asMonths=Cn,qn.asQuarters=Hn,qn.asYears=Rn,qn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*D(this._months/12):NaN},qn._bubble=function(){var e,t,n,s,i,r=this._milliseconds,a=this._days,o=this._months,u=this._data;return 0<=r&&0<=a&&0<=o||r<=0&&a<=0&&o<=0||(r+=864e5*kn(Dn(o)+a),o=a=0),u.milliseconds=r%1e3,e=S(r/1e3),u.seconds=e%60,t=S(e/60),u.minutes=t%60,n=S(t/60),u.hours=n%24,o+=i=S(Sn(a+=S(n/24))),a-=kn(Dn(i)),s=S(o/12),o%=12,u.days=a,u.months=o,u.years=s,this},qn.clone=function(){return jt(this)},qn.get=function(e){return e=H(e),this.isValid()?this[e+"s"]():NaN},qn.milliseconds=Fn,qn.seconds=Ln,qn.minutes=Nn,qn.hours=Gn,qn.days=Vn,qn.weeks=function(){return S(this.days()/7)},qn.months=En,qn.years=In,qn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t,n,s,i,r,a,o,u,l,h,d,c=this.localeData(),f=(n=!e,s=c,i=jt(t=this).abs(),r=An(i.as("s")),a=An(i.as("m")),o=An(i.as("h")),u=An(i.as("d")),l=An(i.as("M")),h=An(i.as("y")),(d=r<=jn.ss&&["s",r]||r<jn.s&&["ss",r]||a<=1&&["m"]||a<jn.m&&["mm",a]||o<=1&&["h"]||o<jn.h&&["hh",o]||u<=1&&["d"]||u<jn.d&&["dd",u]||l<=1&&["M"]||l<jn.M&&["MM",l]||h<=1&&["y"]||["yy",h])[2]=n,d[3]=0<+t,d[4]=s,function(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}.apply(null,d));return e&&(f=c.pastFuture(+this,f)),c.postformat(f)},qn.toISOString=$n,qn.toString=$n,qn.toJSON=$n,qn.locale=Xt,qn.localeData=en,qn.toIsoString=n("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",$n),qn.lang=Kt,I("X",0,0,"unix"),I("x",0,0,"valueOf"),ue("x",se),ue("X",/[+-]?\d+(\.\d{1,3})?/),ce("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),ce("x",function(e,t,n){n._d=new Date(D(e))}),c.version="2.24.0",e=bt,c.fn=mn,c.min=function(){return Wt("isBefore",[].slice.call(arguments,0))},c.max=function(){return Wt("isAfter",[].slice.call(arguments,0))},c.now=function(){return Date.now?Date.now():+new Date},c.utc=y,c.unix=function(e){return bt(1e3*e)},c.months=function(e,t){return vn(e,t,"months")},c.isDate=d,c.locale=ut,c.invalid=p,c.duration=jt,c.isMoment=k,c.weekdays=function(e,t,n){return pn(e,t,n,"weekdays")},c.parseZone=function(){return bt.apply(null,arguments).parseZone()},c.localeData=ht,c.isDuration=Rt,c.monthsShort=function(e,t){return vn(e,t,"monthsShort")},c.weekdaysMin=function(e,t,n){return pn(e,t,n,"weekdaysMin")},c.defineLocale=lt,c.updateLocale=function(e,t){if(null!=t){var n,s,i=st;null!=(s=ot(e))&&(i=s._config),(n=new P(t=x(i,t))).parentLocale=it[e],it[e]=n,ut(e)}else null!=it[e]&&(null!=it[e].parentLocale?it[e]=it[e].parentLocale:null!=it[e]&&delete it[e]);return it[e]},c.locales=function(){return s(it)},c.weekdaysShort=function(e,t,n){return pn(e,t,n,"weekdaysShort")},c.normalizeUnits=H,c.relativeTimeRounding=function(e){return void 0===e?An:"function"==typeof e&&(An=e,!0)},c.relativeTimeThreshold=function(e,t){return void 0!==jn[e]&&(void 0===t?jn[e]:(jn[e]=t,"s"===e&&(jn.ss=t-1),!0))},c.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},c.prototype=mn,c.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},c});
\ No newline at end of file
diff --git a/apps/static/js/plugins/pace/pace.min.js b/apps/static/js/plugins/pace/pace.min.js
deleted file mode 100644
index 9f7c7dc9b..000000000
--- a/apps/static/js/plugins/pace/pace.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! pace 0.5.1 */
-(function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W=[].slice,X={}.hasOwnProperty,Y=function(a,b){function c(){this.constructor=a}for(var d in b)X.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},Z=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};for(t={catchupTime:500,initialRate:.03,minTime:500,ghostTime:500,maxProgressPerFrame:10,easeFactor:1.25,startOnPageLoad:!0,restartOnPushState:!0,restartOnRequestAfter:500,target:"body",elements:{checkInterval:100,selectors:["body"]},eventLag:{minSamples:10,sampleCount:3,lagThreshold:3},ajax:{trackMethods:["GET"],trackWebSockets:!0,ignoreURLs:[]}},B=function(){var a;return null!=(a="undefined"!=typeof performance&&null!==performance?"function"==typeof performance.now?performance.now():void 0:void 0)?a:+new Date},D=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,s=window.cancelAnimationFrame||window.mozCancelAnimationFrame,null==D&&(D=function(a){return setTimeout(a,50)},s=function(a){return clearTimeout(a)}),F=function(a){var b,c;return b=B(),(c=function(){var d;return d=B()-b,d>=33?(b=B(),a(d,function(){return D(c)})):setTimeout(c,33-d)})()},E=function(){var a,b,c;return c=arguments[0],b=arguments[1],a=3<=arguments.length?W.call(arguments,2):[],"function"==typeof c[b]?c[b].apply(c,a):c[b]},u=function(){var a,b,c,d,e,f,g;for(b=arguments[0],d=2<=arguments.length?W.call(arguments,1):[],f=0,g=d.length;g>f;f++)if(c=d[f])for(a in c)X.call(c,a)&&(e=c[a],null!=b[a]&&"object"==typeof b[a]&&null!=e&&"object"==typeof e?u(b[a],e):b[a]=e);return b},p=function(a){var b,c,d,e,f;for(c=b=0,e=0,f=a.length;f>e;e++)d=a[e],c+=Math.abs(d),b++;return c/b},w=function(a,b){var c,d,e;if(null==a&&(a="options"),null==b&&(b=!0),e=document.querySelector("[data-pace-"+a+"]")){if(c=e.getAttribute("data-pace-"+a),!b)return c;try{return JSON.parse(c)}catch(f){return d=f,"undefined"!=typeof console&&null!==console?console.error("Error parsing inline pace options",d):void 0}}},g=function(){function a(){}return a.prototype.on=function(a,b,c,d){var e;return null==d&&(d=!1),null==this.bindings&&(this.bindings={}),null==(e=this.bindings)[a]&&(e[a]=[]),this.bindings[a].push({handler:b,ctx:c,once:d})},a.prototype.once=function(a,b,c){return this.on(a,b,c,!0)},a.prototype.off=function(a,b){var c,d,e;if(null!=(null!=(d=this.bindings)?d[a]:void 0)){if(null==b)return delete this.bindings[a];for(c=0,e=[];c<this.bindings[a].length;)this.bindings[a][c].handler===b?e.push(this.bindings[a].splice(c,1)):e.push(c++);return e}},a.prototype.trigger=function(){var a,b,c,d,e,f,g,h,i;if(c=arguments[0],a=2<=arguments.length?W.call(arguments,1):[],null!=(g=this.bindings)?g[c]:void 0){for(e=0,i=[];e<this.bindings[c].length;)h=this.bindings[c][e],d=h.handler,b=h.ctx,f=h.once,d.apply(null!=b?b:this,a),f?i.push(this.bindings[c].splice(e,1)):i.push(e++);return i}},a}(),null==window.Pace&&(window.Pace={}),u(Pace,g.prototype),C=Pace.options=u({},t,window.paceOptions,w()),T=["ajax","document","eventLag","elements"],P=0,R=T.length;R>P;P++)J=T[P],C[J]===!0&&(C[J]=t[J]);i=function(a){function b(){return U=b.__super__.constructor.apply(this,arguments)}return Y(b,a),b}(Error),b=function(){function a(){this.progress=0}return a.prototype.getElement=function(){var a;if(null==this.el){if(a=document.querySelector(C.target),!a)throw new i;this.el=document.createElement("div"),this.el.className="pace pace-active",document.body.className=document.body.className.replace(/pace-done/g,""),document.body.className+=" pace-running",this.el.innerHTML='<div class="pace-progress">\n  <div class="pace-progress-inner"></div>\n</div>\n<div class="pace-activity"></div>',null!=a.firstChild?a.insertBefore(this.el,a.firstChild):a.appendChild(this.el)}return this.el},a.prototype.finish=function(){var a;return a=this.getElement(),a.className=a.className.replace("pace-active",""),a.className+=" pace-inactive",document.body.className=document.body.className.replace("pace-running",""),document.body.className+=" pace-done"},a.prototype.update=function(a){return this.progress=a,this.render()},a.prototype.destroy=function(){try{this.getElement().parentNode.removeChild(this.getElement())}catch(a){i=a}return this.el=void 0},a.prototype.render=function(){var a,b;return null==document.querySelector(C.target)?!1:(a=this.getElement(),a.children[0].style.width=""+this.progress+"%",(!this.lastRenderedProgress||this.lastRenderedProgress|0!==this.progress|0)&&(a.children[0].setAttribute("data-progress-text",""+(0|this.progress)+"%"),this.progress>=100?b="99":(b=this.progress<10?"0":"",b+=0|this.progress),a.children[0].setAttribute("data-progress",""+b)),this.lastRenderedProgress=this.progress)},a.prototype.done=function(){return this.progress>=100},a}(),h=function(){function a(){this.bindings={}}return a.prototype.trigger=function(a,b){var c,d,e,f,g;if(null!=this.bindings[a]){for(f=this.bindings[a],g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.call(this,b));return g}},a.prototype.on=function(a,b){var c;return null==(c=this.bindings)[a]&&(c[a]=[]),this.bindings[a].push(b)},a}(),O=window.XMLHttpRequest,N=window.XDomainRequest,M=window.WebSocket,v=function(a,b){var c,d,e,f;f=[];for(d in b.prototype)try{e=b.prototype[d],null==a[d]&&"function"!=typeof e?f.push(a[d]=e):f.push(void 0)}catch(g){c=g}return f},z=[],Pace.ignore=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?W.call(arguments,1):[],z.unshift("ignore"),c=b.apply(null,a),z.shift(),c},Pace.track=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?W.call(arguments,1):[],z.unshift("track"),c=b.apply(null,a),z.shift(),c},I=function(a){var b;if(null==a&&(a="GET"),"track"===z[0])return"force";if(!z.length&&C.ajax){if("socket"===a&&C.ajax.trackWebSockets)return!0;if(b=a.toUpperCase(),Z.call(C.ajax.trackMethods,b)>=0)return!0}return!1},j=function(a){function b(){var a,c=this;b.__super__.constructor.apply(this,arguments),a=function(a){var b;return b=a.open,a.open=function(d,e){return I(d)&&c.trigger("request",{type:d,url:e,request:a}),b.apply(a,arguments)}},window.XMLHttpRequest=function(b){var c;return c=new O(b),a(c),c},v(window.XMLHttpRequest,O),null!=N&&(window.XDomainRequest=function(){var b;return b=new N,a(b),b},v(window.XDomainRequest,N)),null!=M&&C.ajax.trackWebSockets&&(window.WebSocket=function(a,b){var d;return d=null!=b?new M(a,b):new M(a),I("socket")&&c.trigger("request",{type:"socket",url:a,protocols:b,request:d}),d},v(window.WebSocket,M))}return Y(b,a),b}(h),Q=null,x=function(){return null==Q&&(Q=new j),Q},H=function(a){var b,c,d,e;for(e=C.ajax.ignoreURLs,c=0,d=e.length;d>c;c++)if(b=e[c],"string"==typeof b){if(-1!==a.indexOf(b))return!0}else if(b.test(a))return!0;return!1},x().on("request",function(b){var c,d,e,f,g;return f=b.type,e=b.request,g=b.url,H(g)?void 0:Pace.running||C.restartOnRequestAfter===!1&&"force"!==I(f)?void 0:(d=arguments,c=C.restartOnRequestAfter||0,"boolean"==typeof c&&(c=0),setTimeout(function(){var b,c,g,h,i,j;if(b="socket"===f?e.readyState<2:0<(h=e.readyState)&&4>h){for(Pace.restart(),i=Pace.sources,j=[],c=0,g=i.length;g>c;c++){if(J=i[c],J instanceof a){J.watch.apply(J,d);break}j.push(void 0)}return j}},c))}),a=function(){function a(){var a=this;this.elements=[],x().on("request",function(){return a.watch.apply(a,arguments)})}return a.prototype.watch=function(a){var b,c,d,e;return d=a.type,b=a.request,e=a.url,H(e)?void 0:(c="socket"===d?new m(b):new n(b),this.elements.push(c))},a}(),n=function(){function a(a){var b,c,d,e,f,g,h=this;if(this.progress=0,null!=window.ProgressEvent)for(c=null,a.addEventListener("progress",function(a){return h.progress=a.lengthComputable?100*a.loaded/a.total:h.progress+(100-h.progress)/2}),g=["load","abort","timeout","error"],d=0,e=g.length;e>d;d++)b=g[d],a.addEventListener(b,function(){return h.progress=100});else f=a.onreadystatechange,a.onreadystatechange=function(){var b;return 0===(b=a.readyState)||4===b?h.progress=100:3===a.readyState&&(h.progress=50),"function"==typeof f?f.apply(null,arguments):void 0}}return a}(),m=function(){function a(a){var b,c,d,e,f=this;for(this.progress=0,e=["error","open"],c=0,d=e.length;d>c;c++)b=e[c],a.addEventListener(b,function(){return f.progress=100})}return a}(),d=function(){function a(a){var b,c,d,f;for(null==a&&(a={}),this.elements=[],null==a.selectors&&(a.selectors=[]),f=a.selectors,c=0,d=f.length;d>c;c++)b=f[c],this.elements.push(new e(b))}return a}(),e=function(){function a(a){this.selector=a,this.progress=0,this.check()}return a.prototype.check=function(){var a=this;return document.querySelector(this.selector)?this.done():setTimeout(function(){return a.check()},C.elements.checkInterval)},a.prototype.done=function(){return this.progress=100},a}(),c=function(){function a(){var a,b,c=this;this.progress=null!=(b=this.states[document.readyState])?b:100,a=document.onreadystatechange,document.onreadystatechange=function(){return null!=c.states[document.readyState]&&(c.progress=c.states[document.readyState]),"function"==typeof a?a.apply(null,arguments):void 0}}return a.prototype.states={loading:0,interactive:50,complete:100},a}(),f=function(){function a(){var a,b,c,d,e,f=this;this.progress=0,a=0,e=[],d=0,c=B(),b=setInterval(function(){var g;return g=B()-c-50,c=B(),e.push(g),e.length>C.eventLag.sampleCount&&e.shift(),a=p(e),++d>=C.eventLag.minSamples&&a<C.eventLag.lagThreshold?(f.progress=100,clearInterval(b)):f.progress=100*(3/(a+3))},50)}return a}(),l=function(){function a(a){this.source=a,this.last=this.sinceLastUpdate=0,this.rate=C.initialRate,this.catchup=0,this.progress=this.lastProgress=0,null!=this.source&&(this.progress=E(this.source,"progress"))}return a.prototype.tick=function(a,b){var c;return null==b&&(b=E(this.source,"progress")),b>=100&&(this.done=!0),b===this.last?this.sinceLastUpdate+=a:(this.sinceLastUpdate&&(this.rate=(b-this.last)/this.sinceLastUpdate),this.catchup=(b-this.progress)/C.catchupTime,this.sinceLastUpdate=0,this.last=b),b>this.progress&&(this.progress+=this.catchup*a),c=1-Math.pow(this.progress/100,C.easeFactor),this.progress+=c*this.rate*a,this.progress=Math.min(this.lastProgress+C.maxProgressPerFrame,this.progress),this.progress=Math.max(0,this.progress),this.progress=Math.min(100,this.progress),this.lastProgress=this.progress,this.progress},a}(),K=null,G=null,q=null,L=null,o=null,r=null,Pace.running=!1,y=function(){return C.restartOnPushState?Pace.restart():void 0},null!=window.history.pushState&&(S=window.history.pushState,window.history.pushState=function(){return y(),S.apply(window.history,arguments)}),null!=window.history.replaceState&&(V=window.history.replaceState,window.history.replaceState=function(){return y(),V.apply(window.history,arguments)}),k={ajax:a,elements:d,document:c,eventLag:f},(A=function(){var a,c,d,e,f,g,h,i;for(Pace.sources=K=[],g=["ajax","elements","document","eventLag"],c=0,e=g.length;e>c;c++)a=g[c],C[a]!==!1&&K.push(new k[a](C[a]));for(i=null!=(h=C.extraSources)?h:[],d=0,f=i.length;f>d;d++)J=i[d],K.push(new J(C));return Pace.bar=q=new b,G=[],L=new l})(),Pace.stop=function(){return Pace.trigger("stop"),Pace.running=!1,q.destroy(),r=!0,null!=o&&("function"==typeof s&&s(o),o=null),A()},Pace.restart=function(){return Pace.trigger("restart"),Pace.stop(),Pace.start()},Pace.go=function(){var a;return Pace.running=!0,q.render(),a=B(),r=!1,o=F(function(b,c){var d,e,f,g,h,i,j,k,m,n,o,p,s,t,u,v;for(k=100-q.progress,e=o=0,f=!0,i=p=0,t=K.length;t>p;i=++p)for(J=K[i],n=null!=G[i]?G[i]:G[i]=[],h=null!=(v=J.elements)?v:[J],j=s=0,u=h.length;u>s;j=++s)g=h[j],m=null!=n[j]?n[j]:n[j]=new l(g),f&=m.done,m.done||(e++,o+=m.tick(b));return d=o/e,q.update(L.tick(b,d)),q.done()||f||r?(q.update(100),Pace.trigger("done"),setTimeout(function(){return q.finish(),Pace.running=!1,Pace.trigger("hide")},Math.max(C.ghostTime,Math.max(C.minTime-(B()-a),0)))):c()})},Pace.start=function(a){u(C,a),Pace.running=!0;try{q.render()}catch(b){i=b}return document.querySelector(".pace")?(Pace.trigger("start"),Pace.go()):setTimeout(Pace.start,50)},"function"==typeof define&&define.amd?define(function(){return Pace}):"object"==typeof exports?module.exports=Pace:C.startOnPageLoad&&Pace.start()}).call(this);
\ No newline at end of file
diff --git a/apps/static/js/plugins/peity/jquery.peity.min.js b/apps/static/js/plugins/peity/jquery.peity.min.js
deleted file mode 100644
index 085b72507..000000000
--- a/apps/static/js/plugins/peity/jquery.peity.min.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Peity jQuery plugin version 2.0.3
-// (c) 2014 Ben Pickles
-//
-// http://benpickles.github.io/peity
-//
-// Released under MIT license.
-(function(e,q,h){var o=function(a,b){var c=q.createElementNS("http://www.w3.org/2000/svg",a);e.each(b,function(a,b){c.setAttribute(a,b)});return c},t="createElementNS"in q&&o("svg",{}).createSVGRect,r=1/(window.devicePixelRatio||1),j=e.fn.peity=function(a,b){t&&this.each(function(){var c=e(this),d=c.data("peity");if(d)a&&(d.type=a),e.extend(d.opts,b);else{var f=j.defaults[a],g={};e.each(c.data(),function(a,b){a in f&&(g[a]=b)});var h=e.extend({},f,g,b),d=new s(c,a,h);c.change(function(){d.draw()}).data("peity",
-    d)}d.draw()});return this},s=function(a,b,c){this.$el=a;this.type=b;this.opts=c},m=s.prototype;m.draw=function(){j.graphers[this.type].call(this,this.opts)};m.fill=function(){var a=this.opts.fill,b=a;e.isFunction(b)||(b=function(b,d){return a[d%a.length]});return b};m.prepare=function(a,b){var c;this.svg?c=e(this.svg).empty():(this.svg=o("svg",{"class":"peity"}),this.$el.hide().after(this.svg),c=e(this.svg).data("peity",this));this.svg.setAttribute("height",b);this.svg.setAttribute("width",a);return c};
-    m.values=function(){return e.map(this.$el.text().split(this.opts.delimiter),function(a){return parseFloat(a)})};j.defaults={};j.graphers={};j.register=function(a,b,c){this.defaults[a]=b;this.graphers[a]=c};j.register("pie",{delimiter:null,diameter:16,fill:["#ff9900","#fff4dd","#ffc66e"]},function(a){if(!a.delimiter){var b=this.$el.text().match(/[^0-9\.]/);a.delimiter=b?b[0]:","}b=this.values();if("/"==a.delimiter)var c=b[0],b=[c,h.max(0,b[1]-c)];for(var d=0,c=b.length,f=0;d<c;d++)f+=b[d];for(var a=
-        this.prepare(a.width||a.diameter,a.height||a.diameter),d=a.width(),g=a.height(),a=d/2,g=g/2,p=h.min(a,g),e=h.PI,j=this.fill(),i=-e/2,d=0;d<c;d++){var n=b[d],l=n/f,k;if(0!=l){if(1==l)k=o("circle",{cx:a,cy:g,r:p});else{k=2*l*e;var l=i+k,m=p*h.cos(i)+a,i=p*h.sin(i)+g,q=p*h.cos(l)+a,r=p*h.sin(l)+g;k=o("path",{d:["M",a,g,"L",m,i,"A",p,p,0,k>e?1:0,1,q,r,"Z"].join(" ")});i=l}k.setAttribute("fill",j.call(this,n,d,b));this.svg.appendChild(k)}}});j.register("line",{delimiter:",",fill:"#c6d9fd",height:16,max:null,
-        min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(a){var b=this.values();1==b.length&&b.push(b[0]);for(var c=h.max.apply(h,b.concat([a.max])),d=h.min.apply(h,b.concat([a.min])),f=this.prepare(a.width,a.height),g=f.width(),f=f.height()-a.strokeWidth,e=g/(b.length-1),c=c-d,j=0==c?f:f/c,m=f+d*j,c=[0,m],i=0;i<b.length;i++)c.push(i*e,f-j*(b[i]-d)+a.strokeWidth/2);c.push(g,m);b=o("polygon",{fill:a.fill,points:c.join(" ")});this.svg.appendChild(b);a.strokeWidth&&(a=o("polyline",{fill:"transparent",
-        points:c.slice(2,c.length-2).join(" "),stroke:a.stroke,"stroke-width":a.strokeWidth,"stroke-linecap":"square"}),this.svg.appendChild(a))});j.register("bar",{delimiter:",",fill:["#4D89F9"],gap:1,height:16,max:null,min:0,width:32},function(a){for(var b=this.values(),c=h.max.apply(h,b.concat([a.max])),d=h.min.apply(h,b.concat([a.min])),f=this.prepare(a.width,a.height),g=f.width(),f=f.height(),e=c-d,j=0==e?0:f/e,a=a.gap,g=(g+a)/b.length,m=this.fill(),i=0;i<b.length;i++){var n=b[i],l=f-j*(n-d),k=j*n;if(0==
-        k){if(k=r,0>=d&&0<c||0==e)l-=r}else 0>k&&(l+=k,k=-k);n=o("rect",{fill:m.call(this,n,i,b),x:i*g,y:l,width:g-a,height:k});this.svg.appendChild(n)}})})(jQuery,document,Math);
diff --git a/apps/static/js/plugins/slimscroll/jquery.slimscroll.js b/apps/static/js/plugins/slimscroll/jquery.slimscroll.js
deleted file mode 100644
index 2ea5b0801..000000000
--- a/apps/static/js/plugins/slimscroll/jquery.slimscroll.js
+++ /dev/null
@@ -1,464 +0,0 @@
-/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
- *
- * Version: 1.3.0
- *
- */
-(function($) {
-
-  jQuery.fn.extend({
-    slimScroll: function(options) {
-
-      var defaults = {
-
-        // width in pixels of the visible scroll area
-        width : 'auto',
-
-        // height in pixels of the visible scroll area
-        height : '250px',
-
-        // width in pixels of the scrollbar and rail
-        size : '7px',
-
-        // scrollbar color, accepts any hex/color value
-        color: '#000',
-
-        // scrollbar position - left/right
-        position : 'right',
-
-        // distance in pixels between the side edge and the scrollbar
-        distance : '1px',
-
-        // default scroll position on load - top / bottom / $('selector')
-        start : 'top',
-
-        // sets scrollbar opacity
-        opacity : .4,
-
-        // enables always-on mode for the scrollbar
-        alwaysVisible : false,
-
-        // check if we should hide the scrollbar when user is hovering over
-        disableFadeOut : false,
-
-        // sets visibility of the rail
-        railVisible : false,
-
-        // sets rail color
-        railColor : '#333',
-
-        // sets rail opacity
-        railOpacity : .2,
-
-        // whether  we should use jQuery UI Draggable to enable bar dragging
-        railDraggable : true,
-
-        // defautlt CSS class of the slimscroll rail
-        railClass : 'slimScrollRail',
-
-        // defautlt CSS class of the slimscroll bar
-        barClass : 'slimScrollBar',
-
-        // defautlt CSS class of the slimscroll wrapper
-        wrapperClass : 'slimScrollDiv',
-
-        // check if mousewheel should scroll the window if we reach top/bottom
-        allowPageScroll : false,
-
-        // scroll amount applied to each mouse wheel step
-        wheelStep : 20,
-
-        // scroll amount applied when user is using gestures
-        touchScrollStep : 200,
-
-        // sets border radius
-        borderRadius: '7px',
-
-        // sets border radius of the rail
-        railBorderRadius : '7px'
-      };
-
-      var o = $.extend(defaults, options);
-
-      // do it for every element that matches selector
-      this.each(function(){
-
-      var isOverPanel, isOverBar, isDragg, queueHide, touchDif,
-        barHeight, percentScroll, lastScroll,
-        divS = '<div></div>',
-        minBarHeight = 30,
-        releaseScroll = false;
-
-        // used in event handlers and for better minification
-        var me = $(this);
-
-        // ensure we are not binding it again
-        if (me.parent().hasClass(o.wrapperClass))
-        {
-            // start from last bar position
-            var offset = me.scrollTop();
-
-            // find bar and rail
-            bar = me.parent().find('.' + o.barClass);
-            rail = me.parent().find('.' + o.railClass);
-
-            getBarHeight();
-
-            // check if we should scroll existing instance
-            if ($.isPlainObject(options))
-            {
-              // Pass height: auto to an existing slimscroll object to force a resize after contents have changed
-              if ( 'height' in options && options.height == 'auto' ) {
-                me.parent().css('height', 'auto');
-                me.css('height', 'auto');
-                var height = me.parent().parent().height();
-                me.parent().css('height', height);
-                me.css('height', height);
-              }
-
-              if ('scrollTo' in options)
-              {
-                // jump to a static point
-                offset = parseInt(o.scrollTo);
-              }
-              else if ('scrollBy' in options)
-              {
-                // jump by value pixels
-                offset += parseInt(o.scrollBy);
-              }
-              else if ('destroy' in options)
-              {
-                // remove slimscroll elements
-                bar.remove();
-                rail.remove();
-                me.unwrap();
-                return;
-              }
-
-              // scroll content by the given offset
-              scrollContent(offset, false, true);
-            }
-
-            return;
-        }
-
-        // optionally set height to the parent's height
-        o.height = (o.height == 'auto') ? me.parent().height() : o.height;
-
-        // wrap content
-        var wrapper = $(divS)
-          .addClass(o.wrapperClass)
-          .css({
-            position: 'relative',
-            overflow: 'hidden',
-            width: o.width,
-            height: o.height
-          });
-
-        // update style for the div
-        me.css({
-          overflow: 'hidden',
-          width: o.width,
-          height: o.height
-        });
-
-        // create scrollbar rail
-        var rail = $(divS)
-          .addClass(o.railClass)
-          .css({
-            width: o.size,
-            height: '100%',
-            position: 'absolute',
-            top: 0,
-            display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
-            'border-radius': o.railBorderRadius,
-            background: o.railColor,
-            opacity: o.railOpacity,
-            zIndex: 90
-          });
-
-        // create scrollbar
-        var bar = $(divS)
-          .addClass(o.barClass)
-          .css({
-            background: o.color,
-            width: o.size,
-            position: 'absolute',
-            top: 0,
-            opacity: o.opacity,
-            display: o.alwaysVisible ? 'block' : 'none',
-            'border-radius' : o.borderRadius,
-            BorderRadius: o.borderRadius,
-            MozBorderRadius: o.borderRadius,
-            WebkitBorderRadius: o.borderRadius,
-            zIndex: 99
-          });
-
-        // set position
-        var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance };
-        rail.css(posCss);
-        bar.css(posCss);
-
-        // wrap it
-        me.wrap(wrapper);
-
-        // append to parent div
-        me.parent().append(bar);
-        me.parent().append(rail);
-
-        // make it draggable and no longer dependent on the jqueryUI
-        if (o.railDraggable){
-          bar.bind("mousedown", function(e) {
-            var $doc = $(document);
-            isDragg = true;
-            t = parseFloat(bar.css('top'));
-            pageY = e.pageY;
-
-            $doc.bind("mousemove.slimscroll", function(e){
-              currTop = t + e.pageY - pageY;
-              bar.css('top', currTop);
-              scrollContent(0, bar.position().top, false);// scroll content
-            });
-
-            $doc.bind("mouseup.slimscroll", function(e) {
-              isDragg = false;hideBar();
-              $doc.unbind('.slimscroll');
-            });
-            return false;
-          }).bind("selectstart.slimscroll", function(e){
-            e.stopPropagation();
-            e.preventDefault();
-            return false;
-          });
-        }
-
-        // on rail over
-        rail.hover(function(){
-          showBar();
-        }, function(){
-          hideBar();
-        });
-
-        // on bar over
-        bar.hover(function(){
-          isOverBar = true;
-        }, function(){
-          isOverBar = false;
-        });
-
-        // show on parent mouseover
-        me.hover(function(){
-          isOverPanel = true;
-          showBar();
-          hideBar();
-        }, function(){
-          isOverPanel = false;
-          hideBar();
-        });
-
-        // support for mobile
-        me.bind('touchstart', function(e,b){
-          if (e.originalEvent.touches.length)
-          {
-            // record where touch started
-            touchDif = e.originalEvent.touches[0].pageY;
-          }
-        });
-
-        me.bind('touchmove', function(e){
-          // prevent scrolling the page if necessary
-          if(!releaseScroll)
-          {
-  		      e.originalEvent.preventDefault();
-		      }
-          if (e.originalEvent.touches.length)
-          {
-            // see how far user swiped
-            var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
-            // scroll content
-            scrollContent(diff, true);
-            touchDif = e.originalEvent.touches[0].pageY;
-          }
-        });
-
-        // set up initial height
-        getBarHeight();
-
-        // check start position
-        if (o.start === 'bottom')
-        {
-          // scroll content to bottom
-          bar.css({ top: me.outerHeight() - bar.outerHeight() });
-          scrollContent(0, true);
-        }
-        else if (o.start !== 'top')
-        {
-          // assume jQuery selector
-          scrollContent($(o.start).position().top, null, true);
-
-          // make sure bar stays hidden
-          if (!o.alwaysVisible) { bar.hide(); }
-        }
-
-        // attach scroll events
-        attachWheel();
-
-        function _onWheel(e)
-        {
-          // use mouse wheel only when mouse is over
-          if (!isOverPanel) { return; }
-
-          var e = e || window.event;
-
-          var delta = 0;
-          if (e.wheelDelta) { delta = -e.wheelDelta/120; }
-          if (e.detail) { delta = e.detail / 3; }
-
-          var target = e.target || e.srcTarget || e.srcElement;
-          if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
-            // scroll content
-            scrollContent(delta, true);
-          }
-
-          // stop window scroll
-          if (e.preventDefault && !releaseScroll) { e.preventDefault(); }
-          if (!releaseScroll) { e.returnValue = false; }
-        }
-
-        function scrollContent(y, isWheel, isJump)
-        {
-          releaseScroll = false;
-          var delta = y;
-          var maxTop = me.outerHeight() - bar.outerHeight();
-
-          if (isWheel)
-          {
-            // move bar with mouse wheel
-            delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
-
-            // move bar, make sure it doesn't go out
-            delta = Math.min(Math.max(delta, 0), maxTop);
-
-            // if scrolling down, make sure a fractional change to the
-            // scroll position isn't rounded away when the scrollbar's CSS is set
-            // this flooring of delta would happened automatically when
-            // bar.css is set below, but we floor here for clarity
-            delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
-
-            // scroll the scrollbar
-            bar.css({ top: delta + 'px' });
-          }
-
-          // calculate actual scroll amount
-          percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
-          delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
-
-          if (isJump)
-          {
-            delta = y;
-            var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
-            offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
-            bar.css({ top: offsetTop + 'px' });
-          }
-
-          // scroll content
-          me.scrollTop(delta);
-
-          // fire scrolling event
-          me.trigger('slimscrolling', ~~delta);
-
-          // ensure bar is visible
-          showBar();
-
-          // trigger hide when scroll is stopped
-          hideBar();
-        }
-
-        function attachWheel()
-        {
-          if (window.addEventListener)
-          {
-            this.addEventListener('DOMMouseScroll', _onWheel, false );
-            this.addEventListener('mousewheel', _onWheel, false );
-            this.addEventListener('MozMousePixelScroll', _onWheel, false );
-          }
-          else
-          {
-            document.attachEvent("onmousewheel", _onWheel)
-          }
-        }
-
-        function getBarHeight()
-        {
-          // calculate scrollbar height and make sure it is not too small
-          barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
-          bar.css({ height: barHeight + 'px' });
-
-          // hide scrollbar if content is not long enough
-          var display = barHeight == me.outerHeight() ? 'none' : 'block';
-          bar.css({ display: display });
-        }
-
-        function showBar()
-        {
-          // recalculate bar height
-          getBarHeight();
-          clearTimeout(queueHide);
-
-          // when bar reached top or bottom
-          if (percentScroll == ~~percentScroll)
-          {
-            //release wheel
-            releaseScroll = o.allowPageScroll;
-
-            // publish approporiate event
-            if (lastScroll != percentScroll)
-            {
-                var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
-                me.trigger('slimscroll', msg);
-            }
-          }
-          else
-          {
-            releaseScroll = false;
-          }
-          lastScroll = percentScroll;
-
-          // show only when required
-          if(barHeight >= me.outerHeight()) {
-            //allow window scroll
-            releaseScroll = true;
-            return;
-          }
-          bar.stop(true,true).fadeIn('fast');
-          if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); }
-        }
-
-        function hideBar()
-        {
-          // only hide when options allow it
-          if (!o.alwaysVisible)
-          {
-            queueHide = setTimeout(function(){
-              if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
-              {
-                bar.fadeOut('slow');
-                rail.fadeOut('slow');
-              }
-            }, 1000);
-          }
-        }
-
-      });
-
-      // maintain chainability
-      return this;
-    }
-  });
-
-  jQuery.fn.extend({
-    slimscroll: jQuery.fn.slimScroll
-  });
-
-})(jQuery);
diff --git a/apps/static/js/plugins/slimscroll/jquery.slimscroll.min.js b/apps/static/js/plugins/slimscroll/jquery.slimscroll.min.js
deleted file mode 100644
index 26220d6b2..000000000
--- a/apps/static/js/plugins/slimscroll/jquery.slimscroll.min.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
- *
- * Version: 1.3.0
- *
- */
-(function(f){jQuery.fn.extend({slimScroll:function(h){var a=f.extend({width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:0.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:0.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px"},h);this.each(function(){function r(d){if(s){d=d||
-window.event;var c=0;d.wheelDelta&&(c=-d.wheelDelta/120);d.detail&&(c=d.detail/3);f(d.target||d.srcTarget||d.srcElement).closest("."+a.wrapperClass).is(b.parent())&&m(c,!0);d.preventDefault&&!k&&d.preventDefault();k||(d.returnValue=!1)}}function m(d,f,h){k=!1;var e=d,g=b.outerHeight()-c.outerHeight();f&&(e=parseInt(c.css("top"))+d*parseInt(a.wheelStep)/100*c.outerHeight(),e=Math.min(Math.max(e,0),g),e=0<d?Math.ceil(e):Math.floor(e),c.css({top:e+"px"}));l=parseInt(c.css("top"))/(b.outerHeight()-c.outerHeight());
-e=l*(b[0].scrollHeight-b.outerHeight());h&&(e=d,d=e/b[0].scrollHeight*b.outerHeight(),d=Math.min(Math.max(d,0),g),c.css({top:d+"px"}));b.scrollTop(e);b.trigger("slimscrolling",~~e);v();p()}function C(){window.addEventListener?(this.addEventListener("DOMMouseScroll",r,!1),this.addEventListener("mousewheel",r,!1),this.addEventListener("MozMousePixelScroll",r,!1)):document.attachEvent("onmousewheel",r)}function w(){u=Math.max(b.outerHeight()/b[0].scrollHeight*b.outerHeight(),D);c.css({height:u+"px"});
-var a=u==b.outerHeight()?"none":"block";c.css({display:a})}function v(){w();clearTimeout(A);l==~~l?(k=a.allowPageScroll,B!=l&&b.trigger("slimscroll",0==~~l?"top":"bottom")):k=!1;B=l;u>=b.outerHeight()?k=!0:(c.stop(!0,!0).fadeIn("fast"),a.railVisible&&g.stop(!0,!0).fadeIn("fast"))}function p(){a.alwaysVisible||(A=setTimeout(function(){a.disableFadeOut&&s||(x||y)||(c.fadeOut("slow"),g.fadeOut("slow"))},1E3))}var s,x,y,A,z,u,l,B,D=30,k=!1,b=f(this);if(b.parent().hasClass(a.wrapperClass)){var n=b.scrollTop(),
-c=b.parent().find("."+a.barClass),g=b.parent().find("."+a.railClass);w();if(f.isPlainObject(h)){if("height"in h&&"auto"==h.height){b.parent().css("height","auto");b.css("height","auto");var q=b.parent().parent().height();b.parent().css("height",q);b.css("height",q)}if("scrollTo"in h)n=parseInt(a.scrollTo);else if("scrollBy"in h)n+=parseInt(a.scrollBy);else if("destroy"in h){c.remove();g.remove();b.unwrap();return}m(n,!1,!0)}}else{a.height="auto"==a.height?b.parent().height():a.height;n=f("<div></div>").addClass(a.wrapperClass).css({position:"relative",
-overflow:"hidden",width:a.width,height:a.height});b.css({overflow:"hidden",width:a.width,height:a.height});var g=f("<div></div>").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.railBorderRadius,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=f("<div></div>").addClass(a.barClass).css({background:a.color,width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?
-"block":"none","border-radius":a.borderRadius,BorderRadius:a.borderRadius,MozBorderRadius:a.borderRadius,WebkitBorderRadius:a.borderRadius,zIndex:99}),q="right"==a.position?{right:a.distance}:{left:a.distance};g.css(q);c.css(q);b.wrap(n);b.parent().append(c);b.parent().append(g);a.railDraggable&&c.bind("mousedown",function(a){var b=f(document);y=!0;t=parseFloat(c.css("top"));pageY=a.pageY;b.bind("mousemove.slimscroll",function(a){currTop=t+a.pageY-pageY;c.css("top",currTop);m(0,c.position().top,!1)});
-b.bind("mouseup.slimscroll",function(a){y=!1;p();b.unbind(".slimscroll")});return!1}).bind("selectstart.slimscroll",function(a){a.stopPropagation();a.preventDefault();return!1});g.hover(function(){v()},function(){p()});c.hover(function(){x=!0},function(){x=!1});b.hover(function(){s=!0;v();p()},function(){s=!1;p()});b.bind("touchstart",function(a,b){a.originalEvent.touches.length&&(z=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){k||b.originalEvent.preventDefault();b.originalEvent.touches.length&&
-(m((z-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0),z=b.originalEvent.touches[0].pageY)});w();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),m(0,!0)):"top"!==a.start&&(m(f(a.start).position().top,null,!0),a.alwaysVisible||c.hide());C()}});return this}});jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})})(jQuery);
\ No newline at end of file
diff --git a/apps/templates/_left_side_bar.html b/apps/templates/_left_side_bar.html
deleted file mode 100644
index 572375ed8..000000000
--- a/apps/templates/_left_side_bar.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<nav class="navbar-default navbar-static-side" role="navigation">
-    <div class="sidebar-collapse">
-        <ul class="nav" id="side-menu">
-            {% include '_user_profile.html' %}
-            {% if request.user.is_common_user or request.COOKIES.IN_ADMIN_PAGE == 'No' %}
-                {% include '_nav_user.html' %}
-            {% else %}
-                {% include '_nav.html' %}
-            {% endif %}
-        </ul>
-    </div>
-</nav>
diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html
index 12c59d209..e69de29bb 100644
--- a/apps/templates/_nav.html
+++ b/apps/templates/_nav.html
@@ -1,195 +0,0 @@
-{% load i18n %}
-
-{# Index #}
-{% if request.user.can_admin_or_audit_current_org %}
-    <li id="index">
-        <a href="{% url 'index' %}">
-            <i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span>
-            <span class="label label-info pull-right"></span>
-        </a>
-    </li>
-{% endif %}
-
-{# Users #}
-{% if request.user.can_admin_current_org %}
-    <li id="users">
-        <a href="#">
-            <i class="fa fa-group" style="width: 14px"></i> <span class="nav-label">{% trans 'Users' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level active">
-            <li id="user"><a href="{% url 'users:user-list' %}">{% trans 'User list' %}</a></li>
-            <li id="user-group"><a href="{% url 'users:user-group-list' %}">{% trans 'User group' %}</a></li>
-        </ul>
-    </li>
-{% endif %}
-
-{# User info #}
-{% if not request.user.can_admin_current_org and request.user.can_audit_current_org %}
-    <li id="users">
-        <a href="{% url 'users:user-profile' %}">
-            <i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span>
-        </a>
-    </li>
-{% endif %}
-
-{# Assets #}
-{% if request.user.can_admin_current_org %}
-    <li id="assets">
-        <a>
-            <i class="fa fa-inbox" style="width: 14px"></i> <span class="nav-label">{% trans 'Assets' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            <li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset list' %}</a></li>
-            <li id="domain"><a href="{% url 'assets:domain-list' %}">{% trans 'Domain list' %}</a></li>
-            <li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
-            <li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
-            <li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
-            <li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li>
-            {% if request.user.is_superuser %}
-            <li id="platform"><a href="{% url 'assets:platform-list' %}">{% trans 'Platform list' %}</a></li>
-            {% endif %}
-        </ul>
-    </li>
-{% endif %}
-
-
-{# Applications #}
-{% if request.user.can_admin_current_org %}
-    <li id="applications">
-        <a>
-            <i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'Applications' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            {% if LICENSE_VALID %}
-            <li id="remote-app"><a href="{% url 'applications:remote-app-list' %}">{% trans 'RemoteApp' %}</a></li>
-            {% endif %}
-            <li id="database-app"><a href="{% url 'applications:database-app-list' %}">{% trans 'DatabaseApp' %}</a></li>
-        </ul>
-    </li>
-{% endif %}
-
-
-{# Perms #}
-{% if request.user.can_admin_current_org %}
-    <li id="perms">
-        <a href="#"><i class="fa fa-edit" style="width: 14px"></i> <span class="nav-label">{% trans 'Perms' %}</span><span class="fa arrow"></span></a>
-        <ul class="nav nav-second-level">
-            <li id="asset-permission">
-                <a href="{% url 'perms:asset-permission-list' %}">{% trans 'Asset permission' %}</a>
-            </li>
-            {% if LICENSE_VALID %}
-            <li id="remote-app-permission">
-                <a href="{% url 'perms:remote-app-permission-list' %}">{% trans 'RemoteApp' %}</a>
-            </li>
-            {% endif %}
-            <li id="database-app-permission">
-                <a href="{% url 'perms:database-app-permission-list' %}">{% trans 'DatabaseApp' %}</a>
-            </li>
-        </ul>
-    </li>
-{% endif %}
-
-
-{# Terminal #}
-{% if request.user.can_admin_or_audit_current_org %}
-    <li id="terminal">
-        <a>
-            <i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            <li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
-            <li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
-            <li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
-
-            {% if request.user.can_admin_current_org %}
-                <li><a href="{% url 'terminal:web-terminal' %}" target="_blank"><span class="nav-label">{% trans 'Web terminal' %}</span></a></li>
-                <li><a href="{% url 'terminal:web-sftp' %}" target="_blank"><span class="nav-label">{% trans 'File manager' %}</span></a></li>
-            {% endif %}
-
-            {% if request.user.is_superuser %}
-                <li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
-            {% endif %}
-        </ul>
-    </li>
-{% endif %}
-
-
-{# Ops #}
-{% if request.user.can_admin_current_org %}
-    <li id="ops">
-        <a>
-            <i class="fa fa-coffee" style="width: 14px"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            <li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
-            <li id="command-executions"><a href="{% url 'ops:command-execution-create' %}">{% trans 'Batch command' %}</a></li>
-            {% if request.user.is_superuser %}
-            <li><a href="{% url 'flower-view' path='' %}" target="_blank" >{% trans 'Task monitor' %}</a></li>
-            {% endif %}
-        </ul>
-    </li>
-{% endif %}
-
-{% if request.user.can_admin_current_org and LICENSE_VALID %}
-    <li id="tickets">
-        <a href="{% url 'tickets:ticket-list' %}">
-            <i class="fa fa-check-square-o" style="width: 14px"></i>
-            <span class="nav-label">{% trans 'Tickets' %}</span>
-        </a>
-    </li>
-{% endif %}
-
-{# Audits #}
-{% if request.user.can_admin_or_audit_current_org %}
-    <li id="audits">
-        <a>
-            <i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            <li id="login-log"><a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a></li>
-            <li id="ftp-log"><a href="{% url 'audits:ftp-log-list' %}">{% trans 'FTP log' %}</a></li>
-            <li id="operate-log"><a href="{% url 'audits:operate-log-list' %}">{% trans 'Operate log' %}</a></li>
-            <li id="password-change-log"><a href="{% url 'audits:password-change-log-list' %}">{% trans 'Password change log' %}</a></li>
-            <li id="command-execution-log"><a href="{% url 'audits:command-execution-log-list' %}">{% trans 'Batch command' %}</a></li>
-        </ul>
-    </li>
-{% endif %}
-
-
-{# X-Pack #}
-{% if request.user.can_admin_current_org and XPACK_PLUGINS %}
-    <li id="xpack">
-        <a>
-            <i class="fa fa-sitemap" style="width: 14px"></i> <span class="nav-label">{% trans 'XPack' %}</span><span class="fa arrow"></span>
-        </a>
-        <ul class="nav nav-second-level">
-            {% for plugin in XPACK_PLUGINS %}
-                {% ifequal plugin.name 'cloud'%}
-                    <li id="{{ plugin.name }}">
-                        <a href="#"><span class="nav-label">{% trans plugin.verbose_name %}</span><span class="fa arrow"></span></a>
-                        <ul class="nav nav-third-level">
-                            <li id="account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
-                            <li id="sync-instance-task"><a href="{% url 'xpack:cloud:sync-instance-task-list' %}">{% trans 'Sync instance' %}</a></li>
-                        </ul>
-                    </li>
-                {% else %}
-                    <li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
-                {% endifequal %}
-            {% endfor %}
-        </ul>
-    </li>
-{% endif %}
-
-{# Settings #}
-{% if request.user.is_superuser %}
-    <li id="settings">
-        <a href="{% url 'settings:basic-setting' %}">
-            <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
-        </a>
-    </li>
-{% endif %}
-
-<script>
-$(document).ready(function () {
-})
-</script>
diff --git a/apps/templates/_nav_user.html b/apps/templates/_nav_user.html
index 7e36f3dcc..e69de29bb 100644
--- a/apps/templates/_nav_user.html
+++ b/apps/templates/_nav_user.html
@@ -1,49 +0,0 @@
-{% load i18n %}
-<li id="assets">
-    <a href="{% url 'assets:user-asset-list' %}">
-        <i class="fa fa-files-o" style="width: 14px"></i><span class="nav-label">{% trans 'My assets' %}</span><span class="label label-info pull-right"></span>
-    </a>
-</li>
-
-<li id="applications">
-    <a>
-        <i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'My applications' %}</span><span class="fa arrow"></span>
-    </a>
-    <ul class="nav nav-second-level">
-        {% if LICENSE_VALID %}
-        <li id="user-remote-app">
-            <a href="{% url 'applications:user-remote-app-list' %}">
-                <i class="" style="width: 14px"></i><span class="nav-label">{% trans 'RemoteApp' %}</span><span class="label label-info pull-right"></span>
-            </a>
-        </li>
-        {% endif %}
-        <li id="user-database-app">
-            <a href="{% url 'applications:user-database-app-list' %}">
-                <i class="" style="width: 14px"></i><span class="nav-label">{% trans 'DatabaseApp' %}</span><span class="label label-info pull-right"></span>
-            </a>
-        </li>
-    </ul>
-</li>
-
-{% if SECURITY_COMMAND_EXECUTION %}
-<li id="ops">
-    <a href="{% url 'ops:command-execution-create' %}">
-        <i class="fa fa-terminal" style="width: 14px"></i> <span class="nav-label">{% trans 'Command execution' %}</span><span class="label label-info pull-right"></span>
-    </a>
-</li>
-{% endif %}
-<li id="users">
-    <a href="{% url 'users:user-profile' %}">
-        <i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span>
-    </a>
-</li>
-<li>
-    <a href="{% url 'terminal:web-terminal' %}" target="_blank"><i class="fa fa-window-maximize" style="width: 14px"></i>
-        <span class="nav-label">{% trans 'Web terminal' %}</span>
-    </a>
-</li>
-<li>
-    <a href="{% url 'terminal:web-sftp' %}" target="_blank"><i class="fa fa-file" style="width: 14px"></i>
-        <span class="nav-label">{% trans 'File manager' %}</span>
-    </a>
-</li>
diff --git a/apps/templates/_user_profile.html b/apps/templates/_user_profile.html
deleted file mode 100644
index 3e72f5b8e..000000000
--- a/apps/templates/_user_profile.html
+++ /dev/null
@@ -1,63 +0,0 @@
-{% load static %}
-{% load i18n %}
-<li class="nav-header">
-    <div class="profile-element" style="height: 65px">
-        <div href="http://www.jumpserver.org" target="_blank" style="width: 100%; background-image: url({% static 'img/header-profile.png' %})">
-             <img alt="logo" height="55" width="185" style="margin-right: 5px" src="{{ LOGO_TEXT_URL }}"/>
-        </div>
-    </div>
-    <div class="logo-element">
-        <img alt="image" height="40" src="{{ LOGO_URL }}"/>
-    </div>
-    {% if ADMIN_OR_AUDIT_ORGS and request.COOKIES.IN_ADMIN_PAGE != 'No' %}
-        {% if ADMIN_OR_AUDIT_ORGS|length > 1 or not CURRENT_ORG.is_default %}
-            <div>
-                <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="display: block; background-color: transparent; color: #8095a8; padding: 14px 20px 14px 25px">
-                    <i class="fa fa-bookmark" style="width: 14px; "></i>
-                    <span class="nav-label" style="padding-left: 7px">
-                        {{ CURRENT_ORG.name }}
-                    </span>
-                    <span class="fa fa-sort-desc pull-right"></span>
-                </a>
-                <ul class="dropdown-menu" style="min-width: 220px;max-width: 400px;max-height: 400px; overflow: auto">
-                    <input type="text" id="left-side-org-filter" placeholder="{% trans 'Search' %}" class="form-control">
-                    {% for org in ADMIN_OR_AUDIT_ORGS %}
-                        <li>
-                            <a class="org-dropdown" href="" data-id="{{ org.id }}">
-                                {{ org.name }}
-                                {% if org.id == CURRENT_ORG.id %}
-                                    <span class="fa fa-circle" style="padding-top: 5px; color: #1ab394"></span>
-                                {% endif %}
-                            </a>
-                        </li>
-                    {% endfor %}
-                </ul>
-            </div>
-        {% endif %}
-    {% endif %}
-</li>
-<script>
-var orgsRef;
-$(document).ready(function () {
-    orgsRef = $(".org-dropdown");
-}).on('click', '#left-side-org-filter', function (e) {
-    e.preventDefault();
-    e.stopPropagation();
-}).on('keyup', '#left-side-org-filter', function () {
-    var input = $("#left-side-org-filter").val();
-    if (!input) {
-        orgsRef.show();
-        return
-    }
-    orgsRef.each(function (i, v) {
-        var itemRef = $(v);
-        var orgItemText = itemRef.text().trim();
-        var findIndex = orgItemText.indexOf(input);
-        if (findIndex === -1) {
-            itemRef.hide();
-        } else {
-            itemRef.show();
-        }
-    });
-})
-</script>
diff --git a/apps/templates/base.html b/apps/templates/base.html
index e400a71f8..ce3e453b1 100644
--- a/apps/templates/base.html
+++ b/apps/templates/base.html
@@ -13,7 +13,6 @@
 </head>
 <body>
 <div id="wrapper">
-    {% include '_left_side_bar.html' %}
     <div id="page-wrapper" class="gray-bg">
         {% include '_header_bar.html' %}
          <div class="alert alert-info help-message alert-dismissable page-message" style="display: none">
diff --git a/apps/terminal/api/command.py b/apps/terminal/api/command.py
index 04b3450fd..9fa401421 100644
--- a/apps/terminal/api/command.py
+++ b/apps/terminal/api/command.py
@@ -9,10 +9,9 @@ from rest_framework.fields import DateTimeField
 from rest_framework.response import Response
 from django.template import loader
 
-from terminal.models import CommandStorage, Session
+from terminal.models import CommandStorage, Session, Command
 from terminal.filters import CommandFilter
 from orgs.utils import current_org
-from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser
 from common.drf.api import JMSBulkModelViewSet
 from common.utils import get_logger
 from terminal.serializers import InsecureCommandAlertSerializer
@@ -29,10 +28,9 @@ __all__ = ['CommandViewSet', 'CommandExportApi', 'InsecureCommandAlertAPI']
 
 class CommandQueryMixin:
     command_store = get_command_storage()
-    permission_classes = [IsOrgAdminOrAppUser | IsOrgAuditor]
     filterset_fields = [
-        "asset", "system_user", "user", "session", "risk_level",
-        "input"
+        "asset", "system_user", "user", "session",
+        "risk_level", "input"
     ]
     default_days_ago = 5
 
@@ -105,9 +103,9 @@ class CommandViewSet(JMSBulkModelViewSet):
 
     """
     command_store = get_command_storage()
-    permission_classes = [IsOrgAdminOrAppUser | IsOrgAuditor]
     serializer_class = SessionCommandSerializer
     filterset_class = CommandFilter
+    model = Command
     ordering_fields = ('timestamp', )
 
     def merge_all_storage_list(self, request, *args, **kwargs):
@@ -168,6 +166,9 @@ class CommandViewSet(JMSBulkModelViewSet):
 
     def get_queryset(self):
         command_storage_id = self.request.query_params.get('command_storage_id')
+        if not command_storage_id:
+            return Command.objects.none()
+
         storage = CommandStorage.objects.get(id=command_storage_id)
         if not storage.is_valid():
             raise StorageInvalid
@@ -191,6 +192,9 @@ class CommandViewSet(JMSBulkModelViewSet):
 
 class CommandExportApi(CommandQueryMixin, generics.ListAPIView):
     serializer_class = SessionCommandSerializer
+    rbac_perms = {
+        'list': 'terminal.view_command'
+    }
 
     def list(self, request, *args, **kwargs):
         queryset = self.filter_queryset(self.get_queryset())
@@ -210,8 +214,10 @@ class CommandExportApi(CommandQueryMixin, generics.ListAPIView):
 
 
 class InsecureCommandAlertAPI(generics.CreateAPIView):
-    permission_classes = [IsAppUser]
     serializer_class = InsecureCommandAlertSerializer
+    rbac_perms = {
+        'POST': 'terminal.add_command'
+    }
 
     def post(self, request, *args, **kwargs):
         serializer = InsecureCommandAlertSerializer(data=request.data, many=True)
diff --git a/apps/terminal/api/session.py b/apps/terminal/api/session.py
index 65ca88243..334a19cf4 100644
--- a/apps/terminal/api/session.py
+++ b/apps/terminal/api/session.py
@@ -11,45 +11,61 @@ from django.core.files.storage import default_storage
 from rest_framework import viewsets, views
 from rest_framework.response import Response
 from rest_framework.decorators import action
+from rest_framework.permissions import IsAuthenticated
+from rest_framework import generics
 
 from common.utils import data_to_json
-from .. import utils
 from common.const.http import GET
 from common.utils import get_logger, get_object_or_none
 from common.mixins.api import AsyncApiMixin
-from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser
 from common.drf.filters import DatetimeRangeFilter
 from common.drf.renders import PassthroughRenderer
 from orgs.mixins.api import OrgBulkModelViewSet
 from orgs.utils import tmp_to_root_org, tmp_to_org
 from users.models import User
+from .. import utils
 from ..utils import find_session_replay_local, download_session_replay
 from ..models import Session
 from .. import serializers
 from terminal.utils import is_session_approver
 
 __all__ = [
-    'SessionViewSet', 'SessionReplayViewSet', 'SessionJoinValidateAPI'
+    'SessionViewSet', 'SessionReplayViewSet', 'SessionJoinValidateAPI',
+    'MySessionAPIView',
 ]
 
 logger = get_logger(__name__)
 
 
+class MySessionAPIView(generics.ListAPIView):
+    permission_classes = (IsAuthenticated, )
+    serializer_class = serializers.SessionSerializer
+
+    def get_queryset(self):
+        with tmp_to_root_org():
+            user = self.request.user
+            qs = Session.objects.filter(user_id=user.id)
+            return qs
+
+
 class SessionViewSet(OrgBulkModelViewSet):
     model = Session
     serializer_classes = {
         'default': serializers.SessionSerializer,
         'display': serializers.SessionDisplaySerializer,
     }
-    permission_classes = (IsOrgAdminOrAppUser,)
     search_fields = [
-        "user", "asset", "system_user", "remote_addr", "protocol", "is_finished", 'login_from',
+        "user", "asset", "system_user", "remote_addr",
+        "protocol", "is_finished", 'login_from',
     ]
     filterset_fields = search_fields + ['terminal']
     date_range_filter_fields = [
         ('date_start', ('date_from', 'date_to'))
     ]
     extra_filter_backends = [DatetimeRangeFilter]
+    rbac_perms = {
+        'download': ['terminal.download_sessionreplay']
+    }
 
     @staticmethod
     def prepare_offline_file(session, local_path):
@@ -103,17 +119,15 @@ class SessionViewSet(OrgBulkModelViewSet):
             serializer.validated_data["terminal"] = self.request.user.terminal
         return super().perform_create(serializer)
 
-    def get_permissions(self):
-        if self.request.method.lower() in ['get', 'options']:
-            self.permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
-        return super().get_permissions()
-
 
 class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet):
     serializer_class = serializers.ReplaySerializer
-    permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
-    session = None
     download_cache_key = "SESSION_REPLAY_DOWNLOAD_{}"
+    session = None
+    rbac_perms = {
+        'create': 'terminal.upload_sessionreplay',
+        'retrieve': 'terminal.download_sessionreplay',
+    }
 
     def create(self, request, *args, **kwargs):
         session_id = kwargs.get('pk')
@@ -175,8 +189,13 @@ class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet):
 
 
 class SessionJoinValidateAPI(views.APIView):
-    permission_classes = (IsAppUser,)
+    """
+    监控用
+    """
     serializer_class = serializers.SessionJoinValidateSerializer
+    rbac_perms = {
+        'POST': 'terminal.validate_sessionactionperm'
+    }
 
     def post(self, request, *args, **kwargs):
         serializer = self.serializer_class(data=request.data)
@@ -199,11 +218,12 @@ class SessionJoinValidateAPI(views.APIView):
         if not user:
             msg = _('User does not exist: {}'.format(user_id))
             return Response({'ok': False, 'msg': msg}, status=401)
+
         with tmp_to_org(session.org):
             if is_session_approver(session_id, user_id):
                 return Response({'ok': True, 'msg': ''}, status=200)
 
-            if not user.admin_or_audit_orgs:
+            if not user.has_perm('terminal.monitor_session'):
                 msg = _('User does not have permission')
                 return Response({'ok': False, 'msg': msg}, status=401)
 
diff --git a/apps/terminal/api/sharing.py b/apps/terminal/api/sharing.py
index 3fe5ca45c..114a00da1 100644
--- a/apps/terminal/api/sharing.py
+++ b/apps/terminal/api/sharing.py
@@ -3,8 +3,9 @@ from rest_framework.decorators import action
 from rest_framework.response import Response
 from django.conf import settings
 from django.utils.translation import ugettext_lazy as _
-from common.permissions import IsAppUser, IsSuperUser
+
 from common.const.http import PATCH
+from common.permissions import IsValidUser
 from orgs.mixins.api import OrgModelViewSet
 from .. import serializers, models
 
@@ -13,21 +14,22 @@ __all__ = ['SessionSharingViewSet', 'SessionJoinRecordsViewSet']
 
 class SessionSharingViewSet(OrgModelViewSet):
     serializer_class = serializers.SessionSharingSerializer
-    permission_classes = (IsAppUser | IsSuperUser, )
     search_fields = ('session', 'creator', 'is_active', 'expired_time')
     filterset_fields = search_fields
     model = models.SessionSharing
+    rbac_perms = {
+        'create': 'terminal.add_supersessionsharing',
+    }
 
-    def get_permissions(self):
-        if self.request.method.lower() in ['post']:
-            self.permission_classes = (IsAppUser,)
-        return super().get_permissions()
+    def get_queryset(self):
+        queryset = models.SessionSharing.objects.filter(creator=self.request.user)
+        return queryset
 
-    def create(self, request, *args, **kwargs):
+    def dispatch(self, request, *args, **kwargs):
         if not settings.SECURITY_SESSION_SHARE:
             detail = _('Secure session sharing settings is disabled')
             raise MethodNotAllowed(self.action, detail=detail)
-        return super().create(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def destroy(self, request, *args, **kwargs):
         raise MethodNotAllowed(self.action)
@@ -35,7 +37,6 @@ class SessionSharingViewSet(OrgModelViewSet):
 
 class SessionJoinRecordsViewSet(OrgModelViewSet):
     serializer_class = serializers.SessionJoinRecordSerializer
-    permission_classes = (IsAppUser | IsSuperUser, )
     search_fields = (
         'sharing', 'session', 'joiner', 'date_joined', 'date_left',
         'login_from', 'is_success', 'is_finished'
@@ -43,11 +44,6 @@ class SessionJoinRecordsViewSet(OrgModelViewSet):
     filterset_fields = search_fields
     model = models.SessionJoinRecord
 
-    def get_permissions(self):
-        if self.request.method.lower() in ['post']:
-            self.permission_classes = (IsAppUser,)
-        return super().get_permissions()
-
     def create(self, request, *args, **kwargs):
         try:
             response = super().create(request, *args, **kwargs)
diff --git a/apps/terminal/api/status.py b/apps/terminal/api/status.py
index fdf3ef1f1..3ec00436b 100644
--- a/apps/terminal/api/status.py
+++ b/apps/terminal/api/status.py
@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
 #
 
+import datetime
 import logging
+from django.utils import timezone
 from django.shortcuts import get_object_or_404
 from rest_framework import viewsets, generics
-from rest_framework.views import  Response
+from rest_framework.views import Response
 from rest_framework import status
 
-from common.permissions import IsAppUser, IsOrgAdminOrAppUser, IsSuperUser
 from ..models import Terminal, Status, Session
 from .. import serializers
 from ..utils import TypedComponentsStatusMetricsUtil
@@ -15,16 +16,12 @@ from ..utils import TypedComponentsStatusMetricsUtil
 logger = logging.getLogger(__file__)
 
 
-__all__ = [
-    'StatusViewSet',
-    'ComponentsMetricsAPIView',
-]
+__all__ = ['StatusViewSet', 'ComponentsMetricsAPIView']
 
 
 class StatusViewSet(viewsets.ModelViewSet):
     queryset = Status.objects.all()
     serializer_class = serializers.StatusSerializer
-    permission_classes = (IsOrgAdminOrAppUser,)
     session_serializer_class = serializers.SessionSerializer
     task_serializer_class = serializers.TaskSerializer
 
@@ -33,14 +30,24 @@ class StatusViewSet(viewsets.ModelViewSet):
         serializer.is_valid(raise_exception=True)
         self.handle_sessions()
         self.perform_create(serializer)
-        tasks = self.request.user.terminal.task_set.filter(is_finished=False)
-        serializer = self.task_serializer_class(tasks, many=True)
-        return Response(serializer.data, status=201)
+        task_serializer = self.get_task_serializer()
+        return Response(task_serializer.data, status=201)
 
     def handle_sessions(self):
         session_ids = self.request.data.get('sessions', [])
         Session.set_sessions_active(session_ids)
 
+    def perform_create(self, serializer):
+        serializer.validated_data.pop('sessions', None)
+        serializer.validated_data["terminal"] = self.request.user.terminal
+        return super().perform_create(serializer)
+
+    def get_task_serializer(self):
+        critical_time = timezone.now() - datetime.timedelta(minutes=10)
+        tasks = self.request.user.terminal.task_set.filter(is_finished=False, date_created__gte=critical_time)
+        serializer = self.task_serializer_class(tasks, many=True)
+        return serializer
+
     def get_queryset(self):
         terminal_id = self.kwargs.get("terminal", None)
         if terminal_id:
@@ -48,20 +55,12 @@ class StatusViewSet(viewsets.ModelViewSet):
             return terminal.status_set.all()
         return super().get_queryset()
 
-    def perform_create(self, serializer):
-        serializer.validated_data.pop('sessions', None)
-        serializer.validated_data["terminal"] = self.request.user.terminal
-        return super().perform_create(serializer)
-
-    def get_permissions(self):
-        if self.action == "create":
-            self.permission_classes = (IsAppUser,)
-        return super().get_permissions()
-
 
 class ComponentsMetricsAPIView(generics.GenericAPIView):
     """ 返回汇总组件指标数据 """
-    permission_classes = (IsSuperUser,)
+    rbac_perms = {
+        'GET': 'terminal.view_terminal'
+    }
 
     def get(self, request, *args, **kwargs):
         util = TypedComponentsStatusMetricsUtil()
diff --git a/apps/terminal/api/storage.py b/apps/terminal/api/storage.py
index db4470f75..f00611bb2 100644
--- a/apps/terminal/api/storage.py
+++ b/apps/terminal/api/storage.py
@@ -10,12 +10,10 @@ from django_filters import utils
 
 from terminal import const
 from common.const.http import GET
-from common.permissions import IsSuperUser, IsOrgAuditor
 from terminal.filters import CommandStorageFilter, CommandFilter, CommandFilterForStorageTree
 from ..models import CommandStorage, ReplayStorage
 from ..serializers import CommandStorageSerializer, ReplayStorageSerializer
 
-
 __all__ = [
     'CommandStorageViewSet', 'CommandStorageTestConnectiveApi',
     'ReplayStorageViewSet', 'ReplayStorageTestConnectiveApi'
@@ -39,10 +37,12 @@ class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
     search_fields = ('name', 'type')
     queryset = CommandStorage.objects.all()
     serializer_class = CommandStorageSerializer
-    permission_classes = (IsSuperUser,)
     filterset_class = CommandStorageFilter
+    rbac_perms = {
+        'tree': 'terminal.view_commandstorage | terminal.view_command'
+    }
 
-    @action(methods=[GET], detail=False, permission_classes=(IsOrgAuditor, ), filterset_class=CommandFilterForStorageTree)
+    @action(methods=[GET], detail=False, filterset_class=CommandFilterForStorageTree)
     def tree(self, request: Request):
         storage_qs = self.get_queryset().exclude(name='null')
         storages_with_count = []
@@ -107,12 +107,9 @@ class ReplayStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
     search_fields = filterset_fields
     queryset = ReplayStorage.objects.all()
     serializer_class = ReplayStorageSerializer
-    permission_classes = (IsSuperUser,)
 
 
 class BaseStorageTestConnectiveMixin:
-    permission_classes = (IsSuperUser,)
-
     def retrieve(self, request, *args, **kwargs):
         instance = self.get_object()
         try:
diff --git a/apps/terminal/api/task.py b/apps/terminal/api/task.py
index 711707c95..c7e1a2681 100644
--- a/apps/terminal/api/task.py
+++ b/apps/terminal/api/task.py
@@ -7,13 +7,11 @@ from rest_framework import status
 from rest_framework.permissions import IsAuthenticated
 
 from common.utils import get_object_or_none
-from common.permissions import IsOrgAdminOrAppUser
 from ..models import Session, Task
 from .. import serializers
 from terminal.utils import is_session_approver
 from orgs.utils import tmp_to_root_org
 
-
 __all__ = ['TaskViewSet', 'KillSessionAPI', 'KillSessionForTicketAPI']
 logger = logging.getLogger(__file__)
 
@@ -22,7 +20,6 @@ class TaskViewSet(BulkModelViewSet):
     queryset = Task.objects.all()
     serializer_class = serializers.TaskSerializer
     filterset_fields = ('is_finished',)
-    permission_classes = (IsOrgAdminOrAppUser,)
 
 
 def kill_sessions(session_ids, user):
@@ -42,12 +39,14 @@ def kill_sessions(session_ids, user):
 
 
 class KillSessionAPI(APIView):
-    permission_classes = (IsOrgAdminOrAppUser,)
+    model = Task
+    rbac_perms = {
+        'POST': 'terminal.terminate_session'
+    }
 
     def post(self, request, *args, **kwargs):
         session_ids = request.data
-        user = request.user
-        validated_session = kill_sessions(session_ids, user)
+        validated_session = kill_sessions(session_ids, request.user)
         return Response({"ok": validated_session})
 
 
@@ -66,4 +65,3 @@ class KillSessionForTicketAPI(APIView):
             validated_session = kill_sessions(session_ids, request.user)
 
         return Response({"ok": validated_session})
-
diff --git a/apps/terminal/api/terminal.py b/apps/terminal/api/terminal.py
index 9767cb951..f3fa8f5d0 100644
--- a/apps/terminal/api/terminal.py
+++ b/apps/terminal/api/terminal.py
@@ -8,13 +8,12 @@ from rest_framework import generics
 from rest_framework.views import APIView, Response
 from rest_framework import status
 from django.conf import settings
-from django_filters import rest_framework as filters
 from django.utils.translation import gettext_lazy as _
 
 from common.exceptions import JMSException
 from common.drf.api import JMSBulkModelViewSet
 from common.utils import get_object_or_none
-from common.permissions import IsAppUser, IsSuperUser, WithBootstrapToken
+from common.permissions import WithBootstrapToken
 from ..models import Terminal
 from .. import serializers
 from .. import exceptions
@@ -29,7 +28,6 @@ logger = logging.getLogger(__file__)
 class TerminalViewSet(JMSBulkModelViewSet):
     queryset = Terminal.objects.filter(is_deleted=False)
     serializer_class = serializers.TerminalSerializer
-    permission_classes = (IsSuperUser,)
     filterset_fields = ['name', 'remote_addr', 'type']
     custom_filter_fields = ['status']
 
@@ -86,7 +84,9 @@ class TerminalViewSet(JMSBulkModelViewSet):
 
 
 class TerminalConfig(APIView):
-    permission_classes = (IsAppUser,)
+    rbac_perms = {
+        'GET': 'terminal.view_terminalconfig'
+    }
 
     def get(self, request):
         config = request.user.terminal.config
diff --git a/apps/terminal/apps.py b/apps/terminal/apps.py
index edaa38cef..fa46ca9e2 100644
--- a/apps/terminal/apps.py
+++ b/apps/terminal/apps.py
@@ -6,9 +6,9 @@ from django.apps import AppConfig
 
 class TerminalConfig(AppConfig):
     name = 'terminal'
-    verbose_name = _('Terminal')
+    verbose_name = _('Terminals')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
         return super().ready()
diff --git a/apps/terminal/const.py b/apps/terminal/const.py
index 74844d714..931a3e887 100644
--- a/apps/terminal/const.py
+++ b/apps/terminal/const.py
@@ -17,6 +17,7 @@ class ReplayStorageTypeChoices(TextChoices):
     oss = 'oss', 'OSS'
     azure = 'azure', 'Azure'
     obs = 'obs', 'OBS'
+    cos = 'cos', 'COS'
 
 
 class CommandStorageTypeChoices(TextChoices):
@@ -47,6 +48,7 @@ class TerminalTypeChoices(TextChoices):
     lion = 'lion', 'Lion'
     core = 'core', 'Core'
     celery = 'celery', 'Celery'
+    magnus = 'magnus',  'Magnus'
 
     @classmethod
     def types(cls):
diff --git a/apps/terminal/migrations/0043_auto_20220217_2135.py b/apps/terminal/migrations/0043_auto_20220217_2135.py
new file mode 100644
index 000000000..1456d7239
--- /dev/null
+++ b/apps/terminal/migrations/0043_auto_20220217_2135.py
@@ -0,0 +1,64 @@
+# Generated by Django 3.1.13 on 2022-02-17 13:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('terminal', '0042_auto_20211229_1619'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='command',
+            options={'ordering': ('-timestamp',), 'verbose_name': 'Command record'},
+        ),
+        migrations.AlterModelOptions(
+            name='commandstorage',
+            options={'verbose_name': 'Command storage'},
+        ),
+        migrations.AlterModelOptions(
+            name='replaystorage',
+            options={'verbose_name': 'Replay storage'},
+        ),
+        migrations.AlterModelOptions(
+            name='session',
+            options={'ordering': ['-date_start'], 'permissions': [('monitor_session', 'Can monitor session'), ('share_session', 'Can share session'), ('terminate_session', 'Can terminate session'), ('validate_sessionactionperm', 'Can validate session action perm')], 'verbose_name': 'Session record'},
+        ),
+        migrations.AlterModelOptions(
+            name='sessionjoinrecord',
+            options={'ordering': ('-date_joined',), 'verbose_name': 'Session join record'},
+        ),
+        migrations.AlterModelOptions(
+            name='sessionsharing',
+            options={'ordering': ('-date_created',), 'permissions': [('add_supersessionsharing', 'Can add super session sharing')], 'verbose_name': 'Session sharing'},
+        ),
+        migrations.AlterModelOptions(
+            name='status',
+            options={'get_latest_by': 'date_created', 'verbose_name': 'Status'},
+        ),
+        migrations.AlterModelOptions(
+            name='task',
+            options={'verbose_name': 'Task'},
+        ),
+        migrations.AlterModelOptions(
+            name='terminal',
+            options={'ordering': ('is_accepted',), 'permissions': (('view_terminalconfig', 'Can view terminal config'),), 'verbose_name': 'Terminal'},
+        ),
+        migrations.CreateModel(
+            name='SessionReplay',
+            fields=[
+                ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
+                ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
+                ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
+                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
+                ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='terminal.session', verbose_name='Session')),
+            ],
+            options={
+                'permissions': [('upload_sessionreplay', 'Can upload session replay'), ('download_sessionreplay', 'Can download session replay')],
+            },
+        ),
+    ]
diff --git a/apps/terminal/migrations/0044_auto_20220223_1539.py b/apps/terminal/migrations/0044_auto_20220223_1539.py
new file mode 100644
index 000000000..a1557b008
--- /dev/null
+++ b/apps/terminal/migrations/0044_auto_20220223_1539.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2022-02-23 07:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('terminal', '0043_auto_20220217_2135'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='session',
+            name='protocol',
+            field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('vnc', 'vnc'), ('telnet', 'telnet'), ('mysql', 'mysql'), ('oracle', 'oracle'), ('mariadb', 'mariadb'), ('sqlserver', 'sqlserver'), ('postgresql', 'postgresql'), ('redis', 'redis'), ('mongodb', 'MongoDB'), ('k8s', 'kubernetes')], db_index=True, default='ssh', max_length=16),
+        ),
+    ]
diff --git a/apps/terminal/migrations/0045_auto_20220228_1144.py b/apps/terminal/migrations/0045_auto_20220228_1144.py
new file mode 100644
index 000000000..000dcb53b
--- /dev/null
+++ b/apps/terminal/migrations/0045_auto_20220228_1144.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.1.13 on 2022-02-28 03:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('terminal', '0044_auto_20220223_1539'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='session',
+            name='login_from',
+            field=models.CharField(choices=[('ST', 'SSH Terminal'), ('RT', 'RDP Terminal'), ('WT', 'Web Terminal'), ('DT', 'DB Terminal')], default='ST', max_length=2, verbose_name='Login from'),
+        ),
+        migrations.AlterField(
+            model_name='sessionjoinrecord',
+            name='login_from',
+            field=models.CharField(choices=[('ST', 'SSH Terminal'), ('RT', 'RDP Terminal'), ('WT', 'Web Terminal'), ('DT', 'DB Terminal')], default='WT', max_length=2, verbose_name='Login from'),
+        ),
+        migrations.AlterField(
+            model_name='terminal',
+            name='type',
+            field=models.CharField(choices=[('koko', 'KoKo'), ('guacamole', 'Guacamole'), ('omnidb', 'OmniDB'), ('xrdp', 'Xrdp'), ('lion', 'Lion'), ('core', 'Core'), ('celery', 'Celery'), ('magnus', 'Magnus')], default='koko', max_length=64, verbose_name='type'),
+        ),
+    ]
diff --git a/apps/terminal/migrations/0046_auto_20220228_1744.py b/apps/terminal/migrations/0046_auto_20220228_1744.py
new file mode 100644
index 000000000..be651022f
--- /dev/null
+++ b/apps/terminal/migrations/0046_auto_20220228_1744.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.13 on 2022-02-28 09:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('terminal', '0045_auto_20220228_1144'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='replaystorage',
+            name='type',
+            field=models.CharField(choices=[('null', 'Null'), ('server', 'Server'), ('s3', 'S3'), ('ceph', 'Ceph'), ('swift', 'Swift'), ('oss', 'OSS'), ('azure', 'Azure'), ('obs', 'OBS'), ('cos', 'COS')], default='server', max_length=16, verbose_name='Type'),
+        ),
+    ]
diff --git a/apps/terminal/migrations/0047_auto_20220302_1951.py b/apps/terminal/migrations/0047_auto_20220302_1951.py
new file mode 100644
index 000000000..a67b260b8
--- /dev/null
+++ b/apps/terminal/migrations/0047_auto_20220302_1951.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.1.14 on 2022-03-02 11:51
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('terminal', '0046_auto_20220228_1744'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='sessionreplay',
+            options={'permissions': [('upload_sessionreplay', 'Can upload session replay'), ('download_sessionreplay', 'Can download session replay')], 'verbose_name': 'Session replay'},
+        ),
+    ]
diff --git a/apps/terminal/models/__init__.py b/apps/terminal/models/__init__.py
index ef5c759a6..e69928b3a 100644
--- a/apps/terminal/models/__init__.py
+++ b/apps/terminal/models/__init__.py
@@ -5,3 +5,4 @@ from .storage import *
 from .task import *
 from .terminal import *
 from .sharing import *
+from .replay import *
diff --git a/apps/terminal/models/command.py b/apps/terminal/models/command.py
index fba906226..7609902f2 100644
--- a/apps/terminal/models/command.py
+++ b/apps/terminal/models/command.py
@@ -2,6 +2,8 @@ from __future__ import unicode_literals
 
 from django.db import models
 from django.db.models.signals import post_save
+from django.utils.translation import ugettext_lazy as _
+
 from ..backends.command.models import AbstractSessionCommand
 
 
@@ -19,3 +21,4 @@ class Command(AbstractSessionCommand):
     class Meta:
         db_table = "terminal_command"
         ordering = ('-timestamp',)
+        verbose_name = _('Command record')
diff --git a/apps/terminal/models/replay.py b/apps/terminal/models/replay.py
new file mode 100644
index 000000000..5f3e372af
--- /dev/null
+++ b/apps/terminal/models/replay.py
@@ -0,0 +1,18 @@
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from common.mixins.models import CommonModelMixin
+from .session import Session
+
+
+class SessionReplay(CommonModelMixin):
+    session = models.ForeignKey(Session, on_delete=models.CASCADE, verbose_name=_("Session"))
+
+    class Meta:
+        verbose_name = _("Session replay")
+        permissions = [
+            ('upload_sessionreplay', _("Can upload session replay")),
+            ('download_sessionreplay', _("Can download session replay")),
+        ]
+
+
diff --git a/apps/terminal/models/session.py b/apps/terminal/models/session.py
index 6d90bc57e..09f1c9e91 100644
--- a/apps/terminal/models/session.py
+++ b/apps/terminal/models/session.py
@@ -13,7 +13,7 @@ from django.core.cache import cache
 from assets.models import Asset
 from users.models import User
 from orgs.mixins.models import OrgModelMixin
-from common.db.models import TextChoices
+from django.db.models import TextChoices
 from ..backends import get_multi_command_storage
 
 
@@ -22,6 +22,7 @@ class Session(OrgModelMixin):
         ST = 'ST', 'SSH Terminal'
         RT = 'RT', 'RDP Terminal'
         WT = 'WT', 'Web Terminal'
+        DT = 'DT', 'DB Terminal'
 
     class PROTOCOL(TextChoices):
         SSH = 'ssh', 'ssh'
@@ -29,11 +30,12 @@ class Session(OrgModelMixin):
         VNC = 'vnc', 'vnc'
         TELNET = 'telnet', 'telnet'
         MYSQL = 'mysql', 'mysql'
-        REDIS = 'redis', 'redis'
         ORACLE = 'oracle', 'oracle'
         MARIADB = 'mariadb', 'mariadb'
         SQLSERVER = 'sqlserver', 'sqlserver'
         POSTGRESQL = 'postgresql', 'postgresql'
+        REDIS = 'redis', 'redis'
+        MONGODB = 'mongodb', 'MongoDB'
         K8S = 'k8s', 'kubernetes'
 
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
@@ -145,8 +147,9 @@ class Session(OrgModelMixin):
     @property
     def db_protocols(self):
         _PROTOCOL = self.PROTOCOL
-        return [_PROTOCOL.MYSQL, _PROTOCOL.MARIADB, _PROTOCOL.REDIS,
-                _PROTOCOL.ORACLE, _PROTOCOL.POSTGRESQL, _PROTOCOL.SQLSERVER]
+        return [_PROTOCOL.MYSQL, _PROTOCOL.MARIADB, _PROTOCOL.ORACLE,
+                _PROTOCOL.POSTGRESQL, _PROTOCOL.SQLSERVER,
+                _PROTOCOL.REDIS, _PROTOCOL.MONGODB]
 
     @property
     def can_terminate(self):
@@ -236,6 +239,13 @@ class Session(OrgModelMixin):
     class Meta:
         db_table = "terminal_session"
         ordering = ["-date_start"]
+        verbose_name = _('Session record')
+        permissions = [
+            ('monitor_session', _('Can monitor session')),
+            ('share_session', _('Can share session')),
+            ('terminate_session', _('Can terminate session')),
+            ('validate_sessionactionperm', _('Can validate session action perm')),
+        ]
 
     def __str__(self):
         return "{0.id} of {0.user} to {0.asset}".format(self)
diff --git a/apps/terminal/models/sharing.py b/apps/terminal/models/sharing.py
index 46b4eb1e8..45d62fb92 100644
--- a/apps/terminal/models/sharing.py
+++ b/apps/terminal/models/sharing.py
@@ -1,9 +1,11 @@
-from django.db import models
 import datetime
-from common.mixins import CommonModelMixin
-from orgs.mixins.models import OrgModelMixin
+
+from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from django.utils import timezone
+
+from common.mixins import CommonModelMixin
+from orgs.mixins.models import OrgModelMixin
 from .session import Session
 
 
@@ -29,6 +31,10 @@ class SessionSharing(CommonModelMixin, OrgModelMixin):
 
     class Meta:
         ordering = ('-date_created', )
+        verbose_name = _('Session sharing')
+        permissions = [
+            ('add_supersessionsharing', _("Can add super session sharing"))
+        ]
 
     def __str__(self):
         return 'Creator: {}'.format(self.creator)
@@ -93,6 +99,7 @@ class SessionJoinRecord(CommonModelMixin, OrgModelMixin):
 
     class Meta:
         ordering = ('-date_joined', )
+        verbose_name = _("Session join record")
 
     def __str__(self):
         return 'Joiner: {}'.format(self.joiner)
diff --git a/apps/terminal/models/status.py b/apps/terminal/models/status.py
index bc195d857..048bca103 100644
--- a/apps/terminal/models/status.py
+++ b/apps/terminal/models/status.py
@@ -30,6 +30,7 @@ class Status(models.Model):
     class Meta:
         db_table = 'terminal_status'
         get_latest_by = 'date_created'
+        verbose_name = _("Status")
 
     def save_to_cache(self):
         if not self.terminal:
@@ -73,3 +74,4 @@ class Status(models.Model):
         return self.save_to_cache()
         # return super().save()
 
+
diff --git a/apps/terminal/models/storage.py b/apps/terminal/models/storage.py
index bb2495c7e..93be5551a 100644
--- a/apps/terminal/models/storage.py
+++ b/apps/terminal/models/storage.py
@@ -109,6 +109,9 @@ class CommandStorage(CommonStorageModelMixin, CommonModelMixin):
             backend = engine_mod.CommandStore(self.config)
             backend.pre_use_check()
 
+    class Meta:
+        verbose_name = _("Command storage")
+
 
 class ReplayStorage(CommonStorageModelMixin, CommonModelMixin):
     type = models.CharField(
@@ -165,3 +168,6 @@ class ReplayStorage(CommonStorageModelMixin, CommonModelMixin):
 
     def is_use(self):
         return Terminal.objects.filter(replay_storage=self.name, is_deleted=False).exists()
+
+    class Meta:
+        verbose_name = _("Replay storage")
diff --git a/apps/terminal/models/task.py b/apps/terminal/models/task.py
index 5f1f3f0fe..0225dc641 100644
--- a/apps/terminal/models/task.py
+++ b/apps/terminal/models/task.py
@@ -23,4 +23,5 @@ class Task(models.Model):
 
     class Meta:
         db_table = "terminal_task"
+        verbose_name = _("Task")
 
diff --git a/apps/terminal/models/terminal.py b/apps/terminal/models/terminal.py
index 7ae262603..8578ab599 100644
--- a/apps/terminal/models/terminal.py
+++ b/apps/terminal/models/terminal.py
@@ -157,15 +157,6 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model):
     def service_account(self):
         return self.user
 
-    def create_app_user(self):
-        random = uuid.uuid4().hex[:6]
-        user, access_key = User.create_app_user(
-            name="{}-{}".format(self.name, random), comment=self.comment
-        )
-        self.user = user
-        self.save()
-        return user, access_key
-
     def delete(self, using=None, keep_parents=False):
         if self.user:
             self.user.delete()
@@ -189,4 +180,8 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model):
     class Meta:
         ordering = ('is_accepted',)
         db_table = "terminal"
+        verbose_name = _("Terminal")
+        permissions = (
+            ('view_terminalconfig', 'Can view terminal config'),
+        )
 
diff --git a/apps/terminal/serializers/storage.py b/apps/terminal/serializers/storage.py
index 3e4e99bc0..2b21625bd 100644
--- a/apps/terminal/serializers/storage.py
+++ b/apps/terminal/serializers/storage.py
@@ -42,8 +42,8 @@ class ReplayStorageTypeBaseSerializer(serializers.Serializer):
 
 class ReplayStorageTypeS3Serializer(ReplayStorageTypeBaseSerializer):
     endpoint_help_text = '''
-        S3 format: http://s3.{REGION_NAME}.amazonaws.com
-        S3(China) format: http://s3.{REGION_NAME}.amazonaws.com.cn
+        S3 format: http://s3.{REGION_NAME}.amazonaws.com <br>
+        S3(China) format: http://s3.{REGION_NAME}.amazonaws.com.cn <br>
         Such as: http://s3.cn-north-1.amazonaws.com.cn
     '''
     ENDPOINT = serializers.CharField(
@@ -73,7 +73,7 @@ class ReplayStorageTypeSwiftSerializer(ReplayStorageTypeBaseSerializer):
 
 class ReplayStorageTypeOSSSerializer(ReplayStorageTypeBaseSerializer):
     endpoint_help_text = '''
-        OSS format: http://{REGION_NAME}.aliyuncs.com
+        OSS format: http://{REGION_NAME}.aliyuncs.com <br>
         Such as: http://oss-cn-hangzhou.aliyuncs.com
     '''
     ENDPOINT = serializers.CharField(
@@ -84,7 +84,7 @@ class ReplayStorageTypeOSSSerializer(ReplayStorageTypeBaseSerializer):
 
 class ReplayStorageTypeOBSSerializer(ReplayStorageTypeBaseSerializer):
     endpoint_help_text = '''
-        OBS format: obs.{REGION_NAME}.myhuaweicloud.com
+        OBS format: obs.{REGION_NAME}.myhuaweicloud.com <br>
         Such as: obs.cn-north-4.myhuaweicloud.com
     '''
     ENDPOINT = serializers.CharField(
@@ -92,6 +92,15 @@ class ReplayStorageTypeOBSSerializer(ReplayStorageTypeBaseSerializer):
     )
 
 
+class ReplayStorageTypeCOSSerializer(ReplayStorageTypeS3Serializer):
+    endpoint_help_text = '''Such as: http://cos.{REGION_NAME}.myqcloud.com'''
+    ENDPOINT = serializers.CharField(
+        validators=[replay_storage_endpoint_format_validator],
+        required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
+        allow_null=True,
+    )
+
+
 class ReplayStorageTypeAzureSerializer(serializers.Serializer):
     class EndpointSuffixChoices(TextChoices):
         china = 'core.chinacloudapi.cn', 'core.chinacloudapi.cn'
@@ -116,7 +125,8 @@ replay_storage_type_serializer_classes_mapping = {
     const.ReplayStorageTypeChoices.swift.value: ReplayStorageTypeSwiftSerializer,
     const.ReplayStorageTypeChoices.oss.value: ReplayStorageTypeOSSSerializer,
     const.ReplayStorageTypeChoices.azure.value: ReplayStorageTypeAzureSerializer,
-    const.ReplayStorageTypeChoices.obs.value: ReplayStorageTypeOBSSerializer
+    const.ReplayStorageTypeChoices.obs.value: ReplayStorageTypeOBSSerializer,
+    const.ReplayStorageTypeChoices.cos.value: ReplayStorageTypeCOSSerializer
 }
 
 # Command storage serializers
@@ -143,7 +153,7 @@ def command_storage_es_host_format_validator(host):
 class CommandStorageTypeESSerializer(serializers.Serializer):
 
     hosts_help_text = '''
-        Tip: If there are multiple hosts, use a comma (,) to separate them. 
+        Tip: If there are multiple hosts, use a comma (,) to separate them. <br>
         (eg: http://www.jumpserver.a.com:9100, http://www.jumpserver.b.com:9100)
     '''
     HOSTS = serializers.ListField(
diff --git a/apps/terminal/serializers/terminal.py b/apps/terminal/serializers/terminal.py
index 5c20f4d92..626529a5e 100644
--- a/apps/terminal/serializers/terminal.py
+++ b/apps/terminal/serializers/terminal.py
@@ -127,13 +127,14 @@ class TerminalRegistrationSerializer(serializers.ModelSerializer):
         valid = self.service_account.is_valid(raise_exception=True)
         return valid
 
-    def save(self, **kwargs):
-        instance = super().save(**kwargs)
+    def create(self, validated_data):
+        instance = super().create(validated_data)
         request = self.context.get('request')
         instance.is_accepted = True
         if request:
             instance.remote_addr = get_request_ip(request)
-        sa = self.service_account.save()
+        sa = self.service_account.create(validated_data)
+        sa.set_component_role()
         instance.user = sa
         instance.command_storage = CommandStorage.default().name
         instance.replay_storage = ReplayStorage.default().name
diff --git a/apps/terminal/signals_handler.py b/apps/terminal/signal_handlers.py
similarity index 100%
rename from apps/terminal/signals_handler.py
rename to apps/terminal/signal_handlers.py
diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py
index 0f19d0435..59cf00fa4 100644
--- a/apps/terminal/tasks.py
+++ b/apps/terminal/tasks.py
@@ -14,7 +14,7 @@ from common.utils import get_log_keep_day
 from ops.celery.decorator import (
     register_as_period_task, after_app_ready_start, after_app_shutdown_clean_periodic
 )
-from .models import Status, Session, Command
+from .models import Status, Session, Command, Task
 from .backends import server_replay_storage
 from .utils import find_session_replay_local
 
@@ -39,6 +39,11 @@ def delete_terminal_status_period():
 def clean_orphan_session():
     active_sessions = Session.objects.filter(is_finished=False)
     for session in active_sessions:
+        # finished task
+        Task.objects.filter(args=str(session.id), is_finished=False).update(
+            is_finished=True, date_finished=timezone.now()
+        )
+        # finished session
         if session.is_active():
             continue
         session.is_finished = True
diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py
index ebf6fa47f..9366332e2 100644
--- a/apps/terminal/urls/api_urls.py
+++ b/apps/terminal/urls/api_urls.py
@@ -24,7 +24,9 @@ router.register(r'session-sharings', api.SessionSharingViewSet, 'session-sharing
 router.register(r'session-join-records', api.SessionJoinRecordsViewSet, 'session-sharing-record')
 
 urlpatterns = [
+    path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'),
     path('terminal-registrations/', api.TerminalRegistrationApi.as_view(), name='terminal-registration'),
+    path('registration/', api.TerminalRegistrationApi.as_view(), name='registration'),
     path('sessions/join/validate/', api.SessionJoinValidateAPI.as_view(), name='join-session-validate'),
     path('sessions/<uuid:pk>/replay/',
          api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
diff --git a/apps/tickets/api/__init__.py b/apps/tickets/api/__init__.py
index 4aad8f12a..0342771e0 100644
--- a/apps/tickets/api/__init__.py
+++ b/apps/tickets/api/__init__.py
@@ -2,5 +2,5 @@
 #
 from .ticket import *
 from .comment import *
-from .common import *
+from .super_ticket import *
 from .relation import *
diff --git a/apps/tickets/api/assignee.py b/apps/tickets/api/assignee.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/tickets/api/common.py b/apps/tickets/api/common.py
deleted file mode 100644
index 2838d23d0..000000000
--- a/apps/tickets/api/common.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from django.shortcuts import get_object_or_404
-from rest_framework.response import Response
-from rest_framework.generics import RetrieveDestroyAPIView
-
-from common.permissions import IsAppUser
-from common.utils import lazyproperty
-from orgs.utils import tmp_to_root_org
-from ..models import Ticket
-
-
-__all__ = ['GenericTicketStatusRetrieveCloseAPI']
-
-
-class GenericTicketStatusRetrieveCloseAPI(RetrieveDestroyAPIView):
-    permission_classes = (IsAppUser, )
-
-    def retrieve(self, request, *args, **kwargs):
-        if self.ticket.state_open:
-            status = 'await'
-        elif self.ticket.state_approve:
-            status = 'approved'
-        else:
-            status = 'rejected'
-        data = {
-            'status': status,
-            'action': self.ticket.state,
-            'processor': str(self.ticket.processor)
-        }
-        return Response(data=data, status=200)
-
-    def destroy(self, request, *args, **kwargs):
-        if self.ticket.status_open:
-            self.ticket.close(processor=self.ticket.applicant)
-        data = {
-            'action': self.ticket.state,
-            'status': self.ticket.status,
-            'processor': str(self.ticket.processor)
-        }
-        return Response(data=data, status=200)
-
-    @lazyproperty
-    def ticket(self):
-        with tmp_to_root_org():
-            return get_object_or_404(Ticket, pk=self.kwargs['pk'])
diff --git a/apps/tickets/api/relation.py b/apps/tickets/api/relation.py
index f1fadad48..674be316c 100644
--- a/apps/tickets/api/relation.py
+++ b/apps/tickets/api/relation.py
@@ -4,7 +4,6 @@ from rest_framework.response import Response
 from rest_framework import status
 
 from common.drf.api import JMSGenericViewSet
-from common.permissions import IsOrgAdminOrAppUser
 from tickets.models import TicketSession
 from tickets.serializers import TicketSessionRelationSerializer
 from terminal.serializers import SessionSerializer
@@ -12,11 +11,11 @@ from orgs.utils import tmp_to_root_org
 
 
 class TicketSessionRelationViewSet(CreateModelMixin, JMSGenericViewSet):
-    queryset = TicketSession
+    queryset = TicketSession.objects.all()
     serializer_class = TicketSessionRelationSerializer
-    permission_classes = (IsOrgAdminOrAppUser, )
 
 
+# Todo: 放到上面的 ViewSet 中
 class TicketSessionApi(views.APIView):
 
     def get(self, request, *args, **kwargs):
diff --git a/apps/tickets/api/super_ticket.py b/apps/tickets/api/super_ticket.py
new file mode 100644
index 000000000..ea186bd1b
--- /dev/null
+++ b/apps/tickets/api/super_ticket.py
@@ -0,0 +1,23 @@
+from rest_framework.generics import RetrieveDestroyAPIView
+
+from orgs.utils import tmp_to_root_org
+from ..serializers import SuperTicketSerializer
+from ..models import Ticket
+
+
+__all__ = ['SuperTicketStatusAPI']
+
+
+class SuperTicketStatusAPI(RetrieveDestroyAPIView):
+    serializer_class = SuperTicketSerializer
+    rbac_perms = {
+        'GET': 'tickets.view_superticket',
+        'DELETE': 'tickets.change_superticket'
+    }
+
+    def get_queryset(self):
+        with tmp_to_root_org():
+            return Ticket.objects.all()
+
+    def perform_destroy(self, instance):
+        instance.close(processor=instance.applicant)
diff --git a/apps/tickets/api/ticket.py b/apps/tickets/api/ticket.py
index 3d1d3081e..e61dda569 100644
--- a/apps/tickets/api/ticket.py
+++ b/apps/tickets/api/ticket.py
@@ -7,7 +7,7 @@ from rest_framework.response import Response
 
 from common.const.http import POST, PUT
 from common.mixins.api import CommonApiMixin
-from common.permissions import IsValidUser, IsOrgAdmin, IsSuperUser
+from common.permissions import IsValidUser
 from common.drf.api import JMSBulkModelViewSet
 
 from tickets import serializers
@@ -81,7 +81,6 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
 
 
 class TicketFlowViewSet(JMSBulkModelViewSet):
-    permission_classes = (IsSuperUser,)
     serializer_class = serializers.TicketFlowSerializer
 
     filterset_fields = ['id', 'type']
diff --git a/apps/tickets/apps.py b/apps/tickets/apps.py
index a7beac9a4..317ac592e 100644
--- a/apps/tickets/apps.py
+++ b/apps/tickets/apps.py
@@ -1,10 +1,12 @@
 from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
 
 
 class TicketsConfig(AppConfig):
     name = 'tickets'
+    verbose_name = _('Tickets')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
         return super().ready()
diff --git a/apps/tickets/migrations/0012_ticketsession.py b/apps/tickets/migrations/0012_ticketsession.py
index 63f19bf38..ac4afdad8 100644
--- a/apps/tickets/migrations/0012_ticketsession.py
+++ b/apps/tickets/migrations/0012_ticketsession.py
@@ -19,5 +19,6 @@ class Migration(migrations.Migration):
                 ('session', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, related_name='ticket_relation', to='terminal.session')),
                 ('ticket', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, related_name='session_relation', to='tickets.ticket')),
             ],
+            options={'verbose_name': 'Ticket session relation'},
         ),
     ]
diff --git a/apps/tickets/migrations/0014_auto_20220217_2135.py b/apps/tickets/migrations/0014_auto_20220217_2135.py
new file mode 100644
index 000000000..246ef1bed
--- /dev/null
+++ b/apps/tickets/migrations/0014_auto_20220217_2135.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.13 on 2022-02-17 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tickets', '0013_ticket_serial_num'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='comment',
+            options={'ordering': ('date_created',), 'verbose_name': 'Comment'},
+        ),
+        migrations.AlterModelOptions(
+            name='ticket',
+            options={'ordering': ('-date_created',), 'verbose_name': 'Ticket'},
+        ),
+        migrations.AlterModelOptions(
+            name='ticketstep',
+            options={'verbose_name': 'Ticket step'},
+        ),
+    ]
diff --git a/apps/tickets/migrations/0015_superticket.py b/apps/tickets/migrations/0015_superticket.py
new file mode 100644
index 000000000..6d2c526b7
--- /dev/null
+++ b/apps/tickets/migrations/0015_superticket.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.14 on 2022-02-28 10:59
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tickets', '0014_auto_20220217_2135'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='SuperTicket',
+            fields=[
+            ],
+            options={
+                'verbose_name': 'Super ticket',
+                'proxy': True,
+                'indexes': [],
+                'constraints': [],
+            },
+            bases=('tickets.ticket',),
+        ),
+    ]
diff --git a/apps/tickets/models/comment.py b/apps/tickets/models/comment.py
index 11ebcd546..c08b29dbe 100644
--- a/apps/tickets/models/comment.py
+++ b/apps/tickets/models/comment.py
@@ -21,6 +21,7 @@ class Comment(CommonModelMixin):
 
     class Meta:
         ordering = ('date_created', )
+        verbose_name = _("Comment")
 
     def set_display_fields(self):
         self.user_display = str(self.user)
diff --git a/apps/tickets/models/relation.py b/apps/tickets/models/relation.py
index ee5889587..ca159804d 100644
--- a/apps/tickets/models/relation.py
+++ b/apps/tickets/models/relation.py
@@ -1,11 +1,14 @@
 from django.db import models
-from django.db.models import Model
+from django.utils.translation import ugettext_lazy as _
 
 
-class TicketSession(Model):
+class TicketSession(models.Model):
     ticket = models.ForeignKey('tickets.Ticket', related_name='session_relation', on_delete=models.CASCADE, db_constraint=False)
     session = models.ForeignKey('terminal.Session', related_name='ticket_relation', on_delete=models.CASCADE, db_constraint=False)
 
+    class Meta:
+        verbose_name = _("Ticket session relation")
+
     @classmethod
     def get_ticket_by_session_id(cls, session_id):
         relation = cls.objects.filter(session=session_id).first()
diff --git a/apps/tickets/models/ticket.py b/apps/tickets/models/ticket.py
index f61870f55..aa536584d 100644
--- a/apps/tickets/models/ticket.py
+++ b/apps/tickets/models/ticket.py
@@ -1,9 +1,10 @@
 # -*- coding: utf-8 -*-
 #
+from typing import Callable
+
 from django.db import models
 from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
-from datetime import timedelta
 from django.db.utils import IntegrityError
 
 from common.exceptions import JMSException
@@ -17,7 +18,7 @@ from tickets.signals import post_change_ticket_action
 from tickets.handler import get_ticket_handler
 from tickets.errors import AlreadyClosed
 
-__all__ = ['Ticket', 'TicketStep', 'TicketAssignee']
+__all__ = ['Ticket', 'TicketStep', 'TicketAssignee', 'SuperTicket']
 
 
 class TicketStep(CommonModelMixin):
@@ -30,6 +31,9 @@ class TicketStep(CommonModelMixin):
     )
     state = models.CharField(choices=ProcessStatus.choices, max_length=64, default=ProcessStatus.notified)
 
+    class Meta:
+        verbose_name = _("Ticket step")
+
 
 class TicketAssignee(CommonModelMixin):
     assignee = models.ForeignKey(
@@ -45,7 +49,82 @@ class TicketAssignee(CommonModelMixin):
         return '{0.assignee.name}({0.assignee.username})_{0.step}'.format(self)
 
 
-class Ticket(CommonModelMixin, OrgModelMixin):
+class StatusMixin:
+    state: str
+    status: str
+    applicant: models.ForeignKey
+    current_node: models.Manager
+    save: Callable
+
+    def set_state_approve(self):
+        self.state = TicketState.approved
+
+    def set_state_reject(self):
+        self.state = TicketState.rejected
+
+    def set_state_closed(self):
+        self.state = TicketState.closed
+
+    def set_status_closed(self):
+        self.status = TicketStatus.closed
+
+    # status
+    @property
+    def status_open(self):
+        return self.status == TicketStatus.open.value
+
+    @property
+    def status_closed(self):
+        return self.status == TicketStatus.closed.value
+
+    @property
+    def state_open(self):
+        return self.state == TicketState.open.value
+
+    @property
+    def state_approve(self):
+        return self.state == TicketState.approved.value
+
+    @property
+    def state_reject(self):
+        return self.state == TicketState.rejected.value
+
+    @property
+    def state_close(self):
+        return self.state == TicketState.closed.value
+
+    # action changed
+    def open(self, applicant):
+        self.applicant = applicant
+        self._change_action(TicketAction.open)
+
+    def approve(self, processor):
+        self.update_current_step_state_and_assignee(processor, TicketState.approved)
+        self._change_action(TicketAction.approve)
+
+    def reject(self, processor):
+        self.update_current_step_state_and_assignee(processor, TicketState.rejected)
+        self._change_action(TicketAction.reject)
+
+    def close(self, processor):
+        self.update_current_step_state_and_assignee(processor, TicketState.closed)
+        self._change_action(TicketAction.close)
+
+    def update_current_step_state_and_assignee(self, processor, state):
+        if self.status_closed:
+            raise AlreadyClosed
+        if state != TicketState.approved:
+            self.state = state
+        current_node = self.current_node
+        current_node.update(state=state)
+        current_node.first().ticket_assignees.filter(assignee=processor).update(state=state)
+
+    def _change_action(self, action):
+        self.save()
+        post_change_ticket_action.send(sender=self.__class__, ticket=self, action=action)
+
+
+class Ticket(CommonModelMixin, StatusMixin, OrgModelMixin):
     title = models.CharField(max_length=256, verbose_name=_("Title"))
     type = models.CharField(
         max_length=64, choices=TicketType.choices,
@@ -81,6 +160,7 @@ class Ticket(CommonModelMixin, OrgModelMixin):
 
     class Meta:
         ordering = ('-date_created',)
+        verbose_name = _('Ticket')
 
     def __str__(self):
         return '{}({})'.format(self.title, self.applicant_display)
@@ -98,52 +178,16 @@ class Ticket(CommonModelMixin, OrgModelMixin):
     def type_login_confirm(self):
         return self.type == TicketType.login_confirm.value
 
-    # status
-    @property
-    def status_open(self):
-        return self.status == TicketStatus.open.value
-
-    @property
-    def status_closed(self):
-        return self.status == TicketStatus.closed.value
-
-    @property
-    def state_open(self):
-        return self.state == TicketState.open.value
-
-    @property
-    def state_approve(self):
-        return self.state == TicketState.approved.value
-
-    @property
-    def state_reject(self):
-        return self.state == TicketState.rejected.value
-
-    @property
-    def state_close(self):
-        return self.state == TicketState.closed.value
-
     @property
     def current_node(self):
         return self.ticket_steps.filter(level=self.approval_step)
 
     @property
     def processor(self):
-        processor = self.current_node.first().ticket_assignees.exclude(state=ProcessStatus.notified).first()
+        processor = self.current_node.first().ticket_assignees\
+            .exclude(state=ProcessStatus.notified).first()
         return processor.assignee if processor else None
 
-    def set_state_approve(self):
-        self.state = TicketState.approved
-
-    def set_state_reject(self):
-        self.state = TicketState.rejected
-
-    def set_state_closed(self):
-        self.state = TicketState.closed
-
-    def set_status_closed(self):
-        self.status = TicketStatus.closed
-
     def create_related_node(self):
         org_id = self.flow.org_id
         approval_rule = self.get_current_ticket_flow_approve()
@@ -187,36 +231,6 @@ class Ticket(CommonModelMixin, OrgModelMixin):
             ticket_assignees.append(TicketAssignee(step=ticket_step, assignee=assignee))
         TicketAssignee.objects.bulk_create(ticket_assignees)
 
-    # action changed
-    def open(self, applicant):
-        self.applicant = applicant
-        self._change_action(TicketAction.open)
-
-    def update_current_step_state_and_assignee(self, processor, state):
-        if self.status_closed:
-            raise AlreadyClosed
-        if state != TicketState.approved:
-            self.state = state
-        current_node = self.current_node
-        current_node.update(state=state)
-        current_node.first().ticket_assignees.filter(assignee=processor).update(state=state)
-
-    def approve(self, processor):
-        self.update_current_step_state_and_assignee(processor, TicketState.approved)
-        self._change_action(TicketAction.approve)
-
-    def reject(self, processor):
-        self.update_current_step_state_and_assignee(processor, TicketState.rejected)
-        self._change_action(TicketAction.reject)
-
-    def close(self, processor):
-        self.update_current_step_state_and_assignee(processor, TicketState.closed)
-        self._change_action(TicketAction.close)
-
-    def _change_action(self, action):
-        self.save()
-        post_change_ticket_action.send(sender=self.__class__, ticket=self, action=action)
-
     def has_current_assignee(self, assignee):
         return self.ticket_steps.filter(ticket_assignees__assignee=assignee, level=self.approval_step).exists()
 
@@ -297,3 +311,9 @@ class Ticket(CommonModelMixin, OrgModelMixin):
                 raise JMSException(detail=_('Please try again'), code='please_try_again')
 
             raise e
+
+
+class SuperTicket(Ticket):
+    class Meta:
+        proxy = True
+        verbose_name = _("Super ticket")
diff --git a/apps/tickets/serializers/__init__.py b/apps/tickets/serializers/__init__.py
index 340fd1496..853feafd1 100644
--- a/apps/tickets/serializers/__init__.py
+++ b/apps/tickets/serializers/__init__.py
@@ -3,3 +3,4 @@
 from .ticket import *
 from .comment import *
 from .relation import *
+from .super_ticket import *
\ No newline at end of file
diff --git a/apps/tickets/serializers/super_ticket.py b/apps/tickets/serializers/super_ticket.py
new file mode 100644
index 000000000..9d84728e4
--- /dev/null
+++ b/apps/tickets/serializers/super_ticket.py
@@ -0,0 +1,21 @@
+from rest_framework import serializers
+from django.utils.translation import gettext_lazy as _
+
+from ..models import SuperTicket
+
+
+__all__ = ['SuperTicketSerializer']
+
+
+class SuperTicketSerializer(serializers.ModelSerializer):
+    processor = serializers.SerializerMethodField(label=_("Processor"))
+
+    class Meta:
+        model = SuperTicket
+        fields = ['id', 'status', 'state', 'processor']
+
+    @staticmethod
+    def get_processor(ticket):
+        if not ticket.processor:
+            return ''
+        return str(ticket.processor)
diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py
index cc4a90936..aec89cfae 100644
--- a/apps/tickets/serializers/ticket/ticket.py
+++ b/apps/tickets/serializers/ticket/ticket.py
@@ -206,6 +206,7 @@ class TicketFlowSerializer(OrgResourceModelSerializerMixin):
         instance_related = getattr(instance, related)
         child_instances = []
         related_model = instance_related.model
+        # Todo: 这个权限的判断
         for level, data in enumerate(childs, 1):
             data_m2m = data.pop(assignees, None)
             child_instance = related_model.objects.create(**data, level=level)
diff --git a/apps/tickets/signals_handler/__init__.py b/apps/tickets/signal_handlers/__init__.py
similarity index 100%
rename from apps/tickets/signals_handler/__init__.py
rename to apps/tickets/signal_handlers/__init__.py
diff --git a/apps/tickets/signals_handler/comment.py b/apps/tickets/signal_handlers/comment.py
similarity index 100%
rename from apps/tickets/signals_handler/comment.py
rename to apps/tickets/signal_handlers/comment.py
diff --git a/apps/tickets/signals_handler/ticket.py b/apps/tickets/signal_handlers/ticket.py
similarity index 100%
rename from apps/tickets/signals_handler/ticket.py
rename to apps/tickets/signal_handlers/ticket.py
diff --git a/apps/tickets/urls/api_urls.py b/apps/tickets/urls/api_urls.py
index 94d0f1cbb..bc6bb7f54 100644
--- a/apps/tickets/urls/api_urls.py
+++ b/apps/tickets/urls/api_urls.py
@@ -16,5 +16,6 @@ router.register('ticket-session-relation', api.TicketSessionRelationViewSet, 'ti
 
 urlpatterns = [
     path('tickets/<uuid:ticket_id>/session/', api.TicketSessionApi.as_view(), name='ticket-sesion'),
+    path('super-tickets/<uuid:pk>/status/', api.SuperTicketStatusAPI.as_view(), name='super-ticket-status'),
 ]
 urlpatterns += router.urls
diff --git a/apps/users/api/group.py b/apps/users/api/group.py
index db8c4109c..4c1f06ea7 100644
--- a/apps/users/api/group.py
+++ b/apps/users/api/group.py
@@ -4,7 +4,6 @@
 from ..serializers import UserGroupSerializer
 from ..models import UserGroup
 from orgs.mixins.api import OrgBulkModelViewSet
-from common.permissions import IsOrgAdmin
 
 
 __all__ = ['UserGroupViewSet']
@@ -14,7 +13,6 @@ class UserGroupViewSet(OrgBulkModelViewSet):
     model = UserGroup
     filterset_fields = ("name",)
     search_fields = filterset_fields
-    permission_classes = (IsOrgAdmin,)
     serializer_class = UserGroupSerializer
     ordering_fields = ('name', )
     ordering = ('name', )
diff --git a/apps/users/api/mixins.py b/apps/users/api/mixins.py
index 23425aa0e..9b7ab97df 100644
--- a/apps/users/api/mixins.py
+++ b/apps/users/api/mixins.py
@@ -1,15 +1,9 @@
 # -*- coding: utf-8 -*-
 #
-from .. import utils
 from users.models import User
 
-from orgs.utils import current_org
-
 
 class UserQuerysetMixin:
     def get_queryset(self):
-        if self.request.query_params.get('all') or current_org.is_root():
-            queryset = User.objects.exclude(role=User.ROLE.APP)
-        else:
-            queryset = utils.get_current_org_members()
+        queryset = User.get_org_users()
         return queryset
diff --git a/apps/users/api/profile.py b/apps/users/api/profile.py
index f55069f50..3f5605144 100644
--- a/apps/users/api/profile.py
+++ b/apps/users/api/profile.py
@@ -2,32 +2,27 @@
 import uuid
 
 from rest_framework import generics
-from common.permissions import IsOrgAdmin
 from rest_framework.permissions import IsAuthenticated
 
 from users.notifications import (
     ResetPasswordMsg, ResetPasswordSuccessMsg, ResetSSHKeyMsg,
     ResetPublicKeySuccessMsg,
 )
-from common.permissions import (
-    IsCurrentUserOrReadOnly
-)
+
 from .. import serializers
 from ..models import User
 from .mixins import UserQuerysetMixin
 
 __all__ = [
     'UserResetPasswordApi', 'UserResetPKApi',
-    'UserProfileApi', 'UserUpdatePKApi',
-    'UserPasswordApi', 'UserSecretKeyApi',
-    'UserPublicKeyApi'
+    'UserProfileApi', 'UserPasswordApi',
+    'UserSecretKeyApi', 'UserPublicKeyApi'
 ]
 
 
 class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
     queryset = User.objects.all()
     serializer_class = serializers.UserSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def perform_update(self, serializer):
         # Note: we are not updating the user object here.
@@ -40,27 +35,14 @@ class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
 
 class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
     serializer_class = serializers.UserSerializer
-    permission_classes = (IsOrgAdmin,)
 
     def perform_update(self, serializer):
         user = self.get_object()
         user.public_key = None
         user.save()
-
         ResetSSHKeyMsg(user).publish_async()
 
 
-# 废弃
-class UserUpdatePKApi(UserQuerysetMixin, generics.UpdateAPIView):
-    serializer_class = serializers.UserPKUpdateSerializer
-    permission_classes = (IsCurrentUserOrReadOnly,)
-
-    def perform_update(self, serializer):
-        user = self.get_object()
-        user.public_key = serializer.validated_data['public_key']
-        user.save()
-
-
 class UserProfileApi(generics.RetrieveUpdateAPIView):
     permission_classes = (IsAuthenticated,)
     serializer_class = serializers.UserProfileSerializer
diff --git a/apps/users/api/relation.py b/apps/users/api/relation.py
index c3da7816e..40f5566ae 100644
--- a/apps/users/api/relation.py
+++ b/apps/users/api/relation.py
@@ -4,7 +4,6 @@
 from django.db.models import F
 
 from common.drf.api import JMSBulkRelationModelViewSet
-from common.permissions import IsOrgAdmin
 from .. import serializers
 from ..models import User
 
@@ -15,7 +14,6 @@ class UserUserGroupRelationViewSet(JMSBulkRelationModelViewSet):
     filterset_fields = ('user', 'usergroup')
     search_fields = filterset_fields
     serializer_class = serializers.UserUserGroupRelationSerializer
-    permission_classes = (IsOrgAdmin,)
     m2m_field = User.groups.field
 
     def get_queryset(self):
diff --git a/apps/users/api/service_account.py b/apps/users/api/service_account.py
index 783ba9162..26ecb9c1b 100644
--- a/apps/users/api/service_account.py
+++ b/apps/users/api/service_account.py
@@ -3,6 +3,7 @@
 from rest_framework import viewsets
 
 from common.permissions import WithBootstrapToken
+from rbac.models import Role, RoleBinding
 from .. import serializers
 
 
@@ -10,3 +11,8 @@ class ServiceAccountRegistrationViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.ServiceAccountSerializer
     permission_classes = (WithBootstrapToken,)
     http_method_names = ['post']
+
+    def perform_create(self, serializer):
+        app = serializer.save()
+        role = Role.BuiltinRole.system_component.get_role()
+        RoleBinding.objects.create(user=app, role=role)
diff --git a/apps/users/api/user.py b/apps/users/api/user.py
index ad003ad21..092aef223 100644
--- a/apps/users/api/user.py
+++ b/apps/users/api/user.py
@@ -6,24 +6,23 @@ from rest_framework.decorators import action
 from rest_framework import generics
 from rest_framework.response import Response
 from rest_framework_bulk import BulkModelViewSet
-from django.db.models import Prefetch
 
-from common.permissions import (
-    IsOrgAdmin, IsOrgAdminOrAppUser,
-    CanUpdateDeleteUser, IsSuperUser
-)
 from common.mixins import CommonApiMixin
 from common.utils import get_logger
+from common.mixins.api import SuggestionMixin
 from orgs.utils import current_org
-from orgs.models import ROLE as ORG_ROLE, OrganizationMember
+from rbac.models import Role, RoleBinding
 from users.utils import LoginBlockUtil, MFABlockUtils
 from .mixins import UserQuerysetMixin
 from ..notifications import ResetMFAMsg
 from .. import serializers
-from ..serializers import UserSerializer, MiniUserSerializer, InviteSerializer
+from ..serializers import (
+    UserSerializer,
+    MiniUserSerializer, InviteSerializer
+)
 from ..models import User
 from ..signals import post_user_create
-from ..filters import OrgRoleUserFilterBackend, UserFilter
+from ..filters import UserFilter
 
 logger = get_logger(__name__)
 __all__ = [
@@ -32,95 +31,70 @@ __all__ = [
 ]
 
 
-class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
+class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelViewSet):
     filterset_class = UserFilter
     search_fields = ('username', 'email', 'name', 'id', 'source', 'role')
-    permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
     serializer_classes = {
         'default': UserSerializer,
         'suggestion': MiniUserSerializer,
         'invite': InviteSerializer,
     }
-    extra_filter_backends = [OrgRoleUserFilterBackend]
     ordering_fields = ('name',)
-    ordering = ('name', )
+    ordering = ('name',)
+    rbac_perms = {
+        'match': 'users.match_user',
+        'invite': 'users.invite_user',
+        'remove': 'users.remove_user',
+        'bulk_remove': 'users.remove_user',
+    }
 
     def get_queryset(self):
-        queryset = super().get_queryset().prefetch_related(
-            'groups'
-        )
-        if not current_org.is_root():
-            # 为在列表中计算用户在真实组织里的角色
-            queryset = queryset.prefetch_related(
-                Prefetch(
-                    'm2m_org_members',
-                    queryset=OrganizationMember.objects.filter(org__id=current_org.id)
-                )
-            )
+        queryset = super().get_queryset().prefetch_related('groups')
         return queryset
 
-    def send_created_signal(self, users):
-        if not isinstance(users, list):
-            users = [users]
-        for user in users:
-            post_user_create.send(self.__class__, user=user)
+    def paginate_queryset(self, queryset):
+        page = super().paginate_queryset(queryset)
+
+        if page:
+            page = self.set_users_roles_for_cache(page)
+        else:
+            self.set_users_roles_for_cache(queryset)
+        return page
 
     @staticmethod
-    def set_users_to_org(users, org_roles, update=False):
-        # 只有真实存在的组织才真正关联用户
-        if not current_org or current_org.is_root():
-            return
-        for user, roles in zip(users, org_roles):
-            if update and roles is None:
-                continue
-            if not roles:
-                # 当前组织创建的用户,至少是该组织的`User`
-                roles = [ORG_ROLE.USER]
-            OrganizationMember.objects.set_user_roles(current_org, user, roles)
+    def set_users_roles_for_cache(queryset):
+        # Todo: 未来有机会用 SQL 实现
+        queryset_list = queryset
+        user_ids = [u.id for u in queryset_list]
+        role_bindings = RoleBinding.objects.filter(user__in=user_ids) \
+            .values('user_id', 'role_id', 'scope')
+
+        role_mapper = {r.id: r for r in Role.objects.all()}
+        user_org_role_mapper = defaultdict(set)
+        user_system_role_mapper = defaultdict(set)
+
+        for binding in role_bindings:
+            role_id = binding['role_id']
+            user_id = binding['user_id']
+            if binding['scope'] == RoleBinding.Scope.system:
+                user_system_role_mapper[user_id].add(role_mapper[role_id])
+            else:
+                user_org_role_mapper[user_id].add(role_mapper[role_id])
+
+        for u in queryset_list:
+            system_roles = user_system_role_mapper[u.id]
+            org_roles = user_org_role_mapper[u.id]
+            u.org_roles.cache_set(org_roles)
+            u.system_roles.cache_set(system_roles)
+        return queryset_list
 
     def perform_create(self, serializer):
-        org_roles = self.get_serializer_org_roles(serializer)
-        # 创建用户
         users = serializer.save()
         if isinstance(users, User):
             users = [users]
-        self.set_users_to_org(users, org_roles)
         self.send_created_signal(users)
 
-    def get_permissions(self):
-        if self.action in ["retrieve", "list"]:
-            if self.request.query_params.get('all'):
-                self.permission_classes = (IsSuperUser,)
-            else:
-                self.permission_classes = (IsOrgAdminOrAppUser,)
-        elif self.action in ['destroy']:
-            self.permission_classes = (IsSuperUser,)
-        return super().get_permissions()
-
-    def perform_bulk_destroy(self, objects):
-        for obj in objects:
-            self.check_object_permissions(self.request, obj)
-            self.perform_destroy(obj)
-
-    @staticmethod
-    def get_serializer_org_roles(serializer):
-        validated_data = serializer.validated_data
-        # `org_roles` 先 `pop`
-        if isinstance(validated_data, list):
-            org_roles = [item.pop('org_roles', None) for item in validated_data]
-        else:
-            org_roles = [validated_data.pop('org_roles', None)]
-        return org_roles
-
-    def perform_update(self, serializer):
-        org_roles = self.get_serializer_org_roles(serializer)
-        users = serializer.save()
-        if isinstance(users, User):
-            users = [users]
-        self.set_users_to_org(users, org_roles, update=True)
-
     def perform_bulk_update(self, serializer):
-        # TODO: 需要测试
         user_ids = [
             d.get("id") or d.get("pk") for d in serializer.validated_data
         ]
@@ -129,46 +103,35 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
             self.check_object_permissions(self.request, user)
         return super().perform_bulk_update(serializer)
 
-    @action(methods=['get'], detail=False, permission_classes=(IsOrgAdmin,))
-    def suggestion(self, *args, **kwargs):
-        queryset = User.objects.exclude(role=User.ROLE.APP)
-        queryset = self.filter_queryset(queryset)[:6]
-        serializer = self.get_serializer(queryset, many=True)
-        return Response(serializer.data)
+    def perform_bulk_destroy(self, objects):
+        for obj in objects:
+            self.check_object_permissions(self.request, obj)
+            self.perform_destroy(obj)
 
-    @action(methods=['post'], detail=False, permission_classes=(IsOrgAdmin,))
+    @action(methods=['post'], detail=False)
     def invite(self, request):
-        data = request.data
-        if not isinstance(data, list):
-            data = [request.data]
         if not current_org or current_org.is_root():
             error = {"error": "Not a valid org"}
             return Response(error, status=400)
 
         serializer_cls = self.get_serializer_class()
-        serializer = serializer_cls(data=data, many=True)
+        serializer = serializer_cls(data=request.data)
         serializer.is_valid(raise_exception=True)
         validated_data = serializer.validated_data
 
-        users_by_role = defaultdict(list)
-        for i in validated_data:
-            users_by_role[i['role']].append(i['user'])
-
-        OrganizationMember.objects.add_users_by_role(
-            current_org,
-            users=users_by_role[ORG_ROLE.USER],
-            admins=users_by_role[ORG_ROLE.ADMIN],
-            auditors=users_by_role[ORG_ROLE.AUDITOR]
-        )
+        users = validated_data['users']
+        org_roles = validated_data['org_roles']
+        for user in users:
+            user.org_roles.set(org_roles)
         return Response(serializer.data, status=201)
 
-    @action(methods=['post'], detail=True, permission_classes=(IsOrgAdmin,))
+    @action(methods=['post'], detail=True)
     def remove(self, request, *args, **kwargs):
         instance = self.get_object()
         instance.remove()
         return Response(status=204)
 
-    @action(methods=['post'], detail=False, permission_classes=(IsOrgAdmin,), url_path='remove')
+    @action(methods=['post'], detail=False, url_path='remove')
     def bulk_remove(self, request, *args, **kwargs):
         qs = self.get_queryset()
         filtered = self.filter_queryset(qs)
@@ -177,9 +140,14 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
             instance.remove()
         return Response(status=204)
 
+    def send_created_signal(self, users):
+        if not isinstance(users, list):
+            users = [users]
+        for user in users:
+            post_user_create.send(self.__class__, user=user)
+
 
 class UserChangePasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.ChangeUserPasswordSerializer
 
     def perform_update(self, serializer):
@@ -189,7 +157,6 @@ class UserChangePasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
 
 
 class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.UserSerializer
 
     def perform_update(self, serializer):
@@ -200,7 +167,6 @@ class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView):
 
 
 class UserResetMFAApi(UserQuerysetMixin, generics.RetrieveAPIView):
-    permission_classes = (IsOrgAdmin,)
     serializer_class = serializers.ResetOTPSerializer
 
     def retrieve(self, request, *args, **kwargs):
diff --git a/apps/users/apps.py b/apps/users/apps.py
index a40d54d84..bd569c9fe 100644
--- a/apps/users/apps.py
+++ b/apps/users/apps.py
@@ -1,12 +1,14 @@
 from __future__ import unicode_literals
 
+from django.utils.translation import ugettext_lazy as _
 from django.apps import AppConfig
 
 
 class UsersConfig(AppConfig):
     name = 'users'
+    verbose_name = _('Users')
 
     def ready(self):
-        from . import signals_handler
+        from . import signal_handlers
         from . import notifications
         super().ready()
diff --git a/apps/users/filters.py b/apps/users/filters.py
index 5b3d86759..5178a7c55 100644
--- a/apps/users/filters.py
+++ b/apps/users/filters.py
@@ -1,63 +1,37 @@
 from django_filters import rest_framework as filters
-from django.db.models import Q
-from rest_framework.compat import coreapi, coreschema
-from rest_framework.filters import BaseFilterBackend
 
 from common.drf.filters import BaseFilterSet
 from users.models.user import User
-from users.const import SystemOrOrgRole
-from orgs.utils import current_org
-
-
-class OrgRoleUserFilterBackend(BaseFilterBackend):
-    def filter_queryset(self, request, queryset, view):
-        org_role = request.query_params.get('org_role')
-        if not org_role:
-            return queryset
-
-        if org_role == 'admins':
-            return queryset & (current_org.admins | User.objects.filter(role=User.ROLE.ADMIN))
-        elif org_role == 'auditors':
-            return queryset & current_org.auditors
-        elif org_role == 'users':
-            return queryset & current_org.users
-        elif org_role == 'members':
-            return queryset & current_org.get_members()
-
-    def get_schema_fields(self, view):
-        return [
-            coreapi.Field(
-                name='org_role', location='query', required=False, type='string',
-                schema=coreschema.String(
-                    title='Organization role users',
-                    description='Organization role users can be {admins|auditors|users|members}'
-                )
-            )
-        ]
+from rbac.models import Role
 
 
 class UserFilter(BaseFilterSet):
-    system_or_org_role = filters.ChoiceFilter(choices=SystemOrOrgRole.choices, method='filter_system_or_org_role')
+    system_roles = filters.ModelChoiceFilter(
+        queryset=Role.objects.filter(scope='system'), method='filter_system_roles'
+    )
+    org_roles = filters.ModelChoiceFilter(
+        queryset=Role.objects.filter(scope='org'), method='filter_org_roles'
+    )
 
     class Meta:
         model = User
         fields = (
-            'id', 'username', 'email', 'name', 'source', 'system_or_org_role'
+            'id', 'username', 'email', 'name', 'source',
+            'org_roles', 'system_roles',
         )
 
-    def filter_system_or_org_role(self, queryset, name, value):
-        value = value.split('_')
-        if len(value) == 1:
-            role_type, value = None, value[0]
-        else:
-            role_type, value = value
-        value = value.title()
-        system_queries = Q(role=value)
-        org_queries = Q(m2m_org_members__role=value, m2m_org_members__org_id=current_org.id)
-        if not role_type:
-            queries = system_queries | org_queries
-        elif role_type == 'system':
-            queries = system_queries
-        elif role_type == 'org':
-            queries = org_queries
-        return queryset.filter(queries)
+    @staticmethod
+    def filter_system_roles(queryset, name, value):
+        queryset = queryset.prefetch_related('role_bindings')\
+            .filter(role_bindings__role_id=value.id)\
+            .distinct()
+        return queryset
+
+    @staticmethod
+    def filter_org_roles(queryset, name, value):
+        queryset = queryset.prefetch_related('role_bindings') \
+            .filter(role_bindings__role_id=value.id) \
+            .distinct()
+        return queryset
+
+
diff --git a/apps/users/migrations/0039_auto_20211229_1852.py b/apps/users/migrations/0039_auto_20211229_1852.py
new file mode 100644
index 000000000..f83b76144
--- /dev/null
+++ b/apps/users/migrations/0039_auto_20211229_1852.py
@@ -0,0 +1,42 @@
+# Generated by Django 3.1.13 on 2021-12-29 10:52
+
+from django.db import migrations, models
+
+
+def migrate_app_users(apps, schema_editor):
+    user_model = apps.get_model('users', 'User')
+    app_users = user_model.objects.filter(role='App')
+    app_users.update(is_service_account=True)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('users', '0038_auto_20211209_1140'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='user',
+            name='is_service_account',
+            field=models.BooleanField(default=False, verbose_name='Is service account'),
+        ),
+        migrations.RunPython(migrate_app_users),
+        migrations.AlterModelOptions(
+            name='user',
+            options={'ordering': ['username'], 'permissions': [('invite', 'Can invite user'), ('remove', 'Can remove user')], 'verbose_name': 'User'},
+        ),
+        migrations.AlterModelOptions(
+            name='userpasswordhistory',
+            options={'verbose_name': 'User password history'},
+        ),
+        migrations.AlterField(
+            model_name='user',
+            name='role',
+            field=models.CharField(blank=True, default='User', max_length=10, verbose_name='Role'),
+        ),
+        migrations.AlterModelOptions(
+            name='user',
+            options={'ordering': ['username'], 'permissions': [('invite_user', 'Can invite user'), ('remove_user', 'Can remove user'), ('match_user', 'Can match user')], 'verbose_name': 'User'},
+        ),
+    ]
diff --git a/apps/users/models/user.py b/apps/users/models/user.py
index d0158f19f..5ff82b803 100644
--- a/apps/users/models/user.py
+++ b/apps/users/models/user.py
@@ -6,6 +6,7 @@ import base64
 import string
 import random
 import datetime
+from typing import Callable
 
 from django.conf import settings
 from django.contrib.auth.models import AbstractUser
@@ -17,12 +18,11 @@ from django.utils import timezone
 from django.shortcuts import reverse
 
 from orgs.utils import current_org
-from orgs.models import OrganizationMember, Organization
+from orgs.models import Organization
 from common.utils import date_expired_default, get_logger, lazyproperty, random_string
 from common import fields
-from common.const import choices
-from common.db.models import TextChoices
-from ..signals import post_user_change_password
+from django.db.models import TextChoices
+from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
 
 __all__ = ['User', 'UserPasswordHistory']
 
@@ -35,6 +35,9 @@ class AuthMixin:
     need_update_password: bool
     public_key: str
     is_local: bool
+    set_password: Callable
+    save: Callable
+    history_passwords: models.Manager
 
     @property
     def password_raw(self):
@@ -162,210 +165,218 @@ class AuthMixin:
             return False
 
 
-class RoleMixin:
-    class ROLE(TextChoices):
-        ADMIN = choices.ADMIN, _('System administrator')
-        AUDITOR = choices.AUDITOR, _('System auditor')
-        USER = choices.USER, _('User')
-        APP = 'App', _('Application')
+class RoleManager(models.Manager):
+    scope = None
+    _cache = None
 
-    role = ROLE.USER
+    def __init__(self, user, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.user = user
 
     @property
-    def role_display(self):
-        return self.get_role_display()
+    def display(self):
+        roles = sorted(list(self.all()), key=lambda r: r.scope)
+        roles_display = [role.display_name for role in roles]
+        return ', '.join(roles_display)
+
+    @property
+    def role_bindings(self):
+        from rbac.models import RoleBinding
+        queryset = RoleBinding.objects.filter(user=self.user)
+        if self.scope:
+            queryset = queryset.filter(scope=self.scope)
+        return queryset
+
+    def _get_queryset(self):
+        from rbac.models import RoleBinding
+        queryset = RoleBinding.get_user_roles(self.user)
+        if self.scope:
+            queryset = queryset.filter(scope=self.scope)
+        return queryset
+
+    def get_queryset(self):
+        if self._cache is not None:
+            return self._cache
+        return self._get_queryset()
+
+    def clear(self):
+        if not self.scope:
+            return
+        return self.role_bindings.delete()
+
+    def add(self, *roles):
+        from rbac.models import RoleBinding
+        items = []
+
+        for role in roles:
+            kwargs = {
+                'role': role,
+                'user': self.user,
+                'scope': role.scope
+            }
+            if self.scope and role.scope != self.scope:
+                continue
+            if not current_org.is_root() and role.scope == RoleBinding.Scope.org:
+                kwargs['org_id'] = current_org.id
+            items.append(RoleBinding(**kwargs))
+
+        try:
+            RoleBinding.objects.bulk_create(items, ignore_conflicts=True)
+        except Exception as e:
+            logger.error('Create role binding error: {}'.format(e))
+
+    def set(self, roles):
+        self.clear()
+        self.add(*roles)
+
+    def cache_set(self, roles):
+        query = self._get_queryset()
+        query._result_cache = roles
+        self._cache = query
+
+
+class OrgRoleManager(RoleManager):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        from rbac.const import Scope
+        self.scope = Scope.org
+
+
+class SystemRoleManager(RoleManager):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        from rbac.const import Scope
+        self.scope = Scope.system
+
+
+class RoleMixin:
+    objects: models.Manager
+    is_authenticated: bool
+    is_valid: bool
+    id: str
+    _org_roles = None
+    _system_roles = None
+    PERM_CACHE_KEY = 'USER_PERMS_{}_{}'
+
+    @lazyproperty
+    def roles(self):
+        return RoleManager(self)
 
     @lazyproperty
     def org_roles(self):
-        from orgs.models import ROLE as ORG_ROLE
-
-        if current_org.is_root():
-            # root 组织, 取 User 本身的角色
-            if self.is_superuser:
-                roles = [ORG_ROLE.ADMIN]
-            elif self.is_super_auditor:
-                roles = [ORG_ROLE.AUDITOR]
-            else:
-                roles = [ORG_ROLE.USER]
-        else:
-            # 是真实组织, 取 OrganizationMember 中的角色
-            roles = [
-                getattr(ORG_ROLE, org_member.role.upper())
-                for org_member in self.m2m_org_members.all()
-                if org_member.org_id == current_org.id
-            ]
-            roles.sort()
-        return roles
+        return OrgRoleManager(self)
 
     @lazyproperty
-    def org_roles_label_list(self):
-        from orgs.models import ROLE as ORG_ROLE
-        return [str(role.label) for role in self.org_roles if role in ORG_ROLE]
+    def system_roles(self):
+        return SystemRoleManager(self)
 
     @lazyproperty
-    def org_roles_value_list(self):
-        from orgs.models import ROLE as ORG_ROLE
-        return [str(role.value) for role in self.org_roles if role in ORG_ROLE]
+    def perms(self):
+        key = self.PERM_CACHE_KEY.format(self.id, current_org.id)
+        perms = cache.get(key)
+        if not perms or settings.DEBUG:
+            perms = self.get_all_permissions()
+            cache.set(key, perms, 3600)
+        return perms
+
+    def expire_perms_cache(self):
+        key = self.PERM_CACHE_KEY.format(self.id, '*')
+        cache.delete_pattern(key)
 
     @lazyproperty
-    def org_role_display(self):
-        return ' | '.join(self.org_roles_label_list)
-
-    @lazyproperty
-    def total_role_display(self):
-        roles = list({self.role_display, *self.org_roles_label_list})
-        roles.sort()
-        return ' | '.join(roles)
-
-    def current_org_roles(self):
-        from orgs.models import OrganizationMember, ROLE as ORG_ROLE
-        if current_org.is_root():
-            if self.is_superuser:
-                return [ORG_ROLE.ADMIN]
-            else:
-                return [ORG_ROLE.USER]
-
-        roles = list(set(OrganizationMember.objects.filter(
-            org_id=current_org.id, user=self
-        ).values_list('role', flat=True)))
-
-        return roles
-
-    @property
     def is_superuser(self):
-        if self.role == self.ROLE.ADMIN:
-            return True
-        else:
-            return False
-
-    @is_superuser.setter
-    def is_superuser(self, value):
-        if value is True:
-            self.role = self.ROLE.ADMIN
-        else:
-            self.role = self.ROLE.USER
-
-    @property
-    def is_super_auditor(self):
-        return self.role == self.ROLE.AUDITOR
-
-    @property
-    def is_common_user(self):
-        if self.is_org_admin:
-            return False
-        if self.is_org_auditor:
-            return False
-        if self.is_app:
-            return False
-        return True
-
-    @property
-    def is_app(self):
-        return self.role == self.ROLE.APP
-
-    @lazyproperty
-    def user_all_orgs(self):
-        from orgs.models import Organization
-        return Organization.get_user_all_orgs(self)
-
-    @lazyproperty
-    def user_orgs(self):
-        from orgs.models import Organization
-        return Organization.get_user_user_orgs(self)
-
-    @lazyproperty
-    def admin_orgs(self):
-        from orgs.models import Organization
-        return Organization.get_user_admin_orgs(self)
-
-    @lazyproperty
-    def audit_orgs(self):
-        from orgs.models import Organization
-        return Organization.get_user_audit_orgs(self)
-
-    @lazyproperty
-    def admin_or_audit_orgs(self):
-        from orgs.models import Organization
-        return Organization.get_user_admin_or_audit_orgs(self)
+        """
+        由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
+        """
+        from rbac.builtin import BuiltinRole
+        # return self.system_roles.all().filter(id=BuiltinRole.system_admin.id).exists()
+        ids = [str(r.id) for r in self.system_roles.all()]
+        yes = BuiltinRole.system_admin.id in ids
+        return yes
 
     @lazyproperty
     def is_org_admin(self):
-        from orgs.models import ROLE as ORG_ROLE
-        if self.is_superuser or self.m2m_org_members.filter(role=ORG_ROLE.ADMIN).exists():
+        from rbac.builtin import BuiltinRole
+        if self.is_superuser:
             return True
-        else:
-            return False
-
-    @lazyproperty
-    def is_org_auditor(self):
-        from orgs.models import ROLE as ORG_ROLE
-        if self.is_super_auditor or self.m2m_org_members.filter(role=ORG_ROLE.AUDITOR).exists():
-            return True
-        else:
-            return False
-
-    @lazyproperty
-    def can_admin_current_org(self):
-        return current_org.can_admin_by(self)
-
-    @lazyproperty
-    def can_audit_current_org(self):
-        return current_org.can_audit_by(self)
-
-    @lazyproperty
-    def can_user_current_org(self):
-        return current_org.can_use_by(self)
-
-    @lazyproperty
-    def can_admin_or_audit_current_org(self):
-        return self.can_admin_current_org or self.can_audit_current_org
+        ids = [str(r.id) for r in self.org_roles.all()]
+        yes = BuiltinRole.org_admin.id in ids
+        return yes
 
     @property
     def is_staff(self):
-        if self.is_authenticated and self.is_valid:
-            return True
-        else:
-            return False
+        return self.is_authenticated and self.is_valid
 
     @is_staff.setter
     def is_staff(self, value):
         pass
 
     @classmethod
-    def create_app_user(cls, name, comment):
+    def create_service_account(cls, name, comment):
         app = cls.objects.create(
             username=name, name=name, email='{}@local.domain'.format(name),
-            is_active=False, role=cls.ROLE.APP, comment=comment,
-            is_first_login=False, created_by='System'
+            comment=comment, is_first_login=False,
+            created_by='System', is_service_account=True,
         )
         access_key = app.create_access_key()
         return app, access_key
 
+    def set_component_role(self):
+        from rbac.models import Role
+        role = Role.BuiltinRole.system_component.get_role()
+        self.system_roles.add(role)
+
     def remove(self):
         if current_org.is_root():
             return
-        org = Organization.get_instance(current_org.id)
-        OrganizationMember.objects.remove_users(org, [self])
+        kwargs = dict(sender=self.__class__, user=self, org=current_org)
+        pre_user_leave_org.send(**kwargs)
+        self.org_roles.clear()
+        post_user_leave_org.send(**kwargs)
 
     @classmethod
     def get_super_admins(cls):
-        return cls.objects.filter(role=cls.ROLE.ADMIN)
+        from rbac.models import Role, RoleBinding
+        system_admin = Role.BuiltinRole.system_admin.get_role()
+        return RoleBinding.get_role_users(system_admin)
 
     @classmethod
-    def get_org_admins(cls, org=None):
-        from orgs.models import Organization
-        if not isinstance(org, Organization):
-            org = current_org
-        org_admins = org.admins
-        return org_admins
+    def get_org_admins(cls):
+        from rbac.models import Role, RoleBinding
+        org_admin = Role.BuiltinRole.org_admin.get_role()
+        return RoleBinding.get_role_users(org_admin)
 
     @classmethod
-    def get_super_and_org_admins(cls, org=None):
+    def get_super_and_org_admins(cls):
         super_admins = cls.get_super_admins()
-        org_admins = cls.get_org_admins(org=org)
+        org_admins = cls.get_org_admins()
         admins = org_admins | super_admins
         return admins.distinct()
 
+    @staticmethod
+    def filter_not_service_account(queryset):
+        return queryset.filter(is_service_account=False)
+
+    @classmethod
+    def get_nature_users(cls):
+        queryset = cls.objects.all()
+        return cls.filter_not_service_account(queryset)
+
+    @classmethod
+    def get_org_users(cls, org=None):
+        queryset = cls.objects.all()
+        if org is None:
+            org = current_org
+        if not org.is_root():
+            queryset = current_org.get_members()
+        queryset = cls.filter_not_service_account(queryset)
+        return queryset
+
+    def get_all_permissions(self):
+        from rbac.models import RoleBinding
+        perms = RoleBinding.get_user_perms(self)
+        return perms
+
 
 class TokenMixin:
     CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
@@ -461,6 +472,7 @@ class MFAMixin:
     )
     is_org_admin: bool
     username: str
+    phone: str
 
     @property
     def mfa_enabled(self):
@@ -532,14 +544,27 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
 
     SOURCE_BACKEND_MAPPING = {
         Source.local: [
-            settings.AUTH_BACKEND_MODEL, settings.AUTH_BACKEND_PUBKEY,
-            settings.AUTH_BACKEND_WECOM, settings.AUTH_BACKEND_DINGTALK,
+            settings.AUTH_BACKEND_MODEL,
+            settings.AUTH_BACKEND_PUBKEY,
+            settings.AUTH_BACKEND_WECOM,
+            settings.AUTH_BACKEND_DINGTALK,
+        ],
+        Source.ldap: [
+            settings.AUTH_BACKEND_LDAP
+        ],
+        Source.openid: [
+            settings.AUTH_BACKEND_OIDC_PASSWORD,
+            settings.AUTH_BACKEND_OIDC_CODE
+        ],
+        Source.radius: [
+            settings.AUTH_BACKEND_RADIUS
+        ],
+        Source.cas: [
+            settings.AUTH_BACKEND_CAS
+        ],
+        Source.saml2: [
+            settings.AUTH_BACKEND_SAML2
         ],
-        Source.ldap: [settings.AUTH_BACKEND_LDAP],
-        Source.openid: [settings.AUTH_BACKEND_OIDC_PASSWORD, settings.AUTH_BACKEND_OIDC_CODE],
-        Source.radius: [settings.AUTH_BACKEND_RADIUS],
-        Source.cas: [settings.AUTH_BACKEND_CAS],
-        Source.saml2: [settings.AUTH_BACKEND_SAML2],
     }
 
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
@@ -555,9 +580,10 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
         blank=True, verbose_name=_('User group')
     )
     role = models.CharField(
-        choices=RoleMixin.ROLE.choices, default='User', max_length=10,
+        default='User', max_length=10,
         blank=True, verbose_name=_('Role')
     )
+    is_service_account = models.BooleanField(default=False, verbose_name=_("Is service account"))
     avatar = models.ImageField(
         upload_to="avatar", null=True, verbose_name=_('Avatar')
     )
@@ -613,7 +639,8 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
 
     @classmethod
     def get_group_ids_by_user_id(cls, user_id):
-        group_ids = cls.groups.through.objects.filter(user_id=user_id).distinct().values_list('usergroup_id', flat=True)
+        group_ids = cls.groups.through.objects.filter(user_id=user_id)\
+            .distinct().values_list('usergroup_id', flat=True)
         group_ids = list(group_ids)
         return group_ids
 
@@ -691,7 +718,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
         if self.username == 'admin':
             self.role = 'Admin'
             self.is_active = True
-        super().save(*args, **kwargs)
+        return super().save(*args, **kwargs)
 
     def is_member_of(self, user_group):
         if user_group in self.groups.all():
@@ -728,7 +755,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
             return True
         if MFABlockUtils.is_user_block(self.username):
             return True
-
         return False
 
     def delete(self, using=None, keep_parents=False):
@@ -737,23 +763,39 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
         return super(User, self).delete()
 
     @classmethod
-    def get_user_allowed_auth_backends(cls, username):
+    def get_user_allowed_auth_backend_paths(cls, username):
         if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE or not username:
-            # return settings.AUTHENTICATION_BACKENDS
             return None
         user = cls.objects.filter(username=username).first()
         if not user:
             return None
-        return user.get_allowed_auth_backends()
+        return user.get_allowed_auth_backend_paths()
 
-    def get_allowed_auth_backends(self):
+    def get_allowed_auth_backend_paths(self):
         if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE:
             return None
         return self.SOURCE_BACKEND_MAPPING.get(self.source, [])
 
+    @property
+    def all_orgs(self):
+        from rbac.builtin import BuiltinRole
+        has_system_role = self.system_roles.all()\
+            .exclude(name=BuiltinRole.system_user.name)\
+            .exists()
+        if has_system_role:
+            orgs = [Organization.root()] + list(Organization.objects.all())
+        else:
+            orgs = list(self.orgs.all().distinct())
+        return orgs
+
     class Meta:
         ordering = ['username']
         verbose_name = _("User")
+        permissions = [
+            ('invite_user', _('Can invite user')),
+            ('remove_user', _('Can remove user')),
+            ('match_user', _('Can match user')),
+        ]
 
     #: Use this method initial user
     @classmethod
@@ -787,3 +829,6 @@ class UserPasswordHistory(models.Model):
 
     def __repr__(self):
         return self.__str__()
+
+    class Meta:
+        verbose_name = _("User password history")
diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py
index 70e836b91..3434260e6 100644
--- a/apps/users/serializers/profile.py
+++ b/apps/users/serializers/profile.py
@@ -107,33 +107,32 @@ class UserRoleSerializer(serializers.Serializer):
 
 
 class UserProfileSerializer(UserSerializer):
-    admin_or_audit_orgs = UserOrgSerializer(many=True, read_only=True)
-    user_all_orgs = UserOrgSerializer(many=True, read_only=True)
-    current_org_roles = serializers.ListField(read_only=True)
+    MFA_LEVEL_CHOICES = (
+        (0, _('Disable')),
+        (1, _('Enable')),
+    )
+
     public_key_comment = serializers.CharField(
         source='get_public_key_comment', required=False, read_only=True, max_length=128
     )
     public_key_hash_md5 = serializers.CharField(
         source='get_public_key_hash_md5', required=False, read_only=True, max_length=128
     )
-    MFA_LEVEL_CHOICES = (
-        (0, _('Disable')),
-        (1, _('Enable')),
-    )
     mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False)
     guide_url = serializers.SerializerMethodField()
     receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True)
+    orgs = UserOrgSerializer(many=True, read_only=True, source='all_orgs')
+    perms = serializers.ListField(label=_("Perms"), read_only=True)
 
     class Meta(UserSerializer.Meta):
-        fields = UserSerializer.Meta.fields + [
-            'public_key_comment', 'public_key_hash_md5',
-            'admin_or_audit_orgs', 'current_org_roles',
-            'guide_url', 'user_all_orgs', 'is_org_admin',
-            'is_superuser', 'receive_backends',
-        ]
         read_only_fields = [
-            'date_joined', 'last_login', 'created_by', 'source', 'receive_backends',
+            'date_joined', 'last_login', 'created_by', 'source',
+            'receive_backends', 'orgs', 'perms',
         ]
+        fields = UserSerializer.Meta.fields + [
+            'public_key_comment', 'public_key_hash_md5', 'guide_url',
+        ] + read_only_fields
+
         extra_kwargs = dict(UserSerializer.Meta.extra_kwargs)
         extra_kwargs.update({
             'name': {'read_only': True, 'max_length': 128},
@@ -144,18 +143,23 @@ class UserProfileSerializer(UserSerializer):
             'is_valid': {'read_only': True},
             'is_active': {'read_only': True},
             'groups': {'read_only': True},
-            'roles': {'read_only': True},
             'password_strategy': {'read_only': True},
             'date_expired': {'read_only': True},
             'date_joined': {'read_only': True},
             'last_login': {'read_only': True},
-            'role': {'read_only': True},
         })
 
         if 'password' in fields:
             fields.remove('password')
             extra_kwargs.pop('password', None)
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        system_roles_field = self.fields.get('system_roles')
+        org_roles_field = self.fields.get('org_roles')
+        system_roles_field.read_only = True
+        org_roles_field.read_only = True
+
     @staticmethod
     def get_guide_url(obj):
         return settings.USER_GUIDE_URL
@@ -172,6 +176,20 @@ class UserProfileSerializer(UserSerializer):
             return public_key
         return None
 
+    def validate_password(self, password):
+        from rbac.models import Role
+        from ..utils import check_password_rules
+        if not self.instance:
+            return password
+
+        is_org_admin = self.instance.org_roles.filter(
+            name=Role.BuiltinRole.org_admin.name
+        ).exsits()
+        if not check_password_rules(password, is_org_admin=is_org_admin):
+            msg = _('Password does not match security rules')
+            raise serializers.ValidationError(msg)
+        return password
+
 
 class UserPKUpdateSerializer(serializers.ModelSerializer):
     class Meta:
diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py
index 021437edc..21d67961b 100644
--- a/apps/users/serializers/user.py
+++ b/apps/users/serializers/user.py
@@ -1,14 +1,18 @@
 # -*- coding: utf-8 -*-
 #
+from functools import partial
 from django.utils.translation import ugettext_lazy as _
 from rest_framework import serializers
 
 from common.mixins import CommonBulkSerializerMixin
-from common.permissions import CanUpdateDeleteUser
 from common.validators import PhoneValidator
-from orgs.models import ROLE as ORG_ROLE
+from rbac.models import Role
+from rbac.builtin import BuiltinRole
+from rbac.permissions import RBACPermission
+from rbac.models import OrgRoleBinding, SystemRoleBinding
 from ..models import User
-from ..const import SystemOrOrgRole, PasswordStrategy
+from ..const import PasswordStrategy
+from rbac.models import Role
 
 __all__ = [
     'UserSerializer', 'MiniUserSerializer',
@@ -16,25 +20,79 @@ __all__ = [
 ]
 
 
-class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
+class RolesSerializerMixin(serializers.Serializer):
+    system_roles = serializers.ManyRelatedField(
+        child_relation=serializers.PrimaryKeyRelatedField(queryset=Role.system_roles),
+        label=_('System roles'),
+    )
+    org_roles = serializers.ManyRelatedField(
+        required=False,
+        child_relation=serializers.PrimaryKeyRelatedField(queryset=Role.org_roles),
+        label=_('Org roles'),
+    )
+    system_roles_display = serializers.SerializerMethodField(label=_('System roles'))
+    org_roles_display = serializers.SerializerMethodField(label=_('Org roles'))
+
+    @staticmethod
+    def get_system_roles_display(user):
+        return user.system_roles.display
+
+    @staticmethod
+    def get_org_roles_display(user):
+        return user.org_roles.display
+
+    def pop_roles_if_need(self, fields):
+        request = self.context.get('request')
+        view = self.context.get('view')
+
+        if not all([request, view, hasattr(view, 'action')]):
+            return fields
+        if request.user.is_anonymous:
+            return fields
+
+        action = view.action or 'list'
+        model_cls_field_mapper = {
+            SystemRoleBinding: ['system_roles', 'system_roles_display'],
+            OrgRoleBinding: ['org_roles', 'system_roles_display']
+        }
+
+        for model_cls, fields_names in model_cls_field_mapper.items():
+            perms = RBACPermission.parse_action_model_perms(action, model_cls)
+            if request.user.has_perms(perms):
+                continue
+            # 没有权限就去掉
+            for field_name in fields_names:
+                fields.pop(field_name, None)
+        return fields
+
+    def get_fields(self):
+        fields = super().get_fields()
+        self.pop_roles_if_need(fields)
+        return fields
+
+
+class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializers.ModelSerializer):
     password_strategy = serializers.ChoiceField(
         choices=PasswordStrategy.choices, default=PasswordStrategy.email, required=False,
         write_only=True, label=_('Password strategy')
     )
     mfa_enabled = serializers.BooleanField(read_only=True, label=_('MFA enabled'))
     mfa_force_enabled = serializers.BooleanField(read_only=True, label=_('MFA force enabled'))
-    mfa_level_display = serializers.ReadOnlyField(source='get_mfa_level_display', label=_('MFA level display'))
+    mfa_level_display = serializers.ReadOnlyField(
+        source='get_mfa_level_display', label=_('MFA level display')
+    )
     login_blocked = serializers.BooleanField(read_only=True, label=_('Login blocked'))
     is_expired = serializers.BooleanField(read_only=True, label=_('Is expired'))
-    can_update = serializers.SerializerMethodField(label=_('Can update'))
-    can_delete = serializers.SerializerMethodField(label=_('Can delete'))
     can_public_key_auth = serializers.ReadOnlyField(
-        source='can_use_ssh_key_login', label=_('Can public key authentication'))
-    org_roles = serializers.ListField(
-        label=_('Organization role name'), allow_null=True, required=False,
-        child=serializers.ChoiceField(choices=ORG_ROLE.choices), default=["User"]
+        source='can_use_ssh_key_login', label=_('Can public key authentication')
     )
-    system_or_org_role = serializers.ChoiceField(read_only=True, choices=SystemOrOrgRole.choices, label=_('Role'))
+    # Todo: 这里看看该怎么搞
+    # can_update = serializers.SerializerMethodField(label=_('Can update'))
+    # can_delete = serializers.SerializerMethodField(label=_('Can delete'))
+    custom_m2m_fields = {
+        'system_roles': [BuiltinRole.system_user],
+        'org_roles': [BuiltinRole.org_user]
+    }
 
     class Meta:
         model = User
@@ -46,9 +104,9 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
         ]
         # small 指的是 不需要计算的直接能从一张表中获取到的数据
         fields_small = fields_mini + fields_write_only + [
-            'email', 'wechat', 'phone', 'mfa_level',
-            'source', 'source_display', 'can_public_key_auth', 'need_update_password',
-            'mfa_enabled', 'is_valid', 'is_expired', 'is_active',  # 布尔字段
+            'email', 'wechat', 'phone', 'mfa_level', 'source', 'source_display',
+            'can_public_key_auth', 'need_update_password',
+            'mfa_enabled', 'is_service_account', 'is_valid', 'is_expired', 'is_active',  # 布尔字段
             'date_expired', 'date_joined', 'last_login',  # 日期字段
             'created_by', 'comment',  # 通用字段
             'is_wecom_bound', 'is_dingtalk_bound', 'is_feishu_bound', 'is_otp_secret_key_bound',
@@ -56,16 +114,18 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
         ]
         # 包含不太常用的字段,可以没有
         fields_verbose = fields_small + [
-            'total_role_display', 'org_role_display',
             'mfa_level_display', 'mfa_force_enabled', 'is_first_login',
-            'date_password_last_updated', 'avatar_url', 'system_or_org_role'
+            'date_password_last_updated', 'avatar_url',
         ]
         # 外键的字段
-        fields_fk = ['role', 'role_display']
+        fields_fk = []
         # 多对多字段
-        fields_m2m = ['groups', 'groups_display', 'org_roles']
+        fields_m2m = [
+            'groups', 'groups_display', 'system_roles', 'org_roles',
+            'system_roles_display', 'org_roles_display'
+        ]
         # 在serializer 上定义的字段
-        fields_custom = ['can_update', 'can_delete', 'login_blocked', 'password_strategy']
+        fields_custom = ['login_blocked', 'password_strategy']
         fields = fields_verbose + fields_fk + fields_m2m + fields_custom
 
         read_only_fields = [
@@ -77,6 +137,7 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
             'public_key': {'write_only': True},
             'is_first_login': {'label': _('Is first login'), 'read_only': True},
             'is_valid': {'label': _('Is valid')},
+            'is_service_account': {'label': _('Is service account')},
             'is_expired': {'label': _('Is expired')},
             'avatar_url': {'label': _('Avatar url')},
             'created_by': {'read_only': True, 'allow_blank': True},
@@ -91,46 +152,10 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
             'is_feishu_bound': {'label': _('Is feishu bound')},
             'is_otp_secret_key_bound': {'label': _('Is OTP bound')},
             'phone': {'validators': [PhoneValidator()]},
+            'system_role_display': {'label': _('System role name')},
         }
 
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.set_role_choices()
-
-    def set_role_choices(self):
-        role = self.fields.get('role')
-        if not role:
-            return
-        choices = role._choices
-        choices.pop(User.ROLE.APP, None)
-        request = self.context.get('request')
-        if request and hasattr(request, 'user') and not request.user.is_superuser:
-            choices.pop(User.ROLE.ADMIN, None)
-            choices.pop(User.ROLE.AUDITOR, None)
-        role._choices = choices
-
-    def validate_role(self, value):
-        request = self.context.get('request')
-        if not request.user.is_superuser and value != User.ROLE.USER:
-            role_display = User.ROLE.USER.label
-            msg = _("Role limit to {}".format(role_display))
-            raise serializers.ValidationError(msg)
-        return value
-
-    @property
-    def is_org_admin(self):
-        roles = []
-        role = self.initial_data.get('role')
-        if role:
-            roles.append(role)
-        org_roles = self.initial_data.get('org_roles')
-        if org_roles:
-            roles.extend(org_roles)
-        is_org_admin = User.ROLE.ADMIN.value in roles
-        return is_org_admin
-
     def validate_password(self, password):
-        from ..utils import check_password_rules
         password_strategy = self.initial_data.get('password_strategy')
         if self.instance is None and password_strategy != PasswordStrategy.custom:
             # 创建用户,使用邮件设置密码
@@ -138,9 +163,6 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
         if self.instance and not password:
             # 更新用户, 未设置密码
             return
-        if not check_password_rules(password, is_org_admin=self.is_org_admin):
-            msg = _('Password does not match security rules')
-            raise serializers.ValidationError(msg)
         return password
 
     @staticmethod
@@ -164,25 +186,54 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
         attrs.pop('password_strategy', None)
         return attrs
 
-    def get_can_update(self, obj):
-        return CanUpdateDeleteUser.has_update_object_permission(
-            self.context['request'], self.context['view'], obj
-        )
+    def save_and_set_custom_m2m_fields(self, validated_data, save_handler, created):
+        m2m_values = {}
+        for f, default_roles in self.custom_m2m_fields.items():
+            roles = validated_data.pop(f, None)
+            if created and not roles:
+                roles = [
+                    Role.objects.filter(id=role.id).first()
+                    for role in default_roles
+                ]
+            m2m_values[f] = roles
 
-    def get_can_delete(self, obj):
-        return CanUpdateDeleteUser.has_delete_object_permission(
-            self.context['request'], self.context['view'], obj
-        )
+        instance = save_handler(validated_data)
+        for field_name, value in m2m_values.items():
+            if value is None:
+                continue
+            field = getattr(instance, field_name)
+            field.set(value)
+        return instance
+
+    def validate_is_active(self, is_active):
+        request = self.context.get('request')
+        if not request or not request.user.is_authenticated:
+            return is_active
+
+        user = request.user
+        if user.id == self.instance.id and not is_active:
+            # 用户自己不能禁用启用自己
+            raise serializers.ValidationError("Cannot inactive self")
+        return is_active
 
     def update(self, instance, validated_data):
-        request = self.context.get('request')
-        if request:
-            user = request.user
-            if user.id == instance.id:
-                # 用户自己不能禁用启用自己
-                validated_data.pop('is_active', None)
+        save_handler = partial(super().update, instance)
+        instance = self.save_and_set_custom_m2m_fields(validated_data, save_handler, created=False)
+        return instance
 
-        return super(UserSerializer, self).update(instance, validated_data)
+    def create(self, validated_data):
+        save_handler = super().create
+        instance = self.save_and_set_custom_m2m_fields(validated_data, save_handler, created=True)
+        return instance
+
+
+class UserRetrieveSerializer(UserSerializer):
+    login_confirm_settings = serializers.PrimaryKeyRelatedField(
+        read_only=True, source='login_confirm_setting.reviewers', many=True
+    )
+
+    class Meta(UserSerializer.Meta):
+        fields = UserSerializer.Meta.fields + ['login_confirm_settings']
 
 
 class MiniUserSerializer(serializers.ModelSerializer):
@@ -191,17 +242,20 @@ class MiniUserSerializer(serializers.ModelSerializer):
         fields = UserSerializer.Meta.fields_mini
 
 
-class InviteSerializer(serializers.Serializer):
-    user = serializers.PrimaryKeyRelatedField(
-        queryset=User.objects.exclude(role=User.ROLE.APP)
+class InviteSerializer(RolesSerializerMixin, serializers.Serializer):
+    users = serializers.PrimaryKeyRelatedField(
+        queryset=User.get_nature_users(), many=True, label=_('Select users'),
+        help_text=_('For security, only list several users')
     )
-    role = serializers.ChoiceField(choices=ORG_ROLE.choices)
+    system_roles = None
+    system_roles_display = None
+    org_roles_display = None
 
 
 class ServiceAccountSerializer(serializers.ModelSerializer):
     class Meta:
         model = User
-        fields = ['id', 'name', 'access_key']
+        fields = ['id', 'name', 'access_key', 'comment']
         read_only_fields = ['access_key']
 
     def __init__(self, *args, **kwargs):
@@ -223,18 +277,12 @@ class ServiceAccountSerializer(serializers.ModelSerializer):
             users = User.objects.exclude(id=self.instance.id)
         else:
             users = User.objects.all()
-        if users.filter(email=email) or \
-                users.filter(username=username):
+        if users.filter(email=email) or users.filter(username=username):
             raise serializers.ValidationError(_('name not unique'), code='unique')
         return name
 
-    def save(self, **kwargs):
-        self.validated_data['email'] = self.get_email()
-        self.validated_data['username'] = self.get_username()
-        self.validated_data['role'] = User.ROLE.APP
-        return super().save(**kwargs)
-
     def create(self, validated_data):
-        instance = super().create(validated_data)
-        instance.create_access_key()
-        return instance
+        name = validated_data['name']
+        comment = validated_data.get('comment', '')
+        user, ak = User.create_service_account(name, comment)
+        return user
diff --git a/apps/users/signals_handler.py b/apps/users/signal_handlers.py
similarity index 91%
rename from apps/users/signals_handler.py
rename to apps/users/signal_handlers.py
index 3e2ceb1f6..c4d84e561 100644
--- a/apps/users/signals_handler.py
+++ b/apps/users/signal_handlers.py
@@ -8,7 +8,7 @@ from django.core.exceptions import PermissionDenied
 from django_cas_ng.signals import cas_user_authenticated
 from django.db.models.signals import post_save
 
-from jms_oidc_rp.signals import openid_create_or_update_user
+from authentication.backends.oidc.signals import openid_create_or_update_user
 
 from authentication.backends.saml2.signals import saml2_create_or_update_user
 from common.utils import get_logger
@@ -43,10 +43,12 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
 
 @receiver(post_save, sender=User)
 def save_passwd_change(sender, instance: User, **kwargs):
-    passwds = UserPasswordHistory.objects.filter(user=instance).order_by('-date_created')\
-                  .values_list('password', flat=True)[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)]
+    passwords = UserPasswordHistory.objects.filter(user=instance) \
+        .order_by('-date_created')\
+        .values_list('password', flat=True)
+    passwords = passwords[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)]
 
-    for p in passwds:
+    for p in passwords:
         if instance.password == p:
             break
     else:
diff --git a/apps/users/signals.py b/apps/users/signals.py
index 37969f839..739379d93 100644
--- a/apps/users/signals.py
+++ b/apps/users/signals.py
@@ -3,3 +3,5 @@ from django.dispatch import Signal
 
 post_user_create = Signal(providing_args=('user',))
 post_user_change_password = Signal(providing_args=('user',))
+pre_user_leave_org = Signal(providing_args=('user', 'org'))
+post_user_leave_org = Signal(providing_args=('user', 'org'))
diff --git a/apps/users/tasks.py b/apps/users/tasks.py
index 58ce4e3ed..a92fc2fb0 100644
--- a/apps/users/tasks.py
+++ b/apps/users/tasks.py
@@ -21,7 +21,7 @@ logger = get_logger(__file__)
 
 @shared_task
 def check_password_expired():
-    users = User.objects.filter(source=User.Source.local.value).exclude(role=User.ROLE.APP)
+    users = User.get_nature_users().filter(source=User.Source.local)
     for user in users:
         if not user.is_valid:
             continue
@@ -49,7 +49,7 @@ def check_password_expired_periodic():
 
 @shared_task
 def check_user_expired():
-    users = User.objects.exclude(role=User.ROLE.APP)
+    users = User.get_nature_users().filter(source=User.Source.local)
     for user in users:
         if not user.is_valid:
             continue
diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py
index 25633ab10..8453ec819 100644
--- a/apps/users/urls/api_urls.py
+++ b/apps/users/urls/api_urls.py
@@ -29,7 +29,6 @@ urlpatterns = [
     path('users/<uuid:pk>/password/', api.UserChangePasswordApi.as_view(), name='change-user-password'),
     path('users/<uuid:pk>/password/reset/', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
     path('users/<uuid:pk>/pubkey/reset/', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
-    path('users/<uuid:pk>/pubkey/update/', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
     path('users/<uuid:pk>/unblock/', api.UserUnblockPKApi.as_view(), name='user-unblock'),
 ]
 urlpatterns += router.urls
diff --git a/apps/users/utils.py b/apps/users/utils.py
index 6f311c74f..3602d67ee 100644
--- a/apps/users/utils.py
+++ b/apps/users/utils.py
@@ -238,9 +238,9 @@ def construct_user_email(username, email):
     return email
 
 
-def get_current_org_members(exclude=()):
+def get_current_org_members():
     from orgs.utils import current_org
-    return current_org.get_members(exclude=exclude)
+    return current_org.get_members()
 
 
 def is_auth_time_valid(session, key):
diff --git a/apps/users/views/profile/__init__.py b/apps/users/views/profile/__init__.py
index b2d0e3491..5abff8d9f 100644
--- a/apps/users/views/profile/__init__.py
+++ b/apps/users/views/profile/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
 from .password import *
-from .pubkey import *
 from .mfa import *
 from .otp import *
 from .reset import *
+from .pubkey import *
diff --git a/apps/users/views/profile/pubkey.py b/apps/users/views/profile/pubkey.py
index e2125f0cc..7408889e1 100644
--- a/apps/users/views/profile/pubkey.py
+++ b/apps/users/views/profile/pubkey.py
@@ -1,46 +1,17 @@
 # ~*~ coding: utf-8 ~*~
 
 from django.http import HttpResponse
-from django.urls import reverse_lazy
-from django.utils.translation import ugettext as _
 from django.views import View
-from django.views.generic.edit import UpdateView
 
 from common.utils import get_logger, ssh_key_gen
-from common.permissions import (
-    IsValidUser,
-    UserCanUpdateSSHKey,
-)
+from common.permissions import IsValidUser
 from common.mixins.views import PermissionsMixin
-from ... import forms
-from ...models import User
 
-__all__ = [
-    'UserPublicKeyUpdateView', 'UserPublicKeyGenerateView',
-]
+__all__ = ['UserPublicKeyGenerateView']
 
 logger = get_logger(__name__)
 
 
-class UserPublicKeyUpdateView(PermissionsMixin, UpdateView):
-    template_name = 'users/user_pubkey_update.html'
-    model = User
-    form_class = forms.UserPublicKeyForm
-    permission_classes = [IsValidUser, UserCanUpdateSSHKey]
-    success_url = reverse_lazy('users:user-profile')
-
-    def get_object(self, queryset=None):
-        return self.request.user
-
-    def get_context_data(self, **kwargs):
-        context = {
-            'app': _('Users'),
-            'action': _('Public key update'),
-        }
-        kwargs.update(context)
-        return super().get_context_data(**kwargs)
-
-
 class UserPublicKeyGenerateView(PermissionsMixin, View):
     permission_classes = [IsValidUser]
 
diff --git a/jms b/jms
index b4eeb7e8a..517fb0602 100755
--- a/jms
+++ b/jms
@@ -63,8 +63,8 @@ def check_database_connection():
             return
         except OperationalError:
             logging.info('Database not setup, retry')
-        except Exception as e:
-            logging.error('Unexpect error occur: {}'.format(str(e)))
+        except Exception as exc:
+            logging.error('Unexpect error occur: {}'.format(str(exc)))
         time.sleep(1)
     logging.error("Connection database failed, exit")
     sys.exit(10)
@@ -96,7 +96,7 @@ def collect_static():
         pass
 
 
-def compile_i81n_file():
+def compile_i18n_file():
     django_mo_file = os.path.join(BASE_DIR, 'apps', 'locale', 'zh', 'LC_MESSAGES', 'django.mo')
     if os.path.exists(django_mo_file):
         return
@@ -134,8 +134,8 @@ def start_services():
     except KeyboardInterrupt:
         logging.info('Cancel ...')
         time.sleep(2)
-    except Exception as e:
-        logging.error("Start service error {}: {}".format(services, e))
+    except Exception as exc:
+        logging.error("Start service error {}: {}".format(services, exc))
         time.sleep(2)
 
 
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 480e19dac..97ee5b2be 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -1,30 +1,30 @@
-amqp==2.5.2
-ansible==2.9.24
+amqp==5.0.9
+ansible==2.10
 asn1crypto==0.24.0
 bcrypt==3.1.4
-billiard==3.6.3.0
+billiard==3.6.4.0
 boto3==1.18.11
 botocore==1.21.11
-celery==4.4.2
+celery==5.2.2
 certifi==2018.1.18
 cffi==1.13.2
 chardet==3.0.4
 configparser==3.5.0
 coreapi==2.3.3
 coreschema==0.0.4
-cryptography==3.3.2
+cryptography==36.0.1
 decorator==4.1.2
-Django==3.1.13
+Django==3.1.14
 django-auth-ldap==2.2.0
 django-bootstrap3==14.2.0
-django-celery-beat==2.0
+django-celery-beat==2.2.1
 django-filter==2.4.0
 django-formtools==2.2
 django-ranged-response==0.2.0
 django-redis-cache==2.1.1
 django-rest-swagger==2.2.0
 django-simple-captcha==0.5.13
-django-timezone-field==4.0
+django-timezone-field==4.1.0
 djangorestframework==3.12.2
 djangorestframework-bulk==0.2.1
 docutils==0.14
@@ -41,7 +41,7 @@ itsdangerous==0.24
 itypes==1.1.0
 Jinja2==2.11.3
 jmespath==0.9.3
-kombu==4.6.8
+kombu==5.2.2
 ldap3==2.4
 MarkupSafe==1.1.1
 mysqlclient==2.0.1
@@ -49,34 +49,33 @@ olefile==0.44
 openapi-codec==1.3.2
 paramiko==2.7.2
 passlib==1.7.1
-Pillow==8.3.2
+Pillow==9.0.0
 pyasn1==0.4.8
 pycparser==2.19
-pycryptodome==3.10.1
-pycryptodomex==3.10.1
+pycryptodome==3.12.0
+pycryptodomex==3.12.0
 pyotp==2.2.6
-PyNaCl==1.2.1
+PyNaCl==1.5.0
 python-dateutil==2.8.2
-#python-gssapi==0.6.4
 pytz==2018.3
 PyYAML==6.0
 redis==3.5.3
 requests==2.25.1
-jms-storage==0.0.41
+jms-storage==0.0.42
 s3transfer==0.5.0
 simplejson==3.13.2
 six==1.11.0
 sshpubkeys==3.1.0
 uritemplate==3.0.0
 urllib3==1.26.5
-vine==1.3.0
+vine==5.0.0
 drf-yasg==1.20.0
 Werkzeug==0.15.3
 drf-nested-routers==0.91
 aliyun-python-sdk-core-v3==2.9.1
 aliyun-python-sdk-ecs==4.10.1
 rest_condition==1.0.3
-python-ldap==3.3.1
+python-ldap==3.4.0
 tencentcloud-sdk-python==3.0.477
 django-radius==1.4.0
 django-redis-sessions==0.6.1
@@ -85,7 +84,7 @@ python-daemon==2.2.3
 httpsig==1.3.0
 treelib==1.5.3
 django-proxy==1.2.1
-flower==0.9.3
+flower==1.0.0
 channels-redis==3.2.0
 channels==2.4.0
 daphne==2.4.1
@@ -129,3 +128,5 @@ kubernetes==21.7.0
 websocket-client==1.2.3
 numpy==1.22.0
 pandas==1.3.5
+pyjwkest==1.4.2
+jsonfield2==4.0.0.post0
diff --git a/utils/clean_db_content_types.py b/utils/clean_db_content_types.py
new file mode 100644
index 000000000..585c72314
--- /dev/null
+++ b/utils/clean_db_content_types.py
@@ -0,0 +1,68 @@
+import os
+import sys
+import django
+
+
+if os.path.exists('../apps'):
+    sys.path.insert(0, '../apps')
+elif os.path.exists('./apps'):
+    sys.path.insert(0, './apps')
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
+django.setup()
+
+from rbac.models import Permission, ContentType
+
+
+def clean_db_content_types():
+    content_type_delete_required = [
+        ('common', 'permission'),
+    ]
+    for app, model in content_type_delete_required:
+        ContentType.objects.filter(app_label=app, model=model).delete()
+
+    permissions_delete_required = [
+        ('perms', 'assetpermission', 'connect_myassets'),
+        ('perms', 'assetpermission', 'view_myassets'),
+        ('perms', 'assetpermission', 'view_userassets'),
+        ('perms', 'assetpermission', 'view_usergroupassets'),
+        ('perms', 'applicationpermission', 'view_myapps'),
+        ('perms', 'applicationpermission', 'connect_myapps'),
+        ('perms', 'applicationpermission', 'view_userapps'),
+        ('perms', 'applicationpermission', 'view_usergroupapps'),
+
+
+        ('perms', 'permeddatabaseapp', 'connect_mydatabaseapp'),
+        ('perms', 'permeddatabaseapp', 'view_mydatabaseapp'),
+        ('perms', 'permedkubernetesapp', 'connect_mykubernetesapp'),
+        ('perms', 'permedkubernetesapp', 'view_mykubernetesapp'),
+        ('perms', 'permedremoteapp', 'connect_myremoteapp'),
+        ('perms', 'permedremoteapp', 'view_myremoteapp'),
+
+        ('applications', 'databaseapp', 'add_databaseapp'),
+        ('applications', 'databaseapp', 'change_databaseapp'),
+        ('applications', 'databaseapp', 'delete_databaseapp'),
+        ('applications', 'databaseapp', 'view_databaseapp'),
+        ('applications', 'kubernetesapp', 'add_kubernetesapp'),
+        ('applications', 'kubernetesapp', 'delete_kubernetesapp'),
+        ('applications', 'kubernetesapp', 'change_kubernetesapp'),
+        ('applications', 'kubernetesapp', 'view_kubernetesapp'),
+        ('applications', 'remoteapp', 'add_remoteapp'),
+        ('applications', 'remoteapp', 'change_remoteapp'),
+        ('applications', 'remoteapp', 'delete_remoteapp'),
+        ('applications', 'remoteapp', 'view_remoteapp'),
+
+        ('settings', 'setting', 'change_terminal_basic_setting'),
+        ('rbac', 'menupermission', 'view_resourcestatistics'),
+
+
+    ]
+    for app, model, codename in permissions_delete_required:
+        print('delete {}.{} ({})'.format(app, codename, model))
+        Permission.objects.filter(
+            codename=codename, content_type__model=model, content_type__app_label=app
+        ).delete()
+
+
+if __name__ == '__main__':
+    clean_db_content_types()
diff --git a/utils/playbooks/change_password/hosts b/utils/playbooks/change_password/hosts
new file mode 100644
index 000000000..497325d73
--- /dev/null
+++ b/utils/playbooks/change_password/hosts
@@ -0,0 +1 @@
+testhost ansible_host=192.168.244.207
diff --git a/utils/playbooks/change_password/main.yml b/utils/playbooks/change_password/main.yml
new file mode 100644
index 000000000..3a981fdb2
--- /dev/null
+++ b/utils/playbooks/change_password/main.yml
@@ -0,0 +1,23 @@
+- hosts: testhost
+  vars:
+    ansible_user: root
+    ansible_ssh_password: Fit2Cloud20202
+    user1: web
+    user1password: Fit2Cloud@12344
+
+  tasks:
+    - name: 监测特权用户密码
+      ping:
+
+    - name: 更改用户密码
+      user:
+        name: "{{ user1 }}"
+        password: "{{ user1password|password_hash('sha512', 'K3mIlKK') }}"
+        update_password: always
+
+    - name: 校验密码是否更改成功
+      vars:
+        - ansible_user: '{{ user1 }}'
+          ansible_ssh_password: '{{ user1password }}'
+      ping:
+
diff --git a/utils/playbooks/change_password/start.sh b/utils/playbooks/change_password/start.sh
new file mode 100755
index 000000000..725cb87d1
--- /dev/null
+++ b/utils/playbooks/change_password/start.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+ansible-playbook -i hosts main.yml
\ No newline at end of file
diff --git a/utils/start_celery_beat.py b/utils/start_celery_beat.py
index 34887fb4d..236a61ba8 100644
--- a/utils/start_celery_beat.py
+++ b/utils/start_celery_beat.py
@@ -22,8 +22,9 @@ redis = Redis(host=CONFIG.REDIS_HOST, port=CONFIG.REDIS_PORT, password=CONFIG.RE
 scheduler = "django_celery_beat.schedulers:DatabaseScheduler"
 
 cmd = [
-    'celery', 'beat',
+    'celery',
     '-A', 'ops',
+    'beat',
     '-l', 'INFO',
     '--scheduler', scheduler,
     '--max-interval', '60'