简单的青柠起始页优化

默认去掉青柠起始页的页脚,使用 alt + t 控制搜索框的显示隐藏 使用alt + g切换时钟显示隐藏,变量全局存储,不需要每次打开都关闭或者显示了 alt+b 显示隐藏 B站热搜

  1. // ==UserScript==
  2. // @license MIT
  3. // @name 简单的青柠起始页优化
  4. // @namespace https://bbs.tampermonkey.net.cn/
  5. // @version 0.4.0
  6. // @description 默认去掉青柠起始页的页脚,使用 alt + t 控制搜索框的显示隐藏 使用alt + g切换时钟显示隐藏,变量全局存储,不需要每次打开都关闭或者显示了 alt+b 显示隐藏 B站热搜
  7. // @author Hei
  8. // @match *://limestart.cn/*
  9. // @grant GM_setClipboard
  10. // @grant unsafeWindow
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_addStyle
  13. // @grant GM_getResourceText
  14. // @grant GM_getResourceURL
  15. // @connect api.bilibili.com
  16. // @connect at.alicdn.com
  17. // @connect i0.hdslb.com
  18. // @connect *
  19. // @require https://lib.baomitu.com/jquery/1.12.4/jquery.min.js
  20. // @resource css https://at.alicdn.com/t/c/font_4423350_7t2u8i9k77r.css
  21. // ==/UserScript==
  22. (function () {
  23. 'use strict';
  24.  
  25. const indexDBREQ = window.indexedDB.open("limestart", 1) // 数据库名称,版本号
  26. const storeName = 'wallpaper' // 表名
  27. let local_db = null;
  28.  
  29. indexDBREQ.onupgradeneeded = (event) => {
  30. local_db = event.target.result // 数据库对象
  31. if (local_db.objectStoreNames.includes(storeName)) {
  32. local_db.deleteObjectStore(storeName)
  33. local_db.createObjectStore(storeName)
  34. }
  35. console.log('upgrad-连接到本地limestart数据库')
  36. }
  37.  
  38. indexDBREQ.onsuccess = (event) => { // 监听数据库创建成功事件
  39. local_db = event.target.result // 数据库对象
  40. console.log('连接到本地limestart数据库')
  41. }
  42.  
  43.  
  44. const indexedAction = {
  45. get(storeName, key) {
  46. return new Promise((res) => {
  47. const transaction = local_db.transaction([storeName], "readwrite");
  48. const store = transaction.objectStore(storeName);
  49. const r = store.get(key)
  50. r.onsuccess = (e) => {
  51. res(e.target.result)
  52. }
  53. r.onerror = (e) => {
  54. res(null)
  55. }
  56. })
  57. },
  58.  
  59. put(val, key) {
  60. return new Promise((res) => {
  61. const transaction = local_db.transaction([storeName], "readwrite");
  62. const store = transaction.objectStore(storeName);
  63. const r = store.put(val, key)
  64. r.onsuccess = (e) => {
  65. res(true)
  66. }
  67. r.onerror = (e) => {
  68. res(false)
  69. }
  70. })
  71. },
  72.  
  73. delete(key) {
  74. return new Promise((res) => {
  75. const transaction = local_db.transaction([storeName], "readwrite");
  76. const store = transaction.objectStore(storeName);
  77. const r = store.delete(key)
  78. r.onsuccess = (e) => {
  79. res(true)
  80. }
  81. r.onerror = (e) => {
  82. res(false)
  83. }
  84. })
  85. }
  86. }
  87.  
  88. // 基础依赖
  89. const _mainKey = 'easy_limestart'
  90. const ls = {
  91. set(key, val) {
  92. let easy_limestart_obj = window.localStorage.getItem(_mainKey);
  93. if (!easy_limestart_obj) {
  94. easy_limestart_obj = {}
  95. } else {
  96. easy_limestart_obj = JSON.parse(easy_limestart_obj)
  97. }
  98. easy_limestart_obj[key] = val;
  99. window.localStorage.setItem(_mainKey, JSON.stringify(easy_limestart_obj))
  100. },
  101. get(key, defaultVal = null) {
  102. let easy_limestart_obj = window.localStorage.getItem(_mainKey);
  103. if (!easy_limestart_obj) {
  104. easy_limestart_obj = {}
  105. return defaultVal
  106. } else {
  107. easy_limestart_obj = JSON.parse(easy_limestart_obj)
  108. }
  109. const val = easy_limestart_obj[key];
  110. if (val !== undefined) {
  111. try {
  112. return JSON.parse(val)
  113. } catch (err) {
  114. return defaultVal || val
  115. }
  116. }
  117. return defaultVal
  118. }
  119. }
  120. //消息弹窗
  121. const Message = {
  122. info(msg) {
  123. const delay = 2000; //多少毫秒后隐藏
  124. const greetingContainer = document.createElement("div")
  125. greetingContainer.id = "greetingContainer"
  126. const greetingBox = document.createElement("div")
  127. greetingBox.id = "greetingBox"
  128. greetingBox.textContent = msg
  129. greetingContainer.append(greetingBox)
  130. document.body.append(greetingContainer)
  131. greetingContainer.offsetLeft; // reflow
  132. greetingBox.style.opacity = 1
  133. greetingBox.style.transform = `translateY(0)`
  134. setTimeout(() => {
  135. greetingBox.style.opacity = 0;
  136. greetingBox.style.transform = `translateY(-100%)`;
  137. greetingBox.addEventListener("transitionend", () => {
  138. greetingContainer.remove();
  139. })
  140. greetingBox.addEventListener("webkitTransitionEnd", () => {
  141. greetingContainer.remove();
  142. })
  143. }, delay)
  144. }
  145. }
  146.  
  147. // 加载B站热搜
  148.  
  149.  
  150. // 去除脚标
  151. const timer = setInterval(() => {
  152. const footer = document.querySelector("footer");
  153. if (footer) {
  154. footer.style.display = 'none'
  155. clearInterval(timer)
  156. }
  157. }, 500);
  158.  
  159. /**
  160. * 显示/隐藏搜索框
  161. */
  162. function showSearchCb() {
  163. const searchSuggestionContainer = document.getElementById("searchSuggestionContainer");
  164. const menuSearchEng = document.getElementById("menuSearchEng");
  165. const searchEl = document.getElementById("searchBar");
  166. if (searchEl) {
  167. searchEl.style.display = showSearch ? 'block' : 'none';
  168. }
  169. if (searchSuggestionContainer) {
  170. searchSuggestionContainer.style.display = showSearch ? 'block' : 'none';
  171. }
  172.  
  173. if (menuSearchEng) {
  174. menuSearchEng.style.display = showSearch ? 'block' : 'none';
  175. }
  176.  
  177. }
  178.  
  179. let showSearch = ls.get("showSearch");
  180. showSearchCb();
  181. // 如果隐藏输入框,那就失去焦点 + 不要cover
  182. if (!showSearch) {
  183. document.getElementById("inputSearch").blur()
  184. document.getElementById("cover").click()
  185. }
  186.  
  187.  
  188.  
  189. // 显示/隐藏时间框
  190. let showTimer = ls.get("showTimer", true)
  191. function showTimerCb() {
  192. const timeContainer = document.getElementById("timeContainer");
  193. if (timeContainer) {
  194. timeContainer.style.display = showTimer ? 'block' : 'none';
  195. }
  196. }
  197. showTimerCb()
  198.  
  199. let showBilibili = false;
  200. let isBliLoading = false;
  201. //B站热搜请求
  202. function requestBiApi() {
  203. const getRow = async (show_name, keyword, icon) => {
  204. const src = await (new Promise((res) => {
  205. if (icon) {
  206. GM_xmlhttpRequest({
  207. url: icon,
  208. method: 'get',
  209. responseType: 'blob',
  210. onload: (data) => {
  211. var blob = new Blob([data.response], { type: 'image/png' });
  212. const fileReader = new FileReader()
  213. fileReader.onload = (e) => {
  214. res(e.target.result)
  215. }
  216. // readAsDataURL
  217. fileReader.readAsDataURL(blob)
  218. }
  219. })
  220. } else {
  221. res(null)
  222. }
  223. }))
  224. const img_html = src ? `<img class="bilibili-trending-img" src="${src}" />` : ''
  225. return `<div class="setOptBox">
  226. <span class="setOptCaption" style="margin-left: -6px;">
  227. <a href="https://search.bilibili.com/all?keyword=${keyword}" class="link" target="_blank"><i class="icon iconfont icon-bilibili" style="padding-right: 5px;"></i>${show_name}${img_html}</a>
  228. </span>
  229. </div>`
  230. }
  231. if (isBliLoading) return;
  232. isBliLoading = true;
  233. $(".cover2.easy-limestart .pContent").html(`<div class="setGroup">
  234. <div class="setOptBox">
  235. <span class="setOptCaption" style="margin-left: -6px;">
  236. <a href="javascript:void(0)" class="link" target="_blank">加载中...</a>
  237. </span>
  238. </div>
  239. </div>`)
  240. GM_xmlhttpRequest({
  241. url: "https://api.bilibili.com/x/web-interface/wbi/search/square?limit=15&platform=web",
  242. method: "get",
  243. onload: async (data) => {
  244. const { data: { trending: { list, title, top_list } }, code, message } = JSON.parse(data.response);
  245. // console.log(code, message)
  246. if (code != 0) {
  247. Message.info(message)
  248. } else {
  249. $(".cover2.easy-limestart .pCaptionBar").html(`<span class="btnRectangle3" style="left: 5px; right: auto; top: 6px;" id="btnRefreshBli" role="button" tabindex="0">刷新</span>${title}`)
  250. // console.log(list, title, top_list)
  251. $("#btnRefreshBli").click(() => {
  252. requestBiApi()
  253. })
  254. const rowList = []
  255. for (const idx in list) {
  256. const { show_name, keyword, icon } = list[idx];
  257. const row = await getRow(show_name, keyword, icon)
  258. rowList.push(row)
  259. }
  260. $(".cover2.easy-limestart .pContent .setGroup").html(rowList.join(""))
  261. isBliLoading = false;
  262. }
  263. },
  264. onabort: () => {
  265. isBliLoading = false;
  266. },
  267. onerror: () => {
  268. isBliLoading = false;
  269. }
  270. })
  271. $(".cover2.easy-limestart").css({
  272. display: "block",
  273. opacity: 1
  274. })
  275. $(".cover2.easy-limestart .popUp").css({
  276. display: "block",
  277. opacity: 0,
  278. transform: 'rotate3d(1, 1, 0, 50deg)'
  279. }).offset();
  280. $(".cover2.easy-limestart .popUp").css({
  281. display: "block",
  282. opacity: 1,
  283. transform: 'none'
  284. })
  285. }
  286. //B站热搜显示
  287. function showBlibiliTrending(init = false) {
  288. // style="display: block; opacity: 1;"
  289. // style="display: block; opacity: 1; transform: none;"
  290. const dialog = `<div class="cover2 easy-limestart">
  291. <div class="popUp" id="bilibiliTrending" style="display: block; opacity: 1; transform: none;">
  292. <span class="btnClose" role="button" tabindex="0" title="关闭"><span class="btnCloseInner"></span></span>
  293. <div class="pCaptionBar scrolled"></div>
  294. <div class="pContent" id="contentGSet">
  295. <div class="setGroup">
  296. <div class="setOptBox">
  297. <span class="setOptCaption" style="margin-left: -6px;">
  298. <a href="javascript:void(0)" class="link" target="_blank">加载中...</a>
  299. </span>
  300. </div>
  301. </div>
  302. </div>
  303. </div>
  304. </div>`
  305. if (init) {
  306. GM_addStyle(`.cover2 {
  307. z-index: 100;
  308. position: fixed;
  309. top: 0;
  310. left: 0;
  311. width: 100%;
  312. height: 100%;
  313. background-color: rgba(0,0,0,.35);
  314. display: none;
  315. opacity: 0;
  316. transition: .25s;
  317. perspective: 1000px;
  318. } .icon-bilibili {
  319. color: #00AFE0;
  320. } .bilibili-trending-img {
  321. height: 16px;
  322. margin-left: 5px;
  323. vertical-align: -2px;
  324. }
  325. #btnRefreshBli:hover {
  326. color: #fff;
  327. background-color: var(--err-color);
  328. }`)
  329. GM_addStyle(GM_getResourceText("css"))
  330. $(document.body).append(dialog);
  331. $(".cover2.easy-limestart .btnClose").hover(() => {
  332. $(".cover2.easy-limestart .popUp").css({
  333. display: "block",
  334. opacity: 1,
  335. transform: 'rotate3d(1, 1, 0, 5deg)'
  336. })
  337. }, () => {
  338. $(".cover2.easy-limestart .popUp").css({
  339. display: "block",
  340. opacity: 1,
  341. transform: 'none'
  342. })
  343. })
  344. $(".cover2.easy-limestart .btnClose").click(() => {
  345. showBilibili = false;
  346. $(".cover2.easy-limestart").fadeOut(100)
  347. })
  348. } else {
  349. showBilibili = !showBilibili;
  350. if (showBilibili) {
  351. requestBiApi()
  352. } else {
  353. $(".cover2.easy-limestart").fadeOut(100)
  354. }
  355. }
  356. }
  357. showBlibiliTrending(true);
  358.  
  359.  
  360. // 增加键盘事件
  361. document.body.addEventListener("keydown", (e) => {
  362. if (e.altKey && e.key.toLowerCase() === 't') {
  363. showSearch = !showSearch;
  364. showSearchCb()
  365. Message.info(showSearch ? "显示搜索框" : '隐藏搜索框')
  366. ls.set("showSearch", showSearch)
  367. }
  368. if (e.altKey && e.key.toLowerCase() === 'g') {
  369. showTimer = !showTimer;
  370. showTimerCb()
  371. Message.info(showTimer ? "显示时间" : '隐藏时间')
  372. ls.set("showTimer", showTimer)
  373. }
  374.  
  375. if (e.altKey && e.key.toLowerCase() === 'b') {
  376. showBlibiliTrending()
  377. }
  378. });
  379.  
  380. //自定义外部链接背景图
  381. GM_addStyle(`.easy_input {
  382. width: calc(100% - 55px);
  383. color: var(--b-alpha-60);
  384. background-color: transparent;
  385. border: none;
  386. transition: .25s;
  387. } button#btnBrowse:disabled {
  388. background-color: gray!important;
  389. }`)
  390.  
  391. const readUrlImg = (url) => {
  392. return new Promise((res) => {
  393. GM_xmlhttpRequest({
  394. url,
  395. method: "get",
  396. responseType: 'blob',
  397. onload: async (data) => {
  398. var blob = new Blob([data.response], { type: 'image/png' });
  399. const fileReader = new FileReader()
  400. fileReader.onload = (e) => {
  401. const img_url = URL.createObjectURL(blob)
  402. res([null, img_url, [data.response]])
  403. }
  404. // readAsDataURL
  405. fileReader.readAsDataURL(blob)
  406. },
  407. onerror: (err) => {
  408. if (err === 'permission not allowed') {
  409. Message.info('授权被拒绝')
  410. } else {
  411. Message.info(err)
  412. }
  413. res([err, null, null])
  414. }
  415. })
  416. })
  417. }
  418.  
  419. $("#contentBg .bgGroup").eq(0).after(`
  420. <div style="position: relative; height:26px; align-items:end;" class='bgGroup'>
  421. <input id="outsideImgUrlInput" placeholder="请输入外部图片链接" class="easy_input" />
  422. <label class="switch">
  423. <input type="checkbox" id="chkOutsideImgUrlBar">
  424. <div class="slider" id="chkOutsideImgUrlBarInner"></div>
  425. </label>
  426. </div>
  427. `);
  428.  
  429. $("#outsideImgUrlInput").on("input", async () => {
  430. const isChecked = $("#chkOutsideImgUrlBar").prop("checked")
  431. console.log(isChecked)
  432. if(!isChecked) return
  433. const key = `custom`
  434. const oldKey = 'oldCustom'
  435. const videoPosterKey = "customVideoPoster"
  436. $("#chkOutsideImgUrlBar").prop("checked", false);
  437. const pageFile = await indexedAction.get(storeName, oldKey); //老的视频或者图片
  438. await indexedAction.put(pageFile, key)
  439. await indexedAction.delete(oldKey)
  440. if (!pageFile) {
  441. await indexedAction.delete(key)
  442. $("#bgPreBoxInnerCustom").addClass("unset").removeAttr("style");
  443. // const gs = JSON.parse(localStorage.getItem("generalSettings") || {});
  444. // gs['bgPreference'] = 'Default1'
  445. // localStorage.setItem("generalSettings", JSON.stringify(gs))
  446. $("#bgPreBoxInnerDefault1").click();
  447. ls.set("easy_custom_out_url_checked", false)
  448. return
  449. }
  450. const img_url = URL.createObjectURL(pageFile)
  451. if (pageFile.type.startsWith("video/")) {
  452. let videoPosterImg = await indexedAction.get(storeName, videoPosterKey); //是否能获取到视频封面
  453. $("#liveBgBox").css({
  454. display: 'block',
  455. opacity: 1,
  456. transform: "scale(1.1)"
  457. }).attr("src", img_url)
  458. $("#bgBox").css({ opacity: 0 });
  459. if (videoPosterImg) {
  460. $("#bgPreBoxInnerCustom").css({ "background-image": `url(${videoPosterImg})` })
  461.  
  462. }
  463. } else {
  464. $("#liveBgBox").css({
  465. display: 'none',
  466. opacity: 1,
  467. transform: 'scale(1.1)'
  468. })
  469. $("#bgBox").css({
  470. opacity: 1,
  471. transform: "scale(1.1)",
  472. display: "block"
  473. });
  474. $("#bgPreBoxInnerCustom").css({ "background-image": `url(${img_url})` })
  475. $("#bgBox").attr("src", img_url)
  476. }
  477.  
  478. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").off("click")
  479. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").click(async () => {
  480. const pageFile = await indexedAction.get(storeName, key)
  481. const img_url = URL.createObjectURL(pageFile)
  482. if (pageFile.type.startsWith("video/")) {
  483. $("#liveBgBox").attr("src", img_url)
  484. } else {
  485. $("#bgBox").attr("src", img_url)
  486. }
  487. })
  488. ls.set("easy_custom_out_url_checked", false)
  489. $("#btnBrowse").attr("disabled", false)
  490. })
  491.  
  492. // https://img0.baidu.com/it/u=68017732,1315184908&fm=253&fmt=auto&app=138&f=JPEG?w=642&h=243
  493. $("#chkOutsideImgUrlBar").change(async (e) => {
  494. const { checked } = e.target
  495. const url = $("#outsideImgUrlInput").val()
  496. const key = `custom`
  497. const oldKey = 'oldCustom'
  498. const videoPosterKey = "customVideoPoster"
  499. if (checked && local_db && url) {
  500. const [error, img_url, bolb] = await readUrlImg($("#outsideImgUrlInput").val());
  501. const file = new File(bolb, 'outside_url_img.png', { type: "image/png" }); //外部链接生成的图片
  502. const oldFile = await indexedAction.get(storeName, key) //拿到当前正在使用的图片或者video
  503. await indexedAction.put(oldFile, oldKey) //拿到当前正在使用的图片或者video
  504. await indexedAction.put(file, key) //外部链接生成的图片 入住 custom
  505. //当前只支持图片,只要存储完成,如果以前用的是视频文件,那就需要隐藏,这里默认隐藏一次
  506. $("#liveBgBox").css({
  507. display: "none",
  508. opacity: 1,
  509. transform: 'scale(1.1)'
  510. })
  511. $("#btnBrowse").attr("disabled", true)
  512. ls.set("easy_custom_out_url", url)
  513. $("#bgPreBoxInnerCustom").removeClass("unset").css({ "background-image": `url(${img_url})` }).click()
  514. $("#bgBox").attr("src", img_url)
  515. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").off("click")
  516. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").click(async () => {
  517. $("#bgBox").attr("src", img_url)
  518. })
  519. } else {
  520. $("#btnBrowse").attr("disabled", false)
  521. if (!checked) {
  522. const pageFile = await indexedAction.get(storeName, oldKey); //老的视频或者图片
  523. await indexedAction.put(pageFile, key)
  524. await indexedAction.delete(oldKey)
  525. if (!pageFile) {
  526. await indexedAction.delete(key)
  527. $("#bgPreBoxInnerCustom").addClass("unset").removeAttr("style");
  528. // const gs = JSON.parse(localStorage.getItem("generalSettings") || {});
  529. // gs['bgPreference'] = 'Default1'
  530. // localStorage.setItem("generalSettings", JSON.stringify(gs))
  531. $("#bgPreBoxInnerDefault1").click();
  532. ls.set("easy_custom_out_url_checked", false)
  533. return
  534. }
  535. const img_url = URL.createObjectURL(pageFile)
  536. if (pageFile.type.startsWith("video/")) {
  537. let videoPosterImg = await indexedAction.get(storeName, videoPosterKey); //是否能获取到视频封面
  538. $("#liveBgBox").css({
  539. display: 'block',
  540. opacity: 1,
  541. transform: "scale(1.1)"
  542. }).attr("src", img_url)
  543. $("#bgBox").css({ opacity: 0 });
  544. if (videoPosterImg) {
  545. $("#bgPreBoxInnerCustom").css({ "background-image": `url(${videoPosterImg})` })
  546.  
  547. }
  548. } else {
  549. $("#liveBgBox").css({
  550. display: 'none',
  551. opacity: 1,
  552. transform: 'scale(1.1)'
  553. })
  554. $("#bgBox").css({
  555. opacity: 1,
  556. transform: "scale(1.1)",
  557. display: "block"
  558. });
  559. $("#bgPreBoxInnerCustom").css({ "background-image": `url(${img_url})` })
  560. $("#bgBox").attr("src", img_url)
  561. }
  562.  
  563. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").off("click")
  564. $("#bgPreBoxInnerCustom, #bgPreviewBoxCustom").click(async () => {
  565. const pageFile = await indexedAction.get(storeName, key)
  566. const img_url = URL.createObjectURL(pageFile)
  567. if (pageFile.type.startsWith("video/")) {
  568. $("#liveBgBox").attr("src", img_url)
  569. } else {
  570. $("#bgBox").attr("src", img_url)
  571. }
  572. })
  573. } else {
  574. if (!local_db) {
  575. Message.info('本地数据库查询失败')
  576. }
  577. if (!url) {
  578. Message.info('无效的链接')
  579. }
  580. }
  581. e.target.checked = false;
  582. }
  583. ls.set("easy_custom_out_url_checked", e.target.checked)
  584. })
  585.  
  586. function initCustomOutSideUrl() {
  587. const checked = ls.get("easy_custom_out_url_checked")
  588. const url = ls.get("easy_custom_out_url")
  589. if (checked) {
  590. $("#outsideImgUrlInput").val(url)
  591. $("#chkOutsideImgUrlBar").prop("checked", checked)
  592. }
  593. $("#btnBrowse").attr("disabled", checked)
  594. }
  595. initCustomOutSideUrl()
  596.  
  597. console.log('start')
  598. })();