// ==UserScript== // @name 简悦( SimpRead ) · 轻阅版 // @namespace http://ksria.com/simpread/ // @version 1.1.2.200206 // @description 简悦 - 让你瞬间进入沉浸式阅读的 User Script 扩展 // @author Kenshin // @include http://*/* // @include https://*/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/highlight.min.js // @require https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js // @require https://greasyfork.org/scripts/40244-mduikit/code/MDUIKit.js?version=697886 // @require https://greasyfork.org/scripts/40236-notify/code/Notify.js?version=770172 // @require https://greasyfork.org/scripts/40172-mousetrap/code/Mousetrap.js?version=262594 // @require https://greasyfork.org/scripts/383025-bloomingmenu/code/BloomingMenu.js?version=698175 // @require https://greasyfork.org/scripts/39995-pureread/code/PureRead.js?version=770507 // @require https://greasyfork.org/scripts/39997-puplugin/code/PuPlugin.js?version=770506 // @resource global_sites http://sr.ksria.cn/website_list_v4.json?data=1.1.2.20200205 // @resource notify_style http://sr.ksria.cn/puread/notify.css?version=1.1.2.20200205 // @resource main_style http://sr.ksria.cn/puread/simpread.css?version=1.1.2.20200205 // @resource mntips_style http://sr.ksria.cn/puread/mintooltip.css?version=1.1.2.202002051244 // @resource option_style http://sr.ksria.cn/puread/setting.css?version=1.1.2.20200205 // @resource user_style http://sr.ksria.cn/puread/little.css?version=1.1.2.202002061430 // @resource theme_common http://sr.ksria.cn/puread/theme_common.css?version=1.1.2.20200205 // @resource theme_dark http://sr.ksria.cn/puread/theme_dark.css?version=1.1.2.20200205 // @resource theme_github http://sr.ksria.cn/puread/theme_github.css?version=1.1.2.20200205 // @resource theme_gothic http://sr.ksria.cn/puread/theme_gothic.css?version=1.1.2.20200205 // @resource theme_night http://sr.ksria.cn/puread/theme_night.css?version=1.1.2.20200205 // @resource theme_pixyii http://sr.ksria.cn/puread/theme_pixyii.css?version=1.1.2.20200205 // @resource theme_engwrite http://sr.ksria.cn/puread/theme_engwrite.css?version=1.1.2.20200205 // @resource theme_monospace http://sr.ksria.cn/puread/theme_monospace.css?version=1.1.2.20200205 // @resource theme_newsprint http://sr.ksria.cn/puread/theme_newsprint.css?version=1.1.2.20200205 // @resource theme_octopress http://sr.ksria.cn/puread/theme_octopress.css?version=1.1.2.20200205 // @resource theme_mobile http://sr.ksria.cn/puread/theme_mobile.css?version=1.1.2.20200205 // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_info // @run-at document-end // @noframes // ==/UserScript== const pr = new PureRead(), style = puplugin.Plugin( "style" ), global_sites = GM_getResourceText( "global_sites" ), notify_style = GM_getResourceText( "notify_style" ), main_style = GM_getResourceText( "main_style" ), mntips_style = GM_getResourceText( "mntips_style" ), option_style = GM_getResourceText( "option_style" ), user_style = GM_getResourceText( "user_style" ), theme_common = GM_getResourceText( "theme_common" ), theme_dark = GM_getResourceText( "theme_dark" ), theme_github = GM_getResourceText( "theme_github" ), theme_gothic = GM_getResourceText( "theme_gothic" ), theme_night = GM_getResourceText( "theme_night" ), theme_pixyii = GM_getResourceText( "theme_pixyii" ), theme_engwrite = GM_getResourceText( "theme_engwrite" ), theme_monospace = GM_getResourceText( "theme_monospace" ), theme_newsprint = GM_getResourceText( "theme_newsprint" ), theme_octopress = GM_getResourceText( "theme_octopress" ), theme_mobile = GM_getResourceText( "theme_mobile" ), theme = { theme_github, theme_newsprint, theme_gothic, theme_engwrite, theme_octopress, theme_pixyii, theme_monospace, theme_night, theme_dark }, focus = { version : "2016-12-29", bgcolor : "rgba( 235, 235, 235, 0.9 )", mask : true, highlight : true, // not-write, only read opacity : 90, shortcuts : "A S", }, read = { version : "2017-03-16", cleanup : true, pure : true, auto : false, controlbar: true, highlight : true, shortcuts : "A A", theme : "github", fontfamily: "default", whitelist : [], exclusion : [ "v2ex.com","issue.github.com","readme.github.com","question.zhihu.com","douban.com","nationalgeographic.com.cn","tech.163.com","docs.microsoft.com","msdn.microsoft.com","baijia.baidu.com","code.oschina.net","http://www.ifanr.com","http://www.ifanr.com/news","http://www.ifanr.com/app","http://www.ifanr.com/minapp","http://www.ifanr.com/dasheng","http://www.ifanr.com/data","https://www.ifanr.com/app","http://www.ifanr.com/weizhizao","http://www.thepaper.cn","http://www.pingwest.com","http://tech2ipo.com","https://www.waerfa.com/social" ], fontsize : "62.5%", layout : "20%", toc : true, toc_hide : true, }, option = { version : "2017-04-03", esc : true, trigger : "read", // include: 'focus' 'read', only by userscript origins : [], trigger_hiden: true, blacklist : [ "google.com","https://www.baidu.com/?vit=1" ], }, opt_value = ` # 是否启用 ESC 退出方式? # 默认为 true,取值范围 true | false set_esc: true # 右下角触发器点击后进入的模式 # 默认为 read,取值范围 focus | read set_trigger: read # 当在非适配的页面是否隐藏触发器 # 默认为 true (隐藏),取值范围 true | false # 当选择了 true 且在非适配页面时,可以通过快捷键使用临时阅读模式 set_trigger_hiden: true # 黑名单,加入其中后,不再启动简悦 # 有别于白名单和排除列表,前两种当前页面还是加载简悦,但黑名单则彻底加载轻阅的代码 # 支持 域名 和 URL, # 例如: # https://www.baidu.com/?vit=1 则在此页面禁用 # google.com 则凡是含有 google.com 的域名都禁用,包括: mail.google.com doc.google.com 等 # mail.google.com 仅在 mail.google.com 下无法使用,但 doc.google.com 则没问题 # 每个名单由小写 , 分隔 set_blacklist: `, focus_value = ` # 是否启用点击空白(遮罩)退出功能? # 默认为 true,取值范围 true | false set_mask: true # 遮罩的背景色,仅支持 rgba 格式 # 默认为 rgba( 235, 235, 235, 1 ) set_bgcolor: rgba( 235, 235, 235, 0.9 ) # 遮罩的透明度 # 默认为 90,取值范围 0 ~ 100 set_opacity: 90 # 启动聚焦模式的快捷键 # 默认为 A S # 必须有两个值,仅支持 shift, 字母和数字,中间必须有空格 set_shortcuts: A S # 当未适配聚焦模式时,是否启用手动聚焦模式? # 默认为启用,取值范围 true | false set_highlight: true `, read_value = ` # 主题样式 # 取值范围 白练 → github, 白磁 → newsprint, 卯之花色 → gothic, 丁子色 → engwrite # 取值范围 娟鼠 → octopress, 月白 → pixyii, 百合 → monospace, 紺鼠 → night, 黒鸢 → dark # 请使用关键字,而非名称,如:pixyii set_theme: github # 字体样式,支持 css font-family 值 # 默认为 default,即浏览器默认值 set_fontfamily: default # 字体大小,支持 css font-size 值 # 默认为 62.5% set_fontsize: 62.5% # 布局宽度,支持 css margin 值,例如: 20px, 80% 等 # 默认为 20% 宽度 set_layout: 20% # 是否一直显示右下角的控制栏? # 默认为不显示,取值范围 true | false set_controlbar: false # 当未适配阅读模式时,是否启用临时阅读模式? # 默认为启用,取值范围 true | false set_highlight: true # 启动阅读模式的快捷键 # 默认为 A A # 必须有两个值,仅支持 shift, 字母和数字,中间必须有空格 set_shortcuts: A A # 如果当前页面适配阅读模式,是否自动进入阅读模式? # 默认为 false,取值范围 true | false set_auto: false # 黑名单,加入其中后,不会自动进入阅读模式 # 此功能在 auto = true 时才会生效 # 支持 minimatch,域名 和 name,例如: "v2ex.com", "http://www.ifanr.com/**/*" # 每个名单由小写 , 分隔 set_exclusion: # 白名单,加入其中后,自动进入阅读模式 # 此功能在 auto = true 时才会生效,并与黑名单互斥 # 支持 minimatch,域名 和 name,例如: "v2ex.com", "http://www.ifanr.com/**/*" # 默认为空,每个名单由小写 , 分隔 set_whitelist: # 是否启用增强解析模式? # 增强解析模式会对版面重新设计,包括:去除多余空格、优化版面结构等,此功能为测试版,遇到解析失败时,请关闭此功能。 # 默认为 true,取值范围 true | false set_cleanup: true # 是否启用纯粹模式? # 比【增强解析模式】还要彻底优化版本,包括:字形、颜色、字号、代码段等,专治页面及不规范,如:微信订阅号,CSDN 等。 # 此功能在 cleanup = true 时才会生效 # 默认为 true,取值范围 true | false set_pure: true # 是否自动生成大纲(目录)? # 只整理 h1, h2, h3, h4 的内容为大纲 # 默认为 true,取值范围 true | false set_toc: true # 大纲(目录)是否开启「鼠标移动到左上角」自动显示? # 关闭意味着「一直显示」 # 此功能在 toc = true 时才会生效 # 默认为 true,取值范围 true | false set_toc_hide: true `; let current_state = "", // include: focus, read, option simpread = { version: "1.1.2", focus, read, option }, org_simp = { ...simpread }; /**************************** * Entry ****************************/ // initialize version(); // blacklist if ( !blacklist() ) { // add simpread style GM_addStyle( notify_style ); GM_addStyle( main_style ); GM_addStyle( mntips_style ); GM_addStyle( option_style ); GM_addStyle( user_style ); GM_addStyle( theme_common ); GM_addStyle( theme_mobile ); // add websites and current can'b read mode if (GM_getValue( "simpread_db" )) { pr.sites = GM_getValue( "simpread_db" ); } else { pr.Addsites( JSON.parse( global_sites )); GM_setValue( "simpread_db", pr.sites ); } pr.cleanup = simpread.read.cleanup; pr.pure = simpread.read.pure; pr.AddPlugin( puplugin.Plugin() ); pr.Getsites(); // global bindShortcuts(); controlbar(); autoOpen(); dragging(); console.log( "[SimpRead Lite] current pureread is ", pr, simpread ); } /**************************** * Method ****************************/ /** * Version */ function version() { // get and set simpread if (GM_getValue( "simpread" )) { simpread = GM_getValue( "simpread" ); } else { GM_setValue( "simpread", simpread ); GM_setValue( "simpread_subver", GM_info.script.version ); new Notify().Render( "简悦 · 轻阅版 版本提示", `安装到最新版 ${GM_info.script.version},请看 详细说明 。` ); } // compare if ( GM_getValue( "simpread_subver" ) != GM_info.script.version ) { GM_setValue( "simpread_subver", GM_info.script.version ); if ( simpread.version != org_simp.version ) { if ( simpread.version == undefined ) { simpread = { ...org_simp }; } else { Object.keys( org_simp ).forEach( key => { key != "version" && Object.keys( org_simp[key] ).forEach( value => { if ( !simpread[key][value] ) { simpread[key][value] = org_simp[key][value]; } }); }); simpread.version = org_simp.version; } GM_setValue( "simpread", simpread ); } new Notify().Render( "简悦 · 轻阅版 版本提示", `升级到正式版 ${GM_info.script.version},请看 更新说明 。` ); } } /** * Black list */ function blacklist() { let is_blacklist = false; for ( const item of simpread.option.blacklist ) { if ( !item.startsWith( "http" ) ) { if ( location.hostname.includes( item ) ) { is_blacklist = true; break; } } else { if ( location.href == item ) { is_blacklist = true; break; } } } console.log( "current site is blacklist", is_blacklist ) return is_blacklist; } /** * Keyboard event handler */ function bindShortcuts() { Mousetrap.bind( [ simpread.focus.shortcuts.toLowerCase() ], () => entryMode( "focus" )); Mousetrap.bind( [ simpread.read.shortcuts.toLowerCase() ], () => entryMode( "read" )); Mousetrap.bind( "esc", ( event, combo ) => { if ( combo == "esc" && simpread.option.esc ) { if ( $( ".simpread-read-root" ).length > 0 ) $( ".simpread-read-root sr-rd-crlbar fab" )[1].click(); if ( $( ".simpread-focus-root" ).length > 0 ) $( "sr-rd-crlbar fab.crlbar-close" )[0].click(); } }); } /** * Auto open read mode */ function autoOpen() { const exclusion = ( minimatch, data ) => { const url = window.location.origin + window.location.pathname; return data.findIndex( item => { item = item.trim(); return item.startsWith( "http" ) ? minimatch( url, item ) : item == pr.current.site.name; }) == -1 ? true : false; }, whitelist = ( minimatch, data ) => { const url = window.location.origin + window.location.pathname; return data.findIndex( item => { item = item.trim(); return item.startsWith( "http" ) ? minimatch( url, item ) : item == pr.current.site.name; }) != -1 ? true : false; }; if ( window.location.href.includes( "simpread_mode=read" ) || ( simpread.read.auto && exclusion( puplugin.Plugin( "minimatch" ), simpread.read.exclusion )) || ( !simpread.read.auto && whitelist( puplugin.Plugin( "minimatch" ), simpread.read.whitelist )) ) { switch ( pr.current.site.name ) { case "my.oschina.net": case "36kr.com": case "chiphell.com": case "question.zhihu.com": $( () => readMode() ); break; case "post.juejin.im": case "entry.juejin.im": setTimeout( ()=>readMode(), 2500 ); break; case "kancloud.cn": case "sspai.com": setTimeout( ()=>readMode(), 1000 ); break; default: if ( pr.state == "adapter" ) readMode(); break; } } } /** * Control bar */ function controlbar() { $( "body" ).append( '' ); $( "sr-rd-crlbar" ).css( "opacity", 1 ); if ( pr.state == "none" ) $( "sr-rd-crlbar fab:not(.setting,.about)" ).addClass( "not-adapter" ); setTimeout( () => { $( "sr-rd-crlbar" ).removeAttr( "style" ); if ( pr.state == "none" && simpread.option.trigger_hiden == true ) $( "sr-rd-crlbar" ).css({ display: "none" }); const position = GM_getValue( "simpread_trigger_pos" ); if ( position && position['sr-rd-crlbar'] ) { const { x, y } = position['sr-rd-crlbar']; $( 'sr-rd-crlbar' ) .attr( 'data-x', x ) .attr( 'data-y', y ) .css({ 'transform': `translate(${x}px, ${y}px)`, '-webkit-transform': `translate(${x}px, ${y}px)`, }); } $( "sr-rd-crlbar" ).removeClass( "hidden" ); }, 1000 * 2 ); $( "sr-rd-crlbar fab:not(.setting,.about)" ).click( event => { if ( $( 'sr-rd-crlbar[draggable="true"]' ).length > 0 ) return; if ( $(event.target).hasClass( "crlbar-close" ) ) { $( ".simpread-focus-root" ).trigger( "click", "okay" ); $( event.target ).removeClass( "crlbar-close" ).text( "简 悦" ); } else entryMode( simpread.option.trigger ); event.preventDefault(); return false; }); $( "sr-rd-crlbar fab:not(.setting,.about)" ).mouseover( () => { if ( $( ".simpread-focus-root" ).length == 0 ) { $( "sr-rd-crlbar fab.setting" ).addClass( "show" ); $( "sr-rd-crlbar fab.about" ).addClass( "show" ); } $( "sr-rd-crlbar" ).one( "mouseleave" , () => { $( "sr-rd-crlbar fab.setting" ).removeClass( "show" ); $( "sr-rd-crlbar fab.about" ).removeClass( "show" ); }); }); $( "sr-rd-crlbar fab.setting" ).click( () => { optionMode(); }); $( "sr-rd-crlbar fab.about" ).click( () => { aboutMode(); }); }; /** * Enter Mode * * @param {string} include: focus, read */ function entryMode( type ) { try { type = type == "focus" ? "focus" : "read"; if ( [ "none" ].includes( pr.state ) ) { if ( simpread[type].highlight == true ) tempMode( type ); else new Notify().Render( `当前未启用 临时阅读模式,并当前站点也未适配,如需要适配请提交到 此页面` ); } else type == "focus" ? focusMode() : readMode(); } catch ( err ) { console.error( err ) new Notify().Render( 2, "当前页面无法使用阅读模式。" ); } } /** * Focus mode * * @param {dom} html element */ function focusMode( element = undefined ) { let $focus = element ? $(element) : pr.Include(), tag, $parent, sel, range, node; const focuscls = "simpread-focus-highlight", focusstyle = "z-index: 2147483646; overflow: visible; position: relative;", maskcls = "simpread-focus-mask", maskstyle = "z-index: auto; opacity: 1; overflow: visible; transform: none; animation: none; position: relative;", bgcls = "simpread-focus-root", bgtmpl = "
", bgclsjq = "." + bgcls, includeStyle = ( $target, style, cls, type ) => { $target.each( ( idx, ele ) => { let bakstyle, selector = $(ele); if ( type === "add" ) { bakstyle = selector.attr( "style" ) == undefined ? "" : selector.attr( "style" ); selector.attr( "style", bakstyle + style ).addClass( cls ); } else if ( type === "delete" ) { bakstyle = selector.attr( "style" ); bakstyle = bakstyle.replace( style, "" ); selector.attr( "style", bakstyle ).removeClass( cls ); } }); }, excludeStyle = ( $target, type ) => { const tags = pr.Exclude( $target ); if ( type == "delete" ) $target.find( tags ).hide(); else if ( type == "add" ) $target.find( tags ).show(); }; if ( current_state == "focus" ) { new Notify().Render( "请误重复进入。" ); return; } else if ( current_state != "" ) { new Notify().Render( "请先退出当前模式。" ); return; } else current_state = "focus"; // set include style includeStyle( $focus, focusstyle, focuscls, "add" ); // set exclude style excludeStyle( $focus, "delete" ); // add simpread-focus-mask $parent = $focus.parent(); tag = $parent[0].tagName; while ( tag.toLowerCase() != "body" ) { includeStyle( $parent, maskstyle, maskcls, "add" ); $parent = $parent.parent(); tag = $parent[0].tagName; } // add background $( "body" ).append( bgtmpl ); // add background color $( bgclsjq ) .css({ "background-color" : style.BgColor( simpread.focus.bgcolor, simpread.focus.opacity ) }) .animate({ opacity: 1 }); // click mask remove it $( bgclsjq ).on( "click", ( event, data ) => { if ( !simpread.focus.mask && !data ) return; $( bgclsjq ).animate({ opacity: 0 }, { complete: ()=> { current_state = ""; includeStyle( $focus, focusstyle, focuscls, "delete" ); excludeStyle( $focus, "add" ); $( bgclsjq ).remove(); $( bgclsjq ).off( "click" ); $( "sr-rd-crlbar fab:not(.setting,.about)" ).removeClass( "crlbar-close" ).text( "简 悦" ); } }); // remove simpread-focus-mask style $parent = $focus.parent(); tag = $parent[0].tagName; while ( tag && tag.toLowerCase() != "body" ) { includeStyle( $parent, maskstyle, maskcls, "delete" ); $parent = $parent.parent(); tag = $parent[0].tagName; } }); // set focus controlbar $( "sr-rd-crlbar fab:not(.setting,.about)" ).addClass( "crlbar-close" ).text( "" ); } /** * Read mode */ function readMode() { const $root = $( "html" ), bgtmpl = `
全文完
本文由 简悦 SimpRead 优化,用以提升阅读体验
使用了 全新的简悦词法分析引擎beta点击查看详细说明
`, multiple = ( include, avatar ) => { const contents = [], names = avatar[ 0 ].name, urls = avatar[ 1 ].url; include.each( ( idx, item ) => { const art = {}; art.name = $( names[idx] ).text(); art.url = $( urls[idx] ).attr( "src" ); art.content = $( item ).html(); !art.url && ( art.url = default_src ); contents.push( art ); }); const child = contents.map( item => { return `
${ item.name }
${ item.content }
`; }); $( "sr-rd-content" ).html( child ); }, paging = page => { const prev = page[0].prev, next = page[1].next, btn_next = mduikit.Button( "btn-next", "后一页 →", { href: next == undefined ? "javascript:;" : next, disable: next == undefined ? true : false, color: "#fff", bgColor: "#1976D2" }), btn_prev = mduikit.Button( "btn-prev", "← 前一页", { href: prev == undefined ? "javascript:;" : prev, disable: prev == undefined ? true : false, color: "#fff", bgColor: "#1976D2" }); if ( !prev && !next ) $( "sr-page" ).remove(); else $( "sr-page" ).html( btn_prev + btn_next ); }, special = ()=> { if ( pr.current.site.name == "qdaily.com" ) { new Notify().Render( "简悦 · 轻阅版 并不支持此站的适配,如需请使用完整版。" ); return true; } if ( pr.html.include.includes && pr.html.include.includes( "sr-rd-content-error" ) ) { //new Notify().Render( `当前页面结构改变导致不匹配阅读模式,请报告 此页面` ); //simpread.read.highlight == true && tempMode( "read" ); //return true; console.warn( '=== Adapter failed call Readability View ===' ) pr.Readability(); pr.ReadMode(); return false; } }; if ( pr.isMathJax() && pr.state == "temp" ) { console.warn( '=== MathJax Mode ===' ) const dom = pr.MathJaxMode(); if ( typeof dom == "undefined" ) { new Notify().Render( "智能感知失败,请移动鼠标框选。" ); highlight().done( dom => { storage.pr.TempMode( "read", dom ); Render( false ); }); } else if ( typeof dom == "string" ) { const html = pr.GetDom( dom, "html" ); pr.Newsite( "read", html ); } else { pr.TempMode( "read", dom[0] ); } } pr.ReadMode(); if ( special() ) return; if ( current_state == "read" ) { new Notify().Render( "请误重复进入。" ); return; } else if ( current_state != "" ) { new Notify().Render( "请先退出当前模式。" ); return; } else current_state = "read"; $( "body" ).addClass( "simpread-hidden" ); $root .addClass( "simpread-font" ) .addClass( "simpread-theme-root" ) .append( bgtmpl ); $( ".simpread-read-root" ) .addClass( "simpread-theme-root" ) .animate( { opacity: 1 }, { delay: 100 }) .addClass( "simpread-read-root-show" ); $( "sr-rd-title" ).html( pr.html.title ); if ( pr.html.desc != "" && pr.html.desc != undefined ) $( "sr-rd-desc" ).html( pr.html.desc ); else $( "sr-rd-desc" ).remove(); if ( pr.html.avatar ) multiple( pr.html.include, pr.html.avatar ); else $( "sr-rd-content" ).html( pr.html.include ); if ( pr.html.paging ) paging( pr.html.paging ); else $( "sr-page" ).remove(); $("sr-rd-content").find( pr.Exclude( $("sr-rd-content") ) ).remove(); pr.Beautify( $( "sr-rd-content" ) ); pr.Format( "simpread-read-root" ); GM_addStyle( theme[`theme_${simpread.read.theme}`] ); style.FontFamily( simpread.read.fontfamily ); style.FontSize( simpread.read.fontsize ); style.Layout( simpread.read.layout ); pr.pure && codeHighlight(); simpread.read.toc && toc(); // exit $( ".simpread-read-root sr-rd-crlbar fab:not(.setting)" ).one( "click", event => { $( ".simpread-read-root" ).animate( { opacity: 0 }, { delay: 100, complete: () => { current_state = ""; $root.removeClass( "simpread-theme-root" ) .removeClass( "simpread-font" ); if ( $root.attr("style") ) $root.attr( "style", $root.attr("style").replace( "font-size: 62.5%!important", "" )); $( "body" ).removeClass( "simpread-hidden" ); $( ".simpread-read-root" ).remove(); $( "head #sr-rd-mobile" ).remove(); } }).addClass( "simpread-read-root-hide" ); }); $( ".simpread-read-root sr-rd-crlbar fab:not(.setting)" ).mouseover( () => { $( ".simpread-read-root sr-rd-crlbar fab.setting" ).addClass( "show" ); }); $( ".simpread-read-root sr-rd-crlbar" ).mouseleave( () => { $( "sr-rd-crlbar fab.setting" ).removeClass( "show" ); }); $( ".simpread-read-root sr-rd-crlbar fab.setting" ).click( () => { wheelmenu(); }); }; /** * Temp Read mode * * @param {string} include: focus, read */ function tempMode( mode = "read" ) { new Notify().Render( "当前并未适配阅读模式,请移动鼠标手动生成 临时阅读模式。" ); highlight().done( dom => { if ( mode == "read" ) { pr.TempMode( mode, dom.outerHTML ); readMode(); } else focusMode( dom ); }); } /** * Highlight * * @return {promise} promise */ function highlight() { const highlight_class= "simpread-highlight-selector"; let $prev; const dtd = $.Deferred(), mousemoveEvent = event => { if ( !$prev ) { $( event.target ).addClass( highlight_class ); } else { $prev.removeClass( highlight_class ); $( event.target ).addClass( highlight_class ); } $prev = $( event.target ); }; $( "body" ).one( "click", event => { if ( !$prev ) return; $( event.target ).removeClass( highlight_class ); $( "body" ).off( "mousemove", mousemoveEvent ); $prev = undefined; dtd.resolve( event.target ); }); $( "body" ).one( "keydown", event => { if ( event.keyCode == 27 && $prev ) { $( event.target ).find( `.${highlight_class}` ).removeClass( highlight_class ); $( "body" ).off( "mousemove", mousemoveEvent ); $prev = undefined; } }); $( "body" ).on( "mousemove", mousemoveEvent ); return dtd; } /** * Code highlight */ function codeHighlight() { $("head").append( '' ); // source from https://simpread.ksria.cn/plugins/details/klGUASLasg version 0.0.5 const $content = $( 'sr-rd-content' ); if ( location.host == "blog.csdn.net" ) { // remove svg $content.find( "svg" ).map( function( idx, item ) { if ( $(item).attr("title") == "CSDN认证原创" ) { $(item).remove(); } else if ( $(item).html().trim().startsWith( ' 0 ) { $content.find( "pre" ).map(function(idx,item){ $(item).prev().remove(); }) } } // format pre if ( $("body").find( "div[data-prism-highlighted]" ).length > 0 ) { // e.g. https://zh.javascript.info/hello-world $content.find( "pre" ).map( ( idx, item ) => { $(item).text( $($("body").find( "pre" )[idx]).find( "code.language-markup" )[0].outerText ) }); } else if ( $("body").find( ".crayon-syntax" ).length > 0 ) { // crayon-syntax $content.find( "table" ).map( ( idx, item ) => { if ( $(item).find( "td" ).attr( "data-settings" ) ) { const html = $($(item).find("tbody td")[1])[0].innerText; $(item).replaceWith( `
${html}
` ); } }); } else if ( $content.find( "table code" ).length > 0 ) { // syntaxhighlighter $content.find( "table" ).map( ( idx, item ) => { if ( $(item).find( "code" ).length > 0 ) { const html = $($(item).find("tbody td")[1])[0].innerText; $(item).replaceWith( `
${html}
` ); } }); } else if ( $content.find( "pre code" ).length == 0 && $content.find( "pre" ).length > 0 ) { //NOT-TODO } else if ( $("body").find( 'pre code li' ).length > 0 ) { // pre code → li $("body").find( 'pre' ).map( function( idx, pre ) { let html = ""; $(pre).find( 'li' ).map( function( idx, item ) { idx = idx; html += item.innerText + "\n" }) $($content.find( "pre" )[idx]).html( html.replace(//ig,">") ); }) } else if ( $content.find( "table pre" ).length > 0 ) { // pre → table $("table").map(function( idx, item ) { let html = ""; $(item).find("pre").map( function(indx, pre) { html += pre.innerText + "\n"; }); html != "" && $(item).replaceWith( "
" + html.replace(//ig,">").trim() + "
" ); }); } else if ( $("body").find( "pre code" ).length > 0 ) { // pre → code let arr = {}; $("body").find( "pre" ).map(function( idx, item ) { let html = ""; if ( $(item).find( "code" ).length > 0 ) { if ( $(item).find( "code" ).length > 1 ) { html = item.innerText; } else { html = $(item).find( "code" ).text(); } arr[idx] = html; } }); $content.find( "pre" ).map(function( idx, item ) { $(item).html( arr[idx].replace(//ig,">").trim() ); }); } // remove lines $content.find( "pre" ).map( function( idx, item ) { const $target = $(item); const lines = item.innerHTML.split('\n'); let html = ""; for( let i = 0; i < lines.length; i++ ) { const line = lines[i]; if ( !/^\d+$/.test( line.trim() ) ) { html += line + "\n"; } } $target.html( html.trim() ) }); // mark Mousetrap.bind( 'h h h', function() { new Notify().Render( "移动鼠标框选需要重新整理并高亮代码的区域。" ); mark().done( dom => { let $target = $(dom), $parent = $target.parent(); if ( $target.is( "td" ) || $parent.is("td") ) { $target = $parent.parent().parent(); } $target.html( `
${dom.innerText}
` ); hljs.highlightBlock($target.find("pre")[0]); }); }); function mark() { const highlight_class = "simpread-highlight-selector"; let $prev; const dtd = $.Deferred(), mousemoveEvent = event => { if ( !$prev ) { $( event.target ).addClass( highlight_class ); } else { $prev.removeClass( highlight_class ); $( event.target ).addClass( highlight_class ); } $prev = $( event.target ); }; $content.one( "click", event => { if ( !$prev ) return; $( event.target ).removeClass( highlight_class ); $content.off( "mousemove", mousemoveEvent ); $prev = undefined; dtd.resolve( event.target ); }); $("html").one( "keydown", event => { if ( event.keyCode == 27 && $prev ) { $content.find( `.${highlight_class}` ).removeClass( highlight_class ); $content.off( "mousemove", mousemoveEvent ); $prev = undefined; event.preventDefault(); return false; } }); $content.on( "mousemove", mousemoveEvent ); return dtd; } $content.find( 'pre' ).map( function(idx,item){ hljs.highlightBlock(item); }); // change pre background by theme switch ( simpread.read.theme ) { case 'github': case 'pixyii': case 'gothic': $("head").append( '' ) break; case 'monospace': $("head").append( '' ) break; case 'engwrite': $("head").append( '' ) break; case 'newsprint': $("head").append( '' ) break; case 'octopress': $("head").append( '' ) break; case 'dark': $("head").append( '' ) break; case 'night': $("head").append( '' ) break; } } /** * Wheel menu buttons */ function wheelmenu() { if ( $(".sr-rd-trigger").length > 0 ) { $(".sr-rd-trigger").remove(); $("head #blooming-menu__root").remove(); return; }; const menu = new BloomingMenu({ startAngle: -180, endAngle: 0, radius: 120, itemsNum: 7, itemAnimationDelay: 0, isAutoClose: false }); menu.render(); menu.open() menu.props.elements.items.forEach(function (item, index) { item.addEventListener('click', function () { if ( $( '.sr-rd-trigger[draggable="true"]' ).length > 0 ) return; switch ( index ) { case 0: case 1: const size = parseFloat( simpread.read.fontsize ) + ( index == 0 ? 3 : -3 ); simpread.read.fontsize = `${size}%`; style.FontSize( simpread.read.fontsize ); GM_setValue( "simpread", simpread ); break; case 2: case 3: const layout = parseFloat( simpread.read.layout ) + ( index == 3 ? 3 : -3 ); simpread.read.layout = `${layout}%`; style.Layout( simpread.read.layout ); GM_setValue( "simpread", simpread ); break; case 4: case 5: const arr = Object.keys( theme ), len = arr.length; let idx = arr.indexOf( `theme_${simpread.read.theme}` ) + + ( index == 4 ? 1 : -1 ); if ( idx == len ) { idx = 0; } else if ( idx == -1 ) { idx = len -1; } // remove old theme $( "head" ).find( "style" ).map( (index, item) => { const $target = $(item), css = $target.text(); if ( css.startsWith( "sr-rd-theme-" + simpread.read.theme ) ) { $target.remove(); } }); // add new theme simpread.read.theme = arr[idx].replace( "theme_", "" ); GM_setValue( "simpread", simpread ); GM_addStyle( theme[`theme_${simpread.read.theme}`] ); break; case 6: menu.remove(); $(".sr-rd-trigger").remove(); $("head #blooming-menu__root").remove(); break; } }) }); setTimeout( ()=> { $(".simpread-read-root").append( `
` ); $(".sr-rd-trigger").append( $(".blooming-menu__container") ); $(".simpread-read-root .blooming-menu__main-content").html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[0]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[1]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[2]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[3]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[4]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[5]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn")[6]).html( `` ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[0]).attr( "data-balloon-pos", "left" ).attr( "aria-label", "增加字体" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[1]).attr( "data-balloon-pos", "up" ).attr( "aria-label", "减小字体" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[2]).attr( "data-balloon-pos", "up" ).attr( "aria-label", "减小留白" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[3]).attr( "data-balloon-pos", "up" ).attr( "aria-label", "增加留白" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[4]).attr( "data-balloon-pos", "up" ).attr( "aria-label", "前一个主题" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[5]).attr( "data-balloon-pos", "up" ).attr( "aria-label", "后一个主题" ); $($(".simpread-read-root .blooming-menu__item-btn-wrapper")[6]).attr( "data-balloon-pos", "right" ).attr( "aria-label", "退出" ); $( "body" ).find( ".blooming-menu__container" ).remove(); const position = GM_getValue( "simpread_trigger_pos" ); if ( position && position['div'] ) { const { x, y } = position['div']; $( '.sr-rd-trigger' ) .attr( 'data-x', x ) .attr( 'data-y', y ) .css({ 'transform': `translate(${x}px, ${y}px)`, '-webkit-transform': `translate(${x}px, ${y}px)`, }); } }, 100 ); let preScroll = 0; $(document).scroll( () => { if ( $(document).scrollTop() > preScroll ) { menu.close(); } else { //TO-DO } preScroll = $(document).scrollTop(); }); } /** * toc */ function toc() { const table = [], cls = simpread.read.toc_hide ? "toc-bg-hidden" : ""; $("sr-read").find( "h1, h2, h3, h4" ).map( ( idx, item) => { const $item = $( item ), tag = $item[0].tagName.toLowerCase(), value = $item.text(); let id = $item.attr( "id" ); id = id == undefined ? `sr-toc-${idx}` : `${id}-${idx}` $item.attr( "id", id ); value && table.push({ level: `toc-level-${tag}`, id, value, }); }); console.log( "current toc is ", table ) let tmpl = ""; table.forEach( ( item, idx ) => { tmpl += ` ${ item.value } `; }); $("sr-read").append( `${tmpl}` ); let is_click = false; $("sr-read toc outline a").on( "click", event => { is_click = true; const $target = $( event.target ).parent(); $target.parent().find( "active" ).removeClass( "toc-outline-active" ); $target.find( "active" ).addClass( "toc-outline-active" ); const href = $( event.target ).attr("href"), offsetTop = href === "#" ? 0 : $(href).offset().top - 5; $( "html" ).stop().animate({ scrollTop: offsetTop }, 300, () => { setTimeout( ()=>is_click = false, 500 ); }); event.preventDefault(); }) simpread.read.toc_hide ? $('head').append( `` ) : $( "toc" ).css({ "width" : "initial" }); } /** * Drag */ function dragging() { const position = GM_getValue( "simpread_trigger_pos" ) || {}; interact( '.draggable' ).draggable({ onmove: event => { const target = event.currentTarget, id = target.tagName.toLowerCase(), x = ( parseFloat(target.getAttribute('data-x')) || 0 ) + event.dx, y = ( parseFloat(target.getAttribute('data-y')) || 0 ) + event.dy; $( target ) .attr( "draggable", true ) .attr( 'data-x', x ) .attr( 'data-y', y ) .css({ 'transform': `translate(${x}px, ${y}px)`, '-webkit-transform': `translate(${x}px, ${y}px)` }); position[id] = { x,y }; GM_setValue( "simpread_trigger_pos", position ); }, onend: event => { setTimeout( () => $( event.currentTarget ).attr( "draggable", false ), 500 ); } }); } /** * Option Mode */ function optionMode() { const close = event => { mduikit.Destory(); $( ".simpread-option-root" ) .animate({ opacity: 0 }, { complete: ()=>{ current_state = ""; $( ".simpread-option-root" ).remove(); }}); }, save = event => { setter( $("#txt-option").val(), "option" ); setter( $("#txt-focus ").val(), "focus" ); setter( $("#txt-read ").val(), "read" ); GM_setValue( "simpread", simpread ); console.log( "current simpread is ", simpread ) new Notify().Render( "保存成功,请刷新当前页面,以便新配置文件生效。" ); }, imports = event => { const input = document.createElement( "input" ), $input = $(input), onload = event => { if ( event && event.target && event.target.result ) { try { const json = JSON.parse( event.target.result ); if ( json.version && json.version.replace( /\./g, "" ) < simpread.version.replace( /\./g, "" ) ) { new Notify().Render( 2, "上传的版本太低,已转换为最新版版本!" ); } Object.keys( simpread.focus ).forEach( key => { json.focus[key] != undefined && (simpread.focus[key] = json.focus[key] )}); Object.keys( simpread.read ).forEach( key => { json.read[key] != undefined && (simpread.read[key] = json.read[key] )}); Object.keys( simpread.option ).forEach( key => { json.option[key] != undefined && (simpread.option[key] = json.option[key] )}); GM_setValue( "simpread", simpread ); if ( json.websites ) { pr.sites.custom = [ ...json.websites.custom ]; pr.sites.local = [ ...json.websites.local ]; GM_setValue( "simpread_db", pr.sites ); new Notify().Render( `已导入本地适配源:${ pr.sites.local.length} 条;官方次适配源:${pr.sites.custom.length} 条。` ); console.log( "new simpread db", pr.sites ) } new Notify().Render( "导入成功,请刷新当前页面,以便新配置文件生效。" ); } catch ( error ) { new Notify().Render( 2, "上传失败,配置文件解析失败,请重新确认。" ); } } }; $input.attr({ type : "file", multiple : "false" }) .one( "change", event => { const reader = new FileReader(); reader.onload = onload; reader.readAsText( event.target.files[0] ); }); $input.trigger( "click" ); }, exports = event => { const data = "data:text/json;charset=utf-8," + encodeURIComponent( JSON.stringify( simpread )); const $a = $( `` ).appendTo( "body" ); $a[0].click(); $a.remove(); }, remote = event => { if ( location.protocol == "https:" ) { new Notify().Render( `请勿在 https 下面使用此功能,请前往 http 的页面,如: 点击这里` ); return; } $.getJSON( "http://sr.ksria.cn/website_list_v4.json" + "?_=" + Math.round(+new Date()), result => { const count = pr.Addsites( result ); count > 0 && GM_setValue( "simpread_db", pr.sites ); count == 0 ? new Notify().Render( "适配列表已同步至最新版本。" ) : new Notify().Render( 0, `适配列表已同步成功,本次新增 ${ count } 个站点。` ); }); }, clean = event => { new Notify().Render( "是否清除掉本地配置文件?", "同意 ", () => { simpread = { ...org_simp }; GM_setValue( "simpread", simpread ); pr.sites = { global:[], custom:[], local:[] }; GM_deleteValue( "simpread_db" ); new Notify().Render( "清除成功,请刷新本页!" ); }); }, getter = ( value, type ) => { try { const arr = value.split( "\n" ).map( str => { str = str.trim(); if ( str.startsWith( "set_" ) ) { str = str.replace( "set_", "" ); const key = str.split( ":" )[0]; let value = str.split( ":" )[1]; if ( simpread[type][key] != undefined ) { value = simpread[type][key]; ![ "whitelist", "exclusion", "blacklist" ].includes( key ) && value === "" && ( value = org_simp[type][key] ); return `set_${key}: ${value}`; } } else return str; }); return arr.join( "\n" ); } catch ( error ) { new Notify().Render( 2, "设置出现了问题,请重新打开设置。" ); } }, setter = ( value, type ) => { try { const arr = value.split( "\n" ).forEach( str => { str = str.trim(); if ( str.startsWith( "set_" ) ) { str = str.replace( "set_", "" ); const key = str.split( ":" )[0]; if ( [ "exclusion", "whitelist", "blacklist" ].includes( key )) { const value = str.replace( `${key}:`, "" ).trim(); simpread[type][key] = value.split(","); } else if ( simpread[type][key] != undefined ) { let value = str.split( ":" )[1].trim(); if ( typeof simpread[type][key] == "boolean" ) { simpread[type][key] = value == "true" ? true : false; } else simpread[type][key] = value.trim(); } } }); } catch ( error ) { new Notify().Render( 2, "设置出现了问题,请重新打开设置。" ); } }, btn_cancel = mduikit.Button( "opt-cancel", "关 闭", { color: "rgb(33, 150, 243)", type: "flat", onclick: close, mode: "secondary" }), btn_save = mduikit.Button( "opt-save", "保 存", { color: "rgb(33, 150, 243)", type: "flat", onclick: save }), btn_import = mduikit.Button( "opt-import", "从本地导入配置文件", { color: "#fff", bgColor: "#FF5252", type: "flat", width: "100%", onclick: imports }), btn_export = mduikit.Button( "opt-export", "导出配置文件到本地", { color: "#fff", bgColor: "#2196F3", type: "flat", width: "100%", onclick: exports }), btn_remote = mduikit.Button( "opt-remote", "手动同步适配列表", { color: "#fff", bgColor: "#2196F3", type: "flat", width: "100%", onclick: remote }), btn_clean = mduikit.Button( "opt-clean", "清除数据", { color: "#fff", bgColor: "#757575", type: "flat", width: "100%", onclick: clean }), txt_option = mduikit.Textarea( "txt-option", getter(opt_value, "option"), { color: "rgba(51, 51, 51, 0.6)", state_color: "rgb(33, 150, 243)", size: "11px", height: "130px" }), txt_focus = mduikit.Textarea( "txt-focus", getter(focus_value, "focus"), { color: "rgba(51, 51, 51, 0.6)", state_color: "rgb(33, 150, 243)", size: "11px" }), txt_read = mduikit.Textarea( "txt-read", getter(read_value, "read"), { color: "rgba(51, 51, 51, 0.6)", state_color: "rgb(33, 150, 243)", size: "11px" }), optmpl = `
选项页 导入和导出 ${ btn_import + btn_export } 同步与清除 ${ btn_remote + btn_clean } 全局 ${ txt_option } 聚焦模式 ${ txt_focus } 阅读模式 ${ txt_read } ${btn_cancel + btn_save}
`; if ( current_state == "option" ) { new Notify().Render( "请误重复进入。" ); return; } else if ( current_state != "" ) { new Notify().Render( "请先退出当前模式。" ); return; } else current_state = "option"; $( "html" ).append( optmpl ); $( ".simpread-option-root" ).animate({ opacity: 1 }); const [ h1, h2 ] = [ $("dialog-gp").height(), $(".simpread-option-root").height() ]; if ( h2 <= h1 ) { $("dialog-gp").height( h2 - 80 ); } } /** * About Mode */ function aboutMode() { const close = ( event, cb ) => { mduikit.Destory(); $( ".simpread-option-root" ) .animate({ opacity: 0 }, { complete: ()=>{ current_state = ""; $( ".simpread-option-root" ).remove(); cb && cb(); }}); }, option = event => { close( event, () => optionMode() ); }, btn_cancel = mduikit.Button( "opt-cancel", "关 闭", { color: "rgb(33, 150, 243)", type: "flat", onclick: close }), btn_open = mduikit.Button( "opt-open", "设 定", { color: "rgb(33, 150, 243)", type: "flat", onclick: option, mode: "secondary" }), optmpl = `
关于

