1、前台板块页面搭建

视图文件查询数据传输到前台界面:前台蓝图文件:apps/front/views.py
注意数据的收集方法和数据传输的类型。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import Blueprint, render_template, views, make_response, request, session  # make_response生成response对象,用于返回前端模板# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIO# 将图形验证码保存到Redis         restful输出信息弹窗
from utils import redis_captcha, restful# 验证码表单信息验证   登录、注册的Form表单信息收集
from .forms import SignupForm
from .forms import SigninForm# 导入前台用户模型
from .models import Front_User# 导入数据库连接 db
from exts import db# 确保URL安全的文件:utils/safe_url.py
from utils import safe_url# 导入轮播图模型BannerModel
from apps.cms.models import BannerModel
# 导入板块管理模型
from apps.cms.models import BoardModelfront_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,front是蓝图,在front_signup.html调用生成图形验证码时候需要用# BBS的首页界面路由
@front_bp.route("/")
def index():banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)   # 通过权重查询,每页显示4条boards = BoardModel.query.all()                                              # 查询板块中的所有board_id = request.args.get('board_id', type=int, default=None)              # get方法需要使用args,注意这里的数据类型需要改成intcontext = {                                                                  # 多种数据传输到前台界面"banners": banners,"boards": boards,"current_board_id": board_id,}return render_template("front/front_index.html", **context)            # 渲染到首页界面,查询数据传输到前台界面# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用print("发送的图形验证码是:{}".format(text))# 将图形验证码保存到Redis数据库中redis_captcha.redis_set(text.lower(), text.lower())  # redis_set中需要传参key和value,text没有唯一对应的key,只能都传参text# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 测试referrer的跳转
@front_bp.route("/test/")
def test():return render_template("front/front_test.html")# 用户注册类视图
class SingupView(views.MethodView):def get(self):# 图像验证码生成文件中返回两个参数   text, image# text, image = Captcha.gene_graph_captcha()# print(text)                      # 验证码# print(image)                     # 图形文件,图形类<PIL.Image.Image image mode=RGBA size=100x30 at 0x1EFC9000C88># 从当前页面跳转过来就是None   从其他页面跳转过来输出就是上一个页面信息     referrer是页面的跳转# print(request.referrer)                           # http://127.0.0.1:9999/test/return_to = request.referrer# 确保URL安全的文件:utils/safe_url.pyprint(safe_url.is_safe_url(return_to))              # 判断return_to是否来自站内,是否是安全url,防爬虫if return_to and return_to != request.url and safe_url.is_safe_url(return_to):       # 跳转的url不能是当前页面,request.url是当前的url地址return render_template("front/front_signup.html", return_to=return_to)           # return_to渲染到前端界面else:return render_template("front/front_signup.html")                                # 如果没获取url,直接渲染注册界面# 验证码的form表单信息提交验证def post(self):form = SignupForm(request.form)                       # 收集表单信息# 表单验证通过if form.validate():# 保存到数据库telephone = form.telephone.datausername = form.username.datapassword = form.password1.data                    # forms表单信息# 前台用户模型数据添加到数据库user = Front_User(telephone=telephone, username=username, password=password)db.session.add(user)db.session.commit()                                                   # 提交到数据库# 表单验证通过,提交到数据库成功return restful.success()else:return restful.params_error(message=form.get_error())                  # 表单信息验证出错# 用户登录的类视图
class SinginView(views.MethodView):def get(self):return_to = request.referrer                                                    # referrer是上一个urlif return_to and return_to != request.url and safe_url.is_safe_url(return_to):  # 跳转的url不能是当前页面,判断url是否安全return render_template("front/front_signin.html", return_to=return_to)      # return_to渲染到前端界面else:return render_template("front/front_signin.html")                           # 如果没获取url,直接渲染注册界面def post(self):form = SigninForm(request.form)                                            # 登录界面的Form表单信息if form.validate():                                                        # 表单信息存在# 收集form表单信息telephone = form.telephone.datapassword = form.password.dataremember = form.remember.datauser = Front_User.query.filter_by(telephone=telephone).first()         # 通过手机号验证该用户是否存在数据库if user and user.check_password(password):                             # 判断密码和用户是否正确session['front_user_id'] = user.id                                       # 用户的id存储到session中,用于登录验证if remember:                                                       # 如果remember状态是1# session持久化session.permanent = Truereturn restful.success()                                           # 成功else:return restful.params_error(message="手机号或者密码错误")           # 密码是、用户不正确else:return restful.params_error(message=form.get_error())                  # 表单信息不存在,输出异常信息# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("signup"))          # "signup"视图中不需要反斜线,决定了url_for的路由地址
front_bp.add_url_rule("/signin/", view_func=SinginView.as_view("signin"))          # "signin"视图中不需要反斜线

前台首页界面:templates/front/front_index.html,将前台视图文件中传输过来的数据进行循环渲染到界面。

{% extends 'front/front_base.html' %}{% block title %}
首页
{% endblock %}<!-- 模板继承 -->
{% block main_content %}
<!--   居中样式  -->
<div class="main-container"><div class="lg-container"><!-- bootstrop中复制来的轮播图  --><div id="carousel-example-generic" class="carousel slide" data-ride="carousel"><!-- 指令 --><ol class="carousel-indicators"><li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li><li data-target="#carousel-example-generic" data-slide-to="1"></li><li data-target="#carousel-example-generic" data-slide-to="2"></li></ol><!-- 轮播图 --><div class="carousel-inner" role="listbox"><!--    循环apps/front/views.py文件传输的banners数据      -->{% for banner in banners %}<!--    判断是否第一次循环      -->{% if loop.first %}<div class="item active">{% else %}<div class="item">{% endif %}<!--    轮播图路径,style="width: 300px;height: 300px"轮播图大小 --><img src="{{ banner.image_url }}" alt="..."style="width: 300px;height: 300px"><div class="carousel-caption"></div></div>{% endfor %}</div><!-- 轮播图左右切换按钮 --><a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span><span class="sr-only">Previous</span></a><a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><span class="sr-only">Next</span></a></div><!-- bootstrop中复制来的轮播图 代码结束   --><!--   帖子排序方式     --><div class="post-group"><ul class="post-group-head"><li class=""><a href="#">最新</a></li><li class=""><a href="#">精华帖子</a></li><li class=""><a href="#">点赞最多</a></li><li class=""><a href="#">评论最多</a></li></ul><ul class="post-list-group"><li><div class="author-avatar-group"><img src="#" alt=""></div><div class="post-info-group"><p class="post-title"><a href="#">Python文章</a><span class="label label-danger">精华帖</span></p><p class="post-info"><span>作者:xx</span><span>发表时间:2020-05-23</span><span>评论:0</span><span>阅读:0</span></p></div></li></ul><divstyle="text-align:center;"></div></div></div><!--      帖子标签内容      --><div class="sm-container"><divstyle="padding-bottom:10px;"><a href="#" class="btn btn-warning btn-block">发布帖子</a></div><div class="list-group"><a href="/" class="list-group-item active">所有板块</a><!--     循环显示前台蓝图文件:apps/front/views.py中传输的数据**context           -->{% for board in boards %}<!--         注意这里的current_board_id数据类型是int,才能与board.id相比较           -->{% if current_board_id == board.id %}<!--    url_for('front.index', board_id=board.id)每次点击跳转到front_index.html页面,即当前界面,且传输给一个board_id的参数值,由board.id赋值            --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item active">{{ board.name }}</a>{% else %}<!--  没被选中,即没有被传输相同的board.id,图标样式是class="list-group-item">    --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item">{{ board.name }}</a>{% endif %}{% endfor %}</div></div></div><!--  居中样式  -->{% endblock %}

2、文本编辑页面搭建

将上一篇博客中下载的富文本编辑器的压缩包进行解压,并且命名为editor,添加到项目的static文件下。推荐的Editor文本编辑器支持Markdown的显示,即左右分开显示源码文件和发表效果。

创建文本编辑的界面显示页面:templates/front/front_apost.html

{% extends 'front/front_base.html' %}{% block title %}发布帖子
{% endblock %}<!--补充内容-->
{% block head %}<!--  富文本编辑器关联样式  --><link rel="stylesheet" href="{{ url_for('static',filename='editormd/css/editormd.css') }}"/><script src="{{ url_for('static',filename='front/js/jquery.min.js') }}"></script><script src="{{ url_for('static',filename='editormd/editormd.min.js') }}"></script>
{% endblock %}<!-- 模板继承 -->
{% block main_content %}<div class="main-container"><form action="" method="post"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><div class="form-group"><div class="input-group"><span class="input-group-addon">标题</span><input type="text" class="form-control" name="title"></div></div><div class="form-group"><div class="input-group"><span class="input-group-addon">板块</span><select name="board_id" class="form-control"><option value="">django</option><option value="">flask</option></select></div></div><!--   绑定id为editor    --><div id="editor" class="form-group"><textarea name="content" id="TextContent" ></textarea></div><div class="form-group"><button class="btn btn-danger" id="submit-btn">发布帖子</button></div>
</form><script type="text/javascript">var testEditor;$(function () {testEditor = editormd("editor", {                                             // 绑定editorwidth: "100%",height: 640,syncScrolling: "single",path: "{{ url_for('static',filename='editormd/lib/') }}",                 // 存储路径// 上传图片imageUpload : true,imageFormats : [ "jpg", "jpeg", "gif", "png", "bmp", "webp" ],// 上传图片时指定调用后台的视图函数imageUploadURL : "",});});</script>
</div>{% endblock %}

关联文本编辑页面的样式:static/front/js/jquery.min.js

/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});if(typeof define === "function") { define(function () { return $.noConflict(); }); }

验证前台登录信息装饰器:apps/front/decorators.py

# -*- encoding: utf-8 -*-
"""
@File    : decorators.py
@Time    : 2020/5/12 22:38
@Author  : chen
验证前台登录信息装饰器:apps/front/decorators.py
"""
# 装饰器方法实现另一种判定后台用户当前界面是否是登录界面,不是就重定向到登录界面
from flask import session, g
from flask import redirect, url_for
# 双层装饰器修饰
from functools import wrapsdef login_required(func):@wraps(func)                                              # 没传参的时候,可以省略wraps修饰def inner(*args, **kwargs):                               # 内层函数if 'front_user_id' in session:return func(*args, **kwargs)else:return redirect(url_for("front.signin"))           # 重定向到前台登录界面return inner

前台首页界面关联发布帖子界面:前台首页界面templates/front/front_index.html

{% extends 'front/front_base.html' %}{% block title %}
首页
{% endblock %}<!-- 模板继承 -->
{% block main_content %}
<!--   居中样式  -->
<div class="main-container"><div class="lg-container"><!-- bootstrop中复制来的轮播图  --><div id="carousel-example-generic" class="carousel slide" data-ride="carousel"><!-- 指令 --><ol class="carousel-indicators"><li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li><li data-target="#carousel-example-generic" data-slide-to="1"></li><li data-target="#carousel-example-generic" data-slide-to="2"></li></ol><!-- 轮播图 --><div class="carousel-inner" role="listbox"><!--    循环apps/front/views.py文件传输的banners数据      -->{% for banner in banners %}<!--    判断是否第一次循环      -->{% if loop.first %}<div class="item active">{% else %}<div class="item">{% endif %}<!--    轮播图路径,style="width: 300px;height: 300px"轮播图大小 --><img src="{{ banner.image_url }}" alt="..."style="width: 300px;height: 300px"><div class="carousel-caption"></div></div>{% endfor %}</div><!-- 轮播图左右切换按钮 --><a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span><span class="sr-only">Previous</span></a><a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><span class="sr-only">Next</span></a></div><!-- bootstrop中复制来的轮播图 代码结束   --><!--   帖子排序方式     --><div class="post-group"><ul class="post-group-head"><li class=""><a href="#">最新</a></li><li class=""><a href="#">精华帖子</a></li><li class=""><a href="#">点赞最多</a></li><li class=""><a href="#">评论最多</a></li></ul><ul class="post-list-group"><li><div class="author-avatar-group"><img src="#" alt=""></div><div class="post-info-group"><p class="post-title"><a href="#">Python文章</a><span class="label label-danger">精华帖</span></p><p class="post-info"><span>作者:xx</span><span>发表时间:2020-05-23</span><span>评论:0</span><span>阅读:0</span></p></div></li></ul><divstyle="text-align:center;"></div></div></div><!--      帖子标签内容      --><div class="sm-container"><divstyle="padding-bottom:10px;"><!--       重定向到/apost/路由,文本编辑界面        --><a href="{{ url_for('front.apost') }}" class="btn btn-warning btn-block">发布帖子</a></div><div class="list-group"><a href="/" class="list-group-item active">所有板块</a><!--     循环显示前台蓝图文件:apps/front/views.py中传输的数据**context           -->{% for board in boards %}<!--         注意这里的current_board_id数据类型是int,才能与board.id相比较           -->{% if current_board_id == board.id %}<!--    url_for('front.index', board_id=board.id)每次点击跳转到front_index.html页面,即当前界面,且传输给一个board_id的参数值,由board.id赋值            --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item active">{{ board.name }}</a>{% else %}<!--  没被选中,即没有被传输相同的board.id,图标样式是class="list-group-item">    --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item">{{ board.name }}</a>{% endif %}{% endfor %}</div></div></div><!--  居中样式  -->
{% endblock %}

前台蓝图文件:apps/front/views.py,发布帖子前进行装饰器验证登录权限,创建帖子编辑路由。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import Blueprint, render_template, views, make_response, request, session  # make_response生成response对象,用于返回前端模板# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIO# 将图形验证码保存到Redis         restful输出信息弹窗
from utils import redis_captcha, restful# 验证码表单信息验证   登录、注册的Form表单信息收集
from .forms import SignupForm
from .forms import SigninForm# 导入前台用户模型
from .models import Front_User# 导入数据库连接 db
from exts import db# 确保URL安全的文件:utils/safe_url.py
from utils import safe_url# 导入轮播图模型BannerModel
from apps.cms.models import BannerModel
# 导入板块管理模型
from apps.cms.models import BoardModel# 导入前台界面权限验证装饰器
from .decorators import login_requiredfront_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,front是蓝图,在front_signup.html调用生成图形验证码时候需要用# BBS的首页界面路由
@front_bp.route("/")
def index():banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)   # 通过权重查询,每页显示4条boards = BoardModel.query.all()                                              # 查询板块中的所有board_id = request.args.get('board_id', type=int, default=None)              # get方法需要使用args,注意这里的数据类型需要改成intcontext = {                                                                  # 多种数据传输到前台界面"banners": banners,"boards": boards,"current_board_id": board_id,}return render_template("front/front_index.html", **context)            # 渲染到首页界面,查询数据传输到前台界面# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用print("发送的图形验证码是:{}".format(text))# 将图形验证码保存到Redis数据库中redis_captcha.redis_set(text.lower(), text.lower())  # redis_set中需要传参key和value,text没有唯一对应的key,只能都传参text# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 测试referrer的跳转
@front_bp.route("/test/")
def test():return render_template("front/front_test.html")# 用户注册类视图
class SingupView(views.MethodView):def get(self)return_to = request.referrer# 确保URL安全的文件:utils/safe_url.pyprint(safe_url.is_safe_url(return_to))              # 判断return_to是否来自站内,是否是安全url,防爬虫if return_to and return_to != request.url and safe_url.is_safe_url(return_to):       # 跳转的url不能是当前页面,request.url是当前的url地址return render_template("front/front_signup.html", return_to=return_to)           # return_to渲染到前端界面else:return render_template("front/front_signup.html")                                # 如果没获取url,直接渲染注册界面# 验证码的form表单信息提交验证def post(self):form = SignupForm(request.form)                       # 收集表单信息# 表单验证通过if form.validate():# 保存到数据库telephone = form.telephone.datausername = form.username.datapassword = form.password1.data                    # forms表单信息# 前台用户模型数据添加到数据库user = Front_User(telephone=telephone, username=username, password=password)db.session.add(user)db.session.commit()                                                   # 提交到数据库# 表单验证通过,提交到数据库成功return restful.success()else:return restful.params_error(message=form.get_error())                  # 表单信息验证出错# 用户登录的类视图
class SinginView(views.MethodView):def get(self):return_to = request.referrer                                                    # referrer是上一个urlif return_to and return_to != request.url and safe_url.is_safe_url(return_to):  # 跳转的url不能是当前页面,判断url是否安全return render_template("front/front_signin.html", return_to=return_to)      # return_to渲染到前端界面else:return render_template("front/front_signin.html")                           # 如果没获取url,直接渲染注册界面def post(self):form = SigninForm(request.form)                                            # 登录界面的Form表单信息if form.validate():                                                        # 表单信息存在# 收集form表单信息telephone = form.telephone.datapassword = form.password.dataremember = form.remember.datauser = Front_User.query.filter_by(telephone=telephone).first()         # 通过手机号验证该用户是否存在数据库if user and user.check_password(password):                             # 判断密码和用户是否正确session['front_user_id'] = user.id                                       # 用户的id存储到session中,用于登录验证if remember:                                                       # 如果remember状态是1# session持久化session.permanent = Truereturn restful.success()                                           # 成功else:return restful.params_error(message="手机号或者密码错误")           # 密码是、用户不正确else:return restful.params_error(message=form.get_error())                  # 表单信息不存在,输出异常信息#  帖子编辑提交  的类视图     富文本编辑
class PostView(views.MethodView):# 登录验证,实现帖子编辑前进行权限验证decorators = [login_required]def get(self):return render_template("front/front_apost.html")def post(self):pass# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("signup"))          # "signup"视图中不需要反斜线,决定了url_for的路由地址
front_bp.add_url_rule("/signin/", view_func=SinginView.as_view("signin"))          # "signin"视图中不需要反斜线
front_bp.add_url_rule("/apost/", view_func=PostView.as_view("apost"))              # 绑定帖子编辑提交路由

3、发布帖子信息前验证权限

保存g对象文件:apps/front/hooks.py

# -*- encoding: utf-8 -*-
"""
@File    : hooks.py
@Time    : 2020/5/13 9:36
@Author  : chen
保存g对象文件:apps/front/hooks.py
"""
from flask import request, session, url_for, redirect, g   # g对象全局变量gloabl,方便调用
from .views import front_bp
from .models import Front_User# 钩子函数 ,所有操作前执行该方法,判断当前界面是否是登录界面,不是就将url重定向到登录界面
@front_bp.before_request
def before_request():# 判断front_user_id是否登陆过,登录之后就返回用户名到前台页面if 'front_user_id' in session:user_id = session.get('front_user_id')        # 调用session中user_iduser = Front_User.query.get(user_id)    # 通过user_id查询到用户对象,方便前端界面调用对象中的字段属性if user:g.front_user = user                 # 赋值给g对象,全局变量g.front_user用于渲染到前台界面

前台模板界面:templates/front/front_base.html ,进行g对象渲染显示,权限管理

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>{% block title %} {% endblock %}</title><!--  bootstrap支持css,js的样式  --><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><!--  关联front_index.html的css样式  --><link href="{{ url_for('static', filename='front/css/front_index.css') }}" rel="stylesheet"><link href="{{ url_for('static', filename='front/css/front_base.css') }}" rel="stylesheet"><!--  模板继承,补充内容使用  -->{% block head %}{% endblock %}
</head>
<body>
<!--  bootstrop中复制来的导航条  -->
<nav class="navbar navbar-default"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse"data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">BBS论坛</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="/">首页 <span class="sr-only">(current)</span></a></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">搜索</button></form><ul class="nav navbar-nav navbar-right"><!--     判断是否登录,没有g对象信息就进行登录           -->{% if g.front_user %}<li class="dropdown"><!--   {{ g.front_user.username }}   显示g对象信息 --><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"aria-expanded="false">{{ g.front_user.username }}<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人中心</a></li><li><a href="#">设置</a></li><li><a href="#">退出登录</a></li></ul></li>{% else %}<!--    关联登录注册的url,front.signin是类视图中路由决定     --><li><a href="{{ url_for('front.signin') }}">登陆</a></li><li><a href="{{ url_for('front.signup') }}">注册</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
<!--   bootstrop中复制的导航条代码结束 --><!-- 模板继承 -->
{% block main_content %}{% endblock %}
</body>
</html>

前台蓝图文件:apps/front/views.py,导入验证前台登录信息装饰器decorators.py文件。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import Blueprint, render_template, views, make_response, request, session  # make_response生成response对象,用于返回前端模板# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIO# 将图形验证码保存到Redis         restful输出信息弹窗
from utils import redis_captcha, restful# 验证码表单信息验证   登录、注册的Form表单信息收集
from .forms import SignupForm
from .forms import SigninForm# 导入前台用户模型
from .models import Front_User# 导入数据库连接 db
from exts import db# 确保URL安全的文件:utils/safe_url.py
from utils import safe_url# 导入轮播图模型BannerModel
from apps.cms.models import BannerModel
# 导入板块管理模型
from apps.cms.models import BoardModel# 导入前台界面权限验证装饰器
from .decorators import login_requiredfront_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,front是蓝图,在front_signup.html调用生成图形验证码时候需要用# 权限验证  需要在front_bp产生后,再导入
from .hooks import before_request# BBS的首页界面路由
@front_bp.route("/")
def index():banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)   # 通过权重查询,每页显示4条boards = BoardModel.query.all()                                              # 查询板块中的所有board_id = request.args.get('board_id', type=int, default=None)              # get方法需要使用args,注意这里的数据类型需要改成intcontext = {                                                                  # 多种数据传输到前台界面"banners": banners,"boards": boards,"current_board_id": board_id,}return render_template("front/front_index.html", **context)            # 渲染到首页界面,查询数据传输到前台界面# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用print("发送的图形验证码是:{}".format(text))# 将图形验证码保存到Redis数据库中redis_captcha.redis_set(text.lower(), text.lower())  # redis_set中需要传参key和value,text没有唯一对应的key,只能都传参text# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 测试referrer的跳转
@front_bp.route("/test/")
def test():return render_template("front/front_test.html")# 用户注册类视图
class SingupView(views.MethodView):def get(self):return_to = request.referrer# 确保URL安全的文件:utils/safe_url.pyprint(safe_url.is_safe_url(return_to))              # 判断return_to是否来自站内,是否是安全url,防爬虫if return_to and return_to != request.url and safe_url.is_safe_url(return_to):       # 跳转的url不能是当前页面,request.url是当前的url地址return render_template("front/front_signup.html", return_to=return_to)           # return_to渲染到前端界面else:return render_template("front/front_signup.html")                                # 如果没获取url,直接渲染注册界面# 验证码的form表单信息提交验证def post(self):form = SignupForm(request.form)                       # 收集表单信息# 表单验证通过if form.validate():# 保存到数据库telephone = form.telephone.datausername = form.username.datapassword = form.password1.data                    # forms表单信息# 前台用户模型数据添加到数据库user = Front_User(telephone=telephone, username=username, password=password)db.session.add(user)db.session.commit()                                                   # 提交到数据库# 表单验证通过,提交到数据库成功return restful.success()else:return restful.params_error(message=form.get_error())                  # 表单信息验证出错# 用户登录的类视图
class SinginView(views.MethodView):def get(self):return_to = request.referrer                                                    # referrer是上一个urlif return_to and return_to != request.url and safe_url.is_safe_url(return_to):  # 跳转的url不能是当前页面,判断url是否安全return render_template("front/front_signin.html", return_to=return_to)      # return_to渲染到前端界面else:return render_template("front/front_signin.html")                           # 如果没获取url,直接渲染注册界面def post(self):form = SigninForm(request.form)                                            # 登录界面的Form表单信息if form.validate():                                                        # 表单信息存在# 收集form表单信息telephone = form.telephone.datapassword = form.password.dataremember = form.remember.datauser = Front_User.query.filter_by(telephone=telephone).first()         # 通过手机号验证该用户是否存在数据库if user and user.check_password(password):                             # 判断密码和用户是否正确# 'front_user_id'命名防止与后台验证session相同,会产生覆盖情况bugsession['front_user_id'] = user.id                                 # 用户的id存储到session中,用于登录验证if remember:                                                       # 如果remember状态是1# session持久化session.permanent = Truereturn restful.success()                                           # 成功else:return restful.params_error(message="手机号或者密码错误")           # 密码是、用户不正确else:return restful.params_error(message=form.get_error())                  # 表单信息不存在,输出异常信息#  帖子编辑提交  的类视图     富文本编辑
class PostView(views.MethodView):# 登录验证,实现帖子编辑前进行权限验证decorators = [login_required]def get(self):return render_template("front/front_apost.html")def post(self):pass# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("signup"))          # "signup"视图中不需要反斜线,决定了url_for的路由地址
front_bp.add_url_rule("/signin/", view_func=SinginView.as_view("signin"))          # "signin"视图中不需要反斜线
front_bp.add_url_rule("/apost/", view_func=PostView.as_view("apost"))              # 绑定帖子编辑提交路由

实现效果如下:

4、帖子模型搭建

虚拟环境添加依赖库:

前台模型文件 apps/front/models.py,创建帖子编辑提交模型,且实现将用户输入的content文件text类型转换成content_html的html文件方法。

# -*- encoding: utf-8 -*-
"""
@File    : models.py
@Time    : 2020/5/11 10:00
@Author  : chen
前台模型文件 apps/front/models.py
"""
# 前台管理的模型
from exts import db                   # 数据库连接
import shortuuid                      # 前台用户id加密
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash         # 导入密码加密,解密方法的库
import enum                           # 导入枚举
from markdown import markdown         # 导入帖子编辑的markdown显示功能库
import bleach        # 导入帖子编辑的markdown显示功能库# 性别选择的类
class GenderEnum(enum.Enum):MALE = 1FEMALE = 2SECRET = 3UNKNOW = 4#   前台用户模型类
class Front_User(db.Model):__tablename__ = "front_user"# id 类型不用db.Integer类型,使用String是为了防止爆破,同时使用shortuuid进行加密id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)telephone = db.Column(db.String(11), nullable=False, unique=True)             # 非空唯一username = db.Column(db.String(150), nullable=False)_password = db.Column(db.String(150), nullable=False)                         # 密码加密操作修改字段email = db.Column(db.String(50), unique=True)realname = db.Column(db.String(50))avatar = db.Column(db.String(150))                                            # 头像,二进制数据signatrue = db.Column(db.String(500))                                         # 签名gender = db.Column(db.Enum(GenderEnum), default=GenderEnum.UNKNOW)            # 性别枚举类,默认未知join_time = db.Column(db.DateTime, default=datetime.now)  # 默认当前时间# 修改密码加密操作,manage.py映射数据库时候,使用字段保持相同,由于字段太多,使用传参形式def __init__(self, *args, **kwargs):if 'password' in kwargs:                       # 如果传参中包含有passwordself.password = kwargs.get('password')     # 获取该参数值赋值给passwordkwargs.pop('password')                     # 模型参数中是_password,不是password,弹出# super(FrontUser, self).__init__(*args, **kwargs)   # python2的写法super().__init__(*args, **kwargs)# 密码加密操作@propertydef password(self):             # 密码取值return self._password@password.setter                # 密码加密def password(self, raw_password):self._password = generate_password_hash(raw_password)# 用于验证前台登录密码是否和数据库一致,raw_password是前台登录输入的密码def check_password(self, raw_password):result = check_password_hash(self.password, raw_password)  # 相当于用相同的hash加密算法加密raw_password,检测与数据库中是否一致return result# 帖子编辑提交模型
class PostModel(db.Model):__tablename__ = "post"id = db.Column(db.Integer, primary_key=True, autoincrement=True)title = db.Column(db.String(100), nullable=True)                                      # 帖子标题content = db.Column(db.Text, nullable=True)                                           # 帖子内容content_html = db.Column(db.Text)create_time = db.Column(db.DateTime, default=datetime.now)                            # 默认当前时间# 外键,用于查询排序board_id = db.Column(db.Integer, db.ForeignKey('cms_board.id'))      # 'cms_board.id'中cms_board是cms/models.py的表名author_id = db.Column(db.String(100), db.ForeignKey('front_user.id'))# 这里的id使用String是因为上面定义前台用户id时,使用的就是Str类型shortuuid# 反向查询属性,board = db.relationship("BoardModel", backref="posts")              # posts变成cms/models/BoardModel的属性author = db.relationship("Front_User", backref="posts")             # posts变成Front_User的属性# 实现将用户输入的content文件text类型转换成content_html的html文件,再进行存储@staticmethoddef content_to_content_html(target, value, oldvalue, initiator):# content_html文件中允许使用的标签集合allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'ol', 'pre','strong', 'ul', 'h1', 'h2', 'h3', 'p', 'img', 'video', 'div', 'iframe','p', 'br', 'span', 'hr', 'src', 'class']# content_html文件中允许使用的属性allowed_attrs = {'*': ['class'],'a': ['href', 'rel'],'img': ['src', 'alt']}# 目标文件content_html,由bleach库进行转换         markdown将源文件显示成html文件target.content_html = bleach.linkify(bleach.clean(markdown(value, output_format='html'),                                # output_format='html'输出格式为htmltags=allowed_tags, strip=True, attributes=allowed_attrs))             # strip=True去空格# 监听PostModel.content文件如果调用了set方法,就调用content_to_content_html方法进行转换格式到html文件
db.event.listen(PostModel.content, 'set', PostModel.content_to_content_html)

映射模型到数据库中文件: manage.py ,导入模型。
再将模型映射到数据库中,如下:

5、发布帖子功能

发布帖子的表单信息文件,前台表单信息:apps/front/forms.py

# -*- encoding: utf-8 -*-
"""
@File    : forms.py
@Time    : 2020/5/11 10:00
@Author  : chen
前台表单信息:apps/front/forms.py
"""
# 前台form表单信息
from wtforms import Form, StringField, IntegerField, ValidationError
from wtforms.validators import EqualTo, Email, InputRequired, Length, Regexp
from utils import random_captcha                                               # 随机生成验证码
from utils import redis_captcha                                                # 保存验证码到redis数据库中# 表单信息的父类文件
class BaseForm(Form):def get_error(self):message = self.errors.popitem()[1][0]          # 错误信息的收集,字典类型数据信息提取return message# 注册界面的Form表单类
class SignupForm(BaseForm):telephone = StringField(validators=[Regexp(r'1[345789]\d{9}', message="请输入正确格式的手机号")])sms_captcha = StringField(validators=[Regexp(r'\w{4}', message="请输入正确格式的验证码")])              # \w包含字母username = StringField(validators=[Length(min=2, max=15, message="请输入正确长度的用户名")])password1 = StringField(validators=[Regexp(r'[0-9a-zA-Z_\.]{3,20}', message="请输入正确格式的密码")])password2 = StringField(validators=[EqualTo('password1', message="两次输入密码不一致")])graph_captcha = StringField(validators=[Regexp(r'\w{4}', message="请输入正确格式的验证码")])# 验证手机验证码字段def validate_sms_captcha(self, field):telephone = self.telephone.datasms_captcha = self.sms_captcha.data                            # 获得表单信息sms_captcha_redis = redis_captcha.redis_get(telephone)         # redis数据库中根据手机号调验证码,进行判定是否相同# 判断用户输入的验证码和redis中取出的验证码是否相同if not sms_captcha_redis or sms_captcha_redis.lower() != sms_captcha.lower():raise ValidationError(message="手机验证码输入错误")# 图形验证码字段验证def validate_graph_captcha(self, field):graph_captcha = self.graph_captcha.data                                 # 表单信息收集graph_captcha_redis = redis_captcha.redis_get(graph_captcha)            # redis中是将验证码的text当做key来保存的,调用也是一样# 判定图形验证码是否一致if not graph_captcha_redis or graph_captcha_redis.lower() != graph_captcha.lower():# print("ceshi")raise ValidationError(message="图形验证码输入错误")# 登录界面的Form表单信息收集
class SigninForm(BaseForm):telephone = StringField(validators=[Regexp(r'1[345789]\d{9}', message="请输入正确格式的手机号")])password = StringField(validators=[Regexp(r'[0-9a-zA-Z_\.]{3,20}', message="请输入正确格式的密码")])remember = StringField(IntegerField())# 帖子的Form表单信息收集
class AddPostForm(BaseForm):title = StringField(validators=[InputRequired(message="请输入标题")])content = StringField(validators=[InputRequired(message="请输入内容")])board_id = StringField(validators=[InputRequired(message="请输入板块名称")])

前台蓝图文件:apps/front/views.py, 帖子编辑提交post方法编写。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import (Blueprint,render_template,views,make_response,                  # make_response生成response对象,用于返回前端模板request,session,g,
)# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIO# 将图形验证码保存到Redis         restful输出信息弹窗
from utils import redis_captcha, restful# 验证码表单信息验证
from .forms import (SignupForm,               # 注册的Form表单信息收集SigninForm,               # 登录的Form表单信息收集AddPostForm,              # 帖子提交表单信息
)# 导入前台用户模型
from .models import (Front_User,PostModel,
)# 导入数据库连接 db
from exts import db# 确保URL安全的文件:utils/safe_url.py
from utils import safe_urlfrom apps.cms.models import (BannerModel,                      # 导入后台轮播图模型BannerModelBoardModel,                       # 导入后台板块管理模型
)
# 导入前台界面权限验证装饰器
from .decorators import login_requiredfront_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,front是蓝图,在front_signup.html调用生成图形验证码时候需要用# 权限验证  需要在front_bp产生后,再导入
from .hooks import before_request# BBS的首页界面路由
@front_bp.route("/")
def index():banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)   # 通过权重查询,每页显示4条boards = BoardModel.query.all()                                              # 查询板块中的所有board_id = request.args.get('board_id', type=int, default=None)              # get方法需要使用args,注意这里的数据类型需要改成intcontext = {                                                                  # 多种数据传输到前台界面"banners": banners,"boards": boards,"current_board_id": board_id,}return render_template("front/front_index.html", **context)            # 渲染到首页界面,查询数据传输到前台界面# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用print("发送的图形验证码是:{}".format(text))# 将图形验证码保存到Redis数据库中redis_captcha.redis_set(text.lower(), text.lower())  # redis_set中需要传参key和value,text没有唯一对应的key,只能都传参text# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 测试referrer的跳转
@front_bp.route("/test/")
def test():return render_template("front/front_test.html")# 用户注册类视图
class SingupView(views.MethodView):def get(self):return_to = request.referrer# 确保URL安全的文件:utils/safe_url.pyprint(safe_url.is_safe_url(return_to))              # 判断return_to是否来自站内,是否是安全url,防爬虫if return_to and return_to != request.url and safe_url.is_safe_url(return_to):       # 跳转的url不能是当前页面,request.url是当前的url地址return render_template("front/front_signup.html", return_to=return_to)           # return_to渲染到前端界面else:return render_template("front/front_signup.html")                                # 如果没获取url,直接渲染注册界面# 验证码的form表单信息提交验证def post(self):form = SignupForm(request.form)                       # 收集表单信息# 表单验证通过if form.validate():# 保存到数据库telephone = form.telephone.datausername = form.username.datapassword = form.password1.data                    # forms表单信息# 前台用户模型数据添加到数据库user = Front_User(telephone=telephone, username=username, password=password)db.session.add(user)db.session.commit()                                                   # 提交到数据库# 表单验证通过,提交到数据库成功return restful.success()else:return restful.params_error(message=form.get_error())                  # 表单信息验证出错# 用户登录的类视图
class SinginView(views.MethodView):def get(self):return_to = request.referrer                                                    # referrer是上一个urlif return_to and return_to != request.url and safe_url.is_safe_url(return_to):  # 跳转的url不能是当前页面,判断url是否安全return render_template("front/front_signin.html", return_to=return_to)      # return_to渲染到前端界面else:return render_template("front/front_signin.html")                           # 如果没获取url,直接渲染注册界面def post(self):form = SigninForm(request.form)                                            # 登录界面的Form表单信息if form.validate():                                                        # 表单信息存在# 收集form表单信息telephone = form.telephone.datapassword = form.password.dataremember = form.remember.datauser = Front_User.query.filter_by(telephone=telephone).first()         # 通过手机号验证该用户是否存在数据库if user and user.check_password(password):                             # 判断密码和用户是否正确# 'front_user_id'命名防止与后台验证session相同,会产生覆盖情况bugsession['front_user_id'] = user.id                                 # 用户的id存储到session中,用于登录验证if remember:                                                       # 如果remember状态是1# session持久化session.permanent = Truereturn restful.success()                                           # 成功else:return restful.params_error(message="手机号或者密码错误")           # 密码是、用户不正确else:return restful.params_error(message=form.get_error())                  # 表单信息不存在,输出异常信息#  帖子编辑提交  的类视图     富文本编辑
class PostView(views.MethodView):# 登录验证,实现帖子编辑前进行权限验证decorators = [login_required]# 表单信息收集,传输def get(self):# 查询boards数据进行传输boards = BoardModel.query.all()                                           # boards是list类型return render_template("front/front_apost.html", boards=boards)           # boards数据传输到前端front_apost.html页面# 帖子的Form表单信息收集查询def post(self):form = AddPostForm(request.form)                     # 查询帖子提交的Form表单信息if form.validate():title = form.title.databoard_id = form.board_id.data                    # 收集表单中提交的信息content = form.content.data# 查询用户信息是否在数据库中存在board = BoardModel.query.get(board_id)if not board:return restful.params_error(message="没有这个版块名称")           # 数据库中不存在,返回异常信息# 数据库中board信息存在,传输数据到数据库表中,并修改名称post = PostModel(title=title, board_id=board_id, content=content)post.board = board                                                   # 外键中的信息修改赋值post.author = g.front_user                                           # g对象db.session.add(post)db.session.commit()return restful.success()                                             # 提交成功,为json数据else:return restful.params_error(message=form.get_error())# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("signup"))          # "signup"视图中不需要反斜线,决定了url_for的路由地址
front_bp.add_url_rule("/signin/", view_func=SinginView.as_view("signin"))          # "signin"视图中不需要反斜线
front_bp.add_url_rule("/apost/", view_func=PostView.as_view("apost"))              # 绑定帖子编辑提交路由

帖子编辑界面:templates/front/front_apost.html

{% extends 'front/front_base.html' %}{% block title %}发布帖子
{% endblock %}<!--补充内容-->
{% block head %}<!--  富文本编辑器关联样式  --><link rel="stylesheet" href="{{ url_for('static',filename='editormd/css/editormd.css') }}"/><script src="{{ url_for('static',filename='front/js/jquery.min.js') }}"></script><script src="{{ url_for('static',filename='editormd/editormd.min.js') }}"></script>
{% endblock %}<!-- 模板继承 -->
{% block main_content %}<div class="main-container"><form action="" method="post"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><div class="form-group"><div class="input-group"><span class="input-group-addon">标题</span><input type="text" class="form-control" name="title"></div></div><div class="form-group"><div class="input-group"><span class="input-group-addon">板块</span><!--     name="board_id"是帖子的Form表单信息中的字段       --><select name="board_id" class="form-control"><!--     boards数据循环渲染到前端页面,value="{{ board.id }}"中通过id的值传输{{ board.name }}str         -->{% for board in boards %}<option value="{{ board.id }}">{{ board.name }}</option>{% endfor %}</select></div></div><!--   绑定id为editor    --><div id="editor" class="form-group"><!--     name="content"是帖子的Form表单信息中的字段       --><textarea name="content" id="TextContent" ></textarea></div><div class="form-group"><button class="btn btn-danger" id="submit-btn">发布帖子</button></div>
</form><script type="text/javascript">var testEditor;$(function () {testEditor = editormd("editor", {                                             // 绑定editorwidth: "100%",height: 640,syncScrolling: "single",path: "{{ url_for('static',filename='editormd/lib/') }}",                 // 存储路径// 上传图片imageUpload : true,imageFormats : [ "jpg", "jpeg", "gif", "png", "bmp", "webp" ],// 上传图片时指定调用后台的视图函数imageUploadURL : "",});});</script>
</div>{% endblock %}

关联js样式文件:static/front/js/front_apost.js

// 帖子提交页面关联的js样式:static/front/js/front_apost.js
$(function () {// 提交按钮绑定方法$("#submit-btn").click(function (event) {event.preventDefault();var titleInput = $('input[name="title"]');                      // 收集Form表单填入的信息var boardSelect = $("select[name='board_id']");var contentText = $("textarea[name='content']");var title = titleInput.val();var board_id = boardSelect.val();var content = contentText.val();lgajax.post({'url': '/apost/',                               // 上传路由'data': {                                       // 表单信息'title': title,'content':content,'board_id': board_id},'success': function (data) {if(data['code'] == 200){lgalert.alertConfirm({'msg': '恭喜!帖子发表成功!',                      // 弹出提示框'cancelText': '回到首页','confirmText': '再发一篇','cancelCallback': function () {window.location = '/';                          // 返回首页路由},'confirmCallback': function () {titleInput.val("");                            // 再发一篇,将页面标题清空contentText.val("");                           // 再发一篇,将页面内容清空}});}else{lgalert.alertInfo(data['message']);}}});});
});var lgajax = {'get':function(args) {args['method'] = 'get';this.ajax(args);},'post':function(args) {args['method'] = 'post';this.ajax(args);},'ajax':function(args) {// 设置csrftokenthis._ajaxSetup();$.ajax(args);},'_ajaxSetup': function() {$.ajaxSetup({'beforeSend':function(xhr,settings) {if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {var csrftoken = $('meta[name=csrf-token]').attr('content');xhr.setRequestHeader("X-CSRFToken", csrftoken)}}});}
};

模板文件添加CSRF验证和提示框样式文件:templates/front/front_base.html

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><!--  在头文件中接收csrf信息  --><meta name="csrf-token" content="{{ csrf_token() }}"><title>{% block title %} {% endblock %}</title><!--  bootstrap支持css,js的样式  --><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><!--  关联front_index.html的css样式  --><link href="{{ url_for('static', filename='front/css/front_index.css') }}" rel="stylesheet"><link href="{{ url_for('static', filename='front/css/front_base.css') }}" rel="stylesheet"><!--  提示框的静态资源文件  --><link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}"><!-- 关联提示框的js样式  --><script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script><script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script><!--  模板继承,补充内容使用  -->{% block head %}{% endblock %}
</head>
<body>
<!--  bootstrop中复制来的导航条  -->
<nav class="navbar navbar-default"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse"data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">BBS论坛</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="/">首页 <span class="sr-only">(current)</span></a></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">搜索</button></form><ul class="nav navbar-nav navbar-right"><!--     判断是否登录,没有g对象信息就进行登录           -->{% if g.front_user %}<li class="dropdown"><!--   {{ g.front_user.username }}   显示g对象信息 --><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"aria-expanded="false">{{ g.front_user.username }}<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人中心</a></li><li><a href="#">设置</a></li><li><a href="#">退出登录</a></li></ul></li>{% else %}<!--    关联登录注册的url,front.signin是类视图中路由决定     --><li><a href="{{ url_for('front.signin') }}">登陆</a></li><li><a href="{{ url_for('front.signup') }}">注册</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
<!--   bootstrop中复制的导航条代码结束 --><!-- 模板继承 -->
{% block main_content %}{% endblock %}
</body>
</html>

实现效果如下:

6、帖子信息渲染到前后台页面

视图文件:apps/cms/views.py文件,数据库查询帖子信息,进行传输到后端页面渲染

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
视图文件:apps/cms/views.py文件
"""
# 蓝图文件:实现模块化应用,应用可以分解成一系列的蓝图   后端的类视图函数写在这个文件
from flask import (request, redirect, url_for,                      # 页面跳转redirect   request请求收集Blueprint, render_template, views, session,      # 定义类视图,显示模板文件jsonify, g                                       # jsonify强制转换成json数据
)
from exts import db, mail                            # 数据库中更新密码、邮箱等使用from apps.cms.forms import (LoginForm, ResetPwdForm,                        # ResetPwdForm修改密码的form信息ResetEmailForm,                                 # 导入forms.py文件中的邮箱验证的表单信息类AddBannerForm,                                  # 导入 添加轮播图 的表单信息UpdateBannerForm,                               # 导入 更新轮播图 的表单信息AddBoardsForm,                                  # 导入 增加板块管理 的表单信息UpdateBoardsForm,                               # 导入 编辑板块管理 的表单信息
)from apps.cms.models import (CMS_User,                                       # 后台用户模型CMSPersmission,                                 # CMSPersmission验证用户不同模块权限CMSRole,                                        # 用户角色模型BannerModel,                                    # 导入 轮播图模型BannerModelBoardModel,                                     # 导入 板块管理模型
)
# 导入 帖子 模型文件
from apps.front.models import PostModelfrom .decorators import permission_required            # 传参装饰器验证用户不同模块权限# 导入装饰器:判断当前界面是否是登录界面,不是就将url重定向到登录界面,一般不用,使用的主要是钩子函数
from .decorators import login_required# 导入restful.py中的访问网页状态码的函数          redis_captcha:redis存储、提取、删除验证码功能
from utils import restful, random_captcha, redis_captcha           # 随机生成验证码函数random_captcha()# 导入flask-mail中的Message
from flask_mail import Messagecms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前缀url_prefix# 钩子函数是在cms_bp创建之后才创建的,顺序在cms_bp创建之后
from .hooks import before_request@cms_bp.route("/")                                          # 后台界面
# @login_required             # 装饰器判定当前界面是否是登录界面,但是需要每个路由函数都要加该装饰器,比较麻烦,推荐使用钩子函数
def index():# return "cms index:后端类视图文件"return render_template('cms/cms_index.html')  # 登陆之后进入CMS后台管理界面# 用户注销登录
@cms_bp.route("/logout/")                              # 需要关联到cms/cms_index.html中的注销属性
def logout():# session清除user_iddel session['user_id']# 重定向到登录界面return redirect(url_for('cms.login'))             # 重定向(redirec)为把url变为重定向的url# 定义个人中心的路由
@cms_bp.route("/profile/")
def profile():return render_template("cms/cms_profile.html")   # 模板渲染(render_template)则不会改变url,模板渲染是用模板来渲染请求的url# 定义类视图,显示模板文件   用户登录功能实现
class LoginView(views.MethodView):def get(self, message=None):                                         # message=None时候不传输信息到cms_login.html页面return render_template("cms/cms_login.html", message=message)    # 针对post方法中同样要返回到cms_login.html页面进行代码简化# 用户登录操作验证def post(self):# 收集表单信息login_form = LoginForm(request.form)if login_form.validate():# 数据库验证email = login_form.email.datapassword = login_form.password.dataremember = login_form.remember.data# 查询数据库中的用户信息user = CMS_User.query.filter_by(email=email).first()    # 邮箱唯一,用于查询验证用户if user and user.check_password(password):              # 验证用户和密码是否都正确session['user_id'] = user.id                        # 查询到用户数据时,保存session的id到浏览器if remember:                                        # 如果用户点击了remember选择,在浏览器中进行数据持久化session.permanent = True                        # 数据持久化,默认31天,需要设置session_key在config.py中# 登录成功,跳转到后台首页return redirect(url_for('cms.index'))               # 在蓝图中必须加cms   跳转到index方法else:return self.get(message="邮箱或密码错误")             # 传参到get方法中,多加一个传输错误信息的参数到方法中else:return self.get(message=login_form.get_error())            # login_form是收集到的表单信息,信息提取放置到forms.py的父类中实现# 修改密码的类视图验证
class ResetPwd(views.MethodView):def get(self):return render_template('cms/cms_resetpwd.html')         # 模板渲染到cms_resetpwd.html# post提交密码修改def post(self):# 先审查旧密码是否与数据库中的信息相同form = ResetPwdForm(request.form)if form.validate():oldpwd = form.oldpwd.datanewpwd = form.newpwd.data# 对象user = g.cms_user# 将用户输入的密码进行加密检测是否与数据库中的相同if user.check_password(oldpwd):# 更新我的密码  将新密码赋值,此时的新密码已经经过验证二次密码是否一致user.password = newpwd         # user.password已经调用了models.py中的 @property装饰器进行密码加密# 数据库更新db.session.commit()# return jsonify({"code": 400, "message": "密码修改成功"})        # 代码改写为下面return restful.success("密码修改成功")             # 调用restful.py中定义的访问网页成功的函数else:return restful.params_error(message="旧密码输入错误")      # 参数错误else:return restful.params_error(message=form.get_error())       # 参数错误,信息的收集在forms.py的父类函数中实现  form是收集到的信息# 定义修改邮箱的类视图 验证
class ResetEmail(views.MethodView):def get(self):return render_template("cms/cms_resetemail.html")      # 返回到修改邮箱页面urldef post(self):form = ResetEmailForm(request.form)                    # 接收邮箱验证的form表单信息if form.validate():                                    # 验证表单信息是否通过email = form.email.data                            # 获取form表单中填写的邮箱地址g.cms_user.email = email                           # 数据库中的查询在apps/cms/hooks.py文件中确定了该用户的数据库信息,用全局对象g.cms_user修改邮箱db.session.commit()return restful.success()                           # 邮箱修改成功else:return restful.params_error(form.get_error())      # form是这个类中的所有表单信息# 发送测试邮件进行验证
@cms_bp.route("/send_email/")
def send_mail():message = Message('邮件发送', recipients=['727506892@qq.com'], body='测试邮件发送')   # 主题:邮件发送;收件人:recipients;邮件内容:测试邮件发送mail.send(message)                   # 发送邮件return "邮件已发送"# 邮件发送
class EmailCaptcha(views.MethodView):def get(self):                                  # 根据resetemail.js中的ajax方法来写函数,不需要post请求email = request.args.get('email')           # 查询email参数是否存在if not email:return restful.params_error('请传递邮箱参数')# 发送邮件,内容为一个验证码:4、6位数字英文组合captcha = random_captcha.get_random_captcha(4)            # 生成4位验证码message = Message('BBS论坛邮箱验证码', recipients=[email], body='您的验证码是:%s' % captcha)# 异常处理try:mail.send(message)except:return restful.server_error(message="服务器错误,邮件验证码未发送!")   # 发送异常,服务器错误# 验证码保存,一般有时效性,且频繁请求变化,所以保存在Redis中redis_captcha.redis_set(key=email, value=captcha)        # redis中都是键值对类型,存储验证码return restful.success("邮件验证码发送成功!")# 轮播图管理路由
@cms_bp.route("/banners/")
def banners():# 通过模型中定义的权重priority的倒叙来排序banners = BannerModel.query.order_by(BannerModel.priority.desc()).all()return render_template("cms/cms_banners.html", banners=banners)           # 传输banners数据到cms_banners.html界面渲染# 添加轮播图功能路由,且方法需要与static/cms/js/banners.js中绑定的方法POST相同
@cms_bp.route("/abanner/", methods=['POST'])
def abanner():form = AddBannerForm(request.form)                  # 接收添加轮播图的form表单信息if form.validate():name = form.name.dataimage_url = form.image_url.datalink_url = form.link_url.datapriority = form.priority.databanner = BannerModel(name=name, image_url=image_url, link_url=link_url, priority=priority)     # 轮播图模型db.session.add(banner)                                                                         # 提交数据库db.session.commit()return restful.success()                                                                       # 轮播图信息提交成功else:return restful.params_error(message=form.get_error())                                          # 表单信息错误# 修改 轮播图 路由,方法与static/cms/js/banners.js中绑定的方法POST相同
@cms_bp.route("/ubanner/", methods=['POST'])
def ubanner():# 修改根据banner_id查询再修改form = UpdateBannerForm(request.form)             # 表单信息UpdateBannerForm中的requestif form.validate():                                # 先查询页面表单信息是否存在banner_id = form.banner_id.data               # 收集用户输入的表单信息name = form.name.dataimage_url = form.image_url.datalink_url = form.link_url.datapriority = form.priority.databanner = BannerModel.query.get(banner_id)     # 通过轮播图的模型BannerModel的banner_id查询数据库中轮播图对象if banner:                                     # 再查询数据库对象数据是否存在banner.name = name                        # 将UpdateBannerForm中收集到的form信息命名给数据库中的banner对象banner.image_url = image_urlbanner.link_url = link_urlbanner.priority = prioritydb.session.commit()                       # 数据库信息直接提交修改即可,不用添加新的对象return restful.success()else:return restful.params_error(message=form.get_error())    # 表单信息错误# 删除  轮播图路由,路由命名与banners.js绑定
@cms_bp.route("/dbanner/", methods=['POST'])
def dbanner():# 修改根据banner_id查询再修改,获取post请求参数         get请求方式使用request.args.get()banner_id = request.form.get('banner_id')            # 获取表单数据,这里没有单独创建删除的Form表单,使用之前创建的if not banner_id:return restful.params_error(message="轮播图不存在")banner = BannerModel.query.get(banner_id)           # 根据banner_id查询数据库if banner:db.session.delete(banner)                       # 删除该bannerdb.session.commit()return restful.success()                        # 返回成功else:return restful.params_error("轮播图不存在")      # 根据banner_id查询数据库信息不存在# 帖子管理路由 ,需要和cms_base.js中命名的相同才可以
@cms_bp.route("/posts/")
@permission_required(CMSPersmission.POSTER)                # 传参装饰器验证不同用户不同模块权限
def posts():posts = PostModel.query.all()                          # 数据库查询帖子信息,进行传输到后端页面渲染return render_template("cms/cms_posts.html", posts=posts)# 评论管理路由
@cms_bp.route("/comments/")
@permission_required(CMSPersmission.COMMENTER)             # 传参装饰器验证不同用户不同模块权限
def comments():return render_template("cms/cms_comments.html")# 板块管理路由
@cms_bp.route("/boards/")
@permission_required(CMSPersmission.BOARDER)               # 传参装饰器验证不同用户不同模块权限
def boards():boards = BoardModel.query.all()                        # 数据库查询所有板块名称return render_template("cms/cms_boards.html", boards=boards)        # 数据渲染到cms_boards.html# 增加 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/aboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)               # 传参装饰器验证不同用户不同模块权限
def aboards():form = AddBoardsForm(request.form)                     # 表单信息传输过来,方便修改调用if form.validate():name = form.name.data                              # 表单信息收集board = BoardModel(name=name)                      # 添加信息到板块模型中db.session.add(board)db.session.commit()                                # 提交数据库return restful.success()                           # 数据库添加成功else:return restful.params_error(message=form.get_error())    # 表单信息错误# 编辑 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/uboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)             # 传参装饰器验证不同用户不同模块权限
def uboards():form = UpdateBoardsForm(request.form)                # 表单信息传输过来,方便修改调用if form.validate():board_id = form.board_id.data                    # 表单信息收集name = form.name.databoard = BoardModel.query.get(board_id)           # 根据表单中提交的board_id查询数据库中对象信息if board:board.name = name                            # 表单中提交的name命名给数据库中对象的名字db.session.commit()                          # 修改数据后提交数据库return restful.success()                     # 数据库修改成功else:return restful.params_error(message="没有这个分类板块")  # 数据库中对象信息不存在else:return restful.params_error(message=form.get_error())  # 表单信息错误# 删除 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/dboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)             # 传参装饰器验证不同用户不同模块权限
def dboards():board_id = request.form.get('board_id')                  # 查询表单信息中的board_id,这里没有单独创建删除的Form表单,使用之前创建的if not board_id:return restful.params_error(message="分类板块不存在")  # 表单信息不存在board = BoardModel.query.get(board_id)               # 根据表单中提交的board_id查询数据库中对象信息,注意.getif not board:return restful.params_error(message="分类板块不存在")  # 数据库中对象信息不存在db.session.delete(board)                             # 删除数据库中的信息db.session.commit()                                  # 提交数据库修改return restful.success()                             # 删除成功# 前台用户管理路由
@cms_bp.route("/fusers/")
@permission_required(CMSPersmission.FRONTUSER)             # 传参装饰器验证不同用户不同模块权限
def fuser():return render_template("cms/cms_fuser.html")# 后用户管理路由
@cms_bp.route("/cusers/")
@permission_required(CMSPersmission.CMSUSER)               # 传参装饰器验证不同用户不同模块权限
def cuser():return render_template("cms/cms_cuser.html")# 添加登录路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址# 类视图函数添加绑定路由  注意类视图需要修改ResetPwd.as_view('resetpwd')
cms_bp.add_url_rule("/resetpwd/", view_func=ResetPwd.as_view('resetpwd'))  # view_func 命名操作名字,/resetpwd/路由地址# 添加修改邮箱的类视图路由绑定,路由的命名和cms_base.js中的命名要相同,否则不关联,url=/resetemail/必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/resetemail/", view_func=ResetEmail.as_view('resetemail'))# 绑定路由,路由的命名和cms_base.js中的命名要相同,必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/email_captcha/", view_func=EmailCaptcha.as_view('email_captcha'))

后台管理帖子页面:templates/cms/cms_posts.html

<!--  后台管理帖子页面:templates/cms/cms_posts.html  --><!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}<!-- 页面标题 -->
{% block title %}帖子管理
{% endblock %}{% block head %}<script src="{{ url_for('static', filename='cms/js/posts.js') }}"></script>
{% endblock %}<!--  标题  -->
{% block page_title %}{{self.title()}}
{% endblock %}{% block content %}<table class="table table-bordered"><thead><tr><th>标题</th><th>发布时间</th><th>板块</th><th>作者</th><th>操作</th></tr></thead><tbody><!--   循环渲染帖子信息     -->{% for post in posts  %}<tr data-id="1" data-highlight=""><td><a target="_blank" href="#">{{ post.title }}</a></td><td>{{ post.create_time }}</td><!--    post.board是PostModel中的board反向查询属性,name是BoardModel中的name字段    --><td>{{ post.board.name }}</td><!--    post.author是PostModel中的author反向查询属性,username是Front_User中的username字段            --><td>{{ post.author.username }}</td><td><!-- <button class="btn btn-default btn-xs highlight-btn">取消加精</button> --><button class="btn btn-default btn-xs highlight-btn">加精</button><button class="btn btn-danger btn-xs">移除</button></td></tr>{% endfor %}</tbody></table>
{% endblock %}

关联后端帖子管理js样式文件:static/cms/js/posts.js

var lgajax = {'get':function(args) {args['method'] = 'get';this.ajax(args);},'post':function(args) {args['method'] = 'post';this.ajax(args);},'ajax':function(args) {// 设置csrftokenthis._ajaxSetup();$.ajax(args);},'_ajaxSetup': function() {$.ajaxSetup({'beforeSend':function(xhr,settings) {if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {var csrftoken = $('meta[name=csrf-token]').attr('content');xhr.setRequestHeader("X-CSRFToken", csrftoken)}}});}
};$(function () {$(".highlight-btn").click(function () {var self = $(this);var tr = self.parent().parent();var post_id = tr.attr("data-id");var highlight = parseInt(tr.attr("data-highlight"));var url = "";if(highlight){url = "/cms/uhpost/";}else{url = "/cms/hpost/";}lgajax.post({'url': url,'data': {'post_id': post_id},'success': function (data) {if(data['code'] == 200){lgalert.alertSuccessToast('操作成功!');setTimeout(function () {window.location.reload();},500);}else{zlalert.alertInfo(data['message']);}}});});
});$(function () {$(".btn-xs").click(function () {var self = $(this);var tr = self.parent().parent();var post_id = tr.attr("data-id");lgalert.alertConfirm({"msg":"您确定要删除这篇帖子吗?",'confirmCallback': function () {lgajax.post({'url': '/cms/dpost/','data':{'post_id': post_id},'success': function (data) {if(data['code'] == 200){window.location.reload();}else{lgalert.alertInfo(data['message']);}}})}});});
});

后台模板文件界面:templates/cms/cms_base.html,不同功能绑定不同路由文件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><!--  在头文件中接收csrf信息  --><meta name="csrf-token" content="{{ csrf_token() }}"><title>{% block title %}{% endblock %}</title><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><!--  关联本地的cms_base.css样式 后台管理界面CMS的样式 --><link rel="stylesheet" href="{{ url_for('static', filename='cms/css/cms_base.css') }}">
<!--  关联本地的cms_base.js样式 后台管理界面CMS的样式 --><script src="{{ url_for('static', filename='cms/js/cms_base.js') }}"></script><!--  提示框的静态资源文件  --><link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}">
<!-- 关联提示框的js样式  --><script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script><script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script><!--  预留空间,给之后的html文件进行修改调整  -->{% block head %}{% endblock %}</head>
<body><nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"><div class="container-fluid"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">论坛CMS管理系统</a></div><div id="navbar" class="navbar-collapse collapse"><ul class="nav navbar-nav navbar-right"><!--       从数据库中调用用户名,g对象全局调用g.cms_user对象  .username是该对象的一个字段属性      --><li><a href="#">{{ g.cms_user.username }}</a></li><!--  用户注销,关联到views.py中的@cms_bp.route("/logout/")路由,重定向到该路由      --><li><a href="{{ url_for('cms.logout') }}">注销</a></li></ul><form class="navbar-form navbar-right"><input type="text" class="form-control" placeholder="查找..."></form></div></div></nav><div class="container-fluid"><div class="row"><div class="col-sm-3 col-md-2 sidebar"><ul class="nav-sidebar"><li class="unfold"><a href="#">首页</a></li><li class="profile-li"><a href="#">个人中心<span></span></a><ul class="subnav"><!--          url重定向到/cms/profile/下   路由在views.py中定义了       --><li><a href="{{ url_for('cms.profile') }}">个人信息</a></li><!--         密码修改的url_for 重定向到/cms/resetpwd/  路由在views.py中定义了           --><li><a href="{{ url_for('cms.resetpwd') }}">修改密码</a></li><!--         重定向到修改邮箱的url_for=/cms/resetemail/        --><li><a href="{{ url_for('cms.resetemail') }}">修改邮箱</a></li></ul></li><!--  将全局变量的对象命名为user  -->{% set user = g.cms_user %}<!--        {{ url_for('cms.banners') }}绑定路由          --><li class="nav-group banner-manage"><a href="{{ url_for('cms.banners') }}">轮播图管理</a></li><!--    判断是否有权限进行管理后台,CMSPersmission.ALL_PERMISSION并没有传输过来,无法识别,需要用到钩子函数中的上下文管理器,在hooks.py中编写 -->{% if  user.has_permissions(CMSPersmission.POSTER) %}<li class="nav-group post-manage"><a href="{{ url_for('cms.posts') }}">帖子管理</a></li>{% endif %}{% if  user.has_permissions(CMSPersmission.COMMENTER) %}<li class="comments-manage"><a href="{{ url_for('cms.comments') }}">评论管理</a></li>{% endif %}{% if  user.has_permissions(CMSPersmission.BOARDER) %}<!--        {{ url_for('cms.boards') }}关联路由          --><li class="board-manage"><a href="{{ url_for('cms.boards') }}">板块管理</a></li>{% endif %}{% if  user.has_permissions(CMSPersmission.FRONTUSER) %}<li class="nav-group user-manage"><a href="{{ url_for('cms.fuser') }}">前台用户管理</a></li>{% endif %}{% if  user.has_permissions(CMSPersmission.CMSUSER) %}<li class="nav-group cmsuser-manage"><a href="{{ url_for('cms.cuser') }}">CMS用户管理</a></li>{% endif %}{% if  user.is_developer %}<li class="cmsrole-manage"><a href="{{ url_for('cms.profile') }}">CMS组管理</a></li>{% endif %}</ul></div><div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><h1>{% block page_title %}{% endblock %}</h1><div class="main_content">{% block content %}{% endblock %}</div></div></div></div>
</body>
</html>

前台渲染帖子信息显示:templates/front/front_index.html

{% extends 'front/front_base.html' %}{% block title %}
首页
{% endblock %}<!-- 模板继承 -->
{% block main_content %}
<!--   居中样式  -->
<div class="main-container"><div class="lg-container"><!-- bootstrop中复制来的轮播图  --><div id="carousel-example-generic" class="carousel slide" data-ride="carousel"><!-- 指令 --><ol class="carousel-indicators"><li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li><li data-target="#carousel-example-generic" data-slide-to="1"></li><li data-target="#carousel-example-generic" data-slide-to="2"></li></ol><!-- 轮播图 --><div class="carousel-inner" role="listbox"><!--    循环apps/front/views.py文件传输的banners数据      -->{% for banner in banners %}<!--    判断是否第一次循环      -->{% if loop.first %}<div class="item active">{% else %}<div class="item">{% endif %}<!--    轮播图路径,style="width: 300px;height: 300px"轮播图大小 --><img src="{{ banner.image_url }}" alt="..."style="width: 300px;height: 300px"><div class="carousel-caption"></div></div>{% endfor %}</div><!-- 轮播图左右切换按钮 --><a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span><span class="sr-only">Previous</span></a><a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><span class="sr-only">Next</span></a></div><!-- bootstrop中复制来的轮播图 代码结束   --><!--   帖子排序方式     --><div class="post-group"><ul class="post-group-head"><li class=""><a href="#">最新</a></li><li class=""><a href="#">精华帖子</a></li><li class=""><a href="#">点赞最多</a></li><li class=""><a href="#">评论最多</a></li></ul><ul class="post-list-group"><!--         循环帖子信息,首页渲染           -->{% for post in posts %}<li><div class="author-avatar-group"><img src="#" alt=""></div><div class="post-info-group"><p class="post-title"><a href="#">{{ post.title }}</a><span class="label label-danger">精华帖</span></p><p class="post-info"><!-- post模型中的author外键调用Front_User中的username信息  --><span>作者:{{ post.author.username }}</span><span>发表时间:{{ post.create_time }}</span><span>评论:0</span><span>阅读:0</span></p></div></li>{% endfor %}</ul><divstyle="text-align:center;"></div></div></div><!--      帖子标签内容      --><div class="sm-container"><divstyle="padding-bottom:10px;"><!--       重定向到/apost/路由,文本编辑界面        --><a href="{{ url_for('front.apost') }}" class="btn btn-warning btn-block">发布帖子</a></div><div class="list-group"><a href="/" class="list-group-item active">所有板块</a><!--     循环显示前台蓝图文件:apps/front/views.py中传输的数据**context           -->{% for board in boards %}<!--         注意这里的current_board_id数据类型是int,才能与board.id相比较           -->{% if current_board_id == board.id %}<!--    url_for('front.index', board_id=board.id)每次点击跳转到front_index.html页面,即当前界面,且传输给一个board_id的参数值,由board.id赋值            --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item active">{{ board.name }}</a>{% else %}<!--  没被选中,即没有被传输相同的board.id,图标样式是class="list-group-item">    --><a href="{{ url_for('front.index', board_id=board.id ) }}" class="list-group-item">{{ board.name }}</a>{% endif %}{% endfor %}</div></div></div><!--  居中样式  -->
{% endblock %}

实现效果如下:

Flask项目实战——10—(前台板块页面搭建、文本编辑页面搭建、发布帖子信息前验证权限、帖子模型搭建、发布帖子功能、帖子信息渲染到前后台页面)相关推荐

  1. Flask项目实战——6—(前台用户模型、前台登录注册、图形验证码、手机短信验证码、添加表单验证短信验证码请求)

    1.前台用户模型 前台用户模型定义 创建前台模型文件 apps/front/models.py # -*- encoding: utf-8 -*- """ @File : ...

  2. Flask项目实战——12—(帖子评论和阅读数功能、帖子分类功能实现、项目完善、引入Celery异步发送邮件)

    1.帖子评论和阅读数功能 添加阅读数量字段:前台模型文件 apps/front/models.py # 帖子编辑提交模型 class PostModel(db.Model):__tablename__ ...

  3. 【机器学习项目实战10例目录】项目详解 + 数据集 + 完整源码

    前言 大家好,我是阿光. 本专栏整理了<机器学习项目实战10例>,内包含了各种不同的入门级机器学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集. 正在更新中~ ✨

  4. Python全栈(八)Flask项目实战之10.前台发布帖子和后台帖子管理页面搭建

    文章目录 一.前台板块页面搭建 二.发布帖子页面搭建 三.前台帖子模型创建 四.文章的发布 1.基本实现 2.项目优化 (1)功能优化--Markdown编辑上传上传本地图片 (2)代码优化--抽离A ...

  5. flask项目实战记录一:搭建flask框架

    Flask是一个Web框架,它的作用主要是为了开发Web应用程序,Web,就是网页,所以学会了Flask,自己写可以写网站, 除了Flask框架,Python还有很多web框架,例如Django,To ...

  6. 搜索推荐系统[10]项目实战系列Z1:手把手教学(商品搜索系统、学术文献检索)语义检索系统搭建、召回排序模型详解。

    搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排).系统架构.常见问题.算法项目实战总结.技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排 ...

  7. python视频网站项目_Python Flask 项目实战—构建微电影视频网站

    下单后联系在线客服索取下载链接 课程章节 第1章 课程介绍 1-1 课程导学 1-2 课程介绍 第2章 准备开发环境 2-1 windows环境搭建 2-2 mac os环境搭建 2-3 virtua ...

  8. Flask项目实战——7—(Redis数据库存储验证码信息、验证登录界面的表单信息、注册功能实现、登录实现)

    推荐一个API平台:聚合数据 1.Redis数据库存储验证码信息 保存手机验证码到Redis数据库 公有视图文件:apps/common/views.py # -*- encoding: utf-8 ...

  9. Flask项目实战--管理系统

    项目内容: 1,班级管理 班级列表 班级增加 班级修改 查看班级中的学生 2,学生管理的增删改查 学生列表 学生添加 学生修改 3,权限的增加 角色的增加 角色列表 查看用户权限 添加用户权限 减少用 ...

最新文章

  1. 使用C++实现Socket编程传输协议文件(包括大文件)
  2. 关于一些朋友想做在线教育的回复和分享
  3. 最常用的15大Eclipse开发快捷键技巧
  4. 【PAT】A1074 Reversing Linked List ***
  5. python多线程实现生产者消费者_用Python实现多线程“生产者-消费者”模型的简单例子...
  6. python爬取mysql_Python如何爬取51cto数据并存入MySQL
  7. AIX的完整形式是什么?
  8. 删除IE 下输入后的清除小叉叉
  9. python文字识别 训练_Python3.x:pytesseract识别率提高(样本训练)
  10. 物联网 ToB 的背后,开发者应了解什么?| CSDN 博文精选
  11. 基于模拟退火算法求解TSP问题(JAVA)
  12. 学习记录:python快递价格计算器练习7.12
  13. 微信支付V3微信公众号支付PHP教程(thinkPHP5公众号支付)/JSSDK的使用
  14. 关于自学es6的笔记上传
  15. 【融创同智 竞促发展】2019AIIA杯人工智能巡回赛及专项赛工作通气会在京成功召开
  16. 数据可视化项目(一)
  17. Docker run 容器处于created状态问题
  18. 在window系统安装虚拟linux系统以及搭建web环境教程
  19. JZ高中OJ 1420.佳肴
  20. Android studio 电话号码归属地查询app简易版

热门文章

  1. ipad已停用恢复系统 2种方法
  2. 名人名言 托尔斯泰 富兰克林 马克思 罗兰 培根
  3. live server
  4. 七星彩长奖表图_够力七星彩奖表长条图最新版
  5. “高效的隐私保护的张量分解方法研究”学习笔记(上)
  6. 给树莓派挂载硬盘并搭载PT下载器
  7. DDD 领域驱动设计 - 架构(分层/六边形/RESTful)
  8. PEP8 Python代码编程规范(摘录整理于官网)
  9. unity暂停游戏,退出游戏
  10. SpringBoot 优雅实现动态数据源切换配置