From 03f1bb9f8dff6a4a9ceed2b2670f23bd8b8beefc Mon Sep 17 00:00:00 2001 From: j1z0 Date: Sat, 20 Aug 2016 12:07:45 -0400 Subject: [PATCH 01/42] updated for upgrade to django1.9 --- .../django_ecommerce/contact/__init__.py | 0 .../chp05/django_ecommerce/contact/admin.py | 9 + .../chp05/django_ecommerce/contact/forms.py | 11 + .../chp05/django_ecommerce/contact/models.py | 18 + .../chp05/django_ecommerce/contact/tests.py | 36 + .../chp05/django_ecommerce/contact/views.py | 21 + .../django_ecommerce/__init__.py | 0 .../django_ecommerce/settings.py | 153 + .../django_ecommerce/django_ecommerce/urls.py | 17 + .../django_ecommerce/django_ecommerce/wsgi.py | 32 + .../chp05/django_ecommerce/main/__init__.py | 0 .../chp05/django_ecommerce/main/models.py | 3 + .../chp05/django_ecommerce/main/tests.py | 65 + .../chp05/django_ecommerce/main/views.py | 12 + _chapters/chp05/django_ecommerce/manage.py | 10 + .../django_ecommerce/payments/__init__.py | 0 .../chp05/django_ecommerce/payments/admin.py | 9 + .../chp05/django_ecommerce/payments/forms.py | 47 + .../chp05/django_ecommerce/payments/models.py | 30 + .../chp05/django_ecommerce/payments/tests.py | 413 + .../chp05/django_ecommerce/payments/views.py | 154 + .../django_ecommerce/static/application.js | 42 + .../django_ecommerce/static/bootstrap.css | 255 + .../chp05/django_ecommerce/static/jquery.js | 8981 +++++++++++++++++ .../django_ecommerce/static/jquery.min.js | 18 + .../django_ecommerce/static/jquery_ujs.js | 331 + .../django_ecommerce/templates/base.html | 117 + .../django_ecommerce/templates/cardform.html | 44 + .../django_ecommerce/templates/contact.html | 15 + .../django_ecommerce/templates/edit.html | 18 + .../django_ecommerce/templates/errors.html | 14 + .../django_ecommerce/templates/field.html | 6 + .../templates/flatpages/default.html | 5 + .../django_ecommerce/templates/home.html | 17 + .../django_ecommerce/templates/index.html | 49 + .../django_ecommerce/templates/register.html | 42 + .../django_ecommerce/templates/sign_in.html | 34 + .../django_ecommerce/templates/test.html | 8 + .../django_ecommerce/templates/user.html | 10 + _chapters/chp05/django_ecommerce/test.db | Bin 0 -> 51200 bytes _chapters/chp05/requirements.txt | 5 + .../contact/migrations/0001_initial.py | 30 + .../contact/migrations/__init__.py | 0 .../chp06/django_ecommerce/contact/models.py | 7 +- .../chp06/django_ecommerce/db_backup.json | 1 - .../django_ecommerce/settings.py | 37 +- .../django_ecommerce/django_ecommerce/urls.py | 13 +- _chapters/chp06/django_ecommerce/html1.html | 299 + _chapters/chp06/django_ecommerce/html2.html | 276 + _chapters/chp06/django_ecommerce/html3.html | 299 + _chapters/chp06/django_ecommerce/html4.html | 276 + _chapters/chp06/django_ecommerce/manage.py | 0 .../chp06/django_ecommerce/payments/forms.py | 5 +- .../chp06/django_ecommerce/payments/views.py | 25 +- _chapters/chp06/django_ecommerce/test.db | Bin 51200 -> 172032 bytes _chapters/chp06/requirements.txt | 4 +- .../chp06/tests/contact/testContactModels.py | 14 +- .../chp06/tests/main/testMainPageView.py | 7 +- .../chp06/tests/payments/testCustomer.py | 1 - _chapters/chp06/tests/payments/testForms.py | 34 +- .../chp06/tests/payments/testUserModel.py | 11 +- _chapters/chp06/tests/payments/testViews.py | 97 +- 62 files changed, 12369 insertions(+), 118 deletions(-) create mode 100644 _chapters/chp05/django_ecommerce/contact/__init__.py create mode 100644 _chapters/chp05/django_ecommerce/contact/admin.py create mode 100644 _chapters/chp05/django_ecommerce/contact/forms.py create mode 100644 _chapters/chp05/django_ecommerce/contact/models.py create mode 100644 _chapters/chp05/django_ecommerce/contact/tests.py create mode 100644 _chapters/chp05/django_ecommerce/contact/views.py create mode 100644 _chapters/chp05/django_ecommerce/django_ecommerce/__init__.py create mode 100644 _chapters/chp05/django_ecommerce/django_ecommerce/settings.py create mode 100644 _chapters/chp05/django_ecommerce/django_ecommerce/urls.py create mode 100644 _chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py create mode 100644 _chapters/chp05/django_ecommerce/main/__init__.py create mode 100644 _chapters/chp05/django_ecommerce/main/models.py create mode 100644 _chapters/chp05/django_ecommerce/main/tests.py create mode 100644 _chapters/chp05/django_ecommerce/main/views.py create mode 100755 _chapters/chp05/django_ecommerce/manage.py create mode 100644 _chapters/chp05/django_ecommerce/payments/__init__.py create mode 100644 _chapters/chp05/django_ecommerce/payments/admin.py create mode 100644 _chapters/chp05/django_ecommerce/payments/forms.py create mode 100644 _chapters/chp05/django_ecommerce/payments/models.py create mode 100644 _chapters/chp05/django_ecommerce/payments/tests.py create mode 100644 _chapters/chp05/django_ecommerce/payments/views.py create mode 100755 _chapters/chp05/django_ecommerce/static/application.js create mode 100755 _chapters/chp05/django_ecommerce/static/bootstrap.css create mode 100755 _chapters/chp05/django_ecommerce/static/jquery.js create mode 100755 _chapters/chp05/django_ecommerce/static/jquery.min.js create mode 100755 _chapters/chp05/django_ecommerce/static/jquery_ujs.js create mode 100644 _chapters/chp05/django_ecommerce/templates/base.html create mode 100644 _chapters/chp05/django_ecommerce/templates/cardform.html create mode 100644 _chapters/chp05/django_ecommerce/templates/contact.html create mode 100644 _chapters/chp05/django_ecommerce/templates/edit.html create mode 100644 _chapters/chp05/django_ecommerce/templates/errors.html create mode 100644 _chapters/chp05/django_ecommerce/templates/field.html create mode 100644 _chapters/chp05/django_ecommerce/templates/flatpages/default.html create mode 100644 _chapters/chp05/django_ecommerce/templates/home.html create mode 100644 _chapters/chp05/django_ecommerce/templates/index.html create mode 100644 _chapters/chp05/django_ecommerce/templates/register.html create mode 100644 _chapters/chp05/django_ecommerce/templates/sign_in.html create mode 100644 _chapters/chp05/django_ecommerce/templates/test.html create mode 100644 _chapters/chp05/django_ecommerce/templates/user.html create mode 100644 _chapters/chp05/django_ecommerce/test.db create mode 100644 _chapters/chp05/requirements.txt create mode 100644 _chapters/chp06/django_ecommerce/contact/migrations/0001_initial.py create mode 100644 _chapters/chp06/django_ecommerce/contact/migrations/__init__.py delete mode 100644 _chapters/chp06/django_ecommerce/db_backup.json create mode 100644 _chapters/chp06/django_ecommerce/html1.html create mode 100644 _chapters/chp06/django_ecommerce/html2.html create mode 100644 _chapters/chp06/django_ecommerce/html3.html create mode 100644 _chapters/chp06/django_ecommerce/html4.html mode change 100644 => 100755 _chapters/chp06/django_ecommerce/manage.py diff --git a/_chapters/chp05/django_ecommerce/contact/__init__.py b/_chapters/chp05/django_ecommerce/contact/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/_chapters/chp05/django_ecommerce/contact/admin.py b/_chapters/chp05/django_ecommerce/contact/admin.py new file mode 100644 index 0000000..ce92edb --- /dev/null +++ b/_chapters/chp05/django_ecommerce/contact/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from .models import ContactForm + + +class ContactFormAdmin(admin.ModelAdmin): + class Meta: + model = ContactForm + +admin.site.register(ContactForm, ContactFormAdmin) diff --git a/_chapters/chp05/django_ecommerce/contact/forms.py b/_chapters/chp05/django_ecommerce/contact/forms.py new file mode 100644 index 0000000..fd7230e --- /dev/null +++ b/_chapters/chp05/django_ecommerce/contact/forms.py @@ -0,0 +1,11 @@ +from django.forms import ModelForm +from .models import ContactForm +from django import forms + + +class ContactView(ModelForm): + message = forms.CharField(widget=forms.Textarea) + + class Meta: + fields = ['name', 'email', 'topic', 'message'] + model = ContactForm diff --git a/_chapters/chp05/django_ecommerce/contact/models.py b/_chapters/chp05/django_ecommerce/contact/models.py new file mode 100644 index 0000000..5b46b0b --- /dev/null +++ b/_chapters/chp05/django_ecommerce/contact/models.py @@ -0,0 +1,18 @@ +from django.db import models +import datetime + + +class ContactForm(models.Model): + name = models.CharField(max_length=150) + email = models.EmailField(max_length=250) + topic = models.CharField(max_length=200) + message = models.CharField(max_length=1000) + timestamp = models.DateTimeField( + auto_now_add=True, default=datetime.datetime.now + ) + + def __unicode__(self): + return self.email + + class Meta: + ordering = ['-timestamp'] diff --git a/_chapters/chp05/django_ecommerce/contact/tests.py b/_chapters/chp05/django_ecommerce/contact/tests.py new file mode 100644 index 0000000..30171a3 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/contact/tests.py @@ -0,0 +1,36 @@ +from django.test import TestCase, SimpleTestCase +from .models import ContactForm +from contact.forms import ContactView +from datetime import datetime, timedelta + + +class UserModelTest(TestCase): + + @classmethod + def setUpClass(cls): + super(UserModelTest, cls).setUpClass() + ContactForm(email="test@dummy.com", name="test").save() + ContactForm(email="j@j.com", name="jj").save() + cls.firstUser = ContactForm( + email="first@first.com", + name="first", + timestamp=datetime.today() + timedelta(days=2) + ) + cls.firstUser.save() + #cls.test_user=User(email="j@j.com", name ='test user') + #cls.test_user.save() + + def test_contactform_str_returns_email(self): + self.assertEquals("first@first.com", str(self.firstUser)) + + def test_ordering(self): + contacts = ContactForm.objects.all() + self.assertEquals(self.firstUser, contacts[0]) + +class ContactViewTests(SimpleTestCase): + + def test_displayed_fields(self): + expected_fields = ['name', 'email', 'topic', 'message'] + self.assertEquals(ContactView.Meta.fields, expected_fields) + + diff --git a/_chapters/chp05/django_ecommerce/contact/views.py b/_chapters/chp05/django_ecommerce/contact/views.py new file mode 100644 index 0000000..5125f2f --- /dev/null +++ b/_chapters/chp05/django_ecommerce/contact/views.py @@ -0,0 +1,21 @@ +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from .forms import ContactView +from django.contrib import messages + + +def contact(request): + if request.method == 'POST': + form = ContactView(request.POST) + if form.is_valid(): + our_form = form.save(commit=False) + our_form.save() + messages.add_message( + request, messages.INFO, 'Your message has been sent. Thank you.' + ) + return HttpResponseRedirect('/') + else: + form = ContactView() + t = loader.get_template('contact.html') + c = RequestContext(request, {'form': form, }) + return HttpResponse(t.render(c)) diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/__init__.py b/_chapters/chp05/django_ecommerce/django_ecommerce/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp05/django_ecommerce/django_ecommerce/settings.py new file mode 100644 index 0000000..45a743a --- /dev/null +++ b/_chapters/chp05/django_ecommerce/django_ecommerce/settings.py @@ -0,0 +1,153 @@ +# Django settings for django_ecommerce project. + +import os + +DEBUG = True +TEMPLATE_DEBUG = DEBUG +PROJECT_ROOT = os.path.realpath(os.path.dirname(__file__)) +SITE_ROOT = os.path.dirname(PROJECT_ROOT) +STRIPE_SECRET = 'sk_test_4QBquf6d5EzsnJC1fTI2GBGm' +STRIPE_PUBLISHABLE = 'pk_test_4QBqqGvCk9gaNn3pl1cwxcAS' + +ADMINS = ( + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test.db' + } +} + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [] + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# In a Windows environment this must be set to your system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/var/www/example.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://example.com/media/", "http://media.example.com/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/var/www/example.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://example.com/static/", "http://static.example.com/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = (os.path.join(SITE_ROOT, 'static'),) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '!(1ty%c5a)0l0(p)kxl2igmbobx_64hqh&tv1=+s9@!@zez4o^' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) + +ROOT_URLCONF = 'django_ecommerce.urls' + +# Python dotted path to the WSGI application used by Django's runserver. +WSGI_APPLICATION = 'django_ecommerce.wsgi.application' + +TEMPLATE_DIRS = (os.path.join(SITE_ROOT, 'templates'),) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'main', + 'django.contrib.admin', + 'django.contrib.flatpages', + 'contact', + 'payments', +) + +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp05/django_ecommerce/django_ecommerce/urls.py new file mode 100644 index 0000000..11db113 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/django_ecommerce/urls.py @@ -0,0 +1,17 @@ +from django.conf.urls import patterns, include, url +from payments import views +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + url(r'^admin/', include(admin.site.urls)), + url(r'^$', 'main.views.index', name='home'), + url(r'^pages/', include('django.contrib.flatpages.urls')), + url(r'^contact/', 'contact.views.contact', name='contact'), + + # user registration/authentication + url(r'^sign_in$', views.sign_in, name='sign_in'), + url(r'^sign_out$', views.sign_out, name='sign_out'), + url(r'^register$', views.register, name='register'), + url(r'^edit$', views.edit, name='edit'), +) diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py b/_chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py new file mode 100644 index 0000000..56314ee --- /dev/null +++ b/_chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py @@ -0,0 +1,32 @@ +""" +WSGI config for django_ecommerce project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks +# if running multiple sites in the same mod_wsgi process. To fix this, use +# mod_wsgi daemon mode with each site in its own daemon process, or use +# os.environ["DJANGO_SETTINGS_MODULE"] = "django_ecommerce.settings" +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_ecommerce.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application) diff --git a/_chapters/chp05/django_ecommerce/main/__init__.py b/_chapters/chp05/django_ecommerce/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/_chapters/chp05/django_ecommerce/main/models.py b/_chapters/chp05/django_ecommerce/main/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/main/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/_chapters/chp05/django_ecommerce/main/tests.py b/_chapters/chp05/django_ecommerce/main/tests.py new file mode 100644 index 0000000..1bac4ac --- /dev/null +++ b/_chapters/chp05/django_ecommerce/main/tests.py @@ -0,0 +1,65 @@ +from django.test import TestCase +from django.core.urlresolvers import resolve +from .views import index +from django.shortcuts import render_to_response +from payments.models import User +from django.test import RequestFactory +import mock + + +class MainPageTests(TestCase): + + ############### + #### Setup #### + ############### + + @classmethod + def setUpClass(cls): + super(MainPageTests, cls).setUpClass() + request_factory = RequestFactory() + cls.request = request_factory.get('/') + cls.request.session = {} + + ########################## + ##### Testing routes ##### + ########################## + + def test_root_resolves_to_main_view(self): + main_page = resolve('/') + self.assertEqual(main_page.func, index) + + def test_returns_appropriate_html_response_code(self): + resp = index(self.request) + self.assertEquals(resp.status_code, 200) + + ##################################### + #### Testing templates and views #### + ##################################### + + def test_returns_exact_html(self): + resp = index(self.request) + self.assertEquals( + resp.content, + render_to_response("index.html").content + ) + + def test_index_handles_logged_in_user(self): + # Create a session that appears to have a logged in user + self.request.session = {"user": "1"} + + with mock.patch('main.views.User') as user_mock: + + # Tell the mock what to do when called + config = {'get_by_id.return_value': mock.Mock()} + user_mock.configure_mock(**config) + + # Run the test + resp = index(self.request) + + # Ensure we return the state of the session back to normal + self.request.session = {} + + expected_html = render_to_response( + 'user.html', {'user': user_mock.get_by_id(1)} + ) + self.assertEquals(resp.content, expected_html.content) diff --git a/_chapters/chp05/django_ecommerce/main/views.py b/_chapters/chp05/django_ecommerce/main/views.py new file mode 100644 index 0000000..76a414e --- /dev/null +++ b/_chapters/chp05/django_ecommerce/main/views.py @@ -0,0 +1,12 @@ +from django.shortcuts import render_to_response +from payments.models import User + + +def index(request): + uid = request.session.get('user') + if uid is None: + return render_to_response('index.html') + else: + return render_to_response( + 'user.html', {'user': User.get_by_id(uid)} + ) diff --git a/_chapters/chp05/django_ecommerce/manage.py b/_chapters/chp05/django_ecommerce/manage.py new file mode 100755 index 0000000..2736e0e --- /dev/null +++ b/_chapters/chp05/django_ecommerce/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_ecommerce.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/_chapters/chp05/django_ecommerce/payments/__init__.py b/_chapters/chp05/django_ecommerce/payments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/_chapters/chp05/django_ecommerce/payments/admin.py b/_chapters/chp05/django_ecommerce/payments/admin.py new file mode 100644 index 0000000..763c430 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/payments/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from .models import User + + +class UserAdmin(admin.ModelAdmin): + class Meta: + model = User + +admin.site.register(User, UserAdmin) diff --git a/_chapters/chp05/django_ecommerce/payments/forms.py b/_chapters/chp05/django_ecommerce/payments/forms.py new file mode 100644 index 0000000..bdc5aab --- /dev/null +++ b/_chapters/chp05/django_ecommerce/payments/forms.py @@ -0,0 +1,47 @@ +from django import forms +from django.core.exceptions import NON_FIELD_ERRORS + + +class PaymentForm(forms.Form): + def addError(self, message): + self._errors[NON_FIELD_ERRORS] = self.error_class([message]) + + +class SigninForm(PaymentForm): + email = forms.EmailField(required=True) + password = forms.CharField( + required=True, widget=forms.PasswordInput(render_value=False) + ) + + +class CardForm(PaymentForm): + last_4_digits = forms.CharField( + required=True, + min_length=4, + max_length=4, + widget=forms.HiddenInput() + ) + stripe_token = forms.CharField(required=True, widget=forms.HiddenInput()) + + +class UserForm(CardForm): + name = forms.CharField(required=True) + email = forms.EmailField(required=True) + password = forms.CharField( + required=True, + label=(u'Password'), + widget=forms.PasswordInput(render_value=False) + ) + ver_password = forms.CharField( + required=True, + label=(u' Verify Password'), + widget=forms.PasswordInput(render_value=False) + ) + + def clean(self): + cleaned_data = self.cleaned_data + password = cleaned_data.get('password') + ver_password = cleaned_data.get('ver_password') + if password != ver_password: + raise forms.ValidationError('Passwords do not match') + return cleaned_data diff --git a/_chapters/chp05/django_ecommerce/payments/models.py b/_chapters/chp05/django_ecommerce/payments/models.py new file mode 100644 index 0000000..0fa7526 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/payments/models.py @@ -0,0 +1,30 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser + + +class User(AbstractBaseUser): + name = models.CharField(max_length=255) + email = models.CharField(max_length=255, unique=True) + #password field defined in base class + last_4_digits = models.CharField(max_length=4, blank=True, null=True) + stripe_id = models.CharField(max_length=255) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + USERNAME_FIELD = 'email' + + def __str__(self): + return self.email + + @classmethod + def get_by_id(cls, uid): + return User.objects.get(pk=uid) + + @classmethod + def create(cls, name, email, password, last_4_digits, stripe_id): + new_user = cls(name=name, email=email, + last_4_digits=last_4_digits, stripe_id=stripe_id) + new_user.set_password(password) + + new_user.save() + return new_user diff --git a/_chapters/chp05/django_ecommerce/payments/tests.py b/_chapters/chp05/django_ecommerce/payments/tests.py new file mode 100644 index 0000000..5fa5a9a --- /dev/null +++ b/_chapters/chp05/django_ecommerce/payments/tests.py @@ -0,0 +1,413 @@ +from django.test import TestCase, RequestFactory +from django.shortcuts import render_to_response +from django.db import IntegrityError +import django_ecommerce.settings as settings +from payments.views import soon, register, Customer +from payments.models import User +from payments.forms import SigninForm, UserForm, CardForm +import unittest +import mock + + +######################## +#### Testing Models #### +######################## + + +class UserModelTest(TestCase): + + @classmethod + def setUpClass(cls): + super(UserModelTest, cls).setUpClass() + cls.test_user = User(email="j@j.com", name='test user') + cls.test_user.save() + + def test_user_to_string_print_email(self): + self.assertEquals(str(self.test_user), "j@j.com") + + def test_get_by_id(self): + self.assertEquals(User.get_by_id(1), self.test_user) + + def test_create_user_function_stores_in_database(self): + user = User.create("test", "test@t.com", "tt", "1234", "22") + self.assertEquals(User.objects.get(email="test@t.com"), user) + + def test_create_user_allready_exists_throws_IntegrityError(self): + from django.db import IntegrityError + self.assertRaises( + IntegrityError, + User.create, + "test user", + "j@j.com", + "jj", + "1234", + 89 + ) + + +####################### +#### Testing Forms #### +####################### + +class FormTesterMixin(): + + def assertFormError(self, form_cls, expected_error_name, + expected_error_msg, data): + + from pprint import pformat + test_form = form_cls(data=data) + #if we get an error then the form should not be valid + self.assertFalse(test_form.is_valid()) + + self.assertEquals( + test_form.errors[expected_error_name], + expected_error_msg, + msg="Expected {} : Actual {} : using data {}".format( + test_form.errors[expected_error_name], + expected_error_msg, pformat(data) + ) + ) + + +class FormTests(unittest.TestCase, FormTesterMixin): + + def test_signin_form_data_validation_for_invalid_data(self): + invalid_data_list = [ + {'data': {'email': 'j@j.com'}, + 'error': ('password', [u'This field is required.'])}, + {'data': {'password': '1234'}, + 'error': ('email', [u'This field is required.'])} + ] + + for invalid_data in invalid_data_list: + self.assertFormError(SigninForm, + invalid_data['error'][0], + invalid_data['error'][1], + invalid_data["data"]) + + def test_user_form_passwords_match(self): + form = UserForm( + { + 'name': 'jj', + 'email': 'j@j.com', + 'password': '1234', + 'ver_password': '1234', + 'last_4_digits': '3333', + 'stripe_token': '1'} + ) + # Is the data valid? + if form.is_valid(): + # Is the data clean? + self.assertTrue(form.cleaned_data) + + def test_user_form_passwords_dont_match_throws_error(self): + form = UserForm( + { + 'name': 'jj', + 'email': 'j@j.com', + 'password': '234', + 'ver_password': '1234', # bad password + 'last_4_digits': '3333', + 'stripe_token': '1' + } + ) + + # Is the data valid? + self.assertFalse(form.is_valid()) + + def test_card_form_data_validation_for_invalid_data(self): + invalid_data_list = [ + { + 'data': {'last_4_digits': '123'}, + 'error': ( + 'last_4_digits', + [u'Ensure this value has at least 4 characters (it has 3).'] + ) + }, + { + 'data': {'last_4_digits': '12345'}, + 'error': ( + 'last_4_digits', + [u'Ensure this value has at most 4 characters (it has 5).'] + ) + } + ] + + for invalid_data in invalid_data_list: + self.assertFormError( + CardForm, + invalid_data['error'][0], + invalid_data['error'][1], + invalid_data["data"] + ) + + +########################## +##### Testing routes ##### +########################## + +from .views import sign_in, sign_out +from django.core.urlresolvers import resolve +from django.template import RequestContext + +class ViewTesterMixin(object): + + @classmethod + def setupViewTester(cls, url, view_func, expected_html_path, + expected_html_context, + status_code=200, + session={}): + request_factory = RequestFactory() + cls.request = request_factory.get(url) + cls.request.session = session + + cls.status_code = status_code + cls.url = url + cls.view_func = staticmethod(view_func) + + expected_html = "" + if expected_html_path and expected_html_context: + response = render_to_response( + expected_html_path, + expected_html_context, + context_instance=RequestContext(cls.request) + ) + expected_html = response.content + cls.expected_html = expected_html + + def test_resolves_to_correct_view(self): + test_view = resolve(self.url) + self.assertEquals(test_view.func, self.view_func) + + def test_returns_appropriate_respose_code(self): + resp = self.view_func(self.request) + self.assertEquals(resp.status_code, self.status_code) + + def test_returns_correct_html(self): + import pdb; pdb.set_trace() + resp = self.view_func(self.request) + self.assertEquals(resp.content, self.expected_html) + + +class SignInPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super(SignInPageTests, cls).setUpClass() + page = 'sign_in.html' + context = { + 'form': SigninForm(), + 'user': None + } + + ViewTesterMixin.setupViewTester( + '/sign_in', + sign_in, + page, + context, + ) + + +class SignOutPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super(SignOutPageTests, cls).setUpClass() + ViewTesterMixin.setupViewTester( + '/sign_out', + sign_out, + None, None, # a redirect will return no html + status_code=302, + session={"user": "dummy"}, + ) + + def setUp(self): + #sign_out clears the session, so let's reset it everytime + self.request.session = {"user": "dummy"} + + +class RegisterPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super(RegisterPageTests, cls).setUpClass() + page = 'register.html' + context = { + 'form': UserForm(), + 'months': range(1, 12), + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'user': None, + 'years': range(2011, 2036), + } + ViewTesterMixin.setupViewTester( + '/register', + register, + page, + context, + session={"user": "dummy"}, + ) + + def setUp(self): + pass + #request_factory = RequestFactory() + #self.request = request_factory.get(self.url) + + def test_invalid_form_returns_registration_page(self): + + with mock.patch('payments.forms.UserForm.is_valid') as user_mock: + + user_mock.return_value = False + + self.request.method = 'POST' + self.request.POST = None + resp = register(self.request) + self.assertEquals(resp.content, self.expected_html) + + # make sure that we did indeed call our is_valid function + self.assertEquals(user_mock.call_count, 1) + + @mock.patch('payments.views.Customer.create') + @mock.patch.object(User, 'create') + def test_registering_new_user_returns_succesfully( + self, create_mock, stripe_mock + ): + + self.request.session = {} + self.request.method = 'POST' + self.request.POST = { + 'email': 'python@rocks.com', + 'name': 'pyRock', + 'stripe_token': '...', + 'last_4_digits': '4242', + 'password': 'bad_password', + 'ver_password': 'bad_password', + } + + #get the return values of the mocks, for our checks later + new_user = create_mock.return_value + new_cust = stripe_mock.return_value + + resp = register(self.request) + + self.assertEquals(resp.content, "") + self.assertEquals(resp.status_code, 302) + self.assertEquals(self.request.session['user'], new_user.pk) + #verify the user was actually stored in the database. + create_mock.assert_called_with( + 'pyRock', 'python@rocks.com', 'bad_password', '4242', new_cust.id + ) + + # old test + # def test_registering_new_user_returns_succesfully(self): + + # self.request.session = {} + # self.request.method = 'POST' + # self.request.POST = { + # 'email': 'python@rocks.com', + # 'name': 'pyRock', + # 'stripe_token': '...', + # 'last_4_digits': '4242', + # 'password': 'bad_password', + # 'ver_password': 'bad_password', + # } + + # with mock.patch('stripe.Customer') as stripe_mock: + + # config = {'create.return_value': mock.Mock()} + # stripe_mock.configure_mock(**config) + + # resp = register(self.request) + # self.assertEquals(resp.content, "") + # self.assertEquals(resp.status_code, 302) + # self.assertEquals(self.request.session['user'], 1) + + # # verify the user was actually stored in the database. + # # if the user is not there this will throw an error + # User.objects.get(email='python@rocks.com') + + def get_MockUserForm(self): + from django import forms + + class MockUserForm(forms.Form): + + def is_valid(self): + return True + + @property + def cleaned_data(self): + return { + 'email': 'python@rocks.com', + 'name': 'pyRock', + 'stripe_token': '...', + 'last_4_digits': '4242', + 'password': 'bad_password', + 'ver_password': 'bad_password', + } + + def addError(self, error): + pass + + return MockUserForm() + + @mock.patch('payments.views.UserForm', get_MockUserForm) + @mock.patch('payments.models.User.save', side_effect=IntegrityError) + def test_registering_user_twice_cause_error_msg(self, save_mock): + + #create the request used to test the view + self.request.session = {} + self.request.method = 'POST' + self.request.POST = {} + + #create the expected html + html = render_to_response( + 'register.html', + { + 'form': self.get_MockUserForm(), + 'months': list(range(1, 12)), + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'user': None, + 'years': list(range(2011, 2036)), + }, + context_instance=RequestContext(self.request) + ) + + #mock out stripe so we don't hit their server + with mock.patch('payments.views.Customer') as stripe_mock: + + config = {'create.return_value': mock.Mock()} + stripe_mock.configure_mock(**config) + + #run the test + resp = register(self.request) + + #verify that we did things correctly + self.assertEqual(resp.content, html.content) + self.assertEqual(resp.status_code, 200) + self.assertEqual(self.request.session, {}) + + #assert there is no records in the database. + users = User.objects.filter(email="python@rocks.com") + self.assertEqual(len(users), 0) + + +class CustomerTests(TestCase): + + def test_create_subscription(self): + with mock.patch('stripe.Customer.create') as create_mock: + cust_data = {'description': 'test user', 'email': 'test@test.com', + 'card': '4242', 'plan': 'gold'} + Customer.create("subscription", **cust_data) + + create_mock.assert_called_with(**cust_data) + + def test_create_one_time_bill(self): + with mock.patch('stripe.Charge.create') as charge_mock: + cust_data = {'description': 'email', + 'card': '1234', + 'amount': '5000', + 'currency': 'usd'} + Customer.create("one_time", **cust_data) + + charge_mock.assert_called_with(**cust_data) diff --git a/_chapters/chp05/django_ecommerce/payments/views.py b/_chapters/chp05/django_ecommerce/payments/views.py new file mode 100644 index 0000000..1ab1287 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/payments/views.py @@ -0,0 +1,154 @@ +from django.db import IntegrityError +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext +from payments.forms import SigninForm, CardForm, UserForm +from payments.models import User +import django_ecommerce.settings as settings +import stripe +import datetime + +stripe.api_key = settings.STRIPE_SECRET + + +def soon(): + soon = datetime.date.today() + datetime.timedelta(days=30) + return {'month': soon.month, 'year': soon.year} + + +def sign_in(request): + user = None + if request.method == 'POST': + form = SigninForm(request.POST) + if form.is_valid(): + results = User.objects.filter(email=form.cleaned_data['email']) + if len(results) == 1: + if results[0].check_password(form.cleaned_data['password']): + request.session['user'] = results[0].pk + return HttpResponseRedirect('/') + else: + form.addError('Incorrect email address or password') + else: + form.addError('Incorrect email address or password') + else: + form = SigninForm() + + print form.non_field_errors() + + return render_to_response( + 'sign_in.html', + { + 'form': form, + 'user': user + }, + context_instance=RequestContext(request) + ) + + +def sign_out(request): + try: + del request.session['user'] + except KeyError: + pass + return HttpResponseRedirect('/') + + +def register(request): + user = None + if request.method == 'POST': + form = UserForm(request.POST) + if form.is_valid(): + + #update based on your billing method (subscription vs one time) + customer = Customer.create( + email=form.cleaned_data['email'], + description=form.cleaned_data['name'], + card=form.cleaned_data['stripe_token'], + plan="gold", + ) + # customer = stripe.Charge.create( + # description=form.cleaned_data['email'], + # card=form.cleaned_data['stripe_token'], + # amount="5000", + # currency="usd" + # ) + + cd = form.cleaned_data + try: + user = User.create( + cd['name'], + cd['email'], + cd['password'], + cd['last_4_digits'], + customer.id + ) + except IntegrityError: + form.addError(cd['email'] + ' is already a member') + user = None + else: + request.session['user'] = user.pk + return HttpResponseRedirect('/') + + else: + form = UserForm() + + return render_to_response( + 'register.html', + { + 'form': form, + 'months': range(1, 12), + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'user': user, + 'years': range(2011, 2036), + }, + context_instance=RequestContext(request) + ) + + +def edit(request): + uid = request.session.get('user') + + if uid is None: + return HttpResponseRedirect('/') + + user = User.objects.get(pk=uid) + + if request.method == 'POST': + form = CardForm(request.POST) + if form.is_valid(): + + customer = stripe.Customer.retrieve(user.stripe_id) + customer.card = form.cleaned_data['stripe_token'] + customer.save() + + user.last_4_digits = form.cleaned_data['last_4_digits'] + user.stripe_id = customer.id + user.save() + + return HttpResponseRedirect('/') + + else: + form = CardForm() + + return render_to_response( + 'edit.html', + { + 'form': form, + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'months': range(1, 12), + 'years': range(2011, 2036) + }, + context_instance=RequestContext(request) + ) + + +class Customer(object): + + @classmethod + def create(cls, billing_method="subscription", **kwargs): + if billing_method == "subscription": + return stripe.Customer.create(**kwargs) + elif billing_method == "one_time": + return stripe.Charge.create(**kwargs) diff --git a/_chapters/chp05/django_ecommerce/static/application.js b/_chapters/chp05/django_ecommerce/static/application.js new file mode 100755 index 0000000..925fdda --- /dev/null +++ b/_chapters/chp05/django_ecommerce/static/application.js @@ -0,0 +1,42 @@ +$(function() { + + $("#user_form").submit(function() { + if ( $("#credit-card").is(":visible")) { + var form = this; + var card = { + number: $("#credit_card_number").val(), + expMonth: $("#expiry_month").val(), + expYear: $("#expiry_year").val(), + cvc: $("#cvv").val() + }; + + Stripe.createToken(card, function(status, response) { + if (status === 200) { + console.log(status, response); + $("#credit-card-errors").hide(); + $("#last_4_digits").val(response.card.last4); + $("#stripe_token").val(response.id); + form.submit(); + } else { + $("#stripe-error-message").text(response.error.message); + $("#credit-card-errors").show(); + $("#user_submit").attr("disabled", false); + } + }); + + return false; + + } + + return true + + }); + + $("#change-card a").click(function() { + $("#change-card").hide(); + $("#credit-card").show(); + $("#credit_card_number").focus(); + return false; + }); + +}); diff --git a/_chapters/chp05/django_ecommerce/static/bootstrap.css b/_chapters/chp05/django_ecommerce/static/bootstrap.css new file mode 100755 index 0000000..7bdb08a --- /dev/null +++ b/_chapters/chp05/django_ecommerce/static/bootstrap.css @@ -0,0 +1,255 @@ +html,body{margin:0;padding:0;} +h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;} +table{border-collapse:collapse;border-spacing:0;} +ol,ul{list-style:none;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted;} +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;} +button,input{line-height:normal;*overflow:visible;} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +table{border-collapse:collapse;border-spacing:0;} +.clearfix{zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} +.clearfix:after{clear:both;} +.center-block{display:block;margin:0 auto;} +.container{width:940px;margin:0 auto;zoom:1;margin-bottom:18px;}.container:before,.container:after{display:table;content:"";} +.container:after{clear:both;} +.btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;} +.btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.row{zoom:1;margin-bottom:18px;margin-left:-20px;}.row:before,.row:after{display:table;content:"";} +.row:after{clear:both;} +.row .span1,.row .span2,.row .span3,.row .span4,.row .span5,.row .span6,.row .span7,.row .span8,.row .span9,.row .span10,.row .span11,.row .span12,.row .span13,.row .span14,.row .span15,.row .span16{display:inline;float:left;margin-left:20px;} +.row .span1{width:40px;} +.row .span2{width:100px;} +.row .span3{width:160px;} +.row .span4{width:220px;} +.row .span5{width:280px;} +.row .span6{width:340px;} +.row .span7{width:400px;} +.row .span8{width:460px;} +.row .span9{width:520px;} +.row .span10{width:580px;} +.row .span11{width:640px;} +.row .span12{width:700px;} +.row .span13{width:760px;} +.row .span14{width:820px;} +.row .span15{width:880px;} +.row .span16{width:940px;} +.row .offset1{margin-left:80px;} +.row .offset2{margin-left:140px;} +.row .offset3{margin-left:200px;} +.row .offset4{margin-left:260px;} +.row .offset5{margin-left:320px;} +.row .offset6{margin-left:380px;} +.row .offset7{margin-left:440px;} +.row .offset8{margin-left:500px;} +.row .offset9{margin-left:560px;} +.row .offset10{margin-left:620px;} +.row .offset11{margin-left:680px;} +.row .offset12{margin-left:740px;} +html,body{background-color:#fff;} +body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#808080;text-rendering:optimizeLegibility;} +.container{width:940px;margin:0 auto;} +.container-fluid{padding:0 20px;zoom:1;margin-bottom:18px;}.container-fluid:before,.container-fluid:after{display:table;content:"";} +.container-fluid:after{clear:both;} +.container-fluid .sidebar{float:left;width:220px;} +.container-fluid .content{min-width:700px;max-width:1180px;margin-left:240px;} +a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#0050a3;text-decoration:underline;} +.btn{display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(0.25, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-moz-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);padding:4px 14px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:18px;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;} +.primary{background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);color:#fff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border:1px solid #004b9a;border-bottom-color:#003f81;}.primary:hover{color:#fff;} +.btn{-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;transition:0.1s linear all;}.btn.primary{color:#fff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}.btn.primary:hover{color:#fff;} +.btn.large{font-size:16px;line-height:28px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.btn.small{padding-right:9px;padding-left:9px;font-size:11px;} +.btn.disabled{background-image:none;filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn:disabled{background-image:none;filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.btn:disabled.primary{color:#fff;} +.btn:active{-webkit-box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);} +button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;} +p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;} +h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;} +h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;} +h3,h4,h5,h6{line-height:36px;} +h3{font-size:18px;}h3 small{font-size:14px;} +h4{font-size:16px;}h4 small{font-size:12px;} +h5{font-size:14px;} +h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;} +ul,ol{margin:0 0 18px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;color:#808080;} +ul.unstyled{list-style:none;margin-left:0;} +dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;} +dl dt{font-weight:bold;} +dl dd{margin-left:9px;} +hr{margin:0 0 19px;border:0;border-bottom:1px solid #eee;} +strong{font-style:inherit;font-weight:bold;line-height:inherit;} +em{font-style:italic;font-weight:inherit;line-height:inherit;} +.muted{color:#bfbfbf;} +blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;} +blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';} +address{display:block;line-height:18px;margin-bottom:18px;} +code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;} +pre{background-color:#f5f5f5;display:block;padding:17px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;} +form{margin-bottom:18px;} +fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;margin-left:150px;font-size:20px;line-height:1;*margin:0 0 5px 145px;*line-height:1.5;color:#404040;} +.clearfix{margin-bottom:18px;} +label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;} +label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;} +div.input{margin-left:150px;} +input[type=checkbox],input[type=radio]{cursor:pointer;} +input[type=text],input[type=password],textarea,select,.uneditable-input{display:inline-block;width:210px;padding:4px;font-size:13px;line-height:18px;height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +select,input[type=file]{height:27px;line-height:27px;} +textarea{height:auto;} +.uneditable-input{background-color:#eee;display:block;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.075);} +:-moz-placeholder{color:#bfbfbf;} +::-webkit-input-placeholder{color:#bfbfbf;} +input[type=text],input[type=password],select,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);} +input[type=text]:focus,input[type=password]:focus,textarea:focus{outline:none;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);} +form div.error{background:#fae5e3;padding:10px 0;margin:-10px 0 10px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}form div.error>label,form div.error span.help-inline,form div.error span.help-block{color:#9d261d;} +form div.error input[type=text],form div.error input[type=password],form div.error textarea{border-color:#c87872;-webkit-box-shadow:0 0 3px rgba(171, 41, 32, 0.25);-moz-box-shadow:0 0 3px rgba(171, 41, 32, 0.25);box-shadow:0 0 3px rgba(171, 41, 32, 0.25);}form div.error input[type=text]:focus,form div.error input[type=password]:focus,form div.error textarea:focus{border-color:#b9554d;-webkit-box-shadow:0 0 6px rgba(171, 41, 32, 0.5);-moz-box-shadow:0 0 6px rgba(171, 41, 32, 0.5);box-shadow:0 0 6px rgba(171, 41, 32, 0.5);} +form div.error .input-prepend span.add-on,form div.error .input-append span.add-on{background:#f4c8c5;border-color:#c87872;color:#b9554d;} +.input-mini,input.mini,textarea.mini,select.mini{width:60px;} +.input-small,input.small,textarea.small,select.small{width:90px;} +.input-medium,input.medium,textarea.medium,select.medium{width:150px;} +.input-large,input.large,textarea.large,select.large{width:210px;} +.input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;} +.input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;} +textarea.xxlarge{overflow-y:scroll;} +input[readonly]:focus,textarea[readonly]:focus,input.disabled{background:#f5f5f5;border-color:#ddd;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;} +.help-inline,.help-block{font-size:12px;line-height:18px;color:#bfbfbf;} +.help-inline{padding-left:5px;*position:relative;*top:-5px;} +.help-block{display:block;max-width:600px;} +.inline-inputs{color:#808080;}.inline-inputs span,.inline-inputs input[type=text]{display:inline-block;} +.inline-inputs input.mini{width:60px;} +.inline-inputs input.small{width:90px;} +.inline-inputs span{padding:0 2px 0 1px;} +.input-prepend input[type=text],.input-append input[type=text],.input-prepend input[type=password],.input-append input[type=password]{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend .add-on,.input-append .add-on{background:#f5f5f5;float:left;display:block;width:auto;min-width:16px;padding:4px 4px 4px 5px;color:#bfbfbf;font-weight:normal;line-height:18px;height:18px;text-align:center;text-shadow:0 1px 0 #fff;border:1px solid #ccc;border-right-width:0;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;} +.input-prepend .add-on{*margin-top:1px;} +.input-append input[type=text],.input-append input[type=password]{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;border-right-width:1px;border-left-width:0;} +.inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;}.inputs-list li label{display:block;float:none;width:auto;padding:0;line-height:18px;text-align:left;white-space:normal;}.inputs-list li label strong{color:#808080;} +.inputs-list li label small{font-size:12px;font-weight:normal;} +.inputs-list li ul.inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;} +.inputs-list li:first-child{padding-top:5px;} +.inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;} +.form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;} +.form-stacked legend{margin-left:0;} +.form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;} +.form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;} +.form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;} +.form-stacked div.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;} +.form-stacked .actions{margin-left:-20px;padding-left:20px;} +table{width:100%;margin-bottom:18px;padding:0;border-collapse:separate;font-size:13px;}table th,table td{padding:10px 10px 9px;line-height:13.5px;text-align:left;vertical-align:middle;border-bottom:1px solid #ddd;} +table th{padding-top:9px;font-weight:bold;border-bottom-width:2px;} +.zebra-striped tbody tr:nth-child(odd) td{background-color:#f9f9f9;} +.zebra-striped tbody tr:hover td{background-color:#f5f5f5;} +.zebra-striped .header{cursor:pointer;}.zebra-striped .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;} +.zebra-striped .headerSortUp,.zebra-striped .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;} +.zebra-striped .header:hover:after{visibility:visible;} +.zebra-striped .headerSortDown:after,.zebra-striped .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +.zebra-striped .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +table .blue{color:#049cdb;border-bottom-color:#049cdb;} +table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;} +table .green{color:#46a546;border-bottom-color:#46a546;} +table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;} +table .red{color:#9d261d;border-bottom-color:#9d261d;} +table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;} +table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;} +table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;} +table .orange{color:#f89406;border-bottom-color:#f89406;} +table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;} +table .purple{color:#7a43b6;border-bottom-color:#7a43b6;} +table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;} +.topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar .fill{background:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.topbar a:hover,.topbar ul li.active a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;} +.topbar h3{position:relative;}.topbar h3 a{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;} +.topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}.topbar form input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;width:220px;padding:4px 9px;color:#fff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transition:none;-moz-transition:none;transition:none;}.topbar form input:-moz-placeholder{color:#e6e6e6;} +.topbar form input::-webkit-input-placeholder{color:#e6e6e6;} +.topbar form input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#fff;} +.topbar form input:focus,.topbar form input.focused{outline:none;background-color:#fff;color:#404040;text-shadow:0 1px 0 #fff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);} +.topbar ul{display:block;float:left;margin:0 10px 0 0;position:relative;}.topbar ul.secondary-nav{float:right;margin-left:10px;margin-right:0;} +.topbar ul li{display:block;float:left;font-size:13px;}.topbar ul li a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar ul li a:hover{color:#fff;text-decoration:none;} +.topbar ul li.active a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);} +.topbar ul.primary-nav li ul{left:0;} +.topbar ul.secondary-nav li ul{right:0;} +.topbar ul li.menu{position:relative;}.topbar ul li.menu a.menu:after{width:0px;height:0px;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #fff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;} +.topbar ul li.menu.open a.menu,.topbar ul li.menu.open a:hover{background-color:#444;background-color:rgba(255, 255, 255, 0.1);*background-color:#444;color:#fff;} +.topbar ul li.menu.open ul{display:block;}.topbar ul li.menu.open ul li a{background-color:transparent;font-weight:normal;}.topbar ul li.menu.open ul li a:hover{background-color:rgba(255, 255, 255, 0.1);*background-color:#444;color:#fff;} +.topbar ul li.menu.open ul li.active a{background-color:rgba(255, 255, 255, 0.1);font-weight:bold;} +.topbar ul li ul{background-color:#333;float:left;display:none;position:absolute;top:40px;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:0;text-align:left;border:0;zoom:1;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.6);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.6);box-shadow:0 1px 2px rgba(0, 0, 0, 0.6);}.topbar ul li ul li{float:none;clear:both;display:block;background:none;font-size:12px;}.topbar ul li ul li a{display:block;padding:6px 15px;clear:both;font-weight:normal;line-height:19px;color:#bbb;}.topbar ul li ul li a:hover{background-color:#333;background-color:rgba(255, 255, 255, 0.25);color:#fff;} +.topbar ul li ul li.divider{height:1px;overflow:hidden;background:#222;background:rgba(0, 0, 0, 0.2);border-bottom:1px solid rgba(255, 255, 255, 0.1);margin:5px 0;} +.topbar ul li ul li span{clear:both;display:block;background:rgba(0, 0, 0, 0.2);padding:6px 15px;cursor:default;color:#808080;border-top:1px solid rgba(0, 0, 0, 0.2);} +.hero-unit{background-color:#f5f5f5;margin-top:60px;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} +footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;} +.page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;} +.btn{cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(0.25, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-moz-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 0.25, #e6e6e6);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;} +.btn.primary{color:#fff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.disabled{cursor:default;background-image:none;filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;} +.btn:disabled{cursor:default;background-image:none;filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;} +.btn:active{-webkit-box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.1),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.1),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 3px 7px rgba(0, 0, 0, 0.1),0 1px 2px rgba(0, 0, 0, 0.05);} +.btn.large{font-size:16px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.btn.small{padding:7px 9px 7px;font-size:11px;} +button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;} +.alert-message{background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);margin-bottom:18px;padding:7px 14px;color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message h5{line-height:18px;} +.alert-message p{margin-bottom:0;} +.alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;} +.alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);} +.alert-message .close{float:right;margin-top:-2px;color:#000000;font-size:20px;font-weight:bold;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);-khtml-opacity:0.2;-moz-opacity:0.2;opacity:0.2;}.alert-message .close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;} +.alert-message.block-message{background-image:none;background-color:#fdf5d9;padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message p{margin-right:30px;} +.alert-message.block-message .alert-actions{margin-top:5px;} +.alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;} +.alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;} +.alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;} +.tabs,.pills{margin:0 0 20px;padding:0;zoom:1;margin-bottom:18px;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";} +.tabs:after,.pills:after{clear:both;} +.tabs li,.pills li{display:inline;}.tabs li a,.pills li a{float:left;width:auto;} +.tabs{width:100%;border-bottom:1px solid #bfbfbf;}.tabs li a{margin-bottom:-1px;margin-right:2px;padding:0 15px;line-height:35px;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}.tabs li a:hover{background-color:#e6e6e6;border-bottom:1px solid #bfbfbf;} +.tabs li.active a{background-color:#fff;padding:0 14px;border:1px solid #ccc;border-bottom:0;color:#808080;} +.pills li a{margin:5px 3px 5px 0;padding:0 15px;text-shadow:0 1px 1px #fff;line-height:30px;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills li a:hover{background:#0050a3;color:#fff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);} +.pills li.active a{background:#0069d6;color:#fff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);} +.pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}.pagination ul li{display:inline;}.pagination ul li a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;} +.pagination ul li a:hover,.pagination ul li.active a{background-color:#c7eefe;} +.pagination ul li.disabled a,.pagination ul li.disabled a:hover{background-color:transparent;color:#bfbfbf;} +.pagination ul li.next a{border:0;} +.well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);} +.modal-backdrop{background-color:rgba(0, 0, 0, 0.5);position:fixed;top:0;left:0;right:0;bottom:0;z-index:1000;} +.modal{position:fixed;top:50%;left:50%;z-index:2000;width:560px;margin:-280px 0 0 -250px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .modal-header{border-bottom:1px solid #eee;padding:5px 20px;}.modal .modal-header .close{position:absolute;right:10px;top:10px;color:#999;line-height:10px;font-size:18px;} +.modal .modal-body{padding:20px;} +.modal .modal-footer{background-color:#f5f5f5;padding:14px 20px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:18px;margin-bottom:0;}.modal .modal-footer:before,.modal .modal-footer:after{display:table;content:"";} +.modal .modal-footer:after{clear:both;} +.modal .modal-footer .btn{float:right;margin-left:10px;} +.twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.twipsy .twipsy-inner{padding:3px 8px;background-color:#000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.twipsy .twipsy-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover .inner{background-color:#333;background-color:rgba(0, 0, 0, 0.8);*background-color:#333;padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;} +.popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/static/jquery.js b/_chapters/chp05/django_ecommerce/static/jquery.js new file mode 100755 index 0000000..f3201aa --- /dev/null +++ b/_chapters/chp05/django_ecommerce/static/jquery.js @@ -0,0 +1,8981 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z])/ig, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.6.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return (new Function( "return " + data ))(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + + if ( indexOf ) { + return indexOf.call( array, elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + pipe: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action ]( returned ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + }); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + testElementParent, + testElement, + testElementStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains it's value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + body = document.getElementsByTagName( "body" )[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Remove the body element we added + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery.data( elem, deferDataKey, undefined, true ); + if ( defer && + ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && + ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery.data( elem, queueDataKey, undefined, true ) && + !jQuery.data( elem, markDataKey, undefined, true ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.resolve(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + if ( count ) { + jQuery.data( elem, key, count, true ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + if ( elem ) { + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type, undefined, true ); + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + defer; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + rinvalidChar = /\:|^on/, + formHook, boolHook; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = (value || "").split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return undefined; + } + + var isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex" + }, + + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + if ( notxml ) { + name = jQuery.attrFix[ name ] || name; + + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) ) { + + hooks = boolHook; + + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, name ) { + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabIndex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return value; + } + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Event object or event type + var type = event.type || event, + namespaces = [], + exclusive; + + if ( type.indexOf("!") >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.exclusive = exclusive; + event.namespace = namespaces.join("."); + event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle a global trigger + if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + return; + } + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + var cur = elem, + // IE doesn't like method names with a colon (#3533, #8272) + ontype = type.indexOf(":") < 0 ? "on" + type : ""; + + // Fire event on the current element, then bubble up the DOM tree + do { + var handle = jQuery._data( cur, "handle" ); + + event.currentTarget = cur; + if ( handle ) { + handle.apply( cur, data ); + } + + // Trigger an inline bound script + if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { + event.result = false; + event.preventDefault(); + } + + // Bubble up to document, then to window + cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; + } while ( cur && !event.isPropagationStopped() ); + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + var old, + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction)() check here because IE6/7 fails that test. + // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. + try { + if ( ontype && elem[ type ] ) { + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + jQuery.event.triggered = type; + elem[ type ](); + } + } catch ( ieError ) {} + + if ( old ) { + elem[ ontype ] = old; + } + + jQuery.event.triggered = undefined; + } + } + + return event.result; + }, + + handle: function( event ) { + event = jQuery.event.fix( event || window.event ); + // Snapshot the handlers list since a called handler may add/remove events. + var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + run_all = !event.exclusive && !event.namespace, + args = Array.prototype.slice.call( arguments, 0 ); + + // Use the fix-ed Event rather than the (read-only) native event + args[0] = event; + event.currentTarget = this; + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Triggered event must 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event. + if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + + // Check if mouse(over|out) are still within the same parent element + var related = event.relatedTarget, + inside = false, + eventType = event.type; + + event.type = event.data; + + if ( related !== this ) { + + if ( related ) { + inside = jQuery.contains( this, related ); + } + + if ( !inside ) { + + jQuery.event.handle.apply( this, arguments ); + + event.type = eventType; + } + } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( !jQuery.nodeName( this, "form" ) ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( jQuery.nodeName( elem, "select" ) ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( arguments.length === 2 || data === false ) { + fn = data; + data = undefined; + } + + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( name === "die" && !types && + origSelector && origSelector.charAt(0) === "." ) { + + context.unbind( origSelector ); + + return this; + } + + if ( data === false || jQuery.isFunction( data ) ) { + fn = data || returnFalse; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( liveMap[ type ] ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + + // Make sure not to accidentally match a child element with the same selector + if ( related && jQuery.contains( elem, related ) ) { + related = elem; + } + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[ selector ]; + + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + {% block extrahead %} + {% endblock %} + + + + + + + + + +
+ +
+ +

Your MVP!

+
+ +
+ + {% if messages %} +
+
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+
+ {% endif %} + +
+ {% block content %} + {% endblock %} +
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/cardform.html b/_chapters/chp05/django_ecommerce/templates/cardform.html new file mode 100644 index 0000000..d8ae6e7 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/cardform.html @@ -0,0 +1,44 @@ + + + + +
+ +
diff --git a/_chapters/chp05/django_ecommerce/templates/contact.html b/_chapters/chp05/django_ecommerce/templates/contact.html new file mode 100644 index 0000000..263b7c4 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/contact.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} + + +{% block content %} + +

Contact Us

+ +
+ {% csrf_token %} + {{ form.as_table }} +
+ +
+ +{% endblock %} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/edit.html b/_chapters/chp05/django_ecommerce/templates/edit.html new file mode 100644 index 0000000..dfb78ca --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/edit.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+ +
+
+
+ {% csrf_token %} + {% include "cardform.html" %} +
+
+
+{% endblock %} diff --git a/_chapters/chp05/django_ecommerce/templates/errors.html b/_chapters/chp05/django_ecommerce/templates/errors.html new file mode 100644 index 0000000..43500aa --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/errors.html @@ -0,0 +1,14 @@ +{% if form.is_bound and not form.is_valid %} +
+
+ {% for field in form.visible_fields %} + {% for error in field.errors %} +

{{ field.label }}: {{ error }}

+ {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+
+{% endif %} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/field.html b/_chapters/chp05/django_ecommerce/templates/field.html new file mode 100644 index 0000000..7161e09 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/field.html @@ -0,0 +1,6 @@ +
+ {{ field.label_tag }} +
+ {{ field }} +
+
diff --git a/_chapters/chp05/django_ecommerce/templates/flatpages/default.html b/_chapters/chp05/django_ecommerce/templates/flatpages/default.html new file mode 100644 index 0000000..2ec7565 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/flatpages/default.html @@ -0,0 +1,5 @@ +{% extends 'base.html' %} + +{% block content %} +{{ flatpage.content }} +{% endblock %} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/home.html b/_chapters/chp05/django_ecommerce/templates/home.html new file mode 100644 index 0000000..0000fa7 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/home.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+

Welcome to Django-Stripe.

+
+

Use this project/app to integrate Django and Stripe with a simple user registration.



+ Register today to get started!

+

Please Note: This app is running in test mode, use the following credit card information to make a purchase: +

+{% endblock %} diff --git a/_chapters/chp05/django_ecommerce/templates/index.html b/_chapters/chp05/django_ecommerce/templates/index.html new file mode 100644 index 0000000..98470ef --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/index.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} + + {% block content %} + +
+ +

Please put some text here.

+

If you want, you can add some text here as well. Or not.

+ Register today to get started!


+ + +      + + +      + + +
+ +
+ +
+
+

(1) One

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

(2) Two

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

(3) Three

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+
+ +
+

(4) Four

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

(5) Five

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

(6) Six

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut dui ac nisi vestibulum accumsan. Praesent gravida nulla vitae arcu blandit, non congue elit tempus. Suspendisse quis vestibulum diam.

+

+
+ +
+ +{% endblock %} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/register.html b/_chapters/chp05/django_ecommerce/templates/register.html new file mode 100644 index 0000000..df34e4a --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/register.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+ +
+
+
+ {% csrf_token %} + {% if form.is_bound and not form.is_valid %} +
× +
+ {% for field in form.visible_fields %} + {% for error in field.errors %} +

{{ field.label }}: {{ error }}

+ {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+
+ {% endif %} + {% for field in form.visible_fields %} + {% include "field.html" %} + {% endfor %} + + {% include "cardform.html" %} +
+
+
+{% endblock %} diff --git a/_chapters/chp05/django_ecommerce/templates/sign_in.html b/_chapters/chp05/django_ecommerce/templates/sign_in.html new file mode 100644 index 0000000..bbbd771 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/sign_in.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+ +
+
+
{% csrf_token %} + {% if form.is_bound and not form.is_valid %} +
+
+ {% for field in form.visible_fields %} + {% for error in field.errors %} +

{{ field.label }}: {{ error }}

+ {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+
+ {% endif %} + {% for field in form %}{% include "field.html" %}{% endfor %} +
+ +
+
+
+
+{% endblock %} diff --git a/_chapters/chp05/django_ecommerce/templates/test.html b/_chapters/chp05/django_ecommerce/templates/test.html new file mode 100644 index 0000000..a8da357 --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/test.html @@ -0,0 +1,8 @@ +{% extends 'base.html' %} + + +{% block content %} + +{% email %} + +{% endblock %} \ No newline at end of file diff --git a/_chapters/chp05/django_ecommerce/templates/user.html b/_chapters/chp05/django_ecommerce/templates/user.html new file mode 100644 index 0000000..a32168b --- /dev/null +++ b/_chapters/chp05/django_ecommerce/templates/user.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block content %} +
+
+
+
+

Member's Only Page


+

Welcome {{ user.name }}.

+

Click here to make changes to your credit card.

+{% endblock %} diff --git a/_chapters/chp05/django_ecommerce/test.db b/_chapters/chp05/django_ecommerce/test.db new file mode 100644 index 0000000000000000000000000000000000000000..f9fe606038696a2c21f1fe5403550cd7493e07f7 GIT binary patch literal 51200 zcmeHQdu$uYdEeO~MUl2-TQ*JGvaHp^_OY@g?k=D5<4nu4EZd|lS+X9;mEmLg5?>-I z$z%>lX{RZQ1UaC8H9(vE(XL6M+9`$q$$0dg0h zGqbY~QY*{O**@KQ341h;Z@%xF-^|YLemnEc{rNk&y2Pc+)ndHPiHwB-fVs+X48ugx ze*yhhe*Gw7Q4;7c(8YI)Mw#&+LMtk@!S7+33-BHIfAD|df5RWaAHeU~Tth>km8{1T z1u4E+&&D#<@@7SgbxkkKgcoPH#qhN|Gu*INFwDK?;LvY4mm21BrMi@ns$BH$A{Sk{ zbBDXPFc%3gta7(!Rxik8rFc;q=C=twHdiPF9xrvYAy$#9#aykHE0<~-y*}5nTif(R&)QCc zU(5&;3WTPXAr-eUb7N*#F)F=;l6F@a>YVV z0jgHBrUzJ$h4oP{u(`&@z~lQG*jhb;vX*2G;MGc>71#FhwwELJ!N=0j=RJ1}maICn&)pb`lM6^7%d4qKVCU_fajDaJBz9 zp6gZV6e*NW5hN$M9#tERXN?jOadLd}pq04@c%4K#J~=$D@e_C!I0JrGI_iDEZ{8Y#vGKa{iq04m;&ic8? zF6U4NxWBrI+7Q>oNgHW=+w9UMXVE%jy09LyfI-0GN+fMLZm~~-9TNp4*|ef|<+FVj zyPI^dme+$vuKub11&?Hty@`N{!2Ur1-~Y|)fB*D{sfmfe%S6Du|G!MMO<5)a`wIc{ z{=dKa!_>w^;AJ9U-v3`F+NLZMf&GPmdH>&E{b6cjBJeU1!1sSE^CgD)68Ia|0!QHn z{3QAg_|=2fz1HTuf2%RxKgq-GOE*3!>p;-bCGCq3U0$PQKywk(OI*a%+WX3SnXBH& z^$&U(&nTZgXIYQ={%<_fnYktcuL1(*`~R!Z%Vrx*1dI{D z_kRFC%AkMd&qTmP;5j4E8UQ^^>wMVm>ggG>+ryQ_dMYi%YT3Bp^N*svKOWEM#%g8u zdZ1XJUJk`aON+(34<;8L+^$zM!Rd6!J6~vs*Y0HJ=eLSW;Xp05vF7oo{QihkzASin z@3<#0&IdR?G{yU;_|Qbi6ZDBfJXOq<;;E}BoJf|79fZOZADj@pUO@=r`#*p`WzawK zXCh!C@M<8?4P9s>0Gb`}{olfTkzu~bZn9s5Ux8nTUw;n}JnHSSxkM2>o+h@iR37c& zE2FIuk8d5YxdsQpCv|&$u`PmZ+QgCGU94h?IqW?%c-o!9m3XbTU9O^S#ma7BUI=R2 zg{f7o5U>9n!ps1$7{%59-N)kf{Yh{W<}=d^@i z+yRfn%^&P?%?$2s$&z+W#8Qi&Df z2}uX95m^y!<9#xTx(Qh0h-|NNJ!E*D>Fsi@io0QG(9{@jklBZ?e+T=Y4EzuH_vq?> z3s&JT!!YF7cTlGJGZFX@AmBv1ik$;aJdaf3jUw9ZUBmGaw1fC;{|MdeNL*mhIQ@gS zOAew<#Djwa>V|b?V`EK;^rJn*gRTK|FOavDle?<2f3Xj3B6hl*cwZv%u(tHJZg{$(Pwoi`9pT?aff3;mL<;g=QdlcXm!(%P(!O3QOB-i|hPqe$Ka+ zUw^o|IOB;nlfvEE)rYIax$S6kAs1a-Z>%;~J<;oxz+CA;b8TfVcQ^kaB%5bK;~s(I zg(=Z9$N~(Bb5yn-n;O?xX5GS zB0`>@@OpW_-_y#l*2tqb!ilC=+^VdX#kha7D5cX)ZzJ2-s!AIP|HEW%!y6GI+tJ00 z7+pl@Hs>4B{EV=QFkWpIvXSPsVzfCgMy{{&k>#1~Xg<HLU~2R zgWy(rwRj^R7anY`P0#t~iVGX7%f3>g;maokpC?%?4+vbne%T3=g! zIF0(Uy0+|FpUZ9MRs>9!O{Z3(g=8UA!TcgC9@S8YTB;cec|r7f9q?ZnD;!~95stvW zfWHB6nwP(cfQf((fl(L%d))=PFwtJKv=tNWHJe&6(O$D2TBjQUdreL(YQt!y)bN(_ z;37=Ck0V;J4X3@XPSay0$+ma22IuQxzjVeVF=xY$-obym|ZL&eqNXVo4X zB+I23w!|9r1T~N1*W{;MtA|OE4my{I$oHORTWsT^_nJtXsi*B=Q%>ZiWSua;LyD?K zbE{GXiB-BlFz5o>Q1m{L8Rlxmc%gtCU~-$KS}ubOuAKvvdI2gT4@bzqUdJGIxbgK5 zAhQ3N*1l($>r7G;fe#J>r+|eS8Ic~wi27cp%(;ib(69E$eJpw^s zXTo-HNVNBKEcFRad4dySDCG4E-TY)cQQoXia^vG%7{`#yRQdY<83TW&=V!)E1WW`z z+z43EQ-R$sum5|8kzIg)xLa(t&P2dOKt;e}vD@wV{%>U_koSKO2A^iX2cPKK|Iy*2 zHdi139>1=)@F-t4iT~vqwSTl{o-nlaw9`DR!AGsFZ2C3Wp^0r}ZI&AKj1U8p4JK|k z)u?N*^s>+@EQKvvhc$Ps%k@TJ4+EcC9alsfG7T|0`6%in=UaS2KHs8)s3Y0`L`0tz z%gySO7o!c=bM*oO=Sg-cYVqnYF;VLlI`jga@q$)@7RmY><8^~|3B94>wS(U9 zOW9SWOs-b1#)-)feUIBjMp(4gux&A-!`-qRx4ABlfz~)V#07u()@@Rci2*6?l@i{L z{L>WKx#;zol^v&`sI`W}tQ&d$XCZ_BnZH*PfyYhs_Sir0YxCVb-SiDnc{{w6 zs9x!D^f)e6uB?_fxgUS*gTr;{X+K@v6k7 z$|dOnm*1?_$yqINE?Fqoa-|G+b1`xU9RyR2XR7f^maE4z6PG3{vdql_`pa>b3b`xS zHVf$7815cgQe5XoIR4Tk%G{+;cv7MGq(Uz(yfjHfqh=?nlSb{2bI9br&Xubi0)(uk z#?7iC6FYY)&Sk4o`toqLUaw3|PHt~+PgJFNq0*>l%VaOk@D+^GrOEgeE|(?A#*+S#eBu#QvNuE?wTn=%W z&Pr0XHpE?;l!{kWD6`^~1u33tyv|L_CqbY$|Dj8hC>uZjpMm!n_%8e@{4qM0>xb~) z;lIFtg5QFF3%>^c3jR6#6Zre^3uryyZ=%%!pGMw+eg&NmQimmUMo0pF1f3Id@726) z%vPHS>_or{0dSll;Q$FwYF?=sB0X!Re*egWQvk9JbMsGQcB6S8Gw=1G>3pqL7zL&1cx zW#r6LSw<8dddtZBD;=M7tTzB*yNtFkOEw%aCvC3R$3bgIgNLkOBleqG%rAO9NxzsR zz~~A%14wkmYCRnox?-#dNfO%?I3=qvVyr@&)k?=?Xb%;Xdb_Kj*{E8t_1j$c!=N?S zp#zbpVP*tKC&}<`dUX=+3Q{LwJ6sc|WmT8C%dgys%jH#XJ3^ShQjzPbRRVxWoMii?4qG z-$mrQyIaSGY_7QgX!#69%M0j3p@1kPe8~6#!l6yGb{dS3nnOOM z?$2>8-~Y}1fBSu|HuWM`TL7{{oTKfG&M63_>dwn2W*U+cRIt7 z9CGg~6}X#HwHPlUzXB*MZeGpcua z?0xo|%*WYdFB!4NeWNzl`Sajc<#Pd*(^O)3=R#XzlGM`pu+c#tyV5w^p|f4C$obt|8nr&yxvK=;ti?&{6%!i$BqLS>*Y!=sSN`o zN8qYYZ)H5-_+n0>gEoa1( zPbF@bN-8@p*O5|B<_hV^aVs+k&SI{}`HGGXr8Is{l^+$Y9@J)|qLs7S+{aW>Dq16@ zq7~ASqiv!!IxJct;`*%27#PvD8h=deqEd03JEBrhiG+d*!|_vx(Q$BKKxrc>#xii^ z+#xH&1Gk<(azKMz;9C_Da`IfSN~cJnbc!H3$@Qq(U_9vuM=KF0Q%oMTG8X}_lZY=C z<(xVT6-)E*$yAdE)CyE|TLmg#SHX==>Z2nC>(EAx715D$==8pJc=0_0QYCPlLr3>f z3M!pYP^mb63Z31jgO?OzF*q`ej_w^;BAj)v|HH=O4vSG@el@8;#!#CrbH@r!d-F zyCL4JPG1X#W~T#Nx0b^TzU`8yc5Ne{y%$=kkIgPT@I4GY5Tx|FdhVGJz-QZed{e&Q zgpc=%J`ZwJnoVU@t?%kRP&ForozV3Ojy0lUg17nNZDRH@yQ9Djn2OZ5Ppyr9*U?^}49m7|Z8uXPn zFQKD+ui~(+=jNwt3oA=2!Nt(M`dWJfWM1B&=nL?>l=wYDD-GnPiOy-`FP5^ z+8?}C9`(!=_(-~%lcMY5*0N7p+Ad8`3z@O<>Plk%UO))d#Ax8bs&6J4>1diKFy#wP zgnU7ducPOR^1J=jqWoGvQLbi4*JWe8_h!U5?Tu#c#AnuKXRqhNVgGz)t=8Tg4|4tQ znecf1qNom08BSV)E_L1_umArP1AiBO6@D9j3jWvoFaww(O$1B?PQn1lXA%<$sZ^5c zb\r\n\r\n

You can add some text about yourself here. Then when you are done, just add a closing HTML paragraph tag.

\r\n\r\n
    \r\n
  • Bullet Point # 1
  • \r\n
  • Bullet Point # 2
  • \r\n
  • Bullet Point # 3
  • \r\n
  • Bullet Point # 4
  • \r\n
\r\n\r\n
\r\n\r\n

You can add a link or an email address here if you want. Again, you don't have to, but I highly recommend it. Cheers!

\r\n\r\n

Ready? Contact us!

", "enable_comments": false}}, {"pk": 1, "model": "contact.contactform", "fields": {"topic": "test", "timestamp": "2014-07-17T19:45:34.128Z", "message": "test", "name": "Michael Herman", "email": "hermanmu@gmail.com"}}, {"pk": 1, "model": "payments.user", "fields": {"name": "Michael Herman", "created_at": "2014-07-17T21:08:00.046Z", "updated_at": "2014-07-17T21:08:00.046Z", "last_login": "2014-07-17T21:07:59.958Z", "password": "pbkdf2_sha256$10000$rQ3yna6C68Jo$0El1MfrieNk3vW5eUwnCC2g+oYXbQP729s3N7VY5EcM=", "stripe_id": "ch_14HM5C4NgLaEZGGDiAA6QgZs", "email": "michael@mherman.org", "last_4_digits": "4242"}}, {"pk": 2, "model": "payments.user", "fields": {"name": "Jeffrey Herman", "created_at": "2014-07-17T21:32:25.483Z", "updated_at": "2014-07-17T21:32:25.483Z", "last_login": "2014-07-17T21:32:25.234Z", "password": "pbkdf2_sha256$10000$Gjd2L9XrqWdZ$XeLJQD5IzvoKNvy1D/cs3HhZcQt0VYUXs37+Twd3Wpg=", "stripe_id": "cus_4QCsRXUX8T9PtZ", "email": "jesus@jesu.com", "last_4_digits": "4242"}}, {"pk": 3, "model": "payments.user", "fields": {"name": "Michael Herman", "created_at": "2014-07-17T21:42:55.338Z", "updated_at": "2014-07-17T21:50:56.427Z", "last_login": "2014-07-17T21:42:55.262Z", "password": "pbkdf2_sha256$10000$QAF8Dj9Otg1c$HKjr1Gh9UopwnfKzrtvl1z8i0ixAWh9OA4Z0uGCtmek=", "stripe_id": "cus_4QD3pEjShbl2dn", "email": "hermanmu@gmail.com", "last_4_digits": "4242"}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 7}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_flatpage", "name": "Can add flat page", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_flatpage", "name": "Can change flat page", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_flatpage", "name": "Can delete flat page", "content_type": 8}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_contactform", "name": "Can add contact form", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_contactform", "name": "Can change contact form", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_contactform", "name": "Can delete contact form", "content_type": 9}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 10}}, {"pk": 1, "model": "auth.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2014-07-17T19:16:19.908Z", "groups": [], "user_permissions": [], "password": "pbkdf2_sha256$10000$qYpGD7mtCW9a$nTmOV/RVKtrg8Cf94Qly3BLhQQvmUA7sdqZ06d66Meo=", "email": "ad@min.com", "date_joined": "2014-07-17T19:12:18.244Z"}}, {"pk": 1, "model": "admin.logentry", "fields": {"action_flag": 1, "action_time": "2014-07-17T19:18:08.399Z", "object_repr": "/about/ -- About us", "object_id": "1", "change_message": "", "user": 1, "content_type": 8}}] \ No newline at end of file diff --git a/_chapters/chp06/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp06/django_ecommerce/django_ecommerce/settings.py index cb47005..cc68782 100644 --- a/_chapters/chp06/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp06/django_ecommerce/django_ecommerce/settings.py @@ -3,14 +3,11 @@ import os DEBUG = True -TEMPLATE_DEBUG = DEBUG PROJECT_ROOT = os.path.realpath(os.path.dirname(__file__)) SITE_ROOT = os.path.dirname(PROJECT_ROOT) STRIPE_SECRET = 'sk_test_4QBquf6d5EzsnJC1fTI2GBGm' STRIPE_PUBLISHABLE = 'pk_test_4QBqqGvCk9gaNn3pl1cwxcAS' -TEST_RUNNER = 'django.test.runner.DiscoverRunner' - ADMINS = ( # ('Your Name', 'your_email@example.com'), ) @@ -19,12 +16,8 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'django_db', - 'USER': 'djangousr', - 'PASSWORD': 'your_password_here', - 'HOST': 'localhost', - 'PORT': '5432', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test.db' } } @@ -88,13 +81,6 @@ # Make this unique, and don't share it with anybody. SECRET_KEY = '!(1ty%c5a)0l0(p)kxl2igmbobx_64hqh&tv1=+s9@!@zez4o^' -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -# 'django.template.loaders.eggs.Loader', -) - MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -111,8 +97,6 @@ # Python dotted path to the WSGI application used by Django's runserver. WSGI_APPLICATION = 'django_ecommerce.wsgi.application' -TEMPLATE_DIRS = (os.path.join(SITE_ROOT, 'templates'),) - INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', @@ -157,3 +141,20 @@ }, } } + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(SITE_ROOT, 'templates'),], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + 'debug': True, + }, + }, +] diff --git a/_chapters/chp06/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp06/django_ecommerce/django_ecommerce/urls.py index 11db113..f2ae409 100644 --- a/_chapters/chp06/django_ecommerce/django_ecommerce/urls.py +++ b/_chapters/chp06/django_ecommerce/django_ecommerce/urls.py @@ -1,17 +1,20 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from payments import views from django.contrib import admin +from main.views import index as main_index +from contact.views import contact + admin.autodiscover() -urlpatterns = patterns('', +urlpatterns = [ url(r'^admin/', include(admin.site.urls)), - url(r'^$', 'main.views.index', name='home'), + url(r'^$', main_index, name='home'), url(r'^pages/', include('django.contrib.flatpages.urls')), - url(r'^contact/', 'contact.views.contact', name='contact'), + url(r'^contact/', contact, name='contact'), # user registration/authentication url(r'^sign_in$', views.sign_in, name='sign_in'), url(r'^sign_out$', views.sign_out, name='sign_out'), url(r'^register$', views.register, name='register'), url(r'^edit$', views.edit, name='edit'), -) +] diff --git a/_chapters/chp06/django_ecommerce/html1.html b/_chapters/chp06/django_ecommerce/html1.html new file mode 100644 index 0000000..0480f11 --- /dev/null +++ b/_chapters/chp06/django_ecommerce/html1.html @@ -0,0 +1,299 @@ +' + + + + + + Your MVP + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Your MVP!

+
+ +
+ + + +
+ +
+
+
+
+ +
+
+
+ + +
\xc3\x97 +
+ + +

Name: This field is required.

+ + + +

Email: This field is required.

+ + + +

Password: This field is required.

+ + + +

Verify Password: This field is required.

+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + + + + +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + + +
+ + +' + diff --git a/_chapters/chp06/django_ecommerce/html2.html b/_chapters/chp06/django_ecommerce/html2.html new file mode 100644 index 0000000..2ebcc9d --- /dev/null +++ b/_chapters/chp06/django_ecommerce/html2.html @@ -0,0 +1,276 @@ +' + + + + + + Your MVP + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Your MVP!

+
+ +
+ + + +
+ +
+
+
+
+ +
+
+
+ + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + + + + +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + + +
+ + +' + diff --git a/_chapters/chp06/django_ecommerce/html3.html b/_chapters/chp06/django_ecommerce/html3.html new file mode 100644 index 0000000..40c49af --- /dev/null +++ b/_chapters/chp06/django_ecommerce/html3.html @@ -0,0 +1,299 @@ + +' + + + + + + Your MVP + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Your MVP!

+
+ +
+ + + +
+ +
+
+
+
+ +
+
+
+ + +
\xc3\x97 +
+ + +

Name: This field is required.

+ + + +

Email: This field is required.

+ + + +

Password: This field is required.

+ + + +

Verify Password: This field is required.

+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + + + + +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + + +
+ + +' diff --git a/_chapters/chp06/django_ecommerce/html4.html b/_chapters/chp06/django_ecommerce/html4.html new file mode 100644 index 0000000..f808da2 --- /dev/null +++ b/_chapters/chp06/django_ecommerce/html4.html @@ -0,0 +1,276 @@ +' + + + + + + Your MVP + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Your MVP!

+
+ +
+ + + +
+ +
+
+
+
+ +
+
+
+ + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + + + + +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + + +
+ + +' + diff --git a/_chapters/chp06/django_ecommerce/manage.py b/_chapters/chp06/django_ecommerce/manage.py old mode 100644 new mode 100755 diff --git a/_chapters/chp06/django_ecommerce/payments/forms.py b/_chapters/chp06/django_ecommerce/payments/forms.py index 0a99862..0b079ee 100644 --- a/_chapters/chp06/django_ecommerce/payments/forms.py +++ b/_chapters/chp06/django_ecommerce/payments/forms.py @@ -25,16 +25,17 @@ class CardForm(PaymentForm): class UserForm(CardForm): + name = forms.CharField(required=True) email = forms.EmailField(required=True) password = forms.CharField( required=True, - label=('Password'), + label=(u'Password'), widget=forms.PasswordInput(render_value=False) ) ver_password = forms.CharField( required=True, - label=(' Verify Password'), + label=(u' Verify Password'), widget=forms.PasswordInput(render_value=False) ) diff --git a/_chapters/chp06/django_ecommerce/payments/views.py b/_chapters/chp06/django_ecommerce/payments/views.py index 21672c9..2657242 100644 --- a/_chapters/chp06/django_ecommerce/payments/views.py +++ b/_chapters/chp06/django_ecommerce/payments/views.py @@ -1,7 +1,6 @@ from django.db import IntegrityError from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.shortcuts import render from payments.forms import SigninForm, CardForm, UserForm from payments.models import User import django_ecommerce.settings as settings @@ -33,15 +32,15 @@ def sign_in(request): else: form = SigninForm() - print(form.non_field_errors()) + print form.non_field_errors() - return render_to_response( + return render( + request, 'sign_in.html', { 'form': form, 'user': user }, - context_instance=RequestContext(request) ) @@ -92,17 +91,17 @@ def register(request): else: form = UserForm() - return render_to_response( + return render( + request, 'register.html', { 'form': form, - 'months': list(range(1, 12)), + 'months': range(1, 12), 'publishable': settings.STRIPE_PUBLISHABLE, 'soon': soon(), 'user': user, - 'years': list(range(2011, 2036)), + 'years': range(2011, 2036), }, - context_instance=RequestContext(request) ) @@ -131,16 +130,16 @@ def edit(request): else: form = CardForm() - return render_to_response( + return render( + request, 'edit.html', { 'form': form, 'publishable': settings.STRIPE_PUBLISHABLE, 'soon': soon(), - 'months': list(range(1, 12)), - 'years': list(range(2011, 2036)) + 'months': range(1, 12), + 'years': range(2011, 2036) }, - context_instance=RequestContext(request) ) diff --git a/_chapters/chp06/django_ecommerce/test.db b/_chapters/chp06/django_ecommerce/test.db index f9fe606038696a2c21f1fe5403550cd7493e07f7..1a6dc84f3ac7a5a9943c47bf8930adcd185e1169 100644 GIT binary patch literal 172032 zcmeI5eQX=&eaCs?%ZU`pll9`WEz3GlQEWz5MDj#YjN>MZ%O-+tIdSYPE?V49!1=t@&yRF$jL;o0xZrOlgYlCgbFsvAcy%a->tk^mXLpxyIAItvO zfVRV)=eaxH3y+i|WXURhh3LY2p5N=|dG6(T5&i5lS1N5y+H5pyYFm^}Ch|Gw0BxJh1oY?15CRqAE!p1RY%t?ab4rn1#+ z>}hh+$V)D}UV&O(nT3i2U@p&oXuI}n3q-*QF)d}fbf;mtseL= z^$2xs*aGYkqRnaf)!X#sF+IQwg93E)t{$_;?khmk#(n|n=2>H39p%NXJh#gaip`W) zQl+w-$!?^qHt&gaw13O@$m&-Xf#fuN-{F&f;AR~Vc=6%_*IBgkv573FbDQanbcyu> zt6cD)Yt<^ImMt!;mNs~)QEzMYw$griThp7o5GPnk7_WMU;gVO#CA}f%vZ{sEvLEly z(-Kk!TDVuZbD4-zYqBrD(tP zxlK@rq&rF#!f{J$O6%8dNb5JRT#=r>zIL^6{g!mOcuOkWym4)9otV5@T)#m=HEtdr zTGxvoD_$?IUn)Lpb87fV_H>$FuRZ!!wx*S2ryb?R#YJv!%7usZavK!=JlYS1Ui|?B z(62;K;yt-D*>2i<4@G!!W`=ux&Y5<%6GZwXdY}xq)mH1JMw9f6yK1v^TWy|8XI7Kl z?m?QesDxnAN^|Z?Uccy>s`J zhCN!oN9aJ^K(}&$?n<~?VbeSF0k8E@o)>3lxz39&C=4Nsj_;TJff9E1qt)I%nUse^ z;*+xjn^vc0d*d124TX4dVZm`rxA?GRtkaN3Mpo_nkXF`~7HKHgBW$0hD!-uCw;GP; zo)Hqg!FQix-RnA?vUy8(wgS8;%iQh`Yfukk9z{VX5D$s>0g59|l8L&#tyI+w&35B( zi`2*?yI*yZjEo);FJ9z!U+b5tqBN>yTQZ?ohz+MYj|O?MBy%18c43p~zUp*?Q*tXC zWw|VGus0YF(SX6uIUev3RQlM|-(fryDD$pPf)LYjB%dA%(XM`rrET2%TH2i*tzObx zr+!Dnx+>4nYw#6KH7|7F1;KN z`ex<}n^m>Ft!^>51GUomY$msyijhExDf_)4A1jE?s8_RYZ=1i&5*lv3#l1*jO*-JdJaMiRR+byu^Po<{bQ>t2ht9{!u6*-s6WuFQW zBR^vrF*}+y_rxY*_p_9dv&;FXfldNIMqy@uBDH6W5#@#MnecgtRojqepr&GD* zLV$ECf~H)n)R_q~=S)mCUp8%#j?kcO?qwpC%jI+Fl%MrW)5O4jNjjLky1X<*K+KyU z1}kJ`IiJpPBwxKF+wvW(C}&pXbWHf7FC^4M!ruyS33cJ$gkKQ8dK7MI$PolU00ck) z1V8`;KmY_l00ck)1pe;`h(W*1dF*9~L?iy0VYUy4&bR0RObi>6bV7?1O~gn1b75QA zxZl5M%CN}=x*u+G$iHN|uy!Pj68-Uze}QxD^*IwE6%pZrPk2T6N8#UvzY(qqzb9zI z1>p_h^LiLKf&d7B00@8p2!H?xfB*=900@AtahhWTcXLkx9}kF=9NWM`521;`^b}`qoiY9&JRO+V zy_hTg#vjnoT@Ha*U~-ZT@@YW+cwkawXY5achQ|U~Q?O`~M@tBR=7W!aoRq zE&Q(VW#Mze&yl|bxIzl?0|Fob0w4eaAOHd&00JNY0w4eaN1niFB;cE#w2%LfhXcN; zx!&>r|jsM5USL;pAI>-NX{r^ntD?YOJ|83!qh2Iu_UHB#8Q^IZGIpJ~P ztPqd?IR1n9x8i>qe>46Y@z2EX#(y?`HJ*#l#z$iB#@>njMeGlVGJZe+1V8`;KmY_l z00ck)1V8`;-X{Xb0^A%IA>a0=kuUP3BfB$t3HfH!`j%cY9S?BRT!eo402MfWBEY4& zu%TjGjGodf3{6|bsmTDhz=f#+Tj@zwI&9c6hps2|Qp1Q@DvkxXd2X0mFpG{eLEca| zm$@hPQbXM=onY!bRX2;qS(+h3osNsg^b$jrmKjIc}s)P}7z!Yum@JG42%dWm6xmJGA7{Zx~l z@+|owLxm03Lwb>+!-|4JfScils1iF591n!aZhyUBSd038R%|F*#bo_IoMa7bmC^No z@}>Xy0Ra#I0T2KI5C8!X009sH0T2KI5IDpH==wj#|A)ANks1hq00@8p2!H?xfB*=9 z00@8p2oM5T|3^0f0T2KI5C8!X009sH0T2KI5CDP0PXOcp!{5fp5ClK~1V8`;KmY_l z00ck)1V8`;Z2SM&M-Tx45C8!X009sH0T2KI5C8!X009s~o!gE9!KOg`CAOHd&00JNY0w4eaAOHd&aKs1%{TvrL!;aJJc$yx=Q|x$(9Z%BZ z@CkMl+3`3%@{{a1!H(nf7#d^8W9&Fek3oSQ00JNY0w4eaAOHd&00JNY0!N(y#{WmX z2OxhC009sH0T2KI5C8!X009sHfuleGAOHd&00JNY0w4eaAOHd&a1;n&{C^Z$899Og2!H?xfB*=900@8p2!H?x z9CZR1{~z@pfc!xK1V8`;KmY_l00ck)1V8`;jsk&LB;*?#TIYlZ;@=wi?8x(xOMG?s z2l1iUX7tZOZw;5Dh2Zys%YnV2cZks2+(&%sd!*9&_;FsW6}a8;O1-SzQ+L|8m7SK> z)DPQQvsP)fDvf$eFQ}B2^s>6Lu_9-boqFX?fBEB=t``e8iqg&XwP$V?rM2~s7N3_A z{pG#wC!}lZgR~OTxdaQhQcj$g5>~tlDM)s>NnU(xk?YL4lG|!FcD7qeO3TZ6b$Qc@ zO0SqSV($ssrRR=Luck4vPkIc#2|b2c?-;bzTq>=Wm)$W;>=T2j*FOf6OR}>#&WpDQ zhR*P`9!r|VQhq_LZ#5LPT&vWTYGX@TPUkk$8|jj}V#*ndySl5V$KtN0JW7pvTdTK~ z_RHHE!?JhwG%qeLa<6l3b)%}eb=>D?j0kQNp14w!67Fj1f;6~PAzrq$rnG+ThO~b3 z$`$G9>uXmF*KbLei?^i0%^TO&)`_93#q}Gcvotnd&`NC@jnvlewV8rd+|;(43F)rd zEZtU{=Q62OvTKzHsiii#D3r~rx|NVxHMLqLuBDxNtFl$s%63VjUfuuuld*E3@n0`~ta!b+eyR9uH)*?qk#~~VF^X;jJkne%*PcPz|{`8>Dx zn8jA_EtV&pYfov|gK3Y@fq3dxJRjLTK$xt_f?Tmy*as&n4~N7jX9p71L)2-q&%>Qj zo)>3lxz39&l8iW$;rkIg5T&|GAIauB$g1rX#CWYvlDGX?abdx6;aGe~T>md#fzOQb z;^jQoDOwK#ovszC>+PYZddvW8e z00ck)1V8`;KmY_l00ck)1bzw$#Q5*}CVXG>39rWAjJ+HE*XRQyUk?9g_)mtv6#CWR zF9$~h1^-w4!q9U=C%IqqeT^$~_qvyX(twYh$C!M4biwC&`fS5|rPLXzv~>-uI5 zS>~k+!(}Z?wiT>ooWOSb*TDmBWs%2#SqV}@IdD+$s_BD9YB&Iw`L+xD+x98|F z9oCu1^5R;K>txwvs1Yhzg>P?@#dU>tqLz}ESF+_5E$3M~@7V->EI3-iDZ^4H?)3TN z6U%y77YBq@lI4}vvaEWB^}zjvWm?!LtZtxe`uK{>i!bK5UH^d4Y#K_coXMB- z_KbDTh}AiN>?uk&Na#&NHV!!~zlR)Xr!h(2%kbjG1+FvcnN2pADKBfw=2d4pPYi}@ zYI%l4+j*y1O(b_?X?tUc2(~0~~%U(QO9;JZ{4wOA@ly9!Va~B<;`Ena&nb-S@$DOo+q z-Br5#w#-#n($$=eO?OMR+G>p~|Mafkq!&YCai-t9g|$*|E!vOlojuQsGc(-l(~N9a zru!k{q}VM#5W8)))q1JXB)zknU}n{^c2!kdZMr*k|kRyQ|$y^|Wcv+XXq4>&G75*STGd4X)7@LY5w z0yLlgQJF7}oxr^QG+Ja&Bi0bMm1T1`rK>z{G@pGX+e1r#8o6|76Vt24v6Zq@cz~=B zlZWD~o?*D;6|&oOL(XMYYlYbI9x`HcKH6PDu#4Olfz#rB$4xMo8fA@PPVVw^>}j%B zv^u>NmMeg1j|;#OKG0?0g5ymQaGMrt@u^gQ2JeuIelFW7V z-95%*!0t*R>%~e{-O#E^Ze^n^m*oxn%bHy@;MtzN((ct7)oi&l-Jw$NK+=IiCUFAOHd&00JNY0w4eaAOHd&00JQJ!4Sas|AXNSFCYK{ zAOHd&00JNY0w4eaAOHd&@L>|b`2WMC58ogF0w4eaAOHd&00JNY0w4eaAn?Huh!Hnu zMqcs>?+9NNUK3Q|qHr?)llXVze;EH<{FCwX@ks2SV_%QG5ql-}@mMw%iGDBo_2}oL zS}u1Dkk71cX^PaPFN6*I^v|QocNNfY&9J#K zN+nvnbV^q++_@ACuXcsxNuscj4O8zfX&Qt>^aK+fHi9v~R>l?yEt2RrEG0!<)Cj{a zYQ)eLojXoM=a+_Q0CqX*y(^TQWcGN&HTjka^1TAhmu;>|k;KZ96S}D3+AeCi?uyQh zGuJ$IZI`32yF$_!5t>^F8LsIUso31oQ|Xv4VR$qp3~%)G)F_ddo)1w+W;Jy}KR+mt zx@0Fd41@o`V};+OBmiw3BxTt zJ;f7=>3KhOXI4``^m8Ia>gE@R4EyY#DX^u<&J#gh#_-UUF}0_o^o5slh7;>MCTyOp!i6CwY|LPRYJ%AzR^Lq zD%61h2!H?xfB*=900@8p2!H?xfB*;_A_Br8$_1%_00@8p2!H?xfB*=900@8p2!H?x z*a%?#-v$F8K>!3m00ck)1V8`;KmY_l00cnbkP^W7|ByB;(gFbx009sH0T2KI5C8!X z009sH0UH5)|Gy0eK7s%UfB*=900@8p2!H?xfB*=9z#%1o@&6%hSfm94AOHd&00JNY z0w4eaAOHd&00K4w82{T~;3Eit00@8p2!H?xfB*=900@8p2pm!Z82=yAhDBN+00JNY z0w4eaAOHd&00JNY0w7=`fbaja!N5ll009sH0T2KI5C8!X009sH0T4K(1hD>pNE;Su zfdB}A00@8p2!H?xfB*=900@A9jR3~~HW>H_0w4eaAOHd&00JNY0w4eaAOHe~lmOlT z&k6tHBmdzC1V8`;KmY_l00ck)1V8`;KmY_l;HQ|tDK6lfo73*8we70*aH&zVeWdIE qobVq$@*jRc00ck)1V8`;KmY_l00ck)1V8`;4hw;}e=ckr|Nk$+1=QRC literal 51200 zcmeHQdu$uYdEeO~MUl2-TQ*JGvaHp^_OY@g?k=D5<4nu4EZd|lS+X9;mEmLg5?>-I z$z%>lX{RZQ1UaC8H9(vE(XL6M+9`$q$$0dg0h zGqbY~QY*{O**@KQ341h;Z@%xF-^|YLemnEc{rNk&y2Pc+)ndHPiHwB-fVs+X48ugx ze*yhhe*Gw7Q4;7c(8YI)Mw#&+LMtk@!S7+33-BHIfAD|df5RWaAHeU~Tth>km8{1T z1u4E+&&D#<@@7SgbxkkKgcoPH#qhN|Gu*INFwDK?;LvY4mm21BrMi@ns$BH$A{Sk{ zbBDXPFc%3gta7(!Rxik8rFc;q=C=twHdiPF9xrvYAy$#9#aykHE0<~-y*}5nTif(R&)QCc zU(5&;3WTPXAr-eUb7N*#F)F=;l6F@a>YVV z0jgHBrUzJ$h4oP{u(`&@z~lQG*jhb;vX*2G;MGc>71#FhwwELJ!N=0j=RJ1}maICn&)pb`lM6^7%d4qKVCU_fajDaJBz9 zp6gZV6e*NW5hN$M9#tERXN?jOadLd}pq04@c%4K#J~=$D@e_C!I0JrGI_iDEZ{8Y#vGKa{iq04m;&ic8? zF6U4NxWBrI+7Q>oNgHW=+w9UMXVE%jy09LyfI-0GN+fMLZm~~-9TNp4*|ef|<+FVj zyPI^dme+$vuKub11&?Hty@`N{!2Ur1-~Y|)fB*D{sfmfe%S6Du|G!MMO<5)a`wIc{ z{=dKa!_>w^;AJ9U-v3`F+NLZMf&GPmdH>&E{b6cjBJeU1!1sSE^CgD)68Ia|0!QHn z{3QAg_|=2fz1HTuf2%RxKgq-GOE*3!>p;-bCGCq3U0$PQKywk(OI*a%+WX3SnXBH& z^$&U(&nTZgXIYQ={%<_fnYktcuL1(*`~R!Z%Vrx*1dI{D z_kRFC%AkMd&qTmP;5j4E8UQ^^>wMVm>ggG>+ryQ_dMYi%YT3Bp^N*svKOWEM#%g8u zdZ1XJUJk`aON+(34<;8L+^$zM!Rd6!J6~vs*Y0HJ=eLSW;Xp05vF7oo{QihkzASin z@3<#0&IdR?G{yU;_|Qbi6ZDBfJXOq<;;E}BoJf|79fZOZADj@pUO@=r`#*p`WzawK zXCh!C@M<8?4P9s>0Gb`}{olfTkzu~bZn9s5Ux8nTUw;n}JnHSSxkM2>o+h@iR37c& zE2FIuk8d5YxdsQpCv|&$u`PmZ+QgCGU94h?IqW?%c-o!9m3XbTU9O^S#ma7BUI=R2 zg{f7o5U>9n!ps1$7{%59-N)kf{Yh{W<}=d^@i z+yRfn%^&P?%?$2s$&z+W#8Qi&Df z2}uX95m^y!<9#xTx(Qh0h-|NNJ!E*D>Fsi@io0QG(9{@jklBZ?e+T=Y4EzuH_vq?> z3s&JT!!YF7cTlGJGZFX@AmBv1ik$;aJdaf3jUw9ZUBmGaw1fC;{|MdeNL*mhIQ@gS zOAew<#Djwa>V|b?V`EK;^rJn*gRTK|FOavDle?<2f3Xj3B6hl*cwZv%u(tHJZg{$(Pwoi`9pT?aff3;mL<;g=QdlcXm!(%P(!O3QOB-i|hPqe$Ka+ zUw^o|IOB;nlfvEE)rYIax$S6kAs1a-Z>%;~J<;oxz+CA;b8TfVcQ^kaB%5bK;~s(I zg(=Z9$N~(Bb5yn-n;O?xX5GS zB0`>@@OpW_-_y#l*2tqb!ilC=+^VdX#kha7D5cX)ZzJ2-s!AIP|HEW%!y6GI+tJ00 z7+pl@Hs>4B{EV=QFkWpIvXSPsVzfCgMy{{&k>#1~Xg<HLU~2R zgWy(rwRj^R7anY`P0#t~iVGX7%f3>g;maokpC?%?4+vbne%T3=g! zIF0(Uy0+|FpUZ9MRs>9!O{Z3(g=8UA!TcgC9@S8YTB;cec|r7f9q?ZnD;!~95stvW zfWHB6nwP(cfQf((fl(L%d))=PFwtJKv=tNWHJe&6(O$D2TBjQUdreL(YQt!y)bN(_ z;37=Ck0V;J4X3@XPSay0$+ma22IuQxzjVeVF=xY$-obym|ZL&eqNXVo4X zB+I23w!|9r1T~N1*W{;MtA|OE4my{I$oHORTWsT^_nJtXsi*B=Q%>ZiWSua;LyD?K zbE{GXiB-BlFz5o>Q1m{L8Rlxmc%gtCU~-$KS}ubOuAKvvdI2gT4@bzqUdJGIxbgK5 zAhQ3N*1l($>r7G;fe#J>r+|eS8Ic~wi27cp%(;ib(69E$eJpw^s zXTo-HNVNBKEcFRad4dySDCG4E-TY)cQQoXia^vG%7{`#yRQdY<83TW&=V!)E1WW`z z+z43EQ-R$sum5|8kzIg)xLa(t&P2dOKt;e}vD@wV{%>U_koSKO2A^iX2cPKK|Iy*2 zHdi139>1=)@F-t4iT~vqwSTl{o-nlaw9`DR!AGsFZ2C3Wp^0r}ZI&AKj1U8p4JK|k z)u?N*^s>+@EQKvvhc$Ps%k@TJ4+EcC9alsfG7T|0`6%in=UaS2KHs8)s3Y0`L`0tz z%gySO7o!c=bM*oO=Sg-cYVqnYF;VLlI`jga@q$)@7RmY><8^~|3B94>wS(U9 zOW9SWOs-b1#)-)feUIBjMp(4gux&A-!`-qRx4ABlfz~)V#07u()@@Rci2*6?l@i{L z{L>WKx#;zol^v&`sI`W}tQ&d$XCZ_BnZH*PfyYhs_Sir0YxCVb-SiDnc{{w6 zs9x!D^f)e6uB?_fxgUS*gTr;{X+K@v6k7 z$|dOnm*1?_$yqINE?Fqoa-|G+b1`xU9RyR2XR7f^maE4z6PG3{vdql_`pa>b3b`xS zHVf$7815cgQe5XoIR4Tk%G{+;cv7MGq(Uz(yfjHfqh=?nlSb{2bI9br&Xubi0)(uk z#?7iC6FYY)&Sk4o`toqLUaw3|PHt~+PgJFNq0*>l%VaOk@D+^GrOEgeE|(?A#*+S#eBu#QvNuE?wTn=%W z&Pr0XHpE?;l!{kWD6`^~1u33tyv|L_CqbY$|Dj8hC>uZjpMm!n_%8e@{4qM0>xb~) z;lIFtg5QFF3%>^c3jR6#6Zre^3uryyZ=%%!pGMw+eg&NmQimmUMo0pF1f3Id@726) z%vPHS>_or{0dSll;Q$FwYF?=sB0X!Re*egWQvk9JbMsGQcB6S8Gw=1G>3pqL7zL&1cx zW#r6LSw<8dddtZBD;=M7tTzB*yNtFkOEw%aCvC3R$3bgIgNLkOBleqG%rAO9NxzsR zz~~A%14wkmYCRnox?-#dNfO%?I3=qvVyr@&)k?=?Xb%;Xdb_Kj*{E8t_1j$c!=N?S zp#zbpVP*tKC&}<`dUX=+3Q{LwJ6sc|WmT8C%dgys%jH#XJ3^ShQjzPbRRVxWoMii?4qG z-$mrQyIaSGY_7QgX!#69%M0j3p@1kPe8~6#!l6yGb{dS3nnOOM z?$2>8-~Y}1fBSu|HuWM`TL7{{oTKfG&M63_>dwn2W*U+cRIt7 z9CGg~6}X#HwHPlUzXB*MZeGpcua z?0xo|%*WYdFB!4NeWNzl`Sajc<#Pd*(^O)3=R#XzlGM`pu+c#tyV5w^p|f4C$obt|8nr&yxvK=;ti?&{6%!i$BqLS>*Y!=sSN`o zN8qYYZ)H5-_+n0>gEoa1( zPbF@bN-8@p*O5|B<_hV^aVs+k&SI{}`HGGXr8Is{l^+$Y9@J)|qLs7S+{aW>Dq16@ zq7~ASqiv!!IxJct;`*%27#PvD8h=deqEd03JEBrhiG+d*!|_vx(Q$BKKxrc>#xii^ z+#xH&1Gk<(azKMz;9C_Da`IfSN~cJnbc!H3$@Qq(U_9vuM=KF0Q%oMTG8X}_lZY=C z<(xVT6-)E*$yAdE)CyE|TLmg#SHX==>Z2nC>(EAx715D$==8pJc=0_0QYCPlLr3>f z3M!pYP^mb63Z31jgO?OzF*q`ej_w^;BAj)v|HH=O4vSG@el@8;#!#CrbH@r!d-F zyCL4JPG1X#W~T#Nx0b^TzU`8yc5Ne{y%$=kkIgPT@I4GY5Tx|FdhVGJz-QZed{e&Q zgpc=%J`ZwJnoVU@t?%kRP&ForozV3Ojy0lUg17nNZDRH@yQ9Djn2OZ5Ppyr9*U?^}49m7|Z8uXPn zFQKD+ui~(+=jNwt3oA=2!Nt(M`dWJfWM1B&=nL?>l=wYDD-GnPiOy-`FP5^ z+8?}C9`(!=_(-~%lcMY5*0N7p+Ad8`3z@O<>Plk%UO))d#Ax8bs&6J4>1diKFy#wP zgnU7ducPOR^1J=jqWoGvQLbi4*JWe8_h!U5?Tu#c#AnuKXRqhNVgGz)t=8Tg4|4tQ znecf1qNom08BSV)E_L1_umArP1AiBO6@D9j3jWvoFaww(O$1B?PQn1lXA%<$sZ^5c zbRegister Today!" in resp.content) + + def test_invalid_form_returns_registration_page(self): @@ -110,10 +128,10 @@ def test_invalid_form_returns_registration_page(self): self.request.method = 'POST' self.request.POST = None resp = register(self.request) - self.assertEqual(resp.content, self.expected_html) + self.assertEquals(resp.content, self.expected_html) # make sure that we did indeed call our is_valid function - self.assertEqual(user_mock.call_count, 1) + self.assertEquals(user_mock.call_count, 1) @mock.patch('payments.views.Customer.create') @mock.patch.object(User, 'create') @@ -138,9 +156,9 @@ def test_registering_new_user_returns_succesfully( resp = register(self.request) - self.assertEqual(resp.content, b"") - self.assertEqual(resp.status_code, 302) - self.assertEqual(self.request.session['user'], new_user.pk) + self.assertEquals(resp.content, "") + self.assertEquals(resp.status_code, 302) + self.assertEquals(self.request.session['user'], new_user.pk) #verify the user was actually stored in the database. create_mock.assert_called_with( 'pyRock', 'python@rocks.com', 'bad_password', '4242', new_cust.id @@ -208,7 +226,8 @@ def test_registering_user_twice_cause_error_msg(self, save_mock): self.request.POST = {} #create the expected html - html = render_to_response( + html = render( + self.request, 'register.html', { 'form': self.get_MockUserForm(), @@ -217,7 +236,7 @@ def test_registering_user_twice_cause_error_msg(self, save_mock): 'soon': soon(), 'user': None, 'years': list(range(2011, 2036)), - } + }, ) #mock out stripe so we don't hit their server From 35aacd20c1baa6030c23e73db62ef44be9e49ace Mon Sep 17 00:00:00 2001 From: j1z0 Date: Sat, 20 Aug 2016 12:08:14 -0400 Subject: [PATCH 02/42] splitting chapter 05 into three so moving everything accordingly --- .../chp03/django_ecommerce/main/tests.py | 37 ++ _chapters/chp03/django_ecommerce/test.db | Bin 49152 -> 51200 bytes .../chp05/django_ecommerce/payments/tests.py | 413 ------------------ _chapters/chp05/requirements.txt | 5 - .../django_ecommerce/django_ecommerce/urls.py | 17 - .../chp07/django_ecommerce/main/models.py | 3 - .../chp07/django_ecommerce/main/views.py | 12 - .../chp07/django_ecommerce/payments/admin.py | 9 - .../django_ecommerce/contact/__init__.py | 0 .../django_ecommerce/contact/admin.py | 0 .../django_ecommerce/contact/forms.py | 0 .../django_ecommerce/contact/models.py | 0 .../django_ecommerce/contact/views.py | 0 .../django_ecommerce/db_backup.json | 0 .../django_ecommerce/__init__.py | 0 .../django_ecommerce/settings.py | 0 .../django_ecommerce/django_ecommerce/urls.py | 0 .../django_ecommerce/django_ecommerce/wsgi.py | 0 .../django_ecommerce/main/__init__.py | 0 .../django_ecommerce/main/models.py | 0 .../django_ecommerce/main/views.py | 0 .../django_ecommerce/manage.py | 0 .../django_ecommerce/payments/__init__.py | 0 .../django_ecommerce/payments/admin.py | 0 .../django_ecommerce/payments/forms.py | 0 .../django_ecommerce/payments/models.py | 0 .../django_ecommerce/payments/views.py | 10 +- .../django_ecommerce/static/application.js | 0 .../django_ecommerce/static/bootstrap.css | 0 .../django_ecommerce/static/jquery.js | 0 .../django_ecommerce/static/jquery.min.js | 0 .../django_ecommerce/static/jquery_ujs.js | 0 .../django_ecommerce/templates/base.html | 0 .../django_ecommerce/templates/cardform.html | 0 .../django_ecommerce/templates/contact.html | 0 .../django_ecommerce/templates/edit.html | 0 .../django_ecommerce/templates/errors.html | 0 .../django_ecommerce/templates/field.html | 0 .../templates/flatpages/default.html | 0 .../django_ecommerce/templates/home.html | 0 .../django_ecommerce/templates/index.html | 0 .../django_ecommerce/templates/register.html | 0 .../django_ecommerce/templates/sign_in.html | 0 .../django_ecommerce/templates/test.html | 0 .../django_ecommerce/templates/user.html | 0 .../{chp05 => chp08}/django_ecommerce/test.db | Bin _chapters/{chp07 => chp08}/requirements.txt | 0 .../contact => chp08/tests}/__init__.py | 0 .../tests/contact}/__init__.py | 0 .../tests/contact/testContactModels.py | 0 .../tests}/main/__init__.py | 0 .../tests/main/testMainPageView.py | 2 +- .../tests}/payments/__init__.py | 0 .../tests/payments/testCustomer.py | 0 .../tests/payments/testForms.py | 0 .../tests/payments/testUserModel.py | 0 _chapters/chp08/tests/payments/testViews.py | 239 ++++++++++ .../chp09/django_ecommerce/contact/views.py | 2 +- .../django_ecommerce/settings.py | 3 +- .../chp09/django_ecommerce/main/models.py | 7 - .../chp09/django_ecommerce/main/views.py | 51 +-- .../chp09/django_ecommerce/payments/forms.py | 2 +- .../chp09/django_ecommerce/payments/models.py | 2 +- .../chp09/django_ecommerce/payments/views.py | 10 +- .../django_ecommerce/static/application.js | 0 .../django_ecommerce/static/bootstrap.css | 0 .../chp09/django_ecommerce/static/css/mec.css | 41 -- .../django_ecommerce/static/jquery.js | 0 .../django_ecommerce/static/jquery.min.js | 0 .../django_ecommerce/static/jquery_ujs.js | 0 .../django_ecommerce/static/js/application.js | 44 -- .../django_ecommerce/templates/base.html | 0 .../django_ecommerce/templates/cardform.html | 0 .../django_ecommerce/templates/contact.html | 0 .../django_ecommerce/templates/edit.html | 0 .../django_ecommerce/templates/errors.html | 0 .../django_ecommerce/templates/field.html | 0 .../templates/flatpages/default.html | 2 +- .../django_ecommerce/templates/home.html | 0 .../django_ecommerce/templates/index.html | 0 .../django_ecommerce/templates/main/user.html | 10 - .../templates/payments/_cardform.html | 44 -- .../templates/payments/_field.html | 6 - .../templates/payments/register.html | 42 -- .../django_ecommerce/templates/register.html | 0 .../django_ecommerce/templates/sign_in.html | 0 .../django_ecommerce/templates/test.html | 0 .../django_ecommerce/templates/user.html | 0 _chapters/chp09/django_ecommerce/test.db | Bin 49152 -> 51200 bytes _chapters/chp09/requirements.txt | 2 +- .../chp09/tests/contact/testContactModels.py | 6 +- .../chp09/tests/main/testMainPageView.py | 13 +- _chapters/chp09/tests/payments/testForms.py | 2 +- .../chp09/tests/payments/testUserModel.py | 4 +- _chapters/chp09/tests/payments/testViews.py | 24 +- .../chp10/django_ecommerce/main/models.py | 32 -- .../django_ecommerce/main/serializers.py | 42 -- .../chp10/django_ecommerce/payments/admin.py | 9 - .../django_ecommerce/static/js/application.js | 59 --- .../templates/payments/_cardform.html | 44 -- .../templates/payments/_field.html | 6 - .../templates/payments/register.html | 42 -- _chapters/chp10/requirements.txt | 6 - .../django_ecommerce/settings.py | 2 - .../django_ecommerce/django_ecommerce/urls.py | 7 +- .../chp11/django_ecommerce/main/models.py | 21 - .../chp11/django_ecommerce/main/views.py | 42 +- .../chp11/django_ecommerce/payments/models.py | 3 - .../chp11/django_ecommerce/static/css/mec.css | 38 -- .../django_ecommerce/static/js/application.js | 15 - .../django_ecommerce/templates/__base.html | 2 + .../django_ecommerce/templates/main/user.html | 23 +- _chapters/chp11/requirements.txt | 2 - .../chp11/tests/main/testMainPageView.py | 25 +- .../chp11/tests/payments/testUserModel.py | 2 +- .../chp12/django_ecommerce/contact/models.py | 4 +- .../django_ecommerce/settings.py | 3 +- .../django_ecommerce/django_ecommerce/urls.py | 3 - .../main/fixtures/initial_data.json | 0 .../chp12/django_ecommerce/main/models.py | 1 + .../django_ecommerce/main/serializers.py | 28 +- .../chp12/django_ecommerce/main/views.py | 38 ++ .../payments/fixtures/initial_data.json | 0 .../chp12/django_ecommerce/payments/models.py | 11 +- _chapters/chp12/requirements.txt | 1 - .../chp12/tests/main/testMainPageView.py | 9 +- _chapters/chp12/tests/main/testSerializers.py | 5 +- _chapters/chp12/tests/payments/testForms.py | 2 +- .../chp12/tests/payments/testUserModel.py | 11 +- .../chp13/django_ecommerce/contact/models.py | 4 +- .../main/fixtures/initial_data.json | 0 .../chp13/django_ecommerce/main/views.py | 38 ++ .../payments/fixtures/initial_data.json | 0 .../chp13/django_ecommerce/payments/models.py | 8 - _chapters/chp13/tests/main/testJSONViews.py | 3 +- .../chp13/tests/main/testMainPageView.py | 8 +- _chapters/chp13/tests/main/testSerializers.py | 2 +- _chapters/chp13/tests/payments/testForms.py | 2 +- .../chp13/tests/payments/testUserModel.py | 8 +- .../chp14/django_ecommerce/contact/models.py | 2 + .../django_ecommerce/settings.py | 1 - .../django_ecommerce/django_ecommerce/urls.py | 6 +- .../chp14/django_ecommerce/main/models.py | 2 - .../chp14/django_ecommerce/payments/models.py | 14 +- .../chp14/django_ecommerce/static/css/mec.css | 1 + .../django_ecommerce/static/js/application.js | 36 +- .../django_ecommerce/templates/__base.html | 10 +- .../templates/main/_jedibadge.html | 2 +- .../django_ecommerce/templates/main/user.html | 8 +- .../chp14/tests/contact/testContactModels.py | 3 +- _chapters/chp14/tests/main/testJSONViews.py | 6 +- .../chp14/tests/main/testMainPageView.py | 1 - _chapters/chp14/tests/main/testSerializers.py | 1 + .../django_ecommerce/settings.py | 1 - .../django_ecommerce/django_ecommerce/urls.py | 8 +- .../chp15/django_ecommerce/main/models.py | 2 - .../chp15/django_ecommerce/payments/forms.py | 20 +- .../chp15/django_ecommerce/payments/models.py | 11 +- .../chp15/django_ecommerce/payments/urls.py | 7 - .../chp15/django_ecommerce/payments/views.py | 53 +-- .../chp15/django_ecommerce/static/css/mec.css | 13 +- .../django_ecommerce/static/js/application.js | 119 +++-- .../django_ecommerce/templates/__base.html | 11 +- .../templates/main/_jedibadge.html | 2 +- .../django_ecommerce/templates/main/user.html | 8 +- .../templates/payments/_cardform.html | 46 +- .../templates/payments/_field.html | 8 +- .../templates/payments/register.html | 41 +- .../templates/payments/sign_in.html | 2 +- _chapters/chp15/tests/main/testJSONViews.py | 2 +- _chapters/chp15/tests/payments/testViews.py | 104 +++-- .../chp16/django_ecommerce/contact/models.py | 2 - .../django_ecommerce/settings.py | 8 +- .../django_ecommerce/django_ecommerce/urls.py | 6 - .../chp16/django_ecommerce/main/models.py | 2 + .../chp16/django_ecommerce/payments/forms.py | 20 +- .../chp16/django_ecommerce/payments/models.py | 4 +- .../chp16/django_ecommerce/payments/urls.py | 7 - .../chp16/django_ecommerce/payments/views.py | 53 +-- .../chp16/django_ecommerce/static/css/mec.css | 19 +- .../django_ecommerce/static/js/application.js | 73 ++-- .../django_ecommerce/templates/__base.html | 11 +- .../templates/payments/_cardform.html | 46 +- .../templates/payments/_field.html | 8 +- .../templates/payments/register.html | 41 +- .../templates/payments/sign_in.html | 2 +- .../chp16/django_ecommerce/usermap/admin.py | 3 - _chapters/chp16/requirements.txt | 3 - .../chp16/tests/contact/testContactModels.py | 3 +- _chapters/chp16/tests/payments/testViews.py | 104 +++-- .../chp17/django_ecommerce/contact/models.py | 1 - .../django_ecommerce/settings.py | 14 +- .../django_ecommerce/django_ecommerce/urls.py | 8 +- .../django_ecommerce/djangular_polls/admin.py | 35 +- .../chp17/django_ecommerce/main/models.py | 51 +-- .../chp17/django_ecommerce/payments/admin.py | 20 +- .../django_ecommerce/payments/json_views.py | 40 +- .../chp17/django_ecommerce/payments/models.py | 12 +- .../chp17/django_ecommerce/payments/urls.py | 3 - .../chp17/django_ecommerce/payments/views.py | 2 +- .../chp17/django_ecommerce/static/css/mec.css | 7 +- .../django_ecommerce/static/js/application.js | 2 +- .../static/js/registerCtrl.js | 27 +- .../django_ecommerce/templates/__base.html | 10 +- .../templates/main/_announcements.html | 2 +- .../templates/main/_badges.html | 6 +- .../chp17/django_ecommerce/usermap/admin.py | 3 - _chapters/chp17/requirements.txt | 5 - .../chp17/tests/contact/testContactModels.py | 4 +- _chapters/chp17/tests/payments/testViews.py | 2 +- .../django_ecommerce/settings.py | 6 +- .../django_ecommerce/django_ecommerce/urls.py | 4 +- .../django_ecommerce/djangular_polls/admin.py | 35 +- .../chp18/django_ecommerce/main/models.py | 49 +-- .../chp18/django_ecommerce/payments/admin.py | 20 +- .../django_ecommerce/payments/json_views.py | 34 +- .../chp18/django_ecommerce/payments/models.py | 12 +- .../chp18/django_ecommerce/payments/urls.py | 3 - .../chp18/django_ecommerce/payments/views.py | 2 +- .../templates/main/_announcements.html | 2 +- .../templates/main/_badges.html | 6 +- _chapters/chp18/requirements.txt | 2 - .../tests => chp18/tests/contact}/__init__.py | 0 .../tests/contact/testContactModels.py | 0 .../contact => chp18/tests/main}/__init__.py | 0 .../tests/{unit => }/main/testJSONViews.py | 0 .../tests/{unit => }/main/testMainPageView.py | 0 .../tests/{unit => }/main/testSerializers.py | 0 .../main => chp18/tests/payments}/__init__.py | 0 .../tests/payments/testCustomer.py | 0 .../tests/{unit => }/payments/testForms.py | 0 .../{unit => }/payments/testUserModel.py | 0 .../tests/{unit => }/payments/testViews.py | 0 .../chp19/django_ecommerce/contact/models.py | 1 + .../django_ecommerce/settings.py | 5 - .../chp19/django_ecommerce/main/models.py | 2 +- _chapters/chp19/requirements.txt | 6 - .../tests/contact}/__init__.py | 0 .../tests}/contact/testContactModels.py | 0 .../tests/main}/__init__.py | 0 .../tests/{unit => }/main/testJSONViews.py | 0 .../tests/{unit => }/main/testMainPageView.py | 0 .../tests/{unit => }/main/testSerializers.py | 0 .../tests/payments}/__init__.py | 0 .../tests}/payments/testCustomer.py | 0 .../tests/{unit => }/payments/testForms.py | 0 .../{unit => }/payments/testUserModel.py | 0 .../tests/payments/testViews.py | 126 ++---- .../{chp13 => chp20}/angular_test/index.html | 0 .../{chp13 => chp20}/angular_test/polls.html | 0 .../django_ecommerce/contact}/__init__.py | 0 .../django_ecommerce/contact/admin.py | 0 .../django_ecommerce/contact/forms.py | 0 .../contact/migrations/0001_initial.py | 0 .../migrations/0002_auto_20150606_0307.py | 0 .../contact/migrations}/__init__.py | 0 .../django_ecommerce/contact/models.py | 4 +- .../django_ecommerce/contact/views.py | 0 .../django_ecommerce/db_backup.json | 0 .../django_ecommerce}/__init__.py | 0 .../django_ecommerce/guitest_settings.py | 0 .../django_ecommerce/settings.py | 16 +- .../django_ecommerce/django_ecommerce/urls.py | 19 +- .../django_ecommerce/django_ecommerce/wsgi.py | 0 .../djangular_polls}/__init__.py | 0 .../django_ecommerce/djangular_polls/admin.py | 36 ++ .../djangular_polls/json_views.py | 0 .../migrations/0001_initial.py | 0 .../0002_remove_pollitem_percentage.py | 0 .../djangular_polls/migrations}/__init__.py | 0 .../djangular_polls/models.py | 0 .../djangular_polls/serializers.py | 0 .../django_ecommerce/djangular_polls/tests.py | 0 .../django_ecommerce/djangular_polls/urls.py | 0 .../django_ecommerce/djangular_polls/views.py | 0 .../django_ecommerce/main}/__init__.py | 0 .../django_ecommerce/main/admin.py | 0 .../main/fixtures/system_data.json | 0 .../django_ecommerce/main/json_views.py | 0 .../migrations/0001_auto_20150311_1303.py | 0 .../main/migrations/0001_initial.py | 0 .../migrations/0002_auto_20150311_1313.py | 0 .../main/migrations/0002_statusreport.py | 0 .../migrations/0003_auto_20150606_0720.py | 0 .../main/migrations}/__init__.py | 0 .../data_load_marketing_items_0003.py | 0 .../chp20/django_ecommerce/main/models.py | 64 +++ .../django_ecommerce/main/permissions.py | 0 .../django_ecommerce/main/serializers.py | 0 .../main/templatetags}/__init__.py | 0 .../main/templatetags/main_gravatar.py | 0 .../main/templatetags/main_marketing.py | 0 .../django_ecommerce/main/urls.py | 0 .../django_ecommerce/main/views.py | 38 -- .../django_ecommerce/manage.py | 0 .../media/announce/main-logo.png | Bin .../django_ecommerce/media/images/fig.png | Bin .../django_ecommerce/payments}/__init__.py | 0 .../chp20/django_ecommerce/payments/admin.py | 23 + .../payments/fixtures/system_data.json | 0 .../django_ecommerce/payments/forms.py | 20 +- .../django_ecommerce/payments/json_views.py | 34 +- .../payments/migrations/0001_initial.py | 0 .../payments/migrations/0003_initial_data.py | 0 .../migrations/0004_auto_20141001_0546.py | 0 .../migrations/0005_bigcoId_migration.py | 0 .../migrations/0006_auto_20141007_0904.py | 0 .../migrations/0007_auto_20150311_1303.py | 0 .../migrations/0007_auto_20150606_0307.py | 0 .../migrations/0008_auto_20150311_1313.py | 0 .../payments/migrations/0009_merge.py | 0 .../payments}/migrations/__init__.py | 0 .../django_ecommerce/payments/models.py | 11 +- .../django_ecommerce/payments/serializers.py | 0 .../chp20/django_ecommerce/payments/urls.py | 10 + .../django_ecommerce/payments/views.py | 53 ++- .../django_ecommerce/requirements.txt | 0 .../django_ecommerce/runner.py | 0 .../static/css/bootstrap-theme.css | 0 .../static/css/bootstrap-theme.css.map | 0 .../static/css/bootstrap-theme.min.css | 0 .../django_ecommerce/static/css/bootstrap.css | 0 .../static/css/bootstrap.css.map | 0 .../static/css/bootstrap.min.css | 0 .../django_ecommerce/static/css/mec.css | 20 +- .../django_ecommerce/static/css/signin.css | 0 .../static/fonts/Starjedi.eot | Bin .../static/fonts/Starjedi.svg | 0 .../static/fonts/Starjedi.ttf | Bin .../static/fonts/Starjedi.woff | Bin .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../static/img/clone_army.jpg | Bin .../django_ecommerce/static/img/darth.jpg | Bin .../django_ecommerce/static/img/leia.jpg | Bin .../static/img/star-wars-battle.jpg | Bin .../static/img/star-wars-cast.jpg | Bin .../django_ecommerce/static/img/yoda.jpg | Bin .../django_ecommerce/static/js/admin.js | 0 .../static/js/angular-google-maps.min.js | 0 .../django_ecommerce/static/js/angular.min.js | 0 .../static/js/angular.min.js.map | 0 .../django_ecommerce/static/js/application.js | 68 +++ .../django_ecommerce/static/js/bootstrap.js | 0 .../static/js/bootstrap.min.js | 0 .../static/js/jquery-1.11.1.min.js | 0 .../static/js/lodash.underscore.min.js | 0 .../static/js/loggedInCtrl.js | 0 .../static/js/registerCtrl.js | 0 .../static/js/ui-bootstrap-tpls-0.11.0.min.js | 0 .../static/js/ui-bootstrap-tpls.min.js | 0 .../django_ecommerce/static/js/userMapCtrl.js | 0 .../static/js/userPollCtrl.js | 0 .../django_ecommerce/templates/__base.html | 21 +- .../admin/payments/user/change_form.html | 0 .../templates/contact/contact.html | 0 .../templates/djangular_polls/_polls.html | 0 .../templates/flatpages/default.html | 0 .../templates/main/_announcements.html | 2 +- .../templates/main/_badges.html | 6 +- .../templates/main/_jedibadge.html | 2 +- .../templates/main/_lateststatus.html | 0 .../templates/main/_statusupdate.html | 0 .../templates/main/index.html | 0 .../main/templatetags/circle_item.html | 0 .../django_ecommerce/templates/main/user.html | 8 +- .../templates/payments/_cardform.html | 56 +++ .../templates/payments/_field.html | 12 + .../templates/payments/edit.html | 0 .../templates/payments/errors.html | 0 .../templates/payments/register.html | 35 ++ .../templates/payments/sign_in.html | 2 +- .../templates/rest_framework/api.html | 0 .../templates/usermap/usermap.html | 0 .../{chp10 => chp20}/django_ecommerce/test.db | Bin .../django_ecommerce/usermap}/__init__.py | 0 .../django_ecommerce/usermap}/admin.py | 0 .../django_ecommerce/usermap/json_views.py | 0 .../usermap}/migrations/__init__.py | 0 .../django_ecommerce/usermap/models.py | 0 .../django_ecommerce/usermap}/tests.py | 0 .../django_ecommerce/usermap/urls.py | 0 .../django_ecommerce/usermap/views.py | 0 _chapters/chp20/requirements.txt | 12 + .../migrations => chp20/tests}/__init__.py | 0 .../tests/gui}/__init__.py | 0 .../tests/gui/pages}/__init__.py | 0 .../tests/gui/pages/testPage.py | 0 .../tests/unit}/__init__.py | 0 .../tests/unit/contact}/__init__.py | 0 .../tests/unit/contact/testContactModels.py | 0 .../tests/unit/main}/__init__.py | 0 .../tests/unit}/main/testJSONViews.py | 1 - .../tests/unit}/main/testMainPageView.py | 8 +- .../tests/unit}/main/testSerializers.py | 2 +- .../tests/unit/payments}/__init__.py | 0 .../tests/unit/payments/testCustomer.py | 0 .../tests/unit}/payments/testForms.py | 2 +- .../tests/unit}/payments/testUserModel.py | 11 +- .../tests/unit/payments/testViews.py | 0 .../{chp14 => chp21}/angular_test/index.html | 0 .../{chp14 => chp21}/angular_test/polls.html | 0 _chapters/{chp19 => chp21}/deploy/fabfile.py | 0 .../{chp19 => chp21}/deploy/gunicorn_start | 0 .../deploy/nginx/sites-available/mec | 0 .../{chp19 => chp21}/deploy/settings_prod.py | 0 .../deploy/supervisor/mec.conf | 0 .../django_ecommerce/contact}/__init__.py | 0 .../django_ecommerce/contact/admin.py | 0 .../django_ecommerce/contact/forms.py | 0 .../contact/migrations/0001_initial.py | 0 .../migrations/0002_auto_20150606_0307.py | 0 .../contact/migrations}/__init__.py | 0 .../django_ecommerce/contact/models.py | 6 +- .../django_ecommerce/contact/views.py | 2 +- .../chp21/django_ecommerce/db_backup.json | 1 + .../django_ecommerce}/__init__.py | 0 .../django_ecommerce/guitest_settings.py | 0 .../django_ecommerce/settings.py | 29 +- .../django_ecommerce/django_ecommerce/urls.py | 34 ++ .../django_ecommerce/django_ecommerce/wsgi.py | 0 .../djangular_polls}/__init__.py | 0 .../django_ecommerce/djangular_polls/admin.py | 36 ++ .../djangular_polls/json_views.py | 0 .../migrations/0001_initial.py | 0 .../0002_remove_pollitem_percentage.py | 0 .../djangular_polls/migrations}/__init__.py | 0 .../djangular_polls/models.py | 0 .../djangular_polls/serializers.py | 0 .../djangular_polls}/tests.py | 0 .../django_ecommerce/djangular_polls/urls.py | 0 .../django_ecommerce/djangular_polls/views.py | 0 .../django_ecommerce/main}/__init__.py | 0 .../django_ecommerce/main/admin.py | 0 .../main/fixtures/system_data.json | 0 .../django_ecommerce/main/json_views.py | 0 .../migrations/0001_auto_20150311_1303.py | 0 .../main/migrations/0001_initial.py | 0 .../migrations/0002_auto_20150311_1313.py | 0 .../main/migrations/0002_statusreport.py | 0 .../migrations/0003_auto_20150606_0720.py | 0 .../main/migrations}/__init__.py | 0 .../data_load_marketing_items_0003.py | 0 .../chp21/django_ecommerce/main/models.py | 64 +++ .../django_ecommerce/main/permissions.py | 0 .../django_ecommerce/main/serializers.py | 28 ++ .../main/templatetags}/__init__.py | 0 .../main/templatetags/main_gravatar.py | 0 .../main/templatetags/main_marketing.py | 0 .../django_ecommerce/main/urls.py | 0 .../chp21/django_ecommerce/main/views.py | 51 +++ .../django_ecommerce/manage.py | 0 .../media/announce/main-logo.png | Bin .../django_ecommerce/media/images/fig.png | Bin .../django_ecommerce}/payments/__init__.py | 0 .../chp21/django_ecommerce/payments/admin.py | 23 + .../payments/fixtures/system_data.json | 0 .../django_ecommerce/payments/forms.py | 24 +- .../django_ecommerce/payments/json_views.py | 40 +- .../payments/migrations/0001_initial.py | 0 .../payments/migrations/0003_initial_data.py | 0 .../migrations/0004_auto_20141001_0546.py | 0 .../migrations/0005_bigcoId_migration.py | 0 .../migrations/0006_auto_20141007_0904.py | 0 .../migrations/0007_auto_20150311_1303.py | 0 .../migrations/0007_auto_20150606_0307.py | 0 .../migrations/0008_auto_20150311_1313.py | 0 .../payments/migrations/0009_merge.py | 0 .../payments/migrations}/__init__.py | 0 .../django_ecommerce/payments/models.py | 16 +- .../django_ecommerce/payments/serializers.py | 0 .../chp21/django_ecommerce/payments/urls.py | 10 + .../django_ecommerce/payments/views.py | 55 ++- .../django_ecommerce/requirements.txt | 0 .../django_ecommerce/runner.py | 0 .../static/css/bootstrap-theme.css | 0 .../static/css/bootstrap-theme.css.map | 0 .../static/css/bootstrap-theme.min.css | 0 .../django_ecommerce/static/css/bootstrap.css | 0 .../static/css/bootstrap.css.map | 0 .../static/css/bootstrap.min.css | 0 .../chp21/django_ecommerce/static/css/mec.css | 95 ++++ .../django_ecommerce/static/css/signin.css | 0 .../static/fonts/Starjedi.eot | Bin .../static/fonts/Starjedi.svg | 0 .../static/fonts/Starjedi.ttf | Bin .../static/fonts/Starjedi.woff | Bin .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../static/img/clone_army.jpg | Bin .../django_ecommerce/static/img/darth.jpg | Bin .../django_ecommerce/static/img/leia.jpg | Bin .../static/img/star-wars-battle.jpg | Bin .../static/img/star-wars-cast.jpg | Bin .../django_ecommerce/static/img/yoda.jpg | Bin .../django_ecommerce/static/js/admin.js | 0 .../static/js/angular-google-maps.min.js | 0 .../django_ecommerce/static/js/angular.min.js | 0 .../static/js/angular.min.js.map | 0 .../django_ecommerce/static/js/application.js | 68 +++ .../django_ecommerce/static/js/bootstrap.js | 0 .../static/js/bootstrap.min.js | 0 .../static/js/jquery-1.11.1.min.js | 0 .../static/js/lodash.underscore.min.js | 0 .../static/js/loggedInCtrl.js | 0 .../static/js/registerCtrl.js | 27 +- .../static/js/ui-bootstrap-tpls-0.11.0.min.js | 0 .../static/js/ui-bootstrap-tpls.min.js | 0 .../django_ecommerce/static/js/userMapCtrl.js | 0 .../static/js/userPollCtrl.js | 0 .../django_ecommerce/templates/__base.html | 23 +- .../admin/payments/user/change_form.html | 0 .../templates/contact/contact.html | 0 .../templates/djangular_polls/_polls.html | 0 .../templates/flatpages/default.html | 2 +- .../templates/main/_announcements.html | 2 +- .../templates/main/_badges.html | 6 +- .../templates/main/_jedibadge.html | 2 +- .../templates/main/_lateststatus.html | 0 .../templates/main/_statusupdate.html | 0 .../templates/main/index.html | 0 .../main/templatetags/circle_item.html | 0 .../django_ecommerce/templates/main/user.html | 27 ++ .../templates/payments/_cardform.html | 56 +++ .../templates/payments/_field.html | 12 + .../templates/payments/edit.html | 0 .../templates/payments/errors.html | 0 .../templates/payments/register.html | 35 ++ .../templates/payments/sign_in.html | 2 +- .../templates/rest_framework/api.html | 0 .../templates/usermap/usermap.html | 0 .../{chp07 => chp21}/django_ecommerce/test.db | Bin 51200 -> 49152 bytes .../django_ecommerce/usermap}/__init__.py | 0 .../django_ecommerce/usermap}/admin.py | 0 .../django_ecommerce/usermap/json_views.py | 0 .../usermap/migrations}/__init__.py | 0 .../django_ecommerce/usermap/models.py | 0 .../django_ecommerce/usermap/tests.py | 0 .../django_ecommerce/usermap/urls.py | 0 .../django_ecommerce/usermap/views.py | 0 _chapters/chp21/requirements.txt | 18 + .../unit/contact => chp21/tests}/__init__.py | 0 .../unit/main => chp21/tests/gui}/__init__.py | 0 .../tests/gui/pages}/__init__.py | 0 .../tests/gui/pages/testPage.py | 0 _chapters/chp21/tests/unit/__init__.py | 0 .../chp21/tests/unit/contact/__init__.py | 0 .../tests/unit/contact/testContactModels.py} | 12 +- _chapters/chp21/tests/unit/main/__init__.py | 0 .../tests/unit}/main/testJSONViews.py | 6 +- .../tests/unit/main/testMainPageView.py} | 41 +- .../tests/unit}/main/testSerializers.py | 4 +- .../chp21/tests/unit/payments/__init__.py | 0 .../chp21/tests/unit/payments/testCustomer.py | 24 + .../chp21/tests/unit/payments/testForms.py | 99 +++++ .../tests/unit/payments/testUserModel.py | 32 ++ .../tests/unit}/payments/testViews.py | 104 ++--- 561 files changed, 2472 insertions(+), 2450 deletions(-) delete mode 100644 _chapters/chp05/django_ecommerce/payments/tests.py delete mode 100644 _chapters/chp05/requirements.txt delete mode 100644 _chapters/chp07/django_ecommerce/django_ecommerce/urls.py delete mode 100644 _chapters/chp07/django_ecommerce/main/models.py delete mode 100644 _chapters/chp07/django_ecommerce/main/views.py delete mode 100644 _chapters/chp07/django_ecommerce/payments/admin.py rename _chapters/{chp05 => chp08}/django_ecommerce/contact/__init__.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/contact/admin.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/contact/forms.py (100%) rename _chapters/{chp07 => chp08}/django_ecommerce/contact/models.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/contact/views.py (100%) rename _chapters/{chp07 => chp08}/django_ecommerce/db_backup.json (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/django_ecommerce/__init__.py (100%) rename _chapters/{chp07 => chp08}/django_ecommerce/django_ecommerce/settings.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/django_ecommerce/urls.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/django_ecommerce/wsgi.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/main/__init__.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/main/models.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/main/views.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/manage.py (100%) mode change 100755 => 100644 rename _chapters/{chp05 => chp08}/django_ecommerce/payments/__init__.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/payments/admin.py (100%) rename _chapters/{chp07 => chp08}/django_ecommerce/payments/forms.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/payments/models.py (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/payments/views.py (95%) rename _chapters/{chp05 => chp08}/django_ecommerce/static/application.js (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/static/bootstrap.css (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/static/jquery.js (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/static/jquery.min.js (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/static/jquery_ujs.js (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/base.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/cardform.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/contact.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/edit.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/errors.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/field.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/flatpages/default.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/home.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/index.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/register.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/sign_in.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/test.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/templates/user.html (100%) rename _chapters/{chp05 => chp08}/django_ecommerce/test.db (100%) rename _chapters/{chp07 => chp08}/requirements.txt (100%) rename _chapters/{chp07/django_ecommerce/contact => chp08/tests}/__init__.py (100%) rename _chapters/{chp07/django_ecommerce/django_ecommerce => chp08/tests/contact}/__init__.py (100%) rename _chapters/{chp07 => chp08}/tests/contact/testContactModels.py (100%) rename _chapters/{chp07/django_ecommerce => chp08/tests}/main/__init__.py (100%) rename _chapters/{chp07 => chp08}/tests/main/testMainPageView.py (97%) rename _chapters/{chp07/django_ecommerce => chp08/tests}/payments/__init__.py (100%) rename _chapters/{chp07 => chp08}/tests/payments/testCustomer.py (100%) rename _chapters/{chp07 => chp08}/tests/payments/testForms.py (100%) rename _chapters/{chp07 => chp08}/tests/payments/testUserModel.py (100%) create mode 100644 _chapters/chp08/tests/payments/testViews.py rename _chapters/{chp07 => chp09}/django_ecommerce/static/application.js (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/static/bootstrap.css (100%) delete mode 100644 _chapters/chp09/django_ecommerce/static/css/mec.css rename _chapters/{chp07 => chp09}/django_ecommerce/static/jquery.js (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/static/jquery.min.js (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/static/jquery_ujs.js (100%) delete mode 100755 _chapters/chp09/django_ecommerce/static/js/application.js rename _chapters/{chp07 => chp09}/django_ecommerce/templates/base.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/cardform.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/contact.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/edit.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/errors.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/field.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/home.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/index.html (100%) delete mode 100644 _chapters/chp09/django_ecommerce/templates/main/user.html delete mode 100644 _chapters/chp09/django_ecommerce/templates/payments/_cardform.html delete mode 100644 _chapters/chp09/django_ecommerce/templates/payments/_field.html delete mode 100644 _chapters/chp09/django_ecommerce/templates/payments/register.html rename _chapters/{chp07 => chp09}/django_ecommerce/templates/register.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/sign_in.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/test.html (100%) rename _chapters/{chp07 => chp09}/django_ecommerce/templates/user.html (100%) delete mode 100644 _chapters/chp10/django_ecommerce/main/models.py delete mode 100644 _chapters/chp10/django_ecommerce/main/serializers.py delete mode 100644 _chapters/chp10/django_ecommerce/payments/admin.py delete mode 100755 _chapters/chp10/django_ecommerce/static/js/application.js delete mode 100644 _chapters/chp10/django_ecommerce/templates/payments/_cardform.html delete mode 100644 _chapters/chp10/django_ecommerce/templates/payments/_field.html delete mode 100644 _chapters/chp10/django_ecommerce/templates/payments/register.html delete mode 100644 _chapters/chp10/requirements.txt rename _chapters/{chp09 => chp12}/django_ecommerce/main/fixtures/initial_data.json (100%) rename _chapters/{chp10 => chp12}/django_ecommerce/payments/fixtures/initial_data.json (100%) rename _chapters/{chp10 => chp13}/django_ecommerce/main/fixtures/initial_data.json (100%) rename _chapters/{chp11 => chp13}/django_ecommerce/payments/fixtures/initial_data.json (100%) delete mode 100644 _chapters/chp15/django_ecommerce/payments/urls.py delete mode 100644 _chapters/chp16/django_ecommerce/payments/urls.py delete mode 100644 _chapters/chp16/django_ecommerce/usermap/admin.py delete mode 100644 _chapters/chp17/django_ecommerce/usermap/admin.py rename _chapters/{chp07/tests => chp18/tests/contact}/__init__.py (100%) rename _chapters/{chp10 => chp18}/tests/contact/testContactModels.py (100%) rename _chapters/{chp07/tests/contact => chp18/tests/main}/__init__.py (100%) rename _chapters/chp18/tests/{unit => }/main/testJSONViews.py (100%) rename _chapters/chp18/tests/{unit => }/main/testMainPageView.py (100%) rename _chapters/chp18/tests/{unit => }/main/testSerializers.py (100%) rename _chapters/{chp07/tests/main => chp18/tests/payments}/__init__.py (100%) rename _chapters/{chp10 => chp18}/tests/payments/testCustomer.py (100%) rename _chapters/chp18/tests/{unit => }/payments/testForms.py (100%) rename _chapters/chp18/tests/{unit => }/payments/testUserModel.py (100%) rename _chapters/chp18/tests/{unit => }/payments/testViews.py (100%) rename _chapters/{chp07/tests/payments => chp19/tests/contact}/__init__.py (100%) rename _chapters/{chp18/tests/unit => chp19/tests}/contact/testContactModels.py (100%) rename _chapters/{chp09/django_ecommerce/main/templatetags => chp19/tests/main}/__init__.py (100%) rename _chapters/chp19/tests/{unit => }/main/testJSONViews.py (100%) rename _chapters/chp19/tests/{unit => }/main/testMainPageView.py (100%) rename _chapters/chp19/tests/{unit => }/main/testSerializers.py (100%) rename _chapters/{chp10/django_ecommerce/contact => chp19/tests/payments}/__init__.py (100%) rename _chapters/{chp18/tests/unit => chp19/tests}/payments/testCustomer.py (100%) rename _chapters/chp19/tests/{unit => }/payments/testForms.py (100%) rename _chapters/chp19/tests/{unit => }/payments/testUserModel.py (100%) rename _chapters/{chp07 => chp19}/tests/payments/testViews.py (69%) rename _chapters/{chp13 => chp20}/angular_test/index.html (100%) rename _chapters/{chp13 => chp20}/angular_test/polls.html (100%) rename _chapters/{chp10/django_ecommerce/django_ecommerce => chp20/django_ecommerce/contact}/__init__.py (100%) rename _chapters/{chp07 => chp20}/django_ecommerce/contact/admin.py (100%) rename _chapters/{chp07 => chp20}/django_ecommerce/contact/forms.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/contact/migrations/0001_initial.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py (100%) rename _chapters/{chp10/django_ecommerce/main => chp20/django_ecommerce/contact/migrations}/__init__.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/contact/models.py (78%) rename _chapters/{chp10 => chp20}/django_ecommerce/contact/views.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/db_backup.json (100%) rename _chapters/{chp10/django_ecommerce/main/templatetags => chp20/django_ecommerce/django_ecommerce}/__init__.py (100%) rename _chapters/{chp18 => chp20}/django_ecommerce/django_ecommerce/guitest_settings.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/django_ecommerce/settings.py (94%) rename _chapters/{chp10 => chp20}/django_ecommerce/django_ecommerce/urls.py (52%) rename _chapters/{chp07 => chp20}/django_ecommerce/django_ecommerce/wsgi.py (100%) rename _chapters/{chp10/django_ecommerce/payments => chp20/django_ecommerce/djangular_polls}/__init__.py (100%) create mode 100644 _chapters/chp20/django_ecommerce/djangular_polls/admin.py rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/json_views.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/migrations/0001_initial.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py (100%) rename _chapters/{chp10/tests => chp20/django_ecommerce/djangular_polls/migrations}/__init__.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/models.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/serializers.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/tests.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/urls.py (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/djangular_polls/views.py (100%) rename _chapters/{chp10/tests/contact => chp20/django_ecommerce/main}/__init__.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/main/admin.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/main/fixtures/system_data.json (100%) rename _chapters/{chp11 => chp20}/django_ecommerce/main/json_views.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/main/migrations/0001_auto_20150311_1303.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/main/migrations/0001_initial.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/main/migrations/0002_auto_20150311_1313.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/main/migrations/0002_statusreport.py (100%) rename _chapters/{chp18 => chp20}/django_ecommerce/main/migrations/0003_auto_20150606_0720.py (100%) rename _chapters/{chp10/tests/main => chp20/django_ecommerce/main/migrations}/__init__.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/main/migrations/data_load_marketing_items_0003.py (100%) create mode 100644 _chapters/chp20/django_ecommerce/main/models.py rename _chapters/{chp11 => chp20}/django_ecommerce/main/permissions.py (100%) rename _chapters/{chp11 => chp20}/django_ecommerce/main/serializers.py (100%) rename _chapters/{chp10/tests/payments => chp20/django_ecommerce/main/templatetags}/__init__.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/main/templatetags/main_gravatar.py (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/main/templatetags/main_marketing.py (100%) rename _chapters/{chp11 => chp20}/django_ecommerce/main/urls.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/main/views.py (54%) rename _chapters/{chp07 => chp20}/django_ecommerce/manage.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/media/announce/main-logo.png (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/media/images/fig.png (100%) rename _chapters/{chp12/django_ecommerce/contact/migrations => chp20/django_ecommerce/payments}/__init__.py (100%) create mode 100644 _chapters/chp20/django_ecommerce/payments/admin.py rename _chapters/{chp12 => chp20}/django_ecommerce/payments/fixtures/system_data.json (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/payments/forms.py (67%) rename _chapters/{chp16 => chp20}/django_ecommerce/payments/json_views.py (64%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0001_initial.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0003_initial_data.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0005_bigcoId_migration.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py (100%) rename _chapters/{chp12 => chp20}/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/payments/migrations/0009_merge.py (100%) rename _chapters/{chp12/django_ecommerce/main => chp20/django_ecommerce/payments}/migrations/__init__.py (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/payments/models.py (81%) rename _chapters/{chp17 => chp20}/django_ecommerce/payments/serializers.py (100%) create mode 100644 _chapters/chp20/django_ecommerce/payments/urls.py rename _chapters/{chp10 => chp20}/django_ecommerce/payments/views.py (76%) rename _chapters/{chp18 => chp20}/django_ecommerce/requirements.txt (100%) rename _chapters/{chp18 => chp20}/django_ecommerce/runner.py (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap-theme.css (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap-theme.css.map (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap-theme.min.css (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap.css (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap.css.map (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/bootstrap.min.css (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/static/css/mec.css (84%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/css/signin.css (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/Starjedi.eot (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/Starjedi.svg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/Starjedi.ttf (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/Starjedi.woff (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/clone_army.jpg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/darth.jpg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/leia.jpg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/star-wars-battle.jpg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/star-wars-cast.jpg (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/img/yoda.jpg (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/static/js/admin.js (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/static/js/angular-google-maps.min.js (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/static/js/angular.min.js (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/static/js/angular.min.js.map (100%) create mode 100755 _chapters/chp20/django_ecommerce/static/js/application.js rename _chapters/{chp09 => chp20}/django_ecommerce/static/js/bootstrap.js (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/js/bootstrap.min.js (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/static/js/jquery-1.11.1.min.js (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/static/js/lodash.underscore.min.js (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/static/js/loggedInCtrl.js (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/static/js/registerCtrl.js (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js (100%) rename _chapters/{chp17 => chp20}/django_ecommerce/static/js/ui-bootstrap-tpls.min.js (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/static/js/userMapCtrl.js (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/static/js/userPollCtrl.js (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/__base.html (73%) rename _chapters/{chp17 => chp20}/django_ecommerce/templates/admin/payments/user/change_form.html (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/templates/contact/contact.html (100%) rename _chapters/{chp14 => chp20}/django_ecommerce/templates/djangular_polls/_polls.html (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/flatpages/default.html (100%) rename _chapters/{chp11 => chp20}/django_ecommerce/templates/main/_announcements.html (88%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/main/_badges.html (60%) rename _chapters/{chp11 => chp20}/django_ecommerce/templates/main/_jedibadge.html (83%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/main/_lateststatus.html (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/main/_statusupdate.html (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/templates/main/index.html (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/templates/main/templatetags/circle_item.html (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/templates/main/user.html (70%) create mode 100644 _chapters/chp20/django_ecommerce/templates/payments/_cardform.html create mode 100644 _chapters/chp20/django_ecommerce/templates/payments/_field.html rename _chapters/{chp09 => chp20}/django_ecommerce/templates/payments/edit.html (100%) rename _chapters/{chp09 => chp20}/django_ecommerce/templates/payments/errors.html (100%) create mode 100644 _chapters/chp20/django_ecommerce/templates/payments/register.html rename _chapters/{chp09 => chp20}/django_ecommerce/templates/payments/sign_in.html (93%) rename _chapters/{chp11 => chp20}/django_ecommerce/templates/rest_framework/api.html (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/templates/usermap/usermap.html (100%) rename _chapters/{chp10 => chp20}/django_ecommerce/test.db (100%) rename _chapters/{chp12/django_ecommerce/payments/migrations => chp20/django_ecommerce/usermap}/__init__.py (100%) rename _chapters/{chp14/django_ecommerce/djangular_polls => chp20/django_ecommerce/usermap}/admin.py (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/usermap/json_views.py (100%) rename _chapters/{chp13/django_ecommerce/contact => chp20/django_ecommerce/usermap}/migrations/__init__.py (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/usermap/models.py (100%) rename _chapters/{chp15/django_ecommerce/djangular_polls => chp20/django_ecommerce/usermap}/tests.py (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/usermap/urls.py (100%) rename _chapters/{chp16 => chp20}/django_ecommerce/usermap/views.py (100%) create mode 100644 _chapters/chp20/requirements.txt rename _chapters/{chp13/django_ecommerce/main/migrations => chp20/tests}/__init__.py (100%) rename _chapters/{chp13/django_ecommerce/payments/migrations => chp20/tests/gui}/__init__.py (100%) rename _chapters/{chp14/django_ecommerce/djangular_polls => chp20/tests/gui/pages}/__init__.py (100%) rename _chapters/{chp18 => chp20}/tests/gui/pages/testPage.py (100%) rename _chapters/{chp14/django_ecommerce/djangular_polls/migrations => chp20/tests/unit}/__init__.py (100%) rename _chapters/{chp15/django_ecommerce/djangular_polls => chp20/tests/unit/contact}/__init__.py (100%) rename _chapters/{chp19 => chp20}/tests/unit/contact/testContactModels.py (100%) rename _chapters/{chp15/django_ecommerce/djangular_polls/migrations => chp20/tests/unit/main}/__init__.py (100%) rename _chapters/{chp11/tests => chp20/tests/unit}/main/testJSONViews.py (99%) rename _chapters/{chp10/tests => chp20/tests/unit}/main/testMainPageView.py (87%) rename _chapters/{chp11/tests => chp20/tests/unit}/main/testSerializers.py (99%) rename _chapters/{chp16/django_ecommerce/usermap => chp20/tests/unit/payments}/__init__.py (100%) rename _chapters/{chp19 => chp20}/tests/unit/payments/testCustomer.py (100%) rename _chapters/{chp10/tests => chp20/tests/unit}/payments/testForms.py (98%) rename _chapters/{chp10/tests => chp20/tests/unit}/payments/testUserModel.py (70%) rename _chapters/{chp19 => chp20}/tests/unit/payments/testViews.py (100%) rename _chapters/{chp14 => chp21}/angular_test/index.html (100%) rename _chapters/{chp14 => chp21}/angular_test/polls.html (100%) rename _chapters/{chp19 => chp21}/deploy/fabfile.py (100%) rename _chapters/{chp19 => chp21}/deploy/gunicorn_start (100%) rename _chapters/{chp19 => chp21}/deploy/nginx/sites-available/mec (100%) rename _chapters/{chp19 => chp21}/deploy/settings_prod.py (100%) rename _chapters/{chp19 => chp21}/deploy/supervisor/mec.conf (100%) rename _chapters/{chp16/django_ecommerce/usermap/migrations => chp21/django_ecommerce/contact}/__init__.py (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/contact/admin.py (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/contact/forms.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/contact/migrations/0001_initial.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py (100%) rename _chapters/{chp17/django_ecommerce/usermap => chp21/django_ecommerce/contact/migrations}/__init__.py (100%) rename _chapters/{chp05 => chp21}/django_ecommerce/contact/models.py (72%) rename _chapters/{chp07 => chp21}/django_ecommerce/contact/views.py (92%) create mode 100644 _chapters/chp21/django_ecommerce/db_backup.json rename _chapters/{chp17/django_ecommerce/usermap/migrations => chp21/django_ecommerce/django_ecommerce}/__init__.py (100%) rename _chapters/{chp19 => chp21}/django_ecommerce/django_ecommerce/guitest_settings.py (100%) rename _chapters/{chp05 => chp21}/django_ecommerce/django_ecommerce/settings.py (88%) create mode 100644 _chapters/chp21/django_ecommerce/django_ecommerce/urls.py rename _chapters/{chp10 => chp21}/django_ecommerce/django_ecommerce/wsgi.py (100%) rename _chapters/{chp18/tests/gui => chp21/django_ecommerce/djangular_polls}/__init__.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/djangular_polls/admin.py rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/json_views.py (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/migrations/0001_initial.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py (100%) rename _chapters/{chp18/tests/gui/pages => chp21/django_ecommerce/djangular_polls/migrations}/__init__.py (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/models.py (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/serializers.py (100%) rename _chapters/{chp16/django_ecommerce/usermap => chp21/django_ecommerce/djangular_polls}/tests.py (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/urls.py (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/djangular_polls/views.py (100%) rename _chapters/{chp18/tests/unit => chp21/django_ecommerce/main}/__init__.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/main/admin.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/main/fixtures/system_data.json (100%) rename _chapters/{chp12 => chp21}/django_ecommerce/main/json_views.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/main/migrations/0001_auto_20150311_1303.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/main/migrations/0001_initial.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/main/migrations/0002_auto_20150311_1313.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/main/migrations/0002_statusreport.py (100%) rename _chapters/{chp19 => chp21}/django_ecommerce/main/migrations/0003_auto_20150606_0720.py (100%) rename _chapters/{chp18/tests/unit/contact => chp21/django_ecommerce/main/migrations}/__init__.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/main/migrations/data_load_marketing_items_0003.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/main/models.py rename _chapters/{chp12 => chp21}/django_ecommerce/main/permissions.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/main/serializers.py rename _chapters/{chp18/tests/unit/main => chp21/django_ecommerce/main/templatetags}/__init__.py (100%) rename _chapters/{chp11 => chp21}/django_ecommerce/main/templatetags/main_gravatar.py (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/main/templatetags/main_marketing.py (100%) rename _chapters/{chp12 => chp21}/django_ecommerce/main/urls.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/main/views.py rename _chapters/{chp10 => chp21}/django_ecommerce/manage.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/media/announce/main-logo.png (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/media/images/fig.png (100%) rename _chapters/{chp18/tests/unit => chp21/django_ecommerce}/payments/__init__.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/payments/admin.py rename _chapters/{chp13 => chp21}/django_ecommerce/payments/fixtures/system_data.json (100%) rename _chapters/{chp05 => chp21}/django_ecommerce/payments/forms.py (64%) rename _chapters/{chp15 => chp21}/django_ecommerce/payments/json_views.py (59%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0001_initial.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0003_initial_data.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0005_bigcoId_migration.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py (100%) rename _chapters/{chp13 => chp21}/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/payments/migrations/0009_merge.py (100%) rename _chapters/{chp19/tests/gui => chp21/django_ecommerce/payments/migrations}/__init__.py (100%) rename _chapters/{chp07 => chp21}/django_ecommerce/payments/models.py (72%) rename _chapters/{chp18 => chp21}/django_ecommerce/payments/serializers.py (100%) create mode 100644 _chapters/chp21/django_ecommerce/payments/urls.py rename _chapters/{chp07 => chp21}/django_ecommerce/payments/views.py (75%) rename _chapters/{chp19 => chp21}/django_ecommerce/requirements.txt (100%) rename _chapters/{chp19 => chp21}/django_ecommerce/runner.py (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap-theme.css (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap-theme.css.map (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap-theme.min.css (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap.css (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap.css.map (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/bootstrap.min.css (100%) create mode 100644 _chapters/chp21/django_ecommerce/static/css/mec.css rename _chapters/{chp10 => chp21}/django_ecommerce/static/css/signin.css (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/Starjedi.eot (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/Starjedi.svg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/Starjedi.ttf (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/Starjedi.woff (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/clone_army.jpg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/darth.jpg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/leia.jpg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/star-wars-battle.jpg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/star-wars-cast.jpg (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/img/yoda.jpg (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/static/js/admin.js (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/static/js/angular-google-maps.min.js (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/static/js/angular.min.js (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/static/js/angular.min.js.map (100%) create mode 100755 _chapters/chp21/django_ecommerce/static/js/application.js rename _chapters/{chp10 => chp21}/django_ecommerce/static/js/bootstrap.js (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/js/bootstrap.min.js (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/static/js/jquery-1.11.1.min.js (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/static/js/lodash.underscore.min.js (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/static/js/loggedInCtrl.js (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/static/js/registerCtrl.js (70%) rename _chapters/{chp18 => chp21}/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js (100%) rename _chapters/{chp18 => chp21}/django_ecommerce/static/js/ui-bootstrap-tpls.min.js (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/static/js/userMapCtrl.js (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/static/js/userPollCtrl.js (100%) rename _chapters/{chp09 => chp21}/django_ecommerce/templates/__base.html (73%) rename _chapters/{chp18 => chp21}/django_ecommerce/templates/admin/payments/user/change_form.html (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/contact/contact.html (100%) rename _chapters/{chp15 => chp21}/django_ecommerce/templates/djangular_polls/_polls.html (100%) rename _chapters/{chp07 => chp21}/django_ecommerce/templates/flatpages/default.html (67%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/main/_announcements.html (88%) rename _chapters/{chp11 => chp21}/django_ecommerce/templates/main/_badges.html (60%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/main/_jedibadge.html (83%) rename _chapters/{chp11 => chp21}/django_ecommerce/templates/main/_lateststatus.html (100%) rename _chapters/{chp11 => chp21}/django_ecommerce/templates/main/_statusupdate.html (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/main/index.html (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/main/templatetags/circle_item.html (100%) create mode 100644 _chapters/chp21/django_ecommerce/templates/main/user.html create mode 100644 _chapters/chp21/django_ecommerce/templates/payments/_cardform.html create mode 100644 _chapters/chp21/django_ecommerce/templates/payments/_field.html rename _chapters/{chp10 => chp21}/django_ecommerce/templates/payments/edit.html (100%) rename _chapters/{chp10 => chp21}/django_ecommerce/templates/payments/errors.html (100%) create mode 100644 _chapters/chp21/django_ecommerce/templates/payments/register.html rename _chapters/{chp10 => chp21}/django_ecommerce/templates/payments/sign_in.html (93%) rename _chapters/{chp12 => chp21}/django_ecommerce/templates/rest_framework/api.html (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/templates/usermap/usermap.html (100%) rename _chapters/{chp07 => chp21}/django_ecommerce/test.db (94%) rename _chapters/{chp19/tests/gui/pages => chp21/django_ecommerce/usermap}/__init__.py (100%) rename _chapters/{chp15/django_ecommerce/djangular_polls => chp21/django_ecommerce/usermap}/admin.py (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/usermap/json_views.py (100%) rename _chapters/{chp19/tests/unit => chp21/django_ecommerce/usermap/migrations}/__init__.py (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/usermap/models.py (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/usermap/tests.py (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/usermap/urls.py (100%) rename _chapters/{chp17 => chp21}/django_ecommerce/usermap/views.py (100%) create mode 100644 _chapters/chp21/requirements.txt rename _chapters/{chp19/tests/unit/contact => chp21/tests}/__init__.py (100%) rename _chapters/{chp19/tests/unit/main => chp21/tests/gui}/__init__.py (100%) rename _chapters/{chp19/tests/unit/payments => chp21/tests/gui/pages}/__init__.py (100%) rename _chapters/{chp19 => chp21}/tests/gui/pages/testPage.py (100%) create mode 100644 _chapters/chp21/tests/unit/__init__.py create mode 100644 _chapters/chp21/tests/unit/contact/__init__.py rename _chapters/{chp05/django_ecommerce/contact/tests.py => chp21/tests/unit/contact/testContactModels.py} (82%) create mode 100644 _chapters/chp21/tests/unit/main/__init__.py rename _chapters/{chp12/tests => chp21/tests/unit}/main/testJSONViews.py (93%) rename _chapters/{chp05/django_ecommerce/main/tests.py => chp21/tests/unit/main/testMainPageView.py} (53%) rename _chapters/{chp10/tests => chp21/tests/unit}/main/testSerializers.py (97%) create mode 100644 _chapters/chp21/tests/unit/payments/__init__.py create mode 100644 _chapters/chp21/tests/unit/payments/testCustomer.py create mode 100644 _chapters/chp21/tests/unit/payments/testForms.py create mode 100644 _chapters/chp21/tests/unit/payments/testUserModel.py rename _chapters/{chp10/tests => chp21/tests/unit}/payments/testViews.py (75%) diff --git a/_chapters/chp03/django_ecommerce/main/tests.py b/_chapters/chp03/django_ecommerce/main/tests.py index 1723f55..7496a54 100644 --- a/_chapters/chp03/django_ecommerce/main/tests.py +++ b/_chapters/chp03/django_ecommerce/main/tests.py @@ -62,3 +62,40 @@ def test_index_handles_logged_in_user(self): 'user.html', {'user': user_mock.get_by_id(1)} ) self.assertEquals(resp.content, expected_html.content) + + + +class User(models.Model): + + name = model.CharField() + pwd = model.CharField() + birthdate = model.DateField() + + def get_sign(): + if birthdate.month == "Jan": + return "Capricorn" + elif birthdate.monty == "Feb" + return "Sagitarius" + + def is_active_twitter_user(): + # call twitter with + # is user.name a user on twitter + is_active = twitter.check_user(user.name) + return is_active + + +def test_get_sign() + myuser = User("jj", "pwd", "Jan") + self.assertEquals(myuser.get_sign(), "Capricorn") + + myuser = User("jj", "pwd", "Feb") + self.assertEquals(myuser.get_sign(), "Sagitarius") + + myuser = User("jj", "pwd", "32") + self.assertEquals(myuser.get_sign(), "Sagitarius") + + + + +--- test.py + diff --git a/_chapters/chp03/django_ecommerce/test.db b/_chapters/chp03/django_ecommerce/test.db index 586f4f4e8d1bbc6fd2502b191d9a032735606373..a53c1100bc81174d828c9c420ba22939e0c7775f 100644 GIT binary patch delta 726 zcmZo@U~ZVeJVBZ@kb!~0Xrh8WkfHl^(Z-Y*b8$0W19M$N3k4%1D}6qtMF1?40g2W2H0Sr#RRhi3X`dKo7fdlf~62WF-P z+Dh9R85jaxZlP;n2z9xEiJpOlp}B$OW+l&?OwvFX8?tjT{bOKby2-$t%dE`o!MvS$ z64Onlo0|oB4m0_wF#9rAmYJBQ=Nnj-S7w%G7MkXorj}V6StM2#SCm>LrB)PhFyCQN zCFDhRkV%s@Jry>)`|q_8Vq#|OS&V5WlgyBbj(bd8AeS>Tive8@3=JM;jmC5i aR^O&rM?x_$+0J`4D2XCPG%(B{fdT-SX~@(7 delta 148 zcmZpez}(QlJVBb(hk=2?V4{LOkfHmGd1Fe9xwwh0fw``sxq_jkm7$rHp{1UsfrY8D z(dO&sngT++K=rJQN({_mOf#8e80Rx8Z4TrRVBD;9@E(&87xNtkR%T@e=3HiFW)J4= z%#$_?@(41ss4)97PS*5P*zE4V*M^UkIhBF=A@d>T#mp_tsX!IM%$rmDXBz+jvGXFr diff --git a/_chapters/chp05/django_ecommerce/payments/tests.py b/_chapters/chp05/django_ecommerce/payments/tests.py deleted file mode 100644 index 5fa5a9a..0000000 --- a/_chapters/chp05/django_ecommerce/payments/tests.py +++ /dev/null @@ -1,413 +0,0 @@ -from django.test import TestCase, RequestFactory -from django.shortcuts import render_to_response -from django.db import IntegrityError -import django_ecommerce.settings as settings -from payments.views import soon, register, Customer -from payments.models import User -from payments.forms import SigninForm, UserForm, CardForm -import unittest -import mock - - -######################## -#### Testing Models #### -######################## - - -class UserModelTest(TestCase): - - @classmethod - def setUpClass(cls): - super(UserModelTest, cls).setUpClass() - cls.test_user = User(email="j@j.com", name='test user') - cls.test_user.save() - - def test_user_to_string_print_email(self): - self.assertEquals(str(self.test_user), "j@j.com") - - def test_get_by_id(self): - self.assertEquals(User.get_by_id(1), self.test_user) - - def test_create_user_function_stores_in_database(self): - user = User.create("test", "test@t.com", "tt", "1234", "22") - self.assertEquals(User.objects.get(email="test@t.com"), user) - - def test_create_user_allready_exists_throws_IntegrityError(self): - from django.db import IntegrityError - self.assertRaises( - IntegrityError, - User.create, - "test user", - "j@j.com", - "jj", - "1234", - 89 - ) - - -####################### -#### Testing Forms #### -####################### - -class FormTesterMixin(): - - def assertFormError(self, form_cls, expected_error_name, - expected_error_msg, data): - - from pprint import pformat - test_form = form_cls(data=data) - #if we get an error then the form should not be valid - self.assertFalse(test_form.is_valid()) - - self.assertEquals( - test_form.errors[expected_error_name], - expected_error_msg, - msg="Expected {} : Actual {} : using data {}".format( - test_form.errors[expected_error_name], - expected_error_msg, pformat(data) - ) - ) - - -class FormTests(unittest.TestCase, FormTesterMixin): - - def test_signin_form_data_validation_for_invalid_data(self): - invalid_data_list = [ - {'data': {'email': 'j@j.com'}, - 'error': ('password', [u'This field is required.'])}, - {'data': {'password': '1234'}, - 'error': ('email', [u'This field is required.'])} - ] - - for invalid_data in invalid_data_list: - self.assertFormError(SigninForm, - invalid_data['error'][0], - invalid_data['error'][1], - invalid_data["data"]) - - def test_user_form_passwords_match(self): - form = UserForm( - { - 'name': 'jj', - 'email': 'j@j.com', - 'password': '1234', - 'ver_password': '1234', - 'last_4_digits': '3333', - 'stripe_token': '1'} - ) - # Is the data valid? - if form.is_valid(): - # Is the data clean? - self.assertTrue(form.cleaned_data) - - def test_user_form_passwords_dont_match_throws_error(self): - form = UserForm( - { - 'name': 'jj', - 'email': 'j@j.com', - 'password': '234', - 'ver_password': '1234', # bad password - 'last_4_digits': '3333', - 'stripe_token': '1' - } - ) - - # Is the data valid? - self.assertFalse(form.is_valid()) - - def test_card_form_data_validation_for_invalid_data(self): - invalid_data_list = [ - { - 'data': {'last_4_digits': '123'}, - 'error': ( - 'last_4_digits', - [u'Ensure this value has at least 4 characters (it has 3).'] - ) - }, - { - 'data': {'last_4_digits': '12345'}, - 'error': ( - 'last_4_digits', - [u'Ensure this value has at most 4 characters (it has 5).'] - ) - } - ] - - for invalid_data in invalid_data_list: - self.assertFormError( - CardForm, - invalid_data['error'][0], - invalid_data['error'][1], - invalid_data["data"] - ) - - -########################## -##### Testing routes ##### -########################## - -from .views import sign_in, sign_out -from django.core.urlresolvers import resolve -from django.template import RequestContext - -class ViewTesterMixin(object): - - @classmethod - def setupViewTester(cls, url, view_func, expected_html_path, - expected_html_context, - status_code=200, - session={}): - request_factory = RequestFactory() - cls.request = request_factory.get(url) - cls.request.session = session - - cls.status_code = status_code - cls.url = url - cls.view_func = staticmethod(view_func) - - expected_html = "" - if expected_html_path and expected_html_context: - response = render_to_response( - expected_html_path, - expected_html_context, - context_instance=RequestContext(cls.request) - ) - expected_html = response.content - cls.expected_html = expected_html - - def test_resolves_to_correct_view(self): - test_view = resolve(self.url) - self.assertEquals(test_view.func, self.view_func) - - def test_returns_appropriate_respose_code(self): - resp = self.view_func(self.request) - self.assertEquals(resp.status_code, self.status_code) - - def test_returns_correct_html(self): - import pdb; pdb.set_trace() - resp = self.view_func(self.request) - self.assertEquals(resp.content, self.expected_html) - - -class SignInPageTests(TestCase, ViewTesterMixin): - - @classmethod - def setUpClass(cls): - super(SignInPageTests, cls).setUpClass() - page = 'sign_in.html' - context = { - 'form': SigninForm(), - 'user': None - } - - ViewTesterMixin.setupViewTester( - '/sign_in', - sign_in, - page, - context, - ) - - -class SignOutPageTests(TestCase, ViewTesterMixin): - - @classmethod - def setUpClass(cls): - super(SignOutPageTests, cls).setUpClass() - ViewTesterMixin.setupViewTester( - '/sign_out', - sign_out, - None, None, # a redirect will return no html - status_code=302, - session={"user": "dummy"}, - ) - - def setUp(self): - #sign_out clears the session, so let's reset it everytime - self.request.session = {"user": "dummy"} - - -class RegisterPageTests(TestCase, ViewTesterMixin): - - @classmethod - def setUpClass(cls): - super(RegisterPageTests, cls).setUpClass() - page = 'register.html' - context = { - 'form': UserForm(), - 'months': range(1, 12), - 'publishable': settings.STRIPE_PUBLISHABLE, - 'soon': soon(), - 'user': None, - 'years': range(2011, 2036), - } - ViewTesterMixin.setupViewTester( - '/register', - register, - page, - context, - session={"user": "dummy"}, - ) - - def setUp(self): - pass - #request_factory = RequestFactory() - #self.request = request_factory.get(self.url) - - def test_invalid_form_returns_registration_page(self): - - with mock.patch('payments.forms.UserForm.is_valid') as user_mock: - - user_mock.return_value = False - - self.request.method = 'POST' - self.request.POST = None - resp = register(self.request) - self.assertEquals(resp.content, self.expected_html) - - # make sure that we did indeed call our is_valid function - self.assertEquals(user_mock.call_count, 1) - - @mock.patch('payments.views.Customer.create') - @mock.patch.object(User, 'create') - def test_registering_new_user_returns_succesfully( - self, create_mock, stripe_mock - ): - - self.request.session = {} - self.request.method = 'POST' - self.request.POST = { - 'email': 'python@rocks.com', - 'name': 'pyRock', - 'stripe_token': '...', - 'last_4_digits': '4242', - 'password': 'bad_password', - 'ver_password': 'bad_password', - } - - #get the return values of the mocks, for our checks later - new_user = create_mock.return_value - new_cust = stripe_mock.return_value - - resp = register(self.request) - - self.assertEquals(resp.content, "") - self.assertEquals(resp.status_code, 302) - self.assertEquals(self.request.session['user'], new_user.pk) - #verify the user was actually stored in the database. - create_mock.assert_called_with( - 'pyRock', 'python@rocks.com', 'bad_password', '4242', new_cust.id - ) - - # old test - # def test_registering_new_user_returns_succesfully(self): - - # self.request.session = {} - # self.request.method = 'POST' - # self.request.POST = { - # 'email': 'python@rocks.com', - # 'name': 'pyRock', - # 'stripe_token': '...', - # 'last_4_digits': '4242', - # 'password': 'bad_password', - # 'ver_password': 'bad_password', - # } - - # with mock.patch('stripe.Customer') as stripe_mock: - - # config = {'create.return_value': mock.Mock()} - # stripe_mock.configure_mock(**config) - - # resp = register(self.request) - # self.assertEquals(resp.content, "") - # self.assertEquals(resp.status_code, 302) - # self.assertEquals(self.request.session['user'], 1) - - # # verify the user was actually stored in the database. - # # if the user is not there this will throw an error - # User.objects.get(email='python@rocks.com') - - def get_MockUserForm(self): - from django import forms - - class MockUserForm(forms.Form): - - def is_valid(self): - return True - - @property - def cleaned_data(self): - return { - 'email': 'python@rocks.com', - 'name': 'pyRock', - 'stripe_token': '...', - 'last_4_digits': '4242', - 'password': 'bad_password', - 'ver_password': 'bad_password', - } - - def addError(self, error): - pass - - return MockUserForm() - - @mock.patch('payments.views.UserForm', get_MockUserForm) - @mock.patch('payments.models.User.save', side_effect=IntegrityError) - def test_registering_user_twice_cause_error_msg(self, save_mock): - - #create the request used to test the view - self.request.session = {} - self.request.method = 'POST' - self.request.POST = {} - - #create the expected html - html = render_to_response( - 'register.html', - { - 'form': self.get_MockUserForm(), - 'months': list(range(1, 12)), - 'publishable': settings.STRIPE_PUBLISHABLE, - 'soon': soon(), - 'user': None, - 'years': list(range(2011, 2036)), - }, - context_instance=RequestContext(self.request) - ) - - #mock out stripe so we don't hit their server - with mock.patch('payments.views.Customer') as stripe_mock: - - config = {'create.return_value': mock.Mock()} - stripe_mock.configure_mock(**config) - - #run the test - resp = register(self.request) - - #verify that we did things correctly - self.assertEqual(resp.content, html.content) - self.assertEqual(resp.status_code, 200) - self.assertEqual(self.request.session, {}) - - #assert there is no records in the database. - users = User.objects.filter(email="python@rocks.com") - self.assertEqual(len(users), 0) - - -class CustomerTests(TestCase): - - def test_create_subscription(self): - with mock.patch('stripe.Customer.create') as create_mock: - cust_data = {'description': 'test user', 'email': 'test@test.com', - 'card': '4242', 'plan': 'gold'} - Customer.create("subscription", **cust_data) - - create_mock.assert_called_with(**cust_data) - - def test_create_one_time_bill(self): - with mock.patch('stripe.Charge.create') as charge_mock: - cust_data = {'description': 'email', - 'card': '1234', - 'amount': '5000', - 'currency': 'usd'} - Customer.create("one_time", **cust_data) - - charge_mock.assert_called_with(**cust_data) diff --git a/_chapters/chp05/requirements.txt b/_chapters/chp05/requirements.txt deleted file mode 100644 index 36b6852..0000000 --- a/_chapters/chp05/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -Django==1.9.9 -mock==1.0.1 -requests==2.3.0 -stripe==1.9.2 -wheel==0.24.0 diff --git a/_chapters/chp07/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp07/django_ecommerce/django_ecommerce/urls.py deleted file mode 100644 index 11db113..0000000 --- a/_chapters/chp07/django_ecommerce/django_ecommerce/urls.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.conf.urls import patterns, include, url -from payments import views -from django.contrib import admin -admin.autodiscover() - -urlpatterns = patterns('', - url(r'^admin/', include(admin.site.urls)), - url(r'^$', 'main.views.index', name='home'), - url(r'^pages/', include('django.contrib.flatpages.urls')), - url(r'^contact/', 'contact.views.contact', name='contact'), - - # user registration/authentication - url(r'^sign_in$', views.sign_in, name='sign_in'), - url(r'^sign_out$', views.sign_out, name='sign_out'), - url(r'^register$', views.register, name='register'), - url(r'^edit$', views.edit, name='edit'), -) diff --git a/_chapters/chp07/django_ecommerce/main/models.py b/_chapters/chp07/django_ecommerce/main/models.py deleted file mode 100644 index 71a8362..0000000 --- a/_chapters/chp07/django_ecommerce/main/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/_chapters/chp07/django_ecommerce/main/views.py b/_chapters/chp07/django_ecommerce/main/views.py deleted file mode 100644 index 76a414e..0000000 --- a/_chapters/chp07/django_ecommerce/main/views.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.shortcuts import render_to_response -from payments.models import User - - -def index(request): - uid = request.session.get('user') - if uid is None: - return render_to_response('index.html') - else: - return render_to_response( - 'user.html', {'user': User.get_by_id(uid)} - ) diff --git a/_chapters/chp07/django_ecommerce/payments/admin.py b/_chapters/chp07/django_ecommerce/payments/admin.py deleted file mode 100644 index 763c430..0000000 --- a/_chapters/chp07/django_ecommerce/payments/admin.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.contrib import admin -from .models import User - - -class UserAdmin(admin.ModelAdmin): - class Meta: - model = User - -admin.site.register(User, UserAdmin) diff --git a/_chapters/chp05/django_ecommerce/contact/__init__.py b/_chapters/chp08/django_ecommerce/contact/__init__.py similarity index 100% rename from _chapters/chp05/django_ecommerce/contact/__init__.py rename to _chapters/chp08/django_ecommerce/contact/__init__.py diff --git a/_chapters/chp05/django_ecommerce/contact/admin.py b/_chapters/chp08/django_ecommerce/contact/admin.py similarity index 100% rename from _chapters/chp05/django_ecommerce/contact/admin.py rename to _chapters/chp08/django_ecommerce/contact/admin.py diff --git a/_chapters/chp05/django_ecommerce/contact/forms.py b/_chapters/chp08/django_ecommerce/contact/forms.py similarity index 100% rename from _chapters/chp05/django_ecommerce/contact/forms.py rename to _chapters/chp08/django_ecommerce/contact/forms.py diff --git a/_chapters/chp07/django_ecommerce/contact/models.py b/_chapters/chp08/django_ecommerce/contact/models.py similarity index 100% rename from _chapters/chp07/django_ecommerce/contact/models.py rename to _chapters/chp08/django_ecommerce/contact/models.py diff --git a/_chapters/chp05/django_ecommerce/contact/views.py b/_chapters/chp08/django_ecommerce/contact/views.py similarity index 100% rename from _chapters/chp05/django_ecommerce/contact/views.py rename to _chapters/chp08/django_ecommerce/contact/views.py diff --git a/_chapters/chp07/django_ecommerce/db_backup.json b/_chapters/chp08/django_ecommerce/db_backup.json similarity index 100% rename from _chapters/chp07/django_ecommerce/db_backup.json rename to _chapters/chp08/django_ecommerce/db_backup.json diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/__init__.py b/_chapters/chp08/django_ecommerce/django_ecommerce/__init__.py similarity index 100% rename from _chapters/chp05/django_ecommerce/django_ecommerce/__init__.py rename to _chapters/chp08/django_ecommerce/django_ecommerce/__init__.py diff --git a/_chapters/chp07/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp08/django_ecommerce/django_ecommerce/settings.py similarity index 100% rename from _chapters/chp07/django_ecommerce/django_ecommerce/settings.py rename to _chapters/chp08/django_ecommerce/django_ecommerce/settings.py diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp08/django_ecommerce/django_ecommerce/urls.py similarity index 100% rename from _chapters/chp05/django_ecommerce/django_ecommerce/urls.py rename to _chapters/chp08/django_ecommerce/django_ecommerce/urls.py diff --git a/_chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py b/_chapters/chp08/django_ecommerce/django_ecommerce/wsgi.py similarity index 100% rename from _chapters/chp05/django_ecommerce/django_ecommerce/wsgi.py rename to _chapters/chp08/django_ecommerce/django_ecommerce/wsgi.py diff --git a/_chapters/chp05/django_ecommerce/main/__init__.py b/_chapters/chp08/django_ecommerce/main/__init__.py similarity index 100% rename from _chapters/chp05/django_ecommerce/main/__init__.py rename to _chapters/chp08/django_ecommerce/main/__init__.py diff --git a/_chapters/chp05/django_ecommerce/main/models.py b/_chapters/chp08/django_ecommerce/main/models.py similarity index 100% rename from _chapters/chp05/django_ecommerce/main/models.py rename to _chapters/chp08/django_ecommerce/main/models.py diff --git a/_chapters/chp05/django_ecommerce/main/views.py b/_chapters/chp08/django_ecommerce/main/views.py similarity index 100% rename from _chapters/chp05/django_ecommerce/main/views.py rename to _chapters/chp08/django_ecommerce/main/views.py diff --git a/_chapters/chp05/django_ecommerce/manage.py b/_chapters/chp08/django_ecommerce/manage.py old mode 100755 new mode 100644 similarity index 100% rename from _chapters/chp05/django_ecommerce/manage.py rename to _chapters/chp08/django_ecommerce/manage.py diff --git a/_chapters/chp05/django_ecommerce/payments/__init__.py b/_chapters/chp08/django_ecommerce/payments/__init__.py similarity index 100% rename from _chapters/chp05/django_ecommerce/payments/__init__.py rename to _chapters/chp08/django_ecommerce/payments/__init__.py diff --git a/_chapters/chp05/django_ecommerce/payments/admin.py b/_chapters/chp08/django_ecommerce/payments/admin.py similarity index 100% rename from _chapters/chp05/django_ecommerce/payments/admin.py rename to _chapters/chp08/django_ecommerce/payments/admin.py diff --git a/_chapters/chp07/django_ecommerce/payments/forms.py b/_chapters/chp08/django_ecommerce/payments/forms.py similarity index 100% rename from _chapters/chp07/django_ecommerce/payments/forms.py rename to _chapters/chp08/django_ecommerce/payments/forms.py diff --git a/_chapters/chp05/django_ecommerce/payments/models.py b/_chapters/chp08/django_ecommerce/payments/models.py similarity index 100% rename from _chapters/chp05/django_ecommerce/payments/models.py rename to _chapters/chp08/django_ecommerce/payments/models.py diff --git a/_chapters/chp05/django_ecommerce/payments/views.py b/_chapters/chp08/django_ecommerce/payments/views.py similarity index 95% rename from _chapters/chp05/django_ecommerce/payments/views.py rename to _chapters/chp08/django_ecommerce/payments/views.py index 1ab1287..21672c9 100644 --- a/_chapters/chp05/django_ecommerce/payments/views.py +++ b/_chapters/chp08/django_ecommerce/payments/views.py @@ -33,7 +33,7 @@ def sign_in(request): else: form = SigninForm() - print form.non_field_errors() + print(form.non_field_errors()) return render_to_response( 'sign_in.html', @@ -96,11 +96,11 @@ def register(request): 'register.html', { 'form': form, - 'months': range(1, 12), + 'months': list(range(1, 12)), 'publishable': settings.STRIPE_PUBLISHABLE, 'soon': soon(), 'user': user, - 'years': range(2011, 2036), + 'years': list(range(2011, 2036)), }, context_instance=RequestContext(request) ) @@ -137,8 +137,8 @@ def edit(request): 'form': form, 'publishable': settings.STRIPE_PUBLISHABLE, 'soon': soon(), - 'months': range(1, 12), - 'years': range(2011, 2036) + 'months': list(range(1, 12)), + 'years': list(range(2011, 2036)) }, context_instance=RequestContext(request) ) diff --git a/_chapters/chp05/django_ecommerce/static/application.js b/_chapters/chp08/django_ecommerce/static/application.js similarity index 100% rename from _chapters/chp05/django_ecommerce/static/application.js rename to _chapters/chp08/django_ecommerce/static/application.js diff --git a/_chapters/chp05/django_ecommerce/static/bootstrap.css b/_chapters/chp08/django_ecommerce/static/bootstrap.css similarity index 100% rename from _chapters/chp05/django_ecommerce/static/bootstrap.css rename to _chapters/chp08/django_ecommerce/static/bootstrap.css diff --git a/_chapters/chp05/django_ecommerce/static/jquery.js b/_chapters/chp08/django_ecommerce/static/jquery.js similarity index 100% rename from _chapters/chp05/django_ecommerce/static/jquery.js rename to _chapters/chp08/django_ecommerce/static/jquery.js diff --git a/_chapters/chp05/django_ecommerce/static/jquery.min.js b/_chapters/chp08/django_ecommerce/static/jquery.min.js similarity index 100% rename from _chapters/chp05/django_ecommerce/static/jquery.min.js rename to _chapters/chp08/django_ecommerce/static/jquery.min.js diff --git a/_chapters/chp05/django_ecommerce/static/jquery_ujs.js b/_chapters/chp08/django_ecommerce/static/jquery_ujs.js similarity index 100% rename from _chapters/chp05/django_ecommerce/static/jquery_ujs.js rename to _chapters/chp08/django_ecommerce/static/jquery_ujs.js diff --git a/_chapters/chp05/django_ecommerce/templates/base.html b/_chapters/chp08/django_ecommerce/templates/base.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/base.html rename to _chapters/chp08/django_ecommerce/templates/base.html diff --git a/_chapters/chp05/django_ecommerce/templates/cardform.html b/_chapters/chp08/django_ecommerce/templates/cardform.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/cardform.html rename to _chapters/chp08/django_ecommerce/templates/cardform.html diff --git a/_chapters/chp05/django_ecommerce/templates/contact.html b/_chapters/chp08/django_ecommerce/templates/contact.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/contact.html rename to _chapters/chp08/django_ecommerce/templates/contact.html diff --git a/_chapters/chp05/django_ecommerce/templates/edit.html b/_chapters/chp08/django_ecommerce/templates/edit.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/edit.html rename to _chapters/chp08/django_ecommerce/templates/edit.html diff --git a/_chapters/chp05/django_ecommerce/templates/errors.html b/_chapters/chp08/django_ecommerce/templates/errors.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/errors.html rename to _chapters/chp08/django_ecommerce/templates/errors.html diff --git a/_chapters/chp05/django_ecommerce/templates/field.html b/_chapters/chp08/django_ecommerce/templates/field.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/field.html rename to _chapters/chp08/django_ecommerce/templates/field.html diff --git a/_chapters/chp05/django_ecommerce/templates/flatpages/default.html b/_chapters/chp08/django_ecommerce/templates/flatpages/default.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/flatpages/default.html rename to _chapters/chp08/django_ecommerce/templates/flatpages/default.html diff --git a/_chapters/chp05/django_ecommerce/templates/home.html b/_chapters/chp08/django_ecommerce/templates/home.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/home.html rename to _chapters/chp08/django_ecommerce/templates/home.html diff --git a/_chapters/chp05/django_ecommerce/templates/index.html b/_chapters/chp08/django_ecommerce/templates/index.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/index.html rename to _chapters/chp08/django_ecommerce/templates/index.html diff --git a/_chapters/chp05/django_ecommerce/templates/register.html b/_chapters/chp08/django_ecommerce/templates/register.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/register.html rename to _chapters/chp08/django_ecommerce/templates/register.html diff --git a/_chapters/chp05/django_ecommerce/templates/sign_in.html b/_chapters/chp08/django_ecommerce/templates/sign_in.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/sign_in.html rename to _chapters/chp08/django_ecommerce/templates/sign_in.html diff --git a/_chapters/chp05/django_ecommerce/templates/test.html b/_chapters/chp08/django_ecommerce/templates/test.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/test.html rename to _chapters/chp08/django_ecommerce/templates/test.html diff --git a/_chapters/chp05/django_ecommerce/templates/user.html b/_chapters/chp08/django_ecommerce/templates/user.html similarity index 100% rename from _chapters/chp05/django_ecommerce/templates/user.html rename to _chapters/chp08/django_ecommerce/templates/user.html diff --git a/_chapters/chp05/django_ecommerce/test.db b/_chapters/chp08/django_ecommerce/test.db similarity index 100% rename from _chapters/chp05/django_ecommerce/test.db rename to _chapters/chp08/django_ecommerce/test.db diff --git a/_chapters/chp07/requirements.txt b/_chapters/chp08/requirements.txt similarity index 100% rename from _chapters/chp07/requirements.txt rename to _chapters/chp08/requirements.txt diff --git a/_chapters/chp07/django_ecommerce/contact/__init__.py b/_chapters/chp08/tests/__init__.py similarity index 100% rename from _chapters/chp07/django_ecommerce/contact/__init__.py rename to _chapters/chp08/tests/__init__.py diff --git a/_chapters/chp07/django_ecommerce/django_ecommerce/__init__.py b/_chapters/chp08/tests/contact/__init__.py similarity index 100% rename from _chapters/chp07/django_ecommerce/django_ecommerce/__init__.py rename to _chapters/chp08/tests/contact/__init__.py diff --git a/_chapters/chp07/tests/contact/testContactModels.py b/_chapters/chp08/tests/contact/testContactModels.py similarity index 100% rename from _chapters/chp07/tests/contact/testContactModels.py rename to _chapters/chp08/tests/contact/testContactModels.py diff --git a/_chapters/chp07/django_ecommerce/main/__init__.py b/_chapters/chp08/tests/main/__init__.py similarity index 100% rename from _chapters/chp07/django_ecommerce/main/__init__.py rename to _chapters/chp08/tests/main/__init__.py diff --git a/_chapters/chp07/tests/main/testMainPageView.py b/_chapters/chp08/tests/main/testMainPageView.py similarity index 97% rename from _chapters/chp07/tests/main/testMainPageView.py rename to _chapters/chp08/tests/main/testMainPageView.py index 85a5d82..62841f8 100644 --- a/_chapters/chp07/tests/main/testMainPageView.py +++ b/_chapters/chp08/tests/main/testMainPageView.py @@ -14,7 +14,7 @@ class MainPageTests(TestCase): @classmethod def setUpClass(cls): - super().setUpClass() + super(MainPageTests, cls).setUpClass() request_factory = RequestFactory() cls.request = request_factory.get('/') cls.request.session = {} diff --git a/_chapters/chp07/django_ecommerce/payments/__init__.py b/_chapters/chp08/tests/payments/__init__.py similarity index 100% rename from _chapters/chp07/django_ecommerce/payments/__init__.py rename to _chapters/chp08/tests/payments/__init__.py diff --git a/_chapters/chp07/tests/payments/testCustomer.py b/_chapters/chp08/tests/payments/testCustomer.py similarity index 100% rename from _chapters/chp07/tests/payments/testCustomer.py rename to _chapters/chp08/tests/payments/testCustomer.py diff --git a/_chapters/chp07/tests/payments/testForms.py b/_chapters/chp08/tests/payments/testForms.py similarity index 100% rename from _chapters/chp07/tests/payments/testForms.py rename to _chapters/chp08/tests/payments/testForms.py diff --git a/_chapters/chp07/tests/payments/testUserModel.py b/_chapters/chp08/tests/payments/testUserModel.py similarity index 100% rename from _chapters/chp07/tests/payments/testUserModel.py rename to _chapters/chp08/tests/payments/testUserModel.py diff --git a/_chapters/chp08/tests/payments/testViews.py b/_chapters/chp08/tests/payments/testViews.py new file mode 100644 index 0000000..c787fb5 --- /dev/null +++ b/_chapters/chp08/tests/payments/testViews.py @@ -0,0 +1,239 @@ +from payments.views import sign_in, sign_out, register, soon +from django.test import TestCase, RequestFactory +from payments.models import User +from payments.forms import SigninForm, UserForm +from django.db import IntegrityError +from django.core.urlresolvers import resolve +from django.shortcuts import render_to_response +import django_ecommerce.settings as settings +import mock + + +class ViewTesterMixin(object): + + @classmethod + def setupViewTester(cls, url, view_func, expected_html, + status_code=200, + session={}): + request_factory = RequestFactory() + cls.request = request_factory.get(url) + cls.request.session = session + cls.status_code = status_code + cls.url = url + cls.view_func = staticmethod(view_func) + cls.expected_html = expected_html + + def test_resolves_to_correct_view(self): + test_view = resolve(self.url) + self.assertEqual(test_view.func, self.view_func) + + def test_returns_appropriate_respose_code(self): + resp = self.view_func(self.request) + self.assertEqual(resp.status_code, self.status_code) + + def test_returns_correct_html(self): + resp = self.view_func(self.request) + self.assertEqual(resp.content, self.expected_html) + + +class SignInPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super().setUpClass() + html = render_to_response( + 'sign_in.html', + { + 'form': SigninForm(), + 'user': None + } + ) + + ViewTesterMixin.setupViewTester( + '/sign_in', + sign_in, + html.content + ) + + +class SignOutPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super().setUpClass() + ViewTesterMixin.setupViewTester( + '/sign_out', + sign_out, + b"", # a redirect will return an empty bytestring + status_code=302, + session={"user": "dummy"}, + ) + + def setUp(self): + #sign_out clears the session, so let's reset it everytime + self.request.session = {"user": "dummy"} + + +class RegisterPageTests(TestCase, ViewTesterMixin): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + html = render_to_response( + 'register.html', + { + 'form': UserForm(), + 'months': list(range(1, 12)), + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'user': None, + 'years': list(range(2011, 2036)), + } + ) + ViewTesterMixin.setupViewTester( + '/register', + register, + html.content, + ) + + def setUp(self): + request_factory = RequestFactory() + self.request = request_factory.get(self.url) + + def test_invalid_form_returns_registration_page(self): + + with mock.patch('payments.forms.UserForm.is_valid') as user_mock: + + user_mock.return_value = False + + self.request.method = 'POST' + self.request.POST = None + resp = register(self.request) + self.assertEqual(resp.content, self.expected_html) + + # make sure that we did indeed call our is_valid function + self.assertEqual(user_mock.call_count, 1) + + @mock.patch('payments.views.Customer.create') + @mock.patch.object(User, 'create') + def test_registering_new_user_returns_succesfully( + self, create_mock, stripe_mock + ): + + self.request.session = {} + self.request.method = 'POST' + self.request.POST = { + 'email': 'python@rocks.com', + 'name': 'pyRock', + 'stripe_token': '...', + 'last_4_digits': '4242', + 'password': 'bad_password', + 'ver_password': 'bad_password', + } + + #get the return values of the mocks, for our checks later + new_user = create_mock.return_value + new_cust = stripe_mock.return_value + + resp = register(self.request) + + self.assertEqual(resp.content, b"") + self.assertEqual(resp.status_code, 302) + self.assertEqual(self.request.session['user'], new_user.pk) + #verify the user was actually stored in the database. + create_mock.assert_called_with( + 'pyRock', 'python@rocks.com', 'bad_password', '4242', new_cust.id + ) + + # old test + # def test_registering_new_user_returns_succesfully(self): + + # self.request.session = {} + # self.request.method = 'POST' + # self.request.POST = { + # 'email': 'python@rocks.com', + # 'name': 'pyRock', + # 'stripe_token': '...', + # 'last_4_digits': '4242', + # 'password': 'bad_password', + # 'ver_password': 'bad_password', + # } + + # with mock.patch('stripe.Customer') as stripe_mock: + + # config = {'create.return_value': mock.Mock()} + # stripe_mock.configure_mock(**config) + + # resp = register(self.request) + # self.assertEquals(resp.content, "") + # self.assertEquals(resp.status_code, 302) + # self.assertEquals(self.request.session['user'], 1) + + # # verify the user was actually stored in the database. + # # if the user is not there this will throw an error + # User.objects.get(email='python@rocks.com') + + def get_MockUserForm(self): + from django import forms + + class MockUserForm(forms.Form): + + def is_valid(self): + return True + + @property + def cleaned_data(self): + return { + 'email': 'python@rocks.com', + 'name': 'pyRock', + 'stripe_token': '...', + 'last_4_digits': '4242', + 'password': 'bad_password', + 'ver_password': 'bad_password', + } + + def addError(self, error): + pass + + return MockUserForm() + + @mock.patch('payments.views.UserForm', get_MockUserForm) + @mock.patch('payments.models.User.save', side_effect=IntegrityError) + def test_registering_user_twice_cause_error_msg(self, save_mock): + + #create the request used to test the view + self.request.session = {} + self.request.method = 'POST' + self.request.POST = {} + + #create the expected html + html = render_to_response( + 'register.html', + { + 'form': self.get_MockUserForm(), + 'months': list(range(1, 12)), + 'publishable': settings.STRIPE_PUBLISHABLE, + 'soon': soon(), + 'user': None, + 'years': list(range(2011, 2036)), + } + ) + + #mock out stripe so we don't hit their server + with mock.patch('payments.views.Customer') as stripe_mock: + + config = {'create.return_value': mock.Mock()} + stripe_mock.configure_mock(**config) + + #run the test + resp = register(self.request) + + #verify that we did things correctly + self.assertEqual(resp.content, html.content) + self.assertEqual(resp.status_code, 200) + self.assertEqual(self.request.session, {}) + + #assert there is no records in the database. + users = User.objects.filter(email="python@rocks.com") + self.assertEqual(len(users), 0) diff --git a/_chapters/chp09/django_ecommerce/contact/views.py b/_chapters/chp09/django_ecommerce/contact/views.py index 0309934..5125f2f 100644 --- a/_chapters/chp09/django_ecommerce/contact/views.py +++ b/_chapters/chp09/django_ecommerce/contact/views.py @@ -16,6 +16,6 @@ def contact(request): return HttpResponseRedirect('/') else: form = ContactView() - t = loader.get_template('contact/contact.html') + t = loader.get_template('contact.html') c = RequestContext(request, {'form': form, }) return HttpResponse(t.render(c)) diff --git a/_chapters/chp09/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp09/django_ecommerce/django_ecommerce/settings.py index daeb677..cb47005 100644 --- a/_chapters/chp09/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp09/django_ecommerce/django_ecommerce/settings.py @@ -8,6 +8,7 @@ SITE_ROOT = os.path.dirname(PROJECT_ROOT) STRIPE_SECRET = 'sk_test_4QBquf6d5EzsnJC1fTI2GBGm' STRIPE_PUBLISHABLE = 'pk_test_4QBqqGvCk9gaNn3pl1cwxcAS' + TEST_RUNNER = 'django.test.runner.DiscoverRunner' ADMINS = ( @@ -21,7 +22,7 @@ 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'django_db', 'USER': 'djangousr', - 'PASSWORD': 'djangousr', + 'PASSWORD': 'your_password_here', 'HOST': 'localhost', 'PORT': '5432', } diff --git a/_chapters/chp09/django_ecommerce/main/models.py b/_chapters/chp09/django_ecommerce/main/models.py index 1b8cc9d..71a8362 100644 --- a/_chapters/chp09/django_ecommerce/main/models.py +++ b/_chapters/chp09/django_ecommerce/main/models.py @@ -1,10 +1,3 @@ from django.db import models - # Create your models here. -class MarketingItem(models.Model): - img = models.CharField(max_length=255) - heading = models.CharField(max_length=300) - caption = models.TextField() - button_link = models.URLField(null=True, default="register") - button_title = models.CharField(max_length=20, default="View details") diff --git a/_chapters/chp09/django_ecommerce/main/views.py b/_chapters/chp09/django_ecommerce/main/views.py index 8453efc..76a414e 100644 --- a/_chapters/chp09/django_ecommerce/main/views.py +++ b/_chapters/chp09/django_ecommerce/main/views.py @@ -1,59 +1,12 @@ from django.shortcuts import render_to_response from payments.models import User -from main.models import MarketingItem -#from main.templatetags.main_marketing import marketing__circle_item - - -class market_item(object): - - def __init__(self, img, heading, caption, button_link="register", - button_title="View details"): - self.img = img - self.heading = heading - self.caption = caption - self.button_link = button_link - self.button_title = button_title - -market_items = [ - market_item( - img="yoda.jpg", - heading="Hone your Jedi Skills", - caption="All members have access to our unique" - " training and achievements latters. Progress through the " - "levels and show everyone who the top Jedi Master is!", - button_title="Sign Up Now" - ), - market_item( - img="clone_army.jpg", - heading="Build your Clan", - caption="Engage in meaningful conversation, or " - "bloodthirsty battle! If it's related to " - "Star Wars, in any way, you better believe we do it.", - button_title="Sign Up Now" - ), - market_item( - img="leia.jpg", - heading="Find Love", - caption="Everybody knows Star Wars fans are the " - "best mates for Star Wars fans. Find your " - "Princess Leia or Han Solo and explore the " - "stars together.", - button_title="Sign Up Now" - ), -] def index(request): uid = request.session.get('user') - market_items = MarketingItem.objects.all() if uid is None: - return render_to_response( - 'main/index.html', - {'marketing_items': market_items} - ) + return render_to_response('index.html') else: return render_to_response( - 'main/user.html', - {'marketing_items': market_items, - 'user': User.get_by_id(uid)} + 'user.html', {'user': User.get_by_id(uid)} ) diff --git a/_chapters/chp09/django_ecommerce/payments/forms.py b/_chapters/chp09/django_ecommerce/payments/forms.py index 9af62da..0a99862 100644 --- a/_chapters/chp09/django_ecommerce/payments/forms.py +++ b/_chapters/chp09/django_ecommerce/payments/forms.py @@ -34,7 +34,7 @@ class UserForm(CardForm): ) ver_password = forms.CharField( required=True, - label=('Verify Password'), + label=(' Verify Password'), widget=forms.PasswordInput(render_value=False) ) diff --git a/_chapters/chp09/django_ecommerce/payments/models.py b/_chapters/chp09/django_ecommerce/payments/models.py index 646f9bf..e07919c 100644 --- a/_chapters/chp09/django_ecommerce/payments/models.py +++ b/_chapters/chp09/django_ecommerce/payments/models.py @@ -22,7 +22,7 @@ def get_by_id(cls, uid): return User.objects.get(pk=uid) @classmethod - def create(cls, name, email, password, last_4_digits, stripe_id=''): + def create(cls, name, email, password, last_4_digits, stripe_id): new_user = cls(name=name, email=email, last_4_digits=last_4_digits, stripe_id=stripe_id) new_user.set_password(password) diff --git a/_chapters/chp09/django_ecommerce/payments/views.py b/_chapters/chp09/django_ecommerce/payments/views.py index 89b72b3..bce896d 100644 --- a/_chapters/chp09/django_ecommerce/payments/views.py +++ b/_chapters/chp09/django_ecommerce/payments/views.py @@ -1,4 +1,4 @@ -from django.db import IntegrityError +from django.db import IntegrityError, transaction from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext @@ -37,7 +37,7 @@ def sign_in(request): print(form.non_field_errors()) return render_to_response( - 'payments/sign_in.html', + 'sign_in.html', { 'form': form, 'user': user @@ -75,8 +75,6 @@ def register(request): # ) cd = form.cleaned_data - from django.db import transaction - try: with transaction.atomic(): user = User.create(cd['name'], cd['email'], cd['password'], @@ -101,7 +99,7 @@ def register(request): form = UserForm() return render_to_response( - 'payments/register.html', + 'register.html', { 'form': form, 'months': list(range(1, 12)), @@ -140,7 +138,7 @@ def edit(request): form = CardForm() return render_to_response( - 'payments/edit.html', + 'edit.html', { 'form': form, 'publishable': settings.STRIPE_PUBLISHABLE, diff --git a/_chapters/chp07/django_ecommerce/static/application.js b/_chapters/chp09/django_ecommerce/static/application.js similarity index 100% rename from _chapters/chp07/django_ecommerce/static/application.js rename to _chapters/chp09/django_ecommerce/static/application.js diff --git a/_chapters/chp07/django_ecommerce/static/bootstrap.css b/_chapters/chp09/django_ecommerce/static/bootstrap.css similarity index 100% rename from _chapters/chp07/django_ecommerce/static/bootstrap.css rename to _chapters/chp09/django_ecommerce/static/bootstrap.css diff --git a/_chapters/chp09/django_ecommerce/static/css/mec.css b/_chapters/chp09/django_ecommerce/static/css/mec.css deleted file mode 100644 index 7894c6b..0000000 --- a/_chapters/chp09/django_ecommerce/static/css/mec.css +++ /dev/null @@ -1,41 +0,0 @@ -@font-face { - font-family: 'starjedi'; - /*IE9 */ - src: url('../fonts/Starjedi.eot'); - /* Chrome, FF, Opera */ - src: url('../fonts/Starjedi.woff') format('woff'), - /* android, safari, iOS */ - url('../fonts/Starjedi.ttf') format('truetype'), - /* legacy safari */ - url('../fonts/Starjedi.svg') format('svg'); - font-weight: normal; - font-style: normal; -} - -body { - padding-bottom: 40px; - background-color: #eee; -} - -h1, .nav li, .navbar-brand { - font-family: 'starjedi', sans-serif; -} - -.center-text { - text-align: center; -} - -.featurette-divider { - margin: 80px 0; -} - -.navbar-brand { - height: 40px; -} - -#darth_caption { - top: 0%; - left: 5%; - right: 60%; - text-align: left; -} \ No newline at end of file diff --git a/_chapters/chp07/django_ecommerce/static/jquery.js b/_chapters/chp09/django_ecommerce/static/jquery.js similarity index 100% rename from _chapters/chp07/django_ecommerce/static/jquery.js rename to _chapters/chp09/django_ecommerce/static/jquery.js diff --git a/_chapters/chp07/django_ecommerce/static/jquery.min.js b/_chapters/chp09/django_ecommerce/static/jquery.min.js similarity index 100% rename from _chapters/chp07/django_ecommerce/static/jquery.min.js rename to _chapters/chp09/django_ecommerce/static/jquery.min.js diff --git a/_chapters/chp07/django_ecommerce/static/jquery_ujs.js b/_chapters/chp09/django_ecommerce/static/jquery_ujs.js similarity index 100% rename from _chapters/chp07/django_ecommerce/static/jquery_ujs.js rename to _chapters/chp09/django_ecommerce/static/jquery_ujs.js diff --git a/_chapters/chp09/django_ecommerce/static/js/application.js b/_chapters/chp09/django_ecommerce/static/js/application.js deleted file mode 100755 index 709b3ac..0000000 --- a/_chapters/chp09/django_ecommerce/static/js/application.js +++ /dev/null @@ -1,44 +0,0 @@ -$(function() { - - $("#user_form").submit(function() { - if ( $("#credit-card").is(":visible")) { - var form = this; - var card = { - number: $("#credit_card_number").val(), - expMonth: $("#expiry_month").val(), - expYear: $("#expiry_year").val(), - cvc: $("#cvv").val() - }; - - Stripe.createToken(card, function(status, response) { - if (status === 200) { - console.log(status, response); - $("#credit-card-errors").hide(); - $("#last_4_digits").val(response.card.last4); - $("#stripe_token").val(response.id); - form.submit(); - } else { - // submit anyway - form.submit(); - // $("#stripe-error-message").text(response.error.message); - // $("#credit-card-errors").show(); - // $("#user_submit").attr("disabled", false); - } - }); - - return false; - - } - - return true - - }); - - $("#change-card a").click(function() { - $("#change-card").hide(); - $("#credit-card").show(); - $("#credit_card_number").focus(); - return false; - }); - -}); diff --git a/_chapters/chp07/django_ecommerce/templates/base.html b/_chapters/chp09/django_ecommerce/templates/base.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/base.html rename to _chapters/chp09/django_ecommerce/templates/base.html diff --git a/_chapters/chp07/django_ecommerce/templates/cardform.html b/_chapters/chp09/django_ecommerce/templates/cardform.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/cardform.html rename to _chapters/chp09/django_ecommerce/templates/cardform.html diff --git a/_chapters/chp07/django_ecommerce/templates/contact.html b/_chapters/chp09/django_ecommerce/templates/contact.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/contact.html rename to _chapters/chp09/django_ecommerce/templates/contact.html diff --git a/_chapters/chp07/django_ecommerce/templates/edit.html b/_chapters/chp09/django_ecommerce/templates/edit.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/edit.html rename to _chapters/chp09/django_ecommerce/templates/edit.html diff --git a/_chapters/chp07/django_ecommerce/templates/errors.html b/_chapters/chp09/django_ecommerce/templates/errors.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/errors.html rename to _chapters/chp09/django_ecommerce/templates/errors.html diff --git a/_chapters/chp07/django_ecommerce/templates/field.html b/_chapters/chp09/django_ecommerce/templates/field.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/field.html rename to _chapters/chp09/django_ecommerce/templates/field.html diff --git a/_chapters/chp09/django_ecommerce/templates/flatpages/default.html b/_chapters/chp09/django_ecommerce/templates/flatpages/default.html index e90e16f..2ec7565 100644 --- a/_chapters/chp09/django_ecommerce/templates/flatpages/default.html +++ b/_chapters/chp09/django_ecommerce/templates/flatpages/default.html @@ -1,4 +1,4 @@ -{% extends "__base.html" %} +{% extends 'base.html' %} {% block content %} {{ flatpage.content }} diff --git a/_chapters/chp07/django_ecommerce/templates/home.html b/_chapters/chp09/django_ecommerce/templates/home.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/home.html rename to _chapters/chp09/django_ecommerce/templates/home.html diff --git a/_chapters/chp07/django_ecommerce/templates/index.html b/_chapters/chp09/django_ecommerce/templates/index.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/index.html rename to _chapters/chp09/django_ecommerce/templates/index.html diff --git a/_chapters/chp09/django_ecommerce/templates/main/user.html b/_chapters/chp09/django_ecommerce/templates/main/user.html deleted file mode 100644 index b2b5638..0000000 --- a/_chapters/chp09/django_ecommerce/templates/main/user.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "__base.html" %} -{% block content %} -
-
-
-
-

Member's Only Page


-

Welcome {{ user.name }}.

-

Click here to make changes to your credit card.

-{% endblock %} diff --git a/_chapters/chp09/django_ecommerce/templates/payments/_cardform.html b/_chapters/chp09/django_ecommerce/templates/payments/_cardform.html deleted file mode 100644 index d8ae6e7..0000000 --- a/_chapters/chp09/django_ecommerce/templates/payments/_cardform.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - -
- -
diff --git a/_chapters/chp09/django_ecommerce/templates/payments/_field.html b/_chapters/chp09/django_ecommerce/templates/payments/_field.html deleted file mode 100644 index 7161e09..0000000 --- a/_chapters/chp09/django_ecommerce/templates/payments/_field.html +++ /dev/null @@ -1,6 +0,0 @@ -
- {{ field.label_tag }} -
- {{ field }} -
-
diff --git a/_chapters/chp09/django_ecommerce/templates/payments/register.html b/_chapters/chp09/django_ecommerce/templates/payments/register.html deleted file mode 100644 index 7d36007..0000000 --- a/_chapters/chp09/django_ecommerce/templates/payments/register.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "__base.html" %} -{% block content %} -
-
-
-
- -
-
-
- {% csrf_token %} - {% if form.is_bound and not form.is_valid %} -
× -
- {% for field in form.visible_fields %} - {% for error in field.errors %} -

{{ field.label }}: {{ error }}

- {% endfor %} - {% endfor %} - {% for error in form.non_field_errors %} -

{{ error }}

- {% endfor %} -
-
- {% endif %} - {% for field in form.visible_fields %} - {% include "payments/_field.html" %} - {% endfor %} - - {% include "payments/_cardform.html" %} -
-
-
-{% endblock %} diff --git a/_chapters/chp07/django_ecommerce/templates/register.html b/_chapters/chp09/django_ecommerce/templates/register.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/register.html rename to _chapters/chp09/django_ecommerce/templates/register.html diff --git a/_chapters/chp07/django_ecommerce/templates/sign_in.html b/_chapters/chp09/django_ecommerce/templates/sign_in.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/sign_in.html rename to _chapters/chp09/django_ecommerce/templates/sign_in.html diff --git a/_chapters/chp07/django_ecommerce/templates/test.html b/_chapters/chp09/django_ecommerce/templates/test.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/test.html rename to _chapters/chp09/django_ecommerce/templates/test.html diff --git a/_chapters/chp07/django_ecommerce/templates/user.html b/_chapters/chp09/django_ecommerce/templates/user.html similarity index 100% rename from _chapters/chp07/django_ecommerce/templates/user.html rename to _chapters/chp09/django_ecommerce/templates/user.html diff --git a/_chapters/chp09/django_ecommerce/test.db b/_chapters/chp09/django_ecommerce/test.db index 126c853bd7ce737481e73ccbb26d286851bc839c..f9fe606038696a2c21f1fe5403550cd7493e07f7 100644 GIT binary patch delta 1210 zcmds0O-~a+7~bu4k)RMsL{cS&1cD}{&Ft*Xel!@^LMbh2YoRT46QbRIZ`-Zyw%zV} zfCg_IM0+q^^nm^V(}VGZ`Xju1)8F95Z7>o|33rppJDEI__kEsco;f%G2gl&}HWeWV z0zIEyCL`}3y$wuIq>BJI9j_cSfuY^yQL0BS(O5;0rhbPeIO}u?>88fJ6=xccyLR93DoU8o<5% z(5?RGXUq4lQ}&yY;HO;)WN(MP*r-0<-Em$ex_2cPKSfKu8b7t4D@M{AlA@^vSeduv zb!MZn>J!AkD&G$6NL$#@pzf=y#RuYUb1|@$#2qn-a{2Q8GoHK~@dFPb_F}<75iu}Po`F75sNY{{UICAg{19ut0r>krfdxDC2$-@7a=^?-~0I5 zF>R}(onv;Tz!3r7fm84m9DyIUohD#ifG5?0;!$#%mTNXw3e|L}GN3^Xkf8*fz0*_a z`ncWDNgNVj+Ob6L{g>XIIK~DlNw7eG1>3F}V26YHCmcSZR2P$CEtZIE&L<5y=vNRO z;5HdUC@}SKVl&mK#I0z1y;j9(HN1#o93zW<{dYL*l#1s+6!R?2Goe~h)6Ai-@+Tab cn5?Aza#=)c;e>e>fxmlkm}43GA_TL)0C?I>{Qv*} delta 153 zcmZpez}(QlJVBb(m4Si5V4{LOqwB_mCGK3zOfn42VoWocWHt-(a4@m8`8J9=8g7>H zdcnxW%$&=>oXfnOc@j`Ao_TYw|8yIm;9>^mhs=kV7Xt;SGPB4rJ2FnL_mbLtu-{&T zj~8e - - - -
- -
diff --git a/_chapters/chp10/django_ecommerce/templates/payments/_field.html b/_chapters/chp10/django_ecommerce/templates/payments/_field.html deleted file mode 100644 index 7161e09..0000000 --- a/_chapters/chp10/django_ecommerce/templates/payments/_field.html +++ /dev/null @@ -1,6 +0,0 @@ -
- {{ field.label_tag }} -
- {{ field }} -
-
diff --git a/_chapters/chp10/django_ecommerce/templates/payments/register.html b/_chapters/chp10/django_ecommerce/templates/payments/register.html deleted file mode 100644 index 7d36007..0000000 --- a/_chapters/chp10/django_ecommerce/templates/payments/register.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "__base.html" %} -{% block content %} -
-
-
-
- -
-
-
- {% csrf_token %} - {% if form.is_bound and not form.is_valid %} -
× -
- {% for field in form.visible_fields %} - {% for error in field.errors %} -

{{ field.label }}: {{ error }}

- {% endfor %} - {% endfor %} - {% for error in form.non_field_errors %} -

{{ error }}

- {% endfor %} -
-
- {% endif %} - {% for field in form.visible_fields %} - {% include "payments/_field.html" %} - {% endfor %} - - {% include "payments/_cardform.html" %} -
-
-
-{% endblock %} diff --git a/_chapters/chp10/requirements.txt b/_chapters/chp10/requirements.txt deleted file mode 100644 index cdd1183..0000000 --- a/_chapters/chp10/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Django==1.7 -django-embed-video==0.11 -mock==1.0.1 -psycopg2==2.5.3 -requests==2.3.0 -stripe==1.9.2 diff --git a/_chapters/chp11/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp11/django_ecommerce/django_ecommerce/settings.py index e70706e..daeb677 100644 --- a/_chapters/chp11/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp11/django_ecommerce/django_ecommerce/settings.py @@ -124,8 +124,6 @@ 'django.contrib.flatpages', 'contact', 'payments', - 'embed_video', - 'rest_framework', ) SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' diff --git a/_chapters/chp11/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp11/django_ecommerce/django_ecommerce/urls.py index f225b07..11db113 100644 --- a/_chapters/chp11/django_ecommerce/django_ecommerce/urls.py +++ b/_chapters/chp11/django_ecommerce/django_ecommerce/urls.py @@ -3,20 +3,15 @@ from django.contrib import admin admin.autodiscover() -urlpatterns = patterns( - '', +urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), url(r'^$', 'main.views.index', name='home'), url(r'^pages/', include('django.contrib.flatpages.urls')), url(r'^contact/', 'contact.views.contact', name='contact'), - url(r'^report$', 'main.views.report', name="report"), # user registration/authentication url(r'^sign_in$', views.sign_in, name='sign_in'), url(r'^sign_out$', views.sign_out, name='sign_out'), url(r'^register$', views.register, name='register'), url(r'^edit$', views.edit, name='edit'), - - # api - url(r'^api/v1/', include('main.urls')), ) diff --git a/_chapters/chp11/django_ecommerce/main/models.py b/_chapters/chp11/django_ecommerce/main/models.py index 484fda4..1b8cc9d 100644 --- a/_chapters/chp11/django_ecommerce/main/models.py +++ b/_chapters/chp11/django_ecommerce/main/models.py @@ -8,24 +8,3 @@ class MarketingItem(models.Model): caption = models.TextField() button_link = models.URLField(null=True, default="register") button_title = models.CharField(max_length=20, default="View details") - - -class StatusReport(models.Model): - user = models.ForeignKey('payments.User') - when = models.DateTimeField(auto_now_add=True) - status = models.CharField(max_length=200) - -class Announcement(models.Model): - when = models.DateTimeField(auto_now=True) - img = models.CharField(max_length=255, null=True) - vid = models.URLField(null=True) - info = models.TextField() - - -class Badge(models.Model): - img = models.CharField(max_length=255) - name = models.CharField(max_length=100) - desc = models.TextField() - - class Meta: - ordering = ('name',) diff --git a/_chapters/chp11/django_ecommerce/main/views.py b/_chapters/chp11/django_ecommerce/main/views.py index e55af77..8453efc 100644 --- a/_chapters/chp11/django_ecommerce/main/views.py +++ b/_chapters/chp11/django_ecommerce/main/views.py @@ -1,7 +1,7 @@ -from django.shortcuts import render_to_response, RequestContext +from django.shortcuts import render_to_response from payments.models import User -from main.models import MarketingItem, StatusReport, Announcement -from datetime import date, timedelta +from main.models import MarketingItem +#from main.templatetags.main_marketing import marketing__circle_item class market_item(object): @@ -45,45 +45,15 @@ def __init__(self, img, heading, caption, button_link="register", def index(request): uid = request.session.get('user') + market_items = MarketingItem.objects.all() if uid is None: - #main landing page - market_items = MarketingItem.objects.all() return render_to_response( 'main/index.html', {'marketing_items': market_items} ) else: - #membership page - status = StatusReport.objects.all().order_by('-when')[:20] - - announce_date = date.today() - timedelta(days=30) - announce = (Announcement.objects.filter( - when__gt=announce_date).order_by('-when') - ) - - usr = User.get_by_id(uid) - badges = usr.badges.all() - return render_to_response( 'main/user.html', - { - 'user': usr, - 'badges': badges, - 'reports': status, - 'announce': announce - }, - context_instance=RequestContext(request), + {'marketing_items': market_items, + 'user': User.get_by_id(uid)} ) - - -def report(request): - if request.method == "POST": - status = request.POST.get("status", "") - #update the database with the status - if status: - uid = request.session.get('user') - user = User.get_by_id(uid) - StatusReport(user=user, status=status).save() - - #always return something - return index(request) diff --git a/_chapters/chp11/django_ecommerce/payments/models.py b/_chapters/chp11/django_ecommerce/payments/models.py index 93db6d9..646f9bf 100644 --- a/_chapters/chp11/django_ecommerce/payments/models.py +++ b/_chapters/chp11/django_ecommerce/payments/models.py @@ -1,5 +1,4 @@ from django.db import models -from main.models import Badge from django.contrib.auth.models import AbstractBaseUser from datetime import datetime @@ -12,8 +11,6 @@ class User(AbstractBaseUser): stripe_id = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) - rank = models.CharField(max_length=50, default="Padwan") - badges = models.ManyToManyField(Badge) USERNAME_FIELD = 'email' diff --git a/_chapters/chp11/django_ecommerce/static/css/mec.css b/_chapters/chp11/django_ecommerce/static/css/mec.css index 6cda1a0..7894c6b 100644 --- a/_chapters/chp11/django_ecommerce/static/css/mec.css +++ b/_chapters/chp11/django_ecommerce/static/css/mec.css @@ -38,42 +38,4 @@ h1, .nav li, .navbar-brand { left: 5%; right: 60%; text-align: left; -} - -/* Member Page */ -.info-box { - border: 2px solid #000000; - margin-bottom: 20px; - padding-left: 10px; - padding-right: 10px; - padding-bottom: 5px; - background-color: #eee; -} -#user_info { - text-align: center; - margin-left: 20px; -} -#user_info ul { - list-style-type: none; - text-align: left; -} -.member-page { - padding-top: 20px; - padding-bottom: 40px; - background-color: white; - margin-left: 0px; - margin-right: 0px; - margin-top: -20px; -} - -.full-image { - overflow: hidden; -} - -.full-image img { - position: relative; - display: block; - margin: auto; - min-width: 100%; - min-height: 100px; } \ No newline at end of file diff --git a/_chapters/chp11/django_ecommerce/static/js/application.js b/_chapters/chp11/django_ecommerce/static/js/application.js index 3a3d6a5..709b3ac 100755 --- a/_chapters/chp11/django_ecommerce/static/js/application.js +++ b/_chapters/chp11/django_ecommerce/static/js/application.js @@ -41,19 +41,4 @@ $(function() { return false; }); - //show status - $("#show-achieve").click(function() { - console.log("test") - a = $("#achievements"); - l = $("#show-achieve"); - if (a.hasClass("hide")) { - a.hide().removeClass('hide').slideDown('slow'); - l.html("Hide Achievements"); - } else { - a.addClass("hide"); - l.html("Show Achievements"); - } - return false; - }); - }); diff --git a/_chapters/chp11/django_ecommerce/templates/__base.html b/_chapters/chp11/django_ecommerce/templates/__base.html index 3f9bb21..fc93370 100644 --- a/_chapters/chp11/django_ecommerce/templates/__base.html +++ b/_chapters/chp11/django_ecommerce/templates/__base.html @@ -65,6 +65,8 @@ + + - + - - - - - {% block extrajs %}{% endblock %} + \ No newline at end of file diff --git a/_chapters/chp14/django_ecommerce/templates/main/_jedibadge.html b/_chapters/chp14/django_ecommerce/templates/main/_jedibadge.html index 47c221a..6047b5d 100755 --- a/_chapters/chp14/django_ecommerce/templates/main/_jedibadge.html +++ b/_chapters/chp14/django_ecommerce/templates/main/_jedibadge.html @@ -8,7 +8,7 @@

Jedi Badge

  • Rank: {{user.rank}}
  • Name: {{user.name }}
  • Email: {{user.email }}
  • -
  • [[ show_hide_label ]] Achievements
  • +
  • Show Achievements
  • Click here to make changes to your credit card.

    \ No newline at end of file diff --git a/_chapters/chp14/django_ecommerce/templates/main/user.html b/_chapters/chp14/django_ecommerce/templates/main/user.html index ffcd014..e47af35 100644 --- a/_chapters/chp14/django_ecommerce/templates/main/user.html +++ b/_chapters/chp14/django_ecommerce/templates/main/user.html @@ -1,9 +1,7 @@ {% extends "__base.html" %} {% load staticfiles %} {% block content %} -
    -
    +
    {% include "main/_badges.html" %}
    @@ -17,11 +15,7 @@
    {% include "main/_jedibadge.html" %} {% include "main/_statusupdate.html" %} - {% include "djangular_polls/_polls.html" %}
    -
    -{% endblock %} -{% block extrajs %} {% endblock %} \ No newline at end of file diff --git a/_chapters/chp14/tests/contact/testContactModels.py b/_chapters/chp14/tests/contact/testContactModels.py index c97b852..d2684e7 100644 --- a/_chapters/chp14/tests/contact/testContactModels.py +++ b/_chapters/chp14/tests/contact/testContactModels.py @@ -7,8 +7,7 @@ class UserModelTest(TestCase): @classmethod - def setUpClass(cls): - super().setUpClass() + def setUpTestData(cls): ContactForm(email="test@dummy.com", name="test").save() ContactForm(email="j@j.com", name="jj").save() cls.firstUser = ContactForm( diff --git a/_chapters/chp14/tests/main/testJSONViews.py b/_chapters/chp14/tests/main/testJSONViews.py index 9e309ed..21521a9 100644 --- a/_chapters/chp14/tests/main/testJSONViews.py +++ b/_chapters/chp14/tests/main/testJSONViews.py @@ -5,7 +5,7 @@ from rest_framework.test import APIRequestFactory, force_authenticate from rest_framework import status from payments.models import User - +from datetime import datetime class JsonViewTests(TestCase): @@ -16,9 +16,11 @@ def setUpClass(cls): @classmethod def setUpTestData(cls): - cls.test_user = User(id=2222, email="test@user.com") + cls.test_user = User(id=2222, + email="test@user.com",last_login=datetime.now()) cls.test_user.save() + def get_request(self, method='GET', authed=True): request_method = getattr(self.factory, method.lower()) request = request_method("") diff --git a/_chapters/chp14/tests/main/testMainPageView.py b/_chapters/chp14/tests/main/testMainPageView.py index 4053cee..75fdf8d 100644 --- a/_chapters/chp14/tests/main/testMainPageView.py +++ b/_chapters/chp14/tests/main/testMainPageView.py @@ -8,7 +8,6 @@ import mock from main.migrations.data_load_marketing_items_0003 import init_marketing_data - class MainPageTests(TestCase): ############### diff --git a/_chapters/chp14/tests/main/testSerializers.py b/_chapters/chp14/tests/main/testSerializers.py index 6c19bff..467e792 100644 --- a/_chapters/chp14/tests/main/testSerializers.py +++ b/_chapters/chp14/tests/main/testSerializers.py @@ -66,3 +66,4 @@ def test_json_to_new_StatusReport(self): self.assertEqual(self.new_status.status, status.status) self.assertIsNotNone(status.when) self.assertEqual(self.new_status.user, status.user) + diff --git a/_chapters/chp15/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp15/django_ecommerce/django_ecommerce/settings.py index 479a8c7..e70706e 100644 --- a/_chapters/chp15/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp15/django_ecommerce/django_ecommerce/settings.py @@ -126,7 +126,6 @@ 'payments', 'embed_video', 'rest_framework', - 'djangular_polls' ) SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' diff --git a/_chapters/chp15/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp15/django_ecommerce/django_ecommerce/urls.py index 14c99b5..f225b07 100644 --- a/_chapters/chp15/django_ecommerce/django_ecommerce/urls.py +++ b/_chapters/chp15/django_ecommerce/django_ecommerce/urls.py @@ -1,13 +1,7 @@ from django.conf.urls import patterns, include, url -from django.contrib import admin from payments import views -from main.urls import urlpatterns as main_json_urls -from djangular_polls.urls import urlpatterns as djangular_polls_json_urls -from payments.urls import urlpatterns as payments_json_urls - +from django.contrib import admin admin.autodiscover() -main_json_urls.extend(djangular_polls_json_urls) -main_json_urls.extend(payments_json_urls) urlpatterns = patterns( '', diff --git a/_chapters/chp15/django_ecommerce/main/models.py b/_chapters/chp15/django_ecommerce/main/models.py index 15a96df..484fda4 100644 --- a/_chapters/chp15/django_ecommerce/main/models.py +++ b/_chapters/chp15/django_ecommerce/main/models.py @@ -15,8 +15,6 @@ class StatusReport(models.Model): when = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=200) - - class Announcement(models.Model): when = models.DateTimeField(auto_now=True) img = models.CharField(max_length=255, null=True) diff --git a/_chapters/chp15/django_ecommerce/payments/forms.py b/_chapters/chp15/django_ecommerce/payments/forms.py index 698b1c4..9af62da 100644 --- a/_chapters/chp15/django_ecommerce/payments/forms.py +++ b/_chapters/chp15/django_ecommerce/payments/forms.py @@ -13,9 +13,6 @@ class SigninForm(PaymentForm): required=True, widget=forms.PasswordInput(render_value=False) ) - form_name = 'signin_form' - ng_scope_prefix = 'signinform' - class CardForm(PaymentForm): last_4_digits = forms.CharField( @@ -28,8 +25,7 @@ class CardForm(PaymentForm): class UserForm(CardForm): - - name = forms.CharField(required=True, min_length=3) + name = forms.CharField(required=True) email = forms.EmailField(required=True) password = forms.CharField( required=True, @@ -42,20 +38,6 @@ class UserForm(CardForm): widget=forms.PasswordInput(render_value=False) ) - form_name = 'user_form' - ng_scope_prefix = "userform" - - def __init__(self, *args, ** kwargs): - super(UserForm, self).__init__(*args, **kwargs) - for name, field in self.fields.items(): - attrs = {"ng-model": "%s.%s" % (self.ng_scope_prefix, name)} - - if field.required: - attrs.update({"required": True}) - if field.min_length: - attrs.update({"ng-minlength": field.min_length}) - field.widget.attrs.update(attrs) - def clean(self): cleaned_data = self.cleaned_data password = cleaned_data.get('password') diff --git a/_chapters/chp15/django_ecommerce/payments/models.py b/_chapters/chp15/django_ecommerce/payments/models.py index 294e916..fc477da 100644 --- a/_chapters/chp15/django_ecommerce/payments/models.py +++ b/_chapters/chp15/django_ecommerce/payments/models.py @@ -1,7 +1,8 @@ +from django.db import models from main.models import Badge from django.contrib.auth.models import AbstractBaseUser from datetime import datetime -from django.db import models +from django.db import connection, models class User(AbstractBaseUser): @@ -14,13 +15,14 @@ class User(AbstractBaseUser): updated_at = models.DateTimeField(auto_now=True) rank = models.CharField(max_length=50, default="Padwan") badges = models.ManyToManyField(Badge) - bigCoID = models.CharField(max_length=50, unique=True) + bigCoID = models.CharField(max_length=50,unique=True) USERNAME_FIELD = 'email' def __str__(self): return self.email + @classmethod def get_by_id(cls, uid): return User.objects.get(pk=uid) @@ -32,9 +34,10 @@ def create(cls, name, email, password, last_4_digits, stripe_id=''): new_user.set_password(password) #set bigCoID - new_user.bigCoID = ("%s%s%s" % (new_user.name[:2], + new_user.bigCoID = ("%s%s%s" % (new_user.name[:2], new_user.rank[:1], - datetime.now().strftime("%Y%m%d%H%M%S%f"),)) + datetime.now().strftime("%Y%m%d%H%M%S%f"), + )) new_user.save() return new_user diff --git a/_chapters/chp15/django_ecommerce/payments/urls.py b/_chapters/chp15/django_ecommerce/payments/urls.py deleted file mode 100644 index 7270671..0000000 --- a/_chapters/chp15/django_ecommerce/payments/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls import patterns, url - - -urlpatterns = patterns( - 'payments.json_views', - url(r'^users$', 'post_user'), -) diff --git a/_chapters/chp15/django_ecommerce/payments/views.py b/_chapters/chp15/django_ecommerce/payments/views.py index 72d2e53..89b72b3 100644 --- a/_chapters/chp15/django_ecommerce/payments/views.py +++ b/_chapters/chp15/django_ecommerce/payments/views.py @@ -1,6 +1,5 @@ -from django.db import IntegrityError, transaction -from django.http import HttpResponseRedirect, HttpResponseBadRequest, \ - HttpResponse +from django.db import IntegrityError +from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from payments.forms import SigninForm, CardForm, UserForm @@ -9,7 +8,6 @@ import stripe import datetime import socket -import json stripe.api_key = settings.STRIPE_SECRET @@ -59,26 +57,26 @@ def sign_out(request): def register(request): user = None if request.method == 'POST': - # We only talk AJAX posts now - if not request.is_ajax(): - return HttpResponseBadRequest("I only speak AJAX nowadays") - - data = json.loads(request.body.decode()) - form = UserForm(data) - + form = UserForm(request.POST) if form.is_valid(): - try: - customer = Customer.create( - "subscription", - email=form.cleaned_data['email'], - description=form.cleaned_data['name'], - card=form.cleaned_data['stripe_token'], - plan="gold", - ) - except Exception as exp: - form.addError(exp) + + #update based on your billing method (subscription vs one time) + customer = Customer.create( + email=form.cleaned_data['email'], + description=form.cleaned_data['name'], + card=form.cleaned_data['stripe_token'], + plan="gold", + ) + # customer = stripe.Charge.create( + # description=form.cleaned_data['email'], + # card=form.cleaned_data['stripe_token'], + # amount="5000", + # currency="usd" + # ) cd = form.cleaned_data + from django.db import transaction + try: with transaction.atomic(): user = User.create(cd['name'], cd['email'], cd['password'], @@ -91,16 +89,13 @@ def register(request): UnpaidUsers(email=cd['email']).save() except IntegrityError: - resp = json.dumps({"status": "fail", "errors": - cd['email'] + ' is already a member'}) + import traceback + form.addError(cd['email'] + ' is already a member' + + traceback.format_exc()) + user = None else: request.session['user'] = user.pk - resp = json.dumps({"status": "ok", "url": '/'}) - - return HttpResponse(resp, content_type="application/json") - else: # form not valid - resp = json.dumps({"status": "form-invalid", "errors": form.errors}) - return HttpResponse(resp, content_type="application/json") + return HttpResponseRedirect('/') else: form = UserForm() diff --git a/_chapters/chp15/django_ecommerce/static/css/mec.css b/_chapters/chp15/django_ecommerce/static/css/mec.css index 11de092..6cda1a0 100644 --- a/_chapters/chp15/django_ecommerce/static/css/mec.css +++ b/_chapters/chp15/django_ecommerce/static/css/mec.css @@ -60,6 +60,7 @@ h1, .nav li, .navbar-brand { .member-page { padding-top: 20px; padding-bottom: 40px; + background-color: white; margin-left: 0px; margin-right: 0px; margin-top: -20px; @@ -75,16 +76,4 @@ h1, .nav li, .navbar-brand { margin: auto; min-width: 100%; min-height: 100px; -} - -input.ng-dirty.ng-valid { - border:1px solid Green; -} -input.ng-dirty.ng-invalid { - border:1px solid Red; -} - -.custom-error { - color: #FF0000; - font-family: sans-serif; } \ No newline at end of file diff --git a/_chapters/chp15/django_ecommerce/static/js/application.js b/_chapters/chp15/django_ecommerce/static/js/application.js index 75da0bf..3a3d6a5 100755 --- a/_chapters/chp15/django_ecommerce/static/js/application.js +++ b/_chapters/chp15/django_ecommerce/static/js/application.js @@ -1,68 +1,59 @@ -var mecApp = angular.module('mecApp', []); - -mecApp.config(function($interpolateProvider, $httpProvider) { - $interpolateProvider.startSymbol('[[') - .endSymbol(']]'); - $httpProvider.defaults.headers.common['X-CSRFToken'] = $('input[name=csrfmiddlewaretoken]').val(); -}); - - $(function() { - // $("#user_form").submit(function() { - // if ( $("#credit-card").is(":visible")) { - // var form = this; - // var card = { - // number: $("#credit_card_number").val(), - // expMonth: $("#expiry_month").val(), - // expYear: $("#expiry_year").val(), - // cvc: $("#cvv").val() - // }; - - // Stripe.createToken(card, function(status, response) { - // if (status === 200) { - // console.log(status, response); - // $("#credit-card-errors").hide(); - // $("#last_4_digits").val(response.card.last4); - // $("#stripe_token").val(response.id); - // form.submit(); - // } else { - // // submit anyway - // form.submit(); - // // $("#stripe-error-message").text(response.error.message); - // // $("#credit-card-errors").show(); - // // $("#user_submit").attr("disabled", false); - // } - // }); - - // return false; - - // } - - // return true - - // }); - - // $("#change-card a").click(function() { - // $("#change-card").hide(); - // $("#credit-card").show(); - // $("#credit_card_number").focus(); - // return false; - // }); - - // //show status - // $("#show-achieve").click(function() { - // console.log("test") - // a = $("#achievements"); - // l = $("#show-achieve"); - // if (a.hasClass("hide")) { - // a.hide().removeClass('hide').slideDown('slow'); - // l.html("Hide Achievements"); - // } else { - // a.addClass("hide"); - // l.html("Show Achievements"); - // } - // return false; - // }); + $("#user_form").submit(function() { + if ( $("#credit-card").is(":visible")) { + var form = this; + var card = { + number: $("#credit_card_number").val(), + expMonth: $("#expiry_month").val(), + expYear: $("#expiry_year").val(), + cvc: $("#cvv").val() + }; + + Stripe.createToken(card, function(status, response) { + if (status === 200) { + console.log(status, response); + $("#credit-card-errors").hide(); + $("#last_4_digits").val(response.card.last4); + $("#stripe_token").val(response.id); + form.submit(); + } else { + // submit anyway + form.submit(); + // $("#stripe-error-message").text(response.error.message); + // $("#credit-card-errors").show(); + // $("#user_submit").attr("disabled", false); + } + }); + + return false; + + } + + return true + + }); + + $("#change-card a").click(function() { + $("#change-card").hide(); + $("#credit-card").show(); + $("#credit_card_number").focus(); + return false; + }); + + //show status + $("#show-achieve").click(function() { + console.log("test") + a = $("#achievements"); + l = $("#show-achieve"); + if (a.hasClass("hide")) { + a.hide().removeClass('hide').slideDown('slow'); + l.html("Hide Achievements"); + } else { + a.addClass("hide"); + l.html("Show Achievements"); + } + return false; + }); }); diff --git a/_chapters/chp15/django_ecommerce/templates/__base.html b/_chapters/chp15/django_ecommerce/templates/__base.html index 6811140..3f9bb21 100644 --- a/_chapters/chp15/django_ecommerce/templates/__base.html +++ b/_chapters/chp15/django_ecommerce/templates/__base.html @@ -1,7 +1,7 @@ {% load static %} - + @@ -73,14 +73,9 @@ - + - - - - - - {% block extrajs %}{% endblock %} + \ No newline at end of file diff --git a/_chapters/chp15/django_ecommerce/templates/main/_jedibadge.html b/_chapters/chp15/django_ecommerce/templates/main/_jedibadge.html index 47c221a..6047b5d 100755 --- a/_chapters/chp15/django_ecommerce/templates/main/_jedibadge.html +++ b/_chapters/chp15/django_ecommerce/templates/main/_jedibadge.html @@ -8,7 +8,7 @@

    Jedi Badge

  • Rank: {{user.rank}}
  • Name: {{user.name }}
  • Email: {{user.email }}
  • -
  • [[ show_hide_label ]] Achievements
  • +
  • Show Achievements
  • Click here to make changes to your credit card.

    \ No newline at end of file diff --git a/_chapters/chp15/django_ecommerce/templates/main/user.html b/_chapters/chp15/django_ecommerce/templates/main/user.html index ffcd014..e47af35 100644 --- a/_chapters/chp15/django_ecommerce/templates/main/user.html +++ b/_chapters/chp15/django_ecommerce/templates/main/user.html @@ -1,9 +1,7 @@ {% extends "__base.html" %} {% load staticfiles %} {% block content %} -
    -
    +
    {% include "main/_badges.html" %}
    @@ -17,11 +15,7 @@
    {% include "main/_jedibadge.html" %} {% include "main/_statusupdate.html" %} - {% include "djangular_polls/_polls.html" %}
    -
    -{% endblock %} -{% block extrajs %} {% endblock %} \ No newline at end of file diff --git a/_chapters/chp15/django_ecommerce/templates/payments/_cardform.html b/_chapters/chp15/django_ecommerce/templates/payments/_cardform.html index 29e4238..d8ae6e7 100644 --- a/_chapters/chp15/django_ecommerce/templates/payments/_cardform.html +++ b/_chapters/chp15/django_ecommerce/templates/payments/_cardform.html @@ -1,48 +1,36 @@ - - -
    -
    -
    - [[ stripe_errormsg ]]
    + + + + \ No newline at end of file +
    diff --git a/_chapters/chp15/django_ecommerce/templates/payments/register.html b/_chapters/chp15/django_ecommerce/templates/payments/register.html index 6e46262..7d36007 100644 --- a/_chapters/chp15/django_ecommerce/templates/payments/register.html +++ b/_chapters/chp15/django_ecommerce/templates/payments/register.html @@ -9,27 +9,34 @@

    Register Today!

    -
    -
    +
    + {% include "payments/_cardform.html" %} +
    {% endblock %} diff --git a/_chapters/chp15/django_ecommerce/templates/payments/sign_in.html b/_chapters/chp15/django_ecommerce/templates/payments/sign_in.html index 5dc5cca..c26d0ca 100644 --- a/_chapters/chp15/django_ecommerce/templates/payments/sign_in.html +++ b/_chapters/chp15/django_ecommerce/templates/payments/sign_in.html @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    -
    {% endblock %} diff --git a/_chapters/chp16/django_ecommerce/templates/payments/sign_in.html b/_chapters/chp16/django_ecommerce/templates/payments/sign_in.html index 5dc5cca..c26d0ca 100644 --- a/_chapters/chp16/django_ecommerce/templates/payments/sign_in.html +++ b/_chapters/chp16/django_ecommerce/templates/payments/sign_in.html @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    -
    {{ a.info | safe }}
    diff --git a/_chapters/chp17/django_ecommerce/templates/main/_badges.html b/_chapters/chp17/django_ecommerce/templates/main/_badges.html index e2a77bd..a1d2af0 100755 --- a/_chapters/chp17/django_ecommerce/templates/main/_badges.html +++ b/_chapters/chp17/django_ecommerce/templates/main/_badges.html @@ -4,9 +4,9 @@

    Achievements

    {% for bdg in badges %}

    {{ bdg.name }}

    -  {{ bdg.name }} +  {{ bdg.name }}

    {{ bdg.desc }}

    {% endfor %} diff --git a/_chapters/chp17/django_ecommerce/usermap/admin.py b/_chapters/chp17/django_ecommerce/usermap/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/_chapters/chp17/django_ecommerce/usermap/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/_chapters/chp17/requirements.txt b/_chapters/chp17/requirements.txt index e80f540..7216e2b 100644 --- a/_chapters/chp17/requirements.txt +++ b/_chapters/chp17/requirements.txt @@ -1,12 +1,7 @@ Django==1.7 -Pillow==2.7.0 -django-admin-bootstrapped==2.3.4 django-embed-video==0.11 -django-rest-framework-mongoengine==1.5.4 djangorestframework==2.4.2 mock==1.0.1 -mongoengine==0.8.7 psycopg2==2.5.3 -pymongo==2.7.2 requests==2.3.0 stripe==1.9.2 diff --git a/_chapters/chp17/tests/contact/testContactModels.py b/_chapters/chp17/tests/contact/testContactModels.py index 1bb5e72..d2684e7 100644 --- a/_chapters/chp17/tests/contact/testContactModels.py +++ b/_chapters/chp17/tests/contact/testContactModels.py @@ -16,8 +16,8 @@ def setUpTestData(cls): timestamp=datetime.today() + timedelta(days=2) ) cls.firstUser.save() - # cls.test_user=User(email="j@j.com", name ='test user') - # cls.test_user.save() + #cls.test_user=User(email="j@j.com", name ='test user') + #cls.test_user.save() def test_contactform_str_returns_email(self): self.assertEquals("first@first.com", str(self.firstUser)) diff --git a/_chapters/chp17/tests/payments/testViews.py b/_chapters/chp17/tests/payments/testViews.py index d227451..1ca0128 100644 --- a/_chapters/chp17/tests/payments/testViews.py +++ b/_chapters/chp17/tests/payments/testViews.py @@ -80,7 +80,7 @@ class RegisterPageTests(TestCase, ViewTesterMixin): @classmethod def setUpClass(cls): - super().setUpClass() + super().setUpClass() html = render_to_response( 'payments/register.html', { diff --git a/_chapters/chp18/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp18/django_ecommerce/django_ecommerce/settings.py index 28a5181..04b957c 100644 --- a/_chapters/chp18/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp18/django_ecommerce/django_ecommerce/settings.py @@ -61,12 +61,12 @@ # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/var/www/example.com/media/" -MEDIA_ROOT = os.path.join(SITE_ROOT, 'media') +MEDIA_ROOT = '' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://example.com/media/", "http://media.example.com/" -MEDIA_URL = '/media/' +MEDIA_URL = '' # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files @@ -125,8 +125,6 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'main', - 'django_admin_bootstrapped.bootstrap3', - 'django_admin_bootstrapped', 'django.contrib.admin', 'django.contrib.flatpages', 'contact', diff --git a/_chapters/chp18/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp18/django_ecommerce/django_ecommerce/urls.py index 5189ca3..b531795 100644 --- a/_chapters/chp18/django_ecommerce/django_ecommerce/urls.py +++ b/_chapters/chp18/django_ecommerce/django_ecommerce/urls.py @@ -1,5 +1,3 @@ -from django.conf import settings -from django.conf.urls.static import static from django.conf.urls import patterns, include, url from django.contrib import admin from payments import views @@ -31,4 +29,4 @@ # api url(r'^api/v1/', include('main.urls')), -) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +) diff --git a/_chapters/chp18/django_ecommerce/djangular_polls/admin.py b/_chapters/chp18/django_ecommerce/djangular_polls/admin.py index 3849734..8c38f3f 100644 --- a/_chapters/chp18/django_ecommerce/djangular_polls/admin.py +++ b/_chapters/chp18/django_ecommerce/djangular_polls/admin.py @@ -1,36 +1,3 @@ from django.contrib import admin -from django.utils.html import format_html -from djangular_polls.models import Poll, PollItem - - -class PollItemInline(admin.TabularInline): - model = PollItem - - readonly_fields = ('votes',) - ordering = ('-votes',) - - -@admin.register(Poll) -class PollAdmin(admin.ModelAdmin): - - inlines = [PollItemInline, ] - - list_display = ('publish_date', 'title', 'highest_vote', - 'list_items', 'total_votes', ) - - def highest_vote(self, poll): - try: - return poll.poll_items().order_by('-votes')[0].text - except IndexError: - return "No Poll Items for this Poll" - - def list_items(self, poll): - html = "

    %s

      " % (poll.title) - html += "\n".join("
    • %s - %d
    • " % - (pi.text, pi.votes) for pi in poll.poll_items()) - html += "
    " - return format_html(html) - - list_items.short_description = "Poll results" - list_items.allow_tags = True +# Register your models here. diff --git a/_chapters/chp18/django_ecommerce/main/models.py b/_chapters/chp18/django_ecommerce/main/models.py index 7571825..484fda4 100644 --- a/_chapters/chp18/django_ecommerce/main/models.py +++ b/_chapters/chp18/django_ecommerce/main/models.py @@ -1,28 +1,13 @@ from django.db import models -from embed_video.fields import EmbedVideoField - -class ThumbnailMixin(object): - '''use this mixin if you want to easily show thumbnails for an image field - in your admin view - ''' - - def thumbnail(self): - if self.img: - return u'' % (self.img.url) - else: - return "no image" - - thumbnail.allow_tags = True - - -class MarketingItem(models.Model, ThumbnailMixin): - img = models.ImageField(upload_to="marketing/") +# Create your models here. +class MarketingItem(models.Model): + img = models.CharField(max_length=255) heading = models.CharField(max_length=300) caption = models.TextField() - button_link = models.CharField(null=True, blank=True, max_length=200, default="register") - button_title = models.CharField(max_length=20,default="View details") + button_link = models.URLField(null=True, default="register") + button_title = models.CharField(max_length=20, default="View details") class StatusReport(models.Model): @@ -31,34 +16,16 @@ class StatusReport(models.Model): status = models.CharField(max_length=200) class Announcement(models.Model): - when = models.DateTimeField(auto_now=True) - img = models.ImageField(upload_to="announce/", null=True, blank=True) - vid = EmbedVideoField(null=True, blank=True) + img = models.CharField(max_length=255, null=True) + vid = models.URLField(null=True) info = models.TextField() - def thumbnail(self): - if self.img: - return u'' % (self.img.url) - else: - return "no image" - - thumbnail.allow_tags = True - class Badge(models.Model): - - img = models.ImageField(upload_to="images/") + img = models.CharField(max_length=255) name = models.CharField(max_length=100) desc = models.TextField() - def thumbnail(self): - if self.img: - return u'' % (self.img.url) - else: - return "no image" - - thumbnail.allow_tags = True - class Meta: ordering = ('name',) diff --git a/_chapters/chp18/django_ecommerce/payments/admin.py b/_chapters/chp18/django_ecommerce/payments/admin.py index 4efec24..763c430 100644 --- a/_chapters/chp18/django_ecommerce/payments/admin.py +++ b/_chapters/chp18/django_ecommerce/payments/admin.py @@ -1,23 +1,9 @@ from django.contrib import admin -from django.contrib.auth.models import User as DjangoUser -from django.contrib.sites.models import Site -from django.contrib.auth.models import Group from .models import User -@admin.register(User) class UserAdmin(admin.ModelAdmin): + class Meta: + model = User - list_display = ('name', 'email', 'rank', 'last_4_digits', 'stripe_id') - ordering = ('-created_at',) - fieldsets = ( - ('User Info', {'fields': ('name', 'email', 'rank',)}), - ('Billing', {'fields': ('stripe_id',)}), - ('Badges', {'fields': ('badges',)}), - ) - filter_horizontal = ('badges',) - - -admin.site.unregister(DjangoUser) -admin.site.unregister(Group) -admin.site.unregister(Site) +admin.site.register(User, UserAdmin) diff --git a/_chapters/chp18/django_ecommerce/payments/json_views.py b/_chapters/chp18/django_ecommerce/payments/json_views.py index 4cba6cf..3105344 100644 --- a/_chapters/chp18/django_ecommerce/payments/json_views.py +++ b/_chapters/chp18/django_ecommerce/payments/json_views.py @@ -1,15 +1,10 @@ from payments.models import User, UnpaidUsers from payments.forms import UserForm from payments.views import Customer -from payments.serializers import PasswordSerializer - +from rest_framework.response import Response from django.db import transaction from django.db import IntegrityError -from django.http import Http404 - -from rest_framework import generics, status, permissions from rest_framework.decorators import api_view -from rest_framework.response import Response @api_view(['POST']) @@ -19,7 +14,7 @@ def post_user(request): print("in post user") if form.is_valid(): try: - # update based on your billing method (subscription vs one time) + #update based on your billing method (subscription vs one time) customer = Customer.create( "subscription", email=form.cleaned_data['email'], @@ -56,28 +51,3 @@ def post_user(request): else: # for not valid resp = {"status": "form-invalid", "errors": form.errors} return Response(resp) - - -class ChangePassword(generics.GenericAPIView): - """ - Change password of any user if superadmin. - * pwd - * pwd2 - """ - permission_classes = (permissions.IsAdminUser,) - serializer_class = PasswordSerializer - - def get_object(self, pk): - try: - return User.objects.get(pk=pk) - except User.DoesNotExist: - raise Http404 - - def put(self, request, pk, format=None): - user = self.get_object(pk) - serializer = PasswordSerializer(user, data=request.DATA) - if serializer.is_valid(): - serializer.save() - return Response("Password Changed.") - return Response(serializer.errors, - status=status.HTTP_400_BAD_REQUEST) diff --git a/_chapters/chp18/django_ecommerce/payments/models.py b/_chapters/chp18/django_ecommerce/payments/models.py index 539a6d2..11590c6 100644 --- a/_chapters/chp18/django_ecommerce/payments/models.py +++ b/_chapters/chp18/django_ecommerce/payments/models.py @@ -7,7 +7,7 @@ class User(AbstractBaseUser): name = models.CharField(max_length=255) email = models.CharField(max_length=255, unique=True) - # password field defined in base class + #password field defined in base class last_4_digits = models.CharField(max_length=4, blank=True, null=True) stripe_id = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) @@ -21,7 +21,6 @@ class User(AbstractBaseUser): def __str__(self): return self.email - @classmethod def get_by_id(cls, uid): return User.objects.get(pk=uid) @@ -32,11 +31,10 @@ def create(cls, name, email, password, last_4_digits, stripe_id=''): last_4_digits=last_4_digits, stripe_id=stripe_id) new_user.set_password(password) - # set bigCoID - new_user.bigCoID = "%s%s%s" % ( - new_user.name[:2], - new_user.rank[:1], - datetime.now().strftime("%Y%m%d%H%M%S%f"),) + #set bigCoID + new_user.bigCoID = "%s%s%s" % (new_user.name[:2], + new_user.rank[:1], + datetime.now().strftime("%Y%m%d%H%M%S%f"),) new_user.save() return new_user diff --git a/_chapters/chp18/django_ecommerce/payments/urls.py b/_chapters/chp18/django_ecommerce/payments/urls.py index 70bab59..7270671 100644 --- a/_chapters/chp18/django_ecommerce/payments/urls.py +++ b/_chapters/chp18/django_ecommerce/payments/urls.py @@ -1,10 +1,7 @@ from django.conf.urls import patterns, url -from .json_views import ChangePassword urlpatterns = patterns( 'payments.json_views', url(r'^users$', 'post_user'), - url(r'^users/password/(?P[0-9]+)$', ChangePassword.as_view(), - name='change_password'), ) diff --git a/_chapters/chp18/django_ecommerce/payments/views.py b/_chapters/chp18/django_ecommerce/payments/views.py index a5e445e..72d2e53 100644 --- a/_chapters/chp18/django_ecommerce/payments/views.py +++ b/_chapters/chp18/django_ecommerce/payments/views.py @@ -65,7 +65,7 @@ def register(request): data = json.loads(request.body.decode()) form = UserForm(data) - + if form.is_valid(): try: customer = Customer.create( diff --git a/_chapters/chp18/django_ecommerce/templates/main/_announcements.html b/_chapters/chp18/django_ecommerce/templates/main/_announcements.html index fc3be38..523b6a2 100755 --- a/_chapters/chp18/django_ecommerce/templates/main/_announcements.html +++ b/_chapters/chp18/django_ecommerce/templates/main/_announcements.html @@ -8,7 +8,7 @@

    orders from the Council

    {% if a.vid %} {% video a.vid "medium" %} {% else %} - + {% endif %}
    {{ a.info | safe }}
    diff --git a/_chapters/chp18/django_ecommerce/templates/main/_badges.html b/_chapters/chp18/django_ecommerce/templates/main/_badges.html index e2a77bd..a1d2af0 100755 --- a/_chapters/chp18/django_ecommerce/templates/main/_badges.html +++ b/_chapters/chp18/django_ecommerce/templates/main/_badges.html @@ -4,9 +4,9 @@

    Achievements

    {% for bdg in badges %}

    {{ bdg.name }}

    -  {{ bdg.name }} +  {{ bdg.name }}

    {{ bdg.desc }}

    {% endfor %} diff --git a/_chapters/chp18/requirements.txt b/_chapters/chp18/requirements.txt index e80f540..db0d1da 100644 --- a/_chapters/chp18/requirements.txt +++ b/_chapters/chp18/requirements.txt @@ -1,6 +1,4 @@ Django==1.7 -Pillow==2.7.0 -django-admin-bootstrapped==2.3.4 django-embed-video==0.11 django-rest-framework-mongoengine==1.5.4 djangorestframework==2.4.2 diff --git a/_chapters/chp07/tests/__init__.py b/_chapters/chp18/tests/contact/__init__.py similarity index 100% rename from _chapters/chp07/tests/__init__.py rename to _chapters/chp18/tests/contact/__init__.py diff --git a/_chapters/chp10/tests/contact/testContactModels.py b/_chapters/chp18/tests/contact/testContactModels.py similarity index 100% rename from _chapters/chp10/tests/contact/testContactModels.py rename to _chapters/chp18/tests/contact/testContactModels.py diff --git a/_chapters/chp07/tests/contact/__init__.py b/_chapters/chp18/tests/main/__init__.py similarity index 100% rename from _chapters/chp07/tests/contact/__init__.py rename to _chapters/chp18/tests/main/__init__.py diff --git a/_chapters/chp18/tests/unit/main/testJSONViews.py b/_chapters/chp18/tests/main/testJSONViews.py similarity index 100% rename from _chapters/chp18/tests/unit/main/testJSONViews.py rename to _chapters/chp18/tests/main/testJSONViews.py diff --git a/_chapters/chp18/tests/unit/main/testMainPageView.py b/_chapters/chp18/tests/main/testMainPageView.py similarity index 100% rename from _chapters/chp18/tests/unit/main/testMainPageView.py rename to _chapters/chp18/tests/main/testMainPageView.py diff --git a/_chapters/chp18/tests/unit/main/testSerializers.py b/_chapters/chp18/tests/main/testSerializers.py similarity index 100% rename from _chapters/chp18/tests/unit/main/testSerializers.py rename to _chapters/chp18/tests/main/testSerializers.py diff --git a/_chapters/chp07/tests/main/__init__.py b/_chapters/chp18/tests/payments/__init__.py similarity index 100% rename from _chapters/chp07/tests/main/__init__.py rename to _chapters/chp18/tests/payments/__init__.py diff --git a/_chapters/chp10/tests/payments/testCustomer.py b/_chapters/chp18/tests/payments/testCustomer.py similarity index 100% rename from _chapters/chp10/tests/payments/testCustomer.py rename to _chapters/chp18/tests/payments/testCustomer.py diff --git a/_chapters/chp18/tests/unit/payments/testForms.py b/_chapters/chp18/tests/payments/testForms.py similarity index 100% rename from _chapters/chp18/tests/unit/payments/testForms.py rename to _chapters/chp18/tests/payments/testForms.py diff --git a/_chapters/chp18/tests/unit/payments/testUserModel.py b/_chapters/chp18/tests/payments/testUserModel.py similarity index 100% rename from _chapters/chp18/tests/unit/payments/testUserModel.py rename to _chapters/chp18/tests/payments/testUserModel.py diff --git a/_chapters/chp18/tests/unit/payments/testViews.py b/_chapters/chp18/tests/payments/testViews.py similarity index 100% rename from _chapters/chp18/tests/unit/payments/testViews.py rename to _chapters/chp18/tests/payments/testViews.py diff --git a/_chapters/chp19/django_ecommerce/contact/models.py b/_chapters/chp19/django_ecommerce/contact/models.py index 5de0efe..1aa9e86 100644 --- a/_chapters/chp19/django_ecommerce/contact/models.py +++ b/_chapters/chp19/django_ecommerce/contact/models.py @@ -9,6 +9,7 @@ class ContactForm(models.Model): message = models.CharField(max_length=1000) timestamp = models.DateTimeField(auto_now_add=True) + def __str__(self): return self.email diff --git a/_chapters/chp19/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp19/django_ecommerce/django_ecommerce/settings.py index c0498a5..28a5181 100644 --- a/_chapters/chp19/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp19/django_ecommerce/django_ecommerce/settings.py @@ -167,8 +167,3 @@ }, } } - -try: - from .settings_prod import * -except ImportError: - pass diff --git a/_chapters/chp19/django_ecommerce/main/models.py b/_chapters/chp19/django_ecommerce/main/models.py index ba5ab4f..7571825 100644 --- a/_chapters/chp19/django_ecommerce/main/models.py +++ b/_chapters/chp19/django_ecommerce/main/models.py @@ -27,7 +27,7 @@ class MarketingItem(models.Model, ThumbnailMixin): class StatusReport(models.Model): user = models.ForeignKey('payments.User') - when = models.DateTimeField(auto_now_add=True, blank=True) + when = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=200) class Announcement(models.Model): diff --git a/_chapters/chp19/requirements.txt b/_chapters/chp19/requirements.txt index 4f6fae2..e80f540 100644 --- a/_chapters/chp19/requirements.txt +++ b/_chapters/chp19/requirements.txt @@ -1,18 +1,12 @@ Django==1.7 --e git+https://github.com/pashinin/fabric.git@324c6f01ff3147d5e7a13a3498a0d4122f33dbe8#egg=Fabric-origin/HEAD Pillow==2.7.0 django-admin-bootstrapped==2.3.4 django-embed-video==0.11 django-rest-framework-mongoengine==1.5.4 djangorestframework==2.4.2 -ecdsa==0.13 mock==1.0.1 mongoengine==0.8.7 -paramiko==1.15.2 psycopg2==2.5.3 -pycrypto==2.6.1 pymongo==2.7.2 requests==2.3.0 -selenium==2.45.0 -six==1.9.0 stripe==1.9.2 diff --git a/_chapters/chp07/tests/payments/__init__.py b/_chapters/chp19/tests/contact/__init__.py similarity index 100% rename from _chapters/chp07/tests/payments/__init__.py rename to _chapters/chp19/tests/contact/__init__.py diff --git a/_chapters/chp18/tests/unit/contact/testContactModels.py b/_chapters/chp19/tests/contact/testContactModels.py similarity index 100% rename from _chapters/chp18/tests/unit/contact/testContactModels.py rename to _chapters/chp19/tests/contact/testContactModels.py diff --git a/_chapters/chp09/django_ecommerce/main/templatetags/__init__.py b/_chapters/chp19/tests/main/__init__.py similarity index 100% rename from _chapters/chp09/django_ecommerce/main/templatetags/__init__.py rename to _chapters/chp19/tests/main/__init__.py diff --git a/_chapters/chp19/tests/unit/main/testJSONViews.py b/_chapters/chp19/tests/main/testJSONViews.py similarity index 100% rename from _chapters/chp19/tests/unit/main/testJSONViews.py rename to _chapters/chp19/tests/main/testJSONViews.py diff --git a/_chapters/chp19/tests/unit/main/testMainPageView.py b/_chapters/chp19/tests/main/testMainPageView.py similarity index 100% rename from _chapters/chp19/tests/unit/main/testMainPageView.py rename to _chapters/chp19/tests/main/testMainPageView.py diff --git a/_chapters/chp19/tests/unit/main/testSerializers.py b/_chapters/chp19/tests/main/testSerializers.py similarity index 100% rename from _chapters/chp19/tests/unit/main/testSerializers.py rename to _chapters/chp19/tests/main/testSerializers.py diff --git a/_chapters/chp10/django_ecommerce/contact/__init__.py b/_chapters/chp19/tests/payments/__init__.py similarity index 100% rename from _chapters/chp10/django_ecommerce/contact/__init__.py rename to _chapters/chp19/tests/payments/__init__.py diff --git a/_chapters/chp18/tests/unit/payments/testCustomer.py b/_chapters/chp19/tests/payments/testCustomer.py similarity index 100% rename from _chapters/chp18/tests/unit/payments/testCustomer.py rename to _chapters/chp19/tests/payments/testCustomer.py diff --git a/_chapters/chp19/tests/unit/payments/testForms.py b/_chapters/chp19/tests/payments/testForms.py similarity index 100% rename from _chapters/chp19/tests/unit/payments/testForms.py rename to _chapters/chp19/tests/payments/testForms.py diff --git a/_chapters/chp19/tests/unit/payments/testUserModel.py b/_chapters/chp19/tests/payments/testUserModel.py similarity index 100% rename from _chapters/chp19/tests/unit/payments/testUserModel.py rename to _chapters/chp19/tests/payments/testUserModel.py diff --git a/_chapters/chp07/tests/payments/testViews.py b/_chapters/chp19/tests/payments/testViews.py similarity index 69% rename from _chapters/chp07/tests/payments/testViews.py rename to _chapters/chp19/tests/payments/testViews.py index 2e9256d..d227451 100644 --- a/_chapters/chp07/tests/payments/testViews.py +++ b/_chapters/chp19/tests/payments/testViews.py @@ -8,6 +8,7 @@ import django_ecommerce.settings as settings import mock import socket +import json class ViewTesterMixin(object): @@ -26,15 +27,15 @@ def setupViewTester(cls, url, view_func, expected_html, def test_resolves_to_correct_view(self): test_view = resolve(self.url) - self.assertEqual(test_view.func, self.view_func) + self.assertEquals(test_view.func, self.view_func) def test_returns_appropriate_respose_code(self): resp = self.view_func(self.request) - self.assertEqual(resp.status_code, self.status_code) + self.assertEquals(resp.status_code, self.status_code) def test_returns_correct_html(self): resp = self.view_func(self.request) - self.assertEqual(resp.content, self.expected_html) + self.assertEquals(resp.content, self.expected_html) class SignInPageTests(TestCase, ViewTesterMixin): @@ -43,7 +44,7 @@ class SignInPageTests(TestCase, ViewTesterMixin): def setUpClass(cls): super().setUpClass() html = render_to_response( - 'sign_in.html', + 'payments/sign_in.html', { 'form': SigninForm(), 'user': None @@ -79,9 +80,9 @@ class RegisterPageTests(TestCase, ViewTesterMixin): @classmethod def setUpClass(cls): - super().setUpClass() + super().setUpClass() html = render_to_response( - 'register.html', + 'payments/register.html', { 'form': UserForm(), 'months': list(range(1, 12)), @@ -98,8 +99,22 @@ def setUpClass(cls): ) def setUp(self): - request_factory = RequestFactory() - self.request = request_factory.get(self.url) + self.request_factory = RequestFactory() + #----changes for angular forms chapter + data = json.dumps({ + 'email': 'python@rocks.com', + 'name': 'pyRock', + 'stripe_token': '...', + 'last_4_digits': '4242', + 'password': 'bad_password', + 'ver_password': 'bad_password', + }) + self.post_request = self.request_factory.post(self.url, data=data, + content_type="application/json", + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.post_request.session = {} + + self.request = self.request_factory.get(self.url) def test_invalid_form_returns_registration_page(self): @@ -107,13 +122,15 @@ def test_invalid_form_returns_registration_page(self): user_mock.return_value = False - self.request.method = 'POST' - self.request.POST = None - resp = register(self.request) - self.assertEqual(resp.content, self.expected_html) + self.post_request._data = b'{}' + + resp = register(self.post_request) + + #should return a list of errors + self.assertContains(resp, '"status": "form-invalid"') # make sure that we did indeed call our is_valid function - self.assertEqual(user_mock.call_count, 1) + self.assertEquals(user_mock.call_count, 1) def get_mock_cust(): @@ -127,26 +144,17 @@ def id(self): @mock.patch('payments.views.Customer.create', return_value=get_mock_cust()) def test_registering_new_user_returns_succesfully(self, stripe_mock): - - self.request.session = {} - self.request.method = 'POST' - self.request.POST = { - 'email': 'python@rocks.com', - 'name': 'pyRock', - 'stripe_token': '...', - 'last_4_digits': '4242', - 'password': 'bad_password', - 'ver_password': 'bad_password', - } - - resp = register(self.request) - - self.assertEqual(resp.content, b"") - self.assertEqual(resp.status_code, 302) + + resp = register(self.post_request) + + self.assertContains(resp, b'"status": "ok"') users = User.objects.filter(email="python@rocks.com") self.assertEqual(len(users), 1) self.assertEqual(users[0].stripe_id, '1234') + + #clean up + users[0].delete() def get_MockUserForm(self): @@ -177,24 +185,6 @@ def addError(self, error): @mock.patch('payments.models.User.save', side_effect=IntegrityError) def test_registering_user_twice_cause_error_msg(self, save_mock): - #create the request used to test the view - self.request.session = {} - self.request.method = 'POST' - self.request.POST = {} - - #create the expected html - html = render_to_response( - 'register.html', - { - 'form': self.get_MockUserForm(), - 'months': list(range(1, 12)), - 'publishable': settings.STRIPE_PUBLISHABLE, - 'soon': soon(), - 'user': None, - 'years': list(range(2011, 2036)), - } - ) - #mock out stripe so we don't hit their server with mock.patch('payments.views.Customer') as stripe_mock: @@ -202,12 +192,10 @@ def test_registering_user_twice_cause_error_msg(self, save_mock): stripe_mock.configure_mock(**config) #run the test - resp = register(self.request) + resp = register(self.post_request) #verify that we did things correctly - self.assertEqual(resp.content, html.content) - self.assertEqual(resp.status_code, 200) - self.assertEqual(self.request.session, {}) + self.assertContains(resp, 'python@rocks.com is already a member') #assert there is no records in the database. users = User.objects.filter(email="python@rocks.com") @@ -215,18 +203,6 @@ def test_registering_user_twice_cause_error_msg(self, save_mock): def test_registering_user_when_stripe_is_down(self): - #create the request used to test the view - self.request.session = {} - self.request.method = 'POST' - self.request.POST = { - 'email': 'python@rocks.com', - 'name': 'pyRock', - 'stripe_token': '...', - 'last_4_digits': '4242', - 'password': 'bad_password', - 'ver_password': 'bad_password', - } - #mock out Stripe and ask it to throw a connection error with mock.patch( 'stripe.Customer.create', @@ -234,34 +210,22 @@ def test_registering_user_when_stripe_is_down(self): ) as stripe_mock: #run the test - register(self.request) + register(self.post_request) #assert there is a record in the database without Stripe id. users = User.objects.filter(email="python@rocks.com") self.assertEquals(len(users), 1) self.assertEquals(users[0].stripe_id, '') - # check the associated table got updated. - unpaid = UnpaidUsers.objects.filter(email="python@rocks.com") - self.assertEquals(len(unpaid), 1) - self.assertIsNotNone(unpaid[0].last_notification) + # check the associated table got updated. + unpaid = UnpaidUsers.objects.filter(email="python@rocks.com") + self.assertEquals(len(unpaid), 1) + self.assertIsNotNone(unpaid[0].last_notification) @mock.patch('payments.models.UnpaidUsers.save', side_effect=IntegrityError) def test_registering_user_when_strip_is_down_all_or_nothing(self, save_mock): - #create the request used to test the view - self.request.session = {} - self.request.method = 'POST' - self.request.POST = { - 'email': 'python@rocks.com', - 'name': 'pyRock', - 'stripe_token': '...', - 'last_4_digits': '4242', - 'password': 'bad_password', - 'ver_password': 'bad_password', - } - #mock out stripe and ask it to throw a connection error with mock.patch( 'stripe.Customer.create', @@ -269,7 +233,7 @@ def test_registering_user_when_strip_is_down_all_or_nothing(self, save_mock): ) as stripe_mock: #run the test - resp = register(self.request) + resp = register(self.post_request) #assert there is no new record in the database users = User.objects.filter(email="python@rocks.com") diff --git a/_chapters/chp13/angular_test/index.html b/_chapters/chp20/angular_test/index.html similarity index 100% rename from _chapters/chp13/angular_test/index.html rename to _chapters/chp20/angular_test/index.html diff --git a/_chapters/chp13/angular_test/polls.html b/_chapters/chp20/angular_test/polls.html similarity index 100% rename from _chapters/chp13/angular_test/polls.html rename to _chapters/chp20/angular_test/polls.html diff --git a/_chapters/chp10/django_ecommerce/django_ecommerce/__init__.py b/_chapters/chp20/django_ecommerce/contact/__init__.py similarity index 100% rename from _chapters/chp10/django_ecommerce/django_ecommerce/__init__.py rename to _chapters/chp20/django_ecommerce/contact/__init__.py diff --git a/_chapters/chp07/django_ecommerce/contact/admin.py b/_chapters/chp20/django_ecommerce/contact/admin.py similarity index 100% rename from _chapters/chp07/django_ecommerce/contact/admin.py rename to _chapters/chp20/django_ecommerce/contact/admin.py diff --git a/_chapters/chp07/django_ecommerce/contact/forms.py b/_chapters/chp20/django_ecommerce/contact/forms.py similarity index 100% rename from _chapters/chp07/django_ecommerce/contact/forms.py rename to _chapters/chp20/django_ecommerce/contact/forms.py diff --git a/_chapters/chp12/django_ecommerce/contact/migrations/0001_initial.py b/_chapters/chp20/django_ecommerce/contact/migrations/0001_initial.py similarity index 100% rename from _chapters/chp12/django_ecommerce/contact/migrations/0001_initial.py rename to _chapters/chp20/django_ecommerce/contact/migrations/0001_initial.py diff --git a/_chapters/chp12/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py b/_chapters/chp20/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py similarity index 100% rename from _chapters/chp12/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py rename to _chapters/chp20/django_ecommerce/contact/migrations/0002_auto_20150606_0307.py diff --git a/_chapters/chp10/django_ecommerce/main/__init__.py b/_chapters/chp20/django_ecommerce/contact/migrations/__init__.py similarity index 100% rename from _chapters/chp10/django_ecommerce/main/__init__.py rename to _chapters/chp20/django_ecommerce/contact/migrations/__init__.py diff --git a/_chapters/chp10/django_ecommerce/contact/models.py b/_chapters/chp20/django_ecommerce/contact/models.py similarity index 78% rename from _chapters/chp10/django_ecommerce/contact/models.py rename to _chapters/chp20/django_ecommerce/contact/models.py index 7add1d1..5de0efe 100644 --- a/_chapters/chp10/django_ecommerce/contact/models.py +++ b/_chapters/chp20/django_ecommerce/contact/models.py @@ -7,9 +7,7 @@ class ContactForm(models.Model): email = models.EmailField(max_length=250) topic = models.CharField(max_length=200) message = models.CharField(max_length=1000) - timestamp = models.DateTimeField( - auto_now_add=True, default=datetime.datetime.now - ) + timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return self.email diff --git a/_chapters/chp10/django_ecommerce/contact/views.py b/_chapters/chp20/django_ecommerce/contact/views.py similarity index 100% rename from _chapters/chp10/django_ecommerce/contact/views.py rename to _chapters/chp20/django_ecommerce/contact/views.py diff --git a/_chapters/chp10/django_ecommerce/db_backup.json b/_chapters/chp20/django_ecommerce/db_backup.json similarity index 100% rename from _chapters/chp10/django_ecommerce/db_backup.json rename to _chapters/chp20/django_ecommerce/db_backup.json diff --git a/_chapters/chp10/django_ecommerce/main/templatetags/__init__.py b/_chapters/chp20/django_ecommerce/django_ecommerce/__init__.py similarity index 100% rename from _chapters/chp10/django_ecommerce/main/templatetags/__init__.py rename to _chapters/chp20/django_ecommerce/django_ecommerce/__init__.py diff --git a/_chapters/chp18/django_ecommerce/django_ecommerce/guitest_settings.py b/_chapters/chp20/django_ecommerce/django_ecommerce/guitest_settings.py similarity index 100% rename from _chapters/chp18/django_ecommerce/django_ecommerce/guitest_settings.py rename to _chapters/chp20/django_ecommerce/django_ecommerce/guitest_settings.py diff --git a/_chapters/chp10/django_ecommerce/django_ecommerce/settings.py b/_chapters/chp20/django_ecommerce/django_ecommerce/settings.py similarity index 94% rename from _chapters/chp10/django_ecommerce/django_ecommerce/settings.py rename to _chapters/chp20/django_ecommerce/django_ecommerce/settings.py index e2863d3..28a5181 100644 --- a/_chapters/chp10/django_ecommerce/django_ecommerce/settings.py +++ b/_chapters/chp20/django_ecommerce/django_ecommerce/settings.py @@ -1,6 +1,7 @@ # Django settings for django_ecommerce project. import os +import mongoengine DEBUG = True TEMPLATE_DEBUG = DEBUG @@ -16,6 +17,10 @@ MANAGERS = ADMINS +# DATABASES + +mongoengine.connect("mec-geodata") + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', @@ -52,16 +57,16 @@ USE_L10N = True # If you set this to False, Django will not use timezone-aware datetimes. -USE_TZ = True +USE_TZ = True # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/var/www/example.com/media/" -MEDIA_ROOT = '' +MEDIA_ROOT = os.path.join(SITE_ROOT, 'media') # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://example.com/media/", "http://media.example.com/" -MEDIA_URL = '' +MEDIA_URL = '/media/' # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files @@ -120,11 +125,16 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'main', + 'django_admin_bootstrapped.bootstrap3', + 'django_admin_bootstrapped', 'django.contrib.admin', 'django.contrib.flatpages', 'contact', 'payments', 'embed_video', + 'rest_framework', + 'djangular_polls', + 'usermap' ) SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' diff --git a/_chapters/chp10/django_ecommerce/django_ecommerce/urls.py b/_chapters/chp20/django_ecommerce/django_ecommerce/urls.py similarity index 52% rename from _chapters/chp10/django_ecommerce/django_ecommerce/urls.py rename to _chapters/chp20/django_ecommerce/django_ecommerce/urls.py index 2af3e78..5189ca3 100644 --- a/_chapters/chp10/django_ecommerce/django_ecommerce/urls.py +++ b/_chapters/chp20/django_ecommerce/django_ecommerce/urls.py @@ -1,7 +1,18 @@ +from django.conf import settings +from django.conf.urls.static import static from django.conf.urls import patterns, include, url -from payments import views from django.contrib import admin +from payments import views +from main.urls import urlpatterns as main_json_urls +from djangular_polls.urls import urlpatterns as djangular_polls_json_urls +from payments.urls import urlpatterns as payments_json_urls +from usermap.urls import urlpatterns as map_json_urls + admin.autodiscover() +main_json_urls.extend(djangular_polls_json_urls) +main_json_urls.extend(payments_json_urls) +main_json_urls.extend(map_json_urls) + urlpatterns = patterns( '', @@ -10,10 +21,14 @@ url(r'^pages/', include('django.contrib.flatpages.urls')), url(r'^contact/', 'contact.views.contact', name='contact'), url(r'^report$', 'main.views.report', name="report"), + url(r'^usermap/', 'usermap.views.usermap', name='usermap'), # user registration/authentication url(r'^sign_in$', views.sign_in, name='sign_in'), url(r'^sign_out$', views.sign_out, name='sign_out'), url(r'^register$', views.register, name='register'), url(r'^edit$', views.edit, name='edit'), -) + + # api + url(r'^api/v1/', include('main.urls')), +) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/_chapters/chp07/django_ecommerce/django_ecommerce/wsgi.py b/_chapters/chp20/django_ecommerce/django_ecommerce/wsgi.py similarity index 100% rename from _chapters/chp07/django_ecommerce/django_ecommerce/wsgi.py rename to _chapters/chp20/django_ecommerce/django_ecommerce/wsgi.py diff --git a/_chapters/chp10/django_ecommerce/payments/__init__.py b/_chapters/chp20/django_ecommerce/djangular_polls/__init__.py similarity index 100% rename from _chapters/chp10/django_ecommerce/payments/__init__.py rename to _chapters/chp20/django_ecommerce/djangular_polls/__init__.py diff --git a/_chapters/chp20/django_ecommerce/djangular_polls/admin.py b/_chapters/chp20/django_ecommerce/djangular_polls/admin.py new file mode 100644 index 0000000..3849734 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/djangular_polls/admin.py @@ -0,0 +1,36 @@ +from django.contrib import admin +from django.utils.html import format_html + +from djangular_polls.models import Poll, PollItem + + +class PollItemInline(admin.TabularInline): + model = PollItem + + readonly_fields = ('votes',) + ordering = ('-votes',) + + +@admin.register(Poll) +class PollAdmin(admin.ModelAdmin): + + inlines = [PollItemInline, ] + + list_display = ('publish_date', 'title', 'highest_vote', + 'list_items', 'total_votes', ) + + def highest_vote(self, poll): + try: + return poll.poll_items().order_by('-votes')[0].text + except IndexError: + return "No Poll Items for this Poll" + + def list_items(self, poll): + html = "

    %s

      " % (poll.title) + html += "\n".join("
    • %s - %d
    • " % + (pi.text, pi.votes) for pi in poll.poll_items()) + html += "
    " + return format_html(html) + + list_items.short_description = "Poll results" + list_items.allow_tags = True diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/json_views.py b/_chapters/chp20/django_ecommerce/djangular_polls/json_views.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/json_views.py rename to _chapters/chp20/django_ecommerce/djangular_polls/json_views.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/migrations/0001_initial.py b/_chapters/chp20/django_ecommerce/djangular_polls/migrations/0001_initial.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/migrations/0001_initial.py rename to _chapters/chp20/django_ecommerce/djangular_polls/migrations/0001_initial.py diff --git a/_chapters/chp17/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py b/_chapters/chp20/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py similarity index 100% rename from _chapters/chp17/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py rename to _chapters/chp20/django_ecommerce/djangular_polls/migrations/0002_remove_pollitem_percentage.py diff --git a/_chapters/chp10/tests/__init__.py b/_chapters/chp20/django_ecommerce/djangular_polls/migrations/__init__.py similarity index 100% rename from _chapters/chp10/tests/__init__.py rename to _chapters/chp20/django_ecommerce/djangular_polls/migrations/__init__.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/models.py b/_chapters/chp20/django_ecommerce/djangular_polls/models.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/models.py rename to _chapters/chp20/django_ecommerce/djangular_polls/models.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/serializers.py b/_chapters/chp20/django_ecommerce/djangular_polls/serializers.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/serializers.py rename to _chapters/chp20/django_ecommerce/djangular_polls/serializers.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/tests.py b/_chapters/chp20/django_ecommerce/djangular_polls/tests.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/tests.py rename to _chapters/chp20/django_ecommerce/djangular_polls/tests.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/urls.py b/_chapters/chp20/django_ecommerce/djangular_polls/urls.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/urls.py rename to _chapters/chp20/django_ecommerce/djangular_polls/urls.py diff --git a/_chapters/chp14/django_ecommerce/djangular_polls/views.py b/_chapters/chp20/django_ecommerce/djangular_polls/views.py similarity index 100% rename from _chapters/chp14/django_ecommerce/djangular_polls/views.py rename to _chapters/chp20/django_ecommerce/djangular_polls/views.py diff --git a/_chapters/chp10/tests/contact/__init__.py b/_chapters/chp20/django_ecommerce/main/__init__.py similarity index 100% rename from _chapters/chp10/tests/contact/__init__.py rename to _chapters/chp20/django_ecommerce/main/__init__.py diff --git a/_chapters/chp17/django_ecommerce/main/admin.py b/_chapters/chp20/django_ecommerce/main/admin.py similarity index 100% rename from _chapters/chp17/django_ecommerce/main/admin.py rename to _chapters/chp20/django_ecommerce/main/admin.py diff --git a/_chapters/chp12/django_ecommerce/main/fixtures/system_data.json b/_chapters/chp20/django_ecommerce/main/fixtures/system_data.json similarity index 100% rename from _chapters/chp12/django_ecommerce/main/fixtures/system_data.json rename to _chapters/chp20/django_ecommerce/main/fixtures/system_data.json diff --git a/_chapters/chp11/django_ecommerce/main/json_views.py b/_chapters/chp20/django_ecommerce/main/json_views.py similarity index 100% rename from _chapters/chp11/django_ecommerce/main/json_views.py rename to _chapters/chp20/django_ecommerce/main/json_views.py diff --git a/_chapters/chp17/django_ecommerce/main/migrations/0001_auto_20150311_1303.py b/_chapters/chp20/django_ecommerce/main/migrations/0001_auto_20150311_1303.py similarity index 100% rename from _chapters/chp17/django_ecommerce/main/migrations/0001_auto_20150311_1303.py rename to _chapters/chp20/django_ecommerce/main/migrations/0001_auto_20150311_1303.py diff --git a/_chapters/chp12/django_ecommerce/main/migrations/0001_initial.py b/_chapters/chp20/django_ecommerce/main/migrations/0001_initial.py similarity index 100% rename from _chapters/chp12/django_ecommerce/main/migrations/0001_initial.py rename to _chapters/chp20/django_ecommerce/main/migrations/0001_initial.py diff --git a/_chapters/chp17/django_ecommerce/main/migrations/0002_auto_20150311_1313.py b/_chapters/chp20/django_ecommerce/main/migrations/0002_auto_20150311_1313.py similarity index 100% rename from _chapters/chp17/django_ecommerce/main/migrations/0002_auto_20150311_1313.py rename to _chapters/chp20/django_ecommerce/main/migrations/0002_auto_20150311_1313.py diff --git a/_chapters/chp12/django_ecommerce/main/migrations/0002_statusreport.py b/_chapters/chp20/django_ecommerce/main/migrations/0002_statusreport.py similarity index 100% rename from _chapters/chp12/django_ecommerce/main/migrations/0002_statusreport.py rename to _chapters/chp20/django_ecommerce/main/migrations/0002_statusreport.py diff --git a/_chapters/chp18/django_ecommerce/main/migrations/0003_auto_20150606_0720.py b/_chapters/chp20/django_ecommerce/main/migrations/0003_auto_20150606_0720.py similarity index 100% rename from _chapters/chp18/django_ecommerce/main/migrations/0003_auto_20150606_0720.py rename to _chapters/chp20/django_ecommerce/main/migrations/0003_auto_20150606_0720.py diff --git a/_chapters/chp10/tests/main/__init__.py b/_chapters/chp20/django_ecommerce/main/migrations/__init__.py similarity index 100% rename from _chapters/chp10/tests/main/__init__.py rename to _chapters/chp20/django_ecommerce/main/migrations/__init__.py diff --git a/_chapters/chp12/django_ecommerce/main/migrations/data_load_marketing_items_0003.py b/_chapters/chp20/django_ecommerce/main/migrations/data_load_marketing_items_0003.py similarity index 100% rename from _chapters/chp12/django_ecommerce/main/migrations/data_load_marketing_items_0003.py rename to _chapters/chp20/django_ecommerce/main/migrations/data_load_marketing_items_0003.py diff --git a/_chapters/chp20/django_ecommerce/main/models.py b/_chapters/chp20/django_ecommerce/main/models.py new file mode 100644 index 0000000..7571825 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/main/models.py @@ -0,0 +1,64 @@ +from django.db import models + +from embed_video.fields import EmbedVideoField + + +class ThumbnailMixin(object): + '''use this mixin if you want to easily show thumbnails for an image field + in your admin view + ''' + + def thumbnail(self): + if self.img: + return u'' % (self.img.url) + else: + return "no image" + + thumbnail.allow_tags = True + + +class MarketingItem(models.Model, ThumbnailMixin): + img = models.ImageField(upload_to="marketing/") + heading = models.CharField(max_length=300) + caption = models.TextField() + button_link = models.CharField(null=True, blank=True, max_length=200, default="register") + button_title = models.CharField(max_length=20,default="View details") + + +class StatusReport(models.Model): + user = models.ForeignKey('payments.User') + when = models.DateTimeField(auto_now_add=True) + status = models.CharField(max_length=200) + +class Announcement(models.Model): + + when = models.DateTimeField(auto_now=True) + img = models.ImageField(upload_to="announce/", null=True, blank=True) + vid = EmbedVideoField(null=True, blank=True) + info = models.TextField() + + def thumbnail(self): + if self.img: + return u'' % (self.img.url) + else: + return "no image" + + thumbnail.allow_tags = True + + +class Badge(models.Model): + + img = models.ImageField(upload_to="images/") + name = models.CharField(max_length=100) + desc = models.TextField() + + def thumbnail(self): + if self.img: + return u'' % (self.img.url) + else: + return "no image" + + thumbnail.allow_tags = True + + class Meta: + ordering = ('name',) diff --git a/_chapters/chp11/django_ecommerce/main/permissions.py b/_chapters/chp20/django_ecommerce/main/permissions.py similarity index 100% rename from _chapters/chp11/django_ecommerce/main/permissions.py rename to _chapters/chp20/django_ecommerce/main/permissions.py diff --git a/_chapters/chp11/django_ecommerce/main/serializers.py b/_chapters/chp20/django_ecommerce/main/serializers.py similarity index 100% rename from _chapters/chp11/django_ecommerce/main/serializers.py rename to _chapters/chp20/django_ecommerce/main/serializers.py diff --git a/_chapters/chp10/tests/payments/__init__.py b/_chapters/chp20/django_ecommerce/main/templatetags/__init__.py similarity index 100% rename from _chapters/chp10/tests/payments/__init__.py rename to _chapters/chp20/django_ecommerce/main/templatetags/__init__.py diff --git a/_chapters/chp10/django_ecommerce/main/templatetags/main_gravatar.py b/_chapters/chp20/django_ecommerce/main/templatetags/main_gravatar.py similarity index 100% rename from _chapters/chp10/django_ecommerce/main/templatetags/main_gravatar.py rename to _chapters/chp20/django_ecommerce/main/templatetags/main_gravatar.py diff --git a/_chapters/chp09/django_ecommerce/main/templatetags/main_marketing.py b/_chapters/chp20/django_ecommerce/main/templatetags/main_marketing.py similarity index 100% rename from _chapters/chp09/django_ecommerce/main/templatetags/main_marketing.py rename to _chapters/chp20/django_ecommerce/main/templatetags/main_marketing.py diff --git a/_chapters/chp11/django_ecommerce/main/urls.py b/_chapters/chp20/django_ecommerce/main/urls.py similarity index 100% rename from _chapters/chp11/django_ecommerce/main/urls.py rename to _chapters/chp20/django_ecommerce/main/urls.py diff --git a/_chapters/chp10/django_ecommerce/main/views.py b/_chapters/chp20/django_ecommerce/main/views.py similarity index 54% rename from _chapters/chp10/django_ecommerce/main/views.py rename to _chapters/chp20/django_ecommerce/main/views.py index e55af77..5773a54 100644 --- a/_chapters/chp10/django_ecommerce/main/views.py +++ b/_chapters/chp20/django_ecommerce/main/views.py @@ -4,44 +4,6 @@ from datetime import date, timedelta -class market_item(object): - - def __init__(self, img, heading, caption, button_link="register", - button_title="View details"): - self.img = img - self.heading = heading - self.caption = caption - self.button_link = button_link - self.button_title = button_title - -market_items = [ - market_item( - img="yoda.jpg", - heading="Hone your Jedi Skills", - caption="All members have access to our unique" - " training and achievements latters. Progress through the " - "levels and show everyone who the top Jedi Master is!", - button_title="Sign Up Now" - ), - market_item( - img="clone_army.jpg", - heading="Build your Clan", - caption="Engage in meaningful conversation, or " - "bloodthirsty battle! If it's related to " - "Star Wars, in any way, you better believe we do it.", - button_title="Sign Up Now" - ), - market_item( - img="leia.jpg", - heading="Find Love", - caption="Everybody knows Star Wars fans are the " - "best mates for Star Wars fans. Find your " - "Princess Leia or Han Solo and explore the " - "stars together.", - button_title="Sign Up Now" - ), -] - def index(request): uid = request.session.get('user') diff --git a/_chapters/chp07/django_ecommerce/manage.py b/_chapters/chp20/django_ecommerce/manage.py similarity index 100% rename from _chapters/chp07/django_ecommerce/manage.py rename to _chapters/chp20/django_ecommerce/manage.py diff --git a/_chapters/chp17/django_ecommerce/media/announce/main-logo.png b/_chapters/chp20/django_ecommerce/media/announce/main-logo.png similarity index 100% rename from _chapters/chp17/django_ecommerce/media/announce/main-logo.png rename to _chapters/chp20/django_ecommerce/media/announce/main-logo.png diff --git a/_chapters/chp17/django_ecommerce/media/images/fig.png b/_chapters/chp20/django_ecommerce/media/images/fig.png similarity index 100% rename from _chapters/chp17/django_ecommerce/media/images/fig.png rename to _chapters/chp20/django_ecommerce/media/images/fig.png diff --git a/_chapters/chp12/django_ecommerce/contact/migrations/__init__.py b/_chapters/chp20/django_ecommerce/payments/__init__.py similarity index 100% rename from _chapters/chp12/django_ecommerce/contact/migrations/__init__.py rename to _chapters/chp20/django_ecommerce/payments/__init__.py diff --git a/_chapters/chp20/django_ecommerce/payments/admin.py b/_chapters/chp20/django_ecommerce/payments/admin.py new file mode 100644 index 0000000..4efec24 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/payments/admin.py @@ -0,0 +1,23 @@ +from django.contrib import admin +from django.contrib.auth.models import User as DjangoUser +from django.contrib.sites.models import Site +from django.contrib.auth.models import Group +from .models import User + + +@admin.register(User) +class UserAdmin(admin.ModelAdmin): + + list_display = ('name', 'email', 'rank', 'last_4_digits', 'stripe_id') + ordering = ('-created_at',) + fieldsets = ( + ('User Info', {'fields': ('name', 'email', 'rank',)}), + ('Billing', {'fields': ('stripe_id',)}), + ('Badges', {'fields': ('badges',)}), + ) + filter_horizontal = ('badges',) + + +admin.site.unregister(DjangoUser) +admin.site.unregister(Group) +admin.site.unregister(Site) diff --git a/_chapters/chp12/django_ecommerce/payments/fixtures/system_data.json b/_chapters/chp20/django_ecommerce/payments/fixtures/system_data.json similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/fixtures/system_data.json rename to _chapters/chp20/django_ecommerce/payments/fixtures/system_data.json diff --git a/_chapters/chp10/django_ecommerce/payments/forms.py b/_chapters/chp20/django_ecommerce/payments/forms.py similarity index 67% rename from _chapters/chp10/django_ecommerce/payments/forms.py rename to _chapters/chp20/django_ecommerce/payments/forms.py index 9af62da..698b1c4 100644 --- a/_chapters/chp10/django_ecommerce/payments/forms.py +++ b/_chapters/chp20/django_ecommerce/payments/forms.py @@ -13,6 +13,9 @@ class SigninForm(PaymentForm): required=True, widget=forms.PasswordInput(render_value=False) ) + form_name = 'signin_form' + ng_scope_prefix = 'signinform' + class CardForm(PaymentForm): last_4_digits = forms.CharField( @@ -25,7 +28,8 @@ class CardForm(PaymentForm): class UserForm(CardForm): - name = forms.CharField(required=True) + + name = forms.CharField(required=True, min_length=3) email = forms.EmailField(required=True) password = forms.CharField( required=True, @@ -38,6 +42,20 @@ class UserForm(CardForm): widget=forms.PasswordInput(render_value=False) ) + form_name = 'user_form' + ng_scope_prefix = "userform" + + def __init__(self, *args, ** kwargs): + super(UserForm, self).__init__(*args, **kwargs) + for name, field in self.fields.items(): + attrs = {"ng-model": "%s.%s" % (self.ng_scope_prefix, name)} + + if field.required: + attrs.update({"required": True}) + if field.min_length: + attrs.update({"ng-minlength": field.min_length}) + field.widget.attrs.update(attrs) + def clean(self): cleaned_data = self.cleaned_data password = cleaned_data.get('password') diff --git a/_chapters/chp16/django_ecommerce/payments/json_views.py b/_chapters/chp20/django_ecommerce/payments/json_views.py similarity index 64% rename from _chapters/chp16/django_ecommerce/payments/json_views.py rename to _chapters/chp20/django_ecommerce/payments/json_views.py index 3105344..4cba6cf 100644 --- a/_chapters/chp16/django_ecommerce/payments/json_views.py +++ b/_chapters/chp20/django_ecommerce/payments/json_views.py @@ -1,10 +1,15 @@ from payments.models import User, UnpaidUsers from payments.forms import UserForm from payments.views import Customer -from rest_framework.response import Response +from payments.serializers import PasswordSerializer + from django.db import transaction from django.db import IntegrityError +from django.http import Http404 + +from rest_framework import generics, status, permissions from rest_framework.decorators import api_view +from rest_framework.response import Response @api_view(['POST']) @@ -14,7 +19,7 @@ def post_user(request): print("in post user") if form.is_valid(): try: - #update based on your billing method (subscription vs one time) + # update based on your billing method (subscription vs one time) customer = Customer.create( "subscription", email=form.cleaned_data['email'], @@ -51,3 +56,28 @@ def post_user(request): else: # for not valid resp = {"status": "form-invalid", "errors": form.errors} return Response(resp) + + +class ChangePassword(generics.GenericAPIView): + """ + Change password of any user if superadmin. + * pwd + * pwd2 + """ + permission_classes = (permissions.IsAdminUser,) + serializer_class = PasswordSerializer + + def get_object(self, pk): + try: + return User.objects.get(pk=pk) + except User.DoesNotExist: + raise Http404 + + def put(self, request, pk, format=None): + user = self.get_object(pk) + serializer = PasswordSerializer(user, data=request.DATA) + if serializer.is_valid(): + serializer.save() + return Response("Password Changed.") + return Response(serializer.errors, + status=status.HTTP_400_BAD_REQUEST) diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0001_initial.py b/_chapters/chp20/django_ecommerce/payments/migrations/0001_initial.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0001_initial.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0001_initial.py diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0003_initial_data.py b/_chapters/chp20/django_ecommerce/payments/migrations/0003_initial_data.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0003_initial_data.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0003_initial_data.py diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py b/_chapters/chp20/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0004_auto_20141001_0546.py diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0005_bigcoId_migration.py b/_chapters/chp20/django_ecommerce/payments/migrations/0005_bigcoId_migration.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0005_bigcoId_migration.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0005_bigcoId_migration.py diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py b/_chapters/chp20/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0006_auto_20141007_0904.py diff --git a/_chapters/chp17/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py b/_chapters/chp20/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py similarity index 100% rename from _chapters/chp17/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0007_auto_20150311_1303.py diff --git a/_chapters/chp12/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py b/_chapters/chp20/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py similarity index 100% rename from _chapters/chp12/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0007_auto_20150606_0307.py diff --git a/_chapters/chp17/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py b/_chapters/chp20/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py similarity index 100% rename from _chapters/chp17/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0008_auto_20150311_1313.py diff --git a/_chapters/chp17/django_ecommerce/payments/migrations/0009_merge.py b/_chapters/chp20/django_ecommerce/payments/migrations/0009_merge.py similarity index 100% rename from _chapters/chp17/django_ecommerce/payments/migrations/0009_merge.py rename to _chapters/chp20/django_ecommerce/payments/migrations/0009_merge.py diff --git a/_chapters/chp12/django_ecommerce/main/migrations/__init__.py b/_chapters/chp20/django_ecommerce/payments/migrations/__init__.py similarity index 100% rename from _chapters/chp12/django_ecommerce/main/migrations/__init__.py rename to _chapters/chp20/django_ecommerce/payments/migrations/__init__.py diff --git a/_chapters/chp10/django_ecommerce/payments/models.py b/_chapters/chp20/django_ecommerce/payments/models.py similarity index 81% rename from _chapters/chp10/django_ecommerce/payments/models.py rename to _chapters/chp20/django_ecommerce/payments/models.py index 93db6d9..539a6d2 100644 --- a/_chapters/chp10/django_ecommerce/payments/models.py +++ b/_chapters/chp20/django_ecommerce/payments/models.py @@ -1,25 +1,27 @@ -from django.db import models from main.models import Badge from django.contrib.auth.models import AbstractBaseUser from datetime import datetime +from django.db import models class User(AbstractBaseUser): name = models.CharField(max_length=255) email = models.CharField(max_length=255, unique=True) - #password field defined in base class + # password field defined in base class last_4_digits = models.CharField(max_length=4, blank=True, null=True) stripe_id = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) rank = models.CharField(max_length=50, default="Padwan") badges = models.ManyToManyField(Badge) + bigCoID = models.CharField(max_length=50, unique=True) USERNAME_FIELD = 'email' def __str__(self): return self.email + @classmethod def get_by_id(cls, uid): return User.objects.get(pk=uid) @@ -30,6 +32,11 @@ def create(cls, name, email, password, last_4_digits, stripe_id=''): last_4_digits=last_4_digits, stripe_id=stripe_id) new_user.set_password(password) + # set bigCoID + new_user.bigCoID = "%s%s%s" % ( + new_user.name[:2], + new_user.rank[:1], + datetime.now().strftime("%Y%m%d%H%M%S%f"),) new_user.save() return new_user diff --git a/_chapters/chp17/django_ecommerce/payments/serializers.py b/_chapters/chp20/django_ecommerce/payments/serializers.py similarity index 100% rename from _chapters/chp17/django_ecommerce/payments/serializers.py rename to _chapters/chp20/django_ecommerce/payments/serializers.py diff --git a/_chapters/chp20/django_ecommerce/payments/urls.py b/_chapters/chp20/django_ecommerce/payments/urls.py new file mode 100644 index 0000000..70bab59 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/payments/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import patterns, url +from .json_views import ChangePassword + + +urlpatterns = patterns( + 'payments.json_views', + url(r'^users$', 'post_user'), + url(r'^users/password/(?P[0-9]+)$', ChangePassword.as_view(), + name='change_password'), +) diff --git a/_chapters/chp10/django_ecommerce/payments/views.py b/_chapters/chp20/django_ecommerce/payments/views.py similarity index 76% rename from _chapters/chp10/django_ecommerce/payments/views.py rename to _chapters/chp20/django_ecommerce/payments/views.py index 89b72b3..a5e445e 100644 --- a/_chapters/chp10/django_ecommerce/payments/views.py +++ b/_chapters/chp20/django_ecommerce/payments/views.py @@ -1,5 +1,6 @@ -from django.db import IntegrityError -from django.http import HttpResponseRedirect +from django.db import IntegrityError, transaction +from django.http import HttpResponseRedirect, HttpResponseBadRequest, \ + HttpResponse from django.shortcuts import render_to_response from django.template import RequestContext from payments.forms import SigninForm, CardForm, UserForm @@ -8,6 +9,7 @@ import stripe import datetime import socket +import json stripe.api_key = settings.STRIPE_SECRET @@ -57,26 +59,26 @@ def sign_out(request): def register(request): user = None if request.method == 'POST': - form = UserForm(request.POST) - if form.is_valid(): + # We only talk AJAX posts now + if not request.is_ajax(): + return HttpResponseBadRequest("I only speak AJAX nowadays") - #update based on your billing method (subscription vs one time) - customer = Customer.create( - email=form.cleaned_data['email'], - description=form.cleaned_data['name'], - card=form.cleaned_data['stripe_token'], - plan="gold", - ) - # customer = stripe.Charge.create( - # description=form.cleaned_data['email'], - # card=form.cleaned_data['stripe_token'], - # amount="5000", - # currency="usd" - # ) + data = json.loads(request.body.decode()) + form = UserForm(data) - cd = form.cleaned_data - from django.db import transaction + if form.is_valid(): + try: + customer = Customer.create( + "subscription", + email=form.cleaned_data['email'], + description=form.cleaned_data['name'], + card=form.cleaned_data['stripe_token'], + plan="gold", + ) + except Exception as exp: + form.addError(exp) + cd = form.cleaned_data try: with transaction.atomic(): user = User.create(cd['name'], cd['email'], cd['password'], @@ -89,13 +91,16 @@ def register(request): UnpaidUsers(email=cd['email']).save() except IntegrityError: - import traceback - form.addError(cd['email'] + ' is already a member' + - traceback.format_exc()) - user = None + resp = json.dumps({"status": "fail", "errors": + cd['email'] + ' is already a member'}) else: request.session['user'] = user.pk - return HttpResponseRedirect('/') + resp = json.dumps({"status": "ok", "url": '/'}) + + return HttpResponse(resp, content_type="application/json") + else: # form not valid + resp = json.dumps({"status": "form-invalid", "errors": form.errors}) + return HttpResponse(resp, content_type="application/json") else: form = UserForm() diff --git a/_chapters/chp18/django_ecommerce/requirements.txt b/_chapters/chp20/django_ecommerce/requirements.txt similarity index 100% rename from _chapters/chp18/django_ecommerce/requirements.txt rename to _chapters/chp20/django_ecommerce/requirements.txt diff --git a/_chapters/chp18/django_ecommerce/runner.py b/_chapters/chp20/django_ecommerce/runner.py similarity index 100% rename from _chapters/chp18/django_ecommerce/runner.py rename to _chapters/chp20/django_ecommerce/runner.py diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap-theme.css b/_chapters/chp20/django_ecommerce/static/css/bootstrap-theme.css similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap-theme.css rename to _chapters/chp20/django_ecommerce/static/css/bootstrap-theme.css diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap-theme.css.map b/_chapters/chp20/django_ecommerce/static/css/bootstrap-theme.css.map similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap-theme.css.map rename to _chapters/chp20/django_ecommerce/static/css/bootstrap-theme.css.map diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap-theme.min.css b/_chapters/chp20/django_ecommerce/static/css/bootstrap-theme.min.css similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap-theme.min.css rename to _chapters/chp20/django_ecommerce/static/css/bootstrap-theme.min.css diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap.css b/_chapters/chp20/django_ecommerce/static/css/bootstrap.css similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap.css rename to _chapters/chp20/django_ecommerce/static/css/bootstrap.css diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap.css.map b/_chapters/chp20/django_ecommerce/static/css/bootstrap.css.map similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap.css.map rename to _chapters/chp20/django_ecommerce/static/css/bootstrap.css.map diff --git a/_chapters/chp09/django_ecommerce/static/css/bootstrap.min.css b/_chapters/chp20/django_ecommerce/static/css/bootstrap.min.css similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/bootstrap.min.css rename to _chapters/chp20/django_ecommerce/static/css/bootstrap.min.css diff --git a/_chapters/chp10/django_ecommerce/static/css/mec.css b/_chapters/chp20/django_ecommerce/static/css/mec.css similarity index 84% rename from _chapters/chp10/django_ecommerce/static/css/mec.css rename to _chapters/chp20/django_ecommerce/static/css/mec.css index 6cda1a0..766d8e2 100644 --- a/_chapters/chp10/django_ecommerce/static/css/mec.css +++ b/_chapters/chp20/django_ecommerce/static/css/mec.css @@ -60,7 +60,6 @@ h1, .nav li, .navbar-brand { .member-page { padding-top: 20px; padding-bottom: 40px; - background-color: white; margin-left: 0px; margin-right: 0px; margin-top: -20px; @@ -76,4 +75,21 @@ h1, .nav li, .navbar-brand { margin: auto; min-width: 100%; min-height: 100px; -} \ No newline at end of file +} + +input.ng-dirty.ng-valid { + border:1px solid Green; +} +input.ng-dirty.ng-invalid { + border:1px solid Red; +} + +.custom-error { + color: #FF0000; + font-family: sans-serif; +} + +.angular-google-map-container { + height: 400px; +} + diff --git a/_chapters/chp09/django_ecommerce/static/css/signin.css b/_chapters/chp20/django_ecommerce/static/css/signin.css similarity index 100% rename from _chapters/chp09/django_ecommerce/static/css/signin.css rename to _chapters/chp20/django_ecommerce/static/css/signin.css diff --git a/_chapters/chp09/django_ecommerce/static/fonts/Starjedi.eot b/_chapters/chp20/django_ecommerce/static/fonts/Starjedi.eot similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/Starjedi.eot rename to _chapters/chp20/django_ecommerce/static/fonts/Starjedi.eot diff --git a/_chapters/chp09/django_ecommerce/static/fonts/Starjedi.svg b/_chapters/chp20/django_ecommerce/static/fonts/Starjedi.svg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/Starjedi.svg rename to _chapters/chp20/django_ecommerce/static/fonts/Starjedi.svg diff --git a/_chapters/chp09/django_ecommerce/static/fonts/Starjedi.ttf b/_chapters/chp20/django_ecommerce/static/fonts/Starjedi.ttf similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/Starjedi.ttf rename to _chapters/chp20/django_ecommerce/static/fonts/Starjedi.ttf diff --git a/_chapters/chp09/django_ecommerce/static/fonts/Starjedi.woff b/_chapters/chp20/django_ecommerce/static/fonts/Starjedi.woff similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/Starjedi.woff rename to _chapters/chp20/django_ecommerce/static/fonts/Starjedi.woff diff --git a/_chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot b/_chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot rename to _chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.eot diff --git a/_chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg b/_chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg rename to _chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.svg diff --git a/_chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf b/_chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf rename to _chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.ttf diff --git a/_chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff b/_chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from _chapters/chp09/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff rename to _chapters/chp20/django_ecommerce/static/fonts/glyphicons-halflings-regular.woff diff --git a/_chapters/chp09/django_ecommerce/static/img/clone_army.jpg b/_chapters/chp20/django_ecommerce/static/img/clone_army.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/clone_army.jpg rename to _chapters/chp20/django_ecommerce/static/img/clone_army.jpg diff --git a/_chapters/chp09/django_ecommerce/static/img/darth.jpg b/_chapters/chp20/django_ecommerce/static/img/darth.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/darth.jpg rename to _chapters/chp20/django_ecommerce/static/img/darth.jpg diff --git a/_chapters/chp09/django_ecommerce/static/img/leia.jpg b/_chapters/chp20/django_ecommerce/static/img/leia.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/leia.jpg rename to _chapters/chp20/django_ecommerce/static/img/leia.jpg diff --git a/_chapters/chp09/django_ecommerce/static/img/star-wars-battle.jpg b/_chapters/chp20/django_ecommerce/static/img/star-wars-battle.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/star-wars-battle.jpg rename to _chapters/chp20/django_ecommerce/static/img/star-wars-battle.jpg diff --git a/_chapters/chp09/django_ecommerce/static/img/star-wars-cast.jpg b/_chapters/chp20/django_ecommerce/static/img/star-wars-cast.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/star-wars-cast.jpg rename to _chapters/chp20/django_ecommerce/static/img/star-wars-cast.jpg diff --git a/_chapters/chp09/django_ecommerce/static/img/yoda.jpg b/_chapters/chp20/django_ecommerce/static/img/yoda.jpg similarity index 100% rename from _chapters/chp09/django_ecommerce/static/img/yoda.jpg rename to _chapters/chp20/django_ecommerce/static/img/yoda.jpg diff --git a/_chapters/chp17/django_ecommerce/static/js/admin.js b/_chapters/chp20/django_ecommerce/static/js/admin.js similarity index 100% rename from _chapters/chp17/django_ecommerce/static/js/admin.js rename to _chapters/chp20/django_ecommerce/static/js/admin.js diff --git a/_chapters/chp16/django_ecommerce/static/js/angular-google-maps.min.js b/_chapters/chp20/django_ecommerce/static/js/angular-google-maps.min.js similarity index 100% rename from _chapters/chp16/django_ecommerce/static/js/angular-google-maps.min.js rename to _chapters/chp20/django_ecommerce/static/js/angular-google-maps.min.js diff --git a/_chapters/chp14/django_ecommerce/static/js/angular.min.js b/_chapters/chp20/django_ecommerce/static/js/angular.min.js similarity index 100% rename from _chapters/chp14/django_ecommerce/static/js/angular.min.js rename to _chapters/chp20/django_ecommerce/static/js/angular.min.js diff --git a/_chapters/chp14/django_ecommerce/static/js/angular.min.js.map b/_chapters/chp20/django_ecommerce/static/js/angular.min.js.map similarity index 100% rename from _chapters/chp14/django_ecommerce/static/js/angular.min.js.map rename to _chapters/chp20/django_ecommerce/static/js/angular.min.js.map diff --git a/_chapters/chp20/django_ecommerce/static/js/application.js b/_chapters/chp20/django_ecommerce/static/js/application.js new file mode 100755 index 0000000..d8894b3 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/static/js/application.js @@ -0,0 +1,68 @@ +var mecApp = angular.module('mecApp', ['google-maps']); + +mecApp.config(function($interpolateProvider, $httpProvider) { + $interpolateProvider.startSymbol('[[') + .endSymbol(']]'); + $httpProvider.defaults.headers.common['X-CSRFToken'] = $('input[name=csrfmiddlewaretoken]').val(); +}); + + +$(function() { + + // $("#user_form").submit(function() { + // if ( $("#credit-card").is(":visible")) { + // var form = this; + // var card = { + // number: $("#credit_card_number").val(), + // expMonth: $("#expiry_month").val(), + // expYear: $("#expiry_year").val(), + // cvc: $("#cvv").val() + // }; + + // Stripe.createToken(card, function(status, response) { + // if (status === 200) { + // console.log(status, response); + // $("#credit-card-errors").hide(); + // $("#last_4_digits").val(response.card.last4); + // $("#stripe_token").val(response.id); + // form.submit(); + // } else { + // // submit anyway + // form.submit(); + // // $("#stripe-error-message").text(response.error.message); + // // $("#credit-card-errors").show(); + // // $("#user_submit").attr("disabled", false); + // } + // }); + + // return false; + + // } + + // return true + + // }); + + // $("#change-card a").click(function() { + // $("#change-card").hide(); + // $("#credit-card").show(); + // $("#credit_card_number").focus(); + // return false; + // }); + + // //show status + // $("#show-achieve").click(function() { + // console.log("test") + // a = $("#achievements"); + // l = $("#show-achieve"); + // if (a.hasClass("hide")) { + // a.hide().removeClass('hide').slideDown('slow'); + // l.html("Hide Achievements"); + // } else { + // a.addClass("hide"); + // l.html("Show Achievements"); + // } + // return false; + // }); + +}); diff --git a/_chapters/chp09/django_ecommerce/static/js/bootstrap.js b/_chapters/chp20/django_ecommerce/static/js/bootstrap.js similarity index 100% rename from _chapters/chp09/django_ecommerce/static/js/bootstrap.js rename to _chapters/chp20/django_ecommerce/static/js/bootstrap.js diff --git a/_chapters/chp09/django_ecommerce/static/js/bootstrap.min.js b/_chapters/chp20/django_ecommerce/static/js/bootstrap.min.js similarity index 100% rename from _chapters/chp09/django_ecommerce/static/js/bootstrap.min.js rename to _chapters/chp20/django_ecommerce/static/js/bootstrap.min.js diff --git a/_chapters/chp09/django_ecommerce/static/js/jquery-1.11.1.min.js b/_chapters/chp20/django_ecommerce/static/js/jquery-1.11.1.min.js similarity index 100% rename from _chapters/chp09/django_ecommerce/static/js/jquery-1.11.1.min.js rename to _chapters/chp20/django_ecommerce/static/js/jquery-1.11.1.min.js diff --git a/_chapters/chp16/django_ecommerce/static/js/lodash.underscore.min.js b/_chapters/chp20/django_ecommerce/static/js/lodash.underscore.min.js similarity index 100% rename from _chapters/chp16/django_ecommerce/static/js/lodash.underscore.min.js rename to _chapters/chp20/django_ecommerce/static/js/lodash.underscore.min.js diff --git a/_chapters/chp14/django_ecommerce/static/js/loggedInCtrl.js b/_chapters/chp20/django_ecommerce/static/js/loggedInCtrl.js similarity index 100% rename from _chapters/chp14/django_ecommerce/static/js/loggedInCtrl.js rename to _chapters/chp20/django_ecommerce/static/js/loggedInCtrl.js diff --git a/_chapters/chp16/django_ecommerce/static/js/registerCtrl.js b/_chapters/chp20/django_ecommerce/static/js/registerCtrl.js similarity index 100% rename from _chapters/chp16/django_ecommerce/static/js/registerCtrl.js rename to _chapters/chp20/django_ecommerce/static/js/registerCtrl.js diff --git a/_chapters/chp17/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js b/_chapters/chp20/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js similarity index 100% rename from _chapters/chp17/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js rename to _chapters/chp20/django_ecommerce/static/js/ui-bootstrap-tpls-0.11.0.min.js diff --git a/_chapters/chp17/django_ecommerce/static/js/ui-bootstrap-tpls.min.js b/_chapters/chp20/django_ecommerce/static/js/ui-bootstrap-tpls.min.js similarity index 100% rename from _chapters/chp17/django_ecommerce/static/js/ui-bootstrap-tpls.min.js rename to _chapters/chp20/django_ecommerce/static/js/ui-bootstrap-tpls.min.js diff --git a/_chapters/chp16/django_ecommerce/static/js/userMapCtrl.js b/_chapters/chp20/django_ecommerce/static/js/userMapCtrl.js similarity index 100% rename from _chapters/chp16/django_ecommerce/static/js/userMapCtrl.js rename to _chapters/chp20/django_ecommerce/static/js/userMapCtrl.js diff --git a/_chapters/chp14/django_ecommerce/static/js/userPollCtrl.js b/_chapters/chp20/django_ecommerce/static/js/userPollCtrl.js similarity index 100% rename from _chapters/chp14/django_ecommerce/static/js/userPollCtrl.js rename to _chapters/chp20/django_ecommerce/static/js/userPollCtrl.js diff --git a/_chapters/chp10/django_ecommerce/templates/__base.html b/_chapters/chp20/django_ecommerce/templates/__base.html similarity index 73% rename from _chapters/chp10/django_ecommerce/templates/__base.html rename to _chapters/chp20/django_ecommerce/templates/__base.html index 3f9bb21..58b487e 100644 --- a/_chapters/chp10/django_ecommerce/templates/__base.html +++ b/_chapters/chp20/django_ecommerce/templates/__base.html @@ -1,7 +1,7 @@ {% load static %} - + @@ -25,7 +25,6 @@
    @@ -65,6 +63,7 @@
    + - - - + - + + + + + + + + + {% block extrajs %}{% endblock %} \ No newline at end of file diff --git a/_chapters/chp17/django_ecommerce/templates/admin/payments/user/change_form.html b/_chapters/chp20/django_ecommerce/templates/admin/payments/user/change_form.html similarity index 100% rename from _chapters/chp17/django_ecommerce/templates/admin/payments/user/change_form.html rename to _chapters/chp20/django_ecommerce/templates/admin/payments/user/change_form.html diff --git a/_chapters/chp09/django_ecommerce/templates/contact/contact.html b/_chapters/chp20/django_ecommerce/templates/contact/contact.html similarity index 100% rename from _chapters/chp09/django_ecommerce/templates/contact/contact.html rename to _chapters/chp20/django_ecommerce/templates/contact/contact.html diff --git a/_chapters/chp14/django_ecommerce/templates/djangular_polls/_polls.html b/_chapters/chp20/django_ecommerce/templates/djangular_polls/_polls.html similarity index 100% rename from _chapters/chp14/django_ecommerce/templates/djangular_polls/_polls.html rename to _chapters/chp20/django_ecommerce/templates/djangular_polls/_polls.html diff --git a/_chapters/chp10/django_ecommerce/templates/flatpages/default.html b/_chapters/chp20/django_ecommerce/templates/flatpages/default.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/flatpages/default.html rename to _chapters/chp20/django_ecommerce/templates/flatpages/default.html diff --git a/_chapters/chp11/django_ecommerce/templates/main/_announcements.html b/_chapters/chp20/django_ecommerce/templates/main/_announcements.html similarity index 88% rename from _chapters/chp11/django_ecommerce/templates/main/_announcements.html rename to _chapters/chp20/django_ecommerce/templates/main/_announcements.html index 523b6a2..fc3be38 100755 --- a/_chapters/chp11/django_ecommerce/templates/main/_announcements.html +++ b/_chapters/chp20/django_ecommerce/templates/main/_announcements.html @@ -8,7 +8,7 @@

    orders from the Council

    {% if a.vid %} {% video a.vid "medium" %} {% else %} - + {% endif %}
    {{ a.info | safe }}
    diff --git a/_chapters/chp10/django_ecommerce/templates/main/_badges.html b/_chapters/chp20/django_ecommerce/templates/main/_badges.html similarity index 60% rename from _chapters/chp10/django_ecommerce/templates/main/_badges.html rename to _chapters/chp20/django_ecommerce/templates/main/_badges.html index a1d2af0..e2a77bd 100755 --- a/_chapters/chp10/django_ecommerce/templates/main/_badges.html +++ b/_chapters/chp20/django_ecommerce/templates/main/_badges.html @@ -4,9 +4,9 @@

    Achievements

    {% for bdg in badges %}

    {{ bdg.name }}

    -  {{ bdg.name }} +  {{ bdg.name }}

    {{ bdg.desc }}

    {% endfor %} diff --git a/_chapters/chp11/django_ecommerce/templates/main/_jedibadge.html b/_chapters/chp20/django_ecommerce/templates/main/_jedibadge.html similarity index 83% rename from _chapters/chp11/django_ecommerce/templates/main/_jedibadge.html rename to _chapters/chp20/django_ecommerce/templates/main/_jedibadge.html index 6047b5d..47c221a 100755 --- a/_chapters/chp11/django_ecommerce/templates/main/_jedibadge.html +++ b/_chapters/chp20/django_ecommerce/templates/main/_jedibadge.html @@ -8,7 +8,7 @@

    Jedi Badge

  • Rank: {{user.rank}}
  • Name: {{user.name }}
  • Email: {{user.email }}
  • -
  • Show Achievements
  • +
  • [[ show_hide_label ]] Achievements
  • Click here to make changes to your credit card.

    \ No newline at end of file diff --git a/_chapters/chp10/django_ecommerce/templates/main/_lateststatus.html b/_chapters/chp20/django_ecommerce/templates/main/_lateststatus.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/main/_lateststatus.html rename to _chapters/chp20/django_ecommerce/templates/main/_lateststatus.html diff --git a/_chapters/chp10/django_ecommerce/templates/main/_statusupdate.html b/_chapters/chp20/django_ecommerce/templates/main/_statusupdate.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/main/_statusupdate.html rename to _chapters/chp20/django_ecommerce/templates/main/_statusupdate.html diff --git a/_chapters/chp09/django_ecommerce/templates/main/index.html b/_chapters/chp20/django_ecommerce/templates/main/index.html similarity index 100% rename from _chapters/chp09/django_ecommerce/templates/main/index.html rename to _chapters/chp20/django_ecommerce/templates/main/index.html diff --git a/_chapters/chp09/django_ecommerce/templates/main/templatetags/circle_item.html b/_chapters/chp20/django_ecommerce/templates/main/templatetags/circle_item.html similarity index 100% rename from _chapters/chp09/django_ecommerce/templates/main/templatetags/circle_item.html rename to _chapters/chp20/django_ecommerce/templates/main/templatetags/circle_item.html diff --git a/_chapters/chp10/django_ecommerce/templates/main/user.html b/_chapters/chp20/django_ecommerce/templates/main/user.html similarity index 70% rename from _chapters/chp10/django_ecommerce/templates/main/user.html rename to _chapters/chp20/django_ecommerce/templates/main/user.html index e47af35..ffcd014 100644 --- a/_chapters/chp10/django_ecommerce/templates/main/user.html +++ b/_chapters/chp20/django_ecommerce/templates/main/user.html @@ -1,7 +1,9 @@ {% extends "__base.html" %} {% load staticfiles %} {% block content %} -
    +
    +
    {% include "main/_badges.html" %}
    @@ -15,7 +17,11 @@
    {% include "main/_jedibadge.html" %} {% include "main/_statusupdate.html" %} + {% include "djangular_polls/_polls.html" %}
    +
    +{% endblock %} +{% block extrajs %} {% endblock %} \ No newline at end of file diff --git a/_chapters/chp20/django_ecommerce/templates/payments/_cardform.html b/_chapters/chp20/django_ecommerce/templates/payments/_cardform.html new file mode 100644 index 0000000..29e4238 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/templates/payments/_cardform.html @@ -0,0 +1,56 @@ + + +
    +
    +
    + [[ stripe_errormsg ]]
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/_chapters/chp20/django_ecommerce/templates/payments/_field.html b/_chapters/chp20/django_ecommerce/templates/payments/_field.html new file mode 100644 index 0000000..50de6bd --- /dev/null +++ b/_chapters/chp20/django_ecommerce/templates/payments/_field.html @@ -0,0 +1,12 @@ +
    + {{ field.label_tag }} +
    + {{ field }} +
    +
    + {{field.label}} is invalid: + value is required. + Input a valid email address. +
    +
    \ No newline at end of file diff --git a/_chapters/chp09/django_ecommerce/templates/payments/edit.html b/_chapters/chp20/django_ecommerce/templates/payments/edit.html similarity index 100% rename from _chapters/chp09/django_ecommerce/templates/payments/edit.html rename to _chapters/chp20/django_ecommerce/templates/payments/edit.html diff --git a/_chapters/chp09/django_ecommerce/templates/payments/errors.html b/_chapters/chp20/django_ecommerce/templates/payments/errors.html similarity index 100% rename from _chapters/chp09/django_ecommerce/templates/payments/errors.html rename to _chapters/chp20/django_ecommerce/templates/payments/errors.html diff --git a/_chapters/chp20/django_ecommerce/templates/payments/register.html b/_chapters/chp20/django_ecommerce/templates/payments/register.html new file mode 100644 index 0000000..6e46262 --- /dev/null +++ b/_chapters/chp20/django_ecommerce/templates/payments/register.html @@ -0,0 +1,35 @@ +{% extends "__base.html" %} +{% block content %} +
    +
    +
    +
    + +
    +
    +
    + + {% csrf_token %} +
    x +
    +

    [[ error ]]

    +
    +
    + {% for field in form.visible_fields %} + {% include "payments/_field.html" %} + {% endfor %} + + {% include "payments/_cardform.html" %} + +
    +
    +
    +{% endblock %} diff --git a/_chapters/chp09/django_ecommerce/templates/payments/sign_in.html b/_chapters/chp20/django_ecommerce/templates/payments/sign_in.html similarity index 93% rename from _chapters/chp09/django_ecommerce/templates/payments/sign_in.html rename to _chapters/chp20/django_ecommerce/templates/payments/sign_in.html index c26d0ca..5dc5cca 100644 --- a/_chapters/chp09/django_ecommerce/templates/payments/sign_in.html +++ b/_chapters/chp20/django_ecommerce/templates/payments/sign_in.html @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    -
    {{ a.info | safe }}
    diff --git a/_chapters/chp11/django_ecommerce/templates/main/_badges.html b/_chapters/chp21/django_ecommerce/templates/main/_badges.html similarity index 60% rename from _chapters/chp11/django_ecommerce/templates/main/_badges.html rename to _chapters/chp21/django_ecommerce/templates/main/_badges.html index a1d2af0..e2a77bd 100755 --- a/_chapters/chp11/django_ecommerce/templates/main/_badges.html +++ b/_chapters/chp21/django_ecommerce/templates/main/_badges.html @@ -4,9 +4,9 @@

    Achievements

    {% for bdg in badges %}

    {{ bdg.name }}

    -  {{ bdg.name }} +  {{ bdg.name }}

    {{ bdg.desc }}

    {% endfor %} diff --git a/_chapters/chp10/django_ecommerce/templates/main/_jedibadge.html b/_chapters/chp21/django_ecommerce/templates/main/_jedibadge.html similarity index 83% rename from _chapters/chp10/django_ecommerce/templates/main/_jedibadge.html rename to _chapters/chp21/django_ecommerce/templates/main/_jedibadge.html index 6047b5d..47c221a 100755 --- a/_chapters/chp10/django_ecommerce/templates/main/_jedibadge.html +++ b/_chapters/chp21/django_ecommerce/templates/main/_jedibadge.html @@ -8,7 +8,7 @@

    Jedi Badge

  • Rank: {{user.rank}}
  • Name: {{user.name }}
  • Email: {{user.email }}
  • -
  • Show Achievements
  • +
  • [[ show_hide_label ]] Achievements
  • Click here to make changes to your credit card.

    \ No newline at end of file diff --git a/_chapters/chp11/django_ecommerce/templates/main/_lateststatus.html b/_chapters/chp21/django_ecommerce/templates/main/_lateststatus.html similarity index 100% rename from _chapters/chp11/django_ecommerce/templates/main/_lateststatus.html rename to _chapters/chp21/django_ecommerce/templates/main/_lateststatus.html diff --git a/_chapters/chp11/django_ecommerce/templates/main/_statusupdate.html b/_chapters/chp21/django_ecommerce/templates/main/_statusupdate.html similarity index 100% rename from _chapters/chp11/django_ecommerce/templates/main/_statusupdate.html rename to _chapters/chp21/django_ecommerce/templates/main/_statusupdate.html diff --git a/_chapters/chp10/django_ecommerce/templates/main/index.html b/_chapters/chp21/django_ecommerce/templates/main/index.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/main/index.html rename to _chapters/chp21/django_ecommerce/templates/main/index.html diff --git a/_chapters/chp10/django_ecommerce/templates/main/templatetags/circle_item.html b/_chapters/chp21/django_ecommerce/templates/main/templatetags/circle_item.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/main/templatetags/circle_item.html rename to _chapters/chp21/django_ecommerce/templates/main/templatetags/circle_item.html diff --git a/_chapters/chp21/django_ecommerce/templates/main/user.html b/_chapters/chp21/django_ecommerce/templates/main/user.html new file mode 100644 index 0000000..ffcd014 --- /dev/null +++ b/_chapters/chp21/django_ecommerce/templates/main/user.html @@ -0,0 +1,27 @@ +{% extends "__base.html" %} +{% load staticfiles %} +{% block content %} +
    +
    + {% include "main/_badges.html" %} +
    +
    +
    +
    + {% include "main/_announcements.html" %} + {% include "main/_lateststatus.html" %} +
    +
    +
    +
    + {% include "main/_jedibadge.html" %} + {% include "main/_statusupdate.html" %} + {% include "djangular_polls/_polls.html" %} +
    +
    +
    +
    +{% endblock %} +{% block extrajs %} +{% endblock %} \ No newline at end of file diff --git a/_chapters/chp21/django_ecommerce/templates/payments/_cardform.html b/_chapters/chp21/django_ecommerce/templates/payments/_cardform.html new file mode 100644 index 0000000..29e4238 --- /dev/null +++ b/_chapters/chp21/django_ecommerce/templates/payments/_cardform.html @@ -0,0 +1,56 @@ + + +
    +
    +
    + [[ stripe_errormsg ]]
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/_chapters/chp21/django_ecommerce/templates/payments/_field.html b/_chapters/chp21/django_ecommerce/templates/payments/_field.html new file mode 100644 index 0000000..50de6bd --- /dev/null +++ b/_chapters/chp21/django_ecommerce/templates/payments/_field.html @@ -0,0 +1,12 @@ +
    + {{ field.label_tag }} +
    + {{ field }} +
    +
    + {{field.label}} is invalid: + value is required. + Input a valid email address. +
    +
    \ No newline at end of file diff --git a/_chapters/chp10/django_ecommerce/templates/payments/edit.html b/_chapters/chp21/django_ecommerce/templates/payments/edit.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/payments/edit.html rename to _chapters/chp21/django_ecommerce/templates/payments/edit.html diff --git a/_chapters/chp10/django_ecommerce/templates/payments/errors.html b/_chapters/chp21/django_ecommerce/templates/payments/errors.html similarity index 100% rename from _chapters/chp10/django_ecommerce/templates/payments/errors.html rename to _chapters/chp21/django_ecommerce/templates/payments/errors.html diff --git a/_chapters/chp21/django_ecommerce/templates/payments/register.html b/_chapters/chp21/django_ecommerce/templates/payments/register.html new file mode 100644 index 0000000..6e46262 --- /dev/null +++ b/_chapters/chp21/django_ecommerce/templates/payments/register.html @@ -0,0 +1,35 @@ +{% extends "__base.html" %} +{% block content %} +
    +
    +
    +
    + +
    +
    +
    + + {% csrf_token %} +
    x +
    +

    [[ error ]]

    +
    +
    + {% for field in form.visible_fields %} + {% include "payments/_field.html" %} + {% endfor %} + + {% include "payments/_cardform.html" %} + +
    +
    +
    +{% endblock %} diff --git a/_chapters/chp10/django_ecommerce/templates/payments/sign_in.html b/_chapters/chp21/django_ecommerce/templates/payments/sign_in.html similarity index 93% rename from _chapters/chp10/django_ecommerce/templates/payments/sign_in.html rename to _chapters/chp21/django_ecommerce/templates/payments/sign_in.html index c26d0ca..5dc5cca 100644 --- a/_chapters/chp10/django_ecommerce/templates/payments/sign_in.html +++ b/_chapters/chp21/django_ecommerce/templates/payments/sign_in.html @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    -