你好,我是 简悦 · 轻阅版 的开发者 Kenshin,很高兴看到你能使用它。
它是一个阅读模式类的油猴脚本,也是 简悦 的轻量级版本。
拥有 简悦的一切特性,更具有「加载速度快 · 只关注阅读模式呈现」等 特点
简悦的初衷:还原一个干净的阅读空间,提升你的阅读体验。
截至到目前为止,简悦已经精准适配了 ${ pr.sites.global.length }个 网址,详细请看 这里
简悦是一个免费且开源的项目,占用了我绝大多数的业余时间。
如果觉得它还不错,希望可以给我 投票请我喝杯咖啡,这是对简悦的最大鼓励。
现在就加入 Telegram 群,获取简悦的第一手资料。
${btn_open + btn_cancel}
`; if ( current_state == "option" ) { new Notify().Render( "请误重复进入。" ); return; } else if ( current_state != "" ) { new Notify().Render( "请先退出当前模式。" ); return; } else current_state = "option"; $( "html" ).append( optmpl ); $( ".simpread-option-root" ).animate({ opacity: 1 },{ complete: ()=>{ const [ h1, h2 ] = [ $("dialog-gp").height(), $(".simpread-option-root").height() ]; if ( h2 <= h1 ) { $("dialog-gp").animate({height: h2 - 80 }); } }}); }