honmaple 8 years ago
parent
commit
839f7fe457
55 changed files with 891 additions and 908 deletions
  1. 3 2
      forums/admin/topic.py
  2. 12 0
      forums/api/collect/__init__.py
  3. 66 0
      forums/api/collect/models.py
  4. 23 0
      forums/api/collect/urls.py
  5. 121 0
      forums/api/collect/views.py
  6. 3 2
      forums/api/follow/views.py
  7. 160 0
      forums/api/forms.py
  8. 0 36
      forums/api/forums/forms.py
  9. 0 65
      forums/api/setting/forms.py
  10. 7 3
      forums/api/setting/views.py
  11. 6 3
      forums/api/tag/views.py
  12. 0 59
      forums/api/topic/forms.py
  13. 1 51
      forums/api/topic/models.py
  14. 3 12
      forums/api/topic/urls.py
  15. 33 106
      forums/api/topic/views.py
  16. 4 3
      forums/api/upload/views.py
  17. 3 1
      forums/api/urls.py
  18. 7 2
      forums/api/user/views.py
  19. 32 0
      forums/app.py
  20. 2 2
      forums/common/middleware.py
  21. 2 2
      forums/common/utils.py
  22. 2 2
      forums/filters.py
  23. 16 0
      forums/permission.py
  24. BIN
      forums/test.db
  25. 9 11
      static/assets/home.js
  26. 15 15
      static/styles/following.js
  27. 18 13
      static/styles/forums.js
  28. 0 118
      static/styles/mine.js
  29. 22 36
      static/styles/topic.js
  30. 0 2
      templates/base/base.html
  31. 12 12
      templates/base/form.html
  32. 2 2
      templates/base/link.html
  33. 1 1
      templates/base/panel.html
  34. 4 2
      templates/board/board.html
  35. 0 11
      templates/board/topic.html
  36. 0 90
      templates/collect/_macro.html
  37. 31 31
      templates/collect/collect.html
  38. 25 26
      templates/collect/collect_list.html
  39. 33 0
      templates/collect/create.html
  40. 18 0
      templates/collect/delete.html
  41. 32 0
      templates/collect/edit.html
  42. 16 18
      templates/follow/following_collects.html
  43. 17 19
      templates/follow/following_tags.html
  44. 19 28
      templates/follow/following_topics.html
  45. 16 18
      templates/follow/following_users.html
  46. 1 1
      templates/follow/none.html
  47. 1 1
      templates/tag/tag.html
  48. 66 66
      templates/topic/_list_macro.html
  49. 7 0
      templates/topic/_topic.html
  50. 0 6
      templates/topic/ask.html
  51. 14 12
      templates/topic/collect.html
  52. 1 7
      templates/topic/edit.html
  53. 1 7
      templates/topic/topic_list.html
  54. 2 2
      templates/user/info.html
  55. 2 2
      templates/user/user.html

+ 3 - 2
forums/admin/topic.py

@@ -6,13 +6,14 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-17 13:01:43 (CST)
-# Last Update:星期六 2017-3-25 19:37:7 (CST)
+# Last Update:星期二 2017-3-28 18:0:47 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from .views import BaseView
 from forums.extension import db
-from forums.api.topic.models import Topic, Collect, Reply
+from forums.api.topic.models import Topic, Reply
+from forums.api.collect.models import Collect
 from forums.api.tag.models import Tags
 
 

+ 12 - 0
forums/api/collect/__init__.py

@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: __init__.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 16:23:58 (CST)
+# Last Update:星期二 2017-3-28 16:23:58 (CST)
+#          By:
+# Description:
+# **************************************************************************

+ 66 - 0
forums/api/collect/models.py

@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: models.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 17:58:59 (CST)
+# Last Update:星期二 2017-3-28 18:4:27 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from datetime import datetime
+from flask_maple.models import ModelMixin, ModelTimeMixin, ModelUserMixin
+from forums.api.user.models import User
+from forums.extension import db
+
+topics_collects = db.Table(
+    'topics_collects',
+    db.Column('topics_id', db.Integer, db.ForeignKey('topics.id')),
+    db.Column('collects_id', db.Integer, db.ForeignKey('collects.id')))
+
+collect_follow_users = db.Table(
+    'collects_follow_users',
+    db.Column('collects_id', db.Integer, db.ForeignKey('collects.id')),
+    db.Column('follow_users_id', db.Integer, db.ForeignKey('users.id')))
+
+
+class Collect(db.Model, ModelMixin):
+    __tablename__ = 'collects'
+    id = db.Column(db.Integer, primary_key=True)
+    name = db.Column(db.String(32), nullable=False)
+    description = db.Column(db.String(256), nullable=True)
+    is_hidden = db.Column(db.Boolean, default=False)
+    created_at = db.Column(
+        db.DateTime, default=datetime.utcnow(), nullable=False)
+    updated_at = db.Column(
+        db.DateTime, default=datetime.utcnow(), onupdate=datetime.utcnow())
+    author_id = db.Column(
+        db.Integer, db.ForeignKey(
+            'users.id', ondelete="CASCADE"))
+    author = db.relationship(
+        User,
+        backref=db.backref(
+            'collects', cascade='all,delete-orphan', lazy='dynamic'),
+        lazy='joined')
+
+    topics = db.relationship(
+        'Topic',
+        secondary=topics_collects,
+        backref=db.backref(
+            'collects', lazy='dynamic'),
+        lazy='dynamic')
+
+    followers = db.relationship(
+        'User',
+        secondary=collect_follow_users,
+        backref=db.backref(
+            'following_collects', lazy='dynamic'),
+        lazy='dynamic')
+
+    def __str__(self):
+        return self.name
+
+    def __repr__(self):
+        return "<Collect %r>" % self.name

+ 23 - 0
forums/api/collect/urls.py

@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: urls.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 16:15:16 (CST)
+# Last Update:星期二 2017-3-28 21:27:14 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask import Blueprint
+from .views import CollectListView, CollectView, AddToCollectView
+
+site = Blueprint('collect', __name__)
+
+site.add_url_rule('/collect', view_func=CollectListView.as_view('list'))
+site.add_url_rule(
+    '/collect/<int:pk>', view_func=CollectView.as_view('collect'))
+site.add_url_rule(
+    '/topic/<int:topicId>/collect',
+    view_func=AddToCollectView.as_view('add_to_collect'))

+ 121 - 0
forums/api/collect/views.py

@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: views.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 16:15:08 (CST)
+# Last Update:星期二 2017-3-28 21:57:24 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask import redirect, render_template, request, url_for
+from flask_babelex import gettext as _
+from flask_login import current_user
+
+from flask_maple.auth.forms import form_validate
+from flask_maple.response import HTTPResponse
+from forums.api.forms import (CollectForm, ReplyForm, TopicForm,
+                              collect_error_callback, error_callback,
+                              form_board)
+from forums.api.forums.models import Board
+from forums.api.tag.models import Tags
+from forums.api.topic.models import Topic
+from forums.common.serializer import Serializer
+from forums.common.utils import gen_filter_dict, gen_order_by
+from forums.common.views import IsAuthMethodView as MethodView
+
+from .models import Collect
+
+
+class CollectListView(MethodView):
+    def get(self):
+        query_dict = request.data
+        user = request.user
+        form = CollectForm()
+        page, number = self.page_info
+        keys = ['name']
+        order_by = gen_order_by(query_dict, keys)
+        filter_dict = gen_filter_dict(query_dict, keys, user)
+        collects = Collect.query.filter_by(
+            **filter_dict).order_by(*order_by).paginate(page, number, True)
+        data = {'title': 'Collect', 'collects': collects, 'form': form}
+        return render_template('collect/collect_list.html', **data)
+
+    @form_validate(CollectForm, error=collect_error_callback, f='')
+    def post(self):
+        user = request.user
+        form = CollectForm()
+        name = form.name.data
+        description = form.description.data
+        is_hidden = form.is_hidden.data
+        is_hidden = True if is_hidden == 0 else False
+        collect = Collect(
+            name=name, description=description, is_hidden=is_hidden)
+        collect.author = user
+        collect.save()
+        return redirect(url_for('collect.list'))
+
+
+class CollectView(MethodView):
+    def get(self, pk):
+        user = request.user
+        page, number = self.page_info
+        collect = Collect.query.filter_by(
+            id=pk, author_id=user.id).first_or_404()
+        form = CollectForm()
+        form.name.data = collect.name
+        form.description.data = collect.description
+        form.is_hidden.data = 0 if collect.is_hidden else 1
+        topics = collect.topics.paginate(page, number, True)
+        data = {'collect': collect, 'topics': topics, 'form': form}
+        return render_template('collect/collect.html', **data)
+
+    def put(self, pk):
+        post_data = request.data
+        collect = Collect.query.filter_by(id=pk).first_or_404()
+        name = post_data.pop('name', None)
+        description = post_data.pop('description', None)
+        is_hidden = post_data.pop('is_hidden', None)
+        if name is not None:
+            collect.name = name
+        if description is not None:
+            collect.description = description
+        if is_hidden is not None:
+            collect.is_hidden = is_hidden
+        collect.save()
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
+
+    def delete(self, pk):
+        collect = Collect.query.filter_by(id=pk).first_or_404
+        collect.delete()
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
+
+
+class AddToCollectView(MethodView):
+    def post(self, topicId):
+        user = request.user
+        form = request.form.getlist('add-to-collect')
+        topic = Topic.query.filter_by(id=topicId).first_or_404()
+        for cid in form:
+            '''This has a problem'''
+            collect = Collect.query.filter_by(id=cid).first_or_404()
+            if not Collect.query.filter_by(
+                    topics__id=topic.id, author_id=user.id).exists():
+                collect.topics.append(topic)
+                collect.save()
+        return redirect(url_for('topic.topic', topicId=topic.id))
+
+    # def delete(self, topicId):
+    #     user = request.user
+    #     form = request.form.getlist('add-to-collect')
+    #     topic = Topic.query.filter_by(id=topicId).first_or_404()
+    #     for cid in form:
+    #         '''This has a problem'''
+    #         collect = Collect.query.filter_by(id=cid).first_or_404()
+    #         if not Collect.query.filter_by(
+    #                 topics__id=topic.id, author_id=user.id).exists():
+    #             collect.topics.append(topic)
+    #             collect.save()
+    #     return redirect(url_for('topic.topic', topicId=topic.id))

+ 3 - 2
forums/api/follow/views.py

@@ -6,14 +6,15 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-22 21:49:05 (CST)
-# Last Update:星期一 2017-3-27 22:0:17 (CST)
+# Last Update:星期二 2017-3-28 18:2:43 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from flask import render_template, request
 
 from forums.api.tag.models import Tags
-from forums.api.topic.models import Collect, Topic
+from forums.api.topic.models import  Topic
+from forums.api.collect.models import Collect
 from forums.api.user.models import User
 from forums.common.response import HTTPResponse
 from forums.common.views import IsAuthMethodView as MethodView

+ 160 - 0
forums/api/forms.py

@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: forms.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 12:53:02 (CST)
+# Last Update:星期二 2017-3-28 17:59:13 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask import redirect, session, url_for
+from flask_babelex import lazy_gettext as _
+from flask_wtf import FlaskForm as Form
+from flask_wtf.file import FileAllowed, FileField, FileRequired
+from wtforms import (BooleanField, PasswordField, RadioField, SelectField,
+                     StringField, TextAreaField)
+from wtforms.validators import DataRequired, Email, EqualTo, Length
+
+from forums.api.forums.models import Board
+from forums.api.topic.models import Topic
+from forums.api.user.models import UserSetting
+
+
+def error_callback():
+    return redirect(url_for('topic.ask'))
+
+
+def collect_error_callback():
+    return redirect(url_for('collect.list'))
+
+
+def form_board():
+    form = TopicForm()
+    results = []
+    for b in Board.query.filter_by(parent_id=None):
+        if b.parent is None:
+            results.append((b.id, b.name))
+        else:
+            results.append((b.id, b.name + '   --' + b.parent.name))
+    form.category.choices = results
+    return form
+
+
+class BaseForm(Form):
+    username = StringField(
+        _('Username:'), [DataRequired(), Length(
+            min=4, max=20)])
+    password = PasswordField(
+        _('Password:'), [DataRequired(), Length(
+            min=4, max=20)])
+    captcha = StringField(
+        _('Captcha:'), [DataRequired(), Length(
+            min=4, max=4)])
+
+    def validate(self):
+        rv = Form.validate(self)
+        if not rv:
+            return False
+
+        captcha = session['captcha']
+        captcha_data = self.captcha.data
+        if captcha_data.lower() != captcha.lower():
+            self.captcha.errors.append(_('The captcha is error'))
+            return False
+
+        return True
+
+
+class RegisterForm(BaseForm):
+    email = StringField(_('Email:'), [DataRequired(), Email()])
+
+
+class LoginForm(BaseForm):
+    remember = BooleanField(_('Remember me'), default=False)
+
+
+class SortForm(Form):
+    within = SelectField(
+        _('Choice'),
+        coerce=int,
+        choices=[(0, _('All Topics')), (1, _('One Day')), (2, _('One Week')),
+                 (3, _('One Month'))])
+    orderby = SelectField(
+        'orderby', coerce=int, choices=[(0, _('Publish')), (1, _('Author'))])
+    desc = SelectField(
+        'Up and Down', coerce=int, choices=[(0, _('Desc')), (1, _('Asc'))])
+
+
+class SearchForm(Form):
+    search = StringField(_('search'), validators=[DataRequired()])
+
+
+class MessageForm(Form):
+    message = TextAreaField(_('message'), validators=[DataRequired()])
+
+
+class TopicForm(Form):
+    title = StringField(_('Title:'), [DataRequired(), Length(min=4, max=36)])
+    content = TextAreaField(_('Content:'), [DataRequired(), Length(min=6)])
+    category = SelectField(_('Category:'), coerce=int)
+    tags = StringField(_('Tags:'), [DataRequired(), Length(min=2, max=36)])
+    content_type = SelectField(
+        _('ContentType'), choices=Topic.CONTENT_TYPE, coerce=str)
+
+
+class ReplyForm(Form):
+    content = TextAreaField(_('Content:'), [DataRequired()])
+
+
+class CollectForm(Form):
+    name = StringField(_('Name:'), [DataRequired()])
+    description = TextAreaField(_('Description:'))
+    is_hidden = RadioField(
+        'Is_hidden:', choices=[(0, 'is_hidden'), (1, 'is_public')], coerce=int)
+
+
+choices = UserSetting.STATUS
+timezone = UserSetting.TIMEZONE
+locale = UserSetting.LOCALE
+
+
+class AvatarForm(Form):
+    avatar = FileField(
+        _('Upload Avatar:'),
+        validators=[FileRequired(), FileAllowed(['jpg', 'png'],
+                                                '上传文件只能为图片且图片格式为jpg,png')])
+
+
+class PrivacyForm(Form):
+    online_status = SelectField(
+        _('Login status:'), coerce=str, choices=choices)
+    topic_list = SelectField(_('Topic List:'), coerce=str, choices=choices)
+
+    rep_list = SelectField(_('Reply List:'), coerce=str, choices=choices)
+    ntb_list = SelectField(_('Notebook List:'), coerce=str, choices=choices)
+    collect_list = SelectField(_('Collect List:'), coerce=str, choices=choices)
+
+
+class ProfileForm(Form):
+    introduce = TextAreaField(_('Introduce:'), [Length(max=256)])
+    school = StringField(_('School:'), [Length(max=256)])
+    word = TextAreaField(_('Signature:'), [Length(max=256)])
+
+
+class PasswordForm(Form):
+    old_password = PasswordField(
+        _('Old Password:'), [DataRequired(), Length(
+            min=4, max=20)])
+    new_password = PasswordField(
+        _('New Password:'), [DataRequired(), Length(
+            min=4, max=20)])
+    rnew_password = PasswordField(
+        _('New Password again:'), [DataRequired(), EqualTo('new_password')])
+
+
+class BabelForm(Form):
+    timezone = SelectField(_('Timezone:'), coerce=str, choices=timezone)
+    locale = SelectField(_('Locale:'), coerce=str, choices=locale)

+ 0 - 36
forums/api/forums/forms.py

@@ -1,36 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: forms.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-12-29 21:53:47 (CST)
-# Last Update:星期四 2016-12-29 21:53:51 (CST)
-#          By:
-# Description:
-# **************************************************************************
-from flask_wtf import Form
-from wtforms import SelectField, StringField, TextAreaField
-from wtforms.validators import DataRequired
-from flask_babelex import lazy_gettext as _
-
-
-class SortForm(Form):
-    display = SelectField(
-        _('Choice'),
-        coerce=int,
-        choices=[(0, _('All Topics')), (1, _('One Day')), (2, _('One Week')),
-                 (3, _('One Month'))])
-    sort = SelectField(
-        'Sort', coerce=int, choices=[(0, _('Publish')), (1, _('Author'))])
-    st = SelectField(
-        'Up and Down', coerce=int, choices=[(0, _('Desc')), (1, _('Asc'))])
-
-
-class SearchForm(Form):
-    search = StringField(_('search'), validators=[DataRequired()])
-
-
-class MessageForm(Form):
-    message = TextAreaField(_('message'), validators=[DataRequired()])

+ 0 - 65
forums/api/setting/forms.py

@@ -1,65 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: forms.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-12-20 22:13:24 (CST)
-# Last Update:星期六 2017-3-25 18:17:14 (CST)
-#          By:
-# Description:
-# **************************************************************************
-from flask_wtf import Form
-from flask_babelex import lazy_gettext as _
-from wtforms import StringField, PasswordField, TextAreaField, SelectField
-from wtforms.validators import Length, DataRequired, EqualTo
-from flask_wtf.file import FileField, FileAllowed, FileRequired
-from forums.api.user.models import UserSetting
-from flask import redirect, url_for
-
-choices = UserSetting.STATUS
-timezone = UserSetting.TIMEZONE
-locale = UserSetting.LOCALE
-
-
-def error_callback(url):
-    return lambda: redirect(url_for(url))
-
-
-class AvatarForm(Form):
-    avatar = FileField(
-        _('Upload Avatar:'),
-        validators=[FileRequired(), FileAllowed(['jpg', 'png'],
-                                                '上传文件只能为图片且图片格式为jpg,png')])
-
-class PrivacyForm(Form):
-    online_status = SelectField(
-        _('Login status:'), coerce=str, choices=choices)
-    topic_list = SelectField(_('Topic List:'), coerce=str, choices=choices)
-
-    rep_list = SelectField(_('Reply List:'), coerce=str, choices=choices)
-    ntb_list = SelectField(_('Notebook List:'), coerce=str, choices=choices)
-    collect_list = SelectField(_('Collect List:'), coerce=str, choices=choices)
-
-
-class ProfileForm(Form):
-    introduce = TextAreaField(_('Introduce:'), [Length(max=256)])
-    school = StringField(_('School:'), [Length(max=256)])
-    word = TextAreaField(_('Signature:'), [Length(max=256)])
-
-
-class PasswordForm(Form):
-    old_password = PasswordField(
-        _('Old Password:'), [DataRequired(), Length(
-            min=4, max=20)])
-    new_password = PasswordField(
-        _('New Password:'), [DataRequired(), Length(
-            min=4, max=20)])
-    rnew_password = PasswordField(
-        _('New Password again:'), [DataRequired(), EqualTo('new_password')])
-
-
-class BabelForm(Form):
-    timezone = SelectField(_('Timezone:'), coerce=str, choices=timezone)
-    locale = SelectField(_('Locale:'), coerce=str, choices=locale)

+ 7 - 3
forums/api/setting/views.py

@@ -6,18 +6,22 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-20 22:16:04 (CST)
-# Last Update:星期六 2017-3-25 20:22:21 (CST)
+# Last Update:星期二 2017-3-28 15:53:0 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from flask import render_template, redirect, url_for, flash, request
 from flask_login import current_user, logout_user
-from forums.api.setting.forms import (ProfileForm, PasswordForm, PrivacyForm,
-                                      AvatarForm, BabelForm, error_callback)
+from forums.api.forms import (ProfileForm, PasswordForm, PrivacyForm,
+                              AvatarForm, BabelForm)
 from forums.common.views import IsAuthMethodView as MethodView
 from flask_maple.auth.forms import form_validate
 
 
+def error_callback(url):
+    return lambda: redirect(url_for(url))
+
+
 class ProfileView(MethodView):
     def get(self):
         user = request.user

+ 6 - 3
forums/api/tag/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:04 (CST)
-# Last Update:星期一 2017-3-27 21:40:52 (CST)
+# Last Update:星期二 2017-3-28 17:25:34 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -49,7 +49,10 @@ class TagsView(MethodView):
 
 class TagFeedView(MethodView):
     def get(self, name):
-        setting = current_app.config.get('SITE')
+        setting = current_app.config.get('SITE', {
+            'title': 'aaa',
+            'introduce': 'asdadj'
+        })
         title = setting['title']
         introduce = setting['introduce']
         feed = AtomFeed(
@@ -57,7 +60,7 @@ class TagFeedView(MethodView):
             feed_url=request.url,
             url=request.url_root,
             subtitle=introduce)
-        topics = Topic.query.filter_by(tags__name=name).all()
+        topics = Topic.query.filter_by(tags__name=name).limit(10)
         for topic in topics:
             if topic.content_type == Topic.CONTENT_TYPE_MARKDOWN:
                 content = topic.content

+ 0 - 59
forums/api/topic/forms.py

@@ -1,59 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: forms.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-12-18 18:49:00 (CST)
-# Last Update:星期六 2017-3-25 21:47:25 (CST)
-#          By:
-# Description:
-# **************************************************************************
-from flask import request, render_template, redirect, url_for
-from flask_wtf import Form
-from wtforms import (StringField, TextAreaField, SelectField, RadioField)
-from wtforms.validators import DataRequired, Length
-from flask_babelex import lazy_gettext as _
-from forums.api.forums.models import Board
-from .models import Topic
-
-
-def error_callback():
-    return redirect(url_for('topic.ask'))
-
-
-def collect_error_callback():
-    return redirect(url_for('topic.collectlist'))
-
-
-def form_board():
-    form = TopicForm()
-    results = []
-    for b in Board.query.filter_by(parent_id=None):
-        if b.parent is None:
-            results.append((b.id, b.name))
-        else:
-            results.append((b.id, b.name + '   --' + b.parent.name))
-    form.category.choices = results
-    return form
-
-
-class TopicForm(Form):
-    title = StringField(_('Title:'), [DataRequired(), Length(min=4, max=36)])
-    content = TextAreaField(_('Content:'), [DataRequired(), Length(min=6)])
-    category = SelectField(_('Category:'), coerce=int)
-    tags = StringField(_('Tags:'), [DataRequired(), Length(min=2, max=36)])
-    content_type = SelectField(
-        _('ContentType'), choices=Topic.CONTENT_TYPE, coerce=str)
-
-
-class ReplyForm(Form):
-    content = TextAreaField(_('Content:'), [DataRequired()])
-
-
-class CollectForm(Form):
-    name = StringField(_('Name:'), [DataRequired()])
-    description = TextAreaField(_('Description:'))
-    private = RadioField(
-        'Private:', choices=[(0, 'privacy'), (1, 'public')], coerce=int)

+ 1 - 51
forums/api/topic/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 20:52:07 (CST)
-# Last Update:星期六 2017-3-25 21:45:35 (CST)
+# Last Update:星期二 2017-3-28 17:58:52 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -77,56 +77,6 @@ class Topic(db.Model, ModelMixin):
         return "<Topic %r>" % self.title
 
 
-topics_collects = db.Table(
-    'topics_collects',
-    db.Column('topics_id', db.Integer, db.ForeignKey('topics.id')),
-    db.Column('collects_id', db.Integer, db.ForeignKey('collects.id')))
-
-collect_follow_users = db.Table(
-    'collects_follow_users',
-    db.Column('collects_id', db.Integer, db.ForeignKey('collects.id')),
-    db.Column('follow_users_id', db.Integer, db.ForeignKey('users.id')))
-
-
-class Collect(db.Model, ModelMixin):
-    __tablename__ = 'collects'
-    id = db.Column(db.Integer, primary_key=True)
-    name = db.Column(db.String(32), nullable=False)
-    description = db.Column(db.String(256), nullable=True)
-    privacy = db.Column(db.Boolean, default=False)
-    created_at = db.Column(
-        db.DateTime, default=datetime.utcnow(), nullable=False)
-    updated_at = db.Column(
-        db.DateTime, default=datetime.utcnow(), onupdate=datetime.utcnow())
-    author_id = db.Column(
-        db.Integer, db.ForeignKey(
-            'users.id', ondelete="CASCADE"))
-    author = db.relationship(
-        User,
-        backref=db.backref(
-            'collects', cascade='all,delete-orphan', lazy='dynamic'),
-        lazy='joined')
-
-    topics = db.relationship(
-        Topic,
-        secondary=topics_collects,
-        backref=db.backref(
-            'collects', lazy='dynamic'),
-        lazy='dynamic')
-
-    followers = db.relationship(
-        User,
-        secondary=collect_follow_users,
-        backref=db.backref(
-            'following_collects', lazy='dynamic'),
-        lazy='dynamic')
-
-    def __str__(self):
-        return self.name
-
-    def __repr__(self):
-        return "<Collect %r>" % self.name
-
 
 replies_likers = db.Table(
     'replies_likers',

+ 3 - 12
forums/api/topic/urls.py

@@ -6,15 +6,14 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:15:34 (CST)
-# Last Update:星期一 2017-3-27 22:20:52 (CST)
+# Last Update:星期二 2017-3-28 18:1:54 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from flask import Blueprint
 
-from .views import (CollectListView, CollectView, LikeView, ReplyListView,
-                    ReplyView, TopicAskView, TopicEditView, TopicListView,
-                    TopicPreviewView, TopicView, AddToCollectView)
+from .views import (LikeView, ReplyListView, ReplyView, TopicAskView,
+                    TopicEditView, TopicListView, TopicPreviewView, TopicView)
 
 site = Blueprint('topic', __name__)
 
@@ -26,10 +25,6 @@ ask_view = TopicAskView.as_view('ask')
 edit_view = TopicEditView.as_view('edit')
 preview_view = TopicPreviewView.as_view('preview')
 
-collect_list = CollectListView.as_view('collectlist')
-collect = CollectView.as_view('collect')
-add_to_collect = AddToCollectView.as_view('add_to_collect')
-
 reply_list = ReplyListView.as_view('reply_list')
 reply = ReplyView.as_view('reply')
 like_view = LikeView.as_view('reply_like')
@@ -44,7 +39,3 @@ site.add_url_rule('/topic/<int:topicId>/edit', view_func=edit_view)
 site.add_url_rule('/topic/<int:topicId>/replies', view_func=reply_list)
 site.add_url_rule('/replies/<int:replyId>', view_func=reply)
 site.add_url_rule('/replies/<int:replyId>/like', view_func=like_view)
-
-site.add_url_rule('/collect', view_func=collect_list)
-site.add_url_rule('/collect/<int:collectId>', view_func=collect)
-site.add_url_rule('/topic/<int:topicId>/collect', view_func=add_to_collect)

+ 33 - 106
forums/api/topic/views.py

@@ -6,13 +6,13 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:39 (CST)
-# Last Update:星期一 2017-3-27 22:21:2 (CST)
+# Last Update:星期二 2017-3-28 22:24:9 (CST)
 #          By:
 # Description:
 # **************************************************************************
-from flask import redirect, render_template, request, url_for
+from flask import redirect, render_template, request, url_for, Markup
 from flask_babelex import gettext as _
-from flask_login import current_user
+from flask_login import current_user, login_required
 
 from flask_maple.auth.forms import form_validate
 from flask_maple.response import HTTPResponse
@@ -21,10 +21,12 @@ from forums.api.tag.models import Tags
 from forums.common.serializer import Serializer
 from forums.common.utils import gen_filter_dict, gen_order_by
 from forums.common.views import BaseMethodView as MethodView
+from forums.filters import safe_markdown
 
-from .forms import (CollectForm, ReplyForm, TopicForm, collect_error_callback,
-                    error_callback, form_board)
-from .models import Collect, Reply, Topic
+from forums.api.forms import (CollectForm, ReplyForm, TopicForm,
+                              collect_error_callback, error_callback,
+                              form_board)
+from .models import Reply, Topic
 
 
 class TopicAskView(MethodView):
@@ -50,10 +52,14 @@ class TopicEditView(MethodView):
 
 
 class TopicPreviewView(MethodView):
+    @login_required
     def post(self):
-        choice = request.values.get('choice')
-        content = request.values.get('content')
-        return ''
+        post_data = request.data
+        content_type = post_data.pop('content_type', None)
+        content = post_data.pop('content', None)
+        if content_type == Topic.CONTENT_TYPE_MARKDOWN:
+            return safe_markdown(content)
+        return content
 
 
 class TopicListView(MethodView):
@@ -90,9 +96,7 @@ class TopicListView(MethodView):
         for tag in tags:
             topic_tag = Tags.query.filter_by(name=tag).first()
             if topic_tag is None:
-                topic_tag = Tags()
-                topic_tag.name = tag
-                topic_tag.description = tag
+                topic_tag = Tags(name=tag, description=tag)
                 topic_tag.save()
             topic_tags.append(topic_tag)
         topic.tags = topic_tags
@@ -108,100 +112,23 @@ class TopicView(MethodView):
         data = {'title': topic.title, 'form': form, 'topic': topic}
         return render_template('topic/topic.html', **data)
 
-    def put(self, topicId):
-        post_data = request.data
-        topic = Topic.query.filter_by(id=topicId).first()
-        title = post_data.pop('title', None)
-        content = post_data.pop('content', None)
-        content_type = post_data.pop('content_type', None)
-        board = post_data.pop('board', None)
-        if title is not None:
-            topic.title = title
-        if content is not None:
-            topic.content = content
-        if content_type is not None:
-            topic.content_type = content_type
-        if board is not None:
-            topic.board = int(board)
-        topic.save()
-        return redirect(url_for('topic.topic', topicId=topic.id))
-
-    # def delete(self, topicId):
-    #     topic = Topic.query.filter_by(id=topicId).first()
-    #     topic.delete()
-    #     serializer = Serializer(topic, many=False)
-    #     return HTTPResponse(HTTPResponse.NORMAL_STATUS,
-    #                         **serializer.data).to_response()
-
-
-class CollectListView(MethodView):
-    def get(self):
-        form = CollectForm()
-        page, number = self.page_info
-        collects = Collect.get_list(page, number)
-        data = {'collects': collects, 'form': form}
-        return render_template('collect/collect_list.html', **data)
-
-    @form_validate(CollectForm, error=collect_error_callback, f='')
-    def post(self):
-        form = CollectForm()
-        post_data = form.data
-        name = post_data.pop('name', None)
-        description = post_data.pop('description', None)
-        privacy = post_data.pop('private', None)
-        privacy = True if privacy == '0' else False
-        collect = Collect(name=name, description=description, privacy=privacy)
-        collect.author = current_user
-        collect.save()
-        return collect_error_callback()
-
-
-class CollectView(MethodView):
-    def get(self, collectId):
-        form = CollectForm()
-        collect = Collect.get(id=collectId)
-        topics = collect.topics.paginate(1, 23, True)
-        data = {'collect': collect, 'topics': topics, 'form': form}
-        return render_template('collect/collect.html', **data)
-
-    def put(self, collectId):
-        post_data = request.data
-        collect = Collect.query.filter_by(id=collectId).first()
-        name = post_data.pop('name', None)
-        description = post_data.pop('description', None)
-        privacy = post_data.pop('privacy', None)
-        if name is not None:
-            collect.name = name
-        if description is not None:
-            collect.description = description
-        if privacy is not None:
-            collect.privacy = privacy
-        collect.save()
-        serializer = Serializer(collect, many=False)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS,
-                            **serializer.data).to_response()
-
-    def delete(self, collectId):
-        collect = Collect.query.filter_by(id=collectId).first()
-        collect.delete()
-        serializer = Serializer(collect, many=False)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS,
-                            **serializer.data).to_response()
-
-
-class AddToCollectView(MethodView):
-    def post(self, topicId):
-        user = request.user
-        form = request.form.getlist('add-to-collect')
-        topic = Topic.query.filter_by(id=topicId).first_or_404()
-        for cid in form:
-            '''This has a problem'''
-            collect = Collect.query.filter_by(id=cid).first_or_404()
-            if not Collect.query.filter_by(
-                    topics__id=topic.id, author_id=user.id).exists():
-                collect.topics.append(topic)
-                collect.save()
-        return redirect(url_for('topic.topic', topicId=topic.id))
+    # def put(self, topicId):
+    #     post_data = request.data
+    #     topic = Topic.query.filter_by(id=topicId).first_or_404()
+    #     title = post_data.pop('title', None)
+    #     content = post_data.pop('content', None)
+    #     content_type = post_data.pop('content_type', None)
+    #     board = post_data.pop('board', None)
+    #     if title is not None:
+    #         topic.title = title
+    #     if content is not None:
+    #         topic.content = content
+    #     if content_type is not None:
+    #         topic.content_type = content_type
+    #     if board is not None:
+    #         topic.board = int(board)
+    #     topic.save()
+    #     return redirect(url_for('topic.topic', topicId=topic.id))
 
 
 class ReplyListView(MethodView):

+ 4 - 3
forums/api/upload/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-21 21:56:41 (CST)
-# Last Update:星期六 2017-3-25 18:17:15 (CST)
+# Last Update:星期二 2017-3-28 15:58:18 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -15,7 +15,7 @@ from flask import (url_for, redirect, send_from_directory, current_app,
 from flask.views import MethodView
 from flask_login import login_required, current_user
 from flask_maple.auth.forms import form_validate
-from forums.api.setting.forms import error_callback, AvatarForm
+from forums.api.forms import AvatarForm
 from werkzeug import secure_filename
 from time import time
 from random import randint
@@ -26,7 +26,8 @@ import os
 class AvatarView(MethodView):
     decorators = [login_required]
 
-    @form_validate(AvatarForm, error=error_callback('setting.setting'), f='')
+    @form_validate(
+        AvatarForm, error=lambda: redirect(url_for('setting.setting')), f='')
     def post(self):
         form = AvatarForm()
         file = request.files[form.avatar.name]

+ 3 - 1
forums/api/urls.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-01-25 20:12:58 (CST)
-# Last Update:星期六 2017-3-25 20:16:19 (CST)
+# Last Update:星期二 2017-3-28 17:40:36 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -18,6 +18,7 @@ from .user.urls import site as user_site
 from .setting.urls import site as setting_site
 from .follow.urls import site as follow_site
 from .upload.urls import site as upload_site
+from .collect.urls import site as collect_site
 # from .permission.urls import site as perm_site
 # from .mine.urls import site as mine_site
 
@@ -31,5 +32,6 @@ def api_routers(app):
     app.register_blueprint(setting_site)
     app.register_blueprint(follow_site)
     app.register_blueprint(upload_site)
+    app.register_blueprint(collect_site)
     # app.register_blueprint(perm_site)
     # app.register_blueprint(mine_site)

+ 7 - 2
forums/api/user/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:08:06 (CST)
-# Last Update:星期六 2017-3-25 21:8:57 (CST)
+# Last Update:星期二 2017-3-28 22:33:11 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -14,7 +14,8 @@ from flask import redirect, render_template, request, url_for
 from flask_login import current_user
 from forums.api.forums.models import Board
 from forums.api.tag.models import Tags
-from forums.api.topic.models import Topic, Reply, Collect
+from forums.api.topic.models import Topic, Reply
+from forums.api.collect.models import Collect
 from forums.common.utils import gen_filter_dict, gen_order_by
 from forums.common.views import BaseMethodView as MethodView
 
@@ -49,6 +50,8 @@ class UserView(MethodView):
         if setting.topic_list == 1 or (setting.topic_list == 2 and
                                        current_user.is_authenticated):
             topic_is_allowed = True
+        if current_user.is_authenticated and current_user.id == user.id:
+            topic_is_allowed = True
         data = {
             'topics': topics,
             'user': user,
@@ -73,6 +76,8 @@ class UserReplyListView(MethodView):
         if setting.rep_list == 1 or (current_user.is_authenticated and
                                      setting.rep_list == 2):
             replies_is_allowed = True
+        if current_user.is_authenticated and current_user.id == user.id:
+            replies_is_allowed = True
         data = {
             'replies': replies,
             'user': user,

+ 32 - 0
forums/app.py

@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: app.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 16:11:07 (CST)
+# Last Update:星期二 2017-3-28 16:11:19 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask_principal import RoleNeed, UserNeed, identity_loaded
+from flask_login import current_user
+
+
+def register_app(app):
+    @identity_loaded.connect_via(app)
+    def on_identity_loaded(sender, identity):
+        '''基础权限'''
+        identity.user = current_user
+
+        if hasattr(current_user, 'id'):
+            identity.provides.add(UserNeed(current_user.id))
+
+        if hasattr(current_user, 'is_superuser'):
+            if current_user.is_superuser:
+                identity.provides.add(RoleNeed('super'))
+
+        if hasattr(current_user, 'is_authenticated'):
+            if current_user.is_authenticated:
+                identity.provides.add(RoleNeed('auth'))

+ 2 - 2
forums/common/middleware.py

@@ -6,13 +6,13 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-12 13:29:17 (CST)
-# Last Update:星期六 2017-3-25 18:57:58 (CST)
+# Last Update:星期二 2017-3-28 16:53:28 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from flask import g, request
 from flask_login import current_user
-from forums.api.forums.forms import SortForm, SearchForm
+from forums.api.forms import SortForm, SearchForm
 
 
 class GlobalMiddleware(object):

+ 2 - 2
forums/common/utils.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-13 13:40:38 (CST)
-# Last Update:星期二 2017-3-14 16:2:24 (CST)
+# Last Update:星期二 2017-3-28 16:59:0 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -22,7 +22,7 @@ def gen_order_by(query_dict=dict(), keys=[], date_key=True):
     if date_key:
         keys += ['created_at', 'updated_at']
     order_by = ['id']
-    descent = query_dict.pop('descent', None)
+    descent = query_dict.pop('orderby', None)
     if descent is not None:
         descent = descent.split(',')
         descent = list(set(keys) & set(descent))

+ 2 - 2
forums/filters.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-07 21:00:32 (CST)
-# Last Update:星期一 2017-3-27 21:45:50 (CST)
+# Last Update:星期二 2017-3-28 21:12:1 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -141,7 +141,7 @@ def user_is_followed(pk):
 
 
 def is_collected(topicId):
-    from forums.api.topic.models import Collect
+    from forums.api.collect.models import Collect
     return Collect.query.filter_by(
         topics__id=topicId, author_id=current_user.id).exists()
 

+ 16 - 0
forums/permission.py

@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: permission.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-28 16:02:43 (CST)
+# Last Update:星期二 2017-3-28 16:13:3 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask_principal import Permission, RoleNeed
+
+super_permission = Permission(RoleNeed('super'))
+auth_permission = Permission(RoleNeed('auth'))

BIN
forums/test.db


+ 9 - 11
static/assets/home.js

@@ -10,11 +10,12 @@ else
 {$("#showerror").show();$("#changeCode").attr("src",url.captcha+"?code="+Math.random());$("#captcha").val("");if(response.description!==""){$("#error").text(response.description);}
 else{$("#error").text(response.message);}}}
 $('button#login').click(function(){$.ajax({type:"POST",url:url.login,data:JSON.stringify({username:$('#username').val(),password:$('#password').val(),captcha:$("#captcha").val(),remember:$("#remember").is(':checked')}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});$('button#register').click(function(){$.ajax({type:"POST",url:url.register,data:JSON.stringify({username:$('#username').val(),email:$('#email').val(),password:$('#password').val(),captcha:$("#captcha").val()}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});$('button#forget').click(function(){$.ajax({type:"POST",url:url.forget,data:JSON.stringify({email:$('#email').val(),captcha:$("#captcha").val()}),contentType:'application/json;charset=UTF-8',success:function(response){return AuthCallBack(response);}});});});function loadFile(event){var _file=document.getElementById("avatar");var i=_file.value.lastIndexOf('.');var len=_file.value.length;var extEndName=_file.value.substring(i+1,len);var extName="JPG,PNG";if(extName.indexOf(extEndName.toUpperCase())==-1){alert("您只能上传"+extName+"格式的文件");$('#avatar').val('');}else{var reader=new FileReader();reader.onload=function(){var icon='<i class="icon-exchange"></i>'+'\n';var img='<img src="'+reader.result+'" title="avatar" class="avatar img-circle">';$("#show-avatar").html(icon+img);};reader.readAsDataURL(event.target.files[0]);}}
-function SortFuntion(){var display=$('#display').val();var sort=$('#sort').val();var st=$('#st').val();var params={};if(display!='0'){params.within=display;}
-if(sort!='0'){params.orderby=sort;}
-if(st!='0'){params.desc=st;}
+function getQueryParams(k){var p={};location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v;});return k?p[k]:p;}
+function SortFuntion(){var within=$('select#within').val();var orderby=$('select#orderby').val();var desc=$('select#desc').val();var params=getQueryParams();if(within!='0'){params.within=within;}
+if(orderby!='0'){params.orderby=orderby;}
+if(desc!='0'){params.desc=desc;}
 window.location.href=window.location.pathname+'?'+$.param(params);}
-$(document).ready(function(){$('#display').change(function(){SortFuntion();});$('#sort').change(function(){SortFuntion();});$('#st').change(function(){SortFuntion();});$('span#email-confirm').click(function(){$.ajax({type:"POST",url:"/confirm-email",data:JSON.stringify({}),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
+$(document).ready(function(){$('select#within').change(function(){SortFuntion();});$('select#orderby').change(function(){SortFuntion();});$('select#desc').change(function(){SortFuntion();});$('span#email-confirm').click(function(){$.ajax({type:"POST",url:"/confirm-email",data:JSON.stringify({}),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
 {alert(result.error);}else
 {alert(result.error);}}});});});function dispatch(){var q=document.getElementById("search");if(q.value!==""){var url='https://www.google.com/search?q=site:forums.honmaple.org%20'+q.value;if(navigator.userAgent.indexOf('iPad')>-1||navigator.userAgent.indexOf('iPod')>-1||navigator.userAgent.indexOf('iPhone')>-1){location.href=url;}else{window.open(url,"_blank");}
 return false;}else{return false;}}
@@ -23,9 +24,9 @@ function Follow(obj,data,url){if(obj.hasClass('active')){$.ajax({type:"DELETE",u
 {alert('fail');}}});}else{$.ajax({type:"POST",url:url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {obj.text('取消关注').addClass('active');}else
 {alert('fail');}}});}}
-$(document).ready(function(){$('button.topic-following').click(function(){var _$this=$(this);var url="/user/following/topics";var data=JSON.stringify({topicId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.tag-following').click(function(){var _$this=$(this);var url="/user/following/tags";var data=JSON.stringify({tagId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.user-following').click(function(){var _$this=$(this);var url="/user/following/users";var data=JSON.stringify({userId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.collect-following').click(function(){var _$this=$(this);var url="/user/following/collects";var data=JSON.stringify({collectId:_$this.attr("data-id"),});Follow(_$this,data,url);});});function DoCollect(collectData){$(document).ready(function(){$('button#edit-collect-form').click(function(){var data=JSON.stringify({name:$('#name').val(),description:$('#description').val(),is_privacy:$("input[name='is_privacy']:checked").val()});$.ajax({type:"PUT",url:collectData.collect_action_url,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
-{window.location=collectData.collect_action_url;}}});});$('button#delete-collect-form').click(function(){$.ajax({type:"DELETE",url:collectData.collect_action_url,data:JSON.stringify(),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
-{window.location=collectData.collect_url;}}});});$('#delete-from-collect').click(function(){var _$this=$(this);var topicId=_$this.attr('data-id');var data=JSON.stringify({topicId:topicId});$.ajax({type:"DELETE",url:collectData.delete_detail_action_url,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
+$(document).ready(function(){$('button.topic-following').click(function(){var _$this=$(this);var url="/user/following/topics";var data=JSON.stringify({topicId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.tag-following').click(function(){var _$this=$(this);var url="/user/following/tags";var data=JSON.stringify({tagId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.user-following').click(function(){var _$this=$(this);var url="/user/following/users";var data=JSON.stringify({userId:_$this.attr("data-id"),});Follow(_$this,data,url);});$('button.collect-following').click(function(){var _$this=$(this);var url="/user/following/collects";var data=JSON.stringify({collectId:_$this.attr("data-id"),});Follow(_$this,data,url);});});function DoCollect(collect_url){$(document).ready(function(){$('button#edit-collect').click(function(){var data=JSON.stringify({name:$('#name').val(),description:$('#description').val(),is_hidden:$("input[name='is_hidden']:checked").val()});$.ajax({type:"PUT",url:collect_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
+{window.location.href=collect_url;}}});});$('button#delete-collect').click(function(){$.ajax({type:"DELETE",url:collect_url,data:JSON.stringify(),contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
+{window.location.href=collect_url;}}});});$('#delete-from-collect').click(function(){var _$this=$(this);var topicId=_$this.attr('data-id');var data=JSON.stringify({topicId:topicId});$.ajax({type:"DELETE",url:'/topic/'+topicId+'/collect',data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {_$this.parent().remove();}}});});});}
 $(document).ready(function(){$('.like-reply').click(function(){var _$this=$(this);var replyId=_$this.attr('data-id');var like_url="/replies/"+replyId+'/like';var data=JSON.stringify({});if(_$this.hasClass('like-active')){$.ajax({type:"DELETE",url:like_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {_$this.attr("title","赞");_$this.removeClass("like-active");_$this.addClass("like-no-active");}else
@@ -36,13 +37,10 @@ $(document).ready(function(){$('.like-reply').click(function(){var _$this=$(this
 {window.location.href=result.url;}}});});$('#topic-down-vote').click(function(){var data=JSON.stringify({});$.ajax({type:"DELETE",url:voteData.vote_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {$('.votes').html(result.html);}else
 {window.location.href=result.url;}}});});});}
-function PreviewTopic(pre_url){$('#topic-preview').click(function(){var content=$('#content').val();$.post(pre_url,{content:$("#content").val(),choice:$("#choice").val()},function(data){$("#show-preview").html(data);});});}
-function AskTopic(pre_url){$(document).ready(function(){PreviewTopic(pre_url);$('#tokenfield').tokenfield({limit:4});});}
-function EditTopic(pre_url,edit_url){$(document).ready(function(){PreviewTopic(pre_url);$('#tokenfield').tokenfield({limit:4});$('#topic-put-btn').click(function(){var form_data=$("form#topic-put").serializeArray();var data={};$.each(form_data,function(){data[this.name]=this.value;});data=JSON.stringify(data);$.ajax({type:"PUT",url:edit_url,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true){window.location.href=edit_url;}else{alert(result.error);}}});});});}/*!
+$(document).ready(function(){$('#topic-preview').click(function(){var content=$('#content').val();$.post('/topic/preview',{content:$("#content").val(),content_type:$("#content_type").val()},function(data){$("#show-preview").html(data);});});$('#tokenfield').tokenfield({limit:4});$('#topic-put-btn').click(function(){var _$this=$(this);var form_data=$("form#topic-put").serializeArray();var url='/topic/'+_$this.attr("data-id");var data={};$.each(form_data,function(){data[this.name]=this.value;});$.ajax({type:"PUT",url:url,data:JSON.stringify(data),contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200'){window.location.href=url;}}});});});/*!
  * bootstrap-tokenfield 0.12.1
  * https://github.com/sliptree/bootstrap-tokenfield
  * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
  */
-
 !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=global.window&&global.window.$?a(global.window.$):function(b){if(!b.$&&!b.fn)throw new Error("Tokenfield requires a window object with jQuery or a jQuery instance");return a(b.$||b)}:a(jQuery,window)}(function(a,b){"use strict";var c=function(c,d){var e=this;this.$element=a(c),this.textDirection=this.$element.css("direction"),this.options=a.extend(!0,{},a.fn.tokenfield.defaults,{tokens:this.$element.val()},this.$element.data(),d),this._delimiters="string"==typeof this.options.delimiter?[this.options.delimiter]:this.options.delimiter,this._triggerKeys=a.map(this._delimiters,function(a){return a.charCodeAt(0)}),this._firstDelimiter=this._delimiters[0];var f=a.inArray(" ",this._delimiters),g=a.inArray("-",this._delimiters);f>=0&&(this._delimiters[f]="\\s"),g>=0&&(delete this._delimiters[g],this._delimiters.unshift("-"));var h=["\\","$","[","{","^",".","|","?","*","+","(",")"];a.each(this._delimiters,function(b,c){var d=a.inArray(c,h);d>=0&&(e._delimiters[b]="\\"+c)});var i,j=b&&"function"==typeof b.getMatchedCSSRules?b.getMatchedCSSRules(c):null,k=c.style.width,l=this.$element.width();j&&a.each(j,function(a,b){b.style.width&&(i=b.style.width)});var m="rtl"===a("body").css("direction")?"right":"left",n={position:this.$element.css("position")};n[m]=this.$element.css(m),this.$element.data("original-styles",n).data("original-tabindex",this.$element.prop("tabindex")).css("position","absolute").css(m,"-10000px").prop("tabindex",-1),this.$wrapper=a('<div class="tokenfield form-control" />'),this.$element.hasClass("input-lg")&&this.$wrapper.addClass("input-lg"),this.$element.hasClass("input-sm")&&this.$wrapper.addClass("input-sm"),"rtl"===this.textDirection&&this.$wrapper.addClass("rtl");var o=this.$element.prop("id")||(new Date).getTime()+""+Math.floor(100*(1+Math.random()));this.$input=a('<input type="'+this.options.inputType+'" class="token-input" autocomplete="off" />').appendTo(this.$wrapper).prop("placeholder",this.$element.prop("placeholder")).prop("id",o+"-tokenfield").prop("tabindex",this.$element.data("original-tabindex"));var p=a('label[for="'+this.$element.prop("id")+'"]');if(p.length&&p.prop("for",this.$input.prop("id")),this.$copyHelper=a('<input type="text" />').css("position","absolute").css(m,"-10000px").prop("tabindex",-1).prependTo(this.$wrapper),k?this.$wrapper.css("width",k):i?this.$wrapper.css("width",i):this.$element.parents(".form-inline").length&&this.$wrapper.width(l),(this.$element.prop("disabled")||this.$element.parents("fieldset[disabled]").length)&&this.disable(),this.$element.prop("readonly")&&this.readonly(),this.$mirror=a('<span style="position:absolute; top:-999px; left:0; white-space:pre;"/>'),this.$input.css("min-width",this.options.minWidth+"px"),a.each(["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent"],function(a,b){e.$mirror[0].style[b]=e.$input.css(b)}),this.$mirror.appendTo("body"),this.$wrapper.insertBefore(this.$element),this.$element.prependTo(this.$wrapper),this.update(),this.setTokens(this.options.tokens,!1,!this.$element.val()&&this.options.tokens),this.listen(),!a.isEmptyObject(this.options.autocomplete)){var q="rtl"===this.textDirection?"right":"left",r=a.extend({minLength:this.options.showAutocompleteOnFocus?0:null,position:{my:q+" top",at:q+" bottom",of:this.$wrapper}},this.options.autocomplete);this.$input.autocomplete(r)}if(!a.isEmptyObject(this.options.typeahead)){var s=this.options.typeahead,t={minLength:this.options.showAutocompleteOnFocus?0:null},u=a.isArray(s)?s:[s,s];u[0]=a.extend({},t,u[0]),this.$input.typeahead.apply(this.$input,u),this.typeahead=!0}};c.prototype={constructor:c,createToken:function(b,c){var d=this;if(b="string"==typeof b?{value:b,label:b}:a.extend({},b),"undefined"==typeof c&&(c=!0),b.value=a.trim(b.value.toString()),b.label=b.label&&b.label.length?a.trim(b.label):b.value,!(!b.value.length||!b.label.length||b.label.length<=this.options.minLength||this.options.limit&&this.getTokens().length>=this.options.limit)){var e=a.Event("tokenfield:createtoken",{attrs:b});if(this.$element.trigger(e),e.attrs&&!e.isDefaultPrevented()){var f=a('<div class="token" />').append('<span class="token-label" />').append('<a href="#" class="close" tabindex="-1">&times;</a>').data("attrs",b);this.$input.hasClass("tt-input")?this.$input.parent().before(f):this.$input.before(f),this.$input.css("width",this.options.minWidth+"px");var g=f.find(".token-label"),h=f.find(".close");return this.maxTokenWidth||(this.maxTokenWidth=this.$wrapper.width()-h.outerWidth()-parseInt(h.css("margin-left"),10)-parseInt(h.css("margin-right"),10)-parseInt(f.css("border-left-width"),10)-parseInt(f.css("border-right-width"),10)-parseInt(f.css("padding-left"),10)-parseInt(f.css("padding-right"),10),parseInt(g.css("border-left-width"),10)-parseInt(g.css("border-right-width"),10)-parseInt(g.css("padding-left"),10)-parseInt(g.css("padding-right"),10),parseInt(g.css("margin-left"),10)-parseInt(g.css("margin-right"),10)),g.text(b.label).css("max-width",this.maxTokenWidth),f.on("mousedown",function(){return d._disabled||d._readonly?!1:(d.preventDeactivation=!0,void 0)}).on("click",function(a){return d._disabled||d._readonly?!1:(d.preventDeactivation=!1,a.ctrlKey||a.metaKey?(a.preventDefault(),d.toggle(f)):(d.activate(f,a.shiftKey,a.shiftKey),void 0))}).on("dblclick",function(){return d._disabled||d._readonly||!d.options.allowEditing?!1:(d.edit(f),void 0)}),h.on("click",a.proxy(this.remove,this)),this.$element.trigger(a.Event("tokenfield:createdtoken",{attrs:b,relatedTarget:f.get(0)})),c&&this.$element.val(this.getTokensList()).trigger(a.Event("change",{initiator:"tokenfield"})),this.update(),this.$element.get(0)}}},setTokens:function(b,c,d){if(b){c||this.$wrapper.find(".token").remove(),"undefined"==typeof d&&(d=!0),"string"==typeof b&&(b=this._delimiters.length?b.split(new RegExp("["+this._delimiters.join("")+"]")):[b]);var e=this;return a.each(b,function(a,b){e.createToken(b,d)}),this.$element.get(0)}},getTokenData:function(b){var c=b.map(function(){var b=a(this);return b.data("attrs")}).get();return 1==c.length&&(c=c[0]),c},getTokens:function(b){var c=this,d=[],e=b?".active":"";return this.$wrapper.find(".token"+e).each(function(){d.push(c.getTokenData(a(this)))}),d},getTokensList:function(b,c,d){b=b||this._firstDelimiter,c="undefined"!=typeof c&&null!==c?c:this.options.beautify;var e=b+(c&&" "!==b?" ":"");return a.map(this.getTokens(d),function(a){return a.value}).join(e)},getInput:function(){return this.$input.val()},listen:function(){var c=this;this.$element.on("change",a.proxy(this.change,this)),this.$wrapper.on("mousedown",a.proxy(this.focusInput,this)),this.$input.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("paste",a.proxy(this.paste,this)).on("keydown",a.proxy(this.keydown,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.$copyHelper.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keydown",a.proxy(this.keydown,this)).on("keyup",a.proxy(this.keyup,this)),this.$input.on("keypress",a.proxy(this.update,this)).on("keyup",a.proxy(this.update,this)),this.$input.on("autocompletecreate",function(){var b=a(this).data("ui-autocomplete").menu.element,d=c.$wrapper.outerWidth()-parseInt(b.css("border-left-width"),10)-parseInt(b.css("border-right-width"),10);b.css("min-width",d+"px")}).on("autocompleteselect",function(a,b){return c.createToken(b.item)&&(c.$input.val(""),c.$input.data("edit")&&c.unedit(!0)),!1}).on("typeahead:selected typeahead:autocompleted",function(a,b){c.createToken(b)&&(c.$input.typeahead("val",""),c.$input.data("edit")&&c.unedit(!0))}),a(b).on("resize",a.proxy(this.update,this))},keydown:function(b){function c(a){if(e.$input.is(document.activeElement)){if(e.$input.val().length>0)return;a+="All";var c=e.$input.hasClass("tt-input")?e.$input.parent()[a](".token:first"):e.$input[a](".token:first");if(!c.length)return;e.preventInputFocus=!0,e.preventDeactivation=!0,e.activate(c),b.preventDefault()}else e[a](b.shiftKey),b.preventDefault()}function d(c){if(b.shiftKey){if(e.$input.is(document.activeElement)){if(e.$input.val().length>0)return;var d=e.$input.hasClass("tt-input")?e.$input.parent()[c+"All"](".token:first"):e.$input[c+"All"](".token:first");if(!d.length)return;e.activate(d)}var f="prev"===c?"next":"prev",g="prev"===c?"first":"last";e.$firstActiveToken[f+"All"](".token").each(function(){e.deactivate(a(this))}),e.activate(e.$wrapper.find(".token:"+g),!0,!0),b.preventDefault()}}if(this.focused){var e=this;switch(b.keyCode){case 8:if(!this.$input.is(document.activeElement))break;this.lastInputValue=this.$input.val();break;case 37:c("rtl"===this.textDirection?"next":"prev");break;case 38:d("prev");break;case 39:c("rtl"===this.textDirection?"prev":"next");break;case 40:d("next");break;case 65:if(this.$input.val().length>0||!b.ctrlKey&&!b.metaKey)break;this.activateAll(),b.preventDefault();break;case 9:case 13:if(this.$input.data("ui-autocomplete")&&this.$input.data("ui-autocomplete").menu.element.find("li:has(a.ui-state-focus), li.ui-state-focus").length)break;if(this.$input.hasClass("tt-input")&&this.$wrapper.find(".tt-cursor").length)break;if(this.$input.hasClass("tt-input")&&this.$wrapper.find(".tt-hint").val()&&this.$wrapper.find(".tt-hint").val().length)break;if(this.$input.is(document.activeElement)&&this.$input.val().length||this.$input.data("edit"))return this.createTokensFromInput(b,this.$input.data("edit"));if(13===b.keyCode){if(!this.$copyHelper.is(document.activeElement)||1!==this.$wrapper.find(".token.active").length)break;if(!e.options.allowEditing)break;this.edit(this.$wrapper.find(".token.active"))}}this.lastKeyDown=b.keyCode}},keypress:function(b){return-1!==a.inArray(b.which,this._triggerKeys)&&this.$input.is(document.activeElement)?(this.$input.val()&&this.createTokensFromInput(b),!1):void 0},keyup:function(a){if(this.preventInputFocus=!1,this.focused){switch(a.keyCode){case 8:if(this.$input.is(document.activeElement)){if(this.$input.val().length||this.lastInputValue.length&&8===this.lastKeyDown)break;this.preventDeactivation=!0;var b=this.$input.hasClass("tt-input")?this.$input.parent().prevAll(".token:first"):this.$input.prevAll(".token:first");if(!b.length)break;this.activate(b)}else this.remove(a);break;case 46:this.remove(a,"next")}this.lastKeyUp=a.keyCode}},focus:function(){this.focused=!0,this.$wrapper.addClass("focus"),this.$input.is(document.activeElement)&&(this.$wrapper.find(".active").removeClass("active"),this.$firstActiveToken=null,this.options.showAutocompleteOnFocus&&this.search())},blur:function(a){this.focused=!1,this.$wrapper.removeClass("focus"),this.preventDeactivation||this.$element.is(document.activeElement)||(this.$wrapper.find(".active").removeClass("active"),this.$firstActiveToken=null),!this.preventCreateTokens&&(this.$input.data("edit")&&!this.$input.is(document.activeElement)||this.options.createTokensOnBlur)&&this.createTokensFromInput(a),this.preventDeactivation=!1,this.preventCreateTokens=!1},paste:function(a){var b=this;b.options.allowPasting&&setTimeout(function(){b.createTokensFromInput(a)},1)},change:function(a){"tokenfield"!==a.initiator&&this.setTokens(this.$element.val())},createTokensFromInput:function(a,b){if(!(this.$input.val().length<this.options.minLength)){var c=this.getTokensList();return this.setTokens(this.$input.val(),!0),c==this.getTokensList()&&this.$input.val().length?!1:(this.$input.hasClass("tt-input")?this.$input.typeahead("val",""):this.$input.val(""),this.$input.data("edit")&&this.unedit(b),!1)}},next:function(a){if(a){var b=this.$wrapper.find(".active:first"),c=b&&this.$firstActiveToken?b.index()<this.$firstActiveToken.index():!1;if(c)return this.deactivate(b)}var d=this.$wrapper.find(".active:last"),e=d.nextAll(".token:first");return e.length?(this.activate(e,a),void 0):(this.$input.focus(),void 0)},prev:function(a){if(a){var b=this.$wrapper.find(".active:last"),c=b&&this.$firstActiveToken?b.index()>this.$firstActiveToken.index():!1;if(c)return this.deactivate(b)}var d=this.$wrapper.find(".active:first"),e=d.prevAll(".token:first");return e.length||(e=this.$wrapper.find(".token:first")),e.length||a?(this.activate(e,a),void 0):(this.$input.focus(),void 0)},activate:function(b,c,d,e){if(b){if("undefined"==typeof e)var e=!0;if(d)var c=!0;if(this.$copyHelper.focus(),c||(this.$wrapper.find(".active").removeClass("active"),e?this.$firstActiveToken=b:delete this.$firstActiveToken),d&&this.$firstActiveToken){var f=this.$firstActiveToken.index()-2,g=b.index()-2,h=this;this.$wrapper.find(".token").slice(Math.min(f,g)+1,Math.max(f,g)).each(function(){h.activate(a(this),!0)})}b.addClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select()}},activateAll:function(){var b=this;this.$wrapper.find(".token").each(function(c){b.activate(a(this),0!==c,!1,!1)})},deactivate:function(a){a&&(a.removeClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select())},toggle:function(a){a&&(a.toggleClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select())},edit:function(b){if(b){var c=b.data("attrs"),d={attrs:c,relatedTarget:b.get(0)},e=a.Event("tokenfield:edittoken",d);if(this.$element.trigger(e),!e.isDefaultPrevented()){b.find(".token-label").text(c.value);var f=b.outerWidth(),g=this.$input.hasClass("tt-input")?this.$input.parent():this.$input;b.replaceWith(g),this.preventCreateTokens=!0,this.$input.val(c.value).select().data("edit",!0).width(f),this.update(),this.$element.trigger(a.Event("tokenfield:editedtoken",d))}}},unedit:function(a){var b=this.$input.hasClass("tt-input")?this.$input.parent():this.$input;if(b.appendTo(this.$wrapper),this.$input.data("edit",!1),this.$mirror.text(""),this.update(),a){var c=this;setTimeout(function(){c.$input.focus()},1)}},remove:function(b,c){if(!(this.$input.is(document.activeElement)||this._disabled||this._readonly)){var d="click"===b.type?a(b.target).closest(".token"):this.$wrapper.find(".token.active");if("click"!==b.type){if(!c)var c="prev";if(this[c](),"prev"===c)var e=0===d.first().prevAll(".token:first").length}var f={attrs:this.getTokenData(d),relatedTarget:d.get(0)},g=a.Event("tokenfield:removetoken",f);if(this.$element.trigger(g),!g.isDefaultPrevented()){var h=a.Event("tokenfield:removedtoken",f),i=a.Event("change",{initiator:"tokenfield"});d.remove(),this.$element.val(this.getTokensList()).trigger(h).trigger(i),(!this.$wrapper.find(".token").length||"click"===b.type||e)&&this.$input.focus(),this.$input.css("width",this.options.minWidth+"px"),this.update(),b.preventDefault(),b.stopPropagation()}}},update:function(){var a=this.$input.val(),b=parseInt(this.$input.css("padding-left"),10),c=parseInt(this.$input.css("padding-right"),10),d=b+c;if(this.$input.data("edit")){if(a||(a=this.$input.prop("placeholder")),a===this.$mirror.text())return;this.$mirror.text(a);var e=this.$mirror.width()+10;if(e>this.$wrapper.width())return this.$input.width(this.$wrapper.width());this.$input.width(e)}else{var f="rtl"===this.textDirection?this.$input.offset().left+this.$input.outerWidth()-this.$wrapper.offset().left-parseInt(this.$wrapper.css("padding-left"),10)-d-1:this.$wrapper.offset().left+this.$wrapper.width()+parseInt(this.$wrapper.css("padding-left"),10)-this.$input.offset().left-d;isNaN(f)?this.$input.width("100%"):this.$input.width(f)}},focusInput:function(b){if(!(a(b.target).closest(".token").length||a(b.target).closest(".token-input").length||a(b.target).closest(".tt-dropdown-menu").length)){var c=this;setTimeout(function(){c.$input.focus()},0)}},search:function(){this.$input.data("ui-autocomplete")&&this.$input.autocomplete("search")},disable:function(){this.setProperty("disabled",!0)},enable:function(){this.setProperty("disabled",!1)},readonly:function(){this.setProperty("readonly",!0)},writeable:function(){this.setProperty("readonly",!1)},setProperty:function(a,b){this["_"+a]=b,this.$input.prop(a,b),this.$element.prop(a,b),this.$wrapper[b?"addClass":"removeClass"](a)},destroy:function(){this.$element.val(this.getTokensList()),this.$element.css(this.$element.data("original-styles")),this.$element.prop("tabindex",this.$element.data("original-tabindex"));var b=a('label[for="'+this.$input.prop("id")+'"]');b.length&&b.prop("for",this.$element.prop("id")),this.$element.insertBefore(this.$wrapper),this.$element.removeData("original-styles").removeData("original-tabindex").removeData("bs.tokenfield"),this.$wrapper.remove(),this.$mirror.remove();var c=this.$element;return c}};var d=a.fn.tokenfield;return a.fn.tokenfield=function(b,d){var e,f=[];Array.prototype.push.apply(f,arguments);var g=this.each(function(){var g=a(this),h=g.data("bs.tokenfield"),i="object"==typeof b&&b;"string"==typeof b&&h&&h[b]?(f.shift(),e=h[b].apply(h,f)):h||"string"==typeof b||d||(g.data("bs.tokenfield",h=new c(this,i)),g.trigger("tokenfield:initialize"))});return"undefined"!=typeof e?e:g},a.fn.tokenfield.defaults={minWidth:60,minLength:0,allowEditing:!0,allowPasting:!0,limit:0,autocomplete:{},typeahead:{},showAutocompleteOnFocus:!1,createTokensOnBlur:!1,delimiter:",",beautify:!0,inputType:"text"},a.fn.tokenfield.Constructor=c,a.fn.tokenfield.noConflict=function(){return a.fn.tokenfield=d,this},c});/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
 !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){if("string"!=typeof a)throw new Error("See almond README: incorrect module build, no module name");b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice,c=b.call(arguments,1);this.listeners=this.listeners||{},null==c&&(c=[]),0===c.length&&c.push({}),c[0]._type=a,a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" aria-live="assertive" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.highlightFirstItem=function(){var a=this.$results.find(".select2-results__option[aria-selected]"),b=a.filter("[aria-selected=true]");b.length>0?b.first().trigger("mouseenter"):a.first().trigger("mouseenter"),this.ensureHighlightVisible()},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")})})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("unselect",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox"  aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("focus",function(b){a.isOpen()||c.$selection.focus()}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("<span></span>")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">&times;</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.selectionContainer(),g=this.display(e,f);f.append(g),f.prop("title",e.title||e.text),f.data("data",e),b.push(f)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(a){function b(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return b.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},b.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},b.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle",{})}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">&times;</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" aria-autocomplete="list" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a,b){var c=this;this.container=a,a.on("select",function(a){c.select(a.data)}),a.on("unselect",function(a){c.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h<e.length;h++){var i=e[h],j=this._normalizeItem(i),k=this.option(j);this.$element.append(k)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(b){var c=(this._lastTag,this.$element.find("option[data-select2-tag]"));c.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(b,c,d){function e(b){var c=g._normalizeItem(b),d=g.$element.find("option").filter(function(){return a(this).val()===c.id});if(!d.length){var e=g.option(c);e.attr("data-select2-tag",!0),g._removeOldTags(),g.addOptions([e])}f(c)}function f(a){g.trigger("select",{data:a})}var g=this;c.term=c.term||"";var h=this.tokenizer(c,this.options,e);h.term!==c.term&&(this.$search.length&&(this.$search.val(h.term),this.$search.focus()),c.term=h.term),b.call(this,c,d)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);null!=m?(e(m),g=g.substr(h+1)||"",h=0):h++}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="select2-results__option select2-results__option--load-more"role="treeitem" aria-disabled="true"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(a){d._handleSelectOnClose(a)})},a.prototype._handleSelectOnClose=function(a,b){if(b&&null!=b.originalSelect2Event){var c=b.originalSelect2Event;if("select"===c._type||"unselect"===c._type)return}var d=this.getHighlightedResults();if(!(d.length<1)){var e=d.data("data");null!=e.element&&e.element.selected||null==e.element&&e.selected||this.trigger("select",{data:e})}},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close",{originalEvent:c,originalSelect2Event:b})},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend(!0,{},this.defaults,l),null==l.dataAdapter){if(null!=l.ajax?l.dataAdapter=o:null!=l.data?l.dataAdapter=n:l.dataAdapter=m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e<b.addedNodes.length;e++){var f=b.addedNodes[e];f.selected&&(c=!0)}else b.removedNodes&&b.removedNodes.length>0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null;},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d,f=Array.prototype.slice.call(arguments,1);return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2."),d=c[b].apply(c,f)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});

+ 15 - 15
static/styles/following.js

@@ -60,37 +60,37 @@ $(document).ready(function(){
     Follow(_$this,data,url);
   });
 });
-function DoCollect(collectData) {
+function DoCollect(collect_url) {
   $(document).ready(function(){
-    $('button#edit-collect-form').click(function() {
+    $('button#edit-collect').click(function() {
       var data = JSON.stringify({
         name:$('#name').val(),
         description:$('#description').val(),
-        is_privacy:$("input[name='is_privacy']:checked").val()
+        is_hidden:$("input[name='is_hidden']:checked").val()
       });
       $.ajax ({
         type : "PUT",
-        url : collectData.collect_action_url,
+        url : collect_url,
         data:data,
         contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
+        success: function(response) {
+          if (response.status === '200')
           {
-            window.location =collectData.collect_action_url ;
+            window.location.href = collect_url;
           }
         }
       });
     });
-    $('button#delete-collect-form').click(function() {
+    $('button#delete-collect').click(function() {
       $.ajax ({
         type : "DELETE",
-        url : collectData.collect_action_url,
+        url : collect_url,
         data:JSON.stringify(),
         contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
+        success: function(response) {
+          if (response.status === '200')
           {
-            window.location = collectData.collect_url;
+            window.location.href = collect_url;
           }
         }
       });
@@ -103,11 +103,11 @@ function DoCollect(collectData) {
       });
       $.ajax ({
         type : "DELETE",
-        url : collectData.delete_detail_action_url,
+        url : '/topic/' + topicId + '/collect',
         data:data,
         contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
+        success: function(response) {
+          if (response.status === '200')
           {
             _$this.parent().remove();
           }

+ 18 - 13
static/styles/forums.js

@@ -1,27 +1,32 @@
+function getQueryParams(k){
+  var p={};
+  location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v;});
+  return k?p[k]:p;
+}
 function SortFuntion(){
-  var display = $('#display').val();
-  var sort =  $('#sort').val();
-  var st =  $('#st').val();
-  var params = {};
-  if (display != '0'){
-    params.within = display;
+  var within = $('select#within').val();
+  var orderby =  $('select#orderby').val();
+  var desc =  $('select#desc').val();
+  var params = getQueryParams();
+  if (within != '0'){
+    params.within = within;
   }
-  if (sort != '0'){
-    params.orderby = sort;
+  if (orderby != '0'){
+    params.orderby = orderby;
   }
-  if (st != '0'){
-    params.desc = st;
+  if (desc != '0'){
+    params.desc = desc;
   }
   window.location.href = window.location.pathname + '?' + $.param(params);
 }
 $(document).ready(function(){
-  $('#display').change(function() {
+  $('select#within').change(function() {
     SortFuntion();
   });
-  $('#sort').change(function() {
+  $('select#orderby').change(function() {
     SortFuntion();
   });
-  $('#st').change(function() {
+  $('select#desc').change(function() {
     SortFuntion();
   });
   $('span#email-confirm').click(function(){

+ 0 - 118
static/styles/mine.js

@@ -1,118 +0,0 @@
-function Follow(obj,data,url){
-  if(obj.hasClass('active'))
-  {$.ajax ({
-    type : "DELETE",
-    url : url,
-    data:data,
-    contentType: 'application/json;charset=UTF-8',
-    success: function(result) {
-      if (result.judge === true)
-      {
-        obj.text('关注').removeClass('active');
-      } else
-      {alert('fail');}}});
-  }else
-  {$.ajax ({
-    type : "POST",
-    url : url,
-    data:data,
-    contentType: 'application/json;charset=UTF-8',
-    success: function(result) {
-      if (result.judge === true)
-      {
-        obj.text('取消关注').addClass('active');
-      } else
-      {alert('fail');}
-    }});
-  }
-}
-$(document).ready(function(){
-  $('button.tagfollow').click(function(){
-    var _$this = $(this);
-    var url = "/user/follow/tag";
-    var data = JSON.stringify({
-      id:_$this.attr("id"),
-    });
-    Follow(_$this,data,url);
-  });
-  $('button.topicfollow').click(function(){
-    var _$this = $(this);
-    var url = "/user/follow/topic";
-    var data = JSON.stringify({
-      id:_$this.attr("id"),
-    });
-    Follow(_$this,data,url);
-  });
-  $('button.collectfollow').click(function(){
-    var _$this = $(this);
-    var url = "/user/follow/collect";
-    var data = JSON.stringify({
-      id:_$this.attr("id"),
-    });
-    Follow(_$this,data,url);
-  });
-  $('button.userfollow').click(function(){
-    var _$this = $(this);
-    var url = "/user/follow/user";
-    var data = JSON.stringify({
-      id:_$this.attr("id"),
-    });
-    Follow(_$this,data,url);
-  });
-});
-function DoCollect(collectData) {
-  $(document).ready(function(){
-    $('button#edit-collect-form').click(function() {
-      var data = JSON.stringify({
-        name:$('#name').val(),
-        description:$('#description').val(),
-        is_privacy:$("input[name='is_privacy']:checked").val()
-      });
-      $.ajax ({
-        type : "PUT",
-        url : collectData.collect_action_url,
-        data:data,
-        contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
-          {
-            window.location =collectData.collect_action_url ;
-          }
-        }
-      });
-    });
-    $('button#delete-collect-form').click(function() {
-      $.ajax ({
-        type : "DELETE",
-        url : collectData.collect_action_url,
-        data:JSON.stringify(),
-        contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
-          {
-            window.location = collectData.collect_url;
-          }
-        }
-      });
-    });
-    $('#delete-from-collect').click(function() {
-      var _$this = $(this);
-      var topicId = _$this.attr('data-id');
-      var data = JSON.stringify({
-        topicId:topicId
-      });
-      $.ajax ({
-        type : "DELETE",
-        url : collectData.delete_detail_action_url,
-        data:data,
-        contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true)
-          {
-            _$this.parent().remove();
-          }
-        }
-      });
-    });
-  });
-}

+ 22 - 36
static/styles/topic.js

@@ -87,51 +87,37 @@ function DoVote(voteData) {
     });
   });
 }
-function PreviewTopic(pre_url) {
+$(document).ready(function(){
   $('#topic-preview').click(function() {
     var content = $('#content').val();
-    $.post(pre_url, {
+    $.post('/topic/preview', {
       content: $("#content").val(),
-      choice: $("#choice").val()
+      content_type: $("#content_type").val()
     }, function(data) {
       $("#show-preview").html(data);
     });
   });
-}
-function AskTopic(pre_url) {
-  $(document).ready(function(){
-    PreviewTopic(pre_url);
-    $('#tokenfield').tokenfield({
-      limit:4
-    });
+  $('#tokenfield').tokenfield({
+    limit:4
   });
-}
-function EditTopic(pre_url,edit_url) {
-  $(document).ready(function(){
-    PreviewTopic(pre_url);
-    $('#tokenfield').tokenfield({
-      limit:4
+  $('#topic-put-btn').click(function() {
+    var _$this = $(this);
+    var form_data = $("form#topic-put").serializeArray();
+    var url = '/topic/' + _$this.attr("data-id");
+    var data = {};
+    $.each(form_data,function() {
+      data[this.name] = this.value;
     });
-    $('#topic-put-btn').click(function() {
-      var form_data = $("form#topic-put").serializeArray();
-      var data = {};
-      $.each(form_data,function() {
-        data[this.name] = this.value;
-      });
-      data = JSON.stringify(data);
-      $.ajax ({
-        type : "PUT",
-        url : edit_url,
-        data:data,
-        contentType: 'application/json;charset=UTF-8',
-        success: function(result) {
-          if (result.judge === true) {
-            window.location.href= edit_url;
-          }else {
-            alert(result.error);
-          }
+    $.ajax ({
+      type : "PUT",
+      url : url,
+      data:JSON.stringify(data),
+      contentType: 'application/json;charset=UTF-8',
+      success: function(response) {
+        if (response.status === '200') {
+          window.location.href= url;
         }
-      });
+      }
     });
   });
-}
+});

+ 0 - 2
templates/base/base.html

@@ -6,14 +6,12 @@
 {% from 'base/head.html' import breadcrumb %}
 {% block script -%}
 {{ super() }}
-<script type="text/javascript" src="{{ url_for('static',filename='assets/home.js')}}"></script>
 <!-- <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> -->
 {%- endblock script %}
 
 {% block style -%}
 {{ super() }}
 <link href="http://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
-<link rel="stylesheet" href="{{ url_for('static',filename='assets/home.css')}}" type="text/css" media="screen" />
 {%- endblock style %}
 
 {% block main %}

+ 12 - 12
templates/base/form.html

@@ -1,18 +1,18 @@
 {% macro forms(form) %}
 <div class="form-horizontal">
-    {{ form.hidden_tag() }}
-    {% for field in form if field.widget.input_type != 'hidden' %}
-    <div class="form-group">
-        <label class="col-sm-2 control-label">{{ field.label }}</label>
-        <div class="col-sm-10">
-            {{ field(class="form-control")}}
-        </div>
+  {{ form.hidden_tag() }}
+  {% for field in form if field.widget.input_type != 'hidden' %}
+  <div class="form-group">
+    <label class="col-sm-2 control-label">{{ field.label }}</label>
+    <div class="col-sm-10">
+      {{ field(class="form-control")}}
     </div>
-    {% endfor %}
-    <div class="form-group">
-        <div class="col-sm-offset-2 col-sm-10">
-            <button class="btn btn-sm btn-primary" type="submit">{{ _('save') }}</button>
-        </div>
+  </div>
+  {% endfor %}
+  <div class="form-group">
+    <div class="col-sm-offset-2 col-sm-10">
+      <button class="btn btn-sm btn-primary" type="submit">{{ _('save') }}</button>
     </div>
+  </div>
 </div>
 {%- endmacro %}

+ 2 - 2
templates/base/link.html

@@ -59,10 +59,10 @@
 
 
 {% macro collectlist() -%}
-<a href="{{ url_for('topic.collectlist')}}">{{ _('CollectList')}}</a>
+<a href="{{ url_for('collect.list')}}">{{ _('CollectList')}}</a>
 {%- endmacro %}
 
 {% macro collect(c) -%}
-<a href="{{ url_for('topic.collect',collectId=c.id)}}">{{ c.name }}</a>
+<a href="{{ url_for('collect.collect',pk=c.id)}}">{{ c.name }}</a>
 {%- endmacro %}
 

+ 1 - 1
templates/base/panel.html

@@ -6,7 +6,7 @@
     </a>
 </ul>
 <div class="list-group">
-    <a href="{{ url_for('topic.collectlist')}}" class="list-group-item">
+    <a href="{{ url_for('collect.list')}}" class="list-group-item">
         {{ _('Collects')}}
     </a>
     <a href="{{ url_for('follow.topic')}}" class="list-group-item">

+ 4 - 2
templates/board/board.html

@@ -17,8 +17,10 @@
       {% else %}
       {{ board_body(board) }}
       {%- endfor %}
-    </div>
-    {% include "board/topic.html" %}
+      </div>
+      <div class="panel panel-default">
+        {% include "topic/_topic.html" %}
+      </div>
   </div>
   <div class="col-md-3" style="padding-left:0">
     {{ panel_base.board() }}

+ 0 - 11
templates/board/topic.html

@@ -1,11 +0,0 @@
-{% from 'topic/_list_macro.html' import form as topic_form %}
-{% from 'topic/_list_macro.html' import body as topic_body %}
-{% from 'topic/_list_macro.html' import no_topics %}
-<div class="panel panel-default">
-  {{ topic_form() }}
-  {% for topic in topics.items %}
-  {{ topic_body(topic) }}
-  {% else %}
-  {{ no_topics() }}
-  {% endfor %}
-</div>

+ 0 - 90
templates/collect/_macro.html

@@ -1,90 +0,0 @@
-{% macro collect_create(form) -%}
-<div class="modal fade" id="addcollect" tabindex="-1" role="dialog" aria-labelledby="addcollectLabel">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                <h4 class="modal-title" id="addcollectLabel">{{_('Create Collect')}}</h4>
-            </div>
-            <form action="{{ url_for('topic.collectlist')}}" method="POST">
-                {{ form.hidden_tag() }}
-                <div class="modal-body">
-                    <div class="form-group">
-                        {{ form.name.label }}
-                        {{ form.name(class="form-control")}}
-                    </div>
-                    <div class="form-group">
-                        {{ form.description.label }}
-                        {{ form.description(class="form-control")}}
-                    </div>
-                    {% for subfield in form.private %}
-                    <div class="form-group" style="display:inline;">
-                        {{subfield}}
-                        {{subfield.label}}
-                    </div>
-                    {% endfor %}
-                </div>
-                <div class="modal-footer">
-                    <button type="button" class="btn btn-default" data-dismiss="modal">{{_('cancel')}}</button>
-                    <button type="submit" class="btn btn-primary">{{_('confirm')}}</button>
-                </div>
-            </form>
-        </div>
-    </div>
-</div>
-{%- endmacro %}
-{% macro collect_edit(form) -%}
-<div class="modal fade" id="editcollect" tabindex="-1" role="dialog" aria-labelledby="editcollectLabel">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                <h4 class="modal-title" id="editcollectLabel">{{_('Edit the collect')}}</h4>
-            </div>
-            <form id="editcollectform">
-                <div class="modal-body">
-                    <div class="form-group">
-                        {{ form.name.label }}
-                        {{ form.name(class="form-control")}}
-                    </div>
-                    <div class="form-group">
-                        {{ form.description.label }}
-                        {{ form.description(class="form-control")}}
-                    </div>
-                    {% for subfield in form.is_privacy %}
-                    <div class="form-group">
-                        {{subfield}}
-                        {{subfield.label}}
-                    </div>
-                    {% endfor %}
-                </div>
-                <div class="modal-footer">
-                    <button type="button" class="btn btn-default" data-dismiss="modal">{{ _('cancel')}}</button>
-                    <button type="button" id="edit-collect-form" class="btn btn-primary">{{_('confirm')}}</button>
-                </div>
-            </form>
-        </div>
-    </div>
-</div>
-{%- endmacro %}
-
-{% macro collect_delete() -%}
-<div class="modal fade" id="deletecollect" tabindex="-1" role="dialog" aria-labelledby="deletecollectLabel">
-            <div class="modal-dialog" role="document">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                        <h4 class="modal-title" id="deletecollectLabel">{{_('Delete this collect')}}</h4>
-            </div>
-                    <div class="modal-body">
-                        <p>确认要删除收藏夹「flask相关」么?</p>
-                        注意:收藏夹下的收藏也会被删除!
-            </div>
-                    <div class="modal-footer" style="padding-top:5px;padding-bottom:5px;">
-                        <button type="button" class="btn btn-default" data-dismiss="modal">{{_('cancel')}}</button>
-                        <button type="button" id="delete-collect-form" class="btn btn-danger">{{_('confirm')}}</button>
-            </div>
-        </div>
-    </div>
-</div>
-{%- endmacro %}

+ 31 - 31
templates/collect/collect.html

@@ -1,37 +1,37 @@
 {% extends 'base/base.html' %}
 {% block content %}
-{% from 'collect/_macro.html' import collect_edit,collect_delete %}
-{{ breadcrumb(hrefs={_('My Collects'):url_for('topic.collectlist')},active=collect.name)}}
+{{ breadcrumb(hrefs={_('My Collects'):url_for('collect.list')},active=collect.name)}}
+<script type="text/javascript">
+ DoCollect('{{ url_for("collect.collect",pk=collect.id) }}');
+</script>
 <div class="row">
-    <div class="col-md-9">
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                <span class="pull-right">
-                    <a class="btn btn-default btn-sm" href="javascript:void(0)" data-toggle="modal" data-target="#editcollect">{{ _('edit') }}</a>
-                    <a class="btn btn-default btn-sm" href="javascript:void(0)" data-toggle="modal" data-target="#deletecollect">{{ _('delete')}}</a>
-                </span>
-                <h4> <strong>{{ collect.name }}</strong></h4>
-            </div>
-            {% if topics.items %}
-            {% for topic in topics.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                <a href="{{ url_for('topic.topic',topicId=topic.uid)}}">{{ topic.title}}</a>
-                <span id="delete-from-collect" class="pull-right btn btn-sm btn-danger" data-id="{{ topic.uid }}">{{_('delete')}}</span>
-            </div>
-            {% endfor %}
-            {% else %}
-            <div class="panel-body">
-                <span class="text-center" style="display:block;width:100%;color:#999">
-                    {{_('No Collect')}}
-                </span>
-            </div>
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.collect() }}
+  <div class="col-md-9">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <span class="pull-right">
+          <a class="btn btn-default btn-sm" href="javascript:void(0)" data-toggle="modal" data-target="#editcollect">{{ _('edit') }}</a>
+          <a class="btn btn-default btn-sm" href="javascript:void(0)" data-toggle="modal" data-target="#deletecollect">{{ _('delete')}}</a>
+        </span>
+        <h4> <strong>{{ collect.name }}</strong></h4>
+      </div>
+      {% for topic in topics.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        <a href="{{ url_for('topic.topic',topicId=topic.id)}}">{{ topic.title}}</a>
+        <span id="delete-from-collect" class="pull-right btn btn-sm btn-danger" data-id="{{ topic.id }}">{{_('delete')}}</span>
+      </div>
+      {% else %}
+      <div class="panel-body">
+        <span class="text-center" style="display:block;width:100%;color:#999">
+          {{_('No Collect')}}
+        </span>
+      </div>
+      {% endfor %}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.collect() }}
+  </div>
 </div>
-{{ collect_edit(form) }}
-{{ collect_delete() }}
+{% include "collect/edit.html" %}
+{% include "collect/delete.html" %}
 {% endblock %}

+ 25 - 26
templates/collect/collect_list.html

@@ -1,33 +1,32 @@
 {% extends 'base/base.html' %}
 {% block content %}
-{% from 'collect/_macro.html' import collect_create %}
 {{ breadcrumb(active=_('My Collects'))}}
 <div class="row">
-    <div class="col-md-9">
-        <ul class="nav nav-tabs">
-            <li role="presentation" class="active">{{ link.collectlist() }}</li>
-            <li role="presentation">{{ link.following_collect() }}</li>
-        </ul>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                <button type="button" class="btn btn-sm btn-default" data-toggle="modal" data-target="#addcollect">{{_('Create Collect')}}</button>
-            </div>
-            {% for collect in collects.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                <a href="{{ url_for('topic.collect',collectId=collect.id)}}">{{ collect.name}}</a>
-                {% if collect.privacy %}
-                <span class="pull-right">{{_('Privacy')}}</span>
-                {% else %}
-                <span class="pull-right">{{_('Public')}}</span>
-                {% endif %}
-            </div>
-            {% endfor %}
-            {{ p_footer(collects,'topic.collectlist')}}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.collect() }}
+  <div class="col-md-9">
+    <ul class="nav nav-tabs">
+      <li role="presentation" class="active">{{ link.collectlist() }}</li>
+      <li role="presentation">{{ link.following_collect() }}</li>
+    </ul>
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <button type="button" class="btn btn-sm btn-default" data-toggle="modal" data-target="#addcollect">{{_('Create Collect')}}</button>
+      </div>
+      {% for collect in collects.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        <a href="{{ url_for('collect.collect',pk=collect.id)}}">{{ collect.name}}</a>
+        {% if collect.is_hidden %}
+        <span class="pull-right label label-default">{{_('Privacy')}}</span>
+        {% else %}
+        <span class="pull-right label label-default">{{_('Public')}}</span>
+        {% endif %}
+      </div>
+      {% endfor %}
+      {{ p_footer(collects,'collect.list')}}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.collect() }}
+  </div>
 </div>
-{{ collect_create(form) }}
+{% include "collect/create.html" %}
 {% endblock %}

+ 33 - 0
templates/collect/create.html

@@ -0,0 +1,33 @@
+<div class="modal fade" id="addcollect" tabindex="-1" role="dialog">
+  <div class="modal-dialog modal-sm" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title" id="addcollectLabel">{{_('Create Collect')}}</h4>
+      </div>
+      <div class="modal-body">
+        <form action="{{ url_for('collect.list')}}" method="POST">
+          {{ form.hidden_tag() }}
+          <div class="form-group">
+            {{ form.name.label }}
+            {{ form.name(class="form-control")}}
+          </div>
+          <div class="form-group">
+            {{ form.description.label }}
+            {{ form.description(class="form-control")}}
+          </div>
+          {% for subfield in form.is_hidden %}
+          <div class="form-group" style="display:inline;">
+            {{subfield.label}}
+            {{subfield}}
+          </div>
+          {% endfor %}
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">{{_('cancel')}}</button>
+        <button type="submit" class="btn btn-primary">{{_('confirm')}}</button>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>

+ 18 - 0
templates/collect/delete.html

@@ -0,0 +1,18 @@
+<div class="modal fade" id="deletecollect" role="dialog" aria-labelledby="deletecollectLabel">
+  <div class="modal-dialog modal-sm" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title" id="deletecollectLabel">{{_('Delete this collect')}}</h4>
+      </div>
+      <div class="modal-body">
+        <p>确认要删除收藏夹{{ collect.name }}么?</p>
+        注意:收藏夹下的收藏也会被删除!
+      </div>
+      <div class="modal-footer" style="padding-top:5px;padding-bottom:5px;">
+        <button type="button" class="btn btn-default" data-dismiss="modal">{{_('cancel')}}</button>
+        <button type="button" id="delete-collect" class="btn btn-danger">{{_('confirm')}}</button>
+      </div>
+    </div>
+  </div>
+</div>

+ 32 - 0
templates/collect/edit.html

@@ -0,0 +1,32 @@
+<div class="modal fade" id="editcollect" role="dialog" aria-labelledby="editcollectLabel">
+  <div class="modal-dialog modal-sm" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title" id="editcollectLabel">{{_('Edit the collect')}}</h4>
+      </div>
+      <form id="editcollectform">
+        <div class="modal-body">
+          <div class="form-group">
+            {{ form.name.label }}
+            {{ form.name(class="form-control")}}
+          </div>
+          <div class="form-group">
+            {{ form.description.label }}
+            {{ form.description(class="form-control")}}
+          </div>
+          {% for subfield in form.is_hidden %}
+          <div class="form-group">
+            {{subfield}}
+            {{subfield.label}}
+          </div>
+          {% endfor %}
+        </div>
+        <div class="modal-footer">
+          <button type="button" class="btn btn-default" data-dismiss="modal">{{ _('cancel')}}</button>
+          <button type="button" id="edit-collect" class="btn btn-primary">{{_('confirm')}}</button>
+        </div>
+      </form>
+    </div>
+  </div>
+</div>

+ 16 - 18
templates/follow/following_collects.html

@@ -3,24 +3,22 @@
 {% from 'follow/_macro.html' import nav_title %}
 {{ breadcrumb(active=_('Following Collects'))}}
 <div class="row">
-    <div class="col-md-9">
-        {{ nav_title('collect') }}
-        <div class="panel panel-default">
-            {% for collect in collects.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                <span style="float:right">
-                    <button class="btn btn-sm btn-default collectfollow active" id="{{ collect.id}}" style="padding:0 5px">取消关注</button>
-                </span>
-                {{ link.collect(collect)}}
-            </div>
-            {%- else %}
-            {% include 'follow/none.html' %}
-            {% endfor  %}
-            {{ p_footer(collects,'follow.collect')}}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.follow() }}
+  <div class="col-md-9">
+    {{ nav_title('collect') }}
+    <div class="panel panel-default">
+      {% for collect in collects.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        <button class="btn btn-sm btn-default collect-following pull-right active" data-id="{{ collect.id}}" style="padding:0 5px">取消关注</button>
+        {{ link.collect(collect)}}
+      </div>
+      {%- else %}
+      {% include 'follow/none.html' %}
+      {% endfor  %}
+      {{ p_footer(collects,'follow.collect')}}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.follow() }}
+  </div>
 </div>
 {% endblock %}

+ 17 - 19
templates/follow/following_tags.html

@@ -3,25 +3,23 @@
 {% from 'follow/_macro.html' import nav_title %}
 {{ breadcrumb(active=_('Following Tags'))}}
 <div class="row">
-    <div class="col-md-9">
-        {{ nav_title('tag') }}
-        <div class="panel panel-default">
-            {% for tag in tags.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                <span style="float:right">
-                    <button class="btn btn-sm btn-default tagfollow active" id="{{ tag.id}}" style="padding:0 5px">取消关注</button>
-                </span>
-                {{ link.tag(tag)}}
-                <p>{{ tag.description }}</p>
-            </div>
-            {% else %}
-            {% include 'follow/none.html' %}
-            {% endfor  %}
-            {{ p_footer(tags,'follow.tag')}}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.follow() }}
+  <div class="col-md-9">
+    {{ nav_title('tag') }}
+    <div class="panel panel-default">
+      {% for tag in tags.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        <h4>{{ link.tag(tag)}}</h4>
+        <button class="btn btn-sm btn-default tag-following pull-right active" data-id="{{ tag.id}}" style="padding:0 5px">取消关注</button>
+        <small>{{ tag.description }}</small>
+      </div>
+      {% else %}
+      {% include 'follow/none.html' %}
+      {% endfor  %}
+      {{ p_footer(tags,'follow.tag')}}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.follow() }}
+  </div>
 </div>
 {% endblock %}

+ 19 - 28
templates/follow/following_topics.html

@@ -3,34 +3,25 @@
 {% from 'follow/_macro.html' import nav_title %}
 {{ breadcrumb(active=_('Following Topics'))}}
 <div class="row">
-    <div class="col-md-9">
-        {{ nav_title('topic') }}
-        <div class="panel panel-default">
-            {% for topic in topics.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                <span class="pull-right">
-                    <button class="btn btn-sm btn-default topicfollow active" id="{{ topic.id }}" style="padding:0 5px">取消关注</button>
-                </span>
-                <p>{{ link.topic(topic)}}</p>
-                <p class="topic-content">
-                    {% if topic.is_markdown %}
-                    {{ topic.content | truncate(100, True) | markdown }}
-                    {% else %}
-                    {{ topic.content | truncate(100, True) | safe_clean }}
-                    {% endif %}
-                </p>
-                {% for tag in topic.tags %}
-                {{ link.tag(tag)}}
-                {% endfor %}
-            </div>
-            {% else %}
-            {% include 'follow/none.html' %}
-            {% endfor  %}
-            {{ p_footer(topics,'follow.topic')}}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.follow() }}
+  <div class="col-md-9">
+    {{ nav_title('topic') }}
+    <div class="panel panel-default">
+      {% for topic in topics.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        <h4>{{ link.topic(topic)}}</h4>
+        <button class="btn btn-sm btn-default topic-following pull-right active" data-id="{{ topic.id }}" style="padding:0 5px">取消关注</button>
+        {% for tag in topic.tags %}
+        {{ link.tag(tag)}}
+        {% endfor %}
+      </div>
+      {% else %}
+      {% include 'follow/none.html' %}
+      {% endfor  %}
+      {{ p_footer(topics,'follow.topic')}}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.follow() }}
+  </div>
 </div>
 {% endblock %}

+ 16 - 18
templates/follow/following_users.html

@@ -3,24 +3,22 @@
 {% from 'follow/_macro.html' import nav_title %}
 {{ breadcrumb(active=_('Following Users'))}}
 <div class="row">
-    <div class="col-md-9">
-        {{ nav_title('user') }}
-        <div class="panel panel-default">
-            {% for user in users.items %}
-            <div class="panel-body" style="border-bottom:1px solid #eee">
-                {{ link.user(user)}}
-                <span style="float:right">
-                    <button class="btn btn-sm btn-default userfollow active" id="{{ user.id}}" style="padding:0 5px">取消关注</button>
-                </span>
-            </div>
-            {% else %}
-            {% include 'follow/none.html' %}
-            {% endfor  %}
-            {{ p_footer(users,'follow.user')}}
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ panel_base.follow() }}
+  <div class="col-md-9">
+    {{ nav_title('user') }}
+    <div class="panel panel-default">
+      {% for user in users.items %}
+      <div class="panel-body" style="border-bottom:1px solid #eee">
+        {{ link.user(user)}}
+        <button class="btn btn-sm btn-default user-following pull-right active" data-id="{{ user.id}}" style="padding:0 5px">取消关注</button>
+      </div>
+      {% else %}
+      {% include 'follow/none.html' %}
+      {% endfor  %}
+      {{ p_footer(users,'follow.user')}}
     </div>
+  </div>
+  <div class="col-md-3" style="padding-left:0">
+    {{ panel_base.follow() }}
+  </div>
 </div>
 {% endblock %}

+ 1 - 1
templates/follow/none.html

@@ -1,5 +1,5 @@
 <div class="panel-body">
     <span class="text-center" style="display:block;color:#999">
-        {{_('No Following')}}
+        {{_('No Followings')}}
     </span>
 </div>

+ 1 - 1
templates/tag/tag.html

@@ -8,7 +8,7 @@
       <div class="panel-body" style="border-bottom: 1px solid #ddd;">
         {{ title(tag) }}
       </div>
-      {% include "board/topic.html" %}
+      {% include "topic/_topic.html" %}
     </div>
   </div>
   <div class="col-md-3" style="padding-left:0">

+ 66 - 66
templates/topic/_list_macro.html

@@ -1,35 +1,35 @@
 {% import 'base/link.html' as link %}
 {% macro form() -%}
 <div class="panel-heading" style="font-size:12px;">
-    <div class="row">
-        <div class="col-sm-6">
-            {{ _('Choice:') }}
-            {{ g.sort_form.display() }}
-            {{ _('Order:') }}
-            {{ g.sort_form.sort() }}
-            {{ g.sort_form.st() }}
-        </div>
-        <div class="col-sm-2 hidden-xs">
-            {{ _('Author') }}
-        </div>
-        <div class="col-sm-2 hidden-xs">
-            {{_('Replies/Read')}}
-        </div>
-        <div class="col-sm-2 hidden-xs">
-            {{_('Last reply')}}
-        </div>
+  <div class="row">
+    <div class="col-sm-6">
+      {{ _('Choice:') }}
+      {{ g.sort_form.within() }}
+      {{ _('Order:') }}
+      {{ g.sort_form.orderby() }}
+      {{ g.sort_form.desc() }}
     </div>
+    <div class="col-sm-2 hidden-xs">
+      {{ _('Author') }}
+    </div>
+    <div class="col-sm-2 hidden-xs">
+      {{_('Replies/Read')}}
+    </div>
+    <div class="col-sm-2 hidden-xs">
+      {{_('Last reply')}}
+    </div>
+  </div>
 </div>
 {%- endmacro %}
 
 {% macro body(topic) -%}
 <div class="panel-body" style="padding:6px;border-bottom:1px solid #eee">
-    <div class="row">
-        {{ body_header(topic) }}
-        {{ body_title(topic) }}
-        {{ body_read(topic) }}
-        {{ body_reply(topic) }}
-    </div>
+  <div class="row">
+    {{ body_header(topic) }}
+    {{ body_title(topic) }}
+    {{ body_read(topic) }}
+    {{ body_reply(topic) }}
+  </div>
 </div>
 {%- endmacro %}
 
@@ -37,70 +37,70 @@
 {% set author = topic.author %}
 {% set tags = topic.tags %}
 <div class="col-md-6 media">
-    <div class="media-left">
-        {{ link.user_avatar(author) }}
+  <div class="media-left">
+    {{ link.user_avatar(author) }}
+  </div>
+  <div class="media-body">
+    <div class="media-heading">
+      {% if topic.is_top %}
+      <span><i class="fa fa-pushpin text-danger">&nbsp</i></span>
+      {% endif %}
+      <a href="{{url_for('topic.topic',topicId=topic.id)}}" style="color:#555">{{ topic.title }}</a>
     </div>
-    <div class="media-body">
-        <div class="media-heading">
-            {% if topic.is_top %}
-            <span><i class="icon-pushpin text-danger">&nbsp</i></span>
-            {% endif %}
-            <a href="{{url_for('topic.topic',topicId=topic.id)}}" style="color:#555">{{ topic.title }}</a>
-        </div>
-        <span class="visible-xs-inline">
-            <small style="font-size:10px;">由{{ link.user(author) }}</small>
-            <small style="color:#999;font-size:10px;">
-                {{ topic.created_at | timesince }}发布
-            </small>
-        </span>
-        <div class="hidden-xs">
-            {% for tag in tags %}
-            {{ link.tag(tag) }}
-            {% endfor %}
-        </div>
+    <span class="visible-xs-inline">
+      <small style="font-size:10px;">由{{ link.user(author) }}</small>
+      <small style="color:#999;font-size:10px;">
+        {{ topic.created_at | timesince }}发布
+      </small>
+    </span>
+    <div class="hidden-xs">
+      {% for tag in tags %}
+      {{ link.tag(tag) }}
+      {% endfor %}
     </div>
+  </div>
 </div>
 {%- endmacro %}
 
 {% macro body_title(topic) -%}
 <div class="col-md-2 hidden-xs" style="padding-top:5px;">
-    <small>{{ link.user(topic.author) }}</small>
-    <br/>
-    <small style="color:#999;">
-        {{ topic.created_at | timesince }}
-    </small>
+  <small>{{ link.user(topic.author) }}</small>
+  <br/>
+  <small style="color:#999;">
+    {{ topic.created_at | timesince }}
+  </small>
 </div>
 {%- endmacro %}
 
 {% macro body_read(topic) -%}
 <div class="col-md-2  hidden-xs" style="padding:5px 5px 5px 20px;margin:0">
-    {% set rep,read = topic.id,1 %}
-    <span>{{ rep }}</span> /
-    <span style="font-size:12px;color:#999;">
-        {{ read }}
-    </span>
+  {% set rep,read = topic.id,1 %}
+  <span>{{ rep }}</span> /
+  <span style="font-size:12px;color:#999;">
+    {{ read }}
+  </span>
 </div>
 {%- endmacro %}
 
 {% macro body_reply(topic) -%}
 <div class="col-md-2 hidden-xs" style="padding:5px;margin:0;">
-    {% set last_reply = topic.replies.first() %}
-    {% if last_reply %}
-    <span style="font-size:12px">{{ link.user(last_reply.author) }}</span>
-    <br/>
-    <span style="font-size:12px;color:#999;">{{ last_reply.created_at | timesince }}</span>
-    {% else %}
-    <span style="font-size:12px">{{ link.user(topic.author) }}</span>
-    <br/>
-    <span style="font-size:12px;color:#999;">{{ topic.created_at | timesince }}</span>
-    {% endif %}
+  {% set last_reply = topic.replies.first() %}
+  {% if last_reply %}
+  <span style="font-size:12px">{{ link.user(last_reply.author) }}</span>
+  <br/>
+  <span style="font-size:12px;color:#999;">{{ last_reply.created_at | timesince }}</span>
+  {% else %}
+  <span style="font-size:12px">{{ link.user(topic.author) }}</span>
+  <br/>
+  <span style="font-size:12px;color:#999;">{{ topic.created_at | timesince }}</span>
+  {% endif %}
 </div>
 {%- endmacro %}
 
 {% macro no_topics() -%}
 <div class="panel-body">
-    <span class="text-center" style="display:block;width:100%;color:#999">
-        {{ _('No Topic')}}
-    </span>
+  <span class="text-center" style="display:block;width:100%;color:#999">
+    {{ _('No Topic')}}
+  </span>
 </div>
 {%- endmacro %}

+ 7 - 0
templates/topic/_topic.html

@@ -0,0 +1,7 @@
+{% from 'topic/_list_macro.html' import form,body,no_topics %}
+{{ form() }}
+{% for topic in topics.items %}
+{{ body(topic) }}
+{% else %}
+{{ no_topics() }}
+{% endfor %}

+ 0 - 6
templates/topic/ask.html

@@ -1,10 +1,4 @@
 {% extends 'base/base.html' %}
-{% block script -%}
-{{ super() }}
-<script type="text/javascript">
- AskTopic("{{ url_for('topic.preview') }}")
-</script>
-{%- endblock script %}
 {% block content %}
 <style>
  .tokenfield .token {

+ 14 - 12
templates/topic/collect.html

@@ -1,21 +1,23 @@
 <div class="modal fade" id="sidecollect" tabindex="-1" role="dialog" aria-labelledby="sidecollectLabel">
-  <div class="modal-dialog" role="document">
+  <div class="modal-dialog modal-sm" role="document">
     <div class="modal-content">
       <div class="modal-header">
         <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
         <h4 class="modal-title" id="sidecollectLabel">收藏夹</h4>
       </div>
-      {% if not current_user.collects.all() %}
+      {% set collects = current_user.collects.all() %}
+      {% if not collects %}
       <div class="modal-body">
-        <a href="{{ url_for('topic.collectlist') }}" style="text-decoration:none">
+        <a href="{{ url_for('collect.list') }}" style="text-decoration:none">
           <i class="fa fa-plus"></i> 创建
         </a>
       </div>
       {% else %}
-      <form action="{{ url_for('topic.add_to_collect',topicId=topic.id) }}" method="POST">
-        <div class="modal-body">
+      <div class="modal-body">
+        <form action="{{ url_for('collect.add_to_collect',topicId=topic.id) }}" method="POST">
+          {{ form.hidden_tag() }}
           <p> 添加到收藏夹 </p>
-          {% for collect in current_user.collects %}
+          {% for collect in collects %}
           <label data-id="{{ collect.id }}"><input name="add-to-collect" type="checkbox" value="{{ collect.id}}" />{{ collect.name }}</label>
           {% if collect.is_privacy %}
           <span class="label label-default">私密</span>
@@ -24,12 +26,12 @@
           {% endif %}
           <br/>
           {% endfor %}
-        </div>
-        <div class="modal-footer" style="padding-top:5px;padding-bottom:5px;">
-          <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
-          <button type="submit" class="btn btn-primary">提交</button>
-        </div>
-      </form>
+          <div class="modal-footer" style="padding-top:5px;padding-bottom:5px;">
+            <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+            <button type="submit" class="btn btn-primary">提交</button>
+          </div>
+        </form>
+      </div>
       {% endif %}
     </div>
   </div>

+ 1 - 7
templates/topic/edit.html

@@ -1,10 +1,4 @@
 {% extends 'base/base.html' %}
-{% block script -%}
-{{ super() }}
-<script type="text/javascript">
- EditTopic("{{ url_for('topic.preview') }}","{{ url_for('topic.topic',topicId=topic.id) }}")
-</script>
-{%- endblock script %}
 {% block content %}
 {{ breadcrumb(active='编辑')}}
 <style>
@@ -24,7 +18,7 @@
         {% include "topic/ask/form.html" %}
         <div class="col-sm-offset-2 col-sm-10">
           {{ form.content_type(class="input-sm pull-right",style="padding:2")}}
-          <button class="btn btn-sm btn-primary" id="topic-put-btn" type="button">提交问题</button>
+          <button class="btn btn-sm btn-primary" id="topic-put-btn" type="button" data-id="{{ topic.id }}">提交问题</button>
           <button class="btn btn-sm btn-primary" id="topic-preview" type="button">预览</button>
         </div>
         <div class="col-sm-offset-2 col-sm-10 topoc-content" id="show-preview"></div>

+ 1 - 7
templates/topic/topic_list.html

@@ -1,16 +1,10 @@
 {% extends 'base/base.html' %}
 {% block content %}
 {{ breadcrumb(active=_('All Topics')) }}
-{% from 'topic/_list_macro.html' import form,body,no_topics %}
 <div class="row">
   <div class="col-md-9">
     <div class="panel panel-default">
-      {{ form() }}
-      {% for topic in topics.items %}
-      {{ body(topic) }}
-      {% else %}
-      {{ no_topics() }}
-      {% endfor %}
+      {% include "topic/_topic.html" %}
     </div>
   </div>
   <div class="col-md-3" style="padding-left:0">

+ 2 - 2
templates/user/info.html

@@ -1,5 +1,5 @@
 {% macro other(user) -%}
-{% if g.user.is_authenticated and current_user.username != g.user_url %}
+{% if current_user.is_authenticated and current_user.id != user.id %}
 <div class="list-group-item">
   <span class="text-right" style="display:block">
     {% if user.id | user_is_followed %}
@@ -14,7 +14,7 @@
 {%- endmacro %}
 
 {% macro mine(user) -%}
-{% if g.user.is_authenticated and current_user.username == g.user_url and not user.is_confirmed %}
+{% if g.user.is_authenticated and current_user.id == user.id and not user.is_confirmed %}
 <div class="alert alert-info" style="padding:6px;font-size:12px;margin-top:10px;">
   <button type="button" class="close" data-dismiss="alert" aria-label="Close">
     <span aria-hidden="true">&times;</span>

+ 2 - 2
templates/user/user.html

@@ -28,8 +28,8 @@
   <span style="color:#666">{{ topic.board.board }}</span>
   {{ link.topic(topic) }}
   <div style="font-size:12px;color:#999;">
-    {{_('create time:')}}{{ topic.publish | timesince }}
-    · {{ _('the last reply published by %(author)s',author=link.user(topic.author.username))}}
+    {{_('create time:')}}{{ topic.created_at | timesince }}
+    · {{ _('the last reply published by %(author)s',author=link.user(topic.author))}}
   </div>
 </div>
 {% else %}