diff --git a/DONATE.md b/DONATE.md new file mode 100644 index 0000000..7a5cc26 --- /dev/null +++ b/DONATE.md @@ -0,0 +1,11 @@ +如果觉得SoulBook对您有帮助,那么您可以给开发者一些支持: + +- 开源 +- 服务器的费用也是一笔开销 +- 为用户提供更好的搜索环境 +- 一杯咖啡 + +请扫一扫微信支付(留言希望你带上自己的名字^_^): + +donate + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cafe1c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# 基于python3.6镜像来构建owllook镜像 +FROM python:3.6 +MAINTAINER howie6879 +ENV TIME_ZONE=Asia/Shanghai +RUN echo "${TIME_ZONE}" > /etc/timezone \ + && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime +ADD . /owllook +WORKDIR /owllook +RUN pip install --no-cache-dir --trusted-host mirrors.aliyun.com -i http://mirrors.aliyun.com/pypi/simple/ pipenv +RUN pipenv install --skip-lock +RUN find . -name "*.pyc" -delete \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..0d1b2bd --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,13 @@ +## 更新历史记录 + +### 0.1.0 / 2017-03-05: +--- +- 小说的基本搜索解析功能 +- 搜索记录 +- 缓存 +- 书架 +- 书签 +- 登录(暂时不开放注册,可与我申请体验) +- 初步兼容手机(后续跟进) + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..70f1082 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 howie.hu + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..739e047 --- /dev/null +++ b/Pipfile @@ -0,0 +1,35 @@ +[[source]] +verify_ssl = true +name = "pypi" +url = "https://pypi.douban.com/simple/" + +[requires] +python_version = "3.6" + +[dev-packages] + +[packages] +aiohttp = "*" +arrow = "*" +async_timeout = "*" +"bs4" = "*" +cchardet = "*" +gunicorn = "*" +"html5lib" = "*" +Jinja2 = "==2.11.3" +motor = "==1.1" +numpy = "*" +requests = ">=2.20.0" +Sanic = "==0.5.1" +sanic_session = "==0.1.2" +schedule = "*" +uvloop = "*" +pytest = "*" +pymongo = "*" +aiofiles = "*" +pyquery = "*" +ruia-ua = "*" +ruia = ">=0.5.8" +aiocache = "==0.3.2" +aioredis = "==0.3.3" +asyncio_redis = "==0.14.3" diff --git a/README.md b/README.md index 1017942..a1e9ad7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,182 @@ -# SoulBook -在线(解析)阅读小说 +## 基于大佬的项目修改 +早先使用时基于大佬的项目owllook做了一点点修改,更贴近自己的习惯,最近有空正好上传下,方便闲暇时优化,原说明在下面。 + +## owllook - 在线小说搜索引擎 + + [![Python3.6+](https://img.shields.io/badge/python-3.6%2B-orange.svg)](https://github.com/howie6879/owllook) [![license](https://img.shields.io/github/license/howie6879/owllook.svg)](https://github.com/howie6879/owllook) + +`owllook`是一个基于其他搜索引擎构建的垂直小说搜索引擎,owllook目的是让阅读更简单、优雅,让每位读者都有舒适的阅读体验,如**搜书、阅读、收藏、追更、推荐等功能**: + +- ~~演示网址(不维护了,有兴趣自己搭建)~~:[https://www.owllook.net/](https://www.owllook.net/) +- 公众号:[**粮草小说**](https://www.owllook.net/static/novels/img/lcxs_show.jpg),有兴趣的话可以关注下 +- 详细安装介绍:[详细安装介绍](https://mp.weixin.qq.com/s/0CqLiKsyDQ-pVmeo3R-UlA) +- 个人公众号:[老胡的储物柜](https://raw.githubusercontent.com/howie6879/howie6879.github.io/img/pictures/20190529083905.png) + +
+ +衍生项目: + +- 终端看小说: [NIYT](https://github.com/howie6879/NIYT) +- 小说监控工具:[owllook_gui](https://github.com/howie6879/owllook_gui) +- 小说接口:[owllook_api](https://github.com/howie6879/owllook_api) + +**注意**:若将本项目部署并发行,请**声明来源**,本项目纯属**共享学习之用,不得用于商业!** + +### Overview + +`owllook`使用了mongodb储存了用户使用过程中的产生的基本信息,诸如注册信息、搜索小说信息、收藏小说数据等,对于某些必要的缓存,则利用redis进行缓存处理,如小说缓存、session缓存,注意,对于限制数据:都将在24小时删除 + +对于不同网站的小说,页面规则都不尽相同,我希望能够在代码解析后再统一展示出来,这样方便且美观,而不是仅仅跳转到对应网站就完事,清新简洁的阅读体验才是最重要的 + +目前采用的是直接在搜索引擎上进行结果检索,我尽量写少量的规则来完成解析,具体见[规则定义](./docs/规则定义.md),遇到自己喜欢的小说网站,你也可以自己添加解析,`owllook`目前解析了超过 **200+** 网站,追更网站解析了**50+** + +有一些地方需要用到爬虫,比如说排行榜,一些书籍信息等,我不想动用重量级爬虫框架来写,于是我在owllook里面编写了一个很轻量的爬虫框架来做这件事,见 **[ruia](https://github.com/howie6879/ruia)** + +BTW,sanic写界面确实不是很方便,至于为什么写这个,一是想利用`sanic`尽量做成异步服务,二是想就此练习下推荐系统,顺便作为毕业设计 + +#### Installation + +请先装好mongo以及redis,不会安装mongo看[这里](https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-centos-7),`owllook`使用`pipenv`管理虚拟环境,所以使用前请确保安装一个`Python3.6`环境(比如我这里环境路径在`/Users/howie/anaconda3/envs/python36/bin/python3.6`),然后安装好`pipenv` + +mongo以及redis装好后,进入项目目录,依照步骤执行: + +```shell +# 下载代码 +git clone https://github.com/howie6879/owllook +cd owllook +pip install pipenv +# 请先提前创建好Python3.6环境 +pipenv install --python /Users/howie/anaconda3/envs/python36/bin/python3.6 --skip-lock +# 进入虚拟环境 +pipenv shell + +# 先配置好相关数据库配置,具体看`config/dev_config.py` + +# 方案一 +# 运行: +cd owllook +pipenv run gunicorn -c owllook/config/gunicorn.py --worker-class sanic.worker.GunicornWorker owllook.server:app + +# 方案二 推荐 +# 安装docker并打包镜像 +docker build -t howie6879/owllook . +# 实际运行 +docker-compose up -d +# 退出 +docker-compose down +``` + +#### Features + +- 多搜索源 +- 丰富的解析源 +- 界面统一解析 +- 完善的阅读体验 +- 搜索记录 +- 缓存 +- 书架 +- 书签 +- 登录 +- 初步兼容手机(后续跟进) +- 注册(开放注册) +- 上次阅读记录 +- 最新章节 +- 书友推荐(简单地基于相似度进行推荐) +- 目录获取 +- 翻页 +- 搜索排行 +- 章节异步加载 感谢@[mscststs](https://github.com/mscststs) +- 排行榜 - 起点+owllook +- 自带爬虫框架,统一爬虫规范,对爬虫感兴趣的可以看看 - [ruia](https://github.com/howie6879/ruia) + +#### TODO + +- [ ] 评分 +- [ ] 阅读书单 +- [ ] 推荐 +- [ ] 小说圈 - 小说界的消息 + +### Screenshots + +首页: + +![](https://ws1.sinaimg.cn/large/007i3XCUgy1fynmmhyim2j31hc0r140i.jpg) + +搜索: + +![](https://ws1.sinaimg.cn/large/007i3XCUgy1fynmkbqzf3j31h70qz42y.jpg) + +榜单: + +![](https://ws1.sinaimg.cn/large/007i3XCUgy1fynmdwg8tpj31gv0qt42e.jpg) + +书架: + +![books](./docs/imgs/book.jpeg) + +目录解析页: + +![demo](./docs/imgs/chapter.png) + +阅读: + +![content](./docs/imgs/content.png) + +### FAQ + +**为什么首页榜单为空白?** + +这个是根据小说搜索次数显示的,每天刷新一次,使用多了就会有 + +**为什么会出现302跳转?** + +为了防止直接运行服务被恶意域名绑定,所以作出如下修改: + +```shell +vim config/config.py +# 将 true 改为 false +VAL_HOST = os.getenv('VAL_HOST', 'true') +VAL_HOST = os.getenv('VAL_HOST', 'false') +``` + +**小说榜单页面为什么没有内容?** + +需要运行`owllook/spiders/spider_console.py`, +目前代码中是设定60分钟运行一次,运行的时候请酌情更改, +`os.environ['MODE'] = 'PRO'` 这段代码也请注释掉 + +### Reference + +**owllook使用了以下第三方包:** + +- sanic:基于Python 3.5+的异步web服务器 + +- motor:异步的mongodb驱动 + +- ​Jinja2:基于python的模板引擎 + +- aiohttp:异步请求 + +- caddy:基于go的web服务器 + + …...更多见Pipfile,感谢开发者 + +**web框架:** + +- [bootstrap](https://github.com/twbs/bootstrap):Sleek, intuitive, and powerful front-end framework for faster and easier web development. +- [mdui](https://github.com/zdhxiong/mdui ):MDUI 是一个基于 Material Design 的前端框架 + +### License + +`owllook` is offered under the Apache 2 license. + +### Donate + +**捐赠:** + +> 美酒加咖啡,我只要喝一杯~ + +donate + + +感谢以下捐赠者,具体见[捐赠名单](./DONATE.md) ^_^ diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..fb9047d --- /dev/null +++ b/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 17-9-22. +""" +from sys import path as path_sys +from os import path as path_os +# from multiprocessing.managers import BaseManager +# from multiprocessing import freeze_support + +# environment variable +path_sys.append(path_os.dirname(__file__)) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..19794dd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' +services: + web: + image: howie6879/owllook + command: pipenv run gunicorn -c soulbook/config/gunicorn.py --worker-class sanic.worker.GunicornWorker soulbook.server:app + ports: + - "8001:8001" + volumes: + - .:/soulbook \ No newline at end of file diff --git a/docs/imgs/book.jpeg b/docs/imgs/book.jpeg new file mode 100644 index 0000000..ea1d2bb Binary files /dev/null and b/docs/imgs/book.jpeg differ diff --git a/docs/imgs/chapter.png b/docs/imgs/chapter.png new file mode 100644 index 0000000..69e8fef Binary files /dev/null and b/docs/imgs/chapter.png differ diff --git a/docs/imgs/content.png b/docs/imgs/content.png new file mode 100644 index 0000000..1b833f0 Binary files /dev/null and b/docs/imgs/content.png differ diff --git a/docs/imgs/similar.png b/docs/imgs/similar.png new file mode 100644 index 0000000..043ff19 Binary files /dev/null and b/docs/imgs/similar.png differ diff --git a/docs/imgs/the_latest_chapter.jpeg b/docs/imgs/the_latest_chapter.jpeg new file mode 100644 index 0000000..4fb6196 Binary files /dev/null and b/docs/imgs/the_latest_chapter.jpeg differ diff --git a/docs/简介.md b/docs/简介.md new file mode 100644 index 0000000..9c8d1d0 --- /dev/null +++ b/docs/简介.md @@ -0,0 +1,102 @@ +![owllook-demo](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-08-owllook.gif) +## 0.前言 +事情的起因是这样的,前段时间在把之前写的一些同步网络请求代码改成异步的,用的是`aiohttp`,既然是请求,那请求什么呢,就百度吧,请求什么内容呢,我随手一输自己最近看的一本小说,然后作为练手用,就做了这个,我将其取名为`owllook`,准备做成一个推荐系统,所以我就将其部署上去,这才有了这个网站。 + +上一篇介绍了自己在使用`sanic`过程中遇到的一些问题,这次就想介绍下这个[owllook--novels_search](https://github.com/howie6879/novels-search),上面是演示demo,具体可以见[http://www.owllook.net/](http://www.owllook.net/) +**本项目纯属共享学习之用,不得用于商业!** +首先我想说下目前的项目进度: + +v0.1已经上线: + +- 小说的基本搜索解析功能 +- 搜索记录 +- 缓存 +- 书架 +- 书签 +- 登录(**暂时不开放注册,可与我申请体验**) + +TODO: + +- [ ] 注册 +- [ ] 阅读书单 +- [ ] 手机端兼容 +- [ ] 推荐 +- [ ] 排行榜 +- [ ] 个人中心 + +## 1.介绍 +`owllook`的思路很简单,利用百度检索出来的结果,进行过滤解析后再展示,使用的技术如下: + +- sanic:基于Python 3.5+的异步web服务器,快快快 +- sanic_session:sanic的持续会话插件 +- vloop:sanic默认使用uvloop,替代asyncio本身的loop +- motor:异步的mongodb驱动 +- aiohttp:异步请求 +- aiocache:异步缓存,本项目改用了其中的decorator部分,缓存数据库使用redis + +对于用户的一系列操作信息,使用`mongodb`进行存储,而缓存则使用`redis`。 + +对于不同网站的小说,页面规则都不尽相同,我希望能够在代码解析后再统一展示出来,这样方便且美观,而不是仅仅跳转到对应网站就完事,清新简洁的阅读体验才是最重要的。 + +目前采用的是直接在百度上进行结果检索,也不是不能做的更大更全,只是觉得没什么意义,目前的检索结果已经很足够。 + +我尽量写少量的规则来完成解析,具体见[规则定义](https://github.com/howie6879/novels-search/blob/master/docs/%E8%A7%84%E5%88%99%E5%AE%9A%E4%B9%89.md),遇到自己喜欢的小说网站,方便诸位添加解析。 + +一般都是在下班时间编写这个项目,目前`v0.1`版本大概实现。 + +后期我准备将基础功能写好之后,能够实现小说与kindle之间的对接。 + +## 2.解析 +这个项目的思路与技术都比较简单,但是小说网站的解析工作是很难全部解析完毕,下面我将举个例子怎么具体的解析一个网站,欢迎各位添砖加瓦,请先看一遍[规则定义](https://github.com/howie6879/novels-search/blob/master/docs/%E8%A7%84%E5%88%99%E5%AE%9A%E4%B9%89.md)。 +解析也很简单,只要求有点html以及css基础即可。 + +首先进入网站http://www.owllook.net/。 +搜索: +![01](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-10-01.png-blog.howie) + +注意第一条结果显示未解析,这就是我们要解析的对象了,点击进入源网站,审查小说目录对应的元素,这里显示的是:`class="mulu_list`,然后注意其`content_url`类型是`0` +![02](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-10-02.png-blog.howie) + +进入小说章节内容页面,审查元素可以看到`id=htmlContent`,所以`content_selector=htmlContent` + +接下来进入项目,打开`config/rules.py`文件,在RULES字典中加入: + +``` python +'www.ybdu.com': Rules('0', {'class': 'mulu_list'}, {'id': 'htmlContent'}), +``` +这时候再重启服务,刷新页面: +![03](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-10-03.png-blog.howie) +可以看到未解析变成了已解析,这时候点击进入,会出现两种情况: + +- 排版完整,不需要再写样式,此时请直接解析章节内容 +- 排版不行,需要写样 + +现在我演示的这个链接排版就是显示不行,请打开`/static/novels/css/chapter.css`文件,在编写之前,请先看下面这张图: +![04](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-10-04.jpeg-blog.howie) + +图中.mulu_list1:打错了表示区域1,.mulu_list1:表示区域3. + +首先更改区域1的样式,我已经在代码注释写了各个区域样式代码在哪,直接更改就行,比如: + +``` css +/* 区域1 */ +.mulu_list, .acss, .list, #xslist ul, .dirlist, .list-chapter, .list_box ul, #defaulthtml4 table, .article-list > dl, .update, #list .box, .bookcontent > dl, .listmain > dl, .ml_main > dl, #list dl, #chapter_list, .chapterlist, tbody, .mt10, .catalog, #readerlists { + float: left; + overflow: hidden; + padding-bottom: 1px; + margin: auto; + background: #F6F4EC; + border-radius: 15px; +} +``` +可以看到加上了`.mulu_list`,以此类推,最后解析完毕之后如下图: +![05](http://oe7yjec8x.bkt.clouddn.com/howie/2017-03-10-05.png-blog.howie) + +注意章节内容里面一些无关的链接以及原本网页自带的上一章下一章需要隐藏掉哦。 + +## 3.总结 +功能还很简单,解析的网站有很多,希望各位添砖加瓦,容我奉上项目地址:[owllook--novels_search](https://github.com/howie6879/novels-search) + +**仅供分享交流 不可用于商业用途** + + diff --git a/docs/规则定义.md b/docs/规则定义.md new file mode 100644 index 0000000..9c3e799 --- /dev/null +++ b/docs/规则定义.md @@ -0,0 +1,64 @@ +## 规则定义说明: + +对于各类小说网站的解析,只需按照这个要求进行定义,解析便会尽量自动进行,总规则如下: + +```python +# 在项目下config/config.py文件中可以看到 +Rules = namedtuple('Rules', 'content_url chapter_selector content_selector') +# 在自定义字典RULES下面可以看到各个网站的规则,仅限class和id +# demo 'name': Rules('content_url', {chapter_selector}, {content_selector}) +``` + +对于各个`namedtuple`里面的`content_url,chapter_selector,content_selector`,下面通过不同类型的网站一一进行解释: + +- http://www.bxwx9.org/b/57/57181/ + + 打开这个网站,审查元素后你会发现: + + - url输入栏显示内容为`http://www.bxwx9.org/b/57/57181/` + - 主要内容包含在`.TabCss`内 + - 第一章的href值为`8845907.html`,由此可知该章的链接为`http://www.bxwx9.org/b/57/57181/8845907.html` + + 最后写出规则: + + ```python + RULES={ + 'www.bxwx9.org': Rules('0', {'class': 'TabCss'}, {}) + } + ``` + + `key`值起判断该网站是否被解析,注意此时`content_url`为0,这是表示在解析小说目录的时候,每章节对应的url在拼接过程中需要的是当前的url。对于`content_url`,请看下表: + + | 值 | 说明 | + | :----: | :------------------: | + | -1 | 不解析,跳转到原本网页 | + | 0 | 表示章节网页需要当前页面url拼接 | + | 1 | 表示章节链接使用本身自带的链接,不用拼接 | + | netloc | 用域名进行拼接 | + + `chapter_selector`解析章节目录的规则 + + `content_selector`解析目录内容的规则 + + ​ + +- http://www.23us.la/html/0/42/ + + ```python + RULES={ + 'www.23us.la': Rules('http://www.23us.la', {'class': 'inner'}, {}) + } + ``` + +- http://zetianjiba.net + + ```python + RULES={ + 'zetianjiba.net': Rules('1', {'class': 'bg'}, {}) + } + ``` + + ​ + + +`content_selector`值的获取方式同上 \ No newline at end of file diff --git a/examples/latest_chapter.py b/examples/latest_chapter.py new file mode 100644 index 0000000..e22b435 --- /dev/null +++ b/examples/latest_chapter.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +import asyncio +import os +import time +import uvloop + +from pprint import pprint + +os.environ['MODE'] = 'PRO' +from soulbook.fetcher.cache import get_the_latest_chapter, update_all_books + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + +def latest_chapter_task(url): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(get_the_latest_chapter(url)) + loop.run_until_complete(task) + return task.result() + + +def update_all(): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(update_all_books()) + loop.run_until_complete(task) + return task.result() + + +start = time.time() +chapter_url = "/chapter?url=http://www.8535.org/xuanhuan/139075/&novels_name=圣墟" + +result = latest_chapter_task(chapter_url) +pprint(result) +print(time.time() - start) diff --git a/examples/novels/baidu_novels.py b/examples/novels/baidu_novels.py new file mode 100644 index 0000000..8dd54da --- /dev/null +++ b/examples/novels/baidu_novels.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +import aiohttp +import arrow +import asyncio +import async_timeout +import re + +from bs4 import BeautifulSoup +from urllib.parse import urlparse + +from soulbook.fetcher.function import get_random_user_agent +from soulbook.config import CONFIG, LOGGER, BLACK_DOMAIN, RULES, LATEST_RULES + + +async def fetch(client, url, name, is_web): + with async_timeout.timeout(15): + try: + headers = {'user-agent': await get_random_user_agent()} + if is_web: + params = {'wd': name, 'ie': 'utf-8', 'rn': CONFIG.BAIDU_RN, 'vf_bl': 1} + else: + params = {'word': name} + async with client.get(url, params=params, headers=headers) as response: + assert response.status == 200 + LOGGER.info('Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + text = await response.read() + return text + except Exception as e: + LOGGER.exception(e) + return None + + +async def get_real_url(client, url): + with async_timeout.timeout(5): + try: + headers = {'user-agent': await get_random_user_agent()} + async with client.head(url, headers=headers, allow_redirects=True) as response: + assert response.status == 200 + LOGGER.info('Parse url: {}'.format(response.url)) + # text = "" + # try: + # text = await response.text() + # except: + # text = await response.read() + # if text: + # print(text) + # text = re.findall(r'replace\(\"(.*?)\"\)', str(text)) + # text = text[0] if text[0] else "" + url = response.url if response.url else None + return url + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_phone(html): + with async_timeout.timeout(10): + try: + # Get title + data_log = eval(html['data-log']) + url = data_log.get('mu', None) + if not url: + return None + # Get title + title = html.find('h3').get_text() + # Get author and update_time (option) + novel_mess = html.findAll(class_='c-gap-right-large') + basic_mess = [i.get_text() for i in novel_mess] if novel_mess else None + return {'title': title, 'url': url, 'basic_mess': basic_mess} + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_web(html): + with async_timeout.timeout(10): + try: + url = html.find('a').get('href', None) + if not url or 'baidu' in url or urlparse(url).netloc in BLACK_DOMAIN: + return None + netloc = urlparse(url).netloc + is_parse = 1 if netloc in RULES.keys() else 0 + title = html.select('font[size="3"]')[0].get_text() + source = html.select('font[color="#008000"]')[0].get_text() + time = re.findall(r'\d+-\d+-\d+', source) + time = time[0] if time else None + timestamp = 0 + if time: + try: + time_list = [int(i) for i in time.split('-')] + timestamp = arrow.get(time_list[0], time_list[1], time_list[2]).timestamp + except Exception as e: + LOGGER.exception(e) + timestamp = 0 + return {'title': title, 'url': url.replace('index.html', '').replace('Index.html', ''), 'time': time, + 'is_parse': is_parse, + 'timestamp': timestamp, + 'netloc': netloc} + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_web_baidu(client, html): + with async_timeout.timeout(20): + try: + url = html.select('h3.t a')[0].get('href', None) + real_url = await get_real_url(client=client, url=url) if url else None + if real_url: + real_str_url = str(real_url) + netloc = urlparse(real_str_url).netloc + if "http://" + netloc + "/" == real_str_url: + return None + if 'baidu' in real_str_url or netloc in BLACK_DOMAIN: + return None + is_parse = 1 if netloc in RULES.keys() else 0 + title = html.select('h3.t a')[0].get_text() + is_recommend = 1 if netloc in LATEST_RULES.keys() else 0 + # time = re.findall(r'\d+-\d+-\d+', source) + # time = time[0] if time else None + timestamp = 0 + time = "" + # if time: + # try: + # time_list = [int(i) for i in time.split('-')] + # timestamp = arrow.get(time_list[0], time_list[1], time_list[2]).timestamp + # except Exception as e: + # LOGGER.exception(e) + # timestamp = 0 + return {'title': title, 'url': real_str_url.replace('index.html', ''), 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + else: + return None + except Exception as e: + LOGGER.exception(e) + return None + + +async def baidu_search(name, is_web=1): + url = CONFIG.URL_PC if is_web else CONFIG.URL_PHONE + async with aiohttp.ClientSession() as client: + html = await fetch(client=client, url=url, name=name, is_web=is_web) + if html: + soup = BeautifulSoup(html, 'html5lib') + if is_web: + # result = soup.find_all(class_='f') + result = soup.find_all(class_='result') + extra_tasks = [data_extraction_for_web_baidu(client=client, html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + else: + result = soup.find_all(class_='result c-result c-clk-recommend') + extra_tasks = [data_extraction_for_phone(i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + # return await asyncio.gather(*tasks) + done_list, pending_list = await asyncio.wait(tasks) + res = [] + for task in done_list: + res.append(task.result()) + return res + + +if __name__ == '__main__': + import time + + start = time.time() + print(asyncio.get_event_loop().run_until_complete(baidu_search('雪中悍刀行'))) + print(time.time() - start) diff --git a/examples/novels/bing_novels.py b/examples/novels/bing_novels.py new file mode 100644 index 0000000..77aa151 --- /dev/null +++ b/examples/novels/bing_novels.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 21/11/2017. +""" +import aiohttp +import asyncio +import async_timeout + +from bs4 import BeautifulSoup +from urllib.parse import urlparse + +from soulbook.fetcher.function import get_random_user_agent +from soulbook.config import CONFIG, LOGGER, BLACK_DOMAIN, RULES, LATEST_RULES + + +async def fetch(client, url, novels_name): + with async_timeout.timeout(20): + try: + headers = { + 'user-agent': await get_random_user_agent(), + 'referer': "https://www.bing.com/" + } + print(headers) + params = {'q': novels_name, 'ensearch': 0} + async with client.get(url, params=params, headers=headers) as response: + assert response.status == 200 + LOGGER.info('Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + text = await response.read() + return text + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_web_bing(client, html): + with async_timeout.timeout(15): + try: + try: + title = html.select('h2 a')[0].get_text() + url = html.select('h2 a')[0].get('href', None) + netloc = urlparse(url).netloc + url = url.replace('index.html', '').replace('Index.html', '') + if not url or 'baidu' in url or 'baike.so.com' in url or netloc in BLACK_DOMAIN or '.html' in url: + return None + is_parse = 1 if netloc in RULES.keys() else 0 + is_recommend = 1 if netloc in LATEST_RULES.keys() else 0 + # time = html.select('div.b_attribution')[0].get_text() + # time = re.findall(r'\d+-\d+-\d+', time) + # time = time[0] if time else '' + timestamp = 0 + time = '' + # if time: + # try: + # time_list = [int(i) for i in time.split('-')] + # years = str(time_list[0])[-4:] + # timestamp = arrow.get(int(years), time_list[1], time_list[2]).timestamp + # time = years + "-" + str(time_list[1]) + "-" + str(time_list[2]) + # except Exception as e: + # LOGGER.exception(e) + # timestamp = 0 + return {'title': title, + 'url': url, + 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + + except Exception as e: + LOGGER.exception(e) + url, title = None, None + return None + except Exception as e: + LOGGER.exception(e) + return None + + +async def bing_search(novels_name): + url = CONFIG.BY_URL + async with aiohttp.ClientSession() as client: + html = await fetch(client=client, url=url, novels_name=novels_name) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='b_algo') + extra_tasks = [data_extraction_for_web_bing(client=client, html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + return await asyncio.gather(*tasks) + else: + return [] + + +if __name__ == '__main__': + import uvloop + import time + + from pprint import pprint + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + + def novel_task(name): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(bing_search(name)) + loop.run_until_complete(task) + return task.result() + + + start = time.time() + result = novel_task('牧神记 小说 阅读 最新章节') + pprint(result) + print(time.time() - start) diff --git a/examples/novels/duck_go_novels.py b/examples/novels/duck_go_novels.py new file mode 100644 index 0000000..9a00b20 --- /dev/null +++ b/examples/novels/duck_go_novels.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 23/11/2017. +""" +import aiohttp +import asyncio +import async_timeout + +from bs4 import BeautifulSoup +from urllib.parse import parse_qs, urlparse + +from soulbook.fetcher.function import get_random_user_agent +from soulbook.config import CONFIG, LOGGER, BLACK_DOMAIN, RULES, LATEST_RULES + + +async def fetch(client, url, novels_name): + with async_timeout.timeout(20): + try: + headers = { + 'user-agent': await get_random_user_agent(), + 'referer': "https://duckduckgo.com/" + } + params = {'q': novels_name} + # , proxy="http://127.0.0.1:8118" + async with client.get(url, params=params, headers=headers) as response: + assert response.status == 200 + LOGGER.info('Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + text = await response.read() + return text + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_web_duck(client, html): + with async_timeout.timeout(15): + try: + try: + title = html.select('h2 a')[0].get_text() + url = html.select('h2 a')[0].get('href', None) + url = parse_qs(url).get('uddg', ['#'])[0] + netloc = urlparse(url).netloc + url = url.replace('index.html', '').replace('Index.html', '') + if not url or 'baidu' in url or 'baike.so.com' in url or netloc in BLACK_DOMAIN or '.html' in url: + return None + is_parse = 1 if netloc in RULES.keys() else 0 + is_recommend = 1 if netloc in LATEST_RULES.keys() else 0 + # time = html.select('div.b_attribution')[0].get_text() + # time = re.findall(r'\d+-\d+-\d+', time) + # time = time[0] if time else '' + timestamp = 0 + time = '' + return {'title': title, + 'url': url, + 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + + except Exception as e: + LOGGER.exception(e) + url, title = None, None + return None + except Exception as e: + LOGGER.exception(e) + return None + + +async def duck_search(novels_name): + url = CONFIG.DUCKGO_URL + async with aiohttp.ClientSession() as client: + html = await fetch(client=client, url=url, novels_name=novels_name) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='result') + extra_tasks = [data_extraction_for_web_duck(client=client, html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + return await asyncio.gather(*tasks) + else: + return [] + + +if __name__ == '__main__': + import uvloop + import time + + from pprint import pprint + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + + def novel_task(name): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(duck_search(name)) + loop.run_until_complete(task) + return task.result() + + + start = time.time() + result = novel_task('圣墟 小说 阅读 最新章节') + pprint(result) + print(time.time() - start) diff --git a/examples/novels/novels_schedule.py b/examples/novels/novels_schedule.py new file mode 100644 index 0000000..e7e2893 --- /dev/null +++ b/examples/novels/novels_schedule.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +import asyncio +import os +import uvloop + +os.environ['MODE'] = 'PRO' +from soulbook.fetcher.cache import update_all_books + + +def update_all(): + # asyncio.get_event_loop().run_until_complete(update_all_books()) + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(update_all_books(loop=loop, timeout=15)) + loop.run_until_complete(task) + return task.result() or None + + +if __name__ == '__main__': + print(update_all()) diff --git a/examples/novels/parse.py b/examples/novels/parse.py new file mode 100644 index 0000000..b78d587 --- /dev/null +++ b/examples/novels/parse.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +import aiohttp +import asyncio +import time +import uvloop + +from pprint import pprint + +from soulbook.fetcher.function import target_fetch + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + +def novel_task(url): + loop = asyncio.get_event_loop() + + task = asyncio.ensure_future(target_fetch(url, {})) + loop.run_until_complete(task) + return task.result() + + +start = time.time() +# result = novel_task('http://www.shuge.net/html/98/98044') +result = novel_task('http://www.biqugexsw.com/1_1020/') +pprint(result) +print(time.time() - start) diff --git a/examples/novels/so_novels.py b/examples/novels/so_novels.py new file mode 100644 index 0000000..f1b3a34 --- /dev/null +++ b/examples/novels/so_novels.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +import aiohttp +import asyncio +import async_timeout + +from bs4 import BeautifulSoup +from urllib.parse import parse_qs, urlparse + +from soulbook.fetcher.function import get_random_user_agent +from soulbook.config import CONFIG, LOGGER, BLACK_DOMAIN, RULES, LATEST_RULES + + +async def fetch(client, url, novels_name): + with async_timeout.timeout(20): + try: + headers = { + 'User-Agent': await get_random_user_agent(), + 'Referer': "http://www.so.com/haosou.html?src=home" + } + params = {'ie': 'utf-8', 'src': 'noscript_home', 'shb': 1, 'q': novels_name, } + async with client.get(url, params=params, headers=headers) as response: + assert response.status == 200 + LOGGER.info('Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + text = await response.read() + return text + except Exception as e: + LOGGER.exception(e) + return None + + +async def data_extraction_for_web_so(client, html): + with async_timeout.timeout(15): + try: + # 2017.09.09 修改 更加全面地获取title && url + try: + title = html.select('h3 a')[0].get_text() + url = html.select('h3 a')[0].get('href', None) + except Exception as e: + LOGGER.exception(e) + url, title = None, None + return None + # 针对不同的请进行url的提取 + if "www.so.com/link?m=" in url: + url = html.select('h3 a')[0].get('data-url', None) + if "www.so.com/link?url=" in url: + url = parse_qs(urlparse(url).query).get('url', None) + url = url[0] if url else None + + # try: + # url = html.select('h3.res-title a')[0].get('data-url', None) + # title = html.select('h3.res-title a')[0].get_text() + # except IndexError: + # url = html.select('h3.title a')[0].get('href', None) + # url = parse_qs(urlparse(url).query).get('url', None) + # url = url[0] if url else None + # title = html.select('h3.title a')[0].get_text() + # except Exception as e: + # LOGGER.exception(e) + # url, title = None, None + # return None + + # 2017.07.09 此处出现bug url展示形式发生变化 因此对于h3.title a形式依旧不变 但是h3.res-title a则取属性data-url + # url = parse_qs(urlparse(url).query).get('url', None) + # url = url[0] if url else None + + netloc = urlparse(url).netloc + if not url or 'baidu' in url or 'baike.so.com' in url or netloc in BLACK_DOMAIN: + return None + is_parse = 1 if netloc in RULES.keys() else 0 + is_recommend = 1 if netloc in LATEST_RULES.keys() else 0 + time = '' + timestamp = 0 + return {'title': title, 'url': url.replace('index.html', '').replace('Index.html', ''), 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + except Exception as e: + LOGGER.exception(e) + return None + + +async def so_search(novels_name): + url = CONFIG.SO_URL + async with aiohttp.ClientSession() as client: + html = await fetch(client=client, url=url, novels_name=novels_name) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='res-list') + extra_tasks = [data_extraction_for_web_so(client=client, html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + return await asyncio.gather(*tasks) + else: + return [] + + +if __name__ == '__main__': + import uvloop + import time + + from pprint import pprint + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + + def novel_task(name): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(so_search(name)) + loop.run_until_complete(task) + return task.result() + + + start = time.time() + result = novel_task('圣墟 小说 阅读 最新章节') + pprint(result) + print(time.time() - start) diff --git a/examples/user_recommend.py b/examples/user_recommend.py new file mode 100644 index 0000000..2a72b99 --- /dev/null +++ b/examples/user_recommend.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +import asyncio +import collections +import os +from operator import itemgetter + +import uvloop + +os.environ['MODE'] = 'DEV' + +from pprint import pprint +from copy import deepcopy +from soulbook.database.mongodb import MotorBase +from soulbook.recommend.cosinesimilarity import CosineSimilarity +from soulbook.fetcher.function import get_time + + +async def get_user_tag(): + motor_db = MotorBase().get_db() + user_tag_cursor = motor_db.user_tag.find({}, {'data': 1, 'user': 1, '_id': 0}) + result = {} + user_book_dict = {} + + async for document in user_tag_cursor: + if document['data']: + user_book_dict[document['user'].replace('.', '·')] = document['data'] + result[document['user'].replace('.', '·')] = document['data']['user_tag'] + + for key, value in result.items(): + if not value: + continue + print("\nUser:", key) + print("User tags:", set(value)) + print("User books:", user_book_dict[key]["user_novels"]) + result_copy = deepcopy(result) + del result_copy[key] + cos = CosineSimilarity(value, result_copy) + vector = cos.create_vector() + resultDic = cos.calculate(vector) + print("相似用户:") + pprint(resultDic[:10]) + # pprint(type(resultList[1])) + booksDic = collections.defaultdict(float) + for userBooks in resultDic: + for simuser, simrate in userBooks.items(): + # print(simuser, simrate) + for book in user_book_dict[simuser]["user_novels"]: + booksDic[book] += simrate + # print(booksDic) + # 推荐20本书,注意这里的推荐并没有去除用户收藏的书籍 + recommend = sorted(booksDic.items(), key=itemgetter(1), reverse=True)[0:20] + print("书籍推荐:") + pprint(recommend) + recommend_novels = [book for book, simrate in recommend] + await motor_db.user_recommend.update_one( + {"user": key}, + {'$set': {'similar_user': resultDic, 'user_tag': result[key], 'recommend_novels': recommend_novels, + "updated_at": get_time()}}, upsert=True) + + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + +def get_user_tag_test(): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(get_user_tag()) + loop.run_until_complete(task) + return task.result() + + +if __name__ == '__main__': + get_user_tag_test() diff --git a/examples/user_tag.py b/examples/user_tag.py new file mode 100644 index 0000000..5de5426 --- /dev/null +++ b/examples/user_tag.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +import asyncio +import os +import pymongo +import uvloop + +os.environ['MODE'] = 'PRO' + +from pprint import pprint +from urllib.parse import parse_qs, urlparse + +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.function import get_time + +# mongo +MONGODB_SERVER = "localhost" +MONGODB_PORT = 27017 +MONGODB_USERNAME = "" +MONGODB_PASSWORD = "" +MONGODB_DB = "soulbook" +MONGODB_COLLECTION = "all_novels_info" + + +class Novels(object): + def __init__(self): + _mongo_uri = 'mongodb://{account}{host}:{port}/{database}'.format( + account='{username}:{password}@'.format( + username=MONGODB_USERNAME, + password=MONGODB_PASSWORD) if MONGODB_USERNAME else '', + host='localhost', + port=27017, + database=MONGODB_DB) + connection = pymongo.MongoClient(_mongo_uri) + db = connection[MONGODB_DB] + self.collection = db[MONGODB_COLLECTION] + self.result_dict = dict() + + def search_name(self, name): + if name in self.result_dict: + return self.result_dict[name] if self.result_dict[name] else False + result = self.collection.find_one({'novel_name': name}) + if not result: + self.result_dict[name] = False + return False + self.result_dict[name] = {'author': result['author'], 'novels_type': result['novels_type'].split('#')} + return self.result_dict[name] + + +async def get_tag(): + motor_db = MotorBase().get_db() + novels = Novels() + # 获取所有书架链接游标 + books_url_cursor = motor_db.user_message.find({}, {'books_url.book_url': 1, 'user': 1, '_id': 0}) + async for document in books_url_cursor: + if document: + books_url = document.get('books_url', None) + user = document['user'] + if books_url: + all_user = {user + '_novels': [], user + '_tag': [], user + '_author': []} + for book_url in books_url: + chapter_url = book_url['book_url'] + novels_name = parse_qs(urlparse(chapter_url).query).get('novels_name', '')[0] + all_user[user + '_novels'].append(novels_name) + novels_info = novels.search_name(novels_name) + if novels_info: + all_user[user + '_author'].append(novels_info['author']) + all_user[user + '_tag'].extend(novels_info['novels_type']) + data = { + 'user_novels': all_user[user + '_novels'], + 'user_tag': all_user[user + '_tag'], + 'user_author': all_user[user + '_author'], + } + await motor_db.user_tag.update_one( + {"user": user}, + {'$set': {'data': data, "updated_at": get_time()}}, upsert=True) + pprint(data) + + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + +def tag_test(): + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(get_tag()) + loop.run_until_complete(task) + return task.result() + + +if __name__ == '__main__': + tag_test() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1d3f2b8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,67 @@ +# sentry_sdk==1.5.10 +# async_timeou +# requests>=2.20.0 +# Sanic==0.5.1 +# sanic_session==0.1.2 +# pyquery +# asyncio_redis==0.14.3 + + +aiocache==0.3.2 +aiofiles==0.8.0 +aiohttp==3.8.1 +aiomcache==0.6.0 +aioredis==0.3.3 +aiosignal==1.2.0 +arrow==1.2.2 +async-timeout==4.0.2 +asyncio-redis==0.14.3 +asynctest==0.13.0 +attrs==21.4.0 +beautifulsoup4==4.11.1 +bs4==0.0.1 +cchardet==2.1.7 +certifi==2021.10.8 +charset-normalizer==2.0.12 +cssselect==1.1.0 +frozenlist==1.2.0 +gunicorn==20.1.0 +hiredis==2.0.0 +html5lib==1.1 +httptools==0.4.0 +idna==3.3 +idna-ssl==1.1.0 +importlib-metadata==4.8.3 +iniconfig==1.1.1 +Jinja2==2.11.3 +lxml==4.8.0 +MarkupSafe==2.0.1 +motor==1.1 +multidict==5.2.0 +numpy==1.19.5 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pymongo==3.4.0 +pyparsing==3.0.8 +pyquery==1.4.3 +pytest==7.0.1 +python-dateutil==2.8.2 +requests==2.27.1 +ruia==0.8.4 +ruia-ua==0.0.2 +sanic==0.5.1 +sanic-session==0.1.2 +schedule==1.1.0 +sentry-sdk==1.5.10 +six==1.16.0 +soupsieve==2.3.2.post1 +tomli==1.2.3 +typing-extensions==4.1.1 +ujson==4.3.0 +urllib3==1.26.9 +uvloop==0.14.0 +webencodings==0.5.1 +websockets==9.1 +yarl==1.7.2 +zipp==3.6.0 diff --git a/requirements2.txt b/requirements2.txt new file mode 100644 index 0000000..12c95b9 --- /dev/null +++ b/requirements2.txt @@ -0,0 +1,58 @@ +aiocache==0.3.2 +aiofiles==0.8.0 +aiohttp==3.8.1 +aiomcache==0.6.0 +aioredis==0.3.3 +aiosignal==1.2.0 +arrow==1.2.2 +async-timeout==4.0.2 +asyncio-redis==0.14.3 +asynctest==0.13.0 +attrs==21.4.0 +beautifulsoup4==4.11.1 +bs4==0.0.1 +cchardet==2.1.7 +certifi==2021.10.8 +charset-normalizer==2.0.12 +cssselect==1.1.0 +frozenlist==1.2.0 +gunicorn==20.1.0 +hiredis==2.0.0 +html5lib==1.1 +httptools==0.4.0 +idna==3.3 +idna-ssl==1.1.0 +importlib-metadata==4.8.3 +iniconfig==1.1.1 +Jinja2==2.11.3 +lxml==4.8.0 +MarkupSafe==2.0.1 +motor==1.1 +multidict==5.2.0 +numpy==1.19.5 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pymongo==3.4.0 +pyparsing==3.0.8 +pyquery==1.4.3 +pytest==7.0.1 +python-dateutil==2.8.2 +requests==2.27.1 +ruia==0.8.4 +ruia-ua==0.0.2 +sanic==0.5.1 +sanic-session==0.1.2 +schedule==1.1.0 +sentry-sdk==1.5.10 +six==1.16.0 +soupsieve==2.3.2.post1 +tomli==1.2.3 +typing-extensions==4.1.1 +ujson==4.3.0 +urllib3==1.26.9 +uvloop==0.14.0 +webencodings==0.5.1 +websockets==9.1 +yarl==1.7.2 +zipp==3.6.0 diff --git a/soulbook/__init__.py b/soulbook/__init__.py new file mode 100644 index 0000000..fb9047d --- /dev/null +++ b/soulbook/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 17-9-22. +""" +from sys import path as path_sys +from os import path as path_os +# from multiprocessing.managers import BaseManager +# from multiprocessing import freeze_support + +# environment variable +path_sys.append(path_os.dirname(__file__)) \ No newline at end of file diff --git a/soulbook/config/__init__.py b/soulbook/config/__init__.py new file mode 100644 index 0000000..0d30e5c --- /dev/null +++ b/soulbook/config/__init__.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import os +import logging + +from .rules import * + +logging_format = "[%(asctime)s] %(process)d-%(levelname)s " +logging_format += "%(module)s::%(funcName)s():l%(lineno)d: " +logging_format += "%(message)s" + +logging.basicConfig( + format=logging_format, + level=logging.DEBUG +) +LOGGER = logging.getLogger() + + +def load_config(): + """ + Load a config class + """ + + os.environ['MODE'] = 'PRO' + mode = os.environ.get('MODE', 'DEV') + LOGGER.info('soulbook 启动模式:{}'.format(mode)) + try: + # if mode == 'PRO': + # from .pro_config import ProConfig + # return ProConfig + # elif mode == 'DEV': + # from .dev_config import DevConfig + # return DevConfig + # else: + # from .dev_config import DevConfig + # return DevConfig + from .dev_config import DevConfig + return DevConfig + except ImportError: + from .config import Config + return Config + + +CONFIG = load_config() diff --git a/soulbook/config/config.py b/soulbook/config/config.py new file mode 100644 index 0000000..8229946 --- /dev/null +++ b/soulbook/config/config.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +import os + + +class Config(): + """ + Basic config for soulbook + """ + + # Application config + DEBUG = True + VAL_HOST = os.getenv('VAL_HOST', 'true') + FORBIDDEN = ['139.199.198.228'] + HOST = ['127.0.0.1:8001', '0.0.0.0:8001', '127.0.0.1:8002', '0.0.0.0:8002'] + TIMEZONE = 'Asia/Shanghai' + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36' + WEBSITE = dict( + IS_RUNNING=True, + TOKEN='', + AUTHOR_LATEST_COUNT=5, + ) + + SCHEDULED_DICT = dict( + SPIDER_INTERVAL=int(os.getenv('SPIDER_INTERVAL', 120)), + NOVELS_INTERVAL=int(os.getenv('SPIDER_INTERVAL', 180)), + ) + + # Engine config + URL_PHONE = 'https://m.baidu.com/s' + URL_PC = 'http://www.baidu.com/s' + # BAIDU_RN = 15 + BAIDU_RN = 15 + SO_URL = "https://www.so.com/s" + BY_URL = "https://www.bing.com/search" + DUCKGO_URL = "https://duckduckgo.com/html" + + REMOTE_SERVER = { + 'proxy_server': 'http://0.0.0.0:8002/' + } diff --git a/soulbook/config/dev_config.py b/soulbook/config/dev_config.py new file mode 100644 index 0000000..5af8c8d --- /dev/null +++ b/soulbook/config/dev_config.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +import os + +from .config import Config + + +class DevConfig(Config): + """ + Dev config for soulbook + """ + + VAL_HOST = os.getenv('VAL_HOST', 'false') + + # Database config + REDIS_DICT = dict( + IS_CACHE=True, + REDIS_ENDPOINT=os.getenv('REDIS_ENDPOINT', "localhost"), + REDIS_PORT=int(os.getenv('REDIS_PORT', 6379)), + REDIS_PASSWORD=os.getenv('REDIS_PASSWORD', None), + REDIS_PORT=6379, + CACHE_DB=0, + SESSION_DB=1, + POOLSIZE=10, + ) + MONGODB = dict( + MONGO_HOST=os.getenv('MONGO_HOST', "127.0.0.1"), + MONGO_PORT=int(os.getenv('MONGO_PORT', 27017)), + MONGO_USERNAME="", + MONGO_PASSWORD="", + DATABASE='soulbook', + ) + + # website + WEBSITE = dict( + IS_RUNNING=os.getenv('OWLLOOK_IS_RUNNING', True), + TOKEN=os.getenv('OWLLOOK_TOKEN', '') + ) + + AUTH = { + "Owllook-Api-Key": os.getenv('OWLLOOK_API_KEY', "your key") + } diff --git a/soulbook/config/gunicorn.py b/soulbook/config/gunicorn.py new file mode 100644 index 0000000..83dc0a2 --- /dev/null +++ b/soulbook/config/gunicorn.py @@ -0,0 +1,21 @@ +# gunicorn config +# gunicorn -c config/gunicorn.py --worker-class sanic.worker.GunicornWorker server:app +import os + +os.environ['MODE'] = 'PRO' + +WORKERS = os.getenv('WORKERS', 2) +TIMEOUT = os.getenv('TIMEOUT', 60) + +bind = '0.0.0.0:8001' +backlog = 2048 + +workers = WORKERS +worker_connections = 1000 +keepalive = 2 + +spew = False +daemon = False +umask = 0 +timeout = TIMEOUT +preload = True diff --git a/soulbook/config/rules.py b/soulbook/config/rules.py new file mode 100644 index 0000000..2c907a2 --- /dev/null +++ b/soulbook/config/rules.py @@ -0,0 +1,672 @@ +#!/usr/bin/env python +from collections import namedtuple + +####################################### 规则 ########################################### +# DOMAIN +BLACK_DOMAIN = ['www.17k.com', 'mm.17k.com', 'www.xs8.cn', 'www.zongheng.com', 'yunqi.qq.com', 'chuangshi.qq.com', + 'book.qidian.com', 'www.soduso.com', 'pages.book.qq.com', 'book.km.com', 'www.lread.net', + 'www.0dsw.com', 'www.5200xsb.com', 'www.80txt.com', 'www.sodu.tw', 'www.shuquge.com', + 'www.shenmanhua.com', 'xiaoshuo.sogou.com', 'www.999wx.com', 'zetianji8.com', 'www.bookso.net', + 'm.23us.com', 'www.qbxsw.com', 'www.zhuzhudao.com', 'www.shengyan.org', 'www.360doc.com', + 'www.ishuo.cn', 'read.qidian.com', 'www.yunlaige.com', 'www.qidian.com', 'www.sodu888.com', + 'www.siluke.cc', 'read.10086.cn', 'www.pbtxt.com', 'c4txt.com', 'www.bokon.net', 'www.sikushu.net', + 'www.is028.cn', 'www.tadu.com', 'www.kudu8.com', 'www.bmwen.com', 'www.5858xs.com', 'www.yiwan.com', + 'www.x81zw.com', 'www.123du.cc', 'www.chashu.cc', '20xs.com', 'www.haxwx.net', 'www.dushiwenxue.com', + "www.yxdown.com", 'www.jingcaiyuedu.com', 'www.zhetian.org', 'www.xiaoshuo02.com', 'www.xiaoshuo77.com', + 'www.868xh.com', 'dp.changyou.com', 'www.iyouman.com', 'www.qq717.com', 'www.yznn.com', "www.69w.cc", + "www.doupocangqiong1.com", "www.manhuatai.com", "www.5wxs.com", "www.ggshuji.com", "www.msxf.net", + "www.mianhuatang.la", "www.boluoxs.com", "www.lbiquge.top", "www.69shu.com", "www.qingkan520.com", + "book.douban.com", "movie.douban.com", "www.txshuku.com", "lz.book.sohu.com", "www.3gsc.com.cn", + "www.txtshu365.com", "www.517yuedu.com", "www.baike.com", "read.jd.com", "www.zhihu.com", "wshuyi.com", + "www.19lou.tw", "www.chenwangbook.com", "www.aqtxt.com", "book.114la.com", "www.niepo.net", + "me.qidian.com", "www.gengd.com", "www.77l.com", "www.geilwx.com", "www.97xiao.com", "www.anqu.com", + "www.wuxiaxs.com", "yuedu.163.com", "b.faloo.com", "bbs.qidian.com", "jingji.qidian.com", "www.sodu.cc", + "forum.qdmm.com", "www.qdmm.com", "game.91.com", "www.11773.com", "mt.sohu.com", "book.dajianet.com", + "haokan.17k.com", "www.qmdsj.com", "www.jjwxc.net", "ishare.iask.sina.com.cn", "www.cmread.com", + "www.52ranwen.net", "www.dingdianzw.com", "www.topber.com", "www.391k.com", "www.qqxzb.com", + "www.zojpw.com", "www.pp8.com", "www.bxwx.org", "www.hrsxb.com", "www.497.com", "www.d8qu.com", + "www.duwanjuan.com", "www.05935.com", "book.zongheng.com", "www.55x.cn", "www.freexs.cn", + "xiaoshuo.360.cn", "www.3kw.cc", "www.gzbpi.com", "book.sina.com.cn", "www.vodtw.com", "wenda.so.com", + "product.dangdang.com", "www.chuiyao.com", "novel.slieny.com", "www.bilibili.com", "donghua.dmzj.com", + "www.yaojingweiba.com", "www.qb5200.com", "www.520tingshu.com", "www.567zw.com", "www.zjrxz.com", + "v.qq.com", "blog.sina.com.cn", "www.hackhome.com", "news.fznews.com.cn", "www.jingyu.com", + "news.so.com", "www.sodu3.com", "vipreader.qidian.com", "www.mozhua9.com", "www.iqiyi.com", + "xs.sogou.com"] + +# 针对某些网站检索出来的地址和真正的目录地址不一样从而进行替换 +REPLACE_RULES = { + "www.miaobige.com": { + 'old': 'miaobige.com/book/', + 'new': 'miaobige.com/read/' + }, + "www.5ccc.net": { + 'old': '5ccc.net/wksz_info/', + 'new': '5ccc.net/xiaoshuo/' + }, + "www.7kankan.com": { + 'old': '7kankan.com/files/article/info/', + 'new': '7kankan.com/files/article/html/' + }, + "www.xqingdou.net": { + 'old': 'xqingdou.net/book_', + 'new': 'xqingdou.net/chapter_' + }, + "www.wuyanxia.net": { + 'old': 'wuyanxia.net/book/', + 'new': 'wuyanxia.net/read/' + }, + "www.263zw.com": { + 'old': '263zw.com/402770/', + 'new': '263zw.com/402770/list/' + }, +} + +# 搜索引擎检索优先级 +ENGINE_PRIORITY = ['bing', '360', 'baidu', 'bing', 'duck_go'] +# ENGINE_PRIORITY = ['baidu', '360', 'bing', 'duck_go'] + +# Rules +Rules = namedtuple('Rules', 'content_url chapter_selector content_selector') +LatestRules = namedtuple('LatestRules', 'plan meta_value selector') + +# 获取小说最新章节 +PLAN_01 = LatestRules( + True, + {'latest_chapter_name': 'og:novel:latest_chapter_name', 'latest_chapter_url': 'og:novel:latest_chapter_url'}, + None, +) + +LATEST_RULES = { + "www.biqugex.com": PLAN_01, + "www.x23us.com": PLAN_01, + "www.23us.la": PLAN_01, + "www.sqsxs.com": PLAN_01, + "www.nuomi9.com": PLAN_01, + "www.biquge.info": PLAN_01, + "www.biquge.tw": PLAN_01, + "www.qu.la": PLAN_01, + "www.ybdu.com": PLAN_01, + "www.wenxuemi.com": PLAN_01, + "www.biquge.com": PLAN_01, + "www.23us.cc": PLAN_01, + "www.xs222.com": PLAN_01, + "www.lewen8.com": PLAN_01, + "www.bqg5200.com": PLAN_01, + "www.vodtw.com": PLAN_01, + "www.6mao.com": PLAN_01, + "www.biquge.sh": PLAN_01, + "www.touxiang.la": PLAN_01, + "www.bxquge.com": PLAN_01, + "www.beidouxin.com": PLAN_01, + "www.biquge.lu": PLAN_01, + "www.263zw.com": PLAN_01, + "www.3qzone.com": PLAN_01, + "wwww.yooread.com": PLAN_01, + # "www.suimeng.la": PLAN_01, + "www.bequge.com": PLAN_01, + "www.biquku.co": PLAN_01, + "www.xbqge.com": PLAN_01, + "www.aiquxs.com": PLAN_01, + "www.23us.com": PLAN_01, + "www.biqiuge.com": PLAN_01, + "www.ddbiquge.com": PLAN_01, + "www.abocms.cn": PLAN_01, + "www.a306.com": PLAN_01, + "www.liewen.cc": PLAN_01, + "www.8535.org": PLAN_01, + "www.dingdianzw.com": PLAN_01, + "www.biquge.cc": PLAN_01, + "www.111bz.org": PLAN_01, + "www.biqugebook.com": PLAN_01, + "www.e8zw.com": PLAN_01, + "www.xqqxs.com": PLAN_01, + "tianyibook.la": PLAN_01, + "www.lingdianksw.com": PLAN_01, + "www.qb5.tw": PLAN_01, + "www.quanben.com": PLAN_01, + "www.58xs.com": PLAN_01, + "www.biqukan.com": PLAN_01, + "www.yssm.org": PLAN_01, + "www.81zw.com": PLAN_01, + "www.ymoxuan.com": PLAN_01, + "www.mytxt.cc": PLAN_01, + "www.woquge.com": PLAN_01, + "www.biquguo.com": PLAN_01, + "www.8jzw.cc": PLAN_01, + "www.biquge.tv": PLAN_01, + "www.biquge5200.com": PLAN_01, + "www.8jzw.com": PLAN_01, + "www.23xsw.cc": PLAN_01, + "www.miaobige.com": PLAN_01, + "www.xs.la": PLAN_01, + "www.44pq.co": PLAN_01, + "www.50zw.la": PLAN_01, + "www.33xs.com": PLAN_01, + "www.zwdu.com": PLAN_01, + "www.ttzw.com": PLAN_01, + "www.zanghaihuatxt.com": PLAN_01, + "www.kuxiaoshuo.com": PLAN_01, + "www.biqudu.com": PLAN_01, + "www.biqugeg.com": PLAN_01, + "www.23txt.com": PLAN_01, + "www.baquge.tw": PLAN_01, + "www.23qb.com": PLAN_01, + "www.lread.cc": PLAN_01, + "www.biqudao.com": PLAN_01, + "www.laidudu.com": PLAN_01, + "www.kxs7.com": PLAN_01, + "www.biquguan.com": PLAN_01, + "www.biquta.com": PLAN_01, + "www.xs98.com": PLAN_01, + "www.bqge.org": PLAN_01, + "www.58xs.tw": PLAN_01, + "www.187ks.com": PLAN_01, + "www.yikanxiaoshuo.com": PLAN_01, + "www.23zw.me": PLAN_01, + "www.37zw.net": PLAN_01, + "www.biquge.cm": PLAN_01, + "www.kanshu58.com": PLAN_01, + "www.biqumo.com": PLAN_01, + "www.mpxiaoshuo.com": PLAN_01, + "www.23wx.cm": PLAN_01, + "www.biquge.jp": PLAN_01, + "www.biqugexsw.com": PLAN_01, + "www.biqu6.com": PLAN_01, + "www.xiuxs.com": PLAN_01, + "www.booktxt.net": PLAN_01, + "www.biqule.com": PLAN_01, + "www.biquzi.com": PLAN_01, + "www.biquku.la": PLAN_01, + "www.00ksw.org": PLAN_01, + "www.bqg.cc": PLAN_01, + "www.biqugezw.com": PLAN_01, + "www.bbiquge.com": PLAN_01, + "www.aikantxt.la": PLAN_01, + "www.biquge.com.tw": PLAN_01, + "www.xxbiquge.com": PLAN_01, + "www.biquwo.com": PLAN_01, + "www.ixs.cc": PLAN_01, + + # 其他规则 + # "www.50331.net": LatestRules( + # False, + # None, + # {'content_url': "http://www.50331.net/", 'tag': 'span.zzjie a'} + # ) +} + +RULES = { + # demo 'name': Rules('content_url', {chapter_selector}, {content_selector}) + # content_url=1 表示章节链接使用本身自带的链接,不用拼接 + # content_url=0 表示章节网页需要当前页面url拼接 + # 'www.biqule.com': Rules('www.biqule.com', {'class': 'box_con'},{}), + # 'www.lingdiankanshu.com': Rules('www.lingdiankanshu.com', {'class': 'box_con'}, {}), + # 'www.hhlwx.com': Rules('www.hhlwx.co', {'class': 'chapterlist'},{}), + 'www.biquwu.cc': Rules('https://www.biquwu.cc/', {'class': 'box_con'}, {'id': 'content'}), + 'www.quyuege.com': Rules('0', {'class': 'mod-article-list'}, {'class': 'page-content'}), + # 已解析 + 'www.biqugex.com': Rules('http://www.biqugex.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.bbiquge.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.info': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.37zw.net': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquku.la': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.sh': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.co': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.00ksw.org': Rules('0', {'class': 'ml_list'}, {'id': 'articlecontent'}), + # 已解析 + 'www.bqge.org': Rules('http://www.bqge.org/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.aikantxt.la': Rules('http://www.aikantxt.la/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquzi.com': Rules('http://www.biquzi.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.bqg.cc': Rules('http://www.bqg.cc/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.jp': Rules('0', {'id': 'list'}, {'id': 'content'}), + # 已解析 + 'www.vipzw.com': Rules('http://www.vipzw.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge5200.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.zanghaihuatxt.com': Rules('http://www.zanghaihuatxt.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.xiuxs.com': Rules('http://www.xiuxs.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.1biquge.com': Rules('http://www.1biquge.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xiaoshuowan.com': Rules('http://www.xiaoshuowan.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqugela.com': Rules('http://www.biqugela.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqu6.com': Rules('http://www.biqu6.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.zhuaji.org': Rules('0', {'tag': 'dd'}, {'id': 'content'}), + # 已解析 + 'www.sqsxs.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.tv': Rules('http://www.biquge.tv/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquta.com': Rules('https://www.biquta.com/', {'class': 'box_con'}, {'id': 'content'}), + # # 已解析 + 'www.xbiquge.la': Rules('http://www.xbiquge.la/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.cm': Rules('http://www.biquge.cm/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.23qb.com': Rules('https://www.23qb.com/', {'id': 'chapterList'}, {'id': 'TextContent'}), + # 已解析 + # 'www.txtwan.com': Rules('http://www.txtwan.com/', {'id': 'chapterList'}, {'id': 'txt'}), + # 已解析 + 'www.biqugexsw.com': Rules('http://www.biqugexsw.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.kuxiaoshuo.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.laidudu.com': Rules('http://www.laidudu.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.kanshu58.com': Rules('0', {'class': 'chapters'}, {'id': 'content'}), + # 已解析 + 'www.mpxiaoshuo.com': Rules('0', {'class': 'mulu_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.23zw.me': Rules('0', {'id': 'chapter_list'}, {'id': 'text_area'}), + # 已解析 + 'www.187ks.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.58xs.tw': Rules('http://www.58xs.tw/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquguan.com': Rules('http://www.biquguan.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xs98.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.kxs7.com': Rules('http://www.kxs7.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqudao.com': Rules('https://www.biqudao.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.nuomi9.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'book.sfacg.com': Rules('http://book.sfacg.com/', {'class': 'story-catalog'}, {'tag': 'p'}), + # 已解析 + 'www.7kshu.com': Rules('0', {'id': 'chapterlist'}, {'id': 'content'}), + # 已解析 + 'www.lread.cc': Rules('http://www.lread.cc/', {'class': 'box_con'}, {'id': 'booktext'}), + # 已解析 + 'www.baquge.tw': Rules('http://www.baquge.tw/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqudu.com': Rules('https://www.biqudu.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqugeg.com': Rules('http://www.biqugeg.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.23txt.com': Rules('http://www.23txt.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.ttzw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.zwdu.com': Rules('http://www.zwdu.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.33xs.com': Rules('http://www.33xs.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.50zw.la': Rules('0', {'class': 'chapterlist'}, {'id': 'htmlContent'}), + # 已解析 + 'www.44pq.co': Rules('0', {'class': 'chapterlist'}, {'id': 'BookText'}), + # 已解析 + 'www.wddsnxn.org': Rules('1', {'class': 'booklist'}, {'id': 'BookText'}), + # 已解析 + 'mianzhuan.wddsnxn.org': Rules('1', {'class': 'booklist'}, {'id': 'BookText'}), + # 已解析 + 'www.a306.com': Rules('http://www.a306.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xs52.com': Rules('0', {'id': 'chapter_list'}, {'id': 'text_c'}), + # 已解析 + 'www.xs.la': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.23xsw.cc': Rules('http://www.23xsw.cc/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.8jzw.com': Rules('http://www.8jzw.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquguo.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.woquge.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.zhonghuawuxia.com': Rules('1', {'class': 'list'}, {'id': 'content'}), + # 已解析 + 'www.mytxt.cc': Rules('http://www.mytxt.cc/', {'class': 'story_list_m62topxs'}, {'class': 'detail_con_m62topxs'}), + # 已解析 + 'www.136txt.com': Rules('1', {'class': 'directory_con'}, {'id': 'chapterContent'}), + # 已解析 + 'www.xs74.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.yssm.org': Rules('0', {'class': 'chapterlist'}, {'id': 'content'}), + # 已解析 + 'www.luoxia.com': Rules('1', {'class': 'book-list'}, {'tag': 'p'}), + # 已解析 + 'www.sbkk88.com': Rules('http://www.sbkk88.com/', {'class': 'leftList'}, {'id': 'f_article'}), + # 已解析 + 'www.dxsxs.com': Rules('http://www.dxsxs.com/', {'id': 'yuedu'}, {'class': 'zw'}), + # 已解析 + 'www.wenku8.com': Rules('0', {'class': 'css'}, {'id': 'content'}), + # 已解析 + 'www.xqingdou.net': Rules('http://www.xqingdou.net/', {'class': 'dirconone'}, {'id': 'chapter_content'}), + # 已解析 + 'www.zuowe.com': Rules('http://www.zuowe.com/', {'class': 'book_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.biqugek.com': Rules('1', {'class': 'book_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.wuyanxia.net': Rules('http://www.wuyanxia.net/', {'class': 'zjlist4'}, {'id': 'htmlContent'}), + # 已解析 + 'www.50331.net': Rules('http://www.50331.net/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.wenxuemi.com': Rules('http://www.wenxuemi.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xs222.com': Rules('http://www.xs222.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.lewen8.com': Rules('http://www.lewen8.com/', {'id': 'chapterlist'}, {'id': 'content'}), + # 已解析 + 'www.5ccc.net': Rules('0', {'class': 'acss'}, {'id': 'content'}), + # 已解析 + # 'www.suimeng.la': Rules('0', {'class': 'acss'}, {'id': 'ccontent'}), + # 已解析 + 'www.bqg5200.com': Rules('http://www.bqg5200.com/', {'id': 'readerlist'}, {'id': 'content'}), + # 已解析 + 'www.vodtw.com': Rules('0', {'class': 'insert_list'}, {'class': 'contentbox'}), + # 已解析 + 'www.6mao.com': Rules('http://www.6mao.com/', {'class': 'liebiao_bottom'}, {'id': 'neirong'}), + # 已解析 + 'www.touxiang.la': Rules('http://www.touxiang.la/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.7kankan.com': Rules('0', {'class': 'uclist'}, {'id': 'content'}), + # 已解析 + 'www.biqugetw.com': Rules('http://www.biqugetw.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'gdbzkz.com': Rules('1', {'class': 'mulu'}, {'class': 'content-body'}), + # 已解析 + 'www.gdbzkz.com': Rules('1', {'class': 'mulu'}, {'class': 'content-body'}), + # 已解析 + 'www.freexs.cn': Rules('0', {'class': 'readout'}, {'class': 'shuneirong'}), + # 已解析 + 'www.bxquge.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.beidouxin.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.3qzone.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.97xs.net': Rules('1', {'class': 'box'}, {'id': 'htmlContent'}), + # 已解析 + 'www.7dsw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.263zw.com': Rules('1', {'class': 'chapter'}, {'id': 'chapterContent'}), + # 已解析 + 'www.biquge5.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.yooread.com': Rules('http://www.yooread.com', {'id': 'chapterList'}, {'tag': 'p'}), + # 已解析 + 'www.xs82.com': Rules('0', {'class': 'chapterlist'}, {'id': 'content'}), + # 已解析 + 'www.kanshuhai.com': Rules('0', {'id': 'book'}, {'id': 'content'}), + # 已解析 + 'www.bequge.com': Rules('https://www.bequge.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 请求失败 + # 'www.biquge5200.com': Rules('1', {'id': 'list'}, {'id': 'content'}), + # 已解析 + 'www.biquku.co': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xbqge.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.aiquxs.com': Rules('0', {'id': 'list'}, {'id': 'booktext'}), + # 已解析 + # 'www.piaotian.com': Rules('0', {'class': 'centent'}, {'class': 'fonts_mesne'}), + # 已解析 + 'www.ttshu.com': Rules('http://www.ttshu.com', {'class': 'border'}, {'id': 'content'}), + # 已解析 + 'www.23us.com': Rules('0', {'id': 'at'}, {'id': 'contents'}), + # 已解析 + 'www.x23us.com': Rules('0', {'id': 'at'}, {'id': 'contents'}), + # 已解析 + 'www.23wx.cc': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.23wx.cm': Rules('0', {'class': 'book_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.ddbiquge.com': Rules('http://www.ddbiquge.com', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.abocms.cn': Rules('http://www.abocms.cn/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.liewen.cc': Rules('https://www.liewen.cc/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.heiyange.com': Rules('http://www.heiyange.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.8535.org': Rules('0', {'class': 'booklist'}, {'class': 'txtc'}), + # 已解析 + 'www.dingdianzw.com': Rules('http://www.dingdianzw.com/', {'id': 'bgdiv'}, {'id': 'content'}), + # 已解析 + 'www.biquge.cc': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.lewenxiaoshuo.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.111bz.org': Rules('http://www.111bz.org/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqugebook.com': Rules('http://www.biqugebook.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.e8zw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.xqqxs.com': Rules('0', {'class': 'box_con'}, {'class': 'content'}), + # 已解析 + 'www.139book.com': Rules('http://www.139book.com/', {'class': 'list_box'}, {'class': 'box_box'}), + # 已解析 + 'www.jcdf99.com': Rules('0', {'class': 'list_box'}, {'id': 'content'}), + # 已解析 + 'www.tianzeba.com': Rules('http://www.tianzeba.com/', {'class': 'chapterlist'}, {'id': 'BookText'}), + # 已解析 + 'www.kanshuwangzhan.com': Rules('0', {'id': 'chapterlist'}, {'id': 'booktext'}), + # 已解析 + 'tianyibook.la': Rules('http://tianyibook.la/', {'class': 'chapterlist'}, {'id': 'BookText'}), + # 已解析 + 'www.quanben.net': Rules('http://www.quanben.net/', {'class': 'chapterlist'}, {'id': 'BookText'}), + # 已解析 + # 'www.zhetian.org': Rules('http://www.zhetian.org', {'class': 'body '}, {'class': 'content'}), + # 已解析 + 'www.lingdianksw.com': Rules('0', {'class': 'acss'}, {'id': 'ccontent'}), + # 已解析 + 'www.qb5.tw': Rules('http://www.qb5.tw/', {'class': 'zjbox'}, {'id': 'content'}), + # 已解析 + 'www.ybdu.com': Rules('0', {'class': 'mulu_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.quanben.com': Rules('0', {'class': 'mulu_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.fhxs.com': Rules('1', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquge.biz': Rules('http://www.biquge.biz/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.58xs.com': Rules('http://www.58xs.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqukan.com': Rules('http://www.biqukan.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.shuyuelou.com': Rules('http://www.shuyuelou.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.mangg.com': Rules('http://www.mangg.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.50zw.com': Rules('0', {'class': 'chapterlist'}, {'id': 'htmlContent'}), + # 已解析 + 'www.lingdiankanshu.co': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqiku.com': Rules('http://www.biqiku.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.duilianku.com': Rules('http://www.duilianku.com/', {'id': 'list'}, {'class': 'chapter'}), + # 已解析 + 'www.5xiaxiaoshuo.com': Rules('http://www.5xiaxiaoshuo.com/', {'class': 'art_listmain_main'}, {'id': 'content'}), + # 已解析 + 'www.81xsw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.wxguan.com': Rules('http://www.wxguan.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.qb5200.tw': Rules('http://www.qb5200.tw/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.fox2008.cn': Rules('http://www.fox2008.cn/', {'class': 'book'}, {'id': 'chapterContent'}), + # 已解析 + 'www.22zw.com': Rules('0', {'class': 'acss'}, {'id': 'content'}), + # 已解析 + 'www.k6uk.com': Rules('0', {'class': 'acss'}, {'id': 'content'}), + # 已解析 + 'www.126shu.com': Rules('http://www.126shu.com/', {'id': 'list'}, {'id': 'content'}), + # 已解析 + 'www.kooxs.com': Rules('0', {'class': 'list'}, {'id': 'content'}), + # 已解析 + 'www.shubaotxt.com': Rules('0', {'class': 'list'}, {'id': 'content'}), + # 已解析 + 'www.muyuge.com': Rules('1', {'id': 'xslist'}, {'id': 'content'}), + # 已解析 + # 'www.daizhuzai.com': Rules('http://www.daizhuzai.com', {'class': 'dirlist'}, {'class': 'content'}), + # 已解析 + 'www.biqu.la': Rules('0', {'class': 'book_list'}, {'id': 'htmlContent'}), + # 已解析 + 'shushu.com.cn': Rules('http://shushu.com.cn/', {'id': 'dirsort01'}, {'id': 'content'}), + # 已解析 + 'www.shuhai.com': Rules('0', {'class': 'box_chap'}, {'id': 'readcon'}), + # 已解析 + 'www.37yue.com': Rules('0', {'class': 'list-chapter'}, {'class': 'chapter'}), + # 已解析 + 'www.35zw.com': Rules('0', {'class': 'book_list'}, {'id': 'htmlContent'}), + # 已解析 + 'www.xinshu.in': Rules('http://www.xinshu.in/', {'class': 'list_box'}, {'class': 'box_box'}), + # 已解析 + 'www.lwxs520.com': Rules('0', {'class': 'dccss'}, {'id': 'content'}), + # 已解析 + 'www.lwxs.la': Rules('http://www.lwxs.la/', {'id': 'defaulthtml4'}, {'id': 'content'}), + # 已解析 + 'www.biqule.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.33yq.com': Rules('1', {'class': 'box_con'}, {'class': 'zhangjieTXT'}), + # 已解析 + 'www.dishuge.com': Rules('1', {'class': 'update'}, {'tag': 'p'}), + # 已解析 + 'www.qu.la': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.shuge.net': Rules('http://www.shuge.ne/t', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.daomengren.com': Rules('http://www.daomengren.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.81zw.net': Rules('http://www.81zw.net/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.09xs.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.fhxiaoshuo.com': Rules('1', {'class': 'box_con'}, {'class': 'zhangjieTXT'}), + # 已解析 + 'www.yikanxiaoshuo.com': Rules('http://www.yikanxiaoshuo.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.1xiaoshuo.com': Rules('http://www.1xiaoshuo.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.kanshu.la': Rules('http://www.kanshu.la/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.kbiquge.com': Rules('http://www.kbiquge.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.00ksw.net': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.booktxt.net': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'wanmeishijiexiaoshuo.org': Rules('1', {'class': 'bg'}, {'class': 'content'}), + # 已解析 + 'www.sosoxiaoshuo.cc': Rules('http://www.sosoxiaoshuo.cc/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.ciluke.com': Rules('0', {'id': 'list'}, {'id': 'content'}), + # 已解析 + 'www.81zw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.cilook.net': Rules('0', {'id': 'cl_content'}, {'id': 'content'}), + # 已解析 + 'www.baoliny.com': Rules('http://www.baoliny.com/', {'class': 'readerListShow'}, {'id': 'content'}), + # 已解析 + 'www.biquge.tw': Rules('http://www.biquge.tw/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.7788xs.net': Rules('http://www.7788xs.net/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.06sy.com': Rules('http://www.06sy.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqumo.com': Rules('https://www.biqumo.com/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.kanshuzhe.com': Rules('http://www.kanshuzhe.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biqiuge.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.lwxs.com': Rules('0', {'class': 'box_con'}, {'id': 'TXT'}), + # 已解析 + 'www.biqugezw.com': Rules('http://www.biqugezw.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 经常跳到无法预料的网站 故禁止 + # 'www.is028.cn': Rules('http://www.biquge.com.tw', {'class': 'box_con'}, {'id': 'content'}), + # www.is028.cn会跳转到http://www.biquge.com.tw + 'www.biquge.com.tw': Rules('http://www.biquge.com.tw/', {'class': 'box_con'}, {'id': 'content'}), + # 'www.xs82.com': Rules('-1', {'class': 'chapterlist'}, {'id': 'content'}), + # 已解析 + 'www.shuqizw.com': Rules('http://www.shuqizw.com/', {'class': 'article_texttitleb'}, {'id': 'book_text'}), + # 已解析 + 'read.ixdzs.com': Rules('0', {'class': 'catalog'}, {'class': 'content'}), + # 已解析 + 'www.shumilou.net': Rules('0', {'class': 'chapterlist'}, {'id': 'BookText'}), + # 已解析 + 'www.8shuw.com': Rules('1', {'class': 'chapterlist'}, {'id': 'readtext'}), + # 已解析 + # 'www.ttshu.com': Rules('http://www.ttshu.com', {'class': 'border'}, {'id': 'content'}), + # 已解析 + 'www.heiyan.la': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.bbsa5.com': Rules('1', {'class': 'panel'}, {'class': 'content-body'}), + # 已解析 + 'www.tycqxs.com': Rules('http://www.tycqxs.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.miaobige.com': Rules('https://www.miaobige.com/', {'id': 'readerlists'}, {'id': 'content'}), + # 已解析 + 'www.dashubao.net': Rules('0', {'class': 'ml_main'}, {'class': 'yd_text2'}), + # 已解析 + 'www.23zw.com': Rules('0', {'id': 'chapter_list'}, {'id': 'text_area'}), + # 已解析 + 'www.23us.la': Rules('http://www.23us.la/', {'class': 'inner'}, {'id': 'content'}), + # 已解析 + 'www.2952.cc': Rules('0', {'class': 'inner'}, {'id': 'content'}), + # 已解析 + 'www.23us.cc': Rules('0', {'class': 'inner'}, {'id': 'content'}), + # 已解析 + 'www.13xs.com': Rules('0', {'class': 'box_con'}, {'id': 'booktext'}), + # 已解析 + 'www.tsxsw.com': Rules('0', {'class': 'bdsub'}, {'id': 'contents'}), + # 已解析 + 'www.ymoxuan.com': Rules('1', {'class': 'mulu'}, {'id': 'content'}), + 'www.ixs.cc': Rules('https:', {'class': 'mulu'}, {'id': 'content'}), + # 已解析 + 'zetianjiba.net': Rules('1', {'class': 'bg'}, {'class': 'content'}), + # 已解析 + 'www.37zw.com': Rules('0', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.555zw.com': Rules('0', {'class': 'dir'}, {'id': 'content'}), + # 已解析 + 'www.jueshitangmen.info': Rules('1', {'class': 'bg'}, {'class': 'content'}), + # 已解析 + 'www.bxwx9.org': Rules('0', {'class': 'TabCss'}, {'id': 'content'}), + # 已解析 + 'www.xxbiquge.com': Rules('https://www.xxbiquge.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.biquwo.com': Rules('https://www.biquwo.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.fs23.com': Rules('http://www.fs23.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.longtengx.com': Rules('http://www.longtengx.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.lingyu.org': Rules('http://www.lingyu.org/', {'class': 'mt10'}, {'id': 'htmlContent'}), + # 已解析 + 'www.aszw8.com': Rules('0', {'id': 'at'}, {'id': 'contents'}), + # 已解析 + 'www.23us.so': Rules('1', {'id': 'at'}, {'id': 'contents'}), + # 已解析 + 'www.biquge.lu': Rules('http://www.biquge.lu/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.3zm.net': Rules('http://www.3zm.net/', {'class': 'listmain'}, {'id': 'content'}), + # 已解析 + 'www.biquge.com': Rules('http://www.biquge.com/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + 'www.kanshuzhong.com': Rules('0', {'class': 'bookcontent'}, {'class': 'textcontent'}), + # 已解析 + 'www.siluke.tw': Rules('http://www.siluke.tw/', {'class': 'box_con'}, {'id': 'content'}), + # 已解析 + # 'www.ttshu.com': Rules('http://www.ttshu.com', {'class': 'border'}, {'id': 'content'}), + +} diff --git a/soulbook/data/user_agents.txt b/soulbook/data/user_agents.txt new file mode 100644 index 0000000..86f3cca --- /dev/null +++ b/soulbook/data/user_agents.txt @@ -0,0 +1,899 @@ +Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0) +Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0) +Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0) +Mozilla/4.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0) +Mozilla/1.22 (compatible; MSIE 10.0; Windows 3.1) +Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US)) +Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7 +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; InfoPath.3; MS-RTC LM 8; .NET4.0C; .NET4.0E) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0 +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/11.0.696.57) +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) chromeframe/10.0.648.205 +Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; chromeframe/11.0.696.57) +Mozilla/5.0 ( ; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) +Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 5.1; Trident/5.0) +Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 7.1; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C) +Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; AskTB5.5) +Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; InfoPath.2; .NET4.0C; .NET4.0E) +Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C) +Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; FDM; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E; Tablet PC 2.0) +Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320) +Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322) +Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) +Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727) +Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 3.0.04506.30) +Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.0; Trident/4.0; FBSMTWB; .NET CLR 2.0.34861; .NET CLR 3.0.3746.3218; .NET CLR 3.5.33652; msn OptimizedIE8;ENUS) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; Media Center PC 6.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.3; .NET4.0C; .NET4.0E; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MS-RTC LM 8) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 3.0) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; msn OptimizedIE8;ZHCN) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8; InfoPath.3; .NET4.0C; .NET4.0E) chromeframe/8.0.552.224 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8; .NET4.0C; .NET4.0E; Zune 4.7; InfoPath.3) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8; .NET4.0C; .NET4.0E; Zune 4.7) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; Zune 4.0) +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; MS-RTC LM 8; Zune 4.7) +Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre +Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20100101 Firefox/4.2a1pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.2a1pre) Gecko/20110323 Firefox/4.2a1pre +Mozilla/5.0 (X11; Linux x86_64; rv:2.0b9pre) Gecko/20110111 Firefox/4.0b9pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b9pre) Gecko/20101228 Firefox/4.0b9pre +Mozilla/5.0 (Windows NT 5.1; rv:2.0b9pre) Gecko/20110105 Firefox/4.0b9pre +Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b8pre) Gecko/20101114 Firefox/4.0b8pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101213 Firefox/4.0b8pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101128 Firefox/4.0b8pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b8pre) Gecko/20101114 Firefox/4.0b8pre +Mozilla/5.0 (Windows NT 5.1; rv:2.0b8pre) Gecko/20101127 Firefox/4.0b8pre +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8 +Mozilla/5.0 (Windows NT 6.1; rv:2.0b7pre) Gecko/20100921 Firefox/4.0b7pre +Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b7) Gecko/20101111 Firefox/4.0b7 +Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b7) Gecko/20100101 Firefox/4.0b7 +Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b6pre) Gecko/20100903 Firefox/4.0b6pre +Mozilla/5.0 (Windows NT 6.1; rv:2.0b6pre) Gecko/20100903 Firefox/4.0b6pre Firefox/4.0b6pre +Mozilla/5.0 (X11; Linux x86_64; rv:2.0b4) Gecko/20100818 Firefox/4.0b4 +Mozilla/5.0 (X11; Linux i686; rv:2.0b3pre) Gecko/20100731 Firefox/4.0b3pre +Mozilla/5.0 (Windows NT 5.2; rv:2.0b13pre) Gecko/20110304 Firefox/4.0b13pre +Mozilla/5.0 (Windows NT 5.1; rv:2.0b13pre) Gecko/20110223 Firefox/4.0b13pre +Mozilla/5.0 (X11; Linux i686; rv:2.0b12pre) Gecko/20110204 Firefox/4.0b12pre +Mozilla/5.0 (X11; Linux i686; rv:2.0b12pre) Gecko/20100101 Firefox/4.0b12pre +Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b11pre) Gecko/20110128 Firefox/4.0b11pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110131 Firefox/4.0b11pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110129 Firefox/4.0b11pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b11pre) Gecko/20110128 Firefox/4.0b11pre +Mozilla/5.0 (Windows NT 6.1; rv:2.0b11pre) Gecko/20110126 Firefox/4.0b11pre +Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b11pre) Gecko/20110126 Firefox/4.0b11pre +Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b10pre) Gecko/20110118 Firefox/4.0b10pre +Mozilla/5.0 (Windows NT 6.1; rv:2.0b10pre) Gecko/20110113 Firefox/4.0b10pre +Mozilla/5.0 (X11; Linux i686; rv:2.0b10) Gecko/20100101 Firefox/4.0b10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:2.0b10) Gecko/20110126 Firefox/4.0b10 +Mozilla/5.0 (Windows NT 6.1; rv:2.0b10) Gecko/20110126 Firefox/4.0b10 +Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:2.0) Gecko/20110307 Firefox/4.0 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0 +Mozilla/5.0 (X11; Arch Linux i686; rv:2.0) Gecko/20110321 Firefox/4.0 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows NT 6.1; rv:2.0) Gecko/20110319 Firefox/4.0 +Mozilla/5.0 (Windows NT 6.1; rv:1.9) Gecko/20100101 Firefox/4.0 +Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8 +Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 +Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.25 (jaunty) Firefox/3.8 +Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.3a5pre) Gecko/20100526 Firefox/3.7a5pre +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 +Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2b5) Gecko/20091204 Firefox/3.6b5 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2) Gecko/20091218 Firefox 3.6b5 +Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2b4) Gecko/20091124 Firefox/3.6b4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2b4) Gecko/20091124 Firefox/3.6b4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2b1) Gecko/20091014 Firefox/3.6b1 GTB5 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2a1pre) Gecko/20090428 Firefox/3.6a1pre +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2a1pre) Gecko/20090405 Firefox/3.6a1pre +Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.9.2a1pre) Gecko/20090405 Ubuntu/9.04 (jaunty) Firefox/3.6a1pre +Mozilla/5.0 (Windows; Windows NT 5.1; es-ES; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre +Mozilla/5.0 (Windows; Windows NT 5.1; en-US; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre +Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2a1pre) Gecko/20090402 Firefox/3.6a1pre (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9 +Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.2.9) Gecko/20100913 Firefox/3.6.9 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9 ( .NET CLR 3.5.30729; .NET CLR 4.0.20506) +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9 +Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.2.8) Gecko/20101230 Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.8) Gecko/20100804 Gentoo Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.8) Gecko/20100723 SUSE/3.6.8-0.1.1 Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.2.8) Gecko/20100722 Ubuntu/10.04 (lucid) Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux i686; fi-FI; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.8) Gecko/20100727 Firefox/3.6.8 +Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.9.2.8) Gecko/20100725 Gentoo Firefox/3.6.8 +Mozilla/5.0 (X11; U; FreeBSD i386; de-CH; rv:1.9.2.8) Gecko/20100729 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-BR; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.8) Gecko/20100722 AskTbADAP/3.9.1.14019 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; he; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.8) Gecko/20100722 Firefox 3.6.8 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0C) +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.8) Gecko/20100722 Firefox 3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.3) Gecko/20121221 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-TW; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 +Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.7) Gecko/20100809 Fedora/3.6.7-1.fc14 Firefox/3.6.7 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.7) Gecko/20100723 Fedora/3.6.7-1.fc13 Firefox/3.6.7 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100726 CentOS/3.6-3.el5.centos Firefox/3.6.7 +Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.2.7) Gecko/20100713 Firefox/3.6.7 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.7 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-PT; rv:1.9.2.7) Gecko/20100713 Firefox/3.6.7 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 GTB7.1 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 GTB7.0 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; pt-PT; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 +Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 ( .NET CLR 3.5.30729; .NET4.0E) +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.4) Gecko/20100614 Ubuntu/10.04 (lucid) Firefox/3.6.4 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.4) Gecko/20100625 Gentoo Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-TW; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; cs; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100527 Firefox/3.6.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100527 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-CA; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.0 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.4) Gecko/20100513 Firefox/3.6.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.4) Gecko/20100503 Firefox/3.6.4 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; nb-NO; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; cs; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3pre) Gecko/20100405 Firefox/3.6.3plugin1 ( .NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.3) Gecko/20100403 Fedora/3.6.3-4.fc13 Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100403 Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.3) Gecko/20100401 SUSE/3.6.3-1.1 Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100404 Ubuntu/10.04 (lucid) Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3 +Mozilla/5.0 (X11; U; Linux AMD64; en-US; rv:1.9.2.3) Gecko/20100403 Ubuntu/10.10 (maverick) Firefox/3.6.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.0 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; cs; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; ca; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 GTB7.0 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.2) Gecko/20100316 AskTbSPC2/3.9.1.14019 Firefox/3.6.2 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 GTB6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 ( .NET CLR 3.0.04506.648) +Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 ( .NET CLR 3.0.04506.30) +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.7; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10pre) Gecko/20100902 Ubuntu/9.10 (karmic) Firefox/3.6.1pre +Mozilla/5.0 (X11; U; Linux x86_64; ja-JP; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.10 (maverick) Firefox/3.6.16 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/9.10 (karmic) Firefox/3.6.16 FirePHP/0.5 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 ( .NET CLR 3.5.30729; .NET4.0E) +Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en; rv:1.9.1.13) Gecko/20100914 Firefox/3.6.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.16) Gecko/20110319 AskTbUTR/3.11.3.15590 Firefox/3.6.16 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16pre) Gecko/20110304 Ubuntu/10.10 (maverick) Firefox/3.6.15pre +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.15) Gecko/20110303 Ubuntu/10.04 (lucid) Firefox/3.6.15 FirePHP/0.5 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.15) Gecko/20110330 CentOS/3.6-1.el5.centos Firefox/3.6.15 +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 ( .NET CLR 3.5.30729; .NET4.0C) FirePHP/0.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.2.15) Gecko/20110303 AskTbBT4/3.11.3.15590 Firefox/3.6.15 ( .NET CLR 3.5.30729; .NET4.0C) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.14pre) Gecko/20110105 Firefox/3.6.14pre +Mozilla/5.0 (X11; U; Linux armv7l; en-US; rv:1.9.2.14) Gecko/20110224 Firefox/3.6.14 MB860/Version.0.43.3.MB860.AmericaMovil.en.MX +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-AU; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14 GTB7.1 ( .NET CLR 3.5.30729) +Mozilla/5.0 Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.2.13) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; nb-NO; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101223 Gentoo Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101219 Gentoo Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 Red Hat/3.6-3.el4 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101206 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-NZ; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.2.13) Gecko/20101206 Ubuntu/9.10 (karmic) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.2.13) Gecko/20101206 Red Hat/3.6-2.el5 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux x86_64; da-DK; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux MIPS32 1074Kf CPS QuadCore; en-US; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux i686; pt-BR; rv:1.9.2.13) Gecko/20101209 Fedora/3.6.13-1.fc13 Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.2.13) Gecko/20101206 Ubuntu/9.10 (karmic) Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101209 CentOS/3.6-2.el5.centos Firefox/3.6.13 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13 +Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.9.2.12) Gecko/20101030 Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux x86_64; es-MX; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.04 (lucid) Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.2.12) Gecko/20101027 Fedora/3.6.12-1.fc13 Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.2.12) Gecko/20101026 SUSE/3.6.12-0.7.1 Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101102 Gentoo Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101102 Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux ppc; fr; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux i686; ko-KR; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.12) Gecko/20101027 Ubuntu/10.10 (maverick) Firefox/3.6.12 GTB7.1 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.12) Gecko/20101027 Fedora/3.6.12-1.fc13 Firefox/3.6.12 +Mozilla/5.0 (X11; FreeBSD x86_64; rv:2.0) Gecko/20100101 Firefox/3.6.12 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 ( .NET CLR 3.5.30729; .NET4.0E) +Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET CLR 3.5.21022) +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; de; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 GTB5 +Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.2.11) Gecko/20101028 CentOS/3.6-2.el5.centos Firefox/3.6.11 +Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2.3pre) Gecko/20100723 Firefox/3.6.11 +Mozilla/5.0 (Windows; U; Windows NT 5.2; ru; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11 +Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11 ( .NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux x86_64; pt-BR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 GTB7.1 +Mozilla/5.0 (X11; U; Linux x86_64; el-GR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 GTB7.1 +Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10) Gecko/20100915 Ubuntu/9.04 (jaunty) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.2.11) Gecko/20101013 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; en-CA; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100915 Ubuntu/9.10 (karmic) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100915 Ubuntu/10.04 (lucid) Firefox/3.6.10 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.10) Gecko/20100914 SUSE/3.6.10-0.3.1 Firefox/3.6.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ro; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; nl; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.1) Gecko/20100122 firefox/3.6.1 +Mozilla/5.0(Windows; U; Windows NT 7.0; rv:1.9.2) Gecko/20100101 Firefox/3.6 +Mozilla/5.0(Windows; U; Windows NT 5.2; rv:1.9.2) Gecko/20100101 Firefox/3.6 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100222 Ubuntu/10.04 (lucid) Firefox/3.6 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100130 Gentoo Firefox/3.6 +Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2pre) Gecko/20100312 Ubuntu/9.04 (jaunty) Firefox/3.6 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100128 Gentoo Firefox/3.6 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100115 Ubuntu/10.04 (lucid) Firefox/3.6 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 FirePHP/0.4 +Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20100101 Firefox/3.6 +Mozilla/5.0 (X11; FreeBSD i686) Firefox/3.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU; rv:1.9.2) Gecko/20100105 MRA 5.6 (build 03278) Firefox/3.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; lt; rv:1.9.2) Gecko/20100115 Firefox/3.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.3a3pre) Gecko/20100306 Firefox3.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100806 Firefox/3.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.3) Gecko/20100401 Firefox/3.6;MEGAUPLOAD 1.0 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ar; rv:1.9.2) Gecko/20100115 Firefox/3.6 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.2) Gecko/20100115 Firefox/3.6 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.2) Gecko/20100105 Firefox/3.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.2) Gecko/20100115 Firefox/3.6 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b5pre) Gecko/20090517 Firefox/3.5b4pre (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4pre) Gecko/20090409 Firefox/3.5b4pre +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4pre) Gecko/20090401 Firefox/3.5b4pre +Mozilla/5.0 (X11; U; Linux i686; nl-NL; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 GTB5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; fr; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b4) Gecko/20090423 Firefox/3.5b4 GTB5 +Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.9) Gecko/20100402 Ubuntu/9.10 (karmic) Firefox/3.5.9 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.9) Gecko/20100330 Fedora/3.5.9-2.fc12 Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1.1 Firefox/3.5.9 GTB7.0 +Mozilla/5.0 (X11; U; Linux x86_64; es-CL; rv:1.9.1.9) Gecko/20100402 Ubuntu/9.10 (karmic) Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1.1 Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux i686; hu-HU; rv:1.9.1.9) Gecko/20100330 Fedora/3.5.9-1.fc12 Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.9) Gecko/20100317 SUSE/3.5.9-0.1 Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100401 Ubuntu/9.10 (karmic) Firefox/3.5.9 GTB7.1 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.9) Gecko/20100315 Ubuntu/9.10 (karmic) Firefox/3.5.9 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.4) Gecko/20091028 Ubuntu/9.10 (karmic) Firefox/3.5.9 +Mozilla/5.0 (Windows; U; Windows NT 6.1; tr; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; hu; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 +Mozilla/5.0 (Windows; U; Windows NT 6.1; et; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 +Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; es-ES; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.2.13) Gecko/20101203 Firefox/3.5.9 (de) +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB7.0 (.NET CLR 3.0.30618) +Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc11 Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8) Gecko/20100318 Gentoo Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux i686; ja-JP; rv:1.9.1.8) Gecko/20100216 Fedora/3.5.8-1.fc12 Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux i686; es-AR; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.8) Gecko/20100214 Ubuntu/9.10 (karmic) Firefox/3.5.8 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 +Mozilla/5.0 (X11; U; FreeBSD i386; ja-JP; rv:1.9.1.8) Gecko/20100305 Firefox/3.5.8 +Mozilla/5.0 (Windows; U; Windows NT 6.1; sl; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729) FirePHP/0.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB6 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB7.0 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100305 Gentoo Firefox/3.5.7 +Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ; rv:1.9.1.7) Gecko/20100106 Ubuntu/9.10 (karmic) Firefox/3.5.7 +Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.7) Gecko/20091222 SUSE/3.5.7-1.1.1 Firefox/3.5.7 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6 +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.2; fr; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.0.04506.648) +Mozilla/5.0 (Windows; U; Windows NT 5.1; fa; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.7) Gecko/20091221 MRA 5.5 (build 02842) Firefox/3.5.7 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.6) Gecko/20100117 Gentoo Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.6) Gecko/20091216 Fedora/3.5.6-1.fc11 Firefox/3.5.6 GTB6 +Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.1.6) Gecko/20091201 SUSE/3.5.6-1.1.1 Firefox/3.5.6 GTB6 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.6) Gecko/20100118 Gentoo Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 GTB6 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 GTB7.0 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.6) Gecko/20091201 SUSE/3.5.6-1.1.1 Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux i686; cs-CZ; rv:1.9.1.6) Gecko/20100107 Fedora/3.5.6-1.fc12 Firefox/3.5.6 +Mozilla/5.0 (X11; U; Linux i686; ca; rv:1.9.1.6) Gecko/20091215 Ubuntu/9.10 (karmic) Firefox/3.5.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; id; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.4 (build 02647) Firefox/3.5.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.5 (build 02842) Firefox/3.5.6 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 MRA 5.5 (build 02842) Firefox/3.5.6 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 GTB6 (.NET CLR 3.5.30729) FBSMTWB +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729) FBSMTWB +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.5) Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.5 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8pre) Gecko/20091227 Ubuntu/9.10 (karmic) Firefox/3.5.5 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.5) Gecko/20091114 Gentoo Firefox/3.5.5 +Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; uk; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.1.5) Gecko/Firefox/3.5.5 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.5) Gecko/20091102 MRA 5.5 (build 02842) Firefox/3.5.5 +Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.8.1) Gecko/20091102 Firefox/3.5.5 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; pl; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 FBSMTWB +Mozilla/5.0 (X11; U; Linux x86_64; ja; rv:1.9.1.4) Gecko/20091016 SUSE/3.5.4-1.1.2 Firefox/3.5.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 (.NET CLR 3.5.30729) FBSMTWB +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 ( .NET CLR 3.5.30729; .NET4.0E) +Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4 +Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.9.1.5) Gecko/20091109 Ubuntu/9.10 (karmic) Firefox/3.5.3pre +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20090914 Slackware/13.0_stable Firefox/3.5.3 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 +Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.1.3) Gecko/20091020 Ubuntu/9.10 (karmic) Firefox/3.5.3 +Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090919 Firefox/3.5.3 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090912 Gentoo Firefox/3.5.3 FirePHP/0.3 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB5 +Mozilla/5.0 (X11; U; FreeBSD i386; ru-RU; rv:1.9.1.3) Gecko/20090913 Firefox/3.5.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.5.3;MEGAUPLOAD 1.0 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ko; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; fi; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 2.0.50727; .NET CLR 3.0.30618; .NET CLR 3.5.21022; .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; bg; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; ko; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; pl; rv:1.9.1.2) Gecko/20090911 Slackware Firefox/3.5.2 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.2) Gecko/20090803 Slackware Firefox/3.5.2 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.2) Gecko/20090803 Firefox/3.5.2 Slackware +Mozilla/5.0 (X11; U; Linux i686; ru-RU; rv:1.9.1.2) Gecko/20090804 Firefox/3.5.2 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090729 Slackware/13.0 Firefox/3.5.2 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (X11; U; Linux i686 (x86_64); fr; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 GTB7.1 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; es-MX; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 5.1; uk; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 +Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 FirePHP/0.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.16) Gecko/20101130 AskTbMYC/3.9.1.14019 Firefox/3.5.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; it; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.16) Gecko/20101130 MRA 5.4 (build 02647) Firefox/3.5.16 ( .NET CLR 3.5.30729; .NET4.0C) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.16) Gecko/20101130 AskTbPLTV5/3.8.0.12304 Firefox/3.5.16 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.16) Gecko/20101130 Firefox/3.5.16 GTB7.1 +Mozilla/5.0 (X11; U; Linux x86_64; it; rv:1.9.1.15) Gecko/20101027 Fedora/3.5.15-1.fc12 Firefox/3.5.15 +Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.15) Gecko/20101027 Fedora/3.5.15-1.fc12 Firefox/3.5.15 +Mozilla/5.0 (Windows; U; Windows NT 5.0; ru; rv:1.9.1.13) Gecko/20100914 Firefox/3.5.13 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.5.12 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.12) Gecko/20100824 MRA 5.7 (build 03755) Firefox/3.5.12 +Mozilla/5.0 (X11; U; Linux; en-US; rv:1.9.1.11) Gecko/20100720 Firefox/3.5.11 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 ( .NET CLR 3.5.30729; .NET4.0C) +Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 ( .NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.10) Gecko/20100504 Firefox/3.5.11 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.1.10) Gecko/20100506 SUSE/3.5.10-0.1.1 Firefox/3.5.10 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.10) Gecko/20100504 Firefox/3.5.10 GTB7.0 ( .NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux x86_64; rv:1.9.1.1) Gecko/20090716 Linux Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.3) Gecko/20100524 Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090716 Linux Mint/7 (Gloria) Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090716 Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.1) Gecko/20090714 SUSE/3.5.1-1.1 Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux x86; rv:1.9.1.1) Gecko/20090716 Linux Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2pre) Gecko/20090729 Ubuntu/9.04 (jaunty) Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.1) Gecko/20090722 Gentoo Firefox/3.5.1 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.1) Gecko/20090714 SUSE/3.5.1-1.1 Firefox/3.5.1 +Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.1) Gecko/20090718 Firefox/3.5.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; tr; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; sv-SE; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 (.NET CLR 4.0.20506) +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1 GTB5 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11;U; Linux i686; en-GB; rv:1.9.1) Gecko/20090624 Ubuntu/9.04 (jaunty) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1) Gecko/20090630 Firefox/3.5 GTB6 +Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) +Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.04 (jaunty) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.1) Gecko/20090624 Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.1) Gecko/20090624 Ubuntu/9.04 (jaunty) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1) Gecko/20090701 Ubuntu/9.04 (jaunty) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; en-us; rv:1.9.0.2) Gecko/2008092313 Ubuntu/9.04 (jaunty) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1) Gecko/20090624 Ubuntu/8.04 (hardy) Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 +Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.9.1) Gecko/20090624 Firefox/3.5 +Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.1) Gecko/20090703 Firefox/3.5 +Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.9.0.10) Gecko/20090624 Firefox/3.5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1) Gecko/20090612 Firefox/3.5 (.NET CLR 4.0.20506) +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1) Gecko/20090612 Firefox/3.5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 4.0.20506) +Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1) Gecko/20090624 Firefox/3.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729) +Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.25 (KHTML, like Gecko) Chrome/12.0.706.0 Safari/534.25 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24 +Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.702.0 Chrome/12.0.702.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.699.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.699.0 Safari/534.24 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.698.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.697.0 Safari/534.24 +Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.43 Safari/534.24 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.34 Safari/534.24 +Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.34 Safari/534.24 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 +Mozilla/5.0 (Windows NT 6.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24 +Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.14 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.12 Safari/534.24 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.12 Safari/534.24 +Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.04 Chromium/11.0.696.0 Chrome/11.0.696.0 Safari/534.24 +Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.0 Safari/534.24 +Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.694.0 Safari/534.24 +Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.23 (KHTML, like Gecko) Chrome/11.0.686.3 Safari/534.23 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.682.0 Safari/534.21 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.678.0 Safari/534.21 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7_0; en-US) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.678.0 Safari/534.21 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 +Mozilla/5.0 (Windows NT) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.672.2 Safari/534.20 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.20 (KHTML, like Gecko) Chrome/11.0.669.0 Safari/534.20 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.19 (KHTML, like Gecko) Chrome/11.0.661.0 Safari/534.19 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.18 (KHTML, like Gecko) Chrome/11.0.661.0 Safari/534.18 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.18 (KHTML, like Gecko) Chrome/11.0.660.0 Safari/534.18 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.654.0 Safari/534.17 +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.652.0 Safari/534.17 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.82 Safari/534.16 +Mozilla/5.0 (X11; U; Linux armv7l; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 +Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 +Mozilla/5.0 (X11; U; FreeBSD i386; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.134 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.133 Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.133 Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.127 Chrome/10.0.648.127 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.0 Chrome/10.0.648.0 Safari/534.16 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.0 Chrome/10.0.648.0 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.0 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.642.0 Chrome/10.0.642.0 Safari/534.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.639.0 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.638.0 Safari/534.16 +Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.634.0 Safari/534.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.634.0 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 SUSE/10.0.626.0 (KHTML, like Gecko) Chrome/10.0.626.0 Safari/534.16 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Chrome/10.0.613.0 Safari/534.15 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.613.0 Chrome/10.0.613.0 Safari/534.15 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.04 Chromium/10.0.612.3 Chrome/10.0.612.3 Safari/534.15 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Chrome/10.0.612.1 Safari/534.15 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.611.0 Chrome/10.0.611.0 Safari/534.15 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.602.0 Safari/534.14 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML,like Gecko) Chrome/9.1.0.0 Safari/540.0 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML, like Gecko) Ubuntu/10.10 Chrome/9.1.0.0 Safari/540.0 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Ubuntu/10.10 Chromium/9.0.600.0 Chrome/9.0.600.0 Safari/534.14 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.600.0 Safari/534.14 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.599.0 Safari/534.13 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.84 Safari/534.13 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.44 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.19 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.0 Safari/534.13 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.596.0 Safari/534.13 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Ubuntu/10.04 Chromium/9.0.595.0 Chrome/9.0.595.0 Safari/534.13 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Ubuntu/9.10 Chromium/9.0.592.0 Chrome/9.0.592.0 Safari/534.13 +Mozilla/5.0 (X11; U; Windows NT 6; en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.587.0 Safari/534.12 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.579.0 Safari/534.12 +Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/534.12 (KHTML, like Gecko) Chrome/9.0.576.0 Safari/534.12 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML, like Gecko) Ubuntu/10.10 Chrome/8.1.0.0 Safari/540.0 +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.558.0 Safari/534.10 +Mozilla/5.0 (X11; U; CrOS i686 0.9.130; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.344 Safari/534.10 +Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.343 Safari/534.10 +Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.341 Safari/534.10 +Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.339 Safari/534.10 +Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.339 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Ubuntu/10.10 Chromium/8.0.552.237 Chrome/8.0.552.237 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/533.3 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.224 Safari/534.10 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.210 Safari/534.10 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.200 Safari/534.10 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.551.0 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.548.0 Safari/534.10 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.544.0 Safari/534.10 +Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.15) Gecko/20101027 Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.9 (KHTML, like Gecko) Chrome/7.0.531.0 Safari/534.9 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.8 (KHTML, like Gecko) Chrome/7.0.521.0 Safari/534.8 +Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.24 Safari/534.7 +Mozilla/5.0 (X11; U; Linux x86_64; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 +Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.500.0 Safari/534.6 +Mozilla/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ko-KR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.1; fr-FR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.1; cs-CZ) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; sv-se) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ko-kr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; it-it) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-fr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; es-es) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-gb) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27 +Mozilla/5.0 (Windows; U; Windows NT 6.1; sv-SE) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.0; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/534.16+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-ch) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; de-de) AppleWebKit/534.15+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ar) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Android 2.2; Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-HK) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; tr-TR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; nb-NO) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; zh-cn) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5 +Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8G4 Safari/6533.18.5 +Mozilla/5.0 (iPod; U; CPU iPhone OS 4_2_1 like Mac OS X; he-il) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; fr; CPU iPhone OS 4_2_1 like Mac OS X; fr) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_1 like Mac OS X; zh-tw) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8G4 Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3 like Mac OS X; pl-pl) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F190 Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3 like Mac OS X; fr-fr) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F190 Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3 like Mac OS X; en-gb) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F190 Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; nb-no) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; it-it) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; fr) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; fi-fi) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; fi-fi) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5 +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; th-th) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8 +Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ +Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+ +Mozilla/5.0 (Windows; U; Windows NT 6.1; ja-JP) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; fr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; zh-cn) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ru-ru) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ko-kr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; it-it) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-au) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; el-gr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ca-es) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; zh-tw) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; it-it) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; fr-fr) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 +Mozilla/5.0 (Windows; U; Windows NT 5.0; en-en) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; nl-nl) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; ja-jp) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; de-de) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.1 Safari/533.16 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7; en-us) AppleWebKit/533.4 (KHTML, like Gecko) Version/4.1 Safari/533.4 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; tr) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; de) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081212 Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-gb) AppleWebKit/528.10+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; en-us) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; en-gb) AppleWebKit/528.4+ (KHTML, like Gecko) Version/4.0dp1 Safari/526.11.2 +Mozilla/5.0 (Windows; U; Windows NT 6.1; es-ES) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-gb) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; da-dk) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; ja-jp) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.4+ (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; de-de) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ja-jp) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8B5097d Safari/6531.22.7 +Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8B117 Safari/6531.22.7 +Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10gin_lib.cc +Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10 +Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/123 +Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-TW) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; ko-KR) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) AppleWebKit/532+ (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; hu-hu) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/531.21.11 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ru-ru) AppleWebKit/533.2+ (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; de-at) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10 +Mozilla/5.0 (iPhone; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10 +Mozilla/5.0 (iPhone Simulator; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7D11 Safari/531.21.10 +Mozilla/5.0 (iPad; U; CPU OS 3_2_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B500 Safari/53 +Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; es-es) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10 +Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; es-es) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B360 Safari/531.21.10 +Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.1021.10gin_lib.cc +Mozilla/5.0 (iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/532.0+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9.2009 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/532.0+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; nl-nl) AppleWebKit/532.3+ (KHTML, like Gecko) Version/4.0.3 Safari/531.9 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; fi-fi) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.3 Safari/531.21.10 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532+ (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-TW) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; pl-PL) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 5.2; de-DE) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1 +Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/4.0.1 Safari/530.18 +Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.1 Safari/530.18 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ru-RU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; he-IL) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; he-IL) AppleWebKit/528+ (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; es-es) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; en) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 6.0; de-DE) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-PT) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; nb-NO) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; hu-HU) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Mozilla/5.0 (Windows; U; Windows NT 5.1; fi-FI) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16 +Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10 +Opera/9.80 (Windows NT 5.1; U; zh-tw) Presto/2.8.131 Version/11.10 +Opera/9.80 (X11; Linux x86_64; U; Ubuntu/10.10 (maverick); pl) Presto/2.7.62 Version/11.01 +Opera/9.80 (X11; Linux i686; U; ja) Presto/2.7.62 Version/11.01 +Opera/9.80 (X11; Linux i686; U; fr) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.1; U; sv) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.1; U; en-US) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 5.1; U;) Presto/2.7.62 Version/11.01 +Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.7.62 Version/11.01 +Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101213 Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 +Mozilla/5.0 (Windows NT 6.1; U; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01 +Mozilla/5.0 (Windows NT 6.1; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; de) Opera 11.01 +Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00 +Opera/9.80 (X11; Linux i686; U; it) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.6.37 Version/11.00 +Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.1; U; ko) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.1; U; fi) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.1; U; en-GB) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.1 x64; U; en) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 6.0; U; en) Presto/2.7.39 Version/11.00 +Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.7.39 Version/11.00 +Opera/9.80 (Windows NT 5.1; U; MRA 5.5 (build 02842); ru) Presto/2.7.62 Version/11.00 +Opera/9.80 (Windows NT 5.1; U; it) Presto/2.7.62 Version/11.00 +Mozilla/5.0 (Windows NT 6.0; U; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 +Mozilla/5.0 (Windows NT 5.1; U; pl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 +Mozilla/5.0 (Windows NT 5.1; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.00 +Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; pl) Opera 11.00 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; fr) Opera 11.00 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; ja) Opera 11.00 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; en) Opera 11.00 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; pl) Opera 11.00 +Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.6.31 Version/10.70 +Mozilla/5.0 (Windows NT 5.2; U; ru; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.70 +Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.70 +Opera/9.80 (Windows NT 5.2; U; zh-cn) Presto/2.6.30 Version/10.63 +Opera/9.80 (Windows NT 5.2; U; en) Presto/2.6.30 Version/10.63 +Opera/9.80 (Windows NT 5.1; U; MRA 5.6 (build 03278); ru) Presto/2.6.30 Version/10.63 +Opera/9.80 (Windows NT 5.1; U; pl) Presto/2.6.30 Version/10.62 +Mozilla/5.0 (X11; Linux x86_64; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.62 +Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; de) Opera 10.62 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; en) Opera 10.62 +Opera/9.80 (X11; Linux i686; U; pl) Presto/2.6.30 Version/10.61 +Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.6.30 Version/10.61 +Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.6.30 Version/10.61 +Opera/9.80 (Windows NT 6.1; U; en) Presto/2.6.30 Version/10.61 +Opera/9.80 (Windows NT 6.0; U; it) Presto/2.6.30 Version/10.61 +Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.6.30 Version/10.61 +Opera/9.80 (Windows 98; U; de) Presto/2.6.30 Version/10.61 +Opera/9.80 (Macintosh; Intel Mac OS X; U; nl) Presto/2.6.30 Version/10.61 +Opera/9.80 (X11; Linux i686; U; en) Presto/2.5.27 Version/10.60 +Opera/9.80 (Windows NT 6.0; U; nl) Presto/2.6.30 Version/10.60 +Opera/10.60 (Windows NT 5.1; U; zh-cn) Presto/2.6.30 Version/10.60 +Opera/10.60 (Windows NT 5.1; U; en-US) Presto/2.6.30 Version/10.60 +Opera/9.80 (X11; Linux i686; U; it) Presto/2.5.24 Version/10.54 +Opera/9.80 (X11; Linux i686; U; en-GB) Presto/2.5.24 Version/10.53 +Mozilla/5.0 (Windows NT 5.1; U; zh-cn; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 +Mozilla/5.0 (Windows NT 5.1; U; Firefox/5.0; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 +Mozilla/5.0 (Windows NT 5.1; U; Firefox/4.5; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 +Mozilla/5.0 (Windows NT 5.1; U; Firefox/3.5; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.53 +Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; ko) Opera 10.53 +Opera/9.80 (Windows NT 6.1; U; fr) Presto/2.5.24 Version/10.52 +Opera/9.80 (Windows NT 6.1; U; en) Presto/2.5.22 Version/10.51 +Opera/9.80 (Windows NT 6.0; U; cs) Presto/2.5.22 Version/10.51 +Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.5.22 Version/10.51 +Opera/9.80 (Linux i686; U; en) Presto/2.5.22 Version/10.51 +Mozilla/5.0 (Windows NT 6.1; U; en-GB; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.51 +Mozilla/5.0 (Linux i686; U; en; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 10.51 +Mozilla/4.0 (compatible; MSIE 8.0; Linux i686; en) Opera 10.51 +Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.5.22 Version/10.50 +Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.5.22 Version/10.50 +Opera/9.80 (Windows NT 6.1; U; sk) Presto/2.6.22 Version/10.50 +Opera/9.80 (Windows NT 6.1; U; ja) Presto/2.5.22 Version/10.50 +Opera/9.80 (Windows NT 6.0; U; zh-cn) Presto/2.5.22 Version/10.50 +Opera/9.80 (Windows NT 5.1; U; sk) Presto/2.5.22 Version/10.50 +Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.5.22 Version/10.50 +Opera/10.50 (Windows NT 6.1; U; en-GB) Presto/2.2.2 +Opera/9.80 (S60; SymbOS; Opera Tablet/9174; U; en) Presto/2.7.81 Version/10.5 +Opera/9.80 (X11; U; Linux i686; en-US; rv:1.9.2.3) Presto/2.2.15 Version/10.10 +Opera/9.80 (X11; Linux x86_64; U; it) Presto/2.2.15 Version/10.10 +Opera/9.80 (Windows NT 6.1; U; de) Presto/2.2.15 Version/10.10 +Opera/9.80 (Windows NT 6.0; U; Gecko/20100115; pl) Presto/2.2.15 Version/10.10 +Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10 +Opera/9.80 (Windows NT 5.1; U; de) Presto/2.2.15 Version/10.10 +Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.10 +Mozilla/5.0 (Windows NT 6.0; U; tr; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 10.10 +Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; de) Opera 10.10 +Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; tr) Opera 10.10 +Opera/9.80 (X11; Linux x86_64; U; en-GB) Presto/2.2.15 Version/10.01 +Opera/9.80 (X11; Linux x86_64; U; en) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; ru) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; pt-BR) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; pl) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; nb) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; en-GB) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; en) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; Debian; pl) Presto/2.2.15 Version/10.00 +Opera/9.80 (X11; Linux i686; U; de) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.1; U; fi) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.1; U; en) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.1; U; de) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 6.0; U; de) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 5.2; U; en) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 5.1; U; zh-cn) Presto/2.2.15 Version/10.00 +Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.00 \ No newline at end of file diff --git a/soulbook/database/__init__.py b/soulbook/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/soulbook/database/mongodb/__init__.py b/soulbook/database/mongodb/__init__.py new file mode 100644 index 0000000..9ee2906 --- /dev/null +++ b/soulbook/database/mongodb/__init__.py @@ -0,0 +1 @@ +from .motorbase import MotorBase, MotorBaseOld, PyMongoDb diff --git a/soulbook/database/mongodb/motorbase.py b/soulbook/database/mongodb/motorbase.py new file mode 100644 index 0000000..d6a1f3b --- /dev/null +++ b/soulbook/database/mongodb/motorbase.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +import asyncio + +from pymongo import MongoClient +from motor.motor_asyncio import AsyncIOMotorClient + +from soulbook.config import CONFIG +from soulbook.utils.tools import singleton + + +@singleton +class MotorBase: + """ + 更改mongodb连接方式 单例模式下支持多库操作 + About motor's doc: https://github.com/mongodb/motor + """ + _db = {} + _collection = {} + MONGODB = CONFIG.MONGODB + + def __init__(self, loop=None): + self.motor_uri = '' + self.loop = loop or asyncio.get_event_loop() + + def client(self, db): + # motor + self.motor_uri = 'mongodb://{account}{host}:{port}/{database}'.format( + account='{username}:{password}@'.format( + username=self.MONGODB['MONGO_USERNAME'], + password=self.MONGODB['MONGO_PASSWORD']) if self.MONGODB['MONGO_USERNAME'] else '', + host=self.MONGODB['MONGO_HOST'] if self.MONGODB['MONGO_HOST'] else 'localhost', + port=self.MONGODB['MONGO_PORT'] if self.MONGODB['MONGO_PORT'] else 27017, + database=db) + return AsyncIOMotorClient(self.motor_uri, io_loop=self.loop) + + def get_db(self, db=MONGODB['DATABASE']): + """ + Get a db instance + :param db: database name + :return: the motor db instance + """ + if db not in self._db: + self._db[db] = self.client(db)[db] + + return self._db[db] + + def get_collection(self, db_name, collection): + """ + Get a collection instance + :param db_name: database name + :param collection: collection name + :return: the motor collection instance + """ + collection_key = db_name + collection + if collection_key not in self._collection: + self._collection[collection_key] = self.get_db(db_name)[collection] + + return self._collection[collection_key] + + +class MotorBaseOld: + """ + use motor to connect mongodb + 2017-09-21 deleted + """ + _db = None + MONGODB = CONFIG.MONGODB + + def client(self, db): + # motor + self.motor_uri = 'mongodb://{account}{host}:{port}/{database}'.format( + account='{username}:{password}@'.format( + username=self.MONGODB['MONGO_USERNAME'], + password=self.MONGODB['MONGO_PASSWORD']) if self.MONGODB['MONGO_USERNAME'] else '', + host=self.MONGODB['MONGO_HOST'] if self.MONGODB['MONGO_HOST'] else 'localhost', + port=self.MONGODB['MONGO_PORT'] if self.MONGODB['MONGO_PORT'] else 27017, + database=db) + return AsyncIOMotorClient(self.motor_uri) + + @property + def db(self): + if self._db is None: + self._db = self.client(self.MONGODB['DATABASE'])[self.MONGODB['DATABASE']] + + return self._db + + +@singleton +class PyMongoDb: + _db = None + MONGODB = { + 'MONGO_HOST': '127.0.0.1', + 'MONGO_PORT': '', + 'MONGO_USERNAME': '', + 'MONGO_PASSWORD': '', + 'DATABASE': 'soulbook' + } + + def client(self): + # motor + self.mongo_uri = 'mongodb://{account}{host}:{port}/'.format( + account='{username}:{password}@'.format( + username=self.MONGODB['MONGO_USERNAME'], + password=self.MONGODB['MONGO_PASSWORD']) if self.MONGODB['MONGO_USERNAME'] else '', + host=self.MONGODB['MONGO_HOST'] if self.MONGODB['MONGO_HOST'] else 'localhost', + port=self.MONGODB['MONGO_PORT'] if self.MONGODB['MONGO_PORT'] else 27017) + return MongoClient(self.mongo_uri) + + @property + def db(self): + if self._db is None: + self._db = self.client()[self.MONGODB['DATABASE']] + + return self._db + + +if __name__ == '__main__': + def async_callback(func, **kwargs): + """ + Call the asynchronous function + :param func: a async function + :param kwargs: params + :return: result + """ + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(func(**kwargs)) + loop.run_until_complete(task) + return task.result() + + + motor_base = MotorBase() + motor_db = motor_base.get_db() + + + async def insert(data): + print(data) + await motor_db.test.save(data) + + + async_callback(insert, data={'hi': 'soulbook'}) diff --git a/soulbook/database/redis/__init__.py b/soulbook/database/redis/__init__.py new file mode 100644 index 0000000..4f797e6 --- /dev/null +++ b/soulbook/database/redis/__init__.py @@ -0,0 +1 @@ +from .redisbase import * diff --git a/soulbook/database/redis/redisbase.py b/soulbook/database/redis/redisbase.py new file mode 100644 index 0000000..e8a1378 --- /dev/null +++ b/soulbook/database/redis/redisbase.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import asyncio_redis + +from soulbook.config import CONFIG + + +# Token from https://github.com/subyraman/sanic_session +class RedisSession: + """ + A simple wrapper class that allows you to share a connection + pool across your application. + """ + _pool = None + + async def get_redis_pool(self): + if not self._pool: + REDIS_DICT = CONFIG.REDIS_DICT + self._pool = await asyncio_redis.Pool.create( + host=str(REDIS_DICT.get('REDIS_ENDPOINT', "localhost")), port=int(REDIS_DICT.get('REDIS_PORT', 6379)), + poolsize=int(REDIS_DICT.get('POOLSIZE', 10)), password=REDIS_DICT.get('REDIS_PASSWORD', None), + db=REDIS_DICT.get('SESSION_DB', None) + ) + + return self._pool diff --git a/soulbook/fetcher/__init__.py b/soulbook/fetcher/__init__.py new file mode 100644 index 0000000..652fe6f --- /dev/null +++ b/soulbook/fetcher/__init__.py @@ -0,0 +1 @@ +from .response_base import UniResponse, ResponseReply, ResponseCode, ResponseField diff --git a/soulbook/fetcher/cache.py b/soulbook/fetcher/cache.py new file mode 100644 index 0000000..29775f1 --- /dev/null +++ b/soulbook/fetcher/cache.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python +""" + Created by howie.hu. +""" + +import re +import aiohttp +import async_timeout + +from bs4 import BeautifulSoup +from aiocache.serializers import PickleSerializer, JsonSerializer + +from urllib.parse import urlparse, parse_qs, urljoin + +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.decorators import cached +from soulbook.fetcher.function import target_fetch, get_time, get_html_by_requests, \ + get_random_user_agent +from soulbook.fetcher.extract_novels import extract_pre_next_chapter +from soulbook.config import RULES, LATEST_RULES, LOGGER + +list_class_content = ("content", "content_read", "txt") +list_id_content = ("chaptercontent", "content", "txt") + + +@cached(ttl=300, key_from_attr='url', serializer=PickleSerializer(), namespace="main") +async def cache_owllook_novels_content(url, chapter_url, netloc): + headers = { + 'user-agent': await get_random_user_agent() + } + html = await target_fetch(headers=headers, url=url) + if not html: + html = get_html_by_requests(url=url, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + # 章节内容 + content = None + if RULES.get(netloc): + selector = RULES[netloc].content_selector + if selector.get('id', None): + content = soup.find_all(id_=selector['id']) + elif selector.get('class', None): + content = soup.find_all(class_=selector['class']) + else: + content = soup.find_all(selector.get('tag')) + if not content: + # print("novels_content: body") + # content = soup.find_all("body") + + # for i in list_class_content: + # content = soup.find_all(class_=i) + # if content: + # break + # else: + # for i in list_id_content: + # content = soup.find_all(id=i) + # if content: + # break + + for i in list_id_content: + content = soup.find_all(id=i) + if content: + break + else: + for i in list_class_content: + content = soup.find_all(class_=i) + if content: + break + + if not content: + # 直接提取整个版块块 + content = soup.find_all("body") + if content: + # 提取出真正的章节标题 + title_reg = r'(第?\s*[一二两三四五六七八九十○零百千万亿0-91234567890]{1,6}\s*[章回卷节折篇幕集]\s*.*?)[_,-]' + title = soup.title.string + extract_title = re.findall(title_reg, title, re.I) + if extract_title: + title = extract_title[0] + else: + try: + title = soup.select('h1')[0].get_text() + except Exception as e: + print(e) + if not title: + title = soup.title.string + # if "_" in title: + # title = title.split('_')[0] + # elif "-" in title: + # title = title.split('-')[0] + next_chapter = extract_pre_next_chapter(url=url, chapter_url=chapter_url, + html=str(soup)) + content = [str(i) for i in content] + data = { + 'content': str(''.join(content)), + 'next_chapter': next_chapter, + 'title': title + } + else: + data = None + return data + return None + + +ListIdTab = ("box_con", 'box_chap', 'layout layout-col1', 'listmain', "content", "list-chapterAll") +ListClassTab = ( + "box_con", 'box_chap', 'layout layout-col1', 'listmain', 'book chapterlist', 'book_list', + 'TabCss', 'inner', 'mulu', 'bg') + + +@cached(ttl=300, key_from_attr='url', serializer=PickleSerializer(), namespace="main") +async def cache_owllook_novels_chapter(url, netloc): + headers = { + 'user-agent': await get_random_user_agent() + } + html = await target_fetch(headers=headers, url=url) + if not html: + html = get_html_by_requests(url=url, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + # 提取出的内容 + content = None + # 存在解析规则 + if RULES.get(netloc): + # 匹配解析规则 + selector = RULES[netloc].chapter_selector + if selector.get('id', None): + content = soup.find_all(id=selector['id']) + elif selector.get('class', None): + content = soup.find_all(class_=selector['class']) + else: + content = soup.find_all(selector.get('tag')) + else: + data = str(html) + for i in ListIdTab: + if 'id="%s"' % i in data: + content = soup.find_all(id=i) + break + for i in ListClassTab: + if 'class="%s"' % i in data: + content = soup.find_all(class_=i) + break + if not content: + # 直接提取整个版块块 + content = soup.find_all("body") + if content: + # 防止章节被display:none + return str(content).replace('style', '') if content else None + return None + + +@cached(ttl=10800, key_from_attr='search_ranking', serializer=JsonSerializer(), namespace="ranking") +async def cache_owllook_search_ranking(): + motor_db = MotorBase().get_db() + keyword_cursor = motor_db.search_records.find( + {'count': {'$gte': 50}}, + {'keyword': 1, 'count': 1, '_id': 0} + ).sort('count', -1).limit(35) + result = [] + index = 1 + async for document in keyword_cursor: + result.append({'keyword': document['keyword'], 'count': document['count'], 'index': index}) + index += 1 + return result + + +@cached(ttl=3600, key_from_attr='search_ranking', serializer=JsonSerializer(), namespace="ranking") +async def cache_others_search_ranking(spider='qidian', novel_type='全部类别'): + motor_db = MotorBase().get_db() + item_data = await motor_db.novels_ranking.find_one({'spider': spider, 'type': novel_type}, + {'data': 1, '_id': 0}) + return item_data + + +async def get_the_latest_chapter(chapter_url, timeout=15): + try: + with async_timeout.timeout(timeout): + url = parse_qs(urlparse(chapter_url).query).get('url', '') + novels_name = parse_qs(urlparse(chapter_url).query).get('novels_name', '') + data = None + if url and novels_name: + url = url[0] + novels_name = novels_name[0] + netloc = urlparse(url).netloc + if netloc in LATEST_RULES.keys(): + headers = { + 'user-agent': await get_random_user_agent() + } + try: + html = await target_fetch(url=url, headers=headers, timeout=timeout) + if html is None: + html = get_html_by_requests(url=url, headers=headers, timeout=timeout) + except TypeError: + html = get_html_by_requests(url=url, headers=headers, timeout=timeout) + except Exception as e: + LOGGER.exception(e) + return None + try: + soup = BeautifulSoup(html, 'html5lib') + except Exception as e: + LOGGER.exception(e) + return None + latest_chapter_name, latest_chapter_url = None, None + if LATEST_RULES[netloc].plan: + meta_value = LATEST_RULES[netloc].meta_value + latest_chapter_name = soup.select( + 'meta[property="{0}"]'.format( + meta_value["latest_chapter_name"])) or soup.select( + 'meta[name="{0}"]'.format(meta_value["latest_chapter_name"])) + + latest_chapter_name = latest_chapter_name[0].get('content', + None) if latest_chapter_name else None + latest_chapter_url = soup.select( + 'meta[property="{0}"]'.format( + meta_value["latest_chapter_url"])) or soup.select( + 'meta[name="{0}"]'.format(meta_value["latest_chapter_url"])) + latest_chapter_url = urljoin(chapter_url, + latest_chapter_url[0].get('content', + None)) if latest_chapter_url else None + else: + selector = LATEST_RULES[netloc].selector + content_url = selector.get('content_url') + if selector.get('id', None): + latest_chapter_soup = soup.find_all(id=selector['id']) + elif selector.get('class', None): + latest_chapter_soup = soup.find_all(class_=selector['class']) + else: + latest_chapter_soup = soup.select(selector.get('tag')) + if latest_chapter_soup: + if content_url == '1': + # TODO + pass + elif content_url == '0': + # TODO + pass + else: + latest_chapter_url = content_url + latest_chapter_soup[0].get( + 'href', None) + latest_chapter_name = latest_chapter_soup[0].get('title', None) + if latest_chapter_name and latest_chapter_url: + time_current = get_time() + # print(latest_chapter_url) + data = { + "latest_chapter_name": latest_chapter_name, + "latest_chapter_url": latest_chapter_url, + "owllook_chapter_url": chapter_url, + "owllook_content_url": "/owllook_content?url={latest_chapter_url}&name={name}&chapter_url={chapter_url}&novels_name={novels_name}".format( + latest_chapter_url=latest_chapter_url, + name=latest_chapter_name, + chapter_url=url, + novels_name=novels_name, + ), + } + # 存储最新章节 + motor_db = MotorBase().get_db() + await motor_db.latest_chapter.update_one( + {"novels_name": novels_name, 'owllook_chapter_url': chapter_url}, + {'$set': {'data': data, "finished_at": time_current}}, upsert=True) + return data + except Exception as e: + LOGGER.exception(e) + return None + + +async def update_all_books(loop, timeout=15): + try: + motor_db = MotorBase().get_db() + # 获取所有书架链接游标 + books_url_cursor = motor_db.user_message.find({}, {'books_url.book_url': 1, '_id': 0}) + book_urls = [] + already_urls = set() + async for document in books_url_cursor: + if document: + books_url = document['books_url'] + + for book_url in books_url: + chapter_url = book_url['book_url'] + if chapter_url not in already_urls: + try: + await get_the_latest_chapter(chapter_url, timeout) + except Exception as e: + LOGGER.exception(e) + already_urls.add(chapter_url) + # 一组书架链接列表数据 + # book_urls += [book_url['book_url'] for book_url in books_url] + # url_tasks = [get_the_latest_chapter(each_url, loop) for each_url in set(book_urls)] + # tasks = [asyncio.ensure_future(i) for i in url_tasks] + # try: + # await asyncio.gather(*tasks) + # except asyncio.TimeoutError as e: + # pass + except Exception as e: + LOGGER.exception(e) + return False diff --git a/soulbook/fetcher/decorators.py b/soulbook/fetcher/decorators.py new file mode 100644 index 0000000..a1fa8a5 --- /dev/null +++ b/soulbook/fetcher/decorators.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +from functools import wraps + +from sanic import response + +from aiocache.log import logger +from aiocache.utils import get_args_dict, get_cache +from sanic.request import Request + +try: + from ujson import loads as json_loads + from ujson import dumps as json_dumps +except: + from json import loads as json_loads + from json import dumps as json_dumps + +from soulbook.fetcher import UniResponse +from soulbook.config import CONFIG, LOGGER + + +def authenticator(key): + """ + + :param keys: 验证方式 Owllook-Api-Key : Magic Key, Authorization : Token + :return: 返回值 + """ + + def wrapper(func): + @wraps(func) + async def authenticate(request, *args, **kwargs): + value = request.headers.get(key, None) + if value and CONFIG.AUTH[key] == value: + response = await func(request, *args, **kwargs) + return response + else: + return response_handle(request, UniResponse.NOT_AUTHORIZED, status=401) + + return authenticate + + return wrapper + + +def auth_params(*keys): + """ + + :param keys: 判断必须要有的参数 + :return: 返回值 + """ + + def wrapper(func): + @wraps(func) + async def auth_param(request, *args, **kwargs): + request_params = {} + # POST request + if request.method == 'POST' or request.method == 'DELETE': + try: + post_data = json_loads(str(request.body, encoding='utf-8')) + except Exception as e: + LOGGER.exception(e) + return response_handle(request, UniResponse.PARAM_PARSE_ERR, status=400) + else: + request_params.update(post_data) + params = [key for key, value in post_data.items() if value] + elif request.method == 'GET': + request_params.update(request.args) + params = [key for key, value in request.args.items() if value] + else: + # TODO + return response_handle(request, UniResponse.PARAM_UNKNOWN_ERR, status=400) + if set(keys).issubset(set(params)): + try: + kwargs['request_params'] = request_params + response = await func(request, *args, **kwargs) + return response + except Exception as e: + LOGGER.exception(e) + return response_handle(request, UniResponse.SERVER_UNKNOWN_ERR, 500) + else: + return response_handle(request, UniResponse.PARAM_ERR, status=400) + + return auth_param + + return wrapper + + +# Token from https://github.com/argaen/aiocache/blob/master/aiocache/decorators.py +def cached( + ttl=0, key=None, key_from_attr=None, cache=None, serializer=None, plugins=None, **kwargs): + """ + Caches the functions return value into a key generated with module_name, function_name and args. + + In some cases you will need to send more args to configure the cache object. + An example would be endpoint and port for the RedisCache. You can send those args as + kwargs and they will be propagated accordingly. + + :param ttl: int seconds to store the function call. Default is 0 which means no expiration. + :param key: str value to set as key for the function return. Takes precedence over + key_from_attr param. If key and key_from_attr are not passed, it will use module_name + + function_name + args + kwargs + :param key_from_attr: arg or kwarg name from the function to use as a key. + :param cache: cache class to use when calling the ``set``/``get`` operations. + Default is the one configured in ``aiocache.settings.DEFAULT_CACHE`` + :param serializer: serializer instance to use when calling the ``dumps``/``loads``. + Default is the one configured in ``aiocache.settings.DEFAULT_SERIALIZER`` + :param plugins: plugins to use when calling the cmd hooks + Default is the one configured in ``aiocache.settings.DEFAULT_PLUGINS`` + """ + cache_kwargs = kwargs + + def cached_decorator(func): + async def wrapper(*args, **kwargs): + cache_instance = get_cache( + cache=cache, serializer=serializer, plugins=plugins, **cache_kwargs) + args_dict = get_args_dict(func, args, kwargs) + cache_key = key or args_dict.get( + key_from_attr, + (func.__module__ or 'stub') + func.__name__ + str(args) + str(kwargs)) + + try: + if await cache_instance.exists(cache_key): + return await cache_instance.get(cache_key) + + except Exception: + logger.exception("Unexpected error with %s", cache_instance) + + result = await func(*args, **kwargs) + if result: + try: + await cache_instance.set(cache_key, result, ttl=ttl) + except Exception: + logger.exception("Unexpected error with %s", cache_instance) + + return result + + return wrapper + + return cached_decorator + + +def response_handle(request, dict_value, status=200): + """ + Return sanic.response or json depending on the request + :param request: sanic.request.Request or dict + :param dict_value: + :return: + """ + if isinstance(request, Request): + return response.json(dict_value, status=status) + else: + return json_dumps(dict_value, ensure_ascii=False) diff --git a/soulbook/fetcher/extract_novels.py b/soulbook/fetcher/extract_novels.py new file mode 100644 index 0000000..bed486d --- /dev/null +++ b/soulbook/fetcher/extract_novels.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" + +import re + +from bs4 import BeautifulSoup +from collections import OrderedDict +from operator import itemgetter +from urllib.parse import urljoin, urlparse + +from soulbook.config import LOGGER + + +def extract_chapters(chapters_url, html): + """ + 通用解析小说目录 + :param chapter_url: 小说目录页url + :param res: 当前页面html + :return: + """ + # 参考https://greasyfork.org/zh-CN/scripts/292-my-novel-reader + chapters_reg = r'(.*第?\s*[一二两三四五六七八九十○零百千万亿0-91234567890]{1,6}\s*[章回卷节折篇幕集].*?)' + # 这里不能保证获取的章节分得很清楚,但能保证这一串str是章节目录。可以利用bs安心提取a + chapters_res = re.findall(chapters_reg, str(html), re.I) + str_chapters_res = '\n'.join(chapters_res) + chapters_res_soup = BeautifulSoup(str_chapters_res, 'html5lib') + all_chapters = [] + for link in chapters_res_soup.find_all('a'): + each_data = {} + url = urljoin(chapters_url, link.get('href')) or '' + name = link.text or '' + each_data['chapter_url'] = url + each_data['chapter_name'] = name + each_data['index'] = int(urlparse(url).path.split('.')[0].split('/')[-1]) + all_chapters.append(each_data) + chapters_sorted = sorted(all_chapters, reverse=True, key=itemgetter('index')) + return chapters_sorted + + +def extract_pre_next_chapter(url, chapter_url, html): + """ + 获取单章节上一页下一页 + :param chapter_url: + :param html: + :return: + """ + next_chapter = OrderedDict() + try: + # 参考https://greasyfork.org/zh-CN/scripts/292-my-novel-reader + next_reg = r'(.*[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步].*?)' + judge_reg = r'[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步]' + # 这里同样需要利用bs再次解析 + next_res = re.findall(next_reg, html.replace('<<', '').replace('>>', ''), re.I) + str_next_res = '\n'.join(next_res) + next_res_soup = BeautifulSoup(str_next_res, 'html5lib') + for link in next_res_soup.find_all('a'): + text = link.text or '' + text = text.replace(' ', '') + if novels_list(text): + is_next = re.search(judge_reg, text) + # is_ok = is_chapter(text) + if is_next: + url = urljoin(chapter_url, link.get('href')) or '' + regex = re.compile("^http://|^https://") + if regex.sub('', chapter_url) == regex.sub('', url): + url = False + next_chapter[text[:5]] = url + + # nextDic = [{v[0]: v[1]} for v in sorted(next_chapter.items(), key=lambda d: d[1])] + return next_chapter + except Exception as e: + LOGGER.exception(e) + return next_chapter + + +def novels_list(text): + rm_list = ['后一个', '天上掉下个'] + for i in rm_list: + if i in text: + return False + else: + continue + return True diff --git a/soulbook/fetcher/function.py b/soulbook/fetcher/function.py new file mode 100644 index 0000000..a03cd0a --- /dev/null +++ b/soulbook/fetcher/function.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" + +import os +import random +import time + +import aiofiles +import aiohttp +import arrow +import async_timeout +import cchardet +import requests + +from urllib.parse import urlparse + +from soulbook.config import LOGGER, CONFIG +from soulbook.fetcher.novels_factory.ssl_factory import ssl_gen + + +async def _get_data(filename, default='') -> list: + """ + Get data from a file + :param filename: filename + :param default: default value + :return: data + """ + root_folder = os.path.dirname(os.path.dirname(__file__)) + user_agents_file = os.path.join( + os.path.join(root_folder, 'data'), filename) + try: + async with aiofiles.open(user_agents_file, mode='r') as f: + data = [_.strip() for _ in await + f.readlines()] + except: + data = [default] + return data + + +async def get_random_user_agent() -> str: + """ + Get a random user agent string. + :return: Random user agent string. + """ + return random.choice(await _get_data('user_agents.txt', CONFIG.USER_AGENT)) + + +def get_time() -> str: + utc = arrow.utcnow() + local = utc.to(CONFIG.TIMEZONE) + time = local.format("YYYY-MM-DD HH:mm:ss") + return time + + +def get_timestamp() -> float: + return time.time() + + +def get_netloc(url): + """ + 获取netloc + :param url: + :return: netloc + """ + netloc = urlparse(url).netloc + return netloc or None + + +def get_scheme(url): + """ + 获取scheme + :param url: + :return: scheme + """ + netloc = urlparse(url).scheme + return netloc or None + + +async def target_fetch(url, headers, timeout=15): + """ + :param url: target url + :return: text + """ + async with async_timeout.timeout(timeout): + try: + async with aiohttp.ClientSession() as client: + # async with client.get(url, headers=headers) as response: + async with client.get(url, headers=headers, ssl=ssl_gen()) as response: + assert response.status == 200 + LOGGER.info('Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + try: + text = await response.read() + except aiohttp.ServerDisconnectedError as e: + LOGGER.exception(e) + text = None + return text + except Exception as e: + LOGGER.exception(str(e)) + return None + + +def get_html_by_requests(url, headers, timeout=15): + """ + :param url: + :return: + """ + try: + response = requests.get(url=url, headers=headers, verify=False, timeout=timeout) + response.raise_for_status() + content = response.content + charset = cchardet.detect(content) + text = content.decode(charset['encoding']) + return text + except Exception as e: + LOGGER.exception(e) + return None diff --git a/soulbook/fetcher/novels_factory/__init__.py b/soulbook/fetcher/novels_factory/__init__.py new file mode 100644 index 0000000..ef4d99e --- /dev/null +++ b/soulbook/fetcher/novels_factory/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" \ No newline at end of file diff --git a/soulbook/fetcher/novels_factory/baidu_novels.py b/soulbook/fetcher/novels_factory/baidu_novels.py new file mode 100644 index 0000000..483670c --- /dev/null +++ b/soulbook/fetcher/novels_factory/baidu_novels.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import aiohttp +import asyncio +import async_timeout + +from aiocache.serializers import PickleSerializer +from bs4 import BeautifulSoup +from urllib.parse import urlparse + +from soulbook.fetcher.decorators import cached +from soulbook.fetcher.function import get_random_user_agent +from soulbook.fetcher.novels_factory.base_novels import BaseNovels + + +class BaiduNovels(BaseNovels): + + def __init__(self): + super(BaiduNovels, self).__init__() + + async def data_extraction(self, html): + """ + 小说信息抓取函数 + :return: + """ + try: + url = html.select('h3.t a')[0].get('href', None) + real_url = await self.get_real_url(url=url) if url else None + if real_url: + real_str_url = str(real_url) + netloc = urlparse(real_str_url).netloc + if "http://" + netloc + "/" == real_str_url: + return None + if 'baidu' in real_str_url or netloc in self.black_domain: + return None + is_parse = 1 if netloc in self.rules.keys() else 0 + title = html.select('h3.t a')[0].get_text() + is_recommend = 1 if netloc in self.latest_rules.keys() else 0 + # time = re.findall(r'\d+-\d+-\d+', source) + # time = time[0] if time else None + timestamp = 0 + time = "" + return {'title': title, 'url': real_str_url.replace('index.html', ''), 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + else: + return None + except Exception as e: + self.logger.exception(e) + return None + + async def get_real_url(self, url): + """ + 获取百度搜索结果真实url + :param url: + :return: + """ + with async_timeout.timeout(5): + try: + async with aiohttp.ClientSession() as client: + headers = {'user-agent': await get_random_user_agent()} + async with client.head(url, headers=headers, allow_redirects=True) as response: + self.logger.info('Parse url: {}'.format(response.url)) + url = response.url if response.url else None + return url + except Exception as e: + self.logger.exception(e) + return None + + async def novels_search(self, novels_name): + """ + 小说搜索入口函数 + :return: + """ + url = self.config.URL_PC + params = {'wd': novels_name, 'ie': 'utf-8', 'rn': self.config.BAIDU_RN, 'vf_bl': 1} + headers = {'user-agent': await get_random_user_agent()} + html = await self.fetch_url(url=url, params=params, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='result') + extra_tasks = [self.data_extraction(html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + done_list, pending_list = await asyncio.wait(tasks) + res = [task.result() for task in done_list if task.result()] + return res + else: + return [] + + +@cached(ttl=259200, key_from_attr='novels_name', serializer=PickleSerializer(), namespace="novels_name") +async def start(novels_name): + """ + Start spider + :return: + """ + return await BaiduNovels.start(novels_name) + + +if __name__ == '__main__': + # Start + import aiocache + + REDIS_DICT = {} + aiocache.settings.set_defaults( + class_="aiocache.RedisCache", + endpoint=REDIS_DICT.get('REDIS_ENDPOINT', 'localhost'), + port=REDIS_DICT.get('REDIS_PORT', 6379), + db=REDIS_DICT.get('CACHE_DB', 0), + password=REDIS_DICT.get('REDIS_PASSWORD', None), + ) + res = asyncio.get_event_loop().run_until_complete(start('intitle:雪中悍刀行 小说 阅读')) + print(res) diff --git a/soulbook/fetcher/novels_factory/base_novels.py b/soulbook/fetcher/novels_factory/base_novels.py new file mode 100644 index 0000000..c63187b --- /dev/null +++ b/soulbook/fetcher/novels_factory/base_novels.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import aiohttp +import async_timeout + +from soulbook.config import CONFIG, LOGGER, BLACK_DOMAIN, RULES, LATEST_RULES +from soulbook.fetcher.novels_factory.ssl_factory import ssl_gen + + +class BaseNovels: + """ + 小说抓取父类 + """ + + def __init__(self, logger=None): + self.black_domain = BLACK_DOMAIN + self.config = CONFIG + self.latest_rules = LATEST_RULES + self.logger = logger if logger else LOGGER + self.rules = RULES + + async def fetch_url(self, url, params, headers): + """ + 公共抓取函数 + :param client: + :param url: + :param params: + :return: + """ + async with async_timeout.timeout(15): + + try: + async with aiohttp.ClientSession() as client: + print("headers: ", headers) + # async with client.get(url, params=params, headers=headers) as response: + async with client.get(url, params=params, headers=headers, ssl=ssl_gen()) as \ + response: + assert response.status == 200 + LOGGER.info('[fetch] Task url: {}'.format(response.url)) + try: + text = await response.text() + except: + text = await response.read() + return text + except Exception as e: + LOGGER.exception(e) + return None + + @classmethod + async def start(cls, novels_name): + return await cls().novels_search(novels_name) + + async def data_extraction(self, html): + """ + 小说信息抓取函数 + :return: + """ + raise NotImplementedError + + async def novels_search(self, novels_name): + """ + 小说搜索入口函数 + :return: + """ + raise NotImplementedError diff --git a/soulbook/fetcher/novels_factory/bing_novels.py b/soulbook/fetcher/novels_factory/bing_novels.py new file mode 100644 index 0000000..1536f17 --- /dev/null +++ b/soulbook/fetcher/novels_factory/bing_novels.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import asyncio + +from aiocache.serializers import PickleSerializer +from bs4 import BeautifulSoup +from urllib.parse import urlparse + +from soulbook.fetcher.decorators import cached +from soulbook.fetcher.function import get_random_user_agent +from soulbook.fetcher.novels_factory.base_novels import BaseNovels + + +class BingNovels(BaseNovels): + + def __init__(self): + super(BingNovels, self).__init__() + + async def data_extraction(self, html): + """ + 小说信息抓取函数 + :return: + """ + try: + title = html.select('h2 a')[0].get_text() + url = html.select('h2 a')[0].get('href', None) + netloc = urlparse(url).netloc + url = url.replace('index.html', '').replace('Index.html', '') + # if not url or 'baidu' in url or 'baike.so.com' in url or netloc in self.black_domain or '.html' in url: + # return None + is_parse = 1 if netloc in self.rules.keys() else 0 + is_recommend = 1 if netloc in self.latest_rules.keys() else 0 + timestamp = 0 + time = '' + return {'title': title, + 'url': url, + 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + + except Exception as e: + self.logger.exception(e) + return None + + async def novels_search(self, novels_name): + """ + 小说搜索入口函数 + :return: + """ + url = self.config.BY_URL + headers = { + 'user-agent': await get_random_user_agent(), + 'referer': "https://www.bing.com/" + } + params = {'q': novels_name, 'ensearch': 0, "sc": "1-20"} + html = await self.fetch_url(url=url, params=params, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='b_algo') + extra_tasks = [self.data_extraction(html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + done_list, pending_list = await asyncio.wait(tasks) + res = [task.result() for task in done_list if task.result()] + return res + else: + return [] + + +@cached(ttl=259200, key_from_attr='novels_name', serializer=PickleSerializer(), namespace="novels_name") +async def start(novels_name): + """ + Start spider + :return: + """ + return await BingNovels.start(novels_name) + + +if __name__ == '__main__': + # Start + import aiocache + + REDIS_DICT = {} + aiocache.settings.set_defaults( + class_="aiocache.RedisCache", + endpoint=REDIS_DICT.get('REDIS_ENDPOINT', 'localhost'), + port=REDIS_DICT.get('REDIS_PORT', 6379), + db=REDIS_DICT.get('CACHE_DB', 0), + password=REDIS_DICT.get('REDIS_PASSWORD', None), + ) + res = asyncio.get_event_loop().run_until_complete(start('雪中悍刀行 小说 阅读 最新章节')) + print(res) diff --git a/soulbook/fetcher/novels_factory/duck_go_novels.py b/soulbook/fetcher/novels_factory/duck_go_novels.py new file mode 100644 index 0000000..0943e59 --- /dev/null +++ b/soulbook/fetcher/novels_factory/duck_go_novels.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import asyncio + +from aiocache.serializers import PickleSerializer +from bs4 import BeautifulSoup +from urllib.parse import parse_qs, urlparse + +from soulbook.fetcher.decorators import cached +from soulbook.fetcher.function import get_random_user_agent +from soulbook.fetcher.novels_factory.base_novels import BaseNovels + + +class DuckGoNovels(BaseNovels): + + def __init__(self): + super(DuckGoNovels, self).__init__() + + async def data_extraction(self, html): + """ + 小说信息抓取函数 + :return: + """ + try: + title = html.select('h2 a')[0].get_text() + url = html.select('h2 a')[0].get('href', None) + url = parse_qs(url).get('uddg', ['#'])[0] + netloc = urlparse(url).netloc + url = url.replace('index.html', '').replace('Index.html', '') + if not url or 'baidu' in url or 'baike.so.com' in url or netloc in self.black_domain or '.html' in url: + return None + is_parse = 1 if netloc in self.rules.keys() else 0 + is_recommend = 1 if netloc in self.latest_rules.keys() else 0 + # time = html.select('div.b_attribution')[0].get_text() + # time = re.findall(r'\d+-\d+-\d+', time) + # time = time[0] if time else '' + timestamp = 0 + time = '' + return {'title': title, + 'url': url, + 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + + except Exception as e: + self.logger.exception(e) + return None + + async def novels_search(self, novels_name): + """ + 小说搜索入口函数 + :return: + """ + url = self.config.DUCKGO_URL + headers = { + 'user-agent': await get_random_user_agent(), + 'referer': "https://duckduckgo.com/" + } + params = {'q': novels_name} + html = await self.fetch_url(url=url, params=params, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='result') + extra_tasks = [self.data_extraction(html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + done_list, pending_list = await asyncio.wait(tasks) + res = [task.result() for task in done_list if task.result()] + return res + else: + return [] + + +@cached(ttl=259200, key_from_attr='novels_name', serializer=PickleSerializer(), namespace="novels_name") +async def start(novels_name): + """ + Start spider + :return: + """ + return await DuckGoNovels.start(novels_name) + + +if __name__ == '__main__': + # Start + res = asyncio.get_event_loop().run_until_complete(start('雪中悍刀行 小说 阅读 最新章节')) + print(res) diff --git a/soulbook/fetcher/novels_factory/so_novels.py b/soulbook/fetcher/novels_factory/so_novels.py new file mode 100644 index 0000000..410b9eb --- /dev/null +++ b/soulbook/fetcher/novels_factory/so_novels.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import asyncio + +from aiocache.serializers import PickleSerializer +from bs4 import BeautifulSoup +from urllib.parse import parse_qs, urlparse + +from soulbook.fetcher.decorators import cached +from soulbook.fetcher.function import get_random_user_agent +from soulbook.fetcher.novels_factory.base_novels import BaseNovels + + +class SoNovels(BaseNovels): + + def __init__(self): + super(SoNovels, self).__init__() + + async def data_extraction(self, html): + """ + 小说信息抓取函数 + :return: + """ + try: + # 2017.09.09 修改 更加全面地获取title && url + try: + title = html.select('h3 a')[0].get_text() + url = html.select('h3 a')[0].get('href', None) + except Exception as e: + self.logger.exception(e) + return None + + # 针对不同的请进行url的提取 + if "www.so.com/link?m=" in url: + url = html.select('h3 a')[0].get('data-mdurl', None) + if "www.so.com/link?url=" in url: + url = parse_qs(urlparse(url).query).get('url', None) + url = url[0] if url else None + + netloc = urlparse(url).netloc + if not url or 'baidu' in url or 'baike.so.com' in url or netloc in self.black_domain: + return None + is_parse = 1 if netloc in self.rules.keys() else 0 + is_recommend = 1 if netloc in self.latest_rules.keys() else 0 + time = '' + timestamp = 0 + return {'title': title, 'url': url.replace('index.html', '').replace('Index.html', ''), 'time': time, + 'is_parse': is_parse, + 'is_recommend': is_recommend, + 'timestamp': timestamp, + 'netloc': netloc} + except Exception as e: + self.logger.exception(e) + return None + + async def novels_search(self, novels_name): + """ + 小说搜索入口函数 + :return: + """ + url = self.config.SO_URL + + headers = { + 'User-Agent': await get_random_user_agent(), + 'Referer': "http://www.so.com/haosou.html?src=home" + } + params = {'ie': 'utf-8', 'src': 'noscript_home', 'shb': 1, 'q': novels_name, } + html = await self.fetch_url(url=url, params=params, headers=headers) + if html: + soup = BeautifulSoup(html, 'html5lib') + result = soup.find_all(class_='res-list') + extra_tasks = [self.data_extraction(html=i) for i in result] + tasks = [asyncio.ensure_future(i) for i in extra_tasks] + done_list, pending_list = await asyncio.wait(tasks) + res = [task.result() for task in done_list if task.result()] + return res + else: + return [] + + +@cached(ttl=259200, key_from_attr='novels_name', serializer=PickleSerializer(), namespace="novels_name") +async def start(novels_name): + """ + Start spider + :return: + """ + return await SoNovels.start(novels_name) + + +if __name__ == '__main__': + # Start + import aiocache + + REDIS_DICT = {} + aiocache.settings.set_defaults( + class_="aiocache.RedisCache", + endpoint=REDIS_DICT.get('REDIS_ENDPOINT', 'localhost'), + port=REDIS_DICT.get('REDIS_PORT', 6379), + db=REDIS_DICT.get('CACHE_DB', 0), + password=REDIS_DICT.get('REDIS_PASSWORD', None), + ) + res = asyncio.get_event_loop().run_until_complete(start('雪中悍刀行 小说 最新章节')) + print(res) diff --git a/soulbook/fetcher/novels_factory/ssl_factory.py b/soulbook/fetcher/novels_factory/ssl_factory.py new file mode 100644 index 0000000..c62120b --- /dev/null +++ b/soulbook/fetcher/novels_factory/ssl_factory.py @@ -0,0 +1,31 @@ +# coding: utf-8 +""" +绕过 JA3指纹反爬机制 +参考:https://mp.weixin.qq.com/s/tuZScT0qVwPl6bQewqz0zg +""" + +import random +import ssl + +# ssl._create_default_https_context = ssl._create_unverified_context + + +ORIGIN_CIPHERS = ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' + 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES') + + +class SSLFactory: + def __init__(self): + self.ciphers = ORIGIN_CIPHERS.split(":") + + def __call__(self) -> ssl.SSLContext: + random.shuffle(self.ciphers) + ciphers = ":".join(self.ciphers) + ciphers = ciphers + ":!aNULL:!eNULL:!MD5" + + context = ssl.create_default_context() + context.set_ciphers(ciphers) + return context + + +ssl_gen = SSLFactory() diff --git a/soulbook/fetcher/novels_schedule.py b/soulbook/fetcher/novels_schedule.py new file mode 100644 index 0000000..a3a6b35 --- /dev/null +++ b/soulbook/fetcher/novels_schedule.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" + +import asyncio +import os +import schedule +import sys +import time +import uvloop + +os.environ['MODE'] = 'PRO' +sys.path.append('../../') + +from soulbook.fetcher.cache import update_all_books + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +loop = asyncio.get_event_loop() + + +def update_all_books_schedule(): + task = asyncio.ensure_future(update_all_books(loop)) + loop.run_until_complete(task) + return task.result() or None + + +# python novels_schedule.py +schedule.every(180).minutes.do(update_all_books_schedule) + +while True: + schedule.run_pending() + time.sleep(1) diff --git a/soulbook/fetcher/novels_tools.py b/soulbook/fetcher/novels_tools.py new file mode 100644 index 0000000..dea0433 --- /dev/null +++ b/soulbook/fetcher/novels_tools.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" + +from importlib import import_module + + +async def get_novels_info(class_name, novels_name): + novels_module = import_module( + "soulbook.fetcher.{}.{}_novels".format('novels_factory', class_name)) + try: + # 获取对应渠道实例化对象 + novels_info = await novels_module.start(novels_name) + except Exception as e: + print("get_novels_info: %s" % e) + novels_info = None + return novels_info + + +if __name__ == '__main__': + import asyncio + import aiocache + + REDIS_DICT = {} + aiocache.settings.set_defaults( + class_="aiocache.RedisCache", + endpoint=REDIS_DICT.get('REDIS_ENDPOINT', 'localhost'), + port=REDIS_DICT.get('REDIS_PORT', 6379), + db=REDIS_DICT.get('CACHE_DB', 0), + password=REDIS_DICT.get('REDIS_PASSWORD', None), + ) + + res = asyncio.get_event_loop().run_until_complete( + get_novels_info(class_name='baidu', novels_name='intitle:雪中悍刀行 小说 阅读')) + print(res) diff --git a/soulbook/fetcher/response_base.py b/soulbook/fetcher/response_base.py new file mode 100644 index 0000000..b356071 --- /dev/null +++ b/soulbook/fetcher/response_base.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 17-9-22. +""" + + +class ResponseField(): + """ + Define the response field + """ + MESSAGE = 'info' + STATUS = 'state' + DATA = 'data' + FINISH_AT = 'finished_at' + + +class ResponseReply(): + """ + Define field description + """ + # ERROR + UNKNOWN_ERR = '未知错误' + PARAM_ERR = '参数错误!' + PARAM_PARSE_ERR = "参数解析错误!" + DB_ERROR = "数据库操作错误" + # FORBIDDEN + IP_FORBIDDEN = "ip被禁" + # NOT AUTHORIZED + NOT_AUTHORIZED = "验证未通过" + # SUCCESS + SUCCESS = 'ok' + + +class ResponseCode(): + """ + Define the response code + """ + SUCCESS = 200 + CREATED = 201 + NO_CONTENT = 204 + BAD_REQUEST = 400 + NOT_AUTHORIZED = 401 + FORBIDDEN = 403 + NOT_FOUND = 404 + SERVER_ERR = 500 + + +class UniResponse(): + NOT_AUTHORIZED = {ResponseField.MESSAGE: ResponseReply.NOT_AUTHORIZED, + ResponseField.STATUS: ResponseCode.NOT_AUTHORIZED} + PARAM_PARSE_ERR = {ResponseField.MESSAGE: ResponseReply.PARAM_PARSE_ERR, + ResponseField.STATUS: ResponseCode.BAD_REQUEST} + PARAM_ERR = {ResponseField.MESSAGE: ResponseReply.PARAM_ERR, + ResponseField.STATUS: ResponseCode.BAD_REQUEST} + PARAM_UNKNOWN_ERR = {ResponseField.MESSAGE: ResponseReply.UNKNOWN_ERR, + ResponseField.STATUS: ResponseCode.BAD_REQUEST} + IP_FORBIDDEN = {ResponseField.MESSAGE: ResponseReply.IP_FORBIDDEN, + ResponseField.STATUS: ResponseCode.FORBIDDEN} + SERVER_DB_ERR = {ResponseField.MESSAGE: ResponseReply.DB_ERROR, + ResponseField.STATUS: ResponseCode.SERVER_ERR} + SERVER_UNKNOWN_ERR = {ResponseField.MESSAGE: ResponseReply.UNKNOWN_ERR, + ResponseField.STATUS: ResponseCode.SERVER_ERR} + SUCCESS = {ResponseField.MESSAGE: ResponseReply.SUCCESS, + ResponseField.STATUS: ResponseCode.SUCCESS} diff --git a/soulbook/recommend/__init__.py b/soulbook/recommend/__init__.py new file mode 100644 index 0000000..f206c27 --- /dev/null +++ b/soulbook/recommend/__init__.py @@ -0,0 +1 @@ +from .cosinesimilarity import CosineSimilarity \ No newline at end of file diff --git a/soulbook/recommend/cosinesimilarity.py b/soulbook/recommend/cosinesimilarity.py new file mode 100644 index 0000000..0499111 --- /dev/null +++ b/soulbook/recommend/cosinesimilarity.py @@ -0,0 +1,58 @@ +import numpy as np + +from functools import reduce +from math import sqrt + + +class CosineSimilarity(object): + """ + 余弦相似性计算相似度 + """ + + def __init__(self, initQuery, userData): + self.title = initQuery + self.data = userData + + def create_vector(self): + """ + 创建兴趣向量 + :return: wordVector = {} 目标用户以及各个兴趣对应的向量 + """ + wordVector = {} + for web, value in self.data.items(): + wordVector[web] = [] + titleVector, valueVector = [], [] + allWord = set(self.title + value) + for eachWord in allWord: + titleNum = self.title.count(eachWord) + valueNum = value.count(eachWord) + titleVector.append(titleNum) + valueVector.append(valueNum) + wordVector[web].append(titleVector) + wordVector[web].append(valueVector) + return wordVector + + def calculate(self, wordVector): + """ + 计算余弦相似度 + :param wordVector: wordVector = {} 目标用户以及各个兴趣对应的向量 + :return: 返回各个用户相似度值 + """ + resultDic = {} + for web, value in wordVector.items(): + valueArr = np.array(value) + # 余弦相似性 + squares = [] + numerator = reduce(lambda x, y: x + y, valueArr[0] * valueArr[1]) + square_title, square_data = 0.0, 0.0 + for num in range(len(valueArr[0])): + square_title += pow(valueArr[0][num], 2) + square_data += pow(valueArr[1][num], 2) + squares.append(sqrt(square_title)) + squares.append(sqrt(square_data)) + mul_of_squares = reduce(lambda x, y: x * y, squares) + value = float(('%.5f' % (numerator / mul_of_squares))) + if value > 0: + resultDic[web] = value + resultDic = [{v[0]: v[1]} for v in sorted(resultDic.items(), key=lambda d: d[1], reverse=True)] + return resultDic diff --git a/soulbook/recommend/owl_ranking.py b/soulbook/recommend/owl_ranking.py new file mode 100644 index 0000000..4265cc3 --- /dev/null +++ b/soulbook/recommend/owl_ranking.py @@ -0,0 +1 @@ +#!/usr/bin/env python diff --git a/soulbook/run.py b/soulbook/run.py new file mode 100644 index 0000000..c9ce537 --- /dev/null +++ b/soulbook/run.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/8/13. +""" +import os +import subprocess + +if __name__ == '__main__': + os.environ['MODE'] = 'PRO' + servers = [ + ["pipenv", "run", "gunicorn", "-c", "config/gunicorn.py", "--worker-class", "sanic.worker.GunicornWorker", + "server:app"], + ["pipenv", "run", "python", "scheduled_task.py"] + ] + procs = [] + for server in servers: + proc = subprocess.Popen(server) + procs.append(proc) + for proc in procs: + proc.wait() + if proc.poll(): + exit(0) diff --git a/soulbook/scheduled_task.py b/soulbook/scheduled_task.py new file mode 100644 index 0000000..1a6f5dc --- /dev/null +++ b/soulbook/scheduled_task.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/8/13. +""" + +import os +import asyncio +import sys +import time + +import schedule +import uvloop + +os.environ['MODE'] = 'PRO' +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from soulbook.config import CONFIG +from soulbook.spiders import QidianRankingSpider, ZHRankingSpider +from soulbook.fetcher.cache import update_all_books + +asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +loop = asyncio.get_event_loop() + + +def start_spider(): + QidianRankingSpider.start() + ZHRankingSpider.start() + + +def update_all_books_schedule(): + task = asyncio.ensure_future(update_all_books(loop)) + loop.run_until_complete(task) + return task.result() or None + + +def refresh_task(): + schedule.every(CONFIG.SCHEDULED_DICT['SPIDER_INTERVAL']).minutes.do(start_spider) + # schedule.every(CONFIG.SCHEDULED_DICT['NOVELS_INTERVAL']).minutes.do(update_all_books_schedule) + + while True: + schedule.run_pending() + time.sleep(1) + + +if __name__ == '__main__': + start_spider() + refresh_task() diff --git a/soulbook/server.py b/soulbook/server.py new file mode 100644 index 0000000..2078bbf --- /dev/null +++ b/soulbook/server.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +import aiocache +import os +import sys + +from sanic import Sanic +from sanic.response import html, redirect +from sanic_session import RedisSessionInterface + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from soulbook.views import admin_bp, api_bp, except_bp, md_bp, novels_bp, operate_bp +from soulbook.database.redis import RedisSession +from soulbook.config import LOGGER, CONFIG + +app = Sanic(__name__) +app.blueprint(md_bp) +app.blueprint(novels_bp) +app.blueprint(operate_bp) +app.blueprint(except_bp) +app.blueprint(admin_bp) +app.blueprint(api_bp) + + +@app.listener('before_server_start') +def init_cache(app, loop): + LOGGER.info("Starting aiocache") + app.config.from_object(CONFIG) + REDIS_DICT = CONFIG.REDIS_DICT + aiocache.settings.set_defaults( + class_="aiocache.RedisCache", + endpoint=REDIS_DICT.get('REDIS_ENDPOINT', 'localhost'), + port=REDIS_DICT.get('REDIS_PORT', 6379), + db=REDIS_DICT.get('CACHE_DB', 0), + password=REDIS_DICT.get('REDIS_PASSWORD', None), + loop=loop, + ) + LOGGER.info("Starting redis pool") + redis_session = RedisSession() + # redis instance for app + app.get_redis_pool = redis_session.get_redis_pool + # pass the getter method for the connection pool into the session + app.session_interface = RedisSessionInterface( + app.get_redis_pool, cookie_name="owl_sid", expiry=30 * 24 * 60 * 60) + + +@app.middleware('request') +async def add_session_to_request(request): + # before each request initialize a session + # using the client's request + host = request.headers.get('host', None) + user_agent = request.headers.get('user-agent', None) + if user_agent: + user_ip = request.headers.get('X-Forwarded-For') + LOGGER.info('user ip is: {}'.format(user_ip)) + if user_ip in CONFIG.FORBIDDEN: + return html("

网站正在维护...

") + if CONFIG.VAL_HOST == 'true': + if not host or host not in CONFIG.HOST: + return redirect('http://www.owllook.net') + if CONFIG.WEBSITE['IS_RUNNING']: + await app.session_interface.open(request) + else: + return html("

网站正在维护...

") + else: + return html("

网站正在维护...

") + + +@app.middleware('response') +async def save_session(request, response): + # after each request save the session, + # pass the response to set client cookies + # await app.session_interface.save(request, response) + if request.path == '/operate/login' and request['session'].get('user', None): + await app.session_interface.save(request, response) + import datetime + response.cookies['owl_sid']['expires'] = datetime.datetime.now( + ) + datetime.timedelta(days=30) + elif request.path == '/register': + try: + response.cookies['reg_index'] = str(request['session']['index'][0]) + except KeyError as e: + LOGGER.error(e) + + +if __name__ == "__main__": + workers = 1 if os.name == 'nt' else 2 + app.run(host="0.0.0.0", workers=workers, port=8001, debug=CONFIG.DEBUG) diff --git a/soulbook/spiders/README.md b/soulbook/spiders/README.md new file mode 100644 index 0000000..f477493 --- /dev/null +++ b/soulbook/spiders/README.md @@ -0,0 +1,24 @@ +## 小说榜单爬虫 + +> 获取各大小说站的小说榜单排名,使用自己写的简单爬虫框架[talospider](https://github.com/howie6879/talospider)来进行脚本的编写 + +### 1.起点榜单 + +> 网址:http://r.qidian.com/?chn=-1 + +| 参数 | 说明 | +| :-------------------------: | :--: | +| http://r.qidian.com/?chn=-1 | 全部分类 | +| http://r.qidian.com/?chn=21 | 玄幻 | +| http://r.qidian.com/?chn=1 | 奇幻 | +| http://r.qidian.com/?chn=2 | 武侠 | +| http://r.qidian.com/?chn=22 | 仙侠 | +| http://r.qidian.com/?chn=4 | 都市 | +| http://r.qidian.com/?chn=15 | 职场 | +| http://r.qidian.com/?chn=6 | 军事 | +| http://r.qidian.com/?chn=5 | 历史 | +| http://r.qidian.com/?chn=7 | 游戏 | +| http://r.qidian.com/?chn=8 | 体育 | +| http://r.qidian.com/?chn=9 | 科幻 | +| http://r.qidian.com/?chn=10 | 灵异 | +| http://r.qidian.com/?chn=12 | 二次元 | \ No newline at end of file diff --git a/soulbook/spiders/__init__.py b/soulbook/spiders/__init__.py new file mode 100644 index 0000000..8e0a99b --- /dev/null +++ b/soulbook/spiders/__init__.py @@ -0,0 +1,2 @@ +from .qidian_ranking import QidianRankingSpider +from .zh_ranking import ZHRankingSpider \ No newline at end of file diff --git a/soulbook/spiders/heiyan_novel_info.py b/soulbook/spiders/heiyan_novel_info.py new file mode 100644 index 0000000..3ee9a69 --- /dev/null +++ b/soulbook/spiders/heiyan_novel_info.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 30/03/2018. +""" +import time + +from pprint import pprint + +from ruia import Spider, Item, TextField, AttrField +from ruia_ua import middleware as ua_middleware + +from soulbook.database.mongodb import MotorBase + + +class HYNovelInfoItem(Item): + """ + 定义继承自item的Item类 + """ + novel_name = AttrField(css_select="meta[property='og:title']", attr='content') + author = AttrField(css_select="meta[property='og:novel:author']", attr='content') + cover = AttrField(css_select="meta[property='og:image']", attr='content') + abstract = AttrField(css_select="meta[property='og:description']", attr='content') + status = AttrField(css_select="meta[property='og:novel:status']", attr='content') + novels_type = AttrField(css_select="meta[property='og:novel:category']", attr='content') + novel_chapter_url = AttrField(css_select='div#voteList a.index', attr='href') + latest_chapter = AttrField(css_select="meta[property='og:novel:latest_chapter_name']", attr='content') + latest_chapter_url = AttrField(css_select="meta[property='og:novel:latest_chapter_url']", attr='content') + latest_chapter_time = AttrField(css_select="meta[property='og:novel:update_time']", attr='content') + + # novel_name = TextField(css_select='div.c-left>div.mod>div.hd>h2') + # author = TextField(css_select='div.author-zone div.right a.name strong') + # cover = AttrField(css_select='img.book-cover', attr='src') + # abstract = TextField(css_select='pre.note') + # status = '' + # novels_type = TextField(css_select='div.c-left>div.mod>div.hd>p.infos>span.cate>a') + # latest_chapter = '' + # novel_chapter_url = AttrField(css_select='div#voteList a.index', attr='href') + + async def clean_cover(self, cover): + if 'https' in cover: + return cover + else: + return cover.replace('http', 'https') + + async def clean_novels_type(self, novels_type): + types_dict = { + '社会': '都市' + } + print(types_dict.get(str(novels_type).strip(), novels_type)) + return types_dict.get(str(novels_type).strip(), novels_type) + + async def clean_latest_chapter_time(self, latest_chapter_time): + return latest_chapter_time.replace(u'今天', str(time.strftime("%Y-%m-%d ", time.localtime()))).replace(u'昨日', str( + time.strftime("%Y-%m-%d ", time.localtime(time.time() - 24 * 60 * 60)))) + + +class HYNovelInfoSpider(Spider): + start_urls = [] + request_config = { + 'RETRIES': 3, + 'TIMEOUT': 10 + } + + async def parse(self, res): + self.motor_db = MotorBase(loop=self.loop).get_db() + item = await HYNovelInfoItem.get_item(html=res.html) + + item_data = { + 'novel_name': item.novel_name, + 'author': item.author, + 'cover': item.cover, + 'abstract': item.abstract, + 'status': item.status, + 'novels_type': item.novels_type, + 'novel_chapter_url': item.novel_chapter_url, + 'latest_chapter': item.latest_chapter, + 'latest_chapter_time': item.latest_chapter_time, + 'spider': 'heiyan', + 'target_url': res.url, + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()) + } + + print('获取 {} 小说信息成功'.format(item_data['novel_name'])) + await self.save(res_dic=item_data) + + async def save(self, res_dic): + # 存进数据库 + try: + motor_db = MotorBase().get_db() + await motor_db.all_novels_info.update_one({ + 'novel_name': res_dic['novel_name'], 'spider': 'heiyan'}, + {'$set': res_dic}, + upsert=True) + except Exception as e: + self.logger.exception(e) + + +if __name__ == '__main__': + HYNovelInfoSpider.start_urls = ['http://www.heiyan.com/book/62599'] + # HYNovelInfoSpider.start_urls = [each.get('novel_url', '') for each in search_author('火星引力', 'qidian')] + print(HYNovelInfoSpider.start_urls) + HYNovelInfoSpider.start(middleware=ua_middleware) diff --git a/soulbook/spiders/middlewares.py b/soulbook/spiders/middlewares.py new file mode 100644 index 0000000..203bb0e --- /dev/null +++ b/soulbook/spiders/middlewares.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/10/17. +""" + +from ruia import Middleware + +from soulbook.spiders.spider_tools import get_proxy_ip + +owl_middleware = Middleware() + + +@owl_middleware.request +async def add_random_proxy(request): + request.kwargs.update({'proxy': await update_proxy()}) + request.request_config.update({'RETRY_FUNC': retry_func}) + + +async def update_proxy(): + proxy = await get_proxy_ip() + if proxy: + proxy = 'http://' + proxy + else: + proxy = None + return proxy + + +async def retry_func(request): + proxy = await update_proxy() + request.kwargs.update({'proxy': proxy}) + return request diff --git a/soulbook/spiders/qidian_all_novels.py b/soulbook/spiders/qidian_all_novels.py new file mode 100644 index 0000000..e54860b --- /dev/null +++ b/soulbook/spiders/qidian_all_novels.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 25/02/2018. + Target URI: https://www.qidian.com/all + Param:?page=1 +""" +import asyncio +import os +import time + +from ruia import Spider, Item, TextField, AttrField +from ruia_ua import middleware as ua_middleware + +# os.environ['MODE'] = 'PRO' + +from soulbook.database.mongodb import MotorBase +from soulbook.spiders.middlewares import owl_middleware + +try: + import uvloop + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +except ImportError: + pass + +loop = asyncio.get_event_loop() +asyncio.set_event_loop(loop) + + +class QidianNovelsItem(Item): + target_item = TextField(css_select='ul.all-img-list>li') + novel_url = AttrField(css_select='div.book-img-box>a', attr='href') + novel_name = TextField(css_select='div.book-mid-info>h4') + novel_author = TextField(css_select='div.book-mid-info>p.author>a.name') + novel_author_home_url = AttrField(css_select='div.book-mid-info>p.author>a.name', attr='href') + novel_type = TextField(css_select='div.book-mid-info > p.author > a:nth-child(4)') + novel_cover = AttrField(css_select='div.book-img-box img', attr='src') + novel_abstract = TextField(css_select='div.book-mid-info p.intro') + + # novel_latest_chapter = TextField(css_select='div.bookupdate a') + + async def clean_novel_url(self, novel_url): + return 'https:' + novel_url + + async def clean_novel_author(self, novel_author): + if isinstance(novel_author, list): + novel_author = novel_author[0].text + return novel_author + + async def clean_novel_author_home_url(self, novel_author_home_url): + if isinstance(novel_author_home_url, list): + novel_author_home_url = novel_author_home_url[0].get('href').strip() + return 'https:' + novel_author_home_url + + async def clean_novel_cover(self, novel_cover): + return 'https:' + novel_cover + + +class QidianNovelsSpider(Spider): + # start_urls = ['https://www.qidian.com/all?page=1'] + + request_config = { + 'RETRIES': 15, + 'DELAY': 0, + 'TIMEOUT': 3 + } + concurrency = 20 + motor_db = MotorBase(loop=loop).get_db() + + async def parse(self, res): + items_data = await QidianNovelsItem.get_items(html=res.html) + tasks = [] + for item in items_data: + res_dic = { + 'novel_url': item.novel_url, + 'novel_name': item.novel_name, + 'novel_author': item.novel_author, + 'novel_author_home_url': item.novel_author_home_url, + 'novel_type': item.novel_type, + 'novel_cover': item.novel_cover, + 'novel_abstract': item.novel_abstract, + 'spider': 'qidian', + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()), + } + tasks.append(asyncio.ensure_future(self.save(res_dic))) + + good_nums = 0 + if tasks: + done_list, pending_list = await asyncio.wait(tasks) + for task in done_list: + if task.result(): + good_nums += 1 + print(f"共{len(tasks)}本小说,抓取成功{good_nums}本") + + async def save(self, res_dic): + # 存进数据库 + try: + await self.motor_db.all_novels.update_one( + {'novel_url': res_dic['novel_url'], 'novel_name': res_dic['novel_name']}, + {'$set': res_dic}, + upsert=True) + print(res_dic['novel_name'] + ' - 抓取成功') + return True + except Exception as e: + self.logger.exception(e) + return False + + +if __name__ == '__main__': + # 51793 + for page in range(248, 519): + print(f"正在爬取第{page}页") + start_page = page * 100 + end_page = start_page + 100 + if end_page > 51793: + end_page = 51793 + QidianNovelsSpider.start_urls = ['https://www.qidian.com/all?page={i}'.format(i=i) for i in + range(start_page, end_page)] + # 其他多item示例:https://gist.github.com/howie6879/3ef4168159e5047d42d86cb7fb706a2f + QidianNovelsSpider.start(loop=loop, middleware=[ua_middleware, owl_middleware], close_event_loop=False) diff --git a/soulbook/spiders/qidian_honor_spider.py b/soulbook/spiders/qidian_honor_spider.py new file mode 100644 index 0000000..8e4c9a4 --- /dev/null +++ b/soulbook/spiders/qidian_honor_spider.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 11/03/2018. + 获取起点荣誉数据,如:https://book.qidian.com/honor/1009704712 + 荣誉类型: + - 推荐票 + - 收藏 + - 点击 +""" + +from ruia import Spider, Item, TextField +from ruia_ua import middleware + + +class QidianHonorItem(Item): + target_item = TextField(css_select='li.cf') + honor_text = TextField(css_select='span.decs') + honor_time = TextField(css_select='span.time') + + +class QidianHonorSpider(Spider): + start_urls = ['https://book.qidian.com/honor/1009531496'] + + request_config = { + 'RETRIES': 3, + 'DELAY': 0, + 'TIMEOUT': 10 + } + + async def parse(self, res): + items_data = await QidianHonorItem.get_items(html=res.html) + click_list, col_list, rec_list, other_list = [], [], [], [] + for item in items_data: + data = { + 'honor_text': item.honor_text, + 'honor_time': item.honor_time, + } + if "点击" in data['honor_text'] and '月点击' not in data['honor_text']: + click_list.append(data) + elif "收藏" in data['honor_text']: + col_list.append(data) + elif "推荐票" in data['honor_text']: + rec_list.append(data) + else: + other_list.append(data) + print('点击荣誉\n') + for i in click_list: + print(str(i.get('honor_time')) + " - " + str(i.get('honor_text'))) + + print('收藏荣誉\n') + for i in col_list: + print(str(i.get('honor_time')) + " - " + str(i.get('honor_text'))) + + print('推荐票荣誉\n') + for i in rec_list: + print(str(i.get('honor_time')) + " - " + str(i.get('honor_text'))) + + print('强推荣誉\n') + for i in other_list: + print(str(i.get('honor_time')) + " - " + str(i.get('honor_text'))) + + +if __name__ == '__main__': + QidianHonorSpider.start(middleware=middleware) diff --git a/soulbook/spiders/qidian_novel_info.py b/soulbook/spiders/qidian_novel_info.py new file mode 100644 index 0000000..a6f546e --- /dev/null +++ b/soulbook/spiders/qidian_novel_info.py @@ -0,0 +1,85 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python +import os +import time + +from ruia import Spider, Item, TextField, AttrField +from ruia_ua import middleware + +os.environ['MODE'] = 'PRO' + +from soulbook.database.mongodb import MotorBaseOld + + +class QidianNovelInfoItem(Item): + """ + 定义继承自item的Item类 + """ + novel_name = TextField(css_select='.book-info>h1>em') + author = TextField(css_select='a.writer') + # 当提取的值是属性的时候,要定义AttrField + cover = AttrField(css_select='a#bookImg>img', attr='src') + abstract = TextField(css_select='div.book-intro>p') + status = TextField(css_select='p.tag>span.blue') + novels_type = TextField(css_select='p.tag>a.red') + latest_chapter = TextField(css_select='li.update>div.detail>p.cf>a') + latest_chapter_time = TextField(css_select='div.detail>p.cf>em') + + async def clean_cover(self, cover): + return 'http:' + cover + + async def clean_status(self, status): + """ + 当目标值的对象只有一个,默认将值提取出来,否则返回list,可以在这里定义一个函数进行循环提取 + :param ele_tag: + :return: + """ + return '#'.join([i.text for i in status]) + + async def clean_novels_type(self, novels_type): + return '#'.join([i.text for i in novels_type]) + + async def clean_latest_chapter_time(self, latest_chapter_time): + return latest_chapter_time.replace(u'今天', str(time.strftime("%Y-%m-%d ", time.localtime()))).replace(u'昨日', str( + time.strftime("%Y-%m-%d ", time.localtime(time.time() - 24 * 60 * 60)))) + + +class QidianNovelInfoSpider(Spider): + request_config = { + 'RETRIES': 3, + 'TIMEOUT': 10 + } + + async def parse(self, res): + motor_db = MotorBaseOld().db + item = await QidianNovelInfoItem.get_item(html=res.html) + item_data = { + 'novel_name': item.novel_name, + 'author': item.author, + 'cover': item.cover, + 'abstract': item.abstract, + 'status': item.status, + 'novels_type': item.novels_type, + 'latest_chapter': item.latest_chapter, + 'latest_chapter_time': item.latest_chapter_time, + 'spider': 'qidian', + 'target_url': res.url, + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()) + } + print('获取 {} 小说信息成功'.format(item.novel_name)) + await motor_db.all_novels_info.update_one( + {'novel_name': item_data['novel_name'], 'spider': item_data['spider']}, + {'$set': item_data}, + upsert=True) + + +if __name__ == '__main__': + import random + + # 其他多item示例:https://gist.github.com/howie6879/3ef4168159e5047d42d86cb7fb706a2f + QidianNovelInfoSpider.start_urls = ['https://book.qidian.com/info/1004608738', + 'https://book.qidian.com/info/3602691', + 'https://book.qidian.com/info/3347595', 'https://book.qidian.com/info/1887208'] + + # QidianNovelInfoSpider.start_urls = all_urls + QidianNovelInfoSpider.start(middleware=middleware) diff --git a/soulbook/spiders/qidian_ranking.py b/soulbook/spiders/qidian_ranking.py new file mode 100644 index 0000000..8f3f640 --- /dev/null +++ b/soulbook/spiders/qidian_ranking.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +import asyncio +import time + +from ruia import Spider, Item, AttrField, HtmlField, TextField +from ruia_ua import middleware + +from soulbook.database.mongodb import MotorBaseOld + + +class RankingItem(Item): + target_item = TextField(css_select='.rank-list') + ranking_title = TextField(css_select='h3.wrap-title') + more = AttrField(css_select='h3>a.more', attr='href') + book_list = HtmlField(css_select='div.book-list>ul>li', many=True) + + async def clean_ranking_title(self, ranking_title): + if isinstance(ranking_title, list): + return ranking_title[0].text + else: + return str(ranking_title).split('榜')[0] + '榜' + + async def clean_more(self, more): + return "https:" + more + + +class NameItem(Item): + top_name = TextField(css_select='h4', default='') + other_name = TextField(css_select='a.name', default='') + + +class QidianRankingSpider(Spider): + start_urls = [f"https://www.qidian.com/rank/?chn={key}" for key in + [-1, 21, 1, 2, 22, 4, 15, 6, 5, 7, 8, 9, 10, 12]] + + concurrency = 3 + qidian_type = { + '-1': '全部类别', + '21': '玄幻', + '1': '奇幻', + '2': '武侠', + '22': '仙侠', + '4': '都市', + '15': '职场', + '6': '军事', + '5': '历史', + '7': '游戏', + '8': '体育', + '9': '科幻', + '10': '灵异', + '12': '二次元', + } + + async def parse(self, res): + result = [] + res_dic = {} + async for item in RankingItem.get_items(html=res.html): + each_book_list = [] + # 只取排名前十的书籍数据 + for index, value in enumerate(item.book_list[:10]): + item_data = await NameItem.get_item(html=value) + name = item_data.top_name or item_data.other_name + each_book_list.append({ + 'num': index + 1, + 'name': name + }) + data = { + 'title': item.ranking_title, + 'more': item.more, + 'book_list': each_book_list, + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()), + } + result.append(data) + res_dic['data'] = result + res_dic['target_url'] = res.url + res_dic['type'] = self.qidian_type.get(res.url.split('=')[-1]) + res_dic['spider'] = "qidian" + await self.save(res_dic=res_dic) + + async def save(self, res_dic): + # 存进数据库 + try: + motor_db = MotorBaseOld().db + await motor_db.novels_ranking.update_one({ + 'target_url': res_dic['target_url']}, + {'$set': { + 'data': res_dic['data'], + 'spider': res_dic['spider'], + 'type': res_dic['type'], + 'finished_at': time.strftime("%Y-%m-%d %X", time.localtime()) + }}, + upsert=True) + except Exception as e: + self.logger.exception(e) + + +if __name__ == '__main__': + QidianRankingSpider.start(middleware=middleware) diff --git a/soulbook/spiders/spider_console.py b/soulbook/spiders/spider_console.py new file mode 100644 index 0000000..8ac6989 --- /dev/null +++ b/soulbook/spiders/spider_console.py @@ -0,0 +1,23 @@ +# !/usr/bin/env python +import os +import schedule +import sys +import time + +os.environ['MODE'] = 'PRO' +sys.path.append('../../') + +from soulbook.spiders import QidianRankingSpider, ZHRankingSpider + + +def start_spider(): + QidianRankingSpider.start() + ZHRankingSpider.start() + + +# python novels_schedule.py +schedule.every(60).minutes.do(start_spider) + +while True: + schedule.run_pending() + time.sleep(1) diff --git a/soulbook/spiders/spider_tools.py b/soulbook/spiders/spider_tools.py new file mode 100644 index 0000000..c6f2b42 --- /dev/null +++ b/soulbook/spiders/spider_tools.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/10/17. +""" + +import asyncio +import random + +from ruia import Request + +from soulbook.config import CONFIG + + +async def get_proxy_ip(valid: int = 1) -> str: + # random_server = ['http://0.0.0.0:8662/'] + proxy_server = CONFIG.REMOTE_SERVER.get('proxy_server') + # proxy_server = random_server[random.randint(0, 1)] + kwargs = { + 'json': { + "act_id": 1704, + "version": "1.0", + "data": { + "valid": 1 + } + } + } + res = await Request(url=proxy_server, method='POST', res_type='json', **kwargs).fetch() + proxy = '' + if res.status == 200: + proxy = res.html.get('info').get('proxy') + return proxy + + +if __name__ == '__main__': + print(asyncio.get_event_loop().run_until_complete(get_proxy_ip())) diff --git a/soulbook/spiders/zh_ranking.py b/soulbook/spiders/zh_ranking.py new file mode 100644 index 0000000..2bc32da --- /dev/null +++ b/soulbook/spiders/zh_ranking.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 29/11/2017. +""" +import time + +from ruia import Spider, Item, AttrField, HtmlField, TextField +from ruia_ua import middleware + +from soulbook.database.mongodb import MotorBaseOld + + +class RankingItem(Item): + target_item = TextField(css_select='div.rank_i_p_list') + ranking_title = TextField(css_select='div.rank_i_p_tit') + more = AttrField(css_select='div.rank_i_more a', attr='href') + book_list = HtmlField(css_select='div.rank_i_p_list>div.rank_i_li', many=True) + + +class NameItem(Item): + top_name = TextField(css_select='div.rank_i_bname a.rank_i_l_a_book', default='') + other_name = TextField(css_select='div.rank_i_bname a', default='') + + +class ZHRankingSpider(Spider): + start_urls = ['http://book.zongheng.com/rank.html'] + + concurrency = 3 + + async def parse(self, res): + result = [] + res_dic = {} + + async for item in RankingItem.get_items(html=res.html): + each_book_list = [] + # 只取排名前十的书籍数据 + for index, value in enumerate(item.book_list[:10]): + item_data = await NameItem.get_item(html=value) + name = item_data.top_name or item_data.other_name + each_book_list.append({ + 'num': index + 1, + 'name': name + }) + data = { + 'title': item.ranking_title, + 'more': item.more, + 'book_list': each_book_list, + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()), + } + result.append(data) + + res_dic['data'] = result + res_dic['target_url'] = res.url + res_dic['type'] = "人气榜单" + res_dic['spider'] = "zongheng" + await self.save(res_dic) + + async def save(self, res_dic): + try: + motor_db = MotorBaseOld().db + await motor_db.novels_ranking.update_one({ + 'target_url': res_dic['target_url']}, + {'$set': { + 'data': res_dic['data'], + 'spider': res_dic['spider'], + 'type': res_dic['type'], + 'finished_at': time.strftime("%Y-%m-%d %X", time.localtime()) + }}, + upsert=True) + except Exception as e: + self.logger.exception(e) + + +if __name__ == '__main__': + ZHRankingSpider.start(middleware=middleware) diff --git a/soulbook/spiders/zongheng_all_novels.py b/soulbook/spiders/zongheng_all_novels.py new file mode 100644 index 0000000..b2422f3 --- /dev/null +++ b/soulbook/spiders/zongheng_all_novels.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 14/03/2018. + 纵横小说信息提取:http://book.zongheng.com/store/c0/c0/b9/u0/p1/v9/s9/t0/ALL.html +""" +import asyncio +import os +import time + +from ruia import Spider, Item, TextField, AttrField, Request +from ruia_ua import middleware as ua_middleware + +# os.environ['MODE'] = 'PRO' +from soulbook.database.mongodb import MotorBase +from soulbook.spiders.middlewares import owl_middleware + +try: + import uvloop + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +except ImportError: + pass + +loop = asyncio.get_event_loop() +asyncio.set_event_loop(loop) + + +class ZHNovelsItem(Item): + target_item = TextField(css_select='div.store_collist div.bookbox') + novel_url = AttrField(css_select='div.bookinfo div.bookname a', attr='href') + novel_name = TextField(css_select='div.bookinfo div.bookname a') + novel_author = TextField(css_select='div.bookilnk a:nth-child(1)') + novel_author_home_url = AttrField(css_select='div.bookilnk a:nth-child(1)', attr='href') + novel_type = TextField(css_select='div.bookilnk a:nth-child(2)') + novel_cover = AttrField(css_select='div.bookimg img', attr='src') + novel_abstract = TextField(css_select='div.bookintro') + novel_latest_chapter = TextField(css_select='div.bookupdate a') + + # def tal_novel_url(self, novel_url): + # return 'http:' + novel_url + + async def clean_novel_author(self, novel_author): + if novel_author: + if isinstance(novel_author, list): + novel_author = novel_author[0].text + return novel_author + else: + return '' + + # def tal_novel_author_home_url(self, novel_author_home_url): + # if isinstance(novel_author_home_url, list): + # novel_author_home_url = novel_author_home_url[0].get('href').strip() + # return 'http:' + novel_author_home_url + + +class ZHNovelsSpider(Spider): + start_urls = ['http://book.zongheng.com/store/c0/c0/b9/u0/p1/v9/s9/t0/ALL.html'] + + request_config = { + 'RETRIES': 8, + 'DELAY': 0, + 'TIMEOUT': 3 + } + concurrency = 60 + motor_db = MotorBase(loop=loop).get_db() + + async def parse(self, res): + items_data = await ZHNovelsItem.get_items(html=res.html) + tasks = [] + for item in items_data: + if item.novel_url: + res_dic = { + 'novel_url': item.novel_url, + 'novel_name': item.novel_name, + 'novel_author': item.novel_author, + 'novel_author_home_url': item.novel_author_home_url, + 'novel_type': item.novel_type, + 'novel_cover': item.novel_cover, + 'novel_abstract': item.novel_abstract, + 'novel_latest_chapter': item.novel_latest_chapter, + 'spider': 'zongheng', + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()), + } + tasks.append(asyncio.ensure_future(self.save(res_dic))) + # if self.all_novels_col.find_one( + # {"novel_name": item.novel_name, 'novel_author': item.novel_author}) is None: + # self.all_novels_col.insert_one(res_dic) + # # async_callback(self.save, res_dic=res_dic) + # print(item.novel_name + ' - 抓取成功') + good_nums = 0 + if tasks: + done_list, pending_list = await asyncio.wait(tasks) + for task in done_list: + if task.result(): + good_nums += 1 + print(f"共{len(tasks)}本小说,抓取成功{good_nums}本") + + async def save(self, res_dic): + # 存进数据库 + res_dic = res_dic + try: + + await self.motor_db.all_novels.update_one({ + 'novel_url': res_dic['novel_url'], 'novel_name': res_dic['novel_name']}, + {'$set': res_dic}, + upsert=True) + print(res_dic['novel_name'] + ' - 抓取成功') + return True + except Exception as e: + self.logger.exception(e) + return False + + +if __name__ == '__main__': + # 其他多item示例:https://gist.github.com/howie6879/3ef4168159e5047d42d86cb7fb706a2f + # 51793 + for page in range(0, 10): + print(f"正在爬取第{page}页") + start_page = page * 100 + end_page = start_page + 100 + if end_page > 999: + end_page = 999 + ZHNovelsSpider.start_urls = ['http://book.zongheng.com/store/c0/c0/b9/u0/p{i}/v9/s9/t0/ALL.html'.format(i=i) for + i in + range(start_page, end_page)] + # 其他多item示例:https://gist.github.com/howie6879/3ef4168159e5047d42d86cb7fb706a2f + ZHNovelsSpider.start(loop=loop, middleware=[ua_middleware, owl_middleware], close_event_loop=False) diff --git a/soulbook/spiders/zongheng_novel_info.py b/soulbook/spiders/zongheng_novel_info.py new file mode 100644 index 0000000..c3f71d3 --- /dev/null +++ b/soulbook/spiders/zongheng_novel_info.py @@ -0,0 +1,118 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python +import asyncio +import time + +from pprint import pprint + +from ruia import Spider, Item, TextField, AttrField + +from ruia_ua import middleware as ua_middleware + +from soulbook.database.mongodb import MotorBase +from soulbook.spiders.middlewares import owl_middleware + +try: + import uvloop + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +except ImportError: + pass + +loop = asyncio.get_event_loop() +asyncio.set_event_loop(loop) + + +class ZHNovelInfoItem(Item): + """ + 定义继承自item的Item类 + """ + novel_name = TextField(css_select='div.main div.status h1 a') + author = TextField(css_select='div.main div.status div.booksub a') + # 当提取的值是属性的时候,要定义AttrField + cover = AttrField(css_select='div.main div.book_cover img', attr='src') + abstract = TextField(css_select='div.main div.status div.info_con p') + status = AttrField(css_select='div.main div.status h1 em', attr='title') + novels_type = TextField(css_select='div.main div.status div.booksub a') + novel_chapter_url = AttrField(css_select='div.main div.status div.book_btn span.list a', attr='href') + + async def clean_author(self, author): + if isinstance(author, list): + return author[0].text + else: + return author + + async def clean_status(self, status): + """ + 当目标值的对象只有一个,默认将值提取出来,否则返回list,可以在这里定义一个函数进行循环提取 + :param ele_tag: + :return: + """ + if isinstance(status, list): + return '#'.join([i.get('title').strip().replace('作品', '') for i in status]) + else: + return status + + async def clean_novels_type(self, novels_type): + if isinstance(novels_type, list): + try: + return novels_type[1].text + except: + return '' + else: + return '' + + +class ZHNovelInfoSpider(Spider): + start_urls = [] + request_config = { + 'RETRIES': 3, + 'DELAY': 2, + 'TIMEOUT': 10 + } + motor_db = MotorBase(loop=loop).get_db() + + async def parse(self, res): + item = await ZHNovelInfoItem.get_item(html=res.html) + + item_data = { + 'novel_name': item.novel_name, + 'author': item.author, + 'cover': item.cover, + 'abstract': item.abstract, + 'status': item.status, + 'novels_type': item.novels_type, + 'novel_chapter_url': item.novel_chapter_url, + 'target_url': res.url, + 'spider': 'zongheng', + 'updated_at': time.strftime("%Y-%m-%d %X", time.localtime()), + } + + print('获取 {} 小说信息成功'.format(item_data['novel_name'])) + print(item_data) + await self.motor_db.all_novels_info.update_one( + {'novel_name': item_data['novel_name'], 'spider': 'zongheng'}, + {'$set': item_data}, + upsert=True) + + +if __name__ == '__main__': + import random + + # 其他多item示例:https://gist.github.com/howie6879/3ef4168159e5047d42d86cb7fb706a2f + ZHNovelInfoSpider.start_urls = ['http://book.zongheng.com/book/672340.html'] + ZHNovelInfoSpider.start(middleware=[ua_middleware, owl_middleware]) + + # def all_novels_info(): + # all_urls = [] + # + # for each in ZHNovelInfoSpider.all_novels_col.find({'spider': 'zongheng'}): + # if 'zongheng' in each['novel_url']: + # all_urls.append(each['novel_url']) + # random.shuffle(all_urls) + # + # ZHNovelInfoSpider.start_urls = all_urls + # ZHNovelInfoSpider.start() + # + # + # all_novels_info() diff --git a/soulbook/start.sh b/soulbook/start.sh new file mode 100644 index 0000000..2e035fe --- /dev/null +++ b/soulbook/start.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +ps -ef | grep "gunicorn -c config/gunicorn.py --worker-class sanic.worker.GunicornWorker server:app" | grep -v grep | awk '{print $2}' | xargs kill -9 +nohup gunicorn -c config/gunicorn.py --worker-class sanic.worker.GunicornWorker server:app >/dev/null &2>1 & diff --git a/soulbook/static/md/css/md.css b/soulbook/static/md/css/md.css new file mode 100644 index 0000000..fcd8e97 --- /dev/null +++ b/soulbook/static/md/css/md.css @@ -0,0 +1,41 @@ +th, td { + text-align: center !important; +} + +table a { + color: #337ab7 !important; + text-decoration: none !important; +} + +table a:hover { + color: black !important; +} + +.mdui-panel-fluid { + margin-top: 2%; + width: 80%; + margin-left: 10% +} + +.email-error, .pass-error { + height: 12px; + margin-top: 8px; + overflow: hidden; + font-size: 12px; + line-height: 12px; + color: #f44336; + text-overflow: ellipsis; + white-space: nowrap; + visibility: hidden; +} + +.owl-ranking { + max-width: 300px; + height: 500px; + margin: 5px; +} + +.author-name-tag{ + margin-left: 10%; + margin-top: 13px; +} \ No newline at end of file diff --git a/soulbook/static/md/css/mdui.min0.2.1.css b/soulbook/static/md/css/mdui.min0.2.1.css new file mode 100644 index 0000000..4e235df --- /dev/null +++ b/soulbook/static/md/css/mdui.min0.2.1.css @@ -0,0 +1,6 @@ +/*! + * https://github.com/zdhxiong/mdui + * mdui v0.2.1 (http://mdui.org https://cdnjs.cloudflare.com/ajax/libs/mdui/0.2.1/css/mdui.min.css) + * Copyright 2016-2017 zdhxiong + * Licensed under MIT + */.mdui-theme-primary-amber .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#ffc107!important}.mdui-theme-primary-amber .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fff8e1!important}.mdui-theme-primary-amber .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#ffecb3!important}.mdui-theme-primary-amber .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#ffe082!important}.mdui-theme-primary-amber .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#ffd54f!important}.mdui-theme-primary-amber .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#ffca28!important}.mdui-theme-primary-amber .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#ffc107!important}.mdui-theme-primary-amber .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#ffb300!important}.mdui-theme-primary-amber .mdui-color-theme-700{color:rgba(0,0,0,.87)!important;background-color:#ffa000!important}.mdui-theme-primary-amber .mdui-color-theme-800{color:rgba(0,0,0,.87)!important;background-color:#ff8f00!important}.mdui-theme-primary-amber .mdui-color-theme-900{color:rgba(0,0,0,.87)!important;background-color:#ff6f00!important}.mdui-theme-primary-blue .mdui-color-theme{color:#fff!important;background-color:#2196f3!important}.mdui-theme-primary-blue .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e3f2fd!important}.mdui-theme-primary-blue .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#bbdefb!important}.mdui-theme-primary-blue .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#90caf9!important}.mdui-theme-primary-blue .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#64b5f6!important}.mdui-theme-primary-blue .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#42a5f5!important}.mdui-theme-primary-blue .mdui-color-theme-500{color:#fff!important;background-color:#2196f3!important}.mdui-theme-primary-blue .mdui-color-theme-600{color:#fff!important;background-color:#1e88e5!important}.mdui-theme-primary-blue .mdui-color-theme-700{color:#fff!important;background-color:#1976d2!important}.mdui-theme-primary-blue .mdui-color-theme-800{color:#fff!important;background-color:#1565c0!important}.mdui-theme-primary-blue .mdui-color-theme-900{color:#fff!important;background-color:#0d47a1!important}.mdui-theme-primary-blue-grey .mdui-color-theme{color:#fff!important;background-color:#607d8b!important}.mdui-theme-primary-blue-grey .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#eceff1!important}.mdui-theme-primary-blue-grey .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#cfd8dc!important}.mdui-theme-primary-blue-grey .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#b0bec5!important}.mdui-theme-primary-blue-grey .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#90a4ae!important}.mdui-theme-primary-blue-grey .mdui-color-theme-400{color:#fff!important;background-color:#78909c!important}.mdui-theme-primary-blue-grey .mdui-color-theme-500{color:#fff!important;background-color:#607d8b!important}.mdui-theme-primary-blue-grey .mdui-color-theme-600{color:#fff!important;background-color:#546e7a!important}.mdui-theme-primary-blue-grey .mdui-color-theme-700{color:#fff!important;background-color:#455a64!important}.mdui-theme-primary-blue-grey .mdui-color-theme-800{color:#fff!important;background-color:#37474f!important}.mdui-theme-primary-blue-grey .mdui-color-theme-900{color:#fff!important;background-color:#263238!important}.mdui-theme-primary-brown .mdui-color-theme{color:#fff!important;background-color:#795548!important}.mdui-theme-primary-brown .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#efebe9!important}.mdui-theme-primary-brown .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#d7ccc8!important}.mdui-theme-primary-brown .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#bcaaa4!important}.mdui-theme-primary-brown .mdui-color-theme-300{color:#fff!important;background-color:#a1887f!important}.mdui-theme-primary-brown .mdui-color-theme-400{color:#fff!important;background-color:#8d6e63!important}.mdui-theme-primary-brown .mdui-color-theme-500{color:#fff!important;background-color:#795548!important}.mdui-theme-primary-brown .mdui-color-theme-600{color:#fff!important;background-color:#6d4c41!important}.mdui-theme-primary-brown .mdui-color-theme-700{color:#fff!important;background-color:#5d4037!important}.mdui-theme-primary-brown .mdui-color-theme-800{color:#fff!important;background-color:#4e342e!important}.mdui-theme-primary-brown .mdui-color-theme-900{color:#fff!important;background-color:#3e2723!important}.mdui-theme-primary-cyan .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#00bcd4!important}.mdui-theme-primary-cyan .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e0f7fa!important}.mdui-theme-primary-cyan .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#b2ebf2!important}.mdui-theme-primary-cyan .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#80deea!important}.mdui-theme-primary-cyan .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#4dd0e1!important}.mdui-theme-primary-cyan .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#26c6da!important}.mdui-theme-primary-cyan .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#00bcd4!important}.mdui-theme-primary-cyan .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#00acc1!important}.mdui-theme-primary-cyan .mdui-color-theme-700{color:#fff!important;background-color:#0097a7!important}.mdui-theme-primary-cyan .mdui-color-theme-800{color:#fff!important;background-color:#00838f!important}.mdui-theme-primary-cyan .mdui-color-theme-900{color:#fff!important;background-color:#006064!important}.mdui-theme-primary-deep-orange .mdui-color-theme{color:#fff!important;background-color:#ff5722!important}.mdui-theme-primary-deep-orange .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fbe9e7!important}.mdui-theme-primary-deep-orange .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#ffccbc!important}.mdui-theme-primary-deep-orange .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#ffab91!important}.mdui-theme-primary-deep-orange .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#ff8a65!important}.mdui-theme-primary-deep-orange .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#ff7043!important}.mdui-theme-primary-deep-orange .mdui-color-theme-500{color:#fff!important;background-color:#ff5722!important}.mdui-theme-primary-deep-orange .mdui-color-theme-600{color:#fff!important;background-color:#f4511e!important}.mdui-theme-primary-deep-orange .mdui-color-theme-700{color:#fff!important;background-color:#e64a19!important}.mdui-theme-primary-deep-orange .mdui-color-theme-800{color:#fff!important;background-color:#d84315!important}.mdui-theme-primary-deep-orange .mdui-color-theme-900{color:#fff!important;background-color:#bf360c!important}.mdui-theme-primary-deep-purple .mdui-color-theme{color:#fff!important;background-color:#673ab7!important}.mdui-theme-primary-deep-purple .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#ede7f6!important}.mdui-theme-primary-deep-purple .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#d1c4e9!important}.mdui-theme-primary-deep-purple .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#b39ddb!important}.mdui-theme-primary-deep-purple .mdui-color-theme-300{color:#fff!important;background-color:#9575cd!important}.mdui-theme-primary-deep-purple .mdui-color-theme-400{color:#fff!important;background-color:#7e57c2!important}.mdui-theme-primary-deep-purple .mdui-color-theme-500{color:#fff!important;background-color:#673ab7!important}.mdui-theme-primary-deep-purple .mdui-color-theme-600{color:#fff!important;background-color:#5e35b1!important}.mdui-theme-primary-deep-purple .mdui-color-theme-700{color:#fff!important;background-color:#512da8!important}.mdui-theme-primary-deep-purple .mdui-color-theme-800{color:#fff!important;background-color:#4527a0!important}.mdui-theme-primary-deep-purple .mdui-color-theme-900{color:#fff!important;background-color:#311b92!important}.mdui-theme-primary-green .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#4caf50!important}.mdui-theme-primary-green .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e8f5e9!important}.mdui-theme-primary-green .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#c8e6c9!important}.mdui-theme-primary-green .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#a5d6a7!important}.mdui-theme-primary-green .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#81c784!important}.mdui-theme-primary-green .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#66bb6a!important}.mdui-theme-primary-green .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#4caf50!important}.mdui-theme-primary-green .mdui-color-theme-600{color:#fff!important;background-color:#43a047!important}.mdui-theme-primary-green .mdui-color-theme-700{color:#fff!important;background-color:#388e3c!important}.mdui-theme-primary-green .mdui-color-theme-800{color:#fff!important;background-color:#2e7d32!important}.mdui-theme-primary-green .mdui-color-theme-900{color:#fff!important;background-color:#1b5e20!important}.mdui-theme-primary-grey .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#9e9e9e!important}.mdui-theme-primary-grey .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fafafa!important}.mdui-theme-primary-grey .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#f5f5f5!important}.mdui-theme-primary-grey .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#eee!important}.mdui-theme-primary-grey .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#e0e0e0!important}.mdui-theme-primary-grey .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#bdbdbd!important}.mdui-theme-primary-grey .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#9e9e9e!important}.mdui-theme-primary-grey .mdui-color-theme-600{color:#fff!important;background-color:#757575!important}.mdui-theme-primary-grey .mdui-color-theme-700{color:#fff!important;background-color:#616161!important}.mdui-theme-primary-grey .mdui-color-theme-800{color:#fff!important;background-color:#424242!important}.mdui-theme-primary-grey .mdui-color-theme-900{color:#fff!important;background-color:#212121!important}.mdui-theme-primary-indigo .mdui-color-theme{color:#fff!important;background-color:#3f51b5!important}.mdui-theme-primary-indigo .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e8eaf6!important}.mdui-theme-primary-indigo .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#c5cae9!important}.mdui-theme-primary-indigo .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#9fa8da!important}.mdui-theme-primary-indigo .mdui-color-theme-300{color:#fff!important;background-color:#7986cb!important}.mdui-theme-primary-indigo .mdui-color-theme-400{color:#fff!important;background-color:#5c6bc0!important}.mdui-theme-primary-indigo .mdui-color-theme-500{color:#fff!important;background-color:#3f51b5!important}.mdui-theme-primary-indigo .mdui-color-theme-600{color:#fff!important;background-color:#3949ab!important}.mdui-theme-primary-indigo .mdui-color-theme-700{color:#fff!important;background-color:#303f9f!important}.mdui-theme-primary-indigo .mdui-color-theme-800{color:#fff!important;background-color:#283593!important}.mdui-theme-primary-indigo .mdui-color-theme-900{color:#fff!important;background-color:#1a237e!important}.mdui-theme-primary-light-blue .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#03a9f4!important}.mdui-theme-primary-light-blue .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e1f5fe!important}.mdui-theme-primary-light-blue .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#b3e5fc!important}.mdui-theme-primary-light-blue .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#81d4fa!important}.mdui-theme-primary-light-blue .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#4fc3f7!important}.mdui-theme-primary-light-blue .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#29b6f6!important}.mdui-theme-primary-light-blue .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#03a9f4!important}.mdui-theme-primary-light-blue .mdui-color-theme-600{color:#fff!important;background-color:#039be5!important}.mdui-theme-primary-light-blue .mdui-color-theme-700{color:#fff!important;background-color:#0288d1!important}.mdui-theme-primary-light-blue .mdui-color-theme-800{color:#fff!important;background-color:#0277bd!important}.mdui-theme-primary-light-blue .mdui-color-theme-900{color:#fff!important;background-color:#01579b!important}.mdui-theme-primary-light-green .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#8bc34a!important}.mdui-theme-primary-light-green .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#f1f8e9!important}.mdui-theme-primary-light-green .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#dcedc8!important}.mdui-theme-primary-light-green .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#c5e1a5!important}.mdui-theme-primary-light-green .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#aed581!important}.mdui-theme-primary-light-green .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#9ccc65!important}.mdui-theme-primary-light-green .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#8bc34a!important}.mdui-theme-primary-light-green .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#7cb342!important}.mdui-theme-primary-light-green .mdui-color-theme-700{color:#fff!important;background-color:#689f38!important}.mdui-theme-primary-light-green .mdui-color-theme-800{color:#fff!important;background-color:#558b2f!important}.mdui-theme-primary-light-green .mdui-color-theme-900{color:#fff!important;background-color:#33691e!important}.mdui-theme-primary-lime .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#cddc39!important}.mdui-theme-primary-lime .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#f9fbe7!important}.mdui-theme-primary-lime .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#f0f4c3!important}.mdui-theme-primary-lime .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#e6ee9c!important}.mdui-theme-primary-lime .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#dce775!important}.mdui-theme-primary-lime .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#d4e157!important}.mdui-theme-primary-lime .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#cddc39!important}.mdui-theme-primary-lime .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#c0ca33!important}.mdui-theme-primary-lime .mdui-color-theme-700{color:rgba(0,0,0,.87)!important;background-color:#afb42b!important}.mdui-theme-primary-lime .mdui-color-theme-800{color:rgba(0,0,0,.87)!important;background-color:#9e9d24!important}.mdui-theme-primary-lime .mdui-color-theme-900{color:#fff!important;background-color:#827717!important}.mdui-theme-primary-orange .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#ff9800!important}.mdui-theme-primary-orange .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fff3e0!important}.mdui-theme-primary-orange .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#ffe0b2!important}.mdui-theme-primary-orange .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#ffcc80!important}.mdui-theme-primary-orange .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#ffb74d!important}.mdui-theme-primary-orange .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#ffa726!important}.mdui-theme-primary-orange .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#ff9800!important}.mdui-theme-primary-orange .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#fb8c00!important}.mdui-theme-primary-orange .mdui-color-theme-700{color:rgba(0,0,0,.87)!important;background-color:#f57c00!important}.mdui-theme-primary-orange .mdui-color-theme-800{color:#fff!important;background-color:#ef6c00!important}.mdui-theme-primary-orange .mdui-color-theme-900{color:#fff!important;background-color:#e65100!important}.mdui-theme-primary-pink .mdui-color-theme{color:#fff!important;background-color:#e91e63!important}.mdui-theme-primary-pink .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fce4ec!important}.mdui-theme-primary-pink .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#f8bbd0!important}.mdui-theme-primary-pink .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#f48fb1!important}.mdui-theme-primary-pink .mdui-color-theme-300{color:#fff!important;background-color:#f06292!important}.mdui-theme-primary-pink .mdui-color-theme-400{color:#fff!important;background-color:#ec407a!important}.mdui-theme-primary-pink .mdui-color-theme-500{color:#fff!important;background-color:#e91e63!important}.mdui-theme-primary-pink .mdui-color-theme-600{color:#fff!important;background-color:#d81b60!important}.mdui-theme-primary-pink .mdui-color-theme-700{color:#fff!important;background-color:#c2185b!important}.mdui-theme-primary-pink .mdui-color-theme-800{color:#fff!important;background-color:#ad1457!important}.mdui-theme-primary-pink .mdui-color-theme-900{color:#fff!important;background-color:#880e4f!important}.mdui-theme-primary-purple .mdui-color-theme{color:#fff!important;background-color:#9c27b0!important}.mdui-theme-primary-purple .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#f3e5f5!important}.mdui-theme-primary-purple .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#e1bee7!important}.mdui-theme-primary-purple .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#ce93d8!important}.mdui-theme-primary-purple .mdui-color-theme-300{color:#fff!important;background-color:#ba68c8!important}.mdui-theme-primary-purple .mdui-color-theme-400{color:#fff!important;background-color:#ab47bc!important}.mdui-theme-primary-purple .mdui-color-theme-500{color:#fff!important;background-color:#9c27b0!important}.mdui-theme-primary-purple .mdui-color-theme-600{color:#fff!important;background-color:#8e24aa!important}.mdui-theme-primary-purple .mdui-color-theme-700{color:#fff!important;background-color:#7b1fa2!important}.mdui-theme-primary-purple .mdui-color-theme-800{color:#fff!important;background-color:#6a1b9a!important}.mdui-theme-primary-purple .mdui-color-theme-900{color:#fff!important;background-color:#4a148c!important}.mdui-theme-primary-red .mdui-color-theme{color:#fff!important;background-color:#f44336!important}.mdui-theme-primary-red .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#ffebee!important}.mdui-theme-primary-red .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#ffcdd2!important}.mdui-theme-primary-red .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#ef9a9a!important}.mdui-theme-primary-red .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#e57373!important}.mdui-theme-primary-red .mdui-color-theme-400{color:#fff!important;background-color:#ef5350!important}.mdui-theme-primary-red .mdui-color-theme-500{color:#fff!important;background-color:#f44336!important}.mdui-theme-primary-red .mdui-color-theme-600{color:#fff!important;background-color:#e53935!important}.mdui-theme-primary-red .mdui-color-theme-700{color:#fff!important;background-color:#d32f2f!important}.mdui-theme-primary-red .mdui-color-theme-800{color:#fff!important;background-color:#c62828!important}.mdui-theme-primary-red .mdui-color-theme-900{color:#fff!important;background-color:#b71c1c!important}.mdui-theme-primary-teal .mdui-color-theme{color:#fff!important;background-color:#009688!important}.mdui-theme-primary-teal .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#e0f2f1!important}.mdui-theme-primary-teal .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#b2dfdb!important}.mdui-theme-primary-teal .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#80cbc4!important}.mdui-theme-primary-teal .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#4db6ac!important}.mdui-theme-primary-teal .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#26a69a!important}.mdui-theme-primary-teal .mdui-color-theme-500{color:#fff!important;background-color:#009688!important}.mdui-theme-primary-teal .mdui-color-theme-600{color:#fff!important;background-color:#00897b!important}.mdui-theme-primary-teal .mdui-color-theme-700{color:#fff!important;background-color:#00796b!important}.mdui-theme-primary-teal .mdui-color-theme-800{color:#fff!important;background-color:#00695c!important}.mdui-theme-primary-teal .mdui-color-theme-900{color:#fff!important;background-color:#004d40!important}.mdui-theme-primary-yellow .mdui-color-theme{color:rgba(0,0,0,.87)!important;background-color:#ffeb3b!important}.mdui-theme-primary-yellow .mdui-color-theme-50{color:rgba(0,0,0,.87)!important;background-color:#fffde7!important}.mdui-theme-primary-yellow .mdui-color-theme-100{color:rgba(0,0,0,.87)!important;background-color:#fff9c4!important}.mdui-theme-primary-yellow .mdui-color-theme-200{color:rgba(0,0,0,.87)!important;background-color:#fff59d!important}.mdui-theme-primary-yellow .mdui-color-theme-300{color:rgba(0,0,0,.87)!important;background-color:#fff176!important}.mdui-theme-primary-yellow .mdui-color-theme-400{color:rgba(0,0,0,.87)!important;background-color:#ffee58!important}.mdui-theme-primary-yellow .mdui-color-theme-500{color:rgba(0,0,0,.87)!important;background-color:#ffeb3b!important}.mdui-theme-primary-yellow .mdui-color-theme-600{color:rgba(0,0,0,.87)!important;background-color:#fdd835!important}.mdui-theme-primary-yellow .mdui-color-theme-700{color:rgba(0,0,0,.87)!important;background-color:#fbc02d!important}.mdui-theme-primary-yellow .mdui-color-theme-800{color:rgba(0,0,0,.87)!important;background-color:#f9a825!important}.mdui-theme-primary-yellow .mdui-color-theme-900{color:rgba(0,0,0,.87)!important;background-color:#f57f17!important}.mdui-theme-accent-amber .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#ffd740!important}.mdui-theme-accent-amber .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ffe57f!important}.mdui-theme-accent-amber .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#ffd740!important}.mdui-theme-accent-amber .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#ffc400!important}.mdui-theme-accent-amber .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#ffab00!important}.mdui-theme-accent-blue .mdui-color-theme-accent{color:#fff!important;background-color:#448aff!important}.mdui-theme-accent-blue .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#82b1ff!important}.mdui-theme-accent-blue .mdui-color-theme-a200{color:#fff!important;background-color:#448aff!important}.mdui-theme-accent-blue .mdui-color-theme-a400{color:#fff!important;background-color:#2979ff!important}.mdui-theme-accent-blue .mdui-color-theme-a700{color:#fff!important;background-color:#2962ff!important}.mdui-theme-accent-cyan .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#18ffff!important}.mdui-theme-accent-cyan .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#84ffff!important}.mdui-theme-accent-cyan .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#18ffff!important}.mdui-theme-accent-cyan .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#00e5ff!important}.mdui-theme-accent-cyan .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#00b8d4!important}.mdui-theme-accent-deep-orange .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#ff6e40!important}.mdui-theme-accent-deep-orange .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ff9e80!important}.mdui-theme-accent-deep-orange .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#ff6e40!important}.mdui-theme-accent-deep-orange .mdui-color-theme-a400{color:#fff!important;background-color:#ff3d00!important}.mdui-theme-accent-deep-orange .mdui-color-theme-a700{color:#fff!important;background-color:#dd2c00!important}.mdui-theme-accent-deep-purple .mdui-color-theme-accent{color:#fff!important;background-color:#7c4dff!important}.mdui-theme-accent-deep-purple .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#b388ff!important}.mdui-theme-accent-deep-purple .mdui-color-theme-a200{color:#fff!important;background-color:#7c4dff!important}.mdui-theme-accent-deep-purple .mdui-color-theme-a400{color:#fff!important;background-color:#651fff!important}.mdui-theme-accent-deep-purple .mdui-color-theme-a700{color:#fff!important;background-color:#6200ea!important}.mdui-theme-accent-green .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#69f0ae!important}.mdui-theme-accent-green .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#b9f6ca!important}.mdui-theme-accent-green .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#69f0ae!important}.mdui-theme-accent-green .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#00e676!important}.mdui-theme-accent-green .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#00c853!important}.mdui-theme-accent-indigo .mdui-color-theme-accent{color:#fff!important;background-color:#536dfe!important}.mdui-theme-accent-indigo .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#8c9eff!important}.mdui-theme-accent-indigo .mdui-color-theme-a200{color:#fff!important;background-color:#536dfe!important}.mdui-theme-accent-indigo .mdui-color-theme-a400{color:#fff!important;background-color:#3d5afe!important}.mdui-theme-accent-indigo .mdui-color-theme-a700{color:#fff!important;background-color:#304ffe!important}.mdui-theme-accent-light-blue .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#40c4ff!important}.mdui-theme-accent-light-blue .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#80d8ff!important}.mdui-theme-accent-light-blue .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#40c4ff!important}.mdui-theme-accent-light-blue .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#00b0ff!important}.mdui-theme-accent-light-blue .mdui-color-theme-a700{color:#fff!important;background-color:#0091ea!important}.mdui-theme-accent-light-green .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#b2ff59!important}.mdui-theme-accent-light-green .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ccff90!important}.mdui-theme-accent-light-green .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#b2ff59!important}.mdui-theme-accent-light-green .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#76ff03!important}.mdui-theme-accent-light-green .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#64dd17!important}.mdui-theme-accent-lime .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#eeff41!important}.mdui-theme-accent-lime .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#f4ff81!important}.mdui-theme-accent-lime .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#eeff41!important}.mdui-theme-accent-lime .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#c6ff00!important}.mdui-theme-accent-lime .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#aeea00!important}.mdui-theme-accent-orange .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#ffab40!important}.mdui-theme-accent-orange .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ffd180!important}.mdui-theme-accent-orange .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#ffab40!important}.mdui-theme-accent-orange .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#ff9100!important}.mdui-theme-accent-orange .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#ff6d00!important}.mdui-theme-accent-pink .mdui-color-theme-accent{color:#fff!important;background-color:#ff4081!important}.mdui-theme-accent-pink .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ff80ab!important}.mdui-theme-accent-pink .mdui-color-theme-a200{color:#fff!important;background-color:#ff4081!important}.mdui-theme-accent-pink .mdui-color-theme-a400{color:#fff!important;background-color:#f50057!important}.mdui-theme-accent-pink .mdui-color-theme-a700{color:#fff!important;background-color:#c51162!important}.mdui-theme-accent-purple .mdui-color-theme-accent{color:#fff!important;background-color:#e040fb!important}.mdui-theme-accent-purple .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ea80fc!important}.mdui-theme-accent-purple .mdui-color-theme-a200{color:#fff!important;background-color:#e040fb!important}.mdui-theme-accent-purple .mdui-color-theme-a400{color:#fff!important;background-color:#d500f9!important}.mdui-theme-accent-purple .mdui-color-theme-a700{color:#fff!important;background-color:#a0f!important}.mdui-theme-accent-red .mdui-color-theme-accent{color:#fff!important;background-color:#ff5252!important}.mdui-theme-accent-red .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ff8a80!important}.mdui-theme-accent-red .mdui-color-theme-a200{color:#fff!important;background-color:#ff5252!important}.mdui-theme-accent-red .mdui-color-theme-a400{color:#fff!important;background-color:#ff1744!important}.mdui-theme-accent-red .mdui-color-theme-a700{color:#fff!important;background-color:#d50000!important}.mdui-theme-accent-teal .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#64ffda!important}.mdui-theme-accent-teal .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#a7ffeb!important}.mdui-theme-accent-teal .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#64ffda!important}.mdui-theme-accent-teal .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#1de9b6!important}.mdui-theme-accent-teal .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#00bfa5!important}.mdui-theme-accent-yellow .mdui-color-theme-accent{color:rgba(0,0,0,.87)!important;background-color:#ff0!important}.mdui-theme-accent-yellow .mdui-color-theme-a100{color:rgba(0,0,0,.87)!important;background-color:#ffff8d!important}.mdui-theme-accent-yellow .mdui-color-theme-a200{color:rgba(0,0,0,.87)!important;background-color:#ff0!important}.mdui-theme-accent-yellow .mdui-color-theme-a400{color:rgba(0,0,0,.87)!important;background-color:#ffea00!important}.mdui-theme-accent-yellow .mdui-color-theme-a700{color:rgba(0,0,0,.87)!important;background-color:#ffd600!important}.mdui-color-amber{color:rgba(0,0,0,.87)!important;background-color:#ffc107!important}.mdui-color-amber-50{color:rgba(0,0,0,.87)!important;background-color:#fff8e1!important}.mdui-color-amber-100{color:rgba(0,0,0,.87)!important;background-color:#ffecb3!important}.mdui-color-amber-200{color:rgba(0,0,0,.87)!important;background-color:#ffe082!important}.mdui-color-amber-300{color:rgba(0,0,0,.87)!important;background-color:#ffd54f!important}.mdui-color-amber-400{color:rgba(0,0,0,.87)!important;background-color:#ffca28!important}.mdui-color-amber-500{color:rgba(0,0,0,.87)!important;background-color:#ffc107!important}.mdui-color-amber-600{color:rgba(0,0,0,.87)!important;background-color:#ffb300!important}.mdui-color-amber-700{color:rgba(0,0,0,.87)!important;background-color:#ffa000!important}.mdui-color-amber-800{color:rgba(0,0,0,.87)!important;background-color:#ff8f00!important}.mdui-color-amber-900{color:rgba(0,0,0,.87)!important;background-color:#ff6f00!important}.mdui-color-blue{color:#fff!important;background-color:#2196f3!important}.mdui-color-blue-50{color:rgba(0,0,0,.87)!important;background-color:#e3f2fd!important}.mdui-color-blue-100{color:rgba(0,0,0,.87)!important;background-color:#bbdefb!important}.mdui-color-blue-200{color:rgba(0,0,0,.87)!important;background-color:#90caf9!important}.mdui-color-blue-300{color:rgba(0,0,0,.87)!important;background-color:#64b5f6!important}.mdui-color-blue-400{color:rgba(0,0,0,.87)!important;background-color:#42a5f5!important}.mdui-color-blue-500{color:#fff!important;background-color:#2196f3!important}.mdui-color-blue-600{color:#fff!important;background-color:#1e88e5!important}.mdui-color-blue-700{color:#fff!important;background-color:#1976d2!important}.mdui-color-blue-800{color:#fff!important;background-color:#1565c0!important}.mdui-color-blue-900{color:#fff!important;background-color:#0d47a1!important}.mdui-color-blue-grey{color:#fff!important;background-color:#607d8b!important}.mdui-color-blue-grey-50{color:rgba(0,0,0,.87)!important;background-color:#eceff1!important}.mdui-color-blue-grey-100{color:rgba(0,0,0,.87)!important;background-color:#cfd8dc!important}.mdui-color-blue-grey-200{color:rgba(0,0,0,.87)!important;background-color:#b0bec5!important}.mdui-color-blue-grey-300{color:rgba(0,0,0,.87)!important;background-color:#90a4ae!important}.mdui-color-blue-grey-400{color:#fff!important;background-color:#78909c!important}.mdui-color-blue-grey-500{color:#fff!important;background-color:#607d8b!important}.mdui-color-blue-grey-600{color:#fff!important;background-color:#546e7a!important}.mdui-color-blue-grey-700{color:#fff!important;background-color:#455a64!important}.mdui-color-blue-grey-800{color:#fff!important;background-color:#37474f!important}.mdui-color-blue-grey-900{color:#fff!important;background-color:#263238!important}.mdui-color-brown{color:#fff!important;background-color:#795548!important}.mdui-color-brown-50{color:rgba(0,0,0,.87)!important;background-color:#efebe9!important}.mdui-color-brown-100{color:rgba(0,0,0,.87)!important;background-color:#d7ccc8!important}.mdui-color-brown-200{color:rgba(0,0,0,.87)!important;background-color:#bcaaa4!important}.mdui-color-brown-300{color:#fff!important;background-color:#a1887f!important}.mdui-color-brown-400{color:#fff!important;background-color:#8d6e63!important}.mdui-color-brown-500{color:#fff!important;background-color:#795548!important}.mdui-color-brown-600{color:#fff!important;background-color:#6d4c41!important}.mdui-color-brown-700{color:#fff!important;background-color:#5d4037!important}.mdui-color-brown-800{color:#fff!important;background-color:#4e342e!important}.mdui-color-brown-900{color:#fff!important;background-color:#3e2723!important}.mdui-color-cyan{color:rgba(0,0,0,.87)!important;background-color:#00bcd4!important}.mdui-color-cyan-50{color:rgba(0,0,0,.87)!important;background-color:#e0f7fa!important}.mdui-color-cyan-100{color:rgba(0,0,0,.87)!important;background-color:#b2ebf2!important}.mdui-color-cyan-200{color:rgba(0,0,0,.87)!important;background-color:#80deea!important}.mdui-color-cyan-300{color:rgba(0,0,0,.87)!important;background-color:#4dd0e1!important}.mdui-color-cyan-400{color:rgba(0,0,0,.87)!important;background-color:#26c6da!important}.mdui-color-cyan-500{color:rgba(0,0,0,.87)!important;background-color:#00bcd4!important}.mdui-color-cyan-600{color:rgba(0,0,0,.87)!important;background-color:#00acc1!important}.mdui-color-cyan-700{color:#fff!important;background-color:#0097a7!important}.mdui-color-cyan-800{color:#fff!important;background-color:#00838f!important}.mdui-color-cyan-900{color:#fff!important;background-color:#006064!important}.mdui-color-deep-orange{color:#fff!important;background-color:#ff5722!important}.mdui-color-deep-orange-50{color:rgba(0,0,0,.87)!important;background-color:#fbe9e7!important}.mdui-color-deep-orange-100{color:rgba(0,0,0,.87)!important;background-color:#ffccbc!important}.mdui-color-deep-orange-200{color:rgba(0,0,0,.87)!important;background-color:#ffab91!important}.mdui-color-deep-orange-300{color:rgba(0,0,0,.87)!important;background-color:#ff8a65!important}.mdui-color-deep-orange-400{color:rgba(0,0,0,.87)!important;background-color:#ff7043!important}.mdui-color-deep-orange-500{color:#fff!important;background-color:#ff5722!important}.mdui-color-deep-orange-600{color:#fff!important;background-color:#f4511e!important}.mdui-color-deep-orange-700{color:#fff!important;background-color:#e64a19!important}.mdui-color-deep-orange-800{color:#fff!important;background-color:#d84315!important}.mdui-color-deep-orange-900{color:#fff!important;background-color:#bf360c!important}.mdui-color-deep-purple{color:#fff!important;background-color:#673ab7!important}.mdui-color-deep-purple-50{color:rgba(0,0,0,.87)!important;background-color:#ede7f6!important}.mdui-color-deep-purple-100{color:rgba(0,0,0,.87)!important;background-color:#d1c4e9!important}.mdui-color-deep-purple-200{color:rgba(0,0,0,.87)!important;background-color:#b39ddb!important}.mdui-color-deep-purple-300{color:#fff!important;background-color:#9575cd!important}.mdui-color-deep-purple-400{color:#fff!important;background-color:#7e57c2!important}.mdui-color-deep-purple-500{color:#fff!important;background-color:#673ab7!important}.mdui-color-deep-purple-600{color:#fff!important;background-color:#5e35b1!important}.mdui-color-deep-purple-700{color:#fff!important;background-color:#512da8!important}.mdui-color-deep-purple-800{color:#fff!important;background-color:#4527a0!important}.mdui-color-deep-purple-900{color:#fff!important;background-color:#311b92!important}.mdui-color-green{color:rgba(0,0,0,.87)!important;background-color:#4caf50!important}.mdui-color-green-50{color:rgba(0,0,0,.87)!important;background-color:#e8f5e9!important}.mdui-color-green-100{color:rgba(0,0,0,.87)!important;background-color:#c8e6c9!important}.mdui-color-green-200{color:rgba(0,0,0,.87)!important;background-color:#a5d6a7!important}.mdui-color-green-300{color:rgba(0,0,0,.87)!important;background-color:#81c784!important}.mdui-color-green-400{color:rgba(0,0,0,.87)!important;background-color:#66bb6a!important}.mdui-color-green-500{color:rgba(0,0,0,.87)!important;background-color:#4caf50!important}.mdui-color-green-600{color:#fff!important;background-color:#43a047!important}.mdui-color-green-700{color:#fff!important;background-color:#388e3c!important}.mdui-color-green-800{color:#fff!important;background-color:#2e7d32!important}.mdui-color-green-900{color:#fff!important;background-color:#1b5e20!important}.mdui-color-grey{color:rgba(0,0,0,.87)!important;background-color:#9e9e9e!important}.mdui-color-grey-50{color:rgba(0,0,0,.87)!important;background-color:#fafafa!important}.mdui-color-grey-100{color:rgba(0,0,0,.87)!important;background-color:#f5f5f5!important}.mdui-color-grey-200{color:rgba(0,0,0,.87)!important;background-color:#eee!important}.mdui-color-grey-300{color:rgba(0,0,0,.87)!important;background-color:#e0e0e0!important}.mdui-color-grey-400{color:rgba(0,0,0,.87)!important;background-color:#bdbdbd!important}.mdui-color-grey-500{color:rgba(0,0,0,.87)!important;background-color:#9e9e9e!important}.mdui-color-grey-600{color:#fff!important;background-color:#757575!important}.mdui-color-grey-700{color:#fff!important;background-color:#616161!important}.mdui-color-grey-800{color:#fff!important;background-color:#424242!important}.mdui-color-grey-900{color:#fff!important;background-color:#212121!important}.mdui-color-indigo{color:#fff!important;background-color:#3f51b5!important}.mdui-color-indigo-50{color:rgba(0,0,0,.87)!important;background-color:#e8eaf6!important}.mdui-color-indigo-100{color:rgba(0,0,0,.87)!important;background-color:#c5cae9!important}.mdui-color-indigo-200{color:rgba(0,0,0,.87)!important;background-color:#9fa8da!important}.mdui-color-indigo-300{color:#fff!important;background-color:#7986cb!important}.mdui-color-indigo-400{color:#fff!important;background-color:#5c6bc0!important}.mdui-color-indigo-500{color:#fff!important;background-color:#3f51b5!important}.mdui-color-indigo-600{color:#fff!important;background-color:#3949ab!important}.mdui-color-indigo-700{color:#fff!important;background-color:#303f9f!important}.mdui-color-indigo-800{color:#fff!important;background-color:#283593!important}.mdui-color-indigo-900{color:#fff!important;background-color:#1a237e!important}.mdui-color-light-blue{color:rgba(0,0,0,.87)!important;background-color:#03a9f4!important}.mdui-color-light-blue-50{color:rgba(0,0,0,.87)!important;background-color:#e1f5fe!important}.mdui-color-light-blue-100{color:rgba(0,0,0,.87)!important;background-color:#b3e5fc!important}.mdui-color-light-blue-200{color:rgba(0,0,0,.87)!important;background-color:#81d4fa!important}.mdui-color-light-blue-300{color:rgba(0,0,0,.87)!important;background-color:#4fc3f7!important}.mdui-color-light-blue-400{color:rgba(0,0,0,.87)!important;background-color:#29b6f6!important}.mdui-color-light-blue-500{color:rgba(0,0,0,.87)!important;background-color:#03a9f4!important}.mdui-color-light-blue-600{color:#fff!important;background-color:#039be5!important}.mdui-color-light-blue-700{color:#fff!important;background-color:#0288d1!important}.mdui-color-light-blue-800{color:#fff!important;background-color:#0277bd!important}.mdui-color-light-blue-900{color:#fff!important;background-color:#01579b!important}.mdui-color-light-green{color:rgba(0,0,0,.87)!important;background-color:#8bc34a!important}.mdui-color-light-green-50{color:rgba(0,0,0,.87)!important;background-color:#f1f8e9!important}.mdui-color-light-green-100{color:rgba(0,0,0,.87)!important;background-color:#dcedc8!important}.mdui-color-light-green-200{color:rgba(0,0,0,.87)!important;background-color:#c5e1a5!important}.mdui-color-light-green-300{color:rgba(0,0,0,.87)!important;background-color:#aed581!important}.mdui-color-light-green-400{color:rgba(0,0,0,.87)!important;background-color:#9ccc65!important}.mdui-color-light-green-500{color:rgba(0,0,0,.87)!important;background-color:#8bc34a!important}.mdui-color-light-green-600{color:rgba(0,0,0,.87)!important;background-color:#7cb342!important}.mdui-color-light-green-700{color:#fff!important;background-color:#689f38!important}.mdui-color-light-green-800{color:#fff!important;background-color:#558b2f!important}.mdui-color-light-green-900{color:#fff!important;background-color:#33691e!important}.mdui-color-lime{color:rgba(0,0,0,.87)!important;background-color:#cddc39!important}.mdui-color-lime-50{color:rgba(0,0,0,.87)!important;background-color:#f9fbe7!important}.mdui-color-lime-100{color:rgba(0,0,0,.87)!important;background-color:#f0f4c3!important}.mdui-color-lime-200{color:rgba(0,0,0,.87)!important;background-color:#e6ee9c!important}.mdui-color-lime-300{color:rgba(0,0,0,.87)!important;background-color:#dce775!important}.mdui-color-lime-400{color:rgba(0,0,0,.87)!important;background-color:#d4e157!important}.mdui-color-lime-500{color:rgba(0,0,0,.87)!important;background-color:#cddc39!important}.mdui-color-lime-600{color:rgba(0,0,0,.87)!important;background-color:#c0ca33!important}.mdui-color-lime-700{color:rgba(0,0,0,.87)!important;background-color:#afb42b!important}.mdui-color-lime-800{color:rgba(0,0,0,.87)!important;background-color:#9e9d24!important}.mdui-color-lime-900{color:#fff!important;background-color:#827717!important}.mdui-color-orange{color:rgba(0,0,0,.87)!important;background-color:#ff9800!important}.mdui-color-orange-50{color:rgba(0,0,0,.87)!important;background-color:#fff3e0!important}.mdui-color-orange-100{color:rgba(0,0,0,.87)!important;background-color:#ffe0b2!important}.mdui-color-orange-200{color:rgba(0,0,0,.87)!important;background-color:#ffcc80!important}.mdui-color-orange-300{color:rgba(0,0,0,.87)!important;background-color:#ffb74d!important}.mdui-color-orange-400{color:rgba(0,0,0,.87)!important;background-color:#ffa726!important}.mdui-color-orange-500{color:rgba(0,0,0,.87)!important;background-color:#ff9800!important}.mdui-color-orange-600{color:rgba(0,0,0,.87)!important;background-color:#fb8c00!important}.mdui-color-orange-700{color:rgba(0,0,0,.87)!important;background-color:#f57c00!important}.mdui-color-orange-800{color:#fff!important;background-color:#ef6c00!important}.mdui-color-orange-900{color:#fff!important;background-color:#e65100!important}.mdui-color-pink{color:#fff!important;background-color:#e91e63!important}.mdui-color-pink-50{color:rgba(0,0,0,.87)!important;background-color:#fce4ec!important}.mdui-color-pink-100{color:rgba(0,0,0,.87)!important;background-color:#f8bbd0!important}.mdui-color-pink-200{color:rgba(0,0,0,.87)!important;background-color:#f48fb1!important}.mdui-color-pink-300{color:#fff!important;background-color:#f06292!important}.mdui-color-pink-400{color:#fff!important;background-color:#ec407a!important}.mdui-color-pink-500{color:#fff!important;background-color:#e91e63!important}.mdui-color-pink-600{color:#fff!important;background-color:#d81b60!important}.mdui-color-pink-700{color:#fff!important;background-color:#c2185b!important}.mdui-color-pink-800{color:#fff!important;background-color:#ad1457!important}.mdui-color-pink-900{color:#fff!important;background-color:#880e4f!important}.mdui-color-purple{color:#fff!important;background-color:#9c27b0!important}.mdui-color-purple-50{color:rgba(0,0,0,.87)!important;background-color:#f3e5f5!important}.mdui-color-purple-100{color:rgba(0,0,0,.87)!important;background-color:#e1bee7!important}.mdui-color-purple-200{color:rgba(0,0,0,.87)!important;background-color:#ce93d8!important}.mdui-color-purple-300{color:#fff!important;background-color:#ba68c8!important}.mdui-color-purple-400{color:#fff!important;background-color:#ab47bc!important}.mdui-color-purple-500{color:#fff!important;background-color:#9c27b0!important}.mdui-color-purple-600{color:#fff!important;background-color:#8e24aa!important}.mdui-color-purple-700{color:#fff!important;background-color:#7b1fa2!important}.mdui-color-purple-800{color:#fff!important;background-color:#6a1b9a!important}.mdui-color-purple-900{color:#fff!important;background-color:#4a148c!important}.mdui-color-red{color:#fff!important;background-color:#f44336!important}.mdui-color-red-50{color:rgba(0,0,0,.87)!important;background-color:#ffebee!important}.mdui-color-red-100{color:rgba(0,0,0,.87)!important;background-color:#ffcdd2!important}.mdui-color-red-200{color:rgba(0,0,0,.87)!important;background-color:#ef9a9a!important}.mdui-color-red-300{color:rgba(0,0,0,.87)!important;background-color:#e57373!important}.mdui-color-red-400{color:#fff!important;background-color:#ef5350!important}.mdui-color-red-500{color:#fff!important;background-color:#f44336!important}.mdui-color-red-600{color:#fff!important;background-color:#e53935!important}.mdui-color-red-700{color:#fff!important;background-color:#d32f2f!important}.mdui-color-red-800{color:#fff!important;background-color:#c62828!important}.mdui-color-red-900{color:#fff!important;background-color:#b71c1c!important}.mdui-color-teal{color:#fff!important;background-color:#009688!important}.mdui-color-teal-50{color:rgba(0,0,0,.87)!important;background-color:#e0f2f1!important}.mdui-color-teal-100{color:rgba(0,0,0,.87)!important;background-color:#b2dfdb!important}.mdui-color-teal-200{color:rgba(0,0,0,.87)!important;background-color:#80cbc4!important}.mdui-color-teal-300{color:rgba(0,0,0,.87)!important;background-color:#4db6ac!important}.mdui-color-teal-400{color:rgba(0,0,0,.87)!important;background-color:#26a69a!important}.mdui-color-teal-500{color:#fff!important;background-color:#009688!important}.mdui-color-teal-600{color:#fff!important;background-color:#00897b!important}.mdui-color-teal-700{color:#fff!important;background-color:#00796b!important}.mdui-color-teal-800{color:#fff!important;background-color:#00695c!important}.mdui-color-teal-900{color:#fff!important;background-color:#004d40!important}.mdui-color-yellow{color:rgba(0,0,0,.87)!important;background-color:#ffeb3b!important}.mdui-color-yellow-50{color:rgba(0,0,0,.87)!important;background-color:#fffde7!important}.mdui-color-yellow-100{color:rgba(0,0,0,.87)!important;background-color:#fff9c4!important}.mdui-color-yellow-200{color:rgba(0,0,0,.87)!important;background-color:#fff59d!important}.mdui-color-yellow-300{color:rgba(0,0,0,.87)!important;background-color:#fff176!important}.mdui-color-yellow-400{color:rgba(0,0,0,.87)!important;background-color:#ffee58!important}.mdui-color-yellow-500{color:rgba(0,0,0,.87)!important;background-color:#ffeb3b!important}.mdui-color-yellow-600{color:rgba(0,0,0,.87)!important;background-color:#fdd835!important}.mdui-color-yellow-700{color:rgba(0,0,0,.87)!important;background-color:#fbc02d!important}.mdui-color-yellow-800{color:rgba(0,0,0,.87)!important;background-color:#f9a825!important}.mdui-color-yellow-900{color:rgba(0,0,0,.87)!important;background-color:#f57f17!important}.mdui-color-amber-accent{color:rgba(0,0,0,.87)!important;background-color:#ffd740!important}.mdui-color-amber-a100{color:rgba(0,0,0,.87)!important;background-color:#ffe57f!important}.mdui-color-amber-a200{color:rgba(0,0,0,.87)!important;background-color:#ffd740!important}.mdui-color-amber-a400{color:rgba(0,0,0,.87)!important;background-color:#ffc400!important}.mdui-color-amber-a700{color:rgba(0,0,0,.87)!important;background-color:#ffab00!important}.mdui-color-blue-accent{color:#fff!important;background-color:#448aff!important}.mdui-color-blue-a100{color:rgba(0,0,0,.87)!important;background-color:#82b1ff!important}.mdui-color-blue-a200{color:#fff!important;background-color:#448aff!important}.mdui-color-blue-a400{color:#fff!important;background-color:#2979ff!important}.mdui-color-blue-a700{color:#fff!important;background-color:#2962ff!important}.mdui-color-cyan-accent{color:rgba(0,0,0,.87)!important;background-color:#18ffff!important}.mdui-color-cyan-a100{color:rgba(0,0,0,.87)!important;background-color:#84ffff!important}.mdui-color-cyan-a200{color:rgba(0,0,0,.87)!important;background-color:#18ffff!important}.mdui-color-cyan-a400{color:rgba(0,0,0,.87)!important;background-color:#00e5ff!important}.mdui-color-cyan-a700{color:rgba(0,0,0,.87)!important;background-color:#00b8d4!important}.mdui-color-deep-orange-accent{color:rgba(0,0,0,.87)!important;background-color:#ff6e40!important}.mdui-color-deep-orange-a100{color:rgba(0,0,0,.87)!important;background-color:#ff9e80!important}.mdui-color-deep-orange-a200{color:rgba(0,0,0,.87)!important;background-color:#ff6e40!important}.mdui-color-deep-orange-a400{color:#fff!important;background-color:#ff3d00!important}.mdui-color-deep-orange-a700{color:#fff!important;background-color:#dd2c00!important}.mdui-color-deep-purple-accent{color:#fff!important;background-color:#7c4dff!important}.mdui-color-deep-purple-a100{color:rgba(0,0,0,.87)!important;background-color:#b388ff!important}.mdui-color-deep-purple-a200{color:#fff!important;background-color:#7c4dff!important}.mdui-color-deep-purple-a400{color:#fff!important;background-color:#651fff!important}.mdui-color-deep-purple-a700{color:#fff!important;background-color:#6200ea!important}.mdui-color-green-accent{color:rgba(0,0,0,.87)!important;background-color:#69f0ae!important}.mdui-color-green-a100{color:rgba(0,0,0,.87)!important;background-color:#b9f6ca!important}.mdui-color-green-a200{color:rgba(0,0,0,.87)!important;background-color:#69f0ae!important}.mdui-color-green-a400{color:rgba(0,0,0,.87)!important;background-color:#00e676!important}.mdui-color-green-a700{color:rgba(0,0,0,.87)!important;background-color:#00c853!important}.mdui-color-indigo-accent{color:#fff!important;background-color:#536dfe!important}.mdui-color-indigo-a100{color:rgba(0,0,0,.87)!important;background-color:#8c9eff!important}.mdui-color-indigo-a200{color:#fff!important;background-color:#536dfe!important}.mdui-color-indigo-a400{color:#fff!important;background-color:#3d5afe!important}.mdui-color-indigo-a700{color:#fff!important;background-color:#304ffe!important}.mdui-color-light-blue-accent{color:rgba(0,0,0,.87)!important;background-color:#40c4ff!important}.mdui-color-light-blue-a100{color:rgba(0,0,0,.87)!important;background-color:#80d8ff!important}.mdui-color-light-blue-a200{color:rgba(0,0,0,.87)!important;background-color:#40c4ff!important}.mdui-color-light-blue-a400{color:rgba(0,0,0,.87)!important;background-color:#00b0ff!important}.mdui-color-light-blue-a700{color:#fff!important;background-color:#0091ea!important}.mdui-color-light-green-accent{color:rgba(0,0,0,.87)!important;background-color:#b2ff59!important}.mdui-color-light-green-a100{color:rgba(0,0,0,.87)!important;background-color:#ccff90!important}.mdui-color-light-green-a200{color:rgba(0,0,0,.87)!important;background-color:#b2ff59!important}.mdui-color-light-green-a400{color:rgba(0,0,0,.87)!important;background-color:#76ff03!important}.mdui-color-light-green-a700{color:rgba(0,0,0,.87)!important;background-color:#64dd17!important}.mdui-color-lime-accent{color:rgba(0,0,0,.87)!important;background-color:#eeff41!important}.mdui-color-lime-a100{color:rgba(0,0,0,.87)!important;background-color:#f4ff81!important}.mdui-color-lime-a200{color:rgba(0,0,0,.87)!important;background-color:#eeff41!important}.mdui-color-lime-a400{color:rgba(0,0,0,.87)!important;background-color:#c6ff00!important}.mdui-color-lime-a700{color:rgba(0,0,0,.87)!important;background-color:#aeea00!important}.mdui-color-orange-accent{color:rgba(0,0,0,.87)!important;background-color:#ffab40!important}.mdui-color-orange-a100{color:rgba(0,0,0,.87)!important;background-color:#ffd180!important}.mdui-color-orange-a200{color:rgba(0,0,0,.87)!important;background-color:#ffab40!important}.mdui-color-orange-a400{color:rgba(0,0,0,.87)!important;background-color:#ff9100!important}.mdui-color-orange-a700{color:rgba(0,0,0,.87)!important;background-color:#ff6d00!important}.mdui-color-pink-accent{color:#fff!important;background-color:#ff4081!important}.mdui-color-pink-a100{color:rgba(0,0,0,.87)!important;background-color:#ff80ab!important}.mdui-color-pink-a200{color:#fff!important;background-color:#ff4081!important}.mdui-color-pink-a400{color:#fff!important;background-color:#f50057!important}.mdui-color-pink-a700{color:#fff!important;background-color:#c51162!important}.mdui-color-purple-accent{color:#fff!important;background-color:#e040fb!important}.mdui-color-purple-a100{color:rgba(0,0,0,.87)!important;background-color:#ea80fc!important}.mdui-color-purple-a200{color:#fff!important;background-color:#e040fb!important}.mdui-color-purple-a400{color:#fff!important;background-color:#d500f9!important}.mdui-color-purple-a700{color:#fff!important;background-color:#a0f!important}.mdui-color-red-accent{color:#fff!important;background-color:#ff5252!important}.mdui-color-red-a100{color:rgba(0,0,0,.87)!important;background-color:#ff8a80!important}.mdui-color-red-a200{color:#fff!important;background-color:#ff5252!important}.mdui-color-red-a400{color:#fff!important;background-color:#ff1744!important}.mdui-color-red-a700{color:#fff!important;background-color:#d50000!important}.mdui-color-teal-accent{color:rgba(0,0,0,.87)!important;background-color:#64ffda!important}.mdui-color-teal-a100{color:rgba(0,0,0,.87)!important;background-color:#a7ffeb!important}.mdui-color-teal-a200{color:rgba(0,0,0,.87)!important;background-color:#64ffda!important}.mdui-color-teal-a400{color:rgba(0,0,0,.87)!important;background-color:#1de9b6!important}.mdui-color-teal-a700{color:rgba(0,0,0,.87)!important;background-color:#00bfa5!important}.mdui-color-yellow-accent{color:rgba(0,0,0,.87)!important;background-color:#ff0!important}.mdui-color-yellow-a100{color:rgba(0,0,0,.87)!important;background-color:#ffff8d!important}.mdui-color-yellow-a200{color:rgba(0,0,0,.87)!important;background-color:#ff0!important}.mdui-color-yellow-a400{color:rgba(0,0,0,.87)!important;background-color:#ffea00!important}.mdui-color-yellow-a700{color:rgba(0,0,0,.87)!important;background-color:#ffd600!important}.mdui-color-black{color:#fff!important;background-color:#000!important}.mdui-color-white{color:rgba(0,0,0,.87)!important;background-color:#fff!important}.mdui-color-transparent{background-color:transparent!important}.mdui-theme-primary-amber .mdui-text-color-theme{color:#ffc107!important}.mdui-theme-primary-amber .mdui-text-color-theme-50{color:#fff8e1!important}.mdui-theme-primary-amber .mdui-text-color-theme-100{color:#ffecb3!important}.mdui-theme-primary-amber .mdui-text-color-theme-200{color:#ffe082!important}.mdui-theme-primary-amber .mdui-text-color-theme-300{color:#ffd54f!important}.mdui-theme-primary-amber .mdui-text-color-theme-400{color:#ffca28!important}.mdui-theme-primary-amber .mdui-text-color-theme-500{color:#ffc107!important}.mdui-theme-primary-amber .mdui-text-color-theme-600{color:#ffb300!important}.mdui-theme-primary-amber .mdui-text-color-theme-700{color:#ffa000!important}.mdui-theme-primary-amber .mdui-text-color-theme-800{color:#ff8f00!important}.mdui-theme-primary-amber .mdui-text-color-theme-900{color:#ff6f00!important}.mdui-theme-primary-blue .mdui-text-color-theme{color:#2196f3!important}.mdui-theme-primary-blue .mdui-text-color-theme-50{color:#e3f2fd!important}.mdui-theme-primary-blue .mdui-text-color-theme-100{color:#bbdefb!important}.mdui-theme-primary-blue .mdui-text-color-theme-200{color:#90caf9!important}.mdui-theme-primary-blue .mdui-text-color-theme-300{color:#64b5f6!important}.mdui-theme-primary-blue .mdui-text-color-theme-400{color:#42a5f5!important}.mdui-theme-primary-blue .mdui-text-color-theme-500{color:#2196f3!important}.mdui-theme-primary-blue .mdui-text-color-theme-600{color:#1e88e5!important}.mdui-theme-primary-blue .mdui-text-color-theme-700{color:#1976d2!important}.mdui-theme-primary-blue .mdui-text-color-theme-800{color:#1565c0!important}.mdui-theme-primary-blue .mdui-text-color-theme-900{color:#0d47a1!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme{color:#607d8b!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-50{color:#eceff1!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-100{color:#cfd8dc!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-200{color:#b0bec5!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-300{color:#90a4ae!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-400{color:#78909c!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-500{color:#607d8b!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-600{color:#546e7a!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-700{color:#455a64!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-800{color:#37474f!important}.mdui-theme-primary-blue-grey .mdui-text-color-theme-900{color:#263238!important}.mdui-theme-primary-brown .mdui-text-color-theme{color:#795548!important}.mdui-theme-primary-brown .mdui-text-color-theme-50{color:#efebe9!important}.mdui-theme-primary-brown .mdui-text-color-theme-100{color:#d7ccc8!important}.mdui-theme-primary-brown .mdui-text-color-theme-200{color:#bcaaa4!important}.mdui-theme-primary-brown .mdui-text-color-theme-300{color:#a1887f!important}.mdui-theme-primary-brown .mdui-text-color-theme-400{color:#8d6e63!important}.mdui-theme-primary-brown .mdui-text-color-theme-500{color:#795548!important}.mdui-theme-primary-brown .mdui-text-color-theme-600{color:#6d4c41!important}.mdui-theme-primary-brown .mdui-text-color-theme-700{color:#5d4037!important}.mdui-theme-primary-brown .mdui-text-color-theme-800{color:#4e342e!important}.mdui-theme-primary-brown .mdui-text-color-theme-900{color:#3e2723!important}.mdui-theme-primary-cyan .mdui-text-color-theme{color:#00bcd4!important}.mdui-theme-primary-cyan .mdui-text-color-theme-50{color:#e0f7fa!important}.mdui-theme-primary-cyan .mdui-text-color-theme-100{color:#b2ebf2!important}.mdui-theme-primary-cyan .mdui-text-color-theme-200{color:#80deea!important}.mdui-theme-primary-cyan .mdui-text-color-theme-300{color:#4dd0e1!important}.mdui-theme-primary-cyan .mdui-text-color-theme-400{color:#26c6da!important}.mdui-theme-primary-cyan .mdui-text-color-theme-500{color:#00bcd4!important}.mdui-theme-primary-cyan .mdui-text-color-theme-600{color:#00acc1!important}.mdui-theme-primary-cyan .mdui-text-color-theme-700{color:#0097a7!important}.mdui-theme-primary-cyan .mdui-text-color-theme-800{color:#00838f!important}.mdui-theme-primary-cyan .mdui-text-color-theme-900{color:#006064!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme{color:#ff5722!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-50{color:#fbe9e7!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-100{color:#ffccbc!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-200{color:#ffab91!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-300{color:#ff8a65!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-400{color:#ff7043!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-500{color:#ff5722!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-600{color:#f4511e!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-700{color:#e64a19!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-800{color:#d84315!important}.mdui-theme-primary-deep-orange .mdui-text-color-theme-900{color:#bf360c!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme{color:#673ab7!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-50{color:#ede7f6!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-100{color:#d1c4e9!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-200{color:#b39ddb!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-300{color:#9575cd!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-400{color:#7e57c2!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-500{color:#673ab7!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-600{color:#5e35b1!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-700{color:#512da8!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-800{color:#4527a0!important}.mdui-theme-primary-deep-purple .mdui-text-color-theme-900{color:#311b92!important}.mdui-theme-primary-green .mdui-text-color-theme{color:#4caf50!important}.mdui-theme-primary-green .mdui-text-color-theme-50{color:#e8f5e9!important}.mdui-theme-primary-green .mdui-text-color-theme-100{color:#c8e6c9!important}.mdui-theme-primary-green .mdui-text-color-theme-200{color:#a5d6a7!important}.mdui-theme-primary-green .mdui-text-color-theme-300{color:#81c784!important}.mdui-theme-primary-green .mdui-text-color-theme-400{color:#66bb6a!important}.mdui-theme-primary-green .mdui-text-color-theme-500{color:#4caf50!important}.mdui-theme-primary-green .mdui-text-color-theme-600{color:#43a047!important}.mdui-theme-primary-green .mdui-text-color-theme-700{color:#388e3c!important}.mdui-theme-primary-green .mdui-text-color-theme-800{color:#2e7d32!important}.mdui-theme-primary-green .mdui-text-color-theme-900{color:#1b5e20!important}.mdui-theme-primary-grey .mdui-text-color-theme{color:#9e9e9e!important}.mdui-theme-primary-grey .mdui-text-color-theme-50{color:#fafafa!important}.mdui-theme-primary-grey .mdui-text-color-theme-100{color:#f5f5f5!important}.mdui-theme-primary-grey .mdui-text-color-theme-200{color:#eee!important}.mdui-theme-primary-grey .mdui-text-color-theme-300{color:#e0e0e0!important}.mdui-theme-primary-grey .mdui-text-color-theme-400{color:#bdbdbd!important}.mdui-theme-primary-grey .mdui-text-color-theme-500{color:#9e9e9e!important}.mdui-theme-primary-grey .mdui-text-color-theme-600{color:#757575!important}.mdui-theme-primary-grey .mdui-text-color-theme-700{color:#616161!important}.mdui-theme-primary-grey .mdui-text-color-theme-800{color:#424242!important}.mdui-theme-primary-grey .mdui-text-color-theme-900{color:#212121!important}.mdui-theme-primary-indigo .mdui-text-color-theme{color:#3f51b5!important}.mdui-theme-primary-indigo .mdui-text-color-theme-50{color:#e8eaf6!important}.mdui-theme-primary-indigo .mdui-text-color-theme-100{color:#c5cae9!important}.mdui-theme-primary-indigo .mdui-text-color-theme-200{color:#9fa8da!important}.mdui-theme-primary-indigo .mdui-text-color-theme-300{color:#7986cb!important}.mdui-theme-primary-indigo .mdui-text-color-theme-400{color:#5c6bc0!important}.mdui-theme-primary-indigo .mdui-text-color-theme-500{color:#3f51b5!important}.mdui-theme-primary-indigo .mdui-text-color-theme-600{color:#3949ab!important}.mdui-theme-primary-indigo .mdui-text-color-theme-700{color:#303f9f!important}.mdui-theme-primary-indigo .mdui-text-color-theme-800{color:#283593!important}.mdui-theme-primary-indigo .mdui-text-color-theme-900{color:#1a237e!important}.mdui-theme-primary-light-blue .mdui-text-color-theme{color:#03a9f4!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-50{color:#e1f5fe!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-100{color:#b3e5fc!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-200{color:#81d4fa!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-300{color:#4fc3f7!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-400{color:#29b6f6!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-500{color:#03a9f4!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-600{color:#039be5!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-700{color:#0288d1!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-800{color:#0277bd!important}.mdui-theme-primary-light-blue .mdui-text-color-theme-900{color:#01579b!important}.mdui-theme-primary-light-green .mdui-text-color-theme{color:#8bc34a!important}.mdui-theme-primary-light-green .mdui-text-color-theme-50{color:#f1f8e9!important}.mdui-theme-primary-light-green .mdui-text-color-theme-100{color:#dcedc8!important}.mdui-theme-primary-light-green .mdui-text-color-theme-200{color:#c5e1a5!important}.mdui-theme-primary-light-green .mdui-text-color-theme-300{color:#aed581!important}.mdui-theme-primary-light-green .mdui-text-color-theme-400{color:#9ccc65!important}.mdui-theme-primary-light-green .mdui-text-color-theme-500{color:#8bc34a!important}.mdui-theme-primary-light-green .mdui-text-color-theme-600{color:#7cb342!important}.mdui-theme-primary-light-green .mdui-text-color-theme-700{color:#689f38!important}.mdui-theme-primary-light-green .mdui-text-color-theme-800{color:#558b2f!important}.mdui-theme-primary-light-green .mdui-text-color-theme-900{color:#33691e!important}.mdui-theme-primary-lime .mdui-text-color-theme{color:#cddc39!important}.mdui-theme-primary-lime .mdui-text-color-theme-50{color:#f9fbe7!important}.mdui-theme-primary-lime .mdui-text-color-theme-100{color:#f0f4c3!important}.mdui-theme-primary-lime .mdui-text-color-theme-200{color:#e6ee9c!important}.mdui-theme-primary-lime .mdui-text-color-theme-300{color:#dce775!important}.mdui-theme-primary-lime .mdui-text-color-theme-400{color:#d4e157!important}.mdui-theme-primary-lime .mdui-text-color-theme-500{color:#cddc39!important}.mdui-theme-primary-lime .mdui-text-color-theme-600{color:#c0ca33!important}.mdui-theme-primary-lime .mdui-text-color-theme-700{color:#afb42b!important}.mdui-theme-primary-lime .mdui-text-color-theme-800{color:#9e9d24!important}.mdui-theme-primary-lime .mdui-text-color-theme-900{color:#827717!important}.mdui-theme-primary-orange .mdui-text-color-theme{color:#ff9800!important}.mdui-theme-primary-orange .mdui-text-color-theme-50{color:#fff3e0!important}.mdui-theme-primary-orange .mdui-text-color-theme-100{color:#ffe0b2!important}.mdui-theme-primary-orange .mdui-text-color-theme-200{color:#ffcc80!important}.mdui-theme-primary-orange .mdui-text-color-theme-300{color:#ffb74d!important}.mdui-theme-primary-orange .mdui-text-color-theme-400{color:#ffa726!important}.mdui-theme-primary-orange .mdui-text-color-theme-500{color:#ff9800!important}.mdui-theme-primary-orange .mdui-text-color-theme-600{color:#fb8c00!important}.mdui-theme-primary-orange .mdui-text-color-theme-700{color:#f57c00!important}.mdui-theme-primary-orange .mdui-text-color-theme-800{color:#ef6c00!important}.mdui-theme-primary-orange .mdui-text-color-theme-900{color:#e65100!important}.mdui-theme-primary-pink .mdui-text-color-theme{color:#e91e63!important}.mdui-theme-primary-pink .mdui-text-color-theme-50{color:#fce4ec!important}.mdui-theme-primary-pink .mdui-text-color-theme-100{color:#f8bbd0!important}.mdui-theme-primary-pink .mdui-text-color-theme-200{color:#f48fb1!important}.mdui-theme-primary-pink .mdui-text-color-theme-300{color:#f06292!important}.mdui-theme-primary-pink .mdui-text-color-theme-400{color:#ec407a!important}.mdui-theme-primary-pink .mdui-text-color-theme-500{color:#e91e63!important}.mdui-theme-primary-pink .mdui-text-color-theme-600{color:#d81b60!important}.mdui-theme-primary-pink .mdui-text-color-theme-700{color:#c2185b!important}.mdui-theme-primary-pink .mdui-text-color-theme-800{color:#ad1457!important}.mdui-theme-primary-pink .mdui-text-color-theme-900{color:#880e4f!important}.mdui-theme-primary-purple .mdui-text-color-theme{color:#9c27b0!important}.mdui-theme-primary-purple .mdui-text-color-theme-50{color:#f3e5f5!important}.mdui-theme-primary-purple .mdui-text-color-theme-100{color:#e1bee7!important}.mdui-theme-primary-purple .mdui-text-color-theme-200{color:#ce93d8!important}.mdui-theme-primary-purple .mdui-text-color-theme-300{color:#ba68c8!important}.mdui-theme-primary-purple .mdui-text-color-theme-400{color:#ab47bc!important}.mdui-theme-primary-purple .mdui-text-color-theme-500{color:#9c27b0!important}.mdui-theme-primary-purple .mdui-text-color-theme-600{color:#8e24aa!important}.mdui-theme-primary-purple .mdui-text-color-theme-700{color:#7b1fa2!important}.mdui-theme-primary-purple .mdui-text-color-theme-800{color:#6a1b9a!important}.mdui-theme-primary-purple .mdui-text-color-theme-900{color:#4a148c!important}.mdui-theme-primary-red .mdui-text-color-theme{color:#f44336!important}.mdui-theme-primary-red .mdui-text-color-theme-50{color:#ffebee!important}.mdui-theme-primary-red .mdui-text-color-theme-100{color:#ffcdd2!important}.mdui-theme-primary-red .mdui-text-color-theme-200{color:#ef9a9a!important}.mdui-theme-primary-red .mdui-text-color-theme-300{color:#e57373!important}.mdui-theme-primary-red .mdui-text-color-theme-400{color:#ef5350!important}.mdui-theme-primary-red .mdui-text-color-theme-500{color:#f44336!important}.mdui-theme-primary-red .mdui-text-color-theme-600{color:#e53935!important}.mdui-theme-primary-red .mdui-text-color-theme-700{color:#d32f2f!important}.mdui-theme-primary-red .mdui-text-color-theme-800{color:#c62828!important}.mdui-theme-primary-red .mdui-text-color-theme-900{color:#b71c1c!important}.mdui-theme-primary-teal .mdui-text-color-theme{color:#009688!important}.mdui-theme-primary-teal .mdui-text-color-theme-50{color:#e0f2f1!important}.mdui-theme-primary-teal .mdui-text-color-theme-100{color:#b2dfdb!important}.mdui-theme-primary-teal .mdui-text-color-theme-200{color:#80cbc4!important}.mdui-theme-primary-teal .mdui-text-color-theme-300{color:#4db6ac!important}.mdui-theme-primary-teal .mdui-text-color-theme-400{color:#26a69a!important}.mdui-theme-primary-teal .mdui-text-color-theme-500{color:#009688!important}.mdui-theme-primary-teal .mdui-text-color-theme-600{color:#00897b!important}.mdui-theme-primary-teal .mdui-text-color-theme-700{color:#00796b!important}.mdui-theme-primary-teal .mdui-text-color-theme-800{color:#00695c!important}.mdui-theme-primary-teal .mdui-text-color-theme-900{color:#004d40!important}.mdui-theme-primary-yellow .mdui-text-color-theme{color:#ffeb3b!important}.mdui-theme-primary-yellow .mdui-text-color-theme-50{color:#fffde7!important}.mdui-theme-primary-yellow .mdui-text-color-theme-100{color:#fff9c4!important}.mdui-theme-primary-yellow .mdui-text-color-theme-200{color:#fff59d!important}.mdui-theme-primary-yellow .mdui-text-color-theme-300{color:#fff176!important}.mdui-theme-primary-yellow .mdui-text-color-theme-400{color:#ffee58!important}.mdui-theme-primary-yellow .mdui-text-color-theme-500{color:#ffeb3b!important}.mdui-theme-primary-yellow .mdui-text-color-theme-600{color:#fdd835!important}.mdui-theme-primary-yellow .mdui-text-color-theme-700{color:#fbc02d!important}.mdui-theme-primary-yellow .mdui-text-color-theme-800{color:#f9a825!important}.mdui-theme-primary-yellow .mdui-text-color-theme-900{color:#f57f17!important}.mdui-theme-accent-amber .mdui-text-color-theme-accent{color:#ffd740!important}.mdui-theme-accent-amber .mdui-text-color-theme-a100{color:#ffe57f!important}.mdui-theme-accent-amber .mdui-text-color-theme-a200{color:#ffd740!important}.mdui-theme-accent-amber .mdui-text-color-theme-a400{color:#ffc400!important}.mdui-theme-accent-amber .mdui-text-color-theme-a700{color:#ffab00!important}.mdui-theme-accent-blue .mdui-text-color-theme-accent{color:#448aff!important}.mdui-theme-accent-blue .mdui-text-color-theme-a100{color:#82b1ff!important}.mdui-theme-accent-blue .mdui-text-color-theme-a200{color:#448aff!important}.mdui-theme-accent-blue .mdui-text-color-theme-a400{color:#2979ff!important}.mdui-theme-accent-blue .mdui-text-color-theme-a700{color:#2962ff!important}.mdui-theme-accent-cyan .mdui-text-color-theme-accent{color:#18ffff!important}.mdui-theme-accent-cyan .mdui-text-color-theme-a100{color:#84ffff!important}.mdui-theme-accent-cyan .mdui-text-color-theme-a200{color:#18ffff!important}.mdui-theme-accent-cyan .mdui-text-color-theme-a400{color:#00e5ff!important}.mdui-theme-accent-cyan .mdui-text-color-theme-a700{color:#00b8d4!important}.mdui-theme-accent-deep-orange .mdui-text-color-theme-accent{color:#ff6e40!important}.mdui-theme-accent-deep-orange .mdui-text-color-theme-a100{color:#ff9e80!important}.mdui-theme-accent-deep-orange .mdui-text-color-theme-a200{color:#ff6e40!important}.mdui-theme-accent-deep-orange .mdui-text-color-theme-a400{color:#ff3d00!important}.mdui-theme-accent-deep-orange .mdui-text-color-theme-a700{color:#dd2c00!important}.mdui-theme-accent-deep-purple .mdui-text-color-theme-accent{color:#7c4dff!important}.mdui-theme-accent-deep-purple .mdui-text-color-theme-a100{color:#b388ff!important}.mdui-theme-accent-deep-purple .mdui-text-color-theme-a200{color:#7c4dff!important}.mdui-theme-accent-deep-purple .mdui-text-color-theme-a400{color:#651fff!important}.mdui-theme-accent-deep-purple .mdui-text-color-theme-a700{color:#6200ea!important}.mdui-theme-accent-green .mdui-text-color-theme-accent{color:#69f0ae!important}.mdui-theme-accent-green .mdui-text-color-theme-a100{color:#b9f6ca!important}.mdui-theme-accent-green .mdui-text-color-theme-a200{color:#69f0ae!important}.mdui-theme-accent-green .mdui-text-color-theme-a400{color:#00e676!important}.mdui-theme-accent-green .mdui-text-color-theme-a700{color:#00c853!important}.mdui-theme-accent-indigo .mdui-text-color-theme-accent{color:#536dfe!important}.mdui-theme-accent-indigo .mdui-text-color-theme-a100{color:#8c9eff!important}.mdui-theme-accent-indigo .mdui-text-color-theme-a200{color:#536dfe!important}.mdui-theme-accent-indigo .mdui-text-color-theme-a400{color:#3d5afe!important}.mdui-theme-accent-indigo .mdui-text-color-theme-a700{color:#304ffe!important}.mdui-theme-accent-light-blue .mdui-text-color-theme-accent{color:#40c4ff!important}.mdui-theme-accent-light-blue .mdui-text-color-theme-a100{color:#80d8ff!important}.mdui-theme-accent-light-blue .mdui-text-color-theme-a200{color:#40c4ff!important}.mdui-theme-accent-light-blue .mdui-text-color-theme-a400{color:#00b0ff!important}.mdui-theme-accent-light-blue .mdui-text-color-theme-a700{color:#0091ea!important}.mdui-theme-accent-light-green .mdui-text-color-theme-accent{color:#b2ff59!important}.mdui-theme-accent-light-green .mdui-text-color-theme-a100{color:#ccff90!important}.mdui-theme-accent-light-green .mdui-text-color-theme-a200{color:#b2ff59!important}.mdui-theme-accent-light-green .mdui-text-color-theme-a400{color:#76ff03!important}.mdui-theme-accent-light-green .mdui-text-color-theme-a700{color:#64dd17!important}.mdui-theme-accent-lime .mdui-text-color-theme-accent{color:#eeff41!important}.mdui-theme-accent-lime .mdui-text-color-theme-a100{color:#f4ff81!important}.mdui-theme-accent-lime .mdui-text-color-theme-a200{color:#eeff41!important}.mdui-theme-accent-lime .mdui-text-color-theme-a400{color:#c6ff00!important}.mdui-theme-accent-lime .mdui-text-color-theme-a700{color:#aeea00!important}.mdui-theme-accent-orange .mdui-text-color-theme-accent{color:#ffab40!important}.mdui-theme-accent-orange .mdui-text-color-theme-a100{color:#ffd180!important}.mdui-theme-accent-orange .mdui-text-color-theme-a200{color:#ffab40!important}.mdui-theme-accent-orange .mdui-text-color-theme-a400{color:#ff9100!important}.mdui-theme-accent-orange .mdui-text-color-theme-a700{color:#ff6d00!important}.mdui-theme-accent-pink .mdui-text-color-theme-accent{color:#ff4081!important}.mdui-theme-accent-pink .mdui-text-color-theme-a100{color:#ff80ab!important}.mdui-theme-accent-pink .mdui-text-color-theme-a200{color:#ff4081!important}.mdui-theme-accent-pink .mdui-text-color-theme-a400{color:#f50057!important}.mdui-theme-accent-pink .mdui-text-color-theme-a700{color:#c51162!important}.mdui-theme-accent-purple .mdui-text-color-theme-accent{color:#e040fb!important}.mdui-theme-accent-purple .mdui-text-color-theme-a100{color:#ea80fc!important}.mdui-theme-accent-purple .mdui-text-color-theme-a200{color:#e040fb!important}.mdui-theme-accent-purple .mdui-text-color-theme-a400{color:#d500f9!important}.mdui-theme-accent-purple .mdui-text-color-theme-a700{color:#a0f!important}.mdui-theme-accent-red .mdui-text-color-theme-accent{color:#ff5252!important}.mdui-theme-accent-red .mdui-text-color-theme-a100{color:#ff8a80!important}.mdui-theme-accent-red .mdui-text-color-theme-a200{color:#ff5252!important}.mdui-theme-accent-red .mdui-text-color-theme-a400{color:#ff1744!important}.mdui-theme-accent-red .mdui-text-color-theme-a700{color:#d50000!important}.mdui-theme-accent-teal .mdui-text-color-theme-accent{color:#64ffda!important}.mdui-theme-accent-teal .mdui-text-color-theme-a100{color:#a7ffeb!important}.mdui-theme-accent-teal .mdui-text-color-theme-a200{color:#64ffda!important}.mdui-theme-accent-teal .mdui-text-color-theme-a400{color:#1de9b6!important}.mdui-theme-accent-teal .mdui-text-color-theme-a700{color:#00bfa5!important}.mdui-theme-accent-yellow .mdui-text-color-theme-accent{color:#ff0!important}.mdui-theme-accent-yellow .mdui-text-color-theme-a100{color:#ffff8d!important}.mdui-theme-accent-yellow .mdui-text-color-theme-a200{color:#ff0!important}.mdui-theme-accent-yellow .mdui-text-color-theme-a400{color:#ffea00!important}.mdui-theme-accent-yellow .mdui-text-color-theme-a700{color:#ffd600!important}.mdui-text-color-amber{color:#ffc107!important}.mdui-text-color-amber-50{color:#fff8e1!important}.mdui-text-color-amber-100{color:#ffecb3!important}.mdui-text-color-amber-200{color:#ffe082!important}.mdui-text-color-amber-300{color:#ffd54f!important}.mdui-text-color-amber-400{color:#ffca28!important}.mdui-text-color-amber-500{color:#ffc107!important}.mdui-text-color-amber-600{color:#ffb300!important}.mdui-text-color-amber-700{color:#ffa000!important}.mdui-text-color-amber-800{color:#ff8f00!important}.mdui-text-color-amber-900{color:#ff6f00!important}.mdui-text-color-blue{color:#2196f3!important}.mdui-text-color-blue-50{color:#e3f2fd!important}.mdui-text-color-blue-100{color:#bbdefb!important}.mdui-text-color-blue-200{color:#90caf9!important}.mdui-text-color-blue-300{color:#64b5f6!important}.mdui-text-color-blue-400{color:#42a5f5!important}.mdui-text-color-blue-500{color:#2196f3!important}.mdui-text-color-blue-600{color:#1e88e5!important}.mdui-text-color-blue-700{color:#1976d2!important}.mdui-text-color-blue-800{color:#1565c0!important}.mdui-text-color-blue-900{color:#0d47a1!important}.mdui-text-color-blue-grey{color:#607d8b!important}.mdui-text-color-blue-grey-50{color:#eceff1!important}.mdui-text-color-blue-grey-100{color:#cfd8dc!important}.mdui-text-color-blue-grey-200{color:#b0bec5!important}.mdui-text-color-blue-grey-300{color:#90a4ae!important}.mdui-text-color-blue-grey-400{color:#78909c!important}.mdui-text-color-blue-grey-500{color:#607d8b!important}.mdui-text-color-blue-grey-600{color:#546e7a!important}.mdui-text-color-blue-grey-700{color:#455a64!important}.mdui-text-color-blue-grey-800{color:#37474f!important}.mdui-text-color-blue-grey-900{color:#263238!important}.mdui-text-color-brown{color:#795548!important}.mdui-text-color-brown-50{color:#efebe9!important}.mdui-text-color-brown-100{color:#d7ccc8!important}.mdui-text-color-brown-200{color:#bcaaa4!important}.mdui-text-color-brown-300{color:#a1887f!important}.mdui-text-color-brown-400{color:#8d6e63!important}.mdui-text-color-brown-500{color:#795548!important}.mdui-text-color-brown-600{color:#6d4c41!important}.mdui-text-color-brown-700{color:#5d4037!important}.mdui-text-color-brown-800{color:#4e342e!important}.mdui-text-color-brown-900{color:#3e2723!important}.mdui-text-color-cyan{color:#00bcd4!important}.mdui-text-color-cyan-50{color:#e0f7fa!important}.mdui-text-color-cyan-100{color:#b2ebf2!important}.mdui-text-color-cyan-200{color:#80deea!important}.mdui-text-color-cyan-300{color:#4dd0e1!important}.mdui-text-color-cyan-400{color:#26c6da!important}.mdui-text-color-cyan-500{color:#00bcd4!important}.mdui-text-color-cyan-600{color:#00acc1!important}.mdui-text-color-cyan-700{color:#0097a7!important}.mdui-text-color-cyan-800{color:#00838f!important}.mdui-text-color-cyan-900{color:#006064!important}.mdui-text-color-deep-orange{color:#ff5722!important}.mdui-text-color-deep-orange-50{color:#fbe9e7!important}.mdui-text-color-deep-orange-100{color:#ffccbc!important}.mdui-text-color-deep-orange-200{color:#ffab91!important}.mdui-text-color-deep-orange-300{color:#ff8a65!important}.mdui-text-color-deep-orange-400{color:#ff7043!important}.mdui-text-color-deep-orange-500{color:#ff5722!important}.mdui-text-color-deep-orange-600{color:#f4511e!important}.mdui-text-color-deep-orange-700{color:#e64a19!important}.mdui-text-color-deep-orange-800{color:#d84315!important}.mdui-text-color-deep-orange-900{color:#bf360c!important}.mdui-text-color-deep-purple{color:#673ab7!important}.mdui-text-color-deep-purple-50{color:#ede7f6!important}.mdui-text-color-deep-purple-100{color:#d1c4e9!important}.mdui-text-color-deep-purple-200{color:#b39ddb!important}.mdui-text-color-deep-purple-300{color:#9575cd!important}.mdui-text-color-deep-purple-400{color:#7e57c2!important}.mdui-text-color-deep-purple-500{color:#673ab7!important}.mdui-text-color-deep-purple-600{color:#5e35b1!important}.mdui-text-color-deep-purple-700{color:#512da8!important}.mdui-text-color-deep-purple-800{color:#4527a0!important}.mdui-text-color-deep-purple-900{color:#311b92!important}.mdui-text-color-green{color:#4caf50!important}.mdui-text-color-green-50{color:#e8f5e9!important}.mdui-text-color-green-100{color:#c8e6c9!important}.mdui-text-color-green-200{color:#a5d6a7!important}.mdui-text-color-green-300{color:#81c784!important}.mdui-text-color-green-400{color:#66bb6a!important}.mdui-text-color-green-500{color:#4caf50!important}.mdui-text-color-green-600{color:#43a047!important}.mdui-text-color-green-700{color:#388e3c!important}.mdui-text-color-green-800{color:#2e7d32!important}.mdui-text-color-green-900{color:#1b5e20!important}.mdui-text-color-grey{color:#9e9e9e!important}.mdui-text-color-grey-50{color:#fafafa!important}.mdui-text-color-grey-100{color:#f5f5f5!important}.mdui-text-color-grey-200{color:#eee!important}.mdui-text-color-grey-300{color:#e0e0e0!important}.mdui-text-color-grey-400{color:#bdbdbd!important}.mdui-text-color-grey-500{color:#9e9e9e!important}.mdui-text-color-grey-600{color:#757575!important}.mdui-text-color-grey-700{color:#616161!important}.mdui-text-color-grey-800{color:#424242!important}.mdui-text-color-grey-900{color:#212121!important}.mdui-text-color-indigo{color:#3f51b5!important}.mdui-text-color-indigo-50{color:#e8eaf6!important}.mdui-text-color-indigo-100{color:#c5cae9!important}.mdui-text-color-indigo-200{color:#9fa8da!important}.mdui-text-color-indigo-300{color:#7986cb!important}.mdui-text-color-indigo-400{color:#5c6bc0!important}.mdui-text-color-indigo-500{color:#3f51b5!important}.mdui-text-color-indigo-600{color:#3949ab!important}.mdui-text-color-indigo-700{color:#303f9f!important}.mdui-text-color-indigo-800{color:#283593!important}.mdui-text-color-indigo-900{color:#1a237e!important}.mdui-text-color-light-blue{color:#03a9f4!important}.mdui-text-color-light-blue-50{color:#e1f5fe!important}.mdui-text-color-light-blue-100{color:#b3e5fc!important}.mdui-text-color-light-blue-200{color:#81d4fa!important}.mdui-text-color-light-blue-300{color:#4fc3f7!important}.mdui-text-color-light-blue-400{color:#29b6f6!important}.mdui-text-color-light-blue-500{color:#03a9f4!important}.mdui-text-color-light-blue-600{color:#039be5!important}.mdui-text-color-light-blue-700{color:#0288d1!important}.mdui-text-color-light-blue-800{color:#0277bd!important}.mdui-text-color-light-blue-900{color:#01579b!important}.mdui-text-color-light-green{color:#8bc34a!important}.mdui-text-color-light-green-50{color:#f1f8e9!important}.mdui-text-color-light-green-100{color:#dcedc8!important}.mdui-text-color-light-green-200{color:#c5e1a5!important}.mdui-text-color-light-green-300{color:#aed581!important}.mdui-text-color-light-green-400{color:#9ccc65!important}.mdui-text-color-light-green-500{color:#8bc34a!important}.mdui-text-color-light-green-600{color:#7cb342!important}.mdui-text-color-light-green-700{color:#689f38!important}.mdui-text-color-light-green-800{color:#558b2f!important}.mdui-text-color-light-green-900{color:#33691e!important}.mdui-text-color-lime{color:#cddc39!important}.mdui-text-color-lime-50{color:#f9fbe7!important}.mdui-text-color-lime-100{color:#f0f4c3!important}.mdui-text-color-lime-200{color:#e6ee9c!important}.mdui-text-color-lime-300{color:#dce775!important}.mdui-text-color-lime-400{color:#d4e157!important}.mdui-text-color-lime-500{color:#cddc39!important}.mdui-text-color-lime-600{color:#c0ca33!important}.mdui-text-color-lime-700{color:#afb42b!important}.mdui-text-color-lime-800{color:#9e9d24!important}.mdui-text-color-lime-900{color:#827717!important}.mdui-text-color-orange{color:#ff9800!important}.mdui-text-color-orange-50{color:#fff3e0!important}.mdui-text-color-orange-100{color:#ffe0b2!important}.mdui-text-color-orange-200{color:#ffcc80!important}.mdui-text-color-orange-300{color:#ffb74d!important}.mdui-text-color-orange-400{color:#ffa726!important}.mdui-text-color-orange-500{color:#ff9800!important}.mdui-text-color-orange-600{color:#fb8c00!important}.mdui-text-color-orange-700{color:#f57c00!important}.mdui-text-color-orange-800{color:#ef6c00!important}.mdui-text-color-orange-900{color:#e65100!important}.mdui-text-color-pink{color:#e91e63!important}.mdui-text-color-pink-50{color:#fce4ec!important}.mdui-text-color-pink-100{color:#f8bbd0!important}.mdui-text-color-pink-200{color:#f48fb1!important}.mdui-text-color-pink-300{color:#f06292!important}.mdui-text-color-pink-400{color:#ec407a!important}.mdui-text-color-pink-500{color:#e91e63!important}.mdui-text-color-pink-600{color:#d81b60!important}.mdui-text-color-pink-700{color:#c2185b!important}.mdui-text-color-pink-800{color:#ad1457!important}.mdui-text-color-pink-900{color:#880e4f!important}.mdui-text-color-purple{color:#9c27b0!important}.mdui-text-color-purple-50{color:#f3e5f5!important}.mdui-text-color-purple-100{color:#e1bee7!important}.mdui-text-color-purple-200{color:#ce93d8!important}.mdui-text-color-purple-300{color:#ba68c8!important}.mdui-text-color-purple-400{color:#ab47bc!important}.mdui-text-color-purple-500{color:#9c27b0!important}.mdui-text-color-purple-600{color:#8e24aa!important}.mdui-text-color-purple-700{color:#7b1fa2!important}.mdui-text-color-purple-800{color:#6a1b9a!important}.mdui-text-color-purple-900{color:#4a148c!important}.mdui-text-color-red{color:#f44336!important}.mdui-text-color-red-50{color:#ffebee!important}.mdui-text-color-red-100{color:#ffcdd2!important}.mdui-text-color-red-200{color:#ef9a9a!important}.mdui-text-color-red-300{color:#e57373!important}.mdui-text-color-red-400{color:#ef5350!important}.mdui-text-color-red-500{color:#f44336!important}.mdui-text-color-red-600{color:#e53935!important}.mdui-text-color-red-700{color:#d32f2f!important}.mdui-text-color-red-800{color:#c62828!important}.mdui-text-color-red-900{color:#b71c1c!important}.mdui-text-color-teal{color:#009688!important}.mdui-text-color-teal-50{color:#e0f2f1!important}.mdui-text-color-teal-100{color:#b2dfdb!important}.mdui-text-color-teal-200{color:#80cbc4!important}.mdui-text-color-teal-300{color:#4db6ac!important}.mdui-text-color-teal-400{color:#26a69a!important}.mdui-text-color-teal-500{color:#009688!important}.mdui-text-color-teal-600{color:#00897b!important}.mdui-text-color-teal-700{color:#00796b!important}.mdui-text-color-teal-800{color:#00695c!important}.mdui-text-color-teal-900{color:#004d40!important}.mdui-text-color-yellow{color:#ffeb3b!important}.mdui-text-color-yellow-50{color:#fffde7!important}.mdui-text-color-yellow-100{color:#fff9c4!important}.mdui-text-color-yellow-200{color:#fff59d!important}.mdui-text-color-yellow-300{color:#fff176!important}.mdui-text-color-yellow-400{color:#ffee58!important}.mdui-text-color-yellow-500{color:#ffeb3b!important}.mdui-text-color-yellow-600{color:#fdd835!important}.mdui-text-color-yellow-700{color:#fbc02d!important}.mdui-text-color-yellow-800{color:#f9a825!important}.mdui-text-color-yellow-900{color:#f57f17!important}.mdui-text-color-amber-accent{color:#ffd740!important}.mdui-text-color-amber-a100{color:#ffe57f!important}.mdui-text-color-amber-a200{color:#ffd740!important}.mdui-text-color-amber-a400{color:#ffc400!important}.mdui-text-color-amber-a700{color:#ffab00!important}.mdui-text-color-blue-accent{color:#448aff!important}.mdui-text-color-blue-a100{color:#82b1ff!important}.mdui-text-color-blue-a200{color:#448aff!important}.mdui-text-color-blue-a400{color:#2979ff!important}.mdui-text-color-blue-a700{color:#2962ff!important}.mdui-text-color-cyan-accent{color:#18ffff!important}.mdui-text-color-cyan-a100{color:#84ffff!important}.mdui-text-color-cyan-a200{color:#18ffff!important}.mdui-text-color-cyan-a400{color:#00e5ff!important}.mdui-text-color-cyan-a700{color:#00b8d4!important}.mdui-text-color-deep-orange-accent{color:#ff6e40!important}.mdui-text-color-deep-orange-a100{color:#ff9e80!important}.mdui-text-color-deep-orange-a200{color:#ff6e40!important}.mdui-text-color-deep-orange-a400{color:#ff3d00!important}.mdui-text-color-deep-orange-a700{color:#dd2c00!important}.mdui-text-color-deep-purple-accent{color:#7c4dff!important}.mdui-text-color-deep-purple-a100{color:#b388ff!important}.mdui-text-color-deep-purple-a200{color:#7c4dff!important}.mdui-text-color-deep-purple-a400{color:#651fff!important}.mdui-text-color-deep-purple-a700{color:#6200ea!important}.mdui-text-color-green-accent{color:#69f0ae!important}.mdui-text-color-green-a100{color:#b9f6ca!important}.mdui-text-color-green-a200{color:#69f0ae!important}.mdui-text-color-green-a400{color:#00e676!important}.mdui-text-color-green-a700{color:#00c853!important}.mdui-text-color-indigo-accent{color:#536dfe!important}.mdui-text-color-indigo-a100{color:#8c9eff!important}.mdui-text-color-indigo-a200{color:#536dfe!important}.mdui-text-color-indigo-a400{color:#3d5afe!important}.mdui-text-color-indigo-a700{color:#304ffe!important}.mdui-text-color-light-blue-accent{color:#40c4ff!important}.mdui-text-color-light-blue-a100{color:#80d8ff!important}.mdui-text-color-light-blue-a200{color:#40c4ff!important}.mdui-text-color-light-blue-a400{color:#00b0ff!important}.mdui-text-color-light-blue-a700{color:#0091ea!important}.mdui-text-color-light-green-accent{color:#b2ff59!important}.mdui-text-color-light-green-a100{color:#ccff90!important}.mdui-text-color-light-green-a200{color:#b2ff59!important}.mdui-text-color-light-green-a400{color:#76ff03!important}.mdui-text-color-light-green-a700{color:#64dd17!important}.mdui-text-color-lime-accent{color:#eeff41!important}.mdui-text-color-lime-a100{color:#f4ff81!important}.mdui-text-color-lime-a200{color:#eeff41!important}.mdui-text-color-lime-a400{color:#c6ff00!important}.mdui-text-color-lime-a700{color:#aeea00!important}.mdui-text-color-orange-accent{color:#ffab40!important}.mdui-text-color-orange-a100{color:#ffd180!important}.mdui-text-color-orange-a200{color:#ffab40!important}.mdui-text-color-orange-a400{color:#ff9100!important}.mdui-text-color-orange-a700{color:#ff6d00!important}.mdui-text-color-pink-accent{color:#ff4081!important}.mdui-text-color-pink-a100{color:#ff80ab!important}.mdui-text-color-pink-a200{color:#ff4081!important}.mdui-text-color-pink-a400{color:#f50057!important}.mdui-text-color-pink-a700{color:#c51162!important}.mdui-text-color-purple-accent{color:#e040fb!important}.mdui-text-color-purple-a100{color:#ea80fc!important}.mdui-text-color-purple-a200{color:#e040fb!important}.mdui-text-color-purple-a400{color:#d500f9!important}.mdui-text-color-purple-a700{color:#a0f!important}.mdui-text-color-red-accent{color:#ff5252!important}.mdui-text-color-red-a100{color:#ff8a80!important}.mdui-text-color-red-a200{color:#ff5252!important}.mdui-text-color-red-a400{color:#ff1744!important}.mdui-text-color-red-a700{color:#d50000!important}.mdui-text-color-teal-accent{color:#64ffda!important}.mdui-text-color-teal-a100{color:#a7ffeb!important}.mdui-text-color-teal-a200{color:#64ffda!important}.mdui-text-color-teal-a400{color:#1de9b6!important}.mdui-text-color-teal-a700{color:#00bfa5!important}.mdui-text-color-yellow-accent{color:#ff0!important}.mdui-text-color-yellow-a100{color:#ffff8d!important}.mdui-text-color-yellow-a200{color:#ff0!important}.mdui-text-color-yellow-a400{color:#ffea00!important}.mdui-text-color-yellow-a700{color:#ffd600!important}.mdui-text-color-black{color:#000!important}.mdui-text-color-black-text,.mdui-text-color-theme-text{color:rgba(0,0,0,.87)!important}.mdui-text-color-black-secondary,.mdui-text-color-theme-secondary{color:rgba(0,0,0,.54)!important}.mdui-text-color-black-disabled,.mdui-text-color-theme-disabled{color:rgba(0,0,0,.38)!important}.mdui-text-color-black-divider,.mdui-text-color-theme-divider{color:rgba(0,0,0,.12)!important}.mdui-text-color-black-icon,.mdui-text-color-theme-icon{color:rgba(0,0,0,.54)!important}.mdui-text-color-black-icon-disabled,.mdui-text-color-theme-icon-disabled{color:rgba(0,0,0,.26)!important}.mdui-text-color-white{color:#fff!important}.mdui-text-color-white-text{color:#fff!important}.mdui-text-color-white-secondary{color:rgba(255,255,255,.7)!important}.mdui-text-color-white-disabled{color:rgba(255,255,255,.5)!important}.mdui-text-color-white-divider{color:rgba(255,255,255,.12)!important}.mdui-text-color-white-icon{color:#fff!important}.mdui-text-color-white-icon-disabled{color:rgba(255,255,255,.3)!important}.mdui-theme-layout-dark .mdui-text-color-theme-text{color:#fff!important}.mdui-theme-layout-dark .mdui-text-color-theme-secondary{color:rgba(255,255,255,.7)!important}.mdui-theme-layout-dark .mdui-text-color-theme-disabled{color:rgba(255,255,255,.5)!important}.mdui-theme-layout-dark .mdui-text-color-theme-divider{color:rgba(255,255,255,.12)!important}.mdui-theme-layout-dark .mdui-text-color-theme-icon{color:#fff!important}.mdui-theme-layout-dark .mdui-text-color-theme-icon-disabled{color:rgba(255,255,255,.3)!important}.mdui-m-a-0{margin:0!important}.mdui-m-t-0{margin-top:0!important}.mdui-m-r-0{margin-right:0!important}.mdui-m-b-0{margin-bottom:0!important}.mdui-m-l-0{margin-left:0!important}.mdui-m-x-0{margin-right:0!important;margin-left:0!important}.mdui-m-y-0{margin-top:0!important;margin-bottom:0!important}.mdui-p-a-0{padding:0!important}.mdui-p-t-0{padding-top:0!important}.mdui-p-r-0{padding-right:0!important}.mdui-p-b-0{padding-bottom:0!important}.mdui-p-l-0{padding-left:0!important}.mdui-p-x-0{padding-right:0!important;padding-left:0!important}.mdui-p-y-0{padding-top:0!important;padding-bottom:0!important}.mdui-m-a-1{margin:8px!important}.mdui-m-t-1{margin-top:8px!important}.mdui-m-r-1{margin-right:8px!important}.mdui-m-b-1{margin-bottom:8px!important}.mdui-m-l-1{margin-left:8px!important}.mdui-m-x-1{margin-right:8px!important;margin-left:8px!important}.mdui-m-y-1{margin-top:8px!important;margin-bottom:8px!important}.mdui-p-a-1{padding:8px!important}.mdui-p-t-1{padding-top:8px!important}.mdui-p-r-1{padding-right:8px!important}.mdui-p-b-1{padding-bottom:8px!important}.mdui-p-l-1{padding-left:8px!important}.mdui-p-x-1{padding-right:8px!important;padding-left:8px!important}.mdui-p-y-1{padding-top:8px!important;padding-bottom:8px!important}.mdui-m-a-2{margin:16px!important}.mdui-m-t-2{margin-top:16px!important}.mdui-m-r-2{margin-right:16px!important}.mdui-m-b-2{margin-bottom:16px!important}.mdui-m-l-2{margin-left:16px!important}.mdui-m-x-2{margin-right:16px!important;margin-left:16px!important}.mdui-m-y-2{margin-top:16px!important;margin-bottom:16px!important}.mdui-p-a-2{padding:16px!important}.mdui-p-t-2{padding-top:16px!important}.mdui-p-r-2{padding-right:16px!important}.mdui-p-b-2{padding-bottom:16px!important}.mdui-p-l-2{padding-left:16px!important}.mdui-p-x-2{padding-right:16px!important;padding-left:16px!important}.mdui-p-y-2{padding-top:16px!important;padding-bottom:16px!important}.mdui-m-a-3{margin:24px!important}.mdui-m-t-3{margin-top:24px!important}.mdui-m-r-3{margin-right:24px!important}.mdui-m-b-3{margin-bottom:24px!important}.mdui-m-l-3{margin-left:24px!important}.mdui-m-x-3{margin-right:24px!important;margin-left:24px!important}.mdui-m-y-3{margin-top:24px!important;margin-bottom:24px!important}.mdui-p-a-3{padding:24px!important}.mdui-p-t-3{padding-top:24px!important}.mdui-p-r-3{padding-right:24px!important}.mdui-p-b-3{padding-bottom:24px!important}.mdui-p-l-3{padding-left:24px!important}.mdui-p-x-3{padding-right:24px!important;padding-left:24px!important}.mdui-p-y-3{padding-top:24px!important;padding-bottom:24px!important}.mdui-m-a-4{margin:32px!important}.mdui-m-t-4{margin-top:32px!important}.mdui-m-r-4{margin-right:32px!important}.mdui-m-b-4{margin-bottom:32px!important}.mdui-m-l-4{margin-left:32px!important}.mdui-m-x-4{margin-right:32px!important;margin-left:32px!important}.mdui-m-y-4{margin-top:32px!important;margin-bottom:32px!important}.mdui-p-a-4{padding:32px!important}.mdui-p-t-4{padding-top:32px!important}.mdui-p-r-4{padding-right:32px!important}.mdui-p-b-4{padding-bottom:32px!important}.mdui-p-l-4{padding-left:32px!important}.mdui-p-x-4{padding-right:32px!important;padding-left:32px!important}.mdui-p-y-4{padding-top:32px!important;padding-bottom:32px!important}.mdui-m-a-5{margin:40px!important}.mdui-m-t-5{margin-top:40px!important}.mdui-m-r-5{margin-right:40px!important}.mdui-m-b-5{margin-bottom:40px!important}.mdui-m-l-5{margin-left:40px!important}.mdui-m-x-5{margin-right:40px!important;margin-left:40px!important}.mdui-m-y-5{margin-top:40px!important;margin-bottom:40px!important}.mdui-p-a-5{padding:40px!important}.mdui-p-t-5{padding-top:40px!important}.mdui-p-r-5{padding-right:40px!important}.mdui-p-b-5{padding-bottom:40px!important}.mdui-p-l-5{padding-left:40px!important}.mdui-p-x-5{padding-right:40px!important;padding-left:40px!important}.mdui-p-y-5{padding-top:40px!important;padding-bottom:40px!important}.mdui-float-left{float:left!important}.mdui-float-right{float:right!important}.mdui-center{display:block!important;margin-right:auto!important;margin-left:auto!important}.mdui-valign{display:-webkit-box!important;display:-webkit-flex!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-align:center!important;-webkit-align-items:center!important;-ms-flex-align:center!important;align-items:center!important}.mdui-text-left{text-align:left!important}.mdui-text-center{text-align:center!important}.mdui-text-right{text-align:right!important}.mdui-text-lowercase{text-transform:lowercase!important}.mdui-text-uppercase{text-transform:uppercase!important}.mdui-text-capitalize{text-transform:capitalize!important}.mdui-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mdui-clearfix:after,.mdui-clearfix:before{display:table;content:" "}.mdui-clearfix:after{clear:both}.mdui-hidden,[hidden]{display:none!important}.mdui-invisible{visibility:hidden}@media (max-width:599px){.mdui-hidden-xs{display:none!important}}@media (min-width:600px) and (max-width:1023px){.mdui-hidden-sm{display:none!important}}@media (min-width:1024px) and (max-width:1439px){.mdui-hidden-md{display:none!important}}@media (min-width:1440px) and (max-width:1919px){.mdui-hidden-lg{display:none!important}}@media (min-width:1920px){.mdui-hidden-xl{display:none!important}}@media (max-width:599px){.mdui-hidden-xs-down{display:none!important}}@media (max-width:1023px){.mdui-hidden-sm-down{display:none!important}}@media (max-width:1439px){.mdui-hidden-md-down{display:none!important}}@media (max-width:1919px){.mdui-hidden-lg-down{display:none!important}}.mdui-hidden-xl-down{display:none!important}.mdui-hidden-xs-up{display:none!important}@media (min-width:600px){.mdui-hidden-sm-up{display:none!important}}@media (min-width:1024px){.mdui-hidden-md-up{display:none!important}}@media (min-width:1440px){.mdui-hidden-lg-up{display:none!important}}@media (min-width:1920px){.mdui-hidden-xl-up{display:none!important}}/*! normalize.css v6.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{margin:.67em 0;font-size:2em}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{height:0;box-sizing:content-box;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{text-decoration:underline;text-decoration:underline dotted;border-bottom:none}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{color:#000;background-color:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}legend{display:table;max-width:100%;box-sizing:border-box;padding:0;color:inherit;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{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;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}body{font-family:Roboto,Noto,Helvetica,Arial,sans-serif;font-size:14px;color:rgba(0,0,0,.87);background-color:#fff}@media (min-width:600px){body{font-size:14.5px}}@media (min-width:1024px){body{font-size:15px}}body ::-webkit-scrollbar{width:5px;height:5px;background:0 0}@media (min-width:1024px){body ::-webkit-scrollbar{width:8px;height:8px}}body ::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}body.mdui-theme-layout-dark ::-webkit-scrollbar{width:5px;height:5px;background:0 0}@media (min-width:1024px){body.mdui-theme-layout-dark ::-webkit-scrollbar{width:8px;height:8px}}body.mdui-theme-layout-dark ::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3)}*{-webkit-tap-highlight-color:transparent}body.mdui-locked{overflow:hidden}.mdui-overlay{position:fixed;top:-5000px;right:-5000px;bottom:-5000px;left:-5000px;z-index:2000;visibility:hidden;background:rgba(0,0,0,.4);opacity:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,visibility;transition-property:opacity,visibility;-webkit-backface-visibility:hidden;backface-visibility:hidden;will-change:opacity}.mdui-overlay-show{visibility:visible;opacity:1}.mdui-no-transition{-webkit-transition-property:none!important;transition-property:none!important}.mdui-theme-layout-dark{color:#fff;background-color:#303030}.mdui-icon,.mdui-icon::before{display:inline-block;font-size:24px;font-style:normal;font-weight:400;line-height:1;color:inherit;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;vertical-align:middle;direction:ltr}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local('Material Icons'),local('MaterialIcons-Regular'),url(../icons/material-icons/MaterialIcons-Regular.woff2) format('woff2'),url(../icons/material-icons/MaterialIcons-Regular.woff) format('woff')}.material-icons{font-family:'Material Icons';-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;-webkit-font-feature-settings:'liga';-moz-font-feature-settings:'liga';font-feature-settings:'liga'}@font-face{font-family:Roboto;font-style:normal;font-weight:100;src:local('Roboto Thin'),local('Roboto-Thin'),url(../fonts/roboto/Roboto-Thin.woff2) format('woff2'),url(../fonts/roboto/Roboto-Thin.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:100;src:local('Roboto ThinItalic'),local('Roboto-ThinItalic'),url(../fonts/roboto/Roboto-ThinItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-ThinItalic.woff) format('woff')}@font-face{font-family:Roboto;font-style:normal;font-weight:300;src:local('Roboto Light'),local('Roboto-Light'),url(../fonts/roboto/Roboto-Light.woff2) format('woff2'),url(../fonts/roboto/Roboto-Light.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:300;src:local('Roboto LightItalic'),local('Roboto-LightItalic'),url(../fonts/roboto/Roboto-LightItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-LightItalic.woff) format('woff')}@font-face{font-family:Roboto;font-style:normal;font-weight:400;src:local('Roboto Regular'),local('Roboto-Regular'),url(../fonts/roboto/Roboto-Regular.woff2) format('woff2'),url(../fonts/roboto/Roboto-Regular.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:400;src:local('Roboto RegularItalic'),local('Roboto-RegularItalic'),url(../fonts/roboto/Roboto-RegularItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-RegularItalic.woff) format('woff')}@font-face{font-family:Roboto;font-style:normal;font-weight:500;src:local('Roboto Medium'),local('Roboto-Medium'),url(../fonts/roboto/Roboto-Medium.woff2) format('woff2'),url(../fonts/roboto/Roboto-Medium.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:500;src:local('Roboto MediumItalic'),local('Roboto-MediumItalic'),url(../fonts/roboto/Roboto-MediumItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-MediumItalic.woff) format('woff')}@font-face{font-family:Roboto;font-style:normal;font-weight:700;src:local('Roboto Bold'),local('Roboto-Bold'),url(../fonts/roboto/Roboto-Bold.woff2) format('woff2'),url(../fonts/roboto/Roboto-Bold.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:700;src:local('Roboto BoldItalic'),local('Roboto-BoldItalic'),url(../fonts/roboto/Roboto-BoldItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-BoldItalic.woff) format('woff')}@font-face{font-family:Roboto;font-style:normal;font-weight:900;src:local('Roboto Black'),local('Roboto-Black'),url(../fonts/roboto/Roboto-Black.woff2) format('woff2'),url(../fonts/roboto/Roboto-Black.woff) format('woff')}@font-face{font-family:Roboto;font-style:italic;font-weight:900;src:local('Roboto BlackItalic'),local('Roboto-BlackItalic'),url(../fonts/roboto/Roboto-BlackItalic.woff2) format('woff2'),url(../fonts/roboto/Roboto-BlackItalic.woff) format('woff')}.mdui-typo-display-4,.mdui-typo-display-4-opacity{font-size:112px;font-weight:300;letter-spacing:-.04em}.mdui-typo-display-4-opacity{opacity:.54}.mdui-typo-display-3,.mdui-typo-display-3-opacity{font-size:56px;font-weight:400;letter-spacing:-.02em}.mdui-typo-display-3-opacity{opacity:.54}.mdui-typo-display-2,.mdui-typo-display-2-opacity{font-size:45px;font-weight:400;letter-spacing:0}.mdui-typo-display-2-opacity{opacity:.54}.mdui-typo-display-1,.mdui-typo-display-1-opacity{font-size:34px;font-weight:400;letter-spacing:0}.mdui-typo-display-1-opacity{opacity:.54}.mdui-typo-headline,.mdui-typo-headline-opacity{font-size:24px;font-weight:400;-moz-osx-font-smoothing:grayscale}.mdui-typo-headline-opacity{opacity:.87}.mdui-typo-title,.mdui-typo-title-opacity{font-size:20px;font-weight:500;letter-spacing:.02em}.mdui-typo-title-opacity{opacity:.87}.mdui-typo-subheading,.mdui-typo-subheading-opacity{font-size:16px;font-weight:400;letter-spacing:.04em}.mdui-typo-subheading-opacity{opacity:.87}.mdui-typo-body-2,.mdui-typo-body-2-opacity{font-size:14px;font-weight:500;letter-spacing:.04em}.mdui-typo-body-2-opacity{opacity:.87}.mdui-typo-body-1,.mdui-typo-body-1-opacity{font-size:14px;font-weight:400;letter-spacing:.04em}.mdui-typo-body-1-opacity{opacity:.87}.mdui-typo-caption,.mdui-typo-caption-opacity{font-size:12px;font-weight:400;letter-spacing:.08em}.mdui-typo-caption-opacity{opacity:.54}.mdui-typo{line-height:1.8;word-wrap:break-word}.mdui-typo address,.mdui-typo caption,.mdui-typo cite,.mdui-typo code,.mdui-typo dfn,.mdui-typo th{font-style:normal;font-weight:400}.mdui-typo caption,.mdui-typo th{text-align:left}.mdui-typo q:after,.mdui-typo q:before{content:''}.mdui-typo code,.mdui-typo kbd,.mdui-typo pre,.mdui-typo pre tt,.mdui-typo samp{font-family:Consolas,Courier,'Courier New',monospace}.mdui-typo figcaption{font-size:80%;color:rgba(0,0,0,.54)}.mdui-typo [draggable]{cursor:move}.mdui-typo .mdui-table,.mdui-typo .mdui-table-fluid,.mdui-typo dl,.mdui-typo figure,.mdui-typo form,.mdui-typo hr,.mdui-typo ol,.mdui-typo p,.mdui-typo pre,.mdui-typo table,.mdui-typo ul{margin:0 0 1.2em 0}.mdui-typo a{position:relative;overflow:hidden;color:#ff4081;text-decoration:none;outline:0}.mdui-typo a:before{position:absolute;top:auto;bottom:-2px;left:0;width:100%;height:1px;content:' ';background-color:#ff4081;-webkit-transition:all .2s;transition:all .2s;-webkit-transform:scaleX(0);transform:scaleX(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.mdui-typo a:focus:before,.mdui-typo a:hover:before{-webkit-transform:scaleX(1);transform:scaleX(1)}.mdui-typo small{font-size:80%}.mdui-typo blockquote{padding-left:1em;margin:1em 3em 1em 2em;font-weight:400;border-left:4px solid rgba(0,0,0,.12)}@media only screen and (max-width:599px){.mdui-typo blockquote{margin:1em 0}}.mdui-typo blockquote footer{font-size:86%;color:rgba(0,0,0,.54)}.mdui-typo mark{padding:2px;margin:0 5px;background:#fffdd1;border-bottom:1px solid #ffedce}.mdui-typo h1,.mdui-typo h2,.mdui-typo h3,.mdui-typo h4,.mdui-typo h5,.mdui-typo h6{margin-top:1.2em;margin-bottom:.6em;font-family:inherit;font-weight:400;line-height:1.35;color:inherit}.mdui-typo h1 small,.mdui-typo h2 small,.mdui-typo h3 small,.mdui-typo h4 small,.mdui-typo h5 small,.mdui-typo h6 small{font-size:65%;font-weight:400;line-height:1;color:rgba(0,0,0,.54)}.mdui-typo h1{font-size:2em}.mdui-typo h2{font-size:1.8em}.mdui-typo h3{font-size:1.6em}.mdui-typo h4{font-size:1.4em}.mdui-typo h5{font-size:1.2em}.mdui-typo h6{font-size:1.1em}.mdui-typo code{padding:2px 6px;color:#c7254e;background-color:#f7f7f9;border-radius:2px}.mdui-typo pre code{padding:0;font-size:inherit;line-height:1.7;color:inherit;background-color:transparent;border-radius:0}.mdui-typo abbr[title]{text-decoration:none;cursor:help;border-bottom:1px dotted}.mdui-typo ins{text-decoration:none;border-bottom:1px solid}.mdui-typo u{text-decoration:none;border-bottom:1px solid}.mdui-typo del{text-decoration:line-through}.mdui-typo hr{height:10px;margin-bottom:.8em;border:none;border-bottom:1px solid rgba(0,0,0,.12)}.mdui-typo pre{padding:12px 16px;overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid rgba(0,0,0,.12);border-radius:2px}.mdui-typo kbd{padding:2px 6px;font-size:90%;color:#fff;background-color:#333;border-radius:2px}.mdui-typo ul{padding-left:2em;list-style:disc}.mdui-typo ol{padding-left:2em;list-style:decimal}.mdui-typo li ol,.mdui-typo li ul{margin:.8em 0}.mdui-typo li ul{list-style:circle}.mdui-typo img{max-width:100%}.mdui-theme-accent-amber .mdui-typo a{color:#ffd740}.mdui-theme-accent-amber .mdui-typo a:before{background-color:#ffd740}.mdui-theme-accent-blue .mdui-typo a{color:#448aff}.mdui-theme-accent-blue .mdui-typo a:before{background-color:#448aff}.mdui-theme-accent-cyan .mdui-typo a{color:#18ffff}.mdui-theme-accent-cyan .mdui-typo a:before{background-color:#18ffff}.mdui-theme-accent-deep-orange .mdui-typo a{color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-typo a:before{background-color:#ff6e40}.mdui-theme-accent-deep-purple .mdui-typo a{color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-typo a:before{background-color:#7c4dff}.mdui-theme-accent-green .mdui-typo a{color:#69f0ae}.mdui-theme-accent-green .mdui-typo a:before{background-color:#69f0ae}.mdui-theme-accent-indigo .mdui-typo a{color:#536dfe}.mdui-theme-accent-indigo .mdui-typo a:before{background-color:#536dfe}.mdui-theme-accent-light-blue .mdui-typo a{color:#40c4ff}.mdui-theme-accent-light-blue .mdui-typo a:before{background-color:#40c4ff}.mdui-theme-accent-light-green .mdui-typo a{color:#b2ff59}.mdui-theme-accent-light-green .mdui-typo a:before{background-color:#b2ff59}.mdui-theme-accent-lime .mdui-typo a{color:#eeff41}.mdui-theme-accent-lime .mdui-typo a:before{background-color:#eeff41}.mdui-theme-accent-orange .mdui-typo a{color:#ffab40}.mdui-theme-accent-orange .mdui-typo a:before{background-color:#ffab40}.mdui-theme-accent-pink .mdui-typo a{color:#ff4081}.mdui-theme-accent-pink .mdui-typo a:before{background-color:#ff4081}.mdui-theme-accent-purple .mdui-typo a{color:#e040fb}.mdui-theme-accent-purple .mdui-typo a:before{background-color:#e040fb}.mdui-theme-accent-red .mdui-typo a{color:#ff5252}.mdui-theme-accent-red .mdui-typo a:before{background-color:#ff5252}.mdui-theme-accent-teal .mdui-typo a{color:#64ffda}.mdui-theme-accent-teal .mdui-typo a:before{background-color:#64ffda}.mdui-theme-accent-yellow .mdui-typo a{color:#ff0}.mdui-theme-accent-yellow .mdui-typo a:before{background-color:#ff0}.mdui-theme-layout-dark .mdui-typo blockquote{border-left-color:rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-typo blockquote footer{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-typo figcaption{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-typo mark{background:#aaa;border-bottom-color:#bbb}.mdui-theme-layout-dark .mdui-typo h1 small,.mdui-theme-layout-dark .mdui-typo h2 small,.mdui-theme-layout-dark .mdui-typo h3 small,.mdui-theme-layout-dark .mdui-typo h4 small,.mdui-theme-layout-dark .mdui-typo h5 small,.mdui-theme-layout-dark .mdui-typo h6 small{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-typo code{color:#ffcdd2;background-color:#424242}.mdui-theme-layout-dark .mdui-typo pre{background:#424242;border-color:rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-typo kbd{background:#424242}.mdui-theme-layout-dark .mdui-typo hr{border-color:rgba(255,255,255,.12)}.mdui-headroom{-webkit-transition:all .3s cubic-bezier(.4,0,.2,1)!important;transition:all .3s cubic-bezier(.4,0,.2,1)!important}.mdui-headroom-pinned-top{-webkit-transform:translate3d(0,0,0)!important;transform:translate3d(0,0,0)!important}.mdui-headroom-unpinned-top{box-shadow:none!important;-webkit-transform:translate3d(0,-100%,0)!important;transform:translate3d(0,-100%,0)!important}.mdui-headroom-pinned-down{-webkit-transform:translate3d(0,0,0)!important;transform:translate3d(0,0,0)!important}.mdui-headroom-unpinned-down{box-shadow:none!important;-webkit-transform:translate3d(0,100%,0)!important;transform:translate3d(0,100%,0)!important}.mdui-headroom-pinned-toolbar{-webkit-transform:translate3d(0,0,0)!important;transform:translate3d(0,0,0)!important}.mdui-headroom-unpinned-toolbar{-webkit-transform:translate3d(0,-56px,0)!important;transform:translate3d(0,-56px,0)!important}@media (min-width:600px){.mdui-headroom-unpinned-toolbar{-webkit-transform:translate3d(0,-60px,0)!important;transform:translate3d(0,-60px,0)!important}}@media (min-width:1024px){.mdui-headroom-unpinned-toolbar{-webkit-transform:translate3d(0,-64px,0)!important;transform:translate3d(0,-64px,0)!important}}.mdui-collapse-item-header .mdui-collapse-item-arrow,.mdui-collapse-item-header.mdui-collapse-item-arrow{-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1);transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),-webkit-transform .3s cubic-bezier(.4,0,.2,1);-webkit-transform:rotate(0);transform:rotate(0);will-change:transform}.mdui-collapse-item-body{height:0;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1);will-change:height}.mdui-collapse-item-body .mdui-list-item{padding-left:72px}.mdui-collapse-item-open>.mdui-collapse-item-header .mdui-collapse-item-arrow,.mdui-collapse-item-open>.mdui-collapse-item-header.mdui-collapse-item-arrow{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.mdui-collapse-item-open>.mdui-collapse-item-body{height:auto}.mdui-table{position:relative;width:100%;border-spacing:0;border-collapse:separate;background-color:#fff;border:1px solid rgba(0,0,0,.12);border-top:none;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-table tbody tr{position:relative;-webkit-transition:background-color .28s cubic-bezier(.4,0,.2,1);transition:background-color .28s cubic-bezier(.4,0,.2,1)}.mdui-table td,.mdui-table th{position:relative;box-sizing:border-box;padding:12px 28px;text-align:left;vertical-align:middle;border-top:1px solid rgba(0,0,0,.12)}.mdui-table td:last-child,.mdui-table th:last-child{padding-right:24px}.mdui-table td:first-child,.mdui-table th:first-child{padding-right:0;padding-left:24px}.mdui-table td:nth-child(2),.mdui-table th:nth-child(2){padding-left:24px}.mdui-table th{overflow:hidden;font-size:13px;font-weight:700;line-height:32px;color:rgba(0,0,0,.54);text-overflow:ellipsis;white-space:nowrap}.mdui-table td{font-size:14px;line-height:24px;color:rgba(0,0,0,.87)}.mdui-table-cell-checkbox{padding-top:0!important;padding-bottom:0!important;padding-left:24px!important}.mdui-table-cell-checkbox .mdui-checkbox{margin-top:7px}.mdui-table-cell-checkbox+td,.mdui-table-cell-checkbox+th{padding-left:6px!important}th.mdui-table-cell-checkbox .mdui-checkbox{margin-top:11px}.mdui-table-hoverable tbody tr:hover{background-color:#eee}.mdui-table-fluid{width:100%;overflow-x:auto;border:1px solid rgba(0,0,0,.12);border-top:none;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-table-fluid .mdui-table{margin:0;border:none;box-shadow:none}.mdui-table-col-numeric{text-align:right!important}.mdui-table-row-selected{background-color:#f5f5f5}.mdui-theme-layout-dark .mdui-table{background-color:#303030;border:1px solid rgba(255,255,255,.12);border-top:none}.mdui-theme-layout-dark .mdui-table td,.mdui-theme-layout-dark .mdui-table th{border-bottom:1px solid rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-table th{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-table td{color:#fff}.mdui-theme-layout-dark .mdui-table-hoverable tbody tr:hover{background-color:#616161}.mdui-theme-layout-dark .mdui-table-fluid{border:1px solid rgba(255,255,255,.12);border-top:none}.mdui-theme-layout-dark .mdui-table-fluid .mdui-table{border:none;box-shadow:none}.mdui-theme-layout-dark .mdui-table-row-selected{background-color:#424242}.mdui-divider,.mdui-divider-dark,.mdui-divider-inset,.mdui-divider-inset-dark,.mdui-divider-inset-light,.mdui-divider-light{height:1px;margin:-1px 0 0 0;border:none}.mdui-divider-inset,.mdui-divider-inset-dark,.mdui-divider-inset-light{margin-left:72px}.mdui-divider,.mdui-divider-inset{background-color:rgba(0,0,0,.12)}.mdui-theme-layout-dark .mdui-divider,.mdui-theme-layout-dark .mdui-divider-inset{background-color:rgba(255,255,255,.12)}.mdui-divider-inset-light,.mdui-divider-light{background-color:rgba(255,255,255,.12)}.mdui-divider-dark,.mdui-divider-inset-dark{background-color:rgba(0,0,0,.12)}.mdui-img-fluid,.mdui-video-fluid{display:block;max-width:100%;height:auto}.mdui-img-rounded{border-radius:2px}.mdui-img-circle{border-radius:50%}.mdui-video-container{position:relative;height:0;padding-bottom:56.25%;overflow:hidden}.mdui-video-container embed,.mdui-video-container iframe,.mdui-video-container object{position:absolute;top:0;left:0;width:100%;height:100%}.mdui-ripple{position:relative;overflow:hidden;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-ripple-wave{position:absolute!important;top:0;left:0;z-index:1;padding:0;margin:0;font-size:0;pointer-events:none;background-color:rgba(0,0,0,.1);border-radius:50%;-webkit-transition-duration:1.4s;transition-duration:1.4s;-webkit-transform:translate3d(0,0,0) scale(0);transform:translate3d(0,0,0) scale(0)}.mdui-ripple[class*=mdui-color-] .mdui-ripple-wave{background-color:rgba(255,255,255,.3)}.mdui-ripple-white .mdui-ripple-wave{background-color:rgba(255,255,255,.3)!important}.mdui-ripple-black .mdui-ripple-wave{background-color:rgba(0,0,0,.1)!important}.mdui-ripple-wave-fill{opacity:.35;-webkit-transition-duration:.3s;transition-duration:.3s}.mdui-ripple-wave-out{opacity:0;-webkit-transition-duration:.6s;transition-duration:.6s}.mdui-ripple-amber .mdui-ripple-wave{background-color:rgba(255,193,7,.3)!important}.mdui-ripple-blue .mdui-ripple-wave{background-color:rgba(33,150,243,.3)!important}.mdui-ripple-blue-grey .mdui-ripple-wave{background-color:rgba(96,125,139,.3)!important}.mdui-ripple-brown .mdui-ripple-wave{background-color:rgba(121,85,72,.3)!important}.mdui-ripple-cyan .mdui-ripple-wave{background-color:rgba(0,188,212,.3)!important}.mdui-ripple-deep-orange .mdui-ripple-wave{background-color:rgba(255,87,34,.3)!important}.mdui-ripple-deep-purple .mdui-ripple-wave{background-color:rgba(103,58,183,.3)!important}.mdui-ripple-green .mdui-ripple-wave{background-color:rgba(76,175,80,.3)!important}.mdui-ripple-grey .mdui-ripple-wave{background-color:rgba(158,158,158,.3)!important}.mdui-ripple-indigo .mdui-ripple-wave{background-color:rgba(63,81,181,.3)!important}.mdui-ripple-light-blue .mdui-ripple-wave{background-color:rgba(3,169,244,.3)!important}.mdui-ripple-light-green .mdui-ripple-wave{background-color:rgba(139,195,74,.3)!important}.mdui-ripple-lime .mdui-ripple-wave{background-color:rgba(205,220,57,.3)!important}.mdui-ripple-orange .mdui-ripple-wave{background-color:rgba(255,152,0,.3)!important}.mdui-ripple-pink .mdui-ripple-wave{background-color:rgba(233,30,99,.3)!important}.mdui-ripple-purple .mdui-ripple-wave{background-color:rgba(156,39,176,.3)!important}.mdui-ripple-red .mdui-ripple-wave{background-color:rgba(244,67,54,.3)!important}.mdui-ripple-teal .mdui-ripple-wave{background-color:rgba(0,150,136,.3)!important}.mdui-ripple-yellow .mdui-ripple-wave{background-color:rgba(255,235,59,.3)!important}.mdui-theme-layout-dark .mdui-ripple-wave{background-color:rgba(255,255,255,.3)}.mdui-textfield{position:relative;padding-top:16px;padding-bottom:8px;overflow:hidden}.mdui-textfield-input{display:block;width:100%;height:36px;box-sizing:border-box;-webkit-appearance:none;padding:8px 0 8px 0;margin:0;font-family:inherit;font-size:16px;line-height:20px;color:inherit;resize:none;background:0 0;border:none;border-bottom:1px solid rgba(0,0,0,.12);border-radius:0;outline:0;box-shadow:none;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:border-bottom-color,padding-right;transition-property:border-bottom-color,padding-right;-moz-appearance:none;appearance:none}.mdui-textfield-input::-webkit-input-placeholder{color:inherit;opacity:.35}.mdui-textfield-input[disabled]{color:rgba(0,0,0,.38)}.mdui-textfield-label{display:block;width:100%;font-size:16px;color:rgba(0,0,0,.65);pointer-events:none;-webkit-transition:all .2s;transition:all .2s;-webkit-transform:scale(.75) translateY(0);transform:scale(.75) translateY(0);-webkit-transform-origin:left;transform-origin:left}.mdui-textfield-error{height:12px;margin-top:8px;overflow:hidden;font-size:12px;line-height:12px;color:#f44336;text-overflow:ellipsis;white-space:nowrap;visibility:hidden}textarea.mdui-textfield-input{min-height:57px}.mdui-textfield .mdui-icon{position:absolute;bottom:8px;padding:6px;color:rgba(0,0,0,.54)}.mdui-textfield .mdui-icon~.mdui-textfield-error,.mdui-textfield .mdui-icon~.mdui-textfield-flex-wrap,.mdui-textfield .mdui-icon~.mdui-textfield-input,.mdui-textfield .mdui-icon~.mdui-textfield-label{width:calc(100% - 56px);margin-left:56px}.mdui-textfield-focus .mdui-textfield-input{border-bottom:1px solid #ff4081;box-shadow:0 1px 0 0 #ff4081}.mdui-textfield-focus .mdui-textfield-label{color:#ff4081}.mdui-textfield-focus .mdui-icon{color:#ff4081}.mdui-textfield-floating-label .mdui-textfield-label{color:rgba(0,0,0,.35);-webkit-transform:scale(1) translateY(27px);transform:scale(1) translateY(27px)}.mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label,.mdui-textfield-floating-label.mdui-textfield-not-empty .mdui-textfield-label{color:rgba(0,0,0,.65);-webkit-transform:scale(.75) translateY(0);transform:scale(.75) translateY(0)}.mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#e91e63}.mdui-textfield-expandable{width:36px;min-height:36px;padding:1px 0;-webkit-transition:width .3s cubic-bezier(.4,0,.2,1);transition:width .3s cubic-bezier(.4,0,.2,1)}.mdui-textfield-expandable .mdui-icon{bottom:0;padding:0}.mdui-textfield-expandable .mdui-textfield-input{width:calc(100% - 36px);padding-right:0;margin-left:36px}.mdui-textfield-expandable .mdui-textfield-icon{position:absolute;top:0;left:0}.mdui-textfield-expandable .mdui-textfield-close{position:absolute;top:0;right:0;-webkit-transform:scale(0);transform:scale(0)}.mdui-textfield-expandable.mdui-textfield-expanded{width:100%}.mdui-textfield-expandable.mdui-textfield-expanded .mdui-textfield-input{padding-right:36px}.mdui-textfield-expandable.mdui-textfield-expanded .mdui-textfield-close{-webkit-transform:scale(1);transform:scale(1)}.mdui-textfield-invalid .mdui-textfield-input,.mdui-textfield-invalid-html5 .mdui-textfield-input{border-bottom:1px solid #f44336!important;box-shadow:0 1px 0 0 #f44336!important}.mdui-textfield-invalid-html5.mdui-textfield-focus .mdui-textfield-input,.mdui-textfield-invalid.mdui-textfield-focus .mdui-textfield-input{border-bottom:1px solid #f44336!important;box-shadow:0 1px 0 0 #f44336!important}.mdui-textfield-invalid .mdui-textfield-label,.mdui-textfield-invalid-html5 .mdui-textfield-label{color:#f44336!important}.mdui-textfield-invalid-html5.mdui-textfield-floating-label .mdui-textfield-label,.mdui-textfield-invalid.mdui-textfield-floating-label .mdui-textfield-label{color:rgba(244,67,54,.35)!important}.mdui-textfield-invalid-html5.mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label,.mdui-textfield-invalid-html5.mdui-textfield-floating-label.mdui-textfield-not-empty .mdui-textfield-label,.mdui-textfield-invalid.mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label,.mdui-textfield-invalid.mdui-textfield-floating-label.mdui-textfield-not-empty .mdui-textfield-label{color:#f44336!important}.mdui-textfield-invalid .mdui-textfield-error,.mdui-textfield-invalid-html5 .mdui-textfield-error{visibility:visible}.mdui-textfield-disabled .mdui-textfield-input,.mdui-textfield-disabled .mdui-textfield-input::-webkit-input-placeholder,.mdui-textfield-disabled .mdui-textfield-label{color:rgba(0,0,0,.38)}.mdui-textfield-disabled .mdui-textfield-input{border-bottom:1px dotted rgba(0,0,0,.38)}.mdui-textfield-counter{position:absolute;right:8px;bottom:8px;height:12px;font-size:12px;line-height:12px;color:rgba(0,0,0,.54)}.mdui-textfield-has-counter{padding-bottom:28px}.mdui-theme-accent-amber .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ffd740;box-shadow:0 1px 0 0 #ffd740}.mdui-theme-accent-amber .mdui-textfield-focus .mdui-textfield-label{color:#ffd740}.mdui-theme-accent-amber .mdui-textfield-focus .mdui-icon{color:#ffd740!important}.mdui-theme-accent-amber .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ffd740}.mdui-theme-accent-blue .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#448aff;box-shadow:0 1px 0 0 #448aff}.mdui-theme-accent-blue .mdui-textfield-focus .mdui-textfield-label{color:#448aff}.mdui-theme-accent-blue .mdui-textfield-focus .mdui-icon{color:#448aff!important}.mdui-theme-accent-blue .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#448aff}.mdui-theme-accent-cyan .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#18ffff;box-shadow:0 1px 0 0 #18ffff}.mdui-theme-accent-cyan .mdui-textfield-focus .mdui-textfield-label{color:#18ffff}.mdui-theme-accent-cyan .mdui-textfield-focus .mdui-icon{color:#18ffff!important}.mdui-theme-accent-cyan .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#18ffff}.mdui-theme-accent-deep-orange .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ff6e40;box-shadow:0 1px 0 0 #ff6e40}.mdui-theme-accent-deep-orange .mdui-textfield-focus .mdui-textfield-label{color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-textfield-focus .mdui-icon{color:#ff6e40!important}.mdui-theme-accent-deep-orange .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ff6e40}.mdui-theme-accent-deep-purple .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#7c4dff;box-shadow:0 1px 0 0 #7c4dff}.mdui-theme-accent-deep-purple .mdui-textfield-focus .mdui-textfield-label{color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-textfield-focus .mdui-icon{color:#7c4dff!important}.mdui-theme-accent-deep-purple .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#7c4dff}.mdui-theme-accent-green .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#69f0ae;box-shadow:0 1px 0 0 #69f0ae}.mdui-theme-accent-green .mdui-textfield-focus .mdui-textfield-label{color:#69f0ae}.mdui-theme-accent-green .mdui-textfield-focus .mdui-icon{color:#69f0ae!important}.mdui-theme-accent-green .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#69f0ae}.mdui-theme-accent-indigo .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#536dfe;box-shadow:0 1px 0 0 #536dfe}.mdui-theme-accent-indigo .mdui-textfield-focus .mdui-textfield-label{color:#536dfe}.mdui-theme-accent-indigo .mdui-textfield-focus .mdui-icon{color:#536dfe!important}.mdui-theme-accent-indigo .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#536dfe}.mdui-theme-accent-light-blue .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#40c4ff;box-shadow:0 1px 0 0 #40c4ff}.mdui-theme-accent-light-blue .mdui-textfield-focus .mdui-textfield-label{color:#40c4ff}.mdui-theme-accent-light-blue .mdui-textfield-focus .mdui-icon{color:#40c4ff!important}.mdui-theme-accent-light-blue .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#40c4ff}.mdui-theme-accent-light-green .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#b2ff59;box-shadow:0 1px 0 0 #b2ff59}.mdui-theme-accent-light-green .mdui-textfield-focus .mdui-textfield-label{color:#b2ff59}.mdui-theme-accent-light-green .mdui-textfield-focus .mdui-icon{color:#b2ff59!important}.mdui-theme-accent-light-green .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#b2ff59}.mdui-theme-accent-lime .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#eeff41;box-shadow:0 1px 0 0 #eeff41}.mdui-theme-accent-lime .mdui-textfield-focus .mdui-textfield-label{color:#eeff41}.mdui-theme-accent-lime .mdui-textfield-focus .mdui-icon{color:#eeff41!important}.mdui-theme-accent-lime .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#eeff41}.mdui-theme-accent-orange .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ffab40;box-shadow:0 1px 0 0 #ffab40}.mdui-theme-accent-orange .mdui-textfield-focus .mdui-textfield-label{color:#ffab40}.mdui-theme-accent-orange .mdui-textfield-focus .mdui-icon{color:#ffab40!important}.mdui-theme-accent-orange .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ffab40}.mdui-theme-accent-pink .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ff4081;box-shadow:0 1px 0 0 #ff4081}.mdui-theme-accent-pink .mdui-textfield-focus .mdui-textfield-label{color:#ff4081}.mdui-theme-accent-pink .mdui-textfield-focus .mdui-icon{color:#ff4081!important}.mdui-theme-accent-pink .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ff4081}.mdui-theme-accent-purple .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#e040fb;box-shadow:0 1px 0 0 #e040fb}.mdui-theme-accent-purple .mdui-textfield-focus .mdui-textfield-label{color:#e040fb}.mdui-theme-accent-purple .mdui-textfield-focus .mdui-icon{color:#e040fb!important}.mdui-theme-accent-purple .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#e040fb}.mdui-theme-accent-red .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ff5252;box-shadow:0 1px 0 0 #ff5252}.mdui-theme-accent-red .mdui-textfield-focus .mdui-textfield-label{color:#ff5252}.mdui-theme-accent-red .mdui-textfield-focus .mdui-icon{color:#ff5252!important}.mdui-theme-accent-red .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ff5252}.mdui-theme-accent-teal .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#64ffda;box-shadow:0 1px 0 0 #64ffda}.mdui-theme-accent-teal .mdui-textfield-focus .mdui-textfield-label{color:#64ffda}.mdui-theme-accent-teal .mdui-textfield-focus .mdui-icon{color:#64ffda!important}.mdui-theme-accent-teal .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#64ffda}.mdui-theme-accent-yellow .mdui-textfield-focus .mdui-textfield-input{border-bottom-color:#ff0;box-shadow:0 1px 0 0 #ff0}.mdui-theme-accent-yellow .mdui-textfield-focus .mdui-textfield-label{color:#ff0}.mdui-theme-accent-yellow .mdui-textfield-focus .mdui-icon{color:#ff0!important}.mdui-theme-accent-yellow .mdui-textfield-floating-label.mdui-textfield-focus .mdui-textfield-label{color:#ff0}.mdui-theme-layout-dark .mdui-textfield .mdui-icon{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-textfield-input{border-bottom:1px solid rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-textfield-input::-webkit-input-placeholder{color:rgba(255,255,255,.35)}.mdui-theme-layout-dark .mdui-textfield-input[disabled]{color:rgba(255,255,255,.5)}.mdui-theme-layout-dark .mdui-textfield-label{color:rgba(255,255,255,.65)}.mdui-theme-layout-dark .mdui-textfield-floating-label .mdui-textfield-label{color:rgba(255,255,255,.35)}.mdui-theme-layout-dark .mdui-textfield-counter{color:rgba(255,255,255,.7)}.mdui-checkbox{position:relative;display:inline-block;height:36px;padding-left:36px;line-height:36px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-checkbox input{position:absolute;width:0;height:0;overflow:hidden;visibility:hidden}.mdui-checkbox-icon{position:absolute;top:9px;left:0;display:inline-block;width:18px;height:18px;vertical-align:middle;background-color:transparent;border:none;border-radius:18px;-webkit-transition:box-shadow .14s cubic-bezier(.4,0,.2,1);transition:box-shadow .14s cubic-bezier(.4,0,.2,1)}.mdui-checkbox-icon:after{position:absolute;top:0;left:0;z-index:0;width:18px;height:18px;box-sizing:border-box;content:' ';border:2px solid rgba(0,0,0,.54);border-radius:2px;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1)}.mdui-checkbox-icon:before{position:absolute;top:2px;left:0;z-index:1;width:8px;height:13px;box-sizing:border-box;content:' ';border-right:2px solid #fff;border-bottom:2px solid #fff;opacity:0;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-transform:rotateZ(37deg) scale(0);transform:rotateZ(37deg) scale(0);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ff4081;border-color:#ff4081}.mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:before{opacity:1;-webkit-transform:rotateZ(37deg) scale(1);transform:rotateZ(37deg) scale(1)}.mdui-checkbox input[type=checkbox]:disabled+.mdui-checkbox-icon:after{border-color:rgba(0,0,0,.26)}.mdui-checkbox input[type=checkbox]:disabled:checked+.mdui-checkbox-icon:after{background-color:rgba(0,0,0,.26)!important;border-color:transparent!important}.mdui-checkbox:active input[type=checkbox]+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(0,0,0,.1)}.mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,64,129,.16)}.mdui-theme-accent-amber .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ffd740;border-color:#ffd740}.mdui-theme-accent-amber .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,215,64,.16)}.mdui-theme-accent-blue .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#448aff;border-color:#448aff}.mdui-theme-accent-blue .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(68,138,255,.16)}.mdui-theme-accent-cyan .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#18ffff;border-color:#18ffff}.mdui-theme-accent-cyan .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(24,255,255,.16)}.mdui-theme-accent-deep-orange .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ff6e40;border-color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,110,64,.16)}.mdui-theme-accent-deep-purple .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#7c4dff;border-color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(124,77,255,.16)}.mdui-theme-accent-green .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#69f0ae;border-color:#69f0ae}.mdui-theme-accent-green .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(105,240,174,.16)}.mdui-theme-accent-indigo .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#536dfe;border-color:#536dfe}.mdui-theme-accent-indigo .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(83,109,254,.16)}.mdui-theme-accent-light-blue .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#40c4ff;border-color:#40c4ff}.mdui-theme-accent-light-blue .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(64,196,255,.16)}.mdui-theme-accent-light-green .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#b2ff59;border-color:#b2ff59}.mdui-theme-accent-light-green .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(178,255,89,.16)}.mdui-theme-accent-lime .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#eeff41;border-color:#eeff41}.mdui-theme-accent-lime .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(238,255,65,.16)}.mdui-theme-accent-orange .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ffab40;border-color:#ffab40}.mdui-theme-accent-orange .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,171,64,.16)}.mdui-theme-accent-pink .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ff4081;border-color:#ff4081}.mdui-theme-accent-pink .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,64,129,.16)}.mdui-theme-accent-purple .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#e040fb;border-color:#e040fb}.mdui-theme-accent-purple .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(224,64,251,.16)}.mdui-theme-accent-red .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ff5252;border-color:#ff5252}.mdui-theme-accent-red .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,82,82,.16)}.mdui-theme-accent-teal .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#64ffda;border-color:#64ffda}.mdui-theme-accent-teal .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(100,255,218,.16)}.mdui-theme-accent-yellow .mdui-checkbox input[type=checkbox]:checked+.mdui-checkbox-icon:after{background-color:#ff0;border-color:#ff0}.mdui-theme-accent-yellow .mdui-checkbox:active input[type=checkbox]:checked:not(:disabled)+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,255,0,.16)}.mdui-theme-layout-dark .mdui-checkbox-icon:after{border-color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-checkbox-icon:before{border-right-color:#303030;border-bottom-color:#303030}.mdui-theme-layout-dark .mdui-checkbox input[type=checkbox]:disabled+.mdui-checkbox-icon:after{border-color:rgba(255,255,255,.3)}.mdui-theme-layout-dark .mdui-checkbox input[type=checkbox]:disabled:checked+.mdui-checkbox-icon:after{background-color:rgba(255,255,255,.3)!important;border-color:transparent!important}.mdui-theme-layout-dark .mdui-checkbox:active input[type=checkbox]+.mdui-checkbox-icon{box-shadow:0 0 0 15px rgba(255,255,255,.1)}.mdui-radio{position:relative;display:inline-block;height:36px;padding-left:36px;line-height:36px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-radio input{position:absolute;width:0;height:0;overflow:hidden;visibility:hidden}.mdui-radio-icon{position:absolute;top:9px;left:0;display:inline-block;width:18px;height:18px;box-sizing:border-box;vertical-align:middle;border:2px solid rgba(0,0,0,.54);border-radius:18px;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1),box-shadow .14s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1),box-shadow .14s cubic-bezier(.4,0,.2,1)}.mdui-radio-icon:before{position:absolute;top:0;left:0;width:14px;height:14px;content:' ';background-color:#ff4081;border-radius:14px;opacity:0;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-transform:scale(0);transform:scale(0)}.mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ff4081}.mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{opacity:1;-webkit-transform:scale(.68);transform:scale(.68)}.mdui-radio input[type=radio]:disabled+.mdui-radio-icon{border-color:rgba(0,0,0,.26)!important}.mdui-radio input[type=radio]:disabled:checked+.mdui-radio-icon:before{background-color:rgba(0,0,0,.26)!important}.mdui-radio:active input[type=radio]+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(0,0,0,.1)}.mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,64,129,.16)}.mdui-theme-accent-amber .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ffd740}.mdui-theme-accent-amber .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ffd740}.mdui-theme-accent-amber .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,215,64,.16)}.mdui-theme-accent-blue .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#448aff}.mdui-theme-accent-blue .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#448aff}.mdui-theme-accent-blue .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(68,138,255,.16)}.mdui-theme-accent-cyan .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#18ffff}.mdui-theme-accent-cyan .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#18ffff}.mdui-theme-accent-cyan .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(24,255,255,.16)}.mdui-theme-accent-deep-orange .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,110,64,.16)}.mdui-theme-accent-deep-purple .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(124,77,255,.16)}.mdui-theme-accent-green .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#69f0ae}.mdui-theme-accent-green .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#69f0ae}.mdui-theme-accent-green .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(105,240,174,.16)}.mdui-theme-accent-indigo .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#536dfe}.mdui-theme-accent-indigo .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#536dfe}.mdui-theme-accent-indigo .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(83,109,254,.16)}.mdui-theme-accent-light-blue .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#40c4ff}.mdui-theme-accent-light-blue .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#40c4ff}.mdui-theme-accent-light-blue .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(64,196,255,.16)}.mdui-theme-accent-light-green .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#b2ff59}.mdui-theme-accent-light-green .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#b2ff59}.mdui-theme-accent-light-green .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(178,255,89,.16)}.mdui-theme-accent-lime .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#eeff41}.mdui-theme-accent-lime .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#eeff41}.mdui-theme-accent-lime .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(238,255,65,.16)}.mdui-theme-accent-orange .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ffab40}.mdui-theme-accent-orange .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ffab40}.mdui-theme-accent-orange .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,171,64,.16)}.mdui-theme-accent-pink .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ff4081}.mdui-theme-accent-pink .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ff4081}.mdui-theme-accent-pink .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,64,129,.16)}.mdui-theme-accent-purple .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#e040fb}.mdui-theme-accent-purple .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#e040fb}.mdui-theme-accent-purple .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(224,64,251,.16)}.mdui-theme-accent-red .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ff5252}.mdui-theme-accent-red .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ff5252}.mdui-theme-accent-red .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,82,82,.16)}.mdui-theme-accent-teal .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#64ffda}.mdui-theme-accent-teal .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#64ffda}.mdui-theme-accent-teal .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(100,255,218,.16)}.mdui-theme-accent-yellow .mdui-radio input[type=radio]:checked+.mdui-radio-icon{border-color:#ff0}.mdui-theme-accent-yellow .mdui-radio input[type=radio]:checked+.mdui-radio-icon:before{background-color:#ff0}.mdui-theme-accent-yellow .mdui-radio:active input[type=radio]:checked:not(:disabled)+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,255,0,.16)}.mdui-theme-layout-dark .mdui-radio-icon{border-color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-radio input[type=radio]:disabled+.mdui-radio-icon{border-color:rgba(255,255,255,.3)!important}.mdui-theme-layout-dark .mdui-radio input[type=radio]:disabled:checked+.mdui-radio-icon:before{background-color:rgba(255,255,255,.3)!important}.mdui-theme-layout-dark .mdui-radio:active input[type=radio]+.mdui-radio-icon{box-shadow:0 0 0 15px rgba(255,255,255,.1)}.mdui-switch{display:inline-block;height:36px;line-height:36px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-switch input{position:absolute;width:0;height:0;overflow:hidden;visibility:hidden}.mdui-switch-icon{position:relative;display:inline-block;width:36px;height:14px;vertical-align:middle;background-color:rgba(0,0,0,.38);border-radius:14px;-webkit-transition:background-color .3s cubic-bezier(.4,0,.2,1);transition:background-color .3s cubic-bezier(.4,0,.2,1)}.mdui-switch-icon:before{position:absolute;top:-3px;left:-3px;display:inline-block;width:20px;height:20px;content:' ';background-color:#fafafa;border-radius:20px;box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12);-webkit-transition:box-shadow .14s cubic-bezier(.4,0,.2,1),background-color .3s cubic-bezier(.4,0,.2,1),left .3s cubic-bezier(.4,0,.2,1);transition:box-shadow .14s cubic-bezier(.4,0,.2,1),background-color .3s cubic-bezier(.4,0,.2,1),left .3s cubic-bezier(.4,0,.2,1)}.mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,64,129,.5)}.mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{left:20px;background-color:#ff4081}.mdui-switch:active input[type=checkbox]+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(0,0,0,.1)}.mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,64,129,.16)}.mdui-switch input[type=checkbox]:disabled+.mdui-switch-icon{background-color:rgba(0,0,0,.12)!important}.mdui-switch input[type=checkbox]:disabled+.mdui-switch-icon:before{background-color:#bdbdbd!important}.mdui-theme-accent-amber .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,215,64,.5)}.mdui-theme-accent-amber .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ffd740}.mdui-theme-accent-amber .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,215,64,.16)}.mdui-theme-accent-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(68,138,255,.5)}.mdui-theme-accent-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#448aff}.mdui-theme-accent-blue .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(68,138,255,.16)}.mdui-theme-accent-cyan .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(24,255,255,.5)}.mdui-theme-accent-cyan .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#18ffff}.mdui-theme-accent-cyan .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(24,255,255,.16)}.mdui-theme-accent-deep-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,110,64,.5)}.mdui-theme-accent-deep-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,110,64,.16)}.mdui-theme-accent-deep-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(124,77,255,.5)}.mdui-theme-accent-deep-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(124,77,255,.16)}.mdui-theme-accent-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(105,240,174,.5)}.mdui-theme-accent-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#69f0ae}.mdui-theme-accent-green .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(105,240,174,.16)}.mdui-theme-accent-indigo .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(83,109,254,.5)}.mdui-theme-accent-indigo .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#536dfe}.mdui-theme-accent-indigo .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(83,109,254,.16)}.mdui-theme-accent-light-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(64,196,255,.5)}.mdui-theme-accent-light-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#40c4ff}.mdui-theme-accent-light-blue .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(64,196,255,.16)}.mdui-theme-accent-light-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(178,255,89,.5)}.mdui-theme-accent-light-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#b2ff59}.mdui-theme-accent-light-green .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(178,255,89,.16)}.mdui-theme-accent-lime .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(238,255,65,.5)}.mdui-theme-accent-lime .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#eeff41}.mdui-theme-accent-lime .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(238,255,65,.16)}.mdui-theme-accent-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,171,64,.5)}.mdui-theme-accent-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ffab40}.mdui-theme-accent-orange .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,171,64,.16)}.mdui-theme-accent-pink .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,64,129,.5)}.mdui-theme-accent-pink .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff4081}.mdui-theme-accent-pink .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,64,129,.16)}.mdui-theme-accent-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(224,64,251,.5)}.mdui-theme-accent-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#e040fb}.mdui-theme-accent-purple .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(224,64,251,.16)}.mdui-theme-accent-red .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,82,82,.5)}.mdui-theme-accent-red .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff5252}.mdui-theme-accent-red .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,82,82,.16)}.mdui-theme-accent-teal .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(100,255,218,.5)}.mdui-theme-accent-teal .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#64ffda}.mdui-theme-accent-teal .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(100,255,218,.16)}.mdui-theme-accent-yellow .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,255,0,.5)}.mdui-theme-accent-yellow .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff0}.mdui-theme-accent-yellow .mdui-switch:active input[type=checkbox]:checked:not(:disabled)+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,255,0,.16)}.mdui-theme-layout-dark .mdui-switch-icon{background-color:rgba(255,255,255,.3)}.mdui-theme-layout-dark .mdui-switch-icon:before{background-color:#bdbdbd}.mdui-theme-layout-dark.mdui-theme-accent-amber .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,229,127,.5)}.mdui-theme-layout-dark.mdui-theme-accent-amber .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ffe57f}.mdui-theme-layout-dark.mdui-theme-accent-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(130,177,255,.5)}.mdui-theme-layout-dark.mdui-theme-accent-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#82b1ff}.mdui-theme-layout-dark.mdui-theme-accent-cyan .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(132,255,255,.5)}.mdui-theme-layout-dark.mdui-theme-accent-cyan .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#84ffff}.mdui-theme-layout-dark.mdui-theme-accent-deep-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,158,128,.5)}.mdui-theme-layout-dark.mdui-theme-accent-deep-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff9e80}.mdui-theme-layout-dark.mdui-theme-accent-deep-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(179,136,255,.5)}.mdui-theme-layout-dark.mdui-theme-accent-deep-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#b388ff}.mdui-theme-layout-dark.mdui-theme-accent-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(185,246,202,.5)}.mdui-theme-layout-dark.mdui-theme-accent-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#b9f6ca}.mdui-theme-layout-dark.mdui-theme-accent-indigo .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(140,158,255,.5)}.mdui-theme-layout-dark.mdui-theme-accent-indigo .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#8c9eff}.mdui-theme-layout-dark.mdui-theme-accent-light-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(128,216,255,.5)}.mdui-theme-layout-dark.mdui-theme-accent-light-blue .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#80d8ff}.mdui-theme-layout-dark.mdui-theme-accent-light-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(204,255,144,.5)}.mdui-theme-layout-dark.mdui-theme-accent-light-green .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ccff90}.mdui-theme-layout-dark.mdui-theme-accent-lime .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(244,255,129,.5)}.mdui-theme-layout-dark.mdui-theme-accent-lime .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#f4ff81}.mdui-theme-layout-dark.mdui-theme-accent-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,209,128,.5)}.mdui-theme-layout-dark.mdui-theme-accent-orange .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ffd180}.mdui-theme-layout-dark.mdui-theme-accent-pink .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,128,171,.5)}.mdui-theme-layout-dark.mdui-theme-accent-pink .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff80ab}.mdui-theme-layout-dark.mdui-theme-accent-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(234,128,252,.5)}.mdui-theme-layout-dark.mdui-theme-accent-purple .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ea80fc}.mdui-theme-layout-dark.mdui-theme-accent-red .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,138,128,.5)}.mdui-theme-layout-dark.mdui-theme-accent-red .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ff8a80}.mdui-theme-layout-dark.mdui-theme-accent-teal .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(167,255,235,.5)}.mdui-theme-layout-dark.mdui-theme-accent-teal .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#a7ffeb}.mdui-theme-layout-dark.mdui-theme-accent-yellow .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon{background-color:rgba(255,255,141,.5)}.mdui-theme-layout-dark.mdui-theme-accent-yellow .mdui-switch input[type=checkbox]:checked+.mdui-switch-icon:before{background-color:#ffff8d}.mdui-theme-layout-dark .mdui-switch:active input[type=checkbox]+.mdui-switch-icon:before{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12),0 0 0 15px rgba(255,255,255,.1)}.mdui-theme-layout-dark .mdui-switch input[type=checkbox]:disabled+.mdui-switch-icon{background-color:rgba(255,255,255,.1)!important}.mdui-theme-layout-dark .mdui-switch input[type=checkbox]:disabled+.mdui-switch-icon:before{background-color:#424242!important}.mdui-slider{position:relative;display:block;width:100%;height:36px}.mdui-slider input[type=range]{position:absolute;top:50%;z-index:2;width:100%;height:20px;-webkit-appearance:none;margin-top:-10px;cursor:pointer;opacity:0;-moz-appearance:none;appearance:none}.mdui-slider-fill,.mdui-slider-track{position:absolute;top:50%;height:2px;box-sizing:border-box;margin-top:-1px}.mdui-slider-fill:before,.mdui-slider-track:before{display:block;width:100%;height:100%;content:' '}.mdui-slider-track{right:0}.mdui-slider-track:before{background-color:#bdbdbd;-webkit-transition:background-color .3s cubic-bezier(.4,0,.2,1);transition:background-color .3s cubic-bezier(.4,0,.2,1)}.mdui-slider-fill{left:0}.mdui-slider-fill:before{background-color:#ff4081}.mdui-slider-thumb{position:absolute;top:50%;width:12px;height:12px;box-sizing:border-box;margin-top:-6px;background-color:#ff4081;border:2px solid #ff4081;border-radius:50%;-webkit-transition:background .45s cubic-bezier(.4,0,.2,1),border-color .45s cubic-bezier(.4,0,.2,1),border-radius .2s cubic-bezier(.4,0,.2,1),-webkit-transform .2s cubic-bezier(.4,0,.2,1);transition:background .45s cubic-bezier(.4,0,.2,1),border-color .45s cubic-bezier(.4,0,.2,1),border-radius .2s cubic-bezier(.4,0,.2,1),-webkit-transform .2s cubic-bezier(.4,0,.2,1);transition:background .45s cubic-bezier(.4,0,.2,1),border-color .45s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1),border-radius .2s cubic-bezier(.4,0,.2,1);transition:background .45s cubic-bezier(.4,0,.2,1),border-color .45s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1),border-radius .2s cubic-bezier(.4,0,.2,1),-webkit-transform .2s cubic-bezier(.4,0,.2,1);-webkit-transform:translate(-50%);transform:translate(-50%);will-change:background,border-color,transform,border-radius}.mdui-theme-accent-amber .mdui-slider-fill:before{background-color:#ffd740}.mdui-theme-accent-amber .mdui-slider-thumb{background-color:#ffd740;border:2px solid #ffd740}.mdui-theme-accent-blue .mdui-slider-fill:before{background-color:#448aff}.mdui-theme-accent-blue .mdui-slider-thumb{background-color:#448aff;border:2px solid #448aff}.mdui-theme-accent-cyan .mdui-slider-fill:before{background-color:#18ffff}.mdui-theme-accent-cyan .mdui-slider-thumb{background-color:#18ffff;border:2px solid #18ffff}.mdui-theme-accent-deep-orange .mdui-slider-fill:before{background-color:#ff6e40}.mdui-theme-accent-deep-orange .mdui-slider-thumb{background-color:#ff6e40;border:2px solid #ff6e40}.mdui-theme-accent-deep-purple .mdui-slider-fill:before{background-color:#7c4dff}.mdui-theme-accent-deep-purple .mdui-slider-thumb{background-color:#7c4dff;border:2px solid #7c4dff}.mdui-theme-accent-green .mdui-slider-fill:before{background-color:#69f0ae}.mdui-theme-accent-green .mdui-slider-thumb{background-color:#69f0ae;border:2px solid #69f0ae}.mdui-theme-accent-indigo .mdui-slider-fill:before{background-color:#536dfe}.mdui-theme-accent-indigo .mdui-slider-thumb{background-color:#536dfe;border:2px solid #536dfe}.mdui-theme-accent-light-blue .mdui-slider-fill:before{background-color:#40c4ff}.mdui-theme-accent-light-blue .mdui-slider-thumb{background-color:#40c4ff;border:2px solid #40c4ff}.mdui-theme-accent-light-green .mdui-slider-fill:before{background-color:#b2ff59}.mdui-theme-accent-light-green .mdui-slider-thumb{background-color:#b2ff59;border:2px solid #b2ff59}.mdui-theme-accent-lime .mdui-slider-fill:before{background-color:#eeff41}.mdui-theme-accent-lime .mdui-slider-thumb{background-color:#eeff41;border:2px solid #eeff41}.mdui-theme-accent-orange .mdui-slider-fill:before{background-color:#ffab40}.mdui-theme-accent-orange .mdui-slider-thumb{background-color:#ffab40;border:2px solid #ffab40}.mdui-theme-accent-pink .mdui-slider-fill:before{background-color:#ff4081}.mdui-theme-accent-pink .mdui-slider-thumb{background-color:#ff4081;border:2px solid #ff4081}.mdui-theme-accent-purple .mdui-slider-fill:before{background-color:#e040fb}.mdui-theme-accent-purple .mdui-slider-thumb{background-color:#e040fb;border:2px solid #e040fb}.mdui-theme-accent-red .mdui-slider-fill:before{background-color:#ff5252}.mdui-theme-accent-red .mdui-slider-thumb{background-color:#ff5252;border:2px solid #ff5252}.mdui-theme-accent-teal .mdui-slider-fill:before{background-color:#64ffda}.mdui-theme-accent-teal .mdui-slider-thumb{background-color:#64ffda;border:2px solid #64ffda}.mdui-theme-accent-yellow .mdui-slider-fill:before{background-color:#ff0}.mdui-theme-accent-yellow .mdui-slider-thumb{background-color:#ff0;border:2px solid #ff0}.mdui-slider-focus .mdui-slider-track:before{background-color:#9e9e9e}.mdui-slider-focus .mdui-slider-thumb{-webkit-transform:translate(-50%) scale(1.6);transform:translate(-50%) scale(1.6)}.mdui-slider-zero .mdui-slider-thumb{background-color:#fff;border-color:#bdbdbd}.mdui-slider-zero.mdui-slider-focus .mdui-slider-thumb{border-color:#9e9e9e}.mdui-slider-disabled input[type=range]{cursor:default}.mdui-slider-disabled .mdui-slider-track:before{background-color:#bdbdbd}.mdui-slider-disabled .mdui-slider-fill:before{background-color:#bdbdbd}.mdui-slider-disabled .mdui-slider-thumb{background-color:#bdbdbd;border-color:transparent!important;-webkit-transform:translate(-50%) scale(.72);transform:translate(-50%) scale(.72)}.mdui-slider-discrete .mdui-slider-thumb{width:30px;height:30px;margin-top:-15px;margin-left:-15px;border:none;-webkit-transform:rotate(-45deg) scale(.4);transform:rotate(-45deg) scale(.4)}.mdui-slider-discrete .mdui-slider-thumb span{position:absolute;top:9px;left:-1px;width:100%;font-size:12px;color:#fff;text-align:center;opacity:0;-webkit-transition:opacity .25s cubic-bezier(.4,0,.2,1);transition:opacity .25s cubic-bezier(.4,0,.2,1);-webkit-transform:rotate(45deg);transform:rotate(45deg)}.mdui-slider-discrete.mdui-slider-focus .mdui-slider-thumb{border-radius:15px 15px 15px 0;-webkit-transform:rotate(-45deg) scale(1) translate(22px,-22px);transform:rotate(-45deg) scale(1) translate(22px,-22px)}.mdui-slider-discrete.mdui-slider-focus .mdui-slider-thumb span{opacity:1}.mdui-slider-discrete.mdui-slider-zero .mdui-slider-thumb{background-color:#323232}.mdui-slider-discrete.mdui-slider-zero.mdui-slider-focus .mdui-slider-thumb{background-color:#bdbdbd}.mdui-slider-discrete.mdui-slider-disabled .mdui-slider-thumb{-webkit-transform:rotate(-45deg) scale(.288);transform:rotate(-45deg) scale(.288)}.mdui-slider-discrete.mdui-slider-zero.mdui-slider-disabled .mdui-slider-thumb{background-color:#bdbdbd}.mdui-theme-layout-dark .mdui-slider-track:before{background-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-focus .mdui-slider-track:before{background-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-zero .mdui-slider-thumb{background-color:#303030;border-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-zero.mdui-slider-focus .mdui-slider-thumb{border-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-disabled .mdui-slider-track:before{background-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-disabled .mdui-slider-fill:before{background-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-disabled .mdui-slider-thumb{background-color:#4c4c4c}.mdui-theme-layout-dark .mdui-slider-discrete.mdui-slider-zero .mdui-slider-thumb{background-color:#fefefe}.mdui-theme-layout-dark .mdui-slider-discrete.mdui-slider-zero.mdui-slider-focus .mdui-slider-thumb{background-color:#5c5c5c}.mdui-theme-layout-dark .mdui-slider-discrete.mdui-slider-zero.mdui-slider-disabled .mdui-slider-thumb{background-color:#4c4c4c}.mdui-btn,.mdui-fab{position:relative;display:inline-block;min-width:88px;height:36px;box-sizing:border-box;padding:0 16px;margin:0;overflow:hidden;font-size:14px;font-weight:500;line-height:36px;color:inherit;text-align:center;text-decoration:none;text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;zoom:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:0 0;border:none;border-radius:2px;outline:0;-webkit-transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1);transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1);will-change:box-shadow;-webkit-user-drag:none}.mdui-btn::-moz-focus-inner,.mdui-fab::-moz-focus-inner{border:0}.mdui-btn:hover,.mdui-fab:hover{background-color:rgba(0,0,0,.1)}.mdui-btn:not(.mdui-ripple):active,.mdui-fab:not(.mdui-ripple):active{background-color:rgba(0,0,0,.165)}.mdui-btn[class*=mdui-color-]:hover,.mdui-fab[class*=mdui-color-]:hover{opacity:.87}.mdui-btn:not(.mdui-ripple)[class*=mdui-color-]:active,.mdui-fab:not(.mdui-ripple)[class*=mdui-color-]:active{opacity:.76}.mdui-btn .mdui-icon-left,.mdui-btn .mdui-icon-left::before,.mdui-btn .mdui-icon-right,.mdui-btn .mdui-icon-right::before{height:inherit;font-size:1.3em;line-height:inherit}.mdui-btn .mdui-icon-left{float:left;margin-right:.4em}.mdui-btn .mdui-icon-right{float:right;margin-left:.4em}input.mdui-btn[type=submit]{-webkit-appearance:none}.mdui-btn-raised{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-btn-raised:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.mdui-btn-raised:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.mdui-btn[disabled],.mdui-btn[disabled]:active,.mdui-btn[disabled]:focus,.mdui-btn[disabled]:hover,.mdui-fab[disabled],.mdui-fab[disabled]:active,.mdui-fab[disabled]:focus,.mdui-fab[disabled]:hover{color:rgba(0,0,0,.26)!important;cursor:default!important;background-color:transparent!important;box-shadow:none!important}.mdui-btn[disabled] .mdui-icon,.mdui-btn[disabled]:active .mdui-icon,.mdui-btn[disabled]:focus .mdui-icon,.mdui-btn[disabled]:hover .mdui-icon,.mdui-fab[disabled] .mdui-icon,.mdui-fab[disabled]:active .mdui-icon,.mdui-fab[disabled]:focus .mdui-icon,.mdui-fab[disabled]:hover .mdui-icon{color:rgba(0,0,0,.26)!important}.mdui-btn-raised[disabled],.mdui-btn-raised[disabled]:active,.mdui-btn-raised[disabled]:focus,.mdui-btn-raised[disabled]:hover,.mdui-fab[disabled],.mdui-fab[disabled]:active,.mdui-fab[disabled]:focus,.mdui-fab[disabled]:hover{background-color:rgba(0,0,0,.12)!important;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)!important}.mdui-btn-bold{font-weight:700}.mdui-btn-icon{width:36px;min-width:36px;height:36px;padding:0;margin-right:0;margin-left:0;overflow:hidden;font-size:24px;line-height:normal;border-radius:50%}.mdui-btn-icon .mdui-icon{position:absolute;top:50%;left:50%;width:24px;line-height:24px;-webkit-transform:translate(-12px,-12px);transform:translate(-12px,-12px)}.mdui-btn-icon.mdui-ripple{-webkit-transform:translateZ(0);transform:translateZ(0)}.mdui-btn-block{display:block;width:100%}.mdui-btn-dense{height:32px;font-size:13px;line-height:32px}.mdui-btn-dense.mdui-btn-icon{width:32px;min-width:32px}.mdui-theme-layout-dark .mdui-btn:hover,.mdui-theme-layout-dark .mdui-fab:hover{background-color:rgba(255,255,255,.1)}.mdui-theme-layout-dark .mdui-btn:not(.mdui-ripple):active,.mdui-theme-layout-dark .mdui-fab:not(.mdui-ripple):active{background-color:rgba(255,255,255,.165)}.mdui-theme-layout-dark .mdui-btn[class*=mdui-color-]:hover,.mdui-theme-layout-dark .mdui-fab[class*=mdui-color-]:hover{opacity:.87}.mdui-theme-layout-dark .mdui-btn:not(.mdui-ripple)[class*=mdui-color-]:active,.mdui-theme-layout-dark .mdui-fab:not(.mdui-ripple)[class*=mdui-color-]:active{opacity:.76}.mdui-theme-layout-dark .mdui-btn[disabled],.mdui-theme-layout-dark .mdui-btn[disabled]:active,.mdui-theme-layout-dark .mdui-btn[disabled]:focus,.mdui-theme-layout-dark .mdui-btn[disabled]:hover,.mdui-theme-layout-dark .mdui-fab[disabled],.mdui-theme-layout-dark .mdui-fab[disabled]:active,.mdui-theme-layout-dark .mdui-fab[disabled]:focus,.mdui-theme-layout-dark .mdui-fab[disabled]:hover{color:rgba(255,255,255,.3)!important;background-color:transparent!important}.mdui-theme-layout-dark .mdui-btn[disabled] .mdui-icon,.mdui-theme-layout-dark .mdui-btn[disabled]:active .mdui-icon,.mdui-theme-layout-dark .mdui-btn[disabled]:focus .mdui-icon,.mdui-theme-layout-dark .mdui-btn[disabled]:hover .mdui-icon,.mdui-theme-layout-dark .mdui-fab[disabled] .mdui-icon,.mdui-theme-layout-dark .mdui-fab[disabled]:active .mdui-icon,.mdui-theme-layout-dark .mdui-fab[disabled]:focus .mdui-icon,.mdui-theme-layout-dark .mdui-fab[disabled]:hover .mdui-icon{color:rgba(255,255,255,.3)!important}.mdui-theme-layout-dark .mdui-btn-raised[disabled],.mdui-theme-layout-dark .mdui-btn-raised[disabled]:active,.mdui-theme-layout-dark .mdui-btn-raised[disabled]:focus,.mdui-theme-layout-dark .mdui-btn-raised[disabled]:hover,.mdui-theme-layout-dark .mdui-fab[disabled],.mdui-theme-layout-dark .mdui-fab[disabled]:active,.mdui-theme-layout-dark .mdui-fab[disabled]:focus,.mdui-theme-layout-dark .mdui-fab[disabled]:hover{background-color:rgba(255,255,255,.12)!important}.mdui-fab{width:56px;min-width:56px;height:56px;padding:0!important;margin:auto;overflow:hidden;font-size:24px;line-height:normal!important;border-radius:50%;box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)}.mdui-fab:hover{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.mdui-fab:active{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)}.mdui-fab .mdui-icon{position:absolute;top:0;left:0;width:24px;margin-top:16px;margin-left:16px;line-height:24px}.mdui-fab-mini{width:40px;min-width:40px;height:40px}.mdui-fab-mini .mdui-icon{margin-top:8px;margin-left:8px}.mdui-fab-fixed,.mdui-fab-wrapper{position:fixed!important;right:16px;bottom:16px}@media (min-width:1024px){.mdui-fab-fixed,.mdui-fab-wrapper{right:24px;bottom:24px}}.mdui-fab-wrapper{position:relative;z-index:4000;width:56px;height:56px;padding-top:8px}.mdui-fab-wrapper>.mdui-fab .mdui-icon:not(.mdui-fab-opened){opacity:1;-webkit-transition:all .2s cubic-bezier(.4,0,.2,1);transition:all .2s cubic-bezier(.4,0,.2,1);will-change:opacity,transform}.mdui-fab-wrapper>.mdui-fab .mdui-icon.mdui-fab-opened{opacity:0;-webkit-transition:all .2s cubic-bezier(.4,0,.2,1);transition:all .2s cubic-bezier(.4,0,.2,1);-webkit-transform:rotate(225deg);transform:rotate(225deg);will-change:opacity,transform}.mdui-fab-wrapper>.mdui-fab.mdui-fab-opened .mdui-icon:not(.mdui-fab-opened){opacity:0;-webkit-transform:rotate(225deg);transform:rotate(225deg)}.mdui-fab-wrapper>.mdui-fab.mdui-fab-opened .mdui-icon.mdui-fab-opened{opacity:1;-webkit-transform:rotate(360deg);transform:rotate(360deg)}.mdui-fab-wrapper .mdui-fab-dial{position:absolute;right:0;bottom:64px;left:0;text-align:center;visibility:visible}.mdui-fab-wrapper .mdui-fab-dial .mdui-fab{margin:8px 0;opacity:0;-webkit-transition:box-shadow .2s cubic-bezier(.4,0,1,1),color .2s cubic-bezier(.4,0,.2,1),opacity 150ms cubic-bezier(.4,0,.2,1),-webkit-transform 150ms cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),color .2s cubic-bezier(.4,0,.2,1),opacity 150ms cubic-bezier(.4,0,.2,1),-webkit-transform 150ms cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),color .2s cubic-bezier(.4,0,.2,1),transform 150ms cubic-bezier(.4,0,.2,1),opacity 150ms cubic-bezier(.4,0,.2,1);transition:box-shadow .2s cubic-bezier(.4,0,1,1),color .2s cubic-bezier(.4,0,.2,1),transform 150ms cubic-bezier(.4,0,.2,1),opacity 150ms cubic-bezier(.4,0,.2,1),-webkit-transform 150ms cubic-bezier(.4,0,.2,1);-webkit-transform:scale(0);transform:scale(0)}.mdui-fab-wrapper .mdui-fab-dial.mdui-fab-dial-show .mdui-fab{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.mdui-fab,.mdui-fab-mini,.mdui-fab-wrapper{-webkit-transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1),-webkit-transform .2s;transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1),-webkit-transform .2s;transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1),transform .2s;transition:all .2s cubic-bezier(.4,0,.2,1),box-shadow .2s cubic-bezier(.4,0,1,1),transform .2s,-webkit-transform .2s;-webkit-transform:scale(1) translateZ(0);transform:scale(1) translateZ(0);will-change:transform}.mdui-fab-mini.mdui-fab-hide,.mdui-fab-wrapper.mdui-fab-hide,.mdui-fab.mdui-fab-hide{-webkit-transform:scale(0) translateZ(0);transform:scale(0) translateZ(0)}.mdui-container,.mdui-container-fluid{box-sizing:border-box;padding-right:8px;padding-left:8px;margin-right:auto;margin-left:auto}.mdui-container-fluid:after,.mdui-container:after{display:table;clear:both;content:''}.mdui-container{width:96%;max-width:1280px}@media (min-width:600px){.mdui-container{width:94%}}@media (min-width:1024px){.mdui-container{width:92%}}.mdui-row,[class*=mdui-row-]{margin-right:-8px;margin-left:-8px}.mdui-row:after,[class*=mdui-row-]:after{display:table;clear:both;content:''}.mdui-col,[class*=mdui-col-xs-],[class*=mdui-col-sm-],[class*=mdui-col-md-],[class*=mdui-col-lg-],[class*=mdui-col-xl-]{position:relative;min-height:1px;box-sizing:border-box;padding-right:8px;padding-left:8px}.mdui-row-gapless .mdui-col,.mdui-row-gapless [class*=mdui-col-xs-],.mdui-row-gapless [class*=mdui-col-sm-],.mdui-row-gapless [class*=mdui-col-md-],.mdui-row-gapless [class*=mdui-col-lg-],.mdui-row-gapless [class*=mdui-col-xl-]{padding-right:0;padding-left:0}.mdui-row-gapless .mdui-row,.mdui-row-gapless [class*=mdui-row-]{margin-right:0;margin-left:0}.mdui-col-xs-1{float:left;width:8.333333%}.mdui-col-offset-xs-1{margin-left:8.333333%}.mdui-row-xs-1 .mdui-col{float:left;width:100%}.mdui-col-xs-2{float:left;width:16.666667%}.mdui-col-offset-xs-2{margin-left:16.666667%}.mdui-row-xs-2 .mdui-col{float:left;width:50%}.mdui-col-xs-3{float:left;width:25%}.mdui-col-offset-xs-3{margin-left:25%}.mdui-row-xs-3 .mdui-col{float:left;width:33.333333%}.mdui-col-xs-4{float:left;width:33.333333%}.mdui-col-offset-xs-4{margin-left:33.333333%}.mdui-row-xs-4 .mdui-col{float:left;width:25%}.mdui-col-xs-5{float:left;width:41.666667%}.mdui-col-offset-xs-5{margin-left:41.666667%}.mdui-row-xs-5 .mdui-col{float:left;width:20%}.mdui-col-xs-6{float:left;width:50%}.mdui-col-offset-xs-6{margin-left:50%}.mdui-row-xs-6 .mdui-col{float:left;width:16.666667%}.mdui-col-xs-7{float:left;width:58.333333%}.mdui-col-offset-xs-7{margin-left:58.333333%}.mdui-row-xs-7 .mdui-col{float:left;width:14.285714%}.mdui-col-xs-8{float:left;width:66.666667%}.mdui-col-offset-xs-8{margin-left:66.666667%}.mdui-row-xs-8 .mdui-col{float:left;width:12.5%}.mdui-col-xs-9{float:left;width:75%}.mdui-col-offset-xs-9{margin-left:75%}.mdui-row-xs-9 .mdui-col{float:left;width:11.111111%}.mdui-col-xs-10{float:left;width:83.333333%}.mdui-col-offset-xs-10{margin-left:83.333333%}.mdui-row-xs-10 .mdui-col{float:left;width:10%}.mdui-col-xs-11{float:left;width:91.666667%}.mdui-col-offset-xs-11{margin-left:91.666667%}.mdui-row-xs-11 .mdui-col{float:left;width:9.090909%}.mdui-col-xs-12{float:left;width:100%}.mdui-col-offset-xs-12{margin-left:100%}.mdui-row-xs-12 .mdui-col{float:left;width:8.333333%}@media (min-width:600px){.mdui-col-sm-1{float:left;width:8.333333%}.mdui-col-offset-sm-1{margin-left:8.333333%}.mdui-row-sm-1 .mdui-col{float:left;width:100%}.mdui-col-sm-2{float:left;width:16.666667%}.mdui-col-offset-sm-2{margin-left:16.666667%}.mdui-row-sm-2 .mdui-col{float:left;width:50%}.mdui-col-sm-3{float:left;width:25%}.mdui-col-offset-sm-3{margin-left:25%}.mdui-row-sm-3 .mdui-col{float:left;width:33.333333%}.mdui-col-sm-4{float:left;width:33.333333%}.mdui-col-offset-sm-4{margin-left:33.333333%}.mdui-row-sm-4 .mdui-col{float:left;width:25%}.mdui-col-sm-5{float:left;width:41.666667%}.mdui-col-offset-sm-5{margin-left:41.666667%}.mdui-row-sm-5 .mdui-col{float:left;width:20%}.mdui-col-sm-6{float:left;width:50%}.mdui-col-offset-sm-6{margin-left:50%}.mdui-row-sm-6 .mdui-col{float:left;width:16.666667%}.mdui-col-sm-7{float:left;width:58.333333%}.mdui-col-offset-sm-7{margin-left:58.333333%}.mdui-row-sm-7 .mdui-col{float:left;width:14.285714%}.mdui-col-sm-8{float:left;width:66.666667%}.mdui-col-offset-sm-8{margin-left:66.666667%}.mdui-row-sm-8 .mdui-col{float:left;width:12.5%}.mdui-col-sm-9{float:left;width:75%}.mdui-col-offset-sm-9{margin-left:75%}.mdui-row-sm-9 .mdui-col{float:left;width:11.111111%}.mdui-col-sm-10{float:left;width:83.333333%}.mdui-col-offset-sm-10{margin-left:83.333333%}.mdui-row-sm-10 .mdui-col{float:left;width:10%}.mdui-col-sm-11{float:left;width:91.666667%}.mdui-col-offset-sm-11{margin-left:91.666667%}.mdui-row-sm-11 .mdui-col{float:left;width:9.090909%}.mdui-col-sm-12{float:left;width:100%}.mdui-col-offset-sm-12{margin-left:100%}.mdui-row-sm-12 .mdui-col{float:left;width:8.333333%}}@media (min-width:1024px){.mdui-col-md-1{float:left;width:8.333333%}.mdui-col-offset-md-1{margin-left:8.333333%}.mdui-row-md-1 .mdui-col{float:left;width:100%}.mdui-col-md-2{float:left;width:16.666667%}.mdui-col-offset-md-2{margin-left:16.666667%}.mdui-row-md-2 .mdui-col{float:left;width:50%}.mdui-col-md-3{float:left;width:25%}.mdui-col-offset-md-3{margin-left:25%}.mdui-row-md-3 .mdui-col{float:left;width:33.333333%}.mdui-col-md-4{float:left;width:33.333333%}.mdui-col-offset-md-4{margin-left:33.333333%}.mdui-row-md-4 .mdui-col{float:left;width:25%}.mdui-col-md-5{float:left;width:41.666667%}.mdui-col-offset-md-5{margin-left:41.666667%}.mdui-row-md-5 .mdui-col{float:left;width:20%}.mdui-col-md-6{float:left;width:50%}.mdui-col-offset-md-6{margin-left:50%}.mdui-row-md-6 .mdui-col{float:left;width:16.666667%}.mdui-col-md-7{float:left;width:58.333333%}.mdui-col-offset-md-7{margin-left:58.333333%}.mdui-row-md-7 .mdui-col{float:left;width:14.285714%}.mdui-col-md-8{float:left;width:66.666667%}.mdui-col-offset-md-8{margin-left:66.666667%}.mdui-row-md-8 .mdui-col{float:left;width:12.5%}.mdui-col-md-9{float:left;width:75%}.mdui-col-offset-md-9{margin-left:75%}.mdui-row-md-9 .mdui-col{float:left;width:11.111111%}.mdui-col-md-10{float:left;width:83.333333%}.mdui-col-offset-md-10{margin-left:83.333333%}.mdui-row-md-10 .mdui-col{float:left;width:10%}.mdui-col-md-11{float:left;width:91.666667%}.mdui-col-offset-md-11{margin-left:91.666667%}.mdui-row-md-11 .mdui-col{float:left;width:9.090909%}.mdui-col-md-12{float:left;width:100%}.mdui-col-offset-md-12{margin-left:100%}.mdui-row-md-12 .mdui-col{float:left;width:8.333333%}}@media (min-width:1440px){.mdui-col-lg-1{float:left;width:8.333333%}.mdui-col-offset-lg-1{margin-left:8.333333%}.mdui-row-lg-1 .mdui-col{float:left;width:100%}.mdui-col-lg-2{float:left;width:16.666667%}.mdui-col-offset-lg-2{margin-left:16.666667%}.mdui-row-lg-2 .mdui-col{float:left;width:50%}.mdui-col-lg-3{float:left;width:25%}.mdui-col-offset-lg-3{margin-left:25%}.mdui-row-lg-3 .mdui-col{float:left;width:33.333333%}.mdui-col-lg-4{float:left;width:33.333333%}.mdui-col-offset-lg-4{margin-left:33.333333%}.mdui-row-lg-4 .mdui-col{float:left;width:25%}.mdui-col-lg-5{float:left;width:41.666667%}.mdui-col-offset-lg-5{margin-left:41.666667%}.mdui-row-lg-5 .mdui-col{float:left;width:20%}.mdui-col-lg-6{float:left;width:50%}.mdui-col-offset-lg-6{margin-left:50%}.mdui-row-lg-6 .mdui-col{float:left;width:16.666667%}.mdui-col-lg-7{float:left;width:58.333333%}.mdui-col-offset-lg-7{margin-left:58.333333%}.mdui-row-lg-7 .mdui-col{float:left;width:14.285714%}.mdui-col-lg-8{float:left;width:66.666667%}.mdui-col-offset-lg-8{margin-left:66.666667%}.mdui-row-lg-8 .mdui-col{float:left;width:12.5%}.mdui-col-lg-9{float:left;width:75%}.mdui-col-offset-lg-9{margin-left:75%}.mdui-row-lg-9 .mdui-col{float:left;width:11.111111%}.mdui-col-lg-10{float:left;width:83.333333%}.mdui-col-offset-lg-10{margin-left:83.333333%}.mdui-row-lg-10 .mdui-col{float:left;width:10%}.mdui-col-lg-11{float:left;width:91.666667%}.mdui-col-offset-lg-11{margin-left:91.666667%}.mdui-row-lg-11 .mdui-col{float:left;width:9.090909%}.mdui-col-lg-12{float:left;width:100%}.mdui-col-offset-lg-12{margin-left:100%}.mdui-row-lg-12 .mdui-col{float:left;width:8.333333%}}@media (min-width:1920px){.mdui-col-xl-1{float:left;width:8.333333%}.mdui-col-offset-xl-1{margin-left:8.333333%}.mdui-row-xl-1 .mdui-col{float:left;width:100%}.mdui-col-xl-2{float:left;width:16.666667%}.mdui-col-offset-xl-2{margin-left:16.666667%}.mdui-row-xl-2 .mdui-col{float:left;width:50%}.mdui-col-xl-3{float:left;width:25%}.mdui-col-offset-xl-3{margin-left:25%}.mdui-row-xl-3 .mdui-col{float:left;width:33.333333%}.mdui-col-xl-4{float:left;width:33.333333%}.mdui-col-offset-xl-4{margin-left:33.333333%}.mdui-row-xl-4 .mdui-col{float:left;width:25%}.mdui-col-xl-5{float:left;width:41.666667%}.mdui-col-offset-xl-5{margin-left:41.666667%}.mdui-row-xl-5 .mdui-col{float:left;width:20%}.mdui-col-xl-6{float:left;width:50%}.mdui-col-offset-xl-6{margin-left:50%}.mdui-row-xl-6 .mdui-col{float:left;width:16.666667%}.mdui-col-xl-7{float:left;width:58.333333%}.mdui-col-offset-xl-7{margin-left:58.333333%}.mdui-row-xl-7 .mdui-col{float:left;width:14.285714%}.mdui-col-xl-8{float:left;width:66.666667%}.mdui-col-offset-xl-8{margin-left:66.666667%}.mdui-row-xl-8 .mdui-col{float:left;width:12.5%}.mdui-col-xl-9{float:left;width:75%}.mdui-col-offset-xl-9{margin-left:75%}.mdui-row-xl-9 .mdui-col{float:left;width:11.111111%}.mdui-col-xl-10{float:left;width:83.333333%}.mdui-col-offset-xl-10{margin-left:83.333333%}.mdui-row-xl-10 .mdui-col{float:left;width:10%}.mdui-col-xl-11{float:left;width:91.666667%}.mdui-col-offset-xl-11{margin-left:91.666667%}.mdui-row-xl-11 .mdui-col{float:left;width:9.090909%}.mdui-col-xl-12{float:left;width:100%}.mdui-col-offset-xl-12{margin-left:100%}.mdui-row-xl-12 .mdui-col{float:left;width:8.333333%}}.mdui-toolbar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;box-sizing:border-box;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.mdui-toolbar>*{margin:0 16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mdui-toolbar[class*=mdui-color-]:not(.mdui-color-transparent) .mdui-btn:hover{background-color:rgba(255,255,255,.1)}.mdui-toolbar[class*=mdui-color-]:not(.mdui-color-transparent) .mdui-btn:active{background-color:rgba(255,255,255,.165)}.mdui-toolbar>a{color:inherit;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-toolbar>.mdui-btn-icon{width:48px;min-width:48px;height:48px}.mdui-toolbar>.mdui-btn-icon .mdui-icon{height:24px;line-height:24px}.mdui-toolbar .mdui-icon{color:inherit}.mdui-toolbar-spacer{margin:0;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-toolbar{height:56px;line-height:56px}.mdui-toolbar>.mdui-btn{margin:0 4px}.mdui-toolbar>.mdui-btn+.mdui-btn{margin-left:0}@media (min-width:600px){.mdui-appbar .mdui-toolbar{height:60px;line-height:60px}.mdui-appbar .mdui-toolbar>.mdui-btn{margin:0 6px}.mdui-appbar .mdui-toolbar>.mdui-btn+.mdui-btn{margin-left:0}}@media (min-width:1024px){.mdui-appbar .mdui-toolbar{height:64px;line-height:64px}.mdui-appbar .mdui-toolbar>.mdui-btn{margin:0 8px}.mdui-appbar .mdui-toolbar>.mdui-btn+.mdui-btn{margin-left:0}}.mdui-appbar{z-index:1000;width:100%;box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.mdui-appbar-fixed{position:fixed;top:0;right:0;left:0}.mdui-appbar-with-toolbar{padding-top:56px}@media (min-width:600px){.mdui-appbar-with-toolbar{padding-top:60px}}@media (min-width:1024px){.mdui-appbar-with-toolbar{padding-top:64px}}.mdui-appbar-with-tab{padding-top:48px}.mdui-appbar-with-tab-larger{padding-top:72px}.mdui-appbar-with-toolbar.mdui-appbar-with-tab{padding-top:104px}@media (min-width:600px){.mdui-appbar-with-toolbar.mdui-appbar-with-tab{padding-top:108px}}@media (min-width:1024px){.mdui-appbar-with-toolbar.mdui-appbar-with-tab{padding-top:112px}}.mdui-appbar-with-toolbar.mdui-appbar-with-tab-larger{padding-top:128px}@media (min-width:600px){.mdui-appbar-with-toolbar.mdui-appbar-with-tab-larger{padding-top:132px}}@media (min-width:1024px){.mdui-appbar-with-toolbar.mdui-appbar-with-tab-larger{padding-top:136px}}.mdui-theme-layout-dark .mdui-appbar>[class*=mdui-color-]:not(.mdui-color-transparent){color:#fff!important;background-color:#212121!important}.mdui-card{position:relative;box-sizing:border-box;overflow:hidden;color:#000;background-color:#fff;border-radius:2px;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-card-header{position:relative;height:72px;box-sizing:border-box;padding:16px}.mdui-card-header-avatar{float:left;width:40px;height:40px;border-radius:50%}.mdui-card-header-title{display:block;margin-left:52px;overflow:hidden;font-size:16px;font-weight:500;line-height:20px;text-overflow:ellipsis;white-space:nowrap;opacity:.87}.mdui-card-header-subtitle{display:block;margin-left:52px;overflow:hidden;font-size:14px;font-weight:400;line-height:20px;text-overflow:ellipsis;white-space:nowrap;opacity:.54}.mdui-card-primary{position:relative;padding:24px 16px 16px 16px}.mdui-card-primary-title{display:block;font-size:24px;line-height:36px;opacity:.87}.mdui-card-primary-subtitle{display:block;font-size:14px;line-height:24px;opacity:.54}.mdui-card-content{position:relative;padding:16px;font-size:14px;line-height:24px}.mdui-card-menu{position:absolute;top:16px;right:16px;z-index:1}.mdui-card-menu .mdui-btn{margin-left:8px}.mdui-card-actions{position:relative;box-sizing:border-box;padding:8px}.mdui-card-actions:after,.mdui-card-actions:before{display:table;content:" "}.mdui-card-actions:after{clear:both}.mdui-card-actions:after,.mdui-card-actions:before{display:table;content:" "}.mdui-card-actions:after{clear:both}.mdui-card-actions .mdui-btn{max-width:100%;margin:0 8px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mdui-card-actions .mdui-btn-icon{width:36px;height:36px;margin:0 8px}.mdui-card-actions-stacked .mdui-btn{display:block;margin:0 0 4px 0}.mdui-card-actions-stacked .mdui-btn:last-child{margin:0}.mdui-card-media{position:relative}.mdui-card-media img,.mdui-card-media video{display:block;width:100%}.mdui-card-media-covered{position:absolute;right:0;bottom:0;left:0;color:#fff;background:rgba(0,0,0,.2)}.mdui-card-media-covered .mdui-card-primary-title{opacity:1}.mdui-card-media-covered .mdui-card-primary-subtitle{opacity:.7}.mdui-card-media-covered-top{top:0;bottom:auto}.mdui-card-media-covered-transparent{background:0 0}.mdui-card-media-covered-gradient{background:-webkit-linear-gradient(bottom,rgba(0,0,0,.26),rgba(0,0,0,0));background:linear-gradient(to top,rgba(0,0,0,.26),rgba(0,0,0,0))}.mdui-card-media-covered-gradient.mdui-card-media-covered-top{background:-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,0));background:linear-gradient(to bottom,rgba(0,0,0,.26),rgba(0,0,0,0))}.mdui-theme-layout-dark .mdui-card{color:#fff;background-color:#424242}.mdui-tab{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:48px;max-height:72px;padding:0;margin:0 auto;overflow-x:auto;overflow-y:hidden;white-space:nowrap}.mdui-tab a{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:72px;min-height:48px;max-height:72px;box-sizing:border-box;padding:12px;overflow:hidden;font-size:14px;color:inherit;text-align:center;text-decoration:none;text-overflow:ellipsis;text-transform:uppercase;cursor:pointer;flex-direction:column;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;opacity:.7;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.mdui-tab a .mdui-icon{opacity:.7}@media (min-width:600px){.mdui-tab a{min-width:99px;max-width:264px;padding:12px 24px;-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}}@media (min-width:1024px){.mdui-tab a{min-width:112px}}@media (min-width:1440px){.mdui-tab a{min-width:136px}}@media (min-width:1920px){.mdui-tab a{min-width:160px}}.mdui-tab a label{display:block;width:100%;cursor:pointer}.mdui-tab a .mdui-icon+label{margin-top:8px}.mdui-tab a[disabled]{cursor:default;opacity:.38}.mdui-tab a[disabled] label{cursor:default}.mdui-tab .mdui-tab-active{color:#3f51b5;opacity:1}.mdui-tab .mdui-tab-active .mdui-icon{opacity:1}@media (min-width:600px){.mdui-tab-centered:before{content:' ';-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-tab-centered:after{content:' ';-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-tab-centered a{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}}.mdui-tab-full-width a{max-width:none;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.mdui-tab-scrollable{padding-left:56px}.mdui-tab-scrollable a{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}@media (max-width:599px){.mdui-tab-scrollable{padding-left:60px}}.mdui-tab-indicator{position:absolute;bottom:0;height:2px;background-color:#3f51b5;-webkit-transition:all .35s cubic-bezier(.4,0,.2,1);transition:all .35s cubic-bezier(.4,0,.2,1);will-change:left,width}.mdui-theme-primary-amber .mdui-tab .mdui-tab-active{color:#ffc107}.mdui-theme-primary-amber .mdui-tab-indicator{background-color:#ffc107}.mdui-theme-primary-blue .mdui-tab .mdui-tab-active{color:#2196f3}.mdui-theme-primary-blue .mdui-tab-indicator{background-color:#2196f3}.mdui-theme-primary-blue-grey .mdui-tab .mdui-tab-active{color:#607d8b}.mdui-theme-primary-blue-grey .mdui-tab-indicator{background-color:#607d8b}.mdui-theme-primary-brown .mdui-tab .mdui-tab-active{color:#795548}.mdui-theme-primary-brown .mdui-tab-indicator{background-color:#795548}.mdui-theme-primary-cyan .mdui-tab .mdui-tab-active{color:#00bcd4}.mdui-theme-primary-cyan .mdui-tab-indicator{background-color:#00bcd4}.mdui-theme-primary-deep-orange .mdui-tab .mdui-tab-active{color:#ff5722}.mdui-theme-primary-deep-orange .mdui-tab-indicator{background-color:#ff5722}.mdui-theme-primary-deep-purple .mdui-tab .mdui-tab-active{color:#673ab7}.mdui-theme-primary-deep-purple .mdui-tab-indicator{background-color:#673ab7}.mdui-theme-primary-green .mdui-tab .mdui-tab-active{color:#4caf50}.mdui-theme-primary-green .mdui-tab-indicator{background-color:#4caf50}.mdui-theme-primary-grey .mdui-tab .mdui-tab-active{color:#9e9e9e}.mdui-theme-primary-grey .mdui-tab-indicator{background-color:#9e9e9e}.mdui-theme-primary-indigo .mdui-tab .mdui-tab-active{color:#3f51b5}.mdui-theme-primary-indigo .mdui-tab-indicator{background-color:#3f51b5}.mdui-theme-primary-light-blue .mdui-tab .mdui-tab-active{color:#03a9f4}.mdui-theme-primary-light-blue .mdui-tab-indicator{background-color:#03a9f4}.mdui-theme-primary-light-green .mdui-tab .mdui-tab-active{color:#8bc34a}.mdui-theme-primary-light-green .mdui-tab-indicator{background-color:#8bc34a}.mdui-theme-primary-lime .mdui-tab .mdui-tab-active{color:#cddc39}.mdui-theme-primary-lime .mdui-tab-indicator{background-color:#cddc39}.mdui-theme-primary-orange .mdui-tab .mdui-tab-active{color:#ff9800}.mdui-theme-primary-orange .mdui-tab-indicator{background-color:#ff9800}.mdui-theme-primary-pink .mdui-tab .mdui-tab-active{color:#e91e63}.mdui-theme-primary-pink .mdui-tab-indicator{background-color:#e91e63}.mdui-theme-primary-purple .mdui-tab .mdui-tab-active{color:#9c27b0}.mdui-theme-primary-purple .mdui-tab-indicator{background-color:#9c27b0}.mdui-theme-primary-red .mdui-tab .mdui-tab-active{color:#f44336}.mdui-theme-primary-red .mdui-tab-indicator{background-color:#f44336}.mdui-theme-primary-teal .mdui-tab .mdui-tab-active{color:#009688}.mdui-theme-primary-teal .mdui-tab-indicator{background-color:#009688}.mdui-theme-primary-yellow .mdui-tab .mdui-tab-active{color:#ffeb3b}.mdui-theme-primary-yellow .mdui-tab-indicator{background-color:#ffeb3b}.mdui-tab[class*=mdui-color-]:not(.mdui-color-white) .mdui-tab-active{color:inherit}.mdui-tab[class*=mdui-color-]:not(.mdui-color-white) .mdui-tab-indicator{background-color:#fff}.mdui-tab[class*=mdui-color-]:not(.mdui-color-white)::-webkit-scrollbar{width:5px;height:5px;background:0 0}@media (min-width:1024px){.mdui-tab[class*=mdui-color-]:not(.mdui-color-white)::-webkit-scrollbar{width:8px;height:8px}}.mdui-tab[class*=mdui-color-]:not(.mdui-color-white)::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3)}.mdui-subheader,.mdui-subheader-inset{position:relative;height:48px;box-sizing:border-box;padding-right:16px;padding-left:16px;overflow:hidden;font-size:14px;font-weight:500;line-height:48px;color:rgba(0,0,0,.54);text-overflow:ellipsis;white-space:nowrap;cursor:default}.mdui-subheader-inset{padding-left:72px}.mdui-theme-layout-dark .mdui-subheader,.mdui-theme-layout-dark .mdui-subheader-inset{color:rgba(255,255,255,.7)}.mdui-grid-list{margin:0 -2px}.mdui-grid-list .mdui-col,.mdui-grid-list [class*=mdui-col-xs-],.mdui-grid-list [class*=mdui-col-sm-],.mdui-grid-list [class*=mdui-col-md-],.mdui-grid-list [class*=mdui-col-lg-],.mdui-grid-list [class*=mdui-col-xl-]{padding-right:2px;padding-left:2px}.mdui-grid-tile{position:relative;box-sizing:border-box;margin-bottom:4px;overflow:hidden}.mdui-grid-tile img{display:block;width:100%}.mdui-grid-tile-actions{position:absolute;right:0;bottom:0;left:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:48px;max-height:68px;box-sizing:border-box;padding:16px;color:#fff;background:rgba(0,0,0,.2);-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.mdui-grid-tile-actions .mdui-icon{color:#fff}.mdui-grid-tile-text{overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.mdui-grid-tile-title{height:16px;overflow:hidden;font-size:16px;line-height:16px;text-overflow:ellipsis;white-space:nowrap}.mdui-grid-tile-title .mdui-icon{margin-right:8px}.mdui-grid-tile-subtitle{height:18px;margin-top:4px;overflow:hidden;font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.mdui-grid-tile-subtitle .mdui-icon{margin-right:8px;font-size:18px}.mdui-grid-tile-buttons{margin:-8px;white-space:nowrap;-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.mdui-grid-tile-buttons .mdui-btn{margin-left:8px}.mdui-grid-tile-buttons .mdui-btn:first-child{margin-left:0}.mdui-grid-tile-text+.mdui-grid-tile-buttons{margin-left:8px}.mdui-grid-tile-buttons+.mdui-grid-tile-text{margin-left:16px}.mdui-grid-tile-actions-top{top:0;bottom:auto}.mdui-grid-tile-actions-transparent{background:0 0}.mdui-grid-tile-actions-gradient{background:-webkit-linear-gradient(bottom,rgba(0,0,0,.26),rgba(0,0,0,0));background:linear-gradient(to top,rgba(0,0,0,.26),rgba(0,0,0,0))}.mdui-grid-tile-actions-gradient.mdui-grid-tile-actions-top{background:-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,0));background:linear-gradient(to bottom,rgba(0,0,0,.26),rgba(0,0,0,0))}.mdui-list{padding:8px 0;margin:0;list-style:none;background-color:transparent}.mdui-list .mdui-list{padding:0}.mdui-list>.mdui-divider,.mdui-list>.mdui-divider-dark,.mdui-list>.mdui-divider-inset,.mdui-list>.mdui-divider-inset-dark,.mdui-list>.mdui-divider-inset-light,.mdui-list>.mdui-divider-light{margin-top:8px;margin-bottom:8px}.mdui-list a{color:inherit;text-decoration:none}.mdui-list .mdui-subheader,.mdui-list .mdui-subheader-inset{margin-top:8px}.mdui-list .mdui-subheader-inset:before,.mdui-list .mdui-subheader:before{position:absolute;right:0;left:0;display:block;height:1px;content:' ';background-color:rgba(0,0,0,.12)}.mdui-list .mdui-subheader-inset:first-child,.mdui-list .mdui-subheader:first-child{margin-top:-8px}.mdui-list .mdui-subheader-inset:first-child:before,.mdui-list .mdui-subheader:first-child:before{background-color:transparent}.mdui-list .mdui-subheader-inset:before{left:72px}.mdui-list-item{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:48px;box-sizing:border-box;padding:0 16px;text-decoration:none;cursor:pointer;-webkit-transition:background-color .3s cubic-bezier(.4,0,.2,1);transition:background-color .3s cubic-bezier(.4,0,.2,1);-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.mdui-list-item:hover{background-color:rgba(0,0,0,.08)}.mdui-list-item:after{height:48px;visibility:hidden;content:' '}.mdui-list-item-icon{width:24px;min-width:24px;height:24px;color:rgba(0,0,0,.54)}.mdui-list-item-avatar{min-width:40px;max-width:40px;height:40px;margin-top:8px;margin-bottom:8px;line-height:40px;color:#fff;text-align:center;background-color:#bdbdbd;border-radius:50%}.mdui-list-item-avatar img{width:100%;height:100%;border-radius:50%}.mdui-list-item-content{padding-top:14px;padding-bottom:14px;font-size:16px;font-weight:400;line-height:20px;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-list-item-text{font-size:14px;color:rgba(0,0,0,.54)}.mdui-list-item-title~.mdui-list-item-text{margin-top:4px}.mdui-list-item-active{font-weight:700;background-color:rgba(0,0,0,.08)}.mdui-list-item-active .mdui-list-item-content{font-weight:700}.mdui-list-item-active .mdui-list-item-text{font-weight:400}.mdui-list-item-one-line,.mdui-list-item-three-line,.mdui-list-item-two-line{display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical}.mdui-list-item-one-line{height:20px;-webkit-line-clamp:1}.mdui-list-item-two-line{height:40px;-webkit-line-clamp:2}.mdui-list-item-three-line{height:60px;-webkit-line-clamp:3}.mdui-list-item-icon~.mdui-list-item-content{margin-left:32px}.mdui-checkbox~.mdui-list-item-content,.mdui-radio~.mdui-list-item-content,.mdui-switch~.mdui-list-item-content{margin-left:20px}.mdui-list-item-avatar~.mdui-list-item-content{margin-left:16px}.mdui-list-item-content~.mdui-checkbox,.mdui-list-item-content~.mdui-list-item-avatar,.mdui-list-item-content~.mdui-list-item-icon,.mdui-list-item-content~.mdui-radio,.mdui-list-item-content~.mdui-switch{margin-left:16px}.mdui-list-item-content~.mdui-checkbox,.mdui-list-item-content~.mdui-radio{padding-left:24px}.mdui-list-dense{padding:4px 0;font-size:13px}.mdui-list-dense>.mdui-divider,.mdui-list-dense>.mdui-divider-dark,.mdui-list-dense>.mdui-divider-inset,.mdui-list-dense>.mdui-divider-inset-dark,.mdui-list-dense>.mdui-divider-inset-light,.mdui-list-dense>.mdui-divider-light{margin-top:4px;margin-bottom:4px}.mdui-list-dense .mdui-subheader,.mdui-list-dense .mdui-subheader-inset{height:40px;margin-top:4px;font-size:12px;line-height:40px}.mdui-list-dense .mdui-subheader-inset:first-child,.mdui-list-dense .mdui-subheader:first-child{margin-top:-4px}.mdui-list-dense .mdui-list-item{min-height:40px}.mdui-list-dense .mdui-list-item:after{height:40px}.mdui-list-dense .mdui-list-item-icon{width:20px;height:20px;font-size:20px}.mdui-list-dense .mdui-list-item-avatar{min-width:36px;height:36px;min-height:36px}.mdui-list-dense .mdui-list-item-content{padding-top:11px;padding-bottom:11px;font-size:13px;line-height:18px}.mdui-list-dense .mdui-list-item-text{font-size:13px}.mdui-list-dense .mdui-list-item-title~.mdui-list-item-text{margin-top:2px}.mdui-list-dense .mdui-list-item-one-line{height:18px}.mdui-list-dense .mdui-list-item-two-line{height:36px}.mdui-list-dense .mdui-list-item-three-line{height:54px}.mdui-theme-layout-dark .mdui-list .mdui-subheader-inset:before,.mdui-theme-layout-dark .mdui-list .mdui-subheader:before{background-color:rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-list .mdui-subheader-inset:first-child:before,.mdui-theme-layout-dark .mdui-list .mdui-subheader:first-child:before{background-color:transparent}.mdui-theme-layout-dark .mdui-list-item{color:#fff}.mdui-theme-layout-dark .mdui-list-item:hover{background-color:rgba(255,255,255,.08)}.mdui-theme-layout-dark .mdui-list-item-icon{color:#fff}.mdui-theme-layout-dark .mdui-list-item-text{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-list-item-active{background-color:rgba(255,255,255,.08)}[class*=mdui-color-] .mdui-list-item{color:inherit}[class*=mdui-color-] .mdui-list-item-icon{color:inherit}body.mdui-loaded{-webkit-transition:padding .3s cubic-bezier(0,0,.2,1);transition:padding .3s cubic-bezier(0,0,.2,1)}body.mdui-loaded .mdui-drawer{-webkit-transition:all .3s cubic-bezier(0,0,.2,1);transition:all .3s cubic-bezier(0,0,.2,1)}.mdui-drawer{position:fixed;top:0;bottom:0;left:0;z-index:5000;width:240px;box-sizing:border-box;margin:0;overflow-x:hidden;overflow-y:auto;white-space:nowrap;will-change:transform}.mdui-drawer-right{right:0;left:auto}@media (max-width:1023px){.mdui-drawer{background-color:#fff;box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);-webkit-transform:translateX(-250px);transform:translateX(-250px)}.mdui-drawer-right{-webkit-transform:translateX(250px);transform:translateX(250px)}}.mdui-drawer-close{-webkit-transform:translateX(-250px);transform:translateX(-250px)}.mdui-drawer-close.mdui-drawer-right{-webkit-transform:translateX(250px);transform:translateX(250px)}.mdui-drawer-open{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}@media (min-width:1024px){body.mdui-drawer-body-left{padding-left:240px}body.mdui-drawer-body-right{padding-right:240px}.mdui-appbar-with-toolbar .mdui-drawer{top:64px}.mdui-appbar-with-tab .mdui-drawer{top:48px}.mdui-appbar-with-tab-larger .mdui-drawer{top:72px}.mdui-appbar-with-toolbar.mdui-appbar-with-tab .mdui-drawer{top:112px}.mdui-appbar-with-toolbar.mdui-appbar-with-tab-larger .mdui-drawer{top:136px}}.mdui-drawer[class*=mdui-color-]:not(.mdui-color-transparent){box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.mdui-drawer-full-height{top:0!important}@media (max-width:1023px){.mdui-theme-layout-dark .mdui-drawer{background-color:#424242}}.mdui-dialog{position:fixed;right:0;left:0;z-index:6000;display:none;width:92%;min-width:180px;max-width:728px;max-height:90%;box-sizing:border-box;margin:auto;overflow:hidden;color:#000;background-color:#fff;border-radius:2px;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12);opacity:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,visibility,-webkit-transform;transition-property:opacity,visibility,-webkit-transform;transition-property:transform,opacity,visibility;transition-property:transform,opacity,visibility,-webkit-transform;-webkit-transform:scale(.95);transform:scale(.95);will-change:top,opacity,transform}@media (min-width:600px){.mdui-dialog{width:85%;max-height:85%}}@media (min-width:1024px){.mdui-dialog{width:80%;max-height:80%}}.mdui-dialog-open{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.mdui-dialog-title{box-sizing:border-box;font-size:20px;font-weight:500;line-height:24px;text-align:left}.mdui-dialog>.mdui-dialog-title{padding:24px 24px 20px 24px}.mdui-dialog-content{box-sizing:border-box;padding:24px;overflow-y:auto;font-size:15px;line-height:1.5;color:rgba(0,0,0,.7)}.mdui-dialog-content>.mdui-dialog-title{padding-bottom:20px}.mdui-dialog-title+.mdui-dialog-content{padding-top:0}.mdui-dialog-actions{box-sizing:border-box;padding:8px;text-align:right}.mdui-dialog-actions .mdui-btn{min-width:64px;margin-left:8px;color:#e91e63}.mdui-dialog-actions .mdui-btn:first-child{margin-left:0}.mdui-dialog-content>.mdui-dialog-actions{padding-top:32px;margin:0 -24px -24px -24px}.mdui-dialog-actions-stacked{padding:8px 0 8px 0}.mdui-dialog-actions-stacked .mdui-btn{width:100%;height:48px;margin:0;line-height:48px!important;text-align:right;border-radius:0}.mdui-theme-accent-amber .mdui-dialog-actions .mdui-btn{color:#ffc107}.mdui-theme-accent-blue .mdui-dialog-actions .mdui-btn{color:#2196f3}.mdui-theme-accent-cyan .mdui-dialog-actions .mdui-btn{color:#00bcd4}.mdui-theme-accent-deep-orange .mdui-dialog-actions .mdui-btn{color:#ff5722}.mdui-theme-accent-deep-purple .mdui-dialog-actions .mdui-btn{color:#673ab7}.mdui-theme-accent-green .mdui-dialog-actions .mdui-btn{color:#4caf50}.mdui-theme-accent-indigo .mdui-dialog-actions .mdui-btn{color:#3f51b5}.mdui-theme-accent-light-blue .mdui-dialog-actions .mdui-btn{color:#03a9f4}.mdui-theme-accent-light-green .mdui-dialog-actions .mdui-btn{color:#8bc34a}.mdui-theme-accent-lime .mdui-dialog-actions .mdui-btn{color:#cddc39}.mdui-theme-accent-orange .mdui-dialog-actions .mdui-btn{color:#ff9800}.mdui-theme-accent-pink .mdui-dialog-actions .mdui-btn{color:#e91e63}.mdui-theme-accent-purple .mdui-dialog-actions .mdui-btn{color:#9c27b0}.mdui-theme-accent-red .mdui-dialog-actions .mdui-btn{color:#f44336}.mdui-theme-accent-teal .mdui-dialog-actions .mdui-btn{color:#009688}.mdui-theme-accent-yellow .mdui-dialog-actions .mdui-btn{color:#ffeb3b}.mdui-theme-layout-dark .mdui-dialog{color:#fff;background-color:#424242}.mdui-theme-layout-dark .mdui-dialog-content{color:rgba(255,255,255,.7)}.mdui-dialog-alert{max-width:448px}.mdui-dialog-confirm{max-width:448px}.mdui-dialog-prompt{max-width:448px}.mdui-dialog-prompt .mdui-textfield{padding-top:0}.mdui-shadow-0{box-shadow:none}.mdui-shadow-1{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12)}.mdui-shadow-2{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-shadow-3{box-shadow:0 3px 3px -2px rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 1px 8px 0 rgba(0,0,0,.12)}.mdui-shadow-4{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.mdui-shadow-5{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12)}.mdui-shadow-6{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)}.mdui-shadow-7{box-shadow:0 4px 5px -2px rgba(0,0,0,.2),0 7px 10px 1px rgba(0,0,0,.14),0 2px 16px 1px rgba(0,0,0,.12)}.mdui-shadow-8{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.mdui-shadow-9{box-shadow:0 5px 6px -3px rgba(0,0,0,.2),0 9px 12px 1px rgba(0,0,0,.14),0 3px 16px 2px rgba(0,0,0,.12)}.mdui-shadow-10{box-shadow:0 6px 6px -3px rgba(0,0,0,.2),0 10px 14px 1px rgba(0,0,0,.14),0 4px 18px 3px rgba(0,0,0,.12)}.mdui-shadow-11{box-shadow:0 6px 7px -4px rgba(0,0,0,.2),0 11px 15px 1px rgba(0,0,0,.14),0 4px 20px 3px rgba(0,0,0,.12)}.mdui-shadow-12{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)}.mdui-shadow-13{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12)}.mdui-shadow-14{box-shadow:0 7px 9px -4px rgba(0,0,0,.2),0 14px 21px 2px rgba(0,0,0,.14),0 5px 26px 4px rgba(0,0,0,.12)}.mdui-shadow-15{box-shadow:0 8px 9px -5px rgba(0,0,0,.2),0 15px 22px 2px rgba(0,0,0,.14),0 6px 28px 5px rgba(0,0,0,.12)}.mdui-shadow-16{box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.mdui-shadow-17{box-shadow:0 8px 11px -5px rgba(0,0,0,.2),0 17px 26px 2px rgba(0,0,0,.14),0 6px 32px 5px rgba(0,0,0,.12)}.mdui-shadow-18{box-shadow:0 9px 11px -5px rgba(0,0,0,.2),0 18px 28px 2px rgba(0,0,0,.14),0 7px 34px 6px rgba(0,0,0,.12)}.mdui-shadow-19{box-shadow:0 9px 12px -6px rgba(0,0,0,.2),0 19px 29px 2px rgba(0,0,0,.14),0 7px 36px 6px rgba(0,0,0,.12)}.mdui-shadow-20{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 20px 31px 3px rgba(0,0,0,.14),0 8px 38px 7px rgba(0,0,0,.12)}.mdui-shadow-21{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 21px 33px 3px rgba(0,0,0,.14),0 8px 40px 7px rgba(0,0,0,.12)}.mdui-shadow-22{box-shadow:0 10px 14px -6px rgba(0,0,0,.2),0 22px 35px 3px rgba(0,0,0,.14),0 8px 42px 7px rgba(0,0,0,.12)}.mdui-shadow-23{box-shadow:0 11px 14px -7px rgba(0,0,0,.2),0 23px 36px 3px rgba(0,0,0,.14),0 9px 44px 8px rgba(0,0,0,.12)}.mdui-shadow-24{box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}.mdui-hoverable{-webkit-transition:box-shadow .25s cubic-bezier(.4,0,.2,1);transition:box-shadow .25s cubic-bezier(.4,0,.2,1);will-change:box-shadow}.mdui-hoverable:focus,.mdui-hoverable:hover{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.mdui-tooltip{position:absolute;z-index:9000;display:inline-block;max-width:180px;min-height:32px;box-sizing:border-box;padding:8px 16px;font-size:14px;font-weight:500;line-height:22px;color:#fff;text-align:left;background-color:rgba(97,97,97,.9);border-radius:2px;opacity:0;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transition-duration:.15s;transition-duration:.15s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform;-webkit-transform:scale(0);transform:scale(0);will-change:opacity,transform}@media (min-width:1024px){.mdui-tooltip{max-width:200px;min-height:24px;padding:4px 8px;font-size:12px;line-height:18px}}.mdui-tooltip-open{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.mdui-snackbar{position:fixed;bottom:0;z-index:7000;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;min-height:48px;box-sizing:border-box;padding:0 24px 0 24px;font-size:14px;line-height:20px;color:#fff;background-color:#323232;box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;will-change:transform}@media (min-width:600px){.mdui-snackbar{width:auto;min-width:288px;max-width:568px;border-radius:2px}}.mdui-snackbar-transition{-webkit-transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1);transition:-webkit-transform .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1);transition:transform .3s cubic-bezier(.4,0,.2,1),-webkit-transform .3s cubic-bezier(.4,0,.2,1)}.mdui-snackbar-text{position:relative;max-width:100%;padding:14px 0 14px 0;overflow:hidden;text-overflow:ellipsis}.mdui-snackbar-action{margin-right:-16px;color:#ff80ab;white-space:nowrap}.mdui-theme-accent-amber .mdui-snackbar-action{color:#ffe57f}.mdui-theme-accent-blue .mdui-snackbar-action{color:#82b1ff}.mdui-theme-accent-cyan .mdui-snackbar-action{color:#84ffff}.mdui-theme-accent-deep-orange .mdui-snackbar-action{color:#ff9e80}.mdui-theme-accent-deep-purple .mdui-snackbar-action{color:#b388ff}.mdui-theme-accent-green .mdui-snackbar-action{color:#b9f6ca}.mdui-theme-accent-indigo .mdui-snackbar-action{color:#8c9eff}.mdui-theme-accent-light-blue .mdui-snackbar-action{color:#80d8ff}.mdui-theme-accent-light-green .mdui-snackbar-action{color:#ccff90}.mdui-theme-accent-lime .mdui-snackbar-action{color:#f4ff81}.mdui-theme-accent-orange .mdui-snackbar-action{color:#ffd180}.mdui-theme-accent-pink .mdui-snackbar-action{color:#ff80ab}.mdui-theme-accent-purple .mdui-snackbar-action{color:#ea80fc}.mdui-theme-accent-red .mdui-snackbar-action{color:#ff8a80}.mdui-theme-accent-teal .mdui-snackbar-action{color:#a7ffeb}.mdui-theme-accent-yellow .mdui-snackbar-action{color:#ffff8d}.mdui-theme-layout-dark .mdui-snackbar{background-color:#5d5d5d}.mdui-chip{display:inline-block;height:32px;box-sizing:border-box;margin:2px 0;white-space:nowrap;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#e0e0e0;border-radius:16px;-webkit-transition:box-shadow .25s cubic-bezier(.4,0,.2,1);transition:box-shadow .25s cubic-bezier(.4,0,.2,1);will-change:box-shadow}.mdui-chip:focus,.mdui-chip:hover{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-chip:active{background-color:#d6d6d6}.mdui-chip-icon{display:inline-block;width:32px;height:32px;margin-right:-4px;overflow:hidden;font-size:18px;line-height:32px;color:#fff;text-align:center;vertical-align:middle;background-color:#989898;border-radius:50%}.mdui-chip-icon .mdui-icon{color:#fff}.mdui-chip-title{display:inline-block;height:32px;padding-right:12px;padding-left:12px;font-size:14px;line-height:32px;vertical-align:middle}.mdui-chip-delete{display:inline-block;width:24px;height:24px;margin-right:4px;margin-left:-8px;overflow:hidden;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;border-radius:50%;opacity:.54;-webkit-transition:opacity .25s cubic-bezier(.4,0,.2,1);transition:opacity .25s cubic-bezier(.4,0,.2,1);will-change:opacity}.mdui-chip-delete:focus,.mdui-chip-delete:hover{opacity:.87}.mdui-theme-layout-dark .mdui-chip{background-color:#484848}.mdui-theme-layout-dark .mdui-chip:active{background-color:#5d5d5d}.mdui-bottom-nav{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:56px;padding:0;margin:0 auto;overflow:hidden;white-space:nowrap}@media (min-width:600px){.mdui-bottom-nav:before{content:' ';-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-bottom-nav:after{content:' ';-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}}.mdui-bottom-nav a{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:32px;max-width:none;padding:8px 12px 10px 12px;overflow:hidden;font-size:12px;color:inherit;text-align:center;text-decoration:none;text-overflow:ellipsis;cursor:pointer;flex-direction:column;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;opacity:.7;-webkit-transition:all .2s cubic-bezier(.4,0,.2,1);transition:all .2s cubic-bezier(.4,0,.2,1);-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:padding}@media (min-width:600px){.mdui-bottom-nav a{max-width:144px}}.mdui-bottom-nav a .mdui-icon{opacity:.7}.mdui-bottom-nav a label{display:block;width:100%;cursor:pointer;-webkit-transition:-webkit-transform .2s cubic-bezier(.4,0,.2,1);transition:-webkit-transform .2s cubic-bezier(.4,0,.2,1);transition:transform .2s cubic-bezier(.4,0,.2,1);transition:transform .2s cubic-bezier(.4,0,.2,1),-webkit-transform .2s cubic-bezier(.4,0,.2,1);will-change:font-size}.mdui-bottom-nav a .mdui-icon+label{margin-top:6px}.mdui-bottom-nav a.mdui-bottom-nav-active{font-size:14px;color:#3f51b5;opacity:1}.mdui-bottom-nav a.mdui-bottom-nav-active .mdui-icon{opacity:1}.mdui-bottom-nav-text-auto a{min-width:32px;padding-right:0;padding-left:0}@media (min-width:600px){.mdui-bottom-nav-text-auto a{max-width:156px}}.mdui-bottom-nav-text-auto a .mdui-icon{padding-top:16px;-webkit-transition:padding-top .2s cubic-bezier(.4,0,.2,1);transition:padding-top .2s cubic-bezier(.4,0,.2,1);will-change:padding-top}.mdui-bottom-nav-text-auto a label{-webkit-transform:scale(0);transform:scale(0)}.mdui-bottom-nav-text-auto a.mdui-bottom-nav-active{padding-right:18px;padding-left:18px}.mdui-bottom-nav-text-auto a.mdui-bottom-nav-active .mdui-icon{padding-top:0}.mdui-bottom-nav-text-auto a.mdui-bottom-nav-active label{-webkit-transform:scale(1);transform:scale(1)}body.mdui-bottom-nav-fixed{padding-bottom:56px}body.mdui-bottom-nav-fixed .mdui-bottom-nav{position:fixed;right:0;bottom:0;left:0}.mdui-theme-primary-amber .mdui-bottom-nav a.mdui-bottom-nav-active{color:#ffc107}.mdui-theme-primary-blue .mdui-bottom-nav a.mdui-bottom-nav-active{color:#2196f3}.mdui-theme-primary-blue-grey .mdui-bottom-nav a.mdui-bottom-nav-active{color:#607d8b}.mdui-theme-primary-brown .mdui-bottom-nav a.mdui-bottom-nav-active{color:#795548}.mdui-theme-primary-cyan .mdui-bottom-nav a.mdui-bottom-nav-active{color:#00bcd4}.mdui-theme-primary-deep-orange .mdui-bottom-nav a.mdui-bottom-nav-active{color:#ff5722}.mdui-theme-primary-deep-purple .mdui-bottom-nav a.mdui-bottom-nav-active{color:#673ab7}.mdui-theme-primary-green .mdui-bottom-nav a.mdui-bottom-nav-active{color:#4caf50}.mdui-theme-primary-grey .mdui-bottom-nav a.mdui-bottom-nav-active{color:#9e9e9e}.mdui-theme-primary-indigo .mdui-bottom-nav a.mdui-bottom-nav-active{color:#3f51b5}.mdui-theme-primary-light-blue .mdui-bottom-nav a.mdui-bottom-nav-active{color:#03a9f4}.mdui-theme-primary-light-green .mdui-bottom-nav a.mdui-bottom-nav-active{color:#8bc34a}.mdui-theme-primary-lime .mdui-bottom-nav a.mdui-bottom-nav-active{color:#cddc39}.mdui-theme-primary-orange .mdui-bottom-nav a.mdui-bottom-nav-active{color:#ff9800}.mdui-theme-primary-pink .mdui-bottom-nav a.mdui-bottom-nav-active{color:#e91e63}.mdui-theme-primary-purple .mdui-bottom-nav a.mdui-bottom-nav-active{color:#9c27b0}.mdui-theme-primary-red .mdui-bottom-nav a.mdui-bottom-nav-active{color:#f44336}.mdui-theme-primary-teal .mdui-bottom-nav a.mdui-bottom-nav-active{color:#009688}.mdui-theme-primary-yellow .mdui-bottom-nav a.mdui-bottom-nav-active{color:#ffeb3b}.mdui-bottom-nav[class*=mdui-color-] .mdui-bottom-nav-active{color:inherit!important}.mdui-progress{position:relative;display:block;width:100%;height:4px;overflow:hidden;background-color:rgba(63,81,181,.2);border-radius:2px}.mdui-progress-determinate{position:absolute;top:0;bottom:0;left:0;background-color:#3f51b5;-webkit-transition:width .3s linear;transition:width .3s linear}.mdui-progress-indeterminate{background-color:#3f51b5}.mdui-progress-indeterminate:before{position:absolute;top:0;bottom:0;left:0;content:' ';background-color:inherit;-webkit-animation:mdui-progress-indeterminate 2s linear infinite;animation:mdui-progress-indeterminate 2s linear infinite;will-change:left,width}.mdui-progress-indeterminate:after{position:absolute;top:0;bottom:0;left:0;content:' ';background-color:inherit;-webkit-animation:mdui-progress-indeterminate-short 2s linear infinite;animation:mdui-progress-indeterminate-short 2s linear infinite;will-change:left,width}@-webkit-keyframes mdui-progress-indeterminate{0%{left:0;width:0}50%{left:30%;width:70%}75%{left:100%;width:0}}@keyframes mdui-progress-indeterminate{0%{left:0;width:0}50%{left:30%;width:70%}75%{left:100%;width:0}}@-webkit-keyframes mdui-progress-indeterminate-short{0%{left:0;width:0}50%{left:0;width:0}75%{left:0;width:25%}100%{left:100%;width:0}}@keyframes mdui-progress-indeterminate-short{0%{left:0;width:0}50%{left:0;width:0}75%{left:0;width:25%}100%{left:100%;width:0}}.mdui-theme-primary-amber .mdui-progress{background-color:rgba(255,193,7,.2)}.mdui-theme-primary-amber .mdui-progress-determinate,.mdui-theme-primary-amber .mdui-progress-indeterminate{background-color:#ffc107}.mdui-theme-primary-blue .mdui-progress{background-color:rgba(33,150,243,.2)}.mdui-theme-primary-blue .mdui-progress-determinate,.mdui-theme-primary-blue .mdui-progress-indeterminate{background-color:#2196f3}.mdui-theme-primary-blue-grey .mdui-progress{background-color:rgba(96,125,139,.2)}.mdui-theme-primary-blue-grey .mdui-progress-determinate,.mdui-theme-primary-blue-grey .mdui-progress-indeterminate{background-color:#607d8b}.mdui-theme-primary-brown .mdui-progress{background-color:rgba(121,85,72,.2)}.mdui-theme-primary-brown .mdui-progress-determinate,.mdui-theme-primary-brown .mdui-progress-indeterminate{background-color:#795548}.mdui-theme-primary-cyan .mdui-progress{background-color:rgba(0,188,212,.2)}.mdui-theme-primary-cyan .mdui-progress-determinate,.mdui-theme-primary-cyan .mdui-progress-indeterminate{background-color:#00bcd4}.mdui-theme-primary-deep-orange .mdui-progress{background-color:rgba(255,87,34,.2)}.mdui-theme-primary-deep-orange .mdui-progress-determinate,.mdui-theme-primary-deep-orange .mdui-progress-indeterminate{background-color:#ff5722}.mdui-theme-primary-deep-purple .mdui-progress{background-color:rgba(103,58,183,.2)}.mdui-theme-primary-deep-purple .mdui-progress-determinate,.mdui-theme-primary-deep-purple .mdui-progress-indeterminate{background-color:#673ab7}.mdui-theme-primary-green .mdui-progress{background-color:rgba(76,175,80,.2)}.mdui-theme-primary-green .mdui-progress-determinate,.mdui-theme-primary-green .mdui-progress-indeterminate{background-color:#4caf50}.mdui-theme-primary-grey .mdui-progress{background-color:rgba(158,158,158,.2)}.mdui-theme-primary-grey .mdui-progress-determinate,.mdui-theme-primary-grey .mdui-progress-indeterminate{background-color:#9e9e9e}.mdui-theme-primary-indigo .mdui-progress{background-color:rgba(63,81,181,.2)}.mdui-theme-primary-indigo .mdui-progress-determinate,.mdui-theme-primary-indigo .mdui-progress-indeterminate{background-color:#3f51b5}.mdui-theme-primary-light-blue .mdui-progress{background-color:rgba(3,169,244,.2)}.mdui-theme-primary-light-blue .mdui-progress-determinate,.mdui-theme-primary-light-blue .mdui-progress-indeterminate{background-color:#03a9f4}.mdui-theme-primary-light-green .mdui-progress{background-color:rgba(139,195,74,.2)}.mdui-theme-primary-light-green .mdui-progress-determinate,.mdui-theme-primary-light-green .mdui-progress-indeterminate{background-color:#8bc34a}.mdui-theme-primary-lime .mdui-progress{background-color:rgba(205,220,57,.2)}.mdui-theme-primary-lime .mdui-progress-determinate,.mdui-theme-primary-lime .mdui-progress-indeterminate{background-color:#cddc39}.mdui-theme-primary-orange .mdui-progress{background-color:rgba(255,152,0,.2)}.mdui-theme-primary-orange .mdui-progress-determinate,.mdui-theme-primary-orange .mdui-progress-indeterminate{background-color:#ff9800}.mdui-theme-primary-pink .mdui-progress{background-color:rgba(233,30,99,.2)}.mdui-theme-primary-pink .mdui-progress-determinate,.mdui-theme-primary-pink .mdui-progress-indeterminate{background-color:#e91e63}.mdui-theme-primary-purple .mdui-progress{background-color:rgba(156,39,176,.2)}.mdui-theme-primary-purple .mdui-progress-determinate,.mdui-theme-primary-purple .mdui-progress-indeterminate{background-color:#9c27b0}.mdui-theme-primary-red .mdui-progress{background-color:rgba(244,67,54,.2)}.mdui-theme-primary-red .mdui-progress-determinate,.mdui-theme-primary-red .mdui-progress-indeterminate{background-color:#f44336}.mdui-theme-primary-teal .mdui-progress{background-color:rgba(0,150,136,.2)}.mdui-theme-primary-teal .mdui-progress-determinate,.mdui-theme-primary-teal .mdui-progress-indeterminate{background-color:#009688}.mdui-theme-primary-yellow .mdui-progress{background-color:rgba(255,235,59,.2)}.mdui-theme-primary-yellow .mdui-progress-determinate,.mdui-theme-primary-yellow .mdui-progress-indeterminate{background-color:#ffeb3b}.mdui-spinner{position:relative;display:inline-block;width:28px;height:28px;-webkit-animation:mdui-spinner 1568ms linear infinite;animation:mdui-spinner 1568ms linear infinite}@-webkit-keyframes mdui-spinner{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdui-spinner{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.mdui-spinner-layer{position:absolute;width:100%;height:100%;border-color:#3f51b5;opacity:0;opacity:1;-webkit-animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both}.mdui-spinner-layer-1{border-color:#42a5f5!important;-webkit-animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-1-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both}.mdui-spinner-layer-2{border-color:#f44336!important;-webkit-animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-2-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both}.mdui-spinner-layer-3{border-color:#fdd835!important;-webkit-animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-3-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both}.mdui-spinner-layer-4{border-color:#4caf50!important;-webkit-animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-layer-fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both,mdui-spinner-layer-4-fade-in-out 5332ms cubic-bezier(.4,0,.2,1) infinite both}@-webkit-keyframes mdui-spinner-layer-fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdui-spinner-layer-fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdui-spinner-layer-1-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@keyframes mdui-spinner-layer-1-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@-webkit-keyframes mdui-spinner-layer-2-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@keyframes mdui-spinner-layer-2-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@-webkit-keyframes mdui-spinner-layer-3-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@keyframes mdui-spinner-layer-3-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@-webkit-keyframes mdui-spinner-layer-4-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}@keyframes mdui-spinner-layer-4-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}.mdui-spinner-gap-patch{position:absolute;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.mdui-spinner-gap-patch .mdui-spinner-circle{left:-450%;width:1000%;box-sizing:border-box}.mdui-spinner-circle-clipper{position:relative;display:inline-block;width:50%;height:100%;overflow:hidden;border-color:inherit}.mdui-spinner-circle-clipper .mdui-spinner-circle{position:absolute;top:0;right:0;bottom:0;width:200%;height:100%;box-sizing:border-box;border-color:inherit;border-style:solid;border-width:3px;border-bottom-color:transparent!important;border-radius:50%;-webkit-animation:none;animation:none}.mdui-spinner-circle-clipper.mdui-spinner-left{float:left}.mdui-spinner-circle-clipper.mdui-spinner-left .mdui-spinner-circle{left:0;border-right-color:transparent!important;-webkit-transform:rotate(129deg);transform:rotate(129deg);-webkit-animation:mdui-spinner-left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both}.mdui-spinner-circle-clipper.mdui-spinner-right{float:right}.mdui-spinner-circle-clipper.mdui-spinner-right .mdui-spinner-circle{left:-100%;border-left-color:transparent!important;-webkit-transform:rotate(-129deg);transform:rotate(-129deg);-webkit-animation:mdui-spinner-right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;animation:mdui-spinner-right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both}@-webkit-keyframes mdui-spinner-left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@keyframes mdui-spinner-left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes mdui-spinner-right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}@keyframes mdui-spinner-right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}.mdui-theme-primary-amber .mdui-spinner-layer{border-color:#ffc107}.mdui-theme-primary-blue .mdui-spinner-layer{border-color:#2196f3}.mdui-theme-primary-blue-grey .mdui-spinner-layer{border-color:#607d8b}.mdui-theme-primary-brown .mdui-spinner-layer{border-color:#795548}.mdui-theme-primary-cyan .mdui-spinner-layer{border-color:#00bcd4}.mdui-theme-primary-deep-orange .mdui-spinner-layer{border-color:#ff5722}.mdui-theme-primary-deep-purple .mdui-spinner-layer{border-color:#673ab7}.mdui-theme-primary-green .mdui-spinner-layer{border-color:#4caf50}.mdui-theme-primary-grey .mdui-spinner-layer{border-color:#9e9e9e}.mdui-theme-primary-indigo .mdui-spinner-layer{border-color:#3f51b5}.mdui-theme-primary-light-blue .mdui-spinner-layer{border-color:#03a9f4}.mdui-theme-primary-light-green .mdui-spinner-layer{border-color:#8bc34a}.mdui-theme-primary-lime .mdui-spinner-layer{border-color:#cddc39}.mdui-theme-primary-orange .mdui-spinner-layer{border-color:#ff9800}.mdui-theme-primary-pink .mdui-spinner-layer{border-color:#e91e63}.mdui-theme-primary-purple .mdui-spinner-layer{border-color:#9c27b0}.mdui-theme-primary-red .mdui-spinner-layer{border-color:#f44336}.mdui-theme-primary-teal .mdui-spinner-layer{border-color:#009688}.mdui-theme-primary-yellow .mdui-spinner-layer{border-color:#ffeb3b}.mdui-panel{width:100%;box-sizing:border-box}.mdui-panel-item{color:rgba(0,0,0,.87);background-color:#fff;border-bottom:1px solid rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition:margin .3s cubic-bezier(.4,0,.2,1);transition:margin .3s cubic-bezier(.4,0,.2,1);will-change:margin}.mdui-panel-item:last-child{border-bottom:none}.mdui-panel-item-header{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:48px;box-sizing:border-box;padding:0 24px;overflow:hidden;font-size:15px;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:height,background-color}.mdui-panel-item-header:active{background-color:#eee}.mdui-panel-item-title{width:36%;min-width:36%;box-sizing:border-box;padding-right:16px;overflow:hidden;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.mdui-panel-item-summary{box-sizing:border-box;padding-right:16px;overflow:hidden;color:rgba(0,0,0,.54);text-overflow:ellipsis;white-space:nowrap;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.mdui-panel-item-arrow{position:absolute;top:12px;right:24px;color:rgba(0,0,0,.54);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .3s cubic-bezier(.4,0,.2,1);transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-transform:rotate(0);transform:rotate(0);will-change:transform,top}.mdui-panel-item-summary+.mdui-panel-item-arrow{position:relative;top:0!important;right:0}.mdui-panel-item-body{height:0;padding:0 24px;overflow:hidden;-webkit-transition:height .3s cubic-bezier(.4,0,.2,1);transition:height .3s cubic-bezier(.4,0,.2,1);will-change:height}.mdui-panel-item-body:after,.mdui-panel-item-body:before{display:table;content:" "}.mdui-panel-item-body:after{clear:both}.mdui-panel-item-body:after,.mdui-panel-item-body:before{display:table;content:" "}.mdui-panel-item-body:after{clear:both}.mdui-panel-item-body:after{height:16px}.mdui-panel-item-actions{display:block;width:100%;padding:16px 24px 0 24px;margin:16px -24px 0 -24px;text-align:right;border-top:1px solid rgba(0,0,0,.12)}.mdui-panel-item-actions .mdui-btn{margin-left:8px}.mdui-panel-item-actions .mdui-btn:first-child{margin-left:0}.mdui-panel-gapless{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.mdui-panel-gapless .mdui-panel-item{box-shadow:none}.mdui-panel-gapless .mdui-panel-item-open{margin-top:0;margin-bottom:0}.mdui-panel-popout .mdui-panel-item-open{margin-right:-16px;margin-left:-16px}.mdui-panel-item-open{height:auto;margin-top:16px;margin-bottom:16px}.mdui-panel-item-open>.mdui-panel-item-header{height:64px}.mdui-panel-item-open>.mdui-panel-item-header .mdui-panel-item-arrow{top:20px;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.mdui-panel-item-open>.mdui-panel-item-body{height:auto}.mdui-theme-layout-dark .mdui-panel-item{color:#fff;background-color:#303030;border-bottom:1px solid rgba(255,255,255,.12)}.mdui-theme-layout-dark .mdui-panel-item:last-child{border-bottom:none}.mdui-theme-layout-dark .mdui-panel-item-header:active{background-color:#424242}.mdui-theme-layout-dark .mdui-panel-item-summary{color:rgba(255,255,255,.7)}.mdui-theme-layout-dark .mdui-panel-item-arrow{color:#fff}.mdui-theme-layout-dark .mdui-panel-item-actions{border-top:1px solid rgba(255,255,255,.12)}.mdui-menu{position:fixed;z-index:99999;display:block;width:168px;box-sizing:border-box;padding:8px 0;margin:0;overflow-y:auto;font-size:16px;color:rgba(0,0,0,.87);list-style:none;visibility:hidden;background-color:#fff;border-radius:2px;box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12);opacity:0;-webkit-transition-timing-function:cubic-bezier(0,0,.2,1);transition-timing-function:cubic-bezier(0,0,.2,1);-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,visibility,-webkit-transform;transition-property:opacity,visibility,-webkit-transform;transition-property:transform,opacity,visibility;transition-property:transform,opacity,visibility,-webkit-transform;-webkit-transform:scale(0);transform:scale(0);will-change:transform,opacity,visibility}.mdui-menu .mdui-divider{margin-top:8px;margin-bottom:8px}.mdui-menu-open{visibility:visible;opacity:1;-webkit-transform:scale(1);transform:scale(1)}.mdui-menu-closing{visibility:visible;opacity:0;-webkit-transform:scale(1);transform:scale(1)}.mdui-menu-item{position:relative}.mdui-menu-item>a{position:relative;display:block;height:48px;padding:0 16px;overflow:hidden;line-height:48px;color:inherit;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdui-menu-item>a:hover{background-color:#eee}.mdui-menu-item>.mdui-menu{position:absolute;box-shadow:0 5px 6px -3px rgba(0,0,0,.2),0 9px 12px 1px rgba(0,0,0,.14),0 3px 16px 2px rgba(0,0,0,.12)}.mdui-menu-item[disabled]>a{color:rgba(0,0,0,.38)!important;cursor:default}.mdui-menu-item[disabled]>a:hover{background-color:inherit!important}.mdui-menu-item[disabled]>a .mdui-icon{color:rgba(0,0,0,.26)}.mdui-menu-item-active{background-color:#eee}.mdui-menu-item-icon{display:inline-block;width:42px;color:rgba(0,0,0,.54)}.mdui-menu-item-helper{float:right}.mdui-menu-item-more{float:right}.mdui-menu-item-more:before{font-size:70%;color:rgba(0,0,0,.54);content:'►'}.mdui-menu-cascade{width:320px;padding:16px 0;overflow-y:visible;font-size:15px}.mdui-menu-cascade>.mdui-menu-item>a{height:32px;padding:0 24px;line-height:32px}.mdui-menu-cascade>.mdui-menu-item .mdui-menu-item-icon{width:40px}.mdui-menu-1x{width:56px}.mdui-menu-cascade .mdui-menu-1x{width:64px}.mdui-menu-2x{width:112px}.mdui-menu-cascade .mdui-menu-2x{width:128px}.mdui-menu-3x{width:168px}.mdui-menu-cascade .mdui-menu-3x{width:192px}.mdui-menu-4x{width:224px}.mdui-menu-cascade .mdui-menu-4x{width:256px}.mdui-menu-5x{width:280px}.mdui-menu-cascade .mdui-menu-5x{width:320px}.mdui-menu-6x{width:336px}.mdui-menu-cascade .mdui-menu-6x{width:384px}.mdui-menu-7x{width:392px}.mdui-menu-cascade .mdui-menu-7x{width:448px}.mdui-menu-8x{width:448px}.mdui-menu-cascade .mdui-menu-8x{width:512px}.mdui-menu-9x{width:504px}.mdui-menu-cascade .mdui-menu-9x{width:576px}.mdui-menu-10x{width:560px}.mdui-menu-cascade .mdui-menu-10x{width:640px}.mdui-theme-layout-dark .mdui-menu{color:#fff;background-color:#424242}.mdui-theme-layout-dark .mdui-menu-item>a:hover{background-color:#616161}.mdui-theme-layout-dark .mdui-menu-item[disabled]>a{color:rgba(255,255,255,.5)!important}.mdui-theme-layout-dark .mdui-menu-item[disabled]>a .mdui-icon{color:rgba(255,255,255,.3)}.mdui-theme-layout-dark .mdui-menu-item-active{background-color:#616161}.mdui-theme-layout-dark .mdui-menu-item-icon{color:#fff}.mdui-theme-layout-dark .mdui-menu-item-more:before{color:#fff} \ No newline at end of file diff --git a/soulbook/static/md/fonts/roboto/LICENSE.txt b/soulbook/static/md/fonts/roboto/LICENSE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/soulbook/static/md/fonts/roboto/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/soulbook/static/md/fonts/roboto/Roboto-Black.woff b/soulbook/static/md/fonts/roboto/Roboto-Black.woff new file mode 100644 index 0000000..9b72779 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Black.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Black.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Black.woff2 new file mode 100644 index 0000000..9559f02 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Black.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff new file mode 100644 index 0000000..609bdf4 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff2 new file mode 100644 index 0000000..5cb417b Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-BlackItalic.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Bold.woff b/soulbook/static/md/fonts/roboto/Roboto-Bold.woff new file mode 100644 index 0000000..93a1d5a Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Bold.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Bold.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Bold.woff2 new file mode 100644 index 0000000..715ac19 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Bold.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff new file mode 100644 index 0000000..f1d7598 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff2 new file mode 100644 index 0000000..c91e0f8 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-BoldItalic.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Light.woff b/soulbook/static/md/fonts/roboto/Roboto-Light.woff new file mode 100644 index 0000000..404afba Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Light.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Light.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Light.woff2 new file mode 100644 index 0000000..5f26201 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Light.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff new file mode 100644 index 0000000..07117c5 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff2 new file mode 100644 index 0000000..5fa7eed Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-LightItalic.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Medium.woff b/soulbook/static/md/fonts/roboto/Roboto-Medium.woff new file mode 100644 index 0000000..b18e6b8 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Medium.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Medium.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Medium.woff2 new file mode 100644 index 0000000..f9f6913 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Medium.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff new file mode 100644 index 0000000..2be7d89 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff2 new file mode 100644 index 0000000..9b7b828 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-MediumItalic.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Regular.woff b/soulbook/static/md/fonts/roboto/Roboto-Regular.woff new file mode 100644 index 0000000..2f53e7c Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Regular.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Regular.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Regular.woff2 new file mode 100644 index 0000000..1d1539e Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Regular.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff new file mode 100644 index 0000000..60f95dd Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff2 new file mode 100644 index 0000000..75495c3 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-RegularItalic.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Thin.woff b/soulbook/static/md/fonts/roboto/Roboto-Thin.woff new file mode 100644 index 0000000..e115692 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Thin.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-Thin.woff2 b/soulbook/static/md/fonts/roboto/Roboto-Thin.woff2 new file mode 100644 index 0000000..a26400d Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-Thin.woff2 differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff b/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff new file mode 100644 index 0000000..eab0271 Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff differ diff --git a/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff2 b/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff2 new file mode 100644 index 0000000..f00aafc Binary files /dev/null and b/soulbook/static/md/fonts/roboto/Roboto-ThinItalic.woff2 differ diff --git a/soulbook/static/md/icons/material-icons/LICENSE.txt b/soulbook/static/md/icons/material-icons/LICENSE.txt new file mode 100644 index 0000000..dc8853a --- /dev/null +++ b/soulbook/static/md/icons/material-icons/LICENSE.txt @@ -0,0 +1,393 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.ijmap b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.ijmap new file mode 100644 index 0000000..d9f1d25 --- /dev/null +++ b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.ijmap @@ -0,0 +1 @@ +{"icons":{"e84d":{"name":"3d Rotation"},"eb3b":{"name":"Ac Unit"},"e190":{"name":"Access Alarm"},"e191":{"name":"Access Alarms"},"e192":{"name":"Access Time"},"e84e":{"name":"Accessibility"},"e914":{"name":"Accessible"},"e84f":{"name":"Account Balance"},"e850":{"name":"Account Balance Wallet"},"e851":{"name":"Account Box"},"e853":{"name":"Account Circle"},"e60e":{"name":"Adb"},"e145":{"name":"Add"},"e439":{"name":"Add A Photo"},"e193":{"name":"Add Alarm"},"e003":{"name":"Add Alert"},"e146":{"name":"Add Box"},"e147":{"name":"Add Circle"},"e148":{"name":"Add Circle Outline"},"e567":{"name":"Add Location"},"e854":{"name":"Add Shopping Cart"},"e39d":{"name":"Add To Photos"},"e05c":{"name":"Add To Queue"},"e39e":{"name":"Adjust"},"e630":{"name":"Airline Seat Flat"},"e631":{"name":"Airline Seat Flat Angled"},"e632":{"name":"Airline Seat Individual Suite"},"e633":{"name":"Airline Seat Legroom Extra"},"e634":{"name":"Airline Seat Legroom Normal"},"e635":{"name":"Airline Seat Legroom Reduced"},"e636":{"name":"Airline Seat Recline Extra"},"e637":{"name":"Airline Seat Recline Normal"},"e195":{"name":"Airplanemode Active"},"e194":{"name":"Airplanemode Inactive"},"e055":{"name":"Airplay"},"eb3c":{"name":"Airport Shuttle"},"e855":{"name":"Alarm"},"e856":{"name":"Alarm Add"},"e857":{"name":"Alarm Off"},"e858":{"name":"Alarm On"},"e019":{"name":"Album"},"eb3d":{"name":"All Inclusive"},"e90b":{"name":"All Out"},"e859":{"name":"Android"},"e85a":{"name":"Announcement"},"e5c3":{"name":"Apps"},"e149":{"name":"Archive"},"e5c4":{"name":"Arrow Back"},"e5db":{"name":"Arrow Downward"},"e5c5":{"name":"Arrow Drop Down"},"e5c6":{"name":"Arrow Drop Down Circle"},"e5c7":{"name":"Arrow Drop Up"},"e5c8":{"name":"Arrow Forward"},"e5d8":{"name":"Arrow Upward"},"e060":{"name":"Art Track"},"e85b":{"name":"Aspect Ratio"},"e85c":{"name":"Assessment"},"e85d":{"name":"Assignment"},"e85e":{"name":"Assignment Ind"},"e85f":{"name":"Assignment Late"},"e860":{"name":"Assignment Return"},"e861":{"name":"Assignment Returned"},"e862":{"name":"Assignment Turned In"},"e39f":{"name":"Assistant"},"e3a0":{"name":"Assistant Photo"},"e226":{"name":"Attach File"},"e227":{"name":"Attach Money"},"e2bc":{"name":"Attachment"},"e3a1":{"name":"Audiotrack"},"e863":{"name":"Autorenew"},"e01b":{"name":"Av Timer"},"e14a":{"name":"Backspace"},"e864":{"name":"Backup"},"e19c":{"name":"Battery Alert"},"e1a3":{"name":"Battery Charging Full"},"e1a4":{"name":"Battery Full"},"e1a5":{"name":"Battery Std"},"e1a6":{"name":"Battery Unknown"},"eb3e":{"name":"Beach Access"},"e52d":{"name":"Beenhere"},"e14b":{"name":"Block"},"e1a7":{"name":"Bluetooth"},"e60f":{"name":"Bluetooth Audio"},"e1a8":{"name":"Bluetooth Connected"},"e1a9":{"name":"Bluetooth Disabled"},"e1aa":{"name":"Bluetooth Searching"},"e3a2":{"name":"Blur Circular"},"e3a3":{"name":"Blur Linear"},"e3a4":{"name":"Blur Off"},"e3a5":{"name":"Blur On"},"e865":{"name":"Book"},"e866":{"name":"Bookmark"},"e867":{"name":"Bookmark Border"},"e228":{"name":"Border All"},"e229":{"name":"Border Bottom"},"e22a":{"name":"Border Clear"},"e22b":{"name":"Border Color"},"e22c":{"name":"Border Horizontal"},"e22d":{"name":"Border Inner"},"e22e":{"name":"Border Left"},"e22f":{"name":"Border Outer"},"e230":{"name":"Border Right"},"e231":{"name":"Border Style"},"e232":{"name":"Border Top"},"e233":{"name":"Border Vertical"},"e06b":{"name":"Branding Watermark"},"e3a6":{"name":"Brightness 1"},"e3a7":{"name":"Brightness 2"},"e3a8":{"name":"Brightness 3"},"e3a9":{"name":"Brightness 4"},"e3aa":{"name":"Brightness 5"},"e3ab":{"name":"Brightness 6"},"e3ac":{"name":"Brightness 7"},"e1ab":{"name":"Brightness Auto"},"e1ac":{"name":"Brightness High"},"e1ad":{"name":"Brightness Low"},"e1ae":{"name":"Brightness Medium"},"e3ad":{"name":"Broken Image"},"e3ae":{"name":"Brush"},"e6dd":{"name":"Bubble Chart"},"e868":{"name":"Bug Report"},"e869":{"name":"Build"},"e43c":{"name":"Burst Mode"},"e0af":{"name":"Business"},"eb3f":{"name":"Business Center"},"e86a":{"name":"Cached"},"e7e9":{"name":"Cake"},"e0b0":{"name":"Call"},"e0b1":{"name":"Call End"},"e0b2":{"name":"Call Made"},"e0b3":{"name":"Call Merge"},"e0b4":{"name":"Call Missed"},"e0e4":{"name":"Call Missed Outgoing"},"e0b5":{"name":"Call Received"},"e0b6":{"name":"Call Split"},"e06c":{"name":"Call To Action"},"e3af":{"name":"Camera"},"e3b0":{"name":"Camera Alt"},"e8fc":{"name":"Camera Enhance"},"e3b1":{"name":"Camera Front"},"e3b2":{"name":"Camera Rear"},"e3b3":{"name":"Camera Roll"},"e5c9":{"name":"Cancel"},"e8f6":{"name":"Card Giftcard"},"e8f7":{"name":"Card Membership"},"e8f8":{"name":"Card Travel"},"eb40":{"name":"Casino"},"e307":{"name":"Cast"},"e308":{"name":"Cast Connected"},"e3b4":{"name":"Center Focus Strong"},"e3b5":{"name":"Center Focus Weak"},"e86b":{"name":"Change History"},"e0b7":{"name":"Chat"},"e0ca":{"name":"Chat Bubble"},"e0cb":{"name":"Chat Bubble Outline"},"e5ca":{"name":"Check"},"e834":{"name":"Check Box"},"e835":{"name":"Check Box Outline Blank"},"e86c":{"name":"Check Circle"},"e5cb":{"name":"Chevron Left"},"e5cc":{"name":"Chevron Right"},"eb41":{"name":"Child Care"},"eb42":{"name":"Child Friendly"},"e86d":{"name":"Chrome Reader Mode"},"e86e":{"name":"Class"},"e14c":{"name":"Clear"},"e0b8":{"name":"Clear All"},"e5cd":{"name":"Close"},"e01c":{"name":"Closed Caption"},"e2bd":{"name":"Cloud"},"e2be":{"name":"Cloud Circle"},"e2bf":{"name":"Cloud Done"},"e2c0":{"name":"Cloud Download"},"e2c1":{"name":"Cloud Off"},"e2c2":{"name":"Cloud Queue"},"e2c3":{"name":"Cloud Upload"},"e86f":{"name":"Code"},"e3b6":{"name":"Collections"},"e431":{"name":"Collections Bookmark"},"e3b7":{"name":"Color Lens"},"e3b8":{"name":"Colorize"},"e0b9":{"name":"Comment"},"e3b9":{"name":"Compare"},"e915":{"name":"Compare Arrows"},"e30a":{"name":"Computer"},"e638":{"name":"Confirmation Number"},"e0d0":{"name":"Contact Mail"},"e0cf":{"name":"Contact Phone"},"e0ba":{"name":"Contacts"},"e14d":{"name":"Content Copy"},"e14e":{"name":"Content Cut"},"e14f":{"name":"Content Paste"},"e3ba":{"name":"Control Point"},"e3bb":{"name":"Control Point Duplicate"},"e90c":{"name":"Copyright"},"e150":{"name":"Create"},"e2cc":{"name":"Create New Folder"},"e870":{"name":"Credit Card"},"e3be":{"name":"Crop"},"e3bc":{"name":"Crop 16 9"},"e3bd":{"name":"Crop 3 2"},"e3bf":{"name":"Crop 5 4"},"e3c0":{"name":"Crop 7 5"},"e3c1":{"name":"Crop Din"},"e3c2":{"name":"Crop Free"},"e3c3":{"name":"Crop Landscape"},"e3c4":{"name":"Crop Original"},"e3c5":{"name":"Crop Portrait"},"e437":{"name":"Crop Rotate"},"e3c6":{"name":"Crop Square"},"e871":{"name":"Dashboard"},"e1af":{"name":"Data Usage"},"e916":{"name":"Date Range"},"e3c7":{"name":"Dehaze"},"e872":{"name":"Delete"},"e92b":{"name":"Delete Forever"},"e16c":{"name":"Delete Sweep"},"e873":{"name":"Description"},"e30b":{"name":"Desktop Mac"},"e30c":{"name":"Desktop Windows"},"e3c8":{"name":"Details"},"e30d":{"name":"Developer Board"},"e1b0":{"name":"Developer Mode"},"e335":{"name":"Device Hub"},"e1b1":{"name":"Devices"},"e337":{"name":"Devices Other"},"e0bb":{"name":"Dialer Sip"},"e0bc":{"name":"Dialpad"},"e52e":{"name":"Directions"},"e52f":{"name":"Directions Bike"},"e532":{"name":"Directions Boat"},"e530":{"name":"Directions Bus"},"e531":{"name":"Directions Car"},"e534":{"name":"Directions Railway"},"e566":{"name":"Directions Run"},"e533":{"name":"Directions Subway"},"e535":{"name":"Directions Transit"},"e536":{"name":"Directions Walk"},"e610":{"name":"Disc Full"},"e875":{"name":"Dns"},"e612":{"name":"Do Not Disturb"},"e611":{"name":"Do Not Disturb Alt"},"e643":{"name":"Do Not Disturb Off"},"e644":{"name":"Do Not Disturb On"},"e30e":{"name":"Dock"},"e7ee":{"name":"Domain"},"e876":{"name":"Done"},"e877":{"name":"Done All"},"e917":{"name":"Donut Large"},"e918":{"name":"Donut Small"},"e151":{"name":"Drafts"},"e25d":{"name":"Drag Handle"},"e613":{"name":"Drive Eta"},"e1b2":{"name":"Dvr"},"e3c9":{"name":"Edit"},"e568":{"name":"Edit Location"},"e8fb":{"name":"Eject"},"e0be":{"name":"Email"},"e63f":{"name":"Enhanced Encryption"},"e01d":{"name":"Equalizer"},"e000":{"name":"Error"},"e001":{"name":"Error Outline"},"e926":{"name":"Euro Symbol"},"e56d":{"name":"Ev Station"},"e878":{"name":"Event"},"e614":{"name":"Event Available"},"e615":{"name":"Event Busy"},"e616":{"name":"Event Note"},"e903":{"name":"Event Seat"},"e879":{"name":"Exit To App"},"e5ce":{"name":"Expand Less"},"e5cf":{"name":"Expand More"},"e01e":{"name":"Explicit"},"e87a":{"name":"Explore"},"e3ca":{"name":"Exposure"},"e3cb":{"name":"Exposure Neg 1"},"e3cc":{"name":"Exposure Neg 2"},"e3cd":{"name":"Exposure Plus 1"},"e3ce":{"name":"Exposure Plus 2"},"e3cf":{"name":"Exposure Zero"},"e87b":{"name":"Extension"},"e87c":{"name":"Face"},"e01f":{"name":"Fast Forward"},"e020":{"name":"Fast Rewind"},"e87d":{"name":"Favorite"},"e87e":{"name":"Favorite Border"},"e06d":{"name":"Featured Play List"},"e06e":{"name":"Featured Video"},"e87f":{"name":"Feedback"},"e05d":{"name":"Fiber Dvr"},"e061":{"name":"Fiber Manual Record"},"e05e":{"name":"Fiber New"},"e06a":{"name":"Fiber Pin"},"e062":{"name":"Fiber Smart Record"},"e2c4":{"name":"File Download"},"e2c6":{"name":"File Upload"},"e3d3":{"name":"Filter"},"e3d0":{"name":"Filter 1"},"e3d1":{"name":"Filter 2"},"e3d2":{"name":"Filter 3"},"e3d4":{"name":"Filter 4"},"e3d5":{"name":"Filter 5"},"e3d6":{"name":"Filter 6"},"e3d7":{"name":"Filter 7"},"e3d8":{"name":"Filter 8"},"e3d9":{"name":"Filter 9"},"e3da":{"name":"Filter 9 Plus"},"e3db":{"name":"Filter B And W"},"e3dc":{"name":"Filter Center Focus"},"e3dd":{"name":"Filter Drama"},"e3de":{"name":"Filter Frames"},"e3df":{"name":"Filter Hdr"},"e152":{"name":"Filter List"},"e3e0":{"name":"Filter None"},"e3e2":{"name":"Filter Tilt Shift"},"e3e3":{"name":"Filter Vintage"},"e880":{"name":"Find In Page"},"e881":{"name":"Find Replace"},"e90d":{"name":"Fingerprint"},"e5dc":{"name":"First Page"},"eb43":{"name":"Fitness Center"},"e153":{"name":"Flag"},"e3e4":{"name":"Flare"},"e3e5":{"name":"Flash Auto"},"e3e6":{"name":"Flash Off"},"e3e7":{"name":"Flash On"},"e539":{"name":"Flight"},"e904":{"name":"Flight Land"},"e905":{"name":"Flight Takeoff"},"e3e8":{"name":"Flip"},"e882":{"name":"Flip To Back"},"e883":{"name":"Flip To Front"},"e2c7":{"name":"Folder"},"e2c8":{"name":"Folder Open"},"e2c9":{"name":"Folder Shared"},"e617":{"name":"Folder Special"},"e167":{"name":"Font Download"},"e234":{"name":"Format Align Center"},"e235":{"name":"Format Align Justify"},"e236":{"name":"Format Align Left"},"e237":{"name":"Format Align Right"},"e238":{"name":"Format Bold"},"e239":{"name":"Format Clear"},"e23a":{"name":"Format Color Fill"},"e23b":{"name":"Format Color Reset"},"e23c":{"name":"Format Color Text"},"e23d":{"name":"Format Indent Decrease"},"e23e":{"name":"Format Indent Increase"},"e23f":{"name":"Format Italic"},"e240":{"name":"Format Line Spacing"},"e241":{"name":"Format List Bulleted"},"e242":{"name":"Format List Numbered"},"e243":{"name":"Format Paint"},"e244":{"name":"Format Quote"},"e25e":{"name":"Format Shapes"},"e245":{"name":"Format Size"},"e246":{"name":"Format Strikethrough"},"e247":{"name":"Format Textdirection L To R"},"e248":{"name":"Format Textdirection R To L"},"e249":{"name":"Format Underlined"},"e0bf":{"name":"Forum"},"e154":{"name":"Forward"},"e056":{"name":"Forward 10"},"e057":{"name":"Forward 30"},"e058":{"name":"Forward 5"},"eb44":{"name":"Free Breakfast"},"e5d0":{"name":"Fullscreen"},"e5d1":{"name":"Fullscreen Exit"},"e24a":{"name":"Functions"},"e927":{"name":"G Translate"},"e30f":{"name":"Gamepad"},"e021":{"name":"Games"},"e90e":{"name":"Gavel"},"e155":{"name":"Gesture"},"e884":{"name":"Get App"},"e908":{"name":"Gif"},"eb45":{"name":"Golf Course"},"e1b3":{"name":"Gps Fixed"},"e1b4":{"name":"Gps Not Fixed"},"e1b5":{"name":"Gps Off"},"e885":{"name":"Grade"},"e3e9":{"name":"Gradient"},"e3ea":{"name":"Grain"},"e1b8":{"name":"Graphic Eq"},"e3eb":{"name":"Grid Off"},"e3ec":{"name":"Grid On"},"e7ef":{"name":"Group"},"e7f0":{"name":"Group Add"},"e886":{"name":"Group Work"},"e052":{"name":"Hd"},"e3ed":{"name":"Hdr Off"},"e3ee":{"name":"Hdr On"},"e3f1":{"name":"Hdr Strong"},"e3f2":{"name":"Hdr Weak"},"e310":{"name":"Headset"},"e311":{"name":"Headset Mic"},"e3f3":{"name":"Healing"},"e023":{"name":"Hearing"},"e887":{"name":"Help"},"e8fd":{"name":"Help Outline"},"e024":{"name":"High Quality"},"e25f":{"name":"Highlight"},"e888":{"name":"Highlight Off"},"e889":{"name":"History"},"e88a":{"name":"Home"},"eb46":{"name":"Hot Tub"},"e53a":{"name":"Hotel"},"e88b":{"name":"Hourglass Empty"},"e88c":{"name":"Hourglass Full"},"e902":{"name":"Http"},"e88d":{"name":"Https"},"e3f4":{"name":"Image"},"e3f5":{"name":"Image Aspect Ratio"},"e0e0":{"name":"Import Contacts"},"e0c3":{"name":"Import Export"},"e912":{"name":"Important Devices"},"e156":{"name":"Inbox"},"e909":{"name":"Indeterminate Check Box"},"e88e":{"name":"Info"},"e88f":{"name":"Info Outline"},"e890":{"name":"Input"},"e24b":{"name":"Insert Chart"},"e24c":{"name":"Insert Comment"},"e24d":{"name":"Insert Drive File"},"e24e":{"name":"Insert Emoticon"},"e24f":{"name":"Insert Invitation"},"e250":{"name":"Insert Link"},"e251":{"name":"Insert Photo"},"e891":{"name":"Invert Colors"},"e0c4":{"name":"Invert Colors Off"},"e3f6":{"name":"Iso"},"e312":{"name":"Keyboard"},"e313":{"name":"Keyboard Arrow Down"},"e314":{"name":"Keyboard Arrow Left"},"e315":{"name":"Keyboard Arrow Right"},"e316":{"name":"Keyboard Arrow Up"},"e317":{"name":"Keyboard Backspace"},"e318":{"name":"Keyboard Capslock"},"e31a":{"name":"Keyboard Hide"},"e31b":{"name":"Keyboard Return"},"e31c":{"name":"Keyboard Tab"},"e31d":{"name":"Keyboard Voice"},"eb47":{"name":"Kitchen"},"e892":{"name":"Label"},"e893":{"name":"Label Outline"},"e3f7":{"name":"Landscape"},"e894":{"name":"Language"},"e31e":{"name":"Laptop"},"e31f":{"name":"Laptop Chromebook"},"e320":{"name":"Laptop Mac"},"e321":{"name":"Laptop Windows"},"e5dd":{"name":"Last Page"},"e895":{"name":"Launch"},"e53b":{"name":"Layers"},"e53c":{"name":"Layers Clear"},"e3f8":{"name":"Leak Add"},"e3f9":{"name":"Leak Remove"},"e3fa":{"name":"Lens"},"e02e":{"name":"Library Add"},"e02f":{"name":"Library Books"},"e030":{"name":"Library Music"},"e90f":{"name":"Lightbulb Outline"},"e919":{"name":"Line Style"},"e91a":{"name":"Line Weight"},"e260":{"name":"Linear Scale"},"e157":{"name":"Link"},"e438":{"name":"Linked Camera"},"e896":{"name":"List"},"e0c6":{"name":"Live Help"},"e639":{"name":"Live Tv"},"e53f":{"name":"Local Activity"},"e53d":{"name":"Local Airport"},"e53e":{"name":"Local Atm"},"e540":{"name":"Local Bar"},"e541":{"name":"Local Cafe"},"e542":{"name":"Local Car Wash"},"e543":{"name":"Local Convenience Store"},"e556":{"name":"Local Dining"},"e544":{"name":"Local Drink"},"e545":{"name":"Local Florist"},"e546":{"name":"Local Gas Station"},"e547":{"name":"Local Grocery Store"},"e548":{"name":"Local Hospital"},"e549":{"name":"Local Hotel"},"e54a":{"name":"Local Laundry Service"},"e54b":{"name":"Local Library"},"e54c":{"name":"Local Mall"},"e54d":{"name":"Local Movies"},"e54e":{"name":"Local Offer"},"e54f":{"name":"Local Parking"},"e550":{"name":"Local Pharmacy"},"e551":{"name":"Local Phone"},"e552":{"name":"Local Pizza"},"e553":{"name":"Local Play"},"e554":{"name":"Local Post Office"},"e555":{"name":"Local Printshop"},"e557":{"name":"Local See"},"e558":{"name":"Local Shipping"},"e559":{"name":"Local Taxi"},"e7f1":{"name":"Location City"},"e1b6":{"name":"Location Disabled"},"e0c7":{"name":"Location Off"},"e0c8":{"name":"Location On"},"e1b7":{"name":"Location Searching"},"e897":{"name":"Lock"},"e898":{"name":"Lock Open"},"e899":{"name":"Lock Outline"},"e3fc":{"name":"Looks"},"e3fb":{"name":"Looks 3"},"e3fd":{"name":"Looks 4"},"e3fe":{"name":"Looks 5"},"e3ff":{"name":"Looks 6"},"e400":{"name":"Looks One"},"e401":{"name":"Looks Two"},"e028":{"name":"Loop"},"e402":{"name":"Loupe"},"e16d":{"name":"Low Priority"},"e89a":{"name":"Loyalty"},"e158":{"name":"Mail"},"e0e1":{"name":"Mail Outline"},"e55b":{"name":"Map"},"e159":{"name":"Markunread"},"e89b":{"name":"Markunread Mailbox"},"e322":{"name":"Memory"},"e5d2":{"name":"Menu"},"e252":{"name":"Merge Type"},"e0c9":{"name":"Message"},"e029":{"name":"Mic"},"e02a":{"name":"Mic None"},"e02b":{"name":"Mic Off"},"e618":{"name":"Mms"},"e253":{"name":"Mode Comment"},"e254":{"name":"Mode Edit"},"e263":{"name":"Monetization On"},"e25c":{"name":"Money Off"},"e403":{"name":"Monochrome Photos"},"e7f2":{"name":"Mood"},"e7f3":{"name":"Mood Bad"},"e619":{"name":"More"},"e5d3":{"name":"More Horiz"},"e5d4":{"name":"More Vert"},"e91b":{"name":"Motorcycle"},"e323":{"name":"Mouse"},"e168":{"name":"Move To Inbox"},"e02c":{"name":"Movie"},"e404":{"name":"Movie Creation"},"e43a":{"name":"Movie Filter"},"e6df":{"name":"Multiline Chart"},"e405":{"name":"Music Note"},"e063":{"name":"Music Video"},"e55c":{"name":"My Location"},"e406":{"name":"Nature"},"e407":{"name":"Nature People"},"e408":{"name":"Navigate Before"},"e409":{"name":"Navigate Next"},"e55d":{"name":"Navigation"},"e569":{"name":"Near Me"},"e1b9":{"name":"Network Cell"},"e640":{"name":"Network Check"},"e61a":{"name":"Network Locked"},"e1ba":{"name":"Network Wifi"},"e031":{"name":"New Releases"},"e16a":{"name":"Next Week"},"e1bb":{"name":"Nfc"},"e641":{"name":"No Encryption"},"e0cc":{"name":"No Sim"},"e033":{"name":"Not Interested"},"e06f":{"name":"Note"},"e89c":{"name":"Note Add"},"e7f4":{"name":"Notifications"},"e7f7":{"name":"Notifications Active"},"e7f5":{"name":"Notifications None"},"e7f6":{"name":"Notifications Off"},"e7f8":{"name":"Notifications Paused"},"e90a":{"name":"Offline Pin"},"e63a":{"name":"Ondemand Video"},"e91c":{"name":"Opacity"},"e89d":{"name":"Open In Browser"},"e89e":{"name":"Open In New"},"e89f":{"name":"Open With"},"e7f9":{"name":"Pages"},"e8a0":{"name":"Pageview"},"e40a":{"name":"Palette"},"e925":{"name":"Pan Tool"},"e40b":{"name":"Panorama"},"e40c":{"name":"Panorama Fish Eye"},"e40d":{"name":"Panorama Horizontal"},"e40e":{"name":"Panorama Vertical"},"e40f":{"name":"Panorama Wide Angle"},"e7fa":{"name":"Party Mode"},"e034":{"name":"Pause"},"e035":{"name":"Pause Circle Filled"},"e036":{"name":"Pause Circle Outline"},"e8a1":{"name":"Payment"},"e7fb":{"name":"People"},"e7fc":{"name":"People Outline"},"e8a2":{"name":"Perm Camera Mic"},"e8a3":{"name":"Perm Contact Calendar"},"e8a4":{"name":"Perm Data Setting"},"e8a5":{"name":"Perm Device Information"},"e8a6":{"name":"Perm Identity"},"e8a7":{"name":"Perm Media"},"e8a8":{"name":"Perm Phone Msg"},"e8a9":{"name":"Perm Scan Wifi"},"e7fd":{"name":"Person"},"e7fe":{"name":"Person Add"},"e7ff":{"name":"Person Outline"},"e55a":{"name":"Person Pin"},"e56a":{"name":"Person Pin Circle"},"e63b":{"name":"Personal Video"},"e91d":{"name":"Pets"},"e0cd":{"name":"Phone"},"e324":{"name":"Phone Android"},"e61b":{"name":"Phone Bluetooth Speaker"},"e61c":{"name":"Phone Forwarded"},"e61d":{"name":"Phone In Talk"},"e325":{"name":"Phone Iphone"},"e61e":{"name":"Phone Locked"},"e61f":{"name":"Phone Missed"},"e620":{"name":"Phone Paused"},"e326":{"name":"Phonelink"},"e0db":{"name":"Phonelink Erase"},"e0dc":{"name":"Phonelink Lock"},"e327":{"name":"Phonelink Off"},"e0dd":{"name":"Phonelink Ring"},"e0de":{"name":"Phonelink Setup"},"e410":{"name":"Photo"},"e411":{"name":"Photo Album"},"e412":{"name":"Photo Camera"},"e43b":{"name":"Photo Filter"},"e413":{"name":"Photo Library"},"e432":{"name":"Photo Size Select Actual"},"e433":{"name":"Photo Size Select Large"},"e434":{"name":"Photo Size Select Small"},"e415":{"name":"Picture As Pdf"},"e8aa":{"name":"Picture In Picture"},"e911":{"name":"Picture In Picture Alt"},"e6c4":{"name":"Pie Chart"},"e6c5":{"name":"Pie Chart Outlined"},"e55e":{"name":"Pin Drop"},"e55f":{"name":"Place"},"e037":{"name":"Play Arrow"},"e038":{"name":"Play Circle Filled"},"e039":{"name":"Play Circle Outline"},"e906":{"name":"Play For Work"},"e03b":{"name":"Playlist Add"},"e065":{"name":"Playlist Add Check"},"e05f":{"name":"Playlist Play"},"e800":{"name":"Plus One"},"e801":{"name":"Poll"},"e8ab":{"name":"Polymer"},"eb48":{"name":"Pool"},"e0ce":{"name":"Portable Wifi Off"},"e416":{"name":"Portrait"},"e63c":{"name":"Power"},"e336":{"name":"Power Input"},"e8ac":{"name":"Power Settings New"},"e91e":{"name":"Pregnant Woman"},"e0df":{"name":"Present To All"},"e8ad":{"name":"Print"},"e645":{"name":"Priority High"},"e80b":{"name":"Public"},"e255":{"name":"Publish"},"e8ae":{"name":"Query Builder"},"e8af":{"name":"Question Answer"},"e03c":{"name":"Queue"},"e03d":{"name":"Queue Music"},"e066":{"name":"Queue Play Next"},"e03e":{"name":"Radio"},"e837":{"name":"Radio Button Checked"},"e836":{"name":"Radio Button Unchecked"},"e560":{"name":"Rate Review"},"e8b0":{"name":"Receipt"},"e03f":{"name":"Recent Actors"},"e91f":{"name":"Record Voice Over"},"e8b1":{"name":"Redeem"},"e15a":{"name":"Redo"},"e5d5":{"name":"Refresh"},"e15b":{"name":"Remove"},"e15c":{"name":"Remove Circle"},"e15d":{"name":"Remove Circle Outline"},"e067":{"name":"Remove From Queue"},"e417":{"name":"Remove Red Eye"},"e928":{"name":"Remove Shopping Cart"},"e8fe":{"name":"Reorder"},"e040":{"name":"Repeat"},"e041":{"name":"Repeat One"},"e042":{"name":"Replay"},"e059":{"name":"Replay 10"},"e05a":{"name":"Replay 30"},"e05b":{"name":"Replay 5"},"e15e":{"name":"Reply"},"e15f":{"name":"Reply All"},"e160":{"name":"Report"},"e8b2":{"name":"Report Problem"},"e56c":{"name":"Restaurant"},"e561":{"name":"Restaurant Menu"},"e8b3":{"name":"Restore"},"e929":{"name":"Restore Page"},"e0d1":{"name":"Ring Volume"},"e8b4":{"name":"Room"},"eb49":{"name":"Room Service"},"e418":{"name":"Rotate 90 Degrees Ccw"},"e419":{"name":"Rotate Left"},"e41a":{"name":"Rotate Right"},"e920":{"name":"Rounded Corner"},"e328":{"name":"Router"},"e921":{"name":"Rowing"},"e0e5":{"name":"Rss Feed"},"e642":{"name":"Rv Hookup"},"e562":{"name":"Satellite"},"e161":{"name":"Save"},"e329":{"name":"Scanner"},"e8b5":{"name":"Schedule"},"e80c":{"name":"School"},"e1be":{"name":"Screen Lock Landscape"},"e1bf":{"name":"Screen Lock Portrait"},"e1c0":{"name":"Screen Lock Rotation"},"e1c1":{"name":"Screen Rotation"},"e0e2":{"name":"Screen Share"},"e623":{"name":"Sd Card"},"e1c2":{"name":"Sd Storage"},"e8b6":{"name":"Search"},"e32a":{"name":"Security"},"e162":{"name":"Select All"},"e163":{"name":"Send"},"e811":{"name":"Sentiment Dissatisfied"},"e812":{"name":"Sentiment Neutral"},"e813":{"name":"Sentiment Satisfied"},"e814":{"name":"Sentiment Very Dissatisfied"},"e815":{"name":"Sentiment Very Satisfied"},"e8b8":{"name":"Settings"},"e8b9":{"name":"Settings Applications"},"e8ba":{"name":"Settings Backup Restore"},"e8bb":{"name":"Settings Bluetooth"},"e8bd":{"name":"Settings Brightness"},"e8bc":{"name":"Settings Cell"},"e8be":{"name":"Settings Ethernet"},"e8bf":{"name":"Settings Input Antenna"},"e8c0":{"name":"Settings Input Component"},"e8c1":{"name":"Settings Input Composite"},"e8c2":{"name":"Settings Input Hdmi"},"e8c3":{"name":"Settings Input Svideo"},"e8c4":{"name":"Settings Overscan"},"e8c5":{"name":"Settings Phone"},"e8c6":{"name":"Settings Power"},"e8c7":{"name":"Settings Remote"},"e1c3":{"name":"Settings System Daydream"},"e8c8":{"name":"Settings Voice"},"e80d":{"name":"Share"},"e8c9":{"name":"Shop"},"e8ca":{"name":"Shop Two"},"e8cb":{"name":"Shopping Basket"},"e8cc":{"name":"Shopping Cart"},"e261":{"name":"Short Text"},"e6e1":{"name":"Show Chart"},"e043":{"name":"Shuffle"},"e1c8":{"name":"Signal Cellular 4 Bar"},"e1cd":{"name":"Signal Cellular Connected No Internet 4 Bar"},"e1ce":{"name":"Signal Cellular No Sim"},"e1cf":{"name":"Signal Cellular Null"},"e1d0":{"name":"Signal Cellular Off"},"e1d8":{"name":"Signal Wifi 4 Bar"},"e1d9":{"name":"Signal Wifi 4 Bar Lock"},"e1da":{"name":"Signal Wifi Off"},"e32b":{"name":"Sim Card"},"e624":{"name":"Sim Card Alert"},"e044":{"name":"Skip Next"},"e045":{"name":"Skip Previous"},"e41b":{"name":"Slideshow"},"e068":{"name":"Slow Motion Video"},"e32c":{"name":"Smartphone"},"eb4a":{"name":"Smoke Free"},"eb4b":{"name":"Smoking Rooms"},"e625":{"name":"Sms"},"e626":{"name":"Sms Failed"},"e046":{"name":"Snooze"},"e164":{"name":"Sort"},"e053":{"name":"Sort By Alpha"},"eb4c":{"name":"Spa"},"e256":{"name":"Space Bar"},"e32d":{"name":"Speaker"},"e32e":{"name":"Speaker Group"},"e8cd":{"name":"Speaker Notes"},"e92a":{"name":"Speaker Notes Off"},"e0d2":{"name":"Speaker Phone"},"e8ce":{"name":"Spellcheck"},"e838":{"name":"Star"},"e83a":{"name":"Star Border"},"e839":{"name":"Star Half"},"e8d0":{"name":"Stars"},"e0d3":{"name":"Stay Current Landscape"},"e0d4":{"name":"Stay Current Portrait"},"e0d5":{"name":"Stay Primary Landscape"},"e0d6":{"name":"Stay Primary Portrait"},"e047":{"name":"Stop"},"e0e3":{"name":"Stop Screen Share"},"e1db":{"name":"Storage"},"e8d1":{"name":"Store"},"e563":{"name":"Store Mall Directory"},"e41c":{"name":"Straighten"},"e56e":{"name":"Streetview"},"e257":{"name":"Strikethrough S"},"e41d":{"name":"Style"},"e5d9":{"name":"Subdirectory Arrow Left"},"e5da":{"name":"Subdirectory Arrow Right"},"e8d2":{"name":"Subject"},"e064":{"name":"Subscriptions"},"e048":{"name":"Subtitles"},"e56f":{"name":"Subway"},"e8d3":{"name":"Supervisor Account"},"e049":{"name":"Surround Sound"},"e0d7":{"name":"Swap Calls"},"e8d4":{"name":"Swap Horiz"},"e8d5":{"name":"Swap Vert"},"e8d6":{"name":"Swap Vertical Circle"},"e41e":{"name":"Switch Camera"},"e41f":{"name":"Switch Video"},"e627":{"name":"Sync"},"e628":{"name":"Sync Disabled"},"e629":{"name":"Sync Problem"},"e62a":{"name":"System Update"},"e8d7":{"name":"System Update Alt"},"e8d8":{"name":"Tab"},"e8d9":{"name":"Tab Unselected"},"e32f":{"name":"Tablet"},"e330":{"name":"Tablet Android"},"e331":{"name":"Tablet Mac"},"e420":{"name":"Tag Faces"},"e62b":{"name":"Tap And Play"},"e564":{"name":"Terrain"},"e262":{"name":"Text Fields"},"e165":{"name":"Text Format"},"e0d8":{"name":"Textsms"},"e421":{"name":"Texture"},"e8da":{"name":"Theaters"},"e8db":{"name":"Thumb Down"},"e8dc":{"name":"Thumb Up"},"e8dd":{"name":"Thumbs Up Down"},"e62c":{"name":"Time To Leave"},"e422":{"name":"Timelapse"},"e922":{"name":"Timeline"},"e425":{"name":"Timer"},"e423":{"name":"Timer 10"},"e424":{"name":"Timer 3"},"e426":{"name":"Timer Off"},"e264":{"name":"Title"},"e8de":{"name":"Toc"},"e8df":{"name":"Today"},"e8e0":{"name":"Toll"},"e427":{"name":"Tonality"},"e913":{"name":"Touch App"},"e332":{"name":"Toys"},"e8e1":{"name":"Track Changes"},"e565":{"name":"Traffic"},"e570":{"name":"Train"},"e571":{"name":"Tram"},"e572":{"name":"Transfer Within A Station"},"e428":{"name":"Transform"},"e8e2":{"name":"Translate"},"e8e3":{"name":"Trending Down"},"e8e4":{"name":"Trending Flat"},"e8e5":{"name":"Trending Up"},"e429":{"name":"Tune"},"e8e6":{"name":"Turned In"},"e8e7":{"name":"Turned In Not"},"e333":{"name":"Tv"},"e169":{"name":"Unarchive"},"e166":{"name":"Undo"},"e5d6":{"name":"Unfold Less"},"e5d7":{"name":"Unfold More"},"e923":{"name":"Update"},"e1e0":{"name":"Usb"},"e8e8":{"name":"Verified User"},"e258":{"name":"Vertical Align Bottom"},"e259":{"name":"Vertical Align Center"},"e25a":{"name":"Vertical Align Top"},"e62d":{"name":"Vibration"},"e070":{"name":"Video Call"},"e071":{"name":"Video Label"},"e04a":{"name":"Video Library"},"e04b":{"name":"Videocam"},"e04c":{"name":"Videocam Off"},"e338":{"name":"Videogame Asset"},"e8e9":{"name":"View Agenda"},"e8ea":{"name":"View Array"},"e8eb":{"name":"View Carousel"},"e8ec":{"name":"View Column"},"e42a":{"name":"View Comfy"},"e42b":{"name":"View Compact"},"e8ed":{"name":"View Day"},"e8ee":{"name":"View Headline"},"e8ef":{"name":"View List"},"e8f0":{"name":"View Module"},"e8f1":{"name":"View Quilt"},"e8f2":{"name":"View Stream"},"e8f3":{"name":"View Week"},"e435":{"name":"Vignette"},"e8f4":{"name":"Visibility"},"e8f5":{"name":"Visibility Off"},"e62e":{"name":"Voice Chat"},"e0d9":{"name":"Voicemail"},"e04d":{"name":"Volume Down"},"e04e":{"name":"Volume Mute"},"e04f":{"name":"Volume Off"},"e050":{"name":"Volume Up"},"e0da":{"name":"Vpn Key"},"e62f":{"name":"Vpn Lock"},"e1bc":{"name":"Wallpaper"},"e002":{"name":"Warning"},"e334":{"name":"Watch"},"e924":{"name":"Watch Later"},"e42c":{"name":"Wb Auto"},"e42d":{"name":"Wb Cloudy"},"e42e":{"name":"Wb Incandescent"},"e436":{"name":"Wb Iridescent"},"e430":{"name":"Wb Sunny"},"e63d":{"name":"Wc"},"e051":{"name":"Web"},"e069":{"name":"Web Asset"},"e16b":{"name":"Weekend"},"e80e":{"name":"Whatshot"},"e1bd":{"name":"Widgets"},"e63e":{"name":"Wifi"},"e1e1":{"name":"Wifi Lock"},"e1e2":{"name":"Wifi Tethering"},"e8f9":{"name":"Work"},"e25b":{"name":"Wrap Text"},"e8fa":{"name":"Youtube Searched For"},"e8ff":{"name":"Zoom In"},"e900":{"name":"Zoom Out"},"e56b":{"name":"Zoom Out Map"}}} \ No newline at end of file diff --git a/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff new file mode 100644 index 0000000..b648a3e Binary files /dev/null and b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff differ diff --git a/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff2 b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff2 new file mode 100644 index 0000000..9fa2112 Binary files /dev/null and b/soulbook/static/md/icons/material-icons/MaterialIcons-Regular.woff2 differ diff --git a/soulbook/static/md/js/index.js b/soulbook/static/md/js/index.js new file mode 100644 index 0000000..4a893e5 --- /dev/null +++ b/soulbook/static/md/js/index.js @@ -0,0 +1,56 @@ +/** + * Created by howie on 12/08/2017. + */ + +var inst_drawer = new mdui.Drawer('#drawer'); +document.getElementById('toggle').addEventListener('click', function () { + inst_drawer.toggle(); +}); + +var tab = new mdui.Tab('#owl-login-tab'); +mdui.JQ('#owl-login').on('open.mdui.dialog', function () { + tab.handleUpdate(); +}); + +var userStatus = { + logout: function () { + $.ajax({ + type: "get", + contentType: "application/json", + url: "/operate/logout", + dataType: 'json', + success: function (data) { + if (data.status == 1) { + window.location.reload() + } + } + }); + }, + login: function () { + var owllook_user = $("#owllook_user").val(); + var owllook_pass = $("#owllook_pass").val(); + if (owllook_user == "" || owllook_pass == "") { + alert('不能有内容为空'); + } else { + var login_pd = {'user': owllook_user, 'pwd': owllook_pass}; + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/login", + data: login_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + window.location.reload() + } + if (data.status == -1) { + alert('用户名错误'); + } + if (data.status == -2) { + alert('密码错误'); + } + } + }); + } + } +}; \ No newline at end of file diff --git a/soulbook/static/md/js/noti.js b/soulbook/static/md/js/noti.js new file mode 100644 index 0000000..a06b977 --- /dev/null +++ b/soulbook/static/md/js/noti.js @@ -0,0 +1,62 @@ +/** + * Created by howie on 12/08/2017. + */ + +var inst_panel = new mdui.Panel('#panel'); + + +$("#authorBtn").click(function () { + var author_name = $("[name='author_name']").val(); + var author_pd = {"author_name": author_name}; + if (author_name == "") { + mdui.snackbar({ + message: '请输入作者名', + timeout: 3000 + }); + } else { + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/author_notification", + data: author_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + mdui.snackbar({ + message: '添加作者成功', + onClose: function () { + location.reload(); + } + }); + } + if (data.status == -1) { + alert('您还没有登录'); + } + if (data.status == 2) { + mdui.snackbar({ + message: '暂无该作者', + timeout: 2000 + }); + } + if (data.status == 3) { + mdui.snackbar({ + message: '作者已添加', + timeout: 2000 + }); + } + if (data.status == 4) { + mdui.snackbar({ + message: '只能添加五位作者', + timeout: 2000 + }); + } + if (data.status == 0) { + mdui.snackbar({ + message: '操作失败', + timeout: 1000 + }); + } + } + }); + } +}); \ No newline at end of file diff --git a/soulbook/static/md/js/setting.js b/soulbook/static/md/js/setting.js new file mode 100644 index 0000000..57500a8 --- /dev/null +++ b/soulbook/static/md/js/setting.js @@ -0,0 +1,88 @@ +/** + * Created by howie on 29/07/2017. + */ +var inst_panel = new mdui.Panel('#panel'); + +document.getElementById('email-close').addEventListener('click', function () { + inst_panel.close('#item-email'); +}); +document.getElementById('pass-close').addEventListener('click', function () { + inst_panel.close('#item-pass'); +}); + +$("#passSubmitBtn").click(function () { + var old_pass = $("[name='old_pass']").val(); + var new_pass = $("[name='new_pass']").val(); + var new_pass1 = $("[name='new_pass1']").val(); + $(".pass-error").css("visibility", "hidden"); + if (old_pass == "" || new_pass == "" || new_pass1 == "") { + $(".pass-error").text("密码不能为空"); + $(".pass-error").css("visibility", "visible"); + } else if (new_pass != new_pass1) { + $(".pass-error").text("两次密码输入不一致"); + $(".pass-error").css("visibility", "visible"); + } else if (new_pass.length < 6) { + $(".pass-error").text("密码不能小于6位数"); + $(".pass-error").css("visibility", "visible"); + } else { + var pass_pd = {"new_pass": new_pass, "old_pass": old_pass}; + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/change_pass", + data: pass_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + mdui.snackbar({ + message: '密码修改成功', + timeout: 3000 + }); + inst_panel.close('#item-pass'); + } + if (data.status == -2) { + $(".pass-error").text("原始密码错误"); + $(".pass-error").css("visibility", "visible"); + } + if (data.status == -1) { + alert('您还没有登录'); + } + } + }); + } + +}); + +$("#emailSubmitBtn").click(function () { + var email = $("[name='email']").val(); + var email_reg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/; + var str_email = email_reg.test(email); + $(".email-error").css("visibility", "hidden"); + if (str_email) { + var email_pd = {"email": email}; + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/change_email", + data: email_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + inst_panel.close('#item-email'); + mdui.snackbar({ + message: '邮箱修改成功', + timeout: 3000 + }); + $("#owl-email").html(email); + //location.reload(); + } + if (data.status == -1) { + alert('您还没有登录'); + } + } + }); + } else { + $(".email-error").text("邮箱格式错误"); + $(".email-error").css("visibility", "visible"); + } +}); \ No newline at end of file diff --git a/soulbook/static/md/js/theme.js b/soulbook/static/md/js/theme.js new file mode 100644 index 0000000..3488e02 --- /dev/null +++ b/soulbook/static/md/js/theme.js @@ -0,0 +1,23 @@ +/** + * Created by howie on 06/08/2017. + */ + +// $("#soulbook-light").checked(function () { +// alert('hello') +// }); +// +$("#themeBtn").click(function () { + var val = $('input:radio[name="owl-theme-layout"]:checked').val(); + if (val == null) { + alert("干啥呢!"); + return false; + } + else { + if (val == 'dark') { + $("body").addClass("mdui-theme-layout-dark"); + } else { + $("body").removeClass("mdui-theme-layout-dark"); + } + } +}); + diff --git a/soulbook/static/novels/css/chapter.css b/soulbook/static/novels/css/chapter.css new file mode 100644 index 0000000..850d57b --- /dev/null +++ b/soulbook/static/novels/css/chapter.css @@ -0,0 +1,169 @@ +body { + background-color: #E5E4DB !important +} + +.all-chapter { + margin-bottom: 50px; +} + +#chapterlist .lm, .ablum_read, .TabCss .p10, .TabCss .h10, div.mb_21b, .booktext li center a, #ClassTitle, .bookurl, .booklist .vt, #bookname, #ad01, .shucansu, .samewriter, .bd > span, .bdsub > dl > dt, .all_ad, li.volume, #list > .notice, .cover, #new_chapter, .con_top, #sidebar, #fmimg, .tjlist, #listtj, #intro > p:last-child, #intro, .star_title, .starlist { + display: none !important; +} + +.mulu h1, .readout h1, #maininfo, .bdsub dd h1 { + background: #F6F4EC; + border-radius: 15px; + margin-top: 35px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding-bottom: 25px; +} + +.mulu h1, .readout h1 { + text-align: center; + line-height: 60px; + height: 60px; +} + +.bdsub dd h1 { + text-align: center; + padding-top: 20px; +} + +#info h1 { + text-align: center; + padding-top: 20px; + color: rgba(50, 68, 84, 0.61); +} + +#info p { + float: left; + margin-top: 25px; + width: 25%; + margin-left: 6%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* 区域1 */ +.chapters, .listmain, .story_list_m62topxs, .directory_con, .book-list, #yuedu table, .leftList, table.css, .dirconone, .book_list, #readerlist, .liebiao_bottom dl, .insert_list dl, .uclist, .list_box, .chapter, .mulu, #chapterslist, .booktext, #chapterslist dl, .box_chap, #book dl, .centent, #chapterlist, .booklist, #at, table.border, .zjlist, .mulu_list, .acss, .list, #xslist ul, .dirlist, .list-chapter, .list_box ul, #defaulthtml4 table, .article-list > dl, .update, #list .box, .bookcontent > dl, .listmain > dl, .ml_main > dl, #list dl, #chapter_list, .chapterlist, .mt10, .catalog, #readerlists, .readout table { + overflow: hidden; + padding-bottom: 1px; + margin: auto; + background: #F6F4EC; + margin-top: 5px; + border-radius: 15px; + margin-bottom: 20px; +} + +table.border, .mulu_list, #list, .article-list { + margin-top: 35px; +} + +/* 区域2 */ +.ml_list h3, .story-catalog h3.catalog-title, .listmain dl dt, .list .topfill h1, .story_list_m62topxs h2, .directory_con .tit, #yuedu table tr th, table.css td.vcss, .book_list h5, #main, .jm h3, #chapterlist .ui-title h3, #readerlist .topa, .insert_list dt, .uclist dt, .list_box td.list_line, .chapter .name, .mulu .mulu-title, #list dl dt h2, #chapterslist dt, .box_chap .t, .centent .list, .booklist .v, table.border .M, .zjlist dt, td.tcss, .acss .vcss, .list .tit, #xslist ul span, .article-list > dl > dt, .update .hd > #list .box-head, .bookcontent dt, .listmain dt, .bdsub tr th, #readerlists > ul > h3, #cl_content > dl > dt, #list dt, .panel > h2, .TabCss dt, .chapterlist > dt, .vcss, .readout td.vcss .chapter_list_chapter_title, .mt10 > .title > h2 { + background: none repeat scroll 0 0 rgba(214, 212, 200, 0.89) !important; + display: inline !important; + float: left !important; + font-size: 14px !important; + line-height: 28px !important; + overflow: hidden !important; + text-align: center !important; + vertical-align: middle !important; + width: 100% !important; + margin: auto auto 5px !important; + padding: 5px 10px !important; +} + +table.css { + width: 100%; +} + +/* 区域3 */ +.ml_list ul li, ul.chaw_c li, .mod-article-list li, .chapter, .story-catalog .catalog-list ul li, #chapter_list .chapter_list_chapterx, .listmain dl dd, .list .block_ul li, .story_list_m62topxs li, .directory_con .con li, .book-list li, #yuedu table tr li, .leftList li, table.css td.ccss, .dirconone ul li, .boxm ul li, .zjlist4 li, ul.pox li, .dccss, #readerlist ul li, .liebiao_bottom dd, .insert_list li, .uclist dd, .list_box td.list_lineb, .chapter li, #category .content .combox h2, .mulu li, .booktext li, #chapterslist dd, .book li, .art_listmain_main span, .box_chap li, #dirsort01 li, #book dd, .centent li, #chapterlist li, .booklist span, .box .zjlist4 li, .chapterlist li, #at td, .readout td, .zjlist dd, .mulu_list > li, .acss .ccss, .list .zl, #xslist ul li, .dirlist li, .list-chapter > li, .list_box ul li, #defaulthtml4 table .dccss, #defaulthtml4 table .dccss a, .article-list > dl > dd, .update .bd, #list td.swbt, .bookcontent dd, .listmain dd, .L, .ml_main > dl > dd, #readerlists > ul > li, .catalog > ul > li, .article_texttitleb > ul > li, #cl_content > dl > dd, #list dd, .panel li, .chapterlist > dd, td.ccss, .chapter_list_chapter, .book_list > ul > li { + border-bottom: 1px dashed #CCC; + display: inline; + float: left; + height: 25px; + line-height: 200%; + margin-bottom: 5px; + text-align: left; + text-indent: 10px; + vertical-align: middle; + width: 26%; + font-size: 15px !important; + margin-left: 5% !important; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.TabCss dd.col4 { + float: right; + margin-right: 4px; +} + +.TabCss dd { + width: 228px !important; + height: 28px; + line-height: 28px; + vertical-align: middle; + text-indent: 10px; + font-size: 12px; + background: #E7F4F4; + text-align: left; + margin-left: 5px; + margin-bottom: 5px; + float: left; + display: inline; + border: 1px solid #9FBFE2; + overflow: hidden; +} + +dd > a { + cursor: pointer; +} + +.panel { + margin-bottom: 20px; + background-color: #E5E4DB !important; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} + +tr > td.ccss, .L, ul#chapterlist li, .list_box td.list_lineb { + width: 19% !important; +} + +#at td.L { + width: 14% !important; +} + +.download-area, .list .part01, .list .part02, .list .topfill .right_juan, .pinglun, #main .kl, div#comment, #dirsort01 li strong, .notice, .zuixin10, .update h2, .bdsub dd h3, .bdsub .tags, .bdsub .tips { + display: none !important; +} + +.book_list > ul > li { + width: 20% !important; + float: right !important; +} + +.boxm ul li { + float: left !important; + width: 25% !important; +} + +#defaulthtml4 table .dccss { + width: 100% !important; +} + +@media (max-width: 960px) { + li.chapter, .list_box td.list_lineb, .chapter li, #category .content .combox h2, .mulu li, .booktext li, #chapterslist dd, .book li, .art_listmain_main span, .box_chap li, #dirsort01 li, #book dd, .centent li, #chapterlist li, .booklist span, .box .zjlist4 li, .chapterlist li, #at td, .readout td, .zjlist dd, .mulu_list > li, .acss .ccss, .list .zl, #xslist ul li, .dirlist li, .list-chapter > li, .list_box ul li, #defaulthtml4 table .dccss, .article-list > dl > dd, .update .bd, #list td.swbt, .bookcontent dd, .listmain dd, .L, .ml_main > dl > dd, #readerlists > ul > li, .catalog > ul > li, .article_texttitleb > ul > li, #cl_content > dl > dd, #list dd, .panel li, .chapterlist > dd, td.ccss, .chapter_list_chapter, .book_list > ul > li { + width: 100% !important; + } + +} + + diff --git a/soulbook/static/novels/css/content.css b/soulbook/static/novels/css/content.css new file mode 100644 index 0000000..b3c95be --- /dev/null +++ b/soulbook/static/novels/css/content.css @@ -0,0 +1,229 @@ +body { + background: url(../img/read_bg.png) #ede7da !important; + color: #333 !important; + font-family: "Microsoft YaHei" !important; +} + +.navbar { + background: rgb(242, 238, 230) !important; +} + +.search { + width: 200px; + height: 50px; +} + +.nav-search { + background: rgba(242, 238, 230, 0.58) !important; +} + +.search input { + font-family: PingFangSC-Regular, HelveticaNeue-Light, 'Helvetica Neue Light', 'Microsoft YaHei', sans-serif; + font-size: 14px; + position: absolute; + z-index: 1; + top: 20px; + right: 40px; + width: 175px; + height: 26px; + transition: border .5s; + border: none; + outline: none; + border-bottom: 1px solid #ccc; + background: 0 0; + -moz-transition: border .5s; /* Firefox 4 */ + -webkit-transition: border .5s; /* Safari and Chrome */ + -o-transition: border .5s; + +} + +.login > a { + margin-top: 8px; +} + +.btn-login { + border: 1px solid #d8d8d8 !important; + color: #b3acac; + background-color: #5c6165; +} + +.btn-login:hover, .btn-login:focus { + color: #fff !important; + background-color: #337ab7; + border-color: #2e6da4; +} + +.search input:focus { + top: 15px; + border-bottom: 3px solid #E67676; +} + +.all-content { + width: 65% !important; + box-shadow: 0 0 15px 0 #CCC; + background: #F6F4EC; + margin: 0 auto 30px; + border-radius: 3px; + padding-bottom: 10px; + min-height: 600px; + margin-bottom: 70px; + border: 1px solid #d8d8d8; + background: url(../img/read_content.png) !important; +} + +.title { + position: relative; +} + +.title > a.bookMark { + position: absolute; + background: url(../img/bookMark.png) no-repeat; + width: 20px; + height: 36px; + right: 30px; + top: -30px; + cursor: pointer; +} + +.title > a.bookMarkAct { + position: absolute; + background: url(../img/bookMark.png) no-repeat -20px 0; + width: 22px; + height: 39px; + right: 30px; + top: -30px; + cursor: pointer; +} + +.title > h2 { + width: 100%; + height: 40px; + line-height: 40px; + text-align: center; + margin-top: 30px; + margin-bottom: 30px; +} + +.show-content, pre { + width: 90%; + line-height: 1.8; + font-size: 16px !important; + padding-top: 16px; + min-height: 360px; + margin-left: 5%; + word-break: break-all; + font-family: "Microsoft YaHei"; + text-align: left; + position: relative; +} + +pre { + border: none !important; + background: none !important; +} + +.left-bar-list { + width: 60px; + position: fixed; + left: 12%; +} + +.left-bar-list .left-bar-flag { + position: relative; + z-index: 100; + cursor: pointer; + border: 1px solid rgba(0, 0, 0, .1); + border-top: none; + background: url("../img/left_bar_flag.png") +} + +.left-bar-list .left-bar-flag .glyphicon { + font-size: 18px; +} + +.left-bar-list .left-bar-flag a { + font: 12px/16px PingFangSC-Regular, '-apple-system', Simsun; + display: block; + width: 58px; + height: 58px; + transition: none; + text-align: center; + padding-top: 12px; + color: #999; + text-decoration: none; + color: rgba(0, 0, 0, .4) +} + +.left-bar-list .left-bar-flag a:hover, .add-color { + color: #E67676 !important; +} + +.show-content > p { + font-size: 16px !important; +} + +.show-content > #content { + text-align: left !important; +} + +.pre_next { + width: 100%; + height: 40px; + text-align: center; + margin-top: 20px; + margin-bottom: 10px; +} + +.pre_next > a:first-child { + margin-right: 40%; +} + +.pre_next > a { + cursor: pointer; + border: 1px solid rgba(90, 76, 76, 0.1); + color: #555; + background: rgba(0, 0, 0, .0001); +} + +.pre_next > a:hover { + border: 1px solid rgba(90, 76, 76, 0.1); + color: #E67676; + background: rgba(0, 0, 0, .0001); +} + +.move { + right: 5% !important; +} + +/*原网页上一章下一章等隐藏*/ + +.mingzhuPage, #htmlContent img, .chapter_Turnpage, #pageselect, .biao-qian-love-bd, #toplink, #tuijian, .chapter_Turnpage a, .bottem, .chapter h1, .content > center, .content p > strong, .content p > a { + display: none !important; +} + +@media (max-width: 1024px) { + .left-bar-list, #cloud-tie-wrapper { + display: none !important; + } + + .all-content { + width: 96% !important; + } + + .move { + display: none; + } + + .search input { + right: 0 !important; + left: 0 !important; + } + + .login > a { + margin-top: 10px !important; + } + + .title > h2 { + font-size: 20px; + } +} \ No newline at end of file diff --git a/soulbook/static/novels/css/index.css b/soulbook/static/novels/css/index.css new file mode 100644 index 0000000..d0f57e3 --- /dev/null +++ b/soulbook/static/novels/css/index.css @@ -0,0 +1,98 @@ +div.search { + width: 100%; + text-align: center; + height: 30px; + line-height: 30px; + +} + +div.logo { + padding-top: 10%; +} + +form.home_search { + height: 120px; + margin-top: 55px; +} + +div.search > .form-control { + width: 55%; + height: 40px; + line-height: 40px; +} + +div.search > .btn-default { + width: 110px; + height: 35px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#f1f1f1)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1); + -webkit-border-radius: 2px; + -webkit-user-select: none; + background-color: #f2f2f2; + border: 1px solid #f2f2f2; + border-radius: 2px; + color: #757575; + cursor: default; + font-size: 13px; + font-weight: bold; + margin: 30px 4px; + min-width: 54px; + padding: 0 12px; + text-align: center; +} + +div.search > .btn-default:hover { + border: 1px solid #c8c8c8; + background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f3f3f3)); + background-image: -webkit-linear-gradient(top, #ffffff, #f4f4f4); + background-color: #ffffff; + color: #474747; +} + +.login > .pull-right { + position: absolute; + right: 0; + top: 5px; + border: 1px solid #4285f4; + font-weight: bold; + outline: none; + background: #4285f4; + background: -webkit-linear-gradient(top, #4387fd, #4683ea); +} + +div.owl_search_ranking { + margin-top: 5%; + width: 55%; +} + +div.owl_search_ranking span { + display: inline-block; + font-size: 12px; + padding: .3em .6em .3em; + margin-bottom: 2px; +} + +@media (max-width: 767px) { + .logo { + padding-top: 28% !important; + } + + .logo img { + width: 100px; + height: 45px; + } + + div.owl_search_ranking { + display: none; + } +} + +@media (min-width: 992px) { + +} + +@media (min-width: 1680px) { + .logo { + padding-top: 20% !important; + } +} \ No newline at end of file diff --git a/soulbook/static/novels/css/main.css b/soulbook/static/novels/css/main.css new file mode 100644 index 0000000..30f3f50 --- /dev/null +++ b/soulbook/static/novels/css/main.css @@ -0,0 +1,163 @@ +/*style for login*/ +.header { + width: 100%; + height: 70px; +} + +.header > li { + list-style: none; +} + +.header .pull-right { + margin-top: 15px; + margin-right: 25px; +} + +.header .dropdown-menu > li > a { + padding: 2px 10px !important; +} + +.header .dropdown-menu .glyphicon { + top: 2px !important; +} + +.header .dropdown-menu { + min-width: 120px !important; +} + +.header span.personal-basic { + margin-left: 10px; +} + +/*style for footer*/ +div.footer > .navbar { + min-height: 40px !important; + margin-top: 40px; + margin-bottom: 0 !important; +} + +.message > .right { + float: left; + position: absolute; + right: 5%; +} + +.message > .left { + float: left; + position: absolute; + left: 2%; +} + +.message > .right > span, .message > .left > span { + text-align: center !important; + line-height: 40px !important; + height: 40px; +} + +.right > span > a, .message > .left > span > a { + color: #666; + margin-left: 25px; +} + +.donate { + width: 60%; + margin-top: 5%; + margin-left: 20%; +} + +.donate_lists { + width: 60%; + margin-top: 5%; + margin-left: 20%; + margin-bottom: 80px; +} + +.alert { + margin-bottom: 0 !important; +} + +.move { + position: fixed; + bottom: 60px; + right: 5px; + border: 1px solid #dcdcdc; + border-radius: 5px; +} + +.move > .move_up, .move > .move_down { + width: 48px; + height: 48px; + line-height: 40px; + border: 1px solid #dcdcdc; + background-color: #fff; + transition: .1s ease-in; + -webkit-transition: .1s ease-in; + -moz-transition: .1s ease-in; + -o-transition: .1s ease-in; + -ms-transition: .1s ease-in; + cursor: pointer; +} + +.move_up > span, .move_down > span { + width: 45px; + height: 43px; + line-height: 43px; + text-align: center; + font-size: 20px; + color: rgba(72, 70, 70, 0.71); +} + +.move > .move_up:hover, .move > .move_down:hover { + background-color: #f9f9f9; +} + +.move > .move_up > span:hover, .move > .move_down > span:hover { + color: #434343; +} + +.modal-dialog { + margin-top: 15% !important; +} + +.modal-dialog .modal-content { + width: 420px; + height: 250px; + margin-left: auto; + margin-right: auto; +} + +@media (max-width: 1024px) { + .right > span > a { + margin-left: 5px !important; + font-size: 12px; + } + + .modal-dialog .modal-content { + width: 80%; + min-width: 265px; + height: 260px; + margin-left: auto; + margin-right: auto; + } + + label { + padding-top: 8px !important; + } + + .navbar-header { + display: none !important; + } + + .login > a { + margin-top: 20px !important; + } + + .header .pull-right { + margin-top: 17px !important; + } + + /*登陆后排版*/ + .header li.pull-right { + margin-right: 0 !important; + } +} \ No newline at end of file diff --git a/soulbook/static/novels/css/register.css b/soulbook/static/novels/css/register.css new file mode 100644 index 0000000..b70403d --- /dev/null +++ b/soulbook/static/novels/css/register.css @@ -0,0 +1,81 @@ +body { + background-color: #f1f1f1 !important; +} + +.logo { + margin: 5% auto; +} + +.register { + width: 40%; + height: 100%; + background-color: white; + margin-top: 4%; + padding: 2rem 4rem; + box-shadow: 0 0 8px rgba(0, 0, 0, .1); +} + +.title { + text-align: center; + font-family: -apple-system, SF UI Display, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif; + font-weight: 700; + font-size: 3rem; + margin: 2rem 0; +} + +.input-group { + position: relative; + width: 100%; +} + +.sign { + padding: 4px 12px 4px 25px !important; + font-family: -apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif; + height: 50px !important; + font-size: 14px; + border: 1px solid #c8c8c8; + border-bottom: none; + width: 100%; + background-color: #f1f1f1; + outline: none +} + +.sign-last { + border-bottom: 1px solid #c8c8c8; + margin-bottom: 2rem; +} + +.icon { + position: absolute !important; + top: 17px !important; + left: 8px; + color: #969696; +} + +.submitBtn { + width: 100%; + margin-bottom: 3rem !important; + padding: 1rem 0 !important; +} + +.errorInfo { + color: red; + text-align: center; +} + +.errorHidden0, .errorHidden1, .errorHidden2, .errorHidden3 { + display: none; +} + +@media (max-width: 960px) { + .register { + width: 100%; + margin-top: -6%; + } +} + +@media (min-width: 1680px) { + .register { + margin-top: 20%; + } +} \ No newline at end of file diff --git a/soulbook/static/novels/css/result.css b/soulbook/static/novels/css/result.css new file mode 100644 index 0000000..ec60968 --- /dev/null +++ b/soulbook/static/novels/css/result.css @@ -0,0 +1,98 @@ +.navbar { + border-radius: 0 !important; + min-height: 65px !important; +} + +.result { + margin-right: 0 !important; + padding-right: 15px; + padding-left: 7%; + margin-bottom: 60px; +} + +.result > p { + padding-left: 4%; +} + +.form-control { + width: 90% !important; + height: 35px !important; + line-height: 35px !important; + margin-top: 20px; + border-radius: 0 !important; +} + +div.item > li { + margin-top: 15px; +} + +div.item > li > a { + color: #38556A; + font-size: 16px; +} + +.netloc { + display: block; + color: #006621; +} + +.result_item { + width: 35em; + height: 95px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding-left: 5%; + font-size: 15px; +} + +.tags { + position: relative; + margin-top: 5px; +} + +.parse-label { + padding: 0.3em .6em .3em !important; + margin-top: 3px; + position: absolute; + right: 0; +} + +.login { + min-height: 65px; + text-align: center; +} + +.login > a { + margin-top: 15px; +} + +.modal-dialog .form-control { + margin-top: 0 !important; +} + +@media (max-width: 767px) { + .result { + padding-right: 0 !important; + } + + .show-result { + margin-bottom: 40px; + } + + .col-xs-12 { + padding-left: 0 !important; + } + + .move { + display: none; + } + + .show-result .tags .label { + right: -10px !important; + } + + .netloc i { + font-size: 13px; + } +} \ No newline at end of file diff --git a/soulbook/static/novels/img/Thumbs.db b/soulbook/static/novels/img/Thumbs.db new file mode 100644 index 0000000..eb4f59d Binary files /dev/null and b/soulbook/static/novels/img/Thumbs.db differ diff --git a/soulbook/static/novels/img/basic_bg.0.4.png b/soulbook/static/novels/img/basic_bg.0.4.png new file mode 100644 index 0000000..3091a35 Binary files /dev/null and b/soulbook/static/novels/img/basic_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_base_bg.0.4.png b/soulbook/static/novels/img/body_base_bg.0.4.png new file mode 100644 index 0000000..e080f6f Binary files /dev/null and b/soulbook/static/novels/img/body_base_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_theme1_bg.0.4.png b/soulbook/static/novels/img/body_theme1_bg.0.4.png new file mode 100644 index 0000000..b178bab Binary files /dev/null and b/soulbook/static/novels/img/body_theme1_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_theme2_bg.0.4.png b/soulbook/static/novels/img/body_theme2_bg.0.4.png new file mode 100644 index 0000000..023ab3e Binary files /dev/null and b/soulbook/static/novels/img/body_theme2_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_theme3_bg.0.4.png b/soulbook/static/novels/img/body_theme3_bg.0.4.png new file mode 100644 index 0000000..0a6d348 Binary files /dev/null and b/soulbook/static/novels/img/body_theme3_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_theme5_bg.0.4.png b/soulbook/static/novels/img/body_theme5_bg.0.4.png new file mode 100644 index 0000000..8090fdb Binary files /dev/null and b/soulbook/static/novels/img/body_theme5_bg.0.4.png differ diff --git a/soulbook/static/novels/img/body_theme6_bg.0.4.png b/soulbook/static/novels/img/body_theme6_bg.0.4.png new file mode 100644 index 0000000..b3e72d0 Binary files /dev/null and b/soulbook/static/novels/img/body_theme6_bg.0.4.png differ diff --git a/soulbook/static/novels/img/bookMark.png b/soulbook/static/novels/img/bookMark.png new file mode 100644 index 0000000..b55808f Binary files /dev/null and b/soulbook/static/novels/img/bookMark.png differ diff --git a/soulbook/static/novels/img/donate.png b/soulbook/static/novels/img/donate.png new file mode 100644 index 0000000..1aa1328 Binary files /dev/null and b/soulbook/static/novels/img/donate.png differ diff --git a/soulbook/static/novels/img/favicon.ico b/soulbook/static/novels/img/favicon.ico new file mode 100644 index 0000000..390a99e Binary files /dev/null and b/soulbook/static/novels/img/favicon.ico differ diff --git a/soulbook/static/novels/img/favicon_old.ico b/soulbook/static/novels/img/favicon_old.ico new file mode 100644 index 0000000..52a5210 Binary files /dev/null and b/soulbook/static/novels/img/favicon_old.ico differ diff --git a/soulbook/static/novels/img/github_owl.png b/soulbook/static/novels/img/github_owl.png new file mode 100644 index 0000000..95c6074 Binary files /dev/null and b/soulbook/static/novels/img/github_owl.png differ diff --git a/soulbook/static/novels/img/lcxs.jpg b/soulbook/static/novels/img/lcxs.jpg new file mode 100644 index 0000000..8bbc19d Binary files /dev/null and b/soulbook/static/novels/img/lcxs.jpg differ diff --git a/soulbook/static/novels/img/lcxs_show.jpg b/soulbook/static/novels/img/lcxs_show.jpg new file mode 100644 index 0000000..b454c36 Binary files /dev/null and b/soulbook/static/novels/img/lcxs_show.jpg differ diff --git a/soulbook/static/novels/img/left_bar_flag.png b/soulbook/static/novels/img/left_bar_flag.png new file mode 100644 index 0000000..3091a35 Binary files /dev/null and b/soulbook/static/novels/img/left_bar_flag.png differ diff --git a/soulbook/static/novels/img/login.png b/soulbook/static/novels/img/login.png new file mode 100644 index 0000000..928ed88 Binary files /dev/null and b/soulbook/static/novels/img/login.png differ diff --git a/soulbook/static/novels/img/logo_home-bak.png b/soulbook/static/novels/img/logo_home-bak.png new file mode 100644 index 0000000..3e70364 Binary files /dev/null and b/soulbook/static/novels/img/logo_home-bak.png differ diff --git a/soulbook/static/novels/img/logo_home.png b/soulbook/static/novels/img/logo_home.png new file mode 100644 index 0000000..052d2a4 Binary files /dev/null and b/soulbook/static/novels/img/logo_home.png differ diff --git a/soulbook/static/novels/img/logo_search-bak.png b/soulbook/static/novels/img/logo_search-bak.png new file mode 100644 index 0000000..43f790e Binary files /dev/null and b/soulbook/static/novels/img/logo_search-bak.png differ diff --git a/soulbook/static/novels/img/logo_search.png b/soulbook/static/novels/img/logo_search.png new file mode 100644 index 0000000..052d2a4 Binary files /dev/null and b/soulbook/static/novels/img/logo_search.png differ diff --git a/soulbook/static/novels/img/logout.png b/soulbook/static/novels/img/logout.png new file mode 100644 index 0000000..8058753 Binary files /dev/null and b/soulbook/static/novels/img/logout.png differ diff --git a/soulbook/static/novels/img/npz_body_theme1_bg.0.4.png b/soulbook/static/novels/img/npz_body_theme1_bg.0.4.png new file mode 100644 index 0000000..b178bab Binary files /dev/null and b/soulbook/static/novels/img/npz_body_theme1_bg.0.4.png differ diff --git a/soulbook/static/novels/img/npz_theme_1_bg.0.4.png b/soulbook/static/novels/img/npz_theme_1_bg.0.4.png new file mode 100644 index 0000000..3aeb98d Binary files /dev/null and b/soulbook/static/novels/img/npz_theme_1_bg.0.4.png differ diff --git a/soulbook/static/novels/img/read_bg.png b/soulbook/static/novels/img/read_bg.png new file mode 100644 index 0000000..50eaf63 Binary files /dev/null and b/soulbook/static/novels/img/read_bg.png differ diff --git a/soulbook/static/novels/img/read_content.png b/soulbook/static/novels/img/read_content.png new file mode 100644 index 0000000..3091a35 Binary files /dev/null and b/soulbook/static/novels/img/read_content.png differ diff --git a/soulbook/static/novels/img/theme_1_bg.0.4.png b/soulbook/static/novels/img/theme_1_bg.0.4.png new file mode 100644 index 0000000..3aeb98d Binary files /dev/null and b/soulbook/static/novels/img/theme_1_bg.0.4.png differ diff --git a/soulbook/static/novels/img/theme_2_bg.0.4.png b/soulbook/static/novels/img/theme_2_bg.0.4.png new file mode 100644 index 0000000..b92ef5e Binary files /dev/null and b/soulbook/static/novels/img/theme_2_bg.0.4.png differ diff --git a/soulbook/static/novels/img/theme_3_bg.0.4.png b/soulbook/static/novels/img/theme_3_bg.0.4.png new file mode 100644 index 0000000..3604b68 Binary files /dev/null and b/soulbook/static/novels/img/theme_3_bg.0.4.png differ diff --git a/soulbook/static/novels/img/theme_5_bg.0.4.png b/soulbook/static/novels/img/theme_5_bg.0.4.png new file mode 100644 index 0000000..8a5dab1 Binary files /dev/null and b/soulbook/static/novels/img/theme_5_bg.0.4.png differ diff --git a/soulbook/static/novels/img/theme_6_bg.0.4.png b/soulbook/static/novels/img/theme_6_bg.0.4.png new file mode 100644 index 0000000..42267cf Binary files /dev/null and b/soulbook/static/novels/img/theme_6_bg.0.4.png differ diff --git a/soulbook/static/novels/img/wechat.jpg b/soulbook/static/novels/img/wechat.jpg new file mode 100644 index 0000000..d9a1327 Binary files /dev/null and b/soulbook/static/novels/img/wechat.jpg differ diff --git a/soulbook/static/novels/js/bookmarks.js b/soulbook/static/novels/js/bookmarks.js new file mode 100644 index 0000000..43e4138 --- /dev/null +++ b/soulbook/static/novels/js/bookmarks.js @@ -0,0 +1,29 @@ +/** + * Created by howie on 24/04/2017. + */ + +$(document).ready(function () { + $(".del-bookmark").click(function () { + var msg = "您真的确定要删除吗?请确认!"; + if (confirm(msg)) { + var bookmarkurl = $(this).find('a.bookmark_url').attr("data-value"); + var del_bm_pd = {"bookmarkurl": bookmarkurl}; + var del_bm_object = $(this).parent(); + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/delete_bookmark", + data: del_bm_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + del_bm_object.remove(); + } + if (data.status == -1) { + alert('您还没有登录'); + } + } + }); + } + }); +}); \ No newline at end of file diff --git a/soulbook/static/novels/js/books.js b/soulbook/static/novels/js/books.js new file mode 100644 index 0000000..e8edb4c --- /dev/null +++ b/soulbook/static/novels/js/books.js @@ -0,0 +1,29 @@ +/** + * Created by howie on 24/04/2017. + */ + +$(document).ready(function () { + $(".del-book").click(function () { + var msg = "您真的确定要删除吗?请确认!"; + if (confirm(msg)) { + var book_url = $(this).find('a.book_url').attr("data-value"); + var del_pd = {"book_url": book_url}; + var del_object = $(this).parent(); + $.ajax({ + type: "post", + contentType: "application/json", + url: "/operate/delete_book", + data: del_pd, + dataType: 'json', + success: function (data) { + if (data.status == 1) { + del_object.remove(); + } + if (data.status == -1) { + alert('您还没有登录'); + } + } + }); + } + }); +}); \ No newline at end of file diff --git a/soulbook/static/novels/js/content.js b/soulbook/static/novels/js/content.js new file mode 100644 index 0000000..2ec47d4 --- /dev/null +++ b/soulbook/static/novels/js/content.js @@ -0,0 +1,311 @@ +/* +Created By MSCSTSTS +2017-09-27 18:13:50 UTC+8 + + +Edit By MSCSTSTS +2017-12-02 19:36:17 UTC+8 ++触屏滑动切换上一章/下一章。 ++键盘左右方向键切换上一章/下一章。 + +*/ + + +//-------------------------------------------------------------------------------------------- + +/* +登录、书签、加入书库等功能,都是基于刷新或者ajax +所以仅需修改部分页面 +最后,将页面滑动到顶部即可。 +处理逻辑: + 1:页面加载,检查浏览器是否支持 ajax 和 sessionStorage + 2:查询下一章 和 上一章是否已被缓存,若没有,则进行 ajax 请求,并将对应页面存入sessionStorage + 3:点击上一章或下一章按钮以后,将缓存中的数据取出,重新渲染页面,回到顶部,修改浏览器url,并重新执行2 +需要缓存的条目: + 1:本页链接 data.url + 2:下一章链接 data.next_chapter."下一章" + 3:上一章链接 data.next_chapter."上一章" + 4:章节名 data.name + 5:小说名 data.novels_name + 6:正文内容 data.soup + 7:是否书签 data.bookmark +需要更新的页面内容: + 0:【索引项】 页面链接(相对地址) 由 缓存数据的本页链接 拼接产生 + 1:书签状态 #bookMark 的 class ,根据 缓存数据 的 是否书签来定义 class + 2:隐藏表单 page_url 的 value , 根据 缓存数据 的 本页链接 + 3:页面标题 page_title , 根据 缓存数据 的 章节名产生 + 4:正文文本 page_chapter_content ,根据 缓存数据 的 正文内容产生 + 5:上一章和下一章的地址 ,根据缓存数据的上一章链接和下一章链接产生 + 6:地址栏URL ,使用replaceState来更新,根据缓存数据的 本页链接 来拼接 +*/ + + +$(document).ready(function () { + + var page_btn_pre = $("div.pre_next > a:nth-child(1)"); + var page_btn_next = $("div.pre_next > a:nth-child(2)"); + var page_title = $("title"); + var page_chapter_name = $("#content_name"); + // var page_chapter_content = $(".show-content>div");//正文 + var page_chapter_content = $($(".show-content").children("*").get(0)); //正文 + var page_bookmark = $("#bookMark");//书签,需要修改样式 + var page_url = $("#url");//页面隐藏表单,本页地址 + + function get_chapter(n_url) { + $.ajax({ + //提交数据的类型 POST GET + type: "GET", + //提交的网址 + url: n_url, + //提交的数据 + data: {is_ajax: "owl_cache"}, + //返回数据的格式 + datatype: "json",//"xml", "html", "script", "json", "jsonp", "text". + //在请求之前调用的函数 + //beforeSend:function(){$("#msg").html("logining");}, + //成功返回之后调用的函数 + success: function (data) { + if (typeof data.name == "undefined") { + + } else { // 正确获取到数据 + var obj = { + url: data.url, + pre_chapter_url: transform(data.next_chapter)[0], + next_chapter_url: transform(data.next_chapter)[1], + name: data.name, + novels_name: data.novels_name, + content: data.soup, + bookmark: data.bookmark, + chapter_url: data.chapter_url + }; + store(n_url, obj); + } + }, + //调用执行后调用的函数 + complete: function () { + + }, + //调用出错执行的函数 + error: function () { + //请求出错处理 + } + }); + } + + function transform(obj) { + var arr = []; + for (var item in obj) { + arr.push(obj[item]); + } + return arr; + } + + function store(n_url, obj) { + window.sessionStorage.setItem(n_url, JSON.stringify(obj)); + } + + function ajax_content_init() { + if (isSupport()) { + log("支持sessionStorage,将为你开启页面缓存"); + search_url = window.location; + // 来自书签页面的跳转不进行缓存 + if (search_url.search.indexOf("from_bookmarks") > 0) { + log('来自书签页面的跳转不进行缓存') + } else { + ajax_task(); + page_bookmark.bind("click", function () { + cache_reset(); + }); + } + } else { + //不支持 + return; + } + } + + function ajax_task() { + store_query();//检查是否已缓存 + page_btn_pre.unbind("click"); + page_btn_pre.click(function () { + event.preventDefault(); + if (window.sessionStorage.getItem(page_btn_pre.attr("href")) === null) { + //若未缓存 + window.location.href = page_btn_pre.attr("href"); + } else { + try { + load(page_btn_pre.attr("href")); + } catch (err) { + window.location.href = page_btn_pre.attr("href"); + } + + } + }); + page_btn_next.unbind("click"); + page_btn_next.click(function () { + event.preventDefault(); + if (window.sessionStorage.getItem(page_btn_next.attr("href")) === null) { + //若未缓存 + window.location.href = page_btn_next.attr("href"); + } else { + try { + load(page_btn_next.attr("href")); + } catch (err) { + window.location.href = page_btn_next.attr("href"); + } + + } + }); + } + + + function load_bookmark(data) { + page_bookmark.removeClass("bookMark"); + page_bookmark.removeClass("bookMarkAct"); + //log(data.bookmark); + if (data.bookmark == 0) { + page_bookmark.addClass("bookMark"); + } else { + page_bookmark.addClass("bookMarkAct"); + } + } + + function load_hiddenForm(data) { + page_url.val(data.url); + } + + function load_title(data) { + page_title.html(data.name + " - SoulBook"); + } + + function stripscript(s) {//用于过滤script标签 + return s.replace(/ + + + + + \ No newline at end of file diff --git a/soulbook/templates/md/admin_book_list.html b/soulbook/templates/md/admin_book_list.html new file mode 100644 index 0000000..59edc02 --- /dev/null +++ b/soulbook/templates/md/admin_book_list.html @@ -0,0 +1,65 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+
该功能正在开发中,敬请期待,若有建议请及时反馈
+
https://github.com/guyue55/soulbook +
+
+ {% include "is_login.html" %} +
+{% endblock %} +{% block script %} +{% endblock %} diff --git a/soulbook/templates/md/admin_bookmarks.html b/soulbook/templates/md/admin_bookmarks.html new file mode 100644 index 0000000..0680862 --- /dev/null +++ b/soulbook/templates/md/admin_bookmarks.html @@ -0,0 +1,91 @@ +{% extends "main.html" %} {% block header %} +{{ title }} + {% endblock %} {% block content %} + + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+ {% if is_bookmark %} + + + + + + + + + + + + {%for item in result%} + + + + + + + + {%endfor%} + +
小说名源目录章节名添加时间操作
+ {{item.novels_name}} + + 查看{{item.novels_name}}源目录 + + {{item.chapter_name}} + {{item.add_time}} + 删除 +
+ {% else %} +
暂无书签数据
+ {% endif %} +
+ {% include "is_login.html" %} +
+{% endblock %} {% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/admin_books.html b/soulbook/templates/md/admin_books.html new file mode 100644 index 0000000..5fb5ebd --- /dev/null +++ b/soulbook/templates/md/admin_books.html @@ -0,0 +1,96 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+ {% if is_bookmark %} + + + + + + + + + + + + {%for item in result%} + + + + + + + + {%endfor%} + +
小说名源目录最新章节上次阅读操作
[{{item.add_time}}]{{item.novels_name}} + 查看{{item.novels_name}}源目录 + + {{item.latest_chapter_name}} + + {{item.last_read_chapter_name}} + + 删除 +
+ {% else %} +
暂无书架数据
+ {% endif %} +
+ {% include "is_login.html" %} +
+{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/admin_setting.html b/soulbook/templates/md/admin_setting.html new file mode 100644 index 0000000..fd98eb2 --- /dev/null +++ b/soulbook/templates/md/admin_setting.html @@ -0,0 +1,119 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+
+
+
用户名
+
{{user}}
+ keyboard_arrow_down +
+
+

用户名无法更改

+
+
+
+
+
邮箱
+
{{email}}
+ keyboard_arrow_down +
+
+
+ + + +
+
+ + +
+
+
+
+
+
修改密码
+
*******
+ keyboard_arrow_down +
+
+
+ + + + +
密码至少 6 位
+ + +
+
+
+ + +
+
+
+
+
+
注册时间
+
{{register_time}}
+ access_time
+
+
+ {% include "is_login.html" %} +
+{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/index.html b/soulbook/templates/md/index.html new file mode 100644 index 0000000..1233de0 --- /dev/null +++ b/soulbook/templates/md/index.html @@ -0,0 +1,156 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} - 网络小说搜索引擎 - 最简洁清新的搜索阅读体验 + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+
+ owllook +
+ + + + + +
+
+
+
+ {% if is_owl %} + {{first_type_title}} + {% elif is_qidian %} + {{first_type_title}} + {% elif is_zh %} + {{first_type_title}} + {% elif is_bd %} + {{first_type_title}} + {% else %} + {% endif %} +
+ {%for each in first_type%} +
+ {{each}} +
+ {%endfor%} + +
+
+
+ {% if is_owl %} + + + + {%for each_head in novels_head%} + + {%endfor%} + + + + {%for each_ranking in search_ranking%} + + + + + + {%endfor%} + +
{{each_head}}
{{each_ranking.index}}{{each_ranking.keyword}}{{each_ranking.count}}
+ {% elif is_qidian_model %} + {%for each_ranking in search_ranking.data%} + + + + {%for each_head in novels_head%} + + {%endfor%} + + + + + {%for each_book in each_ranking.book_list%} + + + + + + {%endfor%} + +
{{each_head}}{{each_ranking.title}}
{{each_book.num}}{{each_book.name}}
+ {%endfor%} + {% elif is_bd %} + + + + {%for each_head in novels_head%} + + {%endfor%} + + + + {%for each_ranking in search_ranking.data%} + + + + + + {%endfor%} + +
{{each_head}}
{{each_ranking.num}}{{each_ranking.name}}{{each_ranking.type}}
+ {% else %} + {% endif %} + +
+ {% include "is_login.html" %} +
+{% endblock %} +{% block script %} +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/is_login.html b/soulbook/templates/md/is_login.html new file mode 100644 index 0000000..efc6955 --- /dev/null +++ b/soulbook/templates/md/is_login.html @@ -0,0 +1,57 @@ +{% if is_login %} + + + + + + + + + + + + + + + + + + + + + + + + +{% else %} +
+
+ 登录 + +
+
+
+ + +
+
+ + +
+
+ +
+
+ + + + + + + + + + + +
+{% endif %} \ No newline at end of file diff --git a/soulbook/templates/md/main.html b/soulbook/templates/md/main.html new file mode 100644 index 0000000..da84a09 --- /dev/null +++ b/soulbook/templates/md/main.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + {% block header %}{% endblock %} + + + + + + +{% block content %}{% endblock %} + + + + + + +{% block script %}{% endblock %} + + diff --git a/soulbook/templates/md/noti_book.html b/soulbook/templates/md/noti_book.html new file mode 100644 index 0000000..7d49768 --- /dev/null +++ b/soulbook/templates/md/noti_book.html @@ -0,0 +1,102 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+ + +
+
+ {% if is_author %} +
+ {%for item in author_list%} +
+ face + {{item.author_name}} + cancel +
+ {%endfor%} +
+ + {% else %} +
+
+ face + 输入作者名, 追踪新书动态 +
+
+ {% endif %} + + + + + + + + + + + + + + + + {% include "is_login.html" %} +
+{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/similar_user.html b/soulbook/templates/md/similar_user.html new file mode 100644 index 0000000..0a4f5b1 --- /dev/null +++ b/soulbook/templates/md/similar_user.html @@ -0,0 +1,87 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + +{% endblock %} + +{% block content %} + + +
+ {% include "user_status.html" %} + +
+ +{% include "toolbar.html" %} + +
+
+ {% if is_similar %} + + + + + + + + + {% for v in similar_user%} + {% for key in v %} + + + + + {% endfor %} + {% endfor %} + +
相似用户相似度
+ {{key|replace('·','.')}} + + {{v[key]}} +
+ {% else %} +
暂无相似书友
+ {% endif %} +
+ {% include "is_login.html" %} +
+{% endblock %} +{% block script %} +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/md/toolbar.html b/soulbook/templates/md/toolbar.html new file mode 100644 index 0000000..624a54a --- /dev/null +++ b/soulbook/templates/md/toolbar.html @@ -0,0 +1,65 @@ +
+
+ menu + owllook +
+
+ +
+ + +
+ +
+
+ color_lens +
+ + + + + + + + + + + +
+
+ + +
+
设置文档主题
+
+ +

主题色

+
+
+ +
+
+ +
+
+
+
+
+ + +
+
\ No newline at end of file diff --git a/soulbook/templates/md/user_status.html b/soulbook/templates/md/user_status.html new file mode 100644 index 0000000..a986da2 --- /dev/null +++ b/soulbook/templates/md/user_status.html @@ -0,0 +1,19 @@ +
+ {% if is_login %} +
+ +
{{user}}
+
我爱读书
+
+ {% else %} +
+ +
+ 点击登录 +
+
以下功能,等你体验
+
+ {% endif %} +
\ No newline at end of file diff --git a/soulbook/templates/novels/admin_bookmarks.html b/soulbook/templates/novels/admin_bookmarks.html new file mode 100644 index 0000000..a6a6347 --- /dev/null +++ b/soulbook/templates/novels/admin_bookmarks.html @@ -0,0 +1,51 @@ +{% extends "main.html" %} {% block header %} +{{ title }} + + +{% endblock %} {% block content %} {% include "search_bar.html" %} +
+ {% if is_bookmark %} + + + + + + + + + + + + {%for item in result%} + + + + + + + + {%endfor%} + +
小说名源目录章节名添加时间操作
+ {{item.novels_name}} + + 查看{{item.novels_name}}源目录 + + {{item.chapter_name}} + {{item.add_time}} + 删除 +
+ {% else %} +
暂无书签数据
+ {% endif %} + {% include "lcxs_show.html" %} + +
+{% endblock %} {% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/admin_books.html b/soulbook/templates/novels/admin_books.html new file mode 100644 index 0000000..9182e7b --- /dev/null +++ b/soulbook/templates/novels/admin_books.html @@ -0,0 +1,63 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
+ {% if is_bookmark %} + + + + + + + + + + + + {%for item in result%} + + + + + + + + {%endfor%} + +
小说名源目录最新章节上次阅读操作
{{item.novels_name}} + + +
{{item.add_time}} +
+ + 目录 + + + {{item.latest_chapter_name}} + + {{item.last_read_chapter_name}} +
[{{item.set_date}}] +
+ 删除 +
+ {% else %} +
书架暂无数据
+ {% endif %} + {% include "lcxs_show.html" %} + +
+{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/admin_lcxs.html b/soulbook/templates/novels/admin_lcxs.html new file mode 100644 index 0000000..c55fc06 --- /dev/null +++ b/soulbook/templates/novels/admin_lcxs.html @@ -0,0 +1,40 @@ +{% extends "main.html" %} + +{% block header %} +关注 - 公众号 + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
+

公众号 - xxxx

+ + + + + + + + + + + + + + + + + + + + +
#名称日期
1 + 【福利】在微信群里面查找&追更小说 - + 左下角扫一扫关注 + 2019-11-05
2 + 书单推荐 + 2019-11-05
+ {% include "lcxs_show.html" %} +
+{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/chapter.html b/soulbook/templates/novels/chapter.html new file mode 100644 index 0000000..2b7b674 --- /dev/null +++ b/soulbook/templates/novels/chapter.html @@ -0,0 +1,42 @@ +{% extends "main.html" %} + +{% block header %} +{{ novels_name }} - SoulBook + +{% endblock %} + +{% block content %} + +
+

+ × +

+

+ 注意!页面内容来自{{url}},本站不储存任何内容,为了更好的阅读体验进行在线解析,若有广告出现,请及时反馈。若您觉得侵犯了您的利益,请通知我们进行删除,然后访问 + 原网页 +

+
+ +
+ + + + {% for item in soup -%} + {{item|safe}} + {%- endfor %} +
+
+ +
+
+ +
+
+ +
+{% endblock %} +{% block script %} + +{% endblock %} + + diff --git a/soulbook/templates/novels/comment.html b/soulbook/templates/novels/comment.html new file mode 100644 index 0000000..ec31f7c --- /dev/null +++ b/soulbook/templates/novels/comment.html @@ -0,0 +1,10 @@ +
+ + \ No newline at end of file diff --git a/soulbook/templates/novels/content.html b/soulbook/templates/novels/content.html new file mode 100644 index 0000000..6a0cfb4 --- /dev/null +++ b/soulbook/templates/novels/content.html @@ -0,0 +1,143 @@ +{% extends "main.html" %} + +{% block header %} +{{ name }} - SoulBook + + + +{% endblock %} + +{% block content %} + + + +
+ + + + + +
+ {% if bookmark %} + + {% else %} + + {% endif %} +
+ {% for v in next_chapter%} + {% if next_chapter[v] %} + + {{v}} + + + {% endfor %} +
+

{{name}}

+
+
+ + +
+ {% if book %} + + +
书架
+
+ {% else %} + + +
书架
+
+ {% endif %} + +
+ +
+
+ {% for item in soup -%} + {{item|safe}} + {%- endfor %} +
+
+ {% for v in next_chapter%} + {% if next_chapter[v] %} + + {{v}} + + + {% endfor %} + +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ {% include "lcxs_show.html" %} + +
+{% endblock %} +{% block script %} + + +{% endblock %} + + diff --git a/soulbook/templates/novels/donate.html b/soulbook/templates/novels/donate.html new file mode 100644 index 0000000..dccd34e --- /dev/null +++ b/soulbook/templates/novels/donate.html @@ -0,0 +1,71 @@ +{% extends "main.html" %} + +{% block header %} +donate - SoulBook +{% endblock %} + +{% block content %} +
+ + +
+{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/feedback.html b/soulbook/templates/novels/feedback.html new file mode 100644 index 0000000..4653a3e --- /dev/null +++ b/soulbook/templates/novels/feedback.html @@ -0,0 +1,24 @@ +{% extends "main.html" %} + +{% block header %} +feedback - owllook + +{% endblock %} + +{% block content %} +
+
+
+
+
+        你好,如果对本站有什么建议,请输入您的意见,本站一定慎重考虑,最后,感谢您对本站的反馈。
+        若您有更多的意见或者想大家一起交流,请看这里加群(能找到隐藏的入口就进来~)。
+    
+ {% include "comment.html" %} +
+{% endblock %} diff --git a/soulbook/templates/novels/footer.html b/soulbook/templates/novels/footer.html new file mode 100644 index 0000000..ecf5787 --- /dev/null +++ b/soulbook/templates/novels/footer.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/soulbook/templates/novels/index.html b/soulbook/templates/novels/index.html new file mode 100644 index 0000000..af71cb7 --- /dev/null +++ b/soulbook/templates/novels/index.html @@ -0,0 +1,52 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} - 最简洁清新的搜索阅读体验 + +{% endblock %} + +{% block content %} + + + + + + + + +
+ + Come From GitHub + {% if is_login %} + {% include "login.html" %} + {% else %} + + {% endif %} +
+
+ + +
+ 搜索排行: + {%for novels_name in search_ranking%} + {{novels_name.keyword}} + {%endfor%} + 更多 +
+ {% include "lcxs_show.html" %} +
+{% endblock %} diff --git a/soulbook/templates/novels/lcxs_show.html b/soulbook/templates/novels/lcxs_show.html new file mode 100644 index 0000000..612f19f --- /dev/null +++ b/soulbook/templates/novels/lcxs_show.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/soulbook/templates/novels/login.html b/soulbook/templates/novels/login.html new file mode 100644 index 0000000..599fec4 --- /dev/null +++ b/soulbook/templates/novels/login.html @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/soulbook/templates/novels/logout.html b/soulbook/templates/novels/logout.html new file mode 100644 index 0000000..ec9bc82 --- /dev/null +++ b/soulbook/templates/novels/logout.html @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/soulbook/templates/novels/main.html b/soulbook/templates/novels/main.html new file mode 100644 index 0000000..444c484 --- /dev/null +++ b/soulbook/templates/novels/main.html @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + {% block header %}{% endblock %} + + + + + + + + + + + + +{% block content %}{% endblock %} +{% include "logout.html" %} +{% include "footer.html" %} + + + + + + +{% block script %}{% endblock %} + + diff --git a/soulbook/templates/novels/parse_error.html b/soulbook/templates/novels/parse_error.html new file mode 100644 index 0000000..4f1cc00 --- /dev/null +++ b/soulbook/templates/novels/parse_error.html @@ -0,0 +1,18 @@ +{% extends "main.html" %} + +{% block header %} +SoulBook - 最简洁清新的搜索阅读体验 + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
+
+

解析失败或者没有下一页

+

点击SoulBook继续搜索

+

如果您觉得是访问出错,请在右下角加群反馈给本站,或者重新刷新一次

+

不然请访问源网页:点这里

+
+
+{% endblock %} diff --git a/soulbook/templates/novels/register.html b/soulbook/templates/novels/register.html new file mode 100644 index 0000000..21e77eb --- /dev/null +++ b/soulbook/templates/novels/register.html @@ -0,0 +1,47 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} - 最简洁清新的搜索阅读体验 + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
+
+

注册

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+

密码(>5) && 用户名(>2)

+

密码不一致

+

不能有内容为空

+

邮箱格式错误

+
+ +
+
+{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/result-bak.html b/soulbook/templates/novels/result-bak.html new file mode 100644 index 0000000..bf41c11 --- /dev/null +++ b/soulbook/templates/novels/result-bak.html @@ -0,0 +1,58 @@ +{% extends "main.html" %} + +{% block header %} +{{ name }} - 搜索结果 - owllook + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
+
+
+

找到 {{count}} 条结果(用时{{time}}s)

+
+ {%for item in result%} +
+
  • + {{item.title}} +
    + {{item.netloc}} + 查看源网址 +
    +
    + {{item.time}} + {% if item.is_recommend %} + 推荐源 + {% else %} + {% endif %} + {% if item.is_parse %} + 已解析 + {% else %} + 通用解析 + {% endif %} +
    +
  • + +
    + {%endfor%} +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/result.html b/soulbook/templates/novels/result.html new file mode 100644 index 0000000..3304c2a --- /dev/null +++ b/soulbook/templates/novels/result.html @@ -0,0 +1,58 @@ +{% extends "main.html" %} + +{% block header %} +{{ name }} - 搜索结果 - owllook + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
    +
    +
    +

    找到 {{count}} 条结果(用时{{time}}s)

    +
    + {%for item in result%} +
    +
  • + {{item.title}} +
    + {{item.netloc}} + 查看源网址 +
    +
    + {{item.time}} + {% if item.is_recommend %} + 推荐源 + {% else %} + {% endif %} + {% if item.is_parse %} + 已解析 + {% else %} + 通用解析 + {% endif %} +
    +
  • + +
    + {%endfor%} +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/search_bar.html b/soulbook/templates/novels/search_bar.html new file mode 100644 index 0000000..27ca196 --- /dev/null +++ b/soulbook/templates/novels/search_bar.html @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/soulbook/templates/novels/search_user-bak.html b/soulbook/templates/novels/search_user-bak.html new file mode 100644 index 0000000..187dc15 --- /dev/null +++ b/soulbook/templates/novels/search_user-bak.html @@ -0,0 +1,51 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
    + {% if is_bookmark %} + + + + + + + + + + + {%for item in result%} + + + + + + + {%endfor%} + +
    小说名源目录最新章节{{username}}上次阅读到
    {{item.novels_name}} + 查看{{item.novels_name}}源目录 + + {{item.latest_chapter_name}} + + {{item.last_read_chapter_name}} +
    + {% else %} +
    书架暂无数据
    + {% endif %} + +
    +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/search_user.html b/soulbook/templates/novels/search_user.html new file mode 100644 index 0000000..f55dadd --- /dev/null +++ b/soulbook/templates/novels/search_user.html @@ -0,0 +1,51 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
    + {% if is_bookmark %} + + + + + + + + + + + {%for item in result%} + + + + + + + {%endfor%} + +
    小说名源目录最新章节{{username}}上次阅读到
    {{item.novels_name}} + 查看{{item.novels_name}}源目录 + + {{item.latest_chapter_name}} + + {{item.last_read_chapter_name}} +
    + {% else %} +
    书架暂无数据
    + {% endif %} + +
    +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/templates/novels/similar_user.html b/soulbook/templates/novels/similar_user.html new file mode 100644 index 0000000..c671572 --- /dev/null +++ b/soulbook/templates/novels/similar_user.html @@ -0,0 +1,48 @@ +{% extends "main.html" %} + +{% block header %} +{{ title }} + + +{% endblock %} + +{% block content %} +{% include "search_bar.html" %} +
    + {% if is_similar %} + + + + + + + + + {% for v in similar_user%} + {% for key in v %} + + + + + {% endfor %} + {% endfor %} + +
    相似用户相似度
    + {{key|replace('·','.')}} + + {{v[key]}} +
    + {% else %} +
    暂无相似书友
    + {% endif %} + {% include "lcxs_show.html" %} + +
    +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/soulbook/utils/__init__.py b/soulbook/utils/__init__.py new file mode 100644 index 0000000..8b519b6 --- /dev/null +++ b/soulbook/utils/__init__.py @@ -0,0 +1 @@ +from .verification import ver_question, get_real_answer diff --git a/soulbook/utils/tools.py b/soulbook/utils/tools.py new file mode 100644 index 0000000..2464d7b --- /dev/null +++ b/soulbook/utils/tools.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +import asyncio +import uvloop + +from functools import wraps + + +def singleton(cls): + """ + A singleton created by using decorator + :param cls: cls + :return: instance + """ + _instances = {} + + @wraps(cls) + def instance(*args, **kw): + if cls not in _instances: + _instances[cls] = cls(*args, **kw) + return _instances[cls] + + return instance + + +def async_callback(func, **kwargs): + """ + Call the asynchronous function + :param func: a async function + :param kwargs: params + :return: result + """ + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(func(**kwargs)) + loop.run_until_complete(task) + return task.result() diff --git a/soulbook/utils/verification/__init__.py b/soulbook/utils/verification/__init__.py new file mode 100644 index 0000000..8b519b6 --- /dev/null +++ b/soulbook/utils/verification/__init__.py @@ -0,0 +1 @@ +from .verification import ver_question, get_real_answer diff --git a/soulbook/utils/verification/verification.conf b/soulbook/utils/verification/verification.conf new file mode 100644 index 0000000..063a5fc --- /dev/null +++ b/soulbook/utils/verification/verification.conf @@ -0,0 +1,59 @@ +[1] +question = 雪中悍刀行 +answer = 烽火戏诸侯 + +[2] +question = 龙族 +answer = 江南 + +[3] +question = 间客 +answer = 猫腻 + +[4] +question = 天龙八部 +answer = 金庸 + +[5] +question = 射雕英雄传 +answer = 金庸 + +[6] +question = 倚天屠龙记 +answer = 金庸 + +[7] +question = 日月当空 +answer = 黄易 + +[8] +question = 寻秦记 +answer = 黄易 + +[9] +question = 大唐双龙传 +answer = 黄易 + +[10] +question = 昆仑 +answer = 凤歌 + +[11] +question = 沧海 +answer = 凤歌 + +[12] +question = 灵飞经 +answer = 凤歌 + +[13] +question = 遮天 +answer = 辰东 + +[14] +question = 诛仙 +answer = 萧鼎 + +[15] +question = 悟空传 +answer = 今何在 \ No newline at end of file diff --git a/soulbook/utils/verification/verification.py b/soulbook/utils/verification/verification.py new file mode 100644 index 0000000..f8fa908 --- /dev/null +++ b/soulbook/utils/verification/verification.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +import logging +import os +import random + +from configparser import ConfigParser + +_dir = os.path.dirname(__file__) +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s') + + +def ver_question() -> tuple: + """ + 针对注册问题验证 + 问: 小说名 + 答: 作者名 + :return: (question,answer) + """ + cf = ConfigParser() + file_name = os.path.join(_dir, 'verification.conf') + try: + cf.read(file_name, encoding='utf-8') + index = random.choice(cf.sections()) + except IndexError as e: + index = "1" + except Exception as e: + logging.exception(e) + return None + question = cf.get(index, "question") + return (index, question) + + +def get_real_answer(index) -> str: + """ + 通过index值获取问题答案 + :param index: + :return: bool + """ + answer = '' + try: + cf = ConfigParser() + file_name = os.path.join(_dir, 'verification.conf') + cf.read(file_name) + answer = cf.get(index, "answer") + except Exception as e: + logging.exception(e) + return answer diff --git a/soulbook/views/__init__.py b/soulbook/views/__init__.py new file mode 100644 index 0000000..2dc5ffa --- /dev/null +++ b/soulbook/views/__init__.py @@ -0,0 +1,6 @@ +from .admin_blueprint import admin_bp +from .api_blueprint import api_bp +from .except_blueprint import except_bp +from .md_blueprint import md_bp +from .novels_blueprint import novels_bp +from .operate_blueprint import operate_bp diff --git a/soulbook/views/admin_blueprint.py b/soulbook/views/admin_blueprint.py new file mode 100644 index 0000000..c2984fd --- /dev/null +++ b/soulbook/views/admin_blueprint.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +from urllib.parse import urlparse, parse_qs +from operator import itemgetter + +from jinja2 import Environment, PackageLoader, select_autoescape +from sanic import Blueprint +from sanic.response import html, redirect + +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.cache import get_the_latest_chapter +from soulbook.config import LOGGER, CONFIG + +admin_bp = Blueprint('admin_blueprint', url_prefix='admin') +admin_bp.static('/static/novels', CONFIG.BASE_DIR + '/static/novels') + + +@admin_bp.listener('before_server_start') +def setup_db(admin_bp, loop): + global motor_base + motor_base = MotorBase() + + +@admin_bp.listener('after_server_stop') +def close_connection(admin_bp, loop): + motor_base = None + + +# jinjia2 config +env = Environment( + loader=PackageLoader('soulbook.views.novels_blueprint', '../templates/novels'), + autoescape=select_autoescape(['html', 'xml', 'tpl'])) + + +def template(tpl, **kwargs): + template = env.get_template(tpl) + return html(template.render(kwargs)) + + +@admin_bp.route("/bookmarks") +async def bookmarks(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + data = await motor_db.user_message.find_one({'user': user}) + if data: + # 获取所有书签 + bookmarks = data.get('bookmarks', None) + if bookmarks: + result = [] + for i in bookmarks: + item_result = {} + bookmark = i.get('bookmark', None) + query = parse_qs(urlparse(bookmark).query) + item_result['novels_name'] = query.get('novels_name', '')[0] if query.get('novels_name', + '') else '' + item_result['chapter_name'] = query.get('name', '')[0] if query.get('name', '') else '' + item_result['chapter_url'] = query.get('chapter_url', '')[0] if query.get('chapter_url', + '') else '' + item_result['bookmark'] = bookmark + item_result['add_time'] = i.get('add_time', '') + result.append(item_result) + return template('admin_bookmarks.html', title='{user}的书签 - soulbook'.format(user=user), + is_login=1, + user=user, + is_bookmark=1, + result=result[::-1]) + return template('admin_bookmarks.html', title='{user}的书签 - soulbook'.format(user=user), + is_login=1, + user=user, + is_bookmark=0) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@admin_bp.route("/books") +async def books(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + data = await motor_db.user_message.find_one({'user': user}) + if data: + books_url = data.get('books_url', None) + if books_url: + result = [] + for i in books_url: + item_result = {} + book_url = i.get('book_url', None) + last_read_url = i.get("last_read_url", "") + book_query = parse_qs(urlparse(book_url).query) + last_read_chapter_name = parse_qs(last_read_url).get('name') + if last_read_chapter_name: + last_read_chapter_name = last_read_chapter_name[0] + else: + last_read_chapter_name = '上次阅读' + if i.get('name_now'): + last_read_chapter_name = i.get('name_now') + # else: + # last_read_chapter_name = '上次阅读' + item_result['novels_name'] = book_query.get('novels_name', '')[0] if book_query.get( + 'novels_name', '') else '' + item_result['book_url'] = book_url + # 原网页 + if not item_result.get('source_book_url'): + item_result['source_book_url'] = book_url.split("?url=")[-1].split("&novels_name=")[0] + latest_data = await motor_db.latest_chapter.find_one({'owllook_chapter_url': book_url}) + if latest_data: + item_result['latest_chapter_name'] = latest_data['data']['latest_chapter_name'] + item_result['owllook_content_url'] = latest_data['data']['owllook_content_url'] + else: + get_latest_data = await get_the_latest_chapter(book_url) or {} + item_result['latest_chapter_name'] = get_latest_data.get('latest_chapter_name', '暂未获取,请反馈') + item_result['owllook_content_url'] = get_latest_data.get('owllook_content_url', '') + item_result['add_time'] = i.get('add_time', '') + item_result['set_time'] = i.get('set_time', 0) + item_result['set_date'] = i.get('set_date', "") + item_result["last_read_url"] = last_read_url if last_read_url else book_url + item_result["last_read_chapter_name"] = last_read_chapter_name + result.append(item_result) + # 以更新时间进行排序 + result = sorted( + result, + reverse=False, + key=itemgetter('set_time')) + return template('admin_books.html', title='{user}的书架 - soulbook'.format(user=user), + is_login=1, + user=user, + is_bookmark=1, + result=result[::-1]) + return template('admin_books.html', title='{user}的书架 - soulbook'.format(user=user), + is_login=1, + user=user, + is_bookmark=0) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@admin_bp.route("/lcxs") +async def lcxs(request): + user = request['session'].get('user', None) + if user: + return template('admin_lcxs.html', + is_login=1, + user=user) + else: + return redirect('/') + + +@admin_bp.route("/search_user") +async def search_user(request): + user = request['session'].get('user', None) + name = request.args.get('ss', None) + if user and name: + try: + motor_db = motor_base.get_db() + data = await motor_db.user_message.find_one({'user': name}) + books_url = data.get('books_url', None) if data else None + if books_url: + result = [] + for i in books_url: + item_result = {} + book_url = i.get('book_url', None) + last_read_url = i.get("last_read_url", "") + book_query = parse_qs(urlparse(book_url).query) + last_read_chapter_name = parse_qs(last_read_url).get('name', ['暂无'])[0] + item_result['novels_name'] = book_query.get('novels_name', '')[0] if book_query.get( + 'novels_name', '') else '' + item_result['book_url'] = book_url + latest_data = await motor_db.latest_chapter.find_one({'owllook_chapter_url': book_url}) + if latest_data: + item_result['latest_chapter_name'] = latest_data['data']['latest_chapter_name'] + item_result['owllook_content_url'] = latest_data['data']['owllook_content_url'] + else: + get_latest_data = await get_the_latest_chapter(book_url) or {} + item_result['latest_chapter_name'] = get_latest_data.get('latest_chapter_name', '暂未获取,请反馈') + item_result['owllook_content_url'] = get_latest_data.get('owllook_content_url', '') + item_result['add_time'] = i.get('add_time', '') + item_result["last_read_url"] = last_read_url if last_read_url else book_url + item_result["last_read_chapter_name"] = last_read_chapter_name + result.append(item_result) + return template('search_user.html', title='{name}的书架 - soulbook'.format(name=name), + is_login=1, + user=user, + username=name, + is_bookmark=1, + result=result[::-1]) + else: + return template('search_user.html', title='{name}的书架 - soulbook'.format(name=name), + is_login=1, + user=user, + is_bookmark=0) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@admin_bp.route("/similar_user") +async def similar_user(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + similar_info = await motor_db.user_recommend.find_one({'user': user}) + if similar_info: + similar_user = similar_info['similar_user'][:20] + user_tag = similar_info['user_tag'] + updated_at = similar_info['updated_at'] + return template('similar_user.html', + title='与' + user + '相似的书友', + is_login=1, + is_similar=1, + user=user, + similar_user=similar_user, + user_tag=user_tag, + updated_at=updated_at) + else: + return template('similar_user.html', + title='与' + user + '相似的书友', + is_login=1, + is_similar=0, + user=user) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') diff --git a/soulbook/views/api_blueprint.py b/soulbook/views/api_blueprint.py new file mode 100644 index 0000000..4fa90df --- /dev/null +++ b/soulbook/views/api_blueprint.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +from urllib.parse import unquote + +from sanic import Blueprint + +from soulbook.fetcher.extract_novels import extract_chapters +from soulbook.fetcher.function import get_time, get_netloc +from soulbook.fetcher.decorators import authenticator, auth_params, response_handle +from soulbook.fetcher import UniResponse, ResponseField +from soulbook.fetcher.cache import cache_owllook_novels_chapter +from soulbook.fetcher.novels_tools import get_novels_info +from soulbook.config import LOGGER + +api_bp = Blueprint('api_blueprint', url_prefix='api') + + +@api_bp.route("/owl_bd_novels/") +@authenticator('Owllook-Api-Key') +async def owl_bd_novels(request, name): + """ + 百度小说信息接口 + :param request: + :param name: 小说名 + :return: 小说相关信息 + """ + name = unquote(name) + novels_name = 'intitle:{name} 小说 阅读'.format(name=name) + try: + res = await get_novels_info(class_name='baidu', novels_name=novels_name) + parse_result = [] + if res: + parse_result = [i for i in res if i] + UniResponse.SUCCESS.update({ResponseField.DATA: parse_result, ResponseField.FINISH_AT: get_time()}) + return response_handle(request, UniResponse.SUCCESS, 200) + except Exception as e: + LOGGER.exception(e) + return response_handle(request, UniResponse.SERVER_UNKNOWN_ERR, 500) + + +@api_bp.route("/owl_novels_chapters", methods=['POST']) +@auth_params('chapters_url', 'novels_name') +@authenticator('Owllook-Api-Key') +async def owl_novels_chapters(request, **kwargs): + """ + 返回章节目录 基本达到通用 + :param request: + :param chapter_url: 章节源目录页url + :param novels_name: 小说名称 + :return: 小说目录信息 + """ + request_params = kwargs["request_params"] + chapters_url = request_params.get('chapters_url', None) + novels_name = request_params.get('novels_name', None) + netloc = get_netloc(chapters_url) + try: + res = await cache_owllook_novels_chapter(url=chapters_url, netloc=netloc) + chapters_sorted = [] + if res: + chapters_sorted = extract_chapters(chapters_url, res) + UniResponse.SUCCESS.update({ResponseField.DATA: { + 'novels_name': novels_name, + 'chapter_url': chapters_url, + 'all_chapters': chapters_sorted + }, ResponseField.FINISH_AT: get_time()}) + return response_handle(request, UniResponse.SUCCESS, 200) + except Exception as e: + LOGGER.exception(e) + return response_handle(request, UniResponse.SERVER_UNKNOWN_ERR, 500) + + +@api_bp.route("/owl_so_novels/") +@authenticator('Owllook-Api-Key') +async def owl_so_novels(request, name): + """ + 360小说信息接口 + :param request: + :param name: 小说名 + :return: 小说相关信息 + """ + name = unquote(name) + novels_name = '{name} 小说 免费阅读'.format(name=name) + try: + res = await get_novels_info(class_name='baidu', novels_name=novels_name) + parse_result = [] + if res: + parse_result = [i for i in res if i] + UniResponse.SUCCESS.update({ResponseField.DATA: parse_result, ResponseField.FINISH_AT: get_time()}) + return response_handle(request, UniResponse.SUCCESS, 200) + except Exception as e: + LOGGER.exception(e) + return response_handle(request, UniResponse.SERVER_UNKNOWN_ERR, 500) diff --git a/soulbook/views/except_blueprint.py b/soulbook/views/except_blueprint.py new file mode 100644 index 0000000..197551b --- /dev/null +++ b/soulbook/views/except_blueprint.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +from jinja2 import Environment, PackageLoader, select_autoescape +from sanic import Blueprint +from sanic.exceptions import NotFound, ServerError +from sanic.response import html, json + +from soulbook.config import CONFIG + +except_bp = Blueprint('except_blueprint', url_prefix='except') +except_bp.static('/static/except', CONFIG.BASE_DIR + '/static/except') + +# jinjia2 config +env = Environment( + loader=PackageLoader('soulbook.views.except_blueprint', '../templates/except'), + autoescape=select_autoescape(['html', 'xml', 'tpl'])) + + +def template(tpl, **kwargs): + template = env.get_template(tpl) + return html(template.render(kwargs)) + + +@except_bp.exception(NotFound) +def ignore_404(request, exception): + if "google3eabdadc11faf3b3" in request.url: + return template('google3eabdadc11faf3b3.html') + return template('404.html') + + +@except_bp.exception(ServerError) +async def server_error(request, exception): + return json( + { + "exception": "{}".format(exception), + "status": exception.status_code + }, + status=exception.status_code) diff --git a/soulbook/views/md_blueprint.py b/soulbook/views/md_blueprint.py new file mode 100644 index 0000000..b5d4bcb --- /dev/null +++ b/soulbook/views/md_blueprint.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python +from urllib.parse import urlparse, parse_qs + +from jinja2 import Environment, PackageLoader, select_autoescape +from sanic import Blueprint +from sanic.response import html, text, redirect + +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.cache import get_the_latest_chapter, cache_owllook_search_ranking, cache_others_search_ranking +from soulbook.config import RULES, LOGGER, REPLACE_RULES, ENGINE_PRIORITY, CONFIG + +md_bp = Blueprint('rank_blueprint', url_prefix='md') +md_bp.static('/static/md', CONFIG.BASE_DIR + '/static/md') + + +@md_bp.listener('before_server_start') +def setup_db(rank_bp, loop): + global motor_base + motor_base = MotorBase() + + +@md_bp.listener('after_server_stop') +def close_connection(rank_bp, loop): + motor_base = None + + +# jinjia2 config +env = Environment( + loader=PackageLoader('soulbook.views.md_blueprint', '../templates/md'), + autoescape=select_autoescape(['html', 'xml', 'tpl'])) + + +def template(tpl, **kwargs): + template = env.get_template(tpl) + return html(template.render(kwargs)) + + +@md_bp.route("/setting") +async def admin_setting(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + data = await motor_db.user.find_one({'user': user}) + if data: + return template('admin_setting.html', title='{user}的设置 - SoulBook'.format(user=user), + is_login=1, + user=user, + register_time=data['register_time'], + email=data.get('email', '请尽快绑定邮箱')) + else: + return text('未知错误') + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/zh_bd_novels") +async def bd_novels(request): + user = request['session'].get('user', None) + first_type_title = "纵横百度小说月票榜" + first_type = [] + title = "SoulBook - 纵横百度小说月票榜" + novels_head = ['#', '小说名', '类型'] + search_ranking = await cache_others_search_ranking(spider='zh_bd_novels', novel_type='全部类别') + if user: + return template('index.html', + title=title, + is_login=1, + is_bd=1, + user=user, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) + else: + return template('index.html', + title=title, + is_login=0, + is_bd=1, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) + + +@md_bp.route("/book_list") +async def book_list(request): + user = request['session'].get('user', None) + if user: + try: + return template('admin_book_list.html', title='{user}的书单 - SoulBook'.format(user=user), + is_login=1, + user=user) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/bookmarks") +async def bookmarks(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + data = await motor_db.user_message.find_one({'user': user}) + if data: + # 获取所有书签 + bookmarks = data.get('bookmarks', None) + if bookmarks: + result = [] + for i in bookmarks: + item_result = {} + bookmark = i.get('bookmark', None) + query = parse_qs(urlparse(bookmark).query) + item_result['novels_name'] = query.get('novels_name', '')[0] if query.get('novels_name', + '') else '' + item_result['chapter_name'] = query.get( + 'name', '')[0] if query.get('name', '') else '' + item_result['chapter_url'] = query.get('chapter_url', '')[0] if query.get('chapter_url', + '') else '' + item_result['bookmark'] = bookmark + item_result['add_time'] = i.get('add_time', '') + result.append(item_result) + return template('admin_bookmarks.html', title='{user}的书签 - SoulBook'.format(user=user), + is_login=1, + user=user, + is_bookmark=1, + result=result[::-1]) + return template('admin_bookmarks.html', title='{user}的书签 - SoulBook'.format(user=user), + is_login=1, + user=user, + is_bookmark=0) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/books") +async def books(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + data = await motor_db.user_message.find_one({'user': user}) + if data: + books_url = data.get('books_url', None) + if books_url: + result = [] + for i in books_url: + item_result = {} + book_url = i.get('book_url', None) + last_read_url = i.get("last_read_url", "") + book_query = parse_qs(urlparse(book_url).query) + last_read_chapter_name = parse_qs( + last_read_url).get('name', ['暂无'])[0] + item_result['novels_name'] = book_query.get('novels_name', '')[0] if book_query.get( + 'novels_name', '') else '' + item_result['book_url'] = book_url + latest_data = await motor_db.latest_chapter.find_one({'owllook_chapter_url': book_url}) + if latest_data: + item_result['latest_chapter_name'] = latest_data['data']['latest_chapter_name'] + item_result['owllook_content_url'] = latest_data['data']['owllook_content_url'] + else: + get_latest_data = await get_the_latest_chapter(book_url) or {} + item_result['latest_chapter_name'] = get_latest_data.get( + 'latest_chapter_name', '暂未获取,请反馈') + item_result['owllook_content_url'] = get_latest_data.get( + 'owllook_content_url', '') + item_result['add_time'] = i.get('add_time', '') + item_result["last_read_url"] = last_read_url if last_read_url else book_url + item_result["last_read_chapter_name"] = last_read_chapter_name + result.append(item_result) + return template('admin_books.html', title='{user}的书架 - SoulBook'.format(user=user), + is_login=1, + user=user, + is_bookmark=1, + result=result[::-1]) + return template('admin_books.html', title='{user}的书架 - SoulBook'.format(user=user), + is_login=1, + user=user, + is_bookmark=0) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/") +async def index(request): + user = request['session'].get('user', None) + novels_head = ['#', '小说名', '搜索次数'] + first_type_title = "搜索排行" + first_type = [] + search_ranking = await cache_owllook_search_ranking() + if user: + return template('index.html', title='SoulBook', is_login=1, user=user, search_ranking=search_ranking, + first_type=first_type, first_type_title=first_type_title, novels_head=novels_head, is_owl=1) + else: + return template('index.html', title='SoulBook', is_login=0, search_ranking=search_ranking, first_type=first_type, + first_type_title=first_type_title, novels_head=novels_head, is_owl=1) + + +@md_bp.route("/noti_book") +async def noti_book(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + is_author = 0 + data = await motor_db.user_message.find_one({'user': user}, {'author_latest': 1, '_id': 0}) + if data: + is_author = 1 + author_list = data.get('author_latest', {}) + return template('noti_book.html', title='新书提醒 - SoulBook'.format(user=user), + is_login=1, + is_author=is_author, + author_list=author_list, + user=user) + else: + return template('noti_book.html', title='新书提醒 - SoulBook'.format(user=user), + is_login=1, + is_author=is_author, + user=user) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/qidian") +async def qidian(request): + user = request['session'].get('user', None) + novels_type = request.args.get('type', '全部类别').strip() + first_type_title = "全部类别" + first_type = [ + '玄幻', + '奇幻', + '武侠', + '仙侠', + '都市', + '职场', + '军事', + '历史', + '游戏', + '体育', + '科幻', + '灵异', + '二次元', + ] + if novels_type in first_type: + novels_head = [novels_type] + elif novels_type == first_type_title: + novels_head = ['#'] + else: + return redirect('qidian') + search_ranking = await cache_others_search_ranking(spider='qidian', novel_type=novels_type) + title = "SoulBook - 起点小说榜单" + if user: + return template('index.html', + title=title, + is_login=1, + is_qidian=1, + is_qidian_model=1, + user=user, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) + else: + return template('index.html', + title=title, + is_login=0, + is_qidian=1, + is_qidian_model=1, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) + + +@md_bp.route("/similar_user") +async def similar_user(request): + user = request['session'].get('user', None) + if user: + try: + motor_db = motor_base.get_db() + similar_info = await motor_db.user_recommend.find_one({'user': user}) + if similar_info: + similar_user = similar_info['similar_user'][:20] + user_tag = similar_info['user_tag'] + updated_at = similar_info['updated_at'] + return template('similar_user.html', + title='与' + user + '相似的书友', + is_login=1, + is_similar=1, + user=user, + similar_user=similar_user, + user_tag=user_tag, + updated_at=updated_at) + else: + return template('similar_user.html', + title='与' + user + '相似的书友', + is_login=1, + is_similar=0, + user=user) + except Exception as e: + LOGGER.error(e) + return redirect('/') + else: + return redirect('/') + + +@md_bp.route("/zongheng") +async def zongheng(request): + user = request['session'].get('user', None) + novels_type = request.args.get('type', '人气榜单').strip() + first_type_title = "人气榜单" + first_type = [] + if novels_type in first_type: + novels_head = [novels_type] + elif novels_type == first_type_title: + novels_head = ['#'] + else: + return redirect('zongheng') + search_ranking = await cache_others_search_ranking(spider='zongheng', novel_type=novels_type) + title = "SoulBook - 纵横小说人气榜单" + if user: + return template('index.html', + title=title, + is_login=1, + is_zh=1, + is_qidian_model=1, + user=user, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) + else: + return template('index.html', + title=title, + is_login=0, + is_qidian_model=1, + search_ranking=search_ranking, + first_type=first_type, + first_type_title=first_type_title, + novels_head=novels_head) \ No newline at end of file diff --git a/soulbook/views/novels_blueprint.py b/soulbook/views/novels_blueprint.py new file mode 100644 index 0000000..2958e11 --- /dev/null +++ b/soulbook/views/novels_blueprint.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python +import time + +from jinja2 import Environment, PackageLoader, select_autoescape +from operator import itemgetter +from sanic import Blueprint +from sanic.response import redirect, html, text, json + +from soulbook.config import RULES, LOGGER, REPLACE_RULES, ENGINE_PRIORITY, CONFIG +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.cache import cache_owllook_novels_content, cache_owllook_novels_chapter, \ + cache_owllook_search_ranking +from soulbook.fetcher.function import get_time, get_netloc, get_scheme, get_timestamp +from soulbook.fetcher.novels_tools import get_novels_info +from soulbook.utils import ver_question + +novels_bp = Blueprint('novels_blueprint') +novels_bp.static('/static/novels', CONFIG.BASE_DIR + '/static/novels') + + +@novels_bp.listener('before_server_start') +def setup_db(novels_bp, loop): + global motor_base + motor_base = MotorBase() + + +@novels_bp.listener('after_server_stop') +def close_connection(novels_bp, loop): + motor_base = None + + +# jinjia2 config +env = Environment( + loader=PackageLoader('soulbook.views.novels_blueprint', '../templates/novels'), + autoescape=select_autoescape(['html', 'xml', 'tpl'])) + + +def template(tpl, **kwargs): + template = env.get_template(tpl) + return html(template.render(kwargs)) + + +@novels_bp.route("/chapter") +async def chapter(request): + """ + 返回小说章节目录页 + : content_url 这决定当前U页面url的生成方式 + : url 章节目录页源url + : novels_name 小说名称 + :return: 小说章节内容页 + """ + url = request.args.get('url', None) + novels_name = request.args.get('novels_name', None) + netloc = get_netloc(url) + # if netloc not in RULES.keys(): + # return redirect(url) + if netloc in REPLACE_RULES.keys(): + url = url.replace(REPLACE_RULES[netloc]['old'], REPLACE_RULES[netloc]['new']) + if RULES.get(netloc): + content_url = RULES[netloc].content_url + else: + content_url = "0" + content = await cache_owllook_novels_chapter(url=url, netloc=netloc) + if content: + content = str(content).strip('[],, Jjs').replace(', ', '').replace('onerror', '').replace('js', '').replace( + '加入书架', '').replace('target="_blank"', '').replace('"_blank"', '') + return template( + 'chapter.html', novels_name=novels_name, url=url, content_url=content_url, soup=content) + else: + return text('解析失败,请将失败页面反馈给本站,请重新刷新一次,或者访问源网页:{url}'.format(url=url)) + + +@novels_bp.route("/owllook_donate") +async def donate(request): + return template('donate.html') + + +@novels_bp.route("/owllook_feedback") +async def feedback(request): + return template('feedback.html') + + +@novels_bp.route("/") +async def index(request): + user = request['session'].get('user', None) + search_ranking = await cache_owllook_search_ranking() + if user: + return template('index.html', title='SoulBook - 网络小说搜索引擎', is_login=1, user=user, + search_ranking=search_ranking[:25]) + else: + return template('index.html', title='soulBook - 网络小说搜索引擎', is_login=0, search_ranking=search_ranking[:25]) + + +@novels_bp.route("/owllook_content") +async def owllook_content(request): + """ + 返回小说章节内容页 + : content_url 这决定当前U页面url的生成方式 + : url 章节内容页源url + : chapter_url 小说目录源url + : novels_name 小说名称 + :return: 小说章节内容页 + """ + url = request.args.get('url', None) + chapter_url = request.args.get('chapter_url', None) + novels_name = request.args.get('novels_name', None) + name = request.args.get('name', '') + is_ajax = request.args.get('is_ajax', '') + # 拼接链接 + if not url.startswith("http"): + # # 协议 + # get_scheme(chapter_url) + # # 域名 + # get_netloc(chapter_url) + if url.startswith("//"): + url = get_scheme(chapter_url) + ":" + url + elif url.startswith("/"): + url = get_scheme(chapter_url) + "://" + get_netloc(chapter_url) + url + else: + url = chapter_url + "/" + url + print("url: %s" % url) + # 当小说内容url不在解析规则内 跳转到原本url + netloc = get_netloc(url) + # if netloc not in RULES.keys(): + # return redirect(url) + user = request['session'].get('user', None) + # 拼接小说目录url + book_url = "/chapter?url={chapter_url}&novels_name={novels_name}".format( + chapter_url=chapter_url, + novels_name=novels_name) + motor_db = motor_base.get_db() + if url == chapter_url: + # 阅读到最后章节时候 在数据库中保存最新阅读章节 + if user and is_ajax == "owl_cache": + owl_referer = request.headers.get('Referer', '').split('owllook_content')[1] + if owl_referer: + latest_read = "/owllook_content" + owl_referer + await motor_db.user_message.update_one( + {'user': user, 'books_url.book_url': book_url}, + {'$set': {'books_url.$.last_read_url': latest_read, + 'books_url.$.set_time': time.time(), + "books_url.$.set_date": get_time() + }}) + return redirect(book_url) + if RULES.get(netloc): + content_url = RULES[netloc].content_url + else: + content_url = "0" + content_data = await cache_owllook_novels_content(url=url, chapter_url=chapter_url,netloc=netloc) + if content_data: + try: + content = content_data.get('content', '获取失败') + next_chapter = content_data.get('next_chapter', []) + title = content_data.get('title', '').replace(novels_name, '') + name = title if title else name + # 拼接小说书签url + bookmark_url = "{path}?url={url}&name={name}&chapter_url={chapter_url}&novels_name={novels_name}".format( + path=request.path, + url=url, + name=name, + chapter_url=chapter_url, + novels_name=novels_name + ) + # 破坏广告链接 + content = str(content).strip('[]Jjs,').replace('http', 'hs').replace('.js', '').replace('();', '') + # content += """欢迎关注公众号【粮草小说】,享受精品书籍推荐以及实体书赠送福利!""" + # content += """欢迎关注公众号【Soul】,享受精品书籍推荐以及实体书赠送福利!""" + if "网页版章节内容慢,请下载爱阅小说app阅读最新内容" in content: + list_split = content.rsplit("网页版章节内容慢,请下载爱阅小说app阅读最新内容", 1) + content = list_split[0] + "" + list_split[-1].split("", 1)[-1] + if user: + bookmark = await motor_db.user_message.find_one({'user': user, 'bookmarks.bookmark': bookmark_url}) + book = await motor_db.user_message.find_one({'user': user, 'books_url.book_url': book_url}) + bookmark = 1 if bookmark else 0 + if book: + # 当书架中存在该书源 + book = 1 + # 保存最后一次阅读记录 + if is_ajax == "owl_cache": + owl_referer = request.headers.get('Referer', bookmark_url).split( + 'owllook_content', 1)[-1] + latest_read = "/owllook_content" + owl_referer + await motor_db.user_message.update_one( + {'user': user, 'books_url.book_url': book_url}, + {'$set': {'books_url.$.last_read_url': latest_read, + "books_url.$.set_time": get_timestamp(), + "books_url.$.set_date": get_time(), + # "books_url.$.name": title + }}) + elif chapter_url and "owllook_content" in request.url: + owl_referer = request.url.split('owllook_content', 1)[-1] + latest_read = "/owllook_content" + owl_referer + await motor_db.user_message.update_one( + {'user': user, 'books_url.book_url': book_url}, + {'$set': {'books_url.$.last_read_url': latest_read, + "books_url.$.set_time": get_timestamp(), + "books_url.$.set_date": get_time(), + "books_url.$.name_now": title + }}) + else: + book = 0 + if is_ajax == "owl_cache": + owl_cache_dict = dict( + is_login=1, + user=user, + name=name, + url=url, + bookmark=bookmark, + book=book, + content_url=content_url, + chapter_url=chapter_url, + novels_name=novels_name, + next_chapter=next_chapter, + soup=content + ) + return json(owl_cache_dict) + return template( + 'content.html', + is_login=1, + user=user, + name=name, + url=url, + bookmark=bookmark, + book=book, + content_url=content_url, + chapter_url=chapter_url, + novels_name=novels_name, + next_chapter=next_chapter, + soup=content) + else: + if is_ajax == "owl_cache": + owl_cache_dict = dict( + is_login=0, + name=name, + url=url, + bookmark=0, + book=0, + content_url=content_url, + chapter_url=chapter_url, + novels_name=novels_name, + next_chapter=next_chapter, + soup=content + ) + return json(owl_cache_dict) + return template( + 'content.html', + is_login=0, + name=name, + url=url, + bookmark=0, + book=0, + content_url=content_url, + chapter_url=chapter_url, + novels_name=novels_name, + next_chapter=next_chapter, + soup=content) + except Exception as e: + LOGGER.exception(e) + return redirect(book_url) + else: + if user: + is_login = 1 + user = user + return template('parse_error.html', url=url, is_login=is_login, user=user) + else: + is_login = 0 + return template('parse_error.html', url=url, is_login=is_login) + + +@novels_bp.route("/register") +async def owllook_register(request): + """ + 用户登录 + :param request: + :return: + : -1 用户名或密码不能为空 + : 0 用户名或密码错误 + : 1 登陆成功 + """ + user = request['session'].get('user', None) + if user: + return redirect('/') + else: + ver_que_ans = ver_question() + if ver_que_ans: + request['session']['index'] = ver_que_ans + return template( + 'register.html', + title='soulbook - 注册 - 网络小说搜索引擎', + question=ver_que_ans[1] + ) + else: + return redirect('/') + + +@novels_bp.route("/search", methods=['GET']) +async def owllook_search(request): + start = time.time() + name = str(request.args.get('wd', '')).strip() + novels_keyword = name.split(' ')[0] + motor_db = motor_base.get_db() + if not name: + return redirect('/') + else: + # 记录搜索小说名 + try: + await motor_db.search_records.update_one({'keyword': name}, {'$inc': {'count': 1}}, upsert=True) + except Exception as e: + LOGGER.exception(e) + # 通过搜索引擎获取检索结果 + parse_result = None + if name.startswith('!baidu'): + novels_keyword = name.split('baidu')[1].strip() + novels_name = 'intitle:{name} 小说 阅读'.format(name=novels_keyword) + parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name) + elif name.startswith('!360'): + novels_keyword = name.split('360')[1].strip() + novels_name = "{name} 小说 最新章节".format(name=novels_keyword) + parse_result = await get_novels_info(class_name='so', novels_name=novels_name) + elif name.startswith('!bing'): + novels_keyword = name.split('bing')[1].strip() + novels_name = "{name} 小说 阅读 最新章节".format(name=novels_keyword) + parse_result = await get_novels_info(class_name='bing', novels_name=novels_name) + elif name.startswith('!duck_go'): + novels_keyword = name.split('duck_go')[1].strip() + novels_name = '{name} 小说 阅读 最新章节'.format(name=novels_keyword) + parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name) + else: + for each_engine in ENGINE_PRIORITY: + # for bing + if each_engine == "bing": + novels_name = "{name} 小说 阅读 最新章节".format(name=name) + parse_result = await get_novels_info(class_name='bing', novels_name=novels_name) + if parse_result: + break + # for 360 so + if each_engine == "360": + novels_name = "{name} 小说 最新章节".format(name=name) + parse_result = await get_novels_info(class_name='so', novels_name=novels_name) + if parse_result: + break + # for baidu + if each_engine == "baidu": + novels_name = 'intitle:{name} 小说 阅读'.format(name=name) + parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name) + if parse_result: + break + # for duckduckgo + if each_engine == "duck_go": + novels_name = '{name} 小说 阅读 最新章节'.format(name=name) + parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name) + if parse_result: + break + if parse_result: + # result_sorted = sorted( + # parse_result, reverse=True, key=lambda res: res['timestamp']) if ':baidu' not in name else parse_result + # 优先依靠是否解析进行排序 其次以更新时间进行排序 + result_sorted = sorted( + parse_result, + reverse=True, + key=itemgetter('is_recommend', 'is_parse', 'timestamp')) + user = request['session'].get('user', None) + if user: + try: + time_current = get_time() + res = await motor_db.user_message.update_one({'user': user}, + {'$set': {'last_update_time': time_current}}, + upsert=True) + # 此处语法操作过多 下次看一遍mongo再改 + if res: + is_ok = await motor_db.user_message.update_one( + {'user': user, 'search_records.keyword': {'$ne': novels_keyword}}, + {'$push': {'search_records': {'keyword': novels_keyword, 'counts': 1}}}, + ) + + if is_ok: + await motor_db.user_message.update_one( + {'user': user, 'search_records.keyword': novels_keyword}, + {'$inc': {'search_records.$.counts': 1}} + ) + + except Exception as e: + LOGGER.exception(e) + return template( + 'result.html', + is_login=1, + user=user, + name=novels_keyword, + time='%.2f' % (time.time() - start), + result=result_sorted, + count=len(parse_result)) + + else: + return template( + 'result.html', + is_login=0, + name=novels_keyword, + time='%.2f' % (time.time() - start), + result=result_sorted, + count=len(parse_result)) + + else: + return html("No Result!请将小说名反馈给本站,谢谢!") diff --git a/soulbook/views/operate_blueprint.py b/soulbook/views/operate_blueprint.py new file mode 100644 index 0000000..d2efae4 --- /dev/null +++ b/soulbook/views/operate_blueprint.py @@ -0,0 +1,422 @@ +#!/usr/bin/env python +import datetime +import hashlib + + +from jinja2 import Environment, PackageLoader, select_autoescape +from sanic import Blueprint +from sanic.response import html, json +from urllib.parse import parse_qs, unquote + +from soulbook.database.mongodb import MotorBase +from soulbook.fetcher.function import get_time, get_timestamp +from soulbook.utils import get_real_answer +from soulbook.config import CONFIG, LOGGER + +try: + from ujson import loads as json_loads + from ujson import dumps as json_dumps +except: + from json import loads as json_loads + from json import dumps as json_dumps + +operate_bp = Blueprint('operate_blueprint', url_prefix='operate') + + +@operate_bp.listener('before_server_start') +def setup_db(operate_bp, loop): + global motor_base + motor_base = MotorBase() + + +@operate_bp.listener('after_server_stop') +def close_connection(operate_bp, loop): + motor_base = None + + +# jinjia2 config +env = Environment( + loader=PackageLoader('soulbook.views.operate_blueprint', '../templates/operate'), + autoescape=select_autoescape(['html', 'xml', 'tpl'])) + + +def template(tpl, **kwargs): + template = env.get_template(tpl) + return html(template.render(kwargs)) + + +@operate_bp.route("/author_notification", methods=['POST']) +async def author_notification(request): + """ + 作者新书通知 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 2 无该作者信息 + : 3 作者已经添加 + : 4 超过添加的上限 + : 0 操作失败 + : 1 操作成功 + """ + user = request['session'].get('user', None) + user_data = parse_qs(str(request.body, encoding='utf-8')) + if user: + try: + motor_db = motor_base.get_db() + all_authors = await motor_db.user_message.find_one({'user': user}, {'author_latest': 1, '_id': 0}) + count = len(all_authors.get('author_latest', [])) + if count == CONFIG.WEBSITE.get("AUTHOR_LATEST_COUNT", 5): + return json({'status': 4}) + author_name = user_data.get('author_name', None)[0] + data = [] + author_cursor = motor_db.all_books.find({'author': author_name}, {'name': 1, 'url': 1, '_id': 0}) + async for document in author_cursor: + data.append(document) + if data: + time = get_time() + res = await motor_db.user_message.update_one({'user': user}, {'$set': {'last_update_time': time}}, + upsert=True) + is_exist = await motor_db.user_message.find_one( + {'user': user, 'author_latest.author_name': author_name}) + if is_exist: + return json({'status': 3}) + if res: + await motor_db.user_message.update_one( + {'user': user, 'author_latest.author_name': {'$ne': author_name}}, + {'$push': { + 'author_latest': {'author_name': author_name, 'add_time': time, + 'set_time': get_timestamp(), 'set_date': time}}}) + is_author_exist = await motor_db.author_message.find_one({'name': author_name}) + if not is_author_exist: + author_data = { + "author_name": author_name, + "nums": len(data), + "updated_time": get_time(), + 'set_time': get_timestamp() + } + await motor_db.author_message.save(author_data) + LOGGER.info('作者添加成功') + return json({'status': 1}) + else: + return json({'status': 2}) + else: + return json({'status': 2}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/change_email", methods=['POST']) +async def change_email(request): + """ + 修改用户邮箱 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 修改邮箱失败 + : 1 添加邮箱成功 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + if user: + try: + email = data.get('email', None)[0] + motor_db = motor_base.get_db() + await motor_db.user.update_one({'user': user}, + {'$set': {'email': email}}) + LOGGER.info('修改邮箱成功') + return json({'status': 1}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/change_pass", methods=['POST']) +async def change_pass(request): + """ + 修改用户密码 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 修改密码失败 + : 1 添加密码成功 + : -2 原始密码错误 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + if user: + try: + new_pass = data.get('new_pass', None)[0] + old_pass = data.get('old_pass', None)[0] + motor_db = motor_base.get_db() + user_data = await motor_db.user.find_one({'user': user}) + if user_data: + pass_first = hashlib.md5((CONFIG.WEBSITE["TOKEN"] + old_pass).encode("utf-8")).hexdigest() + pass_second = hashlib.md5((CONFIG.WEBSITE["TOKEN"] + new_pass).encode("utf-8")).hexdigest() + new_password = hashlib.md5(pass_second.encode("utf-8")).hexdigest() + password = hashlib.md5(pass_first.encode("utf-8")).hexdigest() + if password == user_data.get('password'): + await motor_db.user.update_one({'user': user}, + {'$set': {'password': new_password}}) + LOGGER.info('修改密码成功') + return json({'status': 1}) + else: + return json({'status': -2}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/add_book", methods=['POST']) +async def owllook_add_book(request): + """ + 添加书架 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 添加书架失败 + : 1 添加书架成功 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + novels_name = data.get('novels_name', '') + chapter_url = data.get('chapter_url', '') + last_read_url = data.get('last_read_url', '') + if user and novels_name and chapter_url: + url = "/chapter?url={chapter_url}&novels_name={novels_name}".format(chapter_url=chapter_url[0], + novels_name=novels_name[0]) + time = get_time() + try: + motor_db = motor_base.get_db() + res = await motor_db.user_message.update_one({'user': user}, {'$set': {'last_update_time': time}}, + upsert=True) + if res: + await motor_db.user_message.update_one( + {'user': user, 'books_url.book_url': {'$ne': url}}, + {'$push': { + 'books_url': {'book_url': url, 'add_time': time, + 'set_time': get_timestamp(), 'set_date': time,'last_read_url': + unquote(last_read_url[0])}}}) + LOGGER.info('书架添加成功') + return json({'status': 1}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/add_bookmark", methods=['POST']) +async def owllook_add_bookmark(request): + """ + 添加书签 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 添加书签失败 + : 1 添加书签成功 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + bookmark_url = data.get('bookmark_url', '') + if user and bookmark_url: + url = unquote(bookmark_url[0]) + time = get_time() + try: + motor_db = motor_base.get_db() + res = await motor_db.user_message.update_one({'user': user}, {'$set': {'last_update_time': time}}, + upsert=True) + if res: + await motor_db.user_message.update_one( + {'user': user, 'bookmarks.bookmark': {'$ne': url}}, + {'$push': {'bookmarks': {'bookmark': url, 'add_time': time, 'set_time': + get_timestamp(), 'set_date': time}}}) + LOGGER.info('书签添加成功') + return json({'status': 1}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/delete_book", methods=['POST']) +async def owllook_delete_book(request): + """ + 删除书架 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 删除书架失败 + : 1 删除书架成功 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + if user: + if data.get('book_url', None): + book_url = data.get('book_url', None)[0] + else: + novels_name = data.get('novels_name', '') + chapter_url = data.get('chapter_url', '') + book_url = "/chapter?url={chapter_url}&novels_name={novels_name}".format(chapter_url=chapter_url[0], + novels_name=novels_name[0]) + try: + motor_db = motor_base.get_db() + await motor_db.user_message.update_one({'user': user}, + {'$pull': {'books_url': {"book_url": unquote(book_url)}}}) + LOGGER.info('删除书架成功') + return json({'status': 1}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/delete_bookmark", methods=['POST']) +async def owllook_delete_bookmark(request): + """ + 删除书签 + :param request: + :return: + : -1 用户session失效 需要重新登录 + : 0 删除书签失败 + : 1 删除书签成功 + """ + user = request['session'].get('user', None) + data = parse_qs(str(request.body, encoding='utf-8')) + bookmarkurl = data.get('bookmarkurl', '') + if user and bookmarkurl: + bookmark = unquote(bookmarkurl[0]) + try: + motor_db = motor_base.get_db() + await motor_db.user_message.update_one({'user': user}, + {'$pull': {'bookmarks': {"bookmark": bookmark}}}) + LOGGER.info('删除书签成功') + return json({'status': 1}) + except Exception as e: + LOGGER.exception(e) + return json({'status': 0}) + else: + return json({'status': -1}) + + +@operate_bp.route("/login", methods=['POST']) +async def owllook_login(request): + """ + 用户登录 + :param request: + :return: + : -1 用户名或密码不能为空 + : 0 用户名或密码错误 + : 1 登陆成功 + """ + login_data = parse_qs(str(request.body, encoding='utf-8')) + user = login_data.get('user', [None])[0] + pwd = login_data.get('pwd', [None])[0] + if user and pwd: + motor_db = motor_base.get_db() + data = await motor_db.user.find_one({'user': user}) + if data: + pass_first = hashlib.md5((CONFIG.WEBSITE["TOKEN"] + pwd).encode("utf-8")).hexdigest() + password = hashlib.md5(pass_first.encode("utf-8")).hexdigest() + if password == data.get('password'): + response = json({'status': 1}) + # 将session_id存于cokies + date = datetime.datetime.now() + response.cookies['owl_sid'] = request['session'].sid + response.cookies['owl_sid']['expires'] = date + datetime.timedelta(days=30) + response.cookies['owl_sid']['httponly'] = True + # 此处设置存于服务器session的user值 + request['session']['user'] = user + # response.cookies['user'] = user + # response.cookies['user']['expires'] = date + datetime.timedelta(days=30) + # response.cookies['user']['httponly'] = True + # response = json({'status': 1}) + # response.cookies['user'] = user + return response + else: + return json({'status': -2}) + return json({'status': -1}) + else: + return json({'status': 0}) + + +@operate_bp.route("/logout", methods=['GET']) +async def owllook_logout(request): + """ + 用户登出 + :param request: + :return: + : 0 退出失败 + : 1 退出成功 + """ + user = request['session'].get('user', None) + if user: + response = json({'status': 1}) + del response.cookies['user'] + del response.cookies['owl_sid'] + return response + else: + return json({'status': 0}) + + +@operate_bp.route("/register", methods=['POST']) +async def owllook_register(request): + """ + 用户注册 不允许重名 + :param request: + :return: + : -1 用户名已存在 + : 0 用户名或密码不能为空 + : 1 注册成功 + """ + register_data = parse_qs(str(request.body, encoding='utf-8')) + user = register_data.get('user', [None])[0] + pwd = register_data.get('pwd', [None])[0] + email = register_data.get('email', [None])[0] + answer = register_data.get('answer', [None])[0] + reg_index = request.cookies.get('reg_index') + if user and pwd and email and answer and reg_index and len(user) > 2 and len(pwd) > 5: + motor_db = motor_base.get_db() + is_exist = await motor_db.user.find_one({'user': user}) + if not is_exist: + # 验证问题答案是否准确 + real_answer = get_real_answer(str(reg_index)) + if real_answer and real_answer == answer: + pass_first = hashlib.md5((CONFIG.WEBSITE["TOKEN"] + pwd).encode("utf-8")).hexdigest() + password = hashlib.md5(pass_first.encode("utf-8")).hexdigest() + time = get_time() + data = { + "user": user, + "password": password, + "email": email, + "register_time": time, + } + await motor_db.user.save(data) + return json({'status': 1}) + else: + return json({'status': -2}) + else: + return json({'status': -1}) + else: + return json({'status': 0}) + + # post_data = json_loads(str(request.body, encoding='utf-8')) + # pass_first = hashlib.md5((CONFIG.WEBSITE["TOKEN"] + post_data['pwd']).encode("utf-8")).hexdigest() + # password = hashlib.md5(pass_first.encode("utf-8")).hexdigest() + # time = get_time() + # data = { + # "user": post_data['user'], + # "password": password, + # "email": post_data['email'], + # "register_time": time, + # } + # motor_db = motor_base.get_db() + # await motor_db.user.save(data) diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..ed799d0 --- /dev/null +++ b/start.sh @@ -0,0 +1,2 @@ +sh stop.sh +nohup python3 -m gunicorn -c soulbook/config/gunicorn.py --worker-class sanic.worker.GunicornWorker soulbook.server:app --log-level=debug >/dev/null 2>&1 & diff --git a/stop.sh b/stop.sh new file mode 100644 index 0000000..ec71fd6 --- /dev/null +++ b/stop.sh @@ -0,0 +1 @@ +ps -ef | grep "c soulbook" | grep -v grep | awk '{print $2}' | xargs kill -9 diff --git a/tests/test_heiyan_novel_info.py b/tests/test_heiyan_novel_info.py new file mode 100644 index 0000000..4069bab --- /dev/null +++ b/tests/test_heiyan_novel_info.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +""" + Created by howie.hu at 2018/5/28. +""" +import asyncio +import os +import sys + +import pytest + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from soulbook.spiders.heiyan_novel_info import HYNovelInfoItem + +HTML = """ + + + + + + 神仙微信群无广告,神仙微信群最新章节全文阅读,向阳的心的小说_黑岩网_黑岩阅读网 + + + + + + + + + + + + + + + + + + + + +""" + + +def test_heiyan_novel_info(): + url = 'http://www.heiyan.com/book/62599' + item_data = asyncio.get_event_loop().run_until_complete(HYNovelInfoItem.get_item(html=HTML)) + + assert item_data.novel_name == '神仙微信群'