Merge branch 'release/1.1.3'
221
README.md
@ -3,12 +3,12 @@
|
||||
<p align="center">让你瞬间进入沉浸式阅读的扩展,还原阅读的本质,提升你的阅读体验。</p>
|
||||
<p align="center">为了达到完美的阅读模式这个小目标 ,我适配了 <a target="_blank" href="https://simpread.ksria.cn/sites/">数百种类型</a> 的网站,因此诞生了简悦。</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/kenshin/simpread/releases"><img src="https://img.shields.io/badge/lastest_version-1.1.2-blue.svg"></a>
|
||||
<a href="https://github.com/kenshin/simpread/releases"><img src="https://img.shields.io/badge/lastest_version-1.1.3-blue.svg"></a>
|
||||
<a target="_blank" href="http://ksria.com/simpread"><img src="https://img.shields.io/badge/website-_simpread.ksria.com-1DBA90.svg"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://chrome.google.com/webstore/detail/%E7%AE%80%E6%82%A6-simpread/ijllcpnolfcooahcekpamkbidhejabll"><img src="https://img.shields.io/badge/download-_chrome_webstore-brightgreen.svg"></a>
|
||||
<a href="http://ksria.com/simpread/crx/1.1.2/simpread.crx"><img src="https://img.shields.io/badge/download-_crx-brightgreen.svg"></a>
|
||||
<a href="http://ksria.com/simpread/crx/1.1.3/simpread.crx"><img src="https://img.shields.io/badge/download-_crx-brightgreen.svg"></a>
|
||||
<a target="_blank" href="https://addons.mozilla.org/zh-CN/firefox/addon/simpread"><img src="https://img.shields.io/badge/download-_firefox_addon-DD512A.svg"></a>
|
||||
<a target="_blank" href="https://greasyfork.org/zh-CN/scripts/39998"><img src="https://i.imgur.com/JFhxHeR.png"></a>
|
||||
<a target="_blank" href="https://xteko.com/redir?url=http://sr.ksria.cn/jsbox/simpread-1.0.2.box?201810231502&name=%E7%AE%80%E6%82%A6"><img src="https://i.imgur.com/zZeOllB.png"></a>
|
||||
@ -16,63 +16,113 @@
|
||||
|
||||
***
|
||||
|
||||
#### 马上使用:
|
||||
* [Chrome 应用商店](https://chrome.google.com/webstore/detail/%E7%AE%80%E6%82%A6-simpread/ijllcpnolfcooahcekpamkbidhejabll) 或者 [离线下载](http://ksria.com/simpread/crx/1.1.2/simpread.crx)
|
||||
## 目录
|
||||
|
||||
* [下载](#马上使用)
|
||||
|
||||
* [入门指南](#入门指南)
|
||||
|
||||
* [主要功能一览](#主要功能一览)
|
||||
|
||||
* [全部功能](#全部功能)
|
||||
|
||||
* [如何使用](#如何使用)
|
||||
|
||||
* [简悦各平台版本之间的差异](#简悦各平台版本之间的差异)
|
||||
|
||||
* [截图](#截图)
|
||||
|
||||
* [提交新站](#提交新站)
|
||||
|
||||
* [投票](#投票)
|
||||
|
||||
* [相关链接](#相关链接)
|
||||
|
||||
* [贡献者](#感谢)
|
||||
|
||||
* [参与开发](#协作开发)
|
||||
|
||||
* [请杯咖啡](#请杯咖啡-)
|
||||
|
||||
* [开源列表](#简悦的诞生离不开它们)
|
||||
|
||||
* [许可](#许可)
|
||||
|
||||
***
|
||||
|
||||
## 马上使用
|
||||
* [Chrome 应用商店](https://chrome.google.com/webstore/detail/%E7%AE%80%E6%82%A6-simpread/ijllcpnolfcooahcekpamkbidhejabll) 或者 [离线下载](http://ksria.com/simpread/crx/1.1.3/simpread.crx)
|
||||
|
||||
* [Firefox 扩展中心](https://addons.mozilla.org/zh-CN/firefox/addon/simpread)
|
||||
|
||||
* [支持 UserScript 的浏览器](https://greasyfork.org/zh-CN/scripts/39998) 如:Apple Safari · Microsoft Edge · Opera · Dolphin 详细 [请看这里](https://github.com/Kenshin/simpread-little)
|
||||
* [iOS( iPhone / iPad )版](https://xteko.com/redir?url=http://sr.ksria.cn/jsbox/simpread-1.0.0.box?201805251238&name=%E7%AE%80%E6%82%A6) 详细 [请看这里](http://ksria.com/simpread/docs/#/JSBox)
|
||||
|
||||
* 现在就加入 [Telegram 群](https://t.me/simpread),获取简悦的第一手资料。
|
||||
* 不同简悦如何使用?想知道简悦的高级功能,请看简悦的 [帮助中心](http://ksria.com/simpread/docs/#) 。
|
||||
* [iPhone / iPad 版](https://xteko.com/redir?url=http://sr.ksria.cn/jsbox/simpread-1.0.0.box?201805251238&name=%E7%AE%80%E6%82%A6) 详细 [请看这里](http://ksria.com/simpread/docs/#/JSBox)
|
||||
|
||||
#### 主要功能一览:
|
||||
- [聚焦模式](http://ksria.com/simpread/docs/#/聚焦模式):
|
||||
* [Android 版](http://ksria.com/simpread/docs/#/Android) 详细 [请看这里](http://ksria.com/simpread/docs/#/Android)
|
||||
|
||||
## 入门指南
|
||||
|
||||
* 简悦的内容较多,汇总到了一个页面 [新闻页](https://simp.red/news)
|
||||
|
||||
* 喜欢简悦,但不会用,对新手极度舒适的 [新手入门](http://ksria.com/simpread/guide)
|
||||
|
||||
* 参与讨论请加入 [Telegram 群](https://t.me/simpread)
|
||||
|
||||
* 想知道简悦的高级玩法,请看简悦的 [文档中心](http://ksria.com/simpread/docs/#)
|
||||
|
||||
* 更多联系方式请看 [相关链接](#相关链接)
|
||||
|
||||
## 主要功能一览
|
||||
- [聚焦模式](http://ksria.com/simpread/docs/#/聚焦模式)
|
||||
不改变当前页面的结构,仅仅高亮需要阅读的部分,不分散用户的注意力;适合 `临时阅读` 或者 `未适配阅读模式` 的网站
|
||||
|
||||
- [阅读模式](http://ksria.com/simpread/docs/#/阅读模式):
|
||||
- [阅读模式](http://ksria.com/simpread/docs/#/阅读模式)
|
||||
简悦 `原创` 功能,逐一适配了 [数百种类型](https://simpread.ksria.cn/sites/) 的网站,自动提取 `标题` `描述` `正文` `媒体资源( 图片/ 视频 )` 等,生成 `符合中文阅读` 的页面
|
||||
|
||||
* 支持 `自动生成目录`
|
||||
|
||||
* 支持 [TXT 阅读器](http://ksria.com/simpread/docs/#/TXT-阅读器)
|
||||
* 支持 [自动生成目录](http://ksria.com/simpread/docs/#/目录)
|
||||
|
||||
* 支持 [论坛类页面及分页](http://ksria.com/simpread/docs/#/论坛类页面及分页) 如:知乎 · 百度贴吧等
|
||||
|
||||
*  支持 [代码段的高亮](http://ksria.com/simpread/docs/#/代码段的高亮),包含了大部分常见的网站
|
||||
|
||||
*  支持 [LaTeX 解析](http://ksria.com/simpread/docs/#/LaTeX-阅读器) · [Markdown 阅读器](http://ksria.com/simpread/docs/#/Markdown-阅读器)
|
||||
* 支持 [TXT 阅读器](http://ksria.com/simpread/docs/#/TXT-阅读器) · 支持 [LaTeX 解析](http://ksria.com/simpread/docs/#/LaTeX-阅读器) · [Markdown 阅读器](http://ksria.com/simpread/docs/#/Markdown-阅读器)
|
||||
|
||||
* 更符合 `中文阅读` 习惯的设置,包括:`字间距` `行间距` 等 以及 `自定义 CSS` ,详细请看 [自定义样式](http://ksria.com/simpread/docs/#/自定义样式)
|
||||
|
||||
- 临时阅读模式:
|
||||
将 `非适配阅读模式的页面` 生成 `阅读模式` 一样的排版,支持任意页面,详细请看 [临时阅读模式](http://ksria.com/simpread/docs/#/临时阅读模式) 以及 [操作](http://ksria.com/simpread/welcome/version_1.0.5.html#temp-read-mode)
|
||||
|
||||
- 主动适配:
|
||||
- 主动适配
|
||||
通过简单的一个步骤,就可以让 `非适配页面` 支持阅读模式,详细请看 [主动适配](http://ksria.com/simpread/docs/#/主动适配阅读模式) 以及 [操作](http://ksria.com/simpread/welcome/version_1.0.5.html#mate-read-mode)
|
||||
|
||||
- 智能适配:
|
||||
-  智能适配
|
||||
|
||||
*  全新的 `词法分析引擎`,更智能、更精准的提取正文,辅以精准适配,任意网页均「不在话下」,不仅能自动识别出 Wordpress · Hexo · Ghost · Discuz 等博客 / 论坛的页面,甚至于只要是结构良好的页面,(无需适配)自动生成阅读模式,详细请看 [词法分析引擎](http://ksria.com/simpread/docs/#/词法分析引擎)
|
||||
全新的 `词法分析引擎`,更智能、更精准的提取正文,辅以精准适配,任意网页均「不在话下」,不仅能自动识别出 Wordpress · Hexo · Ghost · Discuz 等博客 / 论坛的页面,甚至于只要是结构良好的页面,(无需适配)自动生成阅读模式,详细请看 [词法分析引擎](http://ksria.com/simpread/docs/#/词法分析引擎)
|
||||
|
||||
- 站点适配源:
|
||||
包括:`官方(主)适配源` `第三方适配源` `站点集市适配源` `自定义适配源`,详细请看 [站点适配源](http://ksria.com/simpread/docs/#/站点适配源)
|
||||
-  智能感知
|
||||
|
||||
- 站点编辑器:
|
||||
当生成的阅读模式出现问题时,简悦会自动重新获取正文,详细说明请看 [智能感知](http://ksria.com/simpread/docs/#/智能感知)
|
||||
|
||||
-  手动框选适配
|
||||
针对 `未适配` 或 `智能识别` 失败的情况,简悦可以使用手动框选的方式,生成阅读模式,详细请看 [手动框选](http://ksria.com/simpread/docs/#/手动框选)
|
||||
|
||||
- 站点适配源
|
||||
包括:`官方适配源` `第三方适配源` `站点集市适配源` `自定义适配源`,详细请看 [站点适配源](http://ksria.com/simpread/docs/#/站点适配源)
|
||||
|
||||
- 站点编辑器
|
||||
页面任意元素,均可隐藏,`可编程,定制化`,详细请看 [站点编辑器](http://ksria.com/simpread/docs/#/站点编辑器)
|
||||
|
||||
- 站点管理器:
|
||||
- 站点管理器
|
||||
可管理全部的适配站点,详细请看 [站点管理器](http://ksria.com/simpread/docs/#/站点管理器)
|
||||
|
||||
-  站点集市:
|
||||
- 站点集市
|
||||
上传并共享自己的适配站点,一键分享临时阅读模式,适配失败的站一键提交,详细请看 [站点集市](https://simpread.ksria.cn/sites)
|
||||
|
||||
-  插件系统:
|
||||
- 插件系统
|
||||
现在开始可以使用 JavaScript 编写基于 `简悦` 的插件了,更上线了 [插件中心](https://simpread.ksria.cn/plugins/) ,如何编写插件请看 → [说明文档](http://ksria.com/simpread/docs/#/插件系统)
|
||||
|
||||
- 多种主题:
|
||||
- 多种主题
|
||||
`白练、白磁、卯之花色、丁子色、娟鼠、月白、百合、紺鼠、黒鸢` 等
|
||||
|
||||
- 丰富的导出功能,包括:
|
||||
- 丰富的导出功能
|
||||
|
||||
- 导出 [Markdown](https://github.com/Kenshin/simpread#感谢) · `HTML` · `PNG` · `PDF` · [epub](http://ksria.com/simpread/docs/#/发送到-Epub)
|
||||
|
||||
@ -80,30 +130,40 @@
|
||||
|
||||
- 导出到 `Pocket` `Linnk` `Instapaper` 的功能,包括:`当前页面的链接` `稍后读`
|
||||
|
||||
- 导出到生产力工具,包括:`Dropbox` `Onenote` `Google Drive` `印象笔记 / Evernote`,详细请看 [授权服务](http://ksria.com/simpread/docs/#/授权服务)
|
||||
- 导出到生产力工具,包括:`坚果云` `语雀` `Dropbox` `Onenote` `Google Drive` `印象笔记 / Evernote`,详细请看 [导出到生产力](http://ksria.com/simpread/docs/#/导出到生产力工具)
|
||||
|
||||
- 同步 · 上传/下载 配置 · 同步适配列表 · [快捷键支持](http://ksria.com/simpread/docs/#/快捷键) 等;
|
||||
|
||||
- 高级定制,包括:`右键菜单` `控制栏可隐藏` `阅读进度可隐藏` `自动进入阅读模式` [白名单](http://ksria.com/simpread/docs/#/FAQ?id=白名单) 以及 [排除列表](http://ksria.com/simpread/docs/#/FAQ?id=排除列表) 等
|
||||
|
||||
- 稍后读;
|
||||
- 稍后读
|
||||
|
||||
#### 截图:
|
||||
## 全部功能
|
||||
|
||||
<details>
|
||||
<img src="http://sr.ksria.cn/feature%201.1.3.png">
|
||||
</details>
|
||||
|
||||
## 截图
|
||||

|
||||

|
||||

|
||||
|
||||
#### 照片集:
|
||||
> 包含了 `稍后读` `阅读模式 · 设置界面` `导出到生产力工具` `发送到 Kindle` `自定义样式` `同步配置` `论坛类页面 · 分页` 等功能的截图(动图)
|
||||
<details><summary>更多截图</summary>
|
||||
<img src="http://sr.ksria.cn/welcome-readme-2.png"/>
|
||||
<img src="http://sr.ksria.cn/welcome-readme-3.png"/>
|
||||
<img src="http://sr.ksria.cn/welcome-readme-4.png"/>
|
||||
<img src="https://i.loli.net/2019/06/26/5d12e86b976f450493.png"/>
|
||||
<img src="https://i.loli.net/2019/06/26/5d12e86bd9d6844696.png"/>
|
||||
<img src="https://i.loli.net/2019/06/26/5d12e86bde78615339.png"/>
|
||||
<img src="https://i.loli.net/2019/06/26/5d12e86c9468188114.png"/>
|
||||
</details>
|
||||
|
||||
* <http://ksria.com/simpread/gallery/>
|
||||
## 如何使用
|
||||
|
||||
#### 全部功能:
|
||||

|
||||
> 简悦虽然拥有众多功能,但它支持 **开箱即用**,新手(不想折腾党)来说,只需要看懂下面两种操作即可。
|
||||
|
||||
#### 如何使用(阅读模式):
|
||||
### 阅读模式
|
||||
|
||||
`简悦`会自动检测当前页面是否已经适配,如适配则在浏览器右上角显示  ,使用以下三种方式启动:
|
||||
> `简悦`会自动检测当前页面是否已经适配,如适配则在浏览器右上角显示  ,使用以下三种方式启动:
|
||||
|
||||
- 点击浏览器右上角 `红色icon`;
|
||||
|
||||
@ -111,15 +171,21 @@
|
||||
|
||||
- 快捷键;(默认为 双击 <kbd>A</kbd> )
|
||||
|
||||
- 简悦支持自动进入阅读模式,详细请看 [自动进入阅读模式](http://ksria.com/simpread/docs/#/自动进入阅读模式)
|
||||
|
||||
#### 如何使用(聚焦模式):
|
||||
`聚焦模式` 会自动获取当前鼠标所在的段落并高亮,适合任意页面。
|
||||
### 聚焦模式
|
||||
|
||||
> `聚焦模式` 会自动获取当前鼠标所在的段落并高亮,适合任意页面。
|
||||
|
||||
- 在需要高亮的区域,右键选择 `简悦 - SimpRead` → `聚焦模式`;
|
||||
|
||||
- 快捷键;(默认为 <kbd>A</kbd> <kbd>S</kbd> )
|
||||
|
||||
#### 提交新站:
|
||||
## 简悦各平台版本之间的差异
|
||||
|
||||
> 包括:Chrome / Firefox / 轻阅版(UserScript)/ JSBox ,请访问 [简悦 · 新闻页](https://www.notion.so/9c109ec145134297ab461f5b52dbadc7?v=ce94e37d8a794cfbbd39bf9dfaf9017a)
|
||||
|
||||
## 提交新站
|
||||
|
||||
* 方式1:通过 **提交到 站点集市** 的方式,详细请看 [站点管理器 · 上传](http://ksria.com/simpread/docs/#/站点管理器?id=上传)
|
||||
|
||||
@ -128,37 +194,52 @@
|
||||
|
||||
* 方式3:通过 **新增站点编辑器** 的方式(适合逐一添加单个新站),详细说明 [请看这里](http://ksria.com/simpread/docs/#/站点编辑器?id=如何新增修改);
|
||||
|
||||
* 方式4:通过 **提交适配源** 的方式(适合同时添加多个新站),详细说明请看 [使用自定义适配源](http://ksria.com/simpread/docs/#/站点适配源?id=如何提交到简悦的官方(次)适配源)
|
||||
* 方式4:通过 **提交适配源** 的方式(适合同时添加多个新站),详细说明请看 [使用自定义适配源](http://ksria.com/simpread/docs/#/如何提交第三方适配源);
|
||||
|
||||
* 更多说明请看 [站点编辑器](http://ksria.com/simpread/docs/#/站点编辑器) [站点管理](http://ksria.com/simpread/docs/#/站点管理器) [站点适配源](http://ksria.com/simpread/docs/#/站点适配源);
|
||||
|
||||
#### Chrome / Firefox / 轻阅版(UserScript)功能差别:
|
||||

|
||||
|
||||
#### 投票:
|
||||
## 投票
|
||||
简悦是一个免费并开源的项目。如果觉得不错,请给我 [投票](https://chrome.google.com/webstore/detail/%E7%AE%80%E6%82%A6-simpread/ijllcpnolfcooahcekpamkbidhejabll/reviews) 。这样让更多人了解并受用与 `简悦` 带来的便利,你的认可是对我最大的鼓励。
|
||||
|
||||
#### 相关链接:
|
||||
## 相关链接
|
||||
* [更新日志](http://ksria.com/simpread/changelog.html)
|
||||
|
||||
* [帮助中心](http://ksria.com/simpread/docs/#)
|
||||
* [新手入门](http://ksria.com/simpread/docs/#/入门指南(-操作指引-))
|
||||
|
||||
* [新手入门](http://ksria.com/simpread/guide)
|
||||
|
||||
* [常见问题](http://ksria.com/simpread/docs/#/FAQ)
|
||||
|
||||
* [反馈](https://github.com/kenshin/simpread/issues)
|
||||
|
||||
* [联系](http://kenshin.wang) · [邮件](kenshin@ksria.com) · [微博](http://weibo.com/23784148) · [Telegram 群](https://t.me/simpread)
|
||||
* 想了解简悦背后的故事? [猛击这里](https://sspai.com/post/39491)
|
||||
|
||||
#### 感谢:
|
||||
- [ksky521](https://github.com/ksky521) 提供 `minimatch` 和 `markdown转换功能` ,解决了 [Fix 11](https://github.com/kenshin/simpread/issues/11),详细请看 Pull requests [#16](https://github.com/kenshin/simpread/pull/16);
|
||||
- [airycanon](https://github.com/airycanon) Pull requests 详细请看 [#23](https://github.com/kenshin/simpread/pull/23) [#44](https://github.com/kenshin/simpread/pull/44) [#124](https://github.com/kenshin/simpread/pull/124);
|
||||
- [mikelei8291](https://github.com/mikelei8291) 详细请看 Pull requests [#114](https://github.com/kenshin/simpread/pull/114);
|
||||
## 感谢
|
||||
|
||||
#### 简悦的诞生离不开它们:
|
||||
[ksky521](https://github.com/ksky521) · [airycanon](https://github.com/airycanon) · [mikelei8291](https://github.com/mikelei8291) · [chenhbc](https://github.com/chenhbc) · [Nihility](https://github.com/NihilityT) · [WangLeto](https://github.com/WangLeto) · [SevenSteven](https://github.com/Seven-Steven) · [Leo](https://github.com/clinyong) · [Jonas · Gao](https://github.com/JonasGao) · [Cologler](https://github.com/Cologler) · [bgh](https://github.com/bldght) · [Ronglong Pu](https://github.com/PuRonglong)
|
||||
|
||||
## 协作开发
|
||||
|
||||
> Pull requests 方式:
|
||||
|
||||
* 请务必从 **develop** 分支开始;( **注意:非 develop 分支的 pr 将不会被合并** )
|
||||
|
||||
* Pull requests
|
||||
|
||||
* 如果需要合并的话,合并后我会通知你;(在下个版本发布时一起发布)
|
||||
|
||||
## 请杯咖啡 ☕
|
||||
如果简悦可以解决你在阅读上痛点,提升 Web 端的阅读体验,可以请我喝杯咖啡,想必也是非常愉悦的事情。 :smile:
|
||||
_如发现下图显示不全,请直接访问 http://sr.ksria.cn/zhifu_m2.png_
|
||||

|
||||
|
||||
## 简悦的诞生离不开它们
|
||||
- [Node.js](https://nodejs.org/) · [NPM](https://www.npmjs.com)
|
||||
- [Webpack](https://webpack.github.io/)
|
||||
- [React](https://facebook.github.io/react)
|
||||
- [ES6](http://es6-features.org/) · [Babel](https://babeljs.io)
|
||||
- [PostCSS](http://postcss.org/) · [cssnext](http://cssnext.io/)
|
||||
- [jQuery](https://jquery.com/) · [Mousetrap](https://craig.is/killing/mice) · [pangu.js](https://github.com/vinta/pangu.js) · [ProgressBar.js](https://kimmobrunfeldt.github.io/progressbar.js/) · [timego.js](http://timeago.org/) · [Velocity.js](http://velocityjs.org/) · [minimatch](https://github.com/isaacs/minimatch) · [to-markdown](https://github.com/domchristie/to-markdown) · [FileSaver.js](https://github.com/eligrey/FileSaver.js) · [dom-to-image](https://github.com/tsayen/dom-to-image)
|
||||
- [jQuery](https://jquery.com/) · [Mousetrap](https://craig.is/killing/mice) · [pangu.js](https://github.com/vinta/pangu.js) · [ProgressBar.js](https://kimmobrunfeldt.github.io/progressbar.js/) · [timego.js](http://timeago.org/) · [Velocity.js](http://velocityjs.org/) · [minimatch](https://github.com/isaacs/minimatch) · [to-markdown](https://github.com/domchristie/to-markdown) · [FileSaver.js](https://github.com/eligrey/FileSaver.js) · [dom-to-image](https://github.com/tsayen/dom-to-image) · [WebDAV]( https://github.com/aslakhellesoy/webdavjs)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/)
|
||||
- [Sketch](https://www.sketchapp.com/) · [Pixelmator](http://www.pixelmator.com/)
|
||||
- Icon from <http://iconfont.cn>
|
||||
@ -168,37 +249,17 @@
|
||||
- Mind Maps <https://coggle.it/>
|
||||
- 咖啡 · 网易音乐 · Google Chrome · rMBP
|
||||
|
||||
#### 协作开发:
|
||||
- Pull requests 方式:
|
||||
* 请从 `develop` 分支开始;( 注意:非 develop 分支的 pr 将不会合并;)
|
||||
* Pull requests
|
||||
* 如果需要合并的话,合并后我会通知你;(在下个版本发布时一起发布)
|
||||
|
||||
- 源代码编译:
|
||||
* git clone https://github.com/Kenshin/simpread.git
|
||||
* cd simpread
|
||||
* npm install
|
||||
* npm run develop ( for Mac ) 用于开发
|
||||
* npm run publish ( for Mac ) 用于发布
|
||||
* npmw run develop ( for Win ) 用于开发
|
||||
* npmw run publish ( for Win ) 用于发布
|
||||
|
||||
#### 请杯咖啡:
|
||||
如果简悦可以解决你在阅读上痛点,提升 Web 端的阅读体验,可以请我喝杯咖啡,想必也是非常愉悦的事情。 :smile:
|
||||
_如发现下图显示不全,请直接访问 http://sr.ksria.cn/zhifu_m2.png_
|
||||

|
||||
|
||||
#### 许可:
|
||||
## 许可
|
||||
[![license-badge]][license-link]
|
||||
|
||||
<!-- Link -->
|
||||
[www-badge]: https://img.shields.io/badge/website-_simpread.ksria.com-1DBA90.svg
|
||||
[www-link]: http://ksria.com/simpread
|
||||
[version-badge]: https://img.shields.io/badge/lastest_version-1.1.2-blue.svg
|
||||
[version-badge]: https://img.shields.io/badge/lastest_version-1.1.3-blue.svg
|
||||
[version-link]: https://github.com/kenshin/simpread/releases
|
||||
[chrome-badge]: https://img.shields.io/badge/download-_chrome_webstore-brightgreen.svg
|
||||
[chrome-link]: https://chrome.google.com/webstore/detail/%E7%AE%80%E6%82%A6-simpread/ijllcpnolfcooahcekpamkbidhejabll
|
||||
[offline-badge]: https://img.shields.io/badge/download-_crx-brightgreen.svg
|
||||
[offline-link]: http://ksria.com/simpread/crx/1.1.2/simpread.crx
|
||||
[offline-link]: http://ksria.com/simpread/crx/1.1.3/simpread.crx
|
||||
[license-badge]: https://img.shields.io/github/license/mashape/apistatus.svg
|
||||
[license-link]: https://opensource.org/licenses/MIT
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"develop": "cross-env NODE_ENV=development webpack --devtool=source-map --progress --colors --watch",
|
||||
"develop": "cross-env NODE_ENV=development webpack --devtool=eval-source-map --progress --colors --watch",
|
||||
"publish": "cross-env NODE_ENV=production webpack -p --progress --colors",
|
||||
"ext_dev": "cross-env NODE_ENV=development webpack --devtool=source-map --progress --colors --watch --config webpack.config.ext.js",
|
||||
"ext_pub": "cross-env NODE_ENV=production webpack -p --progress --color --config webpack.config.ext.js",
|
||||
|
||||
196
src/assets/css/options_notice.css
Normal file
@ -0,0 +1,196 @@
|
||||
.header {
|
||||
background-color: #16666f;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.notice {
|
||||
margin-top: 65px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notice notice {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
notice .loading,
|
||||
notice .failed {
|
||||
position: fixed;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
notice .loading svg {
|
||||
transform: scale(.8);
|
||||
}
|
||||
|
||||
notice .failed {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
notice .failed span {
|
||||
color: #9b9b9b;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
notice .list {
|
||||
margin: 20px 20px 0;
|
||||
width: 300px;
|
||||
min-height: 589px;
|
||||
}
|
||||
|
||||
notice .list.controlbar {
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
notice .detail {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
margin: 20px 20px 0 0;
|
||||
padding: 39px 24px 0px;
|
||||
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
notice .detail img {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
notice .detail .empty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
padding-bottom: 39px;
|
||||
min-height: 550px;
|
||||
|
||||
color: #9b9b9b;
|
||||
font-size: 2.6rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
notice .detail .empty .icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url( data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfhBA0LLBOn/NkLAAABtklEQVRYw+2YPyxDURTGf5WOGCwiETH7s4kaDJo2FokYMBglZ66lOtGEqWIQm9zdoJouGERIEwNlE4NE4i0iBoO0CxIMvW1f26evvJY86Tfdc3LP99137vvuTa6HqlAwzxI9X064ISrxagweE5mfOXp18CHjoCDJFHbYlBCAOiywGWzLSYmAauUIn6koI+2g/Bzb0gMMyDWoZ9pNuTQByUKLbkSyhD6PyZroYcwiN0xSoQUYJGhZ+FKjwKtlNsggeAEIFJIbnJqm7BKpSeAIgHkdjbKgRwGucgIdOrEuYXOdXKo1Fm3pF+QOQBI6Tqg3wnleb8nUVHmtRNQBQueX5AZbclmRTVFYqBcbSKpS9jtocVLcFGgK/I6AhQ/UEPHCsf09GMyU287KaBc/Xm4vF+YbBv5oDzYd8FXUWrRIQoQa+wV1hfsFmj6whTt9IPvFRv3L39Q51AR7AKzKctMHtnCnD2wE6oAbpgF4bJCA3HKbH7vcaCsSdf9ZVCrQVyfW/uIwtwdZHcXUEzuSccKt2pglpoOsfkpQPs4a0p8ROc+1KI3RAHqDdPExpIv78nPQId7plgf4BCjPbVayklPeAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA0LTEzVDExOjQ0OjE5KzA4OjAwPYuVdQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNC0xM1QxMTo0NDoxOSswODowMEzWLckAAAAASUVORK5CYII= );
|
||||
}
|
||||
|
||||
notice .detail .preview {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
notice .detail .preview .title {
|
||||
margin-bottom: 20px;
|
||||
|
||||
font-family: PingFang SC,Hiragino Sans GB,Microsoft Yahei,WenQuanYi Micro Hei,sans-serif;
|
||||
font-size: 3.4rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
notice .detail .preview .desc {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
border-bottom: 1px solid #E0E0E0;
|
||||
}
|
||||
|
||||
notice .list {
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
notice list {
|
||||
display: flex!important;
|
||||
flex-direction: column;
|
||||
|
||||
padding: 16px;
|
||||
min-height: 72px;
|
||||
|
||||
border-bottom: 1px solid #E0E0E0;
|
||||
transition : all 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
}
|
||||
|
||||
notice list:hover {
|
||||
background-color: #f5f5f5;
|
||||
transition : all 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
}
|
||||
|
||||
notice list:hover .meta {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
notice list.active {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
notice list.selected {
|
||||
background-color: #e1f5fe;
|
||||
border-left: 4px solid #29b6f6;
|
||||
}
|
||||
|
||||
notice list.active .title {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
notice list .title {
|
||||
margin-bottom: 4px;
|
||||
padding-right: 25px;
|
||||
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
notice list .category,
|
||||
notice .detail .preview .category {
|
||||
margin-right: 4px;
|
||||
padding: 2px 7px;
|
||||
padding: 2px 4px;
|
||||
|
||||
color: #fff;
|
||||
|
||||
font-weight: 400;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
notice list .meta {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
transition : all 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
}
|
||||
|
||||
notice .date {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Options page style
|
||||
*/
|
||||
|
||||
:root {
|
||||
--text-color: #333;
|
||||
@ -217,7 +220,6 @@ a {
|
||||
/*line-height: 37px;*/
|
||||
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#labs .more .desc {
|
||||
@ -239,6 +241,10 @@ a {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins & sites Card
|
||||
*/
|
||||
|
||||
cards {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
@ -364,6 +370,9 @@ card-empty a {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account
|
||||
*/
|
||||
.avatar {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
@ -382,3 +391,273 @@ card-empty a {
|
||||
border: 5px solid #fff;
|
||||
box-shadow: 0 10px 20px 0 rgba(168,182,191,0.6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notice bubbles
|
||||
*/
|
||||
.bubbles {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
width: 55px;
|
||||
height: 56px;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
box-shadow: 0 2px 6px 0 rgba(0,0,0,.4);
|
||||
|
||||
cursor: pointer;
|
||||
transition: all 500ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
}
|
||||
|
||||
.bubbles.notice {
|
||||
right: 94px;
|
||||
background-color: #16666f;
|
||||
}
|
||||
|
||||
.bubbles.notice:hover {
|
||||
background-color: rgba(22, 102, 111, .8);
|
||||
}
|
||||
|
||||
.bubbles i {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bubbles em {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 11px;
|
||||
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
|
||||
color: #fff;
|
||||
background-color: #e54545;
|
||||
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-style: initial;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.bubbles em.init {
|
||||
line-height: 9px;
|
||||
}
|
||||
|
||||
.bubbles.effect {
|
||||
animation-name: popup;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help bubbles
|
||||
*/
|
||||
.bubbles.help {
|
||||
right: 24px;
|
||||
background-color: #607D8B;
|
||||
}
|
||||
|
||||
.help:hover {
|
||||
background-color: rgba(96, 125, 139, .8);
|
||||
}
|
||||
|
||||
@keyframes popup {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(20px);
|
||||
transform: translateY(20px)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popdown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes popclose {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0px)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(20px)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guide
|
||||
*/
|
||||
.guide-bg {
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 90px;
|
||||
|
||||
animation-name: popup;
|
||||
animation-duration: 1s;
|
||||
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
.guide {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-content: center;
|
||||
|
||||
width: 350px;
|
||||
height: 400px;
|
||||
|
||||
background-color: #F5F6F6;
|
||||
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .12), 0 2px 2px rgba(0, 0, 0, .26);
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.guide .title,
|
||||
.guide .subtitle {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
|
||||
color: #fff;
|
||||
background-color: #26d07c;
|
||||
|
||||
font-size: 17px;
|
||||
text-align: center;
|
||||
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
|
||||
.guide .title {
|
||||
position: absolute;
|
||||
|
||||
left: 0;
|
||||
right: 15px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.guide .title span {
|
||||
animation: .1s reverse fadein,235ms cubic-bezier(.4,0,.2,1) popdown;
|
||||
}
|
||||
|
||||
.guide .subtitle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
|
||||
margin-top: 48px;
|
||||
|
||||
min-height: 55px;
|
||||
line-height: initial;
|
||||
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.guide .loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 20px;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
|
||||
transition : opacity 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
}
|
||||
|
||||
.guide .loading span {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.guide .group {
|
||||
margin-top: -30px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
guid-card {
|
||||
display: flex;
|
||||
|
||||
margin-bottom: 10px;
|
||||
padding: 20px;
|
||||
|
||||
width: 100%;
|
||||
|
||||
background-color: #ffffff;
|
||||
|
||||
border-radius: 4px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(193, 203, 212, 0.7) 0px 0px 0px 1px inset, rgb(193, 203, 212) 0px -1px 0px 0px inset;
|
||||
transition: all 550ms cubic-bezier(0.23, 1, 0.32, 1) 0s;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
guid-card:hover {
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(193, 203, 212, 0.7) 0px 0px 0px 1px inset, rgb(193, 203, 212) 0px -1px 0px 0px inset;
|
||||
transform: translate(0px, -2px);
|
||||
}
|
||||
|
||||
guid-card-tips span {
|
||||
margin-left: 5px;
|
||||
font-size: 14px;
|
||||
color: #0B242F;
|
||||
}
|
||||
|
||||
.guide hr {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
|
||||
text-align: center;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.guide hr:before {
|
||||
content: '...';
|
||||
|
||||
position: relative;
|
||||
top: -10px;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
margin-left: .6em;
|
||||
|
||||
color: rgba(0,0,0,.68);
|
||||
|
||||
font-family: medium-content-slab-serif-font,Georgia,Cambria,"Times New Roman",Times,serif;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-size: 30px;
|
||||
letter-spacing: .6em;
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
|
||||
/**
|
||||
* Option: Focus/Read mode
|
||||
*/
|
||||
* Setting: Focus/Read setting dailog
|
||||
*/
|
||||
|
||||
sr-opt-focus,
|
||||
sr-opt-read {
|
||||
display: -webkit-flex;
|
||||
flex-direction:column;
|
||||
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
}
|
||||
|
||||
sr-opt-gp {
|
||||
@ -81,73 +81,3 @@ sr-opt-theme:not(:first-child) {
|
||||
sr-opt-theme[sr-type="active"] {
|
||||
box-shadow: 0 3px 3px 0 rgba(0,0,0,0.14),0 2px 20px 0 rgba(0,0,0,0.12),0 3px 1px -2px rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
/*
|
||||
opacity {
|
||||
margin: 8px 0 0 0;
|
||||
|
||||
width: 100%;
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
opacity input[type=range] {
|
||||
width: 100%;
|
||||
|
||||
margin: 6px 0;
|
||||
padding: 0;
|
||||
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
opacity input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
opacity input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
background: #9e9e9e;
|
||||
|
||||
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.5), 0px 0px 0px rgba(13, 13, 13, 0.5);
|
||||
border-radius: 1.3px;
|
||||
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
opacity input[type=range]::-webkit-slider-runnable-track:hover {
|
||||
background: #00897B;
|
||||
}
|
||||
|
||||
opacity input[type=range]::-webkit-slider-thumb {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
margin-top: -9px;
|
||||
|
||||
background: rgba(0, 137, 123, 1);
|
||||
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0);
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
border-radius: 50px;
|
||||
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
transition: all .5s ease-in-out .1s;
|
||||
}
|
||||
|
||||
opacity input[type=range]:focus::-webkit-slider-thumb {
|
||||
background: #00897B;
|
||||
}
|
||||
|
||||
opacity input[type=range]:focus::-webkit-slider-thumb:hover {
|
||||
background: #00695C;
|
||||
}
|
||||
|
||||
opacity input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: #00897B;
|
||||
}
|
||||
*/
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Golbal style
|
||||
* SimpRead
|
||||
*/
|
||||
|
||||
.simpread-font {
|
||||
@ -189,7 +189,8 @@ sr-rd-footer-copywrite .sr-icon:hover {
|
||||
}
|
||||
|
||||
sr-rd-footer-copywrite a,
|
||||
sr-rd-footer-copywrite a:link {
|
||||
sr-rd-footer-copywrite a:link,
|
||||
sr-rd-footer-copywrite a:visited {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
@ -262,11 +263,59 @@ sr-rd-crlbar.controlbar:hover {
|
||||
* Highlight
|
||||
*/
|
||||
.simpread-highlight-selector {
|
||||
background-color: #fafafa;
|
||||
outline: 3px dashed #1976d2;
|
||||
opacity: .8;
|
||||
background-color: #fafafa !important;
|
||||
outline: 3px dashed #1976d2 !important;
|
||||
opacity: .8 !important;
|
||||
cursor: pointer !important;
|
||||
transition: opacity .5s ease !important;
|
||||
}
|
||||
|
||||
.simpread-highlight-controlbar {
|
||||
position: relative !important;
|
||||
background-color: #fafafa !important;
|
||||
outline: 3px dashed #1976d2 !important;
|
||||
opacity: .8 !important;
|
||||
transition: opacity .5s ease !important;
|
||||
}
|
||||
|
||||
|
||||
simpread-highlight {
|
||||
position: fixed;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
padding: 15px;
|
||||
|
||||
height: 50px;
|
||||
|
||||
background-color: rgba(50, 50, 50, .9);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .26);
|
||||
|
||||
z-index: 2147483640;
|
||||
}
|
||||
|
||||
sr-highlight-ctl {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
margin: 0 5px;
|
||||
|
||||
width: 50px;
|
||||
height: 20px;
|
||||
|
||||
color: #fff;
|
||||
background-color: #1976d2;
|
||||
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0,0,0,.12);
|
||||
cursor: pointer;
|
||||
transition: opacity .5s ease;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,8 +325,8 @@ toc-bg {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 200px;
|
||||
height: 500px;
|
||||
width: 50px;
|
||||
height: 200px;
|
||||
|
||||
font-size: initial;
|
||||
}
|
||||
@ -296,6 +345,10 @@ toc-bg:hover {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.toc-bg-hidden:hover toc {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
toc * {
|
||||
all: unset;
|
||||
}
|
||||
@ -311,15 +364,31 @@ toc {
|
||||
|
||||
padding: 10px;
|
||||
|
||||
width: 0;
|
||||
max-width: 200px;
|
||||
max-height: 500px;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(158, 158, 158, 0.22);
|
||||
transition: width 1.5s;
|
||||
transition: width .5s;
|
||||
}
|
||||
|
||||
toc:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
toc::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
toc::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background-color: rgba(139,137,134,0.5);
|
||||
}
|
||||
|
||||
|
||||
toc outline {
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
@ -328,7 +397,7 @@ toc outline {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
margin: 2px 0;
|
||||
padding: 2px 0;
|
||||
min-height: 21px;
|
||||
|
||||
line-height: 21px;
|
||||
@ -340,14 +409,21 @@ toc outline a:active,
|
||||
toc outline a:visited,
|
||||
toc outline a:focus
|
||||
{
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
|
||||
color: inherit;
|
||||
font-size: 11px;
|
||||
text-decoration: none!important;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
toc outline a:hover {
|
||||
font-weight: bold!important;
|
||||
text-decoration: underline!important;
|
||||
}
|
||||
|
||||
toc outline a.toc-outline-theme-dark,
|
||||
@ -355,8 +431,11 @@ toc outline a.toc-outline-theme-night {
|
||||
color: #fff!important;
|
||||
}
|
||||
|
||||
.toc-level-h1 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
.toc-level-h2 {
|
||||
padding-left: 10px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.toc-level-h3 {
|
||||
padding-left: 25px;
|
||||
@ -366,17 +445,20 @@ toc outline a.toc-outline-theme-night {
|
||||
}
|
||||
|
||||
.toc-outline-active {
|
||||
background-color: rgb(244, 67, 54);
|
||||
border-left: 2px solid rgb(244, 67, 54);
|
||||
}
|
||||
|
||||
toc outline active {
|
||||
position: absolute;
|
||||
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
padding: 0 0 0 3px;
|
||||
|
||||
border-left: 2px solid #e8e8e8;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -415,7 +497,7 @@ kbd-mapping {
|
||||
flex-flow: row;
|
||||
|
||||
width: 500px;
|
||||
height: 550px;
|
||||
height: 590px;
|
||||
|
||||
background-color: #fff;
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* sr-rd-title, sr-rd-desc, sr-rd-content, p, div, a, img, pre, code, sr-blockquote, ul ol
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root {
|
||||
font-size: 62.5%!important;
|
||||
}
|
||||
|
||||
@ -264,12 +264,11 @@ sr-rd-mult {
|
||||
padding: 16px 0 24px;
|
||||
|
||||
width: 100%;
|
||||
min-width: 840.72px;
|
||||
|
||||
background-color: #fafafa;
|
||||
background-color: #fff;
|
||||
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.16);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(60,64,67,0.3), 0px 2px 6px 2px rgba(60,64,67,0.15);
|
||||
}
|
||||
|
||||
sr-rd-mult:hover {
|
||||
|
||||
@ -23,7 +23,7 @@ sr-rd-content sr-blockquote {
|
||||
* Custom style, include: background; title; desc; sr-rd-content; p,div; a; pre
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root, .simpread-multi-root {
|
||||
color: rgb(235, 235, 235);
|
||||
background: rgb(34, 34, 34);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ sr-blockquote >:last-child {
|
||||
* Custom style, include: background; title; desc; sr-rd-content; p,div; a; pre
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root, .simpread-multi-root {
|
||||
background-color: rgb(252, 245, 237);
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
@ -89,4 +89,12 @@ sr-rd-content li code,
|
||||
sr-rd-content p code {
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple page
|
||||
*/
|
||||
|
||||
.simpread-multi-root {
|
||||
background: #F8F9FA;
|
||||
}
|
||||
@ -26,7 +26,7 @@ sr-rd-content sr-blockquote p {
|
||||
* Custom style, include: background; title; desc; sr-rd-content; p,div; a; pre
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root, .simpread-multi-root {
|
||||
background: rgb(252, 252, 252);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@ -84,4 +84,12 @@ sr-rd-content li code,
|
||||
sr-rd-content p code {
|
||||
color: #949415;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple page
|
||||
*/
|
||||
|
||||
.simpread-multi-root {
|
||||
background: #F8F9FA;
|
||||
}
|
||||
@ -28,7 +28,7 @@ sr-rd-content sr-blockquote:before {
|
||||
* Custom style, include: background; title; desc; sr-rd-content; p,div; a; pre
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root, .simpread-multi-root {
|
||||
background-color: #f3f2ee;
|
||||
color: #2C3E50;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ sr-rd-content sr-blockquote {
|
||||
* Custom style, include: background; title; desc; sr-rd-content; p,div; a; pre
|
||||
*/
|
||||
|
||||
.simpread-theme-root {
|
||||
.simpread-theme-root, .simpread-multi-root {
|
||||
background: rgb(54, 59, 64);
|
||||
color: #b8bfc6;
|
||||
}
|
||||
|
||||
@ -92,4 +92,12 @@ sr-rd-content li code,
|
||||
sr-rd-content p code {
|
||||
color: rgb(122, 122, 122);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple page
|
||||
*/
|
||||
|
||||
.simpread-multi-root {
|
||||
background: #F8F9FA;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 531 B |
BIN
src/assets/images/jianguo_icon.png
Normal file
|
After Width: | Height: | Size: 497 B |
BIN
src/assets/images/plugin_icon.png
Normal file
|
After Width: | Height: | Size: 387 B |
BIN
src/assets/images/read_icon.png
Normal file
|
After Width: | Height: | Size: 596 B |
BIN
src/assets/images/webdav_icon.png
Normal file
|
After Width: | Height: | Size: 367 B |
BIN
src/assets/images/yuque_icon.png
Normal file
|
After Width: | Height: | Size: 534 B |
@ -7,6 +7,7 @@ import {browser} from 'browser';
|
||||
import * as ver from 'version';
|
||||
import * as menu from 'menu';
|
||||
import * as watch from 'watch';
|
||||
import * as WebDAV from 'webdav';
|
||||
|
||||
import PureRead from 'puread';
|
||||
|
||||
@ -47,6 +48,7 @@ storage.Read( () => {
|
||||
}, ver.FixSubver( ver.patch, storage.simpread ));
|
||||
}
|
||||
menu.CreateAll();
|
||||
setTimeout( ()=>uninstall(), 100 );
|
||||
});
|
||||
|
||||
/**
|
||||
@ -83,18 +85,63 @@ menu.OnClicked( ( info, tab ) => {
|
||||
* Listen runtime message, include: `corb`
|
||||
*/
|
||||
browser.runtime.onMessage.addListener( function( request, sender, sendResponse ) {
|
||||
if ( request.type == msg.MESSAGE_ACTION.CORB ) {
|
||||
$.ajax( request.value.settings )
|
||||
.done( result => {
|
||||
sendResponse({ done: result });
|
||||
})
|
||||
.fail( ( jqXHR, textStatus, errorThrown ) => {
|
||||
sendResponse({ fail: { jqXHR, textStatus, errorThrown }});
|
||||
});
|
||||
}
|
||||
return true;
|
||||
if ( request.type == msg.MESSAGE_ACTION.CORB ) {
|
||||
$.ajax( request.value.settings )
|
||||
.done( result => {
|
||||
sendResponse({ done: result });
|
||||
})
|
||||
.fail( ( jqXHR, textStatus, errorThrown ) => {
|
||||
sendResponse({ fail: { jqXHR, textStatus, errorThrown }});
|
||||
});
|
||||
}
|
||||
);
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Listen runtime message, include: `jianguo`
|
||||
*/
|
||||
browser.runtime.onMessage.addListener( function( request, sender, sendResponse ) {
|
||||
if ( request.type == msg.MESSAGE_ACTION.jianguo ) {
|
||||
const { url, user, password, method } = request.value;
|
||||
const dav = new WebDAV.Fs( url, user, password );
|
||||
if ( method.type == "folder" ) {
|
||||
dav.dir( method.root ).mkdir( result => {
|
||||
dav.dir( method.root + "/" + method.folder ).mkdir( result => {
|
||||
sendResponse({ done: result, status: result.status });
|
||||
});
|
||||
})
|
||||
} else if ( method.type == "file" ) {
|
||||
dav.file( method.path ).write( method.content, result => {
|
||||
sendResponse({ done: result, status: result.status });
|
||||
});
|
||||
} else if ( method.type == "read" ) {
|
||||
dav.file( method.path ).read( result => {
|
||||
sendResponse({ done: result.response, status: result.status });
|
||||
});
|
||||
}
|
||||
}
|
||||
//return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Listen runtime message, include: `webdav`
|
||||
*/
|
||||
browser.runtime.onMessage.addListener( function( request, sender, sendResponse ) {
|
||||
if ( request.type == msg.MESSAGE_ACTION.WebDAV ) {
|
||||
const { url, user, password, method } = request.value;
|
||||
const dav = new WebDAV.Fs( url, user, password );
|
||||
if ( method.type == "folder" ) {
|
||||
dav.dir( method.root ).mkdir( result => {
|
||||
sendResponse({ done: result, status: result.status });
|
||||
})
|
||||
} else if ( method.type == "file" ) {
|
||||
dav.file( method.root + "/" + method.name ).write( method.content, result => {
|
||||
sendResponse({ done: result, status: result.status });
|
||||
});
|
||||
}
|
||||
}
|
||||
//return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Listen runtime message, include: `shortcuts` `browser_action`
|
||||
@ -307,4 +354,11 @@ function analytics() {
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-405976-12', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall
|
||||
*/
|
||||
function uninstall() {
|
||||
browser.runtime.setUninstallURL( storage.option.uninstall ? storage.service + "/uninstall" : "" );
|
||||
}
|
||||
@ -1,26 +1,27 @@
|
||||
console.log( "=== simpread contentscripts load ===" )
|
||||
|
||||
import './assets/css/simpread.css';
|
||||
import './assets/css/option.css';
|
||||
import './assets/css/setting.css';
|
||||
import 'notify_css';
|
||||
import 'mintooltip';
|
||||
|
||||
import Velocity from 'velocity';
|
||||
import Notify from 'notify';
|
||||
import Velocity from 'velocity';
|
||||
import Notify from 'notify';
|
||||
|
||||
import {focus} from 'focus';
|
||||
import * as read from 'read';
|
||||
import * as modals from 'modals';
|
||||
import * as kbd from 'keyboard';
|
||||
import {focus} from 'focus';
|
||||
import * as read from 'read';
|
||||
import * as setting from 'setting';
|
||||
import * as kbd from 'keyboard';
|
||||
import * as highlight from 'highlight';
|
||||
|
||||
import * as util from 'util';
|
||||
import * as util from 'util';
|
||||
import { storage, STORAGE_MODE as mode } from 'storage';
|
||||
import * as msg from 'message';
|
||||
import {browser} from 'browser';
|
||||
import * as watch from 'watch';
|
||||
import * as msg from 'message';
|
||||
import {browser} from 'browser';
|
||||
import * as watch from 'watch';
|
||||
|
||||
import PureRead from 'puread';
|
||||
import * as puplugin from 'puplugin';
|
||||
import PureRead from 'puread';
|
||||
import * as puplugin from 'puplugin';
|
||||
|
||||
let pr, // pure read object
|
||||
is_blacklist = false,
|
||||
@ -45,7 +46,7 @@ storage.Read( () => {
|
||||
});
|
||||
} else {
|
||||
bindShortcuts();
|
||||
autoOpen();
|
||||
preload() && autoOpen();
|
||||
}
|
||||
});
|
||||
|
||||
@ -72,6 +73,34 @@ function blacklist() {
|
||||
return is_blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload verify
|
||||
*
|
||||
* @return {boolen}
|
||||
*/
|
||||
|
||||
function preload() {
|
||||
let is_proload = true;
|
||||
if ( storage.option.preload == false ) {
|
||||
is_proload = false;
|
||||
} else if ( storage.option.preload ) {
|
||||
for ( const item of storage.option.lazyload ) {
|
||||
if ( item.trim() != "" && !item.startsWith( "http" ) ) {
|
||||
if ( location.hostname.includes( item ) ) {
|
||||
is_proload = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ( location.href == item ) {
|
||||
is_proload = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return is_proload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen runtime message, include: `focus` `read` `shortcuts` `tab_selected`
|
||||
*/
|
||||
@ -87,7 +116,9 @@ browser.runtime.onMessage.addListener( function( request, sender, sendResponse )
|
||||
bindShortcuts();
|
||||
break;
|
||||
case msg.MESSAGE_ACTION.tab_selected:
|
||||
browserAction( request.value.is_update );
|
||||
if ( preload() == false ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.browser_action, { code: 0 , url: window.location.href } ));
|
||||
} else browserAction( request.value.is_update );
|
||||
break;
|
||||
case msg.MESSAGE_ACTION.read_mode:
|
||||
case msg.MESSAGE_ACTION.browser_click:
|
||||
@ -97,19 +128,18 @@ browser.runtime.onMessage.addListener( function( request, sender, sendResponse )
|
||||
new Notify().Render( "配置文件已更新,刷新当前页面后才能生效。", "刷新", ()=>window.location.reload() );
|
||||
} else {
|
||||
if ( storage.option.br_exit ) {
|
||||
modals.Exist() && modals.Exit();
|
||||
!modals.Exist() && read.Exist( false ) ? read.Exit() : readMode();
|
||||
setting.Exist() && setting.Exit();
|
||||
!setting.Exist() && read.Exist( false ) ? read.Exit() : readMode();
|
||||
}
|
||||
else readMode();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case msg.MESSAGE_ACTION.pending_site:
|
||||
new Notify().Render({ content: "是否提交,以便更好的适配此页面?", action: "是的", cancel: "取消", callback: type => {
|
||||
new Notify().Render({ content: "是否提交,以便更好地适配此页面?", action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.save_site, { url: location.href, site: storage.pr.current.site, uid: storage.user.uid, type: "failed" }));
|
||||
}});
|
||||
localStorage.removeItem( "sr-update-site" );
|
||||
break;
|
||||
case msg.MESSAGE_ACTION.menu_whitelist:
|
||||
case msg.MESSAGE_ACTION.menu_exclusion:
|
||||
@ -145,9 +175,9 @@ function bindShortcuts() {
|
||||
kbd.Bind( [ storage.read.shortcuts.toLowerCase() ], readMode );
|
||||
kbd.ListenESC( combo => {
|
||||
if ( combo == "esc" && storage.option.esc ) {
|
||||
modals.Exist() && modals.Exit();
|
||||
!modals.Exist() && focus.Exist() && focus.Exit();
|
||||
!modals.Exist() && read.Exist() && read.Exit();
|
||||
setting.Exist() && setting.Exit();
|
||||
!setting.Exist() && focus.Exist() && focus.Exit();
|
||||
!setting.Exist() && read.Exist() && read.Exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -203,10 +233,16 @@ function readMode() {
|
||||
} else if ( pr.state == "temp" && pr.dom ) {
|
||||
read.Render();
|
||||
} else {
|
||||
new Notify().Render( "当前并未适配阅读模式,请移动鼠标手动生成 <a href='http://ksria.com/simpread/docs/#/%E4%B8%B4%E6%97%B6%E9%98%85%E8%AF%BB%E6%A8%A1%E5%BC%8F' target='_blank' >临时阅读模式</a>。" );
|
||||
new Notify().Render( "<a href='http://ksria.com/simpread/docs/#/词法分析引擎?id=智能感知' target='_blank' >智能感知</a> 正文失败,请移动鼠标,并通过 <a href='http://ksria.com/simpread/docs/#/手动框选' target='_blank' >手动框选</a> 的方式生成正文。" );
|
||||
read.Highlight().done( dom => {
|
||||
pr.TempMode( mode.read, dom );
|
||||
read.Render();
|
||||
const rerender = element => {
|
||||
pr.TempMode( mode.read, dom );
|
||||
read.Render();
|
||||
};
|
||||
storage.current.highlight ?
|
||||
highlight.Control( dom ).done( newDom => {
|
||||
rerender( newDom );
|
||||
}) : rerender( dom );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
console.log( "=== simpread focus controlbar load ===" )
|
||||
|
||||
import * as modals from 'modals';
|
||||
import * as se from 'siteeditor';
|
||||
import * as conf from 'config';
|
||||
import { storage } from 'storage';
|
||||
import * as output from 'output';
|
||||
import * as watch from 'watch';
|
||||
import {browser,br}from 'browser';
|
||||
import * as msg from 'message';
|
||||
import * as setting from 'setting';
|
||||
import * as se from 'siteeditor';
|
||||
|
||||
import * as conf from 'config';
|
||||
import { storage } from 'storage';
|
||||
import * as output from 'output';
|
||||
import * as watch from 'watch';
|
||||
import {browser,br} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import * as highlight from 'highlight';
|
||||
|
||||
import Fab from 'fab';
|
||||
|
||||
let timer, $root, selector, callback;
|
||||
let focusItems, $root, selector, callback;
|
||||
|
||||
const tooltip_options = {
|
||||
target : "name",
|
||||
@ -44,7 +45,7 @@ class FControl extends React.Component {
|
||||
$( "html, body" ).animate({ scrollTop: 0 }, "normal" );
|
||||
break;
|
||||
case "setting":
|
||||
modals.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
setting.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
break;
|
||||
case "siteeditor":
|
||||
se.Render();
|
||||
@ -80,8 +81,9 @@ class FControl extends React.Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
focusItems = $.extend( true, {}, conf.focusItems );
|
||||
if ( storage.current.site.name.startsWith( "metaread::" ) || storage.current.site.name.startsWith( "txtread::" ) ) {
|
||||
delete conf.focusItems.option;
|
||||
delete focusItems.option;
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +95,7 @@ class FControl extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<sr-rd-crlbar class={ this.props.show ? "" : "controlbar" } style={{ "zIndex": 2147483647 }}>
|
||||
<Fab ref="target" tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" items={ conf.focusItems } onAction={ (event, type)=>this.onAction(event, type ) } />
|
||||
<Fab ref="target" tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" items={ focusItems } onAction={ (event, type)=>this.onAction(event, type ) } />
|
||||
</sr-rd-crlbar>
|
||||
)
|
||||
}
|
||||
|
||||
64
src/help_tips.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"tips": [
|
||||
{
|
||||
"idx": 0,
|
||||
"name": "文档中心",
|
||||
"icon": "<i class=\"fas fa-info-circle\"></i>",
|
||||
"url": "http://ksria.com/simpread/docs"
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"name": "新手入门可以看这篇文章",
|
||||
"icon": "<i class=\"fas fa-file-word\"></i>",
|
||||
"url": "http://ksria.com/simpread/guide/"
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"name": "查看当前版本新增功能",
|
||||
"icon": "<i class=\"fas fa-plus-square\"></i>",
|
||||
"url": "#"
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"name": "查看当前页的功能描述",
|
||||
"icon": "<i class=\"fas fa-binoculars\"></i>",
|
||||
"url": "#"
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"name": "简悦的汇总(新闻页)",
|
||||
"icon": "<i class=\"fas fa-bullhorn\"></i>",
|
||||
"url": "https://simp.red/news"
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"name": "请通过 Github issues 提问",
|
||||
"icon": "<i class=\"fas fa-bug\"></i>",
|
||||
"url": "https://github.com/Kenshin/simpread/issues/new"
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"name": "如果简悦暂时未支持你使用的导出服务",
|
||||
"icon": "<i class=\"fas fa-file-export\"></i>",
|
||||
"url": "https://github.com/Kenshin/simpread/issues/473"
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"name": "YouTube 加载出错的朋友请看这里",
|
||||
"icon": "<i class=\"fas fa-bug\"></i>",
|
||||
"url": "https://github.com/Kenshin/simpread/issues/487"
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"name": "当使用简悦疑似页面变慢的处理方式",
|
||||
"icon": "<i class=\"fas fa-desktop\"></i>",
|
||||
"url": "@performance"
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"name": "当阅读模式并未符合你的期望时,如何进一步优化?",
|
||||
"icon": "<i class=\"fas fa-book-open\"></i>",
|
||||
"url": "https://github.com/Kenshin/simpread/issues/522"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name" : "__MSG_extension_name__",
|
||||
"default_locale" : "en",
|
||||
"version" : "1.1.2.5005",
|
||||
"version" : "1.1.3",
|
||||
"short_name" : "SimpRead",
|
||||
"description" : "__MSG_extension_desc__",
|
||||
"homepage_url" : "http://ksria.com/simpread",
|
||||
@ -44,6 +44,6 @@
|
||||
],
|
||||
"offline_enabled" : true,
|
||||
"update_url" : "https://clients2.google.com/service/update2/crx",
|
||||
"content_security_policy" : "script-src 'self' https://www.google-analytics.com; object-src 'self'",
|
||||
"content_security_policy" : "script-src 'self' 'unsafe-eval' https://www.google-analytics.com; object-src 'self'",
|
||||
"manifest_version": 2
|
||||
}
|
||||
@ -58,7 +58,7 @@ urls = {
|
||||
},
|
||||
|
||||
badges: {
|
||||
version: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAUCAMAAAA6JNosAAACiFBMVEUrqgBVVVU/uBJOxyE9txBNTU1PT09PySJfX19RyiNfX18/uhJNyCBdXV1PT089tw9NTU0ugw4uhA4wiA4wiA8wiQ8wig8xjQ8yjw83jxY3nBE5OTk5kBk7mRc8PDw8mRg8qhI9PT09tw8+Pj4+phg+shM+uBA/Pz8/uRFAuhJBQUFBrxdBuxNCQkJCvBRDtBhDvRVDvRZEREREvhZFRUVFuxlFvhdFvxdGvRlGwBhHR0dHwBpHwRlIwRpIwRtIwhpJSUlJwxtKSkpKxBxLS0tLxR1MTExMxh5NTU1NnS9Nxx9OTk5OyCBPT09PySFQUFBQyiJRUVFSUlJTU1NUVFRVVVVWVlZWxCxXV1dXxS5XyCxYWFhZWVlaWlpbW1tcXFxdXV1dyjReXl5fX19iYmJjY2NkZGRlZWVlyT9mZmZnZ2dpwkhqw0hruU5rxElsbGxsxEpsxUttxkxubm5vb29ycnJy0U51dXV30FV4vl55eXl5umB9fX19zl5+fn5/f3+BgYGCgoKDg4OEhISFhYWGhoaIiIiJiYmKioqLi4uOjo6VxoKYmJiampqcnJydnZ2jo6OlpaWpqamr1Jut5Jmvr6+v45uw5J2x5J2y5J+zs7O1tbW2tra5ubm9vb2/3rTC4bfExMTE6rXFxcXGxsbG7LfIyMjJycnJ7bvKysrLy8vL6cDMzMzQ0NDR0dHU1NTV1dXW1tbX6s/Z8s/a2trc3Nzd3d3e3t7e8dff39/g4ODh4eHi4uLj4+Pj797j79/j8N/l5eXm5ubn5+fo6Ojo9+Po+OPq6urr+Obs+eft7e3t9uru7u7v7+/v+Ozw8PDx8fHy8vL39/f6+vr7+/v9/vz+/v7///8QYwojAAAAEXRSTlMGBo2Njo6Ojo7j4+jo6Ons7CIlfBEAAAABYktHRNeQslo/AAACPUlEQVQ4y5XK91fTUBTA8YcT96wLRSvOqLUqDhRFRBGttea9JD5t02JxgXvgXjhAcW/FBeJe1IFIsRUnxVkbNf+OyUs8p6dVT97nh5dv7r0gIbF1HqV5BrRqCUCil9pcQ5qCdrnU5hjSFnjozTYGuOnNNAaI9GYYA1z/J3njZ1nEmQ/ypthaeLOx8c4irbOA8x/c2qfgL6tMYtWsV+tja/723j1PX9c6E2Di1CWMl3734h2Pa6qLMJbP3jpxo672HsaSBxfX1lVtVGYnK54fIscZxMix9etia8KwSZO3PtE6AwhE3meXcPwyv9jPMbjBLcglo/dUMoxdECQxP5zLlD4TBLkoVWwgx+m6+rXxlT7l/mG9AE/AByV8dSFf/MXnqwqs4WXEL3l3pVTkecl18DbL5/zw8jLLw4hTPU7TBVfHV9rFawP0ApzmQGXhazu37xHDMCkOTkYcO3FX2Vs3Jzn33+U4QfKoMy7iVG/H64Ir4+t8ed9xegKkYb9dPYaQK7wXoW0IyQgtE6dP/ZiPJOwJb0BHfFCdoQhWb8fogiuUZ8uC6LpQ3mfUnzWAGrbslwdCR4Ev8KYCQhnC3X7/y6N2KAmOzTWBhyKZwYig3lqJc6GfX99brS92RtVy+VMo9FTbWwGrG5FkU97slORkhmWTWHaa2WxOVcqhVPJgG5mpfwoL0cvUuUs3i6V7v6ga2KmrydRD21uAg95wY4Cd3lBjQHsbtSGGtAEtcqgNMqQJSGjeMZtSfwM6NAO/AS+YQbktyUjWAAAAAElFTkSuQmCC",
|
||||
version: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAUEAYAAABdavZdAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAABaAAAAWgBwI7h9AAAAB3RJTUUH4wYaASkrBdiNSwAACFdJREFUaN7tWmlMW1cW/p5XwGYz2GxhqaGmFaqK3VKGEorEDD8iBgmQWkpale1HhVhqFIrcEdGECmirIkBB0B+0wkGI8YwERaMWj5BSKmAYiUUomlC2ghqKVDtlqYEAXt/8YK5uxoQkJklNOv4kfPXe8/vuued+95xzL2YuxF2IuxAnFEqvS69Lr3/8MRPFRDFRBQUoQhGKQkLggVtgMpWXj42524qzC/ZP4OJ3BgNrwWX8VaezFm20rdo1Gh4RMi7jMi6r1SxYsO621gOwLMuynok4GY2w4V+hoQzwZ0Sp1XxtsFq+BvBYI2tkjQUF8Cj5TMEjaBeRhn86Fi9e5LFX2avsVU9pcdbgEbSL+CP7D/xeJuN5HHc24ZmX08Ej6DMKz7ycDhziuP+3VqvVarVagMfj8Xg899vzqG1JSXR0XR3wxRcvvTQxAXz11auvrq0B8fFisVLpfj7SXrny/PPXrgHXrr388uQkbT/99IUXBgYAudzHJyHh9PxPfYTmcrlcLhew2+12u/3h36usrKysrAQsFovFYnH3KI7D1Xn55pu5ua+/BrTa9fXOTqCzMy9vcBCwWu/ccTjcz0fQ1KTXX7oErK8bjT/+SO/n5alU77wDqNUqVUsLUFFx40Zmpuv8Lgs6Ly8vLy8PEIvFYrEY6Onp6enpoc99fX19fX2BlpaWliPDKioqKgCz2Ww2mwGFQqFQKICCgoKCggLAy8vLy8sLcDgcDocD6Ovr6+vrA+bm5ubm5iivTqfT6XTA4ODg4OAgEBYWFhYWBiwvLy8vLwOxsbGxsbFAREREREQE5dvY2NjY2ACam5ubm5spX3t7e3t7O1BSUlJSUgJYrVar1QoolUqlUgnk5+fn5+cDHA6Hw+EAu7u7u7u7QFdXV1dXF2AwGAwGw3H7iP3R0dHR0dHUzoGBgYGBAWB6enp6evrBfnZ1XhYWrNbJSYDDiYpiGAA4+nSOYO7iI9jbk8kMBiAoKDJSIKD3hUKRiMcD7Haz+VEWjMuCHhoaGhoaAlpbW1tbW4He3t7e3l4aIVNSUlJSUoDx8fHx8XHg4ODg4OCACr20tLS0tBSoqampqakBNjc3Nzc3AZlMJpPJqNCqq6urq6up0AiWlpaWlpaAjo6Ojo4OIC0tLS0tDZDL5XK5HCgqKioqKqLf9/Pz8/PzA4RCoVAoPD4eInx/f39/f3+grKysrKyMLsT19fX19XUgOzs7OzsbKC8vLy8vB+rq6urq6o7zraysrKys0JJGKpVKpVKgra2tra0NmJqampqaerCfXZ0XHs/X90hy92JyPx8Bh3MkY43muec++wxQKgMCzp8HLBabzWwGqqq+/PLNNwGWPXfuVxE0iVSrq6urq6tAYmJiYmIijTzp6enp6elAZ2dnZ2cnfY9EZiKc+vr6+vr6/3UTy9JIHhgYGBgYCBiNRqPRSL83OTk5OTlJn6+tra2trdEIrdFoNBoNjfDELueF4dxvXFxcXFwcsLCwsLCwAOzt7e3t7QHBwcHBwcHAyMjIyMgILVVIZiELlmB2dnZ2dhYICAgICAgADg8PDw8P6YImEf9BJdKjl4JEeOTvbPFpNIODJSWAzWYysSzw+utKZWkpUF2dmXnlClBX9913Fy+6zuuyoMmEDA8PDw8PUwET4Xl7e3t7e9NIxefz+Xw+YLPZbDYbFaBarVar1cf5yURLJBKJREI3bfdyL8sCW1tbW1tbQGFhYWFhIZCcnJycnAwkJSUlJSXR0oZkBLJgnHmI4BmGYRiG1ubkORkXuSaR3dl/xD/kPrk+yf77yedx7G0etUR4Uny+vs8+e/e86vW3b/f2Au+/L5V+8gng58fjBQYCJpPVurX18LynPuUgqTMhISEhIQHIysrKysoC9Hq9Xq8/fnpAImZMTExMTAyQkZGRkZFBhUva1NTU1NTU44I6ybEkggYFBQUFBQEzMzMzMzM0QwgEAoFAAIhEIpFIdJyPXM/Pz8/PzwPx8fHx8fFAeHh4eHg4fU7sJTX7/v7+/v7+g+07qb8n3d7d3/36VakCA197DZBIBIKQkCfHJxJxuX5+QHS0j49CcTfv0WdOzosvvv028PPPu7sGA7C9vb+/uemGU46xsbGxsTEq6O7u7u7u7uOOIKm5tra2trYWqKqqqqqqojUvETApZRYXFxcXF08WCgERXnFxcXFx8V0r9b+Rsb+/v7+/n2YQ0o8zHykxGhoaGhoaaAYhGYbU+o2NjY2NjSfb87CR93FH6IoKhaKpCTh/XirNyjoSlEwGNDW98kpvL2A2s+zBAfDWWxMTKhV97913n3nmww+B7u6VlY8+AiYmDg/1+sfPx+c7HEIhcOmSQtHWBshkPj7nzgEOB8va7cCtW7/88v33wHvvDQy88QYAREYSP7miRyY3Nzc3N/f0kjaZTCaTiQqWbIKchUNAUvvOzs7Ozg5N3QREQKQGJSCnCqGhoaGhofQ+KSFIbe8MUuuSUxlnvpCQkJCQEFpqOPMRYZHxkD2A8/hOsu9B/Z3sV9d+bXfnzg8/2O2A2by56XAADofVelTykMTO4TAMIJGoVHw+fW97+8YNqxUQiSIjuVxAIJBIOJzHz+dwWCwsC+zsLC7abIDDQQq/Iy9wud7eDAOIRDExXC7A4/n43M8/J4HJycnJycl5mk+if5vw/Hz0dHjq/7HyW4VnXk4HHlvIFrKFBgO00EJ7r2TpgTvgEbSLUDF/wPWffuJBBhlkOt2RA+91kOaBO+ARtGtg/oK/c1J0Oq58VD4qH/32W2aUGWVGxWLcxE3cjInBbdzG7bu3UR78mjg8TEq6dcvdVpxdsENIR43BwGYyzcy/P/9c/DdH7vb2Bx/8B9inWYA0JLhbAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA2LTI1VDIxOjQxOjQzLTA0OjAwaNE+dAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNi0yNVQyMTo0MTo0My0wNDowMBmMhsgAAAAwdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL3RtcC9tYWdpY2stNTcwMVhvSFVsUnY5TnZKWrP6tCkAAAAASUVORK5CYII=",
|
||||
website: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAAUCAYAAAAQqNbAAAAABmJLR0QA/wD/AP+gvaeTAAAJSklEQVRo3u1beVBU6RGfuIAY43qtq9lNsjHZ2tqqZFP51xSa0opR8QZUvK94LZoNqdSKF7JABAUHFOVcUZSKgIDOoKMgl+MAIma4WRQXhAG5L0GiRveX1z2Zx+MJCLtLpTI1XfWr93X3193f16+nv+8VhWLFihU2GzduVAowCIA5IOLOZAvMC4bwnElKjziFjYKKdcOGDTAnCJuzwAwRmjXhuGL9+vUNAmBOCMueaIE5ImdCg2LdunUwNwi/RAvMFIq1a9fC3BCiG2+BmUKxZs0amBuCb79tgZlCsXr1aowEiIZr8+LFC/5oovGlS5e4+L5N7NPacUPCy1fPEKqbMuT5/2t8883LAXVE39X//0M+FM7OzhgJEA3XZseOHeL41atXfLx/m9inbv1oSPCNskNQxrghzx8ugrUTvld/r4SCHUhH9F3XOdL5+D6gWLVqFeSIiIhAcnIyjw8cOMDJ2L9/P/MpKSkIDw/n8aFDh1BWVobKyko8fPgQnp6eog+i+Ph41tXW1sLf35/l9BWfnZ2N6upqVFVVIS8vT7ShDksfTVeuXGF78l1aWso2g8WSIyhzrIgQ7TuoaEpAS3cxmroKUNmcJOqoowRrJ/OYKLvSned19FQiIX8u8g0nBb6UEX33t6Idkb4mEOUN/8DjjhxcK3Huo8urPoaHTVeQ+cCVZfH6Oajr0Anx89H45B4uF9iL8+83xgoyPcf9ulmNcN37ok5TshptT++z3Z0qby5Y6d6kIKLn6VvjUVp/TkAUwm5PHXDv/a1Tmo/B1iVHQv4fUd+Zy3Obu4qQWDCP5eoiB4EvFOQlMLRn4nzuJ33iDzXfUihWrlwJObZt2waDwcDj6OhoFBcX48KFC8xT8ZF+y5YtePToEZycnDB79mwulKamJu6KNI8oLCyMdXS0d3R0sJ2vry+ysrJYTli6dKkYlwqW5s6fP5877Ny5czFnzhxs3rx50FhynMz4oQh1oRNKqtT4c6AtPjthi4Nnpoq6f798hlOZk3hMdDlnF887c3UNXrzsRmTKYuwJGI3L2r0oqY0W7YhitRvxebgtDkS8h84eAyKyPhB1cYLub6G28Lg4hl90U2cJ3CM/4PiHIz8U5tcIhTWR53uc/wnLKa7mziHcrfJj+ZdZ0/H0WQs8znzM+mT9QS5Y6d6kIArRvotHLcm4qffG3i9toS4YeO/ydcrzMdC65IjQ/QxPnzcjIG4mz/3LibEI0Ewzrv95C7yjPmF5XIYL6jvy+sQfar6lUAh/6YIcy5cvR0NDA3bu3In8/Hy4urpCr9czT3LSU+G1t7dzBySUlJRwke/evZt9EEn9UVclm61bt7IP6uBKpZIL1DSPCpbuoDSmgqXjncZviiXHifQxIoKuf4S2J9Uoqg0XOtZaBKW9I+roBQVlTOQxkfelCUbdtY/Q3dOCvyfaMh+RPBtVDdmiHZFvotGPMmUMCh8m4YreSdT5JEwS56ryndDV0wxDmxa17VoYWrVobKvAuexfsz61bA/q2nVCB85CfVshyg0alqsLnFFUcRV+141+jsT+mAtWujcpiBo79YjP3I0DUbZv3Lt8nfJ8DLQuOVQFK1FWlQoflTFmYJoxJ6r8VSitTIGfxjjvixihg798jpBb7w4731IoqGv1B41Gg9DQUD6ClyxZwsc38SQnvbe3NwoLC8VOSZg1axaWLVvGeiI63k3+cnJy+Bin4p03bx7c3d2RlJSE+vp67pQ0hwqWipTGVLDUSYcSS47ANFsRfhpbuAZOQlSKI+5WhKC952vuIqTjF5Q+gcdEAalGm9D0X6L9SZ3oI+rWTDyqzxV5ouCMaSL/VfUNxOgcRJ3yZm/82KwVqDDcxmcnbUS4+Nvg2DVbXLz7O7Q8eYB9oVOFbmaDMJUDHhjS2S4ua5XwQ0gU/QSlTeGCle5NCqK0e0rcr70mFN34N+5dvk5pPgZblxyxWU4or0l9TR6XvVLIS4pENlbw/xyn0qcMO99SKBwdHdEffHx80NjYCJVKxbxarWb+yJEjzFMn7OzshJeXl2izb98+cUwUGRnJYxcXF+6QVJh0LaCuunDhQixYsACtra3Yvn07z6OCpSKlMfnetGnTkGLJEZA6WkTE7ek4mToZnnE22B85Dp3d9QjXfsg6ekEn08bz2JhAo01o+i/Q3lUn8uf+m0ATT5RZ7sbjs9m/Qve/muGvfv81PwR/1XuCvgWJ+YtEWUzeLH5ezF6IBzW34H/DKC+ujsH9mjQeH1f/FG1dNQjOnMq8pmgTF6zJR4LeHuG3f95nTXvP2CAl7wiqW9O4Uw62d/k6pfkYbF3y2LS/rp5GocjtmD8hFGZw5jQohfXTvqNyfsPy1DIXVNbl4njy6GHnWwqFg4MD+gN1RyLqisTTk4iKjnjqbvRVT0c0Hc+PHz+GTqcT7Yno3kudme69bm5ubHP48GG+jxJqamr4A47usWRDBUt3UBqTnOzInmSDxZJDedNGRFyuPZqeFAmX/2K0dJVCpXXH0atGHb2gE6lv89jYcYzykLTpwi++VuRNCTTx3M0KPPkDo7XrPoITHIWj2+Y1PwSS+563Q01LFlq7v0L70wqU1cazzithDO6Vx6BKuHeW18cgo0DJhUG6Yxqhs10WvhnadCh9fAEZxZ5csCa/TZ2luHzPqc+a6OkWaY2k7IPC9UMH1T+dB9y7fJ3SfAy2Lnls2p9f9O9R13ZX+Fgq41gxuX/g9QfFLRGuKYW878qGDHie/fi19Q4l31Io6IjuD3QNmDFjBndB4ulJPMlNcxYtWsRHs52dHYOOapOO5tJH08yZM1ln8mNvb88yE2iO1IaKmsYkJzuaQ7LBYslxPMVaxBeXrPGpv5URflbYd9ZK1P3Jywr+ycbxFs+3RPnRq9bY4ds7zzvRGruVvTyRW6TR5y7Bp0dMbzypnz72Aca5u45ZwfW00RfFdg22Eq4JVvz8PMIKewJ74xyOMdr9VdBRvK1evbpPB4lLeyQ7sh9o7/2t05SPN61LHtsrwRouSmMMiuUZb5R7xFqLOSJ7H3X/631TvqVQUDGYG/yTrUYURCMdw4L+oaDj2Nzgd+OtEcVmj1EjHsOC/kEF20jHvDnh2PVRFpgjkkc1UMEqFy9eDHPCUc0PLDBHXB91nP5wYCO8ZCraFvqwMQf4XlNYYE7QKFqEJ/+LzH8AJAsHnUo27mMAAAAASUVORK5CYII=",
|
||||
githubstar: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEwAAAAUCAMAAAAQlCuDAAACplBMVEVVVVXVVSvmcjP0gEFNTU1PT09fX1/kbzD2gUJfX1/3g0VdXV3nczT1gkJPT09NTU3kcDE5OTk7Ozs8PDw9PT0+Pj5BQUFCQkJDQ0NERERFRUVISEhJSUlMTExNTU1OTk5PT09QUFBRUVFSUlJTU1NUVFRVVVVWVlZXV1dYWFhZWVlaWlpbW1tcXFxdXV1eXl5fX19gYGBiYmJlZWVnZ2dra2t4eHh8fHx/f3+CgoKGhoaIiIiJiYmKioqLi4uMjIyQkJCTk5OZmZmampqenp6fn5+jUiajo6OkpKSmpqanp6eqqqqrq6usrKyuVyivWCivWCmwsLCxYDSzXCy0XC21Wyq1XCy3XCq4Xi65YTG6Zzq7Xiu9YS/BajvCwsLDYi3Dw8PEZjTEb0DExMTFxcXGxsbHZC7Hx8fIZjLKjGzPz8/QazTSqJDTqJHVbDPVk2/V1dXWazLWcjzW1tbZczzb29vcnXrcrJLc3NzdgE7d3d3e3t7f39/g4ODht6DicjXkcDHkdDfk5OTlcTLlczXlxrXl5eXmcjPmdTfml2znczTodDXoe0DpdTbpdzjpfULp6enqdjfq6urrdzjreTrrejzrhk/rmWzrzr/r1srseDnsez3sgkjs1MfteTrtfUHthU3twant1cjt7e3uejvvezzvfkDv0sLv7+/wfD3wfkDwgEPwhEnwlGHwzLjw8PDxfT7xfkDxoXXxrorx8fHyfj/y8vLzf0Dzpnz0gEH1gUL1so71v6L149n2gkP3g0T3wKL3waP37Ob37+v39/f4vZ34yK74z7n46eD4+Pj50Lr52Mf58/D60r361MD61sL61sP65tv6+vr73s/739D739H75tr759z76N376uD85dj85tr859z87OT9/f3+/fz+/f3///9Ftc+TAAAAEXRSTlMGBo2Njo6Ojo7j4+jo6Ons7CIlfBEAAAABYktHROFfCM+mAAACOUlEQVQYGZXBT0iTYQDH8e/7vs82l6ibYgYWMrE0QaE/VEKXBJMIBKVDHSLoUhSdO3axbqVEERFENwnqVpMIIoIoioIuSRCUFSFWrkzd9u7Z++udUgnhXJ+PQ9Rrp1IjrGx/kDfRzYhKiZXdoc9EROUs5awxraJyopxbRvyHgLKM+A8BZblabswohZTWcmn9UfgrYQv/MGKZoz4XBwIQy4nf8vzRPOnxlyNCrhY1HR5M3ZWumw6vvz9Cy4FjjYqfl9ybEm1Dh9ZqkQ25bdu2ttt1Tnenm9qybZNn7a6mjUlbYkSo5viZTBeC4FlxrFhH/uqGI5eDQAjB/LXG4eE5QgJiz+878f6x02e1e3xWbX1TkP5c10PIiFA8bRMfEaiauiJMJmZ2XhFCCD4lsumqn4R84Mm+7tkJfwe9HoMpZ2raB9uLT8iIUDHiqgqBhASuTAGL8BDEcp4biJAFqkY3bB14VMCPNDSesw09FtZbFhkRmut79X0dAul1/CcgUC5T+6MBQdP75J7HIpQHWqo/PGzNFr/Fsm+/dlc3Z/IQybPIiNDs6ND8y9cCaWTQv40Acan3yzQC72D0wrxLKACyfVHncn39jVPF63avHW8JIGCJ00qJZp2OjgdkEk4u79R8T0Im4fh5x8slycR9xaOUnAQ+vUNN7byZcrdPWGMWdnGvnyVOipLOLi/zMMtqTlCWESUvnsqNe6xGlGVmkoRiMUCspkg5Z002QeUs5SyYwufqWipVZGWjWf0CDsxBCuuBMAQAAAAASUVORK5CYII=",
|
||||
changelog: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAUCAYAAABGUvnzAAAABmJLR0QA/wD/AP+gvaeTAAAHa0lEQVRo3uVaaVBU2RV+wYaBmCjjZOJU1kqmUpU/k7/GuFdpLHdFBXcrrj/G/LDUcSGoQUPQCIWZlDNYwdHBYQAdkc1MsyPQsjWLCrgAAg0KCAOoLCL45X6HvGfTpbGtwrFq+lR91eee7fZ7X59zr5Ta8uXLPdavXx+qYFOAK+CdtPLvOmzj0stCtdhYD43krlu3Dq6EcallLgHvlJIQbe3atc0KcCW8nVLqEvBOLWvW1qxZA1eDt7nEZaCtXr0aroaxX1tdBtqqVavgahjzn2KXgbZy5Uo4i/7+frmkvErOm8Tg4OBz7T+8VOQUKM7GjgT6Bp/iXdV1I1lTW7FiBZzF1q1b8SrxrwqOz5GsR4KfZ/9BcqFToDgbOxLoGxjAjy6NbE3Nz88P9uAt02KxoL6+Hnfu3EFRUZHhYwfzkkKdEhUVJXHNzc04cOAAkpKS0NDQINi+fbuRR0lMTMTly5dx48YNHDt2bJgvLi4O+fn5iIiIEFtAQAAqKytRW1uL6upqBAYGGvG5ubmoqamRfQsLC7FhwwbDFxISgqamJsmLjY0Vgh2fjxidVDAMf7RUoqDjIa51deOqwuwrlWKn+Fc2iK22uw9+RbeMnJimNpR0PpKchHvf4Kdmq+GjBFQ1SIxVxdjnrbXexs2HvWI/eMOGgadPDV/fkwG8kzyk+xTeRLmqff1BN7LauvBBZrlTNRyh+fr6wh7BwcHIy8vDjBkzBIsWLTJ8JJhnGHVKWFiYxBw8eBC9vb3Yt28fpk+fjhMnTiAzM9PIoxw5cgSzZs3C4sWLcf/+fWzevHmYb+bMmZg3b54QVldXh2XLlkltEtLa2irdzXjdTvAHceHCBbFv2bIFXV1dMoLpO3XqlBDs+HzE9xPzDfxCEXO/7zGmfB4Hz79/gtHBn+K98+nio2zLKRH77/8djYZHPUbez8JjxE4E5BbjHzdtho+y3GyBZ9gZ/PaTL4y8X6dY0dLbh/c/PgNPtc9HljIMqLGs55HgcUn5+JWKa1Pf6YPwKKn/oTkHRe1dTtVwhKb+kgV7bNy4UTrSbDYjNDRUCNV9JJgvkDplyZIlopOUzs5OLFy4UNY7duzA9evXjTwKiaG+dOlS6UL+kHQfbXos7R0dHaioqBCwjs1mw7Zt28QfHh4u3V1VVSWdXFBQIPajR49KXR8fH1mTSBLs+HyEV8IVA75XKpBWUw/PqJQhW7wFXnE5olO8P0sQ3TM6FT1PnmCMIoDrP5feRm77A+QplLd14FJdk1GTMiby0tD6XCa6+4fyVqi94quq4XU+S3zjT8cLOXoeCX478Qr8VFxKdR28YjPEPjriIh4PDOLHany/rIYjNL54e5C02bNnY//+/TJW7927J91DHwnmGUZdJ4b6pk2bpMv0Grt27RIS9DWFlzN9TVI4dh3rEIcPH0Z5ebnRpcTUqVOl83fv3o3Gxkb5IdHOiWG1WiUvKCgI2dnZRh1+ZxLs+HyEpyJRx7L0IqTdaRxm00HxvJhrrB897od3ggV/yL6KW50PMT7sM3ioLvKJTUZGfdNL8/wyixB/u86wj0+0CDn6mgQzzjejCCm1NsM+WuGxOp/fdaKGIzS+XHtwdLJrOS7nzJmD9vZ2GX/0kWCOTOo6MQS7ngTr6507dwrB+ppy+vRp0dmJ7HYS4FiH4ITgqD106JBh27t3r3zynC8tLZUfIddZWVlCMHXeDVpaWuSHxDWPDxLs+HzEWxfzDPzky6/R0t2DyYo0rvky31PnIHWKfSyJGpuQh3kZxciua8RbX2WLPbqmEelc/y/uRXm/jElBq9rrfXOx2D+6WiPk6HEkmHE/jzajracXv0srEfuHpdUosN1V+11+aQ1HaBxp9uBL5BlI8LJ08uRJOYfpI8EcfdQpeg5HNAnW1zrB+poSGRkpNdmBe/bskY50rEPQzts6xzNH8927d43Ru2DBAqSlpaGkpAQ5OTlykSLBep6/v7/sy/P/7NmzQrDj8xEecbnPoEbotIgYFLZ1olJdaK51PcLM7DLxUexjSdSY+Fx4fZ6M6IrbMDd/g2hbK0KLrwnBetyL8jzUyF19Lhm31D5l6oL0N2sl2hWRehwJHorLxMKzcSjveICqBz3IbGqVs1ziXlLDERq7wR5z587FlClTDPBipPsmTpwoL1LXdTtH5qRJk4z1/PnzZazqawrrsN7kyZNlMtjXdPwOej5jCY5j3UedFzl+8mI2bdo0w8e6zKOP+z2vNuF+IWc4FGGmw/+CKfCfMB36GO5nksQ+am/wsDjTX47CXXWtu+ok07EImILDhz6PR8KkLkN63AvzlD5WnZmmvx6Xff6UakHyzZpncf7P4txPxUuMKfD4UO2oVCPu/9VwhEbCXjco38Y+zsJEgt4QgirrYVX/JGP3pTe24Deffvlaa2gcv68bEyZMwLexj7MYdT77zUF1u5uaFG6qM92CTmDUF+bXWoMEt3DEuhLczmW5Br7KbibBoby8uBK+py4xrgC381kh/EOHh3poktzGy40rQIvJ+G4jNrNNO5ch/2XnvyqleyAtV3GuAAAAAElFTkSuQmCC",
|
||||
@ -74,7 +74,7 @@ export default class About extends React.Component {
|
||||
<div style={ style.root }>
|
||||
<image src={browser.runtime.getURL("assets/images/icon128.png")}></image>
|
||||
<div style={ style.title }>简悦 SimpRead</div>
|
||||
<div>让你瞬间进入沉浸式阅读的扩展</div>
|
||||
<div>为你提供「如杂志般沉浸式阅读体验」的扩展</div>
|
||||
<div style={ style.badges }>
|
||||
<a href={ urls.href.version } target="_blank"><img style={ style.img } src={ urls.badges.version }/></a>
|
||||
<a href={ urls.href.website } target="_blank"><img style={ style.img } src={ urls.badges.website }/></a>
|
||||
@ -91,7 +91,7 @@ export default class About extends React.Component {
|
||||
|
||||
<div className="label">帮助</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<div className="more" style={{cursor: 'default'}}>
|
||||
<div className="more">
|
||||
<div><a style={style.href} target="_blank" href="http://sr.ksria.cn/zhifu_m2.png">如果简悦可以解决你在阅读上痛点,可以请我喝杯咖啡</a></div>
|
||||
<span className="desc">简悦是一个免费且开源的项目</span>
|
||||
<span className="arrow"></span>
|
||||
@ -111,20 +111,20 @@ export default class About extends React.Component {
|
||||
</div>
|
||||
<div style={{ 'margin-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<div className="more">
|
||||
<a style={style.href} target="_blank" href="http://ksria.com/simpread/docs/#/入门指南(-操作指引-)">第一次使用简悦?或者并不了解「阅读模式」请前往 <b>新手入门</b></a>
|
||||
<a style={style.href} target="_blank" href="http://ksria.com/simpread/guide/">第一次使用简悦?或者并不了解「阅读模式」请前往 <b>新手入门</b></a>
|
||||
<span style={{ bottom: "11px" }} className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ 'margin-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<div className="more">
|
||||
<a style={style.href}><b onClick={()=>this.props.onClick("welcome")}>重看引导页面</b> 或者 <a style={style.href} target="_blank" href="http://ksria.com/simpread/welcome/version_1.1.2.5005.html"><b>重看当前版本</b></a> 的功能介绍</a>
|
||||
<a style={style.href}><b onClick={()=>this.props.onClick("welcome")}>重看引导页面</b> 或者 <a style={style.href} target="_blank" href="http://ksria.com/simpread/welcome/version_1.1.3.html"><b>重看当前版本</b></a> 的功能介绍</a>
|
||||
<span style={{ bottom: "11px" }} className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">其它平台的简悦</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<div className="more" style={{cursor: 'default'}}>
|
||||
<div className="more">
|
||||
<div><a style={style.href} target="_blank" href="http://ksria.com/simpread/#downloads">简悦已经上线了 Firefox 版,UserScript 版,JSBox 版,总有一款适合你</a></div>
|
||||
<span className="desc">包括但不限于:Chrome · Firefox · Safari · Apple Safari · Microsoft Edge · Opera · iPhone · iPad</span>
|
||||
<span className="arrow"></span>
|
||||
@ -145,8 +145,8 @@ export default class About extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ 'margin-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<div className="more">
|
||||
<a style={style.href} target="_blank" href="https://twitter.com/wanglei001">可以在 <b>Twitter</b></a> 或 <a style={style.href} target="_blank" href="https://weibo.com/23784148"><b>新浪微博</b></a> 上关注我
|
||||
<div className="more" style={{cursor: 'default'}}>
|
||||
可以在 <a style={style.href} target="_blank" href="https://twitter.com/wanglei001"><b>Twitter</b></a> 或 <a style={style.href} target="_blank" href="https://weibo.com/23784148"><b>新浪微博</b></a> 上关注我
|
||||
<span style={{ bottom: "11px" }} className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -121,7 +121,7 @@ export default class AccountOpt extends React.Component {
|
||||
<sr-opt-gp>
|
||||
<TextField
|
||||
floatingtext="邮箱"
|
||||
placeholder="请使用真是且有效的邮箱地址"
|
||||
placeholder="请使用真实且有效的邮箱地址"
|
||||
errortext={ this.state.email_err }
|
||||
value={ this.props.user.email } onChange={ e=>this.onChangeEmail(e) } disable={false} />
|
||||
</sr-opt-gp>
|
||||
|
||||
@ -32,7 +32,7 @@ export default class Actionbar extends React.Component {
|
||||
const obj = this.props.items[key].items[item];
|
||||
return (
|
||||
<Button shape="circle"
|
||||
icon={ obj.icon }
|
||||
icon={ obj.icon } fontIcon={ obj.fontIcon }
|
||||
color="#fff" backgroundColor={ obj.color }
|
||||
waves="md-waves-effect md-waves-button"
|
||||
tooltip={{ text: obj.name }}
|
||||
@ -43,7 +43,7 @@ export default class Actionbar extends React.Component {
|
||||
return (
|
||||
<sr-opt-gp>
|
||||
<sr-opt-label>{action.name}</sr-opt-label>
|
||||
<actions style={{ display: "flex", margin: "10px 0" }}>{ items }</actions>
|
||||
<actions style={{ display: "flex", margin: "10px 0", "flex-wrap": "wrap" }}>{ items }</actions>
|
||||
</sr-opt-gp>
|
||||
);
|
||||
});
|
||||
|
||||
@ -20,6 +20,10 @@ export default class Auth extends React.Component {
|
||||
instapaper: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
jianguo: {
|
||||
username: "",
|
||||
password: "",
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,11 +31,12 @@ export default class Auth extends React.Component {
|
||||
secret : undefined,
|
||||
linnk : undefined,
|
||||
instapaper : undefined,
|
||||
jianguo: undefined,
|
||||
}
|
||||
|
||||
onChange( state, value, flag ) {
|
||||
let notify;
|
||||
const { dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive } = exp,
|
||||
const { dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive, jianguo, yuque } = exp,
|
||||
clear = ( id, name ) => {
|
||||
Object.keys( storage.secret[id] ).forEach( item => storage.secret[id][item] = "" );
|
||||
storage.Safe( ()=> {
|
||||
@ -42,10 +47,12 @@ export default class Auth extends React.Component {
|
||||
success = ( id, name, data ) => {
|
||||
notify && notify.complete();
|
||||
Object.keys( data ).forEach( item => storage.secret[id][item] = data[item] );
|
||||
id == "jianguo" && ( storage.secret[id]["access_token"] = { username: data.username, password: data.password });
|
||||
storage.Safe( () => {
|
||||
new Notify().Render( `已成功授权 ${name} 。` );
|
||||
id == "linnk" && this.setState({ secret: storage.secret, linnk: false });
|
||||
id == "instapaper" && this.setState({ secret: storage.secret, instapaper: false });
|
||||
id == "jianguo" && this.setState({ secret: storage.secret, jianguo: false });
|
||||
if ( location.hash.startsWith( "#labs?auth=" ) ) {
|
||||
new Notify().Render( "3 秒钟将会关闭此页面..." );
|
||||
setTimeout( () => {
|
||||
@ -72,6 +79,11 @@ export default class Auth extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( state == "jianguo" && !flag && !storage.secret.jianguo.username ) {
|
||||
this.setState({ jianguo: !this.state.jianguo });
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !value ) {
|
||||
state == "pocket" && $( this.refs.pocket_tags ).velocity( value ? "slideDown" : "slideUp" );
|
||||
if ( state == "linnk" ) {
|
||||
@ -82,6 +94,10 @@ export default class Auth extends React.Component {
|
||||
this.props.instapaper.username = "";
|
||||
this.props.instapaper.password = "";
|
||||
}
|
||||
if ( state == "jianguo" ) {
|
||||
this.props.jianguo.username = "";
|
||||
this.props.jianguo.password = "";
|
||||
}
|
||||
clear( state, exp.Name( state ));
|
||||
return;
|
||||
}
|
||||
@ -170,6 +186,39 @@ export default class Auth extends React.Component {
|
||||
});
|
||||
}).fail( error => failed( error, gdrive.id, gdrive.name ));
|
||||
break;
|
||||
case "yuque":
|
||||
yuque.New().Login();
|
||||
yuque.dtd.done( ()=> {
|
||||
yuque.Auth( ( result, error ) => {
|
||||
if ( error ) failed( error, yuque.id, yuque.name );
|
||||
else {
|
||||
yuque.GetUser( ( result, error ) => {
|
||||
if ( error ) failed( error, yuque.id, yuque.name );
|
||||
else {
|
||||
yuque.GetRepos( ( result, error ) => {
|
||||
if ( error ) failed( error, yuque.id, yuque.name );
|
||||
else if ( yuque.repos_id != "" ) {
|
||||
success( yuque.id, yuque.name, { access_token: yuque.access_token, repos_id: yuque.repos_id });
|
||||
} else {
|
||||
yuque.CreateRepo( ( result, error ) => {
|
||||
if ( error ) failed( error, yuque.id, yuque.name );
|
||||
else success( yuque.id, yuque.name, { access_token: yuque.access_token, repos_id: yuque.repos_id });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}).fail( error => failed( error, yuque.id, yuque.name ));
|
||||
break;
|
||||
case "jianguo":
|
||||
jianguo.Auth( this.props.jianguo.username, this.props.jianguo.password, result => {
|
||||
if ( result && result.status == 401 ) {
|
||||
failed( "授权错误,请重新授权。", jianguo.id, jianguo.name );
|
||||
} else success( "jianguo", "坚果云", { username: this.props.jianguo.username, password: this.props.jianguo.password } );
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +236,35 @@ export default class Auth extends React.Component {
|
||||
this.props.instapaper[state] = value;
|
||||
}
|
||||
|
||||
jianguoOnChange( state, value ) {
|
||||
this.props.jianguo[state] = value;
|
||||
}
|
||||
|
||||
webdavOnChange() {
|
||||
this.state.secret.webdav = event.target.value.split("\n");
|
||||
storage.Safe( () => this.setState({ secret: storage.secret }), storage.secret );
|
||||
}
|
||||
|
||||
webdavAuth() {
|
||||
this.state.secret.webdav.forEach( ( item, idx ) => {
|
||||
try {
|
||||
item = JSON.parse( item );
|
||||
if ( Object.keys( item ).join( "" ).replace( /url|name|password|user/ig, "" ) != "" ) {
|
||||
throw "error";
|
||||
}
|
||||
exp.webdav.Auth( item.url, item.user, item.password, result => {
|
||||
if ( result && ( result.status == 201 || result.status == 405 )) {
|
||||
new Notify().Render( `${item.name} 验证成功。` );
|
||||
} else {
|
||||
new Notify().Render( 2, `${item.name} 授权失败,请确认用户名和密码。` );
|
||||
}
|
||||
});
|
||||
} catch( error ) {
|
||||
new Notify().Render( 2, `第 ${idx+1} 条数据格式错误,请重新输入。` );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps( nextProps ) {
|
||||
this.setState({ secret: storage.secret })
|
||||
}
|
||||
@ -305,6 +383,57 @@ export default class Auth extends React.Component {
|
||||
label={ this.state.secret.gdrive.access_token ? "已授权 Google 云端硬盘,是否取消授权?" : "是否连接并授权 Google 云端硬盘 ?" }
|
||||
onChange={ (s)=>this.onChange( "gdrive", s ) } />
|
||||
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="jianguo">
|
||||
<Switch width="100%" checked={ this.state.secret.jianguo && this.state.secret.jianguo.username != "" && this.state.secret.jianguo.password ? true : false }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label={ this.state.secret.jianguo && this.state.secret.jianguo.username != "" ? "已授权 坚果云,是否取消授权?" : "是否连接并授权 坚果云 ?" }
|
||||
onChange={ (s)=>this.onChange( "jianguo", s ) } />
|
||||
</div>
|
||||
{ this.state.jianguo &&
|
||||
<div ref="jianguo">
|
||||
<div style={{ "display": "flex", "flex-direction": "row" }}>
|
||||
<TextField
|
||||
placeholder="请填入 坚果云的 WebDAV 用户名,简悦不会记录你的用户名。"
|
||||
onChange={ (evt)=>this.jianguoOnChange( "username", evt.target.value ) }
|
||||
/>
|
||||
<TextField
|
||||
password={true}
|
||||
placeholder="请填入 坚果云的 WebDAV 密码,简悦不会记录你的密码。"
|
||||
onChange={ (evt)=>this.jianguoOnChange( "password", evt.target.value ) }
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="raised" width="100%" style={{ "margin": "0" }}
|
||||
text="绑定 坚果云 的信息"
|
||||
color="#fff" backgroundColor="#3F51B5"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ (s)=>this.onChange( "jianguo", s, "login" ) } />
|
||||
|
||||
</div> }
|
||||
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="yuque">
|
||||
<Switch width="100%" checked={ this.state.secret.yuque.access_token != "" ? true : false }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label={ this.state.secret.yuque.access_token ? "已授权 语雀,是否取消授权?" : "是否连接并授权 语雀 ?" }
|
||||
onChange={ (s)=>this.onChange( "yuque", s ) } />
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="webdav">
|
||||
<div className="label" style={{'margin-bottom':' -15px'}}>WebDAV</div>
|
||||
<div className="sublabel">简悦支持任意 WebDAV 的服务,包括:Box · TeraCLOUD 等</div>
|
||||
<TextField
|
||||
multi={ true } rows={8}
|
||||
placeholder={ `每行一组,格式为:{ "name": "网盘的名称", "user": "用户名", "password": "密码", "url": "webdav 地址" }` }
|
||||
value={ ( this.state.secret.webdav||[] ).join( "\n" ) }
|
||||
onChange={ (e)=>this.webdavOnChange(e) }
|
||||
/>
|
||||
<Button type="raised" width="100%" style={{ "margin": "0" }}
|
||||
text="验证上述 WebDAV 服务"
|
||||
color="#fff" backgroundColor="#3F51B5"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ (s)=>this.webdavAuth() } />
|
||||
</div>
|
||||
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import * as menu from 'menu';
|
||||
import * as watch from 'watch';
|
||||
import * as exp from 'export';
|
||||
import {br} from 'browser';
|
||||
import * as msg from 'message';
|
||||
|
||||
export default class CommonOpt extends React.Component {
|
||||
|
||||
@ -23,20 +24,43 @@ export default class CommonOpt extends React.Component {
|
||||
|
||||
sync() {
|
||||
let notify;
|
||||
const dbx = exp.dropbox,
|
||||
read = () => {
|
||||
const dbx = exp.dropbox,
|
||||
jianguo = exp.jianguo,
|
||||
write = () => {
|
||||
storage.option.sync = Now();
|
||||
storage.Write( () => {
|
||||
writeConfig();
|
||||
});
|
||||
},
|
||||
readDropbox = () => {
|
||||
notify = new Notify().Render({ content: "数据同步中,请稍等...", state: "loading" });
|
||||
dbx.Exist( dbx.config_name, ( result, error ) => {
|
||||
if ( result == -1 ) {
|
||||
storage.option.sync = Now();
|
||||
storage.Write( () => {
|
||||
dbx.Write( dbx.config_name, storage.Export(), callback );
|
||||
});
|
||||
write();
|
||||
} else {
|
||||
dbx.Read( dbx.config_name, callback );
|
||||
}
|
||||
});
|
||||
},
|
||||
readJianguo = ( obj ) => {
|
||||
notify = new Notify().Render({ content: "数据同步中,请稍等...", state: "loading" });
|
||||
jianguo.Read( obj.username, obj.password, jianguo.config_name, result => {
|
||||
if ( result && result.status == 404 ) {
|
||||
write();
|
||||
} else if ( result && result.status == 200 ) {
|
||||
callback( "read", result.done );
|
||||
}
|
||||
});
|
||||
},
|
||||
writeConfig = () => {
|
||||
if ( storage.option.save_at == "dropbox" ) {
|
||||
dbx.Write( dbx.config_name, storage.Export(), callback );
|
||||
} else {
|
||||
jianguo.Add( storage.secret.jianguo.username, storage.secret.jianguo.password, jianguo.root + "/" + jianguo.config_name, storage.Export(), result => {
|
||||
callback( "write", undefined, result && [ 201, 204 ].includes( result.status ) ? undefined : "error" );
|
||||
});
|
||||
}
|
||||
},
|
||||
callback = ( type, result, error ) => {
|
||||
notify.complete();
|
||||
switch ( type ) {
|
||||
@ -50,11 +74,8 @@ export default class CommonOpt extends React.Component {
|
||||
remote = new Date( json.option.update.replace( /年|月/ig, "-" ).replace( "日", "" ));
|
||||
if ( ver.Compare( json.version ) == 1 ) {
|
||||
new Notify().Render( "本地版本与远程版本不一致,且本地版本较新,是否覆盖远程版本?", "覆盖", () => {
|
||||
storage.option.sync = Now();
|
||||
storage.Write( () => {
|
||||
watch.SendMessage( "import", true );
|
||||
dbx.Write( dbx.config_name, storage.Export(), callback );
|
||||
}, storage.simpread );
|
||||
watch.SendMessage( "import", true );
|
||||
write();
|
||||
});
|
||||
}
|
||||
else if ( local < remote ) {
|
||||
@ -69,11 +90,8 @@ export default class CommonOpt extends React.Component {
|
||||
});
|
||||
} else if ( local > remote ) {
|
||||
new Notify().Render( "本地配置文件较新,是否覆盖远程备份文件?", "覆盖", () => {
|
||||
storage.option.sync = Now();
|
||||
storage.Write( () => {
|
||||
watch.SendMessage( "import", true );
|
||||
dbx.Write( dbx.config_name, storage.Export(), callback );
|
||||
}, storage.simpread );
|
||||
watch.SendMessage( "import", true );
|
||||
write();
|
||||
});
|
||||
} else {
|
||||
new Notify().Render( "本地与远程数据相同,无需重复同步。" );
|
||||
@ -83,26 +101,31 @@ export default class CommonOpt extends React.Component {
|
||||
};
|
||||
|
||||
storage.Safe( ()=> {
|
||||
const sec_dbx = storage.secret.dropbox;
|
||||
!sec_dbx.access_token ?
|
||||
new Notify().Render( `未对 ${ dbx.name } 授权,请先进行授权操作。`, "授权", () => {
|
||||
dbx.New().Auth();
|
||||
dbx.dtd
|
||||
.done( () => {
|
||||
sec_dbx.access_token = dbx.access_token;
|
||||
storage.Safe( () => {
|
||||
new Notify().Render( "授权成功!" );
|
||||
read();
|
||||
}, storage.secret );
|
||||
})
|
||||
.fail( error => {
|
||||
console.error( error )
|
||||
new Notify().Render( 2, `获取 ${ dbx.name } 授权失败,请重新获取。` );
|
||||
});
|
||||
}) : ( () => {
|
||||
dbx.access_token = sec_dbx.access_token;
|
||||
read();
|
||||
})();
|
||||
if ( storage.option.save_at == "dropbox" ) {
|
||||
const sec_dbx = storage.secret.dropbox;
|
||||
!sec_dbx.access_token ?
|
||||
new Notify().Render( `未对 ${ dbx.name } 授权,请先进行授权操作。`, "授权", () => {
|
||||
dbx.New().Auth();
|
||||
dbx.dtd
|
||||
.done( () => {
|
||||
sec_dbx.access_token = dbx.access_token;
|
||||
storage.Safe( () => {
|
||||
new Notify().Render( "授权成功!" );
|
||||
readDropbox();
|
||||
}, storage.secret );
|
||||
})
|
||||
.fail( error => {
|
||||
console.error( error )
|
||||
new Notify().Render( 2, `获取 ${ dbx.name } 授权失败,请重新获取。` );
|
||||
});
|
||||
}) : ( () => {
|
||||
dbx.access_token = sec_dbx.access_token;
|
||||
readDropbox();
|
||||
})();
|
||||
} else {
|
||||
const jianguo = storage.secret.jianguo;
|
||||
!jianguo.access_token ? new Notify().Render( 2, `坚果云 <b>授权</b> 后才能使用此功能,如何授权 <a href="http://ksria.com/simpread/docs/#/坚果云">请看这里</a>。` ) : readJianguo( storage.secret.jianguo );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,13 +151,14 @@ export default class CommonOpt extends React.Component {
|
||||
storage.version != json.version &&
|
||||
storage.Fix( json.read.sites, json.version, storage.version, json.focus.sites );
|
||||
json = ver.Verify( json.version, json );
|
||||
new Notify().Render( "上传版本太低,已自动转换为最新版本。" );
|
||||
new Notify().Render({ type: 2, content: `上传版本太低,已自动转换为最新版本。`, state: "holdon" });
|
||||
}
|
||||
menu.Refresh( json.option.menu );
|
||||
ver.Incompatible( json.version, json );
|
||||
json.option.origins && json.option.origins.length > 0 &&
|
||||
new Notify().Render( "导入的配置文件包含了第三方源,请通过手动导入。" );
|
||||
new Notify().Render({ content: `导入的配置文件包含了第三方源,刷新后请重新 <b>手动导入</b>。`, state: "holdon" });
|
||||
json.option.plugins && json.option.plugins.length > 0 &&
|
||||
new Notify().Render( "导入的配置文件包含了插件,请通过手动导入。" );
|
||||
new Notify().Render({ content: `导入的配置文件包含了插件,刷新后请重新 <b>手动导入</b>。`, state: "holdon" });
|
||||
this.importsecret( json.option.secret, { ...json.secret }, () => {
|
||||
delete json.secret;
|
||||
storage.Write( ()=> {
|
||||
@ -170,6 +194,14 @@ export default class CommonOpt extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
oldnewsites() {
|
||||
new Notify().Render( "此功能转移到 <b>站点管理</b> 选项卡里面,3 秒钟后自动切换到此选项卡。" );
|
||||
setTimeout( ()=> {
|
||||
location.href = location.origin + "/options/options.html#labs";
|
||||
window.dispatchEvent( new CustomEvent( msg.MESSAGE_ACTION.turn_tab, { detail: { page: 3 }}));
|
||||
}, 3000 );
|
||||
}
|
||||
|
||||
newsites() {
|
||||
const notify = new Notify().Render({ content: "数据同步中,请稍等...", state: "loading" });
|
||||
storage.GetRemote( "remote", ( result, error ) => {
|
||||
@ -223,12 +255,15 @@ export default class CommonOpt extends React.Component {
|
||||
render() {
|
||||
return(
|
||||
<div style={{ width: '100%' }}>
|
||||
<Button type="raised" text="同步到你的 Dropbox 账户"
|
||||
icon={ ss.IconPath( "sync_icon" ) }
|
||||
<div className="version-tips" data-hits="sync">
|
||||
<Button type="raised" text={ `同步到你的 ${storage.option.save_at == "dropbox" ? "Dropbox" : "坚果云" } 账户` }
|
||||
icon={ ss.IconPath( storage.option.save_at + "_icon" ) }
|
||||
color="#fff" backgroundColor="#1976D2"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
tooltip={{ text: this.state.sync }}
|
||||
onClick={ ()=>this.sync() } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="config">
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<Button type="raised" text="从本地导入配置文件" width="100%"
|
||||
icon={ ss.IconPath( "import_icon" ) }
|
||||
@ -242,12 +277,15 @@ export default class CommonOpt extends React.Component {
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.export() } />
|
||||
</div>
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
</div>
|
||||
<div className="version-tips" data-hits="oldnewsites" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="手动同步适配列表" width="100%"
|
||||
icon={ ss.IconPath( "update_icon" ) }
|
||||
color="#fff" backgroundColor="#2196F3"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.newsites() } />
|
||||
onClick={ ()=>this.oldnewsites() } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="clear" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="清除数据" width="100%"
|
||||
icon={ ss.IconPath( "clear_icon" ) }
|
||||
tooltip={{ text: "清除掉本地配置文件,需谨慎!" }}
|
||||
|
||||
@ -26,7 +26,7 @@ export default class URL extends React.Component {
|
||||
if ( url == "" ) {
|
||||
code = -2;
|
||||
this.setState({ error : "当前输入不能为空。" });
|
||||
} else if ( !/^http(s)?:\/\//.test( url ) ) {
|
||||
} else if ( !/^http[s|*]?:\/\//.test( url ) ) {
|
||||
code = -1;
|
||||
this.setState({ error : "请输入有效的 url " });
|
||||
} else if ( location.protocol.startsWith( "http" ) && !minimatch( window.location.href, url ) && url != this.props.url ) {
|
||||
|
||||
157
src/module/guide.jsx
Normal file
@ -0,0 +1,157 @@
|
||||
console.log( "===== simpread option guide load =====" )
|
||||
|
||||
import intro from 'intro';
|
||||
|
||||
import {browser} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import {storage} from 'storage';
|
||||
import * as ver from 'version';
|
||||
|
||||
class Guide extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
tips: []
|
||||
};
|
||||
|
||||
static propsType = {
|
||||
tips: React.PropTypes.array,
|
||||
};
|
||||
|
||||
state = {
|
||||
tips: [],
|
||||
}
|
||||
|
||||
onClick( event, idx, url, detail ) {
|
||||
if ( url.startsWith( "http" ) ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url }));
|
||||
} else if ( url.startsWith( "@" ) ) {
|
||||
start( url );
|
||||
this.props.onExit && this.props.onExit();
|
||||
} else if ( url.startsWith( "!!" ) ) {
|
||||
start( url, true, detail );
|
||||
this.props.onExit && this.props.onExit();
|
||||
} else if ( idx == 2 ) {
|
||||
start( storage.version );
|
||||
this.props.onExit && this.props.onExit();
|
||||
} else {
|
||||
const id = location.hash == "" ? "common" : location.hash.replace( "#", "" );
|
||||
if ( id == "account" || id == "about" ) {
|
||||
new Notify().Render( "此页面没有功能描述。" );
|
||||
} else {
|
||||
start( id, false );
|
||||
this.props.onExit && this.props.onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingClick() {
|
||||
$( ".guide .loading" ).html( `<svg width="20" height="20" viewBox="0 0 38 38" stroke="#26d07c"> <g fill="none" fill-rule="evenodd"> <g transform="translate(1 1)" stroke-width="2"> <circle stroke-opacity=".5" cx="18" cy="18" r="18"/> <path d="M36 18c0-9.94-8.06-18-18-18"> <animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"/> </path> </g> </g></svg>` );
|
||||
const ajax = () => {
|
||||
$.ajax({
|
||||
url : storage.help_service + "?=" + Math.round(+new Date()),
|
||||
method: "GET",
|
||||
}).done( ( result, textStatus, jqXHR ) => {
|
||||
if ( result && result.tips.length == 0 ) {
|
||||
$( ".guide .loading" ).html( '<span>没有新的消息</span>' ).css({"animation": ".1s reverse fadein,235ms cubic-bezier(.4,0,.2,1) popup"});
|
||||
} else {
|
||||
$( ".guide" ).find( "hr" ).remove();
|
||||
this.setState({tips: this.state.tips.concat( result.tips ) });
|
||||
$( ".guide .loading" ).remove();
|
||||
}
|
||||
}).fail( error => {
|
||||
$( ".guide .loading" ).html( `<i class="fas fa-bug" style="color:#FF5252;"></i><span style="color:#FF5252;">发生了一些错误,请稍后再试。</span>` )
|
||||
});
|
||||
};
|
||||
setTimeout( ajax, 1000 );
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
storage.GetRemote( "help_tips", ( result, error ) => {
|
||||
result && result.tips && this.setState({ tips: result.tips });
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
$( "guid-card[id='3']" ).after( "<hr>" );
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$( ".guide" ).scroll( event => {
|
||||
if ( $( event.target ).scrollTop() > 35 ) {
|
||||
$( ".guide .title" )
|
||||
.css({"box-shadow": "2px 4px 10px rgba(0,0,0,.2)" })
|
||||
.find( "span" ).text( "帮助中心 > 快捷答案" ).css({"font-weight": "normal", "animation": ".1s reverse fadein,235ms cubic-bezier(.4,0,.2,1) popup" });
|
||||
} else {
|
||||
$( ".guide .title" )
|
||||
.removeAttr( "style" )
|
||||
.find( "span" ).text( "帮助中心" ).removeAttr( "style" );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const tips = this.state.tips.map( item => {
|
||||
return (
|
||||
<guid-card id={ item.idx } class="md-waves-effect" onClick={ e=>this.onClick( e, item.idx, item.url, item.detail ) }>
|
||||
<guid-card-tips>
|
||||
<span dangerouslySetInnerHTML={{__html: item.icon }} ></span>
|
||||
<span>{ item.name }</span>
|
||||
</guid-card-tips>
|
||||
</guid-card>
|
||||
)
|
||||
});
|
||||
return (
|
||||
<div className="guide">
|
||||
<div className="title"><span>帮助中心</span></div>
|
||||
<div className="subtitle"><span>快捷答案</span></div>
|
||||
<div className="group">
|
||||
{ tips }
|
||||
</div>
|
||||
<div className="loading" onClick={ ()=>this.onLoadingClick() }><span className="md-waves-effect">加载更多</span></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show current version intro
|
||||
*
|
||||
* @param {string} id include: version id | hash id, e,g. 1.1.3, common, simple
|
||||
* @param {boolean} verify current url and intros.start()
|
||||
* @param {boolean} detail @see ver.tips data structure
|
||||
*/
|
||||
function start( id, verify = true, detail = {} ) {
|
||||
const [ rm_idx, rm_target, rm_steps ] = [ detail.idx, detail.target, detail.steps ],
|
||||
target = rm_target ? rm_target : ver.tips[ id ].target,
|
||||
idx = rm_idx ? rm_idx : ver.tips[ id ].idx,
|
||||
steps = (() => {
|
||||
const items = rm_steps ? rm_steps : ver.tips[ id ].items;
|
||||
return items.map( item => { return { element: $( ver.tips.root( item.id ) )[0], intro: item.intro }})
|
||||
})(),
|
||||
intros = intro(),
|
||||
start = () => {
|
||||
intros.setOptions({
|
||||
hintButtonLabel: "确认",
|
||||
nextLabel: "下一条 →",
|
||||
prevLabel: "← 上一条",
|
||||
skipLabel: "",
|
||||
doneLabel: "完成",
|
||||
hidePrev: true,
|
||||
hideNext: true,
|
||||
steps
|
||||
});
|
||||
intros.start();
|
||||
};
|
||||
if ( verify && location.hash != `#${ target }` ) {
|
||||
location.href = location.origin + "/options/options.html#labs";
|
||||
window.dispatchEvent( new CustomEvent( msg.MESSAGE_ACTION.turn_tab, { detail: { page: idx }}));
|
||||
setTimeout( start, 500 );
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Guide,
|
||||
start as Start
|
||||
}
|
||||
@ -29,10 +29,14 @@ export default class LabsOpt extends React.Component {
|
||||
!child && ( this.props[model][state]=value );
|
||||
child && ( this.props[model][state][child]=value );
|
||||
child && menu.Refresh( this.props[model][state] );
|
||||
if ( model == "option" && state == "save_at" ) {
|
||||
this.props[model][state] = value ? "dropbox" : "jianguo";
|
||||
}
|
||||
this.props.onChange && this.props.onChange( true );
|
||||
model == "read" && state == "auto" && this.exclusionState( value );
|
||||
model == "read" && state == "toc" && this.tocState( value );
|
||||
model == "read" && state == "cleanup" && this.cleanupState( value );
|
||||
model == "option" && state == "preload" && this.lazyloadState( value );
|
||||
}
|
||||
|
||||
changeExclusion( event ) {
|
||||
@ -45,6 +49,11 @@ export default class LabsOpt extends React.Component {
|
||||
this.props.onChange && this.props.onChange( false );
|
||||
}
|
||||
|
||||
changeLazyload( event ) {
|
||||
this.props.option.lazyload = event.target.value.split("\n");
|
||||
this.props.onChange && this.props.onChange( false );
|
||||
}
|
||||
|
||||
tocState( value ) {
|
||||
$( this.refs.toc ).velocity( value ? "slideDown" : "slideUp" );
|
||||
}
|
||||
@ -53,6 +62,10 @@ export default class LabsOpt extends React.Component {
|
||||
$( this.refs.cleanup ).velocity( value ? "slideDown" : "slideUp" );
|
||||
}
|
||||
|
||||
lazyloadState( value ) {
|
||||
$( this.refs.lazyload ).velocity( value ? "slideDown" : "slideUp" );
|
||||
}
|
||||
|
||||
exclusionState( value ) {
|
||||
$( this.refs.exclusion ).velocity( value ? "slideDown" : "slideUp" );
|
||||
$( this.refs.whitelist ).velocity( !value ? "slideDown" : "slideUp" );
|
||||
@ -67,10 +80,12 @@ export default class LabsOpt extends React.Component {
|
||||
this.exclusionState( this.props.read.auto );
|
||||
this.tocState( this.props.read.toc );
|
||||
this.cleanupState( this.props.read.cleanup == undefined ? true : this.props.read.cleanup );
|
||||
this.lazyloadState( this.props.option.preload );
|
||||
}
|
||||
|
||||
onClick( state ) {
|
||||
state == "custom" && ( location.href = location.origin + "/options/custom.html" );
|
||||
state == "notice" && ( location.href = location.origin + "/options/notice.html?is_update=" + sessionStorage.getItem( "is_update" ));
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -78,16 +93,21 @@ export default class LabsOpt extends React.Component {
|
||||
<div id="labs" style={{ width: '100%' }}>
|
||||
<div className="label">全局</div>
|
||||
<div className="lab">
|
||||
<div className="version-tips" data-hits="esc">
|
||||
<Switch width="100%" checked={ this.props.option.esc }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="是否启用 「ESC」 退出方式?"
|
||||
desc="包括:聚焦模式与阅读模式"
|
||||
onChange={ (s)=>this.onChange(s, "option", "esc") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="br_exit">
|
||||
<Switch width="100%" checked={ this.props.option.br_exit }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="动作栏图标是否改为 「进入/退出 」模式?"
|
||||
desc="包括:聚焦模式和阅读模式,默认(关闭)为「弹出设定对话框」"
|
||||
onChange={ (s)=>this.onChange(s, "option", "br_exit") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="blacklist">
|
||||
<div style={{ 'padding-top': '10px', 'margin-bottom': '8px;' }}>
|
||||
<div className="label" style={{'margin-bottom':' -15px'}}>黑名单</div>
|
||||
<div className="sublabel">加入其中后,不再启动简悦,有别于白名单和排除列表,黑名单则彻底不加载。</div>
|
||||
@ -98,13 +118,29 @@ export default class LabsOpt extends React.Component {
|
||||
onChange={ (e)=>this.blacklist(e) }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="version-tips" data-hits="secret">
|
||||
<Switch width="100%" checked={ this.props.option.secret }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="同步时是否包含授权服务中的授权码?"
|
||||
desc="包括:导出配置文件到本地,默认(关闭)为不同步;启用后,请妥善保管你的授权码"
|
||||
onChange={ (s)=>this.onChange(s, "option", "secret") } />
|
||||
</div>
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="save_at">
|
||||
<Switch width="100%" checked={ this.props.option.save_at == "dropbox" ? true : false }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="保存配置到 Dropbox ?"
|
||||
desc="注意:默认(已勾选状态)保存到 Dropbox ;选否后(非勾选状态)保存到 【坚果云】。"
|
||||
onChange={ (s)=>this.onChange(s, "option", "save_at") } />
|
||||
</div>
|
||||
<Switch width="100%" checked={ this.props.option.uninstall ? true : false }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="删除后是否给我反馈?"
|
||||
desc="建议开启此选项,简悦不会知道你是谁,但你可以帮助简悦变得更好。"
|
||||
onChange={ (s)=>this.onChange(s, "option", "uninstall") } />
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="menu">
|
||||
<div className="label">右键菜单</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Switch width="100%" checked={ this.props.option.menu.focus }
|
||||
@ -140,7 +176,9 @@ export default class LabsOpt extends React.Component {
|
||||
label="是否显示「加入到稍后读」?"
|
||||
onChange={ (s)=>this.onChange(s, "option", "menu", "unrdist" ) } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="focusconfig">
|
||||
<div className="label">聚焦模式</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Switch width="100%" checked={ this.props.focus.mask }
|
||||
@ -158,28 +196,39 @@ export default class LabsOpt extends React.Component {
|
||||
desc="关闭意味着使用「自动聚焦模式」"
|
||||
onChange={ (s)=>this.onChange(s, "focus", "highlight") } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="readconfig">
|
||||
<div className="label">阅读模式</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<div className="version-tips" data-hits="progress">
|
||||
<Switch width="100%" checked={ this.props.read.progress }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="是否显示阅读进度?"
|
||||
onChange={ (s)=>this.onChange(s, "read", "progress") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="readcontrolbar">
|
||||
<Switch width="100%" checked={ this.props.read.controlbar }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="是否一直显示右下角的控制栏?"
|
||||
desc="关闭意味着「鼠标移上时才显示」"
|
||||
onChange={ (s)=>this.onChange(s, "read", "controlbar") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="fap">
|
||||
<Switch width="100%" checked={ this.props.read.fap }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB" waves="md-waves-effect"
|
||||
label="是否启用高级控制栏面板?"
|
||||
desc="关闭意味着「使用浮动控制栏」"
|
||||
onChange={ (s)=>this.onChange(s, "read", "fap") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="highlight">
|
||||
<Switch width="100%" checked={ this.props.read.highlight }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="是否启动临时阅读模式?"
|
||||
desc="当页面未适配阅读模式时,才能使用此功能"
|
||||
label="手动框选时是否启动二次确认模式?"
|
||||
desc="二次确认模式能精准的定位需要阅读模式的内容。"
|
||||
onChange={ (s)=>this.onChange(s, "read", "highlight") } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="toc">
|
||||
<Switch width="100%" checked={ this.props.read.toc }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="是否自动生成大纲(目录)?"
|
||||
@ -192,12 +241,16 @@ export default class LabsOpt extends React.Component {
|
||||
desc="关闭意味着「一直显示」"
|
||||
onChange={ (s)=>this.onChange(s, "read", "toc_hide") } />
|
||||
</div>
|
||||
</div>
|
||||
<div className="version-tips" data-hits="readauto">
|
||||
<Switch width="100%" checked={ this.props.read.auto }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
desc="白名单与排除列表功能互斥,当启用「自动进入阅读模式」,白名单即失效。"
|
||||
label="如果当前页面适配阅读模式,是否自动进入阅读模式?"
|
||||
desc="注意:此功能只包含已适配的站点,智能识别出正文的站点无法使用此功能,但仍可通过手动方式进入阅读模式。"
|
||||
label="如果当前页面为适配站点,是否自动进入阅读模式?"
|
||||
onChange={ (s)=>this.onChange(s, "read", "auto") } />
|
||||
|
||||
</div>
|
||||
<div className="version-tips" data-hits="exclusion">
|
||||
<div ref="exclusion" style={{ 'padding-top': '10px', 'margin-bottom': '8px;' }}>
|
||||
<div className="label" style={{'margin-bottom':' -15px'}}>排除列表</div>
|
||||
<div className="sublabel">加入其中后将不会自动进入阅读模式,仅当启用「自动进入阅读模式」有效。</div>
|
||||
@ -219,8 +272,11 @@ export default class LabsOpt extends React.Component {
|
||||
onChange={ (e)=>this.changeWhitelist(e) }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="pured">
|
||||
<div className="label">词法分析引擎 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎" style={{ color:' #FF5252', borderBottom: '2px dotted', fontSize: '10px', fontWeight: 'bold', cursor: 'pointer' }}>测试版</a></div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<Switch width="100%" checked={ this.props.read.cleanup == undefined ? true : this.props.read.cleanup }
|
||||
@ -228,28 +284,76 @@ export default class LabsOpt extends React.Component {
|
||||
label="是否启用增强解析模式?"
|
||||
desc="增强解析模式会对版面重新设计,包括:去除多余空格、优化版面结构等,此功能为测试版,遇到解析失败时,请关闭此功能。"
|
||||
onChange={ (s)=>this.onChange(s, "read", "cleanup") } />
|
||||
<div className="version-tips" data-hits="puredpure">
|
||||
<div ref="cleanup" style={{ 'padding-top': '10px', 'margin-bottom': '8px;' }}>
|
||||
<Switch width="100%" checked={ this.props.read.pure }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="纯粹模式"
|
||||
desc="比【增强解析模式】还要彻底优化版本,包括:字形、颜色、字号、代码段等,专治页面及不规范,如:微信订阅号,CSDN 等。"
|
||||
onChange={ (s)=>this.onChange(s, "read", "pure") } />
|
||||
<div className="sublabel">如果经常阅读代码的话,请安装 <a target="_blank" href="https://simpread.ksria.cn/plugins/details/klGUASLasg">代码段增强</a> 包括:高亮,去重,支持 CSDN 等特殊情况的代码段</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="preload">
|
||||
<Switch width="100%" checked={ this.props.option.preload }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="是否启用预加载机制?"
|
||||
desc="1. 简悦的词法分析引擎采用了预加载机制,如果你觉得影响性能的话,请关闭此功能。"
|
||||
onChange={ (s)=>this.onChange(s, "option", "preload") } />
|
||||
<div className="sublabel">2. 关闭此功能后,只有进入阅读模式时才会对页面进行解析,所以经常使用简悦的用户请勿关闭它。</div>
|
||||
<div className="sublabel">3. 此功能的优先级比「自动进入阅读模式」高;当关闭此功能时,自动进入阅读模式将不会工作。</div>
|
||||
</div>
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="lazyload">
|
||||
<div ref="lazyload" style={{ 'padding-top': '10px', 'margin-bottom': '8px;' }}>
|
||||
<div className="label" style={{'margin-bottom':' -15px'}}>延迟加载列表</div>
|
||||
<div className="sublabel">加入其中后的网址将不会启用预加载功能。</div>
|
||||
<div className="sublabel">此功能适合「经常使用简悦但又性能不够」的用户、需要动态加载及支持 Mathjax 解析的页面等。</div>
|
||||
<TextField
|
||||
multi={ true } rows={8}
|
||||
placeholder="每行一个,支持: URL, minimatch 等。"
|
||||
value={ ( this.props.option.lazyload||[] ).join( "\n" ) }
|
||||
onChange={ (e)=>this.changeLazyload(e) }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="auth">
|
||||
<div className="label">授权管理</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Auth/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-hits="custom">
|
||||
<div className="label">自定义样式</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab" onClick={ ()=>this.onClick('custom') }>
|
||||
<div className="more">
|
||||
<div className="more" style={{ 'cursor': 'pointer' }}>
|
||||
<div>增强「中文阅读体验」设置</div>
|
||||
<span className="desc">包括:标题、描述、正文的字间距、行间距、首行缩进等及自定义 CSS。</span>
|
||||
<span className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-tips" data-version="1.1.3" data-hits="notice">
|
||||
<div className="label">消息中心</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab">
|
||||
<Switch width="100%" checked={ this.props.option.notice }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="当没有未读信息时,是否显示右下角提示框?"
|
||||
desc="关闭后,当没有新的未读信息时,隐藏未读提示;当有新的消息时,仍会在右下角显示未读提示。"
|
||||
onChange={ (s)=>this.onChange(s, "option", "notice") } />
|
||||
</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab" onClick={ ()=>this.onClick('notice') }>
|
||||
<div className="more" style={{ 'cursor': 'pointer' }}>
|
||||
<div>查看全部消息</div>
|
||||
<span className="desc">简悦会不定期发送一些消息,包括:新的插件上线、新的适配站点上线、修复 Bug 等</span>
|
||||
<span className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
205
src/module/notice.jsx
Normal file
@ -0,0 +1,205 @@
|
||||
console.log( "=== simpread notice load ===" )
|
||||
|
||||
import {storage} from 'storage';
|
||||
import th from 'theme';
|
||||
import * as ss from 'stylesheet';
|
||||
import * as puplugin from 'puplugin';
|
||||
import * as watch from 'watch';
|
||||
import Button from 'button';
|
||||
|
||||
/**
|
||||
* Write storage
|
||||
*
|
||||
* @param {func} callback
|
||||
*/
|
||||
const write = ( callback ) => {
|
||||
storage.Write( () => {
|
||||
console.log( "current notice is ", storage.notice )
|
||||
watch.SendMessage( "option", true );
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
|
||||
export default class Notice extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
is_update: false,
|
||||
step: 20,
|
||||
};
|
||||
|
||||
static propsType = {
|
||||
step: React.PropTypes.number,
|
||||
is_update: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
items: [],
|
||||
detail: "",
|
||||
failed: false,
|
||||
|
||||
total: 0,
|
||||
page : 1,
|
||||
}
|
||||
|
||||
onClick( event, id ) {
|
||||
const markdown = puplugin.Plugin( "markdown" ),
|
||||
converter = new markdown.default.Converter(),
|
||||
obj = this.state.items.filter( item => item.id == id ),
|
||||
item = obj[0],
|
||||
html = converter.makeHtml( item.content ),
|
||||
tmpl = `<div class="preview">
|
||||
<div class="title">${item.title}</div>
|
||||
<div class="desc">
|
||||
<span style="background-color: ${item.category.color}" class="category">${item.category.name}</span>
|
||||
<span class="date">发布于 ${item.date}</span>
|
||||
</div>
|
||||
<sr-rd-content>${html}</sr-rd-content>
|
||||
</div>`;
|
||||
$( "notice .detail" ).addClass( "simpread-theme-root" ).html( tmpl );
|
||||
}
|
||||
|
||||
onReadallClick() {
|
||||
new Notify().Render( "snackbar", "是否将全部消息标记为已读?", "确认", () => {
|
||||
storage.notice.read = [];
|
||||
this.state.items.forEach( ( item, idx ) => { storage.notice.read.push( idx + 1 ) });
|
||||
write( ()=> {
|
||||
new Notify().Render( "已全部设置为已读,3 秒后自动刷新本页..." );
|
||||
setTimeout( ()=>location.reload(), 3000 )
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onLoadmoreClick() {
|
||||
this.setState({ page: this.state.page + 1 });
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if ( this.props.is_update ) {
|
||||
$.ajax( storage.notice_service.message )
|
||||
.done( result => {
|
||||
storage.Notice( undefined, result.notice );
|
||||
storage.notice.latest = result.notice.length;
|
||||
write();
|
||||
this.setState({ items: result.notice, total: Math.ceil( result.notice.length / this.props.step ) });
|
||||
})
|
||||
.fail( ( jqXHR, textStatus, errorThrown ) => {
|
||||
this.setState({ failed: true });
|
||||
});
|
||||
} else {
|
||||
storage.Notice( result => {
|
||||
this.setState({ items: result.notice, total: Math.ceil( result.notice.length / this.props.step ) });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
th.Change( "pixyii" );
|
||||
}
|
||||
|
||||
render() {
|
||||
let dom;
|
||||
const items = this.state.items.slice( 0, this.state.page * this.props.step );
|
||||
if ( this.state.failed ) {
|
||||
dom =
|
||||
<notice>
|
||||
<div className="failed">
|
||||
<div dangerouslySetInnerHTML={{__html: `<svg viewBox="0 0 1024 1024" version="1.1" p-id="2377" width="120" height="120"><defs><style type="text/css"></style></defs><path d="M512.041444 243.968477c-148.07343 0-268.049942 120.022561-268.049942 268.051989 0 147.988496 119.976512 268.053012 268.049942 268.053012 147.987472 0 268.051989-120.064516 268.051989-268.053012C780.093433 363.991038 660.028916 243.968477 512.041444 243.968477zM556.716946 690.765453 467.367989 690.765453l0-89.351004 89.348957 0L556.716946 690.765453zM556.716946 556.652989 467.367989 556.652989l0-223.37751 89.348957 0L556.716946 556.652989zM780.093433 65.22349 243.991502 65.22349c-98.774631 0-178.700985 80.101339-178.700985 178.744987l0 536.105001c0 98.730629 79.925331 178.700985 178.700985 178.700985l536.102954 0c98.600669 0 178.615027-79.969333 178.615027-178.700985L958.709483 243.968477C958.70846 145.32483 878.695125 65.22349 780.093433 65.22349zM869.44546 735.397976c0 73.994248-60.033281 134.069485-134.027529 134.069485L288.667004 869.467461c-73.994248 0-134.115534-60.075237-134.115534-134.069485L154.55147 288.599977c0-73.994248 60.121286-133.939525 134.115534-133.939525l446.750927 0c73.994248 0 134.027529 59.945277 134.027529 133.939525L869.44546 735.397976z" p-id="2378" fill="#16666f"></path></svg>` }}></div>
|
||||
<span>消息中心暂时无法访问,请稍后再试!</span>
|
||||
</div>
|
||||
</notice>;
|
||||
} else if ( this.state.items.length == 0 ) {
|
||||
dom =
|
||||
<notice>
|
||||
<div className="loading" dangerouslySetInnerHTML={{__html: `<svg width="105" height="105" viewBox="0 0 105 105" fill="#16666f"> <circle cx="12.5" cy="12.5" r="12.5"> <animate attributeName="fill-opacity" begin="0s" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="12.5" cy="52.5" r="12.5" fill-opacity=".5"> <animate attributeName="fill-opacity" begin="100ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="52.5" cy="12.5" r="12.5"> <animate attributeName="fill-opacity" begin="300ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="52.5" cy="52.5" r="12.5"> <animate attributeName="fill-opacity" begin="600ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="92.5" cy="12.5" r="12.5"> <animate attributeName="fill-opacity" begin="800ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="92.5" cy="52.5" r="12.5"> <animate attributeName="fill-opacity" begin="400ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="12.5" cy="92.5" r="12.5"> <animate attributeName="fill-opacity" begin="700ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="52.5" cy="92.5" r="12.5"> <animate attributeName="fill-opacity" begin="500ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> <circle cx="92.5" cy="92.5" r="12.5"> <animate attributeName="fill-opacity" begin="200ms" dur="1s" values="1;.2;1" calcMode="linear" repeatCount="indefinite"/> </circle> </svg>` }}></div>
|
||||
</notice>;
|
||||
} else if ( this.state.items.length > 0 ) {
|
||||
dom =
|
||||
<notice>
|
||||
<div>
|
||||
<div className="list controlbar">
|
||||
<Button type="raised" text="全部标记为已读"
|
||||
style={{ "margin": "0" }} width="100%"
|
||||
color="#fff" backgroundColor="#FF5252"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.onReadallClick() } />
|
||||
</div>
|
||||
<List list={ items } onClick={ ( e, id )=> this.onClick( e, id ) } />
|
||||
{ this.state.page < this.state.total &&
|
||||
<div className="list controlbar">
|
||||
<Button type="raised" text="加载更多"
|
||||
style={{ "margin": "0" }} width="100%"
|
||||
color="#fff" backgroundColor="#16666f"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.onLoadmoreClick() } />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className="detail">
|
||||
<div className="empty">
|
||||
<span className="icon"></span>
|
||||
<span>当前未查看任何内容</span>
|
||||
</div>
|
||||
</div>
|
||||
</notice>;
|
||||
}
|
||||
return (
|
||||
<div>{ dom }</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class List extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
list: [],
|
||||
onClick: undefined,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
list : React.PropTypes.array,
|
||||
onClick: React.PropTypes.func,
|
||||
}
|
||||
|
||||
onClick( event, id ) {
|
||||
$( "list" ).removeClass( "selected" );
|
||||
$( `list[id="${id}"]` ).addClass( "selected" );
|
||||
this.props.onClick( event, id );
|
||||
}
|
||||
|
||||
onActive( event, id ) {
|
||||
$( `list[id="${id}"]` ).addClass( "active" );
|
||||
storage.notice.read.push( id );
|
||||
write( () => new Notify().Render( "已设置为已读。" ) );
|
||||
}
|
||||
|
||||
render() {
|
||||
const list = this.props.list.map( item => {
|
||||
const active = storage.notice.read.findIndex( value=>value==item.id ) != -1 ? " active" : "";
|
||||
return (
|
||||
<list id={ item.id } className={ "md-waves-effect" + active } onClick={ e => this.onClick( e, item.id ) }>
|
||||
<div className="title">{ item.title }</div>
|
||||
<span>
|
||||
<span style={{ backgroundColor: item.category.color }} className="category">{ item.category.name }</span>
|
||||
<span className="date">{ item.date }</span>
|
||||
</span>
|
||||
{ active == "" &&
|
||||
<div className="meta">
|
||||
<Button type="raised" text="" shape="circle"
|
||||
icon={ ss.IconPath( "read_icon" ) }
|
||||
tooltip={{ text: "已读" }}
|
||||
color="#fff" backgroundColor="transparent"
|
||||
waves="md-waves-effect md-waves-circle" hoverColor="transparent"
|
||||
onClick={ e => this.onActive( e, item.id ) }
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</list>
|
||||
)
|
||||
});
|
||||
return (
|
||||
<div className="list">
|
||||
{ list }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
console.log( "===== simpread option labs load =====" )
|
||||
console.log( "===== simpread option plugins load =====" )
|
||||
|
||||
import {storage} from 'storage';
|
||||
import * as run from 'runtime';
|
||||
@ -180,14 +180,13 @@ export default class PluginsOpt extends React.Component {
|
||||
storage.option.plugins.forEach( id => {
|
||||
run.Install( id, undefined, result => {
|
||||
if ( !result ) {
|
||||
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
|
||||
return;
|
||||
new Notify().Render( 2, id + " 获取失败,请稍后再试。" );
|
||||
}
|
||||
count++;
|
||||
if ( storage.plugins[id].version != result.version ) {
|
||||
storage.plugins[result.id] = result;
|
||||
is_update = true;
|
||||
}
|
||||
if ( storage.plugins[id].version != result.version ) {
|
||||
storage.plugins[result.id] = result;
|
||||
is_update = true;
|
||||
}
|
||||
count == storage.option.plugins.length && complete();
|
||||
});
|
||||
});
|
||||
@ -204,6 +203,7 @@ export default class PluginsOpt extends React.Component {
|
||||
}
|
||||
|
||||
import() {
|
||||
let newPlugins = {};
|
||||
if ( storage.option.plugins.length == 0 ) {
|
||||
new Notify().Render( "当前配置文件没有任何插件。" );
|
||||
return;
|
||||
@ -214,23 +214,20 @@ export default class PluginsOpt extends React.Component {
|
||||
storage.option.plugins.forEach( id => {
|
||||
run.Install( id, undefined, result => {
|
||||
if ( !result ) {
|
||||
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
|
||||
return;
|
||||
}
|
||||
new Notify().Render( 2, id + " 获取失败,请稍后再试。" );
|
||||
} else newPlugins[result.id] = result;
|
||||
count++;
|
||||
storage.plugins[result.id] = result;
|
||||
count == storage.option.plugins.length && complete();
|
||||
});
|
||||
});
|
||||
}});
|
||||
const complete = () => {
|
||||
storage.Plugins( result => {
|
||||
storage.option.plugins = Object.keys( storage.plugins );
|
||||
storage.Write( () => {
|
||||
new Notify().Render( "已从配置文件导入完毕。" );
|
||||
this.setState({ plugins: Object.values( storage.plugins ) });
|
||||
});
|
||||
}, storage.plugins );
|
||||
}, newPlugins );
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,36 +256,49 @@ export default class PluginsOpt extends React.Component {
|
||||
<div className="label">管理</div>
|
||||
<div className="lab">
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<div className="version-tips" data-hits="pluginconfig" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="从配置文件导入插件" width="100%"
|
||||
tooltip={{ text: "注意:本操作并不能更新本地插件。" }}
|
||||
icon={ ss.IconPath( "import_icon" ) }
|
||||
color="#fff" backgroundColor="#00BCD4"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.import() } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="pluginsite" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="查看并获取更多的插件" width="100%"
|
||||
fontIcon={`<i class="fas fa-external-link-square-alt"></i>`}
|
||||
color="#fff" backgroundColor="#00BCD4"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.addmore() } />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<div className="version-tips" data-hits="pluginupdate" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="更新本地全部插件" width="100%"
|
||||
icon={ ss.IconPath( "update_icon" ) }
|
||||
color="#fff" backgroundColor="#FF5252"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.update() } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="pluginclear" style={{ display: 'inline-flex', width: '50%' }}>
|
||||
<Button type="raised" text="清除本地全部插件" width="100%"
|
||||
icon={ ss.IconPath( "clear_icon" ) }
|
||||
color="#fff" backgroundColor="#757575"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.clear() } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">已安装</div>
|
||||
<div className="label">
|
||||
<span>{ this.state.plugins.length == 0 ? "" : "已安装 " + this.state.plugins.length + " 个插件 " }</span>
|
||||
{ this.state.plugins.length > 5 && <a target="_blank" style={{ color:' #FF5252', borderBottom: '2px dotted', fontSize: '10px', fontWeight: 'bold' }}>过多的插件会使进入阅读模式变慢,建议不要超过 6 个</a> }
|
||||
</div>
|
||||
<div className="version-tips" data-hits="pluginmange">
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Cards plugins={ this.state.plugins } onChange={ t=>this.onChange(t) } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
console.log( "=== simpread option modals ===" )
|
||||
console.log( "=== simpread option setting ===" )
|
||||
|
||||
import FocusOpt from 'focusopt';
|
||||
import ReadOpt from 'readopt';
|
||||
@ -25,14 +25,14 @@ let callback;
|
||||
*/
|
||||
class Modals extends React.Component {
|
||||
|
||||
// close modals
|
||||
// close setting
|
||||
close( restore = rollback() ) {
|
||||
dia.Close();
|
||||
}
|
||||
|
||||
// save modals focus option
|
||||
// save setting focus option
|
||||
save() {
|
||||
console.log( "modals click submit button.", storage.current )
|
||||
console.log( "setting click submit button.", storage.current )
|
||||
watch.Verify( ( state, result ) => {
|
||||
if ( state ) {
|
||||
console.log( "watch.Lock()", result );
|
||||
@ -1,4 +1,4 @@
|
||||
console.log( "=== simpread option siteeditor ===" )
|
||||
console.log( "=== simpread option siteeditor load ===" )
|
||||
|
||||
import { storage } from 'storage';
|
||||
import * as watch from 'watch';
|
||||
@ -39,14 +39,7 @@ class SiteEditor extends React.Component {
|
||||
|
||||
delete() {
|
||||
console.log( "siteeditor click delete button.", storage.current.site )
|
||||
/*
|
||||
if ( site.target != "local" ) {
|
||||
new Notify().Render( 2, `只能删除 <a href='http://ksria.com/simpread/docs/#/FAQ#%E6%97%A0%E6%B3%95%E5%88%A0%E9%99%A4%E5%BD%93%E5%89%8D%E7%AB%99%E7%82%B9' target='_blank'>本地站点</a> ,如需要请使用 站点管理器 删除。` );
|
||||
return;
|
||||
}
|
||||
*/
|
||||
new Notify().Render( "是否删除当前适配站点?", "删除", () => {
|
||||
//site.target != "local" ? new Notify().Render( 3, `<a href='http://ksria.com/simpread/docs/#/FAQ#%E6%97%A0%E6%B3%95%E5%88%A0%E9%99%A4%E5%BD%93%E5%89%8D%E7%AB%99%E7%82%B9' target='_blank'>无法删除</a> 当前站点,如不想显示请加入黑名单。` ) :
|
||||
site.name.startsWith( "tempread::" ) ? new Notify().Render( 2, `当前站点为自动识别,无误删除。` ) :
|
||||
storage.pr.Deletesite( storage.current.site.target, site.url, result => {
|
||||
if ( result == -1 ) new Notify().Render( 2, `此站已被删除,请勿重复操作。` );
|
||||
@ -80,7 +73,8 @@ class SiteEditor extends React.Component {
|
||||
} else if ( site.include.trim() == "" ) {
|
||||
new Notify().Render( 2, "高亮区域不能为空。" );
|
||||
} else {
|
||||
storage.pr.Updatesite( storage.current.site.target, storage.current.url, [ site.url, storage.pr.Cleansite(site) ]);
|
||||
// changed storage.current.site.target to 'local'
|
||||
storage.pr.Updatesite( 'local', storage.current.url, [ site.url, storage.pr.Cleansite(site) ]);
|
||||
storage.Writesite( storage.pr.sites, () => {
|
||||
new Notify().Render( 0, "更新成功,页面刷新后生效!" );
|
||||
watch.SendMessage( "site", true );
|
||||
@ -101,8 +95,12 @@ class SiteEditor extends React.Component {
|
||||
|
||||
render() {
|
||||
site = { ...storage.pr.current.site };
|
||||
if ( storage.pr.state == "temp" ) {
|
||||
storage.pr.dom && ( site.include = storage.pr.dom.outerHTML.replace( storage.pr.dom.innerHTML, "" ).replace( /<\/\S+>$/i, "" ));
|
||||
if ( storage.pr.state == "temp" && storage.pr.dom ) {
|
||||
site.name = site.name.replace( "tempread::", "" );
|
||||
let include = storage.pr.Utils().dom2Xpath( storage.pr.dom );
|
||||
if ( include != "" ) {
|
||||
site.include = `[[\`${include}\`]]`;
|
||||
} else site.include = storage.pr.dom.outerHTML.replace( storage.pr.dom.innerHTML, "" ).replace( /<\/\S+>$/i, "" )
|
||||
}
|
||||
return (
|
||||
<dia.Dialog>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
console.log( "===== simpread option sites load =====" )
|
||||
|
||||
import {storage} from 'storage';
|
||||
import {browser} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import {storage} from 'storage';
|
||||
import {browser} from 'browser';
|
||||
import * as msg from 'message';
|
||||
import * as watch from 'watch';
|
||||
import * as ss from 'stylesheet';
|
||||
import * as ss from 'stylesheet';
|
||||
|
||||
import TextField from 'textfield';
|
||||
import Button from 'button';
|
||||
import TextField from 'textfield';
|
||||
import Button from 'button';
|
||||
|
||||
class Card extends React.Component {
|
||||
|
||||
@ -103,6 +103,23 @@ class Cards extends React.Component {
|
||||
|
||||
export default class SitesOpts extends React.Component {
|
||||
|
||||
newsites() {
|
||||
const notify = new Notify().Render({ content: "数据同步中,请稍等...", state: "loading" });
|
||||
storage.GetRemote( "remote", ( result, error ) => {
|
||||
notify.complete();
|
||||
if ( !error ) {
|
||||
const count = storage.pr.Addsites( result );
|
||||
storage.Writesite( storage.pr.sites, () => {
|
||||
watch.SendMessage( "site", true );
|
||||
count == 0 ? new Notify().Render( "适配列表已同步至最新版本。" ) : new Notify().Render( 0, `适配列表已同步成功,本次新增 ${ count } 个站点,2 秒后自动自动刷新。` );
|
||||
count > 0 && setTimeout( ()=>location.reload(), 2000 );
|
||||
});
|
||||
} else {
|
||||
new Notify().Render( 3, `同步时发生了一些问题,并不会影响本地配置文件,请稍后再试!` );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onClick( state ) {
|
||||
state == "sitemgr" && ( location.href = location.origin + "/options/sitemgr.html" );
|
||||
}
|
||||
@ -114,6 +131,7 @@ export default class SitesOpts extends React.Component {
|
||||
}
|
||||
|
||||
origins( type ) {
|
||||
/*
|
||||
if ( type == "origins" ) {
|
||||
storage.GetRemote( "origins", ( result, error ) => {
|
||||
if ( error ) new Notify().Render( 2, "获取失败,请稍后重新加载。" );
|
||||
@ -124,7 +142,9 @@ export default class SitesOpts extends React.Component {
|
||||
new Notify().Render( "官方源加载成功。" );
|
||||
}
|
||||
});
|
||||
} else if ( type == "import" ) {
|
||||
} else
|
||||
*/
|
||||
if ( type == "import" ) {
|
||||
new Notify().Render( "snackbar", "导入后会覆盖掉原来的第三方适配列表,请问是否覆盖?", "确认", () => {
|
||||
const urls = this.props.option.origins.filter( item => {
|
||||
return item.trim() != "" && item.trim().startsWith( "http" ) && item.trim().endsWith( ".json" )
|
||||
@ -305,17 +325,29 @@ export default class SitesOpts extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div id="labs" style={{ width: '100%' }}>
|
||||
<div className="version-tips" data-hits="newsites">
|
||||
<div className="label">官方主适配源 <a target="_blank" href="https://simpread.ksria.cn/sites/" style={{ color:' #FF5252', borderBottom: '2px dotted', fontSize: '10px', fontWeight: 'bold', cursor: 'pointer' }}>共计 { storage.simpread.sites.length } 类</a></div>
|
||||
<div className="lab">
|
||||
<Button type="raised" text="手动同步适配列表" width="100%"
|
||||
icon={ ss.IconPath( "update_icon" ) }
|
||||
color="#fff" backgroundColor="rgb(103, 58, 183)"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.newsites() } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">第三方适配源</div>
|
||||
<div ref="origins" style={{ 'padding-top': '10px', 'margin-bottom': '8px;' }} className="lab">
|
||||
<div className="version-tips" data-hits="customsites">
|
||||
<TextField
|
||||
multi={ true } rows={8}
|
||||
multi={ true } rows={4}
|
||||
placeholder="仅支持 URL 地址,每行一个。"
|
||||
value={ ( this.props.option.origins||[] ).join( "\n" ) }
|
||||
onChange={ ()=>this.changeOrigins() }
|
||||
/>
|
||||
<div style={{ "display": "flex" }}>
|
||||
<Button type="raised" text="加载第三方适配列表"
|
||||
width="100%" style={{ "margin": "0" }}
|
||||
width="100%" style={{ "display": "none", "margin": "0" }}
|
||||
color="#fff" backgroundColor="#4CAF50"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.origins( "origins" ) } />
|
||||
@ -330,16 +362,11 @@ export default class SitesOpts extends React.Component {
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.origins( "clear" ) } />
|
||||
</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} onClick={ ()=>this.onClick('sitemgr') }>
|
||||
<div className="more">
|
||||
<div>站点管理器</div>
|
||||
<span className="desc">可以编辑全部的适配站点,包括:官方适配源、站点集市适配源、第三方适配源、自定义适配源。</span>
|
||||
<span className="arrow" style={{ 'bottom': '13px' }}></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">管理</div>
|
||||
<div className="version-tips" data-hits="personsites">
|
||||
<div className="label">站点集市 <a target="_blank" href="https://simpread.ksria.cn/sites/" style={{ color:' #FF5252', borderBottom: '2px dotted', fontSize: '10px', fontWeight: 'bold', cursor: 'pointer' }}>共计 { storage.pr.sites.person.length } 类</a></div>
|
||||
<div className="lab">
|
||||
<div style={{ display: 'inline-flex', width: '100%' }}>
|
||||
<Button type="raised" text="打开「站点集市」" width="100%"
|
||||
@ -353,11 +380,22 @@ export default class SitesOpts extends React.Component {
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.clear() } />
|
||||
</div>
|
||||
|
||||
<div style={{ 'padding-top': '10px' }}>
|
||||
<Cards onChange={ t=>this.onChange(t) } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="label">已安装</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Cards onChange={ t=>this.onChange(t) } />
|
||||
<div className="version-tips" data-hits="sitemgr">
|
||||
<div className="label">站点管理器</div>
|
||||
<div style={{ 'padding-top': '10px', 'position': 'relative' }} className="lab" onClick={ ()=>this.onClick('sitemgr') }>
|
||||
<div className="more" style={{ 'cursor': 'pointer' }}>
|
||||
<div>可以管理全部的适配站点</div>
|
||||
<span className="desc">包括:官方适配源、第三方适配源、站点集市适配源、自定义适配源。</span>
|
||||
<span className="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -104,17 +104,21 @@ export default class Unrdist extends React.Component {
|
||||
items = this.state.items.slice( 0, this.state.page * this.props.step ),
|
||||
content = this.state.items && this.state.items.length > 0 ?
|
||||
<div>
|
||||
<div className="version-tips" data-hits="laterlist">
|
||||
<List acIconWaves="md-waves-effect md-waves-circle"
|
||||
acItemWaves="md-waves-effect"
|
||||
title={ this.state.title } contentStyle={ content_style }
|
||||
items={ items } actionItems={ conf.actionItems }
|
||||
priBgColor ="#E1BEE7"
|
||||
onAction={ (e,i,t,d)=>this.onAction(e,i,t,d) } />
|
||||
</div>
|
||||
<div className="version-tips" data-hits="latermore">
|
||||
<Button type="raised" width="100%"
|
||||
text={ disable ? "加载完毕" : "加载更多" } disable={ disable }
|
||||
color="#fff" backgroundColor="rgb(156, 39, 176)"
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>this.onClick() } />
|
||||
</div>
|
||||
</div>
|
||||
: <div style={ style.root }>
|
||||
<span style={ style.icon }></span>
|
||||
|
||||
@ -3,10 +3,11 @@ console.log( "===== simpread option welcome page load =====" )
|
||||
import 'carous_css';
|
||||
import 'carousel';
|
||||
|
||||
import Button from 'button';
|
||||
import Button from 'button';
|
||||
|
||||
import * as ss from 'stylesheet';
|
||||
import {br} from 'browser';
|
||||
import * as ss from 'stylesheet';
|
||||
import {br} from 'browser';
|
||||
import * as msg from 'message';
|
||||
|
||||
const welcbgcls = "welcome",
|
||||
welcbgclsjq = `.${welcbgcls}`,
|
||||
@ -110,12 +111,11 @@ class Welcome extends React.Component {
|
||||
nextClick() {
|
||||
if ( curidx != max ) {
|
||||
$( '.carousel.carousel-slider' ).carousel( "next" );
|
||||
} else {
|
||||
exit();
|
||||
}
|
||||
} else this.closeClick();
|
||||
}
|
||||
|
||||
closeClick() {
|
||||
window.dispatchEvent( new CustomEvent( msg.MESSAGE_ACTION.welcome_close, { detail: { first: this.props.first, version: this.props.version }}));
|
||||
exit();
|
||||
}
|
||||
|
||||
@ -167,9 +167,9 @@ class Welcome extends React.Component {
|
||||
<img src={ ss.IconPath( "welcome" )} style={ style.img }/>
|
||||
<h2 style={{ ...style.h2, ...{ 'margin-bottom': 0 } }}>{ this.props.first ? "欢迎使用 简悦": "简悦 已升至最新版" }</h2>
|
||||
<div style={ style.desc }>
|
||||
{ br.isFirefox() ? "Chrome 好评率超过 99% 的阅读模式现已来到 Firefox。" : "让你瞬间进入沉浸式阅读的 Chrome 扩展,类似 Safari 的阅读模式。" }<br/>
|
||||
去掉干扰元素,提升阅读体验,<strong style={ style.strong }>「简」</strong>单阅读,愉<strong style={ style.strong }>「悦」</strong>心情。<br/>
|
||||
为了达到 <strong style={ style.strong }>「完美」</strong> 的阅读模式,简悦适配了 <strong style={ style.strong }><a target="_blank" href="https://simpread.ksria.cn/sites/">数百种类型</a></strong> 的网站。
|
||||
{ br.isFirefox() ? "Chrome 好评率超过 99% 的阅读模式现已来到 Firefox" : "让你瞬间进入沉浸式阅读的 Chrome 扩展,类似 Safari 的阅读模式" }<br/>
|
||||
去掉干扰元素,提升阅读体验,<strong style={ style.strong }>「简」</strong>单阅读,愉<strong style={ style.strong }>「悦」</strong>心情<br/>
|
||||
为了达到 <strong style={ style.strong }>「完美」</strong> 的阅读模式,简悦适配了 <strong style={ style.strong }><a target="_blank" href="https://simpread.ksria.cn/sites/">数百种类型</a></strong> 的网站
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@ -180,10 +180,10 @@ class Welcome extends React.Component {
|
||||
<img src={ ss.IconPath( "welcome-mode" )} style={ style.img }/>
|
||||
<h2 style={ style.h2 }>阅读模式 与 聚焦模式</h2>
|
||||
<div style={ style.desc }>
|
||||
阅读模式 → <strong>独有功能</strong>,自动提取适配页面的标题、描述、正文、媒体等资源。<br/>
|
||||
支持 <a target="_blank" href="http://ksria.com/simpread/docs/#/临时阅读模式">临时阅读模式</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/主动适配阅读模式">主动适配模式</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎">智能适配模式</a>
|
||||
· <a target="_blank" href="http://ksria.com/simpread/docs/#/论坛类页面及分页">论坛类页面 / 分页</a>。<br/>
|
||||
<a target="_blank" href="http://ksria.com/simpread/docs/#/聚焦模式">聚焦模式</a> → 高亮鼠标所在的文章段落,不改变当前页面的结构。<br/>
|
||||
阅读模式 → <strong>独有功能</strong>,自动提取适配页面的标题、描述、正文、媒体等资源<br/>
|
||||
支持 <a target="_blank" href="http://ksria.com/simpread/docs/#/手动框选">手动框选</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/主动适配阅读模式">主动适配模式</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎">智能适配模式</a>
|
||||
· <a target="_blank" href="http://ksria.com/simpread/docs/#/论坛类页面及分页">论坛类页面 / 分页</a><br/>
|
||||
<a target="_blank" href="http://ksria.com/simpread/docs/#/聚焦模式">聚焦模式</a> → 高亮鼠标所在的文章段落,不改变当前页面的结构<br/>
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -196,7 +196,7 @@ class Welcome extends React.Component {
|
||||
<div style={ style.desc }>
|
||||
全新的 <b>词法分析引擎</b><sup>2.0</sup>,简悦可以识别出 <a target="_blank" href="http://ksria.com/simpread/docs/#/TXT-阅读器">TXT</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=markdown-识别">Markdown</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=latex-识别">LaTeX</a> · <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=代码段的高亮">代码段</a><br/>
|
||||
Wordpress · Hexo · Ghost · Discuz 等博客 / 论坛的页面了!<br/>
|
||||
甚至,只要是结构良好的页面,(无需适配)自动生成阅读模式,详细 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎">请看这里</a> 。
|
||||
甚至,只要是结构良好的页面,(无需适配)自动生成阅读模式,详细 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎">请看这里</a>
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -207,9 +207,9 @@ class Welcome extends React.Component {
|
||||
<img src="http://sr.ksria.cn/welcome-service-v2.png?201806111215" style={ style.img }/>
|
||||
<h2 style={ style.h2 }>连接你的生产力工具</h2>
|
||||
<div style={ style.desc }>
|
||||
支持下载 HTML · PDF · Markdown · PNG · <a target="_blank" href="http://ksria.com/simpread/docs/#/%E5%8F%91%E9%80%81%E5%88%B0-Kindle">Epub</a> 到本地 以及 发送到 <a target="_blank" href="http://ksria.com/simpread/docs/#/%E5%8F%91%E9%80%81%E5%88%B0-Kindle">Kindle</a>。<br/>
|
||||
支持输出到 Dropbox · 印象笔记 · Evernote · Onenote · Google 云端硬盘。<br/>
|
||||
发送页面链接到 Pocket · Instapaper · Linnk,详细 <a target="_blank" href="http://ksria.com/simpread/docs/#/%E6%8E%88%E6%9D%83%E6%9C%8D%E5%8A%A1">请看这里</a> 。
|
||||
支持下载 HTML · PDF · Markdown · PNG · <a target="_blank" href="http://ksria.com/simpread/docs/#/发送到-Epub">Epub</a> 到本地 以及 发送到 <a target="_blank" href="http://ksria.com/simpread/docs/#/发送到-Kindle">Kindle</a><br/>
|
||||
支持输出到 坚果云 · 语雀 · Dropbox · 印象笔记 · Evernote · Onenote · Google 云端硬盘<br/>
|
||||
发送页面链接到 <a target="_blank" href="http://ksria.com/simpread/docs/#/稍后读">稍后读</a> · Pocket · Instapaper · Linnk,详细 <a target="_blank" href="http://ksria.com/simpread/docs/#/导出到生产力工具">请看这里</a>
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -220,9 +220,9 @@ class Welcome extends React.Component {
|
||||
<img src={ ss.IconPath( "welcome-custom" )} style={ style.img }/>
|
||||
<h2 style={ style.h2 }>站点编辑器 · 站点适配源 · 站点管理器</h2>
|
||||
<div style={ style.desc }>
|
||||
页面上任意元素均可隐藏,更支持编程,详细请看 <a href="http://ksria.com/simpread/docs/#/%E7%AB%99%E7%82%B9%E7%BC%96%E8%BE%91%E5%99%A8" target="_blank">站点编辑器</a><br/>
|
||||
更灵活、社区化的多种 <a href="http://ksria.com/simpread/docs/#/%E7%AB%99%E7%82%B9%E9%80%82%E9%85%8D%E6%BA%90" target="_blank">站点适配源</a>。<br/>
|
||||
内置了 <a href="http://ksria.com/simpread/docs/#/%E7%AB%99%E7%82%B9%E7%AE%A1%E7%90%86%E5%99%A8" target="_blank">站点管理器</a>,方便管理全部的适配站点。
|
||||
页面上任意元素均可隐藏,更支持编程,详细请看 <a href="http://ksria.com/simpread/docs/#/站点编辑器" target="_blank">站点编辑器</a><br/>
|
||||
更灵活、社区化的多种 <a href="http://ksria.com/simpread/docs/#/站点适配源" target="_blank">站点适配源</a><br/>
|
||||
内置了 <a href="http://ksria.com/simpread/docs/#/站点管理器" target="_blank">站点管理器</a>,方便管理全部的适配站点
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -234,7 +234,7 @@ class Welcome extends React.Component {
|
||||
<h2 style={ style.h2 }>全新的控制栏面板</h2>
|
||||
<div style={ style.desc }>
|
||||
「告别」传统、单一的控制栏,全部功能「一览无余」<br/>
|
||||
主题、字体样式、大小、版面布局更改一键完成。<br/>
|
||||
主题、字体样式、大小、版面布局更改一键完成<br/>
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -245,9 +245,9 @@ class Welcome extends React.Component {
|
||||
<img src="http://sr.ksria.cn/welcome-plugins.png" style={ style.img }/>
|
||||
<h2 style={ style.h2 }>插件系统</h2>
|
||||
<div style={ style.desc }>
|
||||
<a target="_blank" href="https://simpread.ksria.cn/plugins/details/kw36BtjGu0">字数统计</a> · <a target="_blank" href="https://simpread.ksria.cn/plugins/details/VQOZdNET2d">点击查看大图(Lightbox)</a> · <a target="_blank" href="https://simpread.ksria.cn/plugins/details/ohnTKVHz4a">划词翻译</a> 一个不能少。 <br/>
|
||||
可以使用 JavaScript 编写基于「简悦」的插件了,详细说明请看 <a target="_blank" href="http://ksria.com/simpread/docs/#/%E6%8F%92%E4%BB%B6%E7%B3%BB%E7%BB%9F">说明文档</a><br/>
|
||||
现在就安装适合你的插件吧 → <a target="_blank" href="https://simpread.ksria.cn/plugins/">插件中心</a> 。
|
||||
<a target="_blank" href="https://simpread.ksria.cn/plugins/details/kw36BtjGu0">字数统计</a> · <a target="_blank" href="https://simpread.ksria.cn/plugins/details/klGUASLasg">代码段增强</a> · <a target="_blank" href="https://simpread.ksria.cn/plugins/details/VQOZdNET2d">点击查看大图(Lightbox)</a> · <a target="_blank" href="https://simpread.ksria.cn/plugins/details/ohnTKVHz4a">划词翻译</a> 一个不能少 <br/>
|
||||
使用 JavaScript 编写基于简悦的插件,详细说明请看 <a target="_blank" href="http://ksria.com/simpread/docs/#/插件系统">说明文档</a><br/>
|
||||
现在就安装适合你的插件吧 → <a target="_blank" href="https://simpread.ksria.cn/plugins/">插件中心</a>
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
@ -260,12 +260,12 @@ class Welcome extends React.Component {
|
||||
<div style={ style.desc }>
|
||||
方便提交,让你的站点为数以万计的简悦用户使用<br/>
|
||||
官方主适配源、第三方适配源、站点集市适配源、自定义适配源一站式浏览<br/>
|
||||
现在就访问 <a target="_blank" href="https://simpread.ksria.cn/sites/">站点集市</a> 吧,看看有什么增加的新适配站点。
|
||||
现在就访问 <a target="_blank" href="https://simpread.ksria.cn/sites/">站点集市</a> 吧,看看有什么增加的新适配站点
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
|
||||
{ !first &&
|
||||
{ !first &&
|
||||
<div className="carousel-item" id="5005">
|
||||
<section style={ style.section }>
|
||||
<img src="http://sr.ksria.cn/welcome-puread-ii.png" style={ style.img }/>
|
||||
@ -278,6 +278,32 @@ class Welcome extends React.Component {
|
||||
</section>
|
||||
</div> }
|
||||
|
||||
{ !first && version == "1.1.3" &&
|
||||
<div className="carousel-item" id="1.1.3">
|
||||
<section style={ style.section }>
|
||||
<img src="http://sr.ksria.cn/welcome-newservice.png?201906301335" style={ style.img }/>
|
||||
<h2 style={ style.h2 }>导出服务又添新成员,更支持 WebDAV</h2>
|
||||
<div style={ style.desc }>
|
||||
期待已久的 <a target="_blank" href="http://ksria.com/simpread/docs/#/导出到生产力工具">语雀</a> 和 <a target="_blank" href="http://ksria.com/simpread/docs/#/坚果云">坚果云</a> 现已加入 <a target="_blank" href="http://ksria.com/simpread/docs/#/导出到生产力工具">导出服务</a> 豪华大礼包<br/>
|
||||
配置文件的同步也可使用 <a target="_blank" href="http://ksria.com/simpread/docs/#/同步">坚果云</a> 了<br/>
|
||||
不仅如此,只要是支持 <a target="_blank" href="http://ksria.com/simpread/docs/#/WebDAV">WebDAV</a> 的服务均可使用简悦的导出功能
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
|
||||
{ (( !first && version == "1.1.3" ) || version == "all" ) &&
|
||||
<div className="carousel-item" id="1.1.3">
|
||||
<section style={ style.section }>
|
||||
<img src="http://sr.ksria.cn/welcome-notice.png?20190630" style={ style.img }/>
|
||||
<h2 style={ style.h2 }>消息中心 · 帮助中心 · 新手入门</h2>
|
||||
<div style={ style.desc }>
|
||||
<a target="_blank" href="http://ksria.com/simpread/docs/#/消息中心">消息中心</a> 让沟通更加便利<br/>
|
||||
内置常用的文档说明、常见问题、及选项页全部功能说明的 <a target="_blank" href="http://ksria.com/simpread/docs/#/帮助中心">帮助中心</a><br/>
|
||||
功能太多,无从下手?<a target="_blank" href="http://ksria.com/simpread/guide">新手入门</a> 不再让新手望而却步
|
||||
</div>
|
||||
</section>
|
||||
</div> }
|
||||
|
||||
<div className="carousel-item" id="end">
|
||||
<section style={ style.section }>
|
||||
<img src={ ss.IconPath( "welcome-others" )} style={ style.img }/>
|
||||
@ -286,6 +312,10 @@ class Welcome extends React.Component {
|
||||
分享卡,右键菜单添加 「白名单 / 排除列表 / 黑名单」等<br/>
|
||||
详细说明请看 <a target="_blank" href="http://ksria.com/simpread/welcome/version_1.1.2.5005.html">更新日志</a>
|
||||
</div> }
|
||||
{ !first && version == "1.1.3" && <div style={ style.desc }>
|
||||
<a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=预加载机制">预加载</a> <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=延迟加载">延迟加载</a> <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=智能感知">智能感知</a> 与 <a target="_blank" href="http://ksria.com/simpread/docs/#/手动框选?id=二次确认">更便捷的手动框选</a> 等诸多新功能<br/>
|
||||
详细说明请看 <a target="_blank" href="http://ksria.com/simpread/welcome/version_1.1.3.html">更新日志</a>
|
||||
</div> }
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@ -316,10 +346,10 @@ class Welcome extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit()
|
||||
* Exit
|
||||
*/
|
||||
function exit() {
|
||||
$( welcbgclsjq ).velocity({ opacity: 0 }, { complete: ()=>{
|
||||
$( welcbgclsjq ).velocity({ opacity: 0 }, { complete: () => {
|
||||
ReactDOM.unmountComponentAtNode( $(welcbgclsjq)[0] );
|
||||
}});
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="property">Property</div>
|
||||
<div class="preview simpread-theme-root">
|
||||
<sr-read>
|
||||
<sr-rd-title>简悦 - 让你瞬间进入沉浸式阅读的扩展</sr-rd-title>
|
||||
<sr-rd-title>简悦 SimpRead - 为你提供「如杂志般沉浸式阅读体验」的扩展</sr-rd-title>
|
||||
<sr-rd-desc>为了达到完美的阅读模式这个小目标 ,我适配了 数百种类型 的网站,因此诞生了简悦。</sr-rd-desc>
|
||||
<sr-rd-content>
|
||||
<sr-blockquote>
|
||||
@ -82,7 +82,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span>简悦(SimpRead)- 让你瞬间进入沉浸式阅读的扩展。</span> <span> © 2017 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
<span>简悦 SimpRead - 为你提供「如杂志般沉浸式阅读体验」的扩展</span> <span> © 2017 - 2019 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
</div>
|
||||
<script src="../bundle/common.js"></script>
|
||||
<script src="../bundle/vendors.js"></script>
|
||||
|
||||
@ -123,7 +123,7 @@ function propertyRender() {
|
||||
<group className="lab">
|
||||
<h1>帮助</h1>
|
||||
<group>
|
||||
<p>如何自定义样式,详细 <a href="http://ksria.com/simpread/docs/#/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%B7%E5%BC%8F" target="_blank">请看这里</a><br/>
|
||||
<p>如何自定义样式,详细 <a href="http://ksria.com/simpread/docs/#/自定义样式" target="_blank">请看这里</a><br/>
|
||||
独乐乐不如众乐乐! <a href="https://github.com/Kenshin/simpread/issues/71" target="_blank">分享你的主题</a></p>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
18
src/options/notice.html
Normal file
@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>消息中心 - 简悦 · 选项页</title>
|
||||
</head>
|
||||
<body style="opacity: 0;">
|
||||
<div class="header"><div class="nav"></div><div class="title">消息中心</div></div>
|
||||
<div class="notice"></div>
|
||||
<div class="bottom">
|
||||
<span>简悦 SimpRead - 为你提供「如杂志般沉浸式阅读体验」的扩展</span> <span> © 2017 - 2019 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
</div>
|
||||
<script src="../bundle/common.js"></script>
|
||||
<script src="../bundle/vendors.js"></script>
|
||||
<script src="../bundle/notice.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
50
src/options/notice.js
Normal file
@ -0,0 +1,50 @@
|
||||
console.log( "==== simpread options page: notice load ====" )
|
||||
|
||||
import '../assets/css/simpread.css';
|
||||
import '../assets/css/options_page.css';
|
||||
import '../assets/css/options_notice.css';
|
||||
import 'notify_css';
|
||||
|
||||
import Velocity from 'velocity';
|
||||
import Button from 'button';
|
||||
import * as waves from 'waves';
|
||||
import * as tt from 'tooltip';
|
||||
|
||||
import Notice from 'notice';
|
||||
|
||||
import {storage} from 'storage';
|
||||
import * as ss from 'stylesheet';
|
||||
|
||||
/**
|
||||
* Entry
|
||||
*/
|
||||
storage.Read( () => {
|
||||
console.log( "simpread storage get success!", storage );
|
||||
navRender();
|
||||
noticeRender();
|
||||
tt.Render( "body" );
|
||||
waves.Render({ root: "body" });
|
||||
$( "body" ).velocity({ opacity: 1 }, { duration: 1000, complete: ()=> {
|
||||
$( "body" ).removeAttr( "style" );
|
||||
}});
|
||||
});
|
||||
|
||||
/**
|
||||
* navigation Render
|
||||
*/
|
||||
function navRender() {
|
||||
const navClick = () => {
|
||||
location.href = location.origin + "/options/options.html";
|
||||
};
|
||||
const button = <Button waves="md-waves-effect md-waves-circle" hoverColor="transparent" icon={ ss.IconPath( "gohome_icon" ) } onClick={ ()=>navClick() } />;
|
||||
ReactDOM.render( button, $( ".header .nav" )[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* notice Render
|
||||
*/
|
||||
function noticeRender() {
|
||||
let is_update = location.search == "?is_update=true" ? true : false;
|
||||
ReactDOM.render( <Notice is_update={ is_update } />, $( ".notice" )[0] );
|
||||
history.pushState( "", "", "/options/notice.html" );
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>选项页 - 简悦(SimpRead)</title>
|
||||
<title>选项页 - 简悦 SimpRead</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar"></div>
|
||||
@ -13,7 +13,7 @@
|
||||
<div class="tabscontainer"></div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span>简悦(SimpRead)- 让你瞬间进入沉浸式阅读的扩展。</span> <span> © 2017 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
<span>简悦 SimpRead - 为你提供「如杂志般沉浸式阅读体验」的扩展</span> <span> © 2017 - 2019 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
</div>
|
||||
<script src="../bundle/common.js"></script>
|
||||
<script src="../bundle/vendors.js"></script>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
console.log( "==== simpread options page load ====" )
|
||||
|
||||
import '../assets/css/options_page.css';
|
||||
import '../assets/css/option.css';
|
||||
import '../assets/css/setting.css';
|
||||
import 'notify_css';
|
||||
import 'intro_css';
|
||||
|
||||
import Velocity from 'velocity';
|
||||
import Notify from 'notify';
|
||||
@ -32,6 +33,7 @@ import AccountOps from 'accountopt';
|
||||
import About from 'about';
|
||||
import Unrdist from 'unrdist';
|
||||
import * as welc from 'welcome';
|
||||
import * as guide from 'guide';
|
||||
|
||||
import PureRead from 'puread';
|
||||
|
||||
@ -51,6 +53,21 @@ $( window ).scroll( (event) => {
|
||||
$( ".top" ).css( "transform", `translate3d(0px, ${offset}px, 0px)` );
|
||||
});
|
||||
|
||||
/**
|
||||
* Add event listenr
|
||||
*/
|
||||
window.addEventListener( msg.MESSAGE_ACTION.turn_tab, event => {
|
||||
const idx = event.detail.page;
|
||||
tabChange( idx );
|
||||
});
|
||||
|
||||
window.addEventListener( msg.MESSAGE_ACTION.welcome_close, event => {
|
||||
const { first, version } = event.detail;
|
||||
!first && new Notify().Render({ content: "是否查看新版本的入门指引?", action: "确认", cancel: "取消", callback: type => {
|
||||
type == "action" && guide.Start( version );
|
||||
}});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get tabsItemID from window.location.hash exist
|
||||
*/
|
||||
@ -67,6 +84,8 @@ browser.runtime.onMessage.addListener( function( request, sender, sendResponse )
|
||||
exp[id].Accesstoken( uri );
|
||||
} else if ( id == "yinxiang" ) {
|
||||
exp.evernote.Accesstoken( uri );
|
||||
} else if ( uri.indexOf( "state=yuque_authorize" ) > 0 ) {
|
||||
exp.yuque.Accesstoken( uri );
|
||||
} else {
|
||||
id.startsWith( "http://ksria.com/simpread/auth.html?" ) &&
|
||||
exp.onenote.Accesstoken( uri );
|
||||
@ -75,10 +94,17 @@ browser.runtime.onMessage.addListener( function( request, sender, sendResponse )
|
||||
});
|
||||
|
||||
/**
|
||||
* Entry:
|
||||
* - storage get data form chrome storage
|
||||
* - waves.Render()
|
||||
* - tooltip.Render()
|
||||
* Change tab
|
||||
*
|
||||
* @param {number} tab index
|
||||
*/
|
||||
function tabChange( idx ) {
|
||||
conf.tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
|
||||
mainRender( idx );
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry
|
||||
*/
|
||||
storage.Read( first => {
|
||||
console.log( "simpread storage get success!", storage.focus, storage.read, first );
|
||||
@ -89,15 +115,31 @@ storage.Read( first => {
|
||||
navRender();
|
||||
vernotify( first );
|
||||
mainRender( tabsItemID );
|
||||
setTimeout(() => noticeRender(), 500 );
|
||||
helpRender();
|
||||
tt.Render( "body" );
|
||||
waves.Render({ root: "body" });
|
||||
// only firefox and only usage 1.1.0.3024
|
||||
//if ( br.isFirefox() && ver.sub_ver == "3024" && !localStorage["opt-3024"] ) {
|
||||
// welcomeRender( true );
|
||||
// localStorage["opt-3024"] = ver.sub_ver;
|
||||
//}
|
||||
});
|
||||
|
||||
/**
|
||||
* Pure Read
|
||||
*/
|
||||
function pRead() {
|
||||
storage.puread = new PureRead( storage.sites );
|
||||
storage.pr.origins = storage.option.origins;
|
||||
console.log( "current puread object is ", storage.pr )
|
||||
}
|
||||
|
||||
/**
|
||||
* Incompatible and update
|
||||
*/
|
||||
function updateData() {
|
||||
ver.Incompatible( storage.version, storage.simpread ) && storage.Write( () => {
|
||||
console.log( "current simpread is update ", storage.simpread )
|
||||
watch.SendMessage( "option", true );
|
||||
}, storage.simpread );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash notify
|
||||
*/
|
||||
@ -141,8 +183,9 @@ function vernotify( first ) {
|
||||
watch.SendMessage( "version", true );
|
||||
loadState = { first: true, update: true };
|
||||
welcomeRender( false, version );
|
||||
updateData();
|
||||
}
|
||||
website_sync = true;
|
||||
// website_sync = true; when version is 1.1.3 website_list is newer
|
||||
browser.runtime.sendMessage({ type: "track", value: { eventAction: hash.startsWith( "#firstload?ver=" ) ? "install" : "update" , eventCategory: "install", eventLabel: "install && update" } });
|
||||
history.pushState( "", "", "/options/options.html" );
|
||||
} else if ( hash.startsWith( "#update?patch=" ) ) {
|
||||
@ -176,7 +219,6 @@ function firstLoad( first ) {
|
||||
* @param {string} version
|
||||
*/
|
||||
function welcomeRender( first, version ) {
|
||||
//!( !first && version == "1.0.4" ) && welc.Render( "body", first, version );
|
||||
welc.Render( "body", first, version );
|
||||
}
|
||||
|
||||
@ -208,6 +250,7 @@ function tabsRender( color ) {
|
||||
</section>
|
||||
<section style={{ 'padding': '0;' }}>
|
||||
<div id="labs" style={{ width: '100%' }}>
|
||||
<div className="version-tips" data-hits="focusmode">
|
||||
<div className="label">聚焦模式</div>
|
||||
<div className="lab" style={{ 'padding': '30px 30px 10px 10px' }}>
|
||||
<FocusOpt option={ storage.focus } />
|
||||
@ -217,6 +260,8 @@ function tabsRender( color ) {
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>save( true ) } />
|
||||
</div>
|
||||
</div>
|
||||
<div className="version-tips" data-hits="readmode">
|
||||
<div className="label">阅读模式</div>
|
||||
<div className="lab" style={{ 'padding': '30px 30px 10px 10px' }}>
|
||||
<ReadOpt option={ storage.read } />
|
||||
@ -226,6 +271,7 @@ function tabsRender( color ) {
|
||||
waves="md-waves-effect md-waves-button"
|
||||
onClick={ ()=>save( true ) } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section style={{ 'padding': '0;' }}>
|
||||
@ -245,8 +291,7 @@ function tabsRender( color ) {
|
||||
</Tabs>,
|
||||
tabsOnChange = ( $prev, $target, event ) => {
|
||||
const idx = $target.attr( "id" );
|
||||
mainRender( idx );
|
||||
conf.tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
|
||||
tabChange( idx );
|
||||
},
|
||||
refresh = () => {
|
||||
tt.Render( "body" );
|
||||
@ -277,8 +322,7 @@ function navRender() {
|
||||
function sidebarRender() {
|
||||
const sidebarClick = ( $target, items ) => {
|
||||
const idx = conf.tabsItem.findIndex( item => item.value == items.value );
|
||||
conf.tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
|
||||
mainRender( idx );
|
||||
tabChange( idx );
|
||||
};
|
||||
const sidebar = <side.Sidebar items={ conf.menuItem }
|
||||
waves="md-waves-effect"
|
||||
@ -286,11 +330,68 @@ function sidebarRender() {
|
||||
ReactDOM.render( sidebar, $( ".sidebar" )[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure Read
|
||||
*/
|
||||
function pRead() {
|
||||
storage.puread = new PureRead( storage.sites );
|
||||
storage.pr.origins = storage.option.origins;
|
||||
console.log( "current puread object is ", storage.pr )
|
||||
/*
|
||||
* Notice bubbles
|
||||
*/
|
||||
function noticeRender() {
|
||||
sessionStorage.setItem( "is_update", false );
|
||||
const tmpl = `
|
||||
<div class="md-waves-effect bubbles notice effect">
|
||||
<i><svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2555" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24"><defs><style type="text/css"></style></defs><path d="M787.908422 563.765991 787.908422 349.052814c0-152.726403-96.294137-222.682685-223.373417-236.678444 0.031722-0.931209 0.278339-1.811252 0.278339-2.76702 0-27.231201-22.429849-49.288566-50.031487-49.288566-27.662013 0-50.058093 22.057365-50.058093 49.288566 0 0.937348 0.23843 1.804089 0.295735 2.677992-127.636982 13.70207-224.524636 83.607186-224.524636 236.767472l0 214.713176c0 172.349323-442.565605 257.698177 265.890766 257.698177C1214.842001 821.464167 787.908422 736.115314 787.908422 563.765991L787.908422 563.765991zM514.782881 960.670649c52.405557 0 94.916766-41.893132 94.916766-93.54042L419.849742 867.13023C419.849742 918.777517 462.347648 960.670649 514.782881 960.670649L514.782881 960.670649zM514.782881 960.670649" p-id="2556" fill="#ffffff"></path></svg></i>
|
||||
<em class="init">...</em>
|
||||
</div>
|
||||
`;
|
||||
storage.Notice( result => {
|
||||
if ( $.isEmptyObject( result ) ) {
|
||||
storage.notice.latest = 0;
|
||||
}
|
||||
$.get( storage.notice_service.latest, result => {
|
||||
console.log( "notice latest id ", result )
|
||||
if ( storage.notice.latest == 0 ) {
|
||||
$( "body" ).append( tmpl );
|
||||
sessionStorage.setItem( "is_update", true );
|
||||
} else if ( storage.notice.latest < result ) {
|
||||
$( "body" ).append( tmpl );
|
||||
$( ".bubbles em" ).removeClass( "init" ).text( result - storage.notice.read.length );
|
||||
sessionStorage.setItem( "is_update", true );
|
||||
} else if ( storage.notice.latest > storage.notice.read.length ) {
|
||||
$( "body" ).append( tmpl );
|
||||
$( ".bubbles em" ).removeClass( "init" ).text( storage.notice.latest - storage.notice.read.length );
|
||||
} else if ( storage.notice.latest == storage.notice.read.length && storage.option.notice ) {
|
||||
$( "body" ).append( tmpl );
|
||||
$( ".bubbles em" ).remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
$( "body" ).on( "click", ".notice", event => {
|
||||
location.href = location.origin + "/options/notice.html?is_update=" + sessionStorage.getItem( "is_update" );
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Help bubbles
|
||||
*/
|
||||
function helpRender() {
|
||||
const help_icon = '<svg t="1560061923091" viewBox="0 0 1024 1024" version="1.1" width="24" height="24"><defs><style type="text/css"></style></defs><path d="M512 704c-187.733333 0-341.333333-153.6-341.333333-341.333333s153.6-341.333333 341.333333-341.333334 341.333333 153.6 341.333333 341.333334-153.6 341.333333-341.333333 341.333333z m0-597.333333c-140.8 0-256 115.2-256 256s115.2 256 256 256 256-115.2 256-256-115.2-256-256-256z" p-id="4789" fill="#ffffff"></path><path d="M512 576c119.466667 0 213.333333-93.866667 213.333333-213.333333s-93.866667-213.333333-213.333333-213.333334-213.333333 93.866667-213.333333 213.333334 93.866667 213.333333 213.333333 213.333333z" p-id="4790" fill="#ffffff"></path><path d="M384 776.533333c0-25.6 21.333333-42.666667 42.666667-42.666666h170.666666c21.333333 0 42.666667 17.066667 42.666667 42.666666s-21.333333 42.666667-42.666667 42.666667h-170.666666c-25.6 0-42.666667-17.066667-42.666667-42.666667z m42.666667 110.933334c0-25.6 17.066667-42.666667 42.666666-42.666667h85.333334c25.6 0 42.666667 17.066667 42.666666 42.666667s-17.066667 42.666667-42.666666 42.666666h-85.333334c-21.333333 0-42.666667-21.333333-42.666666-42.666666z m42.666666 85.333333c0-12.8 8.533333-21.333333 21.333334-21.333333h42.666666c12.8 0 21.333333 8.533333 21.333334 21.333333s-8.533333 21.333333-21.333334 21.333333h-42.666666c-12.8-4.266667-21.333333-12.8-21.333334-21.333333z" p-id="4791" fill="#ffffff"></path></svg>',
|
||||
close_icon = '<svg t="1560141389230" viewBox="0 0 1024 1024" version="1.1" width="24" height="24"><defs><style type="text/css"></style></defs><path d="M649.179 512l212.839-212.84c37.881-37.881 37.881-99.298 0-137.179s-99.298-37.881-137.179 0L512 374.821l-212.839-212.84c-37.881-37.881-99.298-37.881-137.179 0s-37.881 99.298 0 137.179L374.821 512 161.982 724.84c-37.881 37.881-37.881 99.297 0 137.179 18.94 18.94 43.765 28.41 68.589 28.41 24.825 0 49.649-9.47 68.589-28.41L512 649.179l212.839 212.84c18.94 18.94 43.765 28.41 68.589 28.41s49.649-9.47 68.59-28.41c37.881-37.882 37.881-99.298 0-137.179L649.179 512z" p-id="1990" fill="#ffffff"></path></svg>',
|
||||
tmpl = `
|
||||
<div class="md-waves-effect bubbles help effect">
|
||||
<i>${help_icon}</i>
|
||||
</div>
|
||||
`,
|
||||
exit = () => {
|
||||
ReactDOM.unmountComponentAtNode( $( ".guide-bg" )[0] );
|
||||
$( ".help i" ).html( help_icon ).css({ "animation": ".1s reverse fadein,235ms cubic-bezier(.4,0,.2,1) popdown" });
|
||||
$( ".guide-bg" ).remove();
|
||||
};
|
||||
$( "body" ).append( tmpl );
|
||||
$( "body" ).on( "click", ".help", event => {
|
||||
if ( $(".guide-bg").length == 0 ) {
|
||||
$( "body" ).append( `<div class="guide-bg"></div>` );
|
||||
ReactDOM.render( <guide.Guide onExit={ ()=> exit() } />, $( ".guide-bg" )[0] );
|
||||
$( ".help i" ).html( close_icon ).css({ "animation": ".1s reverse fadein,235ms cubic-bezier(.4,0,.2,1) popup" });
|
||||
} else {
|
||||
exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span>简悦(SimpRead)- 让你瞬间进入沉浸式阅读的扩展。</span> <span> © 2017 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
<span>简悦 SimpRead - 为你提供「如杂志般沉浸式阅读体验」的扩展</span> <span> © 2017 - 2019 <a href="http://ksria.com/simpread">ksria.com</a> by <a href="http://kenshin.wang" target="_blank">Kenshin Wang</a></span>
|
||||
</div>
|
||||
<script src="../bundle/common.js"></script>
|
||||
<script src="../bundle/vendors.js"></script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
console.log( "==== simpread options page: sitemanager load ====" )
|
||||
|
||||
import '../assets/css/simpread.css';
|
||||
import '../assets/css/option.css';
|
||||
import '../assets/css/setting.css';
|
||||
import '../assets/css/options_page.css';
|
||||
import '../assets/css/options_custom.css';
|
||||
import '../assets/css/options_sitemgr.css';
|
||||
|
||||
@ -9,6 +9,7 @@ import * as output from 'output';
|
||||
import * as watch from 'watch';
|
||||
import * as kbd from 'keyboard';
|
||||
import { storage } from 'storage';
|
||||
import * as run from 'runtime';
|
||||
|
||||
import ReadOpt from 'readopt';
|
||||
import Actionbar from 'actionbar';
|
||||
@ -19,7 +20,7 @@ import Fab from 'fab';
|
||||
import Fap from 'fap'
|
||||
import * as ttips from 'tooltip';
|
||||
|
||||
let notify;
|
||||
let notify, readItems;
|
||||
const tooltip_options = {
|
||||
target : "name",
|
||||
position : "bottom",
|
||||
@ -54,6 +55,9 @@ export default class ReadCtlbar extends React.Component {
|
||||
kbd.Listen( combo => {
|
||||
this.onAction( undefined, combo )
|
||||
});
|
||||
run.Controlbar( undefined, event => {
|
||||
this.onAction( undefined, event.detail.type );
|
||||
});
|
||||
}
|
||||
|
||||
onAction( event, type ) {
|
||||
@ -61,6 +65,8 @@ export default class ReadCtlbar extends React.Component {
|
||||
|
||||
this.verify( type.split( "_" )[0] );
|
||||
|
||||
run.Event( "export", type );
|
||||
|
||||
const action = ( event, type ) => {
|
||||
this.props.multi &&
|
||||
[ "markdown", "dropbox", "yinxiang","evernote", "onenote", "gdrive" ].includes( type ) &&
|
||||
@ -97,9 +103,13 @@ export default class ReadCtlbar extends React.Component {
|
||||
delete news.html;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.temp_site, { url: location.href, site: news, uid: storage.user.uid, type: "temp" }));
|
||||
break;
|
||||
case type.startsWith( "webdav_" ) :
|
||||
const [ title, desc, content ] = [ $( "sr-rd-title" ).text().trim(), $( "sr-rd-desc" ).text().trim(), $( "sr-rd-content" ).html().trim() ];
|
||||
output.Action( type, title, desc, content );
|
||||
break;
|
||||
default:
|
||||
if ( type.indexOf( "_" ) > 0 && type.startsWith( "share" ) ||
|
||||
[ "fullscreen", "save", "markdown", "png", "epub", "pdf", "kindle", "temp", "html", "dropbox", "pocket", "instapaper", "linnk", "yinxiang","evernote", "onenote", "gdrive" ].includes( type )) {
|
||||
[ "fullscreen", "save", "markdown", "png", "epub", "pdf", "kindle", "temp", "html", "dropbox", "pocket", "instapaper", "linnk", "yinxiang","evernote", "onenote", "gdrive", "jianguo", "yuque" ].includes( type )) {
|
||||
const [ title, desc, content ] = [ $( "sr-rd-title" ).text().trim(), $( "sr-rd-desc" ).text().trim(), $( "sr-rd-content" ).html().trim() ];
|
||||
output.Action( type, title, desc, content );
|
||||
}
|
||||
@ -117,6 +127,7 @@ export default class ReadCtlbar extends React.Component {
|
||||
|
||||
onChange( type, custom ) {
|
||||
const [ key, value ] = [ type.split( "_" )[0], type.split( "_" )[1] ];
|
||||
run.Event( "read_ui", { key, value, custom });
|
||||
this.props.onAction && this.props.onAction( key, value, custom );
|
||||
this.verify( key );
|
||||
}
|
||||
@ -126,26 +137,61 @@ export default class ReadCtlbar extends React.Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
readItems = $.extend( true, {}, conf.readItems );
|
||||
try {
|
||||
if ( storage.current.fap ) {
|
||||
delete conf.readItems.exit;
|
||||
delete conf.readItems.option.items.setting;
|
||||
delete readItems.exit;
|
||||
delete readItems.option.items.setting;
|
||||
delete readItems.fontfamily;
|
||||
delete readItems.fontsize;
|
||||
delete readItems.layout;
|
||||
delete readItems.theme;
|
||||
} else {
|
||||
delete readItems.trigger;
|
||||
}
|
||||
if ( this.props.type.startsWith( "txtread::" ) && this.props.type.endsWith( "::local" )) {
|
||||
delete conf.readItems.download;
|
||||
delete conf.readItems.readlater;
|
||||
delete conf.readItems.send;
|
||||
delete conf.readItems.share;
|
||||
delete conf.readItems.option;
|
||||
delete readItems.download;
|
||||
delete readItems.readlater;
|
||||
delete readItems.send;
|
||||
delete readItems.share;
|
||||
delete readItems.option;
|
||||
}
|
||||
if ( this.props.type.startsWith( "metaread::" ) || this.props.type.startsWith( "txtread::" ) ) {
|
||||
delete conf.readItems.option;
|
||||
delete readItems.option;
|
||||
}
|
||||
storage.Safe( () => {
|
||||
storage.secret.webdav.forEach( item => {
|
||||
item = JSON.parse( item );
|
||||
readItems.send.items[ "webdav_" + item.name ] = {
|
||||
name: item.name,
|
||||
icon: ss.IconPath("webdav_icon"),
|
||||
"color": "#00BCD4",
|
||||
};
|
||||
});
|
||||
})
|
||||
// Add test source
|
||||
storage.current.fap && storage.Plugins( () => {
|
||||
!$.isEmptyObject( storage.plugins ) && storage.option.plugins.forEach( id => {
|
||||
const plugin = storage.plugins[id];
|
||||
// Add test source
|
||||
if ( plugin.enable != false && ( plugin.trigger == true || plugin.trigger == "true" )) {
|
||||
//if ( plugin.id == "Y7JxbP7B4H" ) {
|
||||
readItems.trigger.items["plugin_" + plugin.id] = {
|
||||
"name" : plugin.name,
|
||||
"fontIcon" : plugin.icon.type,
|
||||
"color" : plugin.icon.bgColor,
|
||||
};
|
||||
}
|
||||
});
|
||||
if ( readItems.trigger && $.isEmptyObject( readItems.trigger.items )) {
|
||||
delete readItems.trigger;
|
||||
}
|
||||
});
|
||||
} catch ( err ) {
|
||||
// TO-DO
|
||||
}
|
||||
// hack code
|
||||
!/chrome/ig.test( navigator.userAgent ) && ( delete conf.readItems.dyslexia );
|
||||
!/chrome/ig.test( navigator.userAgent ) && ( delete readItems.dyslexia );
|
||||
}
|
||||
|
||||
constructor( props ) {
|
||||
@ -159,12 +205,12 @@ export default class ReadCtlbar extends React.Component {
|
||||
onOpen={ ()=> this.onPop( "open" ) } onClose={ ()=> this.onPop( "close" ) }
|
||||
onAction={ (event, type)=>this.onAction(event, type ) }>
|
||||
<ReadOpt option={ storage.current } onChange={ (t,c)=>this.onChange(t,c)}/>
|
||||
<Actionbar items={ conf.readItems } onAction={ (type)=>this.onAction(undefined, type ) }/>
|
||||
<Actionbar items={ readItems } onAction={ (type)=>this.onAction(undefined, type ) }/>
|
||||
<Sitebar />
|
||||
<Pluginbar />
|
||||
</Fap>
|
||||
:
|
||||
<Fab items={ conf.readItems } tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" onAction={ (event, type)=>this.onAction(event, type ) } />
|
||||
<Fab items={ readItems } tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" onAction={ (event, type)=>this.onAction(event, type ) } />
|
||||
return (
|
||||
<sr-rd-crlbar class={ this.props.show ? "" : "controlbar" } style={{ "zIndex": "2" }}>
|
||||
{ Controlbar }
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
console.log( "=== simpread read load ===" )
|
||||
|
||||
import ProgressBar from 'schedule';
|
||||
import * as spec from 'special';
|
||||
import ReadCtlbar from 'readctlbar';
|
||||
import * as toc from 'toc';
|
||||
import * as modals from 'modals';
|
||||
import * as se from 'siteeditor';
|
||||
import * as kbd from 'keyboard';
|
||||
import ProgressBar from 'schedule';
|
||||
import * as spec from 'special';
|
||||
import ReadCtlbar from 'readctlbar';
|
||||
import * as toc from 'toc';
|
||||
import * as setting from 'setting';
|
||||
import * as se from 'siteeditor';
|
||||
import * as kbd from 'keyboard';
|
||||
|
||||
import { storage, Clone } from 'storage';
|
||||
import th from 'theme';
|
||||
@ -25,6 +25,9 @@ const rdcls = "simpread-read-root",
|
||||
$root = $( "html" ),
|
||||
theme = "simpread-theme-root";
|
||||
|
||||
// load count,.0: call Readability. 1: call highlight 2: all failed
|
||||
let load_count = 0;
|
||||
|
||||
const Footer = () => {
|
||||
const good_icon = '<svg t="1556354786433" viewBox="0 0 1024 1024" version="1.1" width="33" height="33"><defs><style type="text/css"></style></defs><path d="M859.8 191.2c-80.8-84.2-212-84.2-292.8 0L512 248.2l-55-57.2c-81-84.2-212-84.2-292.8 0-91 94.6-91 248.2 0 342.8L512 896l347.8-362C950.8 439.4 950.8 285.8 859.8 191.2z" p-id="6225" fill="#8C8C8C"></path></svg>',
|
||||
bad_icon = '<svg t="1556354650943" viewBox="0 0 1024 1024" version="1.1" p-id="5899" width="33" height="33"><defs><style type="text/css"></style></defs><path d="M458 576c2-36 0-76 16-110 4-10 2-20 2-30-8-42-28-80-30-120 0-2.78 2.008-9.542 2.01-12.314-6.432 4.468-15.214 8.048-22.01 10.314-40 12-35.02 5.146-69.02 27.146l-23.866 14.456c32.686-35.878 77.056-49.562 113.05-77.428 0.388-30.876 1.716-61.354 6.274-91.68C371.22 106.992 243.57 108.536 164.246 191.14c-90.994 94.688-90.994 248.202 0 342.89l305.698 318.192c-0.17-21.312-0.886-42.352-3.944-62.222C454 718 458 648 458 576z" p-id="5900" fill="#8C8C8C"></path><path d="M644 602c-22-52-66-88-126-100-1.7 0-3.758-1.086-5.872-2.638-0.046 0.214-0.082 0.426-0.128 0.638-22 96-46 188-42 284 0 24.454 7.966 50.234 7.666 76.262L512 896l208-216.5C690.306 658.542 660.856 637.242 644 602z" p-id="5901" fill="#8C8C8C"></path><path d="M859.748 191.14c-80.852-84.188-211.978-84.188-292.816 0L528 230.806c0.15 26.35 0.426 52.404-6 77.194-4 20-38 38-32 62 6.006 26.426 16.332 51.41 21.464 77.118C542.028 464.168 569.542 485.792 594 512c45.602 53.532 75.494 114.918 130.566 162.742l135.182-140.71C950.75 439.342 950.75 285.828 859.748 191.14z" p-id="5902" fill="#8C8C8C"></path></svg>',
|
||||
@ -55,82 +58,104 @@ const Footer = () => {
|
||||
|
||||
class Read extends React.Component {
|
||||
|
||||
verifyContent() {
|
||||
if ( $("sr-rd-content").text().length < 100 ) {
|
||||
if ( load_count == 0 ) {
|
||||
new Notify().Render({ content: "检测到正文获取异常,是否重新获取?", action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
load_count++;
|
||||
this.componentWillUnmount();
|
||||
storage.pr.Readability();
|
||||
Render();
|
||||
}});
|
||||
} else if ( load_count == 1 ) {
|
||||
this.componentWillUnmount();
|
||||
new Notify().Render({ content: '获取正文失败,是否使用 <a target="_blank" href="http://ksria.com/simpread/docs/#/手动框选">手动框选</a> 高亮的方式获取?', action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
setTimeout( () => {
|
||||
Highlight().done( dom => {
|
||||
const rerender = element => {
|
||||
load_count++;
|
||||
storage.pr.TempMode( "read", element );
|
||||
Render();
|
||||
};
|
||||
storage.current.highlight ?
|
||||
highlight.Control( dom ).done( newDom => {
|
||||
rerender( newDom );
|
||||
}) : rerender( dom );
|
||||
});
|
||||
}, 200 );
|
||||
}});
|
||||
} else if ( load_count >= 2 ) {
|
||||
this.componentWillUnmount();
|
||||
new Notify().Render({ content: "高亮无法仍无法适配此页面,是否提交?", action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.save_site, { url: location.href, site: {}, uid: storage.user.uid, type: "failed" }));
|
||||
}});
|
||||
load_count = 0;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
loadPlugins( "read_start" );
|
||||
$( "body" ).addClass( "simpread-hidden" );
|
||||
th.Change( this.props.read.theme );
|
||||
// hack code
|
||||
//storage.current.fap && $( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">' );
|
||||
if ( storage.current.fap ) {
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css" integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp" crossorigin="anonymous">' );
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css" integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG" crossorigin="anonymous">' );
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/solid.min.css" />' );
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/brands.min.css" />' );
|
||||
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/fontawesome.min.css" />' );
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
if ( $root.find( "sr-rd-content-error" ).length > 0 ) {
|
||||
// Puread level to III,can't work this flow.
|
||||
this.componentWillUnmount();
|
||||
if ( ! localStorage["sr-update-site"] ) {
|
||||
new Notify().Render({ content: "当前页面结构改变导致不匹配阅读模式,接下来请选择?", action: "更新", cancel: "高亮", callback: type => {
|
||||
if ( type == "action" ) {
|
||||
new Notify().Render( "2 秒钟后将会自动查找更新,请勿关闭此页面..." );
|
||||
localStorage["sr-update-site"] = true;
|
||||
setTimeout( ()=>browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.update_site, { url: location.href, site: storage.pr.current.site } )), 2000 );
|
||||
} else {
|
||||
this.props.read.highlight == true ? setTimeout( () => {
|
||||
Highlight().done( dom => {
|
||||
storage.pr.TempMode( "read", dom );
|
||||
Render();
|
||||
});
|
||||
}, 200 ) : new Notify().Render( `请先开启 <a href='http://ksria.com/simpread/docs/#/%E4%B8%B4%E6%97%B6%E9%98%85%E8%AF%BB%E6%A8%A1%E5%BC%8F' target='_blank' >临时阅读模式</a> 选项!` );
|
||||
}
|
||||
}});
|
||||
} else {
|
||||
new Notify().Render({ content: "更新后仍无法适配此页面,是否提交?", action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.save_site, { url: location.href, site: storage.pr.current.site, uid: storage.user.uid, type: "failed" }));
|
||||
}});
|
||||
}
|
||||
localStorage.removeItem( "sr-update-site" );
|
||||
} else {
|
||||
$root
|
||||
.addClass( "simpread-font" )
|
||||
.addClass( theme )
|
||||
.find( rdclsjq )
|
||||
.addClass( theme )
|
||||
.sreffect( { opacity: 1 }, { delay: 100 })
|
||||
.addClass( "simpread-read-root-show" );
|
||||
|
||||
this.props.read.fontfamily && ss.FontFamily( this.props.read.fontfamily );
|
||||
this.props.read.fontsize && ss.FontSize( this.props.read.fontsize );
|
||||
this.props.read.layout && ss.Layout( this.props.read.layout );
|
||||
this.props.read.site.css && this.props.read.site.css.length > 0
|
||||
&& ss.SiteCSS( this.props.read.site.css );
|
||||
!this.props.wrapper.avatar && this.props.read.toc
|
||||
&& toc.Render( "sr-read", $( "sr-rd-content" ), this.props.read.theme, this.props.read.toc_hide );
|
||||
ss.Preview( this.props.read.custom );
|
||||
|
||||
storage.pr.state == "txt" && !location.href.endsWith( ".md" ) && $( "sr-rd-content" ).css({ "word-wrap": "break-word", "white-space": "pre-wrap" });
|
||||
storage.pr.current.site.desc == "" && $( "sr-rd-desc" ).addClass( "simpread-hidden" );
|
||||
|
||||
excludes( $("sr-rd-content"), this.props.wrapper.exclude );
|
||||
storage.pr.Beautify( $( "sr-rd-content" ) );
|
||||
storage.pr.Format( rdcls );
|
||||
|
||||
kbd.Render( $( "sr-rd-content" ));
|
||||
tooltip.Render( rdclsjq );
|
||||
waves.Render({ root: rdclsjq });
|
||||
storage.Statistics( "read" );
|
||||
|
||||
loadPlugins( "read_complete" );
|
||||
|
||||
// Puread level to III,can't work this flow.
|
||||
//localStorage.removeItem( "sr-update-site" );
|
||||
if ( load_count > 0 && !this.verifyContent() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$root
|
||||
.addClass( "simpread-font" )
|
||||
.addClass( theme )
|
||||
.find( rdclsjq )
|
||||
.addClass( theme )
|
||||
.sreffect( { opacity: 1 }, { delay: 100 })
|
||||
.addClass( "simpread-read-root-show" );
|
||||
|
||||
this.props.read.fontfamily && ss.FontFamily( this.props.read.fontfamily );
|
||||
this.props.read.fontsize && ss.FontSize( this.props.read.fontsize );
|
||||
this.props.read.layout && ss.Layout( this.props.read.layout );
|
||||
this.props.read.site.css && this.props.read.site.css.length > 0
|
||||
&& ss.SiteCSS( this.props.read.site.css );
|
||||
ss.Preview( this.props.read.custom );
|
||||
|
||||
storage.pr.state == "txt" && !location.href.endsWith( ".md" ) && $( "sr-rd-content" ).css({ "word-wrap": "break-word", "white-space": "pre-wrap" });
|
||||
$( "sr-rd-desc" ).text().trim() == "" && $( "sr-rd-desc" ).addClass( "simpread-hidden" );
|
||||
|
||||
excludes( $("sr-rd-content"), this.props.wrapper.exclude );
|
||||
storage.pr.Beautify( $( "sr-rd-content" ) );
|
||||
storage.pr.Format( rdcls );
|
||||
|
||||
kbd.Render( $( "sr-rd-content" ));
|
||||
tooltip.Render( rdclsjq );
|
||||
waves.Render({ root: rdclsjq });
|
||||
storage.Statistics( "read" );
|
||||
|
||||
!this.props.wrapper.avatar && this.props.read.toc
|
||||
&& toc.Render( "sr-read", $( "sr-rd-content" ), this.props.read.theme, this.props.read.toc_hide );
|
||||
|
||||
this.props.wrapper.avatar && $( ".simpread-read-root" ).addClass( "simpread-multi-root" );
|
||||
|
||||
loadPlugins( "read_complete" );
|
||||
|
||||
setTimeout( ()=>{
|
||||
this.verifyContent();
|
||||
}, 50 );
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
run.Event( "read_end" );
|
||||
loadPlugins( "read_end" );
|
||||
ss.FontSize( "" );
|
||||
$root.removeClass( theme )
|
||||
@ -154,7 +179,7 @@ class Read extends React.Component {
|
||||
this.exit();
|
||||
break;
|
||||
case "setting":
|
||||
modals.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
setting.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
break;
|
||||
case "siteeditor":
|
||||
$( "panel-bg" ).length > 0 && $( "panel-bg" )[0].click();
|
||||
@ -170,18 +195,37 @@ class Read extends React.Component {
|
||||
storage.Setcur( storage.current.mode );
|
||||
break;
|
||||
case "remove":
|
||||
new Notify().Render( "移动鼠标选择不想显示的内容,只针对本次有效。" );
|
||||
$( "panel-bg" ).length > 0 && $( "panel-bg" ).trigger( "click" );
|
||||
Highlight().done( dom => {
|
||||
new Notify().Render({ content: "移动鼠标选择不想显示的内容,可多次选择,使用 ESC 退出。", delay: 5000 });
|
||||
highlight.Multi( dom => {
|
||||
const path = storage.pr.Utils().dom2Xpath( dom ),
|
||||
site = { ...storage.pr.current.site };
|
||||
site.exclude.push( `[[\`${path}\`]]` );
|
||||
if ( storage.pr.state == "temp" ) {
|
||||
const include = storage.pr.Utils().dom2Xpath( storage.pr.dom );
|
||||
site.include = `[[\`${include}\`]]`;
|
||||
site.name = site.name.replace( "tempread::", "" );
|
||||
}
|
||||
storage.pr.Updatesite( 'local', storage.current.url, [ site.url, storage.pr.Cleansite(site) ]);
|
||||
storage.Writesite( storage.pr.sites, () => {
|
||||
storage.pr.current.site.name = site.name;
|
||||
storage.pr.current.site.include = site.include;
|
||||
});
|
||||
$(dom).remove();
|
||||
});
|
||||
break;
|
||||
case "highlight":
|
||||
new Notify().Render( "移动鼠标选择高亮区域,以便生成阅读模式,将会在页面刷新后失效。" );
|
||||
new Notify().Render( `移动鼠标选择高亮区域,以便生成阅读模式,此模式将会在页面刷新后失效,详细说明请看 <a href="http://ksria.com/simpread/docs/#/重新高亮" target="_blank">重新高亮</a>` );
|
||||
this.exit();
|
||||
Highlight().done( dom => {
|
||||
storage.pr.TempMode( "read", dom );
|
||||
Render();
|
||||
const rerender = element => {
|
||||
storage.pr.TempMode( "read", element );
|
||||
Render();
|
||||
};
|
||||
storage.current.highlight ?
|
||||
highlight.Control( dom ).done( newDom => {
|
||||
rerender( newDom );
|
||||
}) : rerender( dom );
|
||||
});
|
||||
break;
|
||||
/*
|
||||
@ -198,11 +242,11 @@ class Read extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const Article = this.props.wrapper.avatar ?
|
||||
const Article = this.props.wrapper.avatar && this.props.wrapper.avatar.length > 0 ?
|
||||
<spec.Multiple include={ this.props.wrapper.include } avatar={ this.props.wrapper.avatar } /> :
|
||||
<sr-rd-content dangerouslySetInnerHTML={{__html: this.props.wrapper.include }} ></sr-rd-content>;
|
||||
|
||||
const Page = this.props.wrapper.paging &&
|
||||
const Page = this.props.wrapper.paging && this.props.wrapper.paging.length > 0 &&
|
||||
<spec.Paging paging={ this.props.wrapper.paging } />;
|
||||
return (
|
||||
<sr-read>
|
||||
@ -229,6 +273,7 @@ class Read extends React.Component {
|
||||
* @param {boolean} true: call mathJaxMode(); false: @see mathJaxMode
|
||||
*/
|
||||
function Render( callMathjax = true ) {
|
||||
loadPlugins( "read_start" );
|
||||
callMathjax && mathJaxMode();
|
||||
storage.pr.ReadMode();
|
||||
if ( typeof storage.pr.html.include == "string" && storage.pr.html.include.startsWith( "<sr-rd-content-error>" ) ) {
|
||||
@ -259,7 +304,7 @@ function Highlight() {
|
||||
*/
|
||||
function Exist( action ) {
|
||||
if ( $root.find( rdclsjq ).length > 0 ) {
|
||||
action && modals.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
action && setting.Render( ()=>setTimeout( ()=>se.Render(), 500 ));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -287,10 +332,16 @@ function mathJaxMode() {
|
||||
const dom = storage.pr.MathJaxMode();
|
||||
console.log( 'current get dom is ', dom )
|
||||
if ( typeof dom == "undefined" ) {
|
||||
new Notify().Render( "智能感知失败,请移动鼠标框选。" );
|
||||
new Notify().Render( "<a href='http://ksria.com/simpread/docs/#/词法分析引擎?id=智能感知' target='_blank' >智能感知</a> 失败,请移动鼠标框选。" );
|
||||
Highlight().done( dom => {
|
||||
storage.pr.TempMode( "read", dom );
|
||||
Render( false );
|
||||
const rerender = element => {
|
||||
storage.pr.TempMode( "read", element );
|
||||
Render( false );
|
||||
};
|
||||
storage.current.highlight ?
|
||||
highlight.Control( dom ).done( newDom => {
|
||||
rerender( newDom );
|
||||
}) : rerender( dom );
|
||||
});
|
||||
} else if ( typeof dom == "string" ) {
|
||||
const html = storage.pr.GetDom( dom, "html" );
|
||||
|
||||
@ -5,19 +5,29 @@ let is_click = false;
|
||||
class TOC extends React.Component {
|
||||
|
||||
onClick( event ) {
|
||||
is_click = true;
|
||||
const $target = $( event.target ).parent();
|
||||
$target.parent().find( "active" ).removeClass( "toc-outline-active" );
|
||||
$target.find( "active" ).addClass( "toc-outline-active" );
|
||||
try {
|
||||
is_click = true;
|
||||
let $target = $( event.target ).parent();
|
||||
|
||||
const href = $( event.target ).attr("href"),
|
||||
offsetTop = href === "#" ? 0 : $(href).offset().top - 5;
|
||||
$( "html" ).stop().animate({
|
||||
scrollTop: offsetTop
|
||||
}, 300, () => {
|
||||
setTimeout( ()=>is_click = false, 500 );
|
||||
});
|
||||
event.preventDefault();
|
||||
while ( $target.is( "a" ) ) { $target = $target.parent(); }
|
||||
if ( $target.is( "toc" ) ){
|
||||
return;
|
||||
}
|
||||
|
||||
$target.parent().find( "active" ).removeClass( "toc-outline-active" );
|
||||
$target.find( "active" ).addClass( "toc-outline-active" );
|
||||
|
||||
const href = $target.find( "a" ).attr( "href" ),
|
||||
offsetTop = href === "#" ? 0 : $(href).offset().top - 5;
|
||||
$( "html" ).stop().animate({
|
||||
scrollTop: offsetTop
|
||||
}, 300, () => {
|
||||
setTimeout( ()=>is_click = false, 500 );
|
||||
});
|
||||
event.preventDefault();
|
||||
} catch ( error ) {
|
||||
console.error( "toc error ", error )
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -28,36 +38,42 @@ class TOC extends React.Component {
|
||||
scrollItems = menuItems.map( function() {
|
||||
const item = $( $(this).attr("href") );
|
||||
if ( item.length ) { return item; }
|
||||
});
|
||||
}),
|
||||
setActive = function() {
|
||||
if ( is_click ) return;
|
||||
const fromTop = $(window).scrollTop() + topMenuHeight;
|
||||
let cur = scrollItems.map( function() {
|
||||
if ($(this).offset().top < fromTop)
|
||||
return this;
|
||||
});
|
||||
cur = cur[cur.length - 1];
|
||||
let id = cur && cur.length ? cur[0].id : "";
|
||||
|
||||
if ( lastId !== id ) {
|
||||
lastId = id;
|
||||
id == "" && ( id = "sr-toc-0" );
|
||||
menuItems.parent().find( "active" ).removeClass( "toc-outline-active" );
|
||||
menuItems.filter("[href='#"+id+"']").parent().find( "active" ).addClass( "toc-outline-active" );
|
||||
}
|
||||
};
|
||||
|
||||
$( window ).scroll( function() {
|
||||
if ( is_click ) return;
|
||||
const fromTop = $(this).scrollTop() + topMenuHeight;
|
||||
let cur = scrollItems.map( function() {
|
||||
if ($(this).offset().top < fromTop)
|
||||
return this;
|
||||
});
|
||||
cur = cur[cur.length - 1];
|
||||
const id = cur && cur.length ? cur[0].id : "";
|
||||
|
||||
if ( lastId !== id ) {
|
||||
lastId = id;
|
||||
menuItems.parent().find( "active" ).removeClass( "toc-outline-active" );
|
||||
menuItems.filter("[href='#"+id+"']").parent().find( "active" ).addClass( "toc-outline-active" );
|
||||
}
|
||||
});
|
||||
$( window ).scroll( setActive );
|
||||
|
||||
$( "outline" ).map( ( idx, item ) => {
|
||||
$(item).width( 180 - parseInt( $(item).css("padding-left") ) );
|
||||
})
|
||||
|
||||
this.props.hidden == false && $( "toc" ).css({ "width" : "initial" });
|
||||
|
||||
setActive();
|
||||
}
|
||||
|
||||
render() {
|
||||
const outline = this.props.table.map( item => {
|
||||
return (
|
||||
<outline className={ item.level }>
|
||||
<outline className={ item.level } onClick={ evt=>this.onClick(evt) }>
|
||||
<active></active>
|
||||
<a className={ "toc-outline-theme-" + this.props.theme } href={ "#" + item.id} onClick={ evt=>this.onClick(evt) }>{ item.value }</a>
|
||||
<a className={ "toc-outline-theme-" + this.props.theme } href={ "#" + item.id}><span>{ item.value }</span></a>
|
||||
</outline>
|
||||
)
|
||||
});
|
||||
@ -101,7 +117,7 @@ function Render( root, $target, theme, hidden ) {
|
||||
});
|
||||
console.log( "current toc is ", table )
|
||||
$( root ).append( `<toc-bg class=${cls}></tocbg>` );
|
||||
table.length > 1 && ReactDOM.render( <TOC table={ table } theme={ theme } />, $( "toc-bg" )[0] );
|
||||
table.length > 1 && ReactDOM.render( <TOC hidden={ hidden } table={ table } theme={ theme } />, $( "toc-bg" )[0] );
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@ -83,6 +83,16 @@ const keyboard = {
|
||||
"type" : "gdrive",
|
||||
"desc" : "保存到 Google 云端硬盘",
|
||||
},
|
||||
jg: {
|
||||
"kbd" : "jg",
|
||||
"type" : "jianguo",
|
||||
"desc" : "保存到 坚果云",
|
||||
},
|
||||
yq: {
|
||||
"kbd" : "yq",
|
||||
"type" : "yuque",
|
||||
"desc" : "保存到 语雀",
|
||||
},
|
||||
kd: {
|
||||
"kbd" : "kd",
|
||||
"type" : "kindle",
|
||||
@ -300,7 +310,7 @@ const readItems = {
|
||||
},
|
||||
"dropbox" : {
|
||||
"name" : "保存到 Dropbox",
|
||||
"icon" : ss.IconPath("sync_icon"),
|
||||
"icon" : ss.IconPath("dropbox_icon"),
|
||||
"color": "#00BCD4",
|
||||
},
|
||||
"onenote" : {
|
||||
@ -313,6 +323,16 @@ const readItems = {
|
||||
"icon" : ss.IconPath("gdrive_icon"),
|
||||
"color": "#00BCD4",
|
||||
},
|
||||
"jianguo" : {
|
||||
"name" : "保存到 坚果云",
|
||||
"icon" : ss.IconPath("jianguo_icon"),
|
||||
"color": "#00BCD4",
|
||||
},
|
||||
"yuque" : {
|
||||
"name" : "保存到 语雀",
|
||||
"icon" : ss.IconPath("yuque_icon"),
|
||||
"color": "#00BCD4",
|
||||
},
|
||||
"kindle" : {
|
||||
"name" : "保存到 Kindle",
|
||||
"icon" : ss.IconPath("kindle_icon"),
|
||||
@ -379,6 +399,12 @@ const readItems = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"trigger" : {
|
||||
"name" : "插件触发器",
|
||||
"icon" : ss.IconPath("plugin_icon"),
|
||||
"color": "#00bcd4",
|
||||
"items": {}
|
||||
},
|
||||
/*
|
||||
"down" : {
|
||||
"name" : "向下滚动",
|
||||
@ -555,9 +581,11 @@ readLabels = [ "白练", "白磁", "卯之花色", "丁子色", "娟鼠", "月
|
||||
* Focus controlbar items
|
||||
*/
|
||||
const focusItems = ( items => {
|
||||
const news = { ...items },
|
||||
dels = [ "theme", "fontfamily", "fontsize", "layout", "dyslexia" ];
|
||||
const news = $.extend( true, {}, items ),
|
||||
dels = [ "theme", "fontfamily", "fontsize", "layout", "dyslexia", "trigger" ];
|
||||
dels.forEach( del => delete news[ del ] );
|
||||
delete news.option.items.fullscreen;
|
||||
delete news.option.items.tempread;
|
||||
news.top = {
|
||||
"name" : "返回顶部",
|
||||
"icon" : ss.IconPath("top_icon"),
|
||||
|
||||
@ -2,12 +2,14 @@ console.log( "=== simpread export load ===" )
|
||||
|
||||
import domtoimage from 'dom2image';
|
||||
import FileSaver from 'filesaver';
|
||||
import toMarkdown from 'markdown';
|
||||
import Turndown from 'markdown';
|
||||
import mdgfm from 'mdgfm';
|
||||
import EpubPress from 'epubpress';
|
||||
import Instapaper from 'instapaper';
|
||||
|
||||
import * as msg from 'message';
|
||||
import {browser} from 'browser';
|
||||
import * as puplugin from 'puplugin';
|
||||
|
||||
/**
|
||||
* Create PNG
|
||||
@ -43,7 +45,19 @@ function pdf() {
|
||||
*/
|
||||
function markdown( data, name, callback ) {
|
||||
try {
|
||||
const md = toMarkdown( data, { gfm: true }),
|
||||
const turndownService = new Turndown(),
|
||||
gfm = mdgfm.gfm,
|
||||
tables = mdgfm.tables,
|
||||
strikethrough = mdgfm.strikethrough,
|
||||
codeBlock = mdgfm.highlightedCodeBlock;
|
||||
turndownService.use([ gfm, tables, strikethrough, codeBlock ]);
|
||||
turndownService.addRule( 'pre', {
|
||||
filter: [ 'pre' ],
|
||||
replacement: content => {
|
||||
return '\n\n```\n' + content + '\n```\n\n'
|
||||
}
|
||||
});
|
||||
const md = turndownService.turndown( data ),
|
||||
base64 = "data:text/plain;charset=utf-8," + encodeURIComponent( md );
|
||||
name ? download( base64, name ) : callback( md );
|
||||
} catch( error ) {
|
||||
@ -115,6 +129,8 @@ function unlink( id ) {
|
||||
"yinxiang": "https://app.yinxiang.com/AuthorizedServices.action",
|
||||
"onenote" : "https://account.live.com/consent/Manage",
|
||||
"gdrive" : "https://drive.google.com/drive/my-drive",
|
||||
"yuque" : "https://www.yuque.com/yuque/developer/delete-oauth-apps",
|
||||
"jianguo" : "http://help.jianguoyun.com/?p=2064",
|
||||
"linnk" : "https://linnk.net/",
|
||||
}
|
||||
return content[id]
|
||||
@ -760,13 +776,13 @@ class Onenote {
|
||||
status != "success" && callback( undefined, "error" );
|
||||
}).fail( ( xhr, status, error ) => {
|
||||
console.error( xhr, status, error )
|
||||
callback( undefined, error.toLowerCase() == "unauthorized" ? `${ this.name } 授权过期,请重新授权。` : "error" );
|
||||
callback( undefined, xhr.status == 401 ? `${ this.name } 授权过期,请重新授权。` : "error" );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Onenote
|
||||
* GDrive
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
@ -927,6 +943,292 @@ class GDrive {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jianguo
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Jianguo {
|
||||
|
||||
get id() { return "jianguo"; }
|
||||
get name() { return name( this.id ); }
|
||||
|
||||
get url() {
|
||||
return "https://dav.jianguoyun.com/dav/";
|
||||
}
|
||||
|
||||
get root() {
|
||||
return "SimpRead";
|
||||
}
|
||||
|
||||
get folder() {
|
||||
return "md";
|
||||
}
|
||||
|
||||
get config_name() {
|
||||
return "simpread_config.json";
|
||||
}
|
||||
|
||||
Auth( user, password, callback ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.jianguo, {
|
||||
url: this.url,
|
||||
user,
|
||||
password,
|
||||
method: {
|
||||
type: "folder",
|
||||
root: this.root,
|
||||
folder: this.folder,
|
||||
},
|
||||
}), callback );
|
||||
}
|
||||
|
||||
Add( user, password, path, content, callback ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.jianguo, {
|
||||
url: this.url,
|
||||
user,
|
||||
password,
|
||||
method: {
|
||||
type: "file",
|
||||
path,
|
||||
content
|
||||
},
|
||||
}), callback );
|
||||
}
|
||||
|
||||
Read( user, password, name, callback ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.jianguo, {
|
||||
url: this.url,
|
||||
user,
|
||||
password,
|
||||
method: {
|
||||
type : "read",
|
||||
path: this.root + "/" + name,
|
||||
name,
|
||||
},
|
||||
}), callback );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class WebDAV {
|
||||
|
||||
get id() { return "webdav"; }
|
||||
get name() { return name( this.id ); }
|
||||
|
||||
get root() {
|
||||
return "/SimpRead";
|
||||
}
|
||||
|
||||
Auth( url, user, password, callback ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.WebDAV, {
|
||||
url,
|
||||
user,
|
||||
password,
|
||||
method: {
|
||||
type: "folder",
|
||||
root: this.root,
|
||||
},
|
||||
}), callback );
|
||||
}
|
||||
|
||||
Add( url, user, password, name, content, callback ) {
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.WebDAV, {
|
||||
url,
|
||||
user,
|
||||
password,
|
||||
method: {
|
||||
type: "file",
|
||||
root: this.root,
|
||||
name,
|
||||
content
|
||||
},
|
||||
}), callback );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Yuque
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Yuque {
|
||||
|
||||
get id() { return "yuque"; }
|
||||
get name() { return name( this.id ); }
|
||||
|
||||
get client_id() {
|
||||
return "8p4PvTuP02UN7WhLlQrY";
|
||||
}
|
||||
|
||||
get client_secret() {
|
||||
return "kl0LpKR7Tu3EDEJJOjAPEwEAkxeVYrcY5cEEOPeG";
|
||||
}
|
||||
|
||||
get redirect_uri() {
|
||||
//return "https://simpread.herokuapp.com";
|
||||
return "https://kenshin.github.io/simpread/auth.html";
|
||||
}
|
||||
|
||||
get scopes() {
|
||||
return "repo, doc";
|
||||
}
|
||||
|
||||
New() {
|
||||
this.dtd = $.Deferred();
|
||||
this.code = "";
|
||||
this.access_token = "";
|
||||
this.token_type = "";
|
||||
this.user_id = "";
|
||||
this.repos_id = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
Login() {
|
||||
let url = "https://www.yuque.com/oauth2/authorize?";
|
||||
const params = {
|
||||
client_id : this.client_id,
|
||||
redirect_uri : this.redirect_uri,
|
||||
scope : this.scopes,
|
||||
state : "yuque_authorize",
|
||||
response_type: "code",
|
||||
};
|
||||
Object.keys( params ).forEach( key => {
|
||||
url += `${key}=${params[key]}&`;
|
||||
});
|
||||
url = url.substr( 0, url.length )
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url } ));
|
||||
}
|
||||
|
||||
Accesstoken( url ) {
|
||||
url = url.replace( "http://ksria.com/simpread/auth.html?", "" );
|
||||
if ( url.startsWith( "code" ) ) {
|
||||
this.code = url.replace( "code=", "" ).replace( "&state=yuque_authorize", "" );
|
||||
this.dtd.resolve();
|
||||
} else {
|
||||
this.dtd.reject();
|
||||
}
|
||||
}
|
||||
|
||||
Auth( callback ) {
|
||||
$.ajax({
|
||||
url : " https://www.yuque.com/oauth2/token",
|
||||
type : "POST",
|
||||
data : {
|
||||
client_id : this.client_id,
|
||||
client_secret : this.client_secret,
|
||||
code : this.code,
|
||||
grant_type : "authorization_code",
|
||||
redirect_uri : this.redirect_uri,
|
||||
}
|
||||
}).done( ( result, textStatus, jqXHR ) => {
|
||||
if ( result ) {
|
||||
this.access_token = result.access_token;
|
||||
this.token_type = result.token_type;
|
||||
callback( result, undefined );
|
||||
} else {
|
||||
callback( undefined, "error" );
|
||||
}
|
||||
}).fail( ( jqXHR, textStatus, error ) => {
|
||||
console.error( jqXHR, textStatus, error )
|
||||
callback( undefined, textStatus );
|
||||
});
|
||||
}
|
||||
|
||||
GetUser( callback ) {
|
||||
$.ajax({
|
||||
url : "https://www.yuque.com/api/v2/user",
|
||||
type : "GET",
|
||||
headers : {
|
||||
"Content-Type": "application/json",
|
||||
"X-Auth-Token": this.access_token
|
||||
},
|
||||
}).done( ( result, status, xhr ) => {
|
||||
if ( status == "success" ) {
|
||||
this.user_id = result.data.id;
|
||||
callback( result, undefined );
|
||||
} else callback( result, "error" );
|
||||
}).fail( ( xhr, status, error ) => {
|
||||
callback( undefined, error );
|
||||
});
|
||||
}
|
||||
|
||||
GetRepos( callback ) {
|
||||
$.ajax({
|
||||
url : `https://www.yuque.com/api/v2/users/${this.user_id}/repos`,
|
||||
type : "GET",
|
||||
headers : {
|
||||
"Content-Type": "application/json",
|
||||
"X-Auth-Token": this.access_token
|
||||
},
|
||||
}).done( ( result, status, xhr ) => {
|
||||
if ( status == "success" ) {
|
||||
result.data.forEach( item => {
|
||||
if ( item.slug == "simpread" ) {
|
||||
this.repos_id = item.id;
|
||||
}
|
||||
});
|
||||
callback( result, undefined );
|
||||
} else callback( result, "error" );
|
||||
}).fail( ( xhr, status, error ) => {
|
||||
callback( undefined, xhr );
|
||||
});
|
||||
}
|
||||
|
||||
CreateRepo( callback ) {
|
||||
const data = {
|
||||
name: "SimpRead",
|
||||
slug: "simpread",
|
||||
description: "来自简悦的收藏",
|
||||
public: 0,
|
||||
type: "Book",
|
||||
};
|
||||
$.ajax({
|
||||
url : `https://www.yuque.com/api/v2/users/${this.user_id}/repos`,
|
||||
type : "POST",
|
||||
headers : {
|
||||
"Content-Type": "application/json",
|
||||
"X-Auth-Token": this.access_token
|
||||
},
|
||||
data : JSON.stringify( data )
|
||||
}).done( ( result, status, xhr ) => {
|
||||
if ( status == "success" ) {
|
||||
this.repos_id = result.data.id;
|
||||
callback( result, undefined );
|
||||
} else callback( result, "error" );
|
||||
}).fail( ( xhr, status, error ) => {
|
||||
callback( undefined, xhr );
|
||||
});
|
||||
}
|
||||
|
||||
Add( title, body, callback ) {
|
||||
const data = {
|
||||
title,
|
||||
slug: Math.round(+new Date()),
|
||||
public: 0,
|
||||
body,
|
||||
};
|
||||
$.ajax({
|
||||
url : `https://www.yuque.com/api/v2/repos/${this.repos_id}/docs`,
|
||||
type : "POST",
|
||||
headers : {
|
||||
"Content-Type": "application/json",
|
||||
"X-Auth-Token": `${this.access_token}`
|
||||
},
|
||||
data : JSON.stringify( data )
|
||||
}).done( ( result, status, xhr ) => {
|
||||
status == "success" && callback( result, undefined );
|
||||
status != "success" && callback( undefined, "error" );
|
||||
}).fail( ( xhr, status, error ) => {
|
||||
console.error( xhr, status, error )
|
||||
callback( undefined, error.toLowerCase() == "unauthorized" ? `${ this.name } 授权过期,请重新授权。` : "error" );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kindle
|
||||
*
|
||||
@ -996,8 +1298,12 @@ function name( type ) {
|
||||
return "印象笔记";
|
||||
} else if ( type == "gdrive" ) {
|
||||
return "Google 云端硬盘";
|
||||
} else if ( type == "jianguo" ) {
|
||||
return "坚果云";
|
||||
} else if ( type == "yuque" ) {
|
||||
return "语雀";
|
||||
}
|
||||
return type;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1017,6 +1323,18 @@ function mdWrapper( content, name, notify ) {
|
||||
return dtd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown to HTML
|
||||
*
|
||||
* @param {string} content
|
||||
*/
|
||||
function md2HTML( content ) {
|
||||
const markdown = puplugin.Plugin( "markdown" ),
|
||||
converter = new markdown.default.Converter();
|
||||
converter.setOption( 'noHeaderId', true );
|
||||
return converter.makeHtml( content );
|
||||
}
|
||||
|
||||
let noti; // notify variable
|
||||
|
||||
/**
|
||||
@ -1029,7 +1347,7 @@ let noti; // notify variable
|
||||
* @param {object} notify
|
||||
*/
|
||||
function serviceCallback( result, error, name, type, notify ) {
|
||||
noti.complete();
|
||||
noti && noti.complete();
|
||||
!error && notify.Render( `已成功保存到 ${name}!` );
|
||||
![ "evernote", "yinxiang" ].includes( type ) && error && notify.Render( 2, error == "error" ? "保存失败,请稍后重新再试。" : error );
|
||||
if ( error && error.includes( "重新授权" )) {
|
||||
@ -1074,6 +1392,9 @@ const dropbox = new Dropbox(),
|
||||
evernote = new Evernote(),
|
||||
onenote = new Onenote(),
|
||||
gdrive = new GDrive(),
|
||||
yuque = new Yuque(),
|
||||
jianguo = new Jianguo(),
|
||||
webdav = new WebDAV(),
|
||||
kindle = new Kindle();
|
||||
|
||||
export {
|
||||
@ -1083,9 +1404,10 @@ export {
|
||||
markdown as Markdown,
|
||||
download as Download,
|
||||
prueDownload as PrDownload,
|
||||
md2HTML as MD2HTML,
|
||||
unlink as Unlink,
|
||||
name as Name,
|
||||
dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive,
|
||||
dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive,yuque, jianguo, webdav,
|
||||
kindle,
|
||||
mdWrapper as MDWrapper,
|
||||
serviceCallback as svcCbWrapper,
|
||||
|
||||
@ -25,6 +25,7 @@ function start() {
|
||||
$( "html" ).off( "mousemove", mousemoveEvent );
|
||||
$prev = undefined;
|
||||
dtd.resolve( event.target );
|
||||
return false;
|
||||
});
|
||||
$( "html" ).one( "keydown", event => {
|
||||
if ( event.keyCode == 27 && $prev ) {
|
||||
@ -39,6 +40,40 @@ function start() {
|
||||
return dtd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi Highlight
|
||||
*
|
||||
* @return {func} callback
|
||||
*/
|
||||
function multi( callback ) {
|
||||
let $prev;
|
||||
const mousemoveEvent = event => {
|
||||
if ( !$prev ) {
|
||||
$( event.target ).addClass( highlight_class );
|
||||
} else {
|
||||
$prev.removeClass( highlight_class );
|
||||
$( event.target ).addClass( highlight_class );
|
||||
}
|
||||
$prev = $( event.target );
|
||||
},
|
||||
removeDomHander = event => {
|
||||
callback( event.target );
|
||||
return false;
|
||||
};
|
||||
$( "html" ).on( "click", removeDomHander );
|
||||
$( "html" ).on( "keydown", event => {
|
||||
if ( event.keyCode == 27 && $prev ) {
|
||||
$( "html" ).find( `.${highlight_class}` ).removeClass( highlight_class );
|
||||
$( "html" ).off( "mousemove", mousemoveEvent );
|
||||
$( "html" ).off( "click", removeDomHander );
|
||||
$prev = undefined;
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$( "html" ).on( "mousemove", mousemoveEvent );
|
||||
}
|
||||
|
||||
function annotate() {
|
||||
const dtd = $.Deferred();
|
||||
$( "html" ).one( "mouseup", event => {
|
||||
@ -60,7 +95,52 @@ function annotate() {
|
||||
return dtd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight and controlbar
|
||||
*
|
||||
* @param {element} dom
|
||||
*/
|
||||
function controlbar( dom ) {
|
||||
let $target = $( dom );
|
||||
const dtd = $.Deferred(),
|
||||
tmpl = `<simpread-highlight>
|
||||
<sr-highlight-ctl class="add"><svg t="1560496884701" viewBox="0 0 1024 1024" version="1.1" width="15" height="15"><defs><style type="text/css"></style></defs><path d="M876.089 439.182h-291.271v-291.271c0-40.268-32.549-72.818-72.818-72.818s-72.818 32.549-72.818 72.818v291.271h-291.271c-40.268 0-72.818 32.549-72.818 72.818s32.549 72.818 72.818 72.818h291.271v291.271c0 40.268 32.549 72.818 72.818 72.818s72.818-32.549 72.818-72.818v-291.271h291.271c40.268 0 72.818-32.549 72.818-72.818s-32.549-72.818-72.818-72.818z" p-id="1998" fill="#ffffff"></path></svg></sr-highlight-ctl>
|
||||
<sr-highlight-ctl class="sub"><svg t="1560496679351" viewBox="0 0 1024 1024" version="1.1" width="15" height="15"><defs><style type="text/css"></style></defs><path d="M127.289058 490.154459l0 43.770899c0 32.338522 27.009144 57.108672 58.774615 58.734706 0 0 132.448568 13.021571 325.936327 13.021571s325.936327-13.021571 325.936327-13.021571c31.765471-1.626034 58.774615-26.396183 58.774615-58.734706l0-43.770899c0-32.338522-26.51591-57.068763-58.774615-58.734706 0 0-128.005372-12.005428-325.942467-12.005428s-325.930187 12.005428-325.930187 12.005428C153.804968 433.085696 127.289058 457.815937 127.289058 490.154459z" p-id="3204" fill="#ffffff"></path></svg></sr-highlight-ctl>
|
||||
<sr-highlight-ctl class="done"><svg t="1560496955945" viewBox="0 0 1024 1024" version="1.1" width="15" height="15"><defs><style type="text/css"></style></defs><path d="M416.832 798.08C400.64 798.08 384.512 791.872 372.16 779.52L119.424 525.76C94.784 500.992 94.784 460.8 119.424 436.032 144.128 411.264 184.128 411.264 208.768 436.032L416.832 644.928 814.4 245.76C839.04 220.928 879.04 220.928 903.744 245.76 928.384 270.528 928.384 310.656 903.744 335.424L461.504 779.52C449.152 791.872 432.96 798.08 416.832 798.08Z" p-id="1755" fill="#ffffff"></path></svg></sr-highlight-ctl>
|
||||
<sr-highlight-ctl class="none"><svg t="1560499513561" viewBox="0 0 1024 1024" version="1.1" width="15" height="15"><defs><style type="text/css"></style></defs><path d="M649.179 512l212.839-212.84c37.881-37.881 37.881-99.298 0-137.179s-99.298-37.881-137.179 0L512 374.821l-212.839-212.84c-37.881-37.881-99.298-37.881-137.179 0s-37.881 99.298 0 137.179L374.821 512 161.982 724.84c-37.881 37.881-37.881 99.297 0 137.179 18.94 18.94 43.765 28.41 68.589 28.41 24.825 0 49.649-9.47 68.589-28.41L512 649.179l212.839 212.84c18.94 18.94 43.765 28.41 68.589 28.41s49.649-9.47 68.59-28.41c37.881-37.882 37.881-99.298 0-137.179L649.179 512z" p-id="1990" fill="#ffffff"></path></svg></sr-highlight-ctl>
|
||||
<sr-highlight-ctl class="help"><svg t="1560573280563" viewBox="0 0 1024 1024" version="1.1" width="15" height="15"><defs><style type="text/css"></style></defs><path d="M512 958.326255c247.255337 0 447.696462-200.441125 447.696462-447.696462s-200.441125-447.696462-447.696462-447.696462-447.696462 200.441125-447.696462 447.696462S264.74364 958.326255 512 958.326255zM512 217.681788c35.32146 0 63.956637 28.635177 63.956637 63.956637 0 35.323507-28.635177 63.956637-63.956637 63.956637s-63.956637-28.633131-63.956637-63.956637C448.043363 246.316965 476.67854 217.681788 512 217.681788zM448.043363 510.629793c0-35.32146 28.635177-63.956637 63.956637-63.956637s63.956637 28.635177 63.956637 63.956637l0 223.848231c0 35.323507-28.635177 63.956637-63.956637 63.956637s-63.956637-28.633131-63.956637-63.956637L448.043363 510.629793z" p-id="1979" fill="#ffffff"></path></svg></sr-highlight-ctl>
|
||||
</simpread-highlight>`;
|
||||
$target.addClass( "simpread-highlight-controlbar" )
|
||||
$("html").append( tmpl );
|
||||
$("html").find( "sr-highlight-ctl" ).on( "click", event => {
|
||||
const cls = $( event.currentTarget ).attr( "class" );
|
||||
if ( cls == "add" ) {
|
||||
$target.removeClass( "simpread-highlight-controlbar" );
|
||||
$target = $target.parent();
|
||||
$target.addClass( "simpread-highlight-controlbar" );
|
||||
} else if ( cls == "sub" ) {
|
||||
$target.removeClass( "simpread-highlight-controlbar" );
|
||||
$target = $($target.children()[0]);
|
||||
$target.addClass( "simpread-highlight-controlbar" );
|
||||
} else if ( cls == "none" ) {
|
||||
$target.removeClass( "simpread-highlight-controlbar" );
|
||||
$( "simpread-highlight" ).remove();
|
||||
} else if ( cls == "help" ) {
|
||||
const $a = $( `<a style="display:none" target="_blank" href="http://ksria.com/simpread/docs/#/手动框选"></a>` ).appendTo( "body" );
|
||||
$a[0].click();
|
||||
$a.remove();
|
||||
} else {
|
||||
$target.removeClass( "simpread-highlight-controlbar" );
|
||||
$( "simpread-highlight" ).remove();
|
||||
dtd.resolve( $target[0] );
|
||||
}
|
||||
});
|
||||
return dtd;
|
||||
}
|
||||
|
||||
export {
|
||||
start as Start,
|
||||
multi as Multi,
|
||||
annotate as Annotate,
|
||||
controlbar as Control,
|
||||
}
|
||||
@ -34,6 +34,13 @@ const action = {
|
||||
temp_site : "temp_site",
|
||||
// corb
|
||||
CORB : "corb",
|
||||
// webdav
|
||||
jianguo : "jianguo",
|
||||
WebDAV : "webdav",
|
||||
// event
|
||||
turn_tab : "turn_tab",
|
||||
welcome_close : "welcome_close",
|
||||
"controlbar" : "simpread-plugin_controlbar",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
console.log( "=== simpread control:action load ===" )
|
||||
console.log( "=== simpread output load ===" )
|
||||
|
||||
import * as util from 'util';
|
||||
import * as exp from 'export';
|
||||
@ -138,8 +138,8 @@ function action( type, title, desc, content ) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ( [ "dropbox", "pocket", "instapaper", "linnk", "yinxiang","evernote", "onenote", "gdrive" ].includes( type ) ) {
|
||||
const { dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive } = exp,
|
||||
} else if ( [ "dropbox", "pocket", "instapaper", "linnk", "yinxiang","evernote", "onenote", "gdrive", "jianguo", "yuque" ].includes( type ) ) {
|
||||
const { dropbox, pocket, instapaper, linnk, evernote, onenote, gdrive, jianguo, yuque } = exp,
|
||||
id = type == "yinxiang" ? "evernote" : type;
|
||||
storage.Statistics( "service", type );
|
||||
const service = type => {
|
||||
@ -174,18 +174,29 @@ function action( type, title, desc, content ) {
|
||||
evernote.Add( title, util.HTML2ENML( content, window.location.href ), ( result, error ) => {
|
||||
exp.svcCbWrapper( result, error, evernote.name, type, new Notify() );
|
||||
if ( error == "error" ) {
|
||||
new Notify().Render({ content: "导出失败,是否以 Markdown 格式保存?", action: "是的", cancel: "取消", callback: action => {
|
||||
if ( action == "cancel" ) return;
|
||||
new Notify().Render({ content: "转换为 Markdown 并保存中,请稍等...", delay: 2000 } );
|
||||
exp.MDWrapper( util.ClearMD( content, false ), undefined, new Notify() ).done( result => {
|
||||
content = util.MD2ENML( result );
|
||||
service( type );
|
||||
new Notify().Render( "保存失败,正在尝试优化结构再次保存,请稍等..." );
|
||||
exp.MDWrapper( util.ClearMD( content, false ), undefined, new Notify() ).done( result => {
|
||||
const md = util.MD2ENML( result ),
|
||||
tmpl = util.ClearHTML( exp.MD2HTML( result ));
|
||||
evernote.Add( title, tmpl, ( result, error ) => {
|
||||
exp.svcCbWrapper( result, error, evernote.name, type, new Notify() );
|
||||
if ( error == "error" ) {
|
||||
new Notify().Render({ content: "导出失败,是否以 Markdown 格式保存?", action: "是的", cancel: "取消", callback: action => {
|
||||
if ( action == "cancel" ) return;
|
||||
new Notify().Render({ content: "转换为 Markdown 并保存中,请稍等...", delay: 2000 } );
|
||||
evernote.Add( title, util.HTML2ENML( md, window.location.href ), ( result, error ) => {
|
||||
exp.svcCbWrapper( result, error, evernote.name, type, new Notify() );
|
||||
if ( error == "error" ) {
|
||||
new Notify().Render({ content: `转换后保存失败,是否提交当前站点?`, action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.save_site, { url: location.href, site: {}, uid: storage.user.uid, type: "evernote" }));
|
||||
}});
|
||||
}
|
||||
});
|
||||
}});
|
||||
}
|
||||
});
|
||||
}});
|
||||
new Notify().Render({ content: `此功能为实验性功能,是否提交当前站点?`, action: "是的", cancel: "取消", callback: type => {
|
||||
if ( type == "cancel" ) return;
|
||||
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.save_site, { url: location.href, site: storage.pr.current.site, uid: storage.user.uid, type: "evernote" }));
|
||||
}});
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
@ -194,10 +205,27 @@ function action( type, title, desc, content ) {
|
||||
break;
|
||||
case "gdrive":
|
||||
storage.pr.current.site.avatar[0].name != "" && ( content = util.MULTI2ENML( content ) );
|
||||
exp.MDWrapper( util.ClearMD( content), undefined, new Notify() ).done( result => {
|
||||
exp.MDWrapper( util.ClearMD( content ), undefined, new Notify() ).done( result => {
|
||||
gdrive.Add( "file",( result, error ) => exp.svcCbWrapper( result, error, gdrive.name, type, new Notify() ), gdrive.CreateFile( `${title}.md`, result ));
|
||||
});
|
||||
break;
|
||||
case "jianguo":
|
||||
exp.MDWrapper( util.ClearMD( content ) , undefined, new Notify() ).done( markdown => {
|
||||
title = title.replace( /[|@!#$%^&*()<>/,.+=\\]/ig, "-" );
|
||||
jianguo.Add( storage.secret.jianguo.username, storage.secret.jianguo.password, `${jianguo.root}/${jianguo.folder}/${title}.md`, markdown, result => {
|
||||
let error = undefined;
|
||||
if ( result && ( result.status != 201 && result.status != 204 )) {
|
||||
error = "导出到坚果云失败,请稍后再试。";
|
||||
}
|
||||
exp.svcCbWrapper( result, error, jianguo.name, type, new Notify() );
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "yuque":
|
||||
exp.MDWrapper( util.ClearMD( content ), undefined, new Notify() ).done( result => {
|
||||
yuque.Add( title, result,( result, error ) => exp.svcCbWrapper( result, error, yuque.name, type, new Notify() ));
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@ -212,6 +240,26 @@ function action( type, title, desc, content ) {
|
||||
}
|
||||
} else if ( type.startsWith( "fullscreen" ) ) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else if ( type.startsWith( "webdav_" ) ) {
|
||||
const id = type.replace( "webdav_", "" );
|
||||
storage.Safe( () => {
|
||||
storage.secret.webdav.forEach( item => {
|
||||
item = JSON.parse( item );
|
||||
if ( item.name == id ) {
|
||||
exp.MDWrapper( util.ClearMD( content ) , undefined, new Notify() ).done( markdown => {
|
||||
title = title.replace( /[|@!#$%^&*()<>/,.+=\\]/ig, "-" );
|
||||
new Notify().Render( `开始保存到 ${ item.name},请稍等...` );
|
||||
exp.webdav.Add( item.url, item.user, item.password, `${title}.md`, markdown, result => {
|
||||
let error = undefined;
|
||||
if ( result && ( result.status != 201 && result.status != 204 )) {
|
||||
error = `导出到 ${item.name} 失败,请稍后再试。`;
|
||||
}
|
||||
exp.svcCbWrapper( result, error, item.name, type, new Notify() );
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
else {
|
||||
new Notify().Render( 2, "当前模式下,不支持此功能。" );
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
console.log( "=== simpread runtime load ===" )
|
||||
|
||||
import nanoid from 'nanoid';
|
||||
import {browser} from 'browser';
|
||||
import nanoid from 'nanoid';
|
||||
|
||||
import {browser} from 'browser';
|
||||
import {storage, Clone} from 'storage';
|
||||
import * as highlight from 'highlight';
|
||||
import * as watch from 'watch';
|
||||
|
||||
let is_firstload = true;
|
||||
|
||||
/**
|
||||
* Generate ID
|
||||
@ -40,6 +45,16 @@ function install( id, url, callback ) {
|
||||
.fail( () => callback( undefined, "error" ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch event
|
||||
*
|
||||
* @param {string} type include: export, read_ui, read_start, read_end
|
||||
* @param {string} value
|
||||
*/
|
||||
function dispatch( type, value ) {
|
||||
window.dispatchEvent( new CustomEvent( "simpread-plugin", { detail: { type, value }}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
@ -48,12 +63,16 @@ function install( id, url, callback ) {
|
||||
* @param {object} plugin object
|
||||
*/
|
||||
function exec( state, site, plugin ) {
|
||||
if ( plugin.enable == false ) return;
|
||||
if ( plugin.run_at != state ) return;
|
||||
if ( plugin.site != "" && !plugin.site.split(",").includes( site ) ) return;
|
||||
console.log( "current plugin is running", plugin )
|
||||
new Function( func( plugin.script ) )();
|
||||
plugin.style != "" && addStyle( plugin.style );
|
||||
try {
|
||||
if ( plugin.enable == false ) return;
|
||||
if ( plugin.run_at != state ) return;
|
||||
if ( plugin.site != "" && !plugin.site.split(",").includes( site ) ) return;
|
||||
console.log( "current plugin is running", plugin )
|
||||
new Function( func( plugin.script ) )();
|
||||
plugin.style != "" && addStyle( plugin.style );
|
||||
} catch ( error ) {
|
||||
new Notify().Render( 2, `插件 ${ plugin.name } 运行时出错,可以的话,请 <a href="https://github.com/Kenshin/simpread/issues/new" target="_blank">提交此问题</a> 😁` );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,11 +83,71 @@ function exec( state, site, plugin ) {
|
||||
function func( source ) {
|
||||
window.Notify = Notify;
|
||||
window.browser = browser;
|
||||
window.current = Clone( storage.pr.current );
|
||||
window.current = Clone( storage.current );
|
||||
window.read = Clone( storage.read );
|
||||
return `( function ( $$version, $title, $desc, $content, $footer, $process, $toc, Notify, browser, $$current, $$read ) {
|
||||
window.highlight = highlight;
|
||||
window.db = Storage;
|
||||
window.control = Controlbar;
|
||||
window.SRSave = Save;
|
||||
return `( function ( $$version, $read, $title, $desc, $content, $footer, $process, $toc, Notify, $$highlight, browser, $$storage, $$current, $$read, $$control, $$save ) {
|
||||
${ source }
|
||||
})( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, current, read );`
|
||||
})( "0.0.2", $( "sr-read" ), $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, highlight, browser, db, current, read, control, SRSave );`
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter / Setter plugin config
|
||||
*
|
||||
* @param {string} plugin id
|
||||
* @param {object} data
|
||||
* @param {func} callback
|
||||
*/
|
||||
function Storage( id, data, callback ) {
|
||||
if ( data ) {
|
||||
browser.storage.local.set( { ["plugin-"+id] : data }, () => {
|
||||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
browser.storage.local.get( ["plugin-"+id], result => {
|
||||
callback && callback( result );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contorlbar setting
|
||||
*
|
||||
* @param {string} controlbar type include: markdown
|
||||
* @param {func} callback
|
||||
*/
|
||||
function Controlbar( type, callback ) {
|
||||
if ( callback ) {
|
||||
is_firstload && window.addEventListener( "simpread-plugin_controlbar", event => {
|
||||
callback( event );
|
||||
});
|
||||
} else window.dispatchEvent( new CustomEvent( "simpread-plugin_controlbar", { detail: { type }}));
|
||||
is_firstload = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save adapter storage.Write
|
||||
*
|
||||
* @param {object} simpread data structure
|
||||
* @param {func} callback
|
||||
*/
|
||||
function Save( data, callback ) {
|
||||
if ( data ) {
|
||||
storage.Write( ()=> {
|
||||
watch.SendMessage( "option", true );
|
||||
callback();
|
||||
}, storage.simpread );
|
||||
} else {
|
||||
return {
|
||||
focus : storage.focus,
|
||||
read : storage.read,
|
||||
option : storage.option,
|
||||
version: storage.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,15 +159,45 @@ function addStyle( str ) {
|
||||
$( "head" ).append(`<style type="text/css">${str}</style>`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add trigger
|
||||
*
|
||||
* @param {string} add trigger to fap controlbar
|
||||
*/
|
||||
function addTrigger( str ) {
|
||||
let is_found = false;
|
||||
$( "fap action-bar" ).find( "sr-opt-label" ).map( ( idx, item ) => {
|
||||
if ( $(item).text() == "插件触发器" ) {
|
||||
is_found = true;
|
||||
$(item).next().append( str );
|
||||
}
|
||||
});
|
||||
if ( is_found == false ) {
|
||||
const html = `<sr-opt-gp>
|
||||
<sr-opt-label>插件触发器</sr-opt-label>
|
||||
<actions style="display:flex;margin:10px 0;flex-wrap:wrap;">
|
||||
${str}
|
||||
</actions>
|
||||
</sr-opt-gp>`;
|
||||
$( "fap action-bar" ).append( html );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Plugin
|
||||
*
|
||||
* @param {func} style func
|
||||
* @param {func} plugin func
|
||||
* @param {func} trigger func
|
||||
*/
|
||||
function testPlugin( style, plugin ) {
|
||||
function testPlugin( style, plugin, trigger ) {
|
||||
style && addStyle( style() );
|
||||
plugin && plugin( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, storage.pr.current, storage.read );
|
||||
plugin && plugin( "0.0.2",
|
||||
$( "sr-read" ), $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ),
|
||||
Notify, highlight,
|
||||
browser, Storage,
|
||||
storage.current, storage.read, Controlbar, Save );
|
||||
trigger && addTrigger( trigger() );
|
||||
}
|
||||
|
||||
window.simpread = { testPlugin };
|
||||
@ -97,4 +206,6 @@ export {
|
||||
install as Install,
|
||||
exec as Exec,
|
||||
generateID as ID,
|
||||
dispatch as Event,
|
||||
Controlbar,
|
||||
}
|
||||
@ -12,10 +12,11 @@ import {version,patch}from 'version';
|
||||
*/
|
||||
|
||||
const name = "simpread",
|
||||
remote = "http://sr.ksria.cn/website_list_v3.json",
|
||||
remote = "http://sr.ksria.cn/website_list_v4.json",
|
||||
origins= "http://sr.ksria.cn/website_list_origins.json",
|
||||
versions= "http://sr.ksria.cn/versions.json",
|
||||
local = browser.extension.getURL( "website_list.json" ),
|
||||
help = browser.extension.getURL( "help_tips.json" ),
|
||||
mode = {
|
||||
focus : "focus",
|
||||
read : "read",
|
||||
@ -46,7 +47,7 @@ const name = "simpread",
|
||||
},
|
||||
read = {
|
||||
version : "2017-03-16",
|
||||
progress : true,
|
||||
progress : false,
|
||||
auto : false,
|
||||
controlbar: true,
|
||||
fap : true,
|
||||
@ -107,11 +108,18 @@ const name = "simpread",
|
||||
create : "",
|
||||
update : "",
|
||||
sync : "",
|
||||
save_at : "dropbox", // include: dropbox | jianguo
|
||||
notice : true,
|
||||
//focus : 0,
|
||||
//read : 0,
|
||||
esc : true,
|
||||
br_exit : false,
|
||||
secret : false,
|
||||
preload : true,
|
||||
lazyload : [
|
||||
"baidu.com", "weibo.com", "youtube.com"
|
||||
],
|
||||
uninstall : true,
|
||||
menu : {
|
||||
focus : true,
|
||||
read : true,
|
||||
@ -125,6 +133,11 @@ const name = "simpread",
|
||||
origins : [],
|
||||
blacklist : [
|
||||
"google.com",
|
||||
"youtube.com",
|
||||
"simp.red",
|
||||
"simpread.herokuapp.com",
|
||||
"simpread-test.herokuapp.com",
|
||||
"simpread.ksria.cn"
|
||||
],
|
||||
plugins : [], // plugin id, e.g. kw36BtjGu0
|
||||
},
|
||||
@ -148,6 +161,8 @@ const name = "simpread",
|
||||
"gdrive" : 0,
|
||||
"kindle" : 0,
|
||||
"temp" : 0,
|
||||
"yuque" : 0,
|
||||
"jianguo" : 0,
|
||||
}
|
||||
},
|
||||
user = {
|
||||
@ -158,6 +173,10 @@ const name = "simpread",
|
||||
avatar : "",
|
||||
permission: "",
|
||||
},
|
||||
notice = {
|
||||
latest: 0,
|
||||
read : []
|
||||
},
|
||||
unread = {
|
||||
idx : 0,
|
||||
create : "",
|
||||
@ -184,11 +203,12 @@ let current = {},
|
||||
local : [], // include focus.sites and read.sites
|
||||
},
|
||||
statistics,
|
||||
notice,
|
||||
user,
|
||||
},
|
||||
plugins = {},
|
||||
secret = {
|
||||
version : "2017-11-22",
|
||||
version : "2019-06-08",
|
||||
"dropbox" : {
|
||||
"access_token": ""
|
||||
},
|
||||
@ -217,6 +237,16 @@ let current = {},
|
||||
access_token : "",
|
||||
folder_id : "",
|
||||
},
|
||||
"yuque" : {
|
||||
access_token : "",
|
||||
repos_id: "",
|
||||
},
|
||||
"jianguo" : {
|
||||
username : "",
|
||||
password : "",
|
||||
access_token : "",
|
||||
},
|
||||
"webdav" : []
|
||||
};
|
||||
//stcode = -1;
|
||||
|
||||
@ -278,6 +308,15 @@ class Storage {
|
||||
return simpread[ mode.unrdist ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notice
|
||||
*
|
||||
* @return {object} notice
|
||||
*/
|
||||
get notice() {
|
||||
return simpread.notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version
|
||||
*
|
||||
@ -392,6 +431,27 @@ class Storage {
|
||||
return "https://simpread.ksria.cn";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notice service url
|
||||
*
|
||||
* @return {string} url
|
||||
*/
|
||||
get notice_service() {
|
||||
return {
|
||||
latest: "http://simp.red/notice/latest",
|
||||
message: "http://simp.red/notice",
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get help service url
|
||||
*
|
||||
* @return {string} url
|
||||
*/
|
||||
get help_service() {
|
||||
return "http://sr.ksria.cn/help_tips.json";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get simpread object from chrome storage
|
||||
*
|
||||
@ -562,6 +622,9 @@ class Storage {
|
||||
case "versions":
|
||||
url = versions;
|
||||
break;
|
||||
case "help_tips":
|
||||
url = help;
|
||||
break;
|
||||
default:
|
||||
url = type;
|
||||
}
|
||||
@ -700,6 +763,30 @@ class Storage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notice set/get
|
||||
*
|
||||
* @param {object} notice
|
||||
* @param {function} callback
|
||||
*/
|
||||
Notice( callback, data ) {
|
||||
if ( data ) {
|
||||
browser.storage.local.set( { ["notice"] : data }, () => {
|
||||
console.log( "chrome storage notice set success!", data );
|
||||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
if ( br.isFirefox() && window.location.protocol != "moz-extension:" ) {
|
||||
callback && callback();
|
||||
} else {
|
||||
browser.storage.local.get( ["notice"], result => {
|
||||
console.log( "chrome storage notice get success!", result );
|
||||
callback && callback( result );
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins set/get, plugins not import/export
|
||||
*
|
||||
@ -723,7 +810,7 @@ class Storage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Export, only include: version, option, focus, read, unrdist
|
||||
* Export
|
||||
*
|
||||
* @return {string} object json stringify
|
||||
*/
|
||||
@ -736,6 +823,7 @@ class Storage {
|
||||
websites: { ...this.websites },
|
||||
statistics: { ...this.statistics },
|
||||
user : { ...this.user },
|
||||
notice : { ...this.notice },
|
||||
unrdist : this.unrdist,
|
||||
};
|
||||
this.option.secret && ( download.secret = { ...secret });
|
||||
|
||||
@ -27,13 +27,14 @@ function verifyHtml( html ) {
|
||||
- [['text']] // remove '<text>'
|
||||
- [[/regexp/]] // regexp e.g. $("sr-rd-content").find( "*[src='http://ifanr-cdn.b0.upaiyun.com/wp-content/uploads/2016/09/AppSo-qrcode-signature.jpg']" )
|
||||
- [[[juqery code]]] // new Function, e.g. $("xxx").find() return jquery object
|
||||
- [[`xpath`]] // /html[1]/div[1]/sr-read[1]/sr-rd-content[1]/p[1]
|
||||
|
||||
*
|
||||
* @param {string} verify content
|
||||
* @return {boolen} verify result
|
||||
*/
|
||||
function specTest( content ) {
|
||||
return /^(\[\[)[\[{'/]{1}[ \S]+[}'/\]]\]\]{1}($)/g.test( content );
|
||||
return /^(\[\[)[\[{`'/]{1}[ \S]+[}`'/\]]\]\]{1}($)/g.test( content );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,8 +80,9 @@ function html2enml( html, url ) {
|
||||
$target.remove();
|
||||
|
||||
try {
|
||||
str = `<blockquote>本文由 <a href="http://ksria.com/simpread" target="_blank">简悦 SimpRead</a> 转码,原文地址 <a href="${url}" target="_blank">${url}</a></blockquote><hr></hr><br></br>` + str;
|
||||
str = str.replace( /(id|class|onclick|ondblclick|accesskey|data|dynsrc|tabindex|name)="[\S ][^"]+"/g, "" )
|
||||
const href = url.indexOf("chksm") > 0 ? "" : `,原文地址 <a href="${url}" target="_blank">${url}</a>`;
|
||||
str = `<blockquote>本文由 <a href="http://ksria.com/simpread" target="_blank">简悦 SimpRead</a> 转码${href}</blockquote><hr></hr><br></br>` + str;
|
||||
str = str.replace( /(id|class|onclick|ondblclick|accesskey|data|dynsrc|tabindex|name)="[\S ][^"]*"/ig, "" )
|
||||
//.replace( / style=[ \w="-:\/\/:#;]+/ig, "" ) // style="xxxx"
|
||||
.replace( /label=[\u4e00-\u9fa5 \w="-:\/\/:#;]+"/ig, "" ) // label="xxxx"
|
||||
.replace( / finallycleanhtml=[\u4e00-\u9fa5 \w="-:\/\/:#;]+"/ig, "" ) // finallycleanhtml="xxxx"
|
||||
@ -149,6 +151,21 @@ function clearMD( str, header = true ) {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean HTML
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {string} optimze str
|
||||
*/
|
||||
function clearHTML( str ) {
|
||||
const url = location.href,
|
||||
href = url.indexOf("chksm") > 0 || url.indexOf("#") > 0 ? "" : `,原文地址 <a href="${url}" target="_blank">${url}</a>`;
|
||||
str = `<blockquote>本文由 <a href="http://ksria.com/simpread" target="_blank">简悦 SimpRead</a> 转码${href}</blockquote><hr></hr><br></br>` + str;
|
||||
str = str.replace( /(id|class|onclick|ondblclick|accesskey|data|dynsrc|tabindex|name)="[\S ][^"]*"/ig, "" )
|
||||
.replace( /&/ig, "&" )
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusion
|
||||
*
|
||||
@ -185,6 +202,7 @@ export {
|
||||
md2enml as MD2ENML,
|
||||
multi2enml as MULTI2ENML,
|
||||
clearMD as ClearMD,
|
||||
clearHTML as ClearHTML,
|
||||
exclusion as Exclusion,
|
||||
whitelist as Whitelist,
|
||||
}
|
||||
@ -18,6 +18,7 @@ const version = browser.runtime.getManifest().version.replace( /.\d{2,}/, "" ),
|
||||
[ "1.1.0", "Sat Dec 23 2017 15:09:30 GMT+0800 (CST)" ],
|
||||
[ "1.1.1", "Mon Jun 11 2018 15:10:12 GMT+0800 (CST)" ],
|
||||
[ "1.1.2", "Tue Jun 19 2018 14:15:12 GMT+0800 (CST)" ],
|
||||
[ "1.1.3", "Thu Jun 06 2019 15:47:44 GMT+0800 (CST)" ],
|
||||
]),
|
||||
details = new Map([
|
||||
[ "1.0.0", "" ],
|
||||
@ -30,7 +31,263 @@ const version = browser.runtime.getManifest().version.replace( /.\d{2,}/, "" ),
|
||||
[ "1.1.0", "新增「站点编辑器,站点适配源,站点管理器等」," ],
|
||||
[ "1.1.1", "新增「黑名单,全新的控制栏面板,更丰富的中文定制化,无障碍阅读等」," ],
|
||||
[ "1.1.2", "新增「插件中心,站点集市等」," ],
|
||||
]);
|
||||
[ "1.1.3", "新增「消息中心,帮助中心,入门指引,支持导入语雀 / 坚果云,预加载机制,增强插件 API 等」," ],
|
||||
]),
|
||||
tips = {
|
||||
"root" : value => `.version-tips[data-hits='${value}']`,
|
||||
"1.1.3" : {
|
||||
target: 'labs',
|
||||
idx: 2,
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
intro: '简悦 1.1.3 功能描述:<br>' + details.get( "1.1.3" ) + '详细说明 <a target="_blank" href="http://ksria.com/simpread/welcome/version_1.1.3.html">请看这里</a> 。' ,
|
||||
},
|
||||
{
|
||||
id: 'save_at',
|
||||
intro: '从现在开始可以将配置文件保存到坚果云了,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/坚果云">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'preload',
|
||||
intro: '简悦的词法分析引擎采用了预加载机制,当系统性能吃紧时,可以选择关闭此功能,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=预加载机制">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'lazyload',
|
||||
intro: '此功能适合 <b>经常使用简悦但又性能不够</b> 的用户;需要动态加载的页面;支持 Mathjax 解析的页面等,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=预加载机制">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'jianguo',
|
||||
intro: '你可以在这里输入坚果云的用户名和授权的密码来绑定坚果云,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/坚果云">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'yuque',
|
||||
intro: '连接你的语雀帐号后,就可使用导出到语雀的服务了,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/授权服务">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'webdav',
|
||||
intro: '导出服务 <b>任意支持 WebDAV 协议</b> 了,从现在开始使用你熟悉的网盘吧,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/WebDAV">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'notice',
|
||||
intro: '简悦 1.1.3 版增加了消息中心,为了方便查看简悦的一些最新消息,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/消息中心">请看这里</a> 。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"common" : {
|
||||
target: 'common',
|
||||
idx: 0,
|
||||
items: [
|
||||
{
|
||||
id: 'sync',
|
||||
intro: '简悦支持导出配置文件到 Dropbox 或 坚果云,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/同步">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'config',
|
||||
intro: '从 <b>本地导入配置文件</b> 或 <b>导出配置文件到本地</b> 。<br>注意:简悦支持导入任意版本的配置文件,但请尽量上传匹配版本的配置文件。',
|
||||
},
|
||||
{
|
||||
id: 'oldnewsites',
|
||||
intro: '从 1.1.3 开始,此功能转移到 <b>站点管理</b> 选项卡里面,此功能已废除。',
|
||||
},
|
||||
{
|
||||
id: 'clear',
|
||||
intro: '清除简悦产生的全部数据,等同于重新安装,慎用!使用前 <b>请先备份</b> 。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"simple" : {
|
||||
target: 'simple',
|
||||
idx: 1,
|
||||
items: [
|
||||
{
|
||||
id: 'focusmode',
|
||||
intro: '使用 <a target="_blank" href="http://ksria.com/simpread/docs/#/聚焦模式">聚焦模式</a> 时的选项<br>包括:遮罩的主题色,遮罩的透明度,以及进入聚焦模式的快捷键。<br>这些功能也可以在进入此模式后通过右下角控制栏调整。',
|
||||
},
|
||||
{
|
||||
id: 'readmode',
|
||||
intro: '使用 <a target="_blank" href="http://ksria.com/simpread/docs/#/阅读模式">阅读模式</a> 时的选项<br>包括:主题色,进入阅读模式的快捷键,字体类型,版面布局,甚至正文的字体细调(字间距,行间距等)。<br>这些功能也可以在进入此模式后通过右下角控制栏调整。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"labs" : {
|
||||
target: 'labs',
|
||||
idx: 2,
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
intro: '本页的功能专门针对 <b>不同需求、不同使用场景</b> 的精细调整。<br>如果你是初级用户的话,完全可以无视这些调整,简悦支持 <b>开箱即用</b>。<br>如果想让阅读模式更具个性化,建议花 1 ~ 2 分钟来看下这些功能点。 😊 ' ,
|
||||
},
|
||||
{
|
||||
id: 'esc',
|
||||
intro: '启用此功能后,进入阅读模式 & 聚焦模式,可通过点击 ESC 的方式退出。',
|
||||
},
|
||||
{
|
||||
id: 'br_exit',
|
||||
intro: '点击浏览器右上角 <b>简悦 icon</b> 后的动作,包括:退出当前模式 & 弹出设置对话框。',
|
||||
},
|
||||
{
|
||||
id: 'blacklist',
|
||||
intro: '加入到列表中的 URL 对应的页面将不会运行简悦,适合一些完全不需要简悦的场合,如:视频类的网站。<br>支持绝对地址或主域名,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/FAQ?id=黑名单">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'save_at',
|
||||
intro: '从现在开始可以将配置文件保存到坚果云了,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/坚果云">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'menu',
|
||||
intro: '简悦支持右键菜单,如果你是个鼠标党的话,可以好好利用它们,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/右键菜单">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'focusconfig',
|
||||
intro: '与 <b>基础设定</b> 中不同,这里是关于聚焦模式细节的设定,同时这些选项也只能在选项页中修改。',
|
||||
},
|
||||
{
|
||||
id: 'readconfig',
|
||||
intro: '与 <b>基础设定</b> 中不同,这里是关于阅读模式细节的设定,同时这些选项也只能在选项页中修改。<br><br> <a target="_blank" href="http://ksria.com/simpread/docs/#/阅读模式">阅读模式</a> 是简悦重要的组成部分,除了常规的阅读模式外,简悦还支持多种类型,包括:<br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/论坛类页面及分页">论坛类页面及分页</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/主动适配阅读模式">主动适配</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=智能感知">智能感知</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/手动框选">手动框选</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/TXT-阅读器">TXT 阅读器</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=markdown-识别">Markdown 阅读器</a> <br> - <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=latex-识别">LaTeX 阅读器</a>',
|
||||
},
|
||||
{
|
||||
id: 'progress',
|
||||
intro: '进入阅读模式后会在页面上方显示一个阅读进度条,从 1.1.3 版开始 <b>默认为不启用</b>,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/阅读进度">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'readcontrolbar',
|
||||
intro: '进入阅读模式后,会在页面的右下角显示一个 icon 点击可查看阅读模式的一些功能,你可以在这里选择隐藏(鼠标移上时才显示)它。',
|
||||
},
|
||||
{
|
||||
id: 'fap',
|
||||
intro: '1.1.1 版开始提供 <b>控制栏浮动面板</b> 用来替代原来的 <b>控制栏浮动工具条</b>。<br>如果你并不经常使用简悦的一些高级功能,可以关闭此选项,使用更简洁的 <b>控制栏浮动工具条</b>,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/阅读模式-控制栏">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'highlight',
|
||||
intro: '在 <b>手动框选</b> 方式的基础上增加了 <b>二次确认模式</b>,此模式专门针对页面极其复杂的情况,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/手动框选">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'toc',
|
||||
intro: '进入阅读模式后,会自动生成当前页面的大纲,同时也可选择大纲的显示方式,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/目录">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'readauto',
|
||||
intro: '如果当前 <a target="_blank" href="http://ksria.com/simpread/docs/#/站点适配源">站点已适配</a> 的话,启用此选项后会自动进入到阅读模式,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/适配站点">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'exclusion',
|
||||
intro: '启用 <b>自动进入阅读模式</b> 后,可将不需要自动进入阅读模式的站加入到这个列表中,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/FAQ?id=排除列表">请看这里</a> 。<br>关闭 <b>自动进入阅读模式</b> 后,会有 <a target="_blank" href="http://ksria.com/simpread/docs/#/FAQ?id=白名单">白名单</a> 功能,与 <b>排除列表</b> 相反,加入此的站会自动进入阅读模式。',
|
||||
},
|
||||
{
|
||||
id: 'pured',
|
||||
intro: '简悦从 1.1.2.5005 开始增加了此功能,目前还处于测试版。<br>词法分析引擎会对版面重新设计,包括:去除多余空格、优化版面结构等。<br>注意:经常解析失败时,请关闭此功能,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'puredpure',
|
||||
intro: '包括:字形、颜色、字号、代码段等,如:微信订阅号,CSDN 等。<br>注意:如果经常阅读代码的话,请安装 <a target="_blank" href="https://simpread.ksria.cn/plugins/details/klGUASLasg">代码段增强</a> 插件,功能包括:高亮,去重,支持 CSDN 等特殊情况的代码段。',
|
||||
},
|
||||
{
|
||||
id: 'preload',
|
||||
intro: '简悦的词法分析引擎采用了预加载机制,当系统性能吃紧时,可以选择关闭此功能,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=预加载机制">请看这里</a> 。<br>注意:建议无特殊情况下不要关闭此功能,可以 <b>使用下一条的功能</b> 来规避性能问题。',
|
||||
},
|
||||
{
|
||||
id: 'lazyload',
|
||||
intro: '为了更快的进入到阅读模式,简悦会主动分析每个页面,但加入此列表的 URL 不会被主动分析。<br><br>此功能适合:<br><b> - 经常使用简悦但又性能不够</b> 的用户;<br> - 需要动态加载的页面;<br> - 支持 Mathjax 解析的页面等;<br><br>详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=延迟加载">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'auth',
|
||||
intro: '简悦支持常见的导出服务,你可以授权它们,导出 <b>阅读模式(简悦优化后)的页面</b> 到这些服务,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/授权服务">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'secret',
|
||||
intro: '使用导出服务后,会产生授权码,简悦默认 <b>不会在导出配置时包含它们</b>,如果需要的话,请开启此功能,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/授权服务?id=授权码">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'custom',
|
||||
intro: '简悦可以对 <b>阅读模式生成的页面</b> 更加精细的调整,甚至于 <b>使用 CSS 来深度定制</b>,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/自定义样式">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'notice',
|
||||
intro: '简悦 1.1.3 版增加了消息中心,为了方便查看简悦的一些最新消息,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/消息中心">请看这里</a> 。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"sites" : {
|
||||
target: 'sites',
|
||||
idx: 2,
|
||||
items: [
|
||||
{
|
||||
id: 'newsites',
|
||||
intro: '简悦每隔一段时间会自动同步适配列表,你也可以手动同步。<br>什么是适配列表?详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/适配站点">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'customsites',
|
||||
intro: '从 1.1.3 开始,简悦调整了第三方适配的规则:仅针对个人的适配源,关于这部分的详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/站点适配源?id=第三方适配源">请看这里</a> 。<br><b>注意:</b> 如果你使用了自己的适配源,请先清除再导入。',
|
||||
},
|
||||
{
|
||||
id: 'sitemgr',
|
||||
intro: '用来管理全部的站点,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/站点管理器">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'personsites',
|
||||
intro: '简悦用户自行上传且未收录到 <a target="_blank" href="http://ksria.com/simpread/docs/#/站点适配源?id=官方适配源">官方适配源</a> 里面的适配站点,可以在这里对这些站点进行安装,删除,更新等操作,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/站点集市">请看这里</a> 。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins" : {
|
||||
target: 'plugins',
|
||||
idx: 4,
|
||||
items: [
|
||||
{
|
||||
id: 'pluginsite',
|
||||
intro: '为了让阅读模式更加的丰富,简悦从 1.1.2 版本开始支持插件系统,插件系统 <b>仅支持阅读模式</b>。<br>点击这里打开到插件的官网,关于插件的详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/插件系统">请看这里</a> 。<br>注意:安装过多的插件会引起性能问题,建议 <b>不要超过 6 个</b> 。',
|
||||
},
|
||||
{
|
||||
id: 'pluginconfig',
|
||||
intro: '当用户上传了新的配置文件,需要手动从配置文件读取插件。<br>注意:上传配置文件后会清除当前环境的插件,所以请别忘记手动导入。',
|
||||
},
|
||||
{
|
||||
id: 'pluginupdate',
|
||||
intro: '更新已安装的全部插件到最新版本。',
|
||||
},
|
||||
{
|
||||
id: 'pluginclear',
|
||||
intro: '清除当前环境的全部插件。<br>注意:此操作并不能清除当前的配置文件,如果要清除配置文件,请前往 <b>共通 → 清除数据</b> 操作。',
|
||||
},
|
||||
{
|
||||
id: 'pluginmange',
|
||||
intro: '这是用户的已安装的全部插件,在这里进行管理,包括:禁用, 删除,更新,查看 等操作。<br>同样,在这里安装的插件可以在阅读模式下启用禁用操作,位置在 <b>阅读模式 → 右下角控制栏 → 插件(选项卡)</b> 查看。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"later" : {
|
||||
target: 'later',
|
||||
idx: 5,
|
||||
items: [
|
||||
{
|
||||
id: 'laterlist',
|
||||
intro: '简悦自带了一个未读列表,你可以把任意 URL 通过 <a target="_blank" href="http://ksria.com/simpread/docs/#/右键菜单">右键菜单</a> / <a target="_blank" href="http://ksria.com/simpread/docs/#/阅读模式-控制栏">控制栏 → 动作</a> 发送到稍后读。<br>稍后读也支持发送这些链接到 Pocket · Instapaper · Linnk 里面,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/稍后读">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'latermore',
|
||||
intro: '加载更多的稍后读。',
|
||||
}
|
||||
]
|
||||
},
|
||||
"@performance" : {
|
||||
target: 'labs',
|
||||
idx: 2,
|
||||
items: [
|
||||
{
|
||||
id: 'preload',
|
||||
intro: '简悦的词法分析引擎采用了预加载机制,当系统性能吃紧时,可以选择关闭此功能,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=预加载机制">请看这里</a> 。<br>注意:建议无特殊情况下不要关闭此功能,可以 <b>使用下一条的功能</b> 来规避性能问题。',
|
||||
},
|
||||
{
|
||||
id: 'lazyload',
|
||||
intro: '为了更快的进入到阅读模式,简悦会主动分析每个页面,但加入此列表的 URL 不会被主动分析。<br><br>此功能适合:<br><b> - 经常使用简悦但又性能不够</b> 的用户;<br> - 需要动态加载的页面;<br> - 支持 Mathjax 解析的页面等;<br><br>详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/词法分析引擎?id=延迟加载">请看这里</a> 。',
|
||||
},
|
||||
{
|
||||
id: 'blacklist',
|
||||
intro: '也可以将完全不需要的站点加入到黑名单中,详细说明 <a target="_blank" href="http://ksria.com/simpread/docs/#/FAQ?id=黑名单">请看这里</a> 。',
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify version
|
||||
@ -157,6 +414,26 @@ function Verify( curver, data ) {
|
||||
data.patch != sub_ver && FixSubver( sub_ver, data );
|
||||
}
|
||||
|
||||
if ( curver == "1.1.2" ) {
|
||||
data.option.save_at = "dropbox";
|
||||
data.option.notice = true;
|
||||
data.option.preload = true;
|
||||
data.option.lazyload = [
|
||||
"baidu.com", "weibo.com", "youtube.com"
|
||||
];
|
||||
data.option.uninstall = true;
|
||||
|
||||
data.statistics.service.yuque = 0;
|
||||
data.statistics.service.jianguo = 0;
|
||||
|
||||
data.notice = { "latest": 0, "read": [] };
|
||||
|
||||
data.option.blacklist.findIndex( item => item.toLowerCase() == "youtube.com" ) < 0 && data.option.blacklist.push( "youtube.com" );
|
||||
|
||||
data.patch = 0;
|
||||
curver = "1.1.3";
|
||||
}
|
||||
|
||||
/*
|
||||
if ( curver == "1.0.1" ) {
|
||||
data.option.pocket = { "consumer": "", "access": "" };
|
||||
@ -169,6 +446,29 @@ function Verify( curver, data ) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix Incompatible simpread data structure
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {object} simpread data structure
|
||||
* @return {boolean} true: changed false: not changed
|
||||
*/
|
||||
function Incompatible( ver, data ) {
|
||||
let is_changed = false;
|
||||
if ( ver == "1.1.3" ) {
|
||||
data.option.origins = data.option.origins.filter( item => item != "http://sr.ksria.cn/origins/website_list_en.json" && item != "http://sr.ksria.cn/origins/website_list_tw.json" )
|
||||
if ( data.option.origins.length > 0 ) {
|
||||
is_changed = true;
|
||||
new Notify().Render({ type: 2, content: `检测到你曾经修改过第三方适配源,<b>务必刷新后重新导入</b>!<a target="_blank" href="http://ksria.com/simpread/docs/#/站点适配源?id=第三方适配源">详细说明</a>`, state: "holdon" });
|
||||
}
|
||||
if ( VerifyPlugins( ver, data.option )) {
|
||||
is_changed = true;
|
||||
new Notify().Render({ type: 2, content: `已清理失效的插件,<b>务必刷新后重新导入</b>,详细请看 <a href="http://ksria.com/simpread/welcome/version_${version}.html#badplugins" target="_blank">删除失效的插件</a>`, state: "holdon" });
|
||||
}
|
||||
}
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify with type and version
|
||||
* 1.0.4 before usage http://ksria.com/simpread/changelog.html#{ver}
|
||||
@ -178,10 +478,10 @@ function Verify( curver, data ) {
|
||||
* @param {string} type, include: firstload, update
|
||||
* @param {string} ver, e.g. 1.0.0, 1.0.1
|
||||
*/
|
||||
function Notify( first, type, ver ) {
|
||||
function Notify2( first, type, ver ) {
|
||||
const str = type == "firstload" ? "安装" : "更新",
|
||||
detail = type == "firstload" ? "" : details.get(ver),
|
||||
link = first ? `${detail}如何使用请看 <a href="http://ksria.com/simpread/docs/#/" target="_blank">文档中心</a>` : `${detail}请看 <a href="http://ksria.com/simpread/welcome/version_${ver}.html" target="_blank">更新说明</a>`;
|
||||
link = first ? `${detail}如何使用请看 <a href="http://ksria.com/simpread/guide/" target="_blank">新手入门</a> 及 <a href="http://ksria.com/simpread/docs/#/" target="_blank">文档中心</a>` : `${detail}请看 <a href="http://ksria.com/simpread/welcome/version_${ver}.html" target="_blank">更新说明</a>`;
|
||||
return `${str} 到最新版本 ${ver} ,${ link }`;
|
||||
}
|
||||
|
||||
@ -223,11 +523,37 @@ function FixSubver( patch, target ) {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify current version plugins
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {object} option
|
||||
*/
|
||||
function VerifyPlugins( ver, option ) {
|
||||
try {
|
||||
if ( option.plugins.length == 0 ) return false;
|
||||
const str = option.plugins.join( "," );
|
||||
if ( ver == "1.1.3" ) {
|
||||
const newStr = str.replace( /(E0j1nYBmDD,?|SumEaxStWE,?|UsayAKSuwe,?)/g, "" );
|
||||
if ( str != newStr ) {
|
||||
option.plugins = newStr.replace( /,$/, "" ).split( "," );
|
||||
return true;
|
||||
}
|
||||
} else return false;
|
||||
} catch( error ) {
|
||||
console.error( "version::VerifyPlugin catch", error )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
version,
|
||||
tips,
|
||||
sub_ver as patch,
|
||||
Verify,
|
||||
Notify,
|
||||
Notify2 as Notify,
|
||||
Compare,
|
||||
FixSubver,
|
||||
VerifyPlugins,
|
||||
Incompatible,
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
console.log( "=== simpread storage load ===" )
|
||||
console.log( "=== simpread watch load ===" )
|
||||
|
||||
import * as msg from 'message';
|
||||
import {br,browser}from 'browser';
|
||||
|
||||
77
src/vender/intro/intro.min.css
vendored
Normal file
1
src/vender/intro/intro.min.js
vendored
Normal file
2
src/vender/puread/puplugin.min.js
vendored
2
src/vender/puread/puread.min.js
vendored
167
src/vender/turndown/turndown-plugin-gfm.js
Normal file
@ -0,0 +1,167 @@
|
||||
var turndownPluginGfm = (function (exports) {
|
||||
'use strict';
|
||||
|
||||
var highlightRegExp = /highlight-(?:text|source)-([a-z0-9]+)/;
|
||||
|
||||
function highlightedCodeBlock (turndownService) {
|
||||
turndownService.addRule('highlightedCodeBlock', {
|
||||
filter: function (node) {
|
||||
var firstChild = node.firstChild;
|
||||
return (
|
||||
node.nodeName === 'DIV' &&
|
||||
highlightRegExp.test(node.className) &&
|
||||
firstChild &&
|
||||
firstChild.nodeName === 'PRE'
|
||||
)
|
||||
},
|
||||
replacement: function (content, node, options) {
|
||||
var className = node.className || '';
|
||||
var language = (className.match(highlightRegExp) || [null, ''])[1];
|
||||
|
||||
return (
|
||||
'\n\n' + options.fence + language + '\n' +
|
||||
node.firstChild.textContent +
|
||||
'\n' + options.fence + '\n\n'
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function strikethrough (turndownService) {
|
||||
turndownService.addRule('strikethrough', {
|
||||
filter: ['del', 's', 'strike'],
|
||||
replacement: function (content) {
|
||||
return '~' + content + '~'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var indexOf = Array.prototype.indexOf;
|
||||
var every = Array.prototype.every;
|
||||
var rules = {};
|
||||
|
||||
rules.tableCell = {
|
||||
filter: ['th', 'td'],
|
||||
replacement: function (content, node) {
|
||||
return cell(content, node)
|
||||
}
|
||||
};
|
||||
|
||||
rules.tableRow = {
|
||||
filter: 'tr',
|
||||
replacement: function (content, node) {
|
||||
var borderCells = '';
|
||||
var alignMap = { left: ':--', right: '--:', center: ':-:' };
|
||||
|
||||
if (isHeadingRow(node)) {
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
var border = '---';
|
||||
var align = (
|
||||
node.childNodes[i].getAttribute('align') || ''
|
||||
).toLowerCase();
|
||||
|
||||
if (align) border = alignMap[align] || border;
|
||||
|
||||
borderCells += cell(border, node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
return '\n' + content + (borderCells ? '\n' + borderCells : '')
|
||||
}
|
||||
};
|
||||
|
||||
rules.table = {
|
||||
// Only convert tables with a heading row.
|
||||
// Tables with no heading row are kept using `keep` (see below).
|
||||
filter: function (node) {
|
||||
return node.nodeName === 'TABLE' && isHeadingRow(node.rows[0])
|
||||
},
|
||||
|
||||
replacement: function (content) {
|
||||
// Ensure there are no blank lines
|
||||
content = content.replace('\n\n', '\n');
|
||||
return '\n\n' + content + '\n\n'
|
||||
}
|
||||
};
|
||||
|
||||
rules.tableSection = {
|
||||
filter: ['thead', 'tbody', 'tfoot'],
|
||||
replacement: function (content) {
|
||||
return content
|
||||
}
|
||||
};
|
||||
|
||||
// A tr is a heading row if:
|
||||
// - the parent is a THEAD
|
||||
// - or if its the first child of the TABLE or the first TBODY (possibly
|
||||
// following a blank THEAD)
|
||||
// - and every cell is a TH
|
||||
function isHeadingRow (tr) {
|
||||
var parentNode = tr.parentNode;
|
||||
return (
|
||||
parentNode.nodeName === 'THEAD' ||
|
||||
(
|
||||
parentNode.firstChild === tr &&
|
||||
(parentNode.nodeName === 'TABLE' || isFirstTbody(parentNode)) &&
|
||||
every.call(tr.childNodes, function (n) { return n.nodeName === 'TH' })
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function isFirstTbody (element) {
|
||||
var previousSibling = element.previousSibling;
|
||||
return (
|
||||
element.nodeName === 'TBODY' && (
|
||||
!previousSibling ||
|
||||
(
|
||||
previousSibling.nodeName === 'THEAD' &&
|
||||
/^\s*$/i.test(previousSibling.textContent)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function cell (content, node) {
|
||||
var index = indexOf.call(node.parentNode.childNodes, node);
|
||||
var prefix = ' ';
|
||||
if (index === 0) prefix = '| ';
|
||||
return prefix + content + ' |'
|
||||
}
|
||||
|
||||
function tables (turndownService) {
|
||||
turndownService.keep(function (node) {
|
||||
return node.nodeName === 'TABLE' && !isHeadingRow(node.rows[0])
|
||||
});
|
||||
for (var key in rules) turndownService.addRule(key, rules[key]);
|
||||
}
|
||||
|
||||
function taskListItems (turndownService) {
|
||||
turndownService.addRule('taskListItems', {
|
||||
filter: function (node) {
|
||||
return node.type === 'checkbox' && node.parentNode.nodeName === 'LI'
|
||||
},
|
||||
replacement: function (content, node) {
|
||||
return (node.checked ? '[x]' : '[ ]') + ' '
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function gfm (turndownService) {
|
||||
turndownService.use([
|
||||
highlightedCodeBlock,
|
||||
strikethrough,
|
||||
tables,
|
||||
taskListItems
|
||||
]);
|
||||
}
|
||||
|
||||
exports.gfm = gfm;
|
||||
exports.highlightedCodeBlock = highlightedCodeBlock;
|
||||
exports.strikethrough = strikethrough;
|
||||
exports.tables = tables;
|
||||
exports.taskListItems = taskListItems;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}));
|
||||
|
||||
module.exports = turndownPluginGfm;
|
||||
912
src/vender/turndown/turndown.js
Normal file
@ -0,0 +1,912 @@
|
||||
var TurndownService = (function () {
|
||||
'use strict';
|
||||
|
||||
function extend (destination) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) destination[key] = source[key];
|
||||
}
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
function repeat (character, count) {
|
||||
return Array(count + 1).join(character)
|
||||
}
|
||||
|
||||
var blockElements = [
|
||||
'address', 'article', 'aside', 'audio', 'blockquote', 'body', 'canvas',
|
||||
'center', 'dd', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption',
|
||||
'figure', 'footer', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'header', 'hgroup', 'hr', 'html', 'isindex', 'li', 'main', 'menu', 'nav',
|
||||
'noframes', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table',
|
||||
'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
|
||||
];
|
||||
|
||||
function isBlock (node) {
|
||||
return blockElements.indexOf(node.nodeName.toLowerCase()) !== -1
|
||||
}
|
||||
|
||||
var voidElements = [
|
||||
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
|
||||
'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
|
||||
];
|
||||
|
||||
function isVoid (node) {
|
||||
return voidElements.indexOf(node.nodeName.toLowerCase()) !== -1
|
||||
}
|
||||
|
||||
var voidSelector = voidElements.join();
|
||||
function hasVoid (node) {
|
||||
return node.querySelector && node.querySelector(voidSelector)
|
||||
}
|
||||
|
||||
var rules = {};
|
||||
|
||||
rules.paragraph = {
|
||||
filter: 'p',
|
||||
|
||||
replacement: function (content) {
|
||||
return '\n\n' + content + '\n\n'
|
||||
}
|
||||
};
|
||||
|
||||
rules.lineBreak = {
|
||||
filter: 'br',
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
return options.br + '\n'
|
||||
}
|
||||
};
|
||||
|
||||
rules.heading = {
|
||||
filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
var hLevel = Number(node.nodeName.charAt(1));
|
||||
|
||||
if (options.headingStyle === 'setext' && hLevel < 3) {
|
||||
var underline = repeat((hLevel === 1 ? '=' : '-'), content.length);
|
||||
return (
|
||||
'\n\n' + content + '\n' + underline + '\n\n'
|
||||
)
|
||||
} else {
|
||||
return '\n\n' + repeat('#', hLevel) + ' ' + content + '\n\n'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rules.blockquote = {
|
||||
filter: 'blockquote',
|
||||
|
||||
replacement: function (content) {
|
||||
content = content.replace(/^\n+|\n+$/g, '');
|
||||
content = content.replace(/^/gm, '> ');
|
||||
return '\n\n' + content + '\n\n'
|
||||
}
|
||||
};
|
||||
|
||||
rules.list = {
|
||||
filter: ['ul', 'ol'],
|
||||
|
||||
replacement: function (content, node) {
|
||||
var parent = node.parentNode;
|
||||
if (parent.nodeName === 'LI' && parent.lastElementChild === node) {
|
||||
return '\n' + content
|
||||
} else {
|
||||
return '\n\n' + content + '\n\n'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rules.listItem = {
|
||||
filter: 'li',
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
content = content
|
||||
.replace(/^\n+/, '') // remove leading newlines
|
||||
.replace(/\n+$/, '\n') // replace trailing newlines with just a single one
|
||||
.replace(/\n/gm, '\n '); // indent
|
||||
var prefix = options.bulletListMarker + ' ';
|
||||
var parent = node.parentNode;
|
||||
if (parent.nodeName === 'OL') {
|
||||
var start = parent.getAttribute('start');
|
||||
var index = Array.prototype.indexOf.call(parent.children, node);
|
||||
prefix = (start ? Number(start) + index : index + 1) + '. ';
|
||||
}
|
||||
return (
|
||||
prefix + content + (node.nextSibling && !/\n$/.test(content) ? '\n' : '')
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
rules.indentedCodeBlock = {
|
||||
filter: function (node, options) {
|
||||
return (
|
||||
options.codeBlockStyle === 'indented' &&
|
||||
node.nodeName === 'PRE' &&
|
||||
node.firstChild &&
|
||||
node.firstChild.nodeName === 'CODE'
|
||||
)
|
||||
},
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
return (
|
||||
'\n\n ' +
|
||||
node.firstChild.textContent.replace(/\n/g, '\n ') +
|
||||
'\n\n'
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
rules.fencedCodeBlock = {
|
||||
filter: function (node, options) {
|
||||
return (
|
||||
options.codeBlockStyle === 'fenced' &&
|
||||
node.nodeName === 'PRE' &&
|
||||
node.firstChild &&
|
||||
node.firstChild.nodeName === 'CODE'
|
||||
)
|
||||
},
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
var className = node.firstChild.className || '';
|
||||
var language = (className.match(/language-(\S+)/) || [null, ''])[1];
|
||||
|
||||
return (
|
||||
'\n\n' + options.fence + language + '\n' +
|
||||
node.firstChild.textContent +
|
||||
'\n' + options.fence + '\n\n'
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
rules.horizontalRule = {
|
||||
filter: 'hr',
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
return '\n\n' + options.hr + '\n\n'
|
||||
}
|
||||
};
|
||||
|
||||
rules.inlineLink = {
|
||||
filter: function (node, options) {
|
||||
return (
|
||||
options.linkStyle === 'inlined' &&
|
||||
node.nodeName === 'A' &&
|
||||
node.getAttribute('href')
|
||||
)
|
||||
},
|
||||
|
||||
replacement: function (content, node) {
|
||||
var href = node.getAttribute('href');
|
||||
var title = node.title ? ' "' + node.title + '"' : '';
|
||||
return '[' + content + '](' + href + title + ')'
|
||||
}
|
||||
};
|
||||
|
||||
rules.referenceLink = {
|
||||
filter: function (node, options) {
|
||||
return (
|
||||
options.linkStyle === 'referenced' &&
|
||||
node.nodeName === 'A' &&
|
||||
node.getAttribute('href')
|
||||
)
|
||||
},
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
var href = node.getAttribute('href');
|
||||
var title = node.title ? ' "' + node.title + '"' : '';
|
||||
var replacement;
|
||||
var reference;
|
||||
|
||||
switch (options.linkReferenceStyle) {
|
||||
case 'collapsed':
|
||||
replacement = '[' + content + '][]';
|
||||
reference = '[' + content + ']: ' + href + title;
|
||||
break
|
||||
case 'shortcut':
|
||||
replacement = '[' + content + ']';
|
||||
reference = '[' + content + ']: ' + href + title;
|
||||
break
|
||||
default:
|
||||
var id = this.references.length + 1;
|
||||
replacement = '[' + content + '][' + id + ']';
|
||||
reference = '[' + id + ']: ' + href + title;
|
||||
}
|
||||
|
||||
this.references.push(reference);
|
||||
return replacement
|
||||
},
|
||||
|
||||
references: [],
|
||||
|
||||
append: function (options) {
|
||||
var references = '';
|
||||
if (this.references.length) {
|
||||
references = '\n\n' + this.references.join('\n') + '\n\n';
|
||||
this.references = []; // Reset references
|
||||
}
|
||||
return references
|
||||
}
|
||||
};
|
||||
|
||||
rules.emphasis = {
|
||||
filter: ['em', 'i'],
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
if (!content.trim()) return ''
|
||||
return options.emDelimiter + content + options.emDelimiter
|
||||
}
|
||||
};
|
||||
|
||||
rules.strong = {
|
||||
filter: ['strong', 'b'],
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
if (!content.trim()) return ''
|
||||
return options.strongDelimiter + content + options.strongDelimiter
|
||||
}
|
||||
};
|
||||
|
||||
rules.code = {
|
||||
filter: function (node) {
|
||||
var hasSiblings = node.previousSibling || node.nextSibling;
|
||||
var isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings;
|
||||
|
||||
return node.nodeName === 'CODE' && !isCodeBlock
|
||||
},
|
||||
|
||||
replacement: function (content) {
|
||||
if (!content.trim()) return ''
|
||||
|
||||
var delimiter = '`';
|
||||
var leadingSpace = '';
|
||||
var trailingSpace = '';
|
||||
var matches = content.match(/`+/gm);
|
||||
if (matches) {
|
||||
if (/^`/.test(content)) leadingSpace = ' ';
|
||||
if (/`$/.test(content)) trailingSpace = ' ';
|
||||
while (matches.indexOf(delimiter) !== -1) delimiter = delimiter + '`';
|
||||
}
|
||||
|
||||
return delimiter + leadingSpace + content + trailingSpace + delimiter
|
||||
}
|
||||
};
|
||||
|
||||
rules.image = {
|
||||
filter: 'img',
|
||||
|
||||
replacement: function (content, node) {
|
||||
var alt = node.alt || '';
|
||||
var src = node.getAttribute('src') || '';
|
||||
var title = node.title || '';
|
||||
var titlePart = title ? ' "' + title + '"' : '';
|
||||
return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages a collection of rules used to convert HTML to Markdown
|
||||
*/
|
||||
|
||||
function Rules (options) {
|
||||
this.options = options;
|
||||
this._keep = [];
|
||||
this._remove = [];
|
||||
|
||||
this.blankRule = {
|
||||
replacement: options.blankReplacement
|
||||
};
|
||||
|
||||
this.keepReplacement = options.keepReplacement;
|
||||
|
||||
this.defaultRule = {
|
||||
replacement: options.defaultReplacement
|
||||
};
|
||||
|
||||
this.array = [];
|
||||
for (var key in options.rules) this.array.push(options.rules[key]);
|
||||
}
|
||||
|
||||
Rules.prototype = {
|
||||
add: function (key, rule) {
|
||||
this.array.unshift(rule);
|
||||
},
|
||||
|
||||
keep: function (filter) {
|
||||
this._keep.unshift({
|
||||
filter: filter,
|
||||
replacement: this.keepReplacement
|
||||
});
|
||||
},
|
||||
|
||||
remove: function (filter) {
|
||||
this._remove.unshift({
|
||||
filter: filter,
|
||||
replacement: function () {
|
||||
return ''
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
forNode: function (node) {
|
||||
if (node.isBlank) return this.blankRule
|
||||
var rule;
|
||||
|
||||
if ((rule = findRule(this.array, node, this.options))) return rule
|
||||
if ((rule = findRule(this._keep, node, this.options))) return rule
|
||||
if ((rule = findRule(this._remove, node, this.options))) return rule
|
||||
|
||||
return this.defaultRule
|
||||
},
|
||||
|
||||
forEach: function (fn) {
|
||||
for (var i = 0; i < this.array.length; i++) fn(this.array[i], i);
|
||||
}
|
||||
};
|
||||
|
||||
function findRule (rules, node, options) {
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
var rule = rules[i];
|
||||
if (filterValue(rule, node, options)) return rule
|
||||
}
|
||||
return void 0
|
||||
}
|
||||
|
||||
function filterValue (rule, node, options) {
|
||||
var filter = rule.filter;
|
||||
if (typeof filter === 'string') {
|
||||
if (filter === node.nodeName.toLowerCase()) return true
|
||||
} else if (Array.isArray(filter)) {
|
||||
if (filter.indexOf(node.nodeName.toLowerCase()) > -1) return true
|
||||
} else if (typeof filter === 'function') {
|
||||
if (filter.call(rule, node, options)) return true
|
||||
} else {
|
||||
throw new TypeError('`filter` needs to be a string, array, or function')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The collapseWhitespace function is adapted from collapse-whitespace
|
||||
* by Luc Thevenard.
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Luc Thevenard <lucthevenard@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* collapseWhitespace(options) removes extraneous whitespace from an the given element.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function collapseWhitespace (options) {
|
||||
var element = options.element;
|
||||
var isBlock = options.isBlock;
|
||||
var isVoid = options.isVoid;
|
||||
var isPre = options.isPre || function (node) {
|
||||
return node.nodeName === 'PRE'
|
||||
};
|
||||
|
||||
if (!element.firstChild || isPre(element)) return
|
||||
|
||||
var prevText = null;
|
||||
var prevVoid = false;
|
||||
|
||||
var prev = null;
|
||||
var node = next(prev, element, isPre);
|
||||
|
||||
while (node !== element) {
|
||||
if (node.nodeType === 3 || node.nodeType === 4) { // Node.TEXT_NODE or Node.CDATA_SECTION_NODE
|
||||
var text = node.data.replace(/[ \r\n\t]+/g, ' ');
|
||||
|
||||
if ((!prevText || / $/.test(prevText.data)) &&
|
||||
!prevVoid && text[0] === ' ') {
|
||||
text = text.substr(1);
|
||||
}
|
||||
|
||||
// `text` might be empty at this point.
|
||||
if (!text) {
|
||||
node = remove(node);
|
||||
continue
|
||||
}
|
||||
|
||||
node.data = text;
|
||||
|
||||
prevText = node;
|
||||
} else if (node.nodeType === 1) { // Node.ELEMENT_NODE
|
||||
if (isBlock(node) || node.nodeName === 'BR') {
|
||||
if (prevText) {
|
||||
prevText.data = prevText.data.replace(/ $/, '');
|
||||
}
|
||||
|
||||
prevText = null;
|
||||
prevVoid = false;
|
||||
} else if (isVoid(node)) {
|
||||
// Avoid trimming space around non-block, non-BR void elements.
|
||||
prevText = null;
|
||||
prevVoid = true;
|
||||
}
|
||||
} else {
|
||||
node = remove(node);
|
||||
continue
|
||||
}
|
||||
|
||||
var nextNode = next(prev, node, isPre);
|
||||
prev = node;
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
if (prevText) {
|
||||
prevText.data = prevText.data.replace(/ $/, '');
|
||||
if (!prevText.data) {
|
||||
remove(prevText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove(node) removes the given node from the DOM and returns the
|
||||
* next node in the sequence.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Node} node
|
||||
*/
|
||||
function remove (node) {
|
||||
var next = node.nextSibling || node.parentNode;
|
||||
|
||||
node.parentNode.removeChild(node);
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* next(prev, current, isPre) returns the next node in the sequence, given the
|
||||
* current and previous nodes.
|
||||
*
|
||||
* @param {Node} prev
|
||||
* @param {Node} current
|
||||
* @param {Function} isPre
|
||||
* @return {Node}
|
||||
*/
|
||||
function next (prev, current, isPre) {
|
||||
if ((prev && prev.parentNode === current) || isPre(current)) {
|
||||
return current.nextSibling || current.parentNode
|
||||
}
|
||||
|
||||
return current.firstChild || current.nextSibling || current.parentNode
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up window for Node.js
|
||||
*/
|
||||
|
||||
var root = (typeof window !== 'undefined' ? window : {});
|
||||
|
||||
/*
|
||||
* Parsing HTML strings
|
||||
*/
|
||||
|
||||
function canParseHTMLNatively () {
|
||||
var Parser = root.DOMParser;
|
||||
var canParse = false;
|
||||
|
||||
// Adapted from https://gist.github.com/1129031
|
||||
// Firefox/Opera/IE throw errors on unsupported types
|
||||
try {
|
||||
// WebKit returns null on unsupported types
|
||||
if (new Parser().parseFromString('', 'text/html')) {
|
||||
canParse = true;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return canParse
|
||||
}
|
||||
|
||||
function createHTMLParser () {
|
||||
var Parser = function () {};
|
||||
|
||||
{
|
||||
if (shouldUseActiveX()) {
|
||||
Parser.prototype.parseFromString = function (string) {
|
||||
var doc = new window.ActiveXObject('htmlfile');
|
||||
doc.designMode = 'on'; // disable on-page scripts
|
||||
doc.open();
|
||||
doc.write(string);
|
||||
doc.close();
|
||||
return doc
|
||||
};
|
||||
} else {
|
||||
Parser.prototype.parseFromString = function (string) {
|
||||
var doc = document.implementation.createHTMLDocument('');
|
||||
doc.open();
|
||||
doc.write(string);
|
||||
doc.close();
|
||||
return doc
|
||||
};
|
||||
}
|
||||
}
|
||||
return Parser
|
||||
}
|
||||
|
||||
function shouldUseActiveX () {
|
||||
var useActiveX = false;
|
||||
try {
|
||||
document.implementation.createHTMLDocument('').open();
|
||||
} catch (e) {
|
||||
if (window.ActiveXObject) useActiveX = true;
|
||||
}
|
||||
return useActiveX
|
||||
}
|
||||
|
||||
var HTMLParser = canParseHTMLNatively() ? root.DOMParser : createHTMLParser();
|
||||
|
||||
function RootNode (input) {
|
||||
var root;
|
||||
if (typeof input === 'string') {
|
||||
var doc = htmlParser().parseFromString(
|
||||
// DOM parsers arrange elements in the <head> and <body>.
|
||||
// Wrapping in a custom element ensures elements are reliably arranged in
|
||||
// a single element.
|
||||
'<x-turndown id="turndown-root">' + input + '</x-turndown>',
|
||||
'text/html'
|
||||
);
|
||||
root = doc.getElementById('turndown-root');
|
||||
} else {
|
||||
root = input.cloneNode(true);
|
||||
}
|
||||
collapseWhitespace({
|
||||
element: root,
|
||||
isBlock: isBlock,
|
||||
isVoid: isVoid
|
||||
});
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
var _htmlParser;
|
||||
function htmlParser () {
|
||||
_htmlParser = _htmlParser || new HTMLParser();
|
||||
return _htmlParser
|
||||
}
|
||||
|
||||
function Node (node) {
|
||||
node.isBlock = isBlock(node);
|
||||
node.isCode = node.nodeName.toLowerCase() === 'code' || node.parentNode.isCode;
|
||||
node.isBlank = isBlank(node);
|
||||
node.flankingWhitespace = flankingWhitespace(node);
|
||||
return node
|
||||
}
|
||||
|
||||
function isBlank (node) {
|
||||
return (
|
||||
['A', 'TH', 'TD', 'IFRAME', 'SCRIPT', 'AUDIO', 'VIDEO'].indexOf(node.nodeName) === -1 &&
|
||||
/^\s*$/i.test(node.textContent) &&
|
||||
!isVoid(node) &&
|
||||
!hasVoid(node)
|
||||
)
|
||||
}
|
||||
|
||||
function flankingWhitespace (node) {
|
||||
var leading = '';
|
||||
var trailing = '';
|
||||
|
||||
if (!node.isBlock) {
|
||||
var hasLeading = /^[ \r\n\t]/.test(node.textContent);
|
||||
var hasTrailing = /[ \r\n\t]$/.test(node.textContent);
|
||||
|
||||
if (hasLeading && !isFlankedByWhitespace('left', node)) {
|
||||
leading = ' ';
|
||||
}
|
||||
if (hasTrailing && !isFlankedByWhitespace('right', node)) {
|
||||
trailing = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return { leading: leading, trailing: trailing }
|
||||
}
|
||||
|
||||
function isFlankedByWhitespace (side, node) {
|
||||
var sibling;
|
||||
var regExp;
|
||||
var isFlanked;
|
||||
|
||||
if (side === 'left') {
|
||||
sibling = node.previousSibling;
|
||||
regExp = / $/;
|
||||
} else {
|
||||
sibling = node.nextSibling;
|
||||
regExp = /^ /;
|
||||
}
|
||||
|
||||
if (sibling) {
|
||||
if (sibling.nodeType === 3) {
|
||||
isFlanked = regExp.test(sibling.nodeValue);
|
||||
} else if (sibling.nodeType === 1 && !isBlock(sibling)) {
|
||||
isFlanked = regExp.test(sibling.textContent);
|
||||
}
|
||||
}
|
||||
return isFlanked
|
||||
}
|
||||
|
||||
var reduce = Array.prototype.reduce;
|
||||
var leadingNewLinesRegExp = /^\n*/;
|
||||
var trailingNewLinesRegExp = /\n*$/;
|
||||
var escapes = [
|
||||
[/\\/g, '\\\\'],
|
||||
[/\*/g, '\\*'],
|
||||
[/^-/g, '\\-'],
|
||||
[/^\+ /g, '\\+ '],
|
||||
[/^(=+)/g, '\\$1'],
|
||||
[/^(#{1,6}) /g, '\\$1 '],
|
||||
[/`/g, '\\`'],
|
||||
[/^~~~/g, '\\~~~'],
|
||||
[/\[/g, '\\['],
|
||||
[/\]/g, '\\]'],
|
||||
[/^>/g, '\\>'],
|
||||
[/_/g, '\\_'],
|
||||
[/^(\d+)\. /g, '$1\\. ']
|
||||
];
|
||||
escapes = [];
|
||||
|
||||
function TurndownService (options) {
|
||||
if (!(this instanceof TurndownService)) return new TurndownService(options)
|
||||
|
||||
var defaults = {
|
||||
rules: rules,
|
||||
headingStyle: 'setext',
|
||||
hr: '* * *',
|
||||
bulletListMarker: '*',
|
||||
codeBlockStyle: 'indented',
|
||||
fence: '```',
|
||||
emDelimiter: '_',
|
||||
strongDelimiter: '**',
|
||||
linkStyle: 'inlined',
|
||||
linkReferenceStyle: 'full',
|
||||
br: ' ',
|
||||
blankReplacement: function (content, node) {
|
||||
return node.isBlock ? '\n\n' : ''
|
||||
},
|
||||
keepReplacement: function (content, node) {
|
||||
return node.isBlock ? '\n\n' + node.outerHTML + '\n\n' : node.outerHTML
|
||||
},
|
||||
defaultReplacement: function (content, node) {
|
||||
return node.isBlock ? '\n\n' + content + '\n\n' : content
|
||||
}
|
||||
};
|
||||
this.options = extend({}, defaults, options);
|
||||
this.rules = new Rules(this.options);
|
||||
}
|
||||
|
||||
TurndownService.prototype = {
|
||||
/**
|
||||
* The entry point for converting a string or DOM node to Markdown
|
||||
* @public
|
||||
* @param {String|HTMLElement} input The string or DOM node to convert
|
||||
* @returns A Markdown representation of the input
|
||||
* @type String
|
||||
*/
|
||||
|
||||
turndown: function (input) {
|
||||
if (!canConvert(input)) {
|
||||
throw new TypeError(
|
||||
input + ' is not a string, or an element/document/fragment node.'
|
||||
)
|
||||
}
|
||||
|
||||
if (input === '') return ''
|
||||
|
||||
var output = process.call(this, new RootNode(input));
|
||||
return postProcess.call(this, output)
|
||||
},
|
||||
|
||||
/**
|
||||
* Add one or more plugins
|
||||
* @public
|
||||
* @param {Function|Array} plugin The plugin or array of plugins to add
|
||||
* @returns The Turndown instance for chaining
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
use: function (plugin) {
|
||||
if (Array.isArray(plugin)) {
|
||||
for (var i = 0; i < plugin.length; i++) this.use(plugin[i]);
|
||||
} else if (typeof plugin === 'function') {
|
||||
plugin(this);
|
||||
} else {
|
||||
throw new TypeError('plugin must be a Function or an Array of Functions')
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a rule
|
||||
* @public
|
||||
* @param {String} key The unique key of the rule
|
||||
* @param {Object} rule The rule
|
||||
* @returns The Turndown instance for chaining
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
addRule: function (key, rule) {
|
||||
this.rules.add(key, rule);
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Keep a node (as HTML) that matches the filter
|
||||
* @public
|
||||
* @param {String|Array|Function} filter The unique key of the rule
|
||||
* @returns The Turndown instance for chaining
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
keep: function (filter) {
|
||||
this.rules.keep(filter);
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a node that matches the filter
|
||||
* @public
|
||||
* @param {String|Array|Function} filter The unique key of the rule
|
||||
* @returns The Turndown instance for chaining
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
remove: function (filter) {
|
||||
this.rules.remove(filter);
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Escapes Markdown syntax
|
||||
* @public
|
||||
* @param {String} string The string to escape
|
||||
* @returns A string with Markdown syntax escaped
|
||||
* @type String
|
||||
*/
|
||||
|
||||
escape: function (string) {
|
||||
return escapes.reduce(function (accumulator, escape) {
|
||||
return accumulator.replace(escape[0], escape[1])
|
||||
}, string)
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reduces a DOM node down to its Markdown string equivalent
|
||||
* @private
|
||||
* @param {HTMLElement} parentNode The node to convert
|
||||
* @returns A Markdown representation of the node
|
||||
* @type String
|
||||
*/
|
||||
|
||||
function process (parentNode) {
|
||||
var self = this;
|
||||
return reduce.call(parentNode.childNodes, function (output, node) {
|
||||
node = new Node(node);
|
||||
|
||||
var replacement = '';
|
||||
if (node.nodeType === 3) {
|
||||
replacement = node.isCode ? node.nodeValue : self.escape(node.nodeValue);
|
||||
} else if (node.nodeType === 1) {
|
||||
replacement = replacementForNode.call(self, node);
|
||||
}
|
||||
|
||||
return join(output, replacement)
|
||||
}, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends strings as each rule requires and trims the output
|
||||
* @private
|
||||
* @param {String} output The conversion output
|
||||
* @returns A trimmed version of the ouput
|
||||
* @type String
|
||||
*/
|
||||
|
||||
function postProcess (output) {
|
||||
var self = this;
|
||||
this.rules.forEach(function (rule) {
|
||||
if (typeof rule.append === 'function') {
|
||||
output = join(output, rule.append(self.options));
|
||||
}
|
||||
});
|
||||
|
||||
return output.replace(/^[\t\r\n]+/, '').replace(/[\t\r\n\s]+$/, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an element node to its Markdown equivalent
|
||||
* @private
|
||||
* @param {HTMLElement} node The node to convert
|
||||
* @returns A Markdown representation of the node
|
||||
* @type String
|
||||
*/
|
||||
|
||||
function replacementForNode (node) {
|
||||
var rule = this.rules.forNode(node);
|
||||
var content = process.call(this, node);
|
||||
var whitespace = node.flankingWhitespace;
|
||||
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
||||
return (
|
||||
whitespace.leading +
|
||||
rule.replacement(content, node, this.options) +
|
||||
whitespace.trailing
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the new lines between the current output and the replacement
|
||||
* @private
|
||||
* @param {String} output The current conversion output
|
||||
* @param {String} replacement The string to append to the output
|
||||
* @returns The whitespace to separate the current output and the replacement
|
||||
* @type String
|
||||
*/
|
||||
|
||||
function separatingNewlines (output, replacement) {
|
||||
var newlines = [
|
||||
output.match(trailingNewLinesRegExp)[0],
|
||||
replacement.match(leadingNewLinesRegExp)[0]
|
||||
].sort();
|
||||
var maxNewlines = newlines[newlines.length - 1];
|
||||
return maxNewlines.length < 2 ? maxNewlines : '\n\n'
|
||||
}
|
||||
|
||||
function join (string1, string2) {
|
||||
var separator = separatingNewlines(string1, string2);
|
||||
|
||||
// Remove trailing/leading newlines and replace with separator
|
||||
string1 = string1.replace(trailingNewLinesRegExp, '');
|
||||
string2 = string2.replace(leadingNewLinesRegExp, '');
|
||||
|
||||
return string1 + separator + string2
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an input can be converted
|
||||
* @private
|
||||
* @param {String|HTMLElement} input Describe this parameter
|
||||
* @returns Describe what it returns
|
||||
* @type String|Object|Array|Boolean|Number
|
||||
*/
|
||||
|
||||
function canConvert (input) {
|
||||
return (
|
||||
input != null && (
|
||||
typeof input === 'string' ||
|
||||
(input.nodeType && (
|
||||
input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return TurndownService;
|
||||
|
||||
}());
|
||||
|
||||
|
||||
module.exports = TurndownService;
|
||||
164
src/vender/webdav.js
Normal file
@ -0,0 +1,164 @@
|
||||
/* A simple WebDav implementation in JavaScript
|
||||
https://github.com/aslakhellesoy/webdavjs @license MIT
|
||||
*/
|
||||
var WebDAV = {
|
||||
GET: function(url, callback) {
|
||||
return this.request('GET', url, {}, null, 'text', callback);
|
||||
},
|
||||
|
||||
PROPFIND: function(url, callback) {
|
||||
return this.request('PROPFIND', url, {
|
||||
Depth: "1"
|
||||
}, null, 'xml', callback);
|
||||
},
|
||||
|
||||
MKCOL: function(url, callback) {
|
||||
return this.request('MKCOL', url, {}, null, 'text', callback);
|
||||
},
|
||||
|
||||
DELETE: function(url, callback) {
|
||||
return this.request('DELETE', url, {}, null, 'text', callback);
|
||||
},
|
||||
|
||||
PUT: function(url, data, callback) {
|
||||
return this.request('PUT', url, {}, data, 'text', callback);
|
||||
},
|
||||
|
||||
Author: function(user, password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
},
|
||||
|
||||
request: function(verb, url, headers, data, type, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var body = function() {
|
||||
var b = xhr.responseText;
|
||||
if (type == 'xml') {
|
||||
var xml = xhr.responseXML;
|
||||
if (xml) {
|
||||
b = xml.firstChild.nextSibling ? xml.firstChild.nextSibling : xml.firstChild;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE && verb == "PROPFIND") {
|
||||
var b = body();
|
||||
if (b) {
|
||||
callback(b);
|
||||
}
|
||||
} else if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
callback(xhr);
|
||||
}
|
||||
};
|
||||
}
|
||||
xhr.open(verb, url, !! callback);
|
||||
xhr.setRequestHeader("Content-Type", "text/xml; charset=UTF-8");
|
||||
this.user && this.password &&
|
||||
xhr.setRequestHeader("Authorization", "Basic " + btoa(this.user + ":" + this.password))
|
||||
for (var header in headers) {
|
||||
xhr.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
xhr.send(data);
|
||||
|
||||
if (!callback) {
|
||||
return body();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// An Object-oriented API around WebDAV.
|
||||
WebDAV.Fs = function(rootUrl, user, password) {
|
||||
WebDAV.Author(user, password);
|
||||
this.rootUrl = rootUrl;
|
||||
var fs = this;
|
||||
|
||||
this.file = function(href) {
|
||||
this.type = 'file';
|
||||
|
||||
this.url = fs.urlFor(href);
|
||||
|
||||
this.name = fs.nameFor(this.url);
|
||||
|
||||
this.read = function(callback) {
|
||||
return WebDAV.GET(this.url, callback);
|
||||
};
|
||||
|
||||
this.write = function(data, callback) {
|
||||
return WebDAV.PUT(this.url, data, callback);
|
||||
};
|
||||
|
||||
this.rm = function(callback) {
|
||||
return WebDAV.DELETE(this.url, callback);
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
this.dir = function(href) {
|
||||
this.type = 'dir';
|
||||
|
||||
this.url = fs.urlFor(href);
|
||||
|
||||
this.name = fs.nameFor(this.url);
|
||||
|
||||
this.children = function(callback) {
|
||||
var childrenFunc = function(doc) {
|
||||
if (doc.childNodes == null) {
|
||||
throw ('No such directory: ' + url);
|
||||
}
|
||||
var result = [];
|
||||
// Start at 1, because the 0th is the same as self.
|
||||
for (var i = 1; i < doc.childNodes.length; i++) {
|
||||
var response = doc.childNodes[i];
|
||||
var href = decodeURI( response.getElementsByTagName('d:href')[0].firstChild.nodeValue.replace(/\/$/, ''));
|
||||
var propstat = response.getElementsByTagName('d:propstat')[0];
|
||||
var prop = propstat.getElementsByTagName('d:prop')[0];
|
||||
var resourcetype = prop.getElementsByTagName('d:resourcetype')[0];
|
||||
var collection = resourcetype.getElementsByTagName('d:collection')[0];
|
||||
|
||||
if (collection) {
|
||||
result[i - 1] = new fs.dir(href);
|
||||
} else {
|
||||
result[i - 1] = new fs.file(href);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
WebDAV.PROPFIND(this.url, function(doc) {
|
||||
callback(childrenFunc(doc));
|
||||
});
|
||||
} else {
|
||||
return childrenFunc(WebDAV.PROPFIND(this.url));
|
||||
}
|
||||
};
|
||||
|
||||
this.rm = function(callback) {
|
||||
return WebDAV.DELETE(this.url, callback);
|
||||
};
|
||||
|
||||
this.mkdir = function(callback) {
|
||||
return WebDAV.MKCOL(this.url, callback);
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
this.urlFor = function(href) {
|
||||
return (/^http/.test(href) ? href : this.rootUrl + href);
|
||||
};
|
||||
|
||||
this.nameFor = function(url) {
|
||||
return url.replace(/.*\/(.*)/, '$1');
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = WebDAV;
|
||||
}
|
||||
@ -36,8 +36,8 @@
|
||||
"name" : "36kr.com",
|
||||
"url" : "http://36kr.com/p/",
|
||||
"title" : "<title>",
|
||||
"desc" : "<section class='summary'>",
|
||||
"include" : "<section class='textblock'>",
|
||||
"desc" : "<div class='summary'>",
|
||||
"include" : "<div class='articleDetailContent'>",
|
||||
"exclude" : [
|
||||
"<div class='author-panel'>",
|
||||
"<section class='article-footer-label'>",
|
||||
@ -47,10 +47,10 @@
|
||||
]
|
||||
},{
|
||||
"name" : "pingwest.com",
|
||||
"url" : "http://www.pingwest.com/",
|
||||
"title" : "<h1 class='title'>",
|
||||
"url" : "http*://www.pingwest.com/a/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='sc-container'>",
|
||||
"include" : "<article class=\"article-style\">",
|
||||
"exclude" : [
|
||||
"<p class='post-footer-wx'>"
|
||||
]
|
||||
@ -290,6 +290,15 @@
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "news.geekpark.net",
|
||||
"url" : "https://www.geekpark.net/news/*",
|
||||
"title" : "<h1 class='topic-title'>",
|
||||
"desc" : "[[{$('meta[name=description]').attr('content')}]]",
|
||||
"include" : "<div class='article-content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "jianshu.com",
|
||||
"url" : "http://www.jianshu.com/p/",
|
||||
@ -302,9 +311,9 @@
|
||||
},{
|
||||
"name" : "waerfa.com",
|
||||
"url" : "http://www.waerfa.com/",
|
||||
"title" : "<h1 class='Article__title'>",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='Article__content'>",
|
||||
"include" : "<div class='s-single-article'>",
|
||||
"exclude" : [
|
||||
"<div class='u-post-share-wrap'>"
|
||||
]
|
||||
@ -321,7 +330,7 @@
|
||||
"name" : "article.guokr.com",
|
||||
"url" : "http://www.guokr.com/article/",
|
||||
"title" : "<h1 id='articleTitle'>",
|
||||
"desc" : "[[{$('meta[name=description]').attr('content')}]]",
|
||||
"desc" : "[[{$('meta[name=Description]').attr('content')}]]",
|
||||
"include" : "<div id='articleContent'>",
|
||||
"exclude" : [
|
||||
"<div class='article-extend'>",
|
||||
@ -386,6 +395,24 @@
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "book.douban.com",
|
||||
"url" : "https://book.douban.com/reading/*",
|
||||
"title" : "<h1>",
|
||||
"desc" : "",
|
||||
"include" : "<div class=\"book-content\">",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "people.douban.com",
|
||||
"url" : "https://www.douban.com/people/*/status/*",
|
||||
"title" : "<h1>",
|
||||
"desc" : "",
|
||||
"include" : "<div class=\"status-saying\">",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "douban.com",
|
||||
"url" : "https://www.douban.com/*",
|
||||
@ -487,7 +514,8 @@
|
||||
"<div class='sharing-options'>",
|
||||
"<div class='wptouch-custom-showcase'>",
|
||||
"[[/src=\\S+(ds-logo-1-2-64)\\S+'/]]",
|
||||
"[[/src=\\S+(down)\\S+'/]]"
|
||||
"[[/src=\\S+(down)\\S+'/]]",
|
||||
"<button class='simplefavorite-button'>"
|
||||
]
|
||||
},{
|
||||
"name" : "free.apprcn.com",
|
||||
@ -752,6 +780,15 @@
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "iplaysoft.com",
|
||||
"url" : "https://www.iplaysoft.com/*.html",
|
||||
"title" : "<h1 id='post-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='entry-content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "blog.jobbole.com",
|
||||
"url" : "http://blog.jobbole.com/*/",
|
||||
@ -963,7 +1000,7 @@
|
||||
"desc" : "",
|
||||
"include" : "<div class='p_mainnew'>",
|
||||
"exclude" : [
|
||||
"[[/src=\\S+(liiLIZF8Uh6yM.jpg)\\S+/]]"
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "ifeve.com",
|
||||
@ -1020,10 +1057,10 @@
|
||||
"name" : "my.oschina.net",
|
||||
"url" : "https://my.oschina.net/**/*/blog/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "<div class='blog-abstract'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='articleContent'>",
|
||||
"exclude" : [
|
||||
""
|
||||
"[[/src=\\S+(hot3)\\S+'/]]"
|
||||
]
|
||||
},{
|
||||
"name" : "oschina.net",
|
||||
@ -1044,6 +1081,24 @@
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "news.iteye.com",
|
||||
"url" : "http*://www.iteye.com/news/*",
|
||||
"title" : "<h3 id='h3-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='news_content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "blog.iteye.com",
|
||||
"url" : "http*://*.iteye.com/blog/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='iteye-blog-content-contain'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "geek.csdn.net",
|
||||
"url" : "http*://geek.csdn.net/news/detail/*",
|
||||
@ -1088,7 +1143,7 @@
|
||||
]
|
||||
},{
|
||||
"name" : "freebuf.com",
|
||||
"url" : "http://www.freebuf.com/**/*/*.html",
|
||||
"url" : "http*://www.freebuf.com/**/*/*.html",
|
||||
"title" : "[[{$('.title h2').text()}]]",
|
||||
"desc" : "",
|
||||
"include" : "<div id='contenttxt'>",
|
||||
@ -1124,7 +1179,7 @@
|
||||
]
|
||||
},{
|
||||
"name" : "cnblogs.com",
|
||||
"url" : "http://www.cnblogs.com/**/*/*.html",
|
||||
"url" : "https://www.cnblogs.com/**/*/*.html",
|
||||
"title" : "<a id='cb_post_title_url'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='cnblogs_post_body'>",
|
||||
@ -1769,13 +1824,24 @@
|
||||
"<span class='comment-big'>"
|
||||
]
|
||||
},{
|
||||
"name" : "post.smzdm.com",
|
||||
"url" : "http://post.smzdm.com/p/",
|
||||
"title" : "<h1 class='item-name'>",
|
||||
"name" : "news.smzdm.com",
|
||||
"url" : "http*://news.smzdm.com/p/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<article>",
|
||||
"include" : "<div class='news_content'>",
|
||||
"exclude" : [
|
||||
"<h1 class='item-name'>"
|
||||
"<h1 class='item-name'>",
|
||||
"<span class='embed-card'>"
|
||||
]
|
||||
},{
|
||||
"name" : "post.smzdm.com",
|
||||
"url" : "http*://post.smzdm.com/p/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='news_content'>",
|
||||
"exclude" : [
|
||||
"<h1 class='item-name'>",
|
||||
"<span class='embed-card'>"
|
||||
]
|
||||
},{
|
||||
"name" : "news.mydrivers.com",
|
||||
@ -1853,6 +1919,15 @@
|
||||
"exclude" : [
|
||||
"[[/src=\\S+(favicon)\\S+'/]]"
|
||||
]
|
||||
},{
|
||||
"name" : "news.mittrchina.com",
|
||||
"url" : "http*://www.mittrchina.com/news/*",
|
||||
"title" : "<div class='title'>",
|
||||
"desc" : "<div class='description'>",
|
||||
"include" : "<div class='content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "m.ithome.com",
|
||||
"url" : "http*://wap.ithome.com/html/*.htm",
|
||||
@ -1862,6 +1937,15 @@
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "livesino.net",
|
||||
"url" : "https://livesino.net/archives/*.live",
|
||||
"title" : "<h1 class='post-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='post-content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "ithome.com",
|
||||
"url" : "https://www.ithome.com/html/**/*/*.htm",
|
||||
@ -1894,7 +1978,7 @@
|
||||
"url" : "http*://news.sina.com.cn/**/*/*.shtml",
|
||||
"title" : "<h1 class='main-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='article'>",
|
||||
"include" : "<div class='article'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
@ -2098,7 +2182,7 @@
|
||||
"url" : "http://*.people.com.cn/**/*/*.html",
|
||||
"title" : "<h1>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='rwb_zw'>",
|
||||
"include" : "<div class='box_con'>",
|
||||
"exclude" : [
|
||||
"<div class='edit'>"
|
||||
]
|
||||
@ -2399,7 +2483,7 @@
|
||||
]
|
||||
},{
|
||||
"name" : "pintu360.com",
|
||||
"url" : "http://www.pintu360.com/article/",
|
||||
"url" : "http*://www.pintu360.com/article/",
|
||||
"title" : "<h1 class='title'>",
|
||||
"desc" : "<div class='article-note'>",
|
||||
"include" : "[[[$('.article-content').find('.text')]]]",
|
||||
@ -2552,7 +2636,7 @@
|
||||
"url" : "http*://jingyan.baidu.com/article/*.html",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<li class='exp-content-list'>",
|
||||
"include" : "<div id='format-exp'>",
|
||||
"exclude" : [
|
||||
"<span class='exp-album-enter-mask'>",
|
||||
"<span class='enter-step-btn'>",
|
||||
@ -2829,5 +2913,369 @@
|
||||
"exclude" : [
|
||||
"<span class='show-content'>"
|
||||
]
|
||||
},{
|
||||
"name" : "aktuell.faz.net",
|
||||
"url" : "http*://www.faz.net/aktuell/**/*.html",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='atc-Text'>",
|
||||
"exclude" : [
|
||||
"<aside class='atc-ContainerMore'>",
|
||||
"<div class='o-Ratio_Content'>",
|
||||
"<div class='ctn-PlaceholderContent'>",
|
||||
"<div class='atc-ContainerInfo'>"
|
||||
]
|
||||
},{
|
||||
"name" : "cn.nytstyle.com",
|
||||
"url" : "https://cn.nytstyle.com/*/*/*/",
|
||||
"title" : "<h3 class='articleHeadline'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='content'>",
|
||||
"exclude" : [
|
||||
"<div class='authorIdentification'>",
|
||||
"<div class='articleCR'>",
|
||||
"<div class='articleTool'>",
|
||||
"<div id='disqus_thread'>",
|
||||
"<div id='subscribe_cont'>",
|
||||
"<div class='articleByside'>"
|
||||
]
|
||||
},{
|
||||
"name" : "sueddeutsche.de",
|
||||
"url" : "http://www.sueddeutsche.de/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<section id='article-body'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "nytimes.com",
|
||||
"url" : "https://www.nytimes.com/*/*/*/**/*/*.html",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<section class='meteredContent'>",
|
||||
"exclude" : [
|
||||
"<button class='css-1vkv6l7'>"
|
||||
]
|
||||
},{
|
||||
"name" : "cn.nytimes.com",
|
||||
"url" : "https://cn.nytimes.com/*/*/*/",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<section class='article-body'>",
|
||||
"exclude" : [
|
||||
"<div class='authorIdentification'>",
|
||||
"<div class='articleCR'>",
|
||||
"<div class='articleTool'>",
|
||||
"<div id='disqus_thread'>",
|
||||
"<div id='subscribe_cont'>",
|
||||
"<div class='articleByside'>"
|
||||
]
|
||||
},{
|
||||
"name" : "theverge.com",
|
||||
"url" : "https://www.theverge.com/**/*",
|
||||
"title" : "<h1 class='c-page-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='c-entry-content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "medium.com",
|
||||
"url" : "https://medium.com/@*/**/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='section-content'>",
|
||||
"exclude" : [
|
||||
"<h1 class='graf--title'>",
|
||||
"<img class='progressiveMedia-thumbnail'>",
|
||||
"<canvas class='progressiveMedia-canvas'>",
|
||||
"<figcaption class='imageCaption'>"
|
||||
]
|
||||
},{
|
||||
"name" : "news.bbc.com",
|
||||
"url" : "http*://www.bbc.com/news/**/*",
|
||||
"title" : "<h1 class='story-body__h1'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='story-body__inner'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "simp.bbc.com",
|
||||
"url" : "http*://www.bbc.com/zhongwen/**/*",
|
||||
"title" : "<h1 class='story-body__h1'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='story-body__inner'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "wsj.com",
|
||||
"url" : "https://www.wsj.com/articles/**/*",
|
||||
"title" : "<h1 class='wsj-article-headline'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='wsj-snippet-body'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "economist.com",
|
||||
"url" : "https://www.economist.com/**/*",
|
||||
"title" : "[[{$('.flytitle-and-title__title:first').text()}]]",
|
||||
"desc" : "",
|
||||
"include" : "<div class='blog-post__text'>",
|
||||
"exclude" : [
|
||||
"<div class='newsletter-form--inline'>"
|
||||
]
|
||||
},{
|
||||
"name" : "news.zerohedge.com",
|
||||
"url" : "https://www.zerohedge.com/news/**/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class=\"node__content\">",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "theinitium.com",
|
||||
"url" : "https://theinitium.com/article/*",
|
||||
"title" : "<h1 class=\"p-article__title\">",
|
||||
"desc" : "",
|
||||
"include" : "[[{$($('.p-article__content')[1]).html()}]]",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "news.cnbc.com",
|
||||
"url" : "https://www.cnbc.com/*/*/*/*.html",
|
||||
"title" : "<h1 class='title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='article_body'>",
|
||||
"exclude" : [
|
||||
"<div class='inline-player'>",
|
||||
"<twitter-widget>",
|
||||
"<iframe>",
|
||||
"<div class='embed-container'>"
|
||||
]
|
||||
},{
|
||||
"name" : "news.vox.com",
|
||||
"url" : "https://www.vox.com/*/*/*/**/*",
|
||||
"title" : "<h1 class='c-page-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='c-entry-content '>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "news.theatlantic.com",
|
||||
"url" : "https://www.theatlantic.com/*/archive/**/*",
|
||||
"title" : "<h1 class='c-article-header__hed'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='l-article__container'>",
|
||||
"exclude" : [
|
||||
"<address>",
|
||||
"<figcaption class='c-lead-media__credit'>",
|
||||
"<span class='c-menu__section__icon'>",
|
||||
"<svg>"
|
||||
]
|
||||
},{
|
||||
"name" : "time.com",
|
||||
"url" : "http*://time.com/*/*/",
|
||||
"title" : "<h1 class='heading-content'>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='article-body'>",
|
||||
"exclude" : [
|
||||
"<div class='newsletter-inline'>"
|
||||
]
|
||||
},{
|
||||
"name" : "zh.asia.mercurymarine.com",
|
||||
"url" : "https://www.mercurymarine.com/zh/asia/news/*",
|
||||
"title" : "[[{$('.formatted-copy h2').text()}]]",
|
||||
"desc" : "",
|
||||
"include" : "<div class='formatted-copy'>",
|
||||
"exclude" : [
|
||||
"<h2>"
|
||||
]
|
||||
},{
|
||||
"name" : "article.scientificamerican.com",
|
||||
"url" : "https://www.scientificamerican.com/article/*",
|
||||
"title" : "<h1 class='article-header__title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='article-block'>",
|
||||
"exclude" : [
|
||||
"<figure class='newsletter-promo'>"
|
||||
]
|
||||
},{
|
||||
"name" : "latimes.com",
|
||||
"url" : "http*://www.latimes.com/**/*/*.html",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='collection-cards'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "news.abcnews.com",
|
||||
"url" : "https://abcnews.go.com/*/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='article-copy'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "thehill.com",
|
||||
"url" : "https://thehill.com/*/**/*",
|
||||
"title" : "<h1 class='title'>",
|
||||
"desc" : "",
|
||||
"include" : "[[{$('.field-item.even').html()}]]",
|
||||
"exclude" : [
|
||||
"<iframe>"
|
||||
]
|
||||
},{
|
||||
"name" : "news.nieman.harvard.edu",
|
||||
"url" : "https://nieman.harvard.edu/news/*/*/*",
|
||||
"title" : "<h1 class='blog-h1'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='ui-block-inner'>",
|
||||
"exclude" : [
|
||||
"<div class='tags-top'>",
|
||||
"<div class='social-tools-bottom'>"
|
||||
]
|
||||
},{
|
||||
"name" : "cjr.org",
|
||||
"url" : "https://www.cjr.org/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div id='article-content'>",
|
||||
"exclude" : [
|
||||
"<div id='mc_embed_signup'>",
|
||||
"<div id='post-survey-box'>",
|
||||
"<small>"
|
||||
]
|
||||
},{
|
||||
"name" : "9to5mac.com",
|
||||
"url" : "https://9to5mac.com/*/*/*/*",
|
||||
"title" : "<h1 class='post-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='post-body'>",
|
||||
"exclude" : [
|
||||
"[[/src=\\S+(volta-2.0-cable)\\S+'/]]",
|
||||
"<div class='the9ca7210'>",
|
||||
"[['<p>Check out 9to5Mac on YouTube</p>']]"
|
||||
]
|
||||
},{
|
||||
"name" : "9to5google.com",
|
||||
"url" : "https://9to5google.com/*/*/*/*",
|
||||
"title" : "<h1 class='post-title'>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='post-body'>",
|
||||
"exclude" : [
|
||||
"[[/src=\\S+(volta-2.0-cable)\\S+'/]]",
|
||||
"<div class='the9ca7210'>",
|
||||
"[['<p>Check out 9to5Mac on YouTube</p>']]"
|
||||
]
|
||||
},{
|
||||
"name" : "news.cnet.com",
|
||||
"url" : "http*://www.cnet.com/news/*",
|
||||
"title" : "<h1 class='speakableText'>",
|
||||
"desc" : "",
|
||||
"include" : "<article id='article-body'>",
|
||||
"exclude" : [
|
||||
"<svg class='svg-symbol'>",
|
||||
"<div class='shortcode'>"
|
||||
]
|
||||
},{
|
||||
"name" : "engadget.com",
|
||||
"url" : "https://www.engadget.com/*/*/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='article-text'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "gizmodo.com",
|
||||
"url" : "https://gizmodo.com/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='post-content'>",
|
||||
"exclude" : [
|
||||
"<div class='meta--pe'>",
|
||||
"<div class='instream-native-video'>",
|
||||
"<svg>"
|
||||
]
|
||||
},{
|
||||
"name" : "article.macworld.com",
|
||||
"url" : "https://www.macworld.com/article/*/*.html",
|
||||
"title" : "<title>",
|
||||
"desc" : "[[{$('h3[itemprop=\"description\"]').text()}]]",
|
||||
"include" : "<div id='drr-container'>",
|
||||
"exclude" : [
|
||||
""
|
||||
]
|
||||
},{
|
||||
"name" : "article.newscientist.com",
|
||||
"url" : "https://www.newscientist.com/article/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='article-content'>",
|
||||
"exclude" : [
|
||||
""
|
||||
],
|
||||
"css" : ".simpread-hidden{display:none!important;}"
|
||||
},{
|
||||
"name" : "readwrite.com",
|
||||
"url" : "https://readwrite.com/*/*/*/*/",
|
||||
"title" : "<h1 class=\"entry-title\">",
|
||||
"desc" : "",
|
||||
"include" : "<div class='entry-content'>",
|
||||
"exclude" : [
|
||||
"<div class='clearfix'>",
|
||||
"<div class='menu-social-media-container'>",
|
||||
"<div class='popular-tags'>",
|
||||
"<div class='related_posts'>",
|
||||
"<div class='about-author'>"
|
||||
]
|
||||
},{
|
||||
"name" : "article.techrepublic.com",
|
||||
"url" : "https://www.techrepublic.com/article/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "[[{$('p[itemprop=\"description\"]').text()}]]",
|
||||
"include" : "<div class='content'>",
|
||||
"exclude" : [
|
||||
"<div class='newsletter-shortcode'>",
|
||||
"<div class='shortcode'>"
|
||||
]
|
||||
},{
|
||||
"name" : "blog.techrepublic.com",
|
||||
"url" : "https://www.techrepublic.com/blog/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "[[{$('p[itemprop=\"description\"]').text()}]]",
|
||||
"include" : "<div class='content'>",
|
||||
"exclude" : [
|
||||
"<div class='newsletter-shortcode'>",
|
||||
"<div class='shortcode'>"
|
||||
]
|
||||
},{
|
||||
"name" : "dailydot.com",
|
||||
"url" : "https://www.dailydot.com/*/*",
|
||||
"title" : "<h1 class='dd-article-headline'>",
|
||||
"desc" : "[[{$('p[itemprop=\"description\"]').text()}]]",
|
||||
"include" : "<div class='dd-content-body-inner'>",
|
||||
"exclude" : [
|
||||
"<div class='moviereview'>"
|
||||
]
|
||||
},{
|
||||
"name" : "technologyreview.com",
|
||||
"url" : "https://www.technologyreview.com/s/*/*",
|
||||
"title" : "<title>",
|
||||
"desc" : "",
|
||||
"include" : "<div class='storyContent'>",
|
||||
"exclude" : [
|
||||
"<aside>",
|
||||
"<svg>"
|
||||
]
|
||||
}]
|
||||
}
|
||||
@ -47,10 +47,12 @@ const webpack = require( 'webpack' ),
|
||||
plugins.push(
|
||||
new CopyWebpackPlugin([
|
||||
{ from : "src/manifest.json" , to : '../' },
|
||||
{ from : "src//help_tips.json" , to : '../' },
|
||||
{ from : "src/website_list.json" , to : '../' },
|
||||
{ from : 'src/options/options.html', to : '../options/' },
|
||||
{ from : 'src/options/custom.html', to : '../options/' },
|
||||
{ from : 'src/options/sitemgr.html', to : '../options/' },
|
||||
{ from : 'src/options/notice.html', to : '../options/' },
|
||||
{ context: 'src/assets/images/', from : "*" , to : '../assets/images' },
|
||||
{ context: 'src/_locales/', from : "*/*" , to : '../_locales/' },
|
||||
])
|
||||
@ -136,7 +138,7 @@ const webpack = require( 'webpack' ),
|
||||
'exclude',
|
||||
'name',
|
||||
'url',
|
||||
'modals',
|
||||
'setting',
|
||||
|
||||
// olny options
|
||||
//'welcome',
|
||||
@ -171,6 +173,7 @@ const webpack = require( 'webpack' ),
|
||||
options : './src/options/options.js',
|
||||
custom : './src/options/custom.js',
|
||||
sitemgr : './src/options/sitemgr.js',
|
||||
notice : './src/options/notice.js',
|
||||
},
|
||||
|
||||
output: {
|
||||
@ -222,7 +225,6 @@ const webpack = require( 'webpack' ),
|
||||
notify_css : __dirname + '/src/vender/notify/notify.css',
|
||||
carous_css : __dirname + '/src/vender/carousel/carousel.css',
|
||||
|
||||
markdown : __dirname + '/node_modules/to-markdown/dist/to-markdown.js',
|
||||
epubpress : __dirname + '/node_modules/epub-press-js/build/index.js',
|
||||
nanoid : __dirname + '/node_modules/nanoid/generate.js',
|
||||
|
||||
@ -234,6 +236,11 @@ const webpack = require( 'webpack' ),
|
||||
dom2image : __dirname + '/src/vender/dom2image.min.js',
|
||||
filesaver : __dirname + '/src/vender/filesaver.min.js',
|
||||
instapaper : __dirname + '/src/vender/instapaper.js',
|
||||
webdav : __dirname + '/src/vender/webdav.js',
|
||||
markdown : __dirname + '/src/vender/turndown/turndown.js',
|
||||
mdgfm : __dirname + '/src/vender/turndown/turndown-plugin-gfm.js',
|
||||
intro : __dirname + '/src/vender/intro/intro.min.js',
|
||||
intro_css : __dirname + '/src/vender/intro/intro.min.css',
|
||||
|
||||
util : __dirname + '/src/service/util.js',
|
||||
local : __dirname + '/src/service/local.js',
|
||||
@ -261,7 +268,7 @@ const webpack = require( 'webpack' ),
|
||||
schedule : __dirname + '/src/read/progressbar.jsx',
|
||||
|
||||
keyboard : __dirname + '/src/module/keyboard.js',
|
||||
modals : __dirname + '/src/module/modals.jsx',
|
||||
setting : __dirname + '/src/module/setting.jsx',
|
||||
focusopt : __dirname + '/src/module/focus.jsx',
|
||||
readopt : __dirname + '/src/module/read.jsx',
|
||||
commonopt : __dirname + '/src/module/common.jsx',
|
||||
@ -279,6 +286,8 @@ const webpack = require( 'webpack' ),
|
||||
sitebar : __dirname + '/src/module/sitebar.jsx',
|
||||
enhancesite: __dirname + '/src/module/enhancesite.jsx',
|
||||
sharecard : __dirname + '/src/module/sharecard.jsx',
|
||||
notice : __dirname + '/src/module/notice.jsx',
|
||||
guide : __dirname + '/src/module/guide.jsx',
|
||||
editor : __dirname + '/src/module/common/editor.jsx',
|
||||
themesel : __dirname + '/src/module/common/theme.jsx',
|
||||
shortcuts : __dirname + '/src/module/common/shortcuts.jsx',
|
||||
|
||||