// ==UserScript==
// @name みりほー補助ツール
// @namespace
// @version 4.1.0
// @description タイムラインの時刻表示をミリ秒にする & ツイートのURLを収集するスクリプト
// @author ZERO
// @match https://twitter.com/*
// @match https://x.com/*
// @match https://mobile.twitter.com/*
// @match https://mobile.x.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @connect spark.littlestar.jp
// @supportURL https://spark.littlestar.jp/public/Ver4_Add_Milliseconds_To_Twitter_TL.html
// ==/UserScript==
(function() {
'use strict';
// バージョン情報の変数(この値は適切に設定してください)
let currentVersion = '4.1.0';
let isDarkMode = false;
let Ret_detectMode = "";
function detectMode() {
// 背景色を取得する要素の選択(Twitterの構造に応じて調整が必要)
var bgColor = window.getComputedStyle(document.body).backgroundColor;
// RGB値を分解し、数値に変換
var colors = bgColor.match(/\d+/g).map(Number);
// 背景色がRGBで明るいか暗いかを判定する(単純化された方法)
var brightness = Math.round(((colors[0] * 299) + (colors[1] * 587) + (colors[2] * 114)) / 1000);
if (brightness <= 128) {
//alert("ダークモードです。"); // 暗い背景
return "dark";
} else {
//alert("通常モード(ライトモード)です。"); // 明るい背景
return "light";
}
}
function replaceDomain(url) {
return url.replace('twitter.com', 'x.com');
}
function replaceDomainInText(text) {
// 各行を分割して配列にする
const urls = text.split('\n');
// 各URLに対してreplaceDomain関数を適用する
const replacedUrls = urls.map(url => replaceDomain(url));
// 改行で結合して結果のテキストを返す
return replacedUrls.join('\n');
}
function apply3DEffect(element) {
if (!element) return;
// エレメントのスタイルを設定
element.style.fontFamily = 'Arial, sans-serif';
element.style.fontSize = '12px';
element.style.fontWeight = 'bold';
element.style.color = '#fff';
element.style.position = 'relative';
element.style.display = 'inline-block';
element.style.textTransform = 'uppercase';
element.style.margin = '12px';
// 擬似要素のスタイルを設定
const pseudoElementStyle = document.createElement('style');
pseudoElementStyle.textContent = `
#${element.id}::before {
content: "${element.textContent}";
position: absolute;
top: 1px;
left: 1px;
color: #aaa;
z-index: -1;
text-shadow: -0.5px -0.5px 0 #555, 0.5px -0.5px 0 #555, -0.5px 0.5px 0 #555, 0.5px 0.5px 0 #555;
}
`;
document.head.appendChild(pseudoElementStyle);
}
// document.addEventListener('DOMContentLoaded', function () {
// if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// // // ダークモードの場合のスタイルを適用
// // document.body.style.backgroundColor = 'black';
// // document.body.style.color = 'white';
// isDarkMode = true;
// } else {
// // // ライトモードの場合のスタイルを適用
// // document.body.style.backgroundColor = 'white';
// // document.body.style.color = 'black';
// isDarkMode = false;
// }
// // ダークモードとライトモードの切り替えを監視
// window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
// if (event.matches) {
// // // ダークモードに変更されたときのスタイルを適用
// // document.body.style.backgroundColor = 'black';
// // document.body.style.color = 'white';
// isDarkMode = true;
// } else {
// // // ライトモードに変更されたときのスタイルを適用
// // document.body.style.backgroundColor = 'white';
// // document.body.style.color = 'black';
// isDarkMode = false;
// }
// });
// });
let WEEK_LABEL = ['日', '月', '火', '水', '木', '金', '土'];
let All_URLs = new Set(); // すべてのツイートのURLを保存するセット
let filteredURLs = []; // 空の配列で初期化
//let alertFLG = true;
let toFormatedDateString = function(date, tweetID) {
// yyyy/mm/dd (aaa) HH:MM
try {
let ret = "";
// 大きなフォントサイズで時間を表示
//ret += '';
ret += date.getFullYear();
ret += '/';
ret += ('0' + (date.getMonth() + 1)).slice(-2);
ret += '/';
ret += ('0' + date.getDate()).slice(-2);
ret += ' (';
ret += WEEK_LABEL[(((date.getDay() % WEEK_LABEL.length) + WEEK_LABEL.length) % WEEK_LABEL.length)];
ret += ') ';
ret += ('0' + date.getHours()).slice(-2);
ret += ':';
ret += ('0' + date.getMinutes()).slice(-2);
ret += ':';
ret += ('0' + date.getSeconds()).slice(-2);
ret += '.';
ret += tweetID_to_MS_Func(tweetID);
//ret += ''; // spanタグを閉じる
return ret;
} catch (error) {
console.error('Error in toFormatedDateString:', error);
throw error; // スクリプトの実行を停止
}
}
function LastLine_tweet_URL_to_ID_Func(line) {
var Tweet_ID = "";
if (line) {
let matches = line.match(/\/status\/(\d+)/);
if (matches && matches.length > 1) {
Tweet_ID = matches[1];
}
}
if (Tweet_ID.includes("?")) {
Tweet_ID = Tweet_ID.substring(0, Tweet_ID.indexOf("?"));
}
var Tweet_User_Name = line;
var startIDX = 0;
if (Tweet_User_Name.includes("mobile.twitter.com")) {
startIDX = Tweet_User_Name.indexOf("/mobile.twitter.com/") + "/mobile.twitter.com/".length;
}else if (Tweet_User_Name.includes("mobile.x.com")) {
startIDX = Tweet_User_Name.indexOf("/mobile.x.com/") + "/mobile.x.com/".length;
}else if(Tweet_User_Name.includes("twitter.com")) {
startIDX = Tweet_User_Name.indexOf("/twitter.com/") + "/twitter.com/".length;
}else if(Tweet_User_Name.includes("x.com")) {
startIDX = Tweet_User_Name.indexOf("/x.com/") + "/x.com/".length;
}
var endIDX = Tweet_User_Name.indexOf("/", startIDX);
Tweet_User_Name = "@" + Tweet_User_Name.substring(startIDX, endIDX);
return LastLine_tweet_id2time(Tweet_User_Name, Tweet_ID, line);
}
function LastLine_tweet_id2time(tweet_user_name, Tweet_ID, line) {
var tweet_time = Math.floor(Tweet_ID / 4194304) + 1288834974657;
var ms = tweet_time % 1000;
if (ms < 10) {
ms = "00" + ms;
} else if (ms < 100) {
ms = "0" + ms;
}
var date = new Date(tweet_time);
var FullDate = dateToStr24HPad0(date, 'YYYY/MM/DD HH:mm:ss') + "." + ms;
var FullDate_002 = toFormatedDateString(date, Tweet_ID);
var strReply = line + "\t" + tweet_user_name + " ";
if (IsResult_dateToStr(FullDate) == "超〇"){
strReply += "" + FullDate_002 + " ◎";
}else if (IsResult_dateToStr(FullDate).includes("〇") == true){
strReply += "" + FullDate_002 + " 〇";
}else if (IsResult_dateToStr(FullDate) == "勇敢X"){
strReply += "" + FullDate_002 + " X";
}else if (IsResult_dateToStr(FullDate).includes("X") == true){
strReply += "" + FullDate_002 + " X";
}
return strReply;
}
function LastLine_tweet_URL_to_ID_Func_Lite(line) {
var Tweet_ID = "";
if (line) {
let matches = line.match(/\/status\/(\d+)/);
if (matches && matches.length > 1) {
Tweet_ID = matches[1];
}
}
if (Tweet_ID.includes("?")) {
Tweet_ID = Tweet_ID.substring(0, Tweet_ID.indexOf("?"));
}
var Tweet_User_Name = line;
var startIDX = 0;
if (Tweet_User_Name.includes("mobile.twitter.com")) {
startIDX = Tweet_User_Name.indexOf("/mobile.twitter.com/") + "/mobile.twitter.com/".length;
}else if (Tweet_User_Name.includes("mobile.x.com")) {
startIDX = Tweet_User_Name.indexOf("/mobile.x.com/") + "/mobile.x.com/".length;
}else if(Tweet_User_Name.includes("twitter.com")) {
startIDX = Tweet_User_Name.indexOf("/twitter.com/") + "/twitter.com/".length;
}else if(Tweet_User_Name.includes("x.com")) {
startIDX = Tweet_User_Name.indexOf("/x.com/") + "/x.com/".length;
}
var endIDX = Tweet_User_Name.indexOf("/", startIDX);
Tweet_User_Name = "@" + Tweet_User_Name.substring(startIDX, endIDX);
return LastLine_tweet_id2time_Lite(Tweet_User_Name, Tweet_ID, line);
}
function LastLine_tweet_id2time_Lite(tweet_user_name, Tweet_ID, line) {
var tweet_time = Math.floor(Tweet_ID / 4194304) + 1288834974657;
var ms = tweet_time % 1000;
if (ms < 10) {
ms = "00" + ms;
} else if (ms < 100) {
ms = "0" + ms;
}
var date = new Date(tweet_time);
var FullDate = dateToStr24HPad0(date, 'YYYY/MM/DD HH:mm:ss') + "." + ms;
var FullDate_002 = toFormatedDateString(date, Tweet_ID);
var strReply = line + "\t" + tweet_user_name + " ";
if (IsResult_dateToStr(FullDate) == "超〇"){
strReply = "超〇";
}else if (IsResult_dateToStr(FullDate).includes("〇") == true){
strReply = "〇";
}else if (IsResult_dateToStr(FullDate) == "勇敢X"){
strReply = "勇敢X";
}else if (IsResult_dateToStr(FullDate).includes("X") == true){
strReply = "X";
}
return strReply;
}
function IsResult_dateToStr(timeString) {
try {
var date = new Date(timeString); // 現在の日時を取得
var format = 'YYYY/MM/DD HH:mm:ss.SSS';
format = format.replace(/YYYY/g, date.getFullYear());
format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
format = format.replace(/DD/g, ('0' + date.getDate()).slice(-2));
format = format.replace(/HH/g, ('0' + date.getHours()).slice(-2));
format = format.replace(/mm/g, ('0' + date.getMinutes()).slice(-2));
format = format.replace(/ss/g, ('0' + date.getSeconds()).slice(-2));
format = format.replace(/SSS/g, ('00' + date.getMilliseconds()).slice(-3));
var IsResult_Str = "";
if (date.getSeconds() === 0) {
IsResult_Str = "〇";
} else {
IsResult_Str = "X";
}
if (date.getHours() === 22 && date.getMinutes() === 22 && date.getSeconds() === 22 && date.getMilliseconds() === 222) {
IsResult_Str = "超〇";
}else if (date.getMilliseconds() === 0) {
IsResult_Str = "超〇";
} else if (date.getMilliseconds() >= 997) {
IsResult_Str = "勇敢X";
}
return IsResult_Str;
} catch (error) {
return "判定不能"; // エラーが発生した場合は空の文字列を返す
}
}
function dateToStr24HPad0(date, format) {
if (!format) {
format = 'YYYY/MM/DD HH:mm:ss'
}
format = format.replace(/YYYY/g, date.getFullYear());
format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
format = format.replace(/DD/g, ('0' + date.getDate()).slice(-2));
format = format.replace(/HH/g, ('0' + date.getHours()).slice(-2));
format = format.replace(/mm/g, ('0' + date.getMinutes()).slice(-2));
format = format.replace(/ss/g, ('0' + date.getSeconds()).slice(-2));
return format;
}
// TargetLinesを\nで分割して処理
function processTargetLines(TargetLines) {
// \nで分割
const lines = TargetLines.split("\n");
// 結果を格納する配列
const resultLines = [];
lines.forEach(line => {
// 空行はスキップ
if (line.trim() !== "") {
// LastLine_tweet_URL_to_ID_Func関数で処理
const processedLine = LastLine_tweet_URL_to_ID_Func(line);
if (processedLine) {
resultLines.push(processedLine);
}
}
});
// 結果を改行で結合して返す
return resultLines.join("\n");
}
// 初期フラグ設定
let Yoruho_flag = true;
let Hiruho_flag = false;
let Pita334_flag = false;
let Nyaan_flag = false;
let BackupOnReload_flag = false;
let Ver1_flag = false;
let isHidden_flag = false;
//let bgColor_flag = false;
let Mark_On_Off = true;
let SimplifiedTotal_flag = true;
let getDatePickerValue = "";
let setDatePickerValue = false;
// マーク取り消し使用するか?を切り替える関数
function toggleMark_On_Off(vertFlg) {
closeAndRemoveDialogsByClass('Milly-custom-dialog');
lastProcessedUrl = "";
if(vertFlg){
// フラグを反転させる
Mark_On_Off = !Mark_On_Off;
}
saveLocalStorage("check_Mark_On_Off", Mark_On_Off); // 状態が変更されたら保存
if(Mark_On_Off){
//alert("ツイート欄 の 背景色 を 「 薄いブルー 」 に設定します");
// ここでボタンにエフェクトを適用
applyPressedEffect(document.getElementById("check_Mark_On_Off")); // 仮にボタンのIDを'myButton'とします
} else {
// ボタンのエフェクトを通常状態に戻す
removePressedEffect(document.getElementById("check_Mark_On_Off"));
}
processTweets();
}
// 背景色のフラグ(true: ON, false: OFF)
let backgroundColorFlag = false;
// 背景色を切り替える関数
function toggleBackgroundColor(vertFlg) {
if(vertFlg){
// フラグを反転させる
backgroundColorFlag = !backgroundColorFlag;
saveLocalStorage("backgroundColorFlag", backgroundColorFlag); // 状態が変更されたら保存
// if(isNotCustomize_and_NoBackground == false){
// backgroundColorFlag = false;
// }
}
if(backgroundColorFlag){
//alert("ツイート欄 の 背景色 を 「 薄いブルー 」 に設定します");
// ここでボタンにエフェクトを適用
applyPressedEffect(document.getElementById("bgColorButton")); // 仮にボタンのIDを'myButton'とします
} else {
// ボタンのエフェクトを通常状態に戻す
removePressedEffect(document.getElementById("bgColorButton"));
}
Ret_detectMode = detectMode();
processTweets();
}
// ボタンの表示状態を切り替える関数
function toggleButtonVisibility(buttonId,w_isHidden_flag) {
// 表示/最小化ボタン作成
let toggleButton = document.createElement('button');
//toggleButton.textContent = '<< 最小化';
// モードを判定し、結果を表示
//let Ret_detectMode = detectMode();
var button = document.getElementById(buttonId);
if (w_isHidden_flag == true || isHidden_flag == true
|| (isDarkMode == false && Ret_detectMode == "dark")
|| (isDarkMode == true && Ret_detectMode == "light")) {
// ボタンが表示されている場合は非表示にする
// button.style.display = 'none';
// backgroundColorFlag = false;
saveLocalStorage("backgroundColorFlag", backgroundColorFlag); // 状態が変更されたら保存
} else {
// ボタンが非表示の場合は表示する
//button.style.display = ''; // デフォルトの表示スタイルに戻す(または 'block' など)
saveLocalStorage("backgroundColorFlag", backgroundColorFlag); // 状態が変更されたら保存
}
//toggleBackgroundColor(false);
}
function applyPressedEffect(element) {
if (!element) return;
// 背景色を薄いブルーに設定(コメントアウトされている行は省略)
//element.style.backgroundColor = "#add8e6";
if(element == document.getElementById('bgColorButton')){
// 外側に右下に影を追加することで押された効果を演出(影を明るい色に設定)
element.style.boxShadow = "4px 4px 8px #ccc"; // 明るいグレーの影
// ボタンを軽く右下に動かす
element.style.transform = "translateY(2px) translateX(2px)";
}
if(element == document.getElementById('bgColorButton')){
element.textContent = '色 OFF';
}else if (element == document.getElementById('check_Mark_On_Off')){
element.textContent = 'マーク取り消し機能をON';
}
}
function removePressedEffect(element) {
if (!element) return;
// ボタンの背景色をクリア(元の色に戻す必要があれば、ここで指定)
//element.style.backgroundColor = ""; // 例: 元の背景色がある場合はここに設定
if(element == document.getElementById('bgColorButton')){
// ボックスシャドウをクリア
element.style.boxShadow = "";
// 変形をクリア
element.style.transform = "";
}
if(element == document.getElementById('bgColorButton')){
element.textContent = '背景色';
}else if (element == document.getElementById('check_Mark_On_Off')){
element.textContent = 'マーク取り消し機能をOFF';
}
}
function convertToFullWidthDigits(inputString) {
// 半角数字を全角数字に変換するためのマップ
const digitMap = {
'0': '0',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9'
};
// 入力文字列の各文字を走査し、対応する全角文字に置換
let resultString = inputString.split('').map(char => {
return digitMap[char] || char; // マップで置換、対応する全角数字がなければ元の文字をそのまま使用
}).join('');
return resultString;
}
// 現在設定されている秒数(デフォルトは1秒)
let CurrentNumSetting = 1; // この値を変更することで初期設定を変えられます
// // 現在の設定に基づいてボタンの表示を更新する関数
// function updateButtonVisibility() {
// // 全てのボタンを取得
// const buttons = document.querySelectorAll('button.control-item');
// // 現在の設定に対応するボタンをアクティブにする
// const activeButton = document.getElementById(`adjustTimeButton-${CurrentNumSetting}`);
// if (activeButton) {
// applyPressedEffect(document.getElementById(activeButton)); // 仮にボタンのIDを'myButton'とします
// }else{
// //removePressedEffect(document.getElementById("check_Mark_On_Off"));
// }
// }
function toggleTimeAdjustButtons(shouldDisable) {
// 'adjustTimeButton-*' を含むボタンだけを対象にする
const timeAdjustButtons = document.querySelectorAll('#buttonContainer > button[id^="adjustTimeButton-"]');
// 'disabled' 属性を切り替える
timeAdjustButtons.forEach(button => {
button.disabled = shouldDisable;
if(shouldDisable){
button.textContent = " - ";
}
applyStyleToContainerButtons_002(button.id, shouldDisable);
});
if(shouldDisable == false){
}
}
// ボタンのスタイルを変更する関数
function applyStyleToContainerButtons_002(buttonId, shouldDisable) {
const button = document.getElementById(buttonId);
if (button) {
if (shouldDisable) {
// 無効化時のスタイルを適用
//button.style.opacity = '0.5';
button.style.cursor = 'not-allowed';
button.style.pointerEvents = 'none'; // クリックイベントを無効化
} else {
// 有効化時のスタイルを適用
//button.style.opacity = '1.0';
button.style.cursor = 'pointer';
button.style.pointerEvents = 'auto'; // クリックイベントを有効化
}
}
}
// '±1秒', '±3秒', '±5秒' ボタンを作成し、現在の設定に応じて表示/非表示を制御する関数
function createTimeAdjustButtons(workContainer) {
// 操作の配列を定義
const timeAdjustments = [
{ label: '±0秒 (ピタリ🎯のみ) ', value: 0 },
{ label: '±1秒', value: 1 },
{ label: '±3秒', value: 3 },
{ label: '±5秒', value: 5 }
];
// 各操作に対してボタンを作成
timeAdjustments.forEach(adjustment => {
let button = document.createElement('button');
button.textContent = adjustment.label;
button.classList.add('control-item'); // 'control-item'クラスを追加
//setButtonStyle(button);
button.addEventListener('click', function() {
// 時間を調整し、ボタンの表示を更新
adjustTime(adjustment.value);
updateButtonVisibility(adjustment.value);
saveLocalStorage('targetCurrentNumSetting', adjustment.value); // 状態が変更されたら保存
// if(adjustment.value == 0){
// const message = `ピタリ のみを対象にする為に\nメモリー内部にある 「 ツイートのURL 」 をクリアするには 「 OK 」 ボタン\nを押してください。クリア後、Webページを リロード します。`;
// text_to_speak(message, true);
// const confirmation = confirm(message);
// if (confirmation) {
// }
// }
clearCheckboxStates();
//addCheckboxes();
processTweets();
});
// ボタンのIDを設定して、後で参照できるようにする
button.id = `adjustTimeButton-${adjustment.value}`;
workContainer.appendChild(button); // fieldset_PitaTimeにボタンを追加
if(adjustment.value == 0){
addBreak_002(workContainer,7);
}else{
addSpacer(workContainer);
}
// 初期ボタンの表示を更新
updateButtonVisibility(adjustment.value);
});
}
// 時間を調整し、現在の設定を更新する関数
function adjustTime(seconds) {
CurrentNumSetting = seconds; // 現在の設定を更新
}
// ボタンの表示を現在の設定に基づいて更新し、押下効果を追加する関数
function updateButtonVisibility(currentValue) {
const timeAdjustments = [0, 1, 3, 5];
timeAdjustments.forEach(value => {
let button = document.getElementById(`adjustTimeButton-${value}`);
if (button) {
if(Ver1_flag == false) {
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
var TargetMark = '';
if(Yoruho_flag == true){
TargetMark = '🌙';
}
if(Hiruho_flag == true){
TargetMark = '🌞';
}
if(Pita334_flag == true){
TargetMark = '✨';
}
if(Nyaan_flag == true){
TargetMark = '😾';
}
if(value == 0){
// 内側のフレームのタイトルを設定
const innerLegend = document.createElement('legend');
innerLegend.id = 'innerFieldset_label';
if(TargetMark == ''){
button.textContent = '±0秒 (ピタリ🎯のみ) ';
}else{
button.textContent = '±0秒 (ピタリ' + TargetMark + '🎯のみ) ';
}
}
// ボタンのクラスを設定
button.style.padding = '5px 10px';
button.style.fontSize = '14px';
button.style.margin = '3px';
button.style.cursor = 'pointer';
button.style.transition = 'transform 0.1s ease, border 0.1s ease, color 0.1s ease';
button.style.border = '2px solid white';
button.style.backgroundColor = 'black';
// 現在の設定に基づいて押された状態をキープ
if (currentValue === value) {
button.style.transform = 'scale(0.95)';
button.style.borderColor = 'yellow';
button.style.color = 'yellow';
} else {
button.style.transform = '';
button.style.borderColor = 'white';
button.style.color = 'white';
}
}
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
// var TargetMark = '';
if(Yoruho_flag == true){
TargetMark = '🌙';
}
if(Hiruho_flag == true){
TargetMark = '🌞';
}
if(Pita334_flag == true){
TargetMark = '✨';
}
if(Nyaan_flag == true){
TargetMark = '😾';
}
if(TargetMark == ''){
button.disabled = true;
// 無効化時のスタイルを適用
button.style.opacity = '0.5';
button.style.cursor = 'not-allowed';
button.style.pointerEvents = 'none'; // クリックイベントを無効化
}
else
{
// ボタンの有効/無効状態を設定
button.disabled = (currentValue === value);
// 有効化時のスタイルを適用
button.style.opacity = '1.0';
button.style.cursor = 'pointer';
button.style.pointerEvents = 'auto'; // クリックイベントを有効化
}
// ボタンクリック時に現在の値を更新するイベントリスナーを追加
button.addEventListener('click', () => {
// if(button.textContent == " - "){
// alert(" - ");
// return;
// }
updateButtonVisibility(value);
});
}
});
if(currentValue == 0){
// 'check_Yoruho_flag'チェックボックスのラベルを更新
updateCheckboxLabel('check_Yoruho_flag', convertToFullWidthDigits('よるほー🌙🎯(ピタリのみ)'));
updateCheckboxLabel('check_Hiruho_flag', convertToFullWidthDigits('ひるほー🌞🎯(ピタリのみ)'));
updateCheckboxLabel('check_Pita334_flag', convertToFullWidthDigits('334✨🎯(ピタリのみ)'));
updateCheckboxLabel('check_Nyaan_flag', convertToFullWidthDigits('にゃーん😾🎯(ピタリのみ)'));
}else{
// 'check_Yoruho_flag'チェックボックスのラベルを更新
updateCheckboxLabel('check_Yoruho_flag', convertToFullWidthDigits('よるほー🌙 ±' + currentValue + '秒'));
updateCheckboxLabel('check_Hiruho_flag', convertToFullWidthDigits('ひるほー🌞 ±' + currentValue + '秒'));
updateCheckboxLabel('check_Pita334_flag', convertToFullWidthDigits('334✨ ±' + currentValue + '秒'));
updateCheckboxLabel('check_Nyaan_flag', convertToFullWidthDigits('にゃーん😾 ±' + currentValue + '秒'));
}
}
// チェックボックスに関連付けられているラベルのテキストを変更
function updateCheckboxLabel(checkboxId, newLabelText) {
// チェックボックスのIDからラベル要素を検索
let labels = document.querySelectorAll(`label[for="${checkboxId}"]`);
if (labels.length > 0) {
// ラベルが見つかった場合、テキストを更新
labels[0].textContent = newLabelText;
}
}
// 特定の要素の直後に新しいノードを挿入する関数
function insertAfter(newNode, referenceNode) {
if (referenceNode.nextSibling) {
// 次の兄弟が存在する場合、その前に挿入
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
} else {
// 兄弟がない場合は、親要素の最後に追加
referenceNode.parentNode.appendChild(newNode);
}
}
let voiceHelpEnabled = false;
// ボタンやチェックボックスを追加する関数
function addButtonsToBottom() {
// ボタンコンテナ作成
let buttonContainer = document.createElement('div');
buttonContainer.style.position = 'fixed';
buttonContainer.style.bottom = '10px';
buttonContainer.style.left = '10px';
buttonContainer.style.zIndex = '1000';
buttonContainer.id = 'buttonContainer';
// ── 修正箇所 18:00: 横幅を指定 ──
buttonContainer.style.width = '300px'; // 全体の横幅を 320px に固定
buttonContainer.style.maxWidth = '90vw'; // または画面幅の 90% までに制限
buttonContainer.style.overflowX = 'auto'; // はみ出たらスクロールさせる
buttonContainer.style.backgroundColor = 'black'; // 背景を不透明の黒に設定
buttonContainer.style.color = 'white';
buttonContainer.style.border = '1px solid gold';
buttonContainer.style.padding = '12px';
//buttonContainer.style.border = '1px solid gold'; // 1ピクセルのゴールド色の実線の枠線を設定
//addSpacer(buttonContainer);
// アプリ名を表示するラベルを作成
let AppName = document.createElement('label');
AppName.textContent = `🔗 みりほー補助ツール by ZERO 🔗`;
AppName.id = 'AppName'; // 必要に応じてIDを設定
AppName.style.fontWeight = 'bold';
AppName.style.color = 'white'; // ゴールドに設定
//AppName.style.backgroundColor = 'gold';
AppName.style.backgroundColor = '#00008B'; // 背景色を濃い青色に設定
AppName.style.cursor = 'pointer';
AppName.classList.add('control-item'); // control-itemクラスを追加
// // 画像を作成
// let image = document.createElement('img');
// image.src = 'https://terabot.konjiki.jp/milli_ho_favicon.jpg'; // 画像のURLを指定
// image.style.cursor = 'pointer';
// image.style.marginRight = '1px'; // 画像とラベルの間にマージンを設定
// image.style.width = '16px'; // 画像の幅を設定
// image.style.height = '16px'; // 画像の高さを設定
// image.classList.add('control-item'); // control-itemクラスを追加
// 画像をクリックすると新しいタブでURLを開く
AppName.addEventListener('click', function() {
window.open('https://spark.littlestar.jp/public/Ver4_Add_Milliseconds_To_Twitter_TL.html', '_blank'); // 開きたいURLを指定
});
// buttonContainer.appendChild(image);
buttonContainer.appendChild(AppName);
////apply3DEffect(AppName);
addBreak_002(buttonContainer,7);;
// バージョン情報を表示するラベルを作成
let versionLabel = document.createElement('label');
versionLabel.textContent = `Ver : ${currentVersion}`;
//versionLabel.style.fontWeight = 'bold';
versionLabel.id = 'versionLabel'; // 必要に応じてIDを設定
versionLabel.classList.add('control-item'); // control-itemクラスを追加
buttonContainer.appendChild(versionLabel);
addSpacer(buttonContainer);
Ver1_flag = addCheckbox(false, buttonContainer, 'check_Ver1_flag', 'Ver1に戻す', Ver1_flag, false)
addSpacer(buttonContainer);
voiceHelpEnabled = addCheckbox(true, buttonContainer, 'check_voice_Help_On_Off', '音声ガイド', voiceHelpEnabled, false);
// フィールドセットを作成
let fieldset_Browser = document.createElement('fieldset');
fieldset_Browser.id = 'check_Browser';
fieldset_Browser.classList.add('control-item'); // control-itemクラスを追加
fieldset_Browser.style.border = '1px solid white'; // 修正箇所 18:00: フィールドセットに枠線を追加
fieldset_Browser.style.padding = '8px'; // 修正箇所 18:00: フィールドセットにパディングを追加
// レジェンド(タイトル)を作成
let legend_Browser = document.createElement('legend');
legend_Browser.id = 'legend_Browser'; // IDを設定
legend_Browser.textContent = 'Webページを・・・';
legend_Browser.classList.add('control-item'); // control-itemクラスを追加
fieldset_Browser.appendChild(legend_Browser);
let goBackButton = document.createElement('button');
goBackButton.textContent = ' ◀ 戻る ';
goBackButton.classList.add('control-item'); // control-itemクラスを追加
goBackButton.addEventListener('click', function() {
window.history.back();
});
addSpacer(fieldset_Browser);
fieldset_Browser.appendChild(goBackButton);
setButtonStyle(goBackButton);
addSpacer(fieldset_Browser);
let Update_Button = document.createElement('button');
Update_Button.textContent = ' 【更新】 ';
Update_Button.classList.add('control-item'); // control-itemクラスを追加
Update_Button.addEventListener('click', function() {
const message = ` 「 OK 」 ボタンを押すと・・・収集した「 ツイートのURL 」 がクリアされます。クリア後、Webページを 「更新」(リロード) します。`;
text_to_speak(message, true);
const confirmation = confirm(message);
if (confirmation) {
All_URLs = [];
filteredURLs = []; // filteredURLs を空の配列にリセット
exclusionURL_Map = new Map();
includedURL_Map = new Map();
location.reload();
}
});
fieldset_Browser.appendChild(Update_Button);
setButtonStyle(Update_Button);
addSpacer(fieldset_Browser);
let goForwardButton = document.createElement('button');
goForwardButton.textContent = ' 進む ▶ ';
goForwardButton.classList.add('control-item'); // control-itemクラスを追加
goForwardButton.addEventListener('click', function() {
window.history.forward();
});
fieldset_Browser.appendChild(goForwardButton);
setButtonStyle(goForwardButton);
addSpacer(fieldset_Browser);
buttonContainer.appendChild(fieldset_Browser);
addBreak_002(buttonContainer,7);
// フィールドセットを作成
let fieldset = document.createElement('fieldset');
fieldset.id = 'tweetUrlActions';
fieldset.classList.add('control-item'); // control-itemクラスを追加
fieldset.style.border = '1px solid white'; // 修正箇所 18:00: フィールドセットに枠線を追加
fieldset.style.padding = '8px'; // 修正箇所 18:00: フィールドセットにパディングを追加
// レジェンド(タイトル)を作成
let legend = document.createElement('legend');
legend.textContent = 'ツイートのURLを・・・';
legend.classList.add('control-item'); // control-itemクラスを追加
fieldset.appendChild(legend);
// 'URLをコピー' ボタンを作成
let MemClearButton = document.createElement('button');
MemClearButton.textContent = 'クリア';
MemClearButton.classList.add('control-item'); // control-itemクラスを追加
MemClearButton.addEventListener('click', function() {
const message = `メモリー内部にある 「 ツイートのURL 」 をクリアするには 「 OK 」 ボタン\nを押してください。クリア後、Webページを リロード します。`;
text_to_speak(message, true);
const confirmation = confirm(message);
if (confirmation) {
All_URLs = [];
filteredURLs = []; // filteredURLs を空の配列にリセット
exclusionURL_Map = new Map();
includedURL_Map = new Map();
clearCheckboxStates();
location.reload();
}
});
fieldset.appendChild(MemClearButton);
setButtonStyle(MemClearButton);
addSpacer(fieldset);
// 'URLをコピー' ボタンを作成
let copyButton = document.createElement('button');
copyButton.textContent = 'コピー';
copyButton.classList.add('control-item'); // control-itemクラスを追加
copyButton.addEventListener('click', function() {
text_to_speak("収集したツイートのURLを、メモリー内部にコピーします。簡易集計機能を使用する事をお薦めします",true);
// ここにURLをコピーする関数を呼び出す
copyURLsToClipboard(false);
});
fieldset.appendChild(copyButton);
setButtonStyle(copyButton);
addSpacer(fieldset);
// 'URLをダウンロード' ボタンを作成
let downloadButton = document.createElement('button');
downloadButton.textContent = 'ダウンロード';
downloadButton.classList.add('control-item'); // control-itemクラスを追加
downloadButton.addEventListener('click', function() {
text_to_speak("収集したツイートのURLを、ファイルにして、ダウンロードします。簡易集計機能を使用する事をお薦めします",true);
// ここにURLをダウンロードする関数を呼び出す
exportURLsToTextFile(false);
});
fieldset.appendChild(downloadButton);
setButtonStyle(downloadButton);
addBreak_002(fieldset,7);
BackupOnReload_flag = addCheckbox(true, fieldset, 'check_BackupOnReload_flag', 'リロード時 : 自動保存', BackupOnReload_flag, false);
//Mark_On_Off = addCheckbox(true, fieldset, 'check_Mark_On_Off', "マーク取り消し機能を使用", Mark_On_Off, false);
SimplifiedTotal_flag = addCheckbox(true, fieldset, 'check_SimplifiedTotal_flag', "簡易集計機能を使用", SimplifiedTotal_flag, false);
checkboxesIsVisible = addCheckbox(true, fieldset, 'check_checkboxesIsVisible', '「✓収集する」を表示', checkboxesIsVisible, false);
toggleCheckboxes();
//addBreak_002(fieldset,7);
// // 'マーク取り消し' ボタンを作成
// let btnMark_On_Off = document.createElement('button');
// btnMark_On_Off.id = 'check_Mark_On_Off';
// btnMark_On_Off.textContent = 'マーク取り消し機能をON';
// btnMark_On_Off.classList.add('control-item'); // control-itemクラスを追加
// //btnMark_On_Off.style.display = 'none';
// btnMark_On_Off.addEventListener('click', function() {
// if(btnMark_On_Off.textContent == 'マーク取り消し機能をOFF'){
// text_to_speak("収集したツイートのURLを収集対象外にしたり、任意のツイートのURLを収集対象にします。ツイートの詳細ページで 表示される画面上部中央のボタンで操作してください。",true);
// }
// //マーク取り消し使用するか?を切り替える関数
// toggleMark_On_Off(true);
// });
// fieldset.appendChild(btnMark_On_Off);
// setButtonStyle(btnMark_On_Off);
// addBreak_002(fieldset,7);
buttonContainer.appendChild(fieldset);
// フィールドセットを作成
let fieldset_PitaTime = document.createElement('fieldset');
fieldset_PitaTime.id = 'checkPitaTime';
fieldset_PitaTime.classList.add('control-item'); // control-itemクラスを追加
fieldset_PitaTime.style.border = '1px solid white'; // 修正箇所 18:00: フィールドセットに枠線を追加
// …中略…
// レジェンド(タイトル)を作成
let legend_PitaTime = document.createElement('legend');
legend_PitaTime.id = 'legendPitaTime'; // IDを設定
legend_PitaTime.textContent = 'ピタリ判定対象・・・';
legend_PitaTime.classList.add('control-item'); // control-itemクラスを追加
fieldset_PitaTime.appendChild(legend_PitaTime);
// チェックボックスとラベルの追加
Yoruho_flag = addCheckbox(true, fieldset_PitaTime, 'check_Yoruho_flag', 'よるほー🌙 ±1秒', Yoruho_flag, false);
Hiruho_flag = addCheckbox(true, fieldset_PitaTime, 'check_Hiruho_flag', 'ひるほー🌞 ±1秒', Hiruho_flag, false);
Pita334_flag = addCheckbox(true, fieldset_PitaTime, 'check_Pita334_flag', '334✨ ±1秒', Pita334_flag, false);
Nyaan_flag = addCheckbox(true, fieldset_PitaTime, 'check_Nyaan_flag', 'にゃーん😾 ±1秒', Nyaan_flag, false);
fieldset_PitaTime.classList.add('control-item'); // control-itemクラスを追加
addBreak_002(fieldset_PitaTime,7);
// カレンダー表示ボタン
let calendarButton = document.createElement('button');
calendarButton.textContent = '対象日付';
calendarButton.classList.add('control-item'); // control-itemクラスを追加
calendarButton.onclick = showCalendarModal;
fieldset_PitaTime.appendChild(calendarButton);
setButtonStyle(calendarButton);
addSpacer(fieldset_PitaTime);
// // ヘルプメッセージ用の要素を作成
// let helpMsgElement = document.createElement('span');
// helpMsgElement.textContent = " ( 🎯はピタリの意味 ) "; // テキストを設定
// helpMsgElement.style.color = 'white'; // 色を白に設定
// // fieldset要素にヘルプメッセージを追加
// fieldset_PitaTime.appendChild(helpMsgElement);
addBreak_002(fieldset_PitaTime,7);
// // 水平線を作成
// const hr = document.createElement('hr');
// // helpMsgElementの直後に水平線を挿入
// insertAfter(hr, helpMsgElement);
// 内側のフィールドセットを作成
let innerFieldset = document.createElement('fieldset');
innerFieldset.id = 'innerFieldset';
innerFieldset.classList.add('control-item'); // 同じクラスを追加
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
// 内側のフレームのタイトルを設定
const innerLegend = document.createElement('legend');
innerLegend.id = 'innerFieldset_label';
var TargetMark = '';
if(Yoruho_flag == true){
TargetMark = '🌙';
}
if(Hiruho_flag == true){
TargetMark = '🌞';
}
if(Pita334_flag == true){
TargetMark = '✨';
}
if(Nyaan_flag == true){
TargetMark = '😾';
}
if(TargetMark == ''){
innerLegend.textContent = 'すべてのツイート( ✓ )';
}else{
innerLegend.textContent = `ピタリ(${TargetMark}🎯)と ニアほー(${TargetMark})`;
}
innerFieldset.appendChild(innerLegend);
// スタイルを直接適用
innerFieldset.style.textAlign = 'center'; // テキスト中央揃え
innerFieldset.style.alignItems = 'center'; // 水平方向の中央揃え
// 内側のフィールドセットにボタンを追加する関数を呼び出す
createTimeAdjustButtons(innerFieldset);
//innerFieldset.style.backgroundColor = 'white';
fieldset_PitaTime.appendChild(innerFieldset);
buttonContainer.appendChild(fieldset_PitaTime);
addBreak_002(buttonContainer,7);
let closeButton = document.createElement('button');
closeButton.textContent = ' X ';
closeButton.className = 'close-button'; // CSSでスタイルを調整するためのクラス
closeButton.style.backgroundColor = 'red'; // ボタンの背景色を黒に設定
closeButton.style.color = 'white'; // ボタンの文字色を赤に設定
closeButton.style.border = '1px solid white'; // ボタンの枠線を赤色に設定
closeButton.style.cursor = 'pointer'; // カーソルをポインターに設定
closeButton.style.borderWidth = '1px'; // 枠線の太さ
closeButton.style.borderStyle = 'solid'; // 枠線のスタイル
closeButton.style.padding = '3px';
closeButton.className = 'close-button'; // CSSでスタイルを調整するためのクラス
closeButton.onclick = function() {
text_to_speak("ブラウザの更新ボタンが押された時、または次回起動時には、操作パネルが再び表示されます。",true);
saveLocalStorage('close-button-clicked', true); // 状態が変更されたら保存
// buttonContainerの子要素を全て削除
while (buttonContainer.firstChild) {
buttonContainer.removeChild(buttonContainer.firstChild);
}
// 必要に応じてbuttonContainer自体を非表示にする
buttonContainer.style.display = 'none';
};
// 'toggle-button' を最初に追加してから 'close-button' を追加することで、順序を保持
//buttonContainer.insertBefore(closeButton, buttonContainer.firstChild);
buttonContainer.appendChild(closeButton);
addSmallSpacer(buttonContainer);
// 表示/最小化ボタン作成
let toggleButton = document.createElement('button');
toggleButton.textContent = '<< 最小化';
toggleButton.className = 'toggle-button'; // CSSでスタイルを調整するためのクラスを追加
toggleButton.id = 'toggle-button';
toggleButton.onclick = function() {
if(!isHidden_flag) text_to_speak("ブラウザの更新ボタンが押された時、または次回起動時には、操作パネルが最小化されて、表示されます",true);
toggleButtonVisibility('bgColorButton',isHidden_flag);
toggleDisplayBasedOnFlag(isHidden_flag,true);
};
// ボタンのスタイル設定
toggleButton.style.backgroundColor = 'black'; // 背景色を黒に設定
toggleButton.style.color = '#FFD700'; // テキスト色をゴールドに設定
//toggleButton.style.borderColor = 'red'; // 枠線の色を赤に設定
toggleButton.style.borderColor = '#FFD700'; // テキスト色をゴールドに設定
toggleButton.style.borderWidth = '1px'; // 枠線の太さ
toggleButton.style.borderStyle = 'solid'; // 枠線のスタイル
// ボタンやチェックボックスの作成と追加
buttonContainer.appendChild(toggleButton);
//addSmallSpacer(buttonContainer);
addSpacer(buttonContainer);
isDarkMode = addCheckbox(false, buttonContainer, 'check_isDarkMode', "ダークモード", isDarkMode, false);
addSmallSpacer(buttonContainer);
// '背景色' ボタンを作成
let bgColorButton = document.createElement('button');
bgColorButton.id = 'bgColorButton';
bgColorButton.textContent = '背景色';
bgColorButton.classList.add('control-item'); // control-itemクラスを追加
bgColorButton.style.display = 'none';
//bgColorButton.style.backgroundColor = "#add8e6";
//bgColorButton.style.color = "#00008B"; // ボタンの文字色を濃いブルーに設定
bgColorButton.addEventListener('click', function() {
//text_to_speak("X ( 旧Twitter ) の表示モード と 当スクリプトの表示モードを合わせる事をお薦めします",true);
// 背景色を切り替える関数を呼び出す
toggleBackgroundColor(true);
isNotCustomize_and_NoBackground = false;
});
//bgColor_flag = addCheckbox(true, buttonContainer, 'check_bgColor_flag', '背景色', bgColor_flag, true);
setButtonStyle(bgColorButton);
//addSmallSpacer(buttonContainer);
addSpacer(buttonContainer);
// ボタンをページに追加する(例: bodyに追加)
buttonContainer.appendChild(bgColorButton);
// フィールドセットをドキュメントに追加
document.body.appendChild(buttonContainer);
if(loadLocalStorage_LongName('close-button-clicked',false) == true){
toggleDisplayBasedOnFlag(false,true);
saveLocalStorage('close-button-clicked', false); // 状態が変更されたら保存
}
}
function toggleDisplayBasedOnFlag(w_isHidden_flag, vertFlg) {
let controls = document.querySelectorAll('#buttonContainer > *:not(.toggle-button):not(.close-button)');
let toggleButton = document.getElementById('toggle-button');
let closeButton = document.querySelector('.close-button'); // '×' ボタンを選択
let buttonContainer = document.getElementById('buttonContainer');
if(vertFlg == true){
isHidden_flag = w_isHidden_flag = !w_isHidden_flag;
}
// 引数のフラグに基づいて表示/非表示を切り替え
controls.forEach(control => {
control.style.display = w_isHidden_flag ? 'none' : ''; // isHidden_flagがtrueなら非表示、falseなら表示
});
if(w_isHidden_flag == false){
toggleButtonVisibility('bgColorButton',isHidden_flag);
}
// buttonContainerのスタイルを調整
if (isHidden_flag) {
// 最小化状態のスタイル適用
buttonContainer.style.padding = '3px';
buttonContainer.style.width = 'auto';
buttonContainer.style.height = 'auto';
toggleButton.textContent = '戻す>>';
// ── 修正箇所 18:00: padding を大きく変更 ──
toggleButton.style.setProperty('padding', '5px 5px', 'important');
} else {
// 通常表示状態のスタイル適用
buttonContainer.style.padding = '10px';
buttonContainer.style.width = 'initial';
buttonContainer.style.height = 'initial';
toggleButton.textContent = '<< 最小化';
// ── 修正箇所 18:00: padding を大きく変更 ──
toggleButton.style.setProperty('padding', '5px 5px', 'important');
}
// 最小化状態でも「×」ボタンは常に表示する
closeButton.style.display = '';
saveLocalStorage('toggle-button', isHidden_flag); // 状態が変更されたら保存
}
// ラベルのテキスト内容を変更し、特定の条件でテキスト色を変更する関数
function updateVersionLabel(newVersion) {
let label = document.getElementById('versionLabel');
if (label) {
label.textContent = `Ver : ${newVersion}`;
//label.style.fontWeight = 'bold';
if(newVersion == currentVersion){
label.style.color = '#FFD700'; // テキスト色をゴールドに設定
} else {
label.style.color = 'white'; // それ以外の場合は白色に設定(または他のデフォルト色に設定)
}
}
}
function updateLegendWithSelectedDate(getDatePickerValue) {
// legendの要素をIDを使用して取得
var legendPitaTime = document.getElementById('legendPitaTime');
// カレンダーピッカーから取得した日付が空でない場合に処理を実行
if (getDatePickerValue !== "") {
// レジェンドのテキストを更新
legendPitaTime.textContent = 'ピタリ判定対象 : ' + getDatePickerValue;
} else {
// 日付が選択されていない場合は、デフォルトのテキストを表示
legendPitaTime.textContent = 'ピタリ判定対象 : [ 今日 ]';
}
}
// この関数はカレンダーピッカーの値が変更されたとき、または適切な時に呼び出してください。
// 例: datePickerElement.addEventListener('change', function() {
// updateLegendWithSelectedDate(datePickerElement.value);
// });
// 要素間にスペースを追加する関数
function addSpaceBetweenElements(container, spaceHeight = '10px') {
let spacer = document.createElement('div'); // スペーサーとしてdiv要素を作成
spacer.style.height = spaceHeight; // スペーサーの高さを設定
container.appendChild(spacer); // スペーサーをコンテナに追加
}
function getTodayDate() {
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0'); // 月は0から始まるので1を足す
const dd = String(today.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}
function getYesterdayDate() {
const today = new Date(); // 今日の日付を取得
const yesterday = new Date(today); // 今日の日付のコピーを作成
yesterday.setDate(today.getDate() - 1); // 昨日の日付を設定
const yyyy = yesterday.getFullYear();
const mm = String(yesterday.getMonth() + 1).padStart(2, '0'); // 月は0から始まるので1を足す
const dd = String(yesterday.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}
// カレンダーモーダルを表示する関数
function showCalendarModal() {
// 確認ダイアログのメッセージで使用
const confirmationMessage = `「 対象日付ボタン 」(カレンダーピッカー)で取得した日を対象に処理しますか?\n`
+ `「 OK 」を押すと ${formatDate(getDatePickerValue)} が対象にセットされます。\n`
+ `「今日(直近)」を押すと ${formatDate(new Date())} をセットし\n`
+ `ピタリ判定および対象ツイートのURLを集めます。\n\n`
+ '(「 よるほー ±1秒 」 に✓を入れるなど、最低1つ✓を入れてください)' ;
const message = `「 対象日付ボタン 」(カレンダーピッカー)で取得した日を対象に処理できます\n`
+ `「 取得した日付をセット 」を押すと日付が対象にセットされます。\n`
+ `「 閉じる 」を押すとキャンセルします\n`;
// const confirmation = confirm(message);
text_to_speak(message, true);
let KeepDate = getDatePickerValue;
// モーダルコンテナ作成
let modalContainer = document.createElement('div');
modalContainer.id = 'calendarModal';
modalContainer.style.position = 'fixed';
modalContainer.style.top = '50%';
modalContainer.style.left = '50%';
modalContainer.style.transform = 'translate(-50%, -50%)';
modalContainer.style.backgroundColor = 'white';
modalContainer.style.padding = '20px';
modalContainer.style.zIndex = '1001';
modalContainer.style.border = '1px solid #ccc';
modalContainer.style.borderRadius = '5px';
// フレーム(fieldset)を作成
let calendarFieldset = document.createElement('fieldset');
calendarFieldset.style.padding = '10px'; // パディングを設定して見た目を整える
calendarFieldset.style.marginTop = '10px'; // マージントップを設定して間隔を空ける
// タイトル(legend)を作成
let calendarLegend = document.createElement('legend');
calendarLegend.textContent = 'カレンダーから選択';
calendarLegend.style.color = 'black'; // タイトルのテキストカラーを黒色に設定
////setButtonStyle(calendarLegend);
calendarFieldset.appendChild(calendarLegend);
// カレンダーピッカー作成
let datePicker = document.createElement('input');
datePicker.type = 'date';
calendarFieldset.appendChild(datePicker); // フレームにカレンダーピッカーを追加
isDarkMode = loadLocalStorage_ShortName("isDarkMode");
if(isDarkMode == true){
modalContainer.style.backgroundColor = 'white';
calendarLegend.style.color = 'black'; // タイトルのテキストカラーを黒色に設定
}else{
modalContainer.style.backgroundColor = 'black';
calendarLegend.style.color = 'white'; // タイトルのテキストカラーを黒色に設定
}
// フレームをモーダルコンテナに追加
modalContainer.appendChild(calendarFieldset);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// datePickerの値が変更されたときに実行されるイベントリスナーを追加
datePicker.addEventListener('change', function() {
// datePickerから受け取った値を変数に格納
getDatePickerValue = datePicker.value;
if(getDatePickerValue != ""){
setDatePickerValue = true;
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
}else{
setDatePickerValue = false;
saveLocalStorage("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
}
});
// 今日を取得ボタン
let getTodayButton = document.createElement('button');
getTodayButton.textContent = '今日の日付をセット';
setButtonStyle(getTodayButton);
getTodayButton.onclick = function() {
//getDatePickerValue = datePicker.value = new Date().toISOString().split('T')[0];
getDatePickerValue = datePicker.value = getTodayDate();
if(getDatePickerValue != ""){
setDatePickerValue = true;
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
}else{
setDatePickerValue = false;
saveLocalStorage("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
}
};
modalContainer.appendChild(getTodayButton);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// 昨日を取得ボタン
let getYesterdayButton = document.createElement('button');
getYesterdayButton.textContent = '昨日の日付をセット';
setButtonStyle(getYesterdayButton);
getYesterdayButton.onclick = function() {
// let yesterday = new Date();
// yesterday.setDate(yesterday.getDate() - 1); // 現在の日付から1日減らす
// getDatePickerValue = datePicker.value = yesterday.toISOString().split('T')[0];
getDatePickerValue = datePicker.value = getYesterdayDate();
if(getDatePickerValue != ""){
setDatePickerValue = true;
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
}else{
setDatePickerValue = false;
saveLocalStorage("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
}
};
modalContainer.appendChild(getYesterdayButton);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// 閉じるボタン
let closeButton = document.createElement('button');
closeButton.textContent = '取得した日付をセット';
setButtonStyle(closeButton);
closeButton.onclick = function() {
document.body.removeChild(modalContainer);
if(getDatePickerValue != ""){
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
}
};
modalContainer.appendChild(closeButton);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// キャンセルボタン(日付指定なし、常に今日を使用)
let resetDateButton = document.createElement('button');
resetDateButton.textContent = '今日 ( 直近 ) をセット';
setButtonStyle(resetDateButton);
resetDateButton.onclick = function() {
document.body.removeChild(modalContainer); // モーダルコンテナを削除
// datePickerValueをリセットし、setDatePickerValueフラグをfalseに設定
getDatePickerValue = "";
setDatePickerValue = false;
saveLocalStorage("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
};
// モーダルコンテナまたは適切な要素にキャンセルボタンを追加
modalContainer.appendChild(resetDateButton);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// キャンセルボタン
let cancelButton = document.createElement('button');
cancelButton.textContent = '閉じる';
setButtonStyle(cancelButton);
cancelButton.onclick = function() {
document.body.removeChild(modalContainer); // モーダルコンテナを削除
// datePickerValueをリセットし、setDatePickerValueフラグをfalseに設定
getDatePickerValue = KeepDate;
setDatePickerValue = true;
//if(getDatePickerValue != ""){
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
//}
};
// モーダルコンテナまたは適切な要素にキャンセルボタンを追加
modalContainer.appendChild(cancelButton);
// 使用例: modalContainerに10pxのスペースを追加
addSpaceBetweenElements(modalContainer, '10px');
// モーダルコンテナをドキュメントに追加
document.body.appendChild(modalContainer);
}
// 日付を YYYY/MM/DD 形式でフォーマットする関数
function formatDate(date) {
let d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2) {
month = '0' + month;
}
if (day.length < 2) {
day = '0' + day;
}
return [year, month, day].join('-');
}
function getTodayDateString(getDatePickerValue, isDateType) {
let today;
if(getDatePickerValue === ""){
// タイムゾーンのオフセットを考慮して今日の日付を取得
today = new Date(getTodayDate() + 'T00:00:00');
}else{
// getDatePickerValueから日付を取得し、時刻を00:00:00.000に設定
today = new Date(getDatePickerValue + 'T00:00:00');
}
if (isDateType) {
// SetDate_or_Stringがtrueの場合はDateオブジェクトを返す
const offset = today.getTimezoneOffset() * 60000; // タイムゾーンのオフセットをミリ秒で取得
return new Date(today.getTime() - offset); // ローカルタイムゾーンに合わせたDateオブジェクトを返す
} else {
// SetDate_or_Stringがfalseの場合は日付の文字列を返す
const offset = today.getTimezoneOffset() * 60000; // タイムゾーンのオフセットをミリ秒で取得
const localToday = new Date(today.getTime() - offset); // ローカルタイムゾーンに合わせた日付を取得
return localToday.toISOString().split('T')[0]; // 日付の文字列を返す
}
}
function getPreviousDay(date) {
// 指定された日付のコピーを作成
let previousDay = new Date(date);
// 1日前の日付を計算
previousDay.setDate(date.getDate() - 1);
return previousDay;
}
// スペーサーを追加する関数
function addSmallSpacer(container) {
let spacer = document.createElement('span');
spacer.style.margin = '0 2px'; // 両側に5pxのマージンを設定
container.appendChild(spacer);
}
// スペーサーを追加する関数
function addSpacer(container) {
let spacer = document.createElement('span');
spacer.style.margin = '0 5px'; // 両側に5pxのマージンを設定
container.appendChild(spacer);
}
// 改行を追加する関数
function addBreak(container) {
container.appendChild(document.createElement('br'));
}
// 指定された高さのスペースを追加する関数
function addBreak_002(container, height) {
let spacer = document.createElement('div');
spacer.style.height = height + 'px'; // 高さを設定
spacer.style.clear = 'both'; // 左右のフロートをクリア
container.appendChild(spacer);
}
function apply3DStyle(element) {
// ここに3DスタイルをJavaScriptで定義します
element.style.appearance = 'none';
element.style.webkitAppearance = 'none';
element.style.height = '20px';
element.style.width = '20px';
element.style.border = '1px solid #999';
element.style.borderRadius = '5px';
element.style.position = 'relative';
element.style.top = '5px';
element.style.backgroundColor = '#fff';
element.style.cursor = 'pointer';
// チェックされた状態のスタイル
element.addEventListener('change', () => {
if (element.checked) {
element.style.boxShadow = '0 0 5px #666 inset';
} else {
element.style.boxShadow = 'none';
}
});
}
// function apply3DStyle(element) {
// // ここに3DスタイルをJavaScriptで定義します
// element.style.appearance = 'none';
// element.style.webkitAppearance = 'none';
// element.style.height = '20px';
// element.style.width = '20px';
// element.style.border = '1px solid #999';
// element.style.borderRadius = '5px';
// element.style.position = 'relative';
// element.style.top = '5px';
// element.style.backgroundColor = '#fff';
// element.style.cursor = 'pointer';
// // チェックされた状態のスタイル
// element.addEventListener('change', () => {
// if (element.checked) {
// element.style.boxShadow = '0 0 5px #666 inset';
// } else {
// element.style.boxShadow = 'none';
// }
// });
// }
function setButtonStyle_Disabled(button) {
// 物理的に無効化
button.disabled = true;
// デフォルト外観リセット
button.style.setProperty('-webkit-appearance', 'none', 'important');
button.style.setProperty('-moz-appearance', 'none', 'important');
button.style.setProperty('appearance', 'none', 'important');
// サイズ計算に枠線を含める
button.style.setProperty('box-sizing', 'border-box', 'important');
// ── 修正箇所 18:00: 内側に余白を追加 ──
button.style.setProperty('padding', '6px 12px', 'important');
// ── 修正箇所 18:00: 外側に余白を追加 ──
button.style.setProperty('margin', '4px', 'important');
// 背景・文字色
button.style.setProperty('background-color', 'gray', 'important');
button.style.setProperty('color', 'white', 'important');
// 境界線を個別指定(白にしておくと黒背景でも見えます)
button.style.setProperty('border-style', 'solid', 'important');
button.style.setProperty('border-width', '1px', 'important');
button.style.setProperty('border-color', 'white', 'important');
// カーソル・影・アウトライン・不透明度
button.style.setProperty('cursor', 'not-allowed', 'important');
button.style.setProperty('box-shadow', 'none', 'important');
button.style.setProperty('outline', 'none', 'important');
button.style.setProperty('opacity', '1', 'important');
};
// コンテナー内の全てのボタンにスタイルを適用する関数
function applyStyleToContainerButtons(containerId, vertFlg) {
const container = document.getElementById(containerId);
if (container) {
const buttons = container.getElementsByTagName('button');
for (let i = 0; i < buttons.length; i++) {
if(vertFlg == true){
if (!buttons[i].classList.contains('toggle-button') && !buttons[i].classList.contains('close-button')) {
setButtonStyle_Disabled(buttons[i]);
}
}else{
if (!buttons[i].classList.contains('toggle-button') && !buttons[i].classList.contains('close-button')) {
setButtonStyle(buttons[i]);
}
}
}
}
if(vertFlg == false){
updateButtonVisibility(CurrentNumSetting);
}
}
function setButtonStyle(button) {
// ── 物理的に有効化(disabled 解除) ──
button.disabled = false; // 修正箇所 18:00
// ── デフォルト外観リセット ──
button.style.setProperty('-webkit-appearance', 'none', 'important'); // 修正箇所 18:00
button.style.setProperty('-moz-appearance', 'none', 'important'); // 修正箇所 18:00
button.style.setProperty('appearance', 'none', 'important'); // 修正箇所 18:00
// ── サイズ計算に枠線を含める ──
button.style.setProperty('box-sizing', 'border-box', 'important'); // 修正箇所 18:00
// ── 内側の余白 ──
button.style.setProperty('padding', '6px 12px', 'important'); // 修正箇所 18:00
// ── 外側の余白 ──
button.style.setProperty('margin', '4px', 'important'); // 修正箇所 18:00
// ── 動的に色を切り替える場合のベース設定 ──
let bg = 'black'; // 修正箇所 18:00
let fg = 'white'; // 修正箇所 18:00
// ── 背景色・文字色 ──
button.style.setProperty('background-color', bg, 'important'); // 修正箇所 18:00
button.style.setProperty('color', fg, 'important'); // 修正箇所 18:00
// ── 境界線を完全指定 ──
button.style.setProperty('border-style', 'solid', 'important'); // 修正箇所 18:00
button.style.setProperty('border-width', '1px', 'important'); // 修正箇所 18:00
button.style.setProperty('border-color', 'white', 'important'); // 修正箇所 18:00
// ── カーソル・影・アウトライン・不透明度 ──
button.style.setProperty('cursor', 'pointer', 'important'); // 修正箇所 18:00
button.style.setProperty('box-shadow', 'none', 'important'); // 修正箇所 18:00
button.style.setProperty('outline', 'none', 'important'); // 修正箇所 18:00
button.style.setProperty('opacity', '1', 'important'); // 修正箇所 18:00
}
function applyBlackAndWhiteTheme(containerId, dialogId, dialogClass) {
// コンテナのIDを指定
let container = document.getElementById(containerId);
if (container) {
// コンテナ内の全てのボタン要素を取得
let buttons = container.getElementsByTagName('button');
// 各ボタンの背景色と文字色を変更
for (let button of buttons) {
setButtonStyle(button);
}
}
// ダイアログのIDを指定
let dialog = document.getElementById(dialogId);
if (dialog) {
// ダイアログの背景色と文字色を変更
dialog.style.backgroundColor = 'black';
dialog.style.color = 'white';
// ダイアログ内の全てのボタン要素を取得
let buttons = dialog.getElementsByTagName('button');
// 各ボタンの背景色と文字色を変更
for (let button of buttons) {
setButtonStyle(button);
}
}
// ダイアログのクラス名を指定して全てのダイアログを取得
let dialogs = document.getElementsByClassName(dialogClass);
// 各ダイアログの背景色と文字色を変更し、ダイアログ内の全てのボタン要素のスタイルを変更
for (let dialog of dialogs) {
dialog.style.backgroundColor = 'black';
dialog.style.color = 'white';
let buttons = dialog.getElementsByTagName('button');
for (let button of buttons) {
setButtonStyle(button);
}
}
}
function addCheckbox(isNewlineFlag, container, id, labelText, workBoolean, is3D) {
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = id;
checkbox.checked = loadLocalStorage_LongName(id); // 初期値をLocalStorageから読み込む
checkbox.classList.add('control-item'); // control-itemクラスを追加
// ── 修正箇所 18:00: チェックボックスの余白を追加 ──
checkbox.style.setProperty('margin', '4px', 'important'); // 外側余白
checkbox.style.setProperty('padding', '2px', 'important'); // 内側余白
let label = document.createElement('label');
label.htmlFor = id;
label.textContent = labelText; // ここでラベルテキストを設定
label.classList.add('control-item'); // control-itemクラスを追加
// ── 修正箇所 18:00: ラベルの余白を追加 ──
label.style.setProperty('margin', '0 8px 0 4px', 'important'); // 外側余白(上 0 / 右 8px / 下 0 / 左 4px)
label.style.setProperty('padding', '2px 4px', 'important'); // 内側余白(上下 2px / 左右 4px)
// 3Dスタイルを適用するかどうか
if (is3D) {
apply3DStyle(checkbox);
}
// 特定のチェックボックスID
let specificIds = ['check_Yoruho_flag', 'check_Hiruho_flag', 'check_Pita334_flag', 'check_Nyaan_flag'];
checkbox.addEventListener('change', function() {
if(id == "check_Mark_On_Off"){
// //label.textContent = checkbox.checked ? "Ver1モード解除" : "Ver1に戻す"; // ダークモードの状態に応じてラベルを更新
// // Mark_On_Off = checkbox.checked;
// if(Mark_On_Off){
// saveLocalStorage(id, true); // 状態が変更されたら保存
// }else{
// saveLocalStorage(id, false); // 状態が変更されたら保存
// }
// return;
}
if(id == "check_SimplifiedTotal_flag"){
//label.textContent = checkbox.checked ? "Ver1モード解除" : "Ver1に戻す"; // ダークモードの状態に応じてラベルを更新
SimplifiedTotal_flag = checkbox.checked;
if(checkbox.checked){
saveLocalStorage(id, true); // 状態が変更されたら保存
}else{
saveLocalStorage(id, false); // 状態が変更されたら保存
}
return;
}
if(specificIds.includes(checkbox.id)) {
specificIds.forEach(function(otherCheckboxId) {
if (otherCheckboxId !== checkbox.id) {
let otherCheckbox = document.getElementById(otherCheckboxId);
if (otherCheckbox) {
otherCheckbox.checked = false;
saveLocalStorage(otherCheckboxId, false); // 状態が変更されたら保存
}
}
});
saveLocalStorage(id, checkbox.checked); // 状態が変更されたら保存
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
let label = document.getElementById('innerFieldset_label');
if (label) {
var TargetMark = '';
if(Yoruho_flag == true){
TargetMark = '🌙';
}
if(Hiruho_flag == true){
TargetMark = '🌞';
}
if(Pita334_flag == true){
TargetMark = '✨';
}
if(Nyaan_flag == true){
TargetMark = '😾';
}
if(TargetMark == ''){
label.textContent = 'すべてのツイート( ✓ )';
}else{
label.textContent = `ピタリ(${TargetMark}🎯) と ニアほー(${TargetMark})`;
}
}
updateButtonVisibility(CurrentNumSetting);
//location.reload();
return;
}
saveLocalStorage(id, checkbox.checked); // 状態が変更されたら保存
if(id == "check_BackupOnReload_flag" && !checkbox.checked){
const message = `メモリー内部にある 「 ツイートのURL 」 をクリアするには 「 OK 」 ボタン\nを押してください。クリア後、Webページを リロード します。`;
text_to_speak(message, true);
// if (confirmation) {
// All_URLs = [];
// filteredURLs = []; // filteredURLs を空の配列にリセット
// exclusionURL_Map = new Map();
// includedURL_Map = new Map();
// location.reload();
// }
} else if(id == "check_isDarkMode"){
label.textContent = checkbox.checked ? "ダークモード" : "ダークモード"; // ダークモードの状態に応じてラベルを更新
isDarkMode = checkbox.checked;
text_to_speak("X ( 旧Twitter ) の表示モード と 当スクリプトの表示モードを合わせる事をお薦めします",true);
if(isDarkMode == true){
container.style.border = '1px solid white'; // 1ピクセルの白い実線の枠線を設定
backgroundColorFlag = false;
saveLocalStorage("backgroundColorFlag", backgroundColorFlag); // 状態が変更されたら保存
}else{
container.style.border = '1px solid black'; // 1ピクセルの白い実線の枠線を設定
backgroundColorFlag = false;
saveLocalStorage("backgroundColorFlag", backgroundColorFlag); // 状態が変更されたら保存
}
isNotCustomize_and_NoBackground = false;
if(backgroundColorFlag){
//alert("ツイート欄 の 背景色 を 「 薄いブルー 」 に設定します");
// ここでボタンにエフェクトを適用
applyPressedEffect(document.getElementById("bgColorButton")); // 仮にボタンのIDを'myButton'とします
} else {
// ボタンのエフェクトを通常状態に戻す
removePressedEffect(document.getElementById("bgColorButton"));
}
updateCheckboxColors();
// 'bgColorButton' ボタンの表示状態を切り替える
toggleButtonVisibility('bgColorButton',isHidden_flag);
// 関数の呼び出し
////applyBlackAndWhiteTheme(container.id,'Milly-custom-dialog','control-item');
processTweets();
// const confirmation = confirm(`ピタオメのツイートの文字色も ダークモード/通常モー ド\nを切り替える為にこのWebページをリロードしますか?`);
// if (confirmation) {
// location.reload();
// }
}else if(id == "check_Ver1_flag"){
//label.textContent = checkbox.checked ? "Ver1モード解除" : "Ver1に戻す"; // ダークモードの状態に応じてラベルを更新
Ver1_flag = checkbox.checked;
toggleCheckboxes();
// 'check_Ver1_flag' と 'toggleButton' を除く全ての要素を取得
const controls = document.querySelectorAll('#buttonContainer > *:not(#check_Ver1_flag):not(.toggle-button)');
if(Ver1_flag == true){
updateVersionLabel("0.3+");
processTweets();
}else{
//currentVersion = "3.6";
updateVersionLabel(currentVersion);
processTweets();
//location.reload();
}
// チェック状態に応じて、要素の 'disabled' 属性を切り替える
controls.forEach(control => {
if(Ver1_flag == true){
// 'check_Ver1_flag' がチェックされた場合、要素を無効化
control.disabled = true;
applyStyleToContainerButtons(container.id, true);
} else {
// チェックが外れた場合、要素を有効化
control.disabled = false;
applyStyleToContainerButtons(container.id, false);
}
});
if(Ver1_flag){
text_to_speak("初期開発バージョンに戻しました",true);
}else{
text_to_speak("最新バージョンにしました",true);
}
}else if(id == "check_voice_Help_On_Off"){
voiceHelpEnabled = checkbox.checked;
if(voiceHelpEnabled == true){
saveLocalStorage("check_voice_Help_On_Off", voiceHelpEnabled); // 状態が変更されたら保存
text_to_speak("音声ガイド開始",true); //各機能を2回実行したら、それ以上はヘルプが鳴らないようにします。
//label.textContent = "音声";
}else{
speechSynthesis.cancel(); // 再生中の音声をストップ
saveLocalStorage("check_voice_Help_On_Off", voiceHelpEnabled); // 状態が変更されたら保存
//label.textContent = "音声";
}
}else if(id == "check_checkboxesIsVisible"){
//label.textContent = checkbox.checked ? "Ver1モード解除" : "Ver1に戻す"; // ダークモードの状態に応じてラベルを更新
checkboxesIsVisible = checkbox.checked;
if(checkboxesIsVisible){
saveLocalStorage(id, true); // 状態が変更されたら保存
}else{
saveLocalStorage(id, false); // 状態が変更されたら保存
}
toggleCheckboxes();
}
});
container.appendChild(checkbox);
container.appendChild(label);
if(isNewlineFlag == true){
container.appendChild(document.createElement('br')); // 改行を追加
}
}
// LocalStorageから値を読み込む関数
function loadLocalStorage_ShortName(keyShortName) {
let flagCopy = false;
if(keyShortName == "Yoruho_flag"){
flagCopy = Yoruho_flag = loadLocalStorage_LongName("check_Yoruho_flag",true);
}
if(keyShortName == "Hiruho_flag"){
flagCopy = Hiruho_flag = loadLocalStorage_LongName("check_Hiruho_flag",false);
}
if(keyShortName == "Pita334_flag"){
flagCopy = Pita334_flag = loadLocalStorage_LongName("check_Pita334_flag",false);
}
if(keyShortName == "Nyaan_flag"){
flagCopy = Nyaan_flag = loadLocalStorage_LongName("check_Nyaan_flag",false);
}
if(keyShortName == "BackupOnReload_flag"){
flagCopy = BackupOnReload_flag = loadLocalStorage_LongName("check_BackupOnReload_flag",true);
}
if(keyShortName == "Ver1_flag"){
flagCopy = Ver1_flag = loadLocalStorage_LongName("check_Ver1_flag",false);
}
if(keyShortName == "isDarkMode"){
flagCopy = isDarkMode = loadLocalStorage_LongName("check_isDarkMode",false);
}
if(keyShortName == "Mark_On_Off"){
flagCopy = Mark_On_Off = loadLocalStorage_LongName("check_Mark_On_Off",true);
}
if(keyShortName == "SimplifiedTotal_flag"){
flagCopy = SimplifiedTotal_flag = loadLocalStorage_LongName("check_SimplifiedTotal_flag",true);
}
if(keyShortName == "checkboxesIsVisible"){
flagCopy = checkboxesIsVisible = loadLocalStorage_LongName("check_checkboxesIsVisible",false);
}
if(keyShortName == "toggle-button"){
flagCopy = isHidden_flag = loadLocalStorage_LongName("toggle-button",false);
}
if(keyShortName == "backgroundColorFlag"){
flagCopy = backgroundColorFlag = loadLocalStorage_LongName("backgroundColorFlag",false);
}
if(keyShortName == "voice_Help_On_Off"){
flagCopy = voiceHelpEnabled = loadLocalStorage_LongName("check_voice_Help_On_Off",true);
}
// if(keyShortName == "LastShowPopupVer"){
// flagCopy = LastShowPopupVer = loadLocalStorage_LongName("LastShowPopupVer");
// }
return flagCopy;
}
// LocalStorageから値を読み込む関数
function loadLocalStorage_LongName(keyLongName, defaultValue) {
let value = localStorage.getItem("" + keyLongName);
if(keyLongName == "%getDatePickerValue"){
if(value != null && value != ""){
return value;
}else{
return "";
}
}
if(keyLongName == "LastShowPopupVer"){
if(value != null && value != ""){
return value;
}else{
return "";
}
}
if(keyLongName == "targetCurrentNumSetting"){
if(value != null && value != ""){
CurrentNumSetting = parseInt(value, 10);
updateButtonVisibility(CurrentNumSetting);
return CurrentNumSetting;
}else if(value == 0) {
updateButtonVisibility(0);
return 0;
}else{
updateButtonVisibility(1);
return 1;
}
}
if(keyLongName == "toggle-button"){
if(value != null && value != ""){
if(value == "true"){
isHidden_flag = true;
return true;
}else{
isHidden_flag = false;
return false;
}
return value;
}else{
isHidden_flag = false;
return false;
}
}
if(keyLongName == "backgroundColorFlag"){
if(value != null && value != ""){
if(value == "true"){
backgroundColorFlag = true;
return true;
}else{
backgroundColorFlag = false;
return false;
}
return value;
}else{
backgroundColorFlag = false;
return false;
}
}
if(keyLongName == 'close-button-clicked'){
if(value != null && value != ""){
if(value == "true"){
return true;
}else{
return false;
}
return value;
}else{
return false;
}
}
if(keyLongName == 'check_Mark_On_Off'){
if(value != null && value != ""){
if(value == "true"){
Mark_On_Off = true;
return true;
}else{
Mark_On_Off = false;
return false;
}
return value;
}else{
Mark_On_Off = true;
return true;
}
return true;
}
if(keyLongName == 'check_SimplifiedTotal_flag'){
if(value != null && value != ""){
if(value == "true"){
SimplifiedTotal_flag = true;
return true;
}else{
SimplifiedTotal_flag = false;
return false;
}
return value;
}else{
SimplifiedTotal_flag = true;
return true;
}
}
if(keyLongName == 'check_checkboxesIsVisible'){
if(value != null && value != ""){
if(value == "true"){
checkboxesIsVisible = true;
return true;
}else{
checkboxesIsVisible = false;
return false;
}
return value;
}else{
checkboxesIsVisible = false;
return false;
}
}
if(keyLongName == 'check_voice_Help_On_Off'){
if(value != null && value != ""){
if(value == "true"){
voiceHelpEnabled = true;
return true;
}else{
voiceHelpEnabled = false;
return false;
}
return value;
}else{
voiceHelpEnabled = false;
return false;
}
}
return value !== null ? JSON.parse(value) : defaultValue;
}
// LocalStorageに値を保存する関数
function saveLocalStorage(key, value) {
if(key == "%getDatePickerValue"){
if(value != null && value !=""){
localStorage.setItem(key,value);
}else{
localStorage.setItem(key,"");
}
return;
}
if(key == "LastShowPopupVer"){
if(value != null && value !=""){
localStorage.setItem(key,value);
}else{
localStorage.setItem(key,"");
}
return;
}
if(key == "targetCurrentNumSetting"){
// currentSetting変数が定義されていることを確認
// if (typeof CurrentNumSetting !== 'undefined') {
if(value != null && value !=""){
localStorage.setItem(key,value.toString());
}else{
localStorage.setItem(key,"");
}
// }
return;
}
if(key.startsWith("check_Mark_On_Off")){
if(value != null && value !=""){
localStorage.setItem(key, JSON.stringify(value));
}else{
localStorage.setItem(key, JSON.stringify(value));
}
return;
}
if(key.startsWith("check_SimplifiedTotal_flag")){
localStorage.setItem(key, JSON.stringify(value));
let checkbox = document.getElementById(key);
if(value == "true"){
checkbox.cheked = true;
}else{
checkbox.cheked = false;
}
return;
}
if(key.startsWith("check_checkboxesIsVisible")){
localStorage.setItem(key, JSON.stringify(value));
let checkbox = document.getElementById(key);
if(value == "true"){
checkbox.cheked = true;
}else if(value == "false"){
checkbox.cheked = false;
}else{
checkbox.cheked = false;
}
return;
}
if(key.startsWith("check_voice_Help_On_Off")){
localStorage.setItem(key, JSON.stringify(value));
let checkbox = document.getElementById(key);
if(value == "true"){
checkbox.cheked = true;
}else if(value == "false"){
checkbox.cheked = false;
}else{
checkbox.cheked = false;
}
return;
}
if(key.startsWith("check_")){
localStorage.setItem(key, JSON.stringify(value));
let checkbox = document.getElementById(key);
checkbox.cheked = value;
}else{
localStorage.setItem(key, JSON.stringify(value));
}
if(key == 'toggle-button'){
localStorage.setItem(key, JSON.stringify(value));
}
if(key == 'backgroundColorFlag'){
localStorage.setItem(key, JSON.stringify(value));
}
if(key == 'close-button-clicked'){
localStorage.setItem(key, JSON.stringify(value));
}
return;
}
// 改行を挿入する関数
function insertBreak(container) {
const breakElement = document.createElement('br'); // 要素を作成
container.appendChild(breakElement); // 指定したコンテナに要素を追加
}
function createButton(text, onClick) {
try {
let button = document.createElement('button');
button.textContent = text;
button.addEventListener('click', onClick);
// ボタンにスタイルを追加して隙間を設ける
button.style.marginRight = '10px'; // 右側に10pxのマージンを設定
return button;
} catch (error) {
console.error('Error in createButton:', error);
throw error; // スクリプトの実行を停止
}
}
function addCheckMark(tweet, isChecked, tweetID, tweetLink,timeElement) {
try {
let a = timeElement.parentNode;
//let span = document.createElement('span');
//span.style.fontWeight = 'bold'; // 太文字に設定
let originalTextContent = timeElement.textContent; // 元のテキスト内容を保存
let s0 = timeElement.getAttribute('datetime');
let s1 = toFormatedDateString(new Date(s0), tweetID);
let tweetDateTime = new Date(timeElement.getAttribute('datetime'));
// URLが'https://twitter.com/'で始まっているかどうかを確認
if (!tweetLink.startsWith('https://x.com/')) {
tweetLink = 'https://x.com' + tweetLink;
}
//if(Mark_On_Off == true && isUrlInExclusionMap(tweetLink) == true) {
// let s1 = toFormatedDateString(new Date(s0), tweetID);
if(isDarkMode == true){
timeElement.style.color = 'yellow'; // 太文字に設定
}else{
timeElement.style.color = 'red'; // 太文字に設定
}
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
//if(isChecked){
if(Mark_On_Off == true && isUrlInExclusionMap(tweetLink) == true){
timeElement.textContent ="" + s1;
addUrlAndMarkInfo(tweetLink,false);
}else{
timeElement.textContent ="✓" + s1;
addUrlAndMarkInfo(tweetLink,true);
}
}else{
timeElement.textContent =s1;
}
// // マークを時間要素の直後に挿入
// a.appendChild(span);
// a.removeChild(timeElement);
// a.insertBefore(markSpan, timeElement.nextSibling);
//}
} catch (error) {
console.error('Error in addCheckMark:', error);
throw error; // スクリプトの実行を停止
}
}
let lastProcessedUrl = "";
let processTweets = function() {
try {
if (Ver1_flag == true){
document.querySelectorAll('article[data-testid="tweet"]').forEach(function(tweet) {
// ツイートのURLの取得
let tweetLink = tweet.querySelector('a[href*="/status/"]');
let tweetURL = tweetLink ? tweetLink.getAttribute('href') : '';
// ツイートIDの取得
let tweetID = '';
if (tweetURL) {
let matches = tweetURL.match(/\/status\/(\d+)/);
if (matches && matches.length > 1) {
tweetID = matches[1];
}
}
// ツイートの時間情報の処理
let timeElement = tweet.querySelector('time');
if (timeElement) {
//let a = timeElement.parentNode;
//let span = document.createElement('span');
let s0 = timeElement.getAttribute('datetime');
let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.textContent = s1;
timeElement.style.color = 'gray'; // 太文字に設定
// a.appendChild(span);
// a.removeChild(timeElement);
}
});
}else if (Ver1_flag == false){
document.querySelectorAll('article[data-testid="tweet"]').forEach(function(tweet) {
if (Mark_On_Off == true) {
// 現在のページのURLを取得
const currentPageUrl = window.location.href;
// URLが以前と同じであれば何もしない
if (currentPageUrl === lastProcessedUrl) {
}else{
// URLが更新された場合は、処理を続行し、最後に処理したURLを更新
lastProcessedUrl = currentPageUrl;
// Twitterのツイート詳細ページまたはx.comのURLパターンを定義
const tweetDetailUrlPattern = /https:\/\/(?:twitter\.com|x\.com)\/(?:#!\/)?(\w+)\/status\/(\d+)/;
// URLがパターンにマッチするかチェック
if (tweetDetailUrlPattern.test(currentPageUrl)) {
///////////////////////////////// closeAndRemoveDialogsByClass('Milly-custom-dialog');
//setTimeout(function() {
// ここに遅延させたい処理を書く
// ツイートの時間情報の処理
let w_timeElement = tweet.querySelector('time');
// ダイアログを表示する
///////////////////////////////// showCustomDialog(currentPageUrl, w_timeElement, tweet);
//}, 5000); // 1000ミリ秒 = 1秒
} else {
// ダイアログを閉じる
///////////////////////////////// closeAndRemoveDialogsByClass('Milly-custom-dialog');
}
}
}
// 今日の日付を取得
let today;
// 1日前の日付を計算
let oneDayAgo;
// ツイートのURLの取得
let tweetLink = tweet.querySelector('a[href*="/status/"]');
let tweetURL = tweetLink ? tweetLink.getAttribute('href') : '';
// ツイートIDの取得
let tweetID = '';
if (typeof tweetURL === 'string') { // URLが文字列であることを確認
if (tweetURL) {
let matches = tweetURL.match(/\/status\/(\d+)/);
if (matches && matches.length > 1) {
tweetID = matches[1];
}
}
let timeElement = tweet.querySelector('time');
if (timeElement) {
tweetLink = tweet.querySelector('a[href*="/status/"]').getAttribute('href');
if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
// URLが'https://twitter.com/'で始まっているかどうかを確認
if (!tweetLink.startsWith('https://x.com/')) {
// 相対URLの場合、絶対URLに変換して追加
All_URLs.add('https://x.com' + tweetLink);
tweetLink = 'https://x.com' + tweetLink;
addCheckMark(tweet, true, tweetID, 'https://x.com' + tweetLink, timeElement); // ✓済みマークを追加
} else {
// すでに絶対URLの場合はそのまま追加
All_URLs.add(tweetLink);
addCheckMark(tweet, true, tweetID, tweetLink, timeElement); // ✓済みマークを追加
}
}else{
// URLが'https://twitter.com/'で始まっているかどうかを確認
if (!tweetLink.startsWith('https://x.com/')) {
// 相対URLの場合、絶対URLに変換して追加
tweetLink = 'https://x.com' + tweetLink;
//addCheckMark(tweet, true, tweetID, 'https://x.com' + tweetLink); // ✓済みマークを追加
} else {
// すでに絶対URLの場合はそのまま追加
//addCheckMark(tweet, true, tweetID, tweetLink); // ✓済みマークを追加
}
}
}
//ツイートの時間情報の処理
timeElement = tweet.querySelector('time');
if (timeElement) {
// 今日の日付を取得
//today = new Date();
getDatePickerValue = loadLocalStorage_LongName("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
today = getTodayDateString(getDatePickerValue,true);
// 1日前の日付を計算
//oneDayAgo = new Date(today.getTime() - (24*60*60*1000));
oneDayAgo = getPreviousDay(today);
// 各フラグに応じた時間範囲の設定
let timeRanges_0s = [
{ flag: Yoruho_flag,
startYear: oneDayAgo.getFullYear(), startMonth: oneDayAgo.getMonth(), startDay: oneDayAgo.getDate(),
startHour: 0, startMinute: 0, startSecond: 0,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 0, endMinute: 0, endSecond: 1,
emoji: '🌙',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 0, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 0, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌙🎯'
},
{ flag: Hiruho_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 12, startMinute: 0, startSecond: 0,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 12, endMinute: 0, endSecond: 1,
emoji: '🌞',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 12, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 12, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌞🎯'
},
{ flag: Pita334_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 3, startMinute: 34, startSecond: 0,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 3, endMinute: 34, endSecond: 1,
emoji: '✨',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 3, Pita_startMinute: 34, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 3, Pita_endMinute: 34, Pita_endSecond: 1,
Pita_emoji: '✨🎯'
},
{ flag: Nyaan_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 22, startMinute: 22, startSecond: 22,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 22, endMinute: 22, endSecond: 23,
emoji: '😾',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 22, Pita_startMinute: 22, Pita_startSecond: 22,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 22, Pita_endMinute: 22, Pita_endSecond: 23,
Pita_emoji: '😾🎯'
}
];
// 各フラグに応じた時間範囲の設定
let timeRanges_1s = [
{ flag: Yoruho_flag,
startYear: oneDayAgo.getFullYear(), startMonth: oneDayAgo.getMonth(), startDay: oneDayAgo.getDate(),
startHour: 23, startMinute: 59, startSecond: 59,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 0, endMinute: 0, endSecond: 2,
emoji: '🌙',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 0, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 0, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌙🎯'
},
{ flag: Hiruho_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 11, startMinute: 59, startSecond: 59,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 12, endMinute: 0, endSecond: 2,
emoji: '🌞',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 12, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 12, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌞🎯'
},
{ flag: Pita334_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 3, startMinute: 33, startSecond: 59,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 3, endMinute: 34, endSecond: 2,
emoji: '✨',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 3, Pita_startMinute: 34, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 3, Pita_endMinute: 34, Pita_endSecond: 1,
Pita_emoji: '✨🎯'
},
{ flag: Nyaan_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 22, startMinute: 22, startSecond: 21,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 22, endMinute: 22, endSecond: 24,
emoji: '😾',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 22, Pita_startMinute: 22, Pita_startSecond: 22,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 22, Pita_endMinute: 22, Pita_endSecond: 23,
Pita_emoji: '😾🎯'
}
];
// 各フラグに応じた時間範囲の設定
let timeRanges_3s = [
{ flag: Yoruho_flag,
startYear: oneDayAgo.getFullYear(), startMonth: oneDayAgo.getMonth(), startDay: oneDayAgo.getDate(),
startHour: 23, startMinute: 59, startSecond: 57,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 0, endMinute: 0, endSecond: 4,
emoji: '🌙',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 0, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 0, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌙🎯'
},
{ flag: Hiruho_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 11, startMinute: 59, startSecond: 57,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 12, endMinute: 0, endSecond: 4,
emoji: '🌞',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 12, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 12, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌞🎯'
},
{ flag: Pita334_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 3, startMinute: 33, startSecond: 57,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 3, endMinute: 34, endSecond: 4,
emoji: '✨',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 3, Pita_startMinute: 34, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 3, Pita_endMinute: 34, Pita_endSecond: 1,
Pita_emoji: '✨🎯'
},
{ flag: Nyaan_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 22, startMinute: 22, startSecond: 19,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 22, endMinute: 22, endSecond: 26,
emoji: '😾',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 22, Pita_startMinute: 22, Pita_startSecond: 22,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 22, Pita_endMinute: 22, Pita_endSecond: 23,
Pita_emoji: '😾🎯'
}
];
// 各フラグに応じた時間範囲の設定
let timeRanges_5s = [
{ flag: Yoruho_flag,
startYear: oneDayAgo.getFullYear(), startMonth: oneDayAgo.getMonth(), startDay: oneDayAgo.getDate(),
startHour: 23, startMinute: 59, startSecond: 55,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 0, endMinute: 0, endSecond: 6,
emoji: '🌙',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 0, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 0, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌙🎯'
},
{ flag: Hiruho_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 11, startMinute: 59, startSecond: 55,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 12, endMinute: 0, endSecond: 6,
emoji: '🌞',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 12, Pita_startMinute: 0, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 12, Pita_endMinute: 0, Pita_endSecond: 1,
Pita_emoji: '🌞🎯'
},
{ flag: Pita334_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 3, startMinute: 33, startSecond: 55,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 3, endMinute: 34, endSecond: 6,
emoji: '✨',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 3, Pita_startMinute: 34, Pita_startSecond: 0,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 3, Pita_endMinute: 34, Pita_endSecond: 1,
Pita_emoji: '✨🎯'
},
{ flag: Nyaan_flag,
startYear: today.getFullYear(), startMonth: today.getMonth(), startDay: today.getDate(),
startHour: 22, startMinute: 22, startSecond: 17,
endYear: today.getFullYear(), endMonth: today.getMonth(), endDay: today.getDate(),
endHour: 22, endMinute: 22, endSecond: 28,
emoji: '😾',
Pita_startYear: today.getFullYear(), Pita_startMonth: today.getMonth(), Pita_startDay: today.getDate(),
Pita_startHour: 22, Pita_startMinute: 22, Pita_startSecond: 22,
Pita_endYear: today.getFullYear(), Pita_endMonth: today.getMonth(), Pita_endDay: today.getDate(),
Pita_endHour: 22, Pita_endMinute: 22, Pita_endSecond: 23,
Pita_emoji: '😾🎯'
}
];
// // 各フラグに応じた時間範囲の処理
// timeRanges.forEach(function(timeRange) {
// // Yoruho_flag、Hiruho_flag、Pita334_flag、Nyaan_flag のいずれかが true であるかをチェック
// if (timeRange.flag && (Yoruho_flag || Hiruho_flag || Pita334_flag || Nyaan_flag)) {
// processRange(timeRange, timeElement, tweet, tweetID);
// }
// });
if(Yoruho_flag == true){
if(CurrentNumSetting == 0){
processRange(timeRanges_0s[0], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 1){
processRange(timeRanges_1s[0], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 3){
processRange(timeRanges_3s[0], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 5){
processRange(timeRanges_5s[0], timeElement, tweet, tweetID);
}
}
if(Hiruho_flag == true){
if(CurrentNumSetting == 0){
processRange(timeRanges_0s[1], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 1){
processRange(timeRanges_1s[1], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 3){
processRange(timeRanges_3s[1], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 5){
processRange(timeRanges_5s[1], timeElement, tweet, tweetID);
}
}
if(Pita334_flag == true){
if(CurrentNumSetting == 0){
processRange(timeRanges_0s[2], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 1){
processRange(timeRanges_1s[2], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 3){
processRange(timeRanges_3s[2], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 5){
processRange(timeRanges_5s[2], timeElement, tweet, tweetID);
}
}
if(Nyaan_flag == true){
if(CurrentNumSetting == 0){
processRange(timeRanges_0s[3], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 1){
processRange(timeRanges_1s[3], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 3){
processRange(timeRanges_3s[3], timeElement, tweet, tweetID);
}
if(CurrentNumSetting == 5){
processRange(timeRanges_5s[3], timeElement, tweet, tweetID);
}
}
}
}
});
}
} catch (error) {
console.error('Error in processTweets:', error);
throw error; // スクリプトの実行を停止
}
};
// 時間範囲を処理するための関数
function processRange(range, timeElement, tweet, tweetID) {
if (range.flag) {
let startDateTime = new Date(range.startYear, range.startMonth, range.startDay, range.startHour, range.startMinute, range.startSecond);
let endDateTime = new Date(range.endYear, range.endMonth, range.endDay, range.endHour, range.endMinute, range.endSecond);
let Pita_startDateTime = new Date(range.Pita_startYear, range.Pita_startMonth, range.Pita_startDay, range.Pita_startHour, range.Pita_startMinute, range.Pita_startSecond);
let Pita_endDateTime = new Date(range.Pita_endYear, range.Pita_endMonth, range.Pita_endDay, range.Pita_endHour, range.Pita_endMinute, range.Pita_endSecond);
let tweetDateTime = new Date(timeElement.getAttribute('datetime'));
Pita_Emoji(tweet, tweetID, timeElement, startDateTime, endDateTime, Pita_startDateTime, Pita_endDateTime, range.emoji, range.Pita_Emoji);
}
}
function compareTweetPaths(url1, url2) {
// 正規表現を使用して/status/以降のパスを抽出
const pathRegex = /\/status\/(.*)$/;
// 各URLからパスを抽出
const match1 = url1.match(pathRegex);
const match2 = url2.match(pathRegex);
// 両方のURLからパスが抽出できた場合のみ比較を行う
if (match1 && match2) {
// 抽出したパスが等しいか比較
return match1[1] === match2[1];
}
// パスが抽出できない、または等しくない場合はfalseを返す
return false;
}
// URLがマーク済みかどうかを確認する関数
function isUrlMarked(url) {
const item = filteredURLs.find(item => item.url === url);
if (item) {
return { existFLG: true, marked: item.marked }; // URLが見つかればExistFLGとmarked値を返す
} else {
return { existFLG: false, marked: false }; // URLが見つからなければExistFLGとmarkedをfalseで返す
}
}
// URLをマークする関数
function markUrl(url) {
const item = filteredURLs.find(item => item.url === url);
if (item) {
item.marked = true; // URLが見つかったらmarkedをtrueに設定
item.existFLG = true; // 明示的にExistFLGをtrueに設定
} else {
// URLが見つからなければ新しいオブジェクトを追加
filteredURLs.push({ url: url, marked: true, existFLG: true });
}
}
// // URLを改行付きで出力する関数
// function printUrlsWithNewline() {
// let urlsOutput = filteredURLs.map(item => item.url + (item.marked ? " (Marked)" : "")).join("\n");
// console.log(urlsOutput);
// return urlsOutput;
// }
function printUrlsWithNewline(filteredURLs) {
// MarkedがtrueのURLのみを改行付きで出力
let urlsOutput = filteredURLs.filter(item => item.marked).map(item => item.url).join("\n");
//console.log(urlsOutput);
return urlsOutput;
}
// filteredURLs にURLとマーク情報を追加する関数
function addUrlAndMarkInfo(url, isMarked) {
const item = filteredURLs.find(item => item.url === url);
if (!item) {
// URLがまだリストになければ追加
filteredURLs.push({ url: url, marked: isMarked, existFLG: true });
} else {
// 既に存在するURLの場合は、marked状態を更新
item.marked = isMarked;
item.existFLG = true;
}
}
function resetMarkedStatusInFilteredURLs() {
// filteredURLs 配列内の各オブジェクトに対してループ処理
filteredURLs.forEach(function(item) {
item.marked = false; // Marked プロパティを false に設定
});
}
// tweetIDから日時オブジェクトを返す関数
function Pita_Emoji(tweet, tweetID, timeElement, startDateTime, endDateTime, Pita_startDateTime, Pita_endDateTime, emoji, Pita_emoji) {
let a = timeElement.parentNode;
//let span = document.createElement('span');
//span.style.fontWeight = 'bold'; // 太文字に設定
let originalTextContent = timeElement.textContent; // 元のテキスト内容を保存
let s0 = timeElement.getAttribute('datetime');
let s1 = toFormatedDateString(new Date(s0), tweetID);
if(isNotCustomize_and_NoBackground == false){
if (backgroundColorFlag == true && isDarkMode == true) {
//if(Ret_detectMode == "light"){
// 濃いブルーの背景色に設定し、文字色を白色に
tweet.style.backgroundColor = 'rgba(0, 0, 139, 0.8)';
tweet.style.color = 'white';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'white'; // 子要素にも適用
});
//}
} else if (backgroundColorFlag == true && isDarkMode == false) {
if(Ret_detectMode == "light"){
tweet.style.backgroundColor = 'rgba(173, 216, 230, 0.5)';
tweet.style.color = 'black'; // または元々設定されていた色
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'black'; // 子要素にも適用
});
}
if(Ret_detectMode == "dark"){
tweet.style.backgroundColor = 'white';
tweet.style.color = 'black';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'black'; // 子要素にも適用
});
}
} else if (backgroundColorFlag == false && isDarkMode == true) {
if(Ret_detectMode == "light"){
// 濃いブルーの背景色に設定し、文字色を白色に
tweet.style.backgroundColor = 'rgba(0, 0, 139, 0.8)';
tweet.style.color = 'white';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'white'; // 子要素にも適用
});
}
if(Ret_detectMode == "dark"){
tweet.style.backgroundColor = 'black';
tweet.style.color = 'white';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'white'; // 子要素にも適用
});
}
} else if (isDarkMode == false) {
if(Ret_detectMode == "light"){
// 濃いブルーの背景色に設定し、文字色を白色に
tweet.style.backgroundColor = 'white';
tweet.style.color = 'black';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'black'; // 子要素にも適用
});
}
if(Ret_detectMode == "dark"){
tweet.style.backgroundColor = 'white';
tweet.style.color = 'black';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'black'; // 子要素にも適用
});
}
} else {
if(Ret_detectMode == "light"){
tweet.style.backgroundColor = '';
tweet.style.color = 'gray';
tweet.querySelectorAll('*').forEach(function(child) {
child.style.color = 'gray'; // 子要素にも適用
});
}
}
}else{
if(!((Ret_detectMode == "dark" && isDarkMode == true
|| Ret_detectMode == "light" && isDarkMode == false) && backgroundColorFlag == false)){
isNotCustomize_and_NoBackground = false;
}
}
if (backgroundColorFlag == true && isDarkMode == true) {
timeElement.style.color = 'yellow'; // 太文字に設定
}else if (backgroundColorFlag == true && isDarkMode == false) {
timeElement.style.color = 'red'; // 太文字に設定
}else if (backgroundColorFlag == false && isDarkMode == true) {
timeElement.style.color = 'yellow'; // 太文字に設定
}else{
timeElement.style.color = 'red'; // 太文字に設定
}
let tweetDateTime = new Date(timeElement.getAttribute('datetime'));
isDarkMode = loadLocalStorage_ShortName("isDarkMode");
// ツイートのURLを取得
let tweetLink = tweet.querySelector('a[href*="/status/"]').getAttribute('href');
// URLが'https://twitter.com/'で始まっているかどうかを確認
if (!tweetLink.startsWith('https://x.com/')) {
tweetLink = 'https://x.com' + tweetLink;
}
let urlCheckResult = isUrlMarked(tweetLink);
if (tweetDateTime >= startDateTime && tweetDateTime < endDateTime) {
if(Mark_On_Off == true){
//removeUrlFromIncludedMap(tweetLink);
}
if(Mark_On_Off == true && isUrlInExclusionMap(tweetLink) == true) {
try {
// let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
// ツイートが指定された時間範囲内にあるかチェック
timeElement.textContent = s1;
//markUrl(tweetLink);
// 収集対象にするボタン
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
// setCheckboxByUrl(tweetLink,true);
// addUrlAndMarkInfo(tweetLink,true);
}
} catch (error) {
console.error('Error in addCheck_Un_Mark_Lite:', error);
throw error; // スクリプトの実行を停止
}
//return;
}else{
if (tweetDateTime >= Pita_startDateTime && tweetDateTime < Pita_endDateTime) {
//let urlCheckResult = isUrlMarked(tweetLink); // URLが存在しない、またはマークされていない場合の処理
if (!urlCheckResult.existFLG || !urlCheckResult.marked){//////// URLが存在しない、またはマークされていない場合の処理
// let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
if(LastLine_tweet_URL_to_ID_Func_Lite(tweetLink) == "超〇"){
timeElement.textContent = emoji + '🏅'+ s1;
}else{
// ツイートが指定された時間範囲内にあるかチェック
timeElement.textContent = emoji + '🎯'+ s1;
}
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
setCheckboxByUrl(tweetLink,true);
addUrlAndMarkInfo(tweetLink,true);
}
return true;
}else if ( urlCheckResult.existFLG && urlCheckResult.marked){//////// (URLが存在する、かつ、)マークされている場合の処理【再描画】
// let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
if(LastLine_tweet_URL_to_ID_Func_Lite(tweetLink) == "超〇"){
timeElement.textContent = emoji + '🏅'+ s1;
}else{
// ツイートが指定された時間範囲内にあるかチェック
timeElement.textContent = emoji + '🎯'+ s1;
}
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
setCheckboxByUrl(tweetLink,true);
addUrlAndMarkInfo(tweetLink,true);
}
}
}else{
//let urlCheckResult = isUrlMarked(tweetLink);
if (!urlCheckResult.existFLG || !urlCheckResult.marked){//////// URLが存在しない、またはマークされていない場合の処理
// let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
if(CurrentNumSetting == 0){
timeElement.textContent = s1;
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
//removeUrlFromIncludedMap(tweetLink);
}else{
// setCheckboxByUrl(tweetLink,true);
// addUrlAndMarkInfo(tweetLink,true);
}
}else{
// ツイートが指定された時間範囲内にあるかチェック
timeElement.textContent = emoji + s1;
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
setCheckboxByUrl(tweetLink,true);
addUrlAndMarkInfo(tweetLink,true);
}
}
//markUrl(tweetLink);
return true;
}else if ( urlCheckResult.existFLG && urlCheckResult.marked){//////// (URLが存在する、かつ、)マークされている場合の処理【再描画】
// let s1 = toFormatedDateString(new Date(s0), tweetID);
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
if(CurrentNumSetting == 0){
timeElement.textContent = s1;
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
//removeUrlFromIncludedMap(tweetLink);
}else{
// setCheckboxByUrl(tweetLink,true);
// addUrlAndMarkInfo(tweetLink,true);
}
}else{
// ツイートが指定された時間範囲内にあるかチェック
timeElement.textContent = emoji + s1;
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
setCheckboxByUrl(tweetLink,true);
addUrlAndMarkInfo(tweetLink,true);
}
}
//markUrl(tweetLink);
}
}
}
}else{
if(Mark_On_Off == true && isUrlInIncludedMap(tweetLink)){
//if (/* !urlCheckResult.existFLG && */ !urlCheckResult.marked /* || urlCheckResult.existFLG && !span.textContent.includes('.') */ ) {//【再描画:対応】
// URLが存在しない、かつ、マークされていない場合の処理 URLが存在して、かつ、ミリ秒で使った「.(ピリオド)」が含まれてない場合
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
timeElement.textContent = "✓" + s1;
setCheckboxByUrl(tweetLink,true);
addUrlAndMarkInfo(tweetLink,true);
return false;
//}
}else{
// // 元のテキストに絵文字が含まれていない場合のみ、更新を行う
// if (!originalTextContent.startsWith('🌙') && !originalTextContent.startsWith('🌞') &&
// !originalTextContent.startsWith('✨') && !originalTextContent.startsWith('😾') &&
// !originalTextContent.startsWith("✓")) {
//let urlCheckResult = isUrlMarked(tweetLink);
//if (/* !urlCheckResult.existFLG && */ !urlCheckResult.marked /* || urlCheckResult.existFLG && !span.textContent.includes('.') */ ) {//【再描画:対応】
// URLが存在しない、かつ、マークされていない場合の処理 URLが存在して、かつ、ミリ秒で使った「.(ピリオド)」が含まれてない場合
timeElement.style.fontWeight = 'bold'; // 太文字に設定
timeElement.setAttribute('datetime', s0);
timeElement.setAttribute('local-datetime', s1);
timeElement.textContent = s1;
// a.appendChild(span);
// a.removeChild(timeElement);
if (isUrlInExclusionMap(tweetLink) == true && isUrlInIncludedMap(tweetLink) == false) {
setCheckboxByUrl(tweetLink,false);
addUrlAndMarkInfo(tweetLink,false);
}else{
// setCheckboxByUrl(tweetLink,true);
// addUrlAndMarkInfo(tweetLink,true);
}
return false;
//}
}
}
return false;
}
// tweetIDから日時オブジェクトを返す関数
function tweetID_to_DateTime(tweetID) {
// TweetIDからタイムスタンプ(ミリ秒)を計算
let timestamp = Math.floor(tweetID / 4194304) + 1288834974657;
return new Date(timestamp);
}
let tweetID_to_MS_Func = function(tweetID) {
let tweet_time = Math.floor(tweetID / 4194304) + 1288834974657;
let ms = tweet_time % 1000;
if (ms < 10) {
ms = "00" + ms;
} else if (ms < 100) {
ms = "0" + ms;
}
return ms;
}
window.addEventListener("beforeunload", function(event) {
try {
BackupOnReload_flag = loadLocalStorage_ShortName("BackupOnReload_flag");
Ver1_flag = loadLocalStorage_ShortName("Ver1_flag");
// ページが閉じる前にデータを保存する処理をここに記述
if(BackupOnReload_flag == true){
exportURLsToTextFile(true);
}
resetMarkedStatusInFilteredURLs();
} catch (error) {
// エラーが発生した場合の処理
console.error("データ保存中にエラーが発生しました:", error);
// ユーザーにエラーを通知するためのメッセージを設定
event.returnValue = "ローカルストレージデータの保存中にエラーが発生しました。「次回読込時復元」機能はOFFにする";
}
});
function SimpleAggregationMain(urlsText) {
// データを改行で分割
const LastEditData = urlsText.split('\n');
// データをパースしてオブジェクトの配列に変換
let parsedData = LastEditData.map(line => {
const parts = line.split('\t');
const url = parts[0];
const ori_Id_and_datetimeParts = parts[1];
const Id_and_datetimeParts = parts[1].split(' ');
const id = Id_and_datetimeParts[0];
const datetime = (Id_and_datetimeParts[1] + Id_and_datetimeParts[2] + Id_and_datetimeParts[3]).replace(" (月) ","").replace(" (火) ","").replace(" (水) ","").replace(" (木) ","").replace(" (金) ","").replace(" (土) ","").replace(" (日) ",""); // 日付と時刻の部分を抽出
const flag = Id_and_datetimeParts[4]; // 「X」や「〇」の部分を抽出
return { url, id, datetime, flag ,ori_Id_and_datetimeParts};
});
// 日付でソート(降順)
parsedData.sort((a, b) => new Date(a.datetime) - new Date(b.datetime));
// フラグに基づいてIDを分ける
const oFlags = ["〇", " 〇"];
const o2Flags = ["超〇", "◎"];
const xFlags = ["X", " X"];
const x2Flags = ["勇敢X"];
const pita = parsedData.filter(item => oFlags.includes(item.flag)).map(item => item.id);
const pita2 = parsedData.filter(item => o2Flags.includes(item.flag)).map(item => item.id);
const DQ = parsedData.filter(item => xFlags.includes(item.flag)).map(item => item.id);
const DQ2 = parsedData.filter(item => x2Flags.includes(item.flag)).map(item => item.id);
// IDをスペースで区切る
const pita_Text = pita.join(' ');
const pita2_Text = pita2.join(' ');
const DQ_Text = DQ.join(' ');
const DQ2_Text = DQ2.join(' ');
// 結果を組み立て
const sortedDataText = parsedData.map(item => `${item.url}\t${item.ori_Id_and_datetimeParts}`).join('\n');
let resultText = sortedDataText;
if (pita2_Text) {
resultText += `\n\n\n【超ピッタリ】${pita2_Text}`;
}
if (pita_Text) {
resultText += `\n\n\n【ピッタリ】${pita_Text}`;
}
if (DQ2_Text) {
resultText += `\n\n\n【超オシイ】${DQ2_Text}`;
}
if (DQ_Text) {
resultText += `\n\n\n【どんまい】${DQ_Text}`;
}
return resultText;
}
function exportURLsToTextFile(backUp_Flag) {
try {
// All_URLs の内容を配列に変換し、改行で連結
// 初期フラグ設定
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
BackupOnReload_flag = loadLocalStorage_ShortName("BackupOnReload_flag");
Ver1_flag = loadLocalStorage_ShortName("Ver1_flag");
let urlsText = ""; // 改行で連結
if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
// Setから配列に変換してフィルタリングと重複排除を行う
urlsText = filterAndRemoveDuplicates(All_URLs); // 改行で連結
} else {
// printUrlsWithNewline 関数を呼び出し、その結果を urlsText に割り当てる
urlsText = printUrlsWithNewline(excludeUrlsFromFilteredURLs(filteredURLs)); // この関数はfilteredURLsを引数として受け取るように定義する必要がある
urlsText += "\n";
urlsText += convertMapToText(includedURL_Map); // Mapオブジェクトをテキストに変換
urlsText = filterAndRemoveDuplicates(urlsText.split("\n"));
}
urlsText = replaceDomainInText(urlsText);
SimplifiedTotal_flag = loadLocalStorage_ShortName("SimplifiedTotal_flag");
if(SimplifiedTotal_flag){
urlsText = processTargetLines(urlsText);
urlsText = SimpleAggregationMain(urlsText)
}
if(urlsText != null && urlsText != ""){
// ブロブを作成し、URLを生成
let blob = new Blob([urlsText], {type: 'text/plain'});
let url = window.URL.createObjectURL(blob);
let now = new Date();
let formattedDateTime = now.getFullYear() + '-' +
('0' + (now.getMonth() + 1)).slice(-2) + '-' +
('0' + now.getDate()).slice(-2) + '_' +
('0' + now.getHours()).slice(-2) + '-' +
('0' + now.getMinutes()).slice(-2) + '-' +
('0' + now.getSeconds()).slice(-2);
let filename = 'ツイートのURL_'+ formattedDateTime + '.txt';
let PrevFileName = "";
if(backUp_Flag == true && BackupOnReload_flag == true){
filename = '【自動バックアップ】' + filename;
}else{
if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
filename = '【タイムライン】' + filename;
}else if(Yoruho_flag == true && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
PrevFileName = filename;
filename = convertToFullWidthDigits('【よるほー ±' + CurrentNumSetting + '秒】');
filename = filename + PrevFileName;
}else if(Yoruho_flag == false && Hiruho_flag == true && Pita334_flag == false && Nyaan_flag == false){
PrevFileName = filename;
filename = convertToFullWidthDigits('【ひるほー ±' + CurrentNumSetting + '秒】');
filename = filename + PrevFileName;
}else if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == true && Nyaan_flag == false){
PrevFileName = filename;
filename = convertToFullWidthDigits('【334 ±' + CurrentNumSetting + '秒】');
filename = filename + PrevFileName;
}else if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == true){
PrevFileName = filename;
filename = convertToFullWidthDigits('【にゃーん ±' + CurrentNumSetting + '秒】');
filename = filename + PrevFileName;
}
}
// ダウンロードリンクを作成し、クリックイベントを発火
let a = document.createElement('a');
a.href = url;
a.download = filename; // 任意のファイル名
document.body.appendChild(a);
a.click();
// 後処理
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
return
}
} catch (error) {
console.error('Error exporting URLs:', error);
throw error; // スクリプトの実行を停止
}
}
// All_URLs の内容をクリップボードにコピーする関数
function copyURLsToClipboard(backUp_Flag) {
try {
// All_URLs の内容を改行で連結して文字列に変換
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
BackupOnReload_flag = loadLocalStorage_ShortName("BackupOnReload_flag");
Ver1_flag = loadLocalStorage_ShortName("Ver1_flag");
let urlsText = ""; // 改行で連結
if(Yoruho_flag == false && Hiruho_flag == false && Pita334_flag == false && Nyaan_flag == false){
// Setから配列に変換してフィルタリングと重複排除を行う
urlsText = filterAndRemoveDuplicates(All_URLs); // 改行で連結
} else {
// printUrlsWithNewline 関数を呼び出し、その結果を urlsText に割り当てる
urlsText = printUrlsWithNewline(excludeUrlsFromFilteredURLs(filteredURLs)); // この関数はfilteredURLsを引数として受け取るように定義する必要がある
urlsText += "\n";
urlsText += convertMapToText(includedURL_Map); // Mapオブジェクトをテキストに変換
urlsText = filterAndRemoveDuplicates(urlsText.split("\n"));
}
urlsText = replaceDomainInText(urlsText);
SimplifiedTotal_flag = loadLocalStorage_ShortName("SimplifiedTotal_flag");
if(SimplifiedTotal_flag){
urlsText = processTargetLines(urlsText);
urlsText = SimpleAggregationMain(urlsText)
}
// クリップボードにコピー
navigator.clipboard.writeText(urlsText).then(function() {
if(urlsText != null && urlsText != ""){
if(backUp_Flag == false){
//alert('URLはコピーされました!');
}
}
}, function(err) {
if(urlsText != null && urlsText != ""){
if(backUp_Flag == false){
//alert('エラーが起きました:', err);
}
}
});
} catch (error) {
console.error('Error in copyURLsToClipboard:', error);
throw error; // スクリプトの実行を停止
}
};
// 重複を排除してマージする関数
function removeDuplicatesAndMerge(urlsArray) {
let uniqueUrls = []; // 重複を排除したURLを保存する配列
let seenUrls = {}; // 既に追加されたURLを記録するオブジェクト
urlsArray.forEach(url => {
if (typeof url === 'string') { // URLが文字列であることを確認
if (!seenUrls[url]) { // このURLがまだ追加されていない場合
uniqueUrls.push(url); // 配列に追加
seenUrls[url] = true; // 記録する
}
}
});
return uniqueUrls;
}
// 対象外リストに含まれるURLをAll_URLsから除外し、重複を排除する関数
function filterAndRemoveDuplicates(allUrlsSet) {
// Setから配列に変換
const urlArray = Array.from(allUrlsSet);
// 対象外リストに含まれないURLだけをフィルタリングし、重複排除
const seenUrls = new Set();
const filteredAndUniqueUrls = urlArray.filter(url => {
const isNotExcluded = !isUrlInExclusionMap(url);
const isNotSeen = !seenUrls.has(url);
if (isNotExcluded && isNotSeen) {
seenUrls.add(url); // 見つかったURLを記録
return true;
}
return false;
});
// フィルタリング後のURLリストを改行で結合して返す
return filteredAndUniqueUrls.join("\n");
}
// 非対象リストに基づいてfilteredURLsからURLを除外する関数
function excludeUrlsFromFilteredURLs(filteredURLs) {
// 非対象リストに含まれるURLを除外
return filteredURLs.filter(item => !isUrlInExclusionMap(item.url));
}
function InitFlags_and_SetCheckBox() {
// 初期フラグ設定
Yoruho_flag = loadLocalStorage_ShortName("Yoruho_flag");
Hiruho_flag = loadLocalStorage_ShortName("Hiruho_flag");
Pita334_flag = loadLocalStorage_ShortName("Pita334_flag");
Nyaan_flag = loadLocalStorage_ShortName("Nyaan_flag");
BackupOnReload_flag = loadLocalStorage_ShortName("BackupOnReload_flag");
Ver1_flag = loadLocalStorage_ShortName("Ver1_flag");
isDarkMode = loadLocalStorage_ShortName("isDarkMode");
isHidden_flag = loadLocalStorage_ShortName("toggle-button");
Mark_On_Off = loadLocalStorage_ShortName("Mark_On_Off");
SimplifiedTotal_flag = loadLocalStorage_ShortName("SimplifiedTotal_flag");
backgroundColorFlag = loadLocalStorage_ShortName("backgroundColorFlag");
voiceHelpEnabled = loadLocalStorage_ShortName("voice_Help_On_Off");
checkboxesIsVisible = loadLocalStorage_ShortName("checkboxesIsVisible");
toggleDisplayBasedOnFlag(isHidden_flag,false);
let allCheckboxes = document.querySelectorAll('input[type="checkbox"]');
allCheckboxes.forEach(function(workCheckbox) {
if(workCheckbox.id == "check_Yoruho_flag"){
if(Yoruho_flag == null) Yoruho_flag = true;
workCheckbox.checked = Yoruho_flag;
}
if(workCheckbox.id == "check_Hiruho_flag"){
if(Hiruho_flag == null) Hiruho_flag = false;
workCheckbox.checked = Hiruho_flag;
}
if(workCheckbox.id == "check_Pita334_flag"){
if(Pita334_flag == null) Pita334_flag = false;
workCheckbox.checked = Pita334_flag;
}
if(workCheckbox.id == "check_Nyaan_flag"){
if(Nyaan_flag == null) Nyaan_flag = false;
workCheckbox.checked = Nyaan_flag;
}
if(workCheckbox.id == "check_BackupOnReload_flag"){
if(BackupOnReload_flag == null) BackupOnReload_flag = false;
workCheckbox.checked = BackupOnReload_flag;
}
if(workCheckbox.id == "check_Ver1_flag"){
if(Ver1_flag == null) Ver1_flag = false;
workCheckbox.checked = Ver1_flag;
if(Ver1_flag == true){
updateVersionLabel("0.3+");
}else{
//currentVersion = "3.6";
updateVersionLabel(currentVersion);
}
}
// if(workCheckbox.id == "check_Mark_On_Off"){
// if(Mark_On_Off == null) Mark_On_Off = true;
// workCheckbox.checked = Mark_On_Off;
// }
if(workCheckbox.id == "check_SimplifiedTotal_flag"){
if(SimplifiedTotal_flag == null) SimplifiedTotal_flag = true;
workCheckbox.checked = SimplifiedTotal_flag;
}
if(workCheckbox.id == "check_checkboxesIsVisible"){
if(checkboxesIsVisible == null) checkboxesIsVisible = false;
workCheckbox.checked = checkboxesIsVisible;
}
if(workCheckbox.id == "check_voice_Help_On_Off"){
if(voiceHelpEnabled == null) voiceHelpEnabled = false;
workCheckbox.checked = voiceHelpEnabled;
}
if(workCheckbox.id == "check_isDarkMode"){
if(isDarkMode == null) isDarkMode = false;
workCheckbox.checked = isDarkMode;
// IDを使用してbuttonContainer要素を取得
let buttonContainer = document.getElementById('buttonContainer');
// isDarkModeの値に応じて、ボーダーの色を変更
if (isDarkMode) {
// ダークモードの場合は白いボーダー
buttonContainer.style.border = '1px solid white';
} else {
// ライトモードの場合は黒いボーダー
buttonContainer.style.border = '1px solid black';
}
let label = document.querySelector('label[for="check_isDarkMode"]');
// チェックボックスの状態をチェックして、ラベルのテキストを更新
if (isDarkMode) {
// チェックボックスがチェックされている場合
label.textContent = "ダークモード";
} else {
// チェックボックスがチェックされていない場合
label.textContent = "ダークモード";
}
}
});
}
// ボタンとそのアクションを追加する関数を別の方法で実装
function addActionButton(dialog, text, action) {
const button = document.createElement('button');
button.textContent = text;
button.addEventListener('click', function(event) {
action.call(this, event); // ここでactionを呼び出す際に、現在のコンテキストとイベントを渡す
});
dialog.appendChild(button);
return button; // 必要に応じてボタンを返す
}
window.addEventListener('scroll', function() {
ScrollTopDialogsByClass('Milly-custom-dialog');
});
function ScrollTopDialogsByClass(dialogClass) {
var dialogs = document.querySelectorAll('.' + dialogClass);
dialogs.forEach(function(dialog) {
// 初期表示時のtop位置を取得
const initialTop = parseInt(dialog.getAttribute('data-initial-top'), 10);
dialog.style.top = initialTop + 'px'; // 位置を調整
});
}
let CurScrollTop = 0;
// 最後のダイアログの位置を調整する関数
function scrollTopLastDialogByClass(dialogClass) {
var dialogs = document.querySelectorAll('.' + dialogClass);
if (dialogs.length > 0) {
var lastDialog = dialogs[dialogs.length - 1];
// 初期表示時のtop位置を取得
const initialTop = parseInt(lastDialog.getAttribute('data-initial-top'), 10);
CurScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 現在のスクロール位置を考慮した新しいtop位置を計算
const newTop = initialTop + CurScrollTop + 10;
lastDialog.style.top = newTop + 'px'; // 位置を調整
}
}
const observer = new MutationObserver((mutations) => {
// ダイアログの位置を再調整する関数を呼び出す
ScrollTopDialogsByClass('Milly-custom-dialog');
});
observer.observe(document.body, { childList: true, subtree: true });
// ダイアログを表示する関数の中でaddActionButtonを使用
function showCustomDialog(url, timeElement, tweet) {
// スクロール位置を取得
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// ページ全体のスクロール高さを取得
var scrollHeight = document.documentElement.scrollHeight;
let timeElementRect = timeElement.getBoundingClientRect();
// ダイアログの表示位置をビューポートとスクロール位置を考慮して計算
let dialogTop = timeElementRect.height + scrollTop + 10; // 10pxのオフセット
//let dialogTop = timeElementRect.bottom + scrollTop + 10; // 画面上での絶対位置を計算
dialogTop = 10;
const dialog = document.createElement('div');
dialog.id = 'Milly-custom-dialog';
dialog.classList.add('Milly-custom-dialog');
// ダイアログのスタイル設定...
//dialog.style.position = 'absolute';
//dialog.style.position = 'static';
//dialog.style.position = 'relative';
dialog.style.position = 'fixed';
dialog.setAttribute('data-initial-top', dialogTop.toString()); // ダイアログの位置情報を保存
// スクロール位置に基づいてダイアログのtop位置を設定
dialog.style.top = dialogTop + 'px'; // 計算した位置を設定
dialog.style.left = '50%';
dialog.style.transform = 'translateX(-50%)';
dialog.style.border = '1px solid #ccc';
dialog.style.padding = '10px';
dialog.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
// if (isDarkMode) {
// dialog.style.backgroundColor = 'white';
// dialog.style.color = 'white';
// } else {
// dialog.style.backgroundColor = 'black';
// dialog.style.color = 'white';
// }
Ret_detectMode = detectMode();
if(Ret_detectMode == "dark"){
dialog.style.backgroundColor = 'white';
dialog.style.color = 'white';
dialog.style.borderColor = 'black'; // ボーダー色
}else{
dialog.style.backgroundColor = 'black';
dialog.style.color = 'black';
dialog.style.borderColor = 'white'; // ボーダー色
}
// 収集対象にするボタン
addActionButton(dialog, '対象にする', function() {
if (isUrlInExclusionMap(url)) {
removeUrlFromExclusionMap(url);
}
if (!isUrlInIncludedMap(url)) {
addUrlToIncludedMap(url);
}
processTweets();
});
addSmallSpacer(dialog);
// 対象外にするボタン
addActionButton(dialog, '対象外に', function() {
if (!isUrlInExclusionMap(url)) {
addUrlToExclusionMap(url);
}
if (isUrlInIncludedMap(url)) {
removeUrlFromIncludedMap(url);
}
processTweets();
});
addSmallSpacer(dialog);
// 閉じるボタンの追加
addActionButton(dialog, '閉じる', function() {
// ここではアロー関数ではなく、通常の関数を使用しています
// この関数内の `this` は、イベントリスナーによってクリックされたボタンを指します
var dialog = this.closest('.Milly-custom-dialog');
if (dialog && dialog.parentNode) {
dialog.parentNode.removeChild(dialog);
}
});
// addSmallSpacer(dialog);
// // 閉じるボタンの追加
// addActionButton(dialog, '全て閉じる', function() {
// // ここではアロー関数ではなく、通常の関数を使用しています
// closeAndRemoveDialogsByClass('Milly-custom-dialog');
// });
document.body.appendChild(dialog);
}
function removeDialog() {
// 'this' はイベントが割り当てられたボタンを指す
// ボタンの親要素(ダイアログ)を探し、それを削除する
var dialog = this.closest('.Milly-custom-dialog'); // ボタンが含まれる最も近い '.dialog' 要素を探す
if (dialog && dialog.parentNode) {
dialog.parentNode.removeChild(dialog);
}
}
function closeAndRemoveDialogsByClass(dialogClass) {
var dialogs = document.querySelectorAll('.' + dialogClass);
dialogs.forEach(function(dialog) {
if (dialog && dialog.parentNode) {
dialog.parentNode.removeChild(dialog);
}
});
}
function closeAndRemoveDialog(dialogId) {
var dialog = document.getElementById(dialogId);
if (dialog && dialog.parentNode) {
dialog.parentNode.removeChild(dialog);
}
}
function removeDuplicatePathsInExclusionMap() {
const uniquePaths = new Map();
// exclusionURL_Mapの各エントリを反復処理
exclusionURL_Map.forEach((value, key) => {
// パスがすでにuniquePathsマップに存在するかチェック
if (!uniquePaths.has(key)) {
// 存在しない場合、そのパスとURLをuniquePathsに追加
uniquePaths.set(key, value);
} else {
// 重複したパスが見つかった場合の処理(必要に応じて)
// この例では何もしないが、必要に応じて重複したエントリを特定の基準で処理することが可能
}
});
// 重複を排除したマップを元のマップに再割り当て
exclusionURL_Map = uniquePaths;
}
// 対象外URLリストを保持するグローバル変数
//var exclusionURL_List = [];
// URLのパスを抽出する関数
function extractPath(url) {
const regex = /https:\/\/(?:twitter\.com|x\.com)\/(?:#!\/)?(\w+)\/status\/(\d+)/;
const match = url.match(regex);
// マッチした場合、ユーザー名とツイートIDを連結して返す
return match ? `${match[1]}/status/${match[2]}` : null;
}
// 対象外URLマップを保持するグローバル変数
var exclusionURL_Map = new Map();
// URLを対象外リストに追加する関数
function addUrlToExclusionMap(url) {
const path = extractPath(url);
if (path && !exclusionURL_Map.has(path)) {
exclusionURL_Map.set(path, url);
return true; // 追加された場合はtrueを返す
}
return false; // 既に存在する場合はfalseを返す
}
// URLを対象外リストから削除する関数
function removeUrlFromExclusionMap(url) {
const path = extractPath(url);
if (path && exclusionURL_Map.has(path)) {
exclusionURL_Map.delete(path);
return true; // 削除された場合はtrueを返す
}
return false; // 存在しない場合はfalseを返す
}
// URLが対象外リストに存在するかチェックする関数
function isUrlInExclusionMap(url) {
const path = extractPath(url);
return exclusionURL_Map.has(path);
}
// URLの対象状態を切り替える関数
function toggleUrlInExclusionMap(url) {
if (isUrlInExclusionMap(url)) {
removeUrlFromExclusionMap(url);
return false; // 削除された場合はfalseを返す
} else {
addUrlToExclusionMap(url);
return true; // 追加された場合はtrueを返す
}
}
// マーキングしたURLマップを保持するグローバル変数
var includedURL_Map = new Map();
// URLをマーキングリストに追加する関数
function addUrlToIncludedMap(url) {
const path = extractPath(url);
if (path && !includedURL_Map.has(path)) {
includedURL_Map.set(path, url);
return true; // 追加された場合はtrueを返す
}
return false; // 既に存在する場合はfalseを返す
}
// URLをマーキングリストから削除する関数
function removeUrlFromIncludedMap(url) {
const path = extractPath(url);
if (path && includedURL_Map.has(path)) {
includedURL_Map.delete(path);
return true; // 削除された場合はtrueを返す
}
return false; // 存在しない場合はfalseを返す
}
// URLがマーキングリストに存在するかチェックする関数
function isUrlInIncludedMap(url) {
const path = extractPath(url);
return includedURL_Map.has(path);
}
// URLのマーキング状態を切り替える関数
function toggleUrlInIncludedMap(url) {
if (isUrlInIncludedMap(url)) {
removeUrlFromIncludedMap(url);
return false; // 削除された場合はfalseを返す
} else {
addUrlToIncludedMap(url);
return true; // 追加された場合はtrueを返す
}
}
// Mapオブジェクトの内容を\nで区切られたテキストに変換する関数
function convertMapToText(includedURL_Map) {
let text = '';
for (let url of includedURL_Map.values()) {
text += url + '\n'; // 各URLをテキストに追加し、\nで改行
}
return text.trim(); // 末尾の改行を削除
}
let messageCount = {}; // メッセージごとの再生回数を管理するオブジェクト
const MAX_COUNT = 2; // メッセージの最大再生回数
function text_to_speak(text,always_speak_flag) {
if ('speechSynthesis' in window) {
if (text !== '') {
// メッセージの再生回数をカウント
if (!messageCount[text]) {
messageCount[text] = 0;
}
messageCount[text]++;
// 再生回数が最大回数を超えていない場合に再生
if (messageCount[text] <= MAX_COUNT || always_speak_flag == true) {
if(voiceHelpEnabled){
speechSynthesis.cancel(); // 再生中の音声をストップ
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ja-JP'; // 日本語を指定
speechSynthesis.speak(utterance);
}
} else {
//console.log(`メッセージ "${text}" は最大再生回数に達しました。`);
}
}
} else {
//console.log('このブラウザはWeb Speech APIをサポートしていません。最新のブラウザを使用してください。');
}
}
const versionInfoUrlBase = 'https://spark.littlestar.jp/public/AppVersion/Add_ms_To_X_TL_Version.json';
// ポップアップを表示する関数
function showPopup(message, data) {
let popup = document.createElement('div');
popup.id = 'custom-popup'; // ユニークなIDを設定
// ▼ 視認性・反応性の向上スタイル
popup.style.position = 'fixed';
popup.style.top = '10px';
popup.style.right = '10px';
popup.style.backgroundColor = '#00008B';
popup.style.color = 'white';
popup.style.padding = '20px';
popup.style.borderRadius = '5px';
popup.style.zIndex = '9999'; // 他より上に表示
popup.style.maxWidth = '300px';
popup.style.fontSize = '14px';
popup.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
popup.style.pointerEvents = 'auto'; // 念のため明示
popup.innerHTML = `${message}`;
if (data != null) {
let downloadLink = document.createElement('label');
downloadLink.textContent = "みりほー補助ツール最新版 [ " + data.latest_version + " ] はこちら🔗";
downloadLink.id = 'downloadLink';
downloadLink.style.fontWeight = 'bold';
downloadLink.style.color = 'white';
downloadLink.style.backgroundColor = 'black';
downloadLink.style.cursor = 'pointer';
downloadLink.style.padding = '10px';
downloadLink.style.display = 'block';
downloadLink.style.margin = '10px 0';
downloadLink.classList.add('control-item-002');
downloadLink.addEventListener('click', function() {
window.open('https://spark.littlestar.jp/public/Ver4_Add_Milliseconds_To_Twitter_TL.html', '_blank');
});
popup.appendChild(downloadLink);
}
let closeButton = document.createElement('button');
closeButton.textContent = '閉じる';
closeButton.style.color = 'white';
closeButton.style.backgroundColor = 'black';
closeButton.style.padding = '10px 20px';
closeButton.style.fontSize = '16px';
closeButton.style.borderRadius = '6px';
closeButton.style.margin = '20px auto 0 auto';
closeButton.style.display = 'block';
closeButton.style.cursor = 'pointer';
closeButton.onclick = function() {
let popupElement = document.getElementById('custom-popup');
if (popupElement) {
document.body.removeChild(popupElement);
}
};
popup.appendChild(closeButton);
document.body.appendChild(popup);
}
// 日付をフォーマットする関数
function formatDate_for_ShowPopup(date) {
let hours = String(date.getHours()).padStart(2, '0');
let minutes = String(date.getMinutes()).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
let month = String(date.getMonth() + 1).padStart(2, '0'); // 月は0から始まるため+1
let year = date.getFullYear();
return `${hours}:${minutes} ${year}/${month}/${day}`;
}
function isOlderVersion(currentVersion, savedVersion) {
const currentParts = currentVersion.split('.').map(n => parseInt(n, 10));
const savedParts = savedVersion.split('.').map(n => parseInt(n, 10));
const maxLength = Math.max(currentParts.length, savedParts.length);
// 足りない桁を0で埋める
while (currentParts.length < maxLength) currentParts.push(0);
while (savedParts.length < maxLength) savedParts.push(0);
for (let i = 0; i < maxLength; i++) {
if (currentParts[i] < savedParts[i]) return true;
if (currentParts[i] > savedParts[i]) return false;
}
return false; // バージョンが同じ場合
}
let LastShowPopupVer = loadLocalStorage_LongName("LastShowPopupVer");
// バージョン情報をチェックする関数
function checkVersion() {
const versionInfoUrl = versionInfoUrlBase + '?_=' + new Date().getTime();
GM_xmlhttpRequest({
method: 'GET',
url: versionInfoUrl,
onload: function(response) {
if (response.status === 200) {
//LastShowPopupVer = loadLocalStorage_LongName("LastShowPopupVer");
let data = JSON.parse(response.responseText);
let lastNotificationTimeStr = GM_getValue('lastNotificationTime', null);
let lastNotificationTime = lastNotificationTimeStr ? new Date(lastNotificationTimeStr) : new Date(0);
let currentTime = new Date();
let notificationInterval = 24 * 60 * 60 * 1000; // 24時間をミリ秒で
if (data.latest_version == currentVersion)
{
if(LastShowPopupVer == ""){
LastShowPopupVer = currentVersion + ",";
saveLocalStorage("LastShowPopupVer",LastShowPopupVer);
}
else if (LastShowPopupVer.includes(currentVersion + ","))
{
//if(data.forced_notification == false && ShowPopupFLG == false) return;
if(data.forced_notification == false) return;
}
else
{
LastShowPopupVer = currentVersion + ",";
saveLocalStorage("LastShowPopupVer",LastShowPopupVer);
}
}
// 最新バージョンがある場合
////////////////////if (data.latest_version !== currentVersion) {
if (isOlderVersion(currentVersion, data.latest_version)){
showPopup("最新バージョン [ " + data.latest_version + " ] が利用可能です。", data);
return;
}else{
return;
}
// 強制通知または前回通知から24時間経過している場合
//if (data.forced_notification || (currentTime - lastNotificationTime > notificationInterval)) {
GM_setValue('lastNotificationTime', currentTime.toISOString());
// 無効化するバージョンの確認
if (data.disable_scripts.includes(currentVersion)) {
showPopup("このスクリプトバージョンは\n無効化されています。\n\n最新バージョンにアップデート\nしてください。",data);
let buttonContainer = document.querySelector('#buttonContainer');
if (buttonContainer) {
while (buttonContainer.firstChild) {
buttonContainer.removeChild(buttonContainer.firstChild);
}
buttonContainer.style.display = 'none';
}
return;
}
// 新しいお知らせがある場合
if (data.notification_version.includes(currentVersion)) {
showPopup(data.notification_content, data);
return;
} else {
showPopup("自動でアップデート情報を確認しました。\n(アップデート情報はありません)", data);
return;
}
//}
} else {
console.error('Error fetching version information:', response.statusText);
}
},
onerror: function(error) {
console.error('Error fetching version information:', error);
}
});
}
// // スクリプトが読み込まれたときにバージョンチェックを実行
// window.addEventListener('load', checkVersion);
setInterval(processTweets, 1000);
const CHECK_INTERVAL = 60 * 60 * 1000; // 1時間ごとにチェック
// 定期的にチェック
setInterval(checkVersion, CHECK_INTERVAL);
//======================================================================================
// グローバルにチェックボックスを管理する配列
let checkboxes = [];
// 初回読み込み時にタイムエレメントでチェックを入れたURLを保持する配列
let initiallyCheckedUrls = [];
// メモリ上のチェックされたツイートのURLを保持する配列
let checkedUrls = JSON.parse(localStorage.getItem('checkedUrls')) || [];
// チェックボックスの状態が変わった時のハンドラー
function onCheckboxChange(event) {
let checkbox = event.target;
let tweet = checkbox.closest('article');
if (tweet) {
let tweetLink = tweet.querySelector('a[href*="/status/"]');
if (tweetLink) {
const url = tweetLink.href;
console.log('URL:', url); // デバッグ用にURLを出力
if (checkbox.checked) {
// チェックが入った場合、URLを配列に追加
if (!checkedUrls.includes(url)) {
checkedUrls.push(url);
localStorage.setItem('checkedUrls', JSON.stringify(checkedUrls));
}
Mark_On_Off = true;
// 収集対象にするボタン
if (isUrlInExclusionMap(url)) {
removeUrlFromExclusionMap(url);
}
if (!isUrlInIncludedMap(url)) {
addUrlToIncludedMap(url);
}
//addUrlAndMarkInfo(url,true);
processTweets();
} else {
// チェックが外れた場合、URLを配列から削除
checkedUrls = checkedUrls.filter(checkedUrl => checkedUrl !== url);
localStorage.setItem('checkedUrls', JSON.stringify(checkedUrls));
Mark_On_Off = true;
// 対象外にするボタン
if (!isUrlInExclusionMap(url)) {
addUrlToExclusionMap(url);
}
if (isUrlInIncludedMap(url)) {
removeUrlFromIncludedMap(url);
}
//addUrlAndMarkInfo(url,false);
processTweets();
}
// チェックボックスが操作された場合、initiallyCheckedUrlsから削除
initiallyCheckedUrls = initiallyCheckedUrls.filter(checkedUrl => checkedUrl !== url);
}
}
console.log(checkedUrls); // 結果をコンソールに出力(デバッグ用)
}
// チェックボックスを追加する関数
function addCheckboxes() {
// ツイート欄をすべて取得
let tweets = document.querySelectorAll('article[data-testid="tweet"]');
tweets.forEach((tweet, index) => {
// チェックボックスが既に追加されているか確認
if (tweet.querySelector('.custom-checkbox')) {
return; // 既に追加されている場合はスキップ
}
// チェックボックス要素とラベル要素を作成
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'custom-checkbox';
checkbox.style.marginRight = '10px';
checkbox.id = 'custom-checkbox-' + index; // 一意のIDを設定
// チェックボックスの色を設定
if (backgroundColorFlag && isDarkMode) {
checkbox.style.accentColor = 'yellow';
} else if (backgroundColorFlag && !isDarkMode) {
checkbox.style.accentColor = 'red';
} else if (!backgroundColorFlag && isDarkMode) {
checkbox.style.accentColor = 'yellow';
} else {
checkbox.style.accentColor = 'red';
}
let tweetLink = tweet.querySelector('a[href*="/status/"]');
let labelText = document.createElement('span');
labelText.textContent = '収集する';
// ラベルの文字色を設定
if (backgroundColorFlag && isDarkMode) {
labelText.style.color = 'yellow';
} else if (backgroundColorFlag && !isDarkMode) {
labelText.style.color = 'red';
} else if (!backgroundColorFlag && isDarkMode) {
labelText.style.color = 'yellow';
} else {
labelText.style.color = 'red';
}
if (tweetLink) {
// チェックボックスとラベルをグローバル配列に追加
const url = tweetLink.href;
console.log('URL:', url); // デバッグ用にURLを出力
checkboxes.push({ checkbox: checkbox, url: url, labelText: labelText });
// 初期状態を設定
const checkboxState = getCheckboxStateByUrl(url);
if (checkboxState !== null) {
setCheckboxByUrl(url, checkboxState);
//addUrlAndMarkInfo(url, checkboxState);
}
}
// チェックボックスの状態変化イベントリスナーを追加
checkbox.addEventListener('change', onCheckboxChange);
let label = document.createElement('label');
label.className = 'custom-checkbox-label';
label.style.marginRight = '10px';
//label.style.minHeight = '15px'; // 最小高さを設定してレイアウトの変動を防ぐ
label.style.maxHeight = '15px'; // 最小高さを設定してレイアウトの変動を防ぐ
label.style.display = 'flex'; // フレックスボックスに設定
label.style.alignItems = 'center'; // 縦方向の中央揃えに設定
//label.setAttribute('for', 'custom-checkbox-' + index); // `for`属性にチェックボックスのIDを設定
label.appendChild(checkbox);
label.appendChild(labelText);
// ツイートの右下にチェックボックスとラベルを追加
let tweetFooter = tweet.querySelector('[role="group"]');
if (tweetFooter) {
tweetFooter.prepend(label);
}
});
}
// URLをキーにチェックボックスを操作する関数
function setCheckboxByUrl(url, isChecked) {
checkboxes.forEach(item => {
if (item.url === url) {
item.checkbox.checked = isChecked;
if (isChecked) {
if (!checkedUrls.includes(item.url)) {
checkedUrls.push(item.url);
localStorage.setItem('checkedUrls', JSON.stringify(checkedUrls));
}
} else {
checkedUrls = checkedUrls.filter(checkedUrl => checkedUrl !== item.url);
localStorage.setItem('checkedUrls', JSON.stringify(checkedUrls));
}
// チェックボックスがセットされた場合、initiallyCheckedUrlsから削除
initiallyCheckedUrls = initiallyCheckedUrls.filter(checkedUrl => checkedUrl !== url);
}
});
console.log(checkedUrls); // デバッグ用にコンソールに出力
}
// URLをキーにチェックボックスの状態を取得する関数
function getCheckboxStateByUrl(url) {
const item = checkboxes.find(item => item.url === url);
if (item) {
return item.checkbox.checked;
} else {
return initiallyCheckedUrls.includes(url) ? true : checkedUrls.includes(url) ? false : null;
}
}
// チェックボックスの状態をクリアする関数
function clearCheckboxStates() {
checkedUrls = [];
localStorage.removeItem('checkedUrls');
initiallyCheckedUrls = [];
checkboxes.forEach(item => {
item.checkbox.checked = false;
});
console.log('Cleared all checkbox states');
}
// チェックボックスとラベルの色を設定する関数
function updateCheckboxColors() {
checkboxes.forEach(item => {
// チェックボックスの色を設定
if (backgroundColorFlag && isDarkMode) {
item.checkbox.style.accentColor = 'yellow';
item.labelText.style.color = 'yellow';
} else if (backgroundColorFlag && !isDarkMode) {
item.checkbox.style.accentColor = 'red';
item.labelText.style.color = 'red';
} else if (!backgroundColorFlag && isDarkMode) {
item.checkbox.style.accentColor = 'yellow';
item.labelText.style.color = 'yellow';
} else {
item.checkbox.style.accentColor = 'red';
item.labelText.style.color = 'red';
}
});
}
// 初期ロード時と新しいツイートがロードされた時にチェックボックスを追加
function init() {
setTimeout(function() {
if (Ver1_flag == false && checkboxesIsVisible == true) {
addCheckboxes();
}
}, 5000); // 5000ミリ秒 (5秒)
// MutationObserverを使用して新しいツイートが追加された時にチェックボックスを追加
let observer = new MutationObserver(() => {
if(Ver1_flag == false && checkboxesIsVisible == true){
addCheckboxes();
}
updateCheckboxColors();
});
observer.observe(document, { childList: true, subtree: true });
}
window.addEventListener('load', init);
// グローバル関数として公開
window.setCheckboxByUrl = setCheckboxByUrl;
window.getCheckboxStateByUrl = getCheckboxStateByUrl;
window.clearCheckboxStates = clearCheckboxStates;
let checkboxesIsVisible = false; //変更 2025.02.23
// チェックボックスの表示/非表示を切り替える関数
function toggleCheckboxes() {
// 現在のスクロール位置を記憶
const scrollPosition = window.scrollY;
let w_checkboxesVisible = checkboxesIsVisible;
if(w_checkboxesVisible) addCheckboxes();
if(Ver1_flag == true && w_checkboxesVisible == true){
w_checkboxesVisible = false;
}
document.querySelectorAll('.custom-checkbox-label').forEach(label => {
label.style.display = w_checkboxesVisible ? 'inline-block' : 'none';
});
// スクロール位置を復元
window.scrollTo(0, scrollPosition);
}
//======================================================================================
// document.addEventListener("DOMContentLoaded", function() {
// addButtonsToBottom();
// });
let isNotCustomize_and_NoBackground = false;
// ページ読み込み完了時に 'よるほー' ボタンを追加
window.onload = function() {
// 初回チェック
checkVersion();
//loadLocalStorage(('check_BackupOnReload_flag', true);
addButtonsToBottom();
InitFlags_and_SetCheckBox();
////applyBlackAndWhiteTheme(container.id,'Milly-custom-dialog','control-item');
Ret_detectMode = detectMode();
toggleButtonVisibility('bgColorButton',isHidden_flag);
toggleBackgroundColor(false);
Mark_On_Off = true;
toggleMark_On_Off(false);
Mark_On_Off = true;
if((Ret_detectMode == "dark" && isDarkMode == true
|| Ret_detectMode == "light" && isDarkMode == false) && backgroundColorFlag == false){
isNotCustomize_and_NoBackground = true;
}else{
isNotCustomize_and_NoBackground = false;
}
getDatePickerValue = loadLocalStorage_LongName("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
CurrentNumSetting = loadLocalStorage_LongName("targetCurrentNumSetting",CurrentNumSetting);
applyStyleToContainerButtons('buttonContainer', Ver1_flag);
updateButtonVisibility(CurrentNumSetting);
if(getDatePickerValue != "") {
setDatePickerValue = true;
if(getDatePickerValue != getTodayDateString("",false)){
setDatePickerValue = true;
// getDatePickerValueが有効な値を持っている場合に処理
if(getDatePickerValue !== null && getDatePickerValue !== "" && setDatePickerValue === true){
// 確認ダイアログのメッセージで使用
const confirmationMessage = `「 対象日付ボタン 」(カレンダーピッカー)で取得した日を対象に処理しますか?\n`
+ `「 OK 」を押すと ${formatDate(getDatePickerValue)} が対象にセットされます。\n`
+ `「今日(直近)」を押すと ${formatDate(new Date())} をセットし\n`
+ `ピタリ判定および対象ツイートのURLを集めます。\n\n`
+ '(「 よるほー ±1秒 」 に✓を入れるなど、最低1つ✓を入れてください)' ;
const message = `「 対象日付ボタン 」(カレンダーピッカー)で取得した日を対象に処理できます\n`
+ `「 取得した日付をセット 」を押すと日付が対象にセットされます。\n`
+ `「 閉じる 」を押すとキャンセルします。\n`;
// const confirmation = confirm(message);
text_to_speak(message, true);
const confirmation = confirm(
confirmationMessage
);
if (confirmation) {
// datePickerから取得した日付をtodayに設定
setDatePickerValue = true;
if(getDatePickerValue != ""){
saveLocalStorage("%getDatePickerValue",getDatePickerValue);
updateLegendWithSelectedDate(getDatePickerValue);
}
}else{
// ここでtodayに今日の日付を再設定する場合
getDatePickerValue = "";
setDatePickerValue = false;
saveLocalStorage("%getDatePickerValue","");
updateLegendWithSelectedDate(getDatePickerValue);
}
}
}
}
};
})();