├── bt ├── __init__.py ├── wsgi.py ├── urls.py └── settings.py ├── .gitignore ├── requirements.txt ├── app ├── tests.py ├── apps.py ├── __init__.py ├── admin.py ├── forms.py ├── urls.py ├── models.py └── views.py ├── static ├── js │ ├── common-v2.js │ ├── m-detail.js │ ├── function-new.js │ ├── clipboard.min.js │ ├── webcam.min.js │ ├── layer.js │ └── webcam.js ├── img │ ├── demo01.png │ ├── demo02.png │ ├── logo.jpg │ └── logo.png └── css │ ├── layer.css │ └── style_v2.css ├── upload └── cover │ └── 2.jpg ├── templates ├── app │ ├── detail.html │ ├── demo.html │ ├── commit.html │ ├── index.html │ └── search.html └── base │ ├── form_messages.html │ ├── footer.html │ ├── form_errors.html │ └── base.html ├── manage.py ├── README.md ├── helpers.py └── 开发过程.md /bt/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | __pycache__ 3 | app/migrations 4 | bt/__pycache__/ 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==3.2.11 2 | pymysql==1.0.2 3 | django-ratelimit==1.0.1 -------------------------------------------------------------------------------- /app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /static/js/common-v2.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | $('#search').focus(); 4 | }); 5 | -------------------------------------------------------------------------------- /upload/cover/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_bt/HEAD/upload/cover/2.jpg -------------------------------------------------------------------------------- /static/img/demo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_bt/HEAD/static/img/demo01.png -------------------------------------------------------------------------------- /static/img/demo02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_bt/HEAD/static/img/demo02.png -------------------------------------------------------------------------------- /static/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_bt/HEAD/static/img/logo.jpg -------------------------------------------------------------------------------- /static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_bt/HEAD/static/img/logo.png -------------------------------------------------------------------------------- /app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AppConfig(AppConfig): 5 | name = 'app' 6 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | pymysql.install_as_MySQLdb() 3 | 4 | print("===============install pymysql==============") -------------------------------------------------------------------------------- /templates/app/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | {% endblock content %} 6 | -------------------------------------------------------------------------------- /app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from app.models import Link 5 | 6 | admin.site.register(Link) 7 | -------------------------------------------------------------------------------- /templates/base/form_messages.html: -------------------------------------------------------------------------------- 1 | {% if messages %} 2 |
3 | {% for message in messages %} 4 |
{{ message }}
5 | {% endfor %} 6 |
7 | {% endif %} -------------------------------------------------------------------------------- /app/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from app.models import Link 4 | 5 | 6 | class CommitForm(forms.ModelForm): 7 | class Meta: 8 | model = Link 9 | fields = ['url', 'title', 'cover', 'size', 'desc', 'contact'] 10 | -------------------------------------------------------------------------------- /templates/base/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | app_name = 'app' 5 | urlpatterns = [ 6 | path('index', views.IndexView.as_view(), name='index'), 7 | path('search', views.SearchView.as_view(), name='search'), 8 | # path('detail/', views.DetailView.as_view(), name='detail'), 9 | path('commit', views.CommitView.as_view(), name='commit'), 10 | path('demo', views.DemoView.as_view(), name='demo'), 11 | ] -------------------------------------------------------------------------------- /bt/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for bt 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/2.1/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', 'bt.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /templates/base/form_errors.html: -------------------------------------------------------------------------------- 1 | 2 | {% if form.errors %} 3 |
4 | 15 |
16 | 17 | {% endif %} -------------------------------------------------------------------------------- /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', 'bt.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 一直想做一款管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。现将部分源码开源,如需完整源码,可以联系客服微信购买:lengqin1024 2 | 3 | #### 演示地址 4 | 5 | http://bt.gitapp.cn 6 | 7 | #### 项目简介 8 | 9 | 该项目是基于python的web类库django开发的一套web网站,给同学做的课程作业。 10 | 本人的研究方向是一项关于搜索的研究项目。在该项目中,笔者开发了一个简单版的搜索网站,实现了对数据库数据的检索和更新。通过开发该项目,笔者学习和巩固了python的相关技术和框架。 11 | 12 | #### 运行步骤 13 | 14 | 1. 下载代码后,进入到bt目录下 15 | 2. 新建数据库,并修改settings.py中的数据库配置 16 | 3. 移植sql数据,执行 `python manage.py migrate` 17 | 4. 运行项目,执行 `python manage.py runserver` 18 | 19 | **首页展示** 20 | 21 | ![](https://github.com/geeeeeeeek/bt/blob/master/static/img/demo01.png) 22 | 23 | 24 | **详情页** 25 | 26 | ![](https://github.com/geeeeeeeek/bt/blob/master/static/img/demo02.png) 27 | 28 | 29 | #### 付费咨询 30 | 31 | 微信: lengqin1024 32 | -------------------------------------------------------------------------------- /helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | 5 | def get_page_list(paginator, page): 6 | 7 | """ 8 | 分页逻辑 9 | if 页数>=10: 10 | 当前页<=5时,起始页为1 11 | 当前页>(总页数-5)时,起始页为(总页数-9) 12 | 其他情况 起始页为(当前页-5) 13 | """ 14 | 15 | page_list = [] 16 | 17 | if paginator.num_pages > 10: 18 | if page.number <= 5: 19 | start_page = 1 20 | elif page.number > paginator.num_pages - 5: 21 | start_page = paginator.num_pages - 9 22 | else: 23 | start_page = page.number - 5 24 | 25 | for i in range(start_page, start_page + 10): 26 | page_list.append(i) 27 | else: 28 | for i in range(1, paginator.num_pages + 1): 29 | page_list.append(i) 30 | 31 | return page_list -------------------------------------------------------------------------------- /bt/urls.py: -------------------------------------------------------------------------------- 1 | """bt URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/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: path('', 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: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls.static import static 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | 20 | from app import views 21 | from bt import settings 22 | 23 | urlpatterns = [ 24 | path('admin/', admin.site.urls), 25 | path('app/', include('app.urls')), 26 | path('', views.IndexView.as_view(), name='home'), # 默认首页 27 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 28 | -------------------------------------------------------------------------------- /templates/base/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 电影链接搜索网站 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% block content %} 27 | {% endblock content %} 28 | 29 | {% include "base/footer.html" %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /static/js/m-detail.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function ajaxp(a) { 4 | var b = { 5 | id: mid, 6 | action: a 7 | }; 8 | $.post("/Magnet/ajaxp", b, function(a) { 9 | "true" == a ? "good" == b.action && (alert("操作成功"), a = $("#goodnum").html(), a++, $("#goodnum").html(a)) : alert("您不能重复操作") 10 | }) 11 | } 12 | 13 | function report() { 14 | layer.open({ 15 | type: 1, 16 | btn: ['确认', '取消'], 17 | title: '违规举报', 18 | anim: 1, 19 | yes: function(index, layero) { 20 | $.post("/Magnet/report", { 21 | id: mid, 22 | verify: $('#vcode').val() 23 | }, function(a) { 24 | layer.closeAll(); 25 | if ("error" == a) { 26 | alert("验证码错误"); 27 | } else { 28 | alert("操作成功,此链接已被屏蔽"); 29 | window.location.reload(); 30 | } 31 | }) 32 | }, 33 | content: '
' 34 | }); 35 | } 36 | 37 | function get_hits() { 38 | $.get("/Hits/show/id/" + mid, function(a) { 39 | $("#hits_num").text(a) 40 | }) 41 | } 42 | $(function() { 43 | var a = new ClipboardJS("#copyi"); 44 | a.on("success", function(a) { 45 | alert("复制成功!"); 46 | a.clearSelection() 47 | }); 48 | a.on("error", function(a) { 49 | alert("复制失败!") 50 | }); 51 | setTimeout("get_hits()", 120000) 52 | }); -------------------------------------------------------------------------------- /templates/app/demo.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | WebcamJS Test Page 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | from django.db.models import Q 5 | 6 | 7 | class LinkQuerySet(models.query.QuerySet): 8 | 9 | def get_search_list(self, q): 10 | if q: 11 | return self.filter(status=True).filter(Q(title__contains=q)).order_by('-timestamp') 12 | else: 13 | return self.filter(status=True).order_by('-timestamp') 14 | 15 | def get_recommend_list(self): 16 | return self.filter(status=True).order_by('-hot')[:4] 17 | 18 | def get_fake_list(self): 19 | return self.filter(status=True).order_by('-hot')[:] 20 | 21 | 22 | class Link(models.Model): 23 | list_display = ("url", "desc", "contact") 24 | url = models.CharField(max_length=100, blank=True, null=True) 25 | title = models.CharField(max_length=100, blank=True, null=True) 26 | cover = models.ImageField(upload_to='cover/', null=True) 27 | size = models.CharField(max_length=100, blank=True, null=True) 28 | hot = models.IntegerField(default=0) 29 | desc = models.CharField(max_length=200, blank=True, null=True) 30 | contact = models.CharField(max_length=100, blank=True, null=True) 31 | status = models.BooleanField(default=False) 32 | timestamp = models.DateTimeField(auto_now_add=True, null=True) 33 | objects = LinkQuerySet.as_manager() 34 | 35 | def __str__(self): 36 | return self.title 37 | 38 | class Meta: 39 | db_table = "bt_link" 40 | 41 | def increase_hot_count(self): 42 | self.hot += 1 43 | self.save(update_fields=['hot']) 44 | -------------------------------------------------------------------------------- /templates/app/commit.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 | 10 | 首页 11 | 12 | 提交链接 13 |
14 |
15 |
16 |
17 | {% csrf_token %} 18 |
19 |

请提交合法的链接

20 | 21 | 22 | 封面上传   23 | 24 | 25 | 26 | 27 |
28 | 29 | {% include "base/form_errors.html" %} 30 | {% include "base/form_messages.html" %} 31 |
32 |
33 |
34 |
35 |
36 | 37 | {% endblock content %} 38 | 39 | -------------------------------------------------------------------------------- /templates/app/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 | 10 | 首页 11 | 12 | 提交链接 13 |
14 |
15 |
16 |
17 | 21 | 42 |
43 |
44 |
45 |
46 |
47 | {% endblock content %} 48 | 49 | -------------------------------------------------------------------------------- /static/js/function-new.js: -------------------------------------------------------------------------------- 1 | eval(function(p, a, c, k, e, r) { 2 | e = function(c) { 3 | return (c < a ? '': e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) 4 | }; 5 | if (!''.replace(/^/, String)) { 6 | while (c--) r[e(c)] = k[c] || e(c); 7 | k = [function(e) { 8 | return r[e] 9 | }]; 10 | e = function() { 11 | return '\\w+' 12 | }; 13 | c = 1 14 | }; 15 | while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); 16 | return p 17 | } ('u.5=4(c,b,a){6(1<21.s&&"[1Y 1X]"!==F(b)){a=u.1W({},a);6(I===b||1S 0===b)a.2=-1;6("1O"===1N a.2){k e=a.2,d=a.2=H 1M;d.1A(d.1q()+e)}b=F(b);3 8.5=[l(c),"=",a.y?b:l(b),a.2?"; 2="+a.2.1p():"",a.9?"; 9="+a.9:"",a.o?"; o="+a.o:"",a.C?"; C":""].1o("")}a=b||{};d=a.y?4(a){3 a}:1k;3(e=(H 1g("(?:^|; )"+l(c)+"=([^;]*)")).14(8.5))?d(e[1]):I};k 11={S:4(){3!1}},g=1r.P,M=$.5("N");4 t(c){3 K.Q(K.R()*(c-1))}4 j(){3 T.U.V(/(W|X|Y|Z|10)/i)}4 r(){3 1}r()&&$(".12").13(4(){k c=$("#22-15").16("18").19("1a:?1b=1c:1d:","");1e.1f("p://1h.1i.1j?h="+c);E("\\1l\\1m\\1n\\D\\m\\L\\O\\1s");3!1});1==M||j()||(E("\\1t\\1u\\1v\\1w\\1x\\1y\\1z\\m\\L\\J\\1B\\1C\\1D\\1E\\J\\D\\1F\\1G\\m\\1H\\1I\\1J\\1K\\1L"),$.5("N","1",{9:"/",2:1}));j()&&"/"!=g&&r()&&(q=["<7 z=\'p://f.1P.w/17/1Q.1R\'>\\v/7>","<7 z=\'p://1T.1U.w/1V\'>\\v/7>"],G=t(q.s),8.B(q[G]));6((-1 7 |
8 | 首页 9 | 提交链接 10 |
11 |
12 |
13 | 14 | 15 | 17 | 18 |
19 |
20 |
21 | 时间 22 | 热度 23 | 大小 24 |
25 |
26 |
27 |
28 | 大约{{record_count}}条结果,耗时 {{duration}} 秒。 29 |
30 |
31 |
32 | 33 | {% for item in link_list %} 34 |
35 |
36 |

37 | 38 | {{item.title}} 39 | 40 |

41 |
42 |
43 | {{item.desc}} 44 |
45 |
46 | 视频 47 | 创建时间: {{item.timestamp}} 48 | {{item.size}} 49 | 视频热度:{{item.hot}} 50 |
51 |
52 | {% empty %} 53 |

暂无搜索结果

54 | {% endfor %} 55 | 56 | 57 | {% if is_paginated %} 58 |
    59 | {% if page_obj.has_previous %} 60 |
  • « 61 |
  • 62 | {% endif %} 63 | {% for i in page_list %} 64 | {% if page_obj.number == i %} 65 |
  • {{ i }}
  • 66 | {% else %} 67 |
  • {{ i }}
  • 68 | {% endif %} 69 | {% endfor %} 70 | {% if page_obj.has_next %} 71 |
  • » 73 |
  • 74 | {% endif %} 75 |
76 | {% endif %} 77 | 78 |
79 | 80 |
81 |
82 |
83 | 本站主要用于演示软件项目,不以盈利为目的. 本站没有储存任何数据,亦不对结果承担任何责任!本站已经屏蔽了99%的非法链接,如发现非法,侵权的内容请在线举报,谢谢合作! 84 |
85 | 88 |
89 |
90 |
91 |
92 | 93 | {% endblock content %} 94 | -------------------------------------------------------------------------------- /开发过程.md: -------------------------------------------------------------------------------- 1 | ## 项目开发过程 2 | 3 | ### 项目简介 4 | 该项目是基于python的web类库django开发的一套web网站,做为本人的课程作业。 5 | 本人的研究方向是一项关于搜索的研究项目。在该项目中,笔者开发了一个简单版的搜索网站,实现了对数据库数据的检索和更新。 6 | 7 | ### 启动项目 8 | ``` 9 | django-admin startproject bt 10 | ``` 11 | ### 创建应用 12 | ``` 13 | python3 manage.py startapp app 14 | ``` 15 | 16 | ### model设计 17 | 主要是对提交的链接进行设计,在此项目中,我们需要展示链接的名称、url、联系人、链接简介等字段。 18 | 19 | 设计字段如下: 20 | ```python 21 | class Link(models.Model): 22 | list_display = ("url","desc","contact") 23 | url = models.CharField(max_length=100,blank=True, null=True) 24 | title = models.CharField(max_length=100,blank=True, null=True) 25 | size = models.CharField(max_length=100,blank=True, null=True) 26 | hot = models.IntegerField(default=0) 27 | desc = models.CharField(max_length=200,blank=True, null=True) 28 | contact = models.CharField(max_length=100,blank=True, null=True) 29 | status = models.BooleanField(default=False) 30 | timestamp = models.DateTimeField(auto_now_add=True, null=True) 31 | objects = LinkQuerySet.as_manager() 32 | ``` 33 | 34 | ### 业务编写 35 | 36 | 本项目一共分为4个页面,分别是首页、搜索列表页、详情页、链接提交页。 37 | 38 | 我们一一讲解 39 | 40 | #### 首页 41 | 42 | 首先是首页,它的模版位于templates/app/index.html 它主要是用来展示首页内容, 并提交搜索词,到搜索接口,所有的接口都位于app/urls.py里面,如下 43 | ```python 44 | app_name = 'app' 45 | urlpatterns = [ 46 | path('index', views.IndexView.as_view(), name='index'), 47 | path('search', views.SearchView.as_view(), name='search'), 48 | path('detail/', views.DetailView.as_view(), name='detail'), 49 | path('commit', views.CommitView.as_view(), name='commit'), 50 | ] 51 | ``` 52 | 53 | 我们设置首页的路由为IndexView, 开始编写IndexView的代码。它的代码非常简单: 54 | ```python 55 | class IndexView(generic.TemplateView): 56 | template_name = 'app/index.html' 57 | ``` 58 | 仅仅是展示了首页页面,首页将搜索词交给了search来处理,这一点,我们从index.html关于form的代码中可以看到, 提交给了url 'app:search' 59 | ``` 60 |
61 | 62 | 63 |
64 | ``` 65 | 66 | #### 列表展示页 67 | 从urls.py中可知,app:search指向了SearchView,这个类是本项目的核心代码,它实现了搜索的全过程。 68 | 69 | ```python 70 | class SearchView(generic.ListView): 71 | model = Link 72 | template_name = 'app/search.html' 73 | context_object_name = 'link_list' 74 | paginate_by = 10 75 | q = '' # 搜索词 76 | duration = 0 # 耗时 77 | record_count = 0 78 | 79 | def get_context_data(self, *, object_list=None, **kwargs): 80 | context = super(SearchView, self).get_context_data(**kwargs) 81 | paginator = context.get('paginator') 82 | page = context.get('page_obj') 83 | page_list = get_page_list(paginator, page) 84 | context['page_list'] = page_list 85 | context['q'] = self.q 86 | context['duration'] = round(self.duration,6) 87 | context['record_count'] = self.record_count 88 | return context 89 | 90 | def get_queryset(self): 91 | start = time.time() 92 | self.q = self.request.GET.get("q", "") 93 | search_list = Link.objects.get_search_list(self.q) 94 | # 如搜索为空,则放假数据 95 | if len(search_list) <= 0: 96 | search_list = Link.objects.get_fake_list() 97 | end = time.time() 98 | self.duration = end - start 99 | self.record_count = len(search_list) 100 | return search_list 101 | ``` 102 | 继承了ListView通用类,通过get_queryset()回调函数来实现搜索功能,并通过get_context_data来传递额外的数据给前端。即是列表展示页。 103 | 104 | #### 详情页 105 | 106 | 我们再来开发详情页,从urls.py中看到,详情页是由DetailView来实现的,我们来窥探它的全貌: 107 | ```python 108 | class DetailView(generic.DetailView): 109 | model = Link 110 | template_name = 'app/detail.html' 111 | 112 | def get_object(self, queryset=None): 113 | obj = super().get_object() 114 | obj.increase_hot_count() 115 | return obj 116 | 117 | def get_context_data(self, **kwargs): 118 | context = super(DetailView, self).get_context_data(**kwargs) 119 | recommend_list = Link.objects.get_recommend_list() 120 | context['recommend_list'] = recommend_list 121 | return context 122 | ``` 123 | 它很简单,继承了DetailView通用模板类来显示详情。 124 | 125 | #### 链接提交页 126 | 127 | 最后再来看一下链接提交页,它是由CommitView来实现的。同样是观看代码: 128 | ```python 129 | class CommitView(generic.CreateView): 130 | 131 | model = Link 132 | form_class = CommitForm 133 | template_name = 'app/commit.html' 134 | 135 | @ratelimit(key='ip', rate='2/m') 136 | def post(self, request, *args, **kwargs): 137 | was_limited = getattr(request, 'limited', False) 138 | if was_limited: 139 | messages.warning(self.request, "操作太频繁了,请1分钟后再试") 140 | return render(request, 'app/commit.html', {'form': CommitForm()}) 141 | return super().post(request, *args, **kwargs) 142 | 143 | def get_success_url(self): 144 | messages.success(self.request, "提交成功! 审核期3个工作日。") 145 | return reverse('app:commit') 146 | ``` 147 | 它是继承自CreateView,因为是创建操作嘛,在post中,我们通过ratelimit来限制提交次数。 148 | 149 | ### 运行项目 150 | ``` 151 | python3 manage.py runserver 152 | ``` 153 | 154 | -------------------------------------------------------------------------------- /static/js/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.0 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); -------------------------------------------------------------------------------- /static/css/layer.css: -------------------------------------------------------------------------------- 1 | .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #b2b2b2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#f8f8f8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2e2d3c;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1e9fff;background-color:#1e9fff;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8d8d8d;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #d3d4d3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #e9e7e7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#e9e7e7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} -------------------------------------------------------------------------------- /static/js/webcam.min.js: -------------------------------------------------------------------------------- 1 | // WebcamJS v1.0.25 - http://github.com/jhuckaby/webcamjs - MIT Licensed 2 | (function(e){var t;function a(){var e=Error.apply(this,arguments);e.name=this.name="FlashError";this.stack=e.stack;this.message=e.message}function i(){var e=Error.apply(this,arguments);e.name=this.name="WebcamError";this.stack=e.stack;this.message=e.message}var s=function(){};s.prototype=Error.prototype;a.prototype=new s;i.prototype=new s;var Webcam={version:"1.0.25",protocol:location.protocol.match(/https/i)?"https":"http",loaded:false,live:false,userMedia:true,iOS:/iPad|iPhone|iPod/.test(navigator.userAgent)&&!e.MSStream,params:{width:0,height:0,dest_width:0,dest_height:0,image_format:"jpeg",jpeg_quality:90,enable_flash:true,force_flash:false,flip_horiz:false,fps:30,upload_name:"webcam",constraints:null,swfURL:"",flashNotDetectedText:"ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).",noInterfaceFoundText:"No supported webcam interface found.",unfreeze_snap:true,iosPlaceholderText:"Click here to open camera.",user_callback:null,user_canvas:null},errors:{FlashError:a,WebcamError:i},hooks:{},init:function(){var t=this;this.mediaDevices=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices:navigator.mozGetUserMedia||navigator.webkitGetUserMedia?{getUserMedia:function(e){return new Promise(function(t,a){(navigator.mozGetUserMedia||navigator.webkitGetUserMedia).call(navigator,e,t,a)})}}:null;e.URL=e.URL||e.webkitURL||e.mozURL||e.msURL;this.userMedia=this.userMedia&&!!this.mediaDevices&&!!e.URL;if(this.iOS){this.userMedia=null}if(navigator.userAgent.match(/Firefox\D+(\d+)/)){if(parseInt(RegExp.$1,10)<21)this.userMedia=null}if(this.userMedia){e.addEventListener("beforeunload",function(e){t.reset()})}},exifOrientation:function(e){var t=new DataView(e);if(t.getUint8(0)!=255||t.getUint8(1)!=216){console.log("Not a valid JPEG file");return 0}var a=2;var i=null;while(a8){console.log("Invalid EXIF orientation value ("+p+")");return 0}return p}}}else{a+=2+t.getUint16(a+2)}}return 0},fixOrientation:function(e,t,a){var i=new Image;i.addEventListener("load",function(e){var s=document.createElement("canvas");var r=s.getContext("2d");if(t<5){s.width=i.width;s.height=i.height}else{s.width=i.height;s.height=i.width}switch(t){case 2:r.transform(-1,0,0,1,i.width,0);break;case 3:r.transform(-1,0,0,-1,i.width,i.height);break;case 4:r.transform(1,0,0,-1,0,i.height);break;case 5:r.transform(0,1,1,0,0,0);break;case 6:r.transform(0,1,-1,0,i.height,0);break;case 7:r.transform(0,-1,-1,0,i.height,i.width);break;case 8:r.transform(0,-1,1,0,0,i.width);break}r.drawImage(i,0,0);a.src=s.toDataURL()},false);i.src=e},attach:function(a){if(typeof a=="string"){a=document.getElementById(a)||document.querySelector(a)}if(!a){return this.dispatch("error",new i("Could not locate DOM element to attach to."))}this.container=a;a.innerHTML="";var s=document.createElement("div");a.appendChild(s);this.peg=s;if(!this.params.width)this.params.width=a.offsetWidth;if(!this.params.height)this.params.height=a.offsetHeight;if(!this.params.width||!this.params.height){return this.dispatch("error",new i("No width and/or height for webcam. Please call set() first, or attach to a visible element."))}if(!this.params.dest_width)this.params.dest_width=this.params.width;if(!this.params.dest_height)this.params.dest_height=this.params.height;this.userMedia=t===undefined?this.userMedia:t;if(this.params.force_flash){t=this.userMedia;this.userMedia=null}if(typeof this.params.fps!=="number")this.params.fps=30;var r=this.params.width/this.params.dest_width;var o=this.params.height/this.params.dest_height;if(this.userMedia){var n=document.createElement("video");n.setAttribute("autoplay","autoplay");n.style.width=""+this.params.dest_width+"px";n.style.height=""+this.params.dest_height+"px";if(r!=1||o!=1){a.style.overflow="hidden";n.style.webkitTransformOrigin="0px 0px";n.style.mozTransformOrigin="0px 0px";n.style.msTransformOrigin="0px 0px";n.style.oTransformOrigin="0px 0px";n.style.transformOrigin="0px 0px";n.style.webkitTransform="scaleX("+r+") scaleY("+o+")";n.style.mozTransform="scaleX("+r+") scaleY("+o+")";n.style.msTransform="scaleX("+r+") scaleY("+o+")";n.style.oTransform="scaleX("+r+") scaleY("+o+")";n.style.transform="scaleX("+r+") scaleY("+o+")"}a.appendChild(n);this.video=n;var l=this;this.mediaDevices.getUserMedia({audio:false,video:this.params.constraints||{mandatory:{minWidth:this.params.dest_width,minHeight:this.params.dest_height}}}).then(function(t){n.onloadedmetadata=function(e){l.stream=t;l.loaded=true;l.live=true;l.dispatch("load");l.dispatch("live");l.flip()};if("srcObject"in n){n.srcObject=t}else{n.src=e.URL.createObjectURL(t)}}).catch(function(e){if(l.params.enable_flash&&l.detectFlash()){setTimeout(function(){l.params.force_flash=1;l.attach(a)},1)}else{l.dispatch("error",e)}})}else if(this.iOS){var h=document.createElement("div");h.id=this.container.id+"-ios_div";h.className="webcamjs-ios-placeholder";h.style.width=""+this.params.width+"px";h.style.height=""+this.params.height+"px";h.style.textAlign="center";h.style.display="table-cell";h.style.verticalAlign="middle";h.style.backgroundRepeat="no-repeat";h.style.backgroundSize="contain";h.style.backgroundPosition="center";var c=document.createElement("span");c.className="webcamjs-ios-text";c.innerHTML=this.params.iosPlaceholderText;h.appendChild(c);var d=document.createElement("img");d.id=this.container.id+"-ios_img";d.style.width=""+this.params.dest_width+"px";d.style.height=""+this.params.dest_height+"px";d.style.display="none";h.appendChild(d);var f=document.createElement("input");f.id=this.container.id+"-ios_input";f.setAttribute("type","file");f.setAttribute("accept","image/*");f.setAttribute("capture","camera");var l=this;var m=this.params;f.addEventListener("change",function(e){if(e.target.files.length>0&&e.target.files[0].type.indexOf("image/")==0){var t=URL.createObjectURL(e.target.files[0]);var a=new Image;a.addEventListener("load",function(e){var t=document.createElement("canvas");t.width=m.dest_width;t.height=m.dest_height;var i=t.getContext("2d");ratio=Math.min(a.width/m.dest_width,a.height/m.dest_height);var s=m.dest_width*ratio;var r=m.dest_height*ratio;var o=(a.width-s)/2;var n=(a.height-r)/2;i.drawImage(a,o,n,s,r,0,0,m.dest_width,m.dest_height);var l=t.toDataURL();d.src=l;h.style.backgroundImage="url('"+l+"')"},false);var i=new FileReader;i.addEventListener("load",function(e){var i=l.exifOrientation(e.target.result);if(i>1){l.fixOrientation(t,i,a)}else{a.src=t}},false);var s=new XMLHttpRequest;s.open("GET",t,true);s.responseType="blob";s.onload=function(e){if(this.status==200||this.status===0){i.readAsArrayBuffer(this.response)}};s.send()}},false);f.style.display="none";a.appendChild(f);h.addEventListener("click",function(e){if(m.user_callback){l.snap(m.user_callback,m.user_canvas)}else{f.style.display="block";f.focus();f.click();f.style.display="none"}},false);a.appendChild(h);this.loaded=true;this.live=true}else if(this.params.enable_flash&&this.detectFlash()){e.Webcam=Webcam;var h=document.createElement("div");h.innerHTML=this.getSWFHTML();a.appendChild(h)}else{this.dispatch("error",new i(this.params.noInterfaceFoundText))}if(this.params.crop_width&&this.params.crop_height){var p=Math.floor(this.params.crop_width*r);var u=Math.floor(this.params.crop_height*o);a.style.width=""+p+"px";a.style.height=""+u+"px";a.style.overflow="hidden";a.scrollLeft=Math.floor(this.params.width/2-p/2);a.scrollTop=Math.floor(this.params.height/2-u/2)}else{a.style.width=""+this.params.width+"px";a.style.height=""+this.params.height+"px"}},reset:function(){if(this.preview_active)this.unfreeze();this.unflip();if(this.userMedia){if(this.stream){if(this.stream.getVideoTracks){var e=this.stream.getVideoTracks();if(e&&e[0]&&e[0].stop)e[0].stop()}else if(this.stream.stop){this.stream.stop()}}delete this.stream;delete this.video}if(this.userMedia!==true&&this.loaded&&!this.iOS){var t=this.getMovie();if(t&&t._releaseCamera)t._releaseCamera()}if(this.container){this.container.innerHTML="";delete this.container}this.loaded=false;this.live=false},set:function(){if(arguments.length==1){for(var e in arguments[0]){this.params[e]=arguments[0][e]}}else{this.params[arguments[0]]=arguments[1]}},on:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(!this.hooks[e])this.hooks[e]=[];this.hooks[e].push(t)},off:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(this.hooks[e]){if(t){var a=this.hooks[e].indexOf(t);if(a>-1)this.hooks[e].splice(a,1)}else{this.hooks[e]=[]}}},dispatch:function(){var t=arguments[0].replace(/^on/i,"").toLowerCase();var s=Array.prototype.slice.call(arguments,1);if(this.hooks[t]&&this.hooks[t].length){for(var r=0,o=this.hooks[t].length;rERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.'}if(!this.detectFlash()){this.dispatch("error",new a("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."));return'

'+this.params.flashNotDetectedText+"

"}if(!i){var s="";var r=document.getElementsByTagName("script");for(var o=0,n=r.length;o';return t},getMovie:function(){if(!this.loaded)return this.dispatch("error",new a("Flash Movie is not loaded yet"));var e=document.getElementById("webcam_movie_obj");if(!e||!e._snap)e=document.getElementById("webcam_movie_embed");if(!e)this.dispatch("error",new a("Cannot locate Flash movie in DOM"));return e},freeze:function(){var e=this;var t=this.params;if(this.preview_active)this.unfreeze();var a=this.params.width/this.params.dest_width;var i=this.params.height/this.params.dest_height;this.unflip();var s=t.crop_width||t.dest_width;var r=t.crop_height||t.dest_height;var o=document.createElement("canvas");o.width=s;o.height=r;var n=o.getContext("2d");this.preview_canvas=o;this.preview_context=n;if(a!=1||i!=1){o.style.webkitTransformOrigin="0px 0px";o.style.mozTransformOrigin="0px 0px";o.style.msTransformOrigin="0px 0px";o.style.oTransformOrigin="0px 0px";o.style.transformOrigin="0px 0px";o.style.webkitTransform="scaleX("+a+") scaleY("+i+")";o.style.mozTransform="scaleX("+a+") scaleY("+i+")";o.style.msTransform="scaleX("+a+") scaleY("+i+")";o.style.oTransform="scaleX("+a+") scaleY("+i+")";o.style.transform="scaleX("+a+") scaleY("+i+")"}this.snap(function(){o.style.position="relative";o.style.left=""+e.container.scrollLeft+"px";o.style.top=""+e.container.scrollTop+"px";e.container.insertBefore(o,e.peg);e.container.style.overflow="hidden";e.preview_active=true},o)},unfreeze:function(){if(this.preview_active){this.container.removeChild(this.preview_canvas);delete this.preview_context;delete this.preview_canvas;this.preview_active=false;this.flip()}},flip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(-1)";e.mozTransform="scaleX(-1)";e.msTransform="scaleX(-1)";e.oTransform="scaleX(-1)";e.transform="scaleX(-1)";e.filter="FlipH";e.msFilter="FlipH"}},unflip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(1)";e.mozTransform="scaleX(1)";e.msTransform="scaleX(1)";e.oTransform="scaleX(1)";e.transform="scaleX(1)";e.filter="";e.msFilter=""}},savePreview:function(e,t){var a=this.params;var i=this.preview_canvas;var s=this.preview_context;if(t){var r=t.getContext("2d");r.drawImage(i,0,0)}e(t?null:i.toDataURL("image/"+a.image_format,a.jpeg_quality/100),i,s);if(this.params.unfreeze_snap)this.unfreeze()},snap:function(e,t){if(!e)e=this.params.user_callback;if(!t)t=this.params.user_canvas;var a=this;var s=this.params;if(!this.loaded)return this.dispatch("error",new i("Webcam is not loaded yet"));if(!e)return this.dispatch("error",new i("Please provide a callback function or canvas to snap()"));if(this.preview_active){this.savePreview(e,t);return null}var r=document.createElement("canvas");r.width=this.params.dest_width;r.height=this.params.dest_height;var o=r.getContext("2d");if(this.params.flip_horiz){o.translate(s.dest_width,0);o.scale(-1,1)}var n=function(){if(this.src&&this.width&&this.height){o.drawImage(this,0,0,s.dest_width,s.dest_height)}if(s.crop_width&&s.crop_height){var a=document.createElement("canvas");a.width=s.crop_width;a.height=s.crop_height;var i=a.getContext("2d");i.drawImage(r,Math.floor(s.dest_width/2-s.crop_width/2),Math.floor(s.dest_height/2-s.crop_height/2),s.crop_width,s.crop_height,0,0,s.crop_width,s.crop_height);o=i;r=a}if(t){var n=t.getContext("2d");n.drawImage(r,0,0)}e(t?null:r.toDataURL("image/"+s.image_format,s.jpeg_quality/100),r,o)};if(this.userMedia){o.drawImage(this.video,0,0,this.params.dest_width,this.params.dest_height);n()}else if(this.iOS){var l=document.getElementById(this.container.id+"-ios_div");var h=document.getElementById(this.container.id+"-ios_img");var c=document.getElementById(this.container.id+"-ios_input");iFunc=function(e){n.call(h);h.removeEventListener("load",iFunc);l.style.backgroundImage="none";h.removeAttribute("src");c.value=null};if(!c.value){h.addEventListener("load",iFunc);c.style.display="block";c.focus();c.click();c.style.display="none"}else{iFunc(null)}}else{var d=this.getMovie()._snap();var h=new Image;h.onload=n;h.src="data:image/"+this.params.image_format+";base64,"+d}return null},configure:function(e){if(!e)e="camera";this.getMovie()._configure(e)},flashNotify:function(e,t){switch(e){case"flashLoadComplete":this.loaded=true;this.dispatch("load");break;case"cameraLive":this.live=true;this.dispatch("live");break;case"error":this.dispatch("error",new a(t));break;default:break}},b64ToUint6:function(e){return e>64&&e<91?e-65:e>96&&e<123?e-71:e>47&&e<58?e+4:e===43?62:e===47?63:0},base64DecToArr:function(e,t){var a=e.replace(/[^A-Za-z0-9\+\/]/g,""),i=a.length,s=t?Math.ceil((i*3+1>>2)/t)*t:i*3+1>>2,r=new Uint8Array(s);for(var o,n,l=0,h=0,c=0;c>>(16>>>o&24)&255}l=0}}return r},upload:function(e,t,a){var i=this.params.upload_name||"webcam";var s="";if(e.match(/^data\:image\/(\w+)/))s=RegExp.$1;else throw"Cannot locate image format in Data URI";var r=e.replace(/^data\:image\/\w+\;base64\,/,"");var o=new XMLHttpRequest;o.open("POST",t,true);if(o.upload&&o.upload.addEventListener){o.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=e.loaded/e.total;Webcam.dispatch("uploadProgress",t,e)}},false)}var n=this;o.onload=function(){if(a)a.apply(n,[o.status,o.responseText,o.statusText]);Webcam.dispatch("uploadComplete",o.status,o.responseText,o.statusText)};var l=new Blob([this.base64DecToArr(r)],{type:"image/"+s});var h=new FormData;h.append(i,l,i+"."+s.replace(/e/,""));o.send(h)}};Webcam.init();if(typeof define==="function"&&define.amd){define(function(){return Webcam})}else if(typeof module==="object"&&module.exports){module.exports=Webcam}else{e.Webcam=Webcam}})(window); -------------------------------------------------------------------------------- /static/css/style_v2.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; 6 | min-width: 320px; 7 | font-family: Microsoft Yahei,arial; 8 | -moz-min-width: 320px; 9 | -ms-min-width: 320px; 10 | -o-min-width: 320px; 11 | -webkit-min-width: 320px; 12 | } 13 | 14 | * { 15 | margin: 0; 16 | word-wrap: break-word; 17 | -moz-word-break: normal; 18 | -o-word-break: normal; 19 | word-break: normal; 20 | } 21 | 22 | #wrapper { 23 | margin: 0 auto -40px; 24 | height: auto!important; 25 | height: 100%; 26 | min-height: 100%; 27 | } 28 | 29 | a { 30 | color: #1e0fbe; 31 | text-decoration: none; 32 | } 33 | 34 | a:hover { 35 | text-decoration: underline; 36 | } 37 | 38 | a:visited { 39 | color: #609; 40 | } 41 | 42 | @media screen and (max-width:1080px) { 43 | #center-logo { 44 | margin-bottom: 50px!important; 45 | } 46 | 47 | #search-box #search { 48 | width: 400px!important; 49 | } 50 | } 51 | 52 | @media screen and (max-width:980px) { 53 | .header-div .nav-logo { 54 | display: none!important; 55 | } 56 | 57 | #wall,.adsense { 58 | margin: 0 5px!important; 59 | } 60 | 61 | #sort-bar { 62 | padding-left: 5px!important; 63 | } 64 | 65 | .download { 66 | display: inline-block!important; 67 | } 68 | 69 | #cpv6_right_lower { 70 | display: none; 71 | } 72 | } 73 | 74 | @media screen and (max-width:720px) { 75 | #center-logo { 76 | margin-bottom: 40px!important; 77 | } 78 | 79 | #search-box #search { 80 | width: 305px!important; 81 | } 82 | } 83 | 84 | @media screen and (max-width:665px) { 85 | #sort-bar b,#sort-bar a { 86 | padding: 0 4px!important; 87 | } 88 | } 89 | 90 | @media screen and (max-width:500px) { 91 | .ad { 92 | display: none; 93 | } 94 | } 95 | 96 | @media screen and (max-width:400px) { 97 | #search-box #search { 98 | width: 210px!important; 99 | } 100 | } 101 | 102 | img { 103 | border: none; 104 | } 105 | 106 | .ad { 107 | margin-bottom: 15px; 108 | } 109 | 110 | .header-div { 111 | position: relative; 112 | width: 100%; 113 | height: 79px; 114 | border-bottom: 1px solid #e5e5e5; 115 | background-color: #f1f1f1; 116 | vertical-align: middle; 117 | line-height: 79px; 118 | } 119 | 120 | .header-div form { 121 | margin-left: 5px; 122 | } 123 | 124 | .header-div form a { 125 | text-decoration: none; 126 | margin: 0px 14px; 127 | } 128 | 129 | .header-div .nav-logo { 130 | width: 32px; 131 | vertical-align: middle; 132 | } 133 | 134 | .header-div form #search { 135 | margin: 0; 136 | padding: 4px 7px; 137 | width: 68%; 138 | height: 32px; 139 | max-width: 550px; 140 | outline: none; 141 | border: 1px solid #b9b9b9; 142 | background-color: #fff; 143 | font: 16px arial; 144 | } 145 | 146 | .top-bar { 147 | z-index: 10; 148 | width: 100%; 149 | height: 30px; 150 | background-color: #2d2d2d; 151 | } 152 | 153 | .top-bar .first { 154 | margin-left: 20px; 155 | } 156 | 157 | .top-bar span { 158 | position: relative; 159 | display: inline-block; 160 | } 161 | 162 | .top-bar select { 163 | float: right; 164 | margin: 5px 5px 0 0; 165 | } 166 | 167 | .top-bar .highlight-bar { 168 | position: absolute; 169 | top: 0; 170 | display: inline-block; 171 | width: 40px; 172 | height: 2px; 173 | background-color: #dd4b39; 174 | } 175 | 176 | .top-bar span a { 177 | display: inline-block; 178 | width: 40px; 179 | color: #ccc; 180 | text-align: center; 181 | text-decoration: none; 182 | font-size: 13px; 183 | line-height: 30px; 184 | } 185 | 186 | .top-bar span a:hover { 187 | background-color: #4c4c4c; 188 | cursor: pointer; 189 | } 190 | 191 | .top-bar span a.selected { 192 | color: #fff; 193 | font-weight: 700; 194 | } 195 | 196 | #sort-bar { 197 | float: none; 198 | padding-left: 140px; 199 | height: 38px; 200 | background: #f8f8f8; 201 | line-height: 36px; 202 | } 203 | 204 | .top_fav { 205 | float: right; 206 | } 207 | 208 | .top_fav a { 209 | width: 80px!important; 210 | } 211 | 212 | #sort-bar b,#sort-bar a { 213 | display: inline-block; 214 | padding: 0 8px; 215 | color: #666; 216 | text-align: center; 217 | text-decoration: none; 218 | font-size: 14px; 219 | } 220 | 221 | #sort-bar a:hover { 222 | color: #323232; 223 | } 224 | 225 | #sort-bar b { 226 | border-bottom: 2px solid #38f; 227 | color: #323232; 228 | font-weight: 700; 229 | } 230 | 231 | #search:hover { 232 | border-color: silver!important; 233 | } 234 | 235 | #search:focus { 236 | border-color: #4d90fe!important; 237 | } 238 | 239 | #center-box-wrapper { 240 | display: table-cell; 241 | margin: 0; 242 | padding: 0; 243 | width: 659px; 244 | vertical-align: middle; 245 | } 246 | 247 | #center-box { 248 | margin: 0 auto; 249 | text-align: center; 250 | } 251 | 252 | #first-content { 253 | position: absolute; 254 | top: 30px; 255 | left: 0; 256 | display: table; 257 | margin: 0; 258 | padding: 0; 259 | width: 100%; 260 | height: 80%; 261 | } 262 | 263 | #center-logo { 264 | position: relative; 265 | margin-bottom: 60px; 266 | text-align: center; 267 | } 268 | 269 | #center-logo img { 270 | width: 22%; 271 | max-width:120px; 272 | min-width: 90px; 273 | -moz-min-width: 100px; 274 | -ms-min-width: 100px; 275 | -o-min-width: 100px; 276 | -webkit-min-width: 100px; 277 | } 278 | 279 | .info-box { 280 | margin-top: 10px; 281 | line-height: 1.6; 282 | } 283 | 284 | .info-box ul { 285 | padding: 0; 286 | text-decoration: none; 287 | } 288 | 289 | .info-box ul li { 290 | display: inline-block; 291 | } 292 | 293 | .info-box a { 294 | margin: 0 8px; 295 | color: #0360af; 296 | text-decoration: none; 297 | } 298 | 299 | button.blue,a.blue,input[type=submit].blue { 300 | margin-left: -6px; 301 | padding: 0; 302 | width: 80px; 303 | height: 32px; 304 | outline: medium; 305 | border: 0; 306 | border-bottom: 1px solid #2d78f4; 307 | -webkit-border-radius: 0; 308 | background: #3385ff; 309 | color: #fff; 310 | letter-spacing: 1px; 311 | font-size: 14px; 312 | cursor: pointer; 313 | -webkit-appearance: none; 314 | } 315 | 316 | input[type=submit].blue:hover { 317 | background: #317ef3; 318 | -webkit-box-shadow: 1px 1px 1px #ccc; 319 | box-shadow: 1px 1px 1px #ccc; 320 | -ms-box-shadow: 1px 1px 1px #ccc; 321 | } 322 | 323 | input[type=submit].blue:active { 324 | background: #3075dc; 325 | -webkit-box-shadow: inset 1px 1px 3px #2964bb; 326 | -moz-box-shadow: inset 1px 1px 3px #2964bb; 327 | box-shadow: inset 1px 1px 3px #2964bb; 328 | -ms-box-shadow: inset 1px 1px 3px #2964bb; 329 | -o-box-shadow: inset 1px 1px 3px #2964bb; 330 | } 331 | 332 | #content { 333 | padding: 15px 0 0; 334 | } 335 | 336 | .top a { 337 | color: #666; 338 | } 339 | 340 | #wall { 341 | overflow: hidden; 342 | margin: 0 140px; 343 | } 344 | 345 | #wall ol { 346 | margin-bottom: 10px; 347 | } 348 | 349 | #wall p { 350 | margin: 0; 351 | color: #333; 352 | word-wrap: break-word; 353 | line-height: 2; 354 | -moz-word-break: break-all; 355 | -o-word-break: break-all; 356 | word-break: break-all; 357 | } 358 | 359 | #wall p span { 360 | margin-left: 10px; 361 | } 362 | 363 | #wall p a { 364 | word-wrap: break-word; 365 | -moz-word-break: break-all; 366 | -o-word-break: break-all; 367 | word-break: break-all; 368 | } 369 | 370 | #search-info { 371 | padding: 10px 0; 372 | color: #999; 373 | font-size: 13px; 374 | } 375 | 376 | #search-box { 377 | position: relative; 378 | } 379 | 380 | #search-box #search { 381 | margin: 0; 382 | padding: 4px 7px!important; 383 | width: 520px; 384 | height: 32px; 385 | outline: none; 386 | border: 1px solid #dadada; 387 | background-color: #fff; 388 | vertical-align: top; 389 | font: 16px arial; 390 | } 391 | 392 | .search-statu { 393 | margin-bottom: 15px; 394 | padding-bottom: 10px; 395 | border-bottom: 1px solid #e4e4e4; 396 | color: #666; 397 | } 398 | 399 | .col-md-2,.col-md-8 { 400 | position: relative; 401 | min-height: 1px; 402 | } 403 | 404 | @media(min-width:992px) { 405 | .col-md-8 { 406 | float: left; 407 | width: 70%; 408 | } 409 | 410 | .col-md-2 { 411 | float: left; 412 | width: 30%; 413 | } 414 | } 415 | 416 | .search-notice { 417 | padding: 15px 10px; 418 | border-color: #dcdcdc; 419 | background-image: -webkit-linear-gradient(top,#e8e8e8 0%,#f5f5f5 100%); 420 | background-image: linear-gradient(to bottom,#e8e8e8 0%,#f5f5f5 100%); 421 | background-repeat: repeat-x; 422 | -webkit-box-shadow: inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1); 423 | box-shadow: inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1); 424 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0); 425 | } 426 | 427 | .search-item { 428 | margin: 0 10px 25px 0; 429 | } 430 | 431 | .search-item .item-title h3 { 432 | font-weight: 400; 433 | font-size: medium; 434 | line-height: 1.2; 435 | } 436 | 437 | .search-item .item-title h3 b { 438 | color: red; 439 | font-weight: 400; 440 | } 441 | 442 | .search-item em { 443 | color: #c00; 444 | font-style: normal; 445 | } 446 | 447 | .search-item .item-list { 448 | padding: 5px 0; 449 | } 450 | 451 | .search-item .item-list ul { 452 | margin: 0; 453 | padding: 0; 454 | } 455 | 456 | .search-item .item-list ul li { 457 | text-decoration: none; 458 | } 459 | 460 | .search-item .item-list,.search-item .item-list p { 461 | color: #222!important; 462 | word-wrap: break-word; 463 | font-size: 13px; 464 | line-height: 1.4!important; 465 | } 466 | 467 | .search-item .item-bar { 468 | color: #777; 469 | font-size: 13px; 470 | line-height: 16px; 471 | } 472 | 473 | .search-item .item-bar b { 474 | color: #00802a; 475 | } 476 | 477 | .search-item .item-bar span { 478 | margin-right: 15px; 479 | } 480 | 481 | .search-item .item-bar .download { 482 | display: none; 483 | margin-right: 10px; 484 | padding-left: 20px; 485 | height: 16px; 486 | background: url(/static/img/link.png) no-repeat left; 487 | } 488 | 489 | .highlight { 490 | color: red; 491 | } 492 | 493 | .bottom-pager { 494 | margin: 30px 0 15px; 495 | } 496 | 497 | .bottom-pager a,.bottom-pager span { 498 | display: inline-block; 499 | margin: 2px; 500 | padding: 8px 12px; 501 | border: 1px solid #a1a2a3; 502 | color: #1e0fbe; 503 | vertical-align: middle; 504 | text-align: center; 505 | text-decoration: none; 506 | font: 13px/1.22 arial,helvetica,clean,sans-serif; 507 | } 508 | 509 | .bottom-pager span { 510 | background-color: #eee; 511 | color: #888; 512 | font-weight: 700; 513 | } 514 | 515 | .bottom-pager a:hover { 516 | border: 1px solid #38f; 517 | background: #f2f8ff; 518 | } 519 | 520 | .bottom-pager a:visited { 521 | color: #1e0fbe; 522 | } 523 | 524 | .pagination { 525 | display: inline-block; 526 | margin: 20px 0 45px; 527 | padding-left: 0; 528 | border-radius: 4px; 529 | } 530 | 531 | .pagination li { 532 | display: inline; 533 | } 534 | 535 | .pagination li a,.pagination li span { 536 | position: relative; 537 | float: left; 538 | margin: 0; 539 | margin-left: 2px; 540 | padding: 6px 12px; 541 | border: 1px solid #ddd; 542 | background-color: #fff; 543 | color: #428bca; 544 | text-decoration: none; 545 | font-size: 1em; 546 | line-height: 1.42857143; 547 | } 548 | 549 | .pagination>.active>span { 550 | border-color: #428bca; 551 | background-color: #428bca; 552 | color: #fff; 553 | } 554 | 555 | .pagination>li:first-child>a,.pagination>li:first-child>span { 556 | margin-left: 0; 557 | } 558 | 559 | .new { 560 | position: relative; 561 | top: -4px; 562 | right: -4px; 563 | display: inline-block; 564 | overflow: hidden; 565 | padding: 2px; 566 | background-color: #f13f40; 567 | color: #fff; 568 | vertical-align: text-bottom; 569 | text-align: center; 570 | font-style: normal; 571 | font-size: 12px; 572 | line-height: 100%; 573 | } 574 | 575 | .baidu-box,.bdSug_app,.bdsug_copy { 576 | display: none; 577 | } 578 | 579 | .bdSug_wpr td { 580 | padding-left: 5px!important; 581 | } 582 | 583 | .bdshare-button-style0-16 { 584 | margin-left: 0!important; 585 | } 586 | 587 | .bdshare-button-style0-16:after { 588 | display: inline-block!important; 589 | content: inherit!important; 590 | } 591 | 592 | .bdshare-button-style0-16 a { 593 | margin: 0 10px 0 0!important; 594 | padding-left: 20px!important; 595 | font-size: 16px!important; 596 | } 597 | 598 | .push { 599 | clear: both; 600 | height: 40px; 601 | } 602 | 603 | .footer { 604 | position: relative; 605 | z-index: 99; 606 | width: 100%; 607 | height: 39px; 608 | border-top: 1px solid #e4e4e4; 609 | background: #f2f2f2; 610 | color: #666; 611 | text-align: center; 612 | font-size: small; 613 | line-height: 39px; 614 | } 615 | 616 | .footer a { 617 | color: #666!important; 618 | } 619 | 620 | h2 { 621 | display: block; 622 | margin: 0; 623 | color: #444; 624 | word-wrap: break-word; 625 | font-weight: 700; 626 | font-size: 18px; 627 | -webkit-margin-after: .2em; 628 | -webkit-margin-start: 0; 629 | -webkit-margin-end: 0; 630 | -moz-word-break: break-all; 631 | -o-word-break: break-all; 632 | word-break: break-all; 633 | } 634 | 635 | .fileDetail { 636 | position: relative; 637 | } 638 | 639 | .fileDetail p { 640 | color: #444!important; 641 | font-size: 16px; 642 | line-height: 2!important; 643 | } 644 | 645 | .fileDetail p a { 646 | margin-right: 3px; 647 | } 648 | 649 | .lightColor,.lightColor:visited { 650 | margin-left: 10px; 651 | color: #888; 652 | } 653 | 654 | .pill,.pill:visited { 655 | padding: 2px 4px; 656 | border: 1px solid #e1e1e8; 657 | background-color: #f7f7f9; 658 | color: #d14; 659 | white-space: nowrap; 660 | } 661 | 662 | .fileDetail .pill { 663 | white-space: normal; 664 | } 665 | 666 | .cpill { 667 | padding-right: 9px; 668 | padding-left: 9px; 669 | -webkit-border-radius: 9px; 670 | -moz-border-radius: 9px; 671 | border-radius: 9px; 672 | color: #fff!important; 673 | vertical-align: baseline; 674 | white-space: nowrap; 675 | -ms-border-radius: 9px; 676 | } 677 | 678 | .blue-pill { 679 | background-color: #3a87ad; 680 | } 681 | 682 | .yellow-pill { 683 | background-color: #f89406; 684 | } 685 | 686 | .fileType5 { 687 | background-color: red; 688 | } 689 | 690 | .fileType0,.fileType6 { 691 | background-color: #f89406; 692 | } 693 | 694 | .fileType1,.fileType2,.fileType3,.fileType4,.fileType7 { 695 | background-color: green; 696 | } 697 | 698 | ol li { 699 | color: #444; 700 | line-height: 1.5em; 701 | } 702 | 703 | #latest-search { 704 | float: left; 705 | margin-bottom: 40px; 706 | padding-top: 10px; 707 | border-top: 1px solid #e4e4e4; 708 | } 709 | 710 | #latest-search .latest-desc { 711 | display: block; 712 | margin: 5px; 713 | margin-right: 5px; 714 | color: #666; 715 | font-size: 18px; 716 | } 717 | 718 | #latest-search a { 719 | float: left; 720 | display: block; 721 | margin-right: 15px; 722 | margin-bottom: 6px; 723 | color: #1e0fbe!important; 724 | font-size: 15px; 725 | } 726 | 727 | .ads-badge { 728 | display: inline-block; 729 | margin-right: 5px; 730 | padding: 0 3px; 731 | border-radius: 2px; 732 | background-color: #edb802; 733 | color: #fff; 734 | vertical-align: baseline; 735 | font-size: 14px; 736 | line-height: 18px; 737 | -ms-border-radius: 2px; 738 | } 739 | 740 | .adsense { 741 | overflow: hidden; 742 | margin: 0 0 10px; 743 | max-width: 100%; 744 | } 745 | 746 | .detail-width { 747 | width: 100%; 748 | max-width: 700px; 749 | } 750 | 751 | .detail-table { 752 | margin: 10px 0; 753 | margin-top: 10px; 754 | border: 1px solid silver; 755 | border-collapse: collapse; 756 | text-align: center; 757 | } 758 | 759 | .detail-table tr { 760 | border-bottom: 1px solid silver; 761 | } 762 | 763 | .detail-header-bg th { 764 | padding: 5px; 765 | } 766 | 767 | .detail-table .detail-header-bg { 768 | background: #f1f1f1; 769 | } 770 | 771 | .detail-panel { 772 | margin: 10px 0; 773 | } 774 | 775 | .detail-panel .panel-header { 776 | padding: 5px 0 5px 10px; 777 | min-width: 80px; 778 | border: 1px solid silver; 779 | border-bottom: none; 780 | background-color: #f1f1f1; 781 | font-weight: 700; 782 | -moz-min-width: 80px; 783 | -ms-min-width: 80px; 784 | -o-min-width: 80px; 785 | -webkit-min-width: 80px; 786 | } 787 | 788 | .detail-panel .panel-body { 789 | overflow: hidden; 790 | padding: 5px; 791 | border: 1px solid silver; 792 | } 793 | 794 | .ds-powered-by,.ds-current { 795 | display: none!important; 796 | } 797 | 798 | .taglist_tag span { 799 | float: left; 800 | display: block; 801 | margin: 4px 20px 4px 0; 802 | font-size: 15px; 803 | } 804 | 805 | .taglist_tag a { 806 | color: #000; 807 | } 808 | 809 | #rand_content { 810 | display: none; 811 | } 812 | 813 | .rand_content { 814 | padding: 0; 815 | } 816 | 817 | .rand_content ul { 818 | margin-left: 0; 819 | padding: 0; 820 | } 821 | 822 | .rand_content li { 823 | list-style: none; 824 | line-height: 24px; 825 | } 826 | 827 | .rand_content li a { 828 | color: #333; 829 | font-size: 14px; 830 | } 831 | 832 | .link_op a { 833 | color: #333; 834 | } 835 | 836 | .link_op em { 837 | color: #c00; 838 | font-style: normal; 839 | font-size: 14px; 840 | } 841 | 842 | .fl { 843 | float: left!important; 844 | } 845 | 846 | .mt25 { 847 | margin-top: 25px!important; 848 | } 849 | 850 | .flink { 851 | font-size: 13px; 852 | } 853 | 854 | .flink a { 855 | margin-left: 5px; 856 | color: #1e6fb5; 857 | } 858 | 859 | .flink a:visited { 860 | color: #1e6fb5; 861 | } 862 | 863 | .contact { 864 | margin-top: 8px; 865 | color: #333; 866 | font-size: 13px; 867 | } 868 | 869 | #mg-link { 870 | position: absolute; 871 | left: -99999px; 872 | } 873 | 874 | .vcode-box { 875 | padding: 5px; 876 | } 877 | 878 | .vcode-box img { 879 | display: block; 880 | margin: 10px auto 20px; 881 | } 882 | 883 | .vcode-box #vcode { 884 | display: block; 885 | margin: 0 auto; 886 | padding-left: 10px; 887 | width: 230px; 888 | height: 36px; 889 | border: 1px solid #e6e6e6; 890 | color: #333; 891 | line-height: 30px; 892 | } 893 | 894 | .tras-tip { 895 | margin: 10px 0; 896 | text-align: center; 897 | font-weight: 700; 898 | font-size: 20px; 899 | } 900 | 901 | .cover { 902 | margin: 16px 0; 903 | display:block; 904 | width: 300px; 905 | height: 300px; 906 | } 907 | 908 | .cover img { 909 | width: 300px; 910 | height: 300px; 911 | background-size: cover; 912 | object-fit: cover; 913 | } -------------------------------------------------------------------------------- /static/js/layer.js: -------------------------------------------------------------------------------- 1 | /*!layer-v3.1.1 Web弹层组件 MIT License http://layer.layui.com/ By 贤心*/;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
'+(f?r.title[0]:r.title)+"
":"";return r.zIndex=s,t([r.shade?'
':"",'
'+(e&&2!=r.type?"":u)+'
'+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
'+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
'+e+"
"}():"")+(r.resize?'':"")+"
"],u,i('
')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){s=e.find(".layui-layer-input"),s.focus(),"function"==typeof f&&f(e)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
    '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
  • '+(t[0].content||"no content")+"
  • ";i'+(t[i].content||"no content")+"";return a}()+"
",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
'+(u.length>1?'':"")+'
'+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); -------------------------------------------------------------------------------- /static/js/webcam.js: -------------------------------------------------------------------------------- 1 | // WebcamJS v1.0.25 2 | // Webcam library for capturing JPEG/PNG images in JavaScript 3 | // Attempts getUserMedia, falls back to Flash 4 | // Author: Joseph Huckaby: http://github.com/jhuckaby 5 | // Based on JPEGCam: http://code.google.com/p/jpegcam/ 6 | // Copyright (c) 2012 - 2017 Joseph Huckaby 7 | // Licensed under the MIT License 8 | 9 | (function(window) { 10 | var _userMedia; 11 | 12 | // declare error types 13 | 14 | // inheritance pattern here: 15 | // https://stackoverflow.com/questions/783818/how-do-i-create-a-custom-error-in-javascript 16 | function FlashError() { 17 | var temp = Error.apply(this, arguments); 18 | temp.name = this.name = "FlashError"; 19 | this.stack = temp.stack; 20 | this.message = temp.message; 21 | } 22 | 23 | function WebcamError() { 24 | var temp = Error.apply(this, arguments); 25 | temp.name = this.name = "WebcamError"; 26 | this.stack = temp.stack; 27 | this.message = temp.message; 28 | } 29 | 30 | var IntermediateInheritor = function() {}; 31 | IntermediateInheritor.prototype = Error.prototype; 32 | 33 | FlashError.prototype = new IntermediateInheritor(); 34 | WebcamError.prototype = new IntermediateInheritor(); 35 | 36 | var Webcam = { 37 | version: '1.0.25', 38 | 39 | // globals 40 | protocol: location.protocol.match(/https/i) ? 'https' : 'http', 41 | loaded: false, // true when webcam movie finishes loading 42 | live: false, // true when webcam is initialized and ready to snap 43 | userMedia: true, // true when getUserMedia is supported natively 44 | 45 | iOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, 46 | 47 | params: { 48 | width: 0, 49 | height: 0, 50 | dest_width: 0, // size of captured image 51 | dest_height: 0, // these default to width/height 52 | image_format: 'jpeg', // image format (may be jpeg or png) 53 | jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best) 54 | enable_flash: true, // enable flash fallback, 55 | force_flash: false, // force flash mode, 56 | flip_horiz: false, // flip image horiz (mirror mode) 57 | fps: 30, // camera frames per second 58 | upload_name: 'webcam', // name of file in upload post data 59 | constraints: null, // custom user media constraints, 60 | swfURL: '', // URI to webcam.swf movie (defaults to the js location) 61 | flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).', 62 | noInterfaceFoundText: 'No supported webcam interface found.', 63 | unfreeze_snap: true, // Whether to unfreeze the camera after snap (defaults to true) 64 | iosPlaceholderText: 'Click here to open camera.', 65 | user_callback: null, // callback function for snapshot (used if no user_callback parameter given to snap function) 66 | user_canvas: null // user provided canvas for snapshot (used if no user_canvas parameter given to snap function) 67 | }, 68 | 69 | errors: { 70 | FlashError: FlashError, 71 | WebcamError: WebcamError 72 | }, 73 | 74 | hooks: {}, // callback hook functions 75 | 76 | init: function() { 77 | // initialize, check for getUserMedia support 78 | var self = this; 79 | 80 | // Setup getUserMedia, with polyfill for older browsers 81 | // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia 82 | this.mediaDevices = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ? 83 | navigator.mediaDevices : ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? { 84 | getUserMedia: function(c) { 85 | return new Promise(function(y, n) { 86 | (navigator.mozGetUserMedia || 87 | navigator.webkitGetUserMedia).call(navigator, c, y, n); 88 | }); 89 | } 90 | } : null); 91 | 92 | window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 93 | this.userMedia = this.userMedia && !!this.mediaDevices && !!window.URL; 94 | 95 | if (this.iOS) { 96 | this.userMedia = null; 97 | } 98 | 99 | // Older versions of firefox (< 21) apparently claim support but user media does not actually work 100 | if (navigator.userAgent.match(/Firefox\D+(\d+)/)) { 101 | if (parseInt(RegExp.$1, 10) < 21) this.userMedia = null; 102 | } 103 | 104 | // Make sure media stream is closed when navigating away from page 105 | if (this.userMedia) { 106 | window.addEventListener( 'beforeunload', function(event) { 107 | self.reset(); 108 | } ); 109 | } 110 | }, 111 | 112 | exifOrientation: function(binFile) { 113 | // extract orientation information from the image provided by iOS 114 | // algorithm based on exif-js 115 | var dataView = new DataView(binFile); 116 | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { 117 | console.log('Not a valid JPEG file'); 118 | return 0; 119 | } 120 | var offset = 2; 121 | var marker = null; 122 | while (offset < binFile.byteLength) { 123 | // find 0xFFE1 (225 marker) 124 | if (dataView.getUint8(offset) != 0xFF) { 125 | console.log('Not a valid marker at offset ' + offset + ', found: ' + dataView.getUint8(offset)); 126 | return 0; 127 | } 128 | marker = dataView.getUint8(offset + 1); 129 | if (marker == 225) { 130 | offset += 4; 131 | var str = ""; 132 | for (n = 0; n < 4; n++) { 133 | str += String.fromCharCode(dataView.getUint8(offset+n)); 134 | } 135 | if (str != 'Exif') { 136 | console.log('Not valid EXIF data found'); 137 | return 0; 138 | } 139 | 140 | offset += 6; // tiffOffset 141 | var bigEnd = null; 142 | 143 | // test for TIFF validity and endianness 144 | if (dataView.getUint16(offset) == 0x4949) { 145 | bigEnd = false; 146 | } else if (dataView.getUint16(offset) == 0x4D4D) { 147 | bigEnd = true; 148 | } else { 149 | console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); 150 | return 0; 151 | } 152 | 153 | if (dataView.getUint16(offset+2, !bigEnd) != 0x002A) { 154 | console.log("Not valid TIFF data! (no 0x002A)"); 155 | return 0; 156 | } 157 | 158 | var firstIFDOffset = dataView.getUint32(offset+4, !bigEnd); 159 | if (firstIFDOffset < 0x00000008) { 160 | console.log("Not valid TIFF data! (First offset less than 8)", dataView.getUint32(offset+4, !bigEnd)); 161 | return 0; 162 | } 163 | 164 | // extract orientation data 165 | var dataStart = offset + firstIFDOffset; 166 | var entries = dataView.getUint16(dataStart, !bigEnd); 167 | for (var i=0; i 8) { 178 | console.log('Invalid EXIF orientation value ('+value+')'); 179 | return 0; 180 | } 181 | return value; 182 | } 183 | } 184 | } else { 185 | offset += 2+dataView.getUint16(offset+2); 186 | } 187 | } 188 | return 0; 189 | }, 190 | 191 | fixOrientation: function(origObjURL, orientation, targetImg) { 192 | // fix image orientation based on exif orientation data 193 | // exif orientation information 194 | // http://www.impulseadventure.com/photo/exif-orientation.html 195 | // link source wikipedia (https://en.wikipedia.org/wiki/Exif#cite_note-20) 196 | var img = new Image(); 197 | img.addEventListener('load', function(event) { 198 | var canvas = document.createElement('canvas'); 199 | var ctx = canvas.getContext('2d'); 200 | 201 | // switch width height if orientation needed 202 | if (orientation < 5) { 203 | canvas.width = img.width; 204 | canvas.height = img.height; 205 | } else { 206 | canvas.width = img.height; 207 | canvas.height = img.width; 208 | } 209 | 210 | // transform (rotate) image - see link at beginning this method 211 | switch (orientation) { 212 | case 2: ctx.transform(-1, 0, 0, 1, img.width, 0); break; 213 | case 3: ctx.transform(-1, 0, 0, -1, img.width, img.height); break; 214 | case 4: ctx.transform(1, 0, 0, -1, 0, img.height); break; 215 | case 5: ctx.transform(0, 1, 1, 0, 0, 0); break; 216 | case 6: ctx.transform(0, 1, -1, 0, img.height , 0); break; 217 | case 7: ctx.transform(0, -1, -1, 0, img.height, img.width); break; 218 | case 8: ctx.transform(0, -1, 1, 0, 0, img.width); break; 219 | } 220 | 221 | ctx.drawImage(img, 0, 0); 222 | // pass rotated image data to the target image container 223 | targetImg.src = canvas.toDataURL(); 224 | }, false); 225 | // start transformation by load event 226 | img.src = origObjURL; 227 | }, 228 | 229 | attach: function(elem) { 230 | // create webcam preview and attach to DOM element 231 | // pass in actual DOM reference, ID, or CSS selector 232 | if (typeof(elem) == 'string') { 233 | elem = document.getElementById(elem) || document.querySelector(elem); 234 | } 235 | if (!elem) { 236 | return this.dispatch('error', new WebcamError("Could not locate DOM element to attach to.")); 237 | } 238 | this.container = elem; 239 | elem.innerHTML = ''; // start with empty element 240 | 241 | // insert "peg" so we can insert our preview canvas adjacent to it later on 242 | var peg = document.createElement('div'); 243 | elem.appendChild( peg ); 244 | this.peg = peg; 245 | 246 | // set width/height if not already set 247 | if (!this.params.width) this.params.width = elem.offsetWidth; 248 | if (!this.params.height) this.params.height = elem.offsetHeight; 249 | 250 | // make sure we have a nonzero width and height at this point 251 | if (!this.params.width || !this.params.height) { 252 | return this.dispatch('error', new WebcamError("No width and/or height for webcam. Please call set() first, or attach to a visible element.")); 253 | } 254 | 255 | // set defaults for dest_width / dest_height if not set 256 | if (!this.params.dest_width) this.params.dest_width = this.params.width; 257 | if (!this.params.dest_height) this.params.dest_height = this.params.height; 258 | 259 | this.userMedia = _userMedia === undefined ? this.userMedia : _userMedia; 260 | // if force_flash is set, disable userMedia 261 | if (this.params.force_flash) { 262 | _userMedia = this.userMedia; 263 | this.userMedia = null; 264 | } 265 | 266 | // check for default fps 267 | if (typeof this.params.fps !== "number") this.params.fps = 30; 268 | 269 | // adjust scale if dest_width or dest_height is different 270 | var scaleX = this.params.width / this.params.dest_width; 271 | var scaleY = this.params.height / this.params.dest_height; 272 | 273 | if (this.userMedia) { 274 | // setup webcam video container 275 | var video = document.createElement('video'); 276 | video.setAttribute('autoplay', 'autoplay'); 277 | video.style.width = '' + this.params.dest_width + 'px'; 278 | video.style.height = '' + this.params.dest_height + 'px'; 279 | 280 | if ((scaleX != 1.0) || (scaleY != 1.0)) { 281 | elem.style.overflow = 'hidden'; 282 | video.style.webkitTransformOrigin = '0px 0px'; 283 | video.style.mozTransformOrigin = '0px 0px'; 284 | video.style.msTransformOrigin = '0px 0px'; 285 | video.style.oTransformOrigin = '0px 0px'; 286 | video.style.transformOrigin = '0px 0px'; 287 | video.style.webkitTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 288 | video.style.mozTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 289 | video.style.msTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 290 | video.style.oTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 291 | video.style.transform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 292 | } 293 | 294 | // add video element to dom 295 | elem.appendChild( video ); 296 | this.video = video; 297 | 298 | // ask user for access to their camera 299 | var self = this; 300 | this.mediaDevices.getUserMedia({ 301 | "audio": false, 302 | "video": this.params.constraints || { 303 | mandatory: { 304 | minWidth: this.params.dest_width, 305 | minHeight: this.params.dest_height 306 | } 307 | } 308 | }) 309 | .then( function(stream) { 310 | // got access, attach stream to video 311 | video.onloadedmetadata = function(e) { 312 | self.stream = stream; 313 | self.loaded = true; 314 | self.live = true; 315 | self.dispatch('load'); 316 | self.dispatch('live'); 317 | self.flip(); 318 | }; 319 | // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari. 320 | // older browsers may not have srcObject 321 | if ("srcObject" in video) { 322 | video.srcObject = stream; 323 | } 324 | else { 325 | // using URL.createObjectURL() as fallback for old browsers 326 | video.src = window.URL.createObjectURL(stream); 327 | } 328 | }) 329 | .catch( function(err) { 330 | // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014) 331 | // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now. 332 | if (self.params.enable_flash && self.detectFlash()) { 333 | setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 ); 334 | } 335 | else { 336 | self.dispatch('error', err); 337 | } 338 | }); 339 | } 340 | else if (this.iOS) { 341 | // prepare HTML elements 342 | var div = document.createElement('div'); 343 | div.id = this.container.id+'-ios_div'; 344 | div.className = 'webcamjs-ios-placeholder'; 345 | div.style.width = '' + this.params.width + 'px'; 346 | div.style.height = '' + this.params.height + 'px'; 347 | div.style.textAlign = 'center'; 348 | div.style.display = 'table-cell'; 349 | div.style.verticalAlign = 'middle'; 350 | div.style.backgroundRepeat = 'no-repeat'; 351 | div.style.backgroundSize = 'contain'; 352 | div.style.backgroundPosition = 'center'; 353 | var span = document.createElement('span'); 354 | span.className = 'webcamjs-ios-text'; 355 | span.innerHTML = this.params.iosPlaceholderText; 356 | div.appendChild(span); 357 | var img = document.createElement('img'); 358 | img.id = this.container.id+'-ios_img'; 359 | img.style.width = '' + this.params.dest_width + 'px'; 360 | img.style.height = '' + this.params.dest_height + 'px'; 361 | img.style.display = 'none'; 362 | div.appendChild(img); 363 | var input = document.createElement('input'); 364 | input.id = this.container.id+'-ios_input'; 365 | input.setAttribute('type', 'file'); 366 | input.setAttribute('accept', 'image/*'); 367 | input.setAttribute('capture', 'camera'); 368 | 369 | var self = this; 370 | var params = this.params; 371 | // add input listener to load the selected image 372 | input.addEventListener('change', function(event) { 373 | if (event.target.files.length > 0 && event.target.files[0].type.indexOf('image/') == 0) { 374 | var objURL = URL.createObjectURL(event.target.files[0]); 375 | 376 | // load image with auto scale and crop 377 | var image = new Image(); 378 | image.addEventListener('load', function(event) { 379 | var canvas = document.createElement('canvas'); 380 | canvas.width = params.dest_width; 381 | canvas.height = params.dest_height; 382 | var ctx = canvas.getContext('2d'); 383 | 384 | // crop and scale image for final size 385 | ratio = Math.min(image.width / params.dest_width, image.height / params.dest_height); 386 | var sw = params.dest_width * ratio; 387 | var sh = params.dest_height * ratio; 388 | var sx = (image.width - sw) / 2; 389 | var sy = (image.height - sh) / 2; 390 | ctx.drawImage(image, sx, sy, sw, sh, 0, 0, params.dest_width, params.dest_height); 391 | 392 | var dataURL = canvas.toDataURL(); 393 | img.src = dataURL; 394 | div.style.backgroundImage = "url('"+dataURL+"')"; 395 | }, false); 396 | 397 | // read EXIF data 398 | var fileReader = new FileReader(); 399 | fileReader.addEventListener('load', function(e) { 400 | var orientation = self.exifOrientation(e.target.result); 401 | if (orientation > 1) { 402 | // image need to rotate (see comments on fixOrientation method for more information) 403 | // transform image and load to image object 404 | self.fixOrientation(objURL, orientation, image); 405 | } else { 406 | // load image data to image object 407 | image.src = objURL; 408 | } 409 | }, false); 410 | 411 | // Convert image data to blob format 412 | var http = new XMLHttpRequest(); 413 | http.open("GET", objURL, true); 414 | http.responseType = "blob"; 415 | http.onload = function(e) { 416 | if (this.status == 200 || this.status === 0) { 417 | fileReader.readAsArrayBuffer(this.response); 418 | } 419 | }; 420 | http.send(); 421 | 422 | } 423 | }, false); 424 | input.style.display = 'none'; 425 | elem.appendChild(input); 426 | // make div clickable for open camera interface 427 | div.addEventListener('click', function(event) { 428 | if (params.user_callback) { 429 | // global user_callback defined - create the snapshot 430 | self.snap(params.user_callback, params.user_canvas); 431 | } else { 432 | // no global callback definied for snapshot, load image and wait for external snap method call 433 | input.style.display = 'block'; 434 | input.focus(); 435 | input.click(); 436 | input.style.display = 'none'; 437 | } 438 | }, false); 439 | elem.appendChild(div); 440 | this.loaded = true; 441 | this.live = true; 442 | } 443 | else if (this.params.enable_flash && this.detectFlash()) { 444 | // flash fallback 445 | window.Webcam = Webcam; // needed for flash-to-js interface 446 | var div = document.createElement('div'); 447 | div.innerHTML = this.getSWFHTML(); 448 | elem.appendChild( div ); 449 | } 450 | else { 451 | this.dispatch('error', new WebcamError( this.params.noInterfaceFoundText )); 452 | } 453 | 454 | // setup final crop for live preview 455 | if (this.params.crop_width && this.params.crop_height) { 456 | var scaled_crop_width = Math.floor( this.params.crop_width * scaleX ); 457 | var scaled_crop_height = Math.floor( this.params.crop_height * scaleY ); 458 | 459 | elem.style.width = '' + scaled_crop_width + 'px'; 460 | elem.style.height = '' + scaled_crop_height + 'px'; 461 | elem.style.overflow = 'hidden'; 462 | 463 | elem.scrollLeft = Math.floor( (this.params.width / 2) - (scaled_crop_width / 2) ); 464 | elem.scrollTop = Math.floor( (this.params.height / 2) - (scaled_crop_height / 2) ); 465 | } 466 | else { 467 | // no crop, set size to desired 468 | elem.style.width = '' + this.params.width + 'px'; 469 | elem.style.height = '' + this.params.height + 'px'; 470 | } 471 | }, 472 | 473 | reset: function() { 474 | // shutdown camera, reset to potentially attach again 475 | if (this.preview_active) this.unfreeze(); 476 | 477 | // attempt to fix issue #64 478 | this.unflip(); 479 | 480 | if (this.userMedia) { 481 | if (this.stream) { 482 | if (this.stream.getVideoTracks) { 483 | // get video track to call stop on it 484 | var tracks = this.stream.getVideoTracks(); 485 | if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop(); 486 | } 487 | else if (this.stream.stop) { 488 | // deprecated, may be removed in future 489 | this.stream.stop(); 490 | } 491 | } 492 | delete this.stream; 493 | delete this.video; 494 | } 495 | 496 | if ((this.userMedia !== true) && this.loaded && !this.iOS) { 497 | // call for turn off camera in flash 498 | var movie = this.getMovie(); 499 | if (movie && movie._releaseCamera) movie._releaseCamera(); 500 | } 501 | 502 | if (this.container) { 503 | this.container.innerHTML = ''; 504 | delete this.container; 505 | } 506 | 507 | this.loaded = false; 508 | this.live = false; 509 | }, 510 | 511 | set: function() { 512 | // set one or more params 513 | // variable argument list: 1 param = hash, 2 params = key, value 514 | if (arguments.length == 1) { 515 | for (var key in arguments[0]) { 516 | this.params[key] = arguments[0][key]; 517 | } 518 | } 519 | else { 520 | this.params[ arguments[0] ] = arguments[1]; 521 | } 522 | }, 523 | 524 | on: function(name, callback) { 525 | // set callback hook 526 | name = name.replace(/^on/i, '').toLowerCase(); 527 | if (!this.hooks[name]) this.hooks[name] = []; 528 | this.hooks[name].push( callback ); 529 | }, 530 | 531 | off: function(name, callback) { 532 | // remove callback hook 533 | name = name.replace(/^on/i, '').toLowerCase(); 534 | if (this.hooks[name]) { 535 | if (callback) { 536 | // remove one selected callback from list 537 | var idx = this.hooks[name].indexOf(callback); 538 | if (idx > -1) this.hooks[name].splice(idx, 1); 539 | } 540 | else { 541 | // no callback specified, so clear all 542 | this.hooks[name] = []; 543 | } 544 | } 545 | }, 546 | 547 | dispatch: function() { 548 | // fire hook callback, passing optional value to it 549 | var name = arguments[0].replace(/^on/i, '').toLowerCase(); 550 | var args = Array.prototype.slice.call(arguments, 1); 551 | 552 | if (this.hooks[name] && this.hooks[name].length) { 553 | for (var idx = 0, len = this.hooks[name].length; idx < len; idx++) { 554 | var hook = this.hooks[name][idx]; 555 | 556 | if (typeof(hook) == 'function') { 557 | // callback is function reference, call directly 558 | hook.apply(this, args); 559 | } 560 | else if ((typeof(hook) == 'object') && (hook.length == 2)) { 561 | // callback is PHP-style object instance method 562 | hook[0][hook[1]].apply(hook[0], args); 563 | } 564 | else if (window[hook]) { 565 | // callback is global function name 566 | window[ hook ].apply(window, args); 567 | } 568 | } // loop 569 | return true; 570 | } 571 | else if (name == 'error') { 572 | var message; 573 | if ((args[0] instanceof FlashError) || (args[0] instanceof WebcamError)) { 574 | message = args[0].message; 575 | } else { 576 | message = "Could not access webcam: " + args[0].name + ": " + 577 | args[0].message + " " + args[0].toString(); 578 | } 579 | 580 | // default error handler if no custom one specified 581 | alert("Webcam.js Error: " + message); 582 | } 583 | 584 | return false; // no hook defined 585 | }, 586 | 587 | setSWFLocation: function(value) { 588 | // for backward compatibility. 589 | this.set('swfURL', value); 590 | }, 591 | 592 | detectFlash: function() { 593 | // return true if browser supports flash, false otherwise 594 | // Code snippet borrowed from: https://github.com/swfobject/swfobject 595 | var SHOCKWAVE_FLASH = "Shockwave Flash", 596 | SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", 597 | FLASH_MIME_TYPE = "application/x-shockwave-flash", 598 | win = window, 599 | nav = navigator, 600 | hasFlash = false; 601 | 602 | if (typeof nav.plugins !== "undefined" && typeof nav.plugins[SHOCKWAVE_FLASH] === "object") { 603 | var desc = nav.plugins[SHOCKWAVE_FLASH].description; 604 | if (desc && (typeof nav.mimeTypes !== "undefined" && nav.mimeTypes[FLASH_MIME_TYPE] && nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { 605 | hasFlash = true; 606 | } 607 | } 608 | else if (typeof win.ActiveXObject !== "undefined") { 609 | try { 610 | var ax = new ActiveXObject(SHOCKWAVE_FLASH_AX); 611 | if (ax) { 612 | var ver = ax.GetVariable("$version"); 613 | if (ver) hasFlash = true; 614 | } 615 | } 616 | catch (e) {;} 617 | } 618 | 619 | return hasFlash; 620 | }, 621 | 622 | getSWFHTML: function() { 623 | // Return HTML for embedding flash based webcam capture movie 624 | var html = '', 625 | swfURL = this.params.swfURL; 626 | 627 | // make sure we aren't running locally (flash doesn't work) 628 | if (location.protocol.match(/file/)) { 629 | this.dispatch('error', new FlashError("Flash does not work from local disk. Please run from a web server.")); 630 | return '

ERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.

'; 631 | } 632 | 633 | // make sure we have flash 634 | if (!this.detectFlash()) { 635 | this.dispatch('error', new FlashError("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again.")); 636 | return '

' + this.params.flashNotDetectedText + '

'; 637 | } 638 | 639 | // set default swfURL if not explicitly set 640 | if (!swfURL) { 641 | // find our script tag, and use that base URL 642 | var base_url = ''; 643 | var scpts = document.getElementsByTagName('script'); 644 | for (var idx = 0, len = scpts.length; idx < len; idx++) { 645 | var src = scpts[idx].getAttribute('src'); 646 | if (src && src.match(/\/webcam(\.min)?\.js/)) { 647 | base_url = src.replace(/\/webcam(\.min)?\.js.*$/, ''); 648 | idx = len; 649 | } 650 | } 651 | if (base_url) swfURL = base_url + '/webcam.swf'; 652 | else swfURL = 'webcam.swf'; 653 | } 654 | 655 | // if this is the user's first visit, set flashvar so flash privacy settings panel is shown first 656 | if (window.localStorage && !localStorage.getItem('visited')) { 657 | this.params.new_user = 1; 658 | localStorage.setItem('visited', 1); 659 | } 660 | 661 | // construct flashvars string 662 | var flashvars = ''; 663 | for (var key in this.params) { 664 | if (flashvars) flashvars += '&'; 665 | flashvars += key + '=' + escape(this.params[key]); 666 | } 667 | 668 | // construct object/embed tag 669 | html += ''; 670 | 671 | return html; 672 | }, 673 | 674 | getMovie: function() { 675 | // get reference to movie object/embed in DOM 676 | if (!this.loaded) return this.dispatch('error', new FlashError("Flash Movie is not loaded yet")); 677 | var movie = document.getElementById('webcam_movie_obj'); 678 | if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed'); 679 | if (!movie) this.dispatch('error', new FlashError("Cannot locate Flash movie in DOM")); 680 | return movie; 681 | }, 682 | 683 | freeze: function() { 684 | // show preview, freeze camera 685 | var self = this; 686 | var params = this.params; 687 | 688 | // kill preview if already active 689 | if (this.preview_active) this.unfreeze(); 690 | 691 | // determine scale factor 692 | var scaleX = this.params.width / this.params.dest_width; 693 | var scaleY = this.params.height / this.params.dest_height; 694 | 695 | // must unflip container as preview canvas will be pre-flipped 696 | this.unflip(); 697 | 698 | // calc final size of image 699 | var final_width = params.crop_width || params.dest_width; 700 | var final_height = params.crop_height || params.dest_height; 701 | 702 | // create canvas for holding preview 703 | var preview_canvas = document.createElement('canvas'); 704 | preview_canvas.width = final_width; 705 | preview_canvas.height = final_height; 706 | var preview_context = preview_canvas.getContext('2d'); 707 | 708 | // save for later use 709 | this.preview_canvas = preview_canvas; 710 | this.preview_context = preview_context; 711 | 712 | // scale for preview size 713 | if ((scaleX != 1.0) || (scaleY != 1.0)) { 714 | preview_canvas.style.webkitTransformOrigin = '0px 0px'; 715 | preview_canvas.style.mozTransformOrigin = '0px 0px'; 716 | preview_canvas.style.msTransformOrigin = '0px 0px'; 717 | preview_canvas.style.oTransformOrigin = '0px 0px'; 718 | preview_canvas.style.transformOrigin = '0px 0px'; 719 | preview_canvas.style.webkitTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 720 | preview_canvas.style.mozTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 721 | preview_canvas.style.msTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 722 | preview_canvas.style.oTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 723 | preview_canvas.style.transform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 724 | } 725 | 726 | // take snapshot, but fire our own callback 727 | this.snap( function() { 728 | // add preview image to dom, adjust for crop 729 | preview_canvas.style.position = 'relative'; 730 | preview_canvas.style.left = '' + self.container.scrollLeft + 'px'; 731 | preview_canvas.style.top = '' + self.container.scrollTop + 'px'; 732 | 733 | self.container.insertBefore( preview_canvas, self.peg ); 734 | self.container.style.overflow = 'hidden'; 735 | 736 | // set flag for user capture (use preview) 737 | self.preview_active = true; 738 | 739 | }, preview_canvas ); 740 | }, 741 | 742 | unfreeze: function() { 743 | // cancel preview and resume live video feed 744 | if (this.preview_active) { 745 | // remove preview canvas 746 | this.container.removeChild( this.preview_canvas ); 747 | delete this.preview_context; 748 | delete this.preview_canvas; 749 | 750 | // unflag 751 | this.preview_active = false; 752 | 753 | // re-flip if we unflipped before 754 | this.flip(); 755 | } 756 | }, 757 | 758 | flip: function() { 759 | // flip container horiz (mirror mode) if desired 760 | if (this.params.flip_horiz) { 761 | var sty = this.container.style; 762 | sty.webkitTransform = 'scaleX(-1)'; 763 | sty.mozTransform = 'scaleX(-1)'; 764 | sty.msTransform = 'scaleX(-1)'; 765 | sty.oTransform = 'scaleX(-1)'; 766 | sty.transform = 'scaleX(-1)'; 767 | sty.filter = 'FlipH'; 768 | sty.msFilter = 'FlipH'; 769 | } 770 | }, 771 | 772 | unflip: function() { 773 | // unflip container horiz (mirror mode) if desired 774 | if (this.params.flip_horiz) { 775 | var sty = this.container.style; 776 | sty.webkitTransform = 'scaleX(1)'; 777 | sty.mozTransform = 'scaleX(1)'; 778 | sty.msTransform = 'scaleX(1)'; 779 | sty.oTransform = 'scaleX(1)'; 780 | sty.transform = 'scaleX(1)'; 781 | sty.filter = ''; 782 | sty.msFilter = ''; 783 | } 784 | }, 785 | 786 | savePreview: function(user_callback, user_canvas) { 787 | // save preview freeze and fire user callback 788 | var params = this.params; 789 | var canvas = this.preview_canvas; 790 | var context = this.preview_context; 791 | 792 | // render to user canvas if desired 793 | if (user_canvas) { 794 | var user_context = user_canvas.getContext('2d'); 795 | user_context.drawImage( canvas, 0, 0 ); 796 | } 797 | 798 | // fire user callback if desired 799 | user_callback( 800 | user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ), 801 | canvas, 802 | context 803 | ); 804 | 805 | // remove preview 806 | if (this.params.unfreeze_snap) this.unfreeze(); 807 | }, 808 | 809 | snap: function(user_callback, user_canvas) { 810 | // use global callback and canvas if not defined as parameter 811 | if (!user_callback) user_callback = this.params.user_callback; 812 | if (!user_canvas) user_canvas = this.params.user_canvas; 813 | 814 | // take snapshot and return image data uri 815 | var self = this; 816 | var params = this.params; 817 | 818 | if (!this.loaded) return this.dispatch('error', new WebcamError("Webcam is not loaded yet")); 819 | // if (!this.live) return this.dispatch('error', new WebcamError("Webcam is not live yet")); 820 | if (!user_callback) return this.dispatch('error', new WebcamError("Please provide a callback function or canvas to snap()")); 821 | 822 | // if we have an active preview freeze, use that 823 | if (this.preview_active) { 824 | this.savePreview( user_callback, user_canvas ); 825 | return null; 826 | } 827 | 828 | // create offscreen canvas element to hold pixels 829 | var canvas = document.createElement('canvas'); 830 | canvas.width = this.params.dest_width; 831 | canvas.height = this.params.dest_height; 832 | var context = canvas.getContext('2d'); 833 | 834 | // flip canvas horizontally if desired 835 | if (this.params.flip_horiz) { 836 | context.translate( params.dest_width, 0 ); 837 | context.scale( -1, 1 ); 838 | } 839 | 840 | // create inline function, called after image load (flash) or immediately (native) 841 | var func = function() { 842 | // render image if needed (flash) 843 | if (this.src && this.width && this.height) { 844 | context.drawImage(this, 0, 0, params.dest_width, params.dest_height); 845 | } 846 | 847 | // crop if desired 848 | if (params.crop_width && params.crop_height) { 849 | var crop_canvas = document.createElement('canvas'); 850 | crop_canvas.width = params.crop_width; 851 | crop_canvas.height = params.crop_height; 852 | var crop_context = crop_canvas.getContext('2d'); 853 | 854 | crop_context.drawImage( canvas, 855 | Math.floor( (params.dest_width / 2) - (params.crop_width / 2) ), 856 | Math.floor( (params.dest_height / 2) - (params.crop_height / 2) ), 857 | params.crop_width, 858 | params.crop_height, 859 | 0, 860 | 0, 861 | params.crop_width, 862 | params.crop_height 863 | ); 864 | 865 | // swap canvases 866 | context = crop_context; 867 | canvas = crop_canvas; 868 | } 869 | 870 | // render to user canvas if desired 871 | if (user_canvas) { 872 | var user_context = user_canvas.getContext('2d'); 873 | user_context.drawImage( canvas, 0, 0 ); 874 | } 875 | 876 | // fire user callback if desired 877 | user_callback( 878 | user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ), 879 | canvas, 880 | context 881 | ); 882 | }; 883 | 884 | // grab image frame from userMedia or flash movie 885 | if (this.userMedia) { 886 | // native implementation 887 | context.drawImage(this.video, 0, 0, this.params.dest_width, this.params.dest_height); 888 | 889 | // fire callback right away 890 | func(); 891 | } 892 | else if (this.iOS) { 893 | var div = document.getElementById(this.container.id+'-ios_div'); 894 | var img = document.getElementById(this.container.id+'-ios_img'); 895 | var input = document.getElementById(this.container.id+'-ios_input'); 896 | // function for handle snapshot event (call user_callback and reset the interface) 897 | iFunc = function(event) { 898 | func.call(img); 899 | img.removeEventListener('load', iFunc); 900 | div.style.backgroundImage = 'none'; 901 | img.removeAttribute('src'); 902 | input.value = null; 903 | }; 904 | if (!input.value) { 905 | // No image selected yet, activate input field 906 | img.addEventListener('load', iFunc); 907 | input.style.display = 'block'; 908 | input.focus(); 909 | input.click(); 910 | input.style.display = 'none'; 911 | } else { 912 | // Image already selected 913 | iFunc(null); 914 | } 915 | } 916 | else { 917 | // flash fallback 918 | var raw_data = this.getMovie()._snap(); 919 | 920 | // render to image, fire callback when complete 921 | var img = new Image(); 922 | img.onload = func; 923 | img.src = 'data:image/'+this.params.image_format+';base64,' + raw_data; 924 | } 925 | 926 | return null; 927 | }, 928 | 929 | configure: function(panel) { 930 | // open flash configuration panel -- specify tab name: 931 | // "camera", "privacy", "default", "localStorage", "microphone", "settingsManager" 932 | if (!panel) panel = "camera"; 933 | this.getMovie()._configure(panel); 934 | }, 935 | 936 | flashNotify: function(type, msg) { 937 | // receive notification from flash about event 938 | switch (type) { 939 | case 'flashLoadComplete': 940 | // movie loaded successfully 941 | this.loaded = true; 942 | this.dispatch('load'); 943 | break; 944 | 945 | case 'cameraLive': 946 | // camera is live and ready to snap 947 | this.live = true; 948 | this.dispatch('live'); 949 | break; 950 | 951 | case 'error': 952 | // Flash error 953 | this.dispatch('error', new FlashError(msg)); 954 | break; 955 | 956 | default: 957 | // catch-all event, just in case 958 | // console.log("webcam flash_notify: " + type + ": " + msg); 959 | break; 960 | } 961 | }, 962 | 963 | b64ToUint6: function(nChr) { 964 | // convert base64 encoded character to 6-bit integer 965 | // from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding 966 | return nChr > 64 && nChr < 91 ? nChr - 65 967 | : nChr > 96 && nChr < 123 ? nChr - 71 968 | : nChr > 47 && nChr < 58 ? nChr + 4 969 | : nChr === 43 ? 62 : nChr === 47 ? 63 : 0; 970 | }, 971 | 972 | base64DecToArr: function(sBase64, nBlocksSize) { 973 | // convert base64 encoded string to Uintarray 974 | // from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding 975 | var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, 976 | nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, 977 | taBytes = new Uint8Array(nOutLen); 978 | 979 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { 980 | nMod4 = nInIdx & 3; 981 | nUint24 |= this.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; 982 | if (nMod4 === 3 || nInLen - nInIdx === 1) { 983 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { 984 | taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; 985 | } 986 | nUint24 = 0; 987 | } 988 | } 989 | return taBytes; 990 | }, 991 | 992 | upload: function(image_data_uri, target_url, callback) { 993 | // submit image data to server using binary AJAX 994 | var form_elem_name = this.params.upload_name || 'webcam'; 995 | 996 | // detect image format from within image_data_uri 997 | var image_fmt = ''; 998 | if (image_data_uri.match(/^data\:image\/(\w+)/)) 999 | image_fmt = RegExp.$1; 1000 | else 1001 | throw "Cannot locate image format in Data URI"; 1002 | 1003 | // extract raw base64 data from Data URI 1004 | var raw_image_data = image_data_uri.replace(/^data\:image\/\w+\;base64\,/, ''); 1005 | 1006 | // contruct use AJAX object 1007 | var http = new XMLHttpRequest(); 1008 | http.open("POST", target_url, true); 1009 | 1010 | // setup progress events 1011 | if (http.upload && http.upload.addEventListener) { 1012 | http.upload.addEventListener( 'progress', function(e) { 1013 | if (e.lengthComputable) { 1014 | var progress = e.loaded / e.total; 1015 | Webcam.dispatch('uploadProgress', progress, e); 1016 | } 1017 | }, false ); 1018 | } 1019 | 1020 | // completion handler 1021 | var self = this; 1022 | http.onload = function() { 1023 | if (callback) callback.apply( self, [http.status, http.responseText, http.statusText] ); 1024 | Webcam.dispatch('uploadComplete', http.status, http.responseText, http.statusText); 1025 | }; 1026 | 1027 | // create a blob and decode our base64 to binary 1028 | var blob = new Blob( [ this.base64DecToArr(raw_image_data) ], {type: 'image/'+image_fmt} ); 1029 | 1030 | // stuff into a form, so servers can easily receive it as a standard file upload 1031 | var form = new FormData(); 1032 | form.append( form_elem_name, blob, form_elem_name+"."+image_fmt.replace(/e/, '') ); 1033 | 1034 | // send data to server 1035 | http.send(form); 1036 | } 1037 | 1038 | }; 1039 | 1040 | Webcam.init(); 1041 | 1042 | if (typeof define === 'function' && define.amd) { 1043 | define( function() { return Webcam; } ); 1044 | } 1045 | else if (typeof module === 'object' && module.exports) { 1046 | module.exports = Webcam; 1047 | } 1048 | else { 1049 | window.Webcam = Webcam; 1050 | } 1051 | 1052 | }(window)); 1053 | --------------------------------------------------------------------------------