Merge branch 'feature/plugins' into develop

This commit is contained in:
Kenshin 2018-07-19 10:15:48 +08:00
commit 6a0602f86a
21 changed files with 707 additions and 57 deletions

View File

@ -3,7 +3,7 @@
--text-color: #333;
--secondary-color: color(#333 alpha(-30%));
--background-color: #fff;
--width: 740px;
--width: 835px;
}
* {
@ -237,4 +237,120 @@ a {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////ExMS2tra/v7/Dw8O8vLzCwsK5ubnCwsK6urq+vr68vLy9vb29vb28vLy9vb29vb29vb29vb29vb29vb29vb29vb2+vr6+vr69vb29vb29vb2oiyseAAAAHHRSTlMAAQ0OEBETFRYZGkpMg8rLzM7R0tPW19je4uTlotOuxwAAAAFiS0dEAf8CLd4AAAB1SURBVCjPrdLHDoAgEATQUbH33vb/v1PEEnVXT85pM48QCAC/JhloC3AOJiO9wDms8UoZdN9KEHZUuAKEPeUuOGw9h0j36nqPHVi/g19RE9y3NoNenylwcGqqfQiANFMQwbIhAz/lB0z0yHg81Hzvp/jfj7AA4P8P+rUn4dEAAAAASUVORK5CYII=);
background-position: center;
background-repeat: no-repeat;
}
cards {
display: flex;
flex-flow: row wrap;
}
card {
display: flex;
flex-direction: column;
margin: 6px;
width: 259px;
color: rgba(51, 51, 51, .87);
background-color: #fff;
border-radius: 2px;
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
transition: all .25s ease-out;
}
card:hover {
box-shadow: 0 10px 20px 0 rgba(168,182,191,0.6);
transform: translateY(-1px);
}
card-header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 196px;
background-color: #40C4FF;
}
card-header icon {
display: block;
width: 100px;
height: 100px;
font-size: 80px;
}
card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
height: 134px;
padding: 16px;
}
card-content title {
display: block;
font-size: 20px;
font-weight: 500;
height: 32px;
line-height: 32px;
}
card-content desc {
display: block;
font-size: 14px;
font-weight: 500;
height: 22px;
line-height: 22px;
}
card-content note {
display: block;
margin-top: 16px;
text-align: left;
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
card-footer {
display: flex;
align-items: flex-end;
justify-content: flex-end;
width: 100%;
height: 52px;
}
card-footer i {
font-size: 16px;
}
card-empty {
width: 100%;
padding: 50px;
}
card-empty a {
color: #9b9b9b;
font-size: 30px;
font-weight: 500;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

View File

@ -156,6 +156,10 @@ browser.tabs.onUpdated.addListener( function( tabId, changeInfo, tab ) {
browser.tabs.remove( tabId );
}
});
} else if ( tab.url.startsWith( "http://simpread.ksria.cn/plugins/install/" )) {
const url = tab.url.replace( "http://simpread.ksria.cn/plugins/install/", "" );
browser.tabs.create({ url: browser.extension.getURL( "options/options.html#plugins?install=" + encodeURIComponent(url) ) });
browser.tabs.remove( tabId );
}
if ( !tab.url.startsWith( "chrome://" ) ) {

View File

@ -133,6 +133,8 @@ export default class CommonOpt extends React.Component {
menu.Refresh( json.option.menu );
json.option.origins && json.option.origins.length > 0 &&
new Notify().Render( "导入的配置文件包含了第三方源,请通过手动导入。" );
json.option.plugins && json.option.plugins.length > 0 &&
new Notify().Render( "导入的配置文件包含了插件,请通过手动导入。" );
this.importsecret( json.option.secret, { ...json.secret }, () => {
delete json.secret;
storage.Write( ()=> {
@ -240,7 +242,7 @@ export default class CommonOpt extends React.Component {
</div>
<div style={{ display: 'inline-flex', width: '100%' }}>
<Button type="raised" text="手动同步适配列表" width="100%"
icon={ ss.IconPath( "website_icon" ) }
icon={ ss.IconPath( "update_icon" ) }
color="#fff" backgroundColor="#2196F3"
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.newsites() } />

View File

@ -29,7 +29,7 @@ export default class FocusOpt extends React.Component {
}
render() {
const slider_width = location.protocol.includes( "extension" ) ? "565.8px" : undefined;
const slider_width = location.protocol.includes( "extension" ) ? "660.09px" : undefined;
return (
<sr-opt-focus>
<sr-opt-gp>

75
src/module/pluginbar.jsx Normal file
View File

@ -0,0 +1,75 @@
console.log( "=== simpread plugin bar load ===" )
import {storage} from 'storage';
import Button from 'button';
export default class Pluginbar extends React.Component {
state = {
category: []
};
category = {};
getCategory() {
Object.values( storage.plugins ).forEach( ( item, idx ) => {
if ( this.category[item.category] ) {
this.category[item.category].push( item );
} else {
this.category[item.category] = [];
this.category[item.category].push( item );
}
});
this.setState({ category: Object.keys( this.category ) });
}
enable( id ) {
console.log( id, storage.plugins[id].enable )
const plugin = storage.plugins[id];
plugin.enable = !plugin.enable;
storage.Plugins( () => {
new Notify().Render( "当前插件已" + ( plugin.enable ? "启用" : "禁用" ) + ",请重新进入阅读模式以便生效。" );
// hack code
const bgColor = ( plugin.enable == undefined || plugin.enable == true ) ? plugin.icon.bgColor : "#c3c6c7";
$( this.refs[id].refs.mask ).parent().css( "background-color", bgColor );
}, storage.plugins );
}
componentWillMount() {
storage.Plugins( () => {
this.category = {};
this.getCategory();
});
}
render() {
const child = this.state.category.map( item => {
const actions = this.category[item].map( plugin => {
const bgColor = ( plugin.enable == undefined || plugin.enable == true ) ? plugin.icon.bgColor : "#c3c6c7";
plugin.enable == undefined && ( plugin.enable = true );
return (
<Button ref={plugin.id}
shape="circle" type="flat"
color={ plugin.icon.color } backgroundColor={ bgColor }
tooltip={{ text: plugin.name }}
fontIcon={ plugin.icon.type }
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.enable(plugin.id) } />
)
});
return (
<sr-opt-gp>
<sr-opt-label>{ item }</sr-opt-label>
<actions style={{ display: "flex", margin: "10px 0" }}>{ actions }</actions>
</sr-opt-gp>
)
});
return (
<plugin-bar>{child}</plugin-bar>
)
}
}

292
src/module/plugins.jsx Normal file
View File

@ -0,0 +1,292 @@
console.log( "===== simpread option labs load =====" )
import {storage} from 'storage';
import * as run from 'runtime';
import * as ss from 'stylesheet';
import {browser} from 'browser';
import * as msg from 'message';
import * as watch from 'watch';
import Button from 'button';
class Card extends React.Component {
static defaultProps = {
plugin : {},
};
static propTypes = {
plugin : React.PropTypes.object,
};
update() {
run.Install( this.props.plugin.id, undefined, result => {
if ( result ) {
if ( this.props.plugin.version != result.version ) {
storage.plugins[this.props.plugin.id] = result;
this.props.onChange( "update" );
} else {
new Notify().Render( "当前插件为最新版,无需更新。" );
}
} else new Notify().Render( 2, "更新失败,请稍后再试。" );
});
}
delete() {
new Notify().Render({ mode:"snackbar", content: "是否删除当前插件?", action: "确认", cancel: "取消", callback: type => {
if ( type == "cancel" ) return;
delete storage.plugins[ this.props.plugin.id ];
storage.option.plugins = Object.keys( storage.plugins );
this.props.onChange( "delete" );
}});
}
enable() {
this.props.plugin.enable = this.props.plugin.enable == undefined || this.props.plugin.enable ? false : true;
this.props.onChange( "enable" );
}
addmore() {
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url: "http://simpread.ksria.cn/plugins/details/" + this.props.plugin.id }));
}
render() {
const enable = this.props.plugin.enable == undefined || this.props.plugin.enable ? true : false;
return (
<card>
<card-header style={{ backgroundColor: this.props.plugin.icon.bgColor }}>
<icon style={{ color: this.props.plugin.icon.color }} dangerouslySetInnerHTML={{__html: this.props.plugin.icon.type }} ></icon>
</card-header>
<card-content>
<desc>by { this.props.plugin.user.name }</desc>
<note>{ this.props.plugin.name }</note>
</card-content>
<card-footer>
<Button shape="circle" type="flat"
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
tooltip={{ text: enable == true ? "禁用当前插件" : "启用当前插件" }}
fontIcon={`<i class="fas fa-eye${ enable ? "" : "-slash" }"></i>`}
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.enable() } />
<Button shape="circle" type="flat"
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
tooltip={{ text: "删除当前插件" }}
fontIcon='<i class="fas fa-trash-alt"></i>'
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.delete() } />
<Button shape="circle" type="flat"
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
tooltip={{ text: "更新当前插件到最新版本" }}
fontIcon='<i class="fas fa-cloud"></i>'
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.update() } />
<Button shape="circle" type="flat"
color="#c3c6c7" hoverColor="rgba( 153, 153, 153, .1)"
tooltip={{ text: "查看当前插件的详细信息" }}
fontIcon='<i class="fas fa-ellipsis-h"></i>'
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.addmore() } />
</card-footer>
</card>
)
}
}
class Cards extends React.Component {
static defaultProps = {
plugins : [],
};
static propTypes = {
plugins : React.PropTypes.array,
onChange : React.PropTypes.func,
};
render() {
const card = this.props.plugins.length > 0 ? this.props.plugins.map( ( item, idx ) => {
return (
<Card plugin={ item } onChange={t=>this.props.onChange(t)} />
)
}) : <card-empty><a href="http://simpread.ksria.cn/plugins" target="_blank">没有任何扩展点击打开扩展中心添加</a></card-empty>;
return (
<cards>{ card }</cards>
)
}
}
export default class PluginsOpt extends React.Component {
state = {
plugins: []
};
writecb() {
watch.SendMessage( "option", true );
}
install() {
const id = decodeURIComponent( location.hash ).replace( "#plugins?install=", "" );
run.Install( id, undefined, result => {
if ( result ) {
const add = () => {
storage.plugins[result.id] = result;
storage.Plugins( result => {
new Notify().Render( "当前插件已安装成功2 秒后自动刷新当前页面。" );
setTimeout( ()=> {
location.href = location.origin + location.pathname + "#plugins";
location.reload();
}, 2000 );
}, storage.plugins );
};
if ( !storage.option.plugins.includes( result.id ) ) {
add();
storage.option.plugins.push( result.id );
storage.Write( this.writecb );
} else if ( storage.plugins[result.id].version != result.version ) {
new Notify().Render({ content: "本地版本与安装版本不一致,是否安装新版本?", action: "安装", cancel: "取消", callback: type => {
type == "action" && add();
}});
} else {
new Notify().Render({ content: "本地版本与安装版本一致,是否重新安装?", action: "安装", cancel: "取消", callback: type => {
type == "action" && add();
}});
}
} else new Notify().Render( 2, id + " 获取失败,请稍后再试。" );
});
}
clear() {
new Notify().Render({ mode: "snackbar", content: "是否清除本地全部插件?", action: "是的", cancel: "取消", callback: type => {
if ( type == "action" ) {
storage.option.plugins = [];
storage.Write( this.writecb );
storage.Plugins( () => {
new Notify().Render( "snackbar", "清除成功,此页面需刷新后才能生效!", "刷新 ", ()=>{
location.href = location.origin + location.pathname + "#plugins";
location.reload();
});
}, {} );
}
}});
}
addmore() {
browser.runtime.sendMessage( msg.Add( msg.MESSAGE_ACTION.new_tab, { url: "http://simpread.ksria.cn/plugins" }));
}
update() {
let is_update = false, count = 0;
storage.option.plugins.forEach( id => {
run.Install( id, undefined, result => {
if ( !result ) {
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
return;
}
count++;
if ( storage.plugins[id].version != result.version ) {
storage.plugins[result.id] = result;
is_update = true;
}
count == storage.option.plugins.length && complete();
});
});
const complete = () => {
if ( is_update ) {
storage.Plugins( result => {
new Notify().Render( "本地插件已全部更新完毕。" );
this.setState({ plugins: Object.values( storage.plugins ) });
}, storage.plugins );
} else {
new Notify().Render( "无任何可用更新。" );
}
}
}
import() {
new Notify().Render({ mode:"snackbar", content: "导入意味着从配置文件覆盖当前的插件!", action: "确认", cancel: "取消", callback: type => {
if ( type == "cancel" ) return;
let count = 0;
storage.option.plugins.forEach( id => {
run.Install( id, undefined, result => {
if ( !result ) {
new Notify().Render( 2, id + "获取失败,请稍后再试。" );
return;
}
count++;
storage.plugins[result.id] = result;
count == storage.option.plugins.length && complete();
});
});
}});
const complete = () => {
storage.Plugins( result => {
storage.option.plugins = Object.keys( storage.plugins );
storage.Write( () => {
new Notify().Render( "已从配置文件导入完毕。" );
this.setState({ plugins: Object.values( storage.plugins ) });
});
}, storage.plugins );
}
}
onChange( type ) {
storage.Write( this.writecb );
storage.Plugins( () => {
type == "update" && new Notify().Render( "当前插件已更新成功。" );
type == "delete" && new Notify().Render( "当前插件已删除成功。" );
type == "enable" && new Notify().Render( "当前插件已更改成功。" );
this.setState({ plugins: Object.values( storage.plugins ) });
}, storage.plugins );
}
componentWillMount() {
$( "head" ).append( '<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css" integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp" crossorigin="anonymous">' );
$( "head" ).append( '<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css" integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG" crossorigin="anonymous">' );
storage.Plugins( () => {
decodeURIComponent( location.href ).includes( "#plugins?install=" ) && this.install();
this.setState({ plugins: Object.values( storage.plugins ) });
});
}
render() {
return (
<div id="labs" style={{ width: '100%' }}>
<div className="label">全局</div>
<div className="lab">
<div style={{ display: 'inline-flex', width: '100%' }}>
<Button type="raised" text="从配置文件导入插件" width="100%"
tooltip={{ text: "注意:本操作并不能更新本地插件。" }}
icon={ ss.IconPath( "import_icon" ) }
color="#fff" backgroundColor="#00BCD4"
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.import() } />
<Button type="raised" text="查看并获取更多的插件" width="100%"
fontIcon={`<i class="fas fa-external-link-square-alt"></i>`}
color="#fff" backgroundColor="#00BCD4"
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.addmore() } />
</div>
<div style={{ display: 'inline-flex', width: '100%' }}>
<Button type="raised" text="更新本地全部插件" width="100%"
icon={ ss.IconPath( "update_icon" ) }
color="#fff" backgroundColor="#FF5252"
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.update() } />
<Button type="raised" text="清除本地全部插件" width="100%"
icon={ ss.IconPath( "clear_icon" ) }
color="#fff" backgroundColor="#757575"
waves="md-waves-effect md-waves-button"
onClick={ ()=>this.clear() } />
</div>
</div>
<div className="label">管理</div>
<div style={{ 'padding-top': '10px' }} className="lab">
<Cards plugins={ this.state.plugins } onChange={ t=>this.onChange(t) } />
</div>
</div>
)
}
}

View File

@ -76,7 +76,7 @@ export default class ReadOpt extends React.Component {
}
render() {
const slider_width = location.protocol.includes( "extension" ) ? "565.8px" : undefined;
const slider_width = location.protocol.includes( "extension" ) ? "660.09px" : undefined;
return (
<sr-opt-read>
<sr-opt-gp>

View File

@ -26,10 +26,11 @@ import FocusOpt from 'focusopt';
import ReadOpt from 'readopt';
import CommonOpt from 'commonopt';
import LabsOpt from 'labsopt';
import PluginsOpt from 'pluginsopt';
import About from 'about';
import Unrdist from 'unrdist';
import * as welc from 'welcome';
import * as plug from 'plugin';
import * as run from 'runtime';
import PureRead from 'puread';
@ -181,7 +182,7 @@ function welcomeRender( first, version ) {
function mainRender( idx ) {
$( ".top" ).css( "background-color", conf.topColors[idx] );
$( ".header" ).css( "background-color", conf.topColors[idx] ).find( ".title" ).text( conf.tabsItem[idx].name );
( idx == 3 || idx == 5 ) ? $( '.main' ).addClass( "main_labs" ) : $( '.main' ).removeClass( "main_labs" );
( idx == 3 || idx == 4 || idx == 6 ) ? $( '.main' ).addClass( "main_labs" ) : $( '.main' ).removeClass( "main_labs" );
tabsRender( conf.headerColors[ idx ] );
}
@ -218,6 +219,9 @@ function tabsRender( color ) {
<section style={{ 'padding': '0;' }}>
<LabsOpt option={ storage.option } read={ storage.read } focus={ storage.focus } onChange={ (s)=>save(s) } />
</section>
<section style={{ 'padding': '0;' }}>
<PluginsOpt />
</section>
<section><Unrdist list={ storage.unrdist.map( item => { return { ...item }} ) } /></section>
<section style={{ 'padding': '0;' }}><About option={ storage.option } site={ storage.simpread.sites.length } statistics={ storage.simpread.statistics } onClick={t=>welcomeRender(true)}/></section>
</Tabs>,
@ -268,7 +272,7 @@ function sidebarRender() {
* set user uid
*/
function setUserUID() {
storage.user.uid = plug.ID( "user" );
storage.user.uid = run.ID( "user" );
storage.Write( () => {
console.log( "current user info create!" )
watch.SendMessage( "option", true );

View File

@ -12,6 +12,7 @@ import { storage } from 'storage';
import ReadOpt from 'readopt';
import Actionbar from 'actionbar';
import Pluginbar from 'pluginbar';
import Fab from 'fab';
import Fap from 'fap'
@ -137,12 +138,13 @@ export default class ReadCtlbar extends React.Component {
render() {
const Controlbar = storage.current.fap ?
<Fap items={ [ "样式", "动作" ] } autoHide={ false }
<Fap items={ [ "样式", "动作", "插件" ] } autoHide={ false }
waves="md-waves-effect md-waves-circle md-waves-float"
onOpen={ ()=> this.onPop( "open" ) } onClose={ ()=> this.onPop( "close" ) }
onAction={ (event, type)=>this.onAction(event, type ) }>
<ReadOpt option={ storage.current } onChange={ (t,c)=>this.onChange(t,c)}/>
<Actionbar items={ conf.readItems } onAction={ (type)=>this.onAction(undefined, type ) }/>
<Pluginbar />
</Fap>
:
<Fab items={ conf.readItems } tooltip={ tooltip_options } waves="md-waves-effect md-waves-circle md-waves-float" onAction={ (event, type)=>this.onAction(event, type ) } />

View File

@ -14,6 +14,7 @@ import * as ss from 'stylesheet';
import {browser} from 'browser';
import * as msg from 'message';
import * as highlight from 'highlight';
import * as run from 'runtime';
import * as tooltip from 'tooltip';
import * as waves from 'waves';
@ -40,8 +41,15 @@ const Footer = () => {
class Read extends React.Component {
componentWillMount() {
loadPlugins( "read_start" );
$( "body" ).addClass( "simpread-hidden" );
th.Change( this.props.read.theme );
// hack code
//storage.current.fap && $( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">' );
if ( storage.current.fap ) {
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/solid.css" integrity="sha384-TbilV5Lbhlwdyc4RuIV/JhD8NR+BfMrvz4BL5QFa2we1hQu6wvREr3v6XSRfCTRp" crossorigin="anonymous">' );
$( "head" ).append( '<link rel="stylesheet" class="simpread-fs-style" href="https://use.fontawesome.com/releases/v5.1.0/css/fontawesome.css" integrity="sha384-ozJwkrqb90Oa3ZNb+yKFW2lToAWYdTiF1vt8JiH5ptTGHTGcN7qdoR1F95e0kYyG" crossorigin="anonymous">' );
}
}
async componentDidMount() {
@ -83,10 +91,13 @@ class Read extends React.Component {
tooltip.Render( rdclsjq );
waves.Render({ root: rdclsjq });
storage.Statistics( "read" );
loadPlugins( "read_complete" );
}
}
componentWillUnmount() {
loadPlugins( "read_end" );
ss.FontSize( "" );
$root.removeClass( theme )
.removeClass( "simpread-font" );
@ -248,4 +259,17 @@ function excludes( $target, exclude ) {
$target.find( tags ).remove();
}
/**
* Load plugins from storage and exec
*
* @param {string} state include: plugin.run_at
*/
function loadPlugins( state ) {
storage.Plugins( () => {
storage.option.plugins.forEach( id => {
storage.plugins[id] && run.Exec( state, storage.current.site.name, storage.plugins[id] );
});
});
}
export { Render, Exist, Exit, Highlight };

View File

@ -81,8 +81,8 @@ class TOC extends React.Component {
function Render( root, $target, theme, hidden ) {
// hack code
if ( location.host.includes( "blog.csdn.net" )) return;
if ( location.host.includes( "post.smzdm.com" )) return;
//if ( location.host.includes( "blog.csdn.net" )) return;
//if ( location.host.includes( "post.smzdm.com" )) return;
const table = [],
cls = hidden ? "toc-bg-hidden" : "";

View File

@ -566,6 +566,10 @@ const tabsItem = [{
name: "高级设定",
value: "labs",
route: "#labs",
},{
name: "插件管理",
value: "plugins",
route: "#plugins",
},{
name: "稍后读",
value: "later",
@ -579,8 +583,8 @@ const tabsItem = [{
value: "help",
route: "https://github.com/kenshin/simpread/wiki",
}],
headerColors = [ "#64B5F6", "#81C784", "#9575CD", "#7986CB", "#BA68C8", "#4DB6AC" ],
topColors = [ "#2196F3", "#4CAF50", "#673AB7", "#3F51B5", "#9C27B0", "#009688" ],
headerColors = [ "#64B5F6", "#81C784", "#9575CD", "#7986CB", "#4DD0E1", "#BA68C8", "#4DB6AC" ],
topColors = [ "#2196F3", "#4CAF50", "#673AB7", "#3F51B5", "#00BCD4", "#9C27B0", "#009688" ],
menuItem = tabsItem.map( ( item, idx ) => {
const menu = { ...item };
switch ( idx ) {
@ -598,12 +602,15 @@ const tabsItem = [{
menu.icon = ss.IconPath( "labs_icon" );
break;
case 4:
menu.icon = ss.IconPath( "read_later_icon" );
menu.icon = ss.IconPath( "plugins_icon" );
break;
case 5:
menu.icon = ss.IconPath( "about_icon" );
menu.icon = ss.IconPath( "read_later_icon" );
break;
case 6:
menu.icon = ss.IconPath( "about_icon" );
break;
case 7:
menu.icon = ss.IconPath( "help_icon" );
break;
}

View File

@ -1,35 +0,0 @@
import nanoid from 'nanoid';
/**
* Generate ID
*
* @param {string} generate id, include: user id( uuid v4 ), plugin id( like t.co/cKPFh3Qsh4 )
*/
function generateID( type ) {
if ( type == "user" ) {
const random = "0123456789abcdefghijklmnopqrstuvwxyz",
first = nanoid( random, 8 ),
second = nanoid( random, 4 ),
third = nanoid( random, 4 ),
fourth = nanoid( random, 4 ),
fifth = nanoid( random, 12 );
return `${first}-${second}-${third}-${fourth}-${fifth}`;
} else if ( type == "plugin" ) {
return nanoid( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 10 );
}
}
function install( id ) {
}
function exec( str ) {
}
export {
install as Install,
exec as Exec,
generateID as ID,
}

98
src/service/runtime.js Normal file
View File

@ -0,0 +1,98 @@
console.log( "=== simpread runtime load ===" )
import nanoid from 'nanoid';
import {browser} from 'browser';
import {storage, Clone} from 'storage';
/**
* Generate ID
*
* @param {string} generate id, include: user id( uuid v4 ), plugin id( like t.co/cKPFh3Qsh4 )
*/
function generateID( type ) {
if ( type == "user" ) {
const random = "0123456789abcdefghijklmnopqrstuvwxyz",
first = nanoid( random, 8 ),
second = nanoid( random, 4 ),
third = nanoid( random, 4 ),
fourth = nanoid( random, 4 ),
fifth = nanoid( random, 12 );
return `${first}-${second}-${third}-${fourth}-${fifth}`;
} else if ( type == "plugin" ) {
return nanoid( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 10 );
}
}
/**
* Install plugin
*
* @param {string} id e.g. kw36BtjGu0
* @param {string} url
*/
function install( id, url, callback ) {
url = id ? `http://simpread.ksria.cn/plugins/srplug/${id}.srplug` : url;
url = url + `?${+new Date()}`;
console.log( "install url is", url )
$.ajax({ url, dataType: "json" })
.done( result => callback( result, undefined ))
.fail( () => callback( undefined, "error" ));
}
/**
* Execute
*
* @param {string} state, include: read_start, read_loading, read_complete, read_end
* @param {string} site name, inlcude: "", "xxx", "xxx, yyy"
* @param {object} plugin object
*/
function exec( state, site, plugin ) {
if ( plugin.enable == false ) return;
if ( plugin.run_at != state ) return;
if ( plugin.site != "" && !plugin.site.split(",").includes( site ) ) return;
console.log( "current plugin is running", plugin )
new Function( func( plugin.script ) )();
plugin.style != "" && addStyle( plugin.style );
}
/**
* Contact (function(){})() string
*
* @param {string} source
*/
function func( source ) {
window.Notify = Notify;
window.browser = browser;
window.current = Clone( storage.pr.current );
window.read = Clone( storage.read );
return `( function ( $$version, $title, $desc, $content, $footer, $process, $toc, Notify, browser, $$current, $$read ) {
${ source }
})( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, current, read );`
}
/**
* Add style
*
* @param {string} add css to head
*/
function addStyle( str ) {
$( "head" ).append(`<style type="text/css">${str}</style>`);
}
/**
* Test Plugin
*
* @param {func} style func
* @param {func} plugin func
*/
function testPlugin( style, plugin ) {
style && addStyle( style() );
plugin && plugin( "0.0.1", $( "sr-rd-title" ), $( "sr-rd-desc" ), $( "sr-rd-content" ), $( "sr-rd-footer" ), $( "read-process" ), $( "toc" ), Notify, browser, storage.pr.current, storage.read );
}
window.simpread = { testPlugin };
export {
install as Install,
exec as Exec,
generateID as ID,
}

View File

@ -119,7 +119,8 @@ const name = "simpread",
origins : [],
blacklist : [
"google.com",
]
],
plugins : [], // plugin id, e.g. kw36BtjGu0
},
statistics = {
"focus" : 0,
@ -176,7 +177,8 @@ let current = {},
statistics,
user,
},
secret = {
plugins = {},
secret = {
version : "2017-11-22",
"dropbox" : {
"access_token": ""
@ -312,6 +314,15 @@ class Storage {
return secret;
}
/**
* Get plugins data structure
*
* @return {object} plugins object
*/
get plugins() {
return plugins;
}
/**
* Get all sites structure
*
@ -653,6 +664,28 @@ class Storage {
}
}
/**
* Plugins set/get, plugins not import/export
*
* @param {object} plugins
* @param {function} callback
*/
Plugins( callback, data ) {
if ( data ) {
plugins = { ...data };
browser.storage.local.set( { ["plugins"] : plugins }, () => {
console.log( "chrome storage plugins set success!", plugins );
callback && callback();
});
} else {
browser.storage.local.get( ["plugins"], result => {
console.log( "chrome storage plugins get success!", result );
result && !$.isEmptyObject( result ) && ( plugins = result["plugins"] );
callback && callback();
});
}
}
/**
* Export, only include: version, option, focus, read, unrdist
*

View File

@ -148,6 +148,7 @@ function Verify( curver, data ) {
if ( curver == "1.1.1" ) {
data.user = { "uid": "","name": "","email": "","avatar": "","permission": "" };
data.option.plugins = [];
curver = "1.1.2";
}

View File

@ -1,8 +1,8 @@
/*!
* React Material Design: Button
*
* @version : 0.0.3
* @update : 2018/04/26
* @version : 0.0.4
* @update : 2018/06/21
* @homepage: https://github.com/kenshin/mduikit-ui
* @license : MIT https://github.com/kenshin/mduikit/blob/master/LICENSE
* @author : Kenshin Wang <kenshin@ksria.com>
@ -122,6 +122,23 @@ const cssinjs = () => {
backgroundRepeat: 'no-repeat',
},
font_icon: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '18px',
color: '#fff',
order: -1,
display: 'block',
width: '24px',
height: '24px',
border: 'none',
},
circle: {
borderRadius: '50%',
},
@ -151,6 +168,7 @@ export default class Button extends React.Component {
text : "",
disable : false,
icon : "",
fontIcon: "",
order : "before",
type : "flat",
mode : "primary",
@ -170,6 +188,7 @@ export default class Button extends React.Component {
text : React.PropTypes.string,
disable : React.PropTypes.bool,
icon : React.PropTypes.string,
fontIcon: React.PropTypes.string,
order : React.PropTypes.oneOf([ "before", "after" ]),
type : React.PropTypes.oneOf([ "flat", "raised" ]),
mode : React.PropTypes.oneOf([ "primary", "secondary" ]),
@ -218,7 +237,7 @@ export default class Button extends React.Component {
current.color = this.state.color;
current.backgroundColor = this.state.backgroundColor;
if ( this.props.text == "" && this.props.icon != "" ) {
if ( this.props.text == "" && ( this.props.icon != "" || this.props.fontIcon != "" )) {
delete style.normal_root.minWidth;
delete style.normal_root.borderRadius;
style.normal_root.width = style.normal_root.height;
@ -242,7 +261,13 @@ export default class Button extends React.Component {
( style.root = { ...style.root, ...this.props.style } );
this.props.text == "" && ( style.text.display = "none" );
this.props.icon != "" ? ( style.icon.backgroundImage = `url(${this.props.icon})` ) : ( style.icon.display = "none" );
if ( this.props.fontIcon != "" ) {
style.icon = { ...style.font_icon };
style.icon.display = "flex";
style.icon.color = style.color;
} else this.props.icon != "" ? ( style.icon.backgroundImage = `url(${this.props.icon})` ) : ( style.icon.display = "none" );
this.props.order == "after" && ( style.icon.order = 1 );
this.props.width && ( style.root.width = this.props.width );
@ -261,7 +286,7 @@ export default class Button extends React.Component {
{ ...events }>
<button-mask ref="mask" style={ style.mask }>
<button-span style={ style.span }>
<button-icon style={ style.icon }></button-icon>
<button-icon style={ style.icon } dangerouslySetInnerHTML={{__html: this.props.fontIcon }} ></button-icon>
<button-text style={ style.text }>{ this.props.text }</button-text>
</button-span>
</button-mask>

View File

@ -249,7 +249,7 @@ const webpack = require( 'webpack' ),
export : __dirname + '/src/service/export.js',
highlight : __dirname + '/src/service/highlight.js',
output : __dirname + '/src/service/output.js',
plugin : __dirname + '/src/service/plugin.js',
runtime : __dirname + '/src/service/runtime.js',
focus : __dirname + '/src/focus/focus.js',
controlbar : __dirname + '/src/focus/controlbar.jsx',
@ -265,6 +265,7 @@ const webpack = require( 'webpack' ),
focusopt : __dirname + '/src/module/focus.jsx',
readopt : __dirname + '/src/module/read.jsx',
commonopt : __dirname + '/src/module/common.jsx',
pluginsopt : __dirname + '/src/module/plugins.jsx',
labsopt : __dirname + '/src/module/labs.jsx',
about : __dirname + '/src/module/about.jsx',
unrdist : __dirname + '/src/module/unrdist.jsx',
@ -272,6 +273,7 @@ const webpack = require( 'webpack' ),
authorize : __dirname + '/src/module/authorize.jsx',
siteeditor : __dirname + '/src/module/siteeditor.jsx',
actionbar : __dirname + '/src/module/actionbar.jsx',
pluginbar : __dirname + '/src/module/pluginbar.jsx',
editor : __dirname + '/src/module/common/editor.jsx',
themesel : __dirname + '/src/module/common/theme.jsx',
shortcuts : __dirname + '/src/module/common/shortcuts.jsx',