Hinatazaka46 Layout change NEWS / SCHEDULE

Change the layout of the "News" and "Schedule" pages on the Hinatazaka46 website

目前為 2024-09-25 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Hinatazaka46 Layout change NEWS / SCHEDULE
  3. // @name:ja 日向坂46 レイアウト変更 NEWS / SCHEDULE
  4. // @namespace naoqv.hinatazaka
  5. // @description Change the layout of the "News" and "Schedule" pages on the Hinatazaka46 website
  6. // @description:ja 日向坂46サイト「ニュース」「スケジュール」ページのレイアウト変更
  7. // @version 1.15
  8. // @match https://www.hinatazaka46.com/s/official/news/*
  9. // @match https://www.hinatazaka46.com/s/official/media/*
  10. // @require https://update.gf.qytechs.cn/scripts/510022/1453515/HinatazakaStyleSetting.js
  11. // @require https://update.gf.qytechs.cn/scripts/509934/1453163/HinatazakaExceptionHandlingWrapper.js
  12. // @icon https://cdn.hinatazaka46.com/files/14/hinata/img/favicons/favicon-32x32.png
  13. // @compatible chrome
  14. // @compatible firefox
  15. // @grant none
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. const SCRIPT_NAME = "日向坂46 NEWS / SCHEDULE レイアウト変更";
  20.  
  21. handleException(()=> {
  22.  
  23. const PAGE_TYPE_ERROR_MSG = "Processing of out-of-scope pages. Check the settings @match.";
  24.  
  25. const pageType = (location.href).match(new RegExp('\/(media|news)\/'))[1];
  26. const isDetail = ((location.href).match(new RegExp('\/detail\/')) != null);
  27.  
  28. const SELECTORS = ((x) => {
  29. switch (x) {
  30. case "news":
  31. return {"pArrow": ".p-news__pager-arrow",
  32. "cArrowLeft": ".c-news_pager-arrow--left",
  33. "cArrowRight" : ".c-news_pager-arrow--right",
  34. "cPageMonth": ".c-news__page_month",
  35. "cPageYear": ".c-news__page_year",
  36. "lMainContentsUl": ".l-maincontents--news ul",
  37. "pDate": ".p-news__page_date",
  38. "lSubContents": ".l-sub-contents--news"};
  39. case "media":
  40. return {"pArrow": ".p-schedule__pager-arrow",
  41. "cArrowLeft": ".c-schedule_pager-arrow--left",
  42. "cArrowRight" : ".c-schedule_pager-arrow--right",
  43. "cPageMonth": ".c-schedule__page_month",
  44. "cPageYear": ".c-schedule__page_year",
  45. "lMainContentsUl": ".l-maincontents--schedule ul",
  46. "pDate": ".p-schedule__page_date",
  47. "lSubContents": ".l-sub-contents--schedule"};
  48. default:
  49. throw new Error(PAGE_TYPE_ERROR_MSG);
  50. }
  51. })(pageType);
  52.  
  53. const pageYear = ((y) => {return (y === null || y === undefined) ? null : y.innerText;})(document.querySelector(SELECTORS['cPageYear']));
  54.  
  55. (() => {
  56.  
  57. if (isDetail) {
  58. return;
  59. }
  60.  
  61. darkMode();
  62. menuBarSetting();
  63.  
  64. if (pageYear !== "年") {
  65.  
  66. //console.log("日向坂46 cal");
  67.  
  68. const daysOfWeek = ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'Sa'];
  69. const now = new Date();
  70. const year = now.getFullYear();
  71. const month = now.getMonth() + 1;
  72. // 月初
  73. const first = new Date(year, month - 1, 1);
  74. // 月末
  75. const end = new Date(year, month, 0);
  76. // 月末の日
  77. const endDate = end.getDate();
  78. // 前月末
  79. const endPrevMonth = new Date(year, month - 1, 0);
  80. // 前月末の日
  81. const endDatePrevMonth = endPrevMonth.getDate();
  82. // 月初の曜日
  83. const firstDayOfWeek = first.getDay();
  84.  
  85. let numOfDay = 1;
  86. let calendarHtml = '';
  87.  
  88. const pageMonth = ((m) => {return m !== null ? m.innerText : "";})(document.querySelector(SELECTORS['cPageMonth']));
  89.  
  90. const leftArrowHref = document.querySelector(SELECTORS['cArrowLeft']).children[0].href;
  91. const rightArrowHref = document.querySelector(SELECTORS['cArrowRight']).children[0].href;
  92.  
  93. calendarHtml += '<table class="cale_table" style="width: 210px; margin: -130px 0 20px -50px;">';
  94. calendarHtml += `<tr><td></td><td class="cale_prev"><a id="cale_prev" href="${leftArrowHref}"><</a></td>
  95. <td class="cale_month" colspan="3">${pageYear}&#160;${pageMonth}</td><td class="cale_next"><a href="${rightArrowHref}">></a></td><td></td></tr>`;
  96.  
  97. calendarHtml += '<tr>';
  98.  
  99. for (let i = 0; i < daysOfWeek.length; i++) {
  100. calendarHtml += '<td class="cale_wek' + i + '">' + daysOfWeek[i] + '</td>';
  101. }
  102.  
  103. calendarHtml += '</tr>';
  104.  
  105. for (let w = 0; w < 6; w++) {
  106. calendarHtml += '<tr>'
  107.  
  108. for (let d = 0; d < 7; d++) {
  109. if (w == 0 && d < firstDayOfWeek) {
  110. // 前月
  111. let num = endDatePrevMonth - firstDayOfWeek + d + 1;
  112. calendarHtml += '<td class="cale_day' + d + ' is-disabled">' + num + '</td>';
  113. } else if (numOfDay > endDate) {
  114. // 次月
  115. let num = numOfDay - endDate;
  116. calendarHtml += '<td class="cale_day' + d + ' is-disabled">' + num + '</td>';
  117. numOfDay++;
  118. w = 99; // カレンダーの最下端が次月の日付のみになるのを防止
  119. } else {
  120. calendarHtml += '<td class="cale_day' + d + '">' + numOfDay + '</td>';
  121. numOfDay++;
  122. }
  123. }
  124. calendarHtml += '</tr>'
  125. }
  126.  
  127. calendarHtml += '</table>';
  128.  
  129. document.querySelector(SELECTORS["lSubContents"]).insertAdjacentHTML('afterbegin', calendarHtml);
  130. }
  131. })();
  132.  
  133. (() => {
  134. //console.log("日向坂46 NEWS / スケジュール");
  135.  
  136. // 選択カテゴリ(ALL / 握手会・・・)
  137. const categorySelectorSuffix
  138. = ((c) => {
  139. let value = "";
  140. if (c.length == 0) {
  141. value = "all";
  142. } else {
  143. const tempValue = c[0].value;
  144. switch (tempValue) {
  145. case "birthday":
  146. value = "birth";
  147. break;
  148. case "fanclub":
  149. value = "fanclubonly";
  150. break;
  151. default:
  152. value = tempValue;
  153. }
  154. }
  155.  
  156. return value;
  157. })(document.getElementsByName("cd"));
  158.  
  159. const categoryElem = document.querySelector('.c-button-category.category_' + categorySelectorSuffix);
  160. const categoryStyles = window.getComputedStyle(categoryElem);
  161. const categoryParent = categoryElem.parentElement;
  162. categoryParent.style.marginLeft = "-5px";
  163. categoryParent.style.paddingLeft = "4.5px";
  164. categoryParent.style.marginRight = "40px";
  165. categoryElem.style.color = `rgb(from ${categoryStyles.color} calc(r - 64) calc(g - 64) calc(b - 64))`;
  166. categoryParent.style.backgroundColor = `rgb(from ${categoryStyles.color} calc(r + 64) calc(g + 64) calc(b + 64))`;
  167. categoryParent.style.border = `solid 0.5px ${categoryElem.style.color}`;
  168.  
  169. const now = new Date();
  170. const nowYearMonth = String(now.getFullYear()) + String(now.getMonth() + 1).padStart( 2, '0');
  171.  
  172. // 詳細ページの場合 処理を終了 
  173. if (isDetail) {
  174. return;
  175. }
  176.  
  177. /*
  178. * フルブラウザ上ではNEWS/スケジュールが多い月は見づらいため
  179. * 自動スクロール、表示色を追加設定
  180. */
  181.  
  182. const HOVER_CL = "#ddffff";
  183. const HOVER_BG_CL_UPPER = "#20cccc";
  184. const HOVER_BG_CL_LOWER = "#202040";
  185. const PAST_BG_CL = "#303040";
  186. const TODAY_DATE_CL = "orange";
  187. const TODAY_BG_CL_UPPER = "#30aaaa";
  188. const TODAY_BG_CL_LOWER = "#303050";
  189. const TODAY_BORDER_CL_UPPER = "#5bbee5";
  190. const TODAY_BORDER_CL_LOWER = "#d7eeff";
  191. const PAGER_MARGIN_TOP = 20;
  192.  
  193. const styleElem = document.createElement("style");
  194. styleElem.setAttribute("rel", "stylesheet");
  195.  
  196. let styleText = `
  197. .is-disabled {
  198. color: silver;
  199. }
  200. .p-page-head {
  201. padding-top: 20px;
  202. }
  203. .l-container {
  204. color: ${DEFAULT_CL};
  205. background-color: #202050;
  206. }
  207. .c-pager__item a svg {
  208. fill: #7ab6db;
  209. }
  210. .module-modal.js-member-filter .mordal-box .member-box ul li p.check input[type=checkbox]:checked+label:before {
  211. background-color:#6bcaea;
  212. border:1px solid #6bcaea;
  213. }`;
  214.  
  215. switch (pageType) {
  216. case "news":
  217. styleText += `
  218. .p-news__list {background-color: ${DEFAULT_BG_CL};}
  219. .p-news__item:hover {
  220. background: linear-gradient(${HOVER_BG_CL_UPPER}, 20%, ${HOVER_BG_CL_LOWER});
  221. outline: 1px solid ${TODAY_BORDER_CL_UPPER}; outline-offset: ipx;
  222. }
  223. .p-news__item:hover .c-news__date, .p-news__item:hover .c-news__text, .p-news__item:hover .c-news__time--list {
  224. color: ${HOVER_CL};
  225. }`;
  226. break;
  227. case "media":
  228. styleText += `
  229. .p-schedule__item:hover {
  230. background: linear-gradient(${HOVER_BG_CL_UPPER}, 20%, ${HOVER_BG_CL_LOWER});
  231. outline: 1px solid ${TODAY_BORDER_CL_UPPER}; outline-offset: ipx;
  232. }
  233. .p-schedule__item:hover .c-schedule__text, .p-schedule__item:hover .c-schedule__time--list {
  234. color: ${HOVER_CL};
  235. }
  236. .schedule__list-pastday {background-color: ${PAST_BG_CL};}
  237. .schedule__date-today {color: ${TODAY_DATE_CL};}
  238. .schedule__list-today {background: linear-gradient(${TODAY_BG_CL_UPPER}, 10%, ${TODAY_BG_CL_LOWER}); border: 2px solid;
  239. border-image: linear-gradient(to bottom, ${TODAY_BORDER_CL_UPPER}, ${TODAY_BORDER_CL_LOWER}) 1;}
  240. .schedule__list-future {background-color: ${DEFAULT_BG_CL};}
  241. `;
  242. break;
  243. default:
  244. throw new Error(PAGE_TYPE_ERROR_MSG);
  245. }
  246.  
  247. styleElem.textContent = styleText;
  248.  
  249. document.head.appendChild(styleElem);
  250.  
  251. // リスト上方 "xxxx年 yy月" 行
  252. const pDate = document.querySelector(SELECTORS["pDate"]);
  253. // "xxxx年" ではなく "年"のみの場合
  254. if (pageYear === "年") {
  255. const cPageYear = document.querySelector(SELECTORS["cPageYear"]);
  256. cPageYear.innerText = String(now.getFullYear()) + "年";
  257. cPageYear.style.fontSize = "4.8rem";
  258. document.querySelector(SELECTORS["cPageMonth"]).remove();
  259. document.querySelector(SELECTORS["pArrow"]).remove();
  260. }
  261.  
  262. pDate.style.marginBottom = "5px";
  263.  
  264. // ニュース/スケジュール リスト
  265. const lMainContentsUl = document.querySelector(SELECTORS["lMainContentsUl"]);
  266.  
  267. const lMainContentsUlTop = lMainContentsUl.getBoundingClientRect().top;
  268.  
  269. const pDateHeight = pDate.offsetHeight;
  270.  
  271. // リスト下方 前月/次月ページャ
  272. const pPager = document.querySelector(".p-pager");
  273.  
  274. // "xxxx年" ではなく "年"のみの場合
  275. if (pageYear === "年") {
  276. pPager.innerText = "";
  277. pPager.style.marginTop = "0px";
  278. } else {
  279. pPager.style.marginTop = `${PAGER_MARGIN_TOP}px`;
  280. }
  281.  
  282. const pPagerHeight = PAGER_MARGIN_TOP + pPager.offsetHeight;
  283. const lMainContentsUlHeight = document.documentElement.clientHeight - pDateHeight - pPagerHeight;
  284.  
  285. // スクロール表示
  286. lMainContentsUl.setAttribute("style", `height:${lMainContentsUlHeight}px; overflow: scroll; border: solid 1px #32a1ce;`);
  287.  
  288. const scrollTop = lMainContentsUlTop - pDateHeight;
  289.  
  290. // スクロール位置リセット 〜「再読み込み」ボタン押下時の位置ズレ対応
  291. scrollTo(0, 0);
  292.  
  293. // リスト位置までページ内で縦スクロール
  294. scrollTo({
  295. top: scrollTop,
  296. behavior: "smooth"
  297. });
  298. const dispYear = document.querySelector(SELECTORS['cPageYear']);
  299. const dispMonth = document.querySelector(SELECTORS['cPageMonth']);
  300.  
  301. // 表示対象の年月(ex.202404)を取得。設定がなければ当月
  302. const dispYearMonth
  303. = ((y, m) => {return (y == null || m == null) ? nowYearMonth : y.innerText.replace('年', '') + m.innerText.replace('月', '')})(dispYear, dispMonth);
  304. // NEWS/スケジュールが当月以前の月の場合
  305. if (dispYearMonth < nowYearMonth) {
  306.  
  307. lMainContentsUl.style.background = `${PAST_BG_CL}`;
  308. }
  309.  
  310. const DELTA = 2;
  311. const createAnchor
  312. = (y, d) => `<a href="javascript:document.querySelector('${SELECTORS['lMainContentsUl']}').scroll({top:${y}, behavior: 'smooth'});">${d}</a>`;
  313.  
  314. switch(pageType) {
  315.  
  316. case "news":
  317.  
  318. const newsList = Array.prototype.map.call(document.getElementsByClassName("c-news__date"),
  319. (x) => [parseInt(x.innerText.match(new RegExp(/\d{4}\.\d{2}\.(\d{2})/))[1]), x.getBoundingClientRect().top] );
  320.  
  321. const dayMap = new Map();
  322.  
  323. Array.prototype.forEach.call(newsList, (x) => {
  324.  
  325. if (! dayMap.has(x[0]) || x[1] < dayMap.get(x[0])) {
  326. // Map(day, top)
  327. dayMap.set(x[0], x[1]);
  328. }
  329. });
  330.  
  331. Map.prototype.forEach.call(dayMap, (top, day) => {
  332. Array.prototype.some.call(document.querySelectorAll("table.cale_table tbody tr td"), (td) => {
  333.  
  334. if (!td.classList.contains("is-disabled") && day === parseInt(td.innerText)) {
  335.  
  336. td.innerHTML = createAnchor((top - lMainContentsUlTop - DELTA), day);
  337.  
  338. return true;
  339. }
  340. });
  341. });
  342.  
  343. break;
  344.  
  345. case "media":
  346.  
  347. const today = now.getDate();
  348.  
  349. lMainContentsUl.scroll(0, 0);
  350.  
  351. let isScrolled = false;
  352.  
  353. Array.prototype.forEach.call(document.getElementsByClassName("c-schedule__date--list"), (dayElem) => {
  354.  
  355. // 日付(innerText)の文字列 '(日付)\n(曜日)' から日付を抽出
  356. let day = ((x) => {return parseInt(x.substr(0, x.indexOf(`\n`)));})(dayElem.innerText);
  357.  
  358. Array.prototype.some.call(document.querySelectorAll("table.cale_table tbody tr td"), (td) => {
  359.  
  360. if ( !td.classList.contains("is-disabled") && day === parseInt(td.innerText)) {
  361.  
  362. td.innerHTML = createAnchor((dayElem.getBoundingClientRect().top - lMainContentsUlTop - DELTA), day);
  363.  
  364. return true;
  365. }
  366. });
  367.  
  368. // 表示スケジュールが当月の場合
  369. if (dispYearMonth === nowYearMonth) {
  370. // 過去日
  371. if (day < today) {
  372.  
  373. dayElem.parentElement.classList.add("schedule__list-pastday");
  374.  
  375. // 「今日」(ページを表示した日付)
  376. } else if (day === today) {
  377.  
  378. dayElem.classList.add("schedule__date-today");
  379. dayElem.parentElement.classList.add("schedule__list-today");
  380. }
  381.  
  382. if (day >= today) {
  383.  
  384. dayElem.parentElement.classList.add("schedule__list-future");
  385.  
  386. if (!isScrolled) {
  387. // 「今日」以降(「今日」を含めて)で最早のスケジュール日要素にスクロール
  388. lMainContentsUl.scroll({
  389.  
  390. top: dayElem.getBoundingClientRect().top - lMainContentsUlTop - DELTA,
  391. behavior: "smooth"
  392. });
  393.  
  394. isScrolled = true;
  395. }
  396. }
  397. }
  398. });
  399.  
  400. break;
  401.  
  402. default:
  403. throw new Error(PAGE_TYPE_ERROR_MSG);
  404. }
  405. })();
  406. }, SCRIPT_NAME);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址