├── .gitignore
├── README.md
├── app.py
├── ml
├── back_process.py
├── data
│ └── crfpp_erl.py
├── data_reduce.py
├── my_bert_crf_model.py
├── my_bert_findId.py
└── pre_process.py
├── requirements.txt
├── static
├── css
│ ├── normalize.css
│ └── style.css
├── js
│ ├── index.js
│ ├── jquery-latest.js
│ ├── jquery.mCustomScrollbar.concat.min.js
│ ├── jquery.mCustomScrollbar.min.css
│ └── jquery.min.js
├── res
│ ├── botim.png
│ ├── easybot.png
│ ├── hd1.jpg
│ └── hd2.jpg
└── scss
│ └── style.scss
├── templates
└── index.html
└── util.py
/.gitignore:
--------------------------------------------------------------------------------
1 | venv/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## CCKS 2019 中文短文本的实体链指
2 | * 时间:2019.04.19-2019-07-25
3 | * 名次:15/346
4 | * 最终得分:
5 | * F1:0.780048270313757 P:0.7710536779324055 R:0.7892551892551892
6 | *第一名成绩:F1:0.80143*
7 |
8 |
9 | ### 介绍
10 | > 背景介绍:
11 | 近年来,随着深度学习的重燃以及海量大数据的支撑,NLP领域迎来了蓬勃发展,百度拥有全球最大的中文知识图谱,
12 | 拥有数亿实体、千亿事实,具备丰富的知识标注与关联能力,不仅构建了通用知识图谱,
13 | 还构建了汉语语言知识图谱、关注点图谱、以及包含业务逻辑在内的行业知识图谱等多维度图谱。
14 | 我们希望通过开放百度的数据,邀请学界和业界的青年才俊共同推进算法进步,激发更多灵感和火花。
15 | 面向中文短文本的实体识别与链指,简称ERL(Entity Recognition and Linking),是NLP领域的基础任务之一,
16 | 即对于给定的一个中文短文本(如搜索Query、微博、用户对话内容、文章标题等)识别出其中的实体,并与给定知识库中的对应实体进行关联。ERL整个过程包括实体识别和实体链指两个子任务。
17 | 传统的实体链指任务主要是针对长文档,长文档拥有在写的上下文信息能辅助实体的歧义消解并完成链指。
18 | 相比之下,针对中文短文本的实体链指存在很大的挑战,主要原因如下:
19 | (1)口语化严重,导致实体歧义消解困难;
20 | (2)短文本上下文语境不丰富,须对上下文语境进行精准理解;
21 | (3)相比英文,中文由于语言自身的特点,在短文本的链指问题上更有挑战。
22 | 竞赛任务:
23 | 输入:
24 | 输入文件包括若干行中文短文本。
25 | 输出:
26 | 输出文本每一行包括此中文短文本的实体识别与链指结果,需识别出文本中所有mention(包括实体与概念),每个mention包含信息如下:mention在给定知识库中的ID,mention名和在中文短文本中的位置偏移。
27 |
28 | [比赛官网](https://biendata.com/competition/ccks_2019_el/)
29 |
30 | ### 工程运行
31 | * 配置
32 | * 运行*pip install -r requirements.txt*安装大部分必要的包
33 | * 自行下载bert的*chinese_L-12_H-768_A-12*放在*bert/bertModel*下
34 | * 请自行下载官方数据:train.json、kb_data,并将其放在*ml/data*目录下
35 | * 执行
36 | * 运行*ml/pre_process.py*对数据进行简单的预处理,删掉一些实体重合的数据
37 | * 运行*ml/data_reduce.py*打上BIO标签
38 | * 运行*ml/my_bert_crf_model.py*训练提取实体模型,自行设置模型的保存路径和加载路径
39 | * 运行*ml/my_bert_findId.py*运行消歧模型,从kb_data中找到对应的kb_id,自行设置模型的保存路径和加载路径
40 | * 交互界面
41 | * 套用了https://github.com/zhaoyingjun/chatbot 中的聊天机器人界面
42 | * 在*app.py*中配置监听端口,运行后通过浏览器访问
43 | * 预览:
44 |
45 | ### 模型介绍
46 | * 提取实体模型
47 | * bert + crf
48 | * 线下提取实体F1值:0.8233
49 | * 消歧模型
50 | * bert分类
51 | * 线下纯消歧F1值:0.8949
52 |
53 | (关于keras模型的搭建参考苏剑林同学的[博客](https://spaces.ac.cn/))
54 | * crf++
55 | *ml/data/crfpp_erl.py*
56 | * 该模型没有采用到比赛中,线下实体提取准确率大概为0.76。虽然效果比bert差,
57 | 但是训练快,只需几百秒就可以训练完。
58 |
59 | (crf++参考的是达观比赛官方的baseline)
60 |
61 | ### 后处理 (效果提升不是很大)
62 | *ml/back_process.py*
63 | * 提取一句话中所有的相同的实体
64 | * 粗粒度处理:若两个实体相邻,并且结合起来的实体在kb_data中,则选取结合起来的实体
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env Python
2 | # coding=utf-8
3 | import sys
4 | import os
5 |
6 | curPath = os.path.abspath(os.path.dirname(__file__))
7 | rootPath = os.path.split(curPath)[0]
8 | sys.path.append(rootPath)
9 |
10 | from flask import Flask, render_template, request, make_response
11 | from flask import jsonify
12 |
13 | import sys
14 | import time
15 | import hashlib
16 | import threading
17 | from ml import my_bert_crf_model,my_bert_findId
18 |
19 | def heartbeat():
20 | print(time.strftime('%Y-%m-%d %H:%M:%S - heartbeat', time.localtime(time.time())))
21 | timer = threading.Timer(60, heartbeat)
22 | timer.start()
23 |
24 |
25 | timer = threading.Timer(60, heartbeat)
26 | timer.start()
27 |
28 | try:
29 | import xml.etree.cElementTree as ET
30 | except ImportError:
31 | import xml.etree.ElementTree as ET
32 |
33 | import re
34 |
35 | zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
36 |
37 | app = Flask(__name__, static_url_path="/static")
38 |
39 |
40 | @app.route('/message', methods=['POST'])
41 | def reply():
42 | req_msg = request.form['msg']
43 | res_msg = '^_^'
44 | print(req_msg)
45 | # print(''.join([f + ' ' for fh in req_msg for f in fh]))
46 | # req_msg = ''.join([f + ' ' for fh in req_msg for f in fh])
47 | # print(req_msg)
48 | # res_msg = execute.decode_line(sess, model, enc_vocab, rev_dec_vocab, req_msg)
49 | _=my_bert_crf_model.pred(req_msg)
50 | r =my_bert_findId.pred(_)
51 | print(str(r))
52 | rtn=''
53 | for l in r:
54 | rtn+=l['mention']+'
'+l['content']['data'][0]['object'][:50]+'...'+'
'
55 | res_msg=rtn
56 | res_msg = res_msg.replace('_UNK', '^_^')
57 | res_msg = res_msg.strip()
58 |
59 | # 如果接受到的内容为空,则给出相应的恢复
60 | if res_msg == '':
61 | res_msg = '没有查找到相关信息'
62 | print(res_msg)
63 | return jsonify({'text': res_msg})
64 |
65 |
66 | @app.route("/")
67 | def index():
68 | return render_template("index.html")
69 |
70 |
71 | #
72 |
73 | '''
74 | 初始化seq2seqModel,并进行动作
75 |
76 | 1. 调用执行器的主程序
77 | 2. 生成一个在线decode进程,来提供在线聊天服务
78 | '''
79 | # _________________________________________________________________
80 | import tensorflow as tf
81 | # from seq2seqChatbot import execute
82 |
83 | # sess = tf.Session()
84 | # sess, model, enc_vocab, rev_dec_vocab = execute.init_session(sess, conf='seq2seq_serve.ini')
85 | # _________________________________________________________________
86 |
87 | # 启动APP
88 | if (__name__ == "__main__"):
89 | app.run(host='0.0.0.0', port=8809)
90 |
--------------------------------------------------------------------------------
/ml/back_process.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : back_process.py
4 | @Time : 2019/7/14 18:21
5 | @Author : Blue Keroro
6 | """
7 | import json
8 | import re
9 |
10 | from tqdm import tqdm
11 |
12 | from util import loadData, is_word, loadDataBase
13 | import numpy as np
14 | from random import choice, choices
15 |
16 |
17 | def process(data):
18 | for i, l in enumerate(tqdm(data)):
19 | md = set()
20 | text = l['text']
21 | arr = np.zeros(len(text))
22 | for ment in l['mention_data']:
23 | arr[int(ment['offset']):int(ment['offset']) + len(ment['mention'])] = 1
24 | for ment in l['mention_data']:
25 | if (not is_word(ment['mention'])):
26 | print(l)
27 | print(ment['mention'])
28 | print('')
29 | continue
30 | md.add((ment['kb_id'], ment['mention'], ment['offset']))
31 |
32 | index = 0
33 | while text.find(ment['mention'], index) != -1:
34 | index = text.find(ment['mention'], index)
35 | if len(np.nonzero(arr[index:index + len(ment['mention'])])[0]) == 0:
36 | md.add((ment['kb_id'], ment['mention'], str(index)))
37 | arr[index:index + len(ment['mention'])] = 1
38 | index += 1
39 | l['mention_data'] = sorted([{'kb_id': _[0], 'mention': _[1], 'offset': _[2]} for _ in md],
40 | key=lambda x: int(x['offset']))
41 | return data
42 |
43 |
44 | dataByAlias, dataBySubjectId = loadDataBase('data/kb_data')
45 |
46 |
47 | def merge(data):
48 | for l in data:
49 | t = l['text']
50 | d = {int(m['offset']): m['mention'] for m in l['mention_data']}
51 | mt = {(_['kb_id'], _['mention'], _['offset']) for _ in l['mention_data']}
52 | for s in d:
53 | m = d[s]
54 | s1 = s + len(m)
55 | if s1 in d and (m + d[s1]) in dataByAlias:
56 | mt = {_ for _ in mt if not ((_[1] == d[s1] and _[2] == str(s1)) or (_[1] == m and _[2] == str(s)))}
57 | mt.add(('NIL', m + d[s1], str(s)))
58 | l['mention_data'] = [{"kb_id": _[0], "mention": _[1], 'offset': _[2]} for _ in mt]
59 |
60 |
61 | if __name__ == '__main__':
62 | pass
63 |
--------------------------------------------------------------------------------
/ml/data/crfpp_erl.py:
--------------------------------------------------------------------------------
1 | import codecs
2 | import json
3 | import os
4 |
5 |
6 | # 0 install crf++ https://taku910.github.io/crfpp/
7 | # 1 train data in
8 | # 2 test data in
9 | # 3 crf train
10 | # 4 crf test
11 | # 5 submit test
12 |
13 | def loadData(file_path):
14 | data = []
15 | with open(file_path, 'r', encoding='utf-8') as file:
16 | for line in file:
17 | line = line.strip()
18 | data.append(eval(line))
19 | return data
20 |
21 |
22 | def get_train_data(data_path):
23 | train_data = []
24 | tmp = []
25 | with open(data_path, "r", encoding="utf-8") as f:
26 | for line in f:
27 | if line == '\n':
28 | train_data.append(tmp)
29 | tmp = []
30 | else:
31 | tmp.append(line.strip('\n'))
32 | # random.shuffle(train_data)
33 | train_data = [[j.split('@$$@') for j in i] for i in train_data]
34 | train_x = [[token[0] for token in sen] for sen in train_data]
35 | train_y = [[token[1] for token in sen] for sen in train_data]
36 | return train_x, train_y
37 |
38 |
39 | def get_train_data1(data_path):
40 | train_data = []
41 | tmp = []
42 | with open(data_path, "r", encoding="utf-8") as f:
43 | for line in f:
44 | if line == '\n':
45 | train_data.append(tmp)
46 | tmp = []
47 | else:
48 | tmp.append(line.strip('\n'))
49 | train_data = [[j.split('\t') for j in i] for i in train_data]
50 | train_x = [[token[0] for token in sen] for sen in train_data]
51 | train_y = [[token[1] for token in sen] for sen in train_data]
52 | return train_x, train_y
53 |
54 |
55 | train_x, train_y = get_train_data('train_text.txt')
56 | data = list(zip(train_x, train_y))
57 | train_data = data[:int(len(data) * 0.8)]
58 | test_data = data[int(len(data) * 0.8):]
59 |
60 | with codecs.open('erl_train.txt', 'w', encoding='utf-8') as f_out:
61 | for d in train_data:
62 | for i in range(len(d[0])):
63 | if d[0][i] == ' ':
64 | d[0][i] = '_'
65 | f_out.write(d[0][i] + '\t' + d[1][i] + '\n')
66 | f_out.write('\n')
67 |
68 | # step 2 test data in
69 | with codecs.open('erl_test.txt', 'w', encoding='utf-8') as f_out:
70 | for d in (train_data+test_data):
71 | for i in range(len(d[0])):
72 | if d[0][i] == ' ':
73 | d[0][i] = '_'
74 | f_out.write(d[0][i] + '\n')
75 | f_out.write('\n')
76 |
77 | # 3 crf train
78 | crf_train = "crfpp\crf_learn -f 3 template.txt erl_train.txt erl_model"
79 | os.system(crf_train)
80 |
81 | # 4 crf test
82 | crf_test = "crfpp\crf_test -m erl_model erl_test.txt -o erl_result.txt"
83 | os.system(crf_test)
84 |
85 | raw_data = loadData('train_pre.json')
86 | data_x, data_y = get_train_data1('erl_result.txt')
87 | result = list(zip(data_x, data_y))
88 | with open('erl_crfpp_train_output.json', 'w', encoding='utf-8') as f:
89 | for index in range(len(result)):
90 | mt = set()
91 | text = raw_data[index]['text']
92 | labels = result[index][1]
93 | for i, (s, t) in enumerate(zip(text, labels)):
94 | if t == 'B-ment':
95 | ment = s
96 | offset = i
97 | for index1, (s1, t1) in enumerate(list(zip(text, labels))[i + 1:]):
98 | if t1 == 'I-ment':
99 | ment += s1
100 | else:
101 | break
102 | mt.add((ment, str(offset)))
103 | ms = [{'mention': _[0], 'offset': _[1]} for _ in mt]
104 | line = {'text': text, 'mention_data': ms}
105 | f.write(json.dumps(line, ensure_ascii=False) + '\n')
106 |
--------------------------------------------------------------------------------
/ml/data_reduce.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : data_reduce.py
4 | @Time : 2019/6/4 14:18
5 | @Author : Blue Keroro
6 | """
7 | import sys
8 | import os
9 |
10 | curPath = os.path.abspath(os.path.dirname(__file__))
11 | rootPath = os.path.split(curPath)[0]
12 | sys.path.append(rootPath)
13 |
14 | import random
15 |
16 | from tqdm import tqdm
17 |
18 | from util import loadData
19 |
20 |
21 | def get_train_data(data_path):
22 | train_data = []
23 | tmp = []
24 | with open(data_path, "r", encoding="utf-8") as f:
25 | for line in f:
26 | if line == '\n':
27 | train_data.append(tmp)
28 | tmp = []
29 | else:
30 | tmp.append(line.strip('\n'))
31 | # random.shuffle(train_data)
32 | train_data = [[j.split('@$$@') for j in i] for i in train_data]
33 | train_x = [[token[0] for token in sen] for sen in train_data]
34 | train_y = [[token[1] for token in sen] for sen in train_data]
35 | return train_x, train_y
36 |
37 |
38 | def reduce(line, labels):
39 | label_list = [labels[0] for i in line['text']]
40 | for ment in line['mention_data']:
41 | label_list[int(ment['offset'])] = labels[1]
42 | for i in range(len(ment['mention']) - 1):
43 | label_list[int(ment['offset']) + i + 1] = labels[2]
44 | ret = []
45 | for index in range(len(line['text'])):
46 | ret.append(line['text'][index] + '@$$@' + label_list[index])
47 | return ret
48 |
49 |
50 | def predict_reduce(test_x, pred_y, labels):
51 | index = -1
52 | entitys = list()
53 | while (1):
54 | try:
55 | index = pred_y.index(labels[1], index + 1)
56 | entity = test_x[index]
57 | for i in range(index + 1, len(test_x)):
58 | if pred_y[i] != labels[2]:
59 | break
60 | entity += test_x[i]
61 | entitys.append([entity, index])
62 | except Exception:
63 | break
64 | return entitys
65 |
66 |
67 | def loadBertPred(path):
68 | data = []
69 | with open(path, 'r', encoding='utf-8') as file:
70 | for line in file:
71 | line = line.strip()
72 | line = eval(line)
73 | line = [str(i) for i in line]
74 | data.append(line)
75 | return data
76 | def loadDevData(path):
77 | data = loadData(path)
78 | ret=[]
79 | for sub in data:
80 | ret.append(list(sub['text']))
81 | return ret
82 |
83 | if __name__ == '__main__':
84 | labels = ['O', 'B-ment', 'I-ment']
85 | data = loadData('data/train_pre.json')
86 | with open('data/train_text.txt', 'w', encoding='utf-8') as file:
87 | for line in tqdm(data):
88 | line = reduce(line, labels)
89 | file.write('\n'.join(line))
90 | file.write('\n\n')
91 | # train_x, train_y = get_train_data('data/train_text.txt')
92 | # print(train_x)
93 | # print(train_y)
94 | # print(predict_reduce(train_x[0], train_y[0],labels))
95 |
--------------------------------------------------------------------------------
/ml/my_bert_crf_model.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : my_bert_crf_model.py
4 | @Time : 2019/7/3 1:11
5 | @Author : Blue Keroro
6 | """
7 | import sys
8 | import os
9 |
10 | curPath = os.path.abspath(os.path.dirname(__file__))
11 | rootPath = os.path.split(curPath)[0]
12 | sys.path.append(rootPath)
13 |
14 | from keras_contrib import losses
15 | import json
16 | import numpy as np
17 | from random import choice
18 | from keras_bert import load_trained_model_from_checkpoint, Tokenizer
19 | import re, os
20 | import codecs
21 | from keras.callbacks import Callback
22 | from tqdm import tqdm
23 |
24 | from bert.data_reduce import get_train_data
25 | from util import loadData, loadDataBase
26 | import numpy as np
27 |
28 | maxlen = 100
29 | path = os.path.dirname(__file__) if len(os.path.dirname(__file__)) != 0 else '.'
30 | config_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/bert_config.json'
31 | checkpoint_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/bert_model.ckpt'
32 | dict_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/vocab.txt'
33 |
34 | token_dict = {}
35 |
36 | with codecs.open(dict_path, 'r', 'utf8') as reader:
37 | for line in reader:
38 | token = line.strip()
39 | token_dict[token] = len(token_dict)
40 |
41 |
42 | class OurTokenizer(Tokenizer):
43 | def _tokenize(self, text):
44 | R = []
45 | for c in text:
46 | if c in self._token_dict:
47 | R.append(c)
48 | elif self._is_space(c):
49 | R.append('[unused1]') # space类用未经训练的[unused1]表示
50 | else:
51 | R.append('[UNK]') # 剩余的字符是[UNK]
52 | return R
53 |
54 |
55 | tokenizer = OurTokenizer(token_dict)
56 |
57 | labels = ['O', 'B-ment', 'I-ment']
58 | x, y = get_train_data(path + '/' + 'data/train_text.txt')
59 | y = [[labels.index(tag) for tag in tags] for tags in y]
60 | data = list(zip(x, y))
61 | train_data = data[:int(len(data) * 0.8)]
62 | valid_data = data[int(len(data) * 0.8):]
63 | dataByAlias, dataBySubjectId = loadDataBase(path + '/' + 'data/kb_data')
64 |
65 |
66 | def seq_padding(X, padding=0):
67 | L = [len(x) for x in X]
68 | ML = max(L)
69 | return np.array([
70 | np.concatenate([x, [padding] * (ML - len(x))]) if len(x) < ML else x for x in X
71 | ])
72 |
73 |
74 | class data_generator:
75 | def __init__(self, data, batch_size=64):
76 | self.data = data
77 | self.batch_size = batch_size
78 | self.steps = len(self.data) // self.batch_size
79 | if len(self.data) % self.batch_size != 0:
80 | self.steps += 1
81 |
82 | def __len__(self):
83 | return self.steps
84 |
85 | def __iter__(self):
86 | while True:
87 | idxs = range(len(self.data))
88 | # np.random.shuffle(idxs)
89 | X1, X2, Y = [], [], []
90 | for i in idxs:
91 | d = self.data[i]
92 | text = d[0]
93 | x1, x2 = tokenizer.encode(first=text.lower())
94 | y = [-2] + d[1] + [-2]
95 | X1.append(x1)
96 | X2.append(x2)
97 | Y.append(y)
98 | if len(X1) == self.batch_size or i == idxs[-1]:
99 | X1 = seq_padding(X1)
100 | X2 = seq_padding(X2)
101 | Y = seq_padding(Y, padding=-1)
102 | Y = np.expand_dims(Y, 2)
103 | yield [X1, X2], Y
104 | [X1, X2, Y] = [], [], []
105 |
106 |
107 | class Evaluate(Callback):
108 | def __init__(self):
109 | self.F1 = []
110 | self.best = 0.
111 | self.best_ment = 0.
112 | data = loadData(path + '/' + "data/train_pre.json")
113 | self.test_x = [i['text'] for i in data[int(len(data) * 0.8):]]
114 | self.test_y = [[(ment['mention'], ment['offset']) for ment in ments['mention_data'] if ment['kb_id'] != 'NIL']
115 | for ments in data[int(len(data) * 0.8):]]
116 |
117 | def on_epoch_end(self, epoch, logs=None):
118 | [f1, precision, recall] = self.evaluate()
119 | self.F1.append(f1)
120 | if f1 > self.best:
121 | self.best = f1
122 | model.save_weights(path + '/' + 'best_model.weights')
123 | print('f1: %.4f, precision: %.4f, recall: %.4f, best f1: %.4f\n' % (f1, precision, recall, self.best))
124 | print("F1 list:", evaluator.F1)
125 |
126 | def evaluate(self):
127 | A, B, C = 1e-10, 1e-10, 1e-10
128 | for x, y in tqdm(list(zip(self.test_x, self.test_y))):
129 | R = set(self.extract_items(x))
130 | T = set(y)
131 | A += len(R & T)
132 | B += len(R)
133 | C += len(T)
134 | return [2 * A / (B + C), A / B, A / C]
135 |
136 | def extract_items(self, inputs):
137 | x1, x2 = tokenizer.encode(first=inputs.lower())
138 | raw = model.predict([np.array([x1]), np.array([x2])])[0]
139 | raw = raw[1:1 + len(inputs)]
140 | result = [np.argmax(row) for row in raw]
141 | result_tags = [labels[int(i)] for i in result]
142 | ments = []
143 | for index, (s, t) in enumerate(zip(inputs, result_tags)):
144 | if t == 'B-ment':
145 | ment = s
146 | offset = index
147 | for index1, (s1, t1) in enumerate(list(zip(inputs, result_tags))[index + 1:]):
148 | if t1 == 'I-ment':
149 | ment += s1
150 | else:
151 | break
152 | if ment in dataByAlias or ment.lower() in dataByAlias:
153 | ments.append((ment, str(offset)))
154 | return ments
155 |
156 |
157 | from keras.layers import *
158 | from keras.models import Model
159 | import keras.backend as K
160 | from keras.optimizers import Adam
161 | from keras_contrib.layers import CRF
162 |
163 | bert_model = load_trained_model_from_checkpoint(config_path, checkpoint_path)
164 |
165 | for l in bert_model.layers:
166 | l.trainable = True
167 | BiRNN_UNITS = 768 # 768
168 | x1_in = Input(shape=(None,))
169 | x2_in = Input(shape=(None,))
170 | x = bert_model([x1_in, x2_in])
171 | crf = CRF(len(labels), sparse_target=True)
172 | p = crf(x)
173 |
174 | model = Model([x1_in, x2_in], p)
175 | model.compile(optimizer=Adam(1e-5), loss=losses.crf_loss, metrics=[crf.accuracy])
176 | model.summary()
177 |
178 | train_D = data_generator(train_data)
179 | evaluator = Evaluate()
180 |
181 |
182 | def pred(text):
183 | R = evaluator.extract_items(text)
184 | R = [{'mention': r[0], 'offset': r[1]} for r in R]
185 | R.sort(key=lambda x: int(x['offset']))
186 | l = {}
187 | l['text'] = text
188 | l['mention_data'] = R
189 | return l
190 |
191 |
192 | if __name__ == '__main__':
193 | model.fit_generator(
194 | train_D.__iter__(),
195 | steps_per_epoch=len(train_D),
196 | epochs=10,
197 | callbacks=[evaluator]
198 | )
199 | print('预测eval:')
200 | with open('data/eval.json', 'r', encoding='utf-8')as f1, \
201 | open('data/eval_output_ment.json', 'w', encoding='utf-8') as f2:
202 | for l in tqdm(f1.readlines()):
203 | l = json.loads(l)
204 | R = set(evaluator.extract_items(l['text']))
205 | R = [{'mention': r[0], 'offset': r[1]} for r in R]
206 | R.sort(key=lambda x: int(x['offset']))
207 | l['mention_data'] = R
208 | f2.write(json.dumps(l, ensure_ascii=False) + '\n')
209 |
210 | else:
211 | model.load_weights(path + '/' + 'model/0.82_ment_best_model.weights')
212 | evaluator.extract_items("听说这样可以解决web中keras加载调用的问题")
213 | print('加载权重', path + '/' + 'model/0.82_ment_best_model.weights')
214 |
215 | # print('预测train:')
216 | # with open('../bert/data/train_pre.json', 'r', encoding='utf-8')as f1, \
217 | # open('output_data/train_output_ment.json', 'w', encoding='utf-8') as f2:
218 | # for l in tqdm(f1.readlines()):
219 | # l = json.loads(l)
220 | # R = set(evaluator.extract_items(l['text']))
221 | # R = [{'mention': r[0], 'offset': r[1]} for r in R]
222 | # R.sort(key=lambda x: int(x['offset']))
223 | # l['mention_data'] = R
224 | # f2.write(json.dumps(l, ensure_ascii=False) + '\n')
225 | # print('预测dev:')
226 | # with open('../data/develop.json', 'r', encoding='utf-8')as f1, \
227 | # open('output_data/dev_output_ment.json', 'w', encoding='utf-8') as f2:
228 | # for l in tqdm(f1.readlines()):
229 | # l = json.loads(l)
230 | # R = set(evaluator.extract_items(l['text']))
231 | # R = [{'mention': r[0], 'offset': r[1]} for r in R]
232 | # R.sort(key=lambda x: int(x['offset']))
233 | # l['mention_data'] = R
234 | # f2.write(json.dumps(l, ensure_ascii=False) + '\n')
235 | # print('预测eval:')
236 | # with open('../data/eval722.json', 'r', encoding='utf-8')as f1, \
237 | # open('output_data/eval_output_ment.json', 'w', encoding='utf-8') as f2:
238 | # for l in tqdm(f1.readlines()):
239 | # l = json.loads(l)
240 | # R = set(evaluator.extract_items(l['text']))
241 | # R = [{'mention': r[0], 'offset': r[1]} for r in R]
242 | # R.sort(key=lambda x: int(x['offset']))
243 | # l['mention_data'] = R
244 | # f2.write(json.dumps(l, ensure_ascii=False) + '\n')
245 |
--------------------------------------------------------------------------------
/ml/my_bert_findId.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : my_bert_crf_model.py
4 | @Time : 2019/7/3 1:11
5 | @Author : Blue Keroro
6 | """
7 | import sys
8 | import os
9 |
10 | curPath = os.path.abspath(os.path.dirname(__file__))
11 | rootPath = os.path.split(curPath)[0]
12 | sys.path.append(rootPath)
13 | import json
14 | import numpy as np
15 | from random import choice
16 | from keras_bert import load_trained_model_from_checkpoint, Tokenizer, get_model
17 | import re, os
18 | import codecs
19 | from keras.callbacks import Callback
20 | from tqdm import tqdm
21 |
22 | from bert.data_reduce import get_train_data
23 | from util import loadData, loadDataBase
24 | import numpy as np
25 |
26 | path = os.path.dirname(__file__) if len(os.path.dirname(__file__)) != 0 else '.'
27 | config_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/bert_config.json'
28 | checkpoint_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/bert_model.ckpt'
29 | dict_path = path + '/' + '../bert/bertModel/chinese_L-12_H-768_A-12/vocab.txt'
30 |
31 | token_dict = {}
32 |
33 | with codecs.open(dict_path, 'r', 'utf8') as reader:
34 | for line in reader:
35 | token = line.strip()
36 | token_dict[token] = len(token_dict)
37 |
38 |
39 | class OurTokenizer(Tokenizer):
40 | def _tokenize(self, text):
41 | R = []
42 | for c in text:
43 | if c in self._token_dict:
44 | R.append(c)
45 | elif self._is_space(c):
46 | R.append('[unused1]') # space类用未经训练的[unused1]表示
47 | else:
48 | R.append('[UNK]') # 剩余的字符是[UNK]
49 | return R
50 |
51 |
52 | tokenizer = OurTokenizer(token_dict)
53 | char_size = 512 # 768
54 | data = loadData(path + '/' + 'data/train_pre.json')
55 | # data=[ for line in data]
56 | train_data = data[:int(len(data) * 0.8)]
57 | valid_data = data[int(len(data) * 0.8):]
58 | dataByAlias, dataBySubjectId = loadDataBase(path + '/' + 'data/kb_data')
59 |
60 |
61 | def seq_padding(X, padding=0):
62 | L = [len(x) for x in X]
63 | ML = max(L)
64 | return np.array([
65 | np.concatenate([x, [padding] * (ML - len(x))]) if len(x) < ML else x for x in X
66 | ])
67 |
68 |
69 | class data_generator:
70 | def __init__(self, data, batch_size=4):
71 | self.data = data
72 | self.batch_size = batch_size
73 | length = 0
74 | for i in range(len(self.data)):
75 | for ment in self.data[i]['mention_data']:
76 | if ment['mention'].lower() not in dataByAlias or ment['kb_id'] == 'NIL' or ment[
77 | 'kb_id'] not in dataBySubjectId:
78 | continue
79 | length += 2 if len(dataByAlias[ment['mention'].lower()]) > 1 else 1
80 | self.steps = length // self.batch_size
81 | if length % self.batch_size != 0:
82 | self.steps += 1
83 |
84 | def __len__(self):
85 | return self.steps
86 |
87 | def __iter__(self):
88 | while True:
89 | idxs = range(len(self.data))
90 | # np.random.shuffle(idxs)
91 | X1, X2, S, Y = [], [], [], []
92 | for i in idxs:
93 | for ment in self.data[i]['mention_data']:
94 | text = data[i]['text']
95 | if ment['mention'].lower() not in dataByAlias or ment['kb_id'] == 'NIL' or ment[
96 | 'kb_id'] not in dataBySubjectId:
97 | continue
98 | for l in range(2):
99 | if l == 0:
100 | l = choice(dataByAlias[ment['mention'].lower()])
101 | while l['subject_id'] == ment['kb_id'] and len(dataByAlias[ment['mention'].lower()]) > 1:
102 | l = choice(dataByAlias[ment['mention'].lower()])
103 | elif len(dataByAlias[ment['mention'].lower()]) > 1:
104 | l = dataBySubjectId[ment['kb_id']]
105 | if l == 1 or l == 2:
106 | continue
107 | subject_desc = '\n'.join(l['type'])
108 | subject_desc += '\n\n' + '\n'.join(
109 | [_['predicate'] + ':' + _['object'] for _ in l['data']]).lower()
110 | x1, x2 = tokenizer.encode(first=text.lower() + '\n\n' + subject_desc, max_len=char_size)
111 | s1 = np.zeros(len(text) + 2)
112 | s1[int(ment['offset']) + 1: int(ment['offset']) + 1 + len(ment['mention'])] = 1
113 | if ment['kb_id'] == l['subject_id']:
114 | y = [1]
115 | else:
116 | y = [0]
117 | X1.append(x1)
118 | X2.append(x2)
119 | S.append(s1)
120 | Y.append(y)
121 | if len(X1) == self.batch_size or i == idxs[-1]:
122 | X1 = seq_padding(X1, padding=-1)
123 | X2 = seq_padding(X2, padding=-1)
124 | S = seq_padding(S, padding=-1)
125 | Y = seq_padding(Y, padding=-1)
126 | yield [X1, X2], Y
127 | [X1, X2, S, Y] = [], [], [], []
128 | # evaluator.evaluate()
129 |
130 |
131 | class Evaluate(Callback):
132 | def __init__(self):
133 | self.F1 = []
134 | self.best = 0.
135 | self.best_ment = 0.
136 | self.test_x = valid_data
137 | self.test_y = [[(ment['mention'], ment['offset'], ment['kb_id']) for ment in ments['mention_data'] if
138 | ment['kb_id'] != 'NIL' and ment['kb_id'] in dataBySubjectId]
139 | for ments in valid_data]
140 |
141 | def on_epoch_end(self, epoch, logs=None):
142 | [f1, precision, recall], [f1_ment, precision_ment, recall_ment] = self.evaluate()
143 | self.F1.append(f1)
144 | print("F1 list:", evaluator.F1)
145 | if f1 > self.best:
146 | self.best = f1
147 | t_model.save_weights(path + '/' + 'best_model.weights')
148 | print('f1: %.4f, precision: %.4f, recall: %.4f, best f1: %.4f\n' % (f1, precision, recall, self.best))
149 | if f1_ment > self.best_ment:
150 | self.best_ment = f1_ment
151 | print('实体提取:f1: %.4f, precision: %.4f, recall: %.4f, best f1: %.4f\n' % (
152 | f1_ment, precision_ment, recall_ment, self.best_ment))
153 |
154 | def evaluate(self):
155 | A, B, C = 1e-10, 1e-10, 1e-10
156 | A1, B1, C1 = 1e-10, 1e-10, 1e-10
157 | for x, y in tqdm(list(zip(self.test_x, self.test_y))):
158 | R = set(self.extract_items(x))
159 | T = set(y)
160 | A += len(R & T)
161 | B += len(R)
162 | C += len(T)
163 | R1 = set([(_[0], _[1]) for _ in R])
164 | T1 = set([(_[0], _[1]) for _ in T])
165 | A1 += len(R1 & T1)
166 | B1 += len(R1)
167 | C1 += len(T1)
168 | return [2 * A / (B + C), A / B, A / C], [2 * A1 / (B1 + C1), A1 / B1, A1 / C1]
169 |
170 | def extract_items(self, inputs):
171 | rtn = []
172 | for ment in inputs['mention_data']:
173 | X1, X2, S = [], [], []
174 | kb_ids = []
175 | text = inputs['text']
176 | if ment['mention'].lower() not in dataByAlias:
177 | continue
178 | for l in dataByAlias[ment['mention'].lower()]:
179 | subject_desc = '\n'.join(l['type'])
180 | subject_desc += '\n\n' + '\n'.join([_['predicate'] + ':' + _['object'] for _ in l['data']]).lower()
181 | # x1, x2 = tokenizer.encode(first=text, second=subject_desc)
182 | x1, x2 = tokenizer.encode(first=text.lower() + '\n\n' + subject_desc, max_len=char_size)
183 | s1 = np.zeros(len(text) + 2)
184 | s1[int(ment['offset']) + 1: int(ment['offset']) + 1 + len(ment['mention'])] = 1
185 | X1.append(x1)
186 | X2.append(x2)
187 | S.append(s1)
188 | kb_ids.append(l['subject_id'])
189 | X1 = seq_padding(X1, padding=-1)
190 | X2 = seq_padding(X2, padding=-1)
191 | S = seq_padding(S, padding=-1)
192 | scores = t_model.predict([X1, X2])[:, 0]
193 | index = np.argmax(scores)
194 | rtn.append((ment['mention'], ment['offset'], kb_ids[index]))
195 | return rtn
196 |
197 |
198 | from keras.layers import *
199 | from keras.models import Model
200 | import keras.backend as K
201 | from keras.optimizers import Adam
202 | from keras_contrib.layers import CRF
203 |
204 | bert_model = load_trained_model_from_checkpoint(config_path, checkpoint_path)
205 |
206 | for l in bert_model.layers:
207 | l.trainable = True
208 |
209 | x1_in = Input(shape=(None,))
210 | x2_in = Input(shape=(None,))
211 | s_in = Input(shape=(None,))
212 | y_in = Input(shape=(None,))
213 | x1, x2, s, y = x1_in, x2_in, s_in, y_in
214 | x = bert_model([x1, x2])
215 | x = Lambda(lambda x: x[:, 0])(x)
216 | pt = Dense(1, activation='sigmoid')(x)
217 | t_model = Model([x1_in, x2_in], pt)
218 | t_model.compile(
219 | loss='binary_crossentropy',
220 | optimizer=Adam(1e-5), # 用足够小的学习率
221 | metrics=['accuracy']
222 | )
223 | t_model.summary()
224 | train_D = data_generator(train_data)
225 | evaluator = Evaluate()
226 |
227 |
228 | def pred(data):
229 | R = evaluator.extract_items(data) # [{'kb_id': r[2], 'mention': r[0], 'offset': r[1]} for r in R]
230 | rtn = []
231 | for r in R:
232 | d = {}
233 | d['mention'] = r[0]
234 | d['content'] = dataBySubjectId[r[2]]
235 | rtn.append(d)
236 | return rtn
237 |
238 |
239 | if __name__ == '__main__':
240 | t_model.fit_generator(
241 | train_D.__iter__(),
242 | steps_per_epoch=len(train_D),
243 | epochs=4,
244 | callbacks=[evaluator]
245 | )
246 | print('预测eval:')
247 | with open('data/eval_output_ment.json', 'r', encoding='utf-8')as f1, \
248 | open('data/eval_output.json', 'w', encoding='utf-8') as f2:
249 | for l in tqdm(f1.readlines()):
250 | l = json.loads(l)
251 | R = set(evaluator.extract_items(l))
252 | R = [{'kb_id': r[2], 'mention': r[0], 'offset': r[1]} for r in R]
253 | R.sort(key=lambda x: int(x['offset']))
254 | l['mention_data'] = R
255 | f2.write(json.dumps(l, ensure_ascii=False) + '\n')
256 |
257 | else:
258 | t_model.load_weights(path + '/' + 'model/0.8949_findId_best_model.weights')
259 | # 听说这样可以解决web中keras加载调用的问题
260 | evaluator.extract_items({'text': "南京南站:坐高铁在南京南站下。南京南站", 'mention_data': [{'mention': '南京南站', 'offset': '0'}]})
261 |
262 | # print('预测eval:')
263 | # with open('../data/eval_output24_backprocess_merge.json', 'r', encoding='utf-8')as f1, \
264 | # open('output_data/eval_output24_backprocess_merge_findId.json', 'w', encoding='utf-8') as f2:
265 | # for l in tqdm(f1.readlines()):
266 | # try:
267 | # l = json.loads(l.strip())
268 | # except Exception as e:
269 | # print(str(l))
270 | # raise e
271 | # m1 = [_ for _ in l['mention_data'] if _['kb_id'] == 'NIL']
272 | # m2 = [_ for _ in l['mention_data'] if _['kb_id'] != 'NIL']
273 | # l['mention_data']=m1
274 | # R = set(evaluator.extract_items(l))
275 | # R = [{'kb_id': r[2], 'mention': r[0], 'offset': r[1]} for r in R]+m2
276 | # R.sort(key=lambda x: int(x['offset']))
277 | # l['mention_data'] = R
278 | # f2.write(json.dumps(l, ensure_ascii=False) + '\n')
279 |
--------------------------------------------------------------------------------
/ml/pre_process.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : test.py
4 | @Time : 2019/5/27 8:32
5 | @Author : Blue Keroro
6 | """
7 | import sys
8 | import os
9 |
10 | curPath = os.path.abspath(os.path.dirname(__file__))
11 | rootPath = os.path.split(curPath)[0]
12 | sys.path.append(rootPath)
13 |
14 | from util import loadDataBase, loadData
15 | import json
16 | if __name__ == '__main__':
17 | cnt = 0
18 | data = loadData('data/train.json')
19 | delete = list()
20 | for sub in data:
21 | map = [0 for i in sub['text']]
22 | length = 0
23 | for ment in sub['mention_data']:
24 | length += len(ment['mention'])
25 | for index in range(int(ment['offset']), int(ment['offset']) + len(ment['mention'])):
26 | map[index] = 1
27 | for i in map:
28 | if i == 1:
29 | length -= 1
30 | if length != 0:
31 | print(sub)
32 | delete.append(sub)
33 | cnt += 1
34 | print("条数:", cnt) # 722
35 | for i in delete:
36 | data.remove(i)
37 | with open('data/train_pre.json', 'w', encoding='utf-8') as file:
38 | for i in data:
39 | file.write(json.dumps(i,ensure_ascii=False) + '\n')
40 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | absl-py==0.7.1
2 | astor==0.8.0
3 | Click==7.0
4 | Flask==1.1.1
5 | gast==0.2.2
6 | google-pasta==0.1.7
7 | grpcio==1.22.0
8 | h5py==2.9.0
9 | itsdangerous==1.1.0
10 | jieba==0.39
11 | Jinja2==2.10.1
12 | Keras==2.2.4
13 | Keras-Applications==1.0.8
14 | keras-bert==0.70.1
15 | keras-contrib==2.0.8
16 | keras-embed-sim==0.7.0
17 | keras-layer-normalization==0.12.0
18 | keras-multi-head==0.20.0
19 | keras-pos-embd==0.11.0
20 | keras-position-wise-feed-forward==0.6.0
21 | Keras-Preprocessing==1.1.0
22 | keras-self-attention==0.41.0
23 | keras-transformer==0.29.0
24 | Markdown==3.1.1
25 | MarkupSafe==1.1.1
26 | numpy==1.16.4
27 | protobuf==3.9.0
28 | PyYAML==5.1.1
29 | scipy==1.3.0
30 | six==1.12.0
31 | tensorboard==1.14.0
32 | tensorflow==1.14.0
33 | tensorflow-estimator==1.14.0
34 | termcolor==1.1.0
35 | tqdm==4.32.2
36 | Werkzeug==0.15.4
37 | wrapt==1.11.2
38 |
--------------------------------------------------------------------------------
/static/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v4.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input,select{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{cursor:pointer}[disabled]{cursor:default}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button:-moz-focusring,input:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
2 |
--------------------------------------------------------------------------------
/static/css/style.css:
--------------------------------------------------------------------------------
1 | /*--------------------
2 | Mixins
3 | --------------------*/
4 | /*--------------------
5 | Body
6 | --------------------*/
7 | *,
8 | *::before,
9 | *::after {
10 | box-sizing: border-box;
11 | }
12 |
13 | html,
14 | body {
15 | height: 100%;
16 | }
17 |
18 | body {
19 | background: -webkit-linear-gradient(315deg, #044f48, #2a7561);
20 | background: linear-gradient(135deg, #044f48, #2a7561);
21 | background-size: cover;
22 | font-family: 'Open Sans', sans-serif;
23 | font-size: 12px;
24 | line-height: 1.3;
25 | overflow: hidden;
26 | }
27 |
28 | .bg {
29 | width: 100%;
30 | height: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 1;
34 | background: url("/static/res/hd2.jpg") no-repeat 0 0;
35 | -webkit-filter: blur(80px);
36 | filter: blur(80px);
37 | -webkit-transform: scale(1.2);
38 | transform: scale(1.2);
39 | }
40 |
41 | /*--------------------
42 | Chat
43 | --------------------*/
44 | .chat {
45 | position: absolute;
46 | top: 50%;
47 | left: 50%;
48 | -webkit-transform: translate(-50%, -50%);
49 | transform: translate(-50%, -50%);
50 | width: 300px;
51 | height: 80vh;
52 | max-height: 500px;
53 | z-index: 2;
54 | overflow: hidden;
55 | box-shadow: 0 5px 30px rgba(0, 0, 0, 0.2);
56 | background: rgba(0, 0, 0, 0.5);
57 | border-radius: 20px;
58 | display: -webkit-box;
59 | display: -webkit-flex;
60 | display: -ms-flexbox;
61 | display: flex;
62 | -webkit-box-pack: justify;
63 | -webkit-justify-content: space-between;
64 | -ms-flex-pack: justify;
65 | justify-content: space-between;
66 | -webkit-box-orient: vertical;
67 | -webkit-box-direction: normal;
68 | -webkit-flex-direction: column;
69 | -ms-flex-direction: column;
70 | flex-direction: column;
71 | }
72 |
73 | /*--------------------
74 | Chat Title
75 | --------------------*/
76 | .chat-title {
77 | -webkit-box-flex: 0;
78 | -webkit-flex: 0 1 45px;
79 | -ms-flex: 0 1 45px;
80 | flex: 0 1 45px;
81 | position: relative;
82 | z-index: 2;
83 | background: rgba(0, 0, 0, 0.2);
84 | color: #fff;
85 | text-transform: uppercase;
86 | text-align: left;
87 | padding: 10px 10px 10px 50px;
88 | }
89 | .chat-title h1, .chat-title h2 {
90 | font-weight: normal;
91 | font-size: 10px;
92 | margin: 0;
93 | padding: 0;
94 | }
95 | .chat-title h2 {
96 | color: rgba(255, 255, 255, 0.5);
97 | font-size: 8px;
98 | letter-spacing: 1px;
99 | }
100 | .chat-title .avatar {
101 | position: absolute;
102 | z-index: 1;
103 | top: 8px;
104 | left: 9px;
105 | border-radius: 30px;
106 | width: 30px;
107 | height: 30px;
108 | overflow: hidden;
109 | margin: 0;
110 | padding: 0;
111 | border: 2px solid rgba(255, 255, 255, 0.24);
112 | }
113 | .chat-title .avatar img {
114 | width: 100%;
115 | height: auto;
116 | }
117 |
118 | /*--------------------
119 | Messages
120 | --------------------*/
121 | .messages {
122 | -webkit-box-flex: 1;
123 | -webkit-flex: 1 1 auto;
124 | -ms-flex: 1 1 auto;
125 | flex: 1 1 auto;
126 | color: rgba(255, 255, 255, 0.5);
127 | overflow: hidden;
128 | position: relative;
129 | width: 100%;
130 | }
131 | .messages .messages-content {
132 | position: absolute;
133 | top: 0;
134 | left: 0;
135 | height: 101%;
136 | width: 100%;
137 | }
138 | .messages .message {
139 | clear: both;
140 | float: left;
141 | padding: 6px 10px 7px;
142 | border-radius: 10px 10px 10px 0;
143 | background: rgba(0, 0, 0, 0.3);
144 | margin: 8px 0;
145 | font-size: 11px;
146 | line-height: 1.4;
147 | margin-left: 35px;
148 | position: relative;
149 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
150 | }
151 | .messages .message .timestamp {
152 | position: absolute;
153 | bottom: -15px;
154 | font-size: 9px;
155 | color: rgba(255, 255, 255, 0.3);
156 | }
157 | .messages .message::before {
158 | content: '';
159 | position: absolute;
160 | bottom: -6px;
161 | border-top: 6px solid rgba(0, 0, 0, 0.3);
162 | left: 0;
163 | border-right: 7px solid transparent;
164 | }
165 | .messages .message .avatar {
166 | position: absolute;
167 | z-index: 1;
168 | bottom: -15px;
169 | left: -35px;
170 | border-radius: 30px;
171 | width: 30px;
172 | height: 30px;
173 | overflow: hidden;
174 | margin: 0;
175 | padding: 0;
176 | border: 2px solid rgba(255, 255, 255, 0.24);
177 | }
178 | .messages .message .avatar img {
179 | width: 100%;
180 | height: auto;
181 | }
182 | .messages .message.message-personal {
183 | float: right;
184 | color: #fff;
185 | text-align: right;
186 | background: -webkit-linear-gradient(330deg, #248A52, #257287);
187 | background: linear-gradient(120deg, #248A52, #257287);
188 | border-radius: 10px 10px 0 10px;
189 | }
190 | .messages .message.message-personal::before {
191 | left: auto;
192 | right: 0;
193 | border-right: none;
194 | border-left: 5px solid transparent;
195 | border-top: 4px solid #257287;
196 | bottom: -4px;
197 | }
198 | .messages .message:last-child {
199 | margin-bottom: 30px;
200 | }
201 | .messages .message.new {
202 | -webkit-transform: scale(0);
203 | transform: scale(0);
204 | -webkit-transform-origin: 0 0;
205 | transform-origin: 0 0;
206 | -webkit-animation: bounce 500ms linear both;
207 | animation: bounce 500ms linear both;
208 | }
209 | .messages .message.loading::before {
210 | position: absolute;
211 | top: 50%;
212 | left: 50%;
213 | -webkit-transform: translate(-50%, -50%);
214 | transform: translate(-50%, -50%);
215 | content: '';
216 | display: block;
217 | width: 3px;
218 | height: 3px;
219 | border-radius: 50%;
220 | background: rgba(255, 255, 255, 0.5);
221 | z-index: 2;
222 | margin-top: 4px;
223 | -webkit-animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
224 | animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
225 | border: none;
226 | -webkit-animation-delay: .15s;
227 | animation-delay: .15s;
228 | }
229 | .messages .message.loading span {
230 | display: block;
231 | font-size: 0;
232 | width: 20px;
233 | height: 10px;
234 | position: relative;
235 | }
236 | .messages .message.loading span::before {
237 | position: absolute;
238 | top: 50%;
239 | left: 50%;
240 | -webkit-transform: translate(-50%, -50%);
241 | transform: translate(-50%, -50%);
242 | content: '';
243 | display: block;
244 | width: 3px;
245 | height: 3px;
246 | border-radius: 50%;
247 | background: rgba(255, 255, 255, 0.5);
248 | z-index: 2;
249 | margin-top: 4px;
250 | -webkit-animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
251 | animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
252 | margin-left: -7px;
253 | }
254 | .messages .message.loading span::after {
255 | position: absolute;
256 | top: 50%;
257 | left: 50%;
258 | -webkit-transform: translate(-50%, -50%);
259 | transform: translate(-50%, -50%);
260 | content: '';
261 | display: block;
262 | width: 3px;
263 | height: 3px;
264 | border-radius: 50%;
265 | background: rgba(255, 255, 255, 0.5);
266 | z-index: 2;
267 | margin-top: 4px;
268 | -webkit-animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
269 | animation: ball 0.45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
270 | margin-left: 7px;
271 | -webkit-animation-delay: .3s;
272 | animation-delay: .3s;
273 | }
274 |
275 | /*--------------------
276 | Message Box
277 | --------------------*/
278 | .message-box {
279 | -webkit-box-flex: 0;
280 | -webkit-flex: 0 1 40px;
281 | -ms-flex: 0 1 40px;
282 | flex: 0 1 40px;
283 | width: 100%;
284 | background: rgba(0, 0, 0, 0.3);
285 | padding: 10px;
286 | position: relative;
287 | }
288 | .message-box .message-input {
289 | background: none;
290 | border: none;
291 | outline: none !important;
292 | resize: none;
293 | color: rgba(255, 255, 255, 0.7);
294 | font-size: 11px;
295 | height: 17px;
296 | margin: 0;
297 | padding-right: 20px;
298 | width: 265px;
299 | }
300 | .message-box textarea:focus:-webkit-placeholder {
301 | color: transparent;
302 | }
303 | .message-box .message-submit {
304 | position: absolute;
305 | z-index: 1;
306 | top: 9px;
307 | right: 10px;
308 | color: #fff;
309 | border: none;
310 | background: #248A52;
311 | font-size: 10px;
312 | text-transform: uppercase;
313 | line-height: 1;
314 | padding: 6px 10px;
315 | border-radius: 10px;
316 | outline: none !important;
317 | -webkit-transition: background .2s ease;
318 | transition: background .2s ease;
319 | }
320 | .message-box .message-submit:hover {
321 | background: #1D7745;
322 | }
323 |
324 | /*--------------------
325 | Custom Srollbar
326 | --------------------*/
327 | .mCSB_scrollTools {
328 | margin: 1px -3px 1px 0;
329 | opacity: 0;
330 | }
331 |
332 | .mCSB_inside > .mCSB_container {
333 | margin-right: 0px;
334 | padding: 0 10px;
335 | }
336 |
337 | .mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar {
338 | background-color: rgba(0, 0, 0, 0.5) !important;
339 | }
340 |
341 | /*--------------------
342 | Bounce
343 | --------------------*/
344 | @-webkit-keyframes bounce {
345 | 0% {
346 | -webkit-transform: matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
347 | transform: matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
348 | }
349 | 4.7% {
350 | -webkit-transform: matrix3d(0.45, 0, 0, 0, 0, 0.45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
351 | transform: matrix3d(0.45, 0, 0, 0, 0, 0.45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
352 | }
353 | 9.41% {
354 | -webkit-transform: matrix3d(0.883, 0, 0, 0, 0, 0.883, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
355 | transform: matrix3d(0.883, 0, 0, 0, 0, 0.883, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
356 | }
357 | 14.11% {
358 | -webkit-transform: matrix3d(1.141, 0, 0, 0, 0, 1.141, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
359 | transform: matrix3d(1.141, 0, 0, 0, 0, 1.141, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
360 | }
361 | 18.72% {
362 | -webkit-transform: matrix3d(1.212, 0, 0, 0, 0, 1.212, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
363 | transform: matrix3d(1.212, 0, 0, 0, 0, 1.212, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
364 | }
365 | 24.32% {
366 | -webkit-transform: matrix3d(1.151, 0, 0, 0, 0, 1.151, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
367 | transform: matrix3d(1.151, 0, 0, 0, 0, 1.151, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
368 | }
369 | 29.93% {
370 | -webkit-transform: matrix3d(1.048, 0, 0, 0, 0, 1.048, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
371 | transform: matrix3d(1.048, 0, 0, 0, 0, 1.048, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
372 | }
373 | 35.54% {
374 | -webkit-transform: matrix3d(0.979, 0, 0, 0, 0, 0.979, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
375 | transform: matrix3d(0.979, 0, 0, 0, 0, 0.979, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
376 | }
377 | 41.04% {
378 | -webkit-transform: matrix3d(0.961, 0, 0, 0, 0, 0.961, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
379 | transform: matrix3d(0.961, 0, 0, 0, 0, 0.961, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
380 | }
381 | 52.15% {
382 | -webkit-transform: matrix3d(0.991, 0, 0, 0, 0, 0.991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
383 | transform: matrix3d(0.991, 0, 0, 0, 0, 0.991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
384 | }
385 | 63.26% {
386 | -webkit-transform: matrix3d(1.007, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
387 | transform: matrix3d(1.007, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
388 | }
389 | 85.49% {
390 | -webkit-transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
391 | transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
392 | }
393 | 100% {
394 | -webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
395 | transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
396 | }
397 | }
398 | @keyframes bounce {
399 | 0% {
400 | -webkit-transform: matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
401 | transform: matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
402 | }
403 | 4.7% {
404 | -webkit-transform: matrix3d(0.45, 0, 0, 0, 0, 0.45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
405 | transform: matrix3d(0.45, 0, 0, 0, 0, 0.45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
406 | }
407 | 9.41% {
408 | -webkit-transform: matrix3d(0.883, 0, 0, 0, 0, 0.883, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
409 | transform: matrix3d(0.883, 0, 0, 0, 0, 0.883, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
410 | }
411 | 14.11% {
412 | -webkit-transform: matrix3d(1.141, 0, 0, 0, 0, 1.141, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
413 | transform: matrix3d(1.141, 0, 0, 0, 0, 1.141, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
414 | }
415 | 18.72% {
416 | -webkit-transform: matrix3d(1.212, 0, 0, 0, 0, 1.212, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
417 | transform: matrix3d(1.212, 0, 0, 0, 0, 1.212, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
418 | }
419 | 24.32% {
420 | -webkit-transform: matrix3d(1.151, 0, 0, 0, 0, 1.151, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
421 | transform: matrix3d(1.151, 0, 0, 0, 0, 1.151, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
422 | }
423 | 29.93% {
424 | -webkit-transform: matrix3d(1.048, 0, 0, 0, 0, 1.048, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
425 | transform: matrix3d(1.048, 0, 0, 0, 0, 1.048, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
426 | }
427 | 35.54% {
428 | -webkit-transform: matrix3d(0.979, 0, 0, 0, 0, 0.979, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
429 | transform: matrix3d(0.979, 0, 0, 0, 0, 0.979, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
430 | }
431 | 41.04% {
432 | -webkit-transform: matrix3d(0.961, 0, 0, 0, 0, 0.961, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
433 | transform: matrix3d(0.961, 0, 0, 0, 0, 0.961, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
434 | }
435 | 52.15% {
436 | -webkit-transform: matrix3d(0.991, 0, 0, 0, 0, 0.991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
437 | transform: matrix3d(0.991, 0, 0, 0, 0, 0.991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
438 | }
439 | 63.26% {
440 | -webkit-transform: matrix3d(1.007, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
441 | transform: matrix3d(1.007, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
442 | }
443 | 85.49% {
444 | -webkit-transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
445 | transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
446 | }
447 | 100% {
448 | -webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
449 | transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
450 | }
451 | }
452 | @-webkit-keyframes ball {
453 | from {
454 | -webkit-transform: translateY(0) scaleY(0.8);
455 | transform: translateY(0) scaleY(0.8);
456 | }
457 | to {
458 | -webkit-transform: translateY(-10px);
459 | transform: translateY(-10px);
460 | }
461 | }
462 | @keyframes ball {
463 | from {
464 | -webkit-transform: translateY(0) scaleY(0.8);
465 | transform: translateY(0) scaleY(0.8);
466 | }
467 | to {
468 | -webkit-transform: translateY(-10px);
469 | transform: translateY(-10px);
470 | }
471 | }
472 |
--------------------------------------------------------------------------------
/static/js/index.js:
--------------------------------------------------------------------------------
1 | var $messages = $('.messages-content'),
2 | d, h, m,
3 | i = 0;
4 |
5 | $(window).load(function() {
6 | $messages.mCustomScrollbar();
7 | setTimeout(function() {
8 | //fakeMessage();
9 | }, 100);
10 | });
11 |
12 | function updateScrollbar() {
13 | $messages.mCustomScrollbar("update").mCustomScrollbar('scrollTo', 'bottom', {
14 | scrollInertia: 10,
15 | timeout: 0
16 | });
17 | }
18 |
19 | function setDate(){
20 | d = new Date()
21 | if (m != d.getMinutes()) {
22 | m = d.getMinutes();
23 | $('
' + d.getHours() + ':' + m + '
').appendTo($('.message:last'));
24 | }
25 | }
26 |
27 | function insertMessage() {
28 | msg = $('.message-input').val();
29 | if ($.trim(msg) == '') {
30 | return false;
31 | }
32 | $('' + msg + '
').appendTo($('.mCSB_container')).addClass('new');
33 | setDate();
34 | $('.message-input').val(null);
35 | updateScrollbar();
36 | interact(msg);
37 | setTimeout(function() {
38 | //fakeMessage();
39 | }, 1000 + (Math.random() * 20) * 100);
40 | }
41 |
42 | $('.message-submit').click(function() {
43 | insertMessage();
44 | });
45 |
46 | $(window).on('keydown', function(e) {
47 | if (e.which == 13) {
48 | insertMessage();
49 | return false;
50 | }
51 | })
52 |
53 |
54 | function interact(message){
55 | // loading message
56 | $('').appendTo($('.mCSB_container'));
57 | // make a POST request [ajax call]
58 | $.post('/message', {
59 | msg: message,
60 | }).done(function(reply) {
61 | // Message Received
62 | // remove loading meassage
63 | $('.message.loading').remove();
64 | // Add message to chatbox
65 | $('
' + reply['text'] + '
').appendTo($('.mCSB_container')).addClass('new');
66 | setDate();
67 | updateScrollbar();
68 |
69 | }).fail(function() {
70 | alert('error calling function');
71 | });
72 | }
73 |
--------------------------------------------------------------------------------
/static/js/jquery.mCustomScrollbar.concat.min.js:
--------------------------------------------------------------------------------
1 | /* == jquery mousewheel plugin == Version: 3.1.13, License: MIT License (MIT) */
2 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
3 | /* == malihu jquery custom scrollbar plugin == Version: 3.1.3, License: MIT License (MIT) */
4 | !function(e){"undefined"!=typeof module&&module.exports?module.exports=e:e(jQuery,window,document)}(function(e){!function(t){var o="function"==typeof define&&define.amd,a="undefined"!=typeof module&&module.exports,n="https:"==document.location.protocol?"https:":"http:",i="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";o||(a?require("jquery-mousewheel")(e):e.event.special.mousewheel||e("head").append(decodeURI("%3Cscript src="+n+"//"+i+"%3E%3C/script%3E"))),t()}(function(){var t,o="mCustomScrollbar",a="mCS",n=".mCustomScrollbar",i={setTop:0,setLeft:0,axis:"y",scrollbarPosition:"inside",scrollInertia:950,autoDraggerLength:!0,alwaysShowScrollbar:0,snapOffset:0,mouseWheel:{enable:!0,scrollAmount:"auto",axis:"y",deltaFactor:"auto",disableOver:["select","option","keygen","datalist","textarea"]},scrollButtons:{scrollType:"stepless",scrollAmount:"auto"},keyboard:{enable:!0,scrollType:"stepless",scrollAmount:"auto"},contentTouchScroll:25,documentTouchScroll:!0,advanced:{autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",updateOnContentResize:!0,updateOnImageLoad:"auto",autoUpdateTimeout:60},theme:"light",callbacks:{onTotalScrollOffset:0,onTotalScrollBackOffset:0,alwaysTriggerOffsets:!0}},r=0,l={},s=window.attachEvent&&!window.addEventListener?1:0,c=!1,d=["mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar","mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer","mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"],u={init:function(t){var t=e.extend(!0,{},i,t),o=f.call(this);if(t.live){var s=t.liveSelector||this.selector||n,c=e(s);if("off"===t.live)return void m(s);l[s]=setTimeout(function(){c.mCustomScrollbar(t),"once"===t.live&&c.length&&m(s)},500)}else m(s);return t.setWidth=t.set_width?t.set_width:t.setWidth,t.setHeight=t.set_height?t.set_height:t.setHeight,t.axis=t.horizontalScroll?"x":p(t.axis),t.scrollInertia=t.scrollInertia>0&&t.scrollInertia<17?17:t.scrollInertia,"object"!=typeof t.mouseWheel&&1==t.mouseWheel&&(t.mouseWheel={enable:!0,scrollAmount:"auto",axis:"y",preventDefault:!1,deltaFactor:"auto",normalizeDelta:!1,invert:!1}),t.mouseWheel.scrollAmount=t.mouseWheelPixels?t.mouseWheelPixels:t.mouseWheel.scrollAmount,t.mouseWheel.normalizeDelta=t.advanced.normalizeMouseWheelDelta?t.advanced.normalizeMouseWheelDelta:t.mouseWheel.normalizeDelta,t.scrollButtons.scrollType=g(t.scrollButtons.scrollType),h(t),e(o).each(function(){var o=e(this);if(!o.data(a)){o.data(a,{idx:++r,opt:t,scrollRatio:{y:null,x:null},overflowed:null,contentReset:{y:null,x:null},bindEvents:!1,tweenRunning:!1,sequential:{},langDir:o.css("direction"),cbOffsets:null,trigger:null,poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}});var n=o.data(a),i=n.opt,l=o.data("mcs-axis"),s=o.data("mcs-scrollbar-position"),c=o.data("mcs-theme");l&&(i.axis=l),s&&(i.scrollbarPosition=s),c&&(i.theme=c,h(i)),v.call(this),n&&i.callbacks.onCreate&&"function"==typeof i.callbacks.onCreate&&i.callbacks.onCreate.call(this),e("#mCSB_"+n.idx+"_container img:not(."+d[2]+")").addClass(d[2]),u.update.call(null,o)}})},update:function(t,o){var n=t||f.call(this);return e(n).each(function(){var t=e(this);if(t.data(a)){var n=t.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container"),l=e("#mCSB_"+n.idx),s=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];if(!r.length)return;n.tweenRunning&&N(t),o&&n&&i.callbacks.onBeforeUpdate&&"function"==typeof i.callbacks.onBeforeUpdate&&i.callbacks.onBeforeUpdate.call(this),t.hasClass(d[3])&&t.removeClass(d[3]),t.hasClass(d[4])&&t.removeClass(d[4]),l.css("max-height","none"),l.height()!==t.height()&&l.css("max-height",t.height()),_.call(this),"y"===i.axis||i.advanced.autoExpandHorizontalScroll||r.css("width",x(r)),n.overflowed=y.call(this),M.call(this),i.autoDraggerLength&&S.call(this),b.call(this),T.call(this);var c=[Math.abs(r[0].offsetTop),Math.abs(r[0].offsetLeft)];"x"!==i.axis&&(n.overflowed[0]?s[0].height()>s[0].parent().height()?B.call(this):(V(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}),n.contentReset.y=null):(B.call(this),"y"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[1]&&V(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}))),"y"!==i.axis&&(n.overflowed[1]?s[1].width()>s[1].parent().width()?B.call(this):(V(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}),n.contentReset.x=null):(B.call(this),"x"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[0]&&V(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}))),o&&n&&(2===o&&i.callbacks.onImageLoad&&"function"==typeof i.callbacks.onImageLoad?i.callbacks.onImageLoad.call(this):3===o&&i.callbacks.onSelectorChange&&"function"==typeof i.callbacks.onSelectorChange?i.callbacks.onSelectorChange.call(this):i.callbacks.onUpdate&&"function"==typeof i.callbacks.onUpdate&&i.callbacks.onUpdate.call(this)),X.call(this)}})},scrollTo:function(t,o){if("undefined"!=typeof t&&null!=t){var n=f.call(this);return e(n).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l={trigger:"external",scrollInertia:r.scrollInertia,scrollEasing:"mcsEaseInOut",moveDragger:!1,timeout:60,callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},s=e.extend(!0,{},l,o),c=q.call(this,t),d=s.scrollInertia>0&&s.scrollInertia<17?17:s.scrollInertia;c[0]=Y.call(this,c[0],"y"),c[1]=Y.call(this,c[1],"x"),s.moveDragger&&(c[0]*=i.scrollRatio.y,c[1]*=i.scrollRatio.x),s.dur=oe()?0:d,setTimeout(function(){null!==c[0]&&"undefined"!=typeof c[0]&&"x"!==r.axis&&i.overflowed[0]&&(s.dir="y",s.overwrite="all",V(n,c[0].toString(),s)),null!==c[1]&&"undefined"!=typeof c[1]&&"y"!==r.axis&&i.overflowed[1]&&(s.dir="x",s.overwrite="none",V(n,c[1].toString(),s))},s.timeout)}})}},stop:function(){var t=f.call(this);return e(t).each(function(){var t=e(this);t.data(a)&&N(t)})},disable:function(t){var o=f.call(this);return e(o).each(function(){var o=e(this);if(o.data(a)){{o.data(a)}X.call(this,"remove"),k.call(this),t&&B.call(this),M.call(this,!0),o.addClass(d[3])}})},destroy:function(){var t=f.call(this);return e(t).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx),s=e("#mCSB_"+i.idx+"_container"),c=e(".mCSB_"+i.idx+"_scrollbar");r.live&&m(r.liveSelector||e(t).selector),X.call(this,"remove"),k.call(this),B.call(this),n.removeData(a),K(this,"mcs"),c.remove(),s.find("img."+d[2]).removeClass(d[2]),l.replaceWith(s.contents()),n.removeClass(o+" _"+a+"_"+i.idx+" "+d[6]+" "+d[7]+" "+d[5]+" "+d[3]).addClass(d[4])}})}},f=function(){return"object"!=typeof e(this)||e(this).length<1?n:this},h=function(t){var o=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],a=["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"],n=["minimal","minimal-dark"],i=["minimal","minimal-dark"],r=["minimal","minimal-dark"];t.autoDraggerLength=e.inArray(t.theme,o)>-1?!1:t.autoDraggerLength,t.autoExpandScrollbar=e.inArray(t.theme,a)>-1?!1:t.autoExpandScrollbar,t.scrollButtons.enable=e.inArray(t.theme,n)>-1?!1:t.scrollButtons.enable,t.autoHideScrollbar=e.inArray(t.theme,i)>-1?!0:t.autoHideScrollbar,t.scrollbarPosition=e.inArray(t.theme,r)>-1?"outside":t.scrollbarPosition},m=function(e){l[e]&&(clearTimeout(l[e]),K(l,e))},p=function(e){return"yx"===e||"xy"===e||"auto"===e?"yx":"x"===e||"horizontal"===e?"x":"y"},g=function(e){return"stepped"===e||"pixels"===e||"step"===e||"click"===e?"stepped":"stepless"},v=function(){var t=e(this),n=t.data(a),i=n.opt,r=i.autoExpandScrollbar?" "+d[1]+"_expand":"",l=["",""],s="yx"===i.axis?"mCSB_vertical_horizontal":"x"===i.axis?"mCSB_horizontal":"mCSB_vertical",c="yx"===i.axis?l[0]+l[1]:"x"===i.axis?l[1]:l[0],u="yx"===i.axis?"":"",f=i.autoHideScrollbar?" "+d[6]:"",h="x"!==i.axis&&"rtl"===n.langDir?" "+d[7]:"";i.setWidth&&t.css("width",i.setWidth),i.setHeight&&t.css("height",i.setHeight),i.setLeft="y"!==i.axis&&"rtl"===n.langDir?"989999px":i.setLeft,t.addClass(o+" _"+a+"_"+n.idx+f+h).wrapInner("");var m=e("#mCSB_"+n.idx),p=e("#mCSB_"+n.idx+"_container");"y"===i.axis||i.advanced.autoExpandHorizontalScroll||p.css("width",x(p)),"outside"===i.scrollbarPosition?("static"===t.css("position")&&t.css("position","relative"),t.css("overflow","visible"),m.addClass("mCSB_outside").after(c)):(m.addClass("mCSB_inside").append(c),p.wrap(u)),w.call(this);var g=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];g[0].css("min-height",g[0].height()),g[1].css("min-width",g[1].width())},x=function(t){var o=[t[0].scrollWidth,Math.max.apply(Math,t.children().map(function(){return e(this).outerWidth(!0)}).get())],a=t.parent().width();return o[0]>a?o[0]:o[1]>a?o[1]:"100%"},_=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx+"_container");if(n.advanced.autoExpandHorizontalScroll&&"y"!==n.axis){i.css({width:"auto","min-width":0,"overflow-x":"scroll"});var r=Math.ceil(i[0].scrollWidth);3===n.advanced.autoExpandHorizontalScroll||2!==n.advanced.autoExpandHorizontalScroll&&r>i.parent().width()?i.css({width:r,"min-width":"100%","overflow-x":"inherit"}):i.css({"overflow-x":"inherit",position:"absolute"}).wrap("").css({width:Math.ceil(i[0].getBoundingClientRect().right+.4)-Math.floor(i[0].getBoundingClientRect().left),"min-width":"100%",position:"relative"}).unwrap()}},w=function(){var t=e(this),o=t.data(a),n=o.opt,i=e(".mCSB_"+o.idx+"_scrollbar:first"),r=ee(n.scrollButtons.tabindex)?"tabindex='"+n.scrollButtons.tabindex+"'":"",l=["","","",""],s=["x"===n.axis?l[2]:l[0],"x"===n.axis?l[3]:l[1],l[2],l[3]];n.scrollButtons.enable&&i.prepend(s[0]).append(s[1]).next(".mCSB_scrollTools").prepend(s[2]).append(s[3])},S=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[n.height()/i.outerHeight(!1),n.width()/i.outerWidth(!1)],c=[parseInt(r[0].css("min-height")),Math.round(l[0]*r[0].parent().height()),parseInt(r[1].css("min-width")),Math.round(l[1]*r[1].parent().width())],d=s&&c[1]r&&(r=s),c>l&&(l=c),[r>n.height(),l>n.width()]},B=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx),r=e("#mCSB_"+o.idx+"_container"),l=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")];if(N(t),("x"!==n.axis&&!o.overflowed[0]||"y"===n.axis&&o.overflowed[0])&&(l[0].add(r).css("top",0),V(t,"_resetY")),"y"!==n.axis&&!o.overflowed[1]||"x"===n.axis&&o.overflowed[1]){var s=dx=0;"rtl"===o.langDir&&(s=i.width()-r.outerWidth(!1),dx=Math.abs(s/o.scrollRatio.x)),r.css("left",s),l[1].css("left",dx),V(t,"_resetX")}},T=function(){function t(){r=setTimeout(function(){e.event.special.mousewheel?(clearTimeout(r),R.call(o[0])):t()},100)}var o=e(this),n=o.data(a),i=n.opt;if(!n.bindEvents){if(I.call(this),i.contentTouchScroll&&D.call(this),E.call(this),i.mouseWheel.enable){var r;t()}L.call(this),P.call(this),i.advanced.autoScrollOnFocus&&z.call(this),i.scrollButtons.enable&&H.call(this),i.keyboard.enable&&U.call(this),n.bindEvents=!0}},k=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=".mCSB_"+o.idx+"_scrollbar",l=e("#mCSB_"+o.idx+",#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,"+r+" ."+d[12]+",#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal,"+r+">a"),s=e("#mCSB_"+o.idx+"_container");n.advanced.releaseDraggableSelectors&&l.add(e(n.advanced.releaseDraggableSelectors)),n.advanced.extraDraggableSelectors&&l.add(e(n.advanced.extraDraggableSelectors)),o.bindEvents&&(e(document).add(e(!W()||top.document)).unbind("."+i),l.each(function(){e(this).unbind("."+i)}),clearTimeout(t[0]._focusTimeout),K(t[0],"_focusTimeout"),clearTimeout(o.sequential.step),K(o.sequential,"step"),clearTimeout(s[0].onCompleteTimeout),K(s[0],"onCompleteTimeout"),o.bindEvents=!1)},M=function(t){var o=e(this),n=o.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container_wrapper"),l=r.length?r:e("#mCSB_"+n.idx+"_container"),s=[e("#mCSB_"+n.idx+"_scrollbar_vertical"),e("#mCSB_"+n.idx+"_scrollbar_horizontal")],c=[s[0].find(".mCSB_dragger"),s[1].find(".mCSB_dragger")];"x"!==i.axis&&(n.overflowed[0]&&!t?(s[0].add(c[0]).add(s[0].children("a")).css("display","block"),l.removeClass(d[8]+" "+d[10])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[0].css("display","none"),l.removeClass(d[10])):(s[0].css("display","none"),l.addClass(d[10])),l.addClass(d[8]))),"y"!==i.axis&&(n.overflowed[1]&&!t?(s[1].add(c[1]).add(s[1].children("a")).css("display","block"),l.removeClass(d[9]+" "+d[11])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[1].css("display","none"),l.removeClass(d[11])):(s[1].css("display","none"),l.addClass(d[11])),l.addClass(d[9]))),n.overflowed[0]||n.overflowed[1]?o.removeClass(d[5]):o.addClass(d[5])},O=function(t){var o=t.type,a=t.target.ownerDocument!==document?[e(frameElement).offset().top,e(frameElement).offset().left]:null,n=W()&&t.target.ownerDocument!==top.document?[e(t.view.frameElement).offset().top,e(t.view.frameElement).offset().left]:[0,0];switch(o){case"pointerdown":case"MSPointerDown":case"pointermove":case"MSPointerMove":case"pointerup":case"MSPointerUp":return a?[t.originalEvent.pageY-a[0]+n[0],t.originalEvent.pageX-a[1]+n[1],!1]:[t.originalEvent.pageY,t.originalEvent.pageX,!1];case"touchstart":case"touchmove":case"touchend":var i=t.originalEvent.touches[0]||t.originalEvent.changedTouches[0],r=t.originalEvent.touches.length||t.originalEvent.changedTouches.length;return t.target.ownerDocument!==document?[i.screenY,i.screenX,r>1]:[i.pageY,i.pageX,r>1];default:return a?[t.pageY-a[0]+n[0],t.pageX-a[1]+n[1],!1]:[t.pageY,t.pageX,!1]}},I=function(){function t(e){var t=m.find("iframe");if(t.length){var o=e?"auto":"none";t.css("pointer-events",o)}}function o(e,t,o,a){if(m[0].idleTimer=u.scrollInertia<233?250:0,n.attr("id")===h[1])var i="x",r=(n[0].offsetLeft-t+a)*d.scrollRatio.x;else var i="y",r=(n[0].offsetTop-e+o)*d.scrollRatio.y;V(l,r.toString(),{dir:i,drag:!0})}var n,i,r,l=e(this),d=l.data(a),u=d.opt,f=a+"_"+d.idx,h=["mCSB_"+d.idx+"_dragger_vertical","mCSB_"+d.idx+"_dragger_horizontal"],m=e("#mCSB_"+d.idx+"_container"),p=e("#"+h[0]+",#"+h[1]),g=u.advanced.releaseDraggableSelectors?p.add(e(u.advanced.releaseDraggableSelectors)):p,v=u.advanced.extraDraggableSelectors?e(!W()||top.document).add(e(u.advanced.extraDraggableSelectors)):e(!W()||top.document);p.bind("mousedown."+f+" touchstart."+f+" pointerdown."+f+" MSPointerDown."+f,function(o){if(o.stopImmediatePropagation(),o.preventDefault(),Z(o)){c=!0,s&&(document.onselectstart=function(){return!1}),t(!1),N(l),n=e(this);var a=n.offset(),d=O(o)[0]-a.top,f=O(o)[1]-a.left,h=n.height()+a.top,m=n.width()+a.left;h>d&&d>0&&m>f&&f>0&&(i=d,r=f),C(n,"active",u.autoExpandScrollbar)}}).bind("touchmove."+f,function(e){e.stopImmediatePropagation(),e.preventDefault();var t=n.offset(),a=O(e)[0]-t.top,l=O(e)[1]-t.left;o(i,r,a,l)}),e(document).add(v).bind("mousemove."+f+" pointermove."+f+" MSPointerMove."+f,function(e){if(n){var t=n.offset(),a=O(e)[0]-t.top,l=O(e)[1]-t.left;if(i===a&&r===l)return;o(i,r,a,l)}}).add(g).bind("mouseup."+f+" touchend."+f+" pointerup."+f+" MSPointerUp."+f,function(e){n&&(C(n,"active",u.autoExpandScrollbar),n=null),c=!1,s&&(document.onselectstart=null),t(!0)})},D=function(){function o(e){if(!$(e)||c||O(e)[2])return void(t=0);t=1,b=0,C=0,d=1,y.removeClass("mCS_touch_action");var o=I.offset();u=O(e)[0]-o.top,f=O(e)[1]-o.left,z=[O(e)[0],O(e)[1]]}function n(e){if($(e)&&!c&&!O(e)[2]&&(T.documentTouchScroll||e.preventDefault(),e.stopImmediatePropagation(),(!C||b)&&d)){g=G();var t=M.offset(),o=O(e)[0]-t.top,a=O(e)[1]-t.left,n="mcsLinearOut";if(E.push(o),R.push(a),z[2]=Math.abs(O(e)[0]-z[0]),z[3]=Math.abs(O(e)[1]-z[1]),B.overflowed[0])var i=D[0].parent().height()-D[0].height(),r=u-o>0&&o-u>-(i*B.scrollRatio.y)&&(2*z[3]0&&a-f>-(l*B.scrollRatio.x)&&(2*z[2]30)){_=1e3/(v-p);var n="mcsEaseOut",i=2.5>_,r=i?[E[E.length-2],R[R.length-2]]:[0,0];x=i?[o-r[0],a-r[1]]:[o-h,a-m];var u=[Math.abs(x[0]),Math.abs(x[1])];_=i?[Math.abs(x[0]/4),Math.abs(x[1]/4)]:[_,_];var f=[Math.abs(I[0].offsetTop)-x[0]*l(u[0]/_[0],_[0]),Math.abs(I[0].offsetLeft)-x[1]*l(u[1]/_[1],_[1])];w="yx"===T.axis?[f[0],f[1]]:"x"===T.axis?[null,f[1]]:[f[0],null],S=[4*u[0]+T.scrollInertia,4*u[1]+T.scrollInertia];var y=parseInt(T.contentTouchScroll)||0;w[0]=u[0]>y?w[0]:0,w[1]=u[1]>y?w[1]:0,B.overflowed[0]&&s(w[0],S[0],n,"y",L,!1),B.overflowed[1]&&s(w[1],S[1],n,"x",L,!1)}}}function l(e,t){var o=[1.5*t,2*t,t/1.5,t/2];return e>90?t>4?o[0]:o[3]:e>60?t>3?o[3]:o[2]:e>30?t>8?o[1]:t>6?o[0]:t>4?t:o[2]:t>8?t:o[3]}function s(e,t,o,a,n,i){e&&V(y,e.toString(),{dur:t,scrollEasing:o,dir:a,overwrite:n,drag:i})}var d,u,f,h,m,p,g,v,x,_,w,S,b,C,y=e(this),B=y.data(a),T=B.opt,k=a+"_"+B.idx,M=e("#mCSB_"+B.idx),I=e("#mCSB_"+B.idx+"_container"),D=[e("#mCSB_"+B.idx+"_dragger_vertical"),e("#mCSB_"+B.idx+"_dragger_horizontal")],E=[],R=[],A=0,L="yx"===T.axis?"none":"all",z=[],P=I.find("iframe"),H=["touchstart."+k+" pointerdown."+k+" MSPointerDown."+k,"touchmove."+k+" pointermove."+k+" MSPointerMove."+k,"touchend."+k+" pointerup."+k+" MSPointerUp."+k],U=void 0!==document.body.style.touchAction;I.bind(H[0],function(e){o(e)}).bind(H[1],function(e){n(e)}),M.bind(H[0],function(e){i(e)}).bind(H[2],function(e){r(e)}),P.length&&P.each(function(){e(this).load(function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind(H[0],function(e){o(e),i(e)}).bind(H[1],function(e){n(e)}).bind(H[2],function(e){r(e)})})})},E=function(){function o(){return window.getSelection?window.getSelection().toString():document.selection&&"Control"!=document.selection.type?document.selection.createRange().text:0}function n(e,t,o){d.type=o&&i?"stepped":"stepless",d.scrollAmount=10,F(r,e,t,"mcsLinearOut",o?60:null)}var i,r=e(this),l=r.data(a),s=l.opt,d=l.sequential,u=a+"_"+l.idx,f=e("#mCSB_"+l.idx+"_container"),h=f.parent();f.bind("mousedown."+u,function(e){t||i||(i=1,c=!0)}).add(document).bind("mousemove."+u,function(e){if(!t&&i&&o()){var a=f.offset(),r=O(e)[0]-a.top+f[0].offsetTop,c=O(e)[1]-a.left+f[0].offsetLeft;r>0&&r0&&cr?n("on",38):r>h.height()&&n("on",40)),"y"!==s.axis&&l.overflowed[1]&&(0>c?n("on",37):c>h.width()&&n("on",39)))}}).bind("mouseup."+u+" dragend."+u,function(e){t||(i&&(i=0,n("off",null)),c=!1)})},R=function(){function t(t,a){if(N(o),!A(o,t.target)){var r="auto"!==i.mouseWheel.deltaFactor?parseInt(i.mouseWheel.deltaFactor):s&&t.deltaFactor<100?100:t.deltaFactor||100,d=i.scrollInertia;if("x"===i.axis||"x"===i.mouseWheel.axis)var u="x",f=[Math.round(r*n.scrollRatio.x),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.width()?.9*l.width():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetLeft),p=c[1][0].offsetLeft,g=c[1].parent().width()-c[1].width(),v=t.deltaX||t.deltaY||a;else var u="y",f=[Math.round(r*n.scrollRatio.y),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.height()?.9*l.height():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetTop),p=c[0][0].offsetTop,g=c[0].parent().height()-c[0].height(),v=t.deltaY||a;"y"===u&&!n.overflowed[0]||"x"===u&&!n.overflowed[1]||((i.mouseWheel.invert||t.webkitDirectionInvertedFromDevice)&&(v=-v),i.mouseWheel.normalizeDelta&&(v=0>v?-1:1),(v>0&&0!==p||0>v&&p!==g||i.mouseWheel.preventDefault)&&(t.stopImmediatePropagation(),t.preventDefault()),t.deltaFactor<2&&!i.mouseWheel.normalizeDelta&&(h=t.deltaFactor,d=17),V(o,(m-v*h).toString(),{dir:u,dur:d}))}}if(e(this).data(a)){var o=e(this),n=o.data(a),i=n.opt,r=a+"_"+n.idx,l=e("#mCSB_"+n.idx),c=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")],d=e("#mCSB_"+n.idx+"_container").find("iframe");d.length&&d.each(function(){e(this).load(function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind("mousewheel."+r,function(e,o){t(e,o)})})}),l.bind("mousewheel."+r,function(e,o){t(e,o)})}},W=function(e){var t=null;if(e){try{var o=e.contentDocument||e.contentWindow.document;t=o.body.innerHTML}catch(a){}return null!==t}try{var o=top.document;t=o.body.innerHTML}catch(a){}return null!==t},A=function(t,o){var n=o.nodeName.toLowerCase(),i=t.data(a).opt.mouseWheel.disableOver,r=["select","textarea"];return e.inArray(n,i)>-1&&!(e.inArray(n,r)>-1&&!e(o).is(":focus"))},L=function(){var t,o=e(this),n=o.data(a),i=a+"_"+n.idx,r=e("#mCSB_"+n.idx+"_container"),l=r.parent(),s=e(".mCSB_"+n.idx+"_scrollbar ."+d[12]);s.bind("mousedown."+i+" touchstart."+i+" pointerdown."+i+" MSPointerDown."+i,function(o){c=!0,e(o.target).hasClass("mCSB_dragger")||(t=1)}).bind("touchend."+i+" pointerup."+i+" MSPointerUp."+i,function(e){c=!1}).bind("click."+i,function(a){if(t&&(t=0,e(a.target).hasClass(d[12])||e(a.target).hasClass("mCSB_draggerRail"))){N(o);var i=e(this),s=i.find(".mCSB_dragger");if(i.parent(".mCSB_scrollTools_horizontal").length>0){if(!n.overflowed[1])return;var c="x",u=a.pageX>s.offset().left?-1:1,f=Math.abs(r[0].offsetLeft)-.9*u*l.width()}else{if(!n.overflowed[0])return;var c="y",u=a.pageY>s.offset().top?-1:1,f=Math.abs(r[0].offsetTop)-.9*u*l.height()}V(o,f.toString(),{dir:c,scrollEasing:"mcsEaseInOut"})}})},z=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=e("#mCSB_"+o.idx+"_container"),l=r.parent();r.bind("focusin."+i,function(o){var a=e(document.activeElement),i=r.find(".mCustomScrollBox").length,s=0;a.is(n.advanced.autoScrollOnFocus)&&(N(t),clearTimeout(t[0]._focusTimeout),t[0]._focusTimer=i?(s+17)*i:0,t[0]._focusTimeout=setTimeout(function(){var e=[te(a)[0],te(a)[1]],o=[r[0].offsetTop,r[0].offsetLeft],i=[o[0]+e[0]>=0&&o[0]+e[0]=0&&o[0]+e[1]a");s.bind("mousedown."+r+" touchstart."+r+" pointerdown."+r+" MSPointerDown."+r+" mouseup."+r+" touchend."+r+" pointerup."+r+" MSPointerUp."+r+" mouseout."+r+" pointerout."+r+" MSPointerOut."+r+" click."+r,function(a){function r(e,o){i.scrollAmount=n.scrollButtons.scrollAmount,F(t,e,o)}if(a.preventDefault(),Z(a)){var l=e(this).attr("class");switch(i.type=n.scrollButtons.scrollType,a.type){case"mousedown":case"touchstart":case"pointerdown":case"MSPointerDown":if("stepped"===i.type)return;c=!0,o.tweenRunning=!1,r("on",l);break;case"mouseup":case"touchend":case"pointerup":case"MSPointerUp":case"mouseout":case"pointerout":case"MSPointerOut":if("stepped"===i.type)return;c=!1,i.dir&&r("off",l);break;case"click":if("stepped"!==i.type||o.tweenRunning)return;r("on",l)}}})},U=function(){function t(t){function a(e,t){r.type=i.keyboard.scrollType,r.scrollAmount=i.keyboard.scrollAmount,"stepped"===r.type&&n.tweenRunning||F(o,e,t)}switch(t.type){case"blur":n.tweenRunning&&r.dir&&a("off",null);break;case"keydown":case"keyup":var l=t.keyCode?t.keyCode:t.which,s="on";if("x"!==i.axis&&(38===l||40===l)||"y"!==i.axis&&(37===l||39===l)){if((38===l||40===l)&&!n.overflowed[0]||(37===l||39===l)&&!n.overflowed[1])return;"keyup"===t.type&&(s="off"),e(document.activeElement).is(u)||(t.preventDefault(),t.stopImmediatePropagation(),a(s,l))}else if(33===l||34===l){if((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type){N(o);var f=34===l?-1:1;if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=Math.abs(c[0].offsetLeft)-.9*f*d.width();else var h="y",m=Math.abs(c[0].offsetTop)-.9*f*d.height();V(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}else if((35===l||36===l)&&!e(document.activeElement).is(u)&&((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type)){if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=35===l?Math.abs(d.width()-c.outerWidth(!1)):0;else var h="y",m=35===l?Math.abs(d.height()-c.outerHeight(!1)):0;V(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}}var o=e(this),n=o.data(a),i=n.opt,r=n.sequential,l=a+"_"+n.idx,s=e("#mCSB_"+n.idx),c=e("#mCSB_"+n.idx+"_container"),d=c.parent(),u="input,textarea,select,datalist,keygen,[contenteditable='true']",f=c.find("iframe"),h=["blur."+l+" keydown."+l+" keyup."+l];f.length&&f.each(function(){e(this).load(function(){W(this)&&e(this.contentDocument||this.contentWindow.document).bind(h[0],function(e){t(e)})})}),s.attr("tabindex","0").bind(h[0],function(e){t(e)})},F=function(t,o,n,i,r){function l(e){u.snapAmount&&(f.scrollAmount=u.snapAmount instanceof Array?"x"===f.dir[0]?u.snapAmount[1]:u.snapAmount[0]:u.snapAmount);var o="stepped"!==f.type,a=r?r:e?o?p/1.5:g:1e3/60,n=e?o?7.5:40:2.5,s=[Math.abs(h[0].offsetTop),Math.abs(h[0].offsetLeft)],d=[c.scrollRatio.y>10?10:c.scrollRatio.y,c.scrollRatio.x>10?10:c.scrollRatio.x],m="x"===f.dir[0]?s[1]+f.dir[1]*d[1]*n:s[0]+f.dir[1]*d[0]*n,v="x"===f.dir[0]?s[1]+f.dir[1]*parseInt(f.scrollAmount):s[0]+f.dir[1]*parseInt(f.scrollAmount),x="auto"!==f.scrollAmount?v:m,_=i?i:e?o?"mcsLinearOut":"mcsEaseInOut":"mcsLinear",w=e?!0:!1;return e&&17>a&&(x="x"===f.dir[0]?s[1]:s[0]),V(t,x.toString(),{dir:f.dir[0],scrollEasing:_,dur:a,onComplete:w}),e?void(f.dir=!1):(clearTimeout(f.step),void(f.step=setTimeout(function(){l()},a)))}function s(){clearTimeout(f.step),K(f,"step"),N(t)}var c=t.data(a),u=c.opt,f=c.sequential,h=e("#mCSB_"+c.idx+"_container"),m="stepped"===f.type?!0:!1,p=u.scrollInertia<26?26:u.scrollInertia,g=u.scrollInertia<1?17:u.scrollInertia;switch(o){case"on":if(f.dir=[n===d[16]||n===d[15]||39===n||37===n?"x":"y",n===d[13]||n===d[15]||38===n||37===n?-1:1],N(t),ee(n)&&"stepped"===f.type)return;l(m);break;case"off":s(),(m||c.tweenRunning&&f.dir)&&l(!0)}},q=function(t){var o=e(this).data(a).opt,n=[];return"function"==typeof t&&(t=t()),t instanceof Array?n=t.length>1?[t[0],t[1]]:"x"===o.axis?[null,t[0]]:[t[0],null]:(n[0]=t.y?t.y:t.x||"x"===o.axis?null:t,n[1]=t.x?t.x:t.y||"y"===o.axis?null:t),"function"==typeof n[0]&&(n[0]=n[0]()),"function"==typeof n[1]&&(n[1]=n[1]()),n},Y=function(t,o){if(null!=t&&"undefined"!=typeof t){var n=e(this),i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx+"_container"),s=l.parent(),c=typeof t;o||(o="x"===r.axis?"x":"y");var d="x"===o?l.outerWidth(!1):l.outerHeight(!1),f="x"===o?l[0].offsetLeft:l[0].offsetTop,h="x"===o?"left":"top";switch(c){case"function":return t();case"object":var m=t.jquery?t:e(t);if(!m.length)return;return"x"===o?te(m)[1]:te(m)[0];case"string":case"number":if(ee(t))return Math.abs(t);if(-1!==t.indexOf("%"))return Math.abs(d*parseInt(t)/100);if(-1!==t.indexOf("-="))return Math.abs(f-parseInt(t.split("-=")[1]));if(-1!==t.indexOf("+=")){var p=f+parseInt(t.split("+=")[1]);return p>=0?0:Math.abs(p)}if(-1!==t.indexOf("px")&&ee(t.split("px")[0]))return Math.abs(t.split("px")[0]);if("top"===t||"left"===t)return 0;if("bottom"===t)return Math.abs(s.height()-l.outerHeight(!1));if("right"===t)return Math.abs(s.width()-l.outerWidth(!1));if("first"===t||"last"===t){var m=l.find(":"+t);return"x"===o?te(m)[1]:te(m)[0]}return e(t).length?"x"===o?te(e(t))[1]:te(e(t))[0]:(l.css(h,t),void u.update.call(null,n[0]))}}},X=function(t){function o(){return clearTimeout(f[0].autoUpdate),0===l.parents("html").length?void(l=null):void(f[0].autoUpdate=setTimeout(function(){return c.advanced.updateOnSelectorChange&&(s.poll.change.n=i(),s.poll.change.n!==s.poll.change.o)?(s.poll.change.o=s.poll.change.n,void r(3)):c.advanced.updateOnContentResize&&(s.poll.size.n=l[0].scrollHeight+l[0].scrollWidth+f[0].offsetHeight+l[0].offsetHeight+l[0].offsetWidth,s.poll.size.n!==s.poll.size.o)?(s.poll.size.o=s.poll.size.n,void r(1)):!c.advanced.updateOnImageLoad||"auto"===c.advanced.updateOnImageLoad&&"y"===c.axis||(s.poll.img.n=f.find("img").length,s.poll.img.n===s.poll.img.o)?void((c.advanced.updateOnSelectorChange||c.advanced.updateOnContentResize||c.advanced.updateOnImageLoad)&&o()):(s.poll.img.o=s.poll.img.n,void f.find("img").each(function(){n(this)}))},c.advanced.autoUpdateTimeout))}function n(t){function o(e,t){return function(){return t.apply(e,arguments)}}function a(){this.onload=null,e(t).addClass(d[2]),r(2)}if(e(t).hasClass(d[2]))return void r();var n=new Image;n.onload=o(n,a),n.src=t.src}function i(){c.advanced.updateOnSelectorChange===!0&&(c.advanced.updateOnSelectorChange="*");var e=0,t=f.find(c.advanced.updateOnSelectorChange);
5 |
6 | return c.advanced.updateOnSelectorChange&&t.length>0&&t.each(function(){e+=this.offsetHeight+this.offsetWidth}),e}function r(e){clearTimeout(f[0].autoUpdate),u.update.call(null,l[0],e)}var l=e(this),s=l.data(a),c=s.opt,f=e("#mCSB_"+s.idx+"_container");return t?(clearTimeout(f[0].autoUpdate),void K(f[0],"autoUpdate")):void o()},j=function(e,t,o){return Math.round(e/t)*t-o},N=function(t){var o=t.data(a),n=e("#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal");n.each(function(){J.call(this)})},V=function(t,o,n){function i(e){return s&&c.callbacks[e]&&"function"==typeof c.callbacks[e]}function r(){return[c.callbacks.alwaysTriggerOffsets||w>=S[0]+y,c.callbacks.alwaysTriggerOffsets||-B>=w]}function l(){var e=[h[0].offsetTop,h[0].offsetLeft],o=[x[0].offsetTop,x[0].offsetLeft],a=[h.outerHeight(!1),h.outerWidth(!1)],i=[f.height(),f.width()];t[0].mcs={content:h,top:e[0],left:e[1],draggerTop:o[0],draggerLeft:o[1],topPct:Math.round(100*Math.abs(e[0])/(Math.abs(a[0])-i[0])),leftPct:Math.round(100*Math.abs(e[1])/(Math.abs(a[1])-i[1])),direction:n.dir}}var s=t.data(a),c=s.opt,d={trigger:"internal",dir:"y",scrollEasing:"mcsEaseOut",drag:!1,dur:c.scrollInertia,overwrite:"all",callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},n=e.extend(d,n),u=[n.dur,n.drag?0:n.dur],f=e("#mCSB_"+s.idx),h=e("#mCSB_"+s.idx+"_container"),m=h.parent(),p=c.callbacks.onTotalScrollOffset?q.call(t,c.callbacks.onTotalScrollOffset):[0,0],g=c.callbacks.onTotalScrollBackOffset?q.call(t,c.callbacks.onTotalScrollBackOffset):[0,0];if(s.trigger=n.trigger,(0!==m.scrollTop()||0!==m.scrollLeft())&&(e(".mCSB_"+s.idx+"_scrollbar").css("visibility","visible"),m.scrollTop(0).scrollLeft(0)),"_resetY"!==o||s.contentReset.y||(i("onOverflowYNone")&&c.callbacks.onOverflowYNone.call(t[0]),s.contentReset.y=1),"_resetX"!==o||s.contentReset.x||(i("onOverflowXNone")&&c.callbacks.onOverflowXNone.call(t[0]),s.contentReset.x=1),"_resetY"!==o&&"_resetX"!==o){if(!s.contentReset.y&&t[0].mcs||!s.overflowed[0]||(i("onOverflowY")&&c.callbacks.onOverflowY.call(t[0]),s.contentReset.x=null),!s.contentReset.x&&t[0].mcs||!s.overflowed[1]||(i("onOverflowX")&&c.callbacks.onOverflowX.call(t[0]),s.contentReset.x=null),c.snapAmount){var v=c.snapAmount instanceof Array?"x"===n.dir?c.snapAmount[1]:c.snapAmount[0]:c.snapAmount;o=j(o,v,c.snapOffset)}switch(n.dir){case"x":var x=e("#mCSB_"+s.idx+"_dragger_horizontal"),_="left",w=h[0].offsetLeft,S=[f.width()-h.outerWidth(!1),x.parent().width()-x.width()],b=[o,0===o?0:o/s.scrollRatio.x],y=p[1],B=g[1],T=y>0?y/s.scrollRatio.x:0,k=B>0?B/s.scrollRatio.x:0;break;case"y":var x=e("#mCSB_"+s.idx+"_dragger_vertical"),_="top",w=h[0].offsetTop,S=[f.height()-h.outerHeight(!1),x.parent().height()-x.height()],b=[o,0===o?0:o/s.scrollRatio.y],y=p[0],B=g[0],T=y>0?y/s.scrollRatio.y:0,k=B>0?B/s.scrollRatio.y:0}b[1]<0||0===b[0]&&0===b[1]?b=[0,0]:b[1]>=S[1]?b=[S[0],S[1]]:b[0]=-b[0],t[0].mcs||(l(),i("onInit")&&c.callbacks.onInit.call(t[0])),clearTimeout(h[0].onCompleteTimeout),Q(x[0],_,Math.round(b[1]),u[1],n.scrollEasing),(s.tweenRunning||!(0===w&&b[0]>=0||w===S[0]&&b[0]<=S[0]))&&Q(h[0],_,Math.round(b[0]),u[0],n.scrollEasing,n.overwrite,{onStart:function(){n.callbacks&&n.onStart&&!s.tweenRunning&&(i("onScrollStart")&&(l(),c.callbacks.onScrollStart.call(t[0])),s.tweenRunning=!0,C(x),s.cbOffsets=r())},onUpdate:function(){n.callbacks&&n.onUpdate&&i("whileScrolling")&&(l(),c.callbacks.whileScrolling.call(t[0]))},onComplete:function(){if(n.callbacks&&n.onComplete){"yx"===c.axis&&clearTimeout(h[0].onCompleteTimeout);var e=h[0].idleTimer||0;h[0].onCompleteTimeout=setTimeout(function(){i("onScroll")&&(l(),c.callbacks.onScroll.call(t[0])),i("onTotalScroll")&&b[1]>=S[1]-T&&s.cbOffsets[0]&&(l(),c.callbacks.onTotalScroll.call(t[0])),i("onTotalScrollBack")&&b[1]<=k&&s.cbOffsets[1]&&(l(),c.callbacks.onTotalScrollBack.call(t[0])),s.tweenRunning=!1,h[0].idleTimer=0,C(x,"hide")},e)}}})}},Q=function(e,t,o,a,n,i,r){function l(){S.stop||(x||m.call(),x=G()-v,s(),x>=S.time&&(S.time=x>S.time?x+f-(x-S.time):x+f-1,S.time0?(S.currVal=u(S.time,_,b,a,n),w[t]=Math.round(S.currVal)+"px"):w[t]=o+"px",p.call()}function c(){f=1e3/60,S.time=x+f,h=window.requestAnimationFrame?window.requestAnimationFrame:function(e){return s(),setTimeout(e,.01)},S.id=h(l)}function d(){null!=S.id&&(window.requestAnimationFrame?window.cancelAnimationFrame(S.id):clearTimeout(S.id),S.id=null)}function u(e,t,o,a,n){switch(n){case"linear":case"mcsLinear":return o*e/a+t;case"mcsLinearOut":return e/=a,e--,o*Math.sqrt(1-e*e)+t;case"easeInOutSmooth":return e/=a/2,1>e?o/2*e*e+t:(e--,-o/2*(e*(e-2)-1)+t);case"easeInOutStrong":return e/=a/2,1>e?o/2*Math.pow(2,10*(e-1))+t:(e--,o/2*(-Math.pow(2,-10*e)+2)+t);case"easeInOut":case"mcsEaseInOut":return e/=a/2,1>e?o/2*e*e*e+t:(e-=2,o/2*(e*e*e+2)+t);case"easeOutSmooth":return e/=a,e--,-o*(e*e*e*e-1)+t;case"easeOutStrong":return o*(-Math.pow(2,-10*e/a)+1)+t;case"easeOut":case"mcsEaseOut":default:var i=(e/=a)*e,r=i*e;return t+o*(.499999999999997*r*i+-2.5*i*i+5.5*r+-6.5*i+4*e)}}e._mTween||(e._mTween={top:{},left:{}});var f,h,r=r||{},m=r.onStart||function(){},p=r.onUpdate||function(){},g=r.onComplete||function(){},v=G(),x=0,_=e.offsetTop,w=e.style,S=e._mTween[t];"left"===t&&(_=e.offsetLeft);var b=o-_;S.stop=0,"none"!==i&&d(),c()},G=function(){return window.performance&&window.performance.now?window.performance.now():window.performance&&window.performance.webkitNow?window.performance.webkitNow():Date.now?Date.now():(new Date).getTime()},J=function(){var e=this;e._mTween||(e._mTween={top:{},left:{}});for(var t=["top","left"],o=0;o=0&&a[0]+te(n)[0]=0&&a[1]+te(n)[1].mCSB_container{margin-right:30px}.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container{margin-right:0;margin-left:30px}.mCS-dir-rtl>.mCSB_inside>.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-left:0}.mCSB_scrollTools{position:absolute;width:16px;height:auto;left:auto;top:0;right:0;bottom:0;opacity:.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_outside+.mCSB_scrollTools{right:-26px}.mCS-dir-rtl>.mCSB_inside>.mCSB_scrollTools,.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{right:auto;left:0}.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{left:-26px}.mCSB_scrollTools .mCSB_draggerContainer{position:absolute;top:0;left:0;bottom:0;right:0;height:auto}.mCSB_scrollTools a+.mCSB_draggerContainer{margin:20px 0}.mCSB_scrollTools .mCSB_draggerRail{width:2px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCSB_scrollTools .mCSB_dragger{cursor:pointer;width:100%;height:30px;z-index:1}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{position:relative;width:4px;height:100%;margin:0 auto;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;text-align:center}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:12px}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:8px}.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonUp{display:block;position:absolute;height:20px;width:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools .mCSB_buttonDown{bottom:0}.mCSB_horizontal.mCSB_inside>.mCSB_container{margin-right:0;margin-bottom:30px}.mCSB_horizontal.mCSB_outside>.mCSB_container{min-height:100%}.mCSB_horizontal>.mCSB_container.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal{width:auto;height:16px;top:auto;right:0;bottom:0;left:0}.mCustomScrollBox+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:-26px}.mCSB_scrollTools.mCSB_scrollTools_horizontal a+.mCSB_draggerContainer{margin:0 20px}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:2px;margin:7px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger{width:30px;height:100%;left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:12px;margin:2px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:8px;margin:4px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft,.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{display:block;position:absolute;width:20px;height:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft{left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{right:0}.mCSB_container_wrapper{position:absolute;height:auto;width:auto;overflow:hidden;top:0;left:0;right:0;bottom:0;margin-right:30px;margin-bottom:30px}.mCSB_container_wrapper>.mCSB_container{padding-right:30px;padding-bottom:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:20px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:20px}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden+.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools~.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:20px}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools~.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container_wrapper{margin-right:0;margin-left:30px}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden>.mCSB_container{padding-right:0}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden>.mCSB_container{padding-bottom:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0;margin-left:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight,.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-transition:opacity .2s ease-in-out,background-color .2s ease-in-out;-moz-transition:opacity .2s ease-in-out,background-color .2s ease-in-out;-o-transition:opacity .2s ease-in-out,background-color .2s ease-in-out;transition:opacity .2s ease-in-out,background-color .2s ease-in-out}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar{-webkit-transition:width .2s ease-out .2s,height .2s ease-out .2s,margin-left .2s ease-out .2s,margin-right .2s ease-out .2s,margin-top .2s ease-out .2s,margin-bottom .2s ease-out .2s,opacity .2s ease-in-out,background-color .2s ease-in-out;-moz-transition:width .2s ease-out .2s,height .2s ease-out .2s,margin-left .2s ease-out .2s,margin-right .2s ease-out .2s,margin-top .2s ease-out .2s,margin-bottom .2s ease-out .2s,opacity .2s ease-in-out,background-color .2s ease-in-out;-o-transition:width .2s ease-out .2s,height .2s ease-out .2s,margin-left .2s ease-out .2s,margin-right .2s ease-out .2s,margin-top .2s ease-out .2s,margin-bottom .2s ease-out .2s,opacity .2s ease-in-out,background-color .2s ease-in-out;transition:width .2s ease-out .2s,height .2s ease-out .2s,margin-left .2s ease-out .2s,margin-right .2s ease-out .2s,margin-top .2s ease-out .2s,margin-bottom .2s ease-out .2s,opacity .2s ease-in-out,background-color .2s ease-in-out}.mCS-autoHide>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide>.mCustomScrollBox~.mCSB_scrollTools{opacity:0;filter:"alpha(opacity=0)";-ms-filter:"alpha(opacity=0)"}.mCS-autoHide:hover>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox~.mCSB_scrollTools,.mCustomScrollBox:hover>.mCSB_scrollTools,.mCustomScrollBox:hover~.mCSB_scrollTools,.mCustomScrollbar>.mCustomScrollBox>.mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollbar>.mCustomScrollBox~.mCSB_scrollTools.mCSB_scrollTools_onDrag{opacity:1;filter:"alpha(opacity=100)";-ms-filter:"alpha(opacity=100)"}.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.4);filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.75);filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.85);filter:"alpha(opacity=85)";-ms-filter:"alpha(opacity=85)"}.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.9);filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight,.mCSB_scrollTools .mCSB_buttonUp{background-image:url(mCSB_buttons.png);background-repeat:no-repeat;opacity:.4;filter:"alpha(opacity=40)";-ms-filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_buttonUp{background-position:0 0}.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -20px}.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -40px}.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -56px}.mCSB_scrollTools .mCSB_buttonDown:hover,.mCSB_scrollTools .mCSB_buttonLeft:hover,.mCSB_scrollTools .mCSB_buttonRight:hover,.mCSB_scrollTools .mCSB_buttonUp:hover{opacity:.75;filter:"alpha(opacity=75)";-ms-filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_buttonDown:active,.mCSB_scrollTools .mCSB_buttonLeft:active,.mCSB_scrollTools .mCSB_buttonRight:active,.mCSB_scrollTools .mCSB_buttonUp:active{opacity:.9;filter:"alpha(opacity=90)";-ms-filter:"alpha(opacity=90)"}.mCS-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.15)}.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:rgba(0,0,0,.85)}.mCS-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:rgba(0,0,0,.9)}.mCS-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:4px;background-color:#fff;background-color:rgba(255,255,255,.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px auto}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.85)}.mCS-light-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.9)}.mCS-light-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px 0}.mCS-light-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -20px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -40px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.1);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75);-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px 0}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -20px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -40px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail,.mCS-light-thick.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;background-color:#fff;background-color:rgba(255,255,255,.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px 0}.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:6px;margin:5px auto}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.85)}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.9)}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px 0}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -20px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -40px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.1);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px 0}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -20px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -40px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -56px}.mCS-light-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,.1)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-light-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:2px}.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%}.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:2px;margin:7px auto}.mCS-dark-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.15)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-rounded.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger,.mCS-rounded.mCSB_scrollTools .mCSB_dragger{height:14px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:14px;margin:0 1px}.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger{width:14px}.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:14px;margin:1px 0}.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:16px;height:16px;margin:-1px 0}.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:4px}.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:16px;width:16px;margin:0 -1px}.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:4px;margin:6px 0}.mCS-rounded.mCSB_scrollTools .mCSB_buttonUp{background-position:0 -72px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -92px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -112px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -128px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px -72px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -92px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -112px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools_vertical .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_vertical .mCSB_draggerRail{width:4px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail{background-color:transparent;background-position:center}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQYV2NkIAAYiVbw//9/Y6DiM1ANJoyMjGdBbLgJQAX/kU0DKgDLkaQAvxW4HEvQFwCRcxIJK1XznAAAAABJRU5ErkJggg==);background-repeat:repeat-y;opacity:.3;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail{height:4px;margin:6px 0;background-repeat:repeat-x}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px -72px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -92px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -112px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALElEQVQYV2NkIAAYSVFgDFR8BqrBBEifBbGRTfiPZhpYjiQFBK3A6l6CvgAAE9kGCd1mvgEAAAAASUVORK5CYII=)}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px -72px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -92px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -112px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-y;background-image:-moz-linear-gradient(left,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,.5)),color-stop(100%,rgba(255,255,255,0)));background-image:-webkit-linear-gradient(left,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(left,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(left,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:linear-gradient(to right,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-x;background-image:-moz-linear-gradient(top,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,.5)),color-stop(100%,rgba(255,255,255,0)));background-image:-webkit-linear-gradient(top,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-o-linear-gradient(top,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:-ms-linear-gradient(top,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%);background-image:linear-gradient(to bottom,rgba(255,255,255,.5) 0,rgba(255,255,255,0) 100%)}.mCS-3d-dark.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-3d.mCSB_scrollTools_vertical .mCSB_dragger{height:70px}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger{width:70px}.mCS-3d-dark.mCSB_scrollTools,.mCS-3d.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_draggerRail{-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_draggerRail{width:8px;background-color:#000;background-color:rgba(0,0,0,.2);box-shadow:inset 1px 0 1px rgba(0,0,0,.5),inset -1px 0 1px rgba(255,255,255,.2)}.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#555}.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:8px}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:8px;margin:4px 0;box-shadow:inset 0 1px 1px rgba(0,0,0,.5),inset 0 -1px 1px rgba(255,255,255,.2)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:8px;margin:4px auto}.mCS-3d.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.1);box-shadow:inset 1px 0 1px rgba(0,0,0,.1)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{box-shadow:inset 0 1px 1px rgba(0,0,0,.1)}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)";-ms-filter:"alpha(opacity=30)"}.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer,.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mCSB_inside+.mCS-3d-thick-dark.mCSB_scrollTools_vertical,.mCSB_inside+.mCS-3d-thick.mCSB_scrollTools_vertical{right:1px}.mCS-3d-thick-dark.mCSB_scrollTools_vertical,.mCS-3d-thick.mCSB_scrollTools_vertical{box-shadow:inset 1px 0 1px rgba(0,0,0,.1),inset 0 0 14px rgba(0,0,0,.5)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal,.mCS-3d-thick.mCSB_scrollTools_horizontal{bottom:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,.1),inset 0 0 14px rgba(0,0,0,.5)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;box-shadow:inset 1px 0 0 rgba(255,255,255,.4);width:12px;margin:2px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,.4);height:12px;width:auto}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#555}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{background-color:#000;background-color:rgba(0,0,0,.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,.1)}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-thick-dark.mCSB_scrollTools{box-shadow:inset 0 0 14px rgba(0,0,0,.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{box-shadow:inset 0 1px 1px rgba(0,0,0,.1),inset 0 0 14px rgba(0,0,0,.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,.4),inset -1px 0 0 rgba(0,0,0,.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,.4),inset 0 -1px 0 rgba(0,0,0,.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#777}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{background-color:#fff;background-color:rgba(0,0,0,.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,.1)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical,.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical{right:0;margin:12px 0}.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:0;margin:0 12px}.mCS-dir-rtl>.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical,.mCS-dir-rtl>.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical{left:0;right:auto}.mCS-minimal-dark.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-minimal.mCSB_scrollTools_vertical .mCSB_dragger{height:50px}.mCS-minimal-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-minimal.mCSB_scrollTools_horizontal .mCSB_dragger{width:50px}.mCS-minimal.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-minimal.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.2);filter:"alpha(opacity=20)";-ms-filter:"alpha(opacity=20)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.5);filter:"alpha(opacity=50)";-ms-filter:"alpha(opacity=50)"}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools .mCSB_draggerRail{width:6px;background-color:#000;background-color:rgba(0,0,0,.2)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px}.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:6px;margin:5px 0}.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:12px}.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:12px;margin:2px 0}.mCS-light-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.1)}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset.mCSB_scrollTools .mCSB_draggerRail{width:12px;background-color:#000;background-color:rgba(0,0,0,.2)}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;margin:3px 5px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:6px;margin:5px 3px;position:absolute;width:auto;top:0;bottom:0;left:0;right:0}.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:12px;margin:2px 0}.mCS-inset-2.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-inset-2.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-inset-2.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-inset-2.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.1)}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent;border-width:1px;border-style:solid;border-color:#fff;border-color:rgba(255,255,255,.2);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{border-color:#000;border-color:rgba(0,0,0,.2)}.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,.6)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,.6)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.75)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.85)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,.9)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.75)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.85)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,.9)}
--------------------------------------------------------------------------------
/static/res/botim.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluekeroro/erl-2019/38ecdfbcd3b8f576565515f76e8ec6c714bc9d07/static/res/botim.png
--------------------------------------------------------------------------------
/static/res/easybot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluekeroro/erl-2019/38ecdfbcd3b8f576565515f76e8ec6c714bc9d07/static/res/easybot.png
--------------------------------------------------------------------------------
/static/res/hd1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluekeroro/erl-2019/38ecdfbcd3b8f576565515f76e8ec6c714bc9d07/static/res/hd1.jpg
--------------------------------------------------------------------------------
/static/res/hd2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluekeroro/erl-2019/38ecdfbcd3b8f576565515f76e8ec6c714bc9d07/static/res/hd2.jpg
--------------------------------------------------------------------------------
/static/scss/style.scss:
--------------------------------------------------------------------------------
1 | /*--------------------
2 | Mixins
3 | --------------------*/
4 | @mixin center {
5 | position: absolute;
6 | top: 50%;
7 | left: 50%;
8 | transform: translate(-50%, -50%);
9 | }
10 |
11 | @mixin ball {
12 | @include center;
13 | content: '';
14 | display: block;
15 | width: 3px;
16 | height: 3px;
17 | border-radius: 50%;
18 | background: rgba(255, 255, 255, .5);
19 | z-index: 2;
20 | margin-top: 4px;
21 | animation: ball .45s cubic-bezier(0, 0, 0.15, 1) alternate infinite;
22 | }
23 |
24 |
25 | /*--------------------
26 | Body
27 | --------------------*/
28 | *,
29 | *::before,
30 | *::after {
31 | box-sizing: border-box;
32 | }
33 |
34 | html,
35 | body {
36 | height: 100%;
37 | }
38 |
39 | body {
40 | background: linear-gradient(135deg, #044f48, #2a7561);
41 | background-size: cover;
42 | font-family: 'Open Sans', sans-serif;
43 | font-size: 12px;
44 | line-height: 1.3;
45 | overflow: hidden;
46 | }
47 |
48 | .bg {
49 | width: 100%;
50 | height: 100%;
51 | top: 0;
52 | left: 0;
53 | z-index: 1;
54 | background: url('https://images.unsplash.com/photo-1451186859696-371d9477be93?crop=entropy&fit=crop&fm=jpg&h=975&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925') no-repeat 0 0;
55 | filter: blur(80px);
56 | transform: scale(1.2);
57 | }
58 |
59 |
60 | /*--------------------
61 | Chat
62 | --------------------*/
63 | .chat {
64 | @include center;
65 | width: 300px;
66 | height: 80vh;
67 | max-height: 500px;
68 | z-index: 2;
69 | overflow: hidden;
70 | box-shadow: 0 5px 30px rgba(0, 0, 0, .2);
71 | background: rgba(0, 0, 0, .5);
72 | border-radius: 20px;
73 | display: flex;
74 | justify-content: space-between;
75 | flex-direction: column;
76 | }
77 |
78 |
79 | /*--------------------
80 | Chat Title
81 | --------------------*/
82 | .chat-title {
83 | flex: 0 1 45px;
84 | position: relative;
85 | z-index: 2;
86 | background: rgba(0, 0, 0, 0.2);
87 | color: #fff;
88 | text-transform: uppercase;
89 | text-align: left;
90 | padding: 10px 10px 10px 50px;
91 |
92 | h1, h2 {
93 | font-weight: normal;
94 | font-size: 10px;
95 | margin: 0;
96 | padding: 0;
97 | }
98 |
99 | h2 {
100 | color: rgba(255, 255, 255, .5);
101 | font-size: 8px;
102 | letter-spacing: 1px;
103 | }
104 |
105 | .avatar {
106 | position: absolute;
107 | z-index: 1;
108 | top: 8px;
109 | left: 9px;
110 | border-radius: 30px;
111 | width: 30px;
112 | height: 30px;
113 | overflow: hidden;
114 | margin: 0;
115 | padding: 0;
116 | border: 2px solid rgba(255, 255, 255, 0.24);
117 |
118 | img {
119 | width: 100%;
120 | height: auto;
121 | }
122 | }
123 | }
124 |
125 |
126 | /*--------------------
127 | Messages
128 | --------------------*/
129 | .messages {
130 | flex: 1 1 auto;
131 | color: rgba(255, 255, 255, .5);
132 | overflow: hidden;
133 | position: relative;
134 | width: 100%;
135 |
136 | & .messages-content {
137 | position: absolute;
138 | top: 0;
139 | left: 0;
140 | height: 101%;
141 | width: 100%;
142 | }
143 |
144 |
145 | .message {
146 | clear: both;
147 | float: left;
148 | padding: 6px 10px 7px;
149 | border-radius: 10px 10px 10px 0;
150 | background: rgba(0, 0, 0, .3);
151 | margin: 8px 0;
152 | font-size: 11px;
153 | line-height: 1.4;
154 | margin-left: 35px;
155 | position: relative;
156 | text-shadow: 0 1px 1px rgba(0, 0, 0, .2);
157 |
158 | .timestamp {
159 | position: absolute;
160 | bottom: -15px;
161 | font-size: 9px;
162 | color: rgba(255, 255, 255, .3);
163 | }
164 |
165 | &::before {
166 | content: '';
167 | position: absolute;
168 | bottom: -6px;
169 | border-top: 6px solid rgba(0, 0, 0, .3);
170 | left: 0;
171 | border-right: 7px solid transparent;
172 | }
173 |
174 | .avatar {
175 | position: absolute;
176 | z-index: 1;
177 | bottom: -15px;
178 | left: -35px;
179 | border-radius: 30px;
180 | width: 30px;
181 | height: 30px;
182 | overflow: hidden;
183 | margin: 0;
184 | padding: 0;
185 | border: 2px solid rgba(255, 255, 255, 0.24);
186 |
187 | img {
188 | width: 100%;
189 | height: auto;
190 | }
191 | }
192 |
193 | &.message-personal {
194 | float: right;
195 | color: #fff;
196 | text-align: right;
197 | background: linear-gradient(120deg, #248A52, #257287);
198 | border-radius: 10px 10px 0 10px;
199 |
200 | &::before {
201 | left: auto;
202 | right: 0;
203 | border-right: none;
204 | border-left: 5px solid transparent;
205 | border-top: 4px solid #257287;
206 | bottom: -4px;
207 | }
208 | }
209 |
210 | &:last-child {
211 | margin-bottom: 30px;
212 | }
213 |
214 | &.new {
215 | transform: scale(0);
216 | transform-origin: 0 0;
217 | animation: bounce 500ms linear both;
218 | }
219 |
220 | &.loading {
221 |
222 | &::before {
223 | @include ball;
224 | border: none;
225 | animation-delay: .15s;
226 | }
227 |
228 | & span {
229 | display: block;
230 | font-size: 0;
231 | width: 20px;
232 | height: 10px;
233 | position: relative;
234 |
235 | &::before {
236 | @include ball;
237 | margin-left: -7px;
238 | }
239 |
240 | &::after {
241 | @include ball;
242 | margin-left: 7px;
243 | animation-delay: .3s;
244 | }
245 | }
246 | }
247 |
248 | }
249 | }
250 |
251 |
252 | /*--------------------
253 | Message Box
254 | --------------------*/
255 | .message-box {
256 | flex: 0 1 40px;
257 | width: 100%;
258 | background: rgba(0, 0, 0, 0.3);
259 | padding: 10px;
260 | position: relative;
261 |
262 | & .message-input {
263 | background: none;
264 | border: none;
265 | outline: none!important;
266 | resize: none;
267 | color: rgba(255, 255, 255, .7);
268 | font-size: 11px;
269 | height: 17px;
270 | margin: 0;
271 | padding-right: 20px;
272 | width: 265px;
273 | }
274 |
275 | textarea:focus:-webkit-placeholder{
276 | color: transparent;
277 | }
278 |
279 | & .message-submit {
280 | position: absolute;
281 | z-index: 1;
282 | top: 9px;
283 | right: 10px;
284 | color: #fff;
285 | border: none;
286 | background: #248A52;
287 | font-size: 10px;
288 | text-transform: uppercase;
289 | line-height: 1;
290 | padding: 6px 10px;
291 | border-radius: 10px;
292 | outline: none!important;
293 | transition: background .2s ease;
294 |
295 | &:hover {
296 | background: #1D7745;
297 | }
298 | }
299 | }
300 |
301 |
302 | /*--------------------
303 | Custom Srollbar
304 | --------------------*/
305 | .mCSB_scrollTools {
306 | margin: 1px -3px 1px 0;
307 | opacity: 0;
308 | }
309 |
310 | .mCSB_inside > .mCSB_container {
311 | margin-right: 0px;
312 | padding: 0 10px;
313 | }
314 |
315 | .mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar {
316 | background-color: rgba(0, 0, 0, 0.5)!important;
317 | }
318 |
319 |
320 | /*--------------------
321 | Bounce
322 | --------------------*/
323 | @keyframes bounce {
324 | 0% { transform: matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
325 | 4.7% { transform: matrix3d(0.45, 0, 0, 0, 0, 0.45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
326 | 9.41% { transform: matrix3d(0.883, 0, 0, 0, 0, 0.883, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
327 | 14.11% { transform: matrix3d(1.141, 0, 0, 0, 0, 1.141, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
328 | 18.72% { transform: matrix3d(1.212, 0, 0, 0, 0, 1.212, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
329 | 24.32% { transform: matrix3d(1.151, 0, 0, 0, 0, 1.151, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
330 | 29.93% { transform: matrix3d(1.048, 0, 0, 0, 0, 1.048, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
331 | 35.54% { transform: matrix3d(0.979, 0, 0, 0, 0, 0.979, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
332 | 41.04% { transform: matrix3d(0.961, 0, 0, 0, 0, 0.961, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
333 | 52.15% { transform: matrix3d(0.991, 0, 0, 0, 0, 0.991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
334 | 63.26% { transform: matrix3d(1.007, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
335 | 85.49% { transform: matrix3d(0.999, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
336 | 100% { transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
337 | }
338 |
339 |
340 | @keyframes ball {
341 | from {
342 | transform: translateY(0) scaleY(.8);
343 | }
344 | to {
345 | transform: translateY(-10px);
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ShallDo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Easybot
20 |
Let's talk about what shall we do.
21 |
22 | 
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | # -*- coding:UTF-8 -*-
2 | """
3 | @File : util.py
4 | @Time : 2019/5/19 19:46
5 | @Author : Blue Keroro
6 | """
7 |
8 | import sys
9 | import os
10 |
11 | curPath = os.path.abspath(os.path.dirname(__file__))
12 | rootPath = os.path.split(curPath)[0]
13 | sys.path.append(rootPath)
14 |
15 | from tqdm import tqdm
16 | import json
17 | # import jieba.analyse
18 | # jieba.load_userdict('data/nerDict.txt')
19 |
20 |
21 | def loadData(file_path):
22 | data = []
23 | with open(file_path, 'r', encoding='utf-8') as file:
24 | for line in file:
25 | line = line.strip()
26 | data.append(eval(line))
27 | return data
28 |
29 |
30 | def getAliasfromAbstract(input_text: str):
31 | key_words = ['简称', '别称', '又称']
32 | ret = set()
33 | for key_word in key_words:
34 | start = 0
35 | # if key_word in input_text:
36 | while input_text.find(key_word, start) != -1:
37 | start = input_text.index(key_word, start) + len(key_word)
38 | end = start
39 | for index, ch in enumerate(input_text[start:]):
40 | if is_Chinese(ch) is not True and str(ch).isalpha() is not True and str(ch).isdigit() is not True:
41 | end = index
42 | break
43 | ret.add(input_text[start:start + end])
44 | return ret
45 |
46 |
47 | def loadDataBase(file_path,model=0):
48 | print('加载数据库 start')
49 | dataByAlias = {}
50 | dataBySubjectId = {}
51 | with open(file_path, 'r', encoding='utf-8') as file:
52 | for line in file:
53 | line = line.strip()
54 | line = eval(line)
55 | if line['subject'] not in line['alias']:
56 | line['alias'].append(line['subject'])
57 | # raise Exception('subject not in alias',"subject_id:",line["subject_id"])
58 | # try:
59 | # words = getAliasfromAbstract(line['data'][0]['object'])
60 | # for word in words:
61 | # if word not in line['alias']:
62 | # line['alias'].append(word)
63 | # except Exception:
64 | # print('数据格式异常', 'subject_id:', line["subject_id"])
65 | if line["subject_id"] in dataBySubjectId:
66 | raise Exception('可能有重复的subject_id', "subject_id:", line["subject_id"])
67 | dataBySubjectId[line["subject_id"]] = line
68 | tmp = set()
69 | for alia in line['alias']:
70 | tmp.add(alia)
71 | # alia=alia.replace('《', '').replace('》', '')# 别名去除书名号
72 | # tmp.add(alia)
73 | tmp.add(alia.lower())
74 | line['alias'] = list(tmp)
75 | for alia in line['alias']:
76 | if alia not in dataByAlias:
77 | dataByAlias[alia] = []
78 | dataByAlias[alia].append(line)
79 | if model==1:
80 | path = os.path.dirname(__file__) if len(os.path.dirname(__file__)) != 0 else '.'
81 | train = loadData(path + '/' +'bert/data/train_pre.json')
82 | for l in tqdm(train):
83 | for i in l['mention_data']:
84 | if i['mention'] not in dataByAlias and i['kb_id'] in dataBySubjectId:
85 | dataByAlias[i['mention']]=[dataBySubjectId[i['kb_id']]]
86 | if i['mention'] in dataByAlias and i['kb_id'] in dataBySubjectId:
87 | for _ in dataByAlias[i['mention']]:
88 | if _['subject_id'] ==i['kb_id']:
89 | break
90 | else:
91 | dataByAlias[i['mention']].append(dataBySubjectId[i['kb_id']])
92 | # elif i['mention'] in dataByAlias and i['kb_id'] in dataBySubjectId and i['kb_id'] not in dataByAlias[i['mention']]:
93 | # dataByAlias[i['mention']].append(dataBySubjectId[i['kb_id']])
94 | print('加载数据库 end')
95 | return dataByAlias, dataBySubjectId
96 |
97 |
98 | def is_word(word: str):
99 | for ch in word:
100 | if is_Chinese(ch) or str(ch).isdigit() or str(ch).isalpha():
101 | return True
102 | return False
103 |
104 |
105 | def is_Chinese(ch):
106 | # 中文符号也会返回False
107 | if '\u4e00' <= ch <= '\u9fff':
108 | return True
109 | return False
110 |
111 |
112 | def get_special_mention(input_text: str):
113 | special_chars = [['《', '》'], ['[', ']'], ['【', '】'], ['≪', '≫'], ['(', ')'], ['(', ')'], ['{', '}'], ['〖', '〗'],
114 | ['『', '』']]
115 | split_chars = set(['|', '┆', '┇', '︳', '︱', '▕', '‖', '│', '▎'
116 | , 'Ⅰ', '▏', '▍', '┋', '།', '¦', '┇', '┆', 'Ⅱ', '|', '▋', '▌', '┊'])
117 | ret_set = set()
118 | for special_char in special_chars:
119 | text_list = input_text.split(special_char[0])
120 | for t in text_list:
121 | if special_char[1] in t:
122 | t = t.split(special_char[1])
123 | ret_set.add(t[0])
124 | ret = set()
125 | for ment in ret_set:
126 | for split_char in split_chars:
127 | ret |= set(ment.split(split_char))
128 | return ret
129 |
130 |
131 | def get_mention_by_alias(input_text: str, aliasSet):
132 | ret = set()
133 | for alia in aliasSet:
134 | if alia in input_text:
135 | ret.add(alia)
136 | # elif alia in input_text.replace('·',''): # 对“·”进行处理
137 | # text=input_text.replace('·','')
138 | # index=text.index(alia)
139 | # cnt=-1
140 | # start=-1
141 | # end=0
142 | # for i in range(len(input_text)):
143 | # if input_text[i]!='·':
144 | # cnt+=1
145 | # if cnt==index:
146 | # start=i
147 | # if cnt-index+1==len(alia):
148 | # end=i+1
149 | # break
150 | # if start>-1 and end>start:
151 | # ret.add(input_text[start:end])
152 | return ret
153 |
154 |
155 | def fenci(input_text: str):
156 | words = jieba.analyse.extract_tags(input_text, topK=20, withWeight=True)
157 | ret_words = []
158 | for word in words:
159 | for ch in word[0]:
160 | if is_Chinese(ch) or str(ch).isdigit() or str(ch).isalpha():
161 | ret_words.append(word)
162 | break
163 | return ret_words
164 |
165 |
166 | def save_mention(mentionDict: dict, save_file_path, raw_data_path):
167 | print("save_mention start")
168 | data = loadData(raw_data_path)
169 | with open(save_file_path, 'w', encoding='utf-8') as file:
170 | for d in tqdm(data):
171 | d['mention_data'] = []
172 | if d['text_id'] in mentionDict:
173 | for mention in mentionDict[d['text_id']]:
174 | if mention == '':
175 | continue
176 | offset = 0
177 | while str(d['text']).find(mention, offset) != -1:
178 | offset = str(d['text']).index(mention, offset)
179 | d['mention_data'].append({'mention': mention, 'offset': str(offset)})
180 | offset += 1
181 | file.write(json.dumps(d,ensure_ascii=False) + '\n')
182 | print("save_mention end")
183 |
184 |
185 | def precision_of_special_word():
186 | data = loadData('data/train.json')
187 | cnt = 0
188 | cntSum = 0
189 | for d in data:
190 | pred_mention = get_special_mention(d['text'])
191 | cntSum += len(d['mention_data'])
192 | ment = set()
193 | for mention_data in d['mention_data']:
194 | ment.add(mention_data['mention'])
195 | cnt += len(pred_mention & ment)
196 | print("预测mention的准确性:{}%".format(cnt / cntSum * 100))
197 |
198 |
199 | def fit(dataByAlias, save_file_path, raw_data_path):
200 | data = loadData(raw_data_path)
201 | mentionDict = {}
202 | for d in tqdm(data):
203 | pred_mention = set()
204 | # pred_mention = get_special_mention(d['text'])
205 | pred_mention |= get_mention_by_alias(d['text'], dataByAlias.keys())
206 | mentionDict[d['text_id']] = pred_mention
207 | save_mention(mentionDict, save_file_path, raw_data_path)
208 |
209 |
210 | def merge_mentions(mention_data: list):
211 | mention_data.sort(key=lambda x: int(x['offset']))
212 | ret_ments = []
213 | cnt = 0
214 | for i in range(len(mention_data)):
215 | i += cnt
216 | if i >= len(mention_data):
217 | break
218 | mentStr = mention_data[i]['mention']
219 | while i + 1 < len(mention_data) \
220 | and int(mention_data[i]['offset']) + len(mentStr) == int(mention_data[i + 1]['offset']):
221 | mentStr += mention_data[i + 1]['mention']
222 | cnt += 1
223 | mention_data[i]['mention'] = mentStr
224 | ret_ments.append(mention_data[i])
225 | return ret_ments
226 |
227 |
228 | def is_contact_char(mention: dict, text: str):
229 | # if not mention['mention'][-1].encode('UTF-8').isalpha() and :
230 | # return False
231 | if mention['mention'][0].encode('UTF-8').isalpha() \
232 | and int(mention['offset']) > 0 \
233 | and text[int(mention['offset']) - 1].encode('UTF-8').isalpha():
234 | return True
235 | if mention['mention'][-1].encode('UTF-8').isalpha() \
236 | and int(mention['offset']) + len(mention['mention']) < len(text) \
237 | and text[int(mention['offset']) + len(mention['mention'])].encode('UTF-8').isalpha():
238 | return True
239 | return False
240 |
241 |
242 | def process_mentions(mention_data: list, text: str):
243 | mention_data.sort(key=lambda x: len(x['mention']), reverse=True)
244 | mention_data.sort(key=lambda x: int(x['offset']))
245 | delete_ments = []
246 | # if '品茗施工课堂' in text:
247 | # a=1
248 | for ment in mention_data:
249 | if is_word(ment['mention']) is not True \
250 | or len(ment['mention']) < 2 \
251 | or ment['mention'].isdigit() \
252 | or ment['mention'].isnumeric() \
253 | or is_contact_char(ment, text):
254 | delete_ments.append(ment)
255 | for ment in delete_ments:
256 | mention_data.remove(ment)
257 | if len(mention_data) == 0:
258 | return mention_data
259 | map_list = [0 for i in range(int(mention_data[-1]['offset']) + 50)]
260 | valid_ment = []
261 | for ment in mention_data: # 最大匹配模式
262 | if map_list[int(ment['offset'])] == 0:
263 | valid_ment.append(ment)
264 | for i in range(int(ment['offset']), int(ment['offset']) + len(ment['mention'])):
265 | map_list[i] = 1
266 | return valid_ment
267 | # return merge_mentions(valid_ment)
268 |
269 |
270 | def save_user_word(dataByAlias):
271 | with open("data/user_word.txt",'w',encoding='utf-8') as file:
272 | for alia in dataByAlias:
273 | file.write(alia + '\n')
274 |
275 |
276 | if __name__ == '__main__':
277 | pass
278 | # print('start')
279 | # from time import time
280 | #
281 | # dataByAlias, dataBySubjectId = loadDataBase('data/kb_data',model=1)
282 | #
283 | # save_user_word(dataByAlias)
284 | # start = time()
285 | # save_train_file_path = 'data/save_train_mention.json'
286 | # save_dev_file_path = 'data/save_dev_mention.json'
287 | # train_file_path = 'bert/data/train_pre.json'
288 | # dev_file_path = 'data/develop.json'
289 | # print('train fit')
290 | # fit(dataByAlias, save_train_file_path, train_file_path)
291 | # print('dev fit')
292 | # fit(dataByAlias, save_dev_file_path, dev_file_path)
293 | # print("end", 'use time', time() - start)
294 |
--------------------------------------------------------------------------------