// ==UserScript==
// @name Hinatazaka46 Layout change NEWS / SCHEDULE
// @name:ja 日向坂46 レイアウト変更 NEWS / SCHEDULE
// @namespace naoqv.hinatazaka
// @description Change the layout of the "News" and "Schedule" pages on the Hinatazaka46 website
// @description:ja 日向坂46サイト「ニュース」「スケジュール」ページのレイアウト変更
// @version 1.17
// @match https://www.hinatazaka46.com/s/official/news/*
// @match https://www.hinatazaka46.com/s/official/media/*
// @require https://update.greasyfork.org/scripts/510022/1459768/HinatazakaStyleSetting.js
// @require https://update.greasyfork.org/scripts/509934/1480445/HinatazakaExceptionHandlingWrapper.js
// @icon https://cdn.hinatazaka46.com/files/14/hinata/img/favicons/favicon-32x32.png
// @compatible chrome
// @compatible firefox
// @grant none
// @license MIT
// ==/UserScript==
const SCRIPT_NAME = "日向坂46 NEWS / SCHEDULE レイアウト変更";
handleException(()=> {
const PAGE_TYPE_ERROR_MSG = "Processing of out-of-scope pages. Check the settings @match.";
const pageType = (location.href).match(new RegExp('\/(media|news)\/'))[1];
const isDetail = ((location.href).match(new RegExp('\/detail\/')) != null);
const SELECTORS = ((x) => {
switch (x) {
case "news":
return {"pArrow": ".p-news__pager-arrow",
"cArrowLeft": ".c-news_pager-arrow--left",
"cArrowRight" : ".c-news_pager-arrow--right",
"cPageMonth": ".c-news__page_month",
"cPageYear": ".c-news__page_year",
"lMainContentsUl": ".l-maincontents--news ul",
"pDate": ".p-news__page_date",
"lSubContents": ".l-sub-contents--news"};
case "media":
return {"pArrow": ".p-schedule__pager-arrow",
"cArrowLeft": ".c-schedule_pager-arrow--left",
"cArrowRight" : ".c-schedule_pager-arrow--right",
"cPageMonth": ".c-schedule__page_month",
"cPageYear": ".c-schedule__page_year",
"lMainContentsUl": ".l-maincontents--schedule ul",
"pDate": ".p-schedule__page_date",
"lSubContents": ".l-sub-contents--schedule"};
default:
throw new Error(PAGE_TYPE_ERROR_MSG);
}
})(pageType);
const pageYear = ((y) => {return (y === null || y === undefined) ? null : y.innerText;})(document.querySelector(SELECTORS['cPageYear']));
(() => {
if (isDetail) {
return;
}
darkMode();
menuBarSetting();
if (pageYear !== "年") {
//console.log("日向坂46 cal");
const daysOfWeek = ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'Sa'];
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
// 月初
const first = new Date(year, month - 1, 1);
// 月末
const end = new Date(year, month, 0);
// 月末の日
const endDate = end.getDate();
// 前月末
const endPrevMonth = new Date(year, month - 1, 0);
// 前月末の日
const endDatePrevMonth = endPrevMonth.getDate();
// 月初の曜日
const firstDayOfWeek = first.getDay();
let numOfDay = 1;
let calendarHtml = '';
const pageMonth = ((m) => {return m !== null ? m.innerText : "";})(document.querySelector(SELECTORS['cPageMonth']));
const leftArrowHref = document.querySelector(SELECTORS['cArrowLeft']).children[0].href;
const rightArrowHref = document.querySelector(SELECTORS['cArrowRight']).children[0].href;
calendarHtml += '<table class="cale_table" style="width: 210px; margin: -130px 0 20px -50px;">';
calendarHtml += `<tr><td></td><td class="cale_prev"><a id="cale_prev" href="${leftArrowHref}"><</a></td>
<td class="cale_month" colspan="3">${pageYear} ${pageMonth}</td><td class="cale_next"><a href="${rightArrowHref}">></a></td><td></td></tr>`;
calendarHtml += '<tr>';
for (let i = 0; i < daysOfWeek.length; i++) {
calendarHtml += '<td class="cale_wek' + i + '">' + daysOfWeek[i] + '</td>';
}
calendarHtml += '</tr>';
for (let w = 0; w < 6; w++) {
calendarHtml += '<tr>'
for (let d = 0; d < 7; d++) {
if (w == 0 && d < firstDayOfWeek) {
// 前月
let num = endDatePrevMonth - firstDayOfWeek + d + 1;
calendarHtml += '<td class="cale_day' + d + ' is-disabled">' + num + '</td>';
} else if (numOfDay > endDate) {
// 次月
let num = numOfDay - endDate;
calendarHtml += '<td class="cale_day' + d + ' is-disabled">' + num + '</td>';
numOfDay++;
w = 99; // カレンダーの最下端が次月の日付のみになるのを防止
} else {
calendarHtml += '<td class="cale_day' + d + '">' + numOfDay + '</td>';
numOfDay++;
}
}
calendarHtml += '</tr>'
}
calendarHtml += '</table>';
document.querySelector(SELECTORS["lSubContents"]).insertAdjacentHTML('afterbegin', calendarHtml);
}
})();
(() => {
//console.log("日向坂46 NEWS / スケジュール");
// 選択カテゴリ(ALL / 握手会・・・)
const categorySelectorSuffix
= ((c) => {
let value = "";
if (c.length == 0) {
value = "all";
} else {
const tempValue = c[0].value;
switch (tempValue) {
case "birthday":
value = "birth";
break;
case "fanclub":
value = "fanclubonly";
break;
default:
value = tempValue;
}
}
return value;
})(document.getElementsByName("cd"));
const categoryElem = document.querySelector('.c-button-category.category_' + categorySelectorSuffix);
const categoryStyles = window.getComputedStyle(categoryElem);
const categoryParent = categoryElem.parentElement;
categoryParent.style.marginLeft = "-5px";
categoryParent.style.paddingLeft = "4.5px";
categoryParent.style.marginRight = "40px";
categoryElem.style.color = `rgb(from ${categoryStyles.color} calc(r - 64) calc(g - 64) calc(b - 64))`;
categoryParent.style.backgroundColor = `rgb(from ${categoryStyles.color} calc(r + 64) calc(g + 64) calc(b + 64))`;
categoryParent.style.border = `solid 0.5px ${categoryElem.style.color}`;
const now = new Date();
const nowYearMonth = String(now.getFullYear()) + String(now.getMonth() + 1).padStart( 2, '0');
// 詳細ページの場合 処理を終了
if (isDetail) {
return;
}
/*
* フルブラウザ上ではNEWS/スケジュールが多い月は見づらいため
* 自動スクロール、表示色を追加設定
*/
const HOVER_CL = "#ddffff";
const HOVER_BG_CL_UPPER = "#20cccc";
const HOVER_BG_CL_LOWER = "#202040";
const PAST_BG_CL = "#303040";
const TODAY_DATE_CL = "orange";
const TODAY_BG_CL_UPPER = "#30aaaa";
const TODAY_BG_CL_LOWER = "#303050";
const TODAY_BORDER_CL_UPPER = "#5bbee5";
const TODAY_BORDER_CL_LOWER = "#d7eeff";
const PAGER_MARGIN_TOP = 20;
const styleElem = document.createElement("style");
styleElem.setAttribute("rel", "stylesheet");
let styleText = `
.is-disabled {
color: silver;
}
.p-page-head {
padding-top: 20px;
}
.l-container {
color: ${DEFAULT_CL};
background-color: #202050;
}
.c-pager__item a svg {
fill: #7ab6db;
}
.module-modal.js-member-filter .mordal-box .member-box ul li p.check input[type=checkbox]:checked+label:before {
background-color:#6bcaea;
border:1px solid #6bcaea;
}`;
switch (pageType) {
case "news":
styleText += `
.p-news__list {background-color: ${DEFAULT_BG_CL};}
.p-news__item:hover {
background: linear-gradient(${HOVER_BG_CL_UPPER}, 20%, ${HOVER_BG_CL_LOWER});
outline: 1px solid ${TODAY_BORDER_CL_UPPER}; outline-offset: ipx;
}
.p-news__item:hover .c-news__date, .p-news__item:hover .c-news__text, .p-news__item:hover .c-news__time--list {
color: ${HOVER_CL};
}`;
break;
case "media":
styleText += `
.p-schedule__item:hover {
background: linear-gradient(${HOVER_BG_CL_UPPER}, 20%, ${HOVER_BG_CL_LOWER});
outline: 1px solid ${TODAY_BORDER_CL_UPPER}; outline-offset: ipx;
}
.p-schedule__item:hover .c-schedule__text, .p-schedule__item:hover .c-schedule__time--list {
color: ${HOVER_CL};
}
.schedule__list-pastday {background-color: ${PAST_BG_CL};}
.schedule__date-today {color: ${TODAY_DATE_CL};}
.schedule__list-today {background: linear-gradient(${TODAY_BG_CL_UPPER}, 10%, ${TODAY_BG_CL_LOWER}); border: 2px solid;
border-image: linear-gradient(to bottom, ${TODAY_BORDER_CL_UPPER}, ${TODAY_BORDER_CL_LOWER}) 1;}
.schedule__list-future {background-color: ${DEFAULT_BG_CL};}
`;
break;
default:
throw new Error(PAGE_TYPE_ERROR_MSG);
}
styleElem.textContent = styleText;
document.head.appendChild(styleElem);
// リスト上方 "xxxx年 yy月" 行
const pDate = document.querySelector(SELECTORS["pDate"]);
// "xxxx年" ではなく "年"のみの場合
if (pageYear === "年") {
const cPageYear = document.querySelector(SELECTORS["cPageYear"]);
cPageYear.innerText = String(now.getFullYear()) + "年";
cPageYear.style.fontSize = "4.8rem";
document.querySelector(SELECTORS["cPageMonth"]).remove();
document.querySelector(SELECTORS["pArrow"]).remove();
}
pDate.style.marginBottom = "5px";
// ニュース/スケジュール リスト
const lMainContentsUl = document.querySelector(SELECTORS["lMainContentsUl"]);
const lMainContentsUlTop = lMainContentsUl.getBoundingClientRect().top;
const pDateHeight = pDate.offsetHeight;
// リスト下方 前月/次月ページャ
const pPager = document.querySelector(".p-pager");
// "xxxx年" ではなく "年"のみの場合
if (pageYear === "年") {
pPager.innerText = "";
pPager.style.marginTop = "0px";
} else {
pPager.style.marginTop = `${PAGER_MARGIN_TOP}px`;
}
const pPagerHeight = PAGER_MARGIN_TOP + pPager.offsetHeight;
const lMainContentsUlHeight = document.documentElement.clientHeight - pDateHeight - pPagerHeight;
// スクロール表示
lMainContentsUl.setAttribute("style", `height:${lMainContentsUlHeight}px; overflow: scroll; border: solid 1px #32a1ce;`);
const scrollTop = lMainContentsUlTop - pDateHeight;
// スクロール位置リセット 〜「再読み込み」ボタン押下時の位置ズレ対応
scrollTo(0, 0);
// リスト位置までページ内で縦スクロール
scrollTo({
top: scrollTop,
behavior: "smooth"
});
const dispYear = document.querySelector(SELECTORS['cPageYear']);
const dispMonth = document.querySelector(SELECTORS['cPageMonth']);
// 表示対象の年月(ex.202404)を取得。設定がなければ当月
const dispYearMonth
= ((y, m) => {return (y == null || m == null) ? nowYearMonth : y.innerText.replace('年', '') + m.innerText.replace('月', '')})(dispYear, dispMonth);
// NEWS/スケジュールが当月以前の月の場合
if (dispYearMonth < nowYearMonth) {
lMainContentsUl.style.background = `${PAST_BG_CL}`;
}
const DELTA = 2;
const createAnchor
= (y, d) => `<a href="javascript:document.querySelector('${SELECTORS['lMainContentsUl']}').scroll({top:${y}, behavior: 'smooth'});">${d}</a>`;
switch(pageType) {
case "news":
const newsList = Array.prototype.map.call(document.getElementsByClassName("c-news__date"),
(x) => [parseInt(x.innerText.match(new RegExp(/\d{4}\.\d{2}\.(\d{2})/))[1]), x.getBoundingClientRect().top] );
const dayMap = new Map();
Array.prototype.forEach.call(newsList, (x) => {
if (! dayMap.has(x[0]) || x[1] < dayMap.get(x[0])) {
// Map(day, top)
dayMap.set(x[0], x[1]);
}
});
Map.prototype.forEach.call(dayMap, (top, day) => {
Array.prototype.some.call(document.querySelectorAll("table.cale_table tbody tr td"), (td) => {
if (!td.classList.contains("is-disabled") && day === parseInt(td.innerText)) {
td.innerHTML = createAnchor((top - lMainContentsUlTop - DELTA), day);
return true;
}
});
});
break;
case "media":
const today = now.getDate();
lMainContentsUl.scroll(0, 0);
let isScrolled = false;
Array.prototype.forEach.call(document.getElementsByClassName("c-schedule__date--list"), (dayElem) => {
// 日付(innerText)の文字列 '(日付)\n(曜日)' から日付を抽出
let day = ((x) => {return parseInt(x.substr(0, x.indexOf(`\n`)));})(dayElem.innerText);
Array.prototype.some.call(document.querySelectorAll("table.cale_table tbody tr td"), (td) => {
if ( !td.classList.contains("is-disabled") && day === parseInt(td.innerText)) {
td.innerHTML = createAnchor((dayElem.getBoundingClientRect().top - lMainContentsUlTop - DELTA), day);
return true;
}
});
// 表示スケジュールが当月の場合
if (dispYearMonth === nowYearMonth) {
// 過去日
if (day < today) {
dayElem.parentElement.classList.add("schedule__list-pastday");
// 「今日」(ページを表示した日付)
} else if (day === today) {
dayElem.classList.add("schedule__date-today");
dayElem.parentElement.classList.add("schedule__list-today");
}
if (day >= today) {
dayElem.parentElement.classList.add("schedule__list-future");
if (!isScrolled) {
// 「今日」以降(「今日」を含めて)で最早のスケジュール日要素にスクロール
lMainContentsUl.scroll({
top: dayElem.getBoundingClientRect().top - lMainContentsUlTop - DELTA,
behavior: "smooth"
});
isScrolled = true;
}
}
}
});
break;
default:
throw new Error(PAGE_TYPE_ERROR_MSG);
}
})();
}, SCRIPT_NAME);