├── .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 | * 预览:![image](https://user-images.githubusercontent.com/25412051/61956626-1a506900-aff0-11e9-84dc-b83967581235.JPG) 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 |
25 |
26 |
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 | --------------------------------------------------------------------------------