From 60c17253ab5618e071bc51b0ce9df8053bda9ae3 Mon Sep 17 00:00:00 2001 From: Cesar Canassa Date: Mon, 3 Aug 2015 17:54:18 +0200 Subject: [PATCH 001/100] Add test to check if a 415 status is returned for unsupported media types --- tests/test_response.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_response.py b/tests/test_response.py index 1dd5d5ea08..b53688e5c9 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -5,6 +5,7 @@ from django.utils import six from rest_framework import generics, routers, serializers, status, viewsets +from rest_framework.parsers import JSONParser from rest_framework.renderers import ( BaseRenderer, BrowsableAPIRenderer, JSONRenderer ) @@ -79,6 +80,14 @@ def get(self, request, **kwargs): return Response(DUMMYCONTENT, status=DUMMYSTATUS, content_type='setbyview') +class JSONView(APIView): + parser_classes = (JSONParser,) + + def post(self, request, **kwargs): + assert request.data + return Response(DUMMYCONTENT) + + class HTMLView(APIView): renderer_classes = (BrowsableAPIRenderer, ) @@ -114,6 +123,7 @@ class HTMLNewModelView(generics.ListCreateAPIView): url(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^html$', HTMLView.as_view()), + url(r'^json$', JSONView.as_view()), url(r'^html1$', HTMLView1.as_view()), url(r'^html_new_model$', HTMLNewModelView.as_view()), url(r'^html_new_model_viewset', include(new_model_viewset_router.urls)), @@ -203,6 +213,25 @@ def test_specified_renderer_is_used_on_format_query_with_matching_accept(self): self.assertEqual(resp.status_code, DUMMYSTATUS) +class UnsupportedMediaTypeTests(TestCase): + urls = 'tests.test_response' + + def test_should_allow_posting_json(self): + response = self.client.post('/json', data='{"test": 123}', content_type='application/json') + + self.assertEqual(response.status_code, 200) + + def test_should_not_allow_posting_xml(self): + response = self.client.post('/json', data='123', content_type='application/xml') + + self.assertEqual(response.status_code, 415) + + def test_should_not_allow_posting_a_form(self): + response = self.client.post('/json', data={'test': 123}) + + self.assertEqual(response.status_code, 415) + + class Issue122Tests(TestCase): """ Tests that covers #122. From 48540f180a34345a6278e527cd4e494826f1b8f2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Aug 2015 17:11:53 +0100 Subject: [PATCH 002/100] unittest compat fallback --- rest_framework/compat.py | 8 ++++++++ tests/test_atomic_requests.py | 10 +++++----- tests/test_filters.py | 3 +-- tests/test_permissions.py | 3 +-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 2cff610882..164cf20031 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -67,6 +67,14 @@ def distinct(queryset, base): from django.utils.datastructures import SortedDict as OrderedDict +# unittest.SkipUnless only available in Python 2.7. +try: + import unittest + unittest.skipUnless +except (ImportError, AttributeError): + from django.test.utils import unittest + + # contrib.postgres only supported from 1.8 onwards. try: from django.contrib.postgres import fields as postgres_fields diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index d0d088f52e..8f68306638 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -5,9 +5,9 @@ from django.http import Http404 from django.test import TestCase, TransactionTestCase from django.utils.decorators import method_decorator -from django.utils.unittest import skipUnless from rest_framework import status +from rest_framework.compat import unittest from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework.test import APIRequestFactory @@ -35,7 +35,7 @@ def post(self, request, *args, **kwargs): raise APIException -@skipUnless(connection.features.uses_savepoints, +@unittest.skipUnless(connection.features.uses_savepoints, "'atomic' requires transactions and savepoints.") class DBTransactionTests(TestCase): def setUp(self): @@ -55,7 +55,7 @@ def test_no_exception_conmmit_transaction(self): assert BasicModel.objects.count() == 1 -@skipUnless(connection.features.uses_savepoints, +@unittest.skipUnless(connection.features.uses_savepoints, "'atomic' requires transactions and savepoints.") class DBTransactionErrorTests(TestCase): def setUp(self): @@ -83,7 +83,7 @@ def test_generic_exception_delegate_transaction_management(self): assert BasicModel.objects.count() == 1 -@skipUnless(connection.features.uses_savepoints, +@unittest.skipUnless(connection.features.uses_savepoints, "'atomic' requires transactions and savepoints.") class DBTransactionAPIExceptionTests(TestCase): def setUp(self): @@ -113,7 +113,7 @@ def test_api_exception_rollback_transaction(self): assert BasicModel.objects.count() == 0 -@skipUnless(connection.features.uses_savepoints, +@unittest.skipUnless(connection.features.uses_savepoints, "'atomic' requires transactions and savepoints.") class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase): @property diff --git a/tests/test_filters.py b/tests/test_filters.py index 0610b08557..bce6e08faf 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -8,12 +8,11 @@ from django.db import models from django.test import TestCase from django.test.utils import override_settings -from django.utils import unittest from django.utils.dateparse import parse_date from django.utils.six.moves import reload_module from rest_framework import filters, generics, serializers, status -from rest_framework.compat import django_filters +from rest_framework.compat import django_filters, unittest from rest_framework.test import APIRequestFactory from .models import BaseFilterableItem, BasicModel, FilterableItem diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 3980200020..ffc262a412 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -6,13 +6,12 @@ from django.core.urlresolvers import ResolverMatch from django.db import models from django.test import TestCase -from django.utils import unittest from rest_framework import ( HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, status ) -from rest_framework.compat import get_model_name, guardian +from rest_framework.compat import get_model_name, guardian, unittest from rest_framework.filters import DjangoObjectPermissionsFilter from rest_framework.routers import DefaultRouter from rest_framework.test import APIRequestFactory From f691006f2c79b408b819c716fba0a1bc4ed71fc1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Aug 2015 17:16:19 +0100 Subject: [PATCH 003/100] Resolve generic fields import --- tests/test_relations_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_relations_generic.py b/tests/test_relations_generic.py index 962857365f..340d4d1d1f 100644 --- a/tests/test_relations_generic.py +++ b/tests/test_relations_generic.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from django.contrib.contenttypes.generic import ( +from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation ) from django.contrib.contenttypes.models import ContentType From 4f2769746773f0a796206f4fcf01f180bdaff640 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Aug 2015 17:28:12 +0100 Subject: [PATCH 004/100] Fix get_model import --- rest_framework/compat.py | 8 ++++++++ rest_framework/utils/model_meta.py | 4 ++-- tests/test_utils.py | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 164cf20031..607778a9ea 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -82,6 +82,14 @@ def distinct(queryset, base): postgres_fields = None +# Apps only exists from 1.7 onwards. +try: + from django.apps import apps + get_model = apps.get_model +except ImportError: + from django.db.models import get_model + + # django-filter is optional try: import django_filters diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 3bcd9049c4..9718647695 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -12,7 +12,7 @@ from django.db import models from django.utils import six -from rest_framework.compat import OrderedDict +from rest_framework.compat import OrderedDict, get_model FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance @@ -45,7 +45,7 @@ def _resolve_model(obj): """ if isinstance(obj, six.string_types) and len(obj.split('.')) == 2: app_name, model_name = obj.split('.') - resolved_model = models.get_model(app_name, model_name) + resolved_model = get_model(app_name, model_name) if resolved_model is None: msg = "Django did not return a model for {0}.{1}" raise ImproperlyConfigured(msg.format(app_name, model_name)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 062f78e11a..4c9fd03c85 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -150,12 +150,12 @@ class ResolveModelWithPatchedDjangoTests(TestCase): def setUp(self): """Monkeypatch get_model.""" - self.get_model = rest_framework.utils.model_meta.models.get_model + self.get_model = rest_framework.utils.model_meta.get_model def get_model(app_label, model_name): return None - rest_framework.utils.model_meta.models.get_model = get_model + rest_framework.utils.model_meta.get_model = get_model def tearDown(self): """Revert monkeypatching.""" From 654e0e45279b5023cea7574bda1ebd63dae96347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Thu, 27 Aug 2015 13:09:08 -0400 Subject: [PATCH 005/100] Update ModelSerializer fields behavior --- rest_framework/serializers.py | 21 +++++++++++++++++++-- tests/test_model_serializer.py | 15 +++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c7d4405c59..acaf3bef73 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -12,6 +12,8 @@ """ from __future__ import unicode_literals +import warnings + from django.db import models from django.db.models.fields import Field as DjangoModelField from django.db.models.fields import FieldDoesNotExist @@ -51,6 +53,8 @@ 'instance', 'data', 'partial', 'context', 'allow_null' ) +ALL_FIELDS = '__all__' + # BaseSerializer # -------------- @@ -943,13 +947,13 @@ def get_field_names(self, declared_fields, info): fields = getattr(self.Meta, 'fields', None) exclude = getattr(self.Meta, 'exclude', None) - if fields and not isinstance(fields, (list, tuple)): + if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)): raise TypeError( 'The `fields` option must be a list or tuple. Got %s.' % type(fields).__name__ ) - if exclude and not isinstance(exclude, (list, tuple)): + if exclude and exclude != ALL_FIELDS and not isinstance(exclude, (list, tuple)): raise TypeError( 'The `exclude` option must be a list or tuple. Got %s.' % type(exclude).__name__ @@ -962,6 +966,19 @@ def get_field_names(self, declared_fields, info): ) ) + if fields is None and exclude is None: + warnings.warn( + "Creating a ModelSerializer without either the 'fields' attribute " + "or the 'exclude' attribute will be prohibited. " + "The {serializer_class} serializer needs updating.".format( + serializer_class=self.__class__.__name__ + ), + PendingDeprecationWarning + ) + + if fields == ALL_FIELDS: + fields = None + if fields is not None: # Ensure that all declared fields have also been included in the # `Meta.fields` option. diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 777b956c4b..89557fa1dd 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -321,6 +321,21 @@ class Meta: ExampleSerializer() + def test_fields_and_exclude_behavior(self): + class ImplicitFieldsSerializer(serializers.ModelSerializer): + class Meta: + model = RegularFieldsModel + + class ExplicitFieldsSerializer(serializers.ModelSerializer): + class Meta: + model = RegularFieldsModel + fields = '__all__' + + implicit = ImplicitFieldsSerializer() + explicit = ExplicitFieldsSerializer() + + assert implicit.data == explicit.data + @pytest.mark.skipif(django.VERSION < (1, 8), reason='DurationField is only available for django1.8+') From e70da5ac6b563cf82e8bc86f4a2f7103efc297c8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:03:08 +0100 Subject: [PATCH 006/100] Compat for GenericForeignKey, GenericRelation --- rest_framework/compat.py | 10 ++++++++++ tests/test_relations_generic.py | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 607778a9ea..fa42f95edb 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -90,6 +90,16 @@ def distinct(queryset, base): from django.db.models import get_model +# Import path changes from 1.7 onwards. +try: + from django.contrib.contenttypes.fields import ( + GenericForeignKey, GenericRelation + ) +except ImportError: + from django.contrib.contenttypes.generic import ( + GenericForeignKey, GenericRelation + ) + # django-filter is optional try: import django_filters diff --git a/tests/test_relations_generic.py b/tests/test_relations_generic.py index 340d4d1d1f..5cb2dfc05b 100644 --- a/tests/test_relations_generic.py +++ b/tests/test_relations_generic.py @@ -1,14 +1,12 @@ from __future__ import unicode_literals -from django.contrib.contenttypes.fields import ( - GenericForeignKey, GenericRelation -) from django.contrib.contenttypes.models import ContentType from django.db import models from django.test import TestCase from django.utils.encoding import python_2_unicode_compatible from rest_framework import serializers +from rest_framework.compat import GenericForeignKey, GenericRelation @python_2_unicode_compatible From 25c4c7f9fd3a5d789ec772f341fb5e036fe073dd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:03:16 +0100 Subject: [PATCH 007/100] Pep8 fix --- tests/test_atomic_requests.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index 8f68306638..a97efb69ce 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -35,8 +35,10 @@ def post(self, request, *args, **kwargs): raise APIException -@unittest.skipUnless(connection.features.uses_savepoints, - "'atomic' requires transactions and savepoints.") +@unittest.skipUnless( + connection.features.uses_savepoints, + "'atomic' requires transactions and savepoints." +) class DBTransactionTests(TestCase): def setUp(self): self.view = BasicView.as_view() @@ -55,8 +57,10 @@ def test_no_exception_conmmit_transaction(self): assert BasicModel.objects.count() == 1 -@unittest.skipUnless(connection.features.uses_savepoints, - "'atomic' requires transactions and savepoints.") +@unittest.skipUnless( + connection.features.uses_savepoints, + "'atomic' requires transactions and savepoints." +) class DBTransactionErrorTests(TestCase): def setUp(self): self.view = ErrorView.as_view() @@ -83,8 +87,10 @@ def test_generic_exception_delegate_transaction_management(self): assert BasicModel.objects.count() == 1 -@unittest.skipUnless(connection.features.uses_savepoints, - "'atomic' requires transactions and savepoints.") +@unittest.skipUnless( + connection.features.uses_savepoints, + "'atomic' requires transactions and savepoints." +) class DBTransactionAPIExceptionTests(TestCase): def setUp(self): self.view = APIExceptionView.as_view() @@ -113,8 +119,10 @@ def test_api_exception_rollback_transaction(self): assert BasicModel.objects.count() == 0 -@unittest.skipUnless(connection.features.uses_savepoints, - "'atomic' requires transactions and savepoints.") +@unittest.skipUnless( + connection.features.uses_savepoints, + "'atomic' requires transactions and savepoints." +) class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase): @property def urls(self): From 24a2c3f5c3349ec941790240c1f372a6de723e1b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:19:18 +0100 Subject: [PATCH 008/100] Resolve unittest compat --- rest_framework/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index fa42f95edb..a512af771e 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -72,7 +72,7 @@ def distinct(queryset, base): import unittest unittest.skipUnless except (ImportError, AttributeError): - from django.test.utils import unittest + from django.utils import unittest # contrib.postgres only supported from 1.8 onwards. From a5ddd90df03fe04793e605d26d01f86391fa4771 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:27:49 +0100 Subject: [PATCH 009/100] Log in and log out require escape and mark_safe --- rest_framework/templatetags/rest_framework.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 0069d9a5e2..08acecef77 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -41,8 +41,9 @@ def optional_login(request): except NoReverseMatch: return '' - snippet = "
  • Log in
  • ".format(href=login_url, next=escape(request.path)) - return snippet + snippet = "
  • Log in
  • " + snippet = snippet.format(href=login_url, next=escape(request.path)) + return mark_safe(snippet) @register.simple_tag @@ -64,8 +65,8 @@ def optional_logout(request, user):
  • Log out
  • """ - - return snippet.format(user=user, href=logout_url, next=escape(request.path)) + snippet = snippet.format(user=escape(user), href=logout_url, next=escape(request.path)) + return mark_safe(snippet) @register.simple_tag From 6fa534f21475f4c9527b1181f8a427978ce1c085 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:44:49 +0100 Subject: [PATCH 010/100] Fix urlpatterns in test --- tests/test_atomic_requests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index a97efb69ce..f24c7e6853 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -135,9 +135,8 @@ def get(self, request, *args, **kwargs): BasicModel.objects.all() raise Http404 - return patterns( - '', - url(r'^$', NonAtomicAPIExceptionView.as_view()) + return ( + url(r'^$', NonAtomicAPIExceptionView.as_view()), ) def setUp(self): From 7560e8381f90c646bdfeaf1ee491482f39766f53 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 10:53:44 +0100 Subject: [PATCH 011/100] Drop unused patterns --- tests/test_atomic_requests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index f24c7e6853..2d973cb8a9 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from django.conf.urls import patterns, url +from django.conf.urls import url from django.db import connection, connections, transaction from django.http import Http404 from django.test import TestCase, TransactionTestCase From b51c1ff0b0535a135ba018064c99a5995a9d8893 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 11:55:23 +0100 Subject: [PATCH 012/100] Django 1.9's test case HttpResponse.json() is not cachable. --- rest_framework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/response.py b/rest_framework/response.py index 3f038e3b5f..0e97668eb4 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -98,7 +98,7 @@ def __getstate__(self): state = super(Response, self).__getstate__() for key in ( 'accepted_renderer', 'renderer_context', 'resolver_match', - 'client', 'request', 'wsgi_request' + 'client', 'request', 'json', 'wsgi_request' ): if key in state: del state[key] From 8db6367188d27a41a42bf36eef12774e7cc59354 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 12:08:32 +0100 Subject: [PATCH 013/100] Deal with 1.9's differing null behavior on reverse relationships and m2m --- rest_framework/utils/field_mapping.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index f2598974ef..7805a2853e 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -233,9 +233,11 @@ def get_relation_kwargs(field_name, relation_info): # No further keyword arguments are valid. return kwargs - if model_field.has_default() or model_field.blank or model_field.null: + null = model_field.null and not to_many + + if model_field.has_default() or model_field.blank or null: kwargs['required'] = False - if model_field.null: + if null: kwargs['allow_null'] = True if model_field.validators: kwargs['validators'] = model_field.validators From f3ef13ab59c1ae9504e830ea3f45210fab5e973d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Fri, 28 Aug 2015 08:05:20 -0400 Subject: [PATCH 014/100] Update to match docs on ModelForm fields --- docs/api-guide/serializers.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index abdb67afac..5138c6ce07 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -453,14 +453,31 @@ To do so, open the Django shell, using `python manage.py shell`, then import the ## Specifying which fields to include -If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. +It is strongly recommended that you explicitly set all fields that should be edited in the serializer using the `fields` attribute. Failure to do so can easily lead to security problems when a serializer unexpectedly allows a user to set certain fields, especially when new fields are added to a model. + +The alternative approach would be to include all fields automatically, or blacklist only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. [GitHub][github-vuln-blog]). + +There are, however, two shortcuts available for cases where you can guarantee these security concerns do not apply to you: + +1. Set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used. For example: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account - fields = ('id', 'account_name', 'users', 'created') + fields = '__all__' + +2. Set the exclude attribute of the ModelForm’s inner Meta class to a list of fields to be excluded from the form. + +For example: + + class AccountSerializer(serializers.ModelSerializer): + class Meta: + model = Account + exclude = 'users' + +In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized. The names in the `fields` option will normally map to model fields on the model class. @@ -1035,6 +1052,7 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides a `G The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreSerializer` to support [django-hstore][django-hstore] `DictionaryField` model field and its `schema-mode` feature. [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion +[github-vuln-blog]: https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ From 1fe8e9a0bf37e045457aa91a19748ea1a6479a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Fri, 28 Aug 2015 08:11:07 -0400 Subject: [PATCH 015/100] Add note on deprecation path --- docs/api-guide/serializers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 5138c6ce07..ed1ada92c5 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -483,6 +483,12 @@ The names in the `fields` option will normally map to model fields on the model Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class. +--- + +**Note**: Before version 3.3, the `'__all__'` shortcut did not exist, but omitting the fields attribute had the same effect. Omitting both fields and exclude is now deprecated, but will continue to work as before until version 3.5 + +--- + ## Specifying nested serialization The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: From 78632849cf7d6599883674bbb6baec87dab4fefd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 13:29:57 +0100 Subject: [PATCH 016/100] Comment against model_field.null 1.98 behavior --- rest_framework/utils/field_mapping.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 7805a2853e..62e4dbc127 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -233,6 +233,9 @@ def get_relation_kwargs(field_name, relation_info): # No further keyword arguments are valid. return kwargs + # Currently required for 1.9 master behavior, + # may be able to remove this with 1.9 alpha. + # See https://code.djangoproject.com/ticket/25320 null = model_field.null and not to_many if model_field.has_default() or model_field.blank or null: From 3a0d54c7fbf4b8eb2b3e77ac6e013b4a8ff94e09 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Aug 2015 13:52:18 +0100 Subject: [PATCH 017/100] Fix testing example. Closes #3346 --- docs/api-guide/testing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index dd3295c4f8..69da7d1056 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -200,6 +200,7 @@ You can use any of REST framework's test case classes as you would for the regul from django.core.urlresolvers import reverse from rest_framework import status from rest_framework.test import APITestCase + from myproject.apps.core.models import Account class AccountTests(APITestCase): def test_create_account(self): @@ -210,7 +211,8 @@ You can use any of REST framework's test case classes as you would for the regul data = {'name': 'DabApps'} response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data, data) + self.assertEqual(Account.objects.count(), 1) + self.assertEqual(Account.objects.get().name, 'DabApps') --- From 9dd1b2516be3b1993219ad93e5802f5b8d25e324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Fri, 28 Aug 2015 09:51:11 -0400 Subject: [PATCH 018/100] Update ModelSerializer fields docs --- docs/api-guide/serializers.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index ed1ada92c5..cefcfa0972 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -432,6 +432,7 @@ Declaring a `ModelSerializer` looks like this: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account + fields = ('id', 'account_name', 'users', 'created') By default, all the model fields on the class will be mapped to a corresponding serializer fields. @@ -453,13 +454,16 @@ To do so, open the Django shell, using `python manage.py shell`, then import the ## Specifying which fields to include -It is strongly recommended that you explicitly set all fields that should be edited in the serializer using the `fields` attribute. Failure to do so can easily lead to security problems when a serializer unexpectedly allows a user to set certain fields, especially when new fields are added to a model. +If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. It is strongly recommended that you explicitly set all fields that should be serialized using the `fields` attribute. This will make it less likely to result in unintentionally exposing data when your models change. -The alternative approach would be to include all fields automatically, or blacklist only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. [GitHub][github-vuln-blog]). +For example: -There are, however, two shortcuts available for cases where you can guarantee these security concerns do not apply to you: + class AccountSerializer(serializers.ModelSerializer): + class Meta: + model = Account + fields = ('id', 'account_name', 'users', 'created') -1. Set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used. +You can also set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used. For example: @@ -468,27 +472,21 @@ For example: model = Account fields = '__all__' -2. Set the exclude attribute of the ModelForm’s inner Meta class to a list of fields to be excluded from the form. +You can set the `exclude` attribute of the to a list of fields to be excluded from the serializer. For example: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account - exclude = 'users' + exclude = ('users',) In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized. -The names in the `fields` option will normally map to model fields on the model class. +The names in the `fields` and `exclude` attributes will normally map to model fields on the model class. Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class. ---- - -**Note**: Before version 3.3, the `'__all__'` shortcut did not exist, but omitting the fields attribute had the same effect. Omitting both fields and exclude is now deprecated, but will continue to work as before until version 3.5 - ---- - ## Specifying nested serialization The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: @@ -1058,7 +1056,6 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides a `G The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreSerializer` to support [django-hstore][django-hstore] `DictionaryField` model field and its `schema-mode` feature. [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion -[github-vuln-blog]: https://github.com/blog/1068-public-key-security-vulnerability-and-mitigation [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ From 6926c716993e8d0b4ecdff1006dd23519dda4089 Mon Sep 17 00:00:00 2001 From: Miroslav Shubernetskiy Date: Mon, 31 Aug 2015 21:23:11 -0400 Subject: [PATCH 019/100] added django-url-filter to 3rd party libraries --- docs/api-guide/filtering.md | 5 +++++ docs/topics/third-party-resources.md | 2 ++ 2 files changed, 7 insertions(+) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 6f4648cfd0..72a8d1c5f1 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -403,6 +403,10 @@ The [django-rest-framework-filters package][django-rest-framework-filters] works The [djangorestframework-word-filter][django-rest-framework-word-search-filter] developed as alternative to `filters.SearchFilter` which will search full word in text, or exact match. +## Django URL Filter + +[django-url-filter][django-url-filter] provides a safe way to filter data via human-friendly URLs. It works very similar to DRF serializers and fields in a sense that they can be nested except they are called filtersets and filters. That provides easy way to filter related data. Also this library is generic-purpose so it can be used to filter other sources of data and not only Django `QuerySet`s. + [cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters [django-filter]: https://github.com/alex/django-filter [django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html @@ -413,3 +417,4 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter +[django-url-filter]: https://github.com/miki725/django-url-filter diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md index adf524fd87..9c302f77ed 100644 --- a/docs/topics/third-party-resources.md +++ b/docs/topics/third-party-resources.md @@ -233,6 +233,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ### Filtering * [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters. +* [django-url-filter][django-url-filter] - Allows a safe way to filter data via human-friendly URLs. It is a generic library which is not tied to DRF but it provides easy integration with DRF. ### Misc @@ -343,3 +344,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [drf-tracking]: https://github.com/aschn/drf-tracking [django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions +[django-url-filter]: https://github.com/miki725/django-url-filter From 7c12a988fac0451d79e49cbd9aaeb4ac8764a9f6 Mon Sep 17 00:00:00 2001 From: Britt Dawn Date: Wed, 2 Sep 2015 09:25:00 -0700 Subject: [PATCH 020/100] Replacing self.tag with self.tag_name in Generic Relationships documentation. --- docs/api-guide/relations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 979c3326ce..fd53acbc8a 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -501,7 +501,7 @@ For example, given the following model for a tag, which has a generic relationsh tagged_object = GenericForeignKey('content_type', 'object_id') def __unicode__(self): - return self.tag + return self.tag_name And the following two models, which may be have associated tags: From b9782b6850a032d20c321b960f4e829ed873d418 Mon Sep 17 00:00:00 2001 From: raphaelmerx Date: Wed, 2 Sep 2015 16:47:50 -0700 Subject: [PATCH 021/100] DurationField accepts integers --- rest_framework/fields.py | 2 +- tests/test_fields.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index faf41e7a0c..159784ea33 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1189,7 +1189,7 @@ def __init__(self, *args, **kwargs): def to_internal_value(self, value): if isinstance(value, datetime.timedelta): return value - parsed = parse_duration(value) + parsed = parse_duration(six.text_type(value)) if parsed is not None: return parsed self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]') diff --git a/tests/test_fields.py b/tests/test_fields.py index 8065c8260e..c1d3e3a495 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1069,6 +1069,7 @@ class TestDurationField(FieldValues): '3 08:32:01.000123': datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123), '08:01': datetime.timedelta(minutes=8, seconds=1), datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123): datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123), + 3600: datetime.timedelta(hours=1), } invalid_inputs = { 'abc': ['Duration has wrong format. Use one of these formats instead: [DD] [HH:[MM:]]ss[.uuuuuu].'], From 1c049edd8cc7da265deee47eb6ef004fcb297d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Thu, 3 Sep 2015 09:43:27 +0200 Subject: [PATCH 022/100] Improve documentation of default argument for fields The documentation now not only mentions that callables are acceptable arguments, it also tells the reader what the callable should look like. --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 2b9a6bba68..f681d94537 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -51,7 +51,7 @@ Defaults to `False` If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. -May be set to a function or other callable, in which case the value will be evaluated each time it is used. +May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error. From a57ad0767421a86bd35bf5a72c337d6857ad5f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Thu, 3 Sep 2015 10:35:28 +0200 Subject: [PATCH 023/100] Add link to validators The `set_context` method there is the same as for the default field. --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index f681d94537..2fbb84c032 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -51,7 +51,7 @@ Defaults to `False` If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. -May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. +May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error. From afd2a8f8f0186cfeb781c33922b27372f97f074b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 10:12:52 +0100 Subject: [PATCH 024/100] Adjust ModelField.null mappings now that Django-25320 is resolved --- rest_framework/utils/field_mapping.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 62e4dbc127..f2598974ef 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -233,14 +233,9 @@ def get_relation_kwargs(field_name, relation_info): # No further keyword arguments are valid. return kwargs - # Currently required for 1.9 master behavior, - # may be able to remove this with 1.9 alpha. - # See https://code.djangoproject.com/ticket/25320 - null = model_field.null and not to_many - - if model_field.has_default() or model_field.blank or null: + if model_field.has_default() or model_field.blank or model_field.null: kwargs['required'] = False - if null: + if model_field.null: kwargs['allow_null'] = True if model_field.validators: kwargs['validators'] = model_field.validators From f9d1a34fdd4d8065acb2e9ab9ed4a7b0338b66bd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 11:23:58 +0100 Subject: [PATCH 025/100] Fix nested HTML dictionaries. Closes #3314. --- rest_framework/utils/html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/utils/html.py b/rest_framework/utils/html.py index c5fc5d8dae..3b871027c7 100644 --- a/rest_framework/utils/html.py +++ b/rest_framework/utils/html.py @@ -78,7 +78,7 @@ def parse_html_dict(dictionary, prefix=''): } } """ - ret = {} + ret = MultiValueDict() regex = re.compile(r'^%s\.(.+)$' % re.escape(prefix)) for field, value in dictionary.items(): match = regex.match(field) From 7905e9f9f42727b8b8b4466cb0ad806b264de9bd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 11:57:11 +0100 Subject: [PATCH 026/100] Docs tweak --- docs/api-guide/filtering.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 72a8d1c5f1..c740dfe749 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -331,8 +331,6 @@ The `ordering` attribute may be either a string or a list/tuple of strings. The `DjangoObjectPermissionsFilter` is intended to be used together with the [`django-guardian`][guardian] package, with custom `'view'` permissions added. The filter will ensure that querysets only returns objects for which the user has the appropriate view permission. -This filter class must be used with views that provide either a `queryset` or a `model` attribute. - If you're using `DjangoObjectPermissionsFilter`, you'll probably also want to add an appropriate object permissions class, to ensure that users can only operate on instances if they have the appropriate object permissions. The easiest way to do this is to subclass `DjangoObjectPermissions` and add `'view'` permissions to the `perms_map` attribute. A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectPermissions` might look something like this. From 39ec564ae92805ce630c6855ce95f3d586c7203f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 14:22:13 +0100 Subject: [PATCH 027/100] Do not access settings on compat import --- rest_framework/compat.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 2cff610882..460795f658 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -94,12 +94,11 @@ def clean_manytomany_helptext(text): # Django-guardian is optional. Import only if guardian is in INSTALLED_APPS # Fixes (#1712). We keep the try/except for the test suite. guardian = None -if 'guardian' in settings.INSTALLED_APPS: - try: - import guardian - import guardian.shortcuts # Fixes #1624 - except ImportError: - pass +try: + import guardian + import guardian.shortcuts # Fixes #1624 +except ImportError: + pass def get_model_name(model_cls): From 10da18b20b7feaa7a615d6cddbdabda7968ff98c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 16:24:13 +0100 Subject: [PATCH 028/100] Access settings lazily, not at module import --- rest_framework/fields.py | 114 ++++++++++++++++++++-------------- rest_framework/serializers.py | 5 +- rest_framework/settings.py | 12 +++- 3 files changed, 82 insertions(+), 49 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 159784ea33..8b42690d7d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -9,6 +9,7 @@ import uuid from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ObjectDoesNotExist from django.core.validators import RegexValidator, ip_address_validators @@ -882,12 +883,11 @@ class DecimalField(Field): } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. - coerce_to_string = api_settings.COERCE_DECIMAL_TO_STRING - def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, **kwargs): self.max_digits = max_digits self.decimal_places = decimal_places - self.coerce_to_string = coerce_to_string if (coerce_to_string is not None) else self.coerce_to_string + if coerce_to_string is not None: + self.coerce_to_string = coerce_to_string self.max_value = max_value self.min_value = min_value @@ -967,12 +967,14 @@ def validate_precision(self, value): return value def to_representation(self, value): + coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING) + if not isinstance(value, decimal.Decimal): value = decimal.Decimal(six.text_type(value).strip()) quantized = self.quantize(value) - if not self.coerce_to_string: + if not coerce_to_string: return quantized return '{0:f}'.format(quantized) @@ -994,15 +996,15 @@ class DateTimeField(Field): 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), 'date': _('Expected a datetime but got a date.'), } - format = api_settings.DATETIME_FORMAT - input_formats = api_settings.DATETIME_INPUT_FORMATS - default_timezone = timezone.get_default_timezone() if settings.USE_TZ else None datetime_parser = datetime.datetime.strptime def __init__(self, format=empty, input_formats=None, default_timezone=None, *args, **kwargs): - self.format = format if format is not empty else self.format - self.input_formats = input_formats if input_formats is not None else self.input_formats - self.default_timezone = default_timezone if default_timezone is not None else self.default_timezone + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats + if default_timezone is not None: + self.timezone = default_timezone super(DateTimeField, self).__init__(*args, **kwargs) def enforce_timezone(self, value): @@ -1010,21 +1012,31 @@ def enforce_timezone(self, value): When `self.default_timezone` is `None`, always return naive datetimes. When `self.default_timezone` is not `None`, always return aware datetimes. """ - if (self.default_timezone is not None) and not timezone.is_aware(value): - return timezone.make_aware(value, self.default_timezone) - elif (self.default_timezone is None) and timezone.is_aware(value): + field_timezone = getattr(self, 'timezone', self.default_timezone()) + + if (field_timezone is not None) and not timezone.is_aware(value): + return timezone.make_aware(value, field_timezone) + elif (field_timezone is None) and timezone.is_aware(value): return timezone.make_naive(value, timezone.UTC()) return value + def default_timezone(self): + try: + return timezone.get_default_timezone() if settings.USE_TZ else None + except ImproperlyConfigured: + return None + def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS) + if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime): self.fail('date') if isinstance(value, datetime.datetime): return self.enforce_timezone(value) - for format in self.input_formats: - if format.lower() == ISO_8601: + for input_format in input_formats: + if input_format.lower() == ISO_8601: try: parsed = parse_datetime(value) except (ValueError, TypeError): @@ -1034,25 +1046,27 @@ def to_internal_value(self, value): return self.enforce_timezone(parsed) else: try: - parsed = self.datetime_parser(value, format) + parsed = self.datetime_parser(value, input_format) except (ValueError, TypeError): pass else: return self.enforce_timezone(parsed) - humanized_format = humanize_datetime.datetime_formats(self.input_formats) + humanized_format = humanize_datetime.datetime_formats(input_formats) self.fail('invalid', format=humanized_format) def to_representation(self, value): - if self.format is None: + output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT) + + if output_format is None: return value - if self.format.lower() == ISO_8601: + if output_format.lower() == ISO_8601: value = value.isoformat() if value.endswith('+00:00'): value = value[:-6] + 'Z' return value - return value.strftime(self.format) + return value.strftime(output_format) class DateField(Field): @@ -1060,24 +1074,26 @@ class DateField(Field): 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'), 'datetime': _('Expected a date but got a datetime.'), } - format = api_settings.DATE_FORMAT - input_formats = api_settings.DATE_INPUT_FORMATS datetime_parser = datetime.datetime.strptime def __init__(self, format=empty, input_formats=None, *args, **kwargs): - self.format = format if format is not empty else self.format - self.input_formats = input_formats if input_formats is not None else self.input_formats + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats super(DateField, self).__init__(*args, **kwargs) def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS) + if isinstance(value, datetime.datetime): self.fail('datetime') if isinstance(value, datetime.date): return value - for format in self.input_formats: - if format.lower() == ISO_8601: + for input_format in input_formats: + if input_format.lower() == ISO_8601: try: parsed = parse_date(value) except (ValueError, TypeError): @@ -1087,20 +1103,22 @@ def to_internal_value(self, value): return parsed else: try: - parsed = self.datetime_parser(value, format) + parsed = self.datetime_parser(value, input_format) except (ValueError, TypeError): pass else: return parsed.date() - humanized_format = humanize_datetime.date_formats(self.input_formats) + humanized_format = humanize_datetime.date_formats(input_formats) self.fail('invalid', format=humanized_format) def to_representation(self, value): + output_format = getattr(self, 'format', api_settings.DATE_FORMAT) + if not value: return None - if self.format is None: + if output_format is None: return value # Applying a `DateField` to a datetime value is almost always @@ -1112,33 +1130,35 @@ def to_representation(self, value): 'read-only field and deal with timezone issues explicitly.' ) - if self.format.lower() == ISO_8601: + if output_format.lower() == ISO_8601: if (isinstance(value, str)): value = datetime.datetime.strptime(value, '%Y-%m-%d').date() return value.isoformat() - return value.strftime(self.format) + return value.strftime(output_format) class TimeField(Field): default_error_messages = { 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'), } - format = api_settings.TIME_FORMAT - input_formats = api_settings.TIME_INPUT_FORMATS datetime_parser = datetime.datetime.strptime def __init__(self, format=empty, input_formats=None, *args, **kwargs): - self.format = format if format is not empty else self.format - self.input_formats = input_formats if input_formats is not None else self.input_formats + if format is not empty: + self.format = format + if input_formats is not None: + self.input_formats = input_formats super(TimeField, self).__init__(*args, **kwargs) def to_internal_value(self, value): + input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS) + if isinstance(value, datetime.time): return value - for format in self.input_formats: - if format.lower() == ISO_8601: + for input_format in input_formats: + if input_format.lower() == ISO_8601: try: parsed = parse_time(value) except (ValueError, TypeError): @@ -1148,17 +1168,19 @@ def to_internal_value(self, value): return parsed else: try: - parsed = self.datetime_parser(value, format) + parsed = self.datetime_parser(value, input_format) except (ValueError, TypeError): pass else: return parsed.time() - humanized_format = humanize_datetime.time_formats(self.input_formats) + humanized_format = humanize_datetime.time_formats(input_formats) self.fail('invalid', format=humanized_format) def to_representation(self, value): - if self.format is None: + output_format = getattr(self, 'format', api_settings.TIME_FORMAT) + + if output_format is None: return value # Applying a `TimeField` to a datetime value is almost always @@ -1170,9 +1192,9 @@ def to_representation(self, value): 'read-only field and deal with timezone issues explicitly.' ) - if self.format.lower() == ISO_8601: + if output_format.lower() == ISO_8601: return value.isoformat() - return value.strftime(self.format) + return value.strftime(output_format) class DurationField(Field): @@ -1316,12 +1338,12 @@ class FileField(Field): 'empty': _('The submitted file is empty.'), 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), } - use_url = api_settings.UPLOADED_FILES_USE_URL def __init__(self, *args, **kwargs): self.max_length = kwargs.pop('max_length', None) self.allow_empty_file = kwargs.pop('allow_empty_file', False) - self.use_url = kwargs.pop('use_url', self.use_url) + if 'use_url' in kwargs: + self.use_url = kwargs.pop('use_url') super(FileField, self).__init__(*args, **kwargs) def to_internal_value(self, data): @@ -1342,10 +1364,12 @@ def to_internal_value(self, data): return data def to_representation(self, value): + use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL) + if not value: return None - if self.use_url: + if use_url: if not getattr(value, 'url', None): # If the file has not been saved it may not have a URL. return None diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c7d4405c59..8ba1f3c404 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -786,7 +786,7 @@ class ModelSerializer(Serializer): # you'll also need to ensure you update the `create` method on any generic # views, to correctly handle the 'Location' response header for # "HTTP 201 Created" responses. - url_field_name = api_settings.URL_FIELD_NAME + url_field_name = None # Default `create` and `update` behavior... def create(self, validated_data): @@ -869,6 +869,9 @@ def get_fields(self): Return the dict of field names -> field instances that should be used for `self.fields` when instantiating the serializer. """ + if self.url_field_name is None: + self.url_field_name = api_settings.URL_FIELD_NAME + assert hasattr(self, 'Meta'), ( 'Class {serializer_class} missing "Meta" attribute'.format( serializer_class=self.__class__.__name__ diff --git a/rest_framework/settings.py b/rest_framework/settings.py index e20e512874..bc3868715e 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -26,7 +26,6 @@ from rest_framework import ISO_8601 from rest_framework.compat import importlib -USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None) DEFAULTS = { # Base API policies @@ -188,10 +187,17 @@ class APISettings(object): and return the class, rather than the string literal. """ def __init__(self, user_settings=None, defaults=None, import_strings=None): - self.user_settings = user_settings or {} + if user_settings: + self._user_settings = user_settings self.defaults = defaults or DEFAULTS self.import_strings = import_strings or IMPORT_STRINGS + @property + def user_settings(self): + if not hasattr(self, '_user_settings'): + self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) + return self._user_settings + def __getattr__(self, attr): if attr not in self.defaults.keys(): raise AttributeError("Invalid API setting: '%s'" % attr) @@ -212,7 +218,7 @@ def __getattr__(self, attr): return val -api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) +api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs): From dcdd765647b28135dc764f038e9380cf1ccc0ecc Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Thu, 3 Sep 2015 17:27:33 +0200 Subject: [PATCH 029/100] Avoid passing allow_empty to the ListSerializer children. --- rest_framework/serializers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c7d4405c59..77120a1089 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -113,8 +113,12 @@ def many_init(cls, *args, **kwargs): kwargs['child'] = cls() return CustomListSerializer(*args, **kwargs) """ + allow_empty = kwargs.pop('allow_empty', True) child_serializer = cls(*args, **kwargs) - list_kwargs = {'child': child_serializer} + list_kwargs = { + 'child': child_serializer, + 'allow_empty': allow_empty, + } list_kwargs.update(dict([ (key, value) for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS From 8b7ebb9d2ca6dd3dba4e021ae750166dd7fe5ecd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 16:29:13 +0100 Subject: [PATCH 030/100] Fixed import sorting --- rest_framework/fields.py | 3 +-- rest_framework/settings.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 8b42690d7d..e780c51144 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -9,9 +9,8 @@ import uuid from django.conf import settings -from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ValidationError as DjangoValidationError -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.validators import RegexValidator, ip_address_validators from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField diff --git a/rest_framework/settings.py b/rest_framework/settings.py index bc3868715e..c09472e9dc 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -26,7 +26,6 @@ from rest_framework import ISO_8601 from rest_framework.compat import importlib - DEFAULTS = { # Base API policies 'DEFAULT_RENDERER_CLASSES': ( From f9e53091c1defb0971f941a529b5c813060952b7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 3 Sep 2015 16:40:12 +0100 Subject: [PATCH 031/100] Drop handling of ImproperlyConfigured --- rest_framework/fields.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e780c51144..065051b545 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -10,7 +10,7 @@ from django.conf import settings from django.core.exceptions import ValidationError as DjangoValidationError -from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist from django.core.validators import RegexValidator, ip_address_validators from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField @@ -1020,10 +1020,7 @@ def enforce_timezone(self, value): return value def default_timezone(self): - try: - return timezone.get_default_timezone() if settings.USE_TZ else None - except ImproperlyConfigured: - return None + return timezone.get_default_timezone() if settings.USE_TZ else None def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS) From bb95de13bf512066bcfdba31c1d58b5a9fcad3f8 Mon Sep 17 00:00:00 2001 From: potpath Date: Fri, 4 Sep 2015 16:07:20 +0700 Subject: [PATCH 032/100] Update 6-viewsets-and-routers.md --- docs/tutorial/6-viewsets-and-routers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 63dff73fcf..f1dbe94431 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -45,7 +45,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl return Response(snippet.highlighted) def perform_create(self, serializer): - serializer.save(owner=self.request.user) + serializer.save(owner=self.request.user) This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations. From 4404d40f6068c5e403ddb7a8716e6b53cc3c6ba9 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Sun, 6 Sep 2015 23:02:44 +0200 Subject: [PATCH 033/100] Only pass allow_empty to the `ListSerializer` if it was in the arguments. Helps with backward compatibility. --- rest_framework/serializers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 77120a1089..037939e318 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -113,12 +113,13 @@ def many_init(cls, *args, **kwargs): kwargs['child'] = cls() return CustomListSerializer(*args, **kwargs) """ - allow_empty = kwargs.pop('allow_empty', True) + allow_empty = kwargs.pop('allow_empty', None) child_serializer = cls(*args, **kwargs) list_kwargs = { 'child': child_serializer, - 'allow_empty': allow_empty, } + if allow_empty is not None: + list_kwargs['allow_empty'] = allow_empty list_kwargs.update(dict([ (key, value) for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS From b57e9cf405475e31c3b7d6d6aa4365885a482e7f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 7 Sep 2015 10:22:44 +0100 Subject: [PATCH 034/100] Test protected branches --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 784b54c0cb..b183e14df0 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,6 @@ If you believe you’ve found something in Django REST framework which has secur Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. - [build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master [travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master [coverage-status-image]: https://img.shields.io/codecov/c/github/tomchristie/django-rest-framework/master.svg From aa4818268641d572cc914cbc495f680275643d8e Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Mon, 7 Sep 2015 23:11:01 +0200 Subject: [PATCH 035/100] Add test coverage on allow_empty for nested serializers. --- tests/test_serializer_nested.py | 50 ++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index 22d4deca15..aeb092ee05 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -79,17 +79,23 @@ class NestedSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer): allow_null = NestedSerializer(many=True, allow_null=True) not_allow_null = NestedSerializer(many=True) + allow_empty = NestedSerializer(many=True, allow_empty=True) + not_allow_empty = NestedSerializer(many=True, allow_empty=False) self.Serializer = TestSerializer def test_null_allowed_if_allow_null_is_set(self): input_data = { 'allow_null': None, - 'not_allow_null': [{'example': '2'}, {'example': '3'}] + 'not_allow_null': [{'example': '2'}, {'example': '3'}], + 'allow_empty': [{'example': '2'}], + 'not_allow_empty': [{'example': '2'}], } expected_data = { 'allow_null': None, - 'not_allow_null': [{'example': 2}, {'example': 3}] + 'not_allow_null': [{'example': 2}, {'example': 3}], + 'allow_empty': [{'example': 2}], + 'not_allow_empty': [{'example': 2}], } serializer = self.Serializer(data=input_data) @@ -99,7 +105,9 @@ def test_null_allowed_if_allow_null_is_set(self): def test_null_is_not_allowed_if_allow_null_is_not_set(self): input_data = { 'allow_null': None, - 'not_allow_null': None + 'not_allow_null': None, + 'allow_empty': [{'example': '2'}], + 'not_allow_empty': [{'example': '2'}], } serializer = self.Serializer(data=input_data) @@ -118,10 +126,44 @@ def validate_allow_null(self, value): input_data = { 'allow_null': None, - 'not_allow_null': [{'example': 2}] + 'not_allow_null': [{'example': 2}], + 'allow_empty': [{'example': 2}], + 'not_allow_empty': [{'example': 2}], } serializer = TestSerializer(data=input_data) assert serializer.is_valid() assert serializer.validated_data == input_data assert TestSerializer.validation_was_run + + def test_empty_allowed_if_allow_empty_is_set(self): + input_data = { + 'allow_null': [{'example': '2'}], + 'not_allow_null': [{'example': '2'}], + 'allow_empty': [], + 'not_allow_empty': [{'example': '2'}], + } + expected_data = { + 'allow_null': [{'example': 2}], + 'not_allow_null': [{'example': 2}], + 'allow_empty': [], + 'not_allow_empty': [{'example': 2}], + } + serializer = self.Serializer(data=input_data) + + assert serializer.is_valid(), serializer.errors + assert serializer.validated_data == expected_data + + def test_empty_not_allowed_if_allow_empty_is_set_to_false(self): + input_data = { + 'allow_null': [{'example': '2'}], + 'not_allow_null': [{'example': '2'}], + 'allow_empty': [], + 'not_allow_empty': [], + } + serializer = self.Serializer(data=input_data) + + assert not serializer.is_valid() + + expected_errors = {'not_allow_empty': {'non_field_errors': [serializers.ListSerializer.default_error_messages['empty']]}} + assert serializer.errors == expected_errors From 671de792e203a9f0165a6f943c69427c55ef37fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=ADaz?= Date: Thu, 10 Sep 2015 10:19:40 +0300 Subject: [PATCH 036/100] Fixed typo in ScopedRateThrottle example views --- docs/api-guide/throttling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 3f668867cd..57bce54e78 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -148,7 +148,7 @@ For example, given the following views... throttle_scope = 'contacts' ... - class ContactDetailView(ApiView): + class ContactDetailView(APIView): throttle_scope = 'contacts' ... From 4704da9a1acf3cbf1b1b1396821fa6ee61e0ffce Mon Sep 17 00:00:00 2001 From: Nic Young Date: Thu, 10 Sep 2015 17:17:33 -0700 Subject: [PATCH 037/100] Add note about field level validation, fixes #3306 The documentation added warns the user that field level validation will be skipped if the field is declared on the model with the parameter `required=False`. --- docs/api-guide/serializers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index abdb67afac..1ac845901d 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -189,6 +189,12 @@ Your `validate_` methods should return the validated value or raise raise serializers.ValidationError("Blog post is not about Django") return value +--- + +**Note:** If your `` is declared on your serializer with the parameter `required=False` then this validation step will not take place if the field is not included. + +--- + #### Object-level validation To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is a dictionary of field values. It should raise a `ValidationError` if necessary, or just return the validated values. For example: From ab296ced4ea66b1655313ece62551e6cdf1ecb19 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 11 Sep 2015 13:06:09 +0100 Subject: [PATCH 038/100] Add link to django REST marshmallow --- docs/api-guide/serializers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index abdb67afac..e8c9479719 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1022,6 +1022,10 @@ A new interface for controlling this behavior is currently planned for REST fram The following third party packages are also available. +## Django REST marshmallow + +The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases. + ## MongoengineModelSerializer The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework. @@ -1038,6 +1042,8 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [relations]: relations.md [model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ +[django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ +[marshmallow]: https://marshmallow.readthedocs.org/en/latest/ [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore From 1a8f9d0a0f7edb7a138bd37e31772ff56dbceb2c Mon Sep 17 00:00:00 2001 From: qqbuby Date: Sat, 12 Sep 2015 01:36:40 +0800 Subject: [PATCH 039/100] fix issue #3395 to correct the output text at line 184in docs/tutorial/1-serialization.md --- docs/tutorial/1-serialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index c54af0cec7..e26db8be3e 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -181,7 +181,7 @@ We can also serialize querysets instead of model instances. To do so we simply serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data - # [{'pk': 1, 'title': u'', 'code': u'foo = "bar"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}, {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}] + #[OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] ## Using ModelSerializers From 284f9faa07941a2c804909187211cc782e7c96c9 Mon Sep 17 00:00:00 2001 From: Steven Loria Date: Fri, 11 Sep 2015 23:01:18 -0400 Subject: [PATCH 040/100] Correctly handle [] and {} as invalid inputs to BooleanField --- rest_framework/fields.py | 11 +++++++---- tests/test_fields.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 159784ea33..7c48c621ed 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -608,10 +608,13 @@ def __init__(self, **kwargs): super(BooleanField, self).__init__(**kwargs) def to_internal_value(self, data): - if data in self.TRUE_VALUES: - return True - elif data in self.FALSE_VALUES: - return False + try: + if data in self.TRUE_VALUES: + return True + elif data in self.FALSE_VALUES: + return False + except TypeError: # Input is an unhashable type + pass self.fail('invalid', input=data) def to_representation(self, value): diff --git a/tests/test_fields.py b/tests/test_fields.py index c1d3e3a495..6048f49d00 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -466,6 +466,18 @@ class TestBooleanField(FieldValues): } field = serializers.BooleanField() + def test_disallow_unhashable_collection_types(self): + inputs = ( + [], + {}, + ) + field = serializers.BooleanField() + for input_value in inputs: + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(input_value) + expected = ['"{0}" is not a valid boolean.'.format(input_value)] + assert exc_info.value.detail == expected + class TestNullBooleanField(FieldValues): """ From 5c076e10bbd3defbda05403a678f7397ac5e6f26 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 12 Sep 2015 23:04:18 -0700 Subject: [PATCH 041/100] Add link to serpy serialization, fixes #3391 --- docs/api-guide/serializers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 70739689df..ca540a13b5 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1032,6 +1032,9 @@ The following third party packages are also available. The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases. +## Serpy +The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed. + ## MongoengineModelSerializer The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework. @@ -1050,6 +1053,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.org/en/latest/ +[serpy]: https://github.com/clarkduvall/serpy [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore From 1aa16caffe6a6e7eba409cc1a4be2f9a05777cd6 Mon Sep 17 00:00:00 2001 From: qqbuby Date: Sun, 13 Sep 2015 20:24:22 +0800 Subject: [PATCH 042/100] append a space after the comment mark to remain consistent with the other parts of the tutorial --- docs/tutorial/1-serialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index e26db8be3e..890f8d5617 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -181,7 +181,7 @@ We can also serialize querysets instead of model instances. To do so we simply serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data - #[OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] + # [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] ## Using ModelSerializers From 6baca948309da5cc67f25cdfda8eb7198700541f Mon Sep 17 00:00:00 2001 From: James McMahon Date: Mon, 14 Sep 2015 19:34:07 +0100 Subject: [PATCH 043/100] added knox package reference to 3rd party auth apps --- docs/api-guide/authentication.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 2ccf7d7215..7fda98e673 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -360,6 +360,10 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management. +## django-rest-knox + +[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). + [cite]: http://jacobian.org/writing/rest-worst-practices/ [http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 [http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 @@ -400,3 +404,4 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 [djoser]: https://github.com/sunscrapers/djoser [django-rest-auth]: https://github.com/Tivix/django-rest-auth +[django-rest-knox]: https://github.com/James1345/django-rest-knox From 4e7d0493db7ca415980752f37e5ccc837503be8d Mon Sep 17 00:00:00 2001 From: Nic Young Date: Mon, 14 Sep 2015 21:05:28 -0700 Subject: [PATCH 044/100] Update transifex-client requirement to latest release version --- requirements/requirements-packaging.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index 1efb2f8363..7510dbd795 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -5,4 +5,4 @@ wheel==0.24.0 twine==1.4.0 # Transifex client for managing translation resources. -transifex-client==0.10 +transifex-client==0.11b3 From 566812ac0b577f79801ec67f86f064cf39ebee01 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 17 Sep 2015 15:17:29 +0100 Subject: [PATCH 045/100] Remove method and content overriding --- rest_framework/renderers.py | 38 +++---- rest_framework/request.py | 98 +------------------ rest_framework/settings.py | 3 - .../static/rest_framework/js/ajax-form.js | 97 ++++++++++++++++++ .../static/rest_framework/js/csrf.js | 47 +++++++++ .../rest_framework/js/jquery-1.11.3.min.js | 5 + .../templates/rest_framework/base.html | 29 +++--- .../rest_framework/raw_data_form.html | 1 - tests/test_request.py | 73 -------------- tests/test_views.py | 30 ------ 10 files changed, 180 insertions(+), 241 deletions(-) create mode 100644 rest_framework/static/rest_framework/js/ajax-form.js create mode 100644 rest_framework/static/rest_framework/js/csrf.js create mode 100644 rest_framework/static/rest_framework/js/jquery-1.11.3.min.js diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index fb0dfcbd8f..a450be8480 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -420,9 +420,6 @@ def show_form_for_method(self, view, method, request, obj): if method not in view.allowed_methods: return # Not a valid method - if not api_settings.FORM_METHOD_OVERRIDE: - return # Cannot use form overloading - try: view.check_permissions(request) if obj is not None: @@ -530,13 +527,6 @@ def get_raw_data_form(self, data, view, method, request): instance = None with override_method(view, request, method) as request: - # If we're not using content overloading there's no point in - # supplying a generic form, as the view won't treat the form's - # value as the content of the request. - if not (api_settings.FORM_CONTENT_OVERRIDE and - api_settings.FORM_CONTENTTYPE_OVERRIDE): - return None - # Check permissions if not self.show_form_for_method(view, method, request, instance): return @@ -564,28 +554,22 @@ def get_raw_data_form(self, data, view, method, request): # Generate a generic form that includes a content type field, # and a content field. - content_type_field = api_settings.FORM_CONTENTTYPE_OVERRIDE - content_field = api_settings.FORM_CONTENT_OVERRIDE - media_types = [parser.media_type for parser in view.parser_classes] choices = [(media_type, media_type) for media_type in media_types] initial = media_types[0] - # NB. http://jacobian.org/writing/dynamic-form-generation/ class GenericContentForm(forms.Form): - def __init__(self): - super(GenericContentForm, self).__init__() - - self.fields[content_type_field] = forms.ChoiceField( - label='Media type', - choices=choices, - initial=initial - ) - self.fields[content_field] = forms.CharField( - label='Content', - widget=forms.Textarea, - initial=content - ) + _content_type = forms.ChoiceField( + label='Media type', + choices=choices, + initial=initial, + widget=forms.Select(attrs={'data-override': 'content-type'}) + ) + _content = forms.CharField( + label='Content', + widget=forms.Textarea(attrs={'data-override': 'content'}), + initial=content + ) return GenericContentForm() diff --git a/rest_framework/request.py b/rest_framework/request.py index 7b583485b1..98a70eb3ab 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -129,11 +129,6 @@ class Request(object): - authentication_classes(list/tuple). The authentications used to try authenticating the request's user. """ - - _METHOD_PARAM = api_settings.FORM_METHOD_OVERRIDE - _CONTENT_PARAM = api_settings.FORM_CONTENT_OVERRIDE - _CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE - def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request @@ -162,30 +157,10 @@ def __init__(self, request, parsers=None, authenticators=None, def _default_negotiator(self): return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS() - @property - def method(self): - """ - Returns the HTTP method. - - This allows the `method` to be overridden by using a hidden `form` - field on a form POST request. - """ - if not _hasattr(self, '_method'): - self._load_method_and_content_type() - return self._method - @property def content_type(self): - """ - Returns the content type header. - - This should be used instead of `request.META.get('HTTP_CONTENT_TYPE')`, - as it allows the content type to be overridden by using a hidden form - field on a form POST request. - """ - if not _hasattr(self, '_content_type'): - self._load_method_and_content_type() - return self._content_type + meta = self._request.META + return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', '')) @property def stream(self): @@ -265,9 +240,6 @@ def _load_data_and_files(self): """ Parses the request content into `self.data`. """ - if not _hasattr(self, '_content_type'): - self._load_method_and_content_type() - if not _hasattr(self, '_data'): self._data, self._files = self._parse() if self._files: @@ -276,32 +248,14 @@ def _load_data_and_files(self): else: self._full_data = self._data - def _load_method_and_content_type(self): - """ - Sets the method and content_type, and then check if they've - been overridden. - """ - self._content_type = self.META.get('HTTP_CONTENT_TYPE', - self.META.get('CONTENT_TYPE', '')) - - self._perform_form_overloading() - - if not _hasattr(self, '_method'): - self._method = self._request.method - - # Allow X-HTTP-METHOD-OVERRIDE header - if 'HTTP_X_HTTP_METHOD_OVERRIDE' in self.META: - self._method = self.META['HTTP_X_HTTP_METHOD_OVERRIDE'].upper() - def _load_stream(self): """ Return the content body of the request, as a stream. """ + meta = self._request.META try: content_length = int( - self.META.get( - 'CONTENT_LENGTH', self.META.get('HTTP_CONTENT_LENGTH') - ) + meta.get('CONTENT_LENGTH', meta.get('HTTP_CONTENT_LENGTH', 0)) ) except (ValueError, TypeError): content_length = 0 @@ -313,50 +267,6 @@ def _load_stream(self): else: self._stream = six.BytesIO(self.raw_post_data) - def _perform_form_overloading(self): - """ - If this is a form POST request, then we need to check if the method and - content/content_type have been overridden by setting them in hidden - form fields or not. - """ - - USE_FORM_OVERLOADING = ( - self._METHOD_PARAM or - (self._CONTENT_PARAM and self._CONTENTTYPE_PARAM) - ) - - # We only need to use form overloading on form POST requests. - if ( - self._request.method != 'POST' or - not USE_FORM_OVERLOADING or - not is_form_media_type(self._content_type) - ): - return - - # At this point we're committed to parsing the request as form data. - self._data = self._request.POST - self._files = self._request.FILES - self._full_data = self._data.copy() - self._full_data.update(self._files) - - # Method overloading - change the method and remove the param from the content. - if ( - self._METHOD_PARAM and - self._METHOD_PARAM in self._data - ): - self._method = self._data[self._METHOD_PARAM].upper() - - # Content overloading - modify the content type, and force re-parse. - if ( - self._CONTENT_PARAM and - self._CONTENTTYPE_PARAM and - self._CONTENT_PARAM in self._data and - self._CONTENTTYPE_PARAM in self._data - ): - self._content_type = self._data[self._CONTENTTYPE_PARAM] - self._stream = six.BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding'])) - self._data, self._files, self._full_data = (Empty, Empty, Empty) - def _parse(self): """ Parse the request content, returning a two-tuple of (data, files) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index e20e512874..ec58d3ab66 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -94,9 +94,6 @@ 'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', # Browser enhancements - 'FORM_METHOD_OVERRIDE': '_method', - 'FORM_CONTENT_OVERRIDE': '_content', - 'FORM_CONTENTTYPE_OVERRIDE': '_content_type', 'URL_ACCEPT_OVERRIDE': 'accept', 'URL_FORMAT_OVERRIDE': 'format', diff --git a/rest_framework/static/rest_framework/js/ajax-form.js b/rest_framework/static/rest_framework/js/ajax-form.js new file mode 100644 index 0000000000..db07fd8fb1 --- /dev/null +++ b/rest_framework/static/rest_framework/js/ajax-form.js @@ -0,0 +1,97 @@ +function replaceDocument(docString) { + var doc = document.open("text/html"); + doc.write(docString); + doc.close(); +} + + +function doAjaxSubmit(e) { + var form = $(this); + var btn = $(this.clk); + var method = btn.data('method') || form.data('method') || form.attr('method') || 'GET'; + method = method.toUpperCase() + if (method === 'GET') { + // GET requests can always use standard form submits. + return; + } + + var contentType = + form.find('input[data-override="content-type"]').val() || + form.find('select[data-override="content-type"] option:selected').text(); + if (method === 'POST' && !contentType) { + // POST requests can use standard form submits, unless we have + // overridden the content type. + return; + } + + // At this point we need to make an AJAX form submission. + e.preventDefault(); + + var url = form.attr('action'); + var data; + if (contentType) { + data = form.find('[data-override="content"]').val() || '' + } else { + contentType = form.attr('enctype') || form.attr('encoding') + if (contentType === 'multipart/form-data') { + if (!window.FormData) { + alert('Your browser does not support AJAX multipart form submissions'); + return; + } + // Use the FormData API and allow the content type to be set automatically, + // so it includes the boundary string. + // See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects + contentType = false; + data = new FormData(form[0]); + } else { + contentType = 'application/x-www-form-urlencoded; charset=UTF-8' + data = form.serialize(); + } + } + + var ret = $.ajax({ + url: url, + method: method, + data: data, + contentType: contentType, + processData: false, + headers: {'Accept': 'text/html; q=1.0, */*'}, + }); + ret.always(function(data, textStatus, jqXHR) { + if (textStatus != 'success') { + jqXHR = data; + } + var responseContentType = jqXHR.getResponseHeader("content-type") || ""; + if (responseContentType.toLowerCase().indexOf('text/html') === 0) { + replaceDocument(jqXHR.responseText); + try { + // Modify the location and scroll to top, as if after page load. + history.replaceState({}, '', url); + scroll(0,0); + } catch(err) { + // History API not supported, so redirect. + window.location = url; + } + } else { + // Not HTML content. We can't open this directly, so redirect. + window.location = url; + } + }); + return ret; +} + + +function captureSubmittingElement(e) { + var target = e.target; + var form = this; + form.clk = target; +} + + +$.fn.ajaxForm = function() { + var options = {} + return this + .unbind('submit.form-plugin click.form-plugin') + .bind('submit.form-plugin', options, doAjaxSubmit) + .bind('click.form-plugin', options, captureSubmittingElement); +}; diff --git a/rest_framework/static/rest_framework/js/csrf.js b/rest_framework/static/rest_framework/js/csrf.js new file mode 100644 index 0000000000..4e8da0de54 --- /dev/null +++ b/rest_framework/static/rest_framework/js/csrf.js @@ -0,0 +1,47 @@ +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} + +function sameOrigin(url) { + // test that a given url is a same-origin URL + // url could be relative or scheme relative or absolute + var host = document.location.host; // host + port + var protocol = document.location.protocol; + var sr_origin = '//' + host; + var origin = protocol + sr_origin; + // Allow absolute or scheme relative URLs to same origin + return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || + (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || + // or any other URL that isn't scheme relative or absolute i.e relative. + !(/^(\/\/|http:|https:).*/.test(url)); +} + +var csrftoken = getCookie('csrftoken'); + +$.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) { + // Send the token to same-origin, relative URLs only. + // Send the token only if the method warrants CSRF protection + // Using the CSRFToken value acquired earlier + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } +}); diff --git a/rest_framework/static/rest_framework/js/jquery-1.11.3.min.js b/rest_framework/static/rest_framework/js/jquery-1.11.3.min.js new file mode 100644 index 0000000000..0f60b7bd0d --- /dev/null +++ b/rest_framework/static/rest_framework/js/jquery-1.11.3.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 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="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,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=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.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},m.extend=m.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||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},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=r(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(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[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=r(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&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=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)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(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||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(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 H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(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 pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(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 p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),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))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),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===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(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?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.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 ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.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},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.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=ga.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=ga.selectors={cacheLength:50,createPseudo:ia,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(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===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]||ga.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]&&ga.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(ca,da).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("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.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.replace(Q," ")+" ").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()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(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),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).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:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(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 ta(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 ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(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 wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(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?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(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 wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(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]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.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(ca,da),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(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(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&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.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?m.extend(a,d):d}},e={};return d.pipe=d.then,m.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&&m.isFunction(a.promise)?e:0,g=1===f?a:m.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]&&m.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;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.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.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.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 m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;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},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!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(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(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=wa(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=wa(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?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(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,m.cleanData(ua(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,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("