├── app
├── __init__.py
├── hardware
│ ├── __init__.py
│ ├── relay.py
│ ├── led.py
│ └── dht11.py
├── migrations
│ └── __init__.py
├── static
│ ├── face
│ │ └── readme.text
│ ├── img
│ │ ├── 小埋.png
│ │ ├── face.png
│ │ ├── home.png
│ │ ├── led.png
│ │ ├── look.png
│ │ ├── control.png
│ │ ├── favicon.ico
│ │ ├── image1.jpg
│ │ ├── image2.jpg
│ │ ├── login.png
│ │ ├── raspi.jpg
│ │ ├── background1.jpeg
│ │ └── yinyang.svg
│ └── bootstrap
│ │ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ │ ├── js
│ │ ├── matrix.js
│ │ └── bootstrap.min.js
│ │ └── css
│ │ ├── weather.css
│ │ └── bootstrap-theme.css
├── admin.py
├── apps.py
├── models.py
├── urls.py
├── cp_views.py
├── templates
│ ├── index.html
│ ├── look.html
│ ├── control.html
│ ├── login.html
│ ├── face.html
│ └── base.html
└── views.py
├── raspi
├── __init__.py
├── wsgi.py
├── urls.py
└── settings.py
├── .gitignore
├── db.sqlite3
├── .idea
├── vcs.xml
├── misc.xml
├── codeStyleSettings.xml
├── modules.xml
├── raspi.iml
└── workspace.xml
├── manage.py
└── README.md
/app/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/raspi/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/hardware/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.pyc
3 |
4 | app/static/face/*.jpg
5 |
6 |
7 |
--------------------------------------------------------------------------------
/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/db.sqlite3
--------------------------------------------------------------------------------
/app/static/face/readme.text:
--------------------------------------------------------------------------------
1 | this file store the photo that camera take
--------------------------------------------------------------------------------
/app/static/img/小埋.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/小埋.png
--------------------------------------------------------------------------------
/app/static/img/face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/face.png
--------------------------------------------------------------------------------
/app/static/img/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/home.png
--------------------------------------------------------------------------------
/app/static/img/led.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/led.png
--------------------------------------------------------------------------------
/app/static/img/look.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/look.png
--------------------------------------------------------------------------------
/app/static/img/control.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/control.png
--------------------------------------------------------------------------------
/app/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/favicon.ico
--------------------------------------------------------------------------------
/app/static/img/image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/image1.jpg
--------------------------------------------------------------------------------
/app/static/img/image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/image2.jpg
--------------------------------------------------------------------------------
/app/static/img/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/login.png
--------------------------------------------------------------------------------
/app/static/img/raspi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/raspi.jpg
--------------------------------------------------------------------------------
/app/static/img/background1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/img/background1.jpeg
--------------------------------------------------------------------------------
/app/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import LED_FORM,UserAdmin
3 |
4 | admin.site.register(LED_FORM,UserAdmin)
5 |
--------------------------------------------------------------------------------
/app/apps.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | from django.apps import AppConfig
4 |
5 |
6 | class AppConfig(AppConfig):
7 | name = 'app'
8 |
--------------------------------------------------------------------------------
/app/static/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/app/static/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/app/static/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/app/static/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/p-xy/raspi/HEAD/app/static/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/models.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 | from django.db import models
3 | from django.contrib import admin
4 |
5 | class LED_FORM(models.Model):
6 | switch = models.CharField(max_length=30)
7 | id = models.IntegerField(primary_key=True)
8 |
9 | class UserAdmin(admin.ModelAdmin):
10 | list_display = ('id','switch')
11 |
12 |
--------------------------------------------------------------------------------
/raspi/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for raspi project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "raspi.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/app/urls.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | from django.conf.urls import url
3 | from . import views
4 | #from . import cp_views as views
5 |
6 |
7 | urlpatterns = [
8 | url(r'^login$',views.login ),
9 | url(r'^logout$',views.logout),
10 | url(r'^$',views.index),
11 | url(r'^look$',views.look),
12 | url(r'^control$',views.control),
13 | url(r'^face$',views.face),
14 | #在本地运行,请注释下面两行
15 | url(r'^take_a_photo$',views.take_a_photo),
16 | url(r'^face_compare$',views.face_compare),
17 |
18 | ]
19 |
--------------------------------------------------------------------------------
/.idea/raspi.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/hardware/relay.py:
--------------------------------------------------------------------------------
1 | #-*-coding:utf-8 -*-
2 | import RPi.GPIO as GPIO
3 | import time
4 |
5 |
6 | GPIO.setmode(GPIO.BOARD)
7 | GPIO.setwarnings(False)
8 |
9 | #这是一个继电器类,用控制门的开关
10 | class Relay(object):
11 | def __init__(self,pin_number):
12 | self.pin_number = pin_number
13 |
14 | def open(self):
15 | #设置pin脚为输出
16 | GPIO.setup(self.pin_number,GPIO.OUT)
17 | #输出高电平
18 | GPIO.output(self.pin_number,GPIO.HIGH)
19 | #高电平持续一秒
20 | time.sleep(1)
21 | #输出低电平继电器不工作
22 | GPIO.output(self.pin_number,GPIO.LOW)
23 | #清理IO口
24 | GPIO.cleanup(self.pin_number)
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "raspi.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/raspi/urls.py:
--------------------------------------------------------------------------------
1 | """raspi URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import url,include
17 | from django.contrib import admin
18 |
19 | urlpatterns = [
20 | url(r'^admin/', admin.site.urls),
21 | url(r'^',include('app.urls')),
22 | ]
23 |
--------------------------------------------------------------------------------
/app/hardware/led.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import RPi.GPIO as GPIO
3 | import time
4 |
5 |
6 | '''
7 | There has two ways of numbering the IO pins on a
8 | Rsapberry Pi within RPi.GPIO : "BOARD" and "BCM" .
9 | More detail , just google "the wiki of RPi.GPIO".
10 | '''
11 | GPIO.setmode(GPIO.BOARD)
12 | GPIO.setwarnings(False)
13 |
14 | class LED(object):
15 | '''
16 | pin_number is your IO pins number.
17 | open_or_close is the switch for controll LED.
18 | '''
19 | def __init__(self,pin_number,open_or_close):
20 | self.pin_number = pin_number
21 | self.open_or_close = open_or_close
22 |
23 |
24 | def switch(self):
25 | GPIO.setup(self.pin_number,GPIO.OUT)# set up the channel as output.
26 |
27 | if self.open_or_close == u'开':
28 | GPIO.output(self.pin_number,GPIO.HIGH)
29 |
30 | elif self.open_or_close == u'关':
31 | GPIO.output(self.pin_number,GPIO.LOW)
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/cp_views.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | from django.shortcuts import render,redirect
3 | from django.http import HttpResponse
4 | from django.template import Template
5 | from django.contrib import auth
6 | from django.contrib.auth.decorators import login_required
7 |
8 | @login_required()
9 | def face(request):
10 | return render(request,'face.html')
11 |
12 |
13 | # 主页
14 | @login_required()
15 | def index(request):
16 | return render(request,'index.html')
17 |
18 | #查看状态
19 | @login_required()
20 | def look(request):
21 | return render(request,'look.html')
22 |
23 | #退出
24 | def logout(request):
25 | auth.logout(request)
26 | return redirect('/login')
27 |
28 | # 登录
29 | def login(request):
30 | if request.method =='POST':
31 | username = request.POST['username']
32 | password = request.POST['password']
33 | #验证登录,正确则authenticate()返回一个User对象,错误则返回一个None类
34 | user = auth.authenticate(username=username, password=password)
35 | if user is not None:
36 | if user.is_active:
37 | auth.login(request, user)#登录用户
38 | return redirect('/',{'username':username})#使用重定向而不是render返回首页,可以避免刷新再次提交表单导致出错
39 | else:
40 | NOT_ACTIVE = '你的账户没有激活,请联系管理员'
41 | render(request,'login.html',{'NOT_ACTIVE':NOT_ACTIVE})
42 | else:
43 | ERROR = 'Typing Error'
44 | return render(request,'login.html',{'ERROR':ERROR })
45 |
46 | else:
47 | LOGIN = 'Login System'
48 | return render(request,'login.html',{'LOGIN':LOGIN})
49 |
50 | #控制LED
51 | @login_required()
52 | def control(request):
53 | return render(request,'control.html')
54 |
55 | #人脸比对的页面
56 | @login_required()
57 | def face(request):
58 | return render(request,'face.html')
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 《基于树莓派的智能家居监控系统》
2 |
3 | ## 简介
4 | raspi是我本科毕业论文的一个paper,从2017年3月到6月份完成,对于一个没怎么接触软件领域的微电子学生来说已经是一个天大的工程!毕业万岁~\(≧▽≦)/~
5 |
6 | 关于这个project:
7 | - software:主要使用了 python2.7 + django1.11 + bootstrap3.3
8 | - hardware:树莓派3B+、摄像头、继电器、DHT11温湿度传感器、LED等等
9 | - 系统环境:基于debian7定制的树莓派官方系统raspbian
10 | - 第三方接入:face++人脸比对、百度tts文字转语音
11 |
12 | 实际上软件部分还用到了树莓派的官方系统raspbian提供的驱动,比如摄像头驱动模块picamera、IO接口模块RPi.GPIO。因此,原理上clone这个repository到本地pc上是无法running的,需要做一些修改,后面会提到。
13 |
14 | ## 功能
15 | - 访问权限:只有登录才能进入web页面,控制家居设备。
16 | - 温湿度实时监控
17 | - face++d人脸比对 + 百度语音 +继电器 实现安全门禁,刷脸开门!
18 | - 控制家电开关:这里只实现了LED模拟家庭环境
19 |
20 | 实际上我还接入了google-assistant的SDK,可以chat with google assistant~,但由于当时google-assistant才刚开放(没错就是在github),还处于测试阶段,接入语音控制这些功能都还没有,语音助手纯属就是一个玩具;(
21 |
22 |
23 | ## 页面展示
24 | #### 首页
25 | 
26 | #### 人脸比对
27 | face++的人脸识别技术真是太棒了,支付宝刷脸付款用的也是face++,呐,我用face++的时候老罗的坚果pro2还没用上呢,哈哈。
28 |
29 | 
30 | #### 查看温湿度
31 | 温湿度用的是DHT11这个模块,说来奇葩,和我同一组的两个妹子用的也是DHT11,答辩的时候老师很不高兴。其实最重要的是温湿度显示的实时性,使用ajax和定时器,每隔几秒从后台读取一次数据。
32 |
33 | 
34 | #### LED模拟控制室内灯光
35 | 本来导师强调要控制空调、电视机等一些比较现实的家电的,但想到答辩的时候也不会提供这些,也不好展示,因此就只控制了一个LED,有点囧o(╯□╰)o
36 |
37 | 
38 | #### 登录界面
39 | 没啥好说的,django提供了login验证,只要在后台注册管理员账户,一切都so easy~
40 |
41 | 
42 |
43 |
44 | ## 如何在本地运行
45 | - 安装django1.11 + python2.7 ,注意django的2.0版本和1版本不兼容。
46 | - clone到本地:
47 | > 1. app/urls.py把 from . import views 改成 from . import cp_views as views 。
48 | > 2. app/urls.py 把最后这两行注释:
49 |
url(r'^take_a_photo$',views.take_a_photo)
50 | url(r'^face_compare$',views.face_compare)
51 | > 3. 运行:
52 | $python manage.py makemigrations
53 | $python manage.py migrate
54 | $python manage.py createsuperuser
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/static/bootstrap/js/matrix.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | 11 �� ��setInterval(draw, 33)�趨ˢ�¼��
4 | 12
5 | 13 �� ��String.fromCharCode(1e2+Math.random()*33)���������ĸ
6 | 14
7 | 15 �� ��ctx.fillStyle=��rgba(0,0,0,.05)��; ctx.fillRect(0,0,width,height); ctx.fillStyle=��#0F0��; ��������opacityΪ0.5�İ�����ɫ����
8 | 16
9 | 17 �� ��x = (index * 10)+10;��yPositions[index] = y + 10;˳��ȷ����ʾ��ĸ��λ��
10 | 18
11 | 19 �� ��fillText(text, x, y); ��ָ��λ����ʾһ����ĸ ���ϲ���ѭ�����У��ͻ�������ڿ͵۹�����ƬͷЧ����
12 | 20 */
13 | $(document).ready(function() {
14 | //�豸���
15 | var s = window.screen;
16 | var width = q.width = s.width;
17 | var height = q.height;
18 | var yPositions = Array(300).join(0).split('');
19 | var ctx = q.getContext('2d');
20 | var draw = function() {
21 | ctx.fillStyle = 'rgba(0,0,0,.05)';
22 | ctx.fillRect(0, 0, width, height);
23 | ctx.fillStyle = 'green';/*������ɫ*/
24 | ctx.font = '10pt Georgia';
25 | yPositions.map(function(y, index) {
26 | text = String.fromCharCode(1e2 + Math.random() * 330);
27 | x = (index * 10) + 10;
28 | q.getContext('2d').fillText(text, x, y);
29 | if (y > Math.random() * 1e4) {
30 | yPositions[index] = 0;
31 | } else {
32 | yPositions[index] = y + 10;
33 | }
34 | });
35 | };
36 | RunMatrix();
37 | function RunMatrix() {
38 | Game_Interval = setInterval(draw,30);
39 | }
40 | });
--------------------------------------------------------------------------------
/app/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block head %}
3 |
22 | {% endblock %}
23 | {% block title%}主页{% endblock %}
24 | {% block content %}
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Who are crazy enough to think that they can change
41 | the world, are the ones who do.
42 | --Steve Jobs
43 |
44 |
45 |
46 |
47 | LET 'S MAKE OUR WORLD
48 | BETTER
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
关于树莓派
59 |
60 |

61 |
62 |
63 |
64 |
65 | Raspberry Pi,中文名为“树莓派”,简
66 | 写为RPi,是为学习计算机编程教育而设计,
67 | 只有信用卡大小的微型电脑,其系统基于Linux。
68 | 随着Windows 10 IoT的发布
69 | 我们也将可以用上运行Windows的树莓派。
70 | 自问世以来,受众多计算机发烧友和创客的追捧,
71 | 曾经一“派”难求。别看其外表“娇小”,内“心”却很强大,
72 | 视频、音频等功能通通皆有,可谓是“麻雀虽小,五脏俱全”。
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | {% endblock%}
84 |
85 |
--------------------------------------------------------------------------------
/app/static/img/yinyang.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
31 |
--------------------------------------------------------------------------------
/app/templates/look.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block head %}
3 |
4 |
48 | {% endblock %}
49 | {% block title%}查看{% endblock %}
50 | {% block content %}
51 |
52 |
53 |
54 |
55 |
56 |
一览众山小
57 |
智能时代,连接一切可以连接的,数据化一切可以数据化的。
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
模拟灯光
66 |
这里模拟的是家庭的灯光
67 |
在这里仅提供查看,若要控制开关请移步控制区
68 |
69 |
70 |
71 |
72 |
73 |
灯已:{{ LED }}
74 |

75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
室内的温/湿度
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
97 |
98 |
99 |
100 |
101 |
湿度
102 |
103 |
104 |
105 |
106 |
107 |
132 |
133 | {% endblock%}
134 |
--------------------------------------------------------------------------------
/app/templates/control.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block head %}
3 |
56 | {% endblock %}
57 | {% block title%}控制{% endblock %}
58 | {% block content %}
59 |
60 |
61 |
62 |
63 |
运筹帷幄之中
64 |
这里是家电控制区,可以实时、方便的控制家电
65 |
66 |
67 |
68 |
69 |
70 |
控制灯
71 |

72 |
73 |
85 |
86 |
87 |
88 |
89 |
90 |
129 |
130 | {% endblock%}
131 |
--------------------------------------------------------------------------------
/raspi/settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | """
3 | Django settings for raspi project.
4 |
5 | Generated by 'django-admin startproject' using Django 1.10.6.
6 |
7 | For more information on this file, see
8 | https://docs.djangoproject.com/en/1.10/topics/settings/
9 |
10 | For the full list of settings and their values, see
11 | https://docs.djangoproject.com/en/1.10/ref/settings/
12 | """
13 |
14 | import os
15 |
16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 |
19 |
20 | # Quick-start development settings - unsuitable for production
21 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
22 |
23 | # SECURITY WARNING: keep the secret key used in production secret!
24 | SECRET_KEY = 'f^z$$4k)i0q!3^$4cg@n&s#z12m+w-k_f(&m4v3%l8i38t=0y_'
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = True
28 |
29 | ALLOWED_HOSTS = []
30 |
31 |
32 | # Application definition
33 |
34 | INSTALLED_APPS = [
35 | 'django.contrib.admin',
36 | 'django.contrib.auth',
37 | 'django.contrib.contenttypes',
38 | 'django.contrib.sessions',
39 | 'django.contrib.messages',
40 | 'django.contrib.staticfiles',
41 | 'app',
42 |
43 | ]
44 |
45 | MIDDLEWARE = [
46 | 'django.middleware.security.SecurityMiddleware',
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.messages.middleware.MessageMiddleware',
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 | ]
54 |
55 | ROOT_URLCONF = 'raspi.urls'
56 |
57 | TEMPLATES = [
58 | {
59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60 | 'DIRS': [os.path.join(BASE_DIR,'app/templates')],
61 | 'APP_DIRS': True,
62 | 'OPTIONS': {
63 | 'context_processors': [
64 | 'django.template.context_processors.debug',
65 | 'django.template.context_processors.request',
66 | 'django.contrib.auth.context_processors.auth',
67 | 'django.contrib.messages.context_processors.messages',
68 |
69 | ],
70 | },
71 | },
72 | ]
73 |
74 | WSGI_APPLICATION = 'raspi.wsgi.application'
75 |
76 |
77 | # Database
78 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
79 |
80 | DATABASES = {
81 | 'default': {
82 | 'ENGINE': 'django.db.backends.sqlite3',
83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
84 | }
85 | }
86 |
87 |
88 | # Password validation
89 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
90 |
91 | AUTH_PASSWORD_VALIDATORS = [
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
103 | },
104 | ]
105 |
106 |
107 | # Internationalization
108 | # https://docs.djangoproject.com/en/1.10/topics/i18n/
109 |
110 | LANGUAGE_CODE = 'zh-hans'
111 |
112 | TIME_ZONE = 'UTC'
113 |
114 | USE_I18N = True
115 |
116 | USE_L10N = True
117 |
118 | USE_TZ = True
119 |
120 |
121 | # Static files (CSS, JavaScript, Images)
122 | # https://docs.djangoproject.com/en/1.10/howto/static-files/
123 |
124 | STATIC_URL = '/static/'
125 |
126 | # 用户登录访问限制@login_required()
127 |
128 | LOGIN_URL = '/login'
129 |
--------------------------------------------------------------------------------
/app/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 登录
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/app/templates/face.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block head %}
3 |
64 | {% endblock %}
65 |
66 | {% block title %} 人脸比对 {% endblock %}
67 |
68 | {% block content %}
69 |
70 |
71 |
72 |
73 |
寻他千百度
74 |
75 |
体验Face++的人脸比对能力,对比照片核验用户身份
76 |
该功能是基于Compare API搭建的,运用Face++的人脸比对技术,打造智能家居刷脸控制
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |

85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |

96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
110 |
111 |
112 |
113 |
114 |
115 |
比对结果
116 |
117 |
118 |
119 |
120 |
121 |
122 |
Response JSON
123 |
124 |
125 |
126 |
127 |
128 |
196 |
197 |
198 |
199 | {% endblock %}
200 |
201 |
202 |
203 |
204 |
205 |
--------------------------------------------------------------------------------
/app/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% block title %}{% endblock %}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {% block head %}{% endblock %}
15 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
111 |
112 |
113 |
Raspi
114 |
查看、控制家居设备的Web服务,让家尽在掌控
115 |
基于树莓派,使用Python-django、Bootstrap开发
116 |
源码托管于Github :
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | {% block content %} {% endblock %}
126 |
127 |
128 |
129 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/app/static/bootstrap/css/weather.css:
--------------------------------------------------------------------------------
1 |
2 | @import url(http://fonts.googleapis.com/css?family=Lato:100,300,400,700,900);
3 | body {
4 | background-color: #fff;
5 | }
6 |
7 | *, *:before, *:after {
8 | box-sizing: border-box;
9 | }
10 |
11 | .weather-wrapper {
12 | display: -webkit-box;
13 | display: -webkit-flex;
14 | display: -ms-flexbox;
15 | display: flex;
16 | -webkit-box-orient: horizontal;
17 | -webkit-box-direction: normal;
18 | -webkit-flex-direction: row;
19 | -ms-flex-direction: row;
20 | flex-direction: row;
21 | -webkit-flex-wrap: wrap;
22 | -ms-flex-wrap: wrap;
23 | flex-wrap: wrap;
24 | -webkit-box-pack: center;
25 | -webkit-justify-content: center;
26 | -ms-flex-pack: center;
27 | justify-content: center;
28 | }
29 |
30 | .weather-card {
31 | margin: 20px 20px;
32 | border-radius: 20px;
33 | position: relative;
34 | overflow: hidden;
35 | width: 270px;
36 | height: 250px;
37 | background-color: white;
38 | box-shadow: 0px 0px 25px 1px rgba(50, 50, 50, 0.1);
39 | -webkit-animation: appear 500ms ease-out forwards;
40 | animation: appear 500ms ease-out forwards;
41 | }
42 | .weather-card h1 {
43 | position: absolute;
44 | font-family: 'Lato',sans-serif;
45 | font-weight: 300;
46 | font-size: 50px;
47 | color: #333;
48 | bottom: 10;
49 | left: 35px;
50 | opacity: 0;
51 | -webkit-transform: translateX(150px);
52 | -ms-transform: translateX(150px);
53 | transform: translateX(150px);
54 | -webkit-animation: title-appear 500ms ease-out 500ms forwards;
55 | animation: title-appear 500ms ease-out 500ms forwards;
56 | }
57 | .weather-card p {
58 | position: absolute;
59 | font-family: 'Lato', sans-serif;
60 | font-weight: 300;
61 | font-size: 28px;
62 | color: #333;
63 | bottom: 0;
64 | left: 35px;
65 | -webkit-animation: title-appear 1s ease-out 500ms forwards;
66 | animation: title-appear 1s ease-out 500ms forwards;
67 | }
68 |
69 | .weather-icon {
70 | position: relative;
71 | width: 50px;
72 | height: 50px;
73 | top: 0;
74 | float: right;
75 | margin: 40px 40px 0 0;
76 | -webkit-animation: weather-icon-move 5s ease-in-out infinite;
77 | animation: weather-icon-move 5s ease-in-out infinite;
78 | }
79 |
80 | .sun {
81 | background: #FFCD41;
82 | border-radius: 50%;
83 | box-shadow: rgba(255, 255, 0, 0.1) 0 0 0 4px;
84 | -webkit-animation: light 800ms ease-in-out infinite alternate, weather-icon-move 5s ease-in-out infinite;
85 | animation: light 800ms ease-in-out infinite alternate, weather-icon-move 5s ease-in-out infinite;
86 | }
87 |
88 | @-webkit-keyframes light {
89 | from {
90 | box-shadow: rgba(255, 255, 0, 0.2) 0 0 0 10px;
91 | }
92 | to {
93 | box-shadow: rgba(255, 255, 0, 0.2) 0 0 0 17px;
94 | }
95 | }
96 |
97 | @keyframes light {
98 | from {
99 | box-shadow: rgba(255, 255, 0, 0.2) 0 0 0 10px;
100 | }
101 | to {
102 | box-shadow: rgba(255, 255, 0, 0.2) 0 0 0 17px;
103 | }
104 | }
105 | .cloud {
106 | margin-right: 60px;
107 | background: #e1e1e1;
108 | border-radius: 20px;
109 | width: 25px;
110 | height: 25px;
111 | box-shadow: #e1e1e1 24px -6px 0 2px, #e1e1e1 10px 5px 0 5px, #e1e1e1 30px 5px 0 2px, #e1e1e1 11px -8px 0 -3px, #e1e1e1 25px 11px 0 -1px;
112 | }
113 | .cloud:after {
114 | content: "";
115 | position: absolute;
116 | border-radius: 10px;
117 | background-color: transparent;
118 | width: 4px;
119 | height: 12px;
120 | left: 0;
121 | top: 31px;
122 | -webkit-transform: rotate(30deg);
123 | -ms-transform: rotate(30deg);
124 | transform: rotate(30deg);
125 | -webkit-animation: rain 800ms ease-in-out infinite alternate;
126 | animation: rain 800ms ease-in-out infinite alternate;
127 | }
128 |
129 | @-webkit-keyframes rain {
130 | from {
131 | box-shadow: #2092A9 8px 0px, #2092A9 32px -6px, #2092A9 20px 0px;
132 | }
133 | to {
134 | box-shadow: #2092A9 8px 6px, #2092A9 32px 0px, #2092A9 20px 6px;
135 | }
136 | }
137 |
138 | @keyframes rain {
139 | from {
140 | box-shadow: #2092A9 8px 0px, #2092A9 32px -6px, #2092A9 20px 0px;
141 | }
142 | to {
143 | box-shadow: #2092A9 8px 6px, #2092A9 32px 0px, #2092A9 20px 6px;
144 | }
145 | }
146 | @-webkit-keyframes weather-icon-move {
147 | 50% {
148 | -webkit-transform: translateY(-8px);
149 | transform: translateY(-8px);
150 | }
151 | }
152 | @keyframes weather-icon-move {
153 | 50% {
154 | -webkit-transform: translateY(-8px);
155 | transform: translateY(-8px);
156 | }
157 | }
158 | .inspiration {
159 | color: #aeaeae;
160 | font-family: 'Lato', sans-serif;
161 | font-weight: 300;
162 | font-size: 24px;
163 | text-align: center;
164 | }
165 | .inspiration a {
166 | color: #FA565F;
167 | font-weight: 400;
168 | -webkit-animation: all 300ms ease-in-out;
169 | animation: all 300ms ease-in-out;
170 | }
171 |
172 | @-webkit-keyframes appear {
173 | 0% {
174 | -webkit-transform: scale(0);
175 | transform: scale(0);
176 | }
177 | 50% {
178 | -webkit-transform: scale(1.05);
179 | transform: scale(1.05);
180 | }
181 | 75% {
182 | -webkit-transform: scale(0.95);
183 | transform: scale(0.95);
184 | }
185 | 100% {
186 | -webkit-transform: scale(1);
187 | transform: scale(1);
188 | }
189 | }
190 |
191 | @keyframes appear {
192 | 0% {
193 | -webkit-transform: scale(0);
194 | transform: scale(0);
195 | }
196 | 50% {
197 | -webkit-transform: scale(1.05);
198 | transform: scale(1.05);
199 | }
200 | 75% {
201 | -webkit-transform: scale(0.95);
202 | transform: scale(0.95);
203 | }
204 | 100% {
205 | -webkit-transform: scale(1);
206 | transform: scale(1);
207 | }
208 | }
209 | @-webkit-keyframes title-appear {
210 | from {
211 | opacity: 0;
212 | -webkit-transform: translateX(150px);
213 | transform: translateX(150px);
214 | }
215 | to {
216 | opacity: 1;
217 | -webkit-transform: translateX(0px);
218 | transform: translateX(0px);
219 | }
220 | }
221 | @keyframes title-appear {
222 | from {
223 | opacity: 0;
224 | -webkit-transform: translateX(150px);
225 | transform: translateX(150px);
226 | }
227 | to {
228 | opacity: 1;
229 | -webkit-transform: translateX(0px);
230 | transform: translateX(0px);
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/app/hardware/dht11.py:
--------------------------------------------------------------------------------
1 | import time
2 | import RPi
3 |
4 |
5 | class DHT11Result:
6 | 'DHT11 sensor result returned by DHT11.read() method'
7 |
8 | ERR_NO_ERROR = 0
9 | ERR_MISSING_DATA = 1
10 | ERR_CRC = 2
11 |
12 | error_code = ERR_NO_ERROR
13 | temperature = -1
14 | humidity = -1
15 |
16 | def __init__(self, error_code, temperature, humidity):
17 | self.error_code = error_code
18 | self.temperature = temperature
19 | self.humidity = humidity
20 |
21 | def is_valid(self):
22 | return self.error_code == DHT11Result.ERR_NO_ERROR
23 |
24 |
25 | class DHT11:
26 | 'DHT11 sensor reader class for Raspberry'
27 |
28 | __pin = 0
29 |
30 | def __init__(self, pin):
31 | self.__pin = pin
32 |
33 | def read(self):
34 | RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT)
35 |
36 | # send initial high
37 | self.__send_and_sleep(RPi.GPIO.HIGH, 0.05)
38 |
39 | # pull down to low
40 | self.__send_and_sleep(RPi.GPIO.LOW, 0.02)
41 |
42 | # change to input using pull up
43 | RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP)
44 |
45 | # collect data into an array
46 | data = self.__collect_input()
47 |
48 | # parse lengths of all data pull up periods
49 | pull_up_lengths = self.__parse_data_pull_up_lengths(data)
50 |
51 | # if bit count mismatch, return error (4 byte data + 1 byte checksum)
52 | if len(pull_up_lengths) != 40:
53 | return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)
54 |
55 | # calculate bits from lengths of the pull up periods
56 | bits = self.__calculate_bits(pull_up_lengths)
57 |
58 | # we have the bits, calculate bytes
59 | the_bytes = self.__bits_to_bytes(bits)
60 |
61 | # calculate checksum and check
62 | checksum = self.__calculate_checksum(the_bytes)
63 | if the_bytes[4] != checksum:
64 | return DHT11Result(DHT11Result.ERR_CRC, 0, 0)
65 |
66 | # ok, we have valid data, return it
67 | return DHT11Result(DHT11Result.ERR_NO_ERROR, the_bytes[2], the_bytes[0])
68 |
69 | def __send_and_sleep(self, output, sleep):
70 | RPi.GPIO.output(self.__pin, output)
71 | time.sleep(sleep)
72 |
73 | def __collect_input(self):
74 | # collect the data while unchanged found
75 | unchanged_count = 0
76 |
77 | # this is used to determine where is the end of the data
78 | max_unchanged_count = 100
79 |
80 | last = -1
81 | data = []
82 | while True:
83 | current = RPi.GPIO.input(self.__pin)
84 | data.append(current)
85 | if last != current:
86 | unchanged_count = 0
87 | last = current
88 | else:
89 | unchanged_count += 1
90 | if unchanged_count > max_unchanged_count:
91 | break
92 |
93 | return data
94 |
95 | def __parse_data_pull_up_lengths(self, data):
96 | STATE_INIT_PULL_DOWN = 1
97 | STATE_INIT_PULL_UP = 2
98 | STATE_DATA_FIRST_PULL_DOWN = 3
99 | STATE_DATA_PULL_UP = 4
100 | STATE_DATA_PULL_DOWN = 5
101 |
102 | state = STATE_INIT_PULL_DOWN
103 |
104 | lengths = [] # will contain the lengths of data pull up periods
105 | current_length = 0 # will contain the length of the previous period
106 |
107 | for i in range(len(data)):
108 |
109 | current = data[i]
110 | current_length += 1
111 |
112 | if state == STATE_INIT_PULL_DOWN:
113 | if current == RPi.GPIO.LOW:
114 | # ok, we got the initial pull down
115 | state = STATE_INIT_PULL_UP
116 | continue
117 | else:
118 | continue
119 | if state == STATE_INIT_PULL_UP:
120 | if current == RPi.GPIO.HIGH:
121 | # ok, we got the initial pull up
122 | state = STATE_DATA_FIRST_PULL_DOWN
123 | continue
124 | else:
125 | continue
126 | if state == STATE_DATA_FIRST_PULL_DOWN:
127 | if current == RPi.GPIO.LOW:
128 | # we have the initial pull down, the next will be the data pull up
129 | state = STATE_DATA_PULL_UP
130 | continue
131 | else:
132 | continue
133 | if state == STATE_DATA_PULL_UP:
134 | if current == RPi.GPIO.HIGH:
135 | # data pulled up, the length of this pull up will determine whether it is 0 or 1
136 | current_length = 0
137 | state = STATE_DATA_PULL_DOWN
138 | continue
139 | else:
140 | continue
141 | if state == STATE_DATA_PULL_DOWN:
142 | if current == RPi.GPIO.LOW:
143 | # pulled down, we store the length of the previous pull up period
144 | lengths.append(current_length)
145 | state = STATE_DATA_PULL_UP
146 | continue
147 | else:
148 | continue
149 |
150 | return lengths
151 |
152 | def __calculate_bits(self, pull_up_lengths):
153 | # find shortest and longest period
154 | shortest_pull_up = 1000
155 | longest_pull_up = 0
156 |
157 | for i in range(0, len(pull_up_lengths)):
158 | length = pull_up_lengths[i]
159 | if length < shortest_pull_up:
160 | shortest_pull_up = length
161 | if length > longest_pull_up:
162 | longest_pull_up = length
163 |
164 | # use the halfway to determine whether the period it is long or short
165 | halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
166 | bits = []
167 |
168 | for i in range(0, len(pull_up_lengths)):
169 | bit = False
170 | if pull_up_lengths[i] > halfway:
171 | bit = True
172 | bits.append(bit)
173 |
174 | return bits
175 |
176 | def __bits_to_bytes(self, bits):
177 | the_bytes = []
178 | byte = 0
179 |
180 | for i in range(0, len(bits)):
181 | byte = byte << 1
182 | if (bits[i]):
183 | byte = byte | 1
184 | else:
185 | byte = byte | 0
186 | if ((i + 1) % 8 == 0):
187 | the_bytes.append(byte)
188 | byte = 0
189 |
190 | return the_bytes
191 |
192 | def __calculate_checksum(self, the_bytes):
193 | return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
194 |
--------------------------------------------------------------------------------
/app/views.py:
--------------------------------------------------------------------------------
1 | #-*-coding:utf-8 -*-
2 |
3 | from django.shortcuts import render,redirect
4 | from django.http import HttpResponse
5 | from django.template import Template
6 | from django.contrib import auth
7 | from django.contrib.auth.decorators import login_required
8 | from django.http import JsonResponse
9 | import requests
10 | import json
11 | import time
12 | import datetime
13 | from picamera import PiCamera
14 | import RPi.GPIO as GPIO
15 | from .models import LED_FORM
16 | from hardware.led import LED
17 | from hardware.relay import Relay
18 | from hardware import dht11
19 | import os
20 | import sys
21 |
22 | #拍照功能
23 | @login_required()
24 | def take_a_photo(request):
25 | # 实例化一个相机类
26 | camera = PiCamera()
27 |
28 | #照片名由时间组成
29 | now = datetime.datetime.now()
30 | face_name = now.strftime('%Y-%m-%d-%H-%M-%S')+'.jpg'
31 |
32 | # 拍好照片的存放地址
33 | face_address = 'app/static/face/'
34 |
35 | #拍一个照片需要提供照片名和存放地址
36 | face = face_address + face_name
37 |
38 | #需要把刚拍的照片返回给ajax以更新客户端的图片,但模板的img src不需要app/后缀
39 | img = "/static/face/" + face_name
40 |
41 | #设置相机的分辨率
42 | camera.resolution = (1900,1080)
43 | #相机预览,很可惜只有接上显示屏才能预览,ssh方式无法预览
44 | camera.start_preview()
45 |
46 | #------------百度语音合成 --------------------
47 | reload(sys)
48 | # 设置系统的编码为utf-8
49 | sys.setdefaultencoding('utf-8')
50 | #百度语音合成的应用认证码
51 | access_token = '24.e424356c9a2ce71d33069d0da8802ffc.2592000.1497505014.282335-9650693'
52 | #调用语音合成需要的参数
53 | url_1 = 'http://tsn.baidu.com/text2audio'
54 | tex1 = '当前正在请求拍照'
55 | lan = 'zh'
56 | tok = access_token
57 | ctp = 1
58 | cuid = '28-D2-44-02-98-59'
59 |
60 | camera_url = '%s?tex=%s&lan=%s&tok=%s&ctp=1&cuid=%s' % (url_1,tex1,lan,tok,cuid)
61 | #使用mpg123解析语音
62 | os.system(' mpg123 "%s" ' % (camera_url))
63 | #----------相机操作--------------------
64 |
65 | #相机启动需要点时间
66 | time.sleep(2)
67 | # 拍照,把大小裁剪为1024x768,太大不适合提交给人脸比对
68 | camera.capture(face,resize=(1024,768))
69 |
70 | #关闭相机,否则http一直在连接
71 | camera.close()
72 | #返回照片名以更新客户端照片
73 | return HttpResponse(img)
74 |
75 |
76 | #人脸比对功能
77 | @login_required()
78 | def face_compare(request):
79 | #获取要人脸比对的两张图片,加上app/后缀才能在服务端找到本地图片
80 | face1 = 'app/' + request.GET['img1']
81 | face2 = 'app/' + request.GET['img2']
82 | # face++的应用api_key和api_secret
83 | api_key = 'vigklkgJlKAFaSOuRfQGNcNAPz2Jrkfk'
84 | api_secret = 'rnLgNWHIACuE6KcpWIlxf13Bc6uDpqDW'
85 | # 接入face++ 人脸比对API
86 | url = 'https://api-cn.faceplusplus.com/facepp/v3/compare?api_key=%s&api_secret=%s' % (api_key, api_secret)
87 | # 载入两个本地图片进行比对
88 | files = {
89 | 'image_file1': open(face1, 'rb'),
90 | 'image_file2': open(face2, 'rb'),
91 | }
92 |
93 | # 二进制文件,需要用post multipart/form-data的方式上传
94 | r = requests.post(url=url, files=files)
95 | #人脸比对的response,是一个json包
96 | data = r.json()
97 | #人脸数据,若识别不出人脸则为空list
98 | faces1 = data.get('faces1')
99 | faces2 = data.get('faces2')
100 | #格式化json数据,以便在html中展示
101 | JSON = json.dumps(data,indent=1)
102 |
103 | #----------------------人脸比对语音提示-----------------
104 | reload(sys)
105 | sys.setdefaultencoding('utf-8')
106 | access_token = '24.e424356c9a2ce71d33069d0da8802ffc.2592000.1497505014.282335-9650693'
107 | url_1 = 'http://tsn.baidu.com/text2audio'
108 |
109 | tex2 = '正在进行人脸比对'
110 | lan = 'zh'
111 | tok = access_token
112 | ctp = 1
113 | cuid = '28-D2-44-02-98-59'
114 |
115 | face_url = '%s?tex=%s&lan=%s&tok=%s&ctp=1&cuid=%s' % (url_1,tex2,lan,tok,cuid)
116 | os.system(' mpg123 "%s" ' % (face_url))
117 | time.sleep(1)
118 |
119 | #---------------------
120 |
121 | print(data)
122 | # 若人脸能识别出来,则face1/2是非空list
123 | if (len(faces1) != 0 ) and (len(faces2) != 0):
124 | #认证系数,[0,100],大于60则可认为是同一个人
125 | confidence = data.get('confidence')
126 | if confidence >= 60:
127 |
128 | compare_result = '很大可能是同一个人'
129 | # 若是同一个人,则门开
130 | door = Relay(11)
131 | door.open()
132 | result={ 'compare_result':compare_result,'JSON':JSON }
133 | return JsonResponse(result)
134 |
135 | elif confidence<60:
136 |
137 | compare_result = '很大可能不是同一个人'
138 | result = {'compare_result':compare_result,'JSON':JSON }
139 | return JsonResponse(result)
140 | else :
141 |
142 | compare_result = '未能检测到人脸'
143 | result = {'compare_result':compare_result,'JSON':JSON }
144 | #使用JsonResponse能更好的返回json数据
145 | return JsonResponse(result)
146 |
147 |
148 |
149 |
150 | #人脸比对的页面
151 | @login_required()
152 | def face(request):
153 | return render(request,'face.html')
154 |
155 |
156 | # 主页
157 | @login_required()
158 | def index(request):
159 |
160 | return render(request,'index.html')
161 |
162 |
163 |
164 | #查看状态
165 | @login_required()
166 | def look(request):
167 |
168 | if request.method == 'POST':
169 |
170 | # DHT11温/湿度传感器数据
171 | instance = dht11.DHT11(8)
172 | result = instance.read()
173 |
174 | if result.is_valid():
175 | temp = "%d ℃" % result.temperature
176 | hum = "%d %%" % result.humidity
177 | GPIO.cleanup(8)
178 | return JsonResponse({ 'temperature':temp,'humidity':hum })
179 | else:
180 | return HttpResponse('error')
181 |
182 |
183 |
184 | else:
185 |
186 | #读取LED开关值,id为LED_FORM表单的外键,存储的开关值的列为switch
187 | led = LED_FORM.objects.get(id=1)
188 | #把开关值传递给LED,以上下文方式提交到模板
189 | LED = led.switch
190 |
191 | # DHT11温/湿度传感器数据
192 | instance = dht11.DHT11(8)
193 | result = instance.read()
194 |
195 | temp = " %d ℃" % result.temperature
196 | hum = " %d %%" % result.humidity
197 | GPIO.cleanup(8)
198 |
199 | return render(request,'look.html',{ 'LED':LED ,'temperature':temp,'humidity':hum })
200 |
201 |
202 |
203 | #控制LED
204 | @login_required()
205 | def control(request):
206 |
207 | if request.method=="POST":
208 | ''' LED'''
209 | #读取表单提交数据
210 | state = request.POST.get('switch')
211 | #把开关数据存储到数据库
212 | p1 = LED_FORM.objects.get(id=1)
213 | #switch为存储开关值的列
214 | p1.switch=state
215 | p1.save()
216 | #实例化一个LED类
217 | led = LED(40,state)
218 | #LED.switch()为LED类的开关控制方法
219 | led.switch()
220 | response = u'灯已:'+state
221 | return HttpResponse(response)
222 |
223 | else:
224 |
225 | return render(request,'control.html')
226 |
227 | #退出
228 | def logout(request):
229 | auth.logout(request)
230 | return redirect('/login')
231 |
232 |
233 | # 登录
234 | def login(request):
235 | if request.method =='POST':
236 | username = request.POST['username']
237 | password = request.POST['password']
238 | #验证登录,正确则authenticate()返回一个User对象,错误则返回一个None类
239 | user = auth.authenticate(username=username, password=password)
240 | if user is not None:
241 | if user.is_active:
242 | auth.login(request, user)#登录用户
243 | return redirect('/',{'username':username})#使用重定向而不是render返回首页,可以避免刷新再次提交表单导致出错
244 | else:
245 | NOT_ACTIVE = '你的账户没有激活,请联系管理员'
246 | render(request,'login.html',{'NOT_ACTIVE':NOT_ACTIVE})
247 | else:
248 | ERROR = 'Typing Error'
249 | return render(request,'login.html',{'ERROR':ERROR })
250 |
251 | else:
252 | LOGIN = 'Login System'
253 | return render(request,'login.html',{'LOGIN':LOGIN})
254 |
255 |
256 |
257 |
--------------------------------------------------------------------------------
/app/static/bootstrap/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | .btn-default,
7 | .btn-primary,
8 | .btn-success,
9 | .btn-info,
10 | .btn-warning,
11 | .btn-danger {
12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
15 | }
16 | .btn-default:active,
17 | .btn-primary:active,
18 | .btn-success:active,
19 | .btn-info:active,
20 | .btn-warning:active,
21 | .btn-danger:active,
22 | .btn-default.active,
23 | .btn-primary.active,
24 | .btn-success.active,
25 | .btn-info.active,
26 | .btn-warning.active,
27 | .btn-danger.active {
28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
30 | }
31 | .btn-default.disabled,
32 | .btn-primary.disabled,
33 | .btn-success.disabled,
34 | .btn-info.disabled,
35 | .btn-warning.disabled,
36 | .btn-danger.disabled,
37 | .btn-default[disabled],
38 | .btn-primary[disabled],
39 | .btn-success[disabled],
40 | .btn-info[disabled],
41 | .btn-warning[disabled],
42 | .btn-danger[disabled],
43 | fieldset[disabled] .btn-default,
44 | fieldset[disabled] .btn-primary,
45 | fieldset[disabled] .btn-success,
46 | fieldset[disabled] .btn-info,
47 | fieldset[disabled] .btn-warning,
48 | fieldset[disabled] .btn-danger {
49 | -webkit-box-shadow: none;
50 | box-shadow: none;
51 | }
52 | .btn-default .badge,
53 | .btn-primary .badge,
54 | .btn-success .badge,
55 | .btn-info .badge,
56 | .btn-warning .badge,
57 | .btn-danger .badge {
58 | text-shadow: none;
59 | }
60 | .btn:active,
61 | .btn.active {
62 | background-image: none;
63 | }
64 | .btn-default {
65 | text-shadow: 0 1px 0 #fff;
66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
72 | background-repeat: repeat-x;
73 | border-color: #dbdbdb;
74 | border-color: #ccc;
75 | }
76 | .btn-default:hover,
77 | .btn-default:focus {
78 | background-color: #e0e0e0;
79 | background-position: 0 -15px;
80 | }
81 | .btn-default:active,
82 | .btn-default.active {
83 | background-color: #e0e0e0;
84 | border-color: #dbdbdb;
85 | }
86 | .btn-default.disabled,
87 | .btn-default[disabled],
88 | fieldset[disabled] .btn-default,
89 | .btn-default.disabled:hover,
90 | .btn-default[disabled]:hover,
91 | fieldset[disabled] .btn-default:hover,
92 | .btn-default.disabled:focus,
93 | .btn-default[disabled]:focus,
94 | fieldset[disabled] .btn-default:focus,
95 | .btn-default.disabled.focus,
96 | .btn-default[disabled].focus,
97 | fieldset[disabled] .btn-default.focus,
98 | .btn-default.disabled:active,
99 | .btn-default[disabled]:active,
100 | fieldset[disabled] .btn-default:active,
101 | .btn-default.disabled.active,
102 | .btn-default[disabled].active,
103 | fieldset[disabled] .btn-default.active {
104 | background-color: #e0e0e0;
105 | background-image: none;
106 | }
107 | .btn-primary {
108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
114 | background-repeat: repeat-x;
115 | border-color: #245580;
116 | }
117 | .btn-primary:hover,
118 | .btn-primary:focus {
119 | background-color: #265a88;
120 | background-position: 0 -15px;
121 | }
122 | .btn-primary:active,
123 | .btn-primary.active {
124 | background-color: #265a88;
125 | border-color: #245580;
126 | }
127 | .btn-primary.disabled,
128 | .btn-primary[disabled],
129 | fieldset[disabled] .btn-primary,
130 | .btn-primary.disabled:hover,
131 | .btn-primary[disabled]:hover,
132 | fieldset[disabled] .btn-primary:hover,
133 | .btn-primary.disabled:focus,
134 | .btn-primary[disabled]:focus,
135 | fieldset[disabled] .btn-primary:focus,
136 | .btn-primary.disabled.focus,
137 | .btn-primary[disabled].focus,
138 | fieldset[disabled] .btn-primary.focus,
139 | .btn-primary.disabled:active,
140 | .btn-primary[disabled]:active,
141 | fieldset[disabled] .btn-primary:active,
142 | .btn-primary.disabled.active,
143 | .btn-primary[disabled].active,
144 | fieldset[disabled] .btn-primary.active {
145 | background-color: #265a88;
146 | background-image: none;
147 | }
148 | .btn-success {
149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
155 | background-repeat: repeat-x;
156 | border-color: #3e8f3e;
157 | }
158 | .btn-success:hover,
159 | .btn-success:focus {
160 | background-color: #419641;
161 | background-position: 0 -15px;
162 | }
163 | .btn-success:active,
164 | .btn-success.active {
165 | background-color: #419641;
166 | border-color: #3e8f3e;
167 | }
168 | .btn-success.disabled,
169 | .btn-success[disabled],
170 | fieldset[disabled] .btn-success,
171 | .btn-success.disabled:hover,
172 | .btn-success[disabled]:hover,
173 | fieldset[disabled] .btn-success:hover,
174 | .btn-success.disabled:focus,
175 | .btn-success[disabled]:focus,
176 | fieldset[disabled] .btn-success:focus,
177 | .btn-success.disabled.focus,
178 | .btn-success[disabled].focus,
179 | fieldset[disabled] .btn-success.focus,
180 | .btn-success.disabled:active,
181 | .btn-success[disabled]:active,
182 | fieldset[disabled] .btn-success:active,
183 | .btn-success.disabled.active,
184 | .btn-success[disabled].active,
185 | fieldset[disabled] .btn-success.active {
186 | background-color: #419641;
187 | background-image: none;
188 | }
189 | .btn-info {
190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
196 | background-repeat: repeat-x;
197 | border-color: #28a4c9;
198 | }
199 | .btn-info:hover,
200 | .btn-info:focus {
201 | background-color: #2aabd2;
202 | background-position: 0 -15px;
203 | }
204 | .btn-info:active,
205 | .btn-info.active {
206 | background-color: #2aabd2;
207 | border-color: #28a4c9;
208 | }
209 | .btn-info.disabled,
210 | .btn-info[disabled],
211 | fieldset[disabled] .btn-info,
212 | .btn-info.disabled:hover,
213 | .btn-info[disabled]:hover,
214 | fieldset[disabled] .btn-info:hover,
215 | .btn-info.disabled:focus,
216 | .btn-info[disabled]:focus,
217 | fieldset[disabled] .btn-info:focus,
218 | .btn-info.disabled.focus,
219 | .btn-info[disabled].focus,
220 | fieldset[disabled] .btn-info.focus,
221 | .btn-info.disabled:active,
222 | .btn-info[disabled]:active,
223 | fieldset[disabled] .btn-info:active,
224 | .btn-info.disabled.active,
225 | .btn-info[disabled].active,
226 | fieldset[disabled] .btn-info.active {
227 | background-color: #2aabd2;
228 | background-image: none;
229 | }
230 | .btn-warning {
231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
237 | background-repeat: repeat-x;
238 | border-color: #e38d13;
239 | }
240 | .btn-warning:hover,
241 | .btn-warning:focus {
242 | background-color: #eb9316;
243 | background-position: 0 -15px;
244 | }
245 | .btn-warning:active,
246 | .btn-warning.active {
247 | background-color: #eb9316;
248 | border-color: #e38d13;
249 | }
250 | .btn-warning.disabled,
251 | .btn-warning[disabled],
252 | fieldset[disabled] .btn-warning,
253 | .btn-warning.disabled:hover,
254 | .btn-warning[disabled]:hover,
255 | fieldset[disabled] .btn-warning:hover,
256 | .btn-warning.disabled:focus,
257 | .btn-warning[disabled]:focus,
258 | fieldset[disabled] .btn-warning:focus,
259 | .btn-warning.disabled.focus,
260 | .btn-warning[disabled].focus,
261 | fieldset[disabled] .btn-warning.focus,
262 | .btn-warning.disabled:active,
263 | .btn-warning[disabled]:active,
264 | fieldset[disabled] .btn-warning:active,
265 | .btn-warning.disabled.active,
266 | .btn-warning[disabled].active,
267 | fieldset[disabled] .btn-warning.active {
268 | background-color: #eb9316;
269 | background-image: none;
270 | }
271 | .btn-danger {
272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
278 | background-repeat: repeat-x;
279 | border-color: #b92c28;
280 | }
281 | .btn-danger:hover,
282 | .btn-danger:focus {
283 | background-color: #c12e2a;
284 | background-position: 0 -15px;
285 | }
286 | .btn-danger:active,
287 | .btn-danger.active {
288 | background-color: #c12e2a;
289 | border-color: #b92c28;
290 | }
291 | .btn-danger.disabled,
292 | .btn-danger[disabled],
293 | fieldset[disabled] .btn-danger,
294 | .btn-danger.disabled:hover,
295 | .btn-danger[disabled]:hover,
296 | fieldset[disabled] .btn-danger:hover,
297 | .btn-danger.disabled:focus,
298 | .btn-danger[disabled]:focus,
299 | fieldset[disabled] .btn-danger:focus,
300 | .btn-danger.disabled.focus,
301 | .btn-danger[disabled].focus,
302 | fieldset[disabled] .btn-danger.focus,
303 | .btn-danger.disabled:active,
304 | .btn-danger[disabled]:active,
305 | fieldset[disabled] .btn-danger:active,
306 | .btn-danger.disabled.active,
307 | .btn-danger[disabled].active,
308 | fieldset[disabled] .btn-danger.active {
309 | background-color: #c12e2a;
310 | background-image: none;
311 | }
312 | .thumbnail,
313 | .img-thumbnail {
314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
316 | }
317 | .dropdown-menu > li > a:hover,
318 | .dropdown-menu > li > a:focus {
319 | background-color: #e8e8e8;
320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
325 | background-repeat: repeat-x;
326 | }
327 | .dropdown-menu > .active > a,
328 | .dropdown-menu > .active > a:hover,
329 | .dropdown-menu > .active > a:focus {
330 | background-color: #2e6da4;
331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
336 | background-repeat: repeat-x;
337 | }
338 | .navbar-default {
339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
345 | background-repeat: repeat-x;
346 | border-radius: 4px;
347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
349 | }
350 | .navbar-default .navbar-nav > .open > a,
351 | .navbar-default .navbar-nav > .active > a {
352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
357 | background-repeat: repeat-x;
358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
360 | }
361 | .navbar-brand,
362 | .navbar-nav > li > a {
363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
364 | }
365 | .navbar-inverse {
366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
372 | background-repeat: repeat-x;
373 | border-radius: 4px;
374 | }
375 | .navbar-inverse .navbar-nav > .open > a,
376 | .navbar-inverse .navbar-nav > .active > a {
377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
382 | background-repeat: repeat-x;
383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
385 | }
386 | .navbar-inverse .navbar-brand,
387 | .navbar-inverse .navbar-nav > li > a {
388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
389 | }
390 | .navbar-static-top,
391 | .navbar-fixed-top,
392 | .navbar-fixed-bottom {
393 | border-radius: 0;
394 | }
395 | @media (max-width: 767px) {
396 | .navbar .navbar-nav .open .dropdown-menu > .active > a,
397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
399 | color: #fff;
400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
405 | background-repeat: repeat-x;
406 | }
407 | }
408 | .alert {
409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
412 | }
413 | .alert-success {
414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
419 | background-repeat: repeat-x;
420 | border-color: #b2dba1;
421 | }
422 | .alert-info {
423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
428 | background-repeat: repeat-x;
429 | border-color: #9acfea;
430 | }
431 | .alert-warning {
432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
437 | background-repeat: repeat-x;
438 | border-color: #f5e79e;
439 | }
440 | .alert-danger {
441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
446 | background-repeat: repeat-x;
447 | border-color: #dca7a7;
448 | }
449 | .progress {
450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
455 | background-repeat: repeat-x;
456 | }
457 | .progress-bar {
458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
463 | background-repeat: repeat-x;
464 | }
465 | .progress-bar-success {
466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
471 | background-repeat: repeat-x;
472 | }
473 | .progress-bar-info {
474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
479 | background-repeat: repeat-x;
480 | }
481 | .progress-bar-warning {
482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
487 | background-repeat: repeat-x;
488 | }
489 | .progress-bar-danger {
490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
495 | background-repeat: repeat-x;
496 | }
497 | .progress-bar-striped {
498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
501 | }
502 | .list-group {
503 | border-radius: 4px;
504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
506 | }
507 | .list-group-item.active,
508 | .list-group-item.active:hover,
509 | .list-group-item.active:focus {
510 | text-shadow: 0 -1px 0 #286090;
511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
516 | background-repeat: repeat-x;
517 | border-color: #2b669a;
518 | }
519 | .list-group-item.active .badge,
520 | .list-group-item.active:hover .badge,
521 | .list-group-item.active:focus .badge {
522 | text-shadow: none;
523 | }
524 | .panel {
525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
527 | }
528 | .panel-default > .panel-heading {
529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
534 | background-repeat: repeat-x;
535 | }
536 | .panel-primary > .panel-heading {
537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
542 | background-repeat: repeat-x;
543 | }
544 | .panel-success > .panel-heading {
545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
550 | background-repeat: repeat-x;
551 | }
552 | .panel-info > .panel-heading {
553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
558 | background-repeat: repeat-x;
559 | }
560 | .panel-warning > .panel-heading {
561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
566 | background-repeat: repeat-x;
567 | }
568 | .panel-danger > .panel-heading {
569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
574 | background-repeat: repeat-x;
575 | }
576 | .well {
577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
582 | background-repeat: repeat-x;
583 | border-color: #dcdcdc;
584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
586 | }
587 | /*# sourceMappingURL=bootstrap-theme.css.map */
588 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | 1491659144275
272 |
273 |
274 | 1491659144275
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
--------------------------------------------------------------------------------
/app/static/bootstrap/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under the MIT license
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------