From 539cc4d4a64df1fd80c93f60ad6dc8a2dc7f7372 Mon Sep 17 00:00:00 2001 From: Angelika Brown Date: Fri, 28 Mar 2025 18:17:56 -0400 Subject: [PATCH 01/24] Every post is private now --- .idea/CircusCircus.iml | 2 +- .idea/misc.xml | 5 ++++- forum/templates/viewpost.html | 30 ++++++++++++++++++++++-------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.idea/CircusCircus.iml b/.idea/CircusCircus.iml index 6eba17a..dc4d93b 100644 --- a/.idea/CircusCircus.iml +++ b/.idea/CircusCircus.iml @@ -10,7 +10,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 0f40f39..e2445a2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,9 @@ - + + + diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index 3f489ca..0470c3c 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -2,8 +2,10 @@ {% block body %} {{ path|safe}} +
+{% if current_user.is_authenticated %} {{post.title}}
{{ post.user.username }} @@ -17,7 +19,14 @@ {{post.content}}
+ {% else %} + Login or register to view this post. + {% endif %} +
+ + +

@@ -26,7 +35,7 @@
- + {% if current_user.is_authenticated %} @@ -35,26 +44,33 @@ Login or register to make a comment {% endif %}
+ + + {%if comments%}
+{% if current_user.is_authenticated %} {% for comment in comments %} -
- ({{ comment.user.username }}) - + ({{ comment.user.username }}) -
{{ comment.content }}
- +
{{ comment.get_time_string() }}

- {% endfor %} +{% else %} + Login or register to view comments + {% endif %}
+ + {% endif %} {% endblock %} - - From 962778b8f9cba68084e01be58ab54397ebbf4c43 Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Sat, 29 Mar 2025 15:00:02 -0400 Subject: [PATCH 02/24] A profile page you can view if logged in. --- .DS_Store | Bin 0 -> 6148 bytes .idea/inspectionProfiles/Project_Default.xml | 6 ++++ forum/app.py | 4 +-- forum/models.py | 36 +++++++++++++++++++ forum/routes.py | 30 ++++++++++++++++ forum/templates/header.html | 8 ++++- forum/templates/user.html | 25 +++++++++++++ forum/templates/viewpost.html | 6 ++-- 8 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 .DS_Store create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 forum/templates/user.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bce60af7d6d7e1b646313e41baab387728bab72a GIT binary patch literal 6148 zcmeHKL2uJA82#LJTS^7$0i<1!B5|Ecfx(2hl+sCXKtd~m1EA2fVU3pBrAb>*HK}L# z5BvqL{1W~PCwQOjL6a7tT@V`nB>Oq`drAE4*f9}_)}()fs76E{5@Y!SvNgf&oK~V_ zYc2tWzDJL`)T04K6s)jag>%3;@UJ<*-)tW3Z3GuG5hE1;xjL!oyog zN-0ARB;G74@W1_uAI{Q|?d_Lvl*DP)XnYl=jq>K!wzut7y;q%qnsu^nHc4CE@e96s zs#Fr5mb>AzIGP2u%MX;yy0MJLS`hamP+mTdWnax&Y9jlo)^<#TSMe%AZD&5;+rPc* z?=|*McK!L?n+=q2-8ng_cvr4nzjqiMCqt<|pbrRvwN}SPi>JVztoShPjAf$a5!zVi z(G%@!Pmfzt^r^(W{3svN0mi;fkAZ7OLeGm&OY@?J`$zN^;}+}E5LXi8l0u!)JIeAS zLp)2C^;=L)pr}JJ$aD2PS>~2$s_#B6@Ysp9ymKuYcM*v_M)jCk-p9R+V7Mm6q2GN2LMhY}iVUHNX$WiZGUen^j zpplcXhYw+&EbIwI=%?fUzNV9C8g#jHz&Vh2V8dM2c>h26{rNvHa#zj)=fJsgK$P2^ zb_-pyd+SQ)c(3)5UL$cZZ(&eNklF267kDe)LDGRXp9{dI#f3rCAl#3Dw!vl2fxqg& E4+qi?5&!@I literal 0 HcmV?d00001 diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ac21435 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/forum/app.py b/forum/app.py index 4d8a828..30c63df 100644 --- a/forum/app.py +++ b/forum/app.py @@ -6,8 +6,8 @@ from . import create_app app = create_app() -app.config['SITE_NAME'] = 'Schooner' -app.config['SITE_DESCRIPTION'] = 'a schooner forum' +app.config['SITE_NAME'] = 'BillyBob' +app.config['SITE_DESCRIPTION'] = 'a BillyBob forum no bobs or billies' app.config['FLASK_DEBUG'] = 1 def init_site(): diff --git a/forum/models.py b/forum/models.py index 8add9ae..d2d564f 100644 --- a/forum/models.py +++ b/forum/models.py @@ -17,6 +17,7 @@ class User(UserMixin, db.Model): admin = db.Column(db.Boolean, default=False) posts = db.relationship("Post", backref="user") comments = db.relationship("Comment", backref="user") + # replies = db.relationship("Reply", backref="user") def __init__(self, email, username, password): self.email = email @@ -115,6 +116,41 @@ def get_time_string(self): self.savedresponce = "Just a moment ago!" return self.savedresponce +# class Reply(db.Model): +# id = db.Column(db.Integer, primary_key=True) +# content = db.Column(db.Text) +# postdate = db.Column(db.DateTime) +# user_id = db.Column(db.Integer, db.ForeignKey('user.id')) +# comment_id = db.Column(db.Integer, db.ForeignKey("comment.id")) +# +# lastcheck = None +# savedresponce = None +# def __init__(self, content, postdate): +# self.content = content +# self.postdate = postdate +# def get_time_string(self): +# #this only needs to be calculated every so often, not for every request +# #this can be a rudamentary chache +# now = datetime.datetime.now() +# if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: +# self.lastcheck = now +# else: +# return self.savedresponce +# +# diff = now - self.postdate +# seconds = diff.total_seconds() +# if seconds / (60 * 60 * 24 * 30) > 1: +# self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" +# elif seconds / (60 * 60 * 24) > 1: +# self.savedresponce = " " + str(int(seconds / (60* 60 * 24))) + " days ago" +# elif seconds / (60 * 60) > 1: +# self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" +# elif seconds / (60) > 1: +# self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" +# else: +# self.savedresponce = "Just a moment ago!" +# return self.savedresponce + def error(errormessage): return "" + errormessage + "" diff --git a/forum/routes.py b/forum/routes.py index 75993e5..19a4160 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -117,6 +117,21 @@ def comment(): db.session.commit() return redirect("/viewpost?post=" + str(post_id)) +# @login_required +# @rt.route('/action_reply', methods=['POST', 'GET']) #attempt to mimic comment as a reply +# def reply(): +# comment_id = int(request.args.get("Comment")) +# comment = Post.query.filter(Comment.id == comment_id).first() +# if not comment: +# return error("That post does not exist!") +# content = request.form['content'] +# postdate = datetime.datetime.now() +# reply = Reply(content, postdate) +# current_user.reply.append(reply) +# post.reply.append(reply) +# db.session.commit() +# return redirect("/viewpost?post=" + str(post_id)) + @login_required @rt.route('/action_post', methods=['POST']) def action_post(): @@ -145,3 +160,18 @@ def action_post(): db.session.commit() return redirect("/viewpost?post=" + str(post.id)) +@login_required +@rt.route('/user') +def user(): + user_id = request.args.get('user_id') + + if user_id: + user = User.query.get_or_404(user_id) + posts = Post.query.filter(Post.user_id == user.id).all() + return render_template('user.html', user=user, posts=posts) + elif current_user.is_authenticated: + return redirect(url_for('user', user_id=current_user.id)) + else: + return redirect ('/loginform') + + diff --git a/forum/templates/header.html b/forum/templates/header.html index 9403a0d..6ac0568 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -5,4 +5,10 @@ {% else %} Click here to login or register! {% endif %} -
\ No newline at end of file + + +{% if current_user.is_authenticated %} +
+ Profile +
+{% endif %} \ No newline at end of file diff --git a/forum/templates/user.html b/forum/templates/user.html new file mode 100644 index 0000000..3023640 --- /dev/null +++ b/forum/templates/user.html @@ -0,0 +1,25 @@ +{% extends 'layout.html' %} +{% block body %} +{{ path|safe }} + + {% if current_user.is_authenticated %} +

Welcome, {{ user.username }}

+

These are your posts:

+ {% if posts %} +
    + {% for post in posts %} +
  • + {{post.title}} +

    {{ post.content }}

    +
  • + {% endfor %} +
+ {% else %} +

You have no posts at this time.

+ {% endif %} + + {% else %} + Click here to login or register! + {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index 3f489ca..89db1c5 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -1,7 +1,7 @@ {% extends 'layout.html' %} {% block body %} {{ path|safe}} - +

Test1

{{post.title}} @@ -38,10 +38,10 @@ {%if comments%}
{% for comment in comments %} - +
- ({{ comment.user.username }}) - + ({{ comment.user.username }}) -
{{ comment.content }} From 16c23e51ae587e0c153e48d8684f52f14cf19eaa Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Sat, 29 Mar 2025 15:10:10 -0400 Subject: [PATCH 03/24] attempt 2 --- forum/templates/viewpost.html | 1 - 1 file changed, 1 deletion(-) diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index 89db1c5..3823674 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -1,7 +1,6 @@ {% extends 'layout.html' %} {% block body %} {{ path|safe}} -

Test1

{{post.title}} From 5b15909dbbcd980178d4ecf028be5b85f3affdda Mon Sep 17 00:00:00 2001 From: KunleAdeyanju Date: Sat, 29 Mar 2025 17:23:05 -0400 Subject: [PATCH 04/24] getting somewhere --- forum/.DS_Store | Bin 0 -> 6148 bytes forum/static/style.css | 4 +-- forum/templates/.DS_Store | Bin 0 -> 6148 bytes forum/templates/createpost.html | 7 ++-- forum/templates/header.html | 16 +++++++-- forum/templates/layout.html | 59 ++++++++++++++++++++++---------- forum/templates/viewpost.html | 6 ++-- 7 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 forum/.DS_Store create mode 100644 forum/templates/.DS_Store diff --git a/forum/.DS_Store b/forum/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 -
+ +
-
+
- +
{% endblock %} \ No newline at end of file diff --git a/forum/templates/header.html b/forum/templates/header.html index 9403a0d..de2f78b 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -1,5 +1,17 @@ -{{ config.SITE_NAME }}{% if config.SITE_DESCRIPTION %} - {% endif %} {{ config.SITE_DESCRIPTION }} -
-
- +
+
@@ -29,7 +29,7 @@ {% if current_user.is_authenticated %} - + {% else %} Login or register to make a comment From 8af03111289f4ac601bce3c73efd3ad13a17c2ea Mon Sep 17 00:00:00 2001 From: isiah-a Date: Sat, 29 Mar 2025 17:25:27 -0400 Subject: [PATCH 05/24] Able to get markdown formatting to show up on posts --- forum/__init__.py | 3 +++ forum/app.py | 5 ++--- forum/models.py | 1 + forum/routes.py | 11 ++++++++--- forum/templates/createpost.html | 2 +- forum/templates/header.html | 2 +- forum/templates/viewpost.html | 2 +- 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/forum/__init__.py b/forum/__init__.py index c10b0f3..8c9ceac 100644 --- a/forum/__init__.py +++ b/forum/__init__.py @@ -1,9 +1,12 @@ from flask import Flask from forum.routes import rt +from flask_pagedown import PageDown + def create_app(): """Construct the core application.""" app = Flask(__name__, instance_relative_config=False) + pagedown = PageDown(app) app.config.from_object('config.Config') # I think more blueprints might be used to break routes up into things like # post_routes diff --git a/forum/app.py b/forum/app.py index 4d8a828..94925f6 100644 --- a/forum/app.py +++ b/forum/app.py @@ -5,9 +5,8 @@ from . import create_app app = create_app() - -app.config['SITE_NAME'] = 'Schooner' -app.config['SITE_DESCRIPTION'] = 'a schooner forum' +app.config['SITE_NAME'] = 'Not Schooner' +app.config['SITE_DESCRIPTION'] = 'NOT a schooner forum' app.config['FLASK_DEBUG'] = 1 def init_site(): diff --git a/forum/models.py b/forum/models.py index 8add9ae..ae4c879 100644 --- a/forum/models.py +++ b/forum/models.py @@ -2,6 +2,7 @@ from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin import datetime +import markdown # create db here so it can be imported (with the models) into the App object. from flask_sqlalchemy import SQLAlchemy diff --git a/forum/routes.py b/forum/routes.py index 75993e5..b808fe5 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -2,9 +2,11 @@ from flask_login import current_user, login_user, logout_user from flask_login.utils import login_required import datetime +import markdown from flask import Blueprint, render_template, request, redirect, url_for from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error from forum.user import username_taken, email_taken, valid_username +from markupsafe import Markup ## # This file needs to be broken up into several, to make the project easier to work on. @@ -117,6 +119,7 @@ def comment(): db.session.commit() return redirect("/viewpost?post=" + str(post_id)) + @login_required @rt.route('/action_post', methods=['POST']) def action_post(): @@ -128,18 +131,20 @@ def action_post(): user = current_user title = request.form['title'] content = request.form['content'] + content_html = markdown.markdown(content) + print(f"Generated HTML: {content_html}") #check for valid posting errors = [] retry = False if not valid_title(title): errors.append("Title must be between 4 and 140 characters long!") retry = True - if not valid_content(content): + if not valid_content(content_html): errors.append("Post must be between 10 and 5000 characters long!") retry = True if retry: - return render_template("createpost.html",subforum=subforum, errors=errors) - post = Post(title, content, datetime.datetime.now()) + return render_template("createpost.html", content_html=Markup(content_html), subforum=subforum, errors=errors) + post = Post(title, content_html, datetime.datetime.now()) subforum.posts.append(post) user.posts.append(post) db.session.commit() diff --git a/forum/templates/createpost.html b/forum/templates/createpost.html index 85947a6..129976e 100644 --- a/forum/templates/createpost.html +++ b/forum/templates/createpost.html @@ -13,7 +13,7 @@
-
+
diff --git a/forum/templates/header.html b/forum/templates/header.html index 9403a0d..c973d1f 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -1,4 +1,4 @@ -{{ config.SITE_NAME }}{% if config.SITE_DESCRIPTION %} - {% endif %} {{ config.SITE_DESCRIPTION }} + {{ config.SITE_NAME }}{% if config.SITE_DESCRIPTION %} - {% endif %} {{ config.SITE_DESCRIPTION }}
- {{post.content}} + {{post.content | safe }}
From 9e14d37c189cb02488cecb3a4cc93c10585e80eb Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Sat, 29 Mar 2025 18:01:42 -0400 Subject: [PATCH 06/24] Change password button --- forum/routes.py | 25 ++++++++++++++++++++++++- forum/templates/change_password.html | 15 +++++++++++++++ forum/templates/header.html | 1 + forum/templates/user.html | 13 +++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 forum/templates/change_password.html diff --git a/forum/routes.py b/forum/routes.py index 19a4160..ac3a248 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -168,10 +168,33 @@ def user(): if user_id: user = User.query.get_or_404(user_id) posts = Post.query.filter(Post.user_id == user.id).all() - return render_template('user.html', user=user, posts=posts) + comments = Comment.query.filter(Comment.user_id == user.id).all() + return render_template('user.html', user=user, posts=posts, comments= comments) elif current_user.is_authenticated: return redirect(url_for('user', user_id=current_user.id)) else: return redirect ('/loginform') +@login_required +@rt.route('/change_password', methods=['POST', 'GET']) +def change_password(): + if request.method == 'POST': + old_password = request.form['old_password'] + new_password = request.form['new_password'] + confirm_password = request.form['confirm_password'] + + if not check_password_hash(current_user.password, old_password): + flash('Old password does not match records.') + return redirect(url_for('change_password')) + + if new_password != confirm_password: + flash('New and confirm do not match.') + return redirect(url_for('change_password')) + + current_user.password = generate_password_hash(new_password) + db.session.commit() + + flash('Password has been updated') + return redirect(url_for('user')) + return render_template('change_password.html') \ No newline at end of file diff --git a/forum/templates/change_password.html b/forum/templates/change_password.html new file mode 100644 index 0000000..53d6458 --- /dev/null +++ b/forum/templates/change_password.html @@ -0,0 +1,15 @@ +{% extends 'layout.html' %} +{% block body %} +{{ path|safe }} + +
+

Change Password

+
+
+
+
+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/forum/templates/header.html b/forum/templates/header.html index 6ac0568..191379f 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -10,5 +10,6 @@ {% if current_user.is_authenticated %} {% endif %} \ No newline at end of file diff --git a/forum/templates/user.html b/forum/templates/user.html index 3023640..7b443bf 100644 --- a/forum/templates/user.html +++ b/forum/templates/user.html @@ -17,6 +17,19 @@

These are your posts:

{% else %}

You have no posts at this time.

{% endif %} +

These are your comments:

+ {% if comments %} +
    + {% for comment in comments %} +
  • + {{comment.postdate}} +

    {{ comment.content }}

    +
  • + {% endfor %} +
+ {% else %} +

You have no comments at this time.

+ {% endif %} {% else %} Click here to login or register! From f34d77930b2ef46f8174f2aa7fb42d4ce2f8933c Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Sun, 30 Mar 2025 11:09:16 -0400 Subject: [PATCH 07/24] Fixed flash for password --- forum/routes.py | 21 +++++++++++---------- forum/templates/change_password.html | 13 +++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/forum/routes.py b/forum/routes.py index ac3a248..3038637 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -1,10 +1,11 @@ -from flask import render_template, request, redirect, url_for +from flask import render_template, request, redirect, url_for, flash from flask_login import current_user, login_user, logout_user from flask_login.utils import login_required import datetime from flask import Blueprint, render_template, request, redirect, url_for from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error from forum.user import username_taken, email_taken, valid_username +from werkzeug.security import generate_password_hash, check_password_hash ## # This file needs to be broken up into several, to make the project easier to work on. @@ -171,7 +172,7 @@ def user(): comments = Comment.query.filter(Comment.user_id == user.id).all() return render_template('user.html', user=user, posts=posts, comments= comments) elif current_user.is_authenticated: - return redirect(url_for('user', user_id=current_user.id)) + return redirect(url_for('routes.user', user_id=current_user.id)) else: return redirect ('/loginform') @@ -183,18 +184,18 @@ def change_password(): new_password = request.form['new_password'] confirm_password = request.form['confirm_password'] - if not check_password_hash(current_user.password, old_password): - flash('Old password does not match records.') - return redirect(url_for('change_password')) + if not check_password_hash(current_user.password_hash, old_password): + flash('Old password does not match records.', 'danger') + return redirect(url_for('routes.change_password')) if new_password != confirm_password: - flash('New and confirm do not match.') - return redirect(url_for('change_password')) + flash('New and confirm do not match.', 'warning') + return redirect(url_for('routes.change_password')) - current_user.password = generate_password_hash(new_password) + current_user.password_hash = generate_password_hash(new_password) db.session.commit() - flash('Password has been updated') - return redirect(url_for('user')) + flash('Password has been updated', 'success') + return redirect(url_for('routes.change_password')) return render_template('change_password.html') \ No newline at end of file diff --git a/forum/templates/change_password.html b/forum/templates/change_password.html index 53d6458..c69253f 100644 --- a/forum/templates/change_password.html +++ b/forum/templates/change_password.html @@ -4,6 +4,19 @@

Change Password

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} + {% endwith %} +


From 1f9840fb29c6d0dca4ddb414555b46aae386c21f Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Sun, 30 Mar 2025 14:07:19 -0400 Subject: [PATCH 08/24] replies working properly updated profile to show too --- .DS_Store | Bin 6148 -> 6148 bytes forum/models.py | 76 ++++++++++++++++++---------------- forum/routes.py | 29 ++++++------- forum/templates/user.html | 18 +++++++- forum/templates/viewpost.html | 59 +++++++++++++++++++------- 5 files changed, 114 insertions(+), 68 deletions(-) diff --git a/.DS_Store b/.DS_Store index bce60af7d6d7e1b646313e41baab387728bab72a..e50103f538609ab71aa7d188d253780d287697b7 100644 GIT binary patch delta 103 zcmZoMXffEZkVTx8A&nuQp@^ZBAvdYKxF9JfKZ${XVb^2@)S|p>69WSs1rwvm wjqEavJ(K6L%S-nH6(DIV3ogpb$>Pjj0k!=ck^lez delta 71 zcmZoMXffEZkY#c|YYKlRLmophLkUA7kWOYuo&16AxGV<)nrK;YQC?1dUOEE<1LNj* Nto%%y**X650|4!U6jlHL diff --git a/forum/models.py b/forum/models.py index d2d564f..6a6ccff 100644 --- a/forum/models.py +++ b/forum/models.py @@ -17,7 +17,6 @@ class User(UserMixin, db.Model): admin = db.Column(db.Boolean, default=False) posts = db.relationship("Post", backref="user") comments = db.relationship("Comment", backref="user") - # replies = db.relationship("Reply", backref="user") def __init__(self, email, username, password): self.email = email @@ -87,6 +86,8 @@ class Comment(db.Model): postdate = db.Column(db.DateTime) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) post_id = db.Column(db.Integer, db.ForeignKey("post.id")) + replies = db.relationship('Reply', backref='comment', cascade='all, delete-orphan') + lastcheck = None savedresponce = None @@ -116,40 +117,45 @@ def get_time_string(self): self.savedresponce = "Just a moment ago!" return self.savedresponce -# class Reply(db.Model): -# id = db.Column(db.Integer, primary_key=True) -# content = db.Column(db.Text) -# postdate = db.Column(db.DateTime) -# user_id = db.Column(db.Integer, db.ForeignKey('user.id')) -# comment_id = db.Column(db.Integer, db.ForeignKey("comment.id")) -# -# lastcheck = None -# savedresponce = None -# def __init__(self, content, postdate): -# self.content = content -# self.postdate = postdate -# def get_time_string(self): -# #this only needs to be calculated every so often, not for every request -# #this can be a rudamentary chache -# now = datetime.datetime.now() -# if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: -# self.lastcheck = now -# else: -# return self.savedresponce -# -# diff = now - self.postdate -# seconds = diff.total_seconds() -# if seconds / (60 * 60 * 24 * 30) > 1: -# self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" -# elif seconds / (60 * 60 * 24) > 1: -# self.savedresponce = " " + str(int(seconds / (60* 60 * 24))) + " days ago" -# elif seconds / (60 * 60) > 1: -# self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" -# elif seconds / (60) > 1: -# self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" -# else: -# self.savedresponce = "Just a moment ago!" -# return self.savedresponce +class Reply(db.Model): + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text) + postdate = db.Column(db.DateTime) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + comment_id = db.Column(db.Integer, db.ForeignKey("comment.id")) + + user = db.relationship('User', backref='replies') + + + lastcheck = None + savedresponce = None + def __init__(self, content, postdate, user_id, comment_id): + self.content = content + self.postdate = postdate + self.user_id = user_id + self.comment_id = comment_id + def get_time_string(self): + #this only needs to be calculated every so often, not for every request + #this can be a rudamentary chache + now = datetime.datetime.now() + if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: + self.lastcheck = now + else: + return self.savedresponce + + diff = now - self.postdate + seconds = diff.total_seconds() + if seconds / (60 * 60 * 24 * 30) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" + elif seconds / (60 * 60 * 24) > 1: + self.savedresponce = " " + str(int(seconds / (60* 60 * 24))) + " days ago" + elif seconds / (60 * 60) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" + elif seconds / (60) > 1: + self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" + else: + self.savedresponce = "Just a moment ago!" + return self.savedresponce def error(errormessage): return "" + errormessage + "" diff --git a/forum/routes.py b/forum/routes.py index 3038637..46db246 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -3,7 +3,7 @@ from flask_login.utils import login_required import datetime from flask import Blueprint, render_template, request, redirect, url_for -from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error +from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error, Reply from forum.user import username_taken, email_taken, valid_username from werkzeug.security import generate_password_hash, check_password_hash @@ -118,20 +118,16 @@ def comment(): db.session.commit() return redirect("/viewpost?post=" + str(post_id)) -# @login_required -# @rt.route('/action_reply', methods=['POST', 'GET']) #attempt to mimic comment as a reply -# def reply(): -# comment_id = int(request.args.get("Comment")) -# comment = Post.query.filter(Comment.id == comment_id).first() -# if not comment: -# return error("That post does not exist!") -# content = request.form['content'] -# postdate = datetime.datetime.now() -# reply = Reply(content, postdate) -# current_user.reply.append(reply) -# post.reply.append(reply) -# db.session.commit() -# return redirect("/viewpost?post=" + str(post_id)) +@login_required +@rt.route('/action_reply/comment/', methods=['POST']) #attempt to mimic comment as a reply +def action_reply(comment_id): + comment = Comment.query.get_or_404(comment_id) + content = request.form['content'] + postdate = datetime.datetime.now() + reply = Reply(content=content, postdate=postdate, user_id=current_user.id, comment_id=comment_id) + db.session.add(reply) + db.session.commit() + return redirect(url_for('routes.viewpost', post=comment.post_id)) @login_required @rt.route('/action_post', methods=['POST']) @@ -170,7 +166,8 @@ def user(): user = User.query.get_or_404(user_id) posts = Post.query.filter(Post.user_id == user.id).all() comments = Comment.query.filter(Comment.user_id == user.id).all() - return render_template('user.html', user=user, posts=posts, comments= comments) + replies = Reply.query.filter(Reply.user_id == user.id).all() + return render_template('user.html', user=user, posts=posts, comments= comments, replies= replies) elif current_user.is_authenticated: return redirect(url_for('routes.user', user_id=current_user.id)) else: diff --git a/forum/templates/user.html b/forum/templates/user.html index 7b443bf..f5bab63 100644 --- a/forum/templates/user.html +++ b/forum/templates/user.html @@ -17,12 +17,12 @@

These are your posts:

{% else %}

You have no posts at this time.

{% endif %} -

These are your comments:

+

These are your comments:

{% if comments %}
    {% for comment in comments %}
  • - {{comment.postdate}} + {{comment.post.title}}

    {{ comment.content }}

  • {% endfor %} @@ -31,6 +31,20 @@

    These are your comments:

    You have no comments at this time.

    {% endif %} +

    These are your replies:

    + {% if replies %} + + {% else %} +

    You have no replies at this time.

    + {% endif %} + {% else %} Click here to login or register! {% endif %} diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index 3823674..7bae3f8 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -24,11 +24,8 @@
- - - {% if current_user.is_authenticated %} - + {% else %} Login or register to make a comment @@ -49,24 +46,56 @@
{{ comment.get_time_string() }}
+ + {% if current_user.is_authenticated %} +
+ + +
+ {% endif %} + + {% if comment.replies %} +
+ {% for reply in comment.replies %} +
+
+ ({{ reply.user.username }}) - +
+
+ {{ reply.content }} +
+
+ {{ reply.get_time_string() }} +
+
+ {% endfor %} +
+ {% endif %} +

{% endfor %}
{% endif %} + From 86323d9bcaef712a773b336cd994cb71deb725be Mon Sep 17 00:00:00 2001 From: Isiah-A Date: Sun, 30 Mar 2025 16:09:46 -0400 Subject: [PATCH 09/24] Add files via upload --- CircusCircus/Heroku.md | 32 +++ CircusCircus/Procfile | 1 + CircusCircus/README.md | 54 +++++ .../__pycache__/config.cpython-311.pyc | Bin 0 -> 886 bytes CircusCircus/config.py | 18 ++ CircusCircus/forum/__init__.py | 24 +++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1297 bytes .../forum/__pycache__/app.cpython-311.pyc | Bin 0 -> 3644 bytes .../forum/__pycache__/models.cpython-311.pyc | Bin 0 -> 9862 bytes .../forum/__pycache__/routes.cpython-311.pyc | Bin 0 -> 10697 bytes .../forum/__pycache__/user.cpython-311.pyc | Bin 0 -> 1480 bytes CircusCircus/forum/app.py | 56 +++++ CircusCircus/forum/models.py | 142 +++++++++++++ CircusCircus/forum/routes.py | 152 ++++++++++++++ CircusCircus/forum/static/bootstrap.min.css | 5 + CircusCircus/forum/static/style.css | 191 ++++++++++++++++++ CircusCircus/forum/templates/createpost.html | 21 ++ CircusCircus/forum/templates/header.html | 8 + CircusCircus/forum/templates/layout.html | 30 +++ CircusCircus/forum/templates/login.html | 26 +++ CircusCircus/forum/templates/subforum.html | 63 ++++++ CircusCircus/forum/templates/subforums.html | 18 ++ CircusCircus/forum/templates/viewpost.html | 77 +++++++ CircusCircus/forum/user.py | 25 +++ CircusCircus/instance/circuscircus.db | Bin 0 -> 40960 bytes CircusCircus/requirements.txt | 11 + CircusCircus/run.sh | 6 + 27 files changed, 960 insertions(+) create mode 100644 CircusCircus/Heroku.md create mode 100644 CircusCircus/Procfile create mode 100644 CircusCircus/README.md create mode 100644 CircusCircus/__pycache__/config.cpython-311.pyc create mode 100644 CircusCircus/config.py create mode 100644 CircusCircus/forum/__init__.py create mode 100644 CircusCircus/forum/__pycache__/__init__.cpython-311.pyc create mode 100644 CircusCircus/forum/__pycache__/app.cpython-311.pyc create mode 100644 CircusCircus/forum/__pycache__/models.cpython-311.pyc create mode 100644 CircusCircus/forum/__pycache__/routes.cpython-311.pyc create mode 100644 CircusCircus/forum/__pycache__/user.cpython-311.pyc create mode 100644 CircusCircus/forum/app.py create mode 100644 CircusCircus/forum/models.py create mode 100644 CircusCircus/forum/routes.py create mode 100644 CircusCircus/forum/static/bootstrap.min.css create mode 100644 CircusCircus/forum/static/style.css create mode 100644 CircusCircus/forum/templates/createpost.html create mode 100644 CircusCircus/forum/templates/header.html create mode 100644 CircusCircus/forum/templates/layout.html create mode 100644 CircusCircus/forum/templates/login.html create mode 100644 CircusCircus/forum/templates/subforum.html create mode 100644 CircusCircus/forum/templates/subforums.html create mode 100644 CircusCircus/forum/templates/viewpost.html create mode 100644 CircusCircus/forum/user.py create mode 100644 CircusCircus/instance/circuscircus.db create mode 100644 CircusCircus/requirements.txt create mode 100644 CircusCircus/run.sh diff --git a/CircusCircus/Heroku.md b/CircusCircus/Heroku.md new file mode 100644 index 0000000..e50e01b --- /dev/null +++ b/CircusCircus/Heroku.md @@ -0,0 +1,32 @@ +# Run on Heroku.com + +- get a Heroku account +- create a new heroku app +- download the heroku CLI +- login from terminal (where you will run the app) +- `git push heroku main` + +other things.... + +heroku git:remote -a circuscircus +heroku addons:create heroku-postgresql:hobby-dev -a circuscircus + +## Changes in 2021 + +If you want to run postgresql on your local machine to test first. + +added `heroku` branch, and the postgres support. + +**NOTA BENE:** DO NOT USE _whatever_ as your database password. Pick something else. +``` +CREATE USER ccuser WITH PASSWORD 'whatever'; +ALTER ROLE ccuser SET client_encoding TO 'utf8'; +ALTER ROLE ccuser SET default_transaction_isolation TO 'read committed'; +ALTER ROLE ccuser SET timezone TO 'UTC'; + +CREATE DATABASE circuscircus; +GRANT ALL PRIVILEGES ON DATABASE circuscircus TO ccuser; +``` +(Remember, this stuff is ALL done ot the postgres running on your DEV box, not the app on the internet at Heroku) + +edit the forum.py file and switch dbs \ No newline at end of file diff --git a/CircusCircus/Procfile b/CircusCircus/Procfile new file mode 100644 index 0000000..e8ccc30 --- /dev/null +++ b/CircusCircus/Procfile @@ -0,0 +1 @@ +web: gunicorn forum.app:app diff --git a/CircusCircus/README.md b/CircusCircus/README.md new file mode 100644 index 0000000..b103388 --- /dev/null +++ b/CircusCircus/README.md @@ -0,0 +1,54 @@ +# CircusCircus +This is a minimal forum written in python with Flask. It supports only the bare minumum of features to allow discussions, including user accounts, threads, and comments. + +On first run, the default subforums will be created. Although custom subforums are not supported through any user interface, it is possible to modify forum/setup.py to create custom subforums. + +## Create a Github Organization + +- create an org +- make all group members collaborators +- clone/branch from group's org's repo. +- maintain two branches,`main` & `dev` (plus a different branch for each group member) + +## Features to Add + +- divide `forum.py` into multiple modules (eg. `posts`, `comments`, `auth (login etc)`) +- migrate from sqlite3 to MySQL +- comments on each post (many comments to one post) +- like/dislike/heart/etc emojis on posts +- direct messages from one user to another +- insert pix links and/or video links +- a nice style based on Bootstrap + - a logo on every page + - copyright, about etc on footer of each page +- user settings +- public/private posts + - public posts can be seen by people not logged in + - private posts can only be seen by users logged in +- posts can be plain text or markdown + +## Changes in 2020 + +I had to make a bunch of changes in this code to get it running. Took far longer than it should. +But now, if I have it right, you need to clone this and then + +This currently puts a sqlite3 db in the /tmp directory. + +``` +$ python3.11 -m venv venv +$ source venv/bin/activate +$ pip install -r requirements.txt +$ ./run.sh +``` + +and it should appear on port 5000 + +`http://0.0.0.0:5000` + +## Changes in 2023 + +database is now in `instance/` directory +removed version labels from `requirements.txt` + +The Heroku file is broken. +The Procfile is broken too. diff --git a/CircusCircus/__pycache__/config.cpython-311.pyc b/CircusCircus/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a36d870b43b444e6f39bbe645906928181eeb73b GIT binary patch literal 886 zcmZuv%}*0S6rcU*Zo5hpO&|o+#wZ4r-4JiYn5;`Hm5)}o9N5cb+u6dxwp(VlL>mr0 zc<|tXL=&!_FvdT?lmA0WI5cx2F){I0gC|bT>{5e)xAT7c-kW*v&HQFQ4-IL6$%k)0 z*S`w@{NT<&QfJ0VlNtK}0S6!fiGffT9kDJsk^n^DNt^N@A@&AYz*7!b#ji{g`QLL? z5xf9MdJB+zAhLB#6LHcgWrHcDoz?PzOSZDruu=2ZTiA{Ku#w$yvF}y_kLcPjo-id+ z)obkdIBZb4=|&sXvpVr_(EVhRx%GVVA#AvH4??OzSPxMv;PzDrw_9#-Hlsp_!YW^4=d6`n87}13XePgC+6&Mu zmFTGbYSCP@=5tGHaLz27&rLfAS62%3+PR6GHD9E+&jrdWrnLZ53s-3_CJ!%eRm5u-F?Sn&}OG?F{fn{TkfvSP?EgFIo-`S@z&ZT>c!$YX*Hu5QM%c3UVKCP=Ii& z3nng(V{PQ!z4wpzcK4!>yPxI{N2iZQrw=v#NYguNU&#oM`rsn`=CMDQOvF3;3*H>+ ANdN!< literal 0 HcmV?d00001 diff --git a/CircusCircus/config.py b/CircusCircus/config.py new file mode 100644 index 0000000..da78fea --- /dev/null +++ b/CircusCircus/config.py @@ -0,0 +1,18 @@ +""" +Flask configuration variables. +""" +from os import environ, path + +basedir = path.abspath(path.dirname(__file__)) +# load_dotenv(path.join(basedir, '.env')) + +class Config: + """Set Flask configuration from .env file.""" + # General Config + SECRET_KEY = 'kristofer' + FLASK_APP = 'forum.app' + + # Database + SQLALCHEMY_DATABASE_URI = 'sqlite:///circuscircus.db' + SQLALCHEMY_ECHO = False + SQLALCHEMY_TRACK_MODIFICATIONS = False \ No newline at end of file diff --git a/CircusCircus/forum/__init__.py b/CircusCircus/forum/__init__.py new file mode 100644 index 0000000..8c9ceac --- /dev/null +++ b/CircusCircus/forum/__init__.py @@ -0,0 +1,24 @@ +from flask import Flask +from forum.routes import rt +from flask_pagedown import PageDown + + +def create_app(): + """Construct the core application.""" + app = Flask(__name__, instance_relative_config=False) + pagedown = PageDown(app) + app.config.from_object('config.Config') + # I think more blueprints might be used to break routes up into things like + # post_routes + # subforum_routes + # etc + app.register_blueprint(rt) + # Set globals + from forum.models import db + db.init_app(app) + + with app.app_context(): + # Add some routes + db.create_all() + return app + diff --git a/CircusCircus/forum/__pycache__/__init__.cpython-311.pyc b/CircusCircus/forum/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..692aae763c5a9698b51195ac7419858f83ad265e GIT binary patch literal 1297 zcmZWoL2DC16rSCkO*Uy`unkrtGyzYcC25r+1yN|Fh=o$99-7Ot-JPUcH`y>dv9&Z( zq@XQ@dJ%gOda6i0_#65Q5(yXxX<8%y`_sBqoV4;GL6$Gw}mROLolBY`+F34FKbi9SK0YhFuslacZ zoDy7l#?qW;poo-#ROYojd1$W|br1YP1YnimJ*5Iai-Bwp)~*8_hVWTD1pBDts~{$~ zBN5s_hmd*H$*u?+Xocf?;{dm{%c~)E=Lmpua0%0oW&OV1n`2-@wt@wvB0|1+5b`5^ zt+zVwB(MK|OiMe2<*4JFKptja$8kyNj^v2CXaxAv-6sJppj&7hIe1lElNQk`u($}g zz*zv;3a%%n?V`hIS!YSMOp>}yNm477EK}E*X&2Koz}jqj0ZeNbt1=U_!Av$vhD9M`+BUD|o z%A`ciBBPu$w?Ajoav@!?4PrS|@$k%|$yBJ&?E}HC$G_tE{(~AG zspF9<9%+V=thBrzZrAW|9S>LW@Ci;-yJrIjUeCUoeJj;)td3(<96Jg1?s2A-he#wqyHa4)R-t47bwgGFErH-9tYn{w?2h_kO$RZ=tl}P21 znqy4?lt4g&!N5FVK>O*RW(YD2`^@LC{_JA|NP&O_0tNyM0oJdn%|3#B?Oe)~MQ6pj zEAiZW&OP_Mc+R=}eIgM>FuqLxv-o2kp?^@sAAoz~#g`bNkCBXITta2MfiYb(C8o@7 zu-=+2h05FpSLQePa(E*Qw2;h|gmPpff)Rt1$RiN+2$?zjrX~ulncU2_p)A#e+16YMlNaG2jC{-GPx7 zn?@Ir5!zwyupgowcsn10J)kpsFyeFd&+yRDcwJPcN$4`*|&2R7i$(g|+JDEuB=$E+cQcp$`m&d`{yGtkD8d;7~4Ksnp^t zx+$#{wsc)nh#OhEzP^-Ox$^!}ZD{4jx|ElU<1MKxYtUo3y0o^qdj015^&2a0Z27$_ zYd^_dU3&L}YlVKVI6zo#h0eI4P?}p=ubau1Wb#y=qG4meLKiw5gPIt!PMF z3Q^odKwxe1XR5Mu%$M(1<#E?y?}EP7uD;q-cR_71q#f1V>eE4G*OJ$cjk(%0rZx-t z0(`2`^Gz~!siGSun7<1f;Qg*%m3H!)>2f!~YEnxbpOaBU2rN+E!sQIn=n?&DM3Z^lC^WyxXN+3*KIPYcRJS6X2W!H_+ zcaSqwQz5j9v!_cUy>UPb_O%h<<^65>*pi^^gRsBfKL>l}U9Qf39nZ|( zEdVw~e0c}n-RNWf{JM|ksJ};>O&!$$4cZFj{@*pw40>$@eZZ*BLMvcaYGFucS_+v2 zq0w_AeriJ}FjcdpxIE-3^lF#8T~)}g%imHeVUmYTs>P%}nYNjJt@6j3V7LauE z*|LTn6&jiq>%m71jn+aABrV4Km0Kd{kg z`;G6>wH2GRQM!F@(%X2)MyXDS$KUCo9vB?J@eT@t^XSave*CxM@5Dx;jS`1wq=iQI z4I523Xu?7h&#>s=@eYIWR0je0fE={x|!}U489C)5nraJ5B3v0szKZNUj`NyMfNi>w3rNkHjDxO2Rn}y A{r~^~ literal 0 HcmV?d00001 diff --git a/CircusCircus/forum/__pycache__/models.cpython-311.pyc b/CircusCircus/forum/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fa56b7382df63aba87fc0d58f28432fc7369d2d GIT binary patch literal 9862 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 E4~~nWKL7v# literal 0 HcmV?d00001 diff --git a/CircusCircus/forum/__pycache__/routes.cpython-311.pyc b/CircusCircus/forum/__pycache__/routes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7e2a9b6ef0d1cb7eea39392bc859d7d25fd0d03 GIT binary patch literal 10697 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 literal 0 HcmV?d00001 diff --git a/CircusCircus/forum/__pycache__/user.cpython-311.pyc b/CircusCircus/forum/__pycache__/user.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47e30f8468444bb0cedd1ee25fd1e706e37a5bb4 GIT binary patch literal 1480 zcmdT@&ubGw6rR~+lS!JSq}tNfApWS4sA(zOPqn3gfd57k0+9tFAPBt$dh5wIn{I5ogo+e9TQHAt4GDJ80;It(c-Nko&D?0ZCY?ovVya5XLM?BL}N zURGW2NZO^k5#1_7WU2?zqdHO~;yo0RBGh!wat#he%<1*P=TDXB`h#eCEPD6y{j1l) z&)y_%#mC-`bYtQclW8S{5k#_L9Zex@lRTnrJE!m2PO?GTljf32Qz!r?=$L-=I3k&W z0;ad3WVJca7PQxSleAyeQ@ zjLqkukd2A|j;^kmftQM*t8;cJ=)q8aT>J_`k^E!>H5RI{(B~&*7Ot{zgGf?u-4odC zuQT7K^9XN|?g}K3`4rpx>ps>U*$k;)(viqhuvbfyID1e0kf;HDF~wT94+CO zuOnZgdv_|s<7F1FvUrV6R@h{jC95o1qRIbA9uS>|^9ZJ_Wfk3ke%wbW4C2peZF(&B zM3^BYtqa|Rxv?i#hNERRT4kd(7O${)nI)<$QKAV;n26VOWpk=#@MeP~&`ftGmtEBj z4SF#$xCDro$(lv04Im!5Ppkme*dDVa1}PZY6kJ3~%oyK7XgDaPbuw6L|JI48Xnh(! z(ii&B_i4BkN|b{W)!;-i@FRGk!_#oe)ZZX2@KJibL0aIV^qx4LfRoY*aqI*u+dr6X B7)SsB literal 0 HcmV?d00001 diff --git a/CircusCircus/forum/app.py b/CircusCircus/forum/app.py new file mode 100644 index 0000000..94925f6 --- /dev/null +++ b/CircusCircus/forum/app.py @@ -0,0 +1,56 @@ + +from flask import render_template +from flask_login import LoginManager +from forum.models import Subforum, db, User + +from . import create_app +app = create_app() +app.config['SITE_NAME'] = 'Not Schooner' +app.config['SITE_DESCRIPTION'] = 'NOT a schooner forum' +app.config['FLASK_DEBUG'] = 1 + +def init_site(): + print("creating initial subforums") + admin = add_subforum("Forum", "Announcements, bug reports, and general discussion about the forum belongs here") + add_subforum("Announcements", "View forum announcements here",admin) + add_subforum("Bug Reports", "Report bugs with the forum here", admin) + add_subforum("General Discussion", "Use this subforum to post anything you want") + add_subforum("Other", "Discuss other things here") + +def add_subforum(title, description, parent=None): + sub = Subforum(title, description) + if parent: + for subforum in parent.subforums: + if subforum.title == title: + return + parent.subforums.append(sub) + else: + subforums = Subforum.query.filter(Subforum.parent_id == None).all() + for subforum in subforums: + if subforum.title == title: + return + db.session.add(sub) + print("adding " + title) + db.session.commit() + return sub + +login_manager = LoginManager() +login_manager.init_app(app) + +@login_manager.user_loader +def load_user(userid): + return User.query.get(userid) + +with app.app_context(): + db.create_all() # TODO this may be redundant + if not Subforum.query.all(): + init_site() + +@app.route('/') +def index(): + subforums = Subforum.query.filter(Subforum.parent_id == None).order_by(Subforum.id) + return render_template("subforums.html", subforums=subforums) + + + + diff --git a/CircusCircus/forum/models.py b/CircusCircus/forum/models.py new file mode 100644 index 0000000..ae4c879 --- /dev/null +++ b/CircusCircus/forum/models.py @@ -0,0 +1,142 @@ + +from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import UserMixin +import datetime +import markdown + +# create db here so it can be imported (with the models) into the App object. +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + +#OBJECT MODELS +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.Text, unique=True) + password_hash = db.Column(db.Text) + email = db.Column(db.Text, unique=True) + admin = db.Column(db.Boolean, default=False) + posts = db.relationship("Post", backref="user") + comments = db.relationship("Comment", backref="user") + + def __init__(self, email, username, password): + self.email = email + self.username = username + self.password_hash = generate_password_hash(password) + def check_password(self, password): + return check_password_hash(self.password_hash, password) + +class Post(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.Text) + content = db.Column(db.Text) + comments = db.relationship("Comment", backref="post") + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + subforum_id = db.Column(db.Integer, db.ForeignKey('subforum.id')) + postdate = db.Column(db.DateTime) + + #cache stuff + lastcheck = None + savedresponce = None + def __init__(self, title, content, postdate): + self.title = title + self.content = content + self.postdate = postdate + def get_time_string(self): + #this only needs to be calculated every so often, not for every request + #this can be a rudamentary chache + now = datetime.datetime.now() + if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: + self.lastcheck = now + else: + return self.savedresponce + + diff = now - self.postdate + + seconds = diff.total_seconds() + print(seconds) + if seconds / (60 * 60 * 24 * 30) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" + elif seconds / (60 * 60 * 24) > 1: + self.savedresponce = " " + str(int(seconds / (60* 60 * 24))) + " days ago" + elif seconds / (60 * 60) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" + elif seconds / (60) > 1: + self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" + else: + self.savedresponce = "Just a moment ago!" + + return self.savedresponce + +class Subforum(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.Text, unique=True) + description = db.Column(db.Text) + subforums = db.relationship("Subforum") + parent_id = db.Column(db.Integer, db.ForeignKey('subforum.id')) + posts = db.relationship("Post", backref="subforum") + path = None + hidden = db.Column(db.Boolean, default=False) + def __init__(self, title, description): + self.title = title + self.description = description + +class Comment(db.Model): + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text) + postdate = db.Column(db.DateTime) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + post_id = db.Column(db.Integer, db.ForeignKey("post.id")) + + lastcheck = None + savedresponce = None + def __init__(self, content, postdate): + self.content = content + self.postdate = postdate + def get_time_string(self): + #this only needs to be calculated every so often, not for every request + #this can be a rudamentary chache + now = datetime.datetime.now() + if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: + self.lastcheck = now + else: + return self.savedresponce + + diff = now - self.postdate + seconds = diff.total_seconds() + if seconds / (60 * 60 * 24 * 30) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" + elif seconds / (60 * 60 * 24) > 1: + self.savedresponce = " " + str(int(seconds / (60* 60 * 24))) + " days ago" + elif seconds / (60 * 60) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" + elif seconds / (60) > 1: + self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" + else: + self.savedresponce = "Just a moment ago!" + return self.savedresponce + +def error(errormessage): + return "" + errormessage + "" + +def generateLinkPath(subforumid): + links = [] + subforum = Subforum.query.filter(Subforum.id == subforumid).first() + parent = Subforum.query.filter(Subforum.id == subforum.parent_id).first() + links.append("" + subforum.title + "") + while parent is not None: + links.append("" + parent.title + "") + parent = Subforum.query.filter(Subforum.id == parent.parent_id).first() + links.append("Forum Index") + link = "" + for l in reversed(links): + link = link + " / " + l + return link + + +#Post checks +def valid_title(title): + return len(title) > 4 and len(title) < 140 +def valid_content(content): + return len(content) > 10 and len(content) < 5000 + diff --git a/CircusCircus/forum/routes.py b/CircusCircus/forum/routes.py new file mode 100644 index 0000000..b808fe5 --- /dev/null +++ b/CircusCircus/forum/routes.py @@ -0,0 +1,152 @@ +from flask import render_template, request, redirect, url_for +from flask_login import current_user, login_user, logout_user +from flask_login.utils import login_required +import datetime +import markdown +from flask import Blueprint, render_template, request, redirect, url_for +from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error +from forum.user import username_taken, email_taken, valid_username +from markupsafe import Markup + +## +# This file needs to be broken up into several, to make the project easier to work on. +## + +rt = Blueprint('routes', __name__, template_folder='templates') + +@rt.route('/action_login', methods=['POST']) +def action_login(): + username = request.form['username'] + password = request.form['password'] + user = User.query.filter(User.username == username).first() + if user and user.check_password(password): + login_user(user) + else: + errors = [] + errors.append("Username or password is incorrect!") + return render_template("login.html", errors=errors) + return redirect("/") + + +@login_required +@rt.route('/action_logout') +def action_logout(): + #todo + logout_user() + return redirect("/") + +@rt.route('/action_createaccount', methods=['POST']) +def action_createaccount(): + username = request.form['username'] + password = request.form['password'] + email = request.form['email'] + errors = [] + retry = False + if username_taken(username): + errors.append("Username is already taken!") + retry=True + if email_taken(email): + errors.append("An account already exists with this email!") + retry = True + if not valid_username(username): + errors.append("Username is not valid!") + retry = True + # if not valid_password(password): + # errors.append("Password is not valid!") + # retry = True + if retry: + return render_template("login.html", errors=errors) + user = User(email, username, password) + if user.username == "admin": + user.admin = True + db.session.add(user) + db.session.commit() + login_user(user) + return redirect("/") + + +@rt.route('/subforum') +def subforum(): + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return error("That subforum does not exist!") + posts = Post.query.filter(Post.subforum_id == subforum_id).order_by(Post.id.desc()).limit(50) + if not subforum.path: + subforumpath = generateLinkPath(subforum.id) + + subforums = Subforum.query.filter(Subforum.parent_id == subforum_id).all() + return render_template("subforum.html", subforum=subforum, posts=posts, subforums=subforums, path=subforumpath) + +@rt.route('/loginform') +def loginform(): + return render_template("login.html") + + +@login_required +@rt.route('/addpost') +def addpost(): + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return error("That subforum does not exist!") + + return render_template("createpost.html", subforum=subforum) + +@rt.route('/viewpost') +def viewpost(): + postid = int(request.args.get("post")) + post = Post.query.filter(Post.id == postid).first() + if not post: + return error("That post does not exist!") + if not post.subforum.path: + subforumpath = generateLinkPath(post.subforum.id) + comments = Comment.query.filter(Comment.post_id == postid).order_by(Comment.id.desc()) # no need for scalability now + return render_template("viewpost.html", post=post, path=subforumpath, comments=comments) + +@login_required +@rt.route('/action_comment', methods=['POST', 'GET']) +def comment(): + post_id = int(request.args.get("post")) + post = Post.query.filter(Post.id == post_id).first() + if not post: + return error("That post does not exist!") + content = request.form['content'] + postdate = datetime.datetime.now() + comment = Comment(content, postdate) + current_user.comments.append(comment) + post.comments.append(comment) + db.session.commit() + return redirect("/viewpost?post=" + str(post_id)) + + +@login_required +@rt.route('/action_post', methods=['POST']) +def action_post(): + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return redirect(url_for("subforums")) + + user = current_user + title = request.form['title'] + content = request.form['content'] + content_html = markdown.markdown(content) + print(f"Generated HTML: {content_html}") + #check for valid posting + errors = [] + retry = False + if not valid_title(title): + errors.append("Title must be between 4 and 140 characters long!") + retry = True + if not valid_content(content_html): + errors.append("Post must be between 10 and 5000 characters long!") + retry = True + if retry: + return render_template("createpost.html", content_html=Markup(content_html), subforum=subforum, errors=errors) + post = Post(title, content_html, datetime.datetime.now()) + subforum.posts.append(post) + user.posts.append(post) + db.session.commit() + return redirect("/viewpost?post=" + str(post.id)) + diff --git a/CircusCircus/forum/static/bootstrap.min.css b/CircusCircus/forum/static/bootstrap.min.css new file mode 100644 index 0000000..cd1c616 --- /dev/null +++ b/CircusCircus/forum/static/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px \9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control{height:30px;line-height:30px}select[multiple].form-group-sm .form-control,textarea.form-group-sm .form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control{height:46px;line-height:46px}select[multiple].form-group-lg .form-control,textarea.form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:10px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px)and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px)and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px)and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px)and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px)and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px)and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px)and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/CircusCircus/forum/static/style.css b/CircusCircus/forum/static/style.css new file mode 100644 index 0000000..2dd2e2e --- /dev/null +++ b/CircusCircus/forum/static/style.css @@ -0,0 +1,191 @@ +.page{ + font-family: Verdana; + font-style: normal; + font-size: 14pt; + + margin-left: auto; + margin-right: auto; + margin-top: 1%; + width: 80%; + + padding-left: 3%; + padding-right: 3%; + +} +.header{ + border: 1px solid black; + overflow: hidden; + vertical-align: middle; + padding: 0.5%; + margin-bottom: none; +} +.content{ + margin-top: 0.5%; +} +.login{ + width: 25%; + float: right; + text-align: center; + +} +.loginbox{ + margin-right: auto; + margin-left: auto; + text-align: center; + margin-top: 3%; +} +.loginelement{ + margin-top: 0.5%; +} +.logoutlink{ + padding-left: 0.5%; + font-size: 12pt; +} +.postsubmit{ + margin-left: auto; + margin-right: auto; + text-align: center; + +} +.subforumlisting{ + border-bottom: 1px solid gray; + padding: 1%; + margin-bottom: 1%; +} +.inputbox{ + padding: 0.25%; + margin-top: 0.5%; +} +.errors{ + border-bottom: 1px solid black; + margin-top: 2%; +} +.error{ + color: red; + font-weight: bold; +} +.subforumtitle{ + color: blue; + font-size: 15pt; + font-weight: bold; +} +.subforumdesc{ + font-size: 13pt; + font-style: italic; +} +.subforumheader{ + padding: 0.5%; + padding-left: 1%; + margin-top: none; + padding-bottom: none; + margin-bottom: 0.5%; + border: 1px solid black; + font-size: 15pt; +} +.subforumheadertitle{ + display: inline; + font-weight: bold; +} +.subforumheaderdesc{ + display: inline; +} +.post{ + padding: 1%; + border-bottom: 1px solid gray; +} +.postusername{ + font-style: italic; + font-size: 12pt; +} +.subforumheaderlink{ + font-style: italic; + font-size: 12pt; + margin-top: 0.5%; +} +.postinginfo{ + font-size: 11pt; + font-style: italic; +} +#nolinks{ + text-decoration: none; +} +.noposts{ + padding: 1%; +} +.subsubforums{ + border: 1px solid black; + padding: 0.5%; + margin-top: 0.5%; + padding-left: 1%; +} +.subsubforumtitle{ + font-weight: bolder; + text-decoration: none; + font-size: 14pt; +} +.subsubforumdesc{ + font-style: italic; + font-size: 12pt; +} +.toplevelinfo{ + padding: 0.5%; + font-weight: bold; +} +.actualpost{ + padding: 1%; + border: 1px solid black; + border-collapse: collapse; + margin-bottom: 1%; +} +.actualposttitle{ + border-collapse: collapse; + border-bottom: 1px solid black; + padding: 1%; + font-size: 16pt; + font-weight: bold; +} +.postcontent{ + padding: 1%; +} +.postposter{ + font-size: 12pt; + font-style: italic; + color: black; +} +.posttime{ + font-style: italic; + font-size: 11pt; +} +.whitespace{ + white-space: pre-line; +} +.comments{ + border: 1px solid gray; + padding: 1%; + margin-top: 1%; +} +.comment{ + border-bottom: 1px solid gray; +} +.commentuser{ + display: inline; + font-weight: bold; +} +.commentcontent{ + display: inline; +} +.commenttime{ + font-size: 11pt; + font-style: italic; +} + + +.addcomment{ + display: none; + margin-right: auto; + margin-left: auto; + text-align: center; +} +.varwidth{ + width: 50%; +} diff --git a/CircusCircus/forum/templates/createpost.html b/CircusCircus/forum/templates/createpost.html new file mode 100644 index 0000000..129976e --- /dev/null +++ b/CircusCircus/forum/templates/createpost.html @@ -0,0 +1,21 @@ +{% extends 'layout.html' %} +{% block body %} + + + +
+ Enter your post here:
+
+ You are posting to {{ subforum.title }} +
+ +
+ +
+ +
+ + +
+
+{% endblock %} \ No newline at end of file diff --git a/CircusCircus/forum/templates/header.html b/CircusCircus/forum/templates/header.html new file mode 100644 index 0000000..c973d1f --- /dev/null +++ b/CircusCircus/forum/templates/header.html @@ -0,0 +1,8 @@ + {{ config.SITE_NAME }}{% if config.SITE_DESCRIPTION %} - {% endif %} {{ config.SITE_DESCRIPTION }} + \ No newline at end of file diff --git a/CircusCircus/forum/templates/layout.html b/CircusCircus/forum/templates/layout.html new file mode 100644 index 0000000..a2a5e14 --- /dev/null +++ b/CircusCircus/forum/templates/layout.html @@ -0,0 +1,30 @@ + + + + + + {{ config.SITE_NAME }} + + +
+
+ {% include 'header.html' %} +
+ {% if errors %} +
+ {% for error in errors %} +

+ {{ error }} +

+ + {% endfor %} +
+{% endif %} +
+ {% block body %}{% endblock %} +
+
+ + + + diff --git a/CircusCircus/forum/templates/login.html b/CircusCircus/forum/templates/login.html new file mode 100644 index 0000000..461533e --- /dev/null +++ b/CircusCircus/forum/templates/login.html @@ -0,0 +1,26 @@ +{% extends 'layout.html' %} +{% block body %} + + +
+

Login to Account

+
+
+
+ +
+
+

+
+

Create Account

+
+
+
+
+ +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/CircusCircus/forum/templates/subforum.html b/CircusCircus/forum/templates/subforum.html new file mode 100644 index 0000000..8783cd1 --- /dev/null +++ b/CircusCircus/forum/templates/subforum.html @@ -0,0 +1,63 @@ +{% extends 'layout.html' %} +{% block body %} + +{{ path|safe }} +
+
+ {{ subforum.title}} +
+ -- +
+ {{ subforum.description }} +
+ + {% if subforums %} + + {% for sub in subforums %} +
+ +
+ {{ sub.description }} +
+
+ + {% endfor %} + {% endif %} + + + +
+ + + + + +{% if posts.first() %} + {% for post in posts %} +
+ +
+ by {{post.user.username}} +
+ {{ post.get_time_string() }} +
+
+
+ {% endfor %} + +{% else %} +
+There are no posts in this subforum +
+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/CircusCircus/forum/templates/subforums.html b/CircusCircus/forum/templates/subforums.html new file mode 100644 index 0000000..8cee77d --- /dev/null +++ b/CircusCircus/forum/templates/subforums.html @@ -0,0 +1,18 @@ +{% extends 'layout.html' %} +{% block body%} +
+ Top level subforums: +
+{% for subforum in subforums %} +
+ +
+{{ subforum.description }} +
+
+{% endfor %} +{% endblock %} diff --git a/CircusCircus/forum/templates/viewpost.html b/CircusCircus/forum/templates/viewpost.html new file mode 100644 index 0000000..d91fbfa --- /dev/null +++ b/CircusCircus/forum/templates/viewpost.html @@ -0,0 +1,77 @@ +{% extends 'layout.html' %} +{% block body %} +{{ path|safe}} + +
+
+ {{post.title}} +
+ {{ post.user.username }} + +
+
+ {{ post.get_time_string() }} +
+
+
+ {{post.content | safe }} +
+ +
+
+
+
+ +
+
+
+ + + + {% if current_user.is_authenticated %} + + + {% else %} + Login or register to make a comment + {% endif %} +
+{%if comments%} +
+{% for comment in comments %} + +
+
+ ({{ comment.user.username }}) - +
+
+ {{ comment.content }} +
+ +
+ {{ comment.get_time_string() }} +
+
+
+ +{% endfor %} +
+{% endif %} + + + +{% endblock %} + + diff --git a/CircusCircus/forum/user.py b/CircusCircus/forum/user.py new file mode 100644 index 0000000..8765da1 --- /dev/null +++ b/CircusCircus/forum/user.py @@ -0,0 +1,25 @@ + +from .models import User + +import re + +## +# Some utility routines. +## + +password_regex = re.compile("^[a-zA-Z0-9!@#%&]{6,40}$") +username_regex = re.compile("^[a-zA-Z0-9!@#%&]{4,40}$") +#Account checks +def valid_username(username): + if not username_regex.match(username): + #username does not meet password reqirements + return False + #username is not taken and does meet the password requirements + return True +def valid_password(password): + return password_regex.match(password) + +def username_taken(username): + return User.query.filter(User.username == username).first() +def email_taken(email): + return User.query.filter(User.email == email).first() diff --git a/CircusCircus/instance/circuscircus.db b/CircusCircus/instance/circuscircus.db new file mode 100644 index 0000000000000000000000000000000000000000..4786d723e83c9c5bea2f0f04dbee3e9f061acea1 GIT binary patch literal 40960 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 BnH~TD literal 0 HcmV?d00001 diff --git a/CircusCircus/requirements.txt b/CircusCircus/requirements.txt new file mode 100644 index 0000000..39287bc --- /dev/null +++ b/CircusCircus/requirements.txt @@ -0,0 +1,11 @@ +Flask +Flask-Login +Flask-SQLAlchemy +gunicorn +itsdangerous +Jinja2 +MarkupSafe +SQLAlchemy +Werkzeug +wheel +honcho \ No newline at end of file diff --git a/CircusCircus/run.sh b/CircusCircus/run.sh new file mode 100644 index 0000000..a39697a --- /dev/null +++ b/CircusCircus/run.sh @@ -0,0 +1,6 @@ +# export PORT=5006 +export SECRET_KEY="kristofer" +# honcho start + +# you can ALSO or RATHER use the following command to run the app +cd ./forum; flask run From 434e42fc3f901c1c6250869ab85eccbb58ce01f3 Mon Sep 17 00:00:00 2001 From: Angelika Brown Date: Mon, 31 Mar 2025 09:19:19 -0400 Subject: [PATCH 10/24] Every post is private now --- forum/__init__.py | 1 + forum/app.py | 61 +++++----- forum/models.py | 59 +++++++++ forum/routes.py | 219 +++++++++++++++++++--------------- forum/templates/header.html | 7 +- forum/templates/message.html | 20 ++++ forum/templates/viewpost.html | 16 ++- requirements.txt | 10 +- 8 files changed, 260 insertions(+), 133 deletions(-) create mode 100644 forum/templates/message.html diff --git a/forum/__init__.py b/forum/__init__.py index c10b0f3..59298fb 100644 --- a/forum/__init__.py +++ b/forum/__init__.py @@ -1,3 +1,4 @@ +import socketio from flask import Flask from forum.routes import rt diff --git a/forum/app.py b/forum/app.py index 4d8a828..14d157a 100644 --- a/forum/app.py +++ b/forum/app.py @@ -2,56 +2,61 @@ from flask import render_template from flask_login import LoginManager from forum.models import Subforum, db, User +from flask_socketio import SocketIO from . import create_app app = create_app() +socketio = SocketIO(app) app.config['SITE_NAME'] = 'Schooner' app.config['SITE_DESCRIPTION'] = 'a schooner forum' app.config['FLASK_DEBUG'] = 1 +if __name__ == '__main__': + socketio.run(app,port=5555, debug=True) + def init_site(): - print("creating initial subforums") - admin = add_subforum("Forum", "Announcements, bug reports, and general discussion about the forum belongs here") - add_subforum("Announcements", "View forum announcements here",admin) - add_subforum("Bug Reports", "Report bugs with the forum here", admin) - add_subforum("General Discussion", "Use this subforum to post anything you want") - add_subforum("Other", "Discuss other things here") + print("creating initial subforums") + admin = add_subforum("Forum", "Announcements, bug reports, and general discussion about the forum belongs here") + add_subforum("Announcements", "View forum announcements here",admin) + add_subforum("Bug Reports", "Report bugs with the forum here", admin) + add_subforum("General Discussion", "Use this subforum to post anything you want") + add_subforum("Other", "Discuss other things here") + def add_subforum(title, description, parent=None): - sub = Subforum(title, description) - if parent: - for subforum in parent.subforums: - if subforum.title == title: - return - parent.subforums.append(sub) - else: - subforums = Subforum.query.filter(Subforum.parent_id == None).all() - for subforum in subforums: - if subforum.title == title: - return - db.session.add(sub) - print("adding " + title) - db.session.commit() - return sub + sub = Subforum(title, description) + if parent: + for subforum in parent.subforums: + if subforum.title == title: + return + parent.subforums.append(sub) + else: + subforums = Subforum.query.filter(Subforum.parent_id == None).all() + for subforum in subforums: + if subforum.title == title: + return + db.session.add(sub) + print("adding " + title) + db.session.commit() + return sub login_manager = LoginManager() login_manager.init_app(app) @login_manager.user_loader def load_user(userid): - return User.query.get(userid) + return User.query.get(userid) with app.app_context(): - db.create_all() # TODO this may be redundant - if not Subforum.query.all(): - init_site() + db.create_all() # TODO this may be redundant + if not Subforum.query.all(): + init_site() @app.route('/') def index(): - subforums = Subforum.query.filter(Subforum.parent_id == None).order_by(Subforum.id) - return render_template("subforums.html", subforums=subforums) - + subforums = Subforum.query.filter(Subforum.parent_id == None).order_by(Subforum.id) + return render_template("subforums.html", subforums=subforums) diff --git a/forum/models.py b/forum/models.py index 8add9ae..3e07474 100644 --- a/forum/models.py +++ b/forum/models.py @@ -17,6 +17,10 @@ class User(UserMixin, db.Model): admin = db.Column(db.Boolean, default=False) posts = db.relationship("Post", backref="user") comments = db.relationship("Comment", backref="user") + messages = db.relationship("Message", backref="user") + #sender_id = db.relationship("Message", backref="user" ) + #messages_sent: db.WriteOnlyMapped['Message'] = db.relationship(foreign_keys="Message.sender_id", backref='author') + #messages_received: db.WriteOnlyMapped['Message'] = db.relationship(foreign_keys="Message.recipient_id", backref='recipient') def __init__(self, email, username, password): self.email = email @@ -34,6 +38,8 @@ class Post(db.Model): subforum_id = db.Column(db.Integer, db.ForeignKey('subforum.id')) postdate = db.Column(db.DateTime) + + #cache stuff lastcheck = None savedresponce = None @@ -80,6 +86,7 @@ def __init__(self, title, description): self.title = title self.description = description + class Comment(db.Model): id = db.Column(db.Integer, primary_key=True) content = db.Column(db.Text) @@ -115,6 +122,58 @@ def get_time_string(self): self.savedresponce = "Just a moment ago!" return self.savedresponce +class Message(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.Text) + content = db.Column(db.Text) + postdate = db.Column(db.DateTime) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + #sender_id = db.Column(db.Integer, db.ForeignKey('user_id')) + #recipient_id = db.Column(db.Integer, db.ForeignKey('user.id')) + #post_id = db.Column(db.Integer, db.ForeignKey("post.id")) + #author: db.Mapped[User] = db.relationship(foreign_keys='Message.sender_id', backref='messages_sent') + #recipient: db.Mapped[User] = db.relationship(foreign_keys='recipient_id', backref='messages_received') + + lastcheck = None + savedresponce = None + + def __init__(self, title, content, postdate): + self.title = title + self.content = content + self.postdate = postdate + + def get_time_string(self): + # this only needs to be calculated every so often, not for every request + # this can be a rudamentary chache + now = datetime.datetime.now() + if self.lastcheck is None or (now - self.lastcheck).total_seconds() > 30: + self.lastcheck = now + else: + return self.savedresponce + + diff = now - self.postdate + + seconds = diff.total_seconds() + print(seconds) + if seconds / (60 * 60 * 24 * 30) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60 * 24 * 30))) + " months ago" + elif seconds / (60 * 60 * 24) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60 * 24))) + " days ago" + elif seconds / (60 * 60) > 1: + self.savedresponce = " " + str(int(seconds / (60 * 60))) + " hours ago" + elif seconds / (60) > 1: + self.savedresponce = " " + str(int(seconds / 60)) + " minutes ago" + else: + self.savedresponce = "Just a moment ago!" + + return self.savedresponce + + + + + + + def error(errormessage): return "" + errormessage + "" diff --git a/forum/routes.py b/forum/routes.py index 75993e5..b76426b 100644 --- a/forum/routes.py +++ b/forum/routes.py @@ -1,9 +1,13 @@ +from pyexpat.errors import messages + +import socketio from flask import render_template, request, redirect, url_for from flask_login import current_user, login_user, logout_user from flask_login.utils import login_required import datetime from flask import Blueprint, render_template, request, redirect, url_for -from forum.models import User, Post, Comment, Subforum, valid_content, valid_title, db, generateLinkPath, error + +from forum.models import User, Post, Comment, Subforum, Message, valid_content, valid_title, db, generateLinkPath, error from forum.user import username_taken, email_taken, valid_username ## @@ -14,134 +18,153 @@ @rt.route('/action_login', methods=['POST']) def action_login(): - username = request.form['username'] - password = request.form['password'] - user = User.query.filter(User.username == username).first() - if user and user.check_password(password): - login_user(user) - else: - errors = [] - errors.append("Username or password is incorrect!") - return render_template("login.html", errors=errors) - return redirect("/") + username = request.form['username'] + password = request.form['password'] + user = User.query.filter(User.username == username).first() + if user and user.check_password(password): + login_user(user) + else: + errors = [] + errors.append("Username or password is incorrect!") + return render_template("login.html", errors=errors) + return redirect("/") @login_required @rt.route('/action_logout') def action_logout(): - #todo - logout_user() - return redirect("/") + #todo + logout_user() + return redirect("/") @rt.route('/action_createaccount', methods=['POST']) def action_createaccount(): - username = request.form['username'] - password = request.form['password'] - email = request.form['email'] - errors = [] - retry = False - if username_taken(username): - errors.append("Username is already taken!") - retry=True - if email_taken(email): - errors.append("An account already exists with this email!") - retry = True - if not valid_username(username): - errors.append("Username is not valid!") - retry = True - # if not valid_password(password): - # errors.append("Password is not valid!") - # retry = True - if retry: - return render_template("login.html", errors=errors) - user = User(email, username, password) - if user.username == "admin": - user.admin = True - db.session.add(user) - db.session.commit() - login_user(user) - return redirect("/") + username = request.form['username'] + password = request.form['password'] + email = request.form['email'] + errors = [] + retry = False + if username_taken(username): + errors.append("Username is already taken!") + retry=True + if email_taken(email): + errors.append("An account already exists with this email!") + retry = True + if not valid_username(username): + errors.append("Username is not valid!") + retry = True + # if not valid_password(password): + # errors.append("Password is not valid!") + # retry = True + if retry: + return render_template("login.html", errors=errors) + user = User(email, username, password) + if user.username == "admin": + user.admin = True + db.session.add(user) + db.session.commit() + login_user(user) + return redirect("/") @rt.route('/subforum') def subforum(): - subforum_id = int(request.args.get("sub")) - subforum = Subforum.query.filter(Subforum.id == subforum_id).first() - if not subforum: - return error("That subforum does not exist!") - posts = Post.query.filter(Post.subforum_id == subforum_id).order_by(Post.id.desc()).limit(50) - if not subforum.path: - subforumpath = generateLinkPath(subforum.id) + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return error("That subforum does not exist!") + posts = Post.query.filter(Post.subforum_id == subforum_id).order_by(Post.id.desc()).limit(50) + if not subforum.path: + subforumpath = generateLinkPath(subforum.id) - subforums = Subforum.query.filter(Subforum.parent_id == subforum_id).all() - return render_template("subforum.html", subforum=subforum, posts=posts, subforums=subforums, path=subforumpath) + subforums = Subforum.query.filter(Subforum.parent_id == subforum_id).all() + return render_template("subforum.html", subforum=subforum, posts=posts, subforums=subforums, path=subforumpath) @rt.route('/loginform') def loginform(): - return render_template("login.html") + return render_template("login.html") @login_required @rt.route('/addpost') def addpost(): - subforum_id = int(request.args.get("sub")) - subforum = Subforum.query.filter(Subforum.id == subforum_id).first() - if not subforum: - return error("That subforum does not exist!") + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return error("That subforum does not exist!") - return render_template("createpost.html", subforum=subforum) + return render_template("createpost.html", subforum=subforum) @rt.route('/viewpost') def viewpost(): - postid = int(request.args.get("post")) - post = Post.query.filter(Post.id == postid).first() - if not post: - return error("That post does not exist!") - if not post.subforum.path: - subforumpath = generateLinkPath(post.subforum.id) - comments = Comment.query.filter(Comment.post_id == postid).order_by(Comment.id.desc()) # no need for scalability now - return render_template("viewpost.html", post=post, path=subforumpath, comments=comments) + postid = int(request.args.get("post")) + post = Post.query.filter(Post.id == postid).first() + if not post: + return error("That post does not exist!") + if not post.subforum.path: + subforumpath = generateLinkPath(post.subforum.id) + comments = Comment.query.filter(Comment.post_id == postid).order_by(Comment.id.desc()) # no need for scalability now + return render_template("viewpost.html", post=post, path=subforumpath, comments=comments) @login_required @rt.route('/action_comment', methods=['POST', 'GET']) def comment(): - post_id = int(request.args.get("post")) - post = Post.query.filter(Post.id == post_id).first() - if not post: - return error("That post does not exist!") - content = request.form['content'] - postdate = datetime.datetime.now() - comment = Comment(content, postdate) - current_user.comments.append(comment) - post.comments.append(comment) - db.session.commit() - return redirect("/viewpost?post=" + str(post_id)) + post_id = int(request.args.get("post")) + post = Post.query.filter(Post.id == post_id).first() + if not post: + return error("That post does not exist!") + content = request.form['content'] + postdate = datetime.datetime.now() + comment = Comment(content, postdate) + current_user.comments.append(comment) + post.comments.append(comment) + db.session.commit() + return redirect("/viewpost?post=" + str(post_id)) + +@login_required +@rt.route('/message', methods=['GET', 'POST']) +def message(): + message_id = int(request.args.get("message")) + message = Message.query.filter(Message.id == message_id).first() + if not message: + return error("That message does not exist!") + content = request.form['content'] + postdate = datetime.datetime.now() + current_user.messages.append(message) + messages.append(messages) + db.session.commit() + render_template("message.html", message=message) + @login_required @rt.route('/action_post', methods=['POST']) def action_post(): - subforum_id = int(request.args.get("sub")) - subforum = Subforum.query.filter(Subforum.id == subforum_id).first() - if not subforum: - return redirect(url_for("subforums")) - - user = current_user - title = request.form['title'] - content = request.form['content'] - #check for valid posting - errors = [] - retry = False - if not valid_title(title): - errors.append("Title must be between 4 and 140 characters long!") - retry = True - if not valid_content(content): - errors.append("Post must be between 10 and 5000 characters long!") - retry = True - if retry: - return render_template("createpost.html",subforum=subforum, errors=errors) - post = Post(title, content, datetime.datetime.now()) - subforum.posts.append(post) - user.posts.append(post) - db.session.commit() - return redirect("/viewpost?post=" + str(post.id)) + subforum_id = int(request.args.get("sub")) + subforum = Subforum.query.filter(Subforum.id == subforum_id).first() + if not subforum: + return redirect(url_for("subforums")) + + user = current_user + title = request.form['title'] + content = request.form['content'] + #check for valid posting + errors = [] + retry = False + if not valid_title(title): + errors.append("Title must be between 4 and 140 characters long!") + retry = True + if not valid_content(content): + errors.append("Post must be between 10 and 5000 characters long!") + retry = True + if retry: + return render_template("createpost.html",subforum=subforum, errors=errors) + post = Post(title, content, datetime.datetime.now()) + subforum.posts.append(post) + user.posts.append(post) + db.session.commit() + return redirect("/viewpost?post=" + str(post.id)) + + + + diff --git a/forum/templates/header.html b/forum/templates/header.html index 9403a0d..3b4db36 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -5,4 +5,9 @@ {% else %} Click here to login or register! {% endif %} -
\ No newline at end of file + + +
+ diff --git a/forum/templates/message.html b/forum/templates/message.html new file mode 100644 index 0000000..74eaa61 --- /dev/null +++ b/forum/templates/message.html @@ -0,0 +1,20 @@ +{% extends 'layout.html' %} +{% block body %} + + +
+ Enter your message here:
+
+ You are sending to {{ message.recipient_id}} +
+ +
+ +
+ +
+ + +
+
+{% endblock %} \ No newline at end of file diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index 0470c3c..97321ce 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -33,8 +33,6 @@
-
- {% if current_user.is_authenticated %} @@ -47,6 +45,11 @@ + + + + + {%if comments%}
{% if current_user.is_authenticated %} @@ -72,6 +75,9 @@ {% endif %} + + + + + + + + + {% endblock %} diff --git a/requirements.txt b/requirements.txt index 39287bc..c41e7ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,13 @@ -Flask -Flask-Login +Flask~=3.1.0 +Flask-Login~=0.6.3 Flask-SQLAlchemy gunicorn itsdangerous Jinja2 MarkupSafe SQLAlchemy -Werkzeug +Werkzeug~=3.1.3 wheel -honcho \ No newline at end of file +honcho +Flask-SocketIO~=5.5.1 +python-socketio~=5.12.1 \ No newline at end of file From 46019dd80ae6c548d50345620e41f1731b8cc297 Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Mon, 31 Mar 2025 09:19:42 -0400 Subject: [PATCH 11/24] Changed name --- forum/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forum/app.py b/forum/app.py index 30c63df..0ae7641 100644 --- a/forum/app.py +++ b/forum/app.py @@ -6,8 +6,8 @@ from . import create_app app = create_app() -app.config['SITE_NAME'] = 'BillyBob' -app.config['SITE_DESCRIPTION'] = 'a BillyBob forum no bobs or billies' +app.config['SITE_NAME'] = 'Something, Anything, that is not that' +app.config['SITE_DESCRIPTION'] = 'a forum for Data to learn from' app.config['FLASK_DEBUG'] = 1 def init_site(): From 2ffe668364ca9f4fa5d66db7660650efd980b33b Mon Sep 17 00:00:00 2001 From: KunleAdeyanju Date: Mon, 31 Mar 2025 09:37:36 -0400 Subject: [PATCH 12/24] background color changed --- forum/templates/header.html | 2 +- forum/templates/layout.html | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/forum/templates/header.html b/forum/templates/header.html index de2f78b..b393dbb 100644 --- a/forum/templates/header.html +++ b/forum/templates/header.html @@ -1,4 +1,4 @@ -
{% if current_user.is_authenticated %}
From 15aa4f6c1567942a96045a89052eea5c12377f18 Mon Sep 17 00:00:00 2001 From: jwheller0013 Date: Mon, 31 Mar 2025 11:44:59 -0400 Subject: [PATCH 15/24] Fixed issues with comment windows --- forum/templates/viewpost.html | 49 +++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/forum/templates/viewpost.html b/forum/templates/viewpost.html index bfeead6..c1431ef 100644 --- a/forum/templates/viewpost.html +++ b/forum/templates/viewpost.html @@ -25,24 +25,22 @@
+{% if current_user.is_authenticated %} + -
-
-
- -
-
- -
- - {% if current_user.is_authenticated %} - - - {% else %} - Login or register to make a comment - {% endif %} -
- +
+ +
+{% else %} + +{% endif %} @@ -68,7 +66,7 @@ {% if current_user.is_authenticated %}
- + {% endif %} diff --git a/forum/templates/layout.html b/forum/templates/layout.html index 3c359fa..e7d6e99 100644 --- a/forum/templates/layout.html +++ b/forum/templates/layout.html @@ -10,13 +10,14 @@ {{ config.SITE_NAME }} + - +
@@ -54,5 +55,5 @@ - + diff --git a/forum/templates/message.html b/forum/templates/message.html deleted file mode 100644 index b67d10f..0000000 --- a/forum/templates/message.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'layout.html' %} -{% block body %} -{{ path|safe }} - -

Message

-
-

From: {{ message.sender_id }}

-

To: {{ message.recipient_id }}

-

Content: {{ message.content }}

-

Date: {{ message.postdate }}

-
diff --git a/forum/templates/room.html b/forum/templates/room.html index 8329989..4317f6a 100644 --- a/forum/templates/room.html +++ b/forum/templates/room.html @@ -1,16 +1,52 @@ {% extends 'layout.html' %} {% block body %} -{{ path|safe }} -
-

Forum Chat

-
-

Room Code: {{room}}

- Leave the Chat -
-
+

Howdy!

+ + + + + + + -
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% endblock %} \ No newline at end of file diff --git a/forum/templates/viewmessage.html b/forum/templates/viewmessage.html deleted file mode 100644 index 8c12bfe..0000000 --- a/forum/templates/viewmessage.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends 'layout.html' %} -{% block body %} - - - -{% endblock %} \ No newline at end of file