mirror of
https://github.com/Kenshin/simpread.git
synced 2026-01-25 14:28:34 +00:00
Merge branch 'feature/puread-level-iii-2' into develop
This commit is contained in:
commit
d543e16565
@ -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;
|
||||
}
|
||||
|
||||
@ -13,8 +13,13 @@ sr-rd-title, sr-rd-desc, sr-rd-content {
|
||||
}
|
||||
|
||||
sr-rd-title {
|
||||
text-rendering: optimizelegibility;
|
||||
display: -webkit-box;
|
||||
margin: 1em 0px 0.5em;
|
||||
overflow : hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
sr-rd-content {
|
||||
@ -186,6 +191,14 @@ sr-rd-content pre code * {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
sr-rd-content pre p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
sr-rd-content li code,
|
||||
sr-rd-content p code {
|
||||
margin: 0 4px;
|
||||
@ -193,6 +206,14 @@ sr-rd-content p code {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
sr-rd-content mark {
|
||||
margin: 0 5px;
|
||||
padding: 2px;
|
||||
|
||||
background: #fffdd1;
|
||||
border-bottom: 1px solid #ffedce;
|
||||
}
|
||||
|
||||
.sr-rd-content-img {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
@ -217,6 +238,17 @@ sr-rd-content p code {
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.sr-rd-content-center-small {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.sr-rd-content-center-small img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.sr-rd-content-nobeautify {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -257,6 +289,8 @@ sr-rd-mult sr-rd-mult-avatar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
sr-rd-mult sr-rd-mult-avatar span {
|
||||
|
||||
@ -267,6 +267,8 @@ function browserAction( is_update ) {
|
||||
*/
|
||||
function pRead() {
|
||||
pr = new PureRead( storage.sites );
|
||||
pr.cleanup = storage.read.cleanup == undefined ? true : storage.read.cleanup;
|
||||
pr.pure = storage.read.pure == undefined ? false : storage.read.pure;
|
||||
pr.AddPlugin( puplugin.Plugin() );
|
||||
pr.Getsites();
|
||||
storage.puread = pr;
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
},
|
||||
"content_scripts" : [
|
||||
{
|
||||
"matches" : [ "http://*/*", "https://*/*", "file:///*/*.txt" ],
|
||||
"matches" : [ "http://*/*", "https://*/*", "file:///*/*.txt", "file:///*/*.md" ],
|
||||
"exclude_matches": [ "http://localhost/*", "https://simpread.herokuapp.com/view/*" ],
|
||||
"js" : [
|
||||
"/bundle/common.js",
|
||||
|
||||
@ -32,6 +32,7 @@ export default class LabsOpt extends React.Component {
|
||||
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 );
|
||||
}
|
||||
|
||||
changeExclusion( event ) {
|
||||
@ -48,6 +49,10 @@ export default class LabsOpt extends React.Component {
|
||||
$( this.refs.toc ).velocity( value ? "slideDown" : "slideUp" );
|
||||
}
|
||||
|
||||
cleanupState( value ) {
|
||||
$( this.refs.cleanup ).velocity( value ? "slideDown" : "slideUp" );
|
||||
}
|
||||
|
||||
exclusionState( value ) {
|
||||
$( this.refs.exclusion ).velocity( value ? "slideDown" : "slideUp" );
|
||||
$( this.refs.whitelist ).velocity( !value ? "slideDown" : "slideUp" );
|
||||
@ -61,6 +66,7 @@ export default class LabsOpt extends React.Component {
|
||||
componentDidMount() {
|
||||
this.exclusionState( this.props.read.auto );
|
||||
this.tocState( this.props.read.toc );
|
||||
this.cleanupState( this.props.read.cleanup == undefined ? true : this.props.read.cleanup );
|
||||
}
|
||||
|
||||
onClick( state ) {
|
||||
@ -199,6 +205,22 @@ export default class LabsOpt extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 }
|
||||
thumbedColor="#3F51B5" trackedColor="#7986CB"
|
||||
label="是否启用增强解析模式?"
|
||||
desc="增强解析模式会对版面重新设计,包括:去除多余空格、优化版面结构等,此功能为测试版,遇到解析失败时,请关闭此功能。"
|
||||
onChange={ (s)=>this.onChange(s, "read", "cleanup") } />
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div className="label">授权管理</div>
|
||||
<div style={{ 'padding-top': '10px' }} className="lab">
|
||||
<Auth/>
|
||||
|
||||
@ -126,19 +126,23 @@ export default class ReadCtlbar extends React.Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if ( storage.current.fap ) {
|
||||
delete conf.readItems.exit;
|
||||
delete conf.readItems.option.items.setting;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if ( this.props.type.startsWith( "metaread::" ) || this.props.type.startsWith( "txtread::" ) ) {
|
||||
delete conf.readItems.option;
|
||||
try {
|
||||
if ( storage.current.fap ) {
|
||||
delete conf.readItems.exit;
|
||||
delete conf.readItems.option.items.setting;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if ( this.props.type.startsWith( "metaread::" ) || this.props.type.startsWith( "txtread::" ) ) {
|
||||
delete conf.readItems.option;
|
||||
}
|
||||
} catch ( err ) {
|
||||
// TO-DO
|
||||
}
|
||||
// hack code
|
||||
!/chrome/ig.test( navigator.userAgent ) && ( delete conf.readItems.dyslexia );
|
||||
|
||||
@ -54,6 +54,7 @@ class Read extends React.Component {
|
||||
|
||||
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 => {
|
||||
@ -79,29 +80,29 @@ class Read extends React.Component {
|
||||
localStorage.removeItem( "sr-update-site" );
|
||||
} else {
|
||||
$root
|
||||
.addClass( "simpread-font" )
|
||||
.addClass( theme )
|
||||
.find( rdclsjq )
|
||||
.addClass( "simpread-font" )
|
||||
.addClass( theme )
|
||||
.sreffect( { opacity: 1 }, { delay: 100 })
|
||||
.addClass( "simpread-read-root-show" );
|
||||
.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" && $( "sr-rd-content" ).css({ "word-wrap": "break-word", "white-space": "pre-wrap" });
|
||||
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 );
|
||||
|
||||
!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.read.site.css && this.props.read.site.css.length > 0 &&
|
||||
ss.SiteCSS( this.props.read.site.css );
|
||||
|
||||
kbd.Render( $( "sr-rd-content" ));
|
||||
tooltip.Render( rdclsjq );
|
||||
waves.Render({ root: rdclsjq });
|
||||
@ -109,7 +110,8 @@ class Read extends React.Component {
|
||||
|
||||
loadPlugins( "read_complete" );
|
||||
|
||||
localStorage.removeItem( "sr-update-site" );
|
||||
// Puread level to III,can't work this flow.
|
||||
//localStorage.removeItem( "sr-update-site" );
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,14 +211,17 @@ class Read extends React.Component {
|
||||
/**
|
||||
* Render entry
|
||||
*
|
||||
* @param {boolean} true: call mathJaxMode(); false: @see mathJaxMode
|
||||
*/
|
||||
function Render() {
|
||||
function Render( callMathjax = true ) {
|
||||
callMathjax && mathJaxMode();
|
||||
storage.pr.ReadMode();
|
||||
if ( typeof storage.pr.html.include == "string" && storage.pr.html.include.startsWith( "<sr-rd-content-error>" ) ) {
|
||||
console.warn( '=== Adapter failed call Readability View ===' )
|
||||
storage.pr.Readability();
|
||||
storage.pr.ReadMode();
|
||||
}
|
||||
console.log( "current puread object is ", storage.pr )
|
||||
} else console.warn( '=== Normal Read mode ===' )
|
||||
console.warn( "=== Current PuRead object is ===", storage.pr )
|
||||
ReactDOM.render( <Read read={ storage.current } wrapper={ storage.pr.html } />, getReadRoot() );
|
||||
}
|
||||
|
||||
@ -258,6 +263,29 @@ function Exit() {
|
||||
}).addClass( "simpread-read-root-hide" );
|
||||
}
|
||||
|
||||
/**
|
||||
* MathJax Mode
|
||||
*/
|
||||
function mathJaxMode() {
|
||||
if ( storage.pr.isMathJax() && storage.pr.state == "temp" ) {
|
||||
console.warn( '=== MathJax Mode ===' )
|
||||
const dom = storage.pr.MathJaxMode();
|
||||
console.log( 'current get dom is ', dom )
|
||||
if ( typeof dom == "undefined" ) {
|
||||
new Notify().Render( "智能感知失败,请移动鼠标框选。" );
|
||||
Highlight().done( dom => {
|
||||
storage.pr.TempMode( "read", dom );
|
||||
Render( false );
|
||||
});
|
||||
} else if ( typeof dom == "string" ) {
|
||||
const html = storage.pr.GetDom( dom, "html" );
|
||||
storage.pr.Newsite( "read", html );
|
||||
} else {
|
||||
storage.pr.TempMode( "read", dom[0] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read root
|
||||
*
|
||||
|
||||
@ -1,547 +0,0 @@
|
||||
console.log( "=== PureRead: AdapteSite load ===" )
|
||||
|
||||
import * as util from './util';
|
||||
|
||||
const site = {
|
||||
url : "",
|
||||
target : "",
|
||||
matching : [],
|
||||
name : "", // only read mode
|
||||
title : "", // only read mode
|
||||
desc : "", // only read mode
|
||||
exclude : [],
|
||||
include : "",
|
||||
avatar : [],
|
||||
paging : [],
|
||||
};
|
||||
let minimatch, rdability;
|
||||
|
||||
export default class AdapteSite {
|
||||
|
||||
constructor( sites = { global:[], custom:[], local:[] } ) {
|
||||
this.url = util.getURI();
|
||||
this.sites = sites; // include: global, custom, local, person
|
||||
this.current = {};
|
||||
this.state = "none"; // include: meta, txt, adapter, none, temp
|
||||
this.origins = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global minimatch
|
||||
*/
|
||||
SetMinimatch( value ) {
|
||||
minimatch = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global rdability
|
||||
*/
|
||||
SetRdability( value ) {
|
||||
rdability = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not adapter usage mozilla readability and readtmpl
|
||||
*/
|
||||
Readability() {
|
||||
try {
|
||||
const location = document.location,
|
||||
uri = {
|
||||
spec: location.href,
|
||||
host: location.host,
|
||||
prePath: location.protocol + "//" + location.host,
|
||||
scheme: location.protocol.substr(0, location.protocol.indexOf(":")),
|
||||
pathBase: location.protocol + "//" + location.host + location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1)
|
||||
},
|
||||
article = new rdability.Readability( uri, document.cloneNode(true) ).parse();
|
||||
if ( article && article.content != "" ) {
|
||||
console.warn( "current parse is Readability", article )
|
||||
this.Newsite( "read", article.content );
|
||||
this.dom = $(article.content)[0];
|
||||
this.state = "temp";
|
||||
} else throw "Readability error";
|
||||
} catch ( error ) {
|
||||
const $dom = readtmpl();
|
||||
if ( $dom != -1 ) {
|
||||
this.Newsite( "read", $dom[0].outerHTML );
|
||||
this.dom = $dom[0];
|
||||
this.state = "temp";
|
||||
} else this.current.site = util.clone( site );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site from url
|
||||
*
|
||||
* @param {string} include: global, custom, local
|
||||
* @param {string} url
|
||||
*/
|
||||
Getsite( type, url ) {
|
||||
return this.sites[type].find( item => item[0] == url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sites from url
|
||||
*/
|
||||
Getsites() {
|
||||
const matching = [],
|
||||
meta = readmeta();
|
||||
this.current.url = this.url;
|
||||
if ( meta ) {
|
||||
this.current.auto = meta.auto;
|
||||
this.current.url = meta.url;
|
||||
delete meta.auto;
|
||||
delete meta.url;
|
||||
this.current.site = { ...meta };
|
||||
this.current.site.name.startsWith( "metaread::" ) && ( this.state = "meta" );
|
||||
this.current.site.name.startsWith( "txtread::" ) && ( this.state = "txt" );
|
||||
} else {
|
||||
getsite( "local", new Map( this.sites.local ), this.url, matching );
|
||||
getsite( "global", new Map( this.sites.global ), this.url, matching );
|
||||
getsite( "person", new Map( this.sites.person ), this.url, matching );
|
||||
getsite( "custom", new Map( this.sites.custom ), this.url, matching );
|
||||
if ( matching.length > 0 ) {
|
||||
let found;
|
||||
matching.forEach( site => {
|
||||
if ( site[1].active ) {
|
||||
found = site;
|
||||
this.current.url = found[0];
|
||||
this.current.site = this.Safesite({ ...found[1] }, found[2], found[0] );
|
||||
this.state = "adapter";
|
||||
}
|
||||
});
|
||||
if ( !found ) {
|
||||
const found = matching[0];
|
||||
found[1].active = true;
|
||||
this.current.url = found[0];
|
||||
this.current.site = this.Safesite({ ...found[1] }, found[2], found[0] );
|
||||
this.state = "adapter";
|
||||
}
|
||||
} else {
|
||||
const obj = readmulti();
|
||||
if ( obj != -1 ) {
|
||||
this.Newmultisite( "read", obj );
|
||||
this.state = "temp";
|
||||
} else {
|
||||
this.Readability();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.current.site.matching = matching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new sites to this.sites.global( global sites )
|
||||
*
|
||||
* @param {object} sites.[array]
|
||||
* @return {int} update sites count
|
||||
*/
|
||||
Addsites( result ) {
|
||||
let count = 0;
|
||||
if ( this.sites.global.length == 0 ) {
|
||||
this.sites.global = this.Formatsites( result );
|
||||
count = this.sites.global.length;
|
||||
}
|
||||
else {
|
||||
const obj = addsites( this.Formatsites( result ), this.sites.global );
|
||||
count = obj.count;
|
||||
this.sites.global = obj.newsites;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new sites to this.sites.local( local sites )
|
||||
*
|
||||
* @param {object} new sites
|
||||
* @return {array} this.sites.local
|
||||
*/
|
||||
Addlocalsites( new_sites ) {
|
||||
this.sites.local = [ ...new_sites ];
|
||||
return this.sites.local;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all sites to this.sites
|
||||
*
|
||||
* @param {object} new sites
|
||||
* @return {object} this.sites
|
||||
*/
|
||||
Addallsites( sites ) {
|
||||
this.sites = {
|
||||
global: [ ...sites.global ],
|
||||
person: [ ...sites.person ],
|
||||
custom: [ ...sites.custom ],
|
||||
local : [ ...sites.local ],
|
||||
};
|
||||
return this.sites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new site( read only )
|
||||
*
|
||||
* @param {string} include: focus, read
|
||||
* @param {string} when read html is dom.outerHTML
|
||||
*/
|
||||
Newsite( mode, html ) {
|
||||
const new_site = { mode, url: window.location.href, site: { name: `tempread::${window.location.host}`, title: "<title>", desc: "", include: "", exclude: [] } };
|
||||
html && ( new_site.site.html = html );
|
||||
this.current.mode = new_site.mode,
|
||||
this.current.url = new_site.url;
|
||||
this.current.site = this.Safesite({ ...new_site.site }, "local", new_site.url );
|
||||
console.log( "【read only】current site object is ", this.current )
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new multi-site( read only )
|
||||
*
|
||||
* @param {string} include: focus, read
|
||||
* @param {object} multi-page, avator, include
|
||||
*/
|
||||
Newmultisite( mode, multi ) {
|
||||
const new_site = { mode, url: window.location.href, site: { name: `tempread::${window.location.host}`, title: "<title>", desc: "", include: multi.include, exclude: [], avatar: multi.avatar } };
|
||||
this.current.mode = new_site.mode,
|
||||
this.current.url = new_site.url;
|
||||
this.current.site = this.Safesite({ ...new_site.site }, "local", new_site.url );
|
||||
console.log( "【read only】current multi-site object is ", this.current )
|
||||
}
|
||||
|
||||
/**
|
||||
* Update url and site from param
|
||||
*
|
||||
* @param {string} value is: global, custom, local
|
||||
* @param {string} older url
|
||||
* @param {array} [ url, new site]
|
||||
*/
|
||||
Updatesite( key, older, newer ) {
|
||||
let idx = this.sites[key].findIndex( item => item[0] == older );
|
||||
idx == -1 && ( idx = this.sites[key].length );
|
||||
this.sites[key].splice( idx, 1, newer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete site from this.sites.local
|
||||
*
|
||||
* @param {string} value is: global, custom, local
|
||||
* @param {string} older url
|
||||
* @param {func} callback
|
||||
*/
|
||||
Deletesite( key, older, callback ) {
|
||||
let idx = this.sites[key].findIndex( item => item[0] == older );
|
||||
idx != -1 && this.sites[key].splice( idx, 1 );
|
||||
callback( idx );
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe site, add all site props
|
||||
*
|
||||
* @param {object} modify site
|
||||
* @param {string} target include: global custom local
|
||||
* @param {string} url
|
||||
* @returns {object} site
|
||||
*/
|
||||
Safesite( site, target, url ) {
|
||||
site.url = url;
|
||||
site.target = target;
|
||||
site.name == "" && ( site.name = "tempread::" );
|
||||
( !site.avatar || site.avatar.length == 0 ) && ( site.avatar = [{ name: "" }, { url: "" }]);
|
||||
( !site.paging || site.paging.length == 0 ) && ( site.paging = [{ prev: "" }, { next: "" }]);
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean useless site props
|
||||
*
|
||||
* @param {object} site
|
||||
* @returns {object} site
|
||||
*/
|
||||
Cleansite( site ) {
|
||||
delete site.url;
|
||||
delete site.html;
|
||||
delete site.target;
|
||||
delete site.matching;
|
||||
site.avatar && site.avatar.length > 0 && site.avatar[0].name == "" && delete site.avatar;
|
||||
site.paging && site.paging.length > 0 && site.paging[0].prev == "" && delete site.paging;
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format sites object from local or remote json file
|
||||
*
|
||||
* @param {object} sites.[array]
|
||||
* @return {array} foramat e.g. [[ <url>, object ],[ <url>, object ]]
|
||||
*/
|
||||
Formatsites( result ) {
|
||||
const format = new Map();
|
||||
for ( let site of result.sites ) {
|
||||
if ( verifysite( site ) != 0 ) continue;
|
||||
const url = site.url;
|
||||
delete site.url;
|
||||
format.set( url, site );
|
||||
}
|
||||
return [ ...format ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear sites
|
||||
*
|
||||
* @param {string} site type, only include: global, custom. local
|
||||
*/
|
||||
Clearsites( type ) {
|
||||
type ? ( this.sites[type] = [] ) : ( this.sites = { global:[], custom:[], local:[] });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add urls to origins
|
||||
*
|
||||
* @param {json} result json
|
||||
*/
|
||||
Origins( result ) {
|
||||
let urls = result.origins.map( item => item.url );
|
||||
urls = new Set( this.origins.concat( urls ) );
|
||||
urls.forEach( item => {
|
||||
if ( item.trim() == "" || !item.trim().startsWith( "http" ) || !item.trim().endsWith( ".json" ) ) urls.delete( item );
|
||||
});
|
||||
this.origins = [ ...urls ];
|
||||
return this.origins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new sites to this.sites.custom( custom sites )
|
||||
*
|
||||
* @param {object} new sites
|
||||
* @return {array} this.sites.custom
|
||||
*/
|
||||
Addorigins( new_sites ) {
|
||||
this.sites.custom = [ ...new_sites ];
|
||||
return this.sites.custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear origins
|
||||
*
|
||||
* @returns custom.length
|
||||
*/
|
||||
Clearorigins() {
|
||||
const len = this.sites.custom.length;
|
||||
this.sites.custom = [];
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get readmeta, inlcude: txtread and metaread
|
||||
*
|
||||
* @return {object} meata data or undefined
|
||||
*/
|
||||
function readmeta() {
|
||||
if ( minimatch( location.href, "file://**/*.txt" ) || minimatch( location.href, "http*://**/*.txt" ) ) {
|
||||
return readtxt();
|
||||
}
|
||||
const reg = /<\S+ (class|id)=("|')?[\w-_=;:' ]+("|')?>?$|<[^/][-_a-zA-Z0-9]+>?$/ig, // from util.verifyHtml()
|
||||
meta = {
|
||||
name : $( "meta[name='simpread:name']" ).attr( "content" ),
|
||||
url : $( "meta[name='simpread:url']" ).attr( "content" ),
|
||||
title : $( "meta[name='simpread:title']" ).attr( "content" ),
|
||||
desc : $( "meta[name='simpread:desc']" ).attr( "content" ),
|
||||
include: $( "meta[name='simpread:include']" ).attr( "content" ),
|
||||
exp : $( "meta[name='simpread:exclude']" ).attr( "content" ),
|
||||
auto : $( "meta[name='simpread:auto']" ).attr( "content" ),
|
||||
exclude: [],
|
||||
};
|
||||
if ( meta.name && meta.include ) {
|
||||
if ( meta.url && !minimatch( location.href, meta.url )) {
|
||||
return undefined;
|
||||
}
|
||||
!meta.title && ( meta.title = "<title>" );
|
||||
!meta.desc && ( meta.desc = "" );
|
||||
!meta.exp && ( meta.exp = "" );
|
||||
meta.name = `metaread::${meta.name}`;
|
||||
meta.auto = meta.auto == "true" ? true : false;
|
||||
const idx = [ "title", "desc", "include", "exp" ].findIndex( item => meta[item] != "" && !meta[item].match( reg ));
|
||||
meta.exclude.push( meta.exp );
|
||||
delete meta.exp;
|
||||
console.assert( idx == -1, "meta read mode error. ", meta )
|
||||
return idx == -1 ? meta : undefined;
|
||||
} else {
|
||||
console.warn( "current not found meta data", meta )
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read txt, include: file and http
|
||||
*/
|
||||
function readtxt() {
|
||||
const title = location.pathname.split( "/" ).pop(),
|
||||
type = location.protocol == "file:" ? "local" : "remote",
|
||||
meta = {
|
||||
name : `txtread::${type}`,
|
||||
title : "<title>",
|
||||
desc : "",
|
||||
include: "<pre>",
|
||||
auto : false,
|
||||
exclude: [],
|
||||
};
|
||||
if ( type == "remote" ) {
|
||||
meta.include = "";
|
||||
meta.html = $( "body pre" ).html().replace( /\n/ig, "<br>" );
|
||||
}
|
||||
!$( "title" ).html() && $( "head" ).append( `<title>${ decodeURI(title.replace( ".txt", "" )) }</title>` );
|
||||
return meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read mode template, include:
|
||||
*
|
||||
* - Hexo
|
||||
* - WordPress
|
||||
* - Common( include <article> )
|
||||
*
|
||||
* @return {jquery} jquery object
|
||||
*/
|
||||
function readtmpl() {
|
||||
const $root = $( "body" ),
|
||||
selectors = [
|
||||
".post-content", ".entry-content", ".post-article", ".content-post", ".article-entry", ".article-content",
|
||||
".article-body", ".markdown-body",
|
||||
"[itemprop='articleBody']",
|
||||
"article",
|
||||
".post", ".content",
|
||||
];
|
||||
for ( const selector of selectors ) {
|
||||
const $target = $root.find( selector );
|
||||
if ( $target.length > 0 ) {
|
||||
console.warn( "current selector is", selector );
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read mode multi template, include:
|
||||
*
|
||||
* - Discuz
|
||||
* - Discourse
|
||||
*
|
||||
* @return {object} true: object; false: -1
|
||||
*/
|
||||
function readmulti() {
|
||||
if ( location.pathname.includes( "thread" ) || location.pathname.includes( "forum.php" ) ) {
|
||||
if ( $('.t_f').length > 0 && $('.favatar').find('.authi').length > 0 && $('.avatar').find('img').length > 0 ) {
|
||||
return {
|
||||
avatar: [
|
||||
{"name" : "[[{$('.favatar').find('.authi')}]]"},
|
||||
{"url" : "[[{$('.avatar').find('img')}]]"}
|
||||
],
|
||||
include: "[[{$('.t_f')}]]"
|
||||
};
|
||||
}
|
||||
} else if ( /\/t\/[\w-]+\/\d+/.test( location.pathname ) && $('meta[name=generator]').attr("content").includes("discourse") ) {
|
||||
return {
|
||||
avatar: [
|
||||
{"name" : "[[{$('.topic-avatar').find('.a[data-user-card]')}]]"},
|
||||
{"url" : "[[{$('.topic-avatar').find('img')}]]"}
|
||||
],
|
||||
include: "[[{$('.cooked')}]]"
|
||||
};
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new sites to old sites
|
||||
*
|
||||
* @param {array} new sites from local or remote
|
||||
* @param {array} current sites from this.sites.global
|
||||
* @return {object} count: new sites; forced: update sites( discard, all site must be forced update)
|
||||
*/
|
||||
function addsites( newsites, old ) {
|
||||
const oldsites = new Map( old ),
|
||||
urls = [ ...oldsites.keys() ];
|
||||
let [ count, forced ] = [ 0, 0 ];
|
||||
newsites.map( site => {
|
||||
if ( !urls.includes( site[0] ) ) {
|
||||
count++;
|
||||
} else if ( urls.includes( site[0] )) {
|
||||
forced++;
|
||||
}
|
||||
});
|
||||
return { count, newsites };
|
||||
}
|
||||
|
||||
/**
|
||||
* Find site by url from sites
|
||||
*
|
||||
* @param {string} type, include: global, local, custom
|
||||
* @param {map} sites
|
||||
* @param {string} url
|
||||
* @param {array} matching sites
|
||||
*
|
||||
* @return {array} 0: current site; 1: current url, 2: type
|
||||
*/
|
||||
function getsite( type, sites, url, matching = [] ) {
|
||||
const domain = (names)=>{
|
||||
const arr = names.replace( "www.", "" ).match( /\.\S+\.\S+/g );
|
||||
if ( arr ) {
|
||||
return arr[0].substr(1);
|
||||
} else {
|
||||
return names.replace( "www.", "" );
|
||||
}
|
||||
},
|
||||
urls = [ ...sites.keys() ],
|
||||
arr = url.match( /[.a-zA-z0-9-_]+/g ),
|
||||
uri = arr[1].replace( "www.", "" ),
|
||||
hostname = domain( window.location.hostname ),
|
||||
isroot = ()=>window.location.pathname == "/" || /\/(default|index|portal).[0-9a-zA-Z]+$/.test(window.location.pathname);
|
||||
for ( const cur of urls ) {
|
||||
const name = sites.get(cur).name,
|
||||
sufname= domain( name );
|
||||
if ( !isroot() && !cur.endsWith( "*" ) && cur.replace( /^http[s]?:/, "" ) == url.replace( /^http[s]?:/, "" ) ) {
|
||||
matching.push( [ cur, util.clone( sites.get( cur )), type ] );
|
||||
} else if ( cur.match( /\*/g ) && cur.match( /\*/g ).length == 1 && !isroot() && cur.endsWith( "*" ) && uri.includes( sufname ) && hostname == sufname && url.includes( name ) ) {
|
||||
// e.g. https://www.douban.com/* http://mp.weixin.qq.com/*
|
||||
matching.push( [ cur, util.clone( sites.get( cur )), type ] );
|
||||
} else if ( minimatch( window.location.origin + window.location.pathname, cur ) ) {
|
||||
matching.push( [ cur, util.clone( sites.get( cur )), type ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify site validity, include:
|
||||
* - name, url, include, error is -1
|
||||
* - title include desc, error is -2
|
||||
* - paging, error is -3 ~ -6
|
||||
* - avatar, error is -7 ~ -10
|
||||
*
|
||||
* @param {object} site
|
||||
*/
|
||||
function verifysite( site ) {
|
||||
if ( !site.name || !site.url || !site.include ) return -1;
|
||||
if ( util.verifyHtml( site.title )[0] == -1 ||
|
||||
util.verifyHtml( site.include )[0] == -1 ||
|
||||
util.verifyHtml( site.desc )[0] == -1
|
||||
) {
|
||||
return -2;
|
||||
}
|
||||
if ( site.paging ) {
|
||||
if ( site.paging.length != 2 ) return -3;
|
||||
if ( !site.paging[0].prev ) return -4;
|
||||
if ( !site.paging[1].next ) return -5;
|
||||
if ( util.verifyHtml( site.paging[0].prev )[0] == -1 || util.verifyHtml( site.paging[1].next )[0] == -1 ) {
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
if ( site.avatar ) {
|
||||
if ( site.avatar.length != 2 ) return -7;
|
||||
if ( !site.avatar[0].name ) return -8;
|
||||
if ( !site.avatar[1].url ) return -9;
|
||||
if ( util.verifyHtml( site.avatar[0].name )[0] == -1 || util.verifyHtml( site.avatar[1].url )[0] == -1 ) {
|
||||
return -10;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
console.log( "=== PureRead: Plugin load ===" )
|
||||
|
||||
import pangu from './plugin/pangu.min';
|
||||
import minimatch from './plugin/minimatch';
|
||||
import * as be from './plugin/beautify';
|
||||
import * as ss from './plugin/stylesheet';
|
||||
import * as rda from './plugin/readability';
|
||||
|
||||
const plugins = {
|
||||
pangu : pangu,
|
||||
minimatch: minimatch,
|
||||
beautify : be,
|
||||
style : ss,
|
||||
rdability: rda,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get PureRead Plugin
|
||||
*
|
||||
* @param {string} plugin name, when undefined, return all plugin
|
||||
* @return {object} all plugins
|
||||
* {string} plgin
|
||||
*/
|
||||
export function Plugin( type ) {
|
||||
let result;
|
||||
if ( type == undefined ) {
|
||||
result = plugins;
|
||||
} else {
|
||||
result = plugins[type];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1,385 +0,0 @@
|
||||
console.log( "=== PureRead: Beautify load ===" )
|
||||
|
||||
/**
|
||||
* Beautify html
|
||||
*
|
||||
* @param {string} storage.current.site.name
|
||||
*/
|
||||
function specbeautify( name, $target ) {
|
||||
switch ( name ) {
|
||||
case "sspai.com":
|
||||
//TO-DO
|
||||
$target.find( ".relation-apps" ).remove();
|
||||
$target.find( ".ss-app-card" ).remove();
|
||||
break;
|
||||
case "post.smzdm.com":
|
||||
$target.find( "img.face" ).addClass( "sr-rd-content-nobeautify" );
|
||||
$target.find( ".insert-outer img" ).addClass( "sr-rd-content-nobeautify" );
|
||||
break;
|
||||
case "infoq.com":
|
||||
$target.find( "img" ).map( (index, item) => {
|
||||
if ( $(item).css("float") == "left" ) {
|
||||
$(item).addClass( "sr-rd-content-nobeautify" );
|
||||
}
|
||||
});
|
||||
$target.find( "script" ).remove();
|
||||
break;
|
||||
case "appinn.com":
|
||||
$target.find( ".emoji" ).addClass( "sr-rd-content-nobeautify" );
|
||||
break;
|
||||
case "hacpai.com":
|
||||
$target.find( ".emoji" ).addClass( "sr-rd-content-nobeautify" );
|
||||
break;
|
||||
case "douban.com":
|
||||
$target.find( ".review-content" ).children().unwrap();
|
||||
$target.find( "table" ).addClass( "sr-rd-content-center" );
|
||||
$target.find( "p" ).css({"white-space": "pre-wrap"});
|
||||
$target.find( ".cc" ).removeClass();
|
||||
break;
|
||||
case "qdaily.com":
|
||||
$target.find( "img" ).map( (index, item) => {
|
||||
const $target = $(item),
|
||||
height = Number.parseInt($target.css("height"));
|
||||
if ( height == 0 ) $target.remove();
|
||||
});
|
||||
$target.find( ".com-insert-images" ).map( (index, item) => {
|
||||
const $target = $(item),
|
||||
imgs = $target.find( "img" ).map( (index, item)=>`<div>${item.outerHTML}</div>` ),
|
||||
str = imgs.get().join( "" );
|
||||
$target.empty().removeAttr( "class" ).append( str );
|
||||
});
|
||||
$target.find( ".com-insert-embed" ).remove();
|
||||
break;
|
||||
case "news.mtime.com":
|
||||
$target.find( ".newspictool" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$img = $target.find( "img" ),
|
||||
$label = $target.find( "p:last" );
|
||||
$target.removeAttr( "class" ).addClass( "sr-rd-content-center" ).empty().append( $img ).append( $label );
|
||||
});
|
||||
break;
|
||||
case "blog.csdn.net":
|
||||
$target.find( ".save_code" ).remove();
|
||||
$target.find( ".pre-numbering" ).remove();
|
||||
$target.find( "pre" ).removeAttr( "style" ).removeAttr( "class" );
|
||||
$target.find( "code" ).removeAttr( "style" );
|
||||
$target.find( ".dp-highlighter" ).map( ( index, item )=> {
|
||||
$(item).find(".bar .tools").remove();
|
||||
if ( $(item).next().is( "pre" )) $(item).next().remove();
|
||||
});
|
||||
break;
|
||||
case "news.sohu.com":
|
||||
$target.find( ".conserve-photo" ).remove();
|
||||
$target.find( "table" ).addClass( "sr-rd-content-center" );
|
||||
break;
|
||||
case "qq.com":
|
||||
$target.find( ".rv-root-v2, #backqqcom" ).remove();
|
||||
break;
|
||||
case "azofreeware.com":
|
||||
$target.find( "iframe" ).remove();
|
||||
break;
|
||||
case "apprcn.com":
|
||||
$target.find( "img" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
src = $target.attr( "src" );
|
||||
if ( src && src.includes( "Apprcn_Wechat_Small.jpeg" ) ) $target.parent().remove();
|
||||
});
|
||||
$target.find( "a" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
text = $target.text();
|
||||
if ( text == "来自反斗软件" ) $target.parent().remove();
|
||||
});
|
||||
break;
|
||||
case "tieba.baidu.com":
|
||||
$target.find( ".BDE_Smiley" ).addClass( "sr-rd-content-nobeautify" );
|
||||
$target.find( ".replace_div" ).removeAttr( "class" ).removeAttr( "style" );
|
||||
$target.find( ".replace_tip" ).remove();
|
||||
$target.find( ".d_post_content, .j_d_post_content, .post_bubble_top, .post_bubble_middle, .post_bubble_bottom" ).map( ( idx, target ) => {
|
||||
$( target ).removeAttr( "class" ).removeAttr( "style" );
|
||||
});
|
||||
$( "body" ).find( ".p_author_face" ).map( ( idx, target ) => {
|
||||
const $target = $( target ).find( "img" ),
|
||||
src = $target.attr( "data-tb-lazyload" ),
|
||||
name = $target.attr( "username" );
|
||||
src && $( "sr-rd-mult-avatar" ).find( "span" ).map( ( idx, span ) => {
|
||||
const $span = $( span ),
|
||||
text = $span.text();
|
||||
if ( text == name ) {
|
||||
$span.parent().find( "img" ).attr( "src", src );
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "jingyan.baidu.com":
|
||||
$target.find( ".exp-image-wraper" ).removeAttr( "class" ).removeAttr( "href" );
|
||||
break;
|
||||
case "question.zhihu.com":
|
||||
$target.find( ".zu-edit-button" ).remove();
|
||||
$target.find( "a.external" ).map( ( idx, target ) => {
|
||||
$( target ).removeAttr( "class" )
|
||||
.attr( "style", "border: none;" );
|
||||
});
|
||||
$target.find( ".VagueImage" ).map( ( idx, target ) => {
|
||||
const $target = $( target ),
|
||||
src = $target.attr( "data-src" );
|
||||
$target.replaceWith( `<img class="sr-rd-content-img" src="${ src }" style="zoom: 0.6;">` )
|
||||
});
|
||||
break;
|
||||
case "chiphell.com":
|
||||
$target.find( "img" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$parent = $target.parent(),
|
||||
src = $target.attr( "src" ),
|
||||
smilieid= $target.attr( "smilieid" );
|
||||
if ( $parent.is( "ignore_js_op" )) $target.unwrap();
|
||||
smilieid && src && src.includes( "static/image/smiley" ) &&
|
||||
$target.addClass( "sr-rd-content-nobeautify" ).attr( "style", "width: 50px;" );
|
||||
});
|
||||
$target.find( ".quote" ).remove();
|
||||
break;
|
||||
case "jiemian.com":
|
||||
$target.find( "script" ).remove();
|
||||
break;
|
||||
case "36kr.com":
|
||||
$target.find( ".load-html-img" ).removeAttr( "class" );
|
||||
break;
|
||||
case "cnblogs.com":
|
||||
$target.find( ".cnblogs_code" ).removeClass();
|
||||
$target.find( ".cnblogs_code_hide" ).removeClass().removeAttr( "style" );
|
||||
$target.find( ".cnblogs_code_toolbar" ).remove();
|
||||
$target.find( ".code_img_opened" ).remove();
|
||||
$target.find( ".code_img_closed" ).remove();
|
||||
break;
|
||||
case "news.cnblogs.com":
|
||||
$target.find( ".topic_img" ).remove();
|
||||
break;
|
||||
case "g-cores.com":
|
||||
$target.find( ".swiper-slide-active" ).find( "img" ).map( ( index, item ) => {
|
||||
const $target = $(item);
|
||||
$target.parent().parent().parent().parent().parent().parent().removeAttr( "class" ).removeAttr( "style" ).html($target);
|
||||
});
|
||||
break;
|
||||
case "feng.com":
|
||||
$target.find( "span" ).removeAttr( "style" );
|
||||
break;
|
||||
case "young.ifeng.com":
|
||||
$target.find( "span" ).removeAttr( "style" );
|
||||
break;
|
||||
case "ftchinese.com":
|
||||
$target.find( "script" ).remove();
|
||||
break;
|
||||
case "segmentfault.com":
|
||||
$target.find( ".widget-codetool" ).remove();
|
||||
break;
|
||||
case "mp.weixin.qq.com":
|
||||
$target.find( 'section[powered-by="xiumi.us"]' ).find( "img" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
src = $target.attr( "data-src" );
|
||||
$target.addClass( "sr-rd-content-nobeautify" ).attr( "src", src );
|
||||
});
|
||||
break;
|
||||
case "ruby-china.org":
|
||||
$target.find( ".twemoji" ).remove();
|
||||
break;
|
||||
case "w3cplus.com":
|
||||
$target.find( "iframe" ).addClass( "sr-rd-content-nobeautify" );
|
||||
break;
|
||||
case "zuojj.com":
|
||||
$target.find( ".syntaxhighlighter .Brush" ).attr( "style", "font-size: .7em !important;" )
|
||||
break;
|
||||
case "aotu.io":
|
||||
$target.find( ".highlight table" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$pre = $target.find( "pre" ),
|
||||
$table = $target.find( "table" );
|
||||
$target.html( $pre[1] );
|
||||
$table.unwrap();
|
||||
});
|
||||
$target.find( "table" ).addClass( "sr-rd-content-center" );
|
||||
break;
|
||||
case "colobu.com":
|
||||
$target.find( ".highlight table" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$pre = $target.find( "pre" );
|
||||
$target.html( $pre[1] );
|
||||
$target.unwrap();
|
||||
});
|
||||
break;
|
||||
case "hao.caibaojian.com":
|
||||
$target.find( ".tlink" ).map( ( index, item ) => {
|
||||
const $target = $(item);
|
||||
$target.html( "<link>" );
|
||||
});
|
||||
break;
|
||||
case "wkee.net":
|
||||
$target.find( "script" ).remove();
|
||||
break;
|
||||
case "linux.cn":
|
||||
$target.find( "pre" ).attr( "style", "background-color: #161b20; background-image: none;" );
|
||||
$target.find( "code" ).attr( "style", "background-color: transparent; background-image: none;" );
|
||||
break;
|
||||
case "zhuanlan.zhihu.com":
|
||||
$target.find( "div[data-src]" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
src = $target.attr( "data-src" );
|
||||
$target.replaceWith( `<div class="sr-rd-content-center"><img src="${ src }"></div>` );
|
||||
});
|
||||
break;
|
||||
case "jianshu.com":
|
||||
$target.find( ".image-package" ).map( ( index, item ) => {
|
||||
const $target = $( item ),
|
||||
$div = $target.find( "img" );
|
||||
$target.html( $div );
|
||||
});
|
||||
break;
|
||||
case "medium.com":
|
||||
$target.find( "figure" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$img = $target.find( "img" );
|
||||
$target.replaceWith( `<div class="sr-rd-content-center"><img class="sr-rd-content-nobeautify" src="${ $img.attr('data-src') }" style="max-width:100%"></div>` );
|
||||
});
|
||||
break;
|
||||
case "worldcup.fifa.com":
|
||||
$target.find( "iframe" ).css({ width: "790px", height: "450px" });
|
||||
$target.find( "div" ).removeClass();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove spare tag
|
||||
*
|
||||
* @param {string} storage.current.site.name
|
||||
* @param {jquery} jquery object
|
||||
*/
|
||||
function removeSpareTag( name, $target ) {
|
||||
let [ remove, tag ] = [ false, "" ];
|
||||
if ([ "lib.csdn.net", "huxiu.com", "my.oschina.net", "caixin.com", "163.com", "steachs.com", "hacpai.com", "apprcn.com", "mp.weixin.qq.com" ].includes( name )) {
|
||||
[ remove, tag ] = [ true, "p" ];
|
||||
} else if ([ "nationalgeographic.com.cn", "dgtle.com", "news.mtime.com" ].includes( name )) {
|
||||
[ remove, tag ] = [ true, "div" ];
|
||||
} else if ( ["chiphell.com"].includes( name ) ) {
|
||||
[ remove, tag ] = [ true, "font" ];
|
||||
}
|
||||
if ( remove ) {
|
||||
$target.find( tag ).map( ( index, item ) => {
|
||||
const str = $(item).text().toLowerCase().trim();
|
||||
if ( $(item).find( "img" ).length == 0 && str == "" ) $(item).remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Beautify html, incldue:
|
||||
*
|
||||
* - change all <blockquote> to <sr-blockquote>
|
||||
* - remove useless <br>
|
||||
*
|
||||
* @param {jquery} jquery object
|
||||
*/
|
||||
function htmlbeautify( $target ) {
|
||||
try {
|
||||
$target.html( ( index, html ) => {
|
||||
return html.trim()
|
||||
.replace( /<\/?blockquote/g, (value) => value[1] == "/" ? "</sr-blockquote" : "<sr-blockquote" )
|
||||
.replace( /<br>\n?<br>(\n?<br>)*/g, "<br>" )
|
||||
.replace( /\/(div|p)>\n*(<br>\n)+/g, (value) =>value.replace( "<br>", "" ));
|
||||
});
|
||||
} catch ( error ) {
|
||||
console.error( error );
|
||||
return $target.html();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common Beautify html, include:
|
||||
* - task: all webiste image, remove old image and create new image
|
||||
* - task: all webiste sr-blockquote, remove style
|
||||
* - task: all webiste iframe, embed add center style
|
||||
* - task: all hr tag add simpread-hidden class
|
||||
* - task: all pre/code tag remove class
|
||||
* - task: all a tag remove style
|
||||
*
|
||||
* @param {jquery}
|
||||
*/
|
||||
function commbeautify( name, $target ) {
|
||||
$target.find( "img:not(.sr-rd-content-nobeautify)" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$orgpar = $target.parent(),
|
||||
$img = $( "<img class='sr-rd-content-img-load'>" ),
|
||||
src = $target.attr( "src" ),
|
||||
lazysrc = $target.attr( "data-src" ),
|
||||
zuimei = $target.attr( "data-original" ),
|
||||
cnbeta = $target.attr( "original" ),
|
||||
jianshu = $target.attr( "data-original-src" ),
|
||||
sina = $target.attr( "real_src" ),
|
||||
fixOverflowImgsize = () => {
|
||||
$img.removeClass( "sr-rd-content-img-load" );
|
||||
if ( $img[0].clientWidth > 1000 ) {
|
||||
$img.css( "zoom", "0.6" );
|
||||
}
|
||||
else if ( $img[0].clientHeight > 620 ) {
|
||||
if ( /win|mac/i.test(navigator.platform) ) {
|
||||
$img.attr( "height", 620 );
|
||||
if ( $img[0].clientWidth < $("sr-rd-content").width()) $img.css({ "width":"auto" });
|
||||
}
|
||||
}
|
||||
if ( $img[0].clientWidth > $("sr-rd-content").width()) $img.addClass( "sr-rd-content-img" );
|
||||
},
|
||||
loaderrorHandle = () => {
|
||||
$img.addClass( "simpread-hidden" );
|
||||
if ( $img.parent().hasClass( "sr-rd-content-center" )) {
|
||||
$img.parent().removeAttr( "class" ).addClass( "simpread-hidden" );
|
||||
}
|
||||
};
|
||||
let newsrc,
|
||||
$parent = $target.parent(),
|
||||
tagname = $parent[0].tagName.toLowerCase();
|
||||
|
||||
// remove current image and create new image object
|
||||
newsrc = cnbeta ? cnbeta : src;
|
||||
newsrc = lazysrc ? lazysrc : newsrc;
|
||||
newsrc = zuimei ? zuimei : newsrc;
|
||||
newsrc = jianshu ? jianshu : newsrc;
|
||||
newsrc = sina ? sina : newsrc;
|
||||
// hack code
|
||||
location.host.includes( "infoq.com" ) && ( newsrc = src );
|
||||
!newsrc.startsWith( "http" ) && ( newsrc = newsrc.startsWith( "//" ) ? location.protocol + newsrc : location.origin + newsrc );
|
||||
$img.attr( "src", newsrc )
|
||||
.replaceAll( $target )
|
||||
.wrap( "<div class='sr-rd-content-center'></div>" );
|
||||
if ( !/win|mac/i.test(navigator.platform) ) {
|
||||
$img.on( "load", ()=>fixOverflowImgsize() )
|
||||
.on( "error", ()=>loaderrorHandle() );
|
||||
} else {
|
||||
$img.one( "load", ()=>fixOverflowImgsize() )
|
||||
.one( "error", ()=>loaderrorHandle() );
|
||||
}
|
||||
});
|
||||
$target.find( "sr-blockquote" ).map( ( index, item ) => {
|
||||
const $target = $(item),
|
||||
$parent = $target.parent();
|
||||
$target.removeAttr( "style" ).removeAttr( "class" );
|
||||
if ( name == "dgtle.com" ) {
|
||||
$parent.removeClass( "quote" );
|
||||
}
|
||||
});
|
||||
$target.find( "iframe:not(.sr-rd-content-nobeautify), embed:not(.sr-rd-content-nobeautify)" ).map( ( index, item )=> {
|
||||
$(item).wrap( "<div class='sr-rd-content-center'></div>" );
|
||||
});
|
||||
$target.find( "hr" ).map( ( index, item )=> {
|
||||
$(item).addClass( "simpread-hidden" );
|
||||
});
|
||||
$target.find( "pre" ).map( ( index, item )=> {
|
||||
$(item).find( "code" ).removeAttr( "class" );
|
||||
});
|
||||
$target.find( "pre" ).removeAttr( "class" );
|
||||
$target.find( "a" ).removeAttr( "style" );
|
||||
}
|
||||
|
||||
export {
|
||||
specbeautify,
|
||||
removeSpareTag,
|
||||
htmlbeautify,
|
||||
commbeautify,
|
||||
}
|
||||
@ -1,923 +0,0 @@
|
||||
module.exports = minimatch
|
||||
minimatch.Minimatch = Minimatch
|
||||
|
||||
var path = { sep: '/' }
|
||||
try {
|
||||
path = require('path')
|
||||
} catch (er) {}
|
||||
|
||||
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
|
||||
var expand = require('brace-expansion')
|
||||
|
||||
var plTypes = {
|
||||
'!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
|
||||
'?': { open: '(?:', close: ')?' },
|
||||
'+': { open: '(?:', close: ')+' },
|
||||
'*': { open: '(?:', close: ')*' },
|
||||
'@': { open: '(?:', close: ')' }
|
||||
}
|
||||
|
||||
// any single thing other than /
|
||||
// don't need to escape / when using new RegExp()
|
||||
var qmark = '[^/]'
|
||||
|
||||
// * => any number of characters
|
||||
var star = qmark + '*?'
|
||||
|
||||
// ** when dots are allowed. Anything goes, except .. and .
|
||||
// not (^ or / followed by one or two dots followed by $ or /),
|
||||
// followed by anything, any number of times.
|
||||
var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
|
||||
|
||||
// not a ^ or / followed by a dot,
|
||||
// followed by anything, any number of times.
|
||||
var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
|
||||
|
||||
// characters that need to be escaped in RegExp.
|
||||
var reSpecials = charSet('().*{}+?[]^$\\!')
|
||||
|
||||
// "abc" -> { a:true, b:true, c:true }
|
||||
function charSet (s) {
|
||||
return s.split('').reduce(function (set, c) {
|
||||
set[c] = true
|
||||
return set
|
||||
}, {})
|
||||
}
|
||||
|
||||
// normalizes slashes.
|
||||
var slashSplit = /\/+/
|
||||
|
||||
minimatch.filter = filter
|
||||
function filter (pattern, options) {
|
||||
options = options || {}
|
||||
return function (p, i, list) {
|
||||
return minimatch(p, pattern, options)
|
||||
}
|
||||
}
|
||||
|
||||
function ext (a, b) {
|
||||
a = a || {}
|
||||
b = b || {}
|
||||
var t = {}
|
||||
Object.keys(b).forEach(function (k) {
|
||||
t[k] = b[k]
|
||||
})
|
||||
Object.keys(a).forEach(function (k) {
|
||||
t[k] = a[k]
|
||||
})
|
||||
return t
|
||||
}
|
||||
|
||||
minimatch.defaults = function (def) {
|
||||
if (!def || !Object.keys(def).length) return minimatch
|
||||
|
||||
var orig = minimatch
|
||||
|
||||
var m = function minimatch (p, pattern, options) {
|
||||
return orig.minimatch(p, pattern, ext(def, options))
|
||||
}
|
||||
|
||||
m.Minimatch = function Minimatch (pattern, options) {
|
||||
return new orig.Minimatch(pattern, ext(def, options))
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
Minimatch.defaults = function (def) {
|
||||
if (!def || !Object.keys(def).length) return Minimatch
|
||||
return minimatch.defaults(def).Minimatch
|
||||
}
|
||||
|
||||
function minimatch (p, pattern, options) {
|
||||
if (typeof pattern !== 'string') {
|
||||
throw new TypeError('glob pattern string required')
|
||||
}
|
||||
|
||||
if (!options) options = {}
|
||||
|
||||
// shortcut: comments match nothing.
|
||||
if (!options.nocomment && pattern.charAt(0) === '#') {
|
||||
return false
|
||||
}
|
||||
|
||||
// "" only matches ""
|
||||
if (pattern.trim() === '') return p === ''
|
||||
|
||||
return new Minimatch(pattern, options).match(p)
|
||||
}
|
||||
|
||||
function Minimatch (pattern, options) {
|
||||
if (!(this instanceof Minimatch)) {
|
||||
return new Minimatch(pattern, options)
|
||||
}
|
||||
|
||||
if (typeof pattern !== 'string') {
|
||||
throw new TypeError('glob pattern string required')
|
||||
}
|
||||
|
||||
if (!options) options = {}
|
||||
pattern = pattern.trim()
|
||||
|
||||
// windows support: need to use /, not \
|
||||
if (path.sep !== '/') {
|
||||
pattern = pattern.split(path.sep).join('/')
|
||||
}
|
||||
|
||||
this.options = options
|
||||
this.set = []
|
||||
this.pattern = pattern
|
||||
this.regexp = null
|
||||
this.negate = false
|
||||
this.comment = false
|
||||
this.empty = false
|
||||
|
||||
// make the set of regexps etc.
|
||||
this.make()
|
||||
}
|
||||
|
||||
Minimatch.prototype.debug = function () {}
|
||||
|
||||
Minimatch.prototype.make = make
|
||||
function make () {
|
||||
// don't do it more than once.
|
||||
if (this._made) return
|
||||
|
||||
var pattern = this.pattern
|
||||
var options = this.options
|
||||
|
||||
// empty patterns and comments match nothing.
|
||||
if (!options.nocomment && pattern.charAt(0) === '#') {
|
||||
this.comment = true
|
||||
return
|
||||
}
|
||||
if (!pattern) {
|
||||
this.empty = true
|
||||
return
|
||||
}
|
||||
|
||||
// step 1: figure out negation, etc.
|
||||
this.parseNegate()
|
||||
|
||||
// step 2: expand braces
|
||||
var set = this.globSet = this.braceExpand()
|
||||
|
||||
if (options.debug) this.debug = console.error
|
||||
|
||||
this.debug(this.pattern, set)
|
||||
|
||||
// step 3: now we have a set, so turn each one into a series of path-portion
|
||||
// matching patterns.
|
||||
// These will be regexps, except in the case of "**", which is
|
||||
// set to the GLOBSTAR object for globstar behavior,
|
||||
// and will not contain any / characters
|
||||
set = this.globParts = set.map(function (s) {
|
||||
return s.split(slashSplit)
|
||||
})
|
||||
|
||||
this.debug(this.pattern, set)
|
||||
|
||||
// glob --> regexps
|
||||
set = set.map(function (s, si, set) {
|
||||
return s.map(this.parse, this)
|
||||
}, this)
|
||||
|
||||
this.debug(this.pattern, set)
|
||||
|
||||
// filter out everything that didn't compile properly.
|
||||
set = set.filter(function (s) {
|
||||
return s.indexOf(false) === -1
|
||||
})
|
||||
|
||||
this.debug(this.pattern, set)
|
||||
|
||||
this.set = set
|
||||
}
|
||||
|
||||
Minimatch.prototype.parseNegate = parseNegate
|
||||
function parseNegate () {
|
||||
var pattern = this.pattern
|
||||
var negate = false
|
||||
var options = this.options
|
||||
var negateOffset = 0
|
||||
|
||||
if (options.nonegate) return
|
||||
|
||||
for (var i = 0, l = pattern.length
|
||||
; i < l && pattern.charAt(i) === '!'
|
||||
; i++) {
|
||||
negate = !negate
|
||||
negateOffset++
|
||||
}
|
||||
|
||||
if (negateOffset) this.pattern = pattern.substr(negateOffset)
|
||||
this.negate = negate
|
||||
}
|
||||
|
||||
// Brace expansion:
|
||||
// a{b,c}d -> abd acd
|
||||
// a{b,}c -> abc ac
|
||||
// a{0..3}d -> a0d a1d a2d a3d
|
||||
// a{b,c{d,e}f}g -> abg acdfg acefg
|
||||
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
|
||||
//
|
||||
// Invalid sets are not expanded.
|
||||
// a{2..}b -> a{2..}b
|
||||
// a{b}c -> a{b}c
|
||||
minimatch.braceExpand = function (pattern, options) {
|
||||
return braceExpand(pattern, options)
|
||||
}
|
||||
|
||||
Minimatch.prototype.braceExpand = braceExpand
|
||||
|
||||
function braceExpand (pattern, options) {
|
||||
if (!options) {
|
||||
if (this instanceof Minimatch) {
|
||||
options = this.options
|
||||
} else {
|
||||
options = {}
|
||||
}
|
||||
}
|
||||
|
||||
pattern = typeof pattern === 'undefined'
|
||||
? this.pattern : pattern
|
||||
|
||||
if (typeof pattern === 'undefined') {
|
||||
throw new TypeError('undefined pattern')
|
||||
}
|
||||
|
||||
if (options.nobrace ||
|
||||
!pattern.match(/\{.*\}/)) {
|
||||
// shortcut. no need to expand.
|
||||
return [pattern]
|
||||
}
|
||||
|
||||
return expand(pattern)
|
||||
}
|
||||
|
||||
// parse a component of the expanded set.
|
||||
// At this point, no pattern may contain "/" in it
|
||||
// so we're going to return a 2d array, where each entry is the full
|
||||
// pattern, split on '/', and then turned into a regular expression.
|
||||
// A regexp is made at the end which joins each array with an
|
||||
// escaped /, and another full one which joins each regexp with |.
|
||||
//
|
||||
// Following the lead of Bash 4.1, note that "**" only has special meaning
|
||||
// when it is the *only* thing in a path portion. Otherwise, any series
|
||||
// of * is equivalent to a single *. Globstar behavior is enabled by
|
||||
// default, and can be disabled by setting options.noglobstar.
|
||||
Minimatch.prototype.parse = parse
|
||||
var SUBPARSE = {}
|
||||
function parse (pattern, isSub) {
|
||||
if (pattern.length > 1024 * 64) {
|
||||
throw new TypeError('pattern is too long')
|
||||
}
|
||||
|
||||
var options = this.options
|
||||
|
||||
// shortcuts
|
||||
if (!options.noglobstar && pattern === '**') return GLOBSTAR
|
||||
if (pattern === '') return ''
|
||||
|
||||
var re = ''
|
||||
var hasMagic = !!options.nocase
|
||||
var escaping = false
|
||||
// ? => one single character
|
||||
var patternListStack = []
|
||||
var negativeLists = []
|
||||
var stateChar
|
||||
var inClass = false
|
||||
var reClassStart = -1
|
||||
var classStart = -1
|
||||
// . and .. never match anything that doesn't start with .,
|
||||
// even when options.dot is set.
|
||||
var patternStart = pattern.charAt(0) === '.' ? '' // anything
|
||||
// not (start or / followed by . or .. followed by / or end)
|
||||
: options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
|
||||
: '(?!\\.)'
|
||||
var self = this
|
||||
|
||||
function clearStateChar () {
|
||||
if (stateChar) {
|
||||
// we had some state-tracking character
|
||||
// that wasn't consumed by this pass.
|
||||
switch (stateChar) {
|
||||
case '*':
|
||||
re += star
|
||||
hasMagic = true
|
||||
break
|
||||
case '?':
|
||||
re += qmark
|
||||
hasMagic = true
|
||||
break
|
||||
default:
|
||||
re += '\\' + stateChar
|
||||
break
|
||||
}
|
||||
self.debug('clearStateChar %j %j', stateChar, re)
|
||||
stateChar = false
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0, len = pattern.length, c
|
||||
; (i < len) && (c = pattern.charAt(i))
|
||||
; i++) {
|
||||
this.debug('%s\t%s %s %j', pattern, i, re, c)
|
||||
|
||||
// skip over any that are escaped.
|
||||
if (escaping && reSpecials[c]) {
|
||||
re += '\\' + c
|
||||
escaping = false
|
||||
continue
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '/':
|
||||
// completely not allowed, even escaped.
|
||||
// Should already be path-split by now.
|
||||
return false
|
||||
|
||||
case '\\':
|
||||
clearStateChar()
|
||||
escaping = true
|
||||
continue
|
||||
|
||||
// the various stateChar values
|
||||
// for the "extglob" stuff.
|
||||
case '?':
|
||||
case '*':
|
||||
case '+':
|
||||
case '@':
|
||||
case '!':
|
||||
this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
|
||||
|
||||
// all of those are literals inside a class, except that
|
||||
// the glob [!a] means [^a] in regexp
|
||||
if (inClass) {
|
||||
this.debug(' in class')
|
||||
if (c === '!' && i === classStart + 1) c = '^'
|
||||
re += c
|
||||
continue
|
||||
}
|
||||
|
||||
// if we already have a stateChar, then it means
|
||||
// that there was something like ** or +? in there.
|
||||
// Handle the stateChar, then proceed with this one.
|
||||
self.debug('call clearStateChar %j', stateChar)
|
||||
clearStateChar()
|
||||
stateChar = c
|
||||
// if extglob is disabled, then +(asdf|foo) isn't a thing.
|
||||
// just clear the statechar *now*, rather than even diving into
|
||||
// the patternList stuff.
|
||||
if (options.noext) clearStateChar()
|
||||
continue
|
||||
|
||||
case '(':
|
||||
if (inClass) {
|
||||
re += '('
|
||||
continue
|
||||
}
|
||||
|
||||
if (!stateChar) {
|
||||
re += '\\('
|
||||
continue
|
||||
}
|
||||
|
||||
patternListStack.push({
|
||||
type: stateChar,
|
||||
start: i - 1,
|
||||
reStart: re.length,
|
||||
open: plTypes[stateChar].open,
|
||||
close: plTypes[stateChar].close
|
||||
})
|
||||
// negation is (?:(?!js)[^/]*)
|
||||
re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
|
||||
this.debug('plType %j %j', stateChar, re)
|
||||
stateChar = false
|
||||
continue
|
||||
|
||||
case ')':
|
||||
if (inClass || !patternListStack.length) {
|
||||
re += '\\)'
|
||||
continue
|
||||
}
|
||||
|
||||
clearStateChar()
|
||||
hasMagic = true
|
||||
var pl = patternListStack.pop()
|
||||
// negation is (?:(?!js)[^/]*)
|
||||
// The others are (?:<pattern>)<type>
|
||||
re += pl.close
|
||||
if (pl.type === '!') {
|
||||
negativeLists.push(pl)
|
||||
}
|
||||
pl.reEnd = re.length
|
||||
continue
|
||||
|
||||
case '|':
|
||||
if (inClass || !patternListStack.length || escaping) {
|
||||
re += '\\|'
|
||||
escaping = false
|
||||
continue
|
||||
}
|
||||
|
||||
clearStateChar()
|
||||
re += '|'
|
||||
continue
|
||||
|
||||
// these are mostly the same in regexp and glob
|
||||
case '[':
|
||||
// swallow any state-tracking char before the [
|
||||
clearStateChar()
|
||||
|
||||
if (inClass) {
|
||||
re += '\\' + c
|
||||
continue
|
||||
}
|
||||
|
||||
inClass = true
|
||||
classStart = i
|
||||
reClassStart = re.length
|
||||
re += c
|
||||
continue
|
||||
|
||||
case ']':
|
||||
// a right bracket shall lose its special
|
||||
// meaning and represent itself in
|
||||
// a bracket expression if it occurs
|
||||
// first in the list. -- POSIX.2 2.8.3.2
|
||||
if (i === classStart + 1 || !inClass) {
|
||||
re += '\\' + c
|
||||
escaping = false
|
||||
continue
|
||||
}
|
||||
|
||||
// handle the case where we left a class open.
|
||||
// "[z-a]" is valid, equivalent to "\[z-a\]"
|
||||
if (inClass) {
|
||||
// split where the last [ was, make sure we don't have
|
||||
// an invalid re. if so, re-walk the contents of the
|
||||
// would-be class to re-translate any characters that
|
||||
// were passed through as-is
|
||||
// TODO: It would probably be faster to determine this
|
||||
// without a try/catch and a new RegExp, but it's tricky
|
||||
// to do safely. For now, this is safe and works.
|
||||
var cs = pattern.substring(classStart + 1, i)
|
||||
try {
|
||||
RegExp('[' + cs + ']')
|
||||
} catch (er) {
|
||||
// not a valid class!
|
||||
var sp = this.parse(cs, SUBPARSE)
|
||||
re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
|
||||
hasMagic = hasMagic || sp[1]
|
||||
inClass = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// finish up the class.
|
||||
hasMagic = true
|
||||
inClass = false
|
||||
re += c
|
||||
continue
|
||||
|
||||
default:
|
||||
// swallow any state char that wasn't consumed
|
||||
clearStateChar()
|
||||
|
||||
if (escaping) {
|
||||
// no need
|
||||
escaping = false
|
||||
} else if (reSpecials[c]
|
||||
&& !(c === '^' && inClass)) {
|
||||
re += '\\'
|
||||
}
|
||||
|
||||
re += c
|
||||
|
||||
} // switch
|
||||
} // for
|
||||
|
||||
// handle the case where we left a class open.
|
||||
// "[abc" is valid, equivalent to "\[abc"
|
||||
if (inClass) {
|
||||
// split where the last [ was, and escape it
|
||||
// this is a huge pita. We now have to re-walk
|
||||
// the contents of the would-be class to re-translate
|
||||
// any characters that were passed through as-is
|
||||
cs = pattern.substr(classStart + 1)
|
||||
sp = this.parse(cs, SUBPARSE)
|
||||
re = re.substr(0, reClassStart) + '\\[' + sp[0]
|
||||
hasMagic = hasMagic || sp[1]
|
||||
}
|
||||
|
||||
// handle the case where we had a +( thing at the *end*
|
||||
// of the pattern.
|
||||
// each pattern list stack adds 3 chars, and we need to go through
|
||||
// and escape any | chars that were passed through as-is for the regexp.
|
||||
// Go through and escape them, taking care not to double-escape any
|
||||
// | chars that were already escaped.
|
||||
for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
|
||||
var tail = re.slice(pl.reStart + pl.open.length)
|
||||
this.debug('setting tail', re, pl)
|
||||
// maybe some even number of \, then maybe 1 \, followed by a |
|
||||
tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {
|
||||
if (!$2) {
|
||||
// the | isn't already escaped, so escape it.
|
||||
$2 = '\\'
|
||||
}
|
||||
|
||||
// need to escape all those slashes *again*, without escaping the
|
||||
// one that we need for escaping the | character. As it works out,
|
||||
// escaping an even number of slashes can be done by simply repeating
|
||||
// it exactly after itself. That's why this trick works.
|
||||
//
|
||||
// I am sorry that you have to see this.
|
||||
return $1 + $1 + $2 + '|'
|
||||
})
|
||||
|
||||
this.debug('tail=%j\n %s', tail, tail, pl, re)
|
||||
var t = pl.type === '*' ? star
|
||||
: pl.type === '?' ? qmark
|
||||
: '\\' + pl.type
|
||||
|
||||
hasMagic = true
|
||||
re = re.slice(0, pl.reStart) + t + '\\(' + tail
|
||||
}
|
||||
|
||||
// handle trailing things that only matter at the very end.
|
||||
clearStateChar()
|
||||
if (escaping) {
|
||||
// trailing \\
|
||||
re += '\\\\'
|
||||
}
|
||||
|
||||
// only need to apply the nodot start if the re starts with
|
||||
// something that could conceivably capture a dot
|
||||
var addPatternStart = false
|
||||
switch (re.charAt(0)) {
|
||||
case '.':
|
||||
case '[':
|
||||
case '(': addPatternStart = true
|
||||
}
|
||||
|
||||
// Hack to work around lack of negative lookbehind in JS
|
||||
// A pattern like: *.!(x).!(y|z) needs to ensure that a name
|
||||
// like 'a.xyz.yz' doesn't match. So, the first negative
|
||||
// lookahead, has to look ALL the way ahead, to the end of
|
||||
// the pattern.
|
||||
for (var n = negativeLists.length - 1; n > -1; n--) {
|
||||
var nl = negativeLists[n]
|
||||
|
||||
var nlBefore = re.slice(0, nl.reStart)
|
||||
var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
|
||||
var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
|
||||
var nlAfter = re.slice(nl.reEnd)
|
||||
|
||||
nlLast += nlAfter
|
||||
|
||||
// Handle nested stuff like *(*.js|!(*.json)), where open parens
|
||||
// mean that we should *not* include the ) in the bit that is considered
|
||||
// "after" the negated section.
|
||||
var openParensBefore = nlBefore.split('(').length - 1
|
||||
var cleanAfter = nlAfter
|
||||
for (i = 0; i < openParensBefore; i++) {
|
||||
cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
|
||||
}
|
||||
nlAfter = cleanAfter
|
||||
|
||||
var dollar = ''
|
||||
if (nlAfter === '' && isSub !== SUBPARSE) {
|
||||
dollar = '$'
|
||||
}
|
||||
var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
|
||||
re = newRe
|
||||
}
|
||||
|
||||
// if the re is not "" at this point, then we need to make sure
|
||||
// it doesn't match against an empty path part.
|
||||
// Otherwise a/* will match a/, which it should not.
|
||||
if (re !== '' && hasMagic) {
|
||||
re = '(?=.)' + re
|
||||
}
|
||||
|
||||
if (addPatternStart) {
|
||||
re = patternStart + re
|
||||
}
|
||||
|
||||
// parsing just a piece of a larger pattern.
|
||||
if (isSub === SUBPARSE) {
|
||||
return [re, hasMagic]
|
||||
}
|
||||
|
||||
// skip the regexp for non-magical patterns
|
||||
// unescape anything in it, though, so that it'll be
|
||||
// an exact match against a file etc.
|
||||
if (!hasMagic) {
|
||||
return globUnescape(pattern)
|
||||
}
|
||||
|
||||
var flags = options.nocase ? 'i' : ''
|
||||
try {
|
||||
var regExp = new RegExp('^' + re + '$', flags)
|
||||
} catch (er) {
|
||||
// If it was an invalid regular expression, then it can't match
|
||||
// anything. This trick looks for a character after the end of
|
||||
// the string, which is of course impossible, except in multi-line
|
||||
// mode, but it's not a /m regex.
|
||||
return new RegExp('$.')
|
||||
}
|
||||
|
||||
regExp._glob = pattern
|
||||
regExp._src = re
|
||||
|
||||
return regExp
|
||||
}
|
||||
|
||||
minimatch.makeRe = function (pattern, options) {
|
||||
return new Minimatch(pattern, options || {}).makeRe()
|
||||
}
|
||||
|
||||
Minimatch.prototype.makeRe = makeRe
|
||||
function makeRe () {
|
||||
if (this.regexp || this.regexp === false) return this.regexp
|
||||
|
||||
// at this point, this.set is a 2d array of partial
|
||||
// pattern strings, or "**".
|
||||
//
|
||||
// It's better to use .match(). This function shouldn't
|
||||
// be used, really, but it's pretty convenient sometimes,
|
||||
// when you just want to work with a regex.
|
||||
var set = this.set
|
||||
|
||||
if (!set.length) {
|
||||
this.regexp = false
|
||||
return this.regexp
|
||||
}
|
||||
var options = this.options
|
||||
|
||||
var twoStar = options.noglobstar ? star
|
||||
: options.dot ? twoStarDot
|
||||
: twoStarNoDot
|
||||
var flags = options.nocase ? 'i' : ''
|
||||
|
||||
var re = set.map(function (pattern) {
|
||||
return pattern.map(function (p) {
|
||||
return (p === GLOBSTAR) ? twoStar
|
||||
: (typeof p === 'string') ? regExpEscape(p)
|
||||
: p._src
|
||||
}).join('\\\/')
|
||||
}).join('|')
|
||||
|
||||
// must match entire pattern
|
||||
// ending in a * or ** will make it less strict.
|
||||
re = '^(?:' + re + ')$'
|
||||
|
||||
// can match anything, as long as it's not this.
|
||||
if (this.negate) re = '^(?!' + re + ').*$'
|
||||
|
||||
try {
|
||||
this.regexp = new RegExp(re, flags)
|
||||
} catch (ex) {
|
||||
this.regexp = false
|
||||
}
|
||||
return this.regexp
|
||||
}
|
||||
|
||||
minimatch.match = function (list, pattern, options) {
|
||||
options = options || {}
|
||||
var mm = new Minimatch(pattern, options)
|
||||
list = list.filter(function (f) {
|
||||
return mm.match(f)
|
||||
})
|
||||
if (mm.options.nonull && !list.length) {
|
||||
list.push(pattern)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
Minimatch.prototype.match = match
|
||||
function match (f, partial) {
|
||||
this.debug('match', f, this.pattern)
|
||||
// short-circuit in the case of busted things.
|
||||
// comments, etc.
|
||||
if (this.comment) return false
|
||||
if (this.empty) return f === ''
|
||||
|
||||
if (f === '/' && partial) return true
|
||||
|
||||
var options = this.options
|
||||
|
||||
// windows: need to use /, not \
|
||||
if (path.sep !== '/') {
|
||||
f = f.split(path.sep).join('/')
|
||||
}
|
||||
|
||||
// treat the test path as a set of pathparts.
|
||||
f = f.split(slashSplit)
|
||||
this.debug(this.pattern, 'split', f)
|
||||
|
||||
// just ONE of the pattern sets in this.set needs to match
|
||||
// in order for it to be valid. If negating, then just one
|
||||
// match means that we have failed.
|
||||
// Either way, return on the first hit.
|
||||
|
||||
var set = this.set
|
||||
this.debug(this.pattern, 'set', set)
|
||||
|
||||
// Find the basename of the path by looking for the last non-empty segment
|
||||
var filename
|
||||
var i
|
||||
for (i = f.length - 1; i >= 0; i--) {
|
||||
filename = f[i]
|
||||
if (filename) break
|
||||
}
|
||||
|
||||
for (i = 0; i < set.length; i++) {
|
||||
var pattern = set[i]
|
||||
var file = f
|
||||
if (options.matchBase && pattern.length === 1) {
|
||||
file = [filename]
|
||||
}
|
||||
var hit = this.matchOne(file, pattern, partial)
|
||||
if (hit) {
|
||||
if (options.flipNegate) return true
|
||||
return !this.negate
|
||||
}
|
||||
}
|
||||
|
||||
// didn't get any hits. this is success if it's a negative
|
||||
// pattern, failure otherwise.
|
||||
if (options.flipNegate) return false
|
||||
return this.negate
|
||||
}
|
||||
|
||||
// set partial to true to test if, for example,
|
||||
// "/a/b" matches the start of "/*/b/*/d"
|
||||
// Partial means, if you run out of file before you run
|
||||
// out of pattern, then that's fine, as long as all
|
||||
// the parts match.
|
||||
Minimatch.prototype.matchOne = function (file, pattern, partial) {
|
||||
var options = this.options
|
||||
|
||||
this.debug('matchOne',
|
||||
{ 'this': this, file: file, pattern: pattern })
|
||||
|
||||
this.debug('matchOne', file.length, pattern.length)
|
||||
|
||||
for (var fi = 0,
|
||||
pi = 0,
|
||||
fl = file.length,
|
||||
pl = pattern.length
|
||||
; (fi < fl) && (pi < pl)
|
||||
; fi++, pi++) {
|
||||
this.debug('matchOne loop')
|
||||
var p = pattern[pi]
|
||||
var f = file[fi]
|
||||
|
||||
this.debug(pattern, p, f)
|
||||
|
||||
// should be impossible.
|
||||
// some invalid regexp stuff in the set.
|
||||
if (p === false) return false
|
||||
|
||||
if (p === GLOBSTAR) {
|
||||
this.debug('GLOBSTAR', [pattern, p, f])
|
||||
|
||||
// "**"
|
||||
// a/**/b/**/c would match the following:
|
||||
// a/b/x/y/z/c
|
||||
// a/x/y/z/b/c
|
||||
// a/b/x/b/x/c
|
||||
// a/b/c
|
||||
// To do this, take the rest of the pattern after
|
||||
// the **, and see if it would match the file remainder.
|
||||
// If so, return success.
|
||||
// If not, the ** "swallows" a segment, and try again.
|
||||
// This is recursively awful.
|
||||
//
|
||||
// a/**/b/**/c matching a/b/x/y/z/c
|
||||
// - a matches a
|
||||
// - doublestar
|
||||
// - matchOne(b/x/y/z/c, b/**/c)
|
||||
// - b matches b
|
||||
// - doublestar
|
||||
// - matchOne(x/y/z/c, c) -> no
|
||||
// - matchOne(y/z/c, c) -> no
|
||||
// - matchOne(z/c, c) -> no
|
||||
// - matchOne(c, c) yes, hit
|
||||
var fr = fi
|
||||
var pr = pi + 1
|
||||
if (pr === pl) {
|
||||
this.debug('** at the end')
|
||||
// a ** at the end will just swallow the rest.
|
||||
// We have found a match.
|
||||
// however, it will not swallow /.x, unless
|
||||
// options.dot is set.
|
||||
// . and .. are *never* matched by **, for explosively
|
||||
// exponential reasons.
|
||||
for (; fi < fl; fi++) {
|
||||
if (file[fi] === '.' || file[fi] === '..' ||
|
||||
(!options.dot && file[fi].charAt(0) === '.')) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ok, let's see if we can swallow whatever we can.
|
||||
while (fr < fl) {
|
||||
var swallowee = file[fr]
|
||||
|
||||
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
|
||||
|
||||
// XXX remove this slice. Just pass the start index.
|
||||
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
|
||||
this.debug('globstar found match!', fr, fl, swallowee)
|
||||
// found a match.
|
||||
return true
|
||||
} else {
|
||||
// can't swallow "." or ".." ever.
|
||||
// can only swallow ".foo" when explicitly asked.
|
||||
if (swallowee === '.' || swallowee === '..' ||
|
||||
(!options.dot && swallowee.charAt(0) === '.')) {
|
||||
this.debug('dot detected!', file, fr, pattern, pr)
|
||||
break
|
||||
}
|
||||
|
||||
// ** swallows a segment, and continue.
|
||||
this.debug('globstar swallow a segment, and continue')
|
||||
fr++
|
||||
}
|
||||
}
|
||||
|
||||
// no match was found.
|
||||
// However, in partial mode, we can't say this is necessarily over.
|
||||
// If there's more *pattern* left, then
|
||||
if (partial) {
|
||||
// ran out of file
|
||||
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
|
||||
if (fr === fl) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// something other than **
|
||||
// non-magic patterns just have to match exactly
|
||||
// patterns with magic have been turned into regexps.
|
||||
var hit
|
||||
if (typeof p === 'string') {
|
||||
if (options.nocase) {
|
||||
hit = f.toLowerCase() === p.toLowerCase()
|
||||
} else {
|
||||
hit = f === p
|
||||
}
|
||||
this.debug('string match', p, f, hit)
|
||||
} else {
|
||||
hit = f.match(p)
|
||||
this.debug('pattern match', p, f, hit)
|
||||
}
|
||||
|
||||
if (!hit) return false
|
||||
}
|
||||
|
||||
// Note: ending in / means that we'll get a final ""
|
||||
// at the end of the pattern. This can only match a
|
||||
// corresponding "" at the end of the file.
|
||||
// If the file ends in /, then it can only match a
|
||||
// a pattern that ends in /, unless the pattern just
|
||||
// doesn't have any more for it. But, a/b/ should *not*
|
||||
// match "a/b/*", even though "" matches against the
|
||||
// [^/]*? pattern, except in partial mode, where it might
|
||||
// simply not be reached yet.
|
||||
// However, a/b/ should still satisfy a/*
|
||||
|
||||
// now either we fell off the end of the pattern, or we're done.
|
||||
if (fi === fl && pi === pl) {
|
||||
// ran out of pattern and filename at the same time.
|
||||
// an exact hit!
|
||||
return true
|
||||
} else if (fi === fl) {
|
||||
// ran out of file, but still had pattern left.
|
||||
// this is ok if we're doing the match as part of
|
||||
// a glob fs traversal.
|
||||
return partial
|
||||
} else if (pi === pl) {
|
||||
// ran out of pattern, still have file left.
|
||||
// this is only acceptable if we're on the very last
|
||||
// empty segment of a file with a trailing slash.
|
||||
// a/* should match a/b/
|
||||
var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
|
||||
return emptyFileEnd
|
||||
}
|
||||
|
||||
// should be unreachable.
|
||||
throw new Error('wtf?')
|
||||
}
|
||||
|
||||
// replace stuff like \* with *
|
||||
function globUnescape (s) {
|
||||
return s.replace(/\\(.)/g, '$1')
|
||||
}
|
||||
|
||||
function regExpEscape (s) {
|
||||
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
||||
}
|
||||
10
src/vender/puread/plugin/pangu.min.js
vendored
10
src/vender/puread/plugin/pangu.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,198 +0,0 @@
|
||||
console.log( "=== PureRead: StyleSheet load ===" )
|
||||
|
||||
const [ bgcolorstyl, bgcls ] = [ "background-color", ".simpread-focus-root" ];
|
||||
let origin_read_style = "", html_style_bal = "-1";
|
||||
|
||||
/**
|
||||
* Get background color value for focus mode
|
||||
*
|
||||
* @param {string} background-color, e.g. rgba(235, 235, 235, 0.901961)
|
||||
* @return {string} e.g. 235, 235, 235
|
||||
*/
|
||||
function getColor( value ) {
|
||||
const arr = value ? value.match( /[0-9]+, /ig ) : [];
|
||||
if ( arr.length > 0 ) {
|
||||
return arr.join( "" ).replace( /, $/, "" );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus mode background color for focus mode
|
||||
*
|
||||
* @param {string} background color
|
||||
* @param {number} background opacity
|
||||
* @return {string} new background color
|
||||
*/
|
||||
function backgroundColor( bgcolor, opacity ) {
|
||||
const color = getColor( bgcolor ),
|
||||
newval = `rgba(${color}, ${opacity / 100})`;
|
||||
$( bgcls ).css( bgcolorstyl, newval );
|
||||
return newval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background opacity for focus mode
|
||||
*
|
||||
* @param {string} opacity
|
||||
* @return {string} new background color or null
|
||||
*/
|
||||
function opacity( opacity ) {
|
||||
const bgcolor = $( bgcls ).css( bgcolorstyl ),
|
||||
color = getColor( bgcolor ),
|
||||
newval = `rgba(${color}, ${opacity / 100})`;
|
||||
if ( color ) {
|
||||
$( bgcls ).css( bgcolorstyl, newval );
|
||||
return newval;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read mode font family for read mode
|
||||
*
|
||||
* @param {string} font family name e.g. PingFang SC; Microsoft Yahei
|
||||
*/
|
||||
function fontFamily( family ) {
|
||||
$( "sr-read" ).css( "font-family", family == "default" ? "" : family );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read mode font size for read mode
|
||||
*
|
||||
* @param {string} font size, e.g. 70% 62.5%
|
||||
*/
|
||||
function fontSize( value ) {
|
||||
if ( html_style_bal == "-1" ) {
|
||||
html_style_bal = $( "html" ).attr( "style" );
|
||||
html_style_bal == undefined && ( html_style_bal = "" );
|
||||
}
|
||||
value ? $( "html" ).attr( "style", `font-size: ${value}!important;${html_style_bal}` ) : $( "html" ).attr( "style", html_style_bal );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read mode layout width for read mode
|
||||
*
|
||||
* @param {string} layout width
|
||||
*/
|
||||
function layout( width ) {
|
||||
$( "sr-read" ).css( "margin", width ? `20px ${width}` : "" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom css to <head> for read mode
|
||||
*
|
||||
* @param {string} read.custom[type]
|
||||
* @param {object} read.custom
|
||||
*/
|
||||
function custom( type, props ) {
|
||||
const format = ( name ) => {
|
||||
return name.replace( /[A-Z]/, name => { return `-${name.toLowerCase()}` } );
|
||||
},
|
||||
arr = Object.keys( props ).map( v => {
|
||||
return props[v] && `${format( v )}: ${ props[v] };`
|
||||
});
|
||||
let styles = arr.join( "" );
|
||||
switch ( type ) {
|
||||
case "title":
|
||||
styles = `sr-rd-title {${styles}}`;
|
||||
break;
|
||||
case "desc":
|
||||
styles = `sr-rd-desc {${styles}}`;
|
||||
break;
|
||||
case "art":
|
||||
styles = `sr-rd-content *, sr-rd-content p, sr-rd-content div {${styles}}`;
|
||||
break;
|
||||
case "pre":
|
||||
styles = `sr-rd-content pre {${styles}}`;
|
||||
break;
|
||||
case "code":
|
||||
styles = `sr-rd-content pre code, sr-rd-content pre code * {${styles}}`;
|
||||
break;
|
||||
}
|
||||
|
||||
const $target = $( "head" ).find( `style#simpread-custom-${type}` );
|
||||
if ( $target.length == 0 ) {
|
||||
$( "head" ).append(`<style type="text/css" id="simpread-custom-${type}">${styles}</style>`);
|
||||
} else {
|
||||
$target.html( styles );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add css to <head> for read mode
|
||||
*
|
||||
* @param {string} read.custom.css
|
||||
* @param {object} read.custom.css value
|
||||
*/
|
||||
function css( type, styles ) {
|
||||
const $target = $( "head" ).find( `style#simpread-custom-${type}` );
|
||||
if ( $target.length == 0 ) {
|
||||
$( "head" ).append(`<style type="text/css" id="simpread-custom-${type}">${styles}</style>`);
|
||||
} else {
|
||||
$target.html( styles );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/Remove current site styles( string ) to head for read mdoe
|
||||
*
|
||||
* @param {string} styles
|
||||
*/
|
||||
function siteCSS( styles ) {
|
||||
styles ? $( "head" ).append(`<style type="text/css" id="simpread-site-css">${styles}</style>`) :
|
||||
$( "#simpread-site-css" ).remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom to .preview tag
|
||||
*
|
||||
* @param {object} read.custom
|
||||
* @param {string} theme backgroud color
|
||||
*/
|
||||
function preview( styles ) {
|
||||
Object.keys( styles ).forEach( v => {
|
||||
v != "css" && custom( v, styles[v] );
|
||||
});
|
||||
css( "css", styles["css"] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify custom is exist
|
||||
*
|
||||
* @param {string} verify type
|
||||
* @param {object} read.custom value
|
||||
*/
|
||||
function vfyCustom( type, styles ) {
|
||||
switch( type ) {
|
||||
case "layout":
|
||||
case "margin":
|
||||
case "fontfamily":
|
||||
case "custom":
|
||||
return styles.css != "";
|
||||
case "fontsize":
|
||||
return styles.title.fontSize != "" ||
|
||||
styles.desc.fontSize != "" ||
|
||||
styles.art.fontSize != "" ||
|
||||
styles.css != "";
|
||||
case "theme":
|
||||
return styles.css.search( "simpread-theme-root" ) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getColor as GetColor,
|
||||
backgroundColor as BgColor,
|
||||
opacity as Opacity,
|
||||
fontFamily as FontFamily,
|
||||
fontSize as FontSize,
|
||||
layout as Layout,
|
||||
siteCSS as SiteCSS,
|
||||
preview as Preview,
|
||||
custom as Custom,
|
||||
css as CSS,
|
||||
vfyCustom as VerifyCustom,
|
||||
}
|
||||
3424
src/vender/puread/puplugin.min.js
vendored
Normal file
3424
src/vender/puread/puplugin.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,231 +0,0 @@
|
||||
console.log( "=== PureRead: PureRead load ===" )
|
||||
|
||||
import * as util from './util';
|
||||
import AdapteSite from './adaptesite';
|
||||
|
||||
export default class PureRead extends AdapteSite {
|
||||
|
||||
constructor( sites ) {
|
||||
super( sites );
|
||||
this.version = "0.0.3";
|
||||
this.org_url = location.href;
|
||||
this.html = {}; // clone site, include: title, desc, include, avatar, paging
|
||||
this.plugin = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify current puread is same
|
||||
*
|
||||
* @return {boolean} true: same; false: not same;
|
||||
*/
|
||||
Exist() {
|
||||
return this.org_url == location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Plugin
|
||||
*
|
||||
* @param {object} plugin object
|
||||
*/
|
||||
AddPlugin( plugin ) {
|
||||
this.plugin = {
|
||||
minimatch : plugin.minimatch,
|
||||
pangu : plugin.pangu,
|
||||
beautify : plugin.beautify,
|
||||
stylesheet: plugin.style,
|
||||
rdability : plugin.rdability,
|
||||
};
|
||||
super.SetMinimatch( this.plugin.minimatch );
|
||||
super.SetRdability( this.plugin.rdability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create temp read mode
|
||||
*
|
||||
* @param {string} include: read, focus
|
||||
* @param {dom} html dom element
|
||||
*/
|
||||
TempMode( mode, dom ) {
|
||||
this.state = "temp";
|
||||
this.dom = dom;
|
||||
this.Newsite( mode, dom.outerHTML );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read mode html
|
||||
*/
|
||||
ReadMode() {
|
||||
this.html = wrap( this.current.site );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highlight( focus ) jquery, only usage focus mode
|
||||
*
|
||||
* @return {jquery} jquery object
|
||||
*/
|
||||
Include() {
|
||||
let include = this.current.site.include,
|
||||
$focus = [];
|
||||
const target = util.selector( include );
|
||||
try {
|
||||
if ( util.specTest( target ) ) {
|
||||
const [ value, state ] = util.specAction( include );
|
||||
if ( state == 0 ) {
|
||||
include = include.replace( /\[\[{\$\(|}\]\]|\).html\(\)/g, "" );
|
||||
$focus = $( util.specAction( `[[[${include}]]]` )[0] );
|
||||
} else if ( state == 3 ) {
|
||||
$focus = value;
|
||||
}
|
||||
} else if ( target ) {
|
||||
$focus = $( "body" ).find( target );
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.error( "Get $focus failed", error )
|
||||
}
|
||||
return $focus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exlcude jquery selector array list
|
||||
*
|
||||
* @param {jquery} jquery object
|
||||
* @return {array} jquery selector
|
||||
*/
|
||||
Exclude( $target ) {
|
||||
return excludeSelector( $target, this.current.site.exclude );
|
||||
}
|
||||
|
||||
/**
|
||||
* Beautify html
|
||||
*
|
||||
* @param {jquery} jquery
|
||||
*/
|
||||
Beautify( $target ) {
|
||||
if ( this.plugin.beautify ) {
|
||||
this.plugin.beautify.specbeautify( this.current.site.name, $target );
|
||||
this.plugin.beautify.removeSpareTag( this.current.site.name, $target );
|
||||
this.plugin.beautify.htmlbeautify( $target );
|
||||
this.plugin.beautify.commbeautify( this.current.site.name, $target );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format usage pangu plugin
|
||||
*
|
||||
* @param {string} class name
|
||||
*/
|
||||
Format( cls ) {
|
||||
this.plugin.pangu &&
|
||||
this.plugin.pangu.spacingElementByClassName( cls );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap storage.current.site object
|
||||
*
|
||||
* @param {object} storage.current.site object
|
||||
* @return {object} wrapper object
|
||||
*/
|
||||
function wrap( site ) {
|
||||
const wrapper = util.clone( site ),
|
||||
title = util.selector( site.title == "" ? "<title>" : site.title ),
|
||||
desc = util.selector( site.desc ),
|
||||
include = util.selector( site.include );
|
||||
wrapper.title = site.title == "" || site.title == "<title>" ? $( "head title" ).text() : query( title );
|
||||
wrapper.desc = query( desc );
|
||||
wrapper.include = site.include == "" && site.html != "" ? site.html : query( include, "html" );
|
||||
wrapper.avatar && wrapper.avatar.length > 0 && wrapper.avatar[0].name == "" && delete wrapper.avatar;
|
||||
wrapper.paging && wrapper.paging.length > 0 && wrapper.paging[0].prev == "" && delete wrapper.paging;
|
||||
wrapper.avatar && wrapper.avatar.forEach( item => {
|
||||
const key = Object.keys( item ).join(),
|
||||
value = item[key];
|
||||
item[key] = query( util.selector( value ), "html" );
|
||||
});
|
||||
wrapper.paging && wrapper.paging.forEach( item => {
|
||||
const key = Object.keys( item ).join(),
|
||||
value = item[key];
|
||||
item[key] = query( util.selector( value ) );
|
||||
});
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query content usage jquery
|
||||
*
|
||||
* @param {string} query content
|
||||
* @param {string} type, incldue: text, html and multi
|
||||
* @return {string} query result
|
||||
*/
|
||||
function query( content, type = "text" ) {
|
||||
const $root = $( "html" );
|
||||
if ( util.specTest( content ) ) {
|
||||
const [ value, state ] = util.specAction( content );
|
||||
if ( state == 0 ) {
|
||||
content = value;
|
||||
} else if ( state == 3 ) {
|
||||
content = getcontent( $root.find( value ) );
|
||||
}
|
||||
} else if ( type == "html" ) {
|
||||
content = getcontent( $root.find( content ) );
|
||||
} else if ( type == "multi" ) {
|
||||
// TO-DO
|
||||
} else {
|
||||
content = $root.find( content ).text().trim();
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content from current.site.include
|
||||
*
|
||||
* @param {jquery} jquery object e.g. $root.find( content )
|
||||
* @return {string} $target html
|
||||
*/
|
||||
function getcontent( $target ) {
|
||||
let html = "";
|
||||
switch ( $target.length ) {
|
||||
case 0:
|
||||
html = "<sr-rd-content-error></sr-rd-content-error>";
|
||||
break;
|
||||
case 1:
|
||||
html = $target.html().trim();
|
||||
break;
|
||||
default:
|
||||
html = $target.map( (index, item) => $(item).html() ).get().join( "<br>" );
|
||||
break;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exclude tags list
|
||||
*
|
||||
* @param {jquery} jquery object
|
||||
* @param {array} hidden html
|
||||
* @return {string} tags list string
|
||||
*/
|
||||
function excludeSelector( $target, exclude ) {
|
||||
let tags = [], tag = "";
|
||||
for ( let content of exclude ) {
|
||||
if ( util.specTest( content )) {
|
||||
const [ value, type ] = util.specAction( content );
|
||||
if ( type == 1 ) {
|
||||
tag = value;
|
||||
} else if ( type == 2 ) {
|
||||
const arr = $target.html().match( new RegExp( value, "g" ) );
|
||||
if ( arr && arr.length > 0 ) {
|
||||
const str = arr.join( "" );
|
||||
tag = `*[${str}]`;
|
||||
} else {
|
||||
tag = undefined;
|
||||
}
|
||||
} else if ( type == 3 ) {
|
||||
value.remove();
|
||||
}
|
||||
} else {
|
||||
tag = util.selector( content );
|
||||
}
|
||||
if ( tag ) tags.push( tag );
|
||||
}
|
||||
return tags.join( "," );
|
||||
}
|
||||
1416
src/vender/puread/puread.min.js
vendored
Normal file
1416
src/vender/puread/puread.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,141 +0,0 @@
|
||||
console.log( "=== PureRead: Util load ===" )
|
||||
|
||||
/**
|
||||
* Deep clone object
|
||||
*
|
||||
* @param {object} target object
|
||||
* @return {object} new target object
|
||||
*/
|
||||
function clone( target ) {
|
||||
return $.extend( true, {}, target );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URI
|
||||
*
|
||||
* @return {string} e.g. current site url is http://www.cnbeta.com/articles/1234.html return http://www.cnbeta.com/articles/
|
||||
*/
|
||||
function getURI() {
|
||||
const name = (pathname) => {
|
||||
pathname = pathname != "/" && pathname.endsWith("/") ? pathname = pathname.replace( /\/$/, "" ) : pathname;
|
||||
return pathname.replace( /\/[%@#.~a-zA-Z0-9_-]+$|^\/$/g, "" );
|
||||
},
|
||||
path = name( window.location.pathname );
|
||||
return `${ window.location.protocol }//${ window.location.hostname }${ path }/`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify html
|
||||
*
|
||||
* @param {string} input include html tag, e.g.:
|
||||
<div class="article fmt article__content">
|
||||
*
|
||||
* @return {array} 0: int include ( -1: fail; 0: empty html; 1: success; 2: special tag )
|
||||
* 1: result
|
||||
*/
|
||||
function verifyHtml( html ) {
|
||||
if ( html == "" ) return [ 0, html ];
|
||||
else if ( specTest( html )) return [ 2, html ];
|
||||
const item = html.match( /<\S+ (class|id)=("|')?[\w-_=;:' ]+("|')?>?$|<[^/][-_a-zA-Z0-9]+>?$/ig );
|
||||
if ( item && item.length > 0 ) {
|
||||
return [ 1, item ];
|
||||
} else {
|
||||
return [ -1, undefined ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Conver html to jquery object
|
||||
*
|
||||
* @param {string} input include html tag, e.g.:
|
||||
<div class="article fmt article__content">
|
||||
*
|
||||
* @return {string} formatting e.g.:
|
||||
h2#news_title
|
||||
div.introduction
|
||||
div.content
|
||||
div.clearfix
|
||||
div.rating_box
|
||||
span
|
||||
special tag, @see specTest
|
||||
e.g. [['<strong>▽</strong>']] [[[$('.article-btn')]]]
|
||||
[[/src=\\S+(342459.png)\\S+'/]] [[{$('.content').html()}]]
|
||||
*
|
||||
*/
|
||||
function selector( html ) {
|
||||
const [ code, item ] = verifyHtml( html );
|
||||
if ( code == 2 ) return html;
|
||||
else if ( code == 1 ) {
|
||||
let [tag, prop, value] = item[0].trim().replace( /['"<>]/g, "" ).replace( / /ig, "=" ).split( "=" ); // ["h2", "class", "title"]
|
||||
if ( !prop ) prop = tag;
|
||||
else if ( prop.toLowerCase() === "class") prop = `${tag}.${value}`;
|
||||
else if ( prop.toLowerCase() === "id" ) prop = `${tag}#${value}`;
|
||||
return prop;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify special action, action include:
|
||||
- [[{juqery code}]] // new Function, e.g. $("xxx").xxx() return string
|
||||
- [['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
|
||||
|
||||
*
|
||||
* @param {string} verify content
|
||||
* @return {boolen} verify result
|
||||
*/
|
||||
function specTest( content ) {
|
||||
return /^(\[\[)[\[{'/]{1}[ \S]+[}'/\]]\]\]{1}($)/g.test( content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec special action, action include: @see specTest
|
||||
* type: 0, 3 - be chiefly used in include logic
|
||||
* type: 1, 2 - be chiefly used in exclude logic
|
||||
*
|
||||
* @param {string} content
|
||||
* @return {array} 0: result; 1: type( include: -1:error 0:{} 1:'' 2:// 3:[])
|
||||
*/
|
||||
function specAction( content ) {
|
||||
let [ value, type ] = [ content.replace( /(^)\[\[|\]\]$/g, "" ) ];
|
||||
switch (value[0]) {
|
||||
case "{":
|
||||
value = value.replace( /^{|}$/g, "" );
|
||||
content = ( v=>new Function( `return ${v}` )() )(value);
|
||||
type = 0;
|
||||
break;
|
||||
case "'":
|
||||
content = value.replace( /^'|'$/g, "" );
|
||||
const name = content.match(/^<[a-zA-Z0-9_-]+>/g).join("").replace( /<|>/g, "" );
|
||||
const str = content.replace( /<[/a-zA-Z0-9_-]+>/g, "" );
|
||||
content = `${name}:contains(${str})`;
|
||||
type = 1;
|
||||
break;
|
||||
case "/":
|
||||
content = value.replace( /^\/|\/$/g, "" ).replace( /\\{2}/g, "\\" ).replace( /'/g, '"' );
|
||||
type = 2;
|
||||
break;
|
||||
case "[":
|
||||
value = value.replace( /^{|}$/g, "" );
|
||||
content = ( v=>new Function( `return ${v}` )() )(value)[0];
|
||||
type = 3;
|
||||
break;
|
||||
default:
|
||||
console.error( "Not support current action.", content )
|
||||
type = -1;
|
||||
break;
|
||||
}
|
||||
return [ content, type ];
|
||||
}
|
||||
|
||||
export {
|
||||
clone,
|
||||
getURI,
|
||||
verifyHtml,
|
||||
selector,
|
||||
specTest,
|
||||
specAction
|
||||
}
|
||||
@ -308,8 +308,8 @@ const webpack = require( 'webpack' ),
|
||||
tooltip : __dirname + '/src/vender/mduikit/tooltip.jsx',
|
||||
waves : __dirname + '/src/vender/mduikit/waves.js',
|
||||
|
||||
puread : __dirname + '/src/vender/puread/puread.js',
|
||||
puplugin : __dirname + '/src/vender/puread/plugin.js',
|
||||
puread : __dirname + '/src/vender/puread/puread.min.js',
|
||||
puplugin : __dirname + '/src/vender/puread/puplugin.min.js',
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user