// ==UserScript== // @name HomeTL/List/User/Searchページを自動更新する // @namespace http://coltpythonkingcobra.g1.xrea.com/pseudoTweetdeck/ // @version 1.1.09150000 // @description TwitterWEBの各種ページの自動更新(停止可能)、タイトル文字列の置換と絶対時間表示などを行う // @author // @match https://twitter.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com // @grant none // ==/UserScript== /* //ログファイルの先頭プリフィクス "R","G","B" などの文字列を設定、ログ出力なしならnullにする (filePrx判定すら重くなる原因なので不使用時はコメントアウトしておきます) var filePfx = null; //var filePfx = "G"; */ //グローバル定義時用の汎用カウンタ var def_idx = 0; /************************************************************ 変更される事を考慮した各種const定義(var定義は変更しない事) ************************************************************/ //短周期タイマを一定時間後に停止する=1, 停止させず回ったままにする=0 (短周期タイマ切り替えボタンのクリックで変更可能) const defalutIdleShortTimer = 1; var idleShortTimer = defalutIdleShortTimer;//※ここは変更しない //自動更新OFFモード スクロール位置やエディットのフォーカスによらず強制OFF=1, 従来の動作=0(自動更新ON/OFF切り替えボタンのクリックで変更可能) const defaultAutoOff = 0; var autoOff = defaultAutoOff;//※ここは変更しない //短周期タイマの常時動作ON/自動更新強制OFF の切り替え時に確認ダイアログを出す=1, 出さない=0 const displayChgDialog = 0; //DOM更新の最終タイムスタンプを自動更新OFFでも表示する=1, 表示しない=0 const enableTimeAutoOff = 1; //既読ポイント用のチェックボックスを表示する=1, 表示しない=0 const enableReadPointCheckBox = 1; //表示部クリックで手動更新した時に表示されている最新アーティクルを既読マークする=1, しない=0 const defaultManualUpdateAutoCheck = 1; const manualUpdateAutoCheck = (enableReadPointCheckBox) ? defaultManualUpdateAutoCheck: 0;//※ここは変更しない //同一のアーティクルをTLに複数表示しない(RTが並ばなくなる)表示しない=1, 表示する=0 const notDispSameArticle = 1; //検索ページの「話題のツイート」を使用しない=1, 使用する=0(カスタムCSSでの設定に合わせて下さい) const notDispHotTopicSearch = 1; //デバッグ用の情報を表示部に出す=1, 出さない=0 const dispDebugInfo = 1; //更新間隔定義(秒)=これを初期値として個別ページでインターバル変更ボタンをクリックすることで値の入力・保存が可能、最大3600だが強制再起動が先に掛かる可能性あり const updateIntervalHome = 30; const updateIntervalList = 300; const updateIntervalUser = 600; const updateIntervalSear = 180; //更新間隔 [ unknown(未使用), Home, List, User、Notification(未使用), Search ] const defaultInterval = [1000, parseInt(updateIntervalHome * 1000), parseInt(updateIntervalList * 1000), parseInt(updateIntervalUser * 1000), 1000, parseInt(updateIntervalSear * 1000)]//※ここは変更しない var updateInterval = defaultInterval;//※ここは変更しない //短周期タイマを止めるまでの経過時間(ミリ秒) const shortTimerIdle = 10000; //起動時待ち合わせ時間(ミリ秒) const initWait = 10000; //短周期タイマの間隔(ミリ秒) const shortInterval = 250; //スクロール位置が先頭に戻った後の短縮更新待ちあわせ時間(ミリ秒) const onTopWait = 3000; //強制リロード時の待ち合わせ時間(ミリ秒) const reloadWait = 3000; //強制リロード実施間隔初期値(秒)=これを初期値として個別ページでインターバル変更ボタンを右クリックすることで値の入力・保存が可能、最大7200だがTwitter独自の再起動が先に掛かる可能性あり const defaultReloadTimeout = 3600; var reloadTimeout = defaultReloadTimeout;//※ここは変更しない //手動で更新をかけない経過時間(秒) ※自動更新インターバルの最小値としても使われる ※最後に更新をかけた時から一定時間以内は更新せず既読ポイントをつけるだけ const defaultManualUpdateInterval = 15; var manualUpdateInterval = defaultManualUpdateInterval;//※ここは変更しない //他のタブが強制リロードをしたら一定時間以内に強制リロードしない間隔時間(秒) const forceUpdateLockTime = 120; //マークする件数(最大99までにする事、ローカル保存データのキーを2桁にしているため) const numberReadArticles = 5; /***** 色関係の定義を変えたらヘルプダイアログ中の説明文も変える *****/ const COLOR_GLAY = "rgba(255,255,255,0.3)";//初期色(非稼働) const COLOR_BLACK = "rgba(0,0,0,1.0)";//完全非表示にする場合 const COLOR_WHITE = "rgba(255,255,255,1.0)";//モードの切り替えなどのメッセージ用 const COLOR_BORDER = "rgba(31,31,31,1.0)";//表示部枠の色 //情報表示部の色 const COLOR_UPDATE = "rgba(191,63,63,1.0)";//自動更新トリガ時の色 const COLOR_UPSOON = "rgba(191,191,63,1.0)";//自動更新トリガまで10秒以内の色 const COLOR_ACTIVE = "rgba(63,191,63,1.0)";//自動更新モード有効時の色 const COLOR_PAUSED = "rgba(63,63,191,1.0)";//自動更新の一時停止状態の色 const COLOR_HOLDTM = "rgba(63,191,191,1.0)";//手動更新抑止期間の色 const COLOR_RELOAD = "rgba(191,63,191,1.0)";//強制自動リロード10秒前の色 //インターバル設定ボタンの色 const COLOR_INT_DISABL = "rgba(127,127,127,0.5)";//初期値(非稼働) const COLOR_INT_UPDATE = "rgba(191,63,63,1.0)";//自動更新トリガ時の色 const COLOR_INT_UPSOON = "rgba(191,191,63,1.0)";//自動更新トリガまで10秒以内の色 const COLOR_INT_ACTIVE = "rgba(63,191,63,1.0)";//自動更新モード有効時の色 const COLOR_INT_PAUSED = "rgba(63,63,191,1.0)";//自動更新の一時停止状態の色 const COLOR_INT_HOLDTM = "rgba(63,191,191,1.0)";//手動更新抑止期間の色 const COLOR_INT_RELOAD = "rgba(191,63,191,1.0)";//強制自動リロード10秒前の色 //ヘルプボタンの色 const COLOR_HLP_DISABL = "rgba(127,127,127,0.5)";//初期値(非稼働) const COLOR_HLP_UPDATE = "rgba(191,63,63,0.5)";//自動更新トリガ時の色 const COLOR_HLP_UPSOON = "rgba(191,191,63,0.5)";//自動更新トリガまで10秒以内の色 const COLOR_HLP_ACTIVE = "rgba(63,191,63,0.5)";//自動更新モード有効時の色 const COLOR_HLP_PAUSED = "rgba(63,63,191,0.5)";//自動更新の一時停止状態の色 const COLOR_HLP_HOLDTM = "rgba(63,191,191,0.5)";//手動更新抑止期間の色 const COLOR_HLP_RELOAD = "rgba(191,63,191,0.5)";//強制自動リロード10秒前の色 //短周期タイマ変更ボタンの色 const COLOR_ALW_UPDATE = "rgba(191,63,63,0.5)";//短周期タイマ常時稼動モード状態で自動更新トリガ時の色 const COLOR_ALW_UPSOON = "rgba(191,191,63,0.5)";//短周期タイマ常時稼動モード状態で自動更新トリガまで10秒以内の色 const COLOR_ALW_ACTIVE = "rgba(63,191,63,0.5)";//短周期タイマ常時稼動モード状態で自動更新モード有効時の色 const COLOR_ALW_PAUSED = "rgba(63,63,191,0.5)";//短周期タイマ常時稼動モード状態で自動更新の一時停止状態の色 const COLOR_ALW_HOLDTM = "rgba(63,191,191,0.5)";//短周期タイマ常時稼動モード状態で手動更新抑止期間の色 const COLOR_ALW_RELOAD = "rgba(191,63,191,0.5)";//短周期タイマ常時稼動モード状態で強制自動リロード10秒前の色 const COLOR_IDL_DISABL = "rgba(127,127,127,0.5)";//短周期タイマの一時停止状態の色 const COLOR_IDL_UPDATE = "rgba(191,63,63,1.0)";//短周期タイマ適宜停止モード状態で自動更新トリガ時の色 const COLOR_IDL_UPSOON = "rgba(191,191,63,1.0)";//短周期タイマ適宜停止モード状態で自動更新トリガまで10秒以内の色 const COLOR_IDL_ACTIVE = "rgba(63,191,63,1.0)";//短周期タイマ適宜停止モード状態で自動更新モード有効時の色 const COLOR_IDL_PAUSED = "rgba(63,63,191,1.0)";//短周期タイマ適宜停止モード状態で自動更新の一時停止状態の色 const COLOR_IDL_HOLDTM = "rgba(63,191,191,1.0)";//手動更新抑止期間の色短周期タイマ適宜停止モード状態で const COLOR_IDL_RELOAD = "rgba(191,63,191,1.0)";//短周期タイマ適宜停止モード状態で強制自動リロード10秒前の色 //自動更新ON/OFFボタンの色 const COLOR_AUT_DISABL = "rgba(127,127,127,0.5)";//自動更新OFF時の色 const COLOR_AUT_UPDATE = "rgba(191,63,63,1.0)";//自動更新ON時で自動更新トリガ時の色 const COLOR_AUT_UPSOON = "rgba(191,191,63,1.0)";//自動更新ON時で自動更新トリガまで10秒以内の色 const COLOR_AUT_ACTIVE = "rgba(63,191,63,1.0)";//自動更新ON時で自動更新モード有効時の色 const COLOR_AUT_PAUSED = "rgba(63,63,191,1.0)";//自動更新ON時で自動更新の一時停止状態の色 const COLOR_AUT_HOLDTM = "rgba(63,191,191,1.0)";//自動更新ON時で手動更新抑止期間の色 const COLOR_AUT_RELOAD = "rgba(191,63,191,1.0)";//自動更新ON時で強制自動リロード10秒前の色 //既読ポイントから外したアーティクルの背景色(通常色) const READ_COLOR_DISABLED = COLOR_BLACK; //既読ポイントとしてチェックしたアーティクルの背景色 const DEF_READ_COLOR_ENABLED1 = "rgba(255,255,255,0.1)"; const DEF_READ_COLOR_ENABLED2 = "rgba(255,255,255,0.2)"; const DEF_READ_COLOR_ENABLED3 = "rgba(255,255,255,0.3)"; var READ_COLOR_ENABLED = [];//※ここは変更しない var READ_COLOR_ENABLED1 = [];//※ここは変更しない var READ_COLOR_ENABLED2 = [];//※ここは変更しない var READ_COLOR_ENABLED3 = [];//※ここは変更しない var READ_COLOR_ENABLED4 = [];//※ここは変更しない const readColorPattern = 4;//※ここは変更しない //既読ポイント背景色パターン1 READ_COLOR_ENABLED1 = [DEF_READ_COLOR_ENABLED1, DEF_READ_COLOR_ENABLED1, DEF_READ_COLOR_ENABLED1, DEF_READ_COLOR_ENABLED1, DEF_READ_COLOR_ENABLED1]; //既読ポイント背景色パターン2 READ_COLOR_ENABLED2 = [DEF_READ_COLOR_ENABLED2, "rgba(255,191,191,0.2)", "rgba(191,255,191,0.2)", "rgba(191,191,255,0.2)", DEF_READ_COLOR_ENABLED2]; //既読ポイント背景色パターン3 READ_COLOR_ENABLED3 = [DEF_READ_COLOR_ENABLED3, "rgba(191,255,255,0.3)", "rgba(255,191,255,0.3)", "rgba(255,255,191,0.3)", DEF_READ_COLOR_ENABLED3]; //既読ポイント背景色パターン4 READ_COLOR_ENABLED4 = [READ_COLOR_DISABLED, READ_COLOR_DISABLED, READ_COLOR_DISABLED, READ_COLOR_DISABLED, READ_COLOR_DISABLED]; //既読ポイント背景色パターンの5番目以降がある場合 for( def_idx=0; def_idx < numberReadArticles; def_idx++ ){ READ_COLOR_ENABLED[def_idx] = DEF_READ_COLOR_ENABLED1; if( def_idx > 4 ){ READ_COLOR_ENABLED1[def_idx] = DEF_READ_COLOR_ENABLED1; READ_COLOR_ENABLED2[def_idx] = DEF_READ_COLOR_ENABLED2; READ_COLOR_ENABLED3[def_idx] = DEF_READ_COLOR_ENABLED3; READ_COLOR_ENABLED4[def_idx] = READ_COLOR_DISABLED; } } //情報表示部の固定サイズ(フォントサイズを変更したらここも合わせて変更する) const DISPINFO_WIDTH = "120px"; const DISPINFO_HEIGHT = "22px"; //情報表示部のフォントサイズ unknown,home,list,user,notification,search const titleSize = ["12px","12px","12px","12px","12px","12px"] //更新時間変更ボタン・自動更新停止ボタンのフォントサイズ unknown,home,list,user,notification,search const buttonSize = ["14px","14px","14px","14px","14px","14px"] //ホームTLの「フォロー中」表示を置き換える文字列(置き換えない場合は空文字 "" にしておく) const HOME_TL_NAME = "HomeTL"; //ボタンコントロールの文字列 var BTN_TXT_AUT = "◆";/* 自動更新強制OFF切り替えボタン */ var BTN_TXT_SRT = "▲";/* 短周期タイマの動作切り替えボタン */ var BTN_TXT_INT = "■";/* 更新間隔変更ボタン */ var BTN_TXT_HLP = "?";/* ヘルプダイアログ表示ボタン */ //表示部固定文字列 const TXT_COUNTDOWNTIM = "[ #### / $$$$$$$$ ]"; const TXT_PAUSEDUPDATE = "[ Paused Refresh ]"; const TXT_PAUSEDUPDAT2 = "[ Paused / $$$$$$$$]"; const TXT_MANUALUPDATE = "[ Manually Update. ]"; const TXT_RELOADEXTEND = "[ Reload Extended. ]"; const TXT_FORCERELOADD = "[ Force ReloadPage ]"; const TXT_CHANGEBGTYPE = "[ Change BG Type#. ]"; const TXT_CLEARNEWNOTI = "[ Clear New Info...]"; /******************************************** 以下のconst定義は各自の変更を推奨しないもの *******************************************/ //スクロール位置をトップと判定する範囲 const SCROLL_LIMIT = 5.0; //初期化が必要=0, 初期化済み=1 var initComplete = 0; //前回処理URL var lastURL = null; //更新間隔カウンタ var updateCount = 0; //監視間隔タイマの間隔(ミリ秒) const timerInterval = 1000; //短周期タイマオブジェクト var shortTimer = null; //短周期カウンタ var shortCount = 0; //自動更新が停止中 var displayPaused = 0; //スクロール位置の前回値 var lastYOffset = 0; //表示中のページ種別 0:unknown, 1:Home, 2:List, 3:User, 4:Notification, 5:Search var mode = 0; //このスクリプト固有のプリフィクス(カスタムCSSのセレクタで使用される) const scrmode = "PSEUDOTWEETDECK"; //スクリプト固有のモード記録用属性名 const scrattr = "PTD_TYPE"; //スクリプト固有の固定ツイート判定用属性名 const scrfixed = "PTD_FIXED"; //スクリプト固有の既読ポイント判定用属性名 const scrread = "PTD_READ"; //新着表示部のイベントトラップ目印用属性名 const scrnewtw = "PTD_NEWTWEET"; //最後に更新をかけた時間 var lastTriggerTime = new Date(); //ログを取る場合などの種別プリフィクス用文字列 const modename = ["NULL","HOME","LIST","USER","NOTI","SEAR"]; //モードのインデックス(配列タイプで定義している他の項目に影響するので変更禁止) const MODE_NULL = 0; const MODE_HOME = 1; const MODE_LIST = 2; const MODE_USER = 3; const MODE_NOTI = 4; const MODE_SEAR = 5; //自動更新の情報表示エレメント var elmInfo = null; //ボタンコントロールのエレメント var elmAllParent = null; var elmBtnParent = null; var elmBtnShort = null; var elmBtnInterval = null; var elmBtnAutoOff = null; var elmBtnHelp = null; //MutationObserverでDOM更新が上がってきた最終タイムスタンプ保持 var timeObj = new Date(); var timeStr = toFormatedTimeString(timeObj); //短周期タイマ開始時間を保持 var timeShort = null; //更新情報の表示エレメント、定周期で再利用するオブジェクト変数のグローバル化 var elm0 = null; var elm1 = null; var elm2 = null; var elm3 = null; var elm4 = null; var elm5 = null; var elm6 = null; var elm7 = null; var elm8 = null; var elm9 = null; var tmp = ""; var str = ""; var tmpObj = null; var flgClearNotification = false; //localStorage保存キーのプリフィクス const FORCEUPDATE_HEADER = "PTD_FTIME"; const INTERVAL_HEADER = "PTD_INTE-"; const MANUAL_HEADER = "PTD_MANU-"; const RELOAD_HEADER = "PTD_RELD-"; const AUTOOFF_HEADER = "PTD_AOFF-"; const IDLETM_HEADER = "PTD_IDLE-"; const CHKCOLOR_HEADER = "PTD_CCOL-"; var READ_HEADER = []; for( def_idx=0; def_idx < numberReadArticles; def_idx++ ){ READ_HEADER[def_idx] = "PTD_RD" + ("0" + def_idx).slice(-2) + "-"; } //強制リロードまでの時間管理のため起動した時間を記録 window.localStorage.setItem(FORCEUPDATE_HEADER, ("" + toFormatedDateString(new Date())) ); //操作ボタンコントロールのID const PARENT_BLOCK = "PTD_PARENT_BLOCK"; const BUTTON_BLOCK = "PTD_BUTTON_BLOCK"; const INFO_BLOCK = "PTD_INFO_BLOCK" const HELP_BLOCK = "PTD_HELP_BLOCK" const SHORT_BLOCK = "PTD_SHORT_BLOCK"; const INTERVAL_BLOCK = "PTD_INTERVAL_BLOCK"; const AUTO_BLOCK = "PTD_AUTO_BLOCK"; //表示部・ボタン類の色替え var colorType = 0; const TYPE_UPDATE = 1; const TYPE_UPSOON = 2; const TYPE_ACTIVE = 3; const TYPE_PAUSED = 4; const TYPE_HOLDTM = 5; const TYPE_RELOAD = 6; //チェックボックスエレメントに追加するタイムスタンプの要素名 const CHK_TIMESTAMP = "PTD_CHK_TIMESTAMP"; //チェックされたアーティクルのタイムスタンプ var readTimeStamp = []; //チェックされたアーティクルのURL var readArticleURL = []; for( def_idx=0; def_idx < numberReadArticles; def_idx++ ){ readTimeStamp[def_idx] = ""; readArticleURL[def_idx] = ""; } //チェックされたアーティクルの背景色モード var readEnabledColorMode = 0; //新着が消えるのを待ち合わせるカウンタ var chkNotification = 0; const chkNotificationMax = 100; //ダイアログ表示の余白 const spc = "\r\n  "; const spt = "  "; //URL保存時の基本部 const TWITTER_URL = "https://twitter.com/"; //TwitterURLのパス文字列 const PATH_HOME = "home"; const PATH_LIST = "i/lists/"; const PATH_NOTI = "notifications"; const PATH_SEAR = "search"; //本スクリプトとは無関係のURLかどうかを判定 function isOtherURL(hr){ if( hr.indexOf(TWITTER_URL + "messages") != -1 || hr.indexOf(TWITTER_URL + "compose") != -1 || hr.indexOf(TWITTER_URL + "settings") != -1 || hr.indexOf(TWITTER_URL + "explore") != -1 || hr.indexOf(TWITTER_URL + "i/bookmarks") != -1 || hr.indexOf(TWITTER_URL + "i/circles") != -1 || hr.indexOf(TWITTER_URL + "i/connect_people") != -1 || hr.indexOf(TWITTER_URL + "i/display") != -1 || hr.indexOf(TWITTER_URL + "intent/") != -1 || hr.indexOf(TWITTER_URL + "ja/") != -1 || hr.indexOf(TWITTER_URL + "en/") != -1 || (hr.indexOf(TWITTER_URL) != -1 && hr.indexOf("/status/") != -1) || (hr.indexOf(TWITTER_URL) != -1 && hr.indexOf("/lists") != -1)){ return true; } return false; } //日付時刻の整形 function toFormatedDateString(date){ // yy/mm/dd HH:MM:SS let ret = ("0" + date.getFullYear()).slice(-2) + "/" + ("0" + (date.getMonth() + 1)).slice(-2) + "/" + ("0" + date.getDate()).slice(-2) + " " + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2) + ":" + ("0" + date.getSeconds()).slice(-2); return ret; } //時刻の整形 function toFormatedTimeString(date){ // HH:MM:SS let ret = ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2) + ":" + ("0" + date.getSeconds()).slice(-2); return ret; } /* //一般ファイル吐き出しタイプのログ(ダウンロード処理によって代替するのでダウンロードフォルダへの自動保存設定である必要あり、とても重い) function logSave(text) { var date = (new Date(Date.now()+9*3600*1000)).toISOString(); var name = filePfx + date.replace(/\.\d+|[^\d]/g, "") + ("000" + new Date().getMilliseconds()).slice(-3) + ".log"; var writeText = name + " / " + text; var blob = new Blob( [writeText], {type: "text/plain"} ); var link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = name; link.click(); console.log("☆LogOutput : " + name + " / " + writeText); } */ //更新が必要かどうかをURLとフォーカスコントロールの状態などから判定(内部の画面遷移とlocation.hrefは一致しないタイミングがあるので更新直前に確認が必要) function shouldNotUpdate() { var searchCandidate; var focusElement; var focusAttr; //HOME if( mode == MODE_HOME ){ searchCandidate = document.body.querySelectorAll('div[class="css-1dbjc4n r-13awgt0 r-bnwqim"]'); focusElement = document.activeElement; if(searchCandidate.length > 0) { if(searchCandidate[0].innerHTML != ""){ //更新してしまうと検索ボックスが閉じてしまうため、この場合は更新しない //console.log("※SearchBox Enable"); return true; } } if(window.location.href == TWITTER_URL + PATH_HOME ){ //ツイート入力などにフォーカスが当たっている場合には処理しないようにする(フォーカスが外れる) focusAttr = focusElement.getAttribute("data-testid"); if(focusAttr !== null){ if(focusAttr != "tweet" && focusAttr != "retweet" && focusAttr != "unretweet" && focusAttr != "like" && focusAttr != "unlike" && focusAttr != "AppTabBar_Home_Link"){ //ホームボタンを押すと、ツイートにフォーカスが設定されることがある。その場合は更新する。 //console.log("※InputBox etc Enable + [" + focusAttr + "]"); return true; }else{ //console.log("※Focus Attr + [" + focusAttr + "]"); } } if( autoOff == 1 ){ return true; }else{ return false; } }else{ return true; } //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return true"); return true; } //LIST if( mode == MODE_LIST ){ if(window.location.href.indexOf(TWITTER_URL + PATH_LIST) != -1 ){ //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return false"); if( autoOff == 1 ){ return true; }else{ return false; } } //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return true"); return false; } //USER if( mode == MODE_USER ){ var hr = window.location.href; if(hr == (TWITTER_URL + PATH_HOME) || hr.indexOf(TWITTER_URL + PATH_LIST) != -1 || hr.indexOf(TWITTER_URL + PATH_NOTI) != -1 || hr.indexOf(TWITTER_URL + PATH_SEAR) != -1 || isOtherURL(hr) ){ //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return false"); return true; } //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return true"); if( autoOff == 1 ){ return true; }else{ return false; } } //NOTI if( mode == MODE_NOTI ){ if(window.location.href.indexOf(TWITTER_URL + PATH_NOTI) != -1 ){ //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return false"); return false; } //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return true"); return true; } //SEAR if( mode == MODE_SEAR ){ focusElement = document.activeElement; focusAttr = focusElement.getAttribute("data-testid"); if(focusAttr !== null){ //検索ボックスにフォーカスがあったら更新させない if(focusAttr == "SearchBox_Search_Input"){ //console.log("※InputBox Enable + [" + focusAttr + "] return true"); return true; }else{ //console.log("※Focus Attr + [" + focusAttr + "]"); } } if(window.location.href.indexOf(TWITTER_URL + PATH_SEAR) != -1 ){ //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return false"); if( autoOff == 1 ){ return true; }else{ return false; } } //console.log("※Current href + [" + window.location.href + "] / Focus tagName + [" + focusElement.tagName + "] return true"); return true; } return true; } //定周期更新処理メイン function updateTimeline() { //画面遷移を判定し、変わっていたら初期処理をやりなおす if( lastURL != window.location.href ){ lastURL = window.location.href; initComplete = 0; for(var idx=0; idx < numberReadArticles; idx++){ readTimeStamp[idx] = ""; readArticleURL[idx] = ""; } if(idleShortTimer){ //停止していた短周期タイマを再開 if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); //console.log("★shortTimer: START[clearElements]"); } timeShort = new Date(); } //初期処理 clearElements(); /* //URLに対応する更新時間設定があるか検索 findInterval(); //URLに対応する強制リロード時間設定があるか検索 findReloadTime(); //URLに対応する手動更新抑止時間設定があるか検索 findManualTime(); //URLに対応する自動更新OFF設定があるか検索 findAutoOffSetting(); //URLに対応する短周期タイマ常時ON設定があるか検索 findIdleTimerSetting(); */ //URLに対応する設定をローカルストレージから読み込む findSettings(); //既読ポイント背景色設定の読込 if( enableReadPointCheckBox ){ loadReadPointColorSetting(); } return; } //自動更新に該当しないページは何もせず戻る if( mode == MODE_NULL ){ return; } tmpObj = new Date(); if(idleShortTimer){ //短周期タイマ開始から時間が経過したらタイマを停止させる if( shortTimer !== null ){ if( ( tmpObj.getTime() - timeShort.getTime() ) / (shortTimerIdle ) > 1.0 ){ window.clearInterval(shortTimer); shortTimer = null; //console.log("★shortTimer: OFF"); } }else{ //停止していた短周期タイマをワンショット実行 tmp = document.title; if( tmp.indexOf("@") == 0 || tmp.slice(-3) == "/ X" ){ replaceEtements(); } } if(window.pageYOffset != lastYOffset){ //先頭に戻った場合ワンショットで短周期処理を走らせる if( window.pageYOffset == 0 ){ replaceEtements(); } //スクロール操作中短周期処理を再開させる if( Math.abs(window.pageYOffset - lastYOffset) > SCROLL_LIMIT ){ if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); } timeShort = new Date(); } lastYOffset = window.pageYOffset; } } //console.log("☆PageY-Offset=" + window.pageYOffset); if(window.pageYOffset <= SCROLL_LIMIT) { if(!shouldNotUpdate()) { if(updateCount >= updateInterval[mode]) { //フォーカス判定が外れた状態からの復帰 if( initComplete == 0 ){ clearElements(); } if(reloadTimeout){ //一定時間以上DOM更新が無い場合はリロードする if( ( tmpObj.getTime() - timeObj.getTime() ) > (reloadTimeout * 1000) ){ var f = false; var v = "20" + window.localStorage.getItem(FORCEUPDATE_HEADER); if( v != null ){ var timeSub = (tmpObj.getTime() - (new Date(v)).getTime() ); //短時間に実施しないため前回実施時刻を獲得 if( timeSub > (forceUpdateLockTime * 1000) ){ f = true; }else{ //強制リロードまでの時間を延長 →タイミングが合わないと無尽蔵に増えるので廃止 //reloadTimeout += Math.trunc(Math.random() * forceUpdateLockTime * 0.1); //インターバルが長い設定の時は次回まで長すぎるのでリトライするのは100秒以内くらいにしておく //updateCount = Math.trunc(Math.random() * (updateInterval[mode] / 1000)) * 1000; updateCount = (Math.trunc(Math.random() * 70) + 30) * 1000; forceUpdateColor(COLOR_RELOAD, TXT_RELOADEXTEND); return; } }else{ f = true; } if( f ){ //if(filePfx!==null) logSave( modename[mode] + " RELOAD [" + ("000" + reloadCount).slice(-3) + "] " + timeStr ); window.localStorage.setItem(FORCEUPDATE_HEADER, ("" + toFormatedDateString(new Date())) ); window.setTimeout( function(){ window.location.reload(); }, reloadWait ); displayPaused = 0; updateCount = 0; forceUpdateColor(COLOR_UPDATE, TXT_FORCERELOADD); initComplete = 0; return; } } } //更新トリガ execTrigger(); if(idleShortTimer){ //停止していた短周期タイマをワンショット実行 if( shortTimer === null ){ replaceEtements(); } } //更新されたのでカウンタをリセット displayPaused = 0; updateCount = 0; }else{ if(idleShortTimer && displayPaused){ //停止していた短周期タイマを時差でワンショット実行 if( shortTimer === null ){ window.setTimeout( replaceEtements, timerInterval ); } } //更新待ちカウンタの加算 displayPaused = 0; updateCount += timerInterval; //console.log("☆Counter Update [" + updateCount + "]" ); } }else{ //フォーカスの判定が外れたのでカウントリセット・URLが変わっていたので次回オブジェクト捜索しなおし initComplete = 0; updateCount = updateInterval[mode]; displayPaused = 1; //console.log("☆Wait for current control [" + updateCount + "]" ); } }else{ //先頭に戻された次回に少し待って更新がかかるようにする displayPaused = 1; //手動更新の操作でトリガが掛かった後ならカウンタリセット、それ以外でスクロール位置が先頭に戻ったら少し後にトリガ var dt = new Date(); if( dt - lastTriggerTime > manualUpdateInterval * 1000 ){ updateCount = updateInterval[mode] - onTopWait; }else{ updateCount = 0; } if(updateCount < 1000 ) updateCount = 0; //console.log("☆Wait for window top [" + updateCount + "]" ); } //更新に関する情報を表示する updateDisplayInfo(); } //更新トリガ処理 function execTrigger(){ if( mode == MODE_HOME ){ //メニューのHomeを押す事で更新トリガさせる var homeButton = document.body.querySelectorAll('a[data-testid="AppTabBar_Home_Link"]'); if(homeButton.length > 0) { homeButton[0].click(); //更新トリガの時間を記録 lastTriggerTime = new Date(); //if(filePfx!==null) logSave(modename[mode] + " CLICK [" + ("000" + reloadCount).slice(-3) + "] " + timeStr ); //console.log("★Home Button Update"); } }else if( mode == MODE_LIST || mode == MODE_USER || mode == MODE_SEAR ){ //スクロール操作によって更新トリガさせる window.scrollTo(0, window.innerHeight); window.setTimeout( function(){ window.scrollTo(0, 0);}, 0 ); //更新トリガの時間を記録 lastTriggerTime = new Date(); //if(filePfx!==null) logSave(modename[mode] + " @" + (window.location.href.slice(28) + " ").substr(0,19) + " SCROLL [" + ("000" + reloadCount).slice(-3) + "] " + timeStr ); //console.log("★Scroll Update"); } } //表示用エレメントのチェック〜無ければ捜索 function findDisplayElements(){ //カウンタ表示用のエレメントを捜索 if( elmInfo === null ){ if( mode == MODE_HOME ){ //Home elm0 = document.querySelectorAll('a[role="tab"][aria-selected="true"]'); if( elm0.length > 0 ){ elm1 = elm0[0].firstElementChild; if( elm1 !== null ){ elm2 = elm1.firstElementChild; if( elm2 !== null ){ //情報表示&ボタン類を追加する場所 setupElement(elm2); //「フォロー中」を「Home」に置換 elm3 = elm2.firstElementChild; if( elm3 !== null && HOME_TL_NAME != "" ){ elm3.textContent = HOME_TL_NAME; } //情報表示をする場所 //elmInfo = elm2.firstElementChild; //console.log("★find: Element"); } } } }else if( mode == MODE_LIST ){ //List //リスト作成者のアカウント名に表示項目を追加 elm0 = document.querySelectorAll('h2[dir="ltr"][aria-level="2"][role="heading"]'); if( elm0.length > 0 ){ elm4 = elm0[0].firstElementChild; if( elm4 !== null ){ //情報表示&ボタン類を追加する場所 setupElement(elm4); } } }else if( mode == MODE_USER ){ //User //ユーザーのツイート数に表示項目を追加 elm0 = document.querySelectorAll('h2[dir="ltr"][aria-level="2"][role="heading"]'); if( elm0.length > 0 ){ elm4 = elm0[0].firstElementChild; if( elm4 !== null ){ elm5 = elm4.firstElementChild; if( elm5 !== null ){ //情報表示&ボタン類を追加する場所 setupElement(elm5); } } //console.log("★find: Element"); } }else if( mode == MODE_NOTI ){ //Notification elm0 = document.querySelectorAll('h2[dir="ltr"][aria-level="2"][role="heading"]'); if( elm0.length > 0 ){ //通知ページは勝手に更新が上がってくるので更新間隔の定義なしだからインターバル設定と自動更新強制OFFのボタンなし→強制リロード時間の設定は出来るようにするので自動ボタン復活 setupElement(elm0[0]); //elmBtnInterval.style.display = "none"; elmBtnAutoOff.style.display = "none"; //console.log("★find: Element"); } }else if( mode == MODE_SEAR ){ //Search //検索ページの最新に表示項目を追加 elm0 = document.querySelectorAll('div[data-testid="ScrollSnap-SwipeableList"]'); if( elm0.length > 0 ){ elm1 = elm0[0].firstElementChild; if( elm1 !== null ){ elm2 = elm1.firstElementChild; if( elm2 !== null ){ elm3 = elm2.nextElementSibling; if( elm3 !== null ){ elm4 = elm3.firstElementChild; if( elm4 !== null ){ elm5 = elm4.firstElementChild; if( elm5 !== null ){ elm6 = elm5.firstElementChild; if( elm6 !== null ){ //情報表示&ボタン類を追加する場所 setupElement(elm6); //console.log("★find: Element"); } } } } } } } } } } //情報の表示更新〜色替え function updateDisplayInfo(){ //更新に関する情報を表示する if( initComplete != 0 || displayPaused ){ //表示用エレメントのチェック〜無ければ捜索 findDisplayElements(); //カウンタを更新 if( elmInfo !== null ){ tmp = updateInterval[mode] - updateCount; if(tmp < 1000 ) tmp = 0; //フォーカスを持ったタブのみカウントを都度更新(全部に対して行うと重すぎる) //設定内容を設定先と比較して違ったら代入するようにしているのは、代入によって内部でDOM更新が走り処理が重くなるため if( document.visibilityState === "visible" && document.hasFocus() === true && mode != MODE_NOTI ){ if( ! displayPaused ){ //TXT_COUNTDOWNTIM = " [ #### / $$$$$$$$ ]"; str = TXT_COUNTDOWNTIM.replace("####", ("0000" + (tmp / 1000)).slice(-4) ).replace("$$$$$$$$",timeStr) if(elmInfo.textContent != str) elmInfo.textContent = str; if( tmp == 0 ){ if(elmInfo.style.color != COLOR_UPDATE) elmInfo.style.color = COLOR_UPDATE; colorType = TYPE_UPDATE; }else if( tmp <= 10000 ){ //強制リロード間際の色替え if( tmpObj.getTime() - timeObj.getTime() > (reloadTimeout * 1000 - 10000) && reloadTimeout > 0 ){ if(elmInfo.style.color != COLOR_RELOAD) elmInfo.style.color = COLOR_RELOAD; colorType= TYPE_RELOAD; }else{ if(elmInfo.style.color != COLOR_UPSOON) elmInfo.style.color = COLOR_UPSOON; colorType = TYPE_UPSOON; } }else{ if(elmInfo.style.color != COLOR_ACTIVE) elmInfo.style.color = COLOR_ACTIVE; colorType = TYPE_ACTIVE; } }else{ if( ! enableTimeAutoOff ){ str = TXT_PAUSEDUPDATE; }else{ //TXT_PAUSEDUPDAT2 = "[ Paused / $$$$$$$$]"; str = TXT_PAUSEDUPDAT2.replace("$$$$$$$$",timeStr); } if(elmInfo.textContent != str) elmInfo.textContent = str; if(elmInfo.style.color != COLOR_PAUSED) elmInfo.style.color = COLOR_PAUSED; colorType = TYPE_PAUSED; } }else{ //最後にDOM更新があがってきたタイムスタンプを表示 if( ! displayPaused || mode == MODE_NOTI ){ str = " [Latest / " + timeStr + "]"; if(elmInfo.textContent != str) elmInfo.textContent = str; if( tmp == 0 && mode != MODE_NOTI ){ if(elmInfo.style.color != COLOR_UPDATE) elmInfo.style.color = COLOR_UPDATE; colorType = TYPE_UPDATE; }else if( tmp <= 10000 && mode != MODE_NOTI ){ //強制リロード間際の色替え if( tmpObj.getTime() - timeObj.getTime() > (reloadTimeout * 1000 - 10000) && reloadTimeout > 0 ){ if(elmInfo.style.color != COLOR_RELOAD) elmInfo.style.color = COLOR_RELOAD; colorType= TYPE_RELOAD; }else{ if(elmInfo.style.color != COLOR_UPSOON) elmInfo.style.color = COLOR_UPSOON; colorType = TYPE_UPSOON; } }else{ //通知ページ専用処理 if( mode == MODE_NOTI ){ //強制リロード間際の色替え if( tmpObj.getTime() - timeObj.getTime() > (reloadTimeout * 1000 - 10000) && reloadTimeout > 0 ){ if(elmInfo.style.color != COLOR_RELOAD) elmInfo.style.color = COLOR_RELOAD; colorType= TYPE_RELOAD; }else if(flgClearNotification){ //新着クリア中の表示 forceUpdateColor(COLOR_WHITE,TXT_CLEARNEWNOTI); colorType= 0; }else{ if(elmInfo.style.color != COLOR_ACTIVE) elmInfo.style.color = COLOR_ACTIVE; colorType= TYPE_ACTIVE; } }else{ if(elmInfo.style.color != COLOR_ACTIVE) elmInfo.style.color = COLOR_ACTIVE; colorType = TYPE_ACTIVE; } } }else{ if( ! enableTimeAutoOff ){ str = TXT_PAUSEDUPDATE; }else{ //TXT_PAUSEDUPDAT2 = "[ Paused / $$$$$$$$]"; str = TXT_PAUSEDUPDAT2.replace("$$$$$$$$",timeStr); } if(elmInfo.textContent != str) elmInfo.textContent = str; if(elmInfo.style.color != COLOR_PAUSED) elmInfo.style.color = COLOR_PAUSED; colorType = TYPE_PAUSED; } } if(elmInfo.style.fontSize != titleSize[mode]) elmInfo.style.fontSize = titleSize[mode]; //表示部をクリックしても更新トリガしない期間の色替え if( colorType != TYPE_RELOAD ){ if( tmpObj.getTime() - lastTriggerTime <= manualUpdateInterval * 1000 ){ if(elmInfo.style.color != COLOR_HOLDTM) elmInfo.style.color = COLOR_HOLDTM; colorType = TYPE_HOLDTM; } } //ボタンコントロールの色替え if(colorType == TYPE_UPDATE){ if(elmBtnInterval.style.color != COLOR_INT_UPDATE) elmBtnInterval.style.color = COLOR_INT_UPDATE; if(elmBtnHelp.style.color != COLOR_HLP_UPDATE) elmBtnHelp.style.color = COLOR_HLP_UPDATE; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_UPDATE) elmBtnAutoOff.style.color = COLOR_AUT_UPDATE; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_UPDATE) elmBtnShort.style.color = COLOR_IDL_UPDATE; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_UPDATE) elmBtnShort.style.color = COLOR_ALW_UPDATE; } }else if(colorType == TYPE_UPSOON){ if(elmBtnInterval.style.color != COLOR_INT_UPSOON) elmBtnInterval.style.color = COLOR_INT_UPSOON; if(elmBtnHelp.style.color != COLOR_HLP_UPSOON) elmBtnHelp.style.color = COLOR_HLP_UPSOON; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_UPSOON) elmBtnAutoOff.style.color = COLOR_AUT_UPSOON; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_UPSOON) elmBtnShort.style.color = COLOR_IDL_UPSOON; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_UPSOON) elmBtnShort.style.color = COLOR_ALW_UPSOON; } }else if(colorType == TYPE_ACTIVE){ if(elmBtnInterval.style.color != COLOR_INT_ACTIVE) elmBtnInterval.style.color = COLOR_INT_ACTIVE; if(elmBtnHelp.style.color != COLOR_HLP_ACTIVE) elmBtnHelp.style.color = COLOR_HLP_ACTIVE; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_ACTIVE) elmBtnAutoOff.style.color = COLOR_AUT_ACTIVE; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_ACTIVE) elmBtnShort.style.color = COLOR_IDL_ACTIVE; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_ACTIVE) elmBtnShort.style.color = COLOR_ALW_ACTIVE; } }else if(colorType == TYPE_PAUSED){ if(elmBtnInterval.style.color != COLOR_INT_PAUSED) elmBtnInterval.style.color = COLOR_INT_PAUSED; if(elmBtnHelp.style.color != COLOR_HLP_PAUSED) elmBtnHelp.style.color = COLOR_HLP_PAUSED; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_PAUSED) elmBtnAutoOff.style.color = COLOR_AUT_PAUSED; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_PAUSED) elmBtnShort.style.color = COLOR_IDL_PAUSED; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_PAUSED) elmBtnShort.style.color = COLOR_ALW_PAUSED; } }else if(colorType == TYPE_HOLDTM){ if(elmBtnInterval.style.color != COLOR_INT_HOLDTM) elmBtnInterval.style.color = COLOR_INT_HOLDTM; if(elmBtnHelp.style.color != COLOR_HLP_HOLDTM) elmBtnHelp.style.color = COLOR_HLP_HOLDTM; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_HOLDTM) elmBtnAutoOff.style.color = COLOR_AUT_HOLDTM; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_HOLDTM) elmBtnShort.style.color = COLOR_IDL_HOLDTM; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_HOLDTM) elmBtnShort.style.color = COLOR_ALW_HOLDTM; } }else if(colorType == TYPE_RELOAD){ if(elmBtnInterval.style.color != COLOR_INT_RELOAD) elmBtnInterval.style.color = COLOR_INT_RELOAD; if(elmBtnHelp.style.color != COLOR_HLP_RELOAD) elmBtnHelp.style.color = COLOR_HLP_RELOAD; if( autoOff == 1 ){ if(elmBtnAutoOff.style.color != COLOR_AUT_DISABL) elmBtnAutoOff.style.color = COLOR_AUT_DISABL; }else{ if(elmBtnAutoOff.style.color != COLOR_AUT_RELOAD) elmBtnAutoOff.style.color = COLOR_AUT_RELOAD; } //短周期タイマの稼動状態 if( idleShortTimer == 1 ){ if( shortTimer === null ){ //短周期タイマ停止中 if(elmBtnShort.style.color != COLOR_IDL_DISABL) elmBtnShort.style.color = COLOR_IDL_DISABL; }else{ //短周期タイマ動作中 if(elmBtnShort.style.color != COLOR_IDL_RELOAD) elmBtnShort.style.color = COLOR_IDL_RELOAD; } }else{ //短周期タイマ常時稼動モード if(elmBtnShort.style.color != COLOR_ALW_RELOAD) elmBtnShort.style.color = COLOR_ALW_RELOAD; } } } } } //通常パターン以外での色替え(手動の更新操作やエラー発生時などのモニタリング用) function forceUpdateColor(rgba,txt){ if( elmInfo === null || elmInfo.textContent === null || elmBtnInterval === null || elmBtnHelp === null || elmBtnAutoOff === null || elmBtnShort === null ) return; elmInfo.textContent = txt; elmInfo.style.color = rgba; elmBtnHelp.style.color = COLOR_BLACK; elmBtnInterval.style.color = COLOR_GLAY; elmBtnAutoOff.style.color = COLOR_GLAY; elmBtnShort.style.color = COLOR_GLAY; } //要素の初回書き換わりを監視 window.addEventListener('load', function(){ initComplete = 0; //各種カウンタの初期化 updateCount = 0; displayPaused = 0; shortCount = 0; //console.log("★load: WindowLoaded"); }); //アーティクル数に応じた可変要素の1つ上のエレメントをDOM更新監視(アーティクルの増減をイベントとして受け取る) var observer = new MutationObserver(function(){ //DOMの変化が起こった時の処理 timeObj = new Date(); timeStr = toFormatedTimeString(timeObj); //停止していた短周期タイマを再開 if( idleShortTimer ){ if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); //console.log("★shortTimer: START[DOM]"); } timeShort = new Date(); } //if(filePfx!==null) logSave(modename[mode] + " OBSERV "); console.log("★DOMが変化しました " + new Date().toLocaleTimeString() ); }); //MutationObserver監視時のオプション const configObserver = { attributes: false, characterData: true, childList: true, subtree: false, attributeOldValue: false, characterDataOldValue: false }; //監視するエレメントの取得・再取得 async function clearElements(){ //スクリプト固有の属性を一旦削除 elm0 = document.querySelectorAll('body[' + scrattr +']'); if( elm0.length > 0 ){ elm0[0].removeAttribute( scrattr ); } //URLからモードを決定 getModeFromURL(); if( mode == MODE_NULL ){ lastURL = ""; window.setTimeout(clearElements, initWait); return; } //カスタムCSSで判別するための要素を追加(ユーザーページは消去法で判定しないと無理で、カスタムCSSではそんな判別は出来ないため) elm0 = document.querySelectorAll('body'); if( elm0.length > 0 ){ elm0[0].setAttribute( scrattr, scrmode + modename[mode] ); } //各種カウンタの初期化 updateCount = 0; displayPaused = 0; shortCount = 0; elm0 = elm1 = elm2 = elm3 = elm4 = elm5 = elm6 = elm7 = elm8 = elm9 = null; //監視対象の要素オブジェクト if( mode == MODE_HOME ){ elm1 = document.querySelectorAll('div[aria-label="タイムライン: ホームタイムライン"]'); }else if( mode == MODE_LIST ){ elm1 = document.querySelectorAll('div[aria-label="タイムライン: リスト"]'); }else if( mode == MODE_USER ){ elm1 = document.querySelectorAll('div[aria-label^="タイムライン: "][aria-label$="さんのポスト"]'); }else if( mode == MODE_NOTI ){ elm1 = document.querySelectorAll('div[aria-label="タイムライン: 通知"]'); }else if( mode == MODE_SEAR ){ elm1 = document.querySelectorAll('div[aria-label="タイムライン: タイムラインを検索"]'); } if( elm1.length > 0 ) elm0 = elm1[0].firstElementChild; if( elm0 === null ){ //ドキュメント未構成のため時間を置いてリトライ window.setTimeout(clearElements, initWait); //console.log("☆clearElements Retry Timer" ); return; } var i; //固定ツイートを非表示にするための目印追加 if( mode == MODE_USER ){ elm2 = elm0.firstElementChild; if( elm2 !== null ){ elm3 = elm2.querySelectorAll('span'); for( i=0; i < elm3.length; i++ ){ if( elm3[i].textContent == "固定" ){ elm2.setAttribute(scrfixed,"true"); break; } } } } //検索ページで「話題のツイート」を非表示にしていた場合に「最新」にフォーカスを移す (@madiaで切り替えているからか、style.display値が取得出来ない) if( notDispHotTopicSearch && mode == MODE_SEAR ){ elm3 = document.querySelectorAll('div[aria-label="ホームタイムライン"] div[role="tablist"][data-testid="ScrollSnap-List"] div[role="presentation"]:has(a[role="tab"])'); if( elm3.length > 1 ){ elm4 = elm3[0].querySelectorAll('a[role="tab"]'); elm5 = elm3[1].querySelectorAll('a[role="tab"]'); if( elm4.length > 0 && elm5.length > 0 ){ if( elm4[0].getAttribute("aria-selected") == "true" ){ elm5[0].click(); } } } } //既に張られているovserverを消去 observer.disconnect(); //要素の変化監視をスタート observer.observe(elm0, configObserver); //表示更新用エレメントの再取得 elmInfo = null; initComplete = 1; //if(filePfx!==null) logSave(modename[mode] + " CLEAR "); //console.log("★clear: Element"); } //URLからモードを決定する function getModeFromURL(){ //画面遷移が発生したので動作モードの決定 var hr = window.location.href; if( hr == (TWITTER_URL + PATH_HOME)){ mode = MODE_HOME; }else if( hr.indexOf(TWITTER_URL + PATH_LIST) != -1 ){ mode = MODE_LIST; }else if( hr.indexOf(TWITTER_URL + PATH_NOTI) != -1 ){ mode = MODE_NOTI; }else if( hr.indexOf(TWITTER_URL + PATH_SEAR) != -1 ){ mode = MODE_SEAR; }else if(isOtherURL(hr)){ mode = MODE_NULL; }else if( hr.indexOf(TWITTER_URL) != -1 ){ //ユーザーアカウント名で終わるURLなので消去法で各種ページを除外していかないと判定が出来ない mode = MODE_USER; }else{ mode = MODE_NULL; } } //RTされた同一の記事が複数表示されないようにする function removeSameArticle(){ var e0, e1, e2, e3, at; //重複の削除 e0 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:has(a[href*="/status/"]):has(span[datetime])'); for(var i=e0.length-1; i > -1; i--){ e1 = e0[i].querySelectorAll('a[href*="/status/"]:has(span[datetime])'); if( e1[0] !== null ){ for(var j=0; j < i; j++){ e2 = e0[j].querySelectorAll('a[href*="/status/"]:has(span[datetime])'); if( e2[0] !== null ){ var u1 = e1[0].getAttribute('href'); var u2 = e2[0].getAttribute('href'); //console.log("★ i=" + i + " : " + u1 + " # j=" + j + " : " + u2 ); if( u1 == u2 ){ //console.log("★: HIT i=" + i + " / j=" + j + " : " + u2 ); /* 既読マークを複数打つようになったので特例にしないで非表示にしてしまって問題ない //既読マークがついているアーティクルは非表示にしない e3 = e2[0].nextElementSibling; if( e3 != null ){ at = e3.getAttribute(scrread); if( at.indexOf("true") != 0 ){ //console.log("★: HIT display == none [" + u2 + "]"); e0[j].style.display = "none"; }else{ //console.log("★: HIT display != none [" + u2 + "]"); } }else{ //console.log("★: HIT nextElementSibling == null [" + u2 + "]"); } */ e0[j].style.display = "none"; } } } } } } //短周期処理(タイムスタンプ置換、表示タイトル変更、さらに表示系の自動押下) function replaceEtements() { var e0, e1, e2, e3, e4, e5, e6, idx; //新着表示をクリックした場合にも更新時間を記録 e0 = document.querySelectorAll('div[aria-label="タイムライン: ホームタイムライン"] div div[data-testid="cellInnerDiv"]'); if( e0.length > 0 ){ e1 = e0[0].querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-1777fci r-dfe81l r-1ny4l3l r-o7ynqc r-6416eg r-13qz1uu"]'); if( e1.length > 0 ){ if( e1[0].getAttribute(scrnewtw) != 'true' ){ e1[0].setAttribute(scrnewtw, 'true'); e1[0].addEventListener('click', function(){ //色替え・手動更新表示 forceUpdateColor(COLOR_UPDATE, TXT_MANUALUPDATE); //更新トリガの時間を記録 lastTriggerTime = new Date(); //手動でトリガを掛けたので更新カウンタ時間はリセット updateCount = 0; }); } } } //重複記事の非表示 if( notDispSameArticle ){ removeSameArticle(); } //タイムスタンプ置換 document.querySelectorAll('main div[data-testid="primaryColumn"] section article a[href*="/status/"] time').forEach(function(e) { var a = e.parentNode; var b = a.parentNode; var h = a.getAttribute('href'); var span = document.createElement('span'); var s0 = e.getAttribute('datetime'); var s1 = toFormatedDateString(new Date(s0)); span.setAttribute('datetime', s0); span.setAttribute('local-datetime', s1); span.textContent = s1; a.appendChild(span); a.removeChild(e); var u = window.location.href; u = u.replace( TWITTER_URL, ""); //遷移直後にURLがhomeなのにmodeが0のような事があるので再判定 if( mode == MODE_NULL ){ getModeFromURL(); } if( enableReadPointCheckBox ){ if( mode == MODE_HOME || mode == MODE_LIST || mode == MODE_USER || mode == MODE_SEAR ){ //既読ポイントのチェックボックス追加 let chk = document.createElement('input'); chk.setAttribute('type','checkbox'); chk.setAttribute(scrread,'false'); for( idx=0; idx < numberReadArticles; idx++ ){ if( localStorage.getItem( READ_HEADER[idx] + u ) == h ){ chk.setAttribute(scrread,'true' + idx); chk.checked = true; } } chk.setAttribute('href', h); chk.setAttribute(CHK_TIMESTAMP, s1); chk.style.marginTop = '3px'; chk.style.marginBottom = '0px'; chk.style.marginLeft = '5px'; chk.style.marginRight = '0px'; chk.style.opacity = '0.5'; //stopPropagationのあと無名関数自身をreturn falseで返さないと親エレメントのイベントが止まらない chk.onchange = function(e){e.stopPropagation(); chgReadPoint(e.target,0); appendNextReadCheck(); return false;}; chk.oncontextmenu = function(e){e.stopPropagation(); rclickReadPoint(e.target); return false;}; b.appendChild(chk); //先祖要素を探して色替え for( idx = 0; idx < numberReadArticles; idx++ ){ e1 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:has(input[' + scrread + '="true' + idx + '"][href="' + h + '"])'); if( e1.length> 0 ){ e1[0].style.backgroundColor = READ_COLOR_ENABLED[idx]; readTimeStamp[idx] = s1; readArticleURL[idx] = h; } } }else{ //console.log("★ mode=" + mode + " / " + u ); } } }); //console.log("★ 1 Title=" + document.title); if( document.title.indexOf('@') != -1 ){ let t, idx1, idx2; // Listページ タブタイトルの変更 「@USERNAME/ListName」→「ListName」 if( window.location.href.indexOf(TWITTER_URL + PATH_LIST) !== -1 ){ t = document.title; idx1 = t.indexOf('@'); idx2 = t.indexOf('/'); if( idx1 !== -1 && idx2 !== -1 ) document.title = t.substr(idx2+1); }else{ // Userページ タブタイトルの変更 「Posts with replies by 」など削除 document.title = document.title.replace('Posts with replies by ',''); document.title = document.title.replace('Media posts by ',''); document.title = document.title.replace('Posts liked by ',''); // 通知があると先頭に(1)とかが付くのでこのあとのタブタイトル置換でidx2の値がidx1より手前になってしまうのを回避 // 「(1) DISPNAME (@USERNAME) / X」→「DISPNAME (@USERNAME) / X」 // 「(1) DISPNAME(@USERNAME)さん / X」→「DISPNAME(@USERNAME)さん / X」 t = document.title; idx1 = t.indexOf(")"); idx2 = t.indexOf("@"); if( t.substr(0,1) == "(" && idx1 > 1 && (idx2 - 5) > idx1 ){ document.title = t.substr(idx1+1); //console.log("★ 3 Title=" + document.title); } // Userページ タブタイトルの変更 「DISPNAME (@USERNAME)」→「[@USERNAME]」 t = document.title; idx1 = t.indexOf(' (@'); idx2 = t.indexOf(')'); if( idx1 !== -1 && idx2 !== -1 ) document.title = '[' + t.substr(idx1+3, idx2-idx1-3) + ']'; // Userページ タブタイトルの変更 「DISPNAME(@USERNAME)さん / X」→「[@USERNAME]」 t = document.title; idx1 = t.indexOf('(@'); idx2 = t.indexOf(')さん'); if( idx1 !== -1 && idx2 !== -1 ) document.title = '[' + t.substr(idx1+2, idx2-idx1-2) + ']'; } } // 個別ツイート表示先頭の「Xユーザーの」を削除 if(document.title.indexOf("Xユーザーの") == 0 ){ document.title = document.title.replace('Xユーザーの',''); } // タブタイトル末尾の 「/ X」を削除 document.title = document.title.replace('\/ X',''); //console.log("★ 2 Title=" + document.title); //自動更新OFFのボタンが押されて有効となっている場合は自動クリックも停止 if( autoOff == 0 || displayPaused == 0 ){ // さらに表示・n件のツイートを表示 を自動クリック e2 = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-1777fci r-1pl7oy7 r-1ny4l3l r-o7ynqc r-6416eg r-13qz1uu"]'); if( e2.length !== 0 ){ //console.log("★Trigger1"); e2[0].click(); } e2 = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-16y2uox r-19u6a5r r-1ny4l3l r-m2pi6t r-o7ynqc r-6416eg"]'); if( e2.length !== 0 ){ //console.log("★Trigger2"); e2[0].click(); } e2 = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-1777fci r-dfe81l r-1ny4l3l r-o7ynqc r-6416eg r-13qz1uu"]'); if( e2.length !== 0 ){ e3 = e2[0].firstElementChild; if( e3 !== null ){ e4 = e3.firstElementChild; if( e4 !== null ){ e5 = e4.firstElementChild; if( e5 !== null ){ e6 = e5.textContent; if( e6.indexOf("さらに") > -1 || e6.indexOf("More") > -1 || e6.indexOf("more") > -1 ){ //console.log("★Trigger3"); e2[0].click(); } } } } //console.log("★offset = " + window.pageYOffset + " height = " + window.innerHeight); if( window.pageYOffset <= SCROLL_LIMIT || window.pageYOffset > window.innerHeight / 2 ) { //console.log("★Trigger3"); e2[0].click(); //色替え・手動更新表示 forceUpdateColor(COLOR_UPDATE, TXT_MANUALUPDATE); //更新トリガの時間を記録 lastTriggerTime = new Date(); //スクロールの操作でトリガを掛けたのでインターバル時間はリセット updateCount = 0; } } } } //既読ポイントの変更 function chgReadPoint(obj, idx){ if( ! enableReadPointCheckBox ) return; if( mode != MODE_HOME && mode != MODE_LIST && mode != MODE_USER && mode != MODE_SEAR ) return; var i, j, e0, e1, flg = false; var h = window.location.href; h = h.replace( TWITTER_URL, ""); if( obj !== null ){ var c = obj.checked; var u = obj.getAttribute('href'); //既にチェック済みになっているエレメントを戻す if( idx == 0 ){ e0 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:has(input[' + scrread + '^="true"])'); for( i = 0; i < e0.length; i++ ){ e0[i].style.backgroundColor = READ_COLOR_DISABLED; e1 = e0[i].querySelectorAll('input[' + scrread + ']'); if( e1.length > 0 ){ e1[0].checked = false; e1[0].setAttribute(scrread,'false'); } } } //操作されたエレメントの更新 if( c == true ){ //チェックが付けられたアーティクルの色替えとローカル保存 obj.setAttribute(scrread,'true' + idx ); obj.checked = true; e1 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:has(input[' + scrread + '="true' + idx + '"][href="' + u + '"])'); if( e1.length> 0 ){ e1[0].style.backgroundColor = READ_COLOR_ENABLED[idx]; localStorage.setItem( READ_HEADER[idx] + h, u ); readTimeStamp[idx] = obj.getAttribute(CHK_TIMESTAMP); readArticleURL[idx] = obj.getAttribute('href'); } }else{ //チェックが外されたアーティクルを戻しローカル保存データの破棄 obj.checked = false; obj.setAttribute(scrread,'false'); for( i=0; i< numberReadArticles; i++ ){ localStorage.removeItem( READ_HEADER[i] + h ); readTimeStamp[i] = ""; readArticleURL[i] = ""; } } } } //手動更新時に既読ポイントを自動設定する function updateReadCheck(){ var e0, e1, e2, e3; var idx = 0; e0 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:not([' + scrfixed + ']):has(input[' + scrread + '])'); for( var i=0; i< e0.length; i++ ){ if( e0[i].style.display != "none" ){ e1 = e0[i].querySelectorAll('input[' + scrread + ']'); if( e1.length > 0 ){ e2 = e0[i].firstElementChild; if( e2 !== null ){ //1階層下・2階層下のエレメントでdisplay=noneになっている場合がある if( e2.style.display != "none" ){ e3 = e2.firstElementChild; if( e3 !== null ){ if( e3.style.display != "none" ){ e1[0].checked = true; //チェック更新処理 chgReadPoint(e1[0] ,idx++); if( idx == numberReadArticles ) return; } } } } } } } } //既読ポイント設定時に続く数件を自動設定する function appendNextReadCheck(){ var e0, e1, e2, e3; var idx = 0; e0 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:not([' + scrfixed + ']):has(input[' + scrread + '])'); for( var i=0; i< e0.length; i++ ){ if( e0[i].style.display != "none" ){ e1 = e0[i].querySelectorAll('input[' + scrread + ']'); if( e1.length > 0 ){ e2 = e0[i].firstElementChild; if( e2 !== null ){ //1階層下・2階層下のエレメントでdisplay=noneになっている場合がある if( e2.style.display != "none" ){ e3 = e2.firstElementChild; if( e3 !== null ){ if( e3.style.display != "none" ){ //チェックされた本体の特定 if( idx == 0 && e1[0].checked == true ){ idx++; }else if( idx > 0 ){ //チェック更新処理 e1[0].checked = true; chgReadPoint(e1[0] ,idx++); } if( idx == numberReadArticles ) return; } } } } } } } } //既読マーク背景色切り替え function changeReadPointColor(){ if( ! enableReadPointCheckBox ) return; switch( readEnabledColorMode ){ case 0: READ_COLOR_ENABLED = READ_COLOR_ENABLED1; break; case 1: READ_COLOR_ENABLED = READ_COLOR_ENABLED2; break; case 2: READ_COLOR_ENABLED = READ_COLOR_ENABLED3; break; case 3: READ_COLOR_ENABLED = READ_COLOR_ENABLED4; break; } } //既読マーク背景色設定の読込 function loadReadPointColorSetting(){ if( ! enableReadPointCheckBox ) return; var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); readEnabledColorMode = Number(window.localStorage.getItem(CHKCOLOR_HEADER + u)); changeReadPointColor(); } //既読ポイントチェックボックスの右クリック function rclickReadPoint(obj){ if( ! enableReadPointCheckBox ) return; var i, j, e0; var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); //if( obj.getAttribute(scrread).indexOf('true') != 0 ) return; //表示色の入れ替え if( ++readEnabledColorMode == readColorPattern ) readEnabledColorMode = 0; window.localStorage.setItem(CHKCOLOR_HEADER + u, readEnabledColorMode); changeReadPointColor(); forceUpdateColor(COLOR_WHITE, TXT_CHANGEBGTYPE.replace("#", Number(readEnabledColorMode + 1))); for( i = 0; i < numberReadArticles; i++ ){ e0 = document.querySelectorAll('div[data-testid="cellInnerDiv"]:has(input[' + scrread + '="true' + i + '"])'); for( j = 0; j < e0.length; j++ ){ e0[j].style.backgroundColor = READ_COLOR_ENABLED[i]; } } } //URLごとの各種設定をローカルストレージから読み込む function findSettings(){ var v; var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); //インターバル値を一旦初期状態に戻す updateInterval = defaultInterval; v = localStorage.getItem( INTERVAL_HEADER + u ); if( v !== null ){ v = Number(v); if( v < 10 || v > 3600 ) return; updateInterval[mode] = v * 1000; updateCount = 0; } //強制リロードまでの時間を一旦初期状態に戻す reloadTimeout = defaultReloadTimeout; v = localStorage.getItem( RELOAD_HEADER + u ); if( v !== null ){ v = Number(v); if( v < 0 || v > 7200 ) return; reloadTimeout = v; } //自動更新OFFの設定保存値を読み込む autoOff = defaultAutoOff; v = localStorage.getItem( AUTOOFF_HEADER + u ); if( v !== null ){ v = Number(v); if( v == 0 ){ //AUTO = ONにする autoOff = 0; }else{ //AUTO = OFFにする autoOff = 1; displayPaused = 1; } } //短周期タイマの常時ON設定保存値を読み込む idleShortTimer = defalutIdleShortTimer; v = localStorage.getItem( IDLETM_HEADER + u ); if( v !== null ){ v = Number(v); if( v == 0 ){ //ずっと動作させ続けて停止させない idleShortTimer = 0; }else{ //時間がたつと停止 idleShortTimer = 1; } } //手動更新禁止時間の設定保存値を読み込む manualUpdateInterval = defaultManualUpdateInterval; v = localStorage.getItem( MANUAL_HEADER + u ); if( v !== null ){ v = Number(v); if( v < 5 || v > 60 ) return; manualUpdateInterval = v; } } //クリックで入力ダイアログを入力させるボタン部と自動更新のON/OFFをするボタン部を生成 function setupElement(parentElm){ //既にあったら消す if( elmBtnAutoOff !== null ) elmBtnAutoOff.remove(); if( elmBtnInterval !== null ) elmBtnInterval.remove(); if( elmBtnShort !== null ) elmBtnShort.remove(); if( elmBtnHelp !== null ) elmBtnHelp.remove(); if( elmInfo !== null ) elmInfo.remove(); if( elmBtnParent !== null ) elmBtnParent.remove(); if( elmAllParent !== null ) elmAllParent.remove(); //基準となる元エレメントのプロパティ parentElm.style.display = "flex"; parentElm.style.fontSize = titleSize[mode]; //親エレメント生成 elmAllParent = document.createElement("div"); elmAllParent.setAttribute("id",PARENT_BLOCK); elmAllParent.style.display = "flex"; elmAllParent.style.marginLeft = "3px"; elmAllParent.style.marginRight = "3px"; elmAllParent.style.backgroundColor = COLOR_BLACK; elmAllParent.style.border = COLOR_BORDER; elmAllParent.style.borderStyle = "solid"; elmAllParent.style.borderWidth = "1px"; //stopPropagationのあと無名関数自身をreturn falseで返さないと親エレメントのイベントが止まらない elmAllParent.onclick = function(e){e.stopPropagation();return false;}; elmAllParent.oncontextmenu = function(e){e.stopPropagation();return false;}; parentElm.appendChild(elmAllParent); //自動更新情報表示のエレメント生成 elmInfo = document.createElement("div"); elmInfo.setAttribute("id",INFO_BLOCK); elmInfo.style.display = "block"; elmInfo.style.textAlignLast = "center"; elmInfo.style.width = DISPINFO_WIDTH; elmInfo.style.height = DISPINFO_HEIGHT; elmInfo.style.overflowX = "hidden"; elmInfo.style.overflowY = "hidden"; elmInfo.style.color = COLOR_GLAY; elmInfo.style.backgroundColor = COLOR_BLACK; elmInfo.style.fontSize = titleSize[mode]; elmInfo.style.border = COLOR_BORDER; elmInfo.style.borderStyle = "solid"; elmInfo.style.borderWidth = "1px"; elmInfo.textContent = ""; elmInfo.onclick = clickInformationButton1; elmInfo.oncontextmenu = clickInformationButton2; elmAllParent.appendChild(elmInfo); //ボタン親エレメント生成 elmBtnParent = document.createElement("div"); elmBtnParent.setAttribute("id",BUTTON_BLOCK); elmBtnParent.style.display = "flex"; elmBtnParent.style.marginLeft = "0px"; elmBtnParent.style.marginRight = "0px"; elmBtnParent.style.backgroundColor = COLOR_BORDER; elmBtnParent.style.borderWidth = "0px"; //stopPropagationのあと無名関数自身をreturn falseで返さないと親エレメントのイベントが止まらない elmBtnParent.onclick = function(e){e.stopPropagation();return false;}; elmBtnParent.oncontextmenu = function(e){e.stopPropagation();return false;}; elmAllParent.appendChild(elmBtnParent); //ヘルプダイアログボタンのエレメント生成 elmBtnHelp = document.createElement("div"); elmBtnHelp.setAttribute("id",HELP_BLOCK); elmBtnHelp.style.display = "flex"; elmBtnHelp.style.color = COLOR_GLAY; elmBtnHelp.style.backgroundColor = COLOR_BLACK; elmBtnHelp.style.fontSize = buttonSize[mode]; elmBtnHelp.style.border = COLOR_BORDER; elmBtnHelp.style.borderStyle = "solid"; elmBtnHelp.style.borderWidth = "1px"; elmBtnHelp.textContent = BTN_TXT_HLP; elmBtnHelp.onclick = dispInfoDialog; elmBtnHelp.oncontextmenu = dispReadDialog; elmBtnParent.appendChild(elmBtnHelp); //インターバル設定ボタンのエレメント生成 elmBtnInterval = document.createElement("div"); elmBtnInterval.setAttribute("id",INTERVAL_BLOCK); elmBtnInterval.style.display = "flex"; elmBtnInterval.style.color = COLOR_GLAY; elmBtnInterval.style.backgroundColor = COLOR_BLACK; elmBtnInterval.style.fontSize = buttonSize[mode]; elmBtnInterval.style.border = COLOR_BORDER; elmBtnInterval.style.borderStyle = "solid"; elmBtnInterval.style.borderWidth = "1px"; elmBtnInterval.textContent = BTN_TXT_INT; elmBtnInterval.onclick = dispIntervalSetting; elmBtnInterval.oncontextmenu = dispReloadSetting; elmBtnParent.appendChild(elmBtnInterval); //短周期タイマ設定ボタンのエレメント生成 elmBtnShort = document.createElement("div"); elmBtnShort.setAttribute("id",SHORT_BLOCK); elmBtnShort.style.display = "flex"; elmBtnShort.style.color = COLOR_IDL_DISABL; elmBtnShort.style.backgroundColor = COLOR_BLACK; elmBtnShort.style.fontSize = buttonSize[mode]; elmBtnShort.style.border = COLOR_BORDER; elmBtnShort.style.borderStyle = "solid"; elmBtnShort.style.borderWidth = "1px"; elmBtnShort.textContent = BTN_TXT_SRT; elmBtnShort.onclick = idleTimerChange; elmBtnShort.oncontextmenu = idleTimerChange; elmBtnParent.appendChild(elmBtnShort); //自動更新ON/OFF設定ボタンのエレメント生成 elmBtnAutoOff = document.createElement("div"); elmBtnAutoOff.setAttribute("id",AUTO_BLOCK); elmBtnAutoOff.style.display = "flex"; elmBtnAutoOff.style.color = COLOR_AUT_DISABL; elmBtnAutoOff.style.backgroundColor = COLOR_BLACK; elmBtnAutoOff.style.fontSize = buttonSize[mode]; elmBtnAutoOff.style.border = COLOR_BORDER; elmBtnAutoOff.style.borderStyle = "solid"; elmBtnAutoOff.style.borderWidth = "1px"; elmBtnAutoOff.textContent = BTN_TXT_AUT; elmBtnAutoOff.onclick = switchAutoButton; elmBtnAutoOff.oncontextmenu = dispManualSetting; elmBtnParent.appendChild(elmBtnAutoOff); } //クリック操作による短周期タイマ再開と更新トリガ function clickInformationButton(notUpdate){ //スクロール中は最上部のアーティクルが読まれていない状態の事があるので先頭よりちょっと下に戻るのみ(先頭に戻すとトリガされてしまう) if( window.pageYOffset > window.innerHeight ){ window.scrollTo(0, SCROLL_LIMIT + 1.0); return; } var dt = new Date(); //短周期タイマの再開 if( idleShortTimer ){ if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = dt; //console.log("★shortTimer: START[startShortTimerByButton]"); } } //新着表示を戻す if( mode == MODE_NOTI && notUpdate == false ){ var e = document.querySelectorAll('div[aria-label="ホームタイムライン"] > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1)'); if( e.length > 0 ){ var he = e[0].clientHeight; if( he < 50 ){ e[0].style.height = "100px"; flgClearNotification = true; window.setTimeout( chkNotificationClear, 1000 ); } } } if( mode != MODE_HOME && mode != MODE_LIST && mode != MODE_USER && mode != MODE_SEAR ) return; //自動で最新表示中のアーティクルを既読マークする if( manualUpdateAutoCheck && enableReadPointCheckBox ){ updateReadCheck(); } //前回の手動更新から時間が経過していたらトリガ if( dt - lastTriggerTime > manualUpdateInterval * 1000 && notUpdate == false ){ //色替え・手動更新表示 forceUpdateColor(COLOR_UPDATE, TXT_MANUALUPDATE); //更新トリガの時間を記録 lastTriggerTime = new Date(); //手動でトリガ execTrigger(); //手動でトリガを掛けたのでインターバル時間はリセット updateCount = 0; } } //左クリックで更新あり function clickInformationButton1(){ clickInformationButton(false); } //右クリックで更新なし function clickInformationButton2(){ clickInformationButton(true); } //新着件数表示が無くなるまで待つ function chkNotificationClear(){ var e0 = document.querySelectorAll('div[aria-label="ホームタイムライン"] > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1) > div:nth-of-type(1)'); if( e0.length > 0 ){ var e1 = document.querySelectorAll('nav[aria-label="メインメニュー"] a[aria-label^="通知("][aria-label$="件の未読通知)"]'); if( e1.length > 0 ){ if( ++chkNotification < chkNotificationMax ){ window.setTimeout( chkNotificationClear, 1000 ); return; } } window.setTimeout( function(){ window.scrollTo(0, window.innerHeight);}, 5000 ); window.setTimeout( function(){ window.scrollTo(0, 0); flgClearNotification = false;}, 5100 ); //console.log("★Finish!" + chkNotification); chkNotification = 0; e0[0].style.height = null; } } //情報表示ダイアログ function dispInfoDialog(){ var idx; var aut = (autoOff) ? "ON" : "OFF"; var sht = (idleShortTimer) ? "OFF" : "ON"; var rth = Math.floor(reloadTimeout / 3600); var rtm = Math.floor(reloadTimeout % 3600 / 60); var rts = reloadTimeout % 60; var rtt = ("0" + rth).slice(-2) + ":" + ("0" + rtm).slice(-2) + ":" + ("0" + rts).slice(-2); var reb = (reloadTimeout) ? rtt : "----"; var pat = window.location.href.replace(TWITTER_URL, ""); var siz = window.innerWidth + "x" + window.innerHeight; var obj = new Date(); var ntf = (pat == PATH_NOTI) ? true : false; var msg = spt + "【X(Twitter)WEB 自動更新スクリプト】"; msg += spc + "------------------------------------"; if(!ntf){ msg += spc + "自動更新インターバル = " + updateInterval[mode] / 1000 + "(sec)"; msg += spc + "手動更新操作の禁止期間 = " + manualUpdateInterval + "(sec)"; msg += spc + "自動更新の強制停止モード = " + aut; } msg += spc + "短周期タイマ常時稼動モード = " + sht; msg += spc + "DOM更新受信最終時刻 = " + timeStr; msg += spc + "強制再起動までの待機時間 = " + reb; msg += spc + "URL = /" + pat; msg += spc + "TabSize = " + siz; msg += spc + "------------------------------------"; msg += spc + "情報表示欄"; if(!ntf){ msg += spc + "  : 左クリック=手動更新"; msg += spc + "   +[右クリック動作]"; msg += spc + "  : 右クリック=スクロール戻し"; if(enableReadPointCheckBox){ msg += spc + "   +自動既読マーク"; } if(idleShortTimer){ msg += spc + "   +短周期タイマ再開"; } }else{ msg += spc + "  : 左クリック=新着表示色クリア"; msg += spc + "   +[右クリック動作]"; msg += spc + "  : 右クリック=スクロール戻し"; if(idleShortTimer){ msg += spc + "   +短周期タイマ再開"; } } if(!ntf){ msg += spc + BTN_TXT_HLP + " : 左クリック=ヘルプダイアログの表示"; if(enableReadPointCheckBox && mode != MODE_NOTI){ msg += spc + "  : 左クリック=既読情報ダイアログの表示"; } msg += spc + BTN_TXT_INT + " : 左クリック=インターバル変更"; msg += spc + "  : 右クリック=強制リロード時間変更"; }else{ msg += spc + BTN_TXT_INT + " : 右クリック=強制リロード時間変更"; } msg += spc + BTN_TXT_SRT + " : 短周期タイマ常時稼動切り替え"; if(!ntf){ msg += spc + BTN_TXT_AUT + " : 左クリック=自動更新OFF切り替え"; msg += spc + "  : 右クリック=手動更新禁止期間変更"; } if( enableReadPointCheckBox ){ msg += spc + "チェックボックス : ColorType=[" + readEnabledColorMode + "]"; msg += spc + "  : 左クリック=" + numberReadArticles + "件を既読マーク"; msg += spc + "  : 右クリック=既読マーク背景色切替"; } msg += spc + "------------------------------------"; if(!ntf){ msg += spc + "[COUNT / DOM時刻] , "; } msg += spc + "[Latest / DOM時刻] :"; if(!ntf){ msg += spc + "   緑:自動更新トリガ有効"; msg += spc + "   黄:自動更新トリガ直前"; msg += spc + "   赤:自動更新トリガ発行中"; if( ! enableTimeAutoOff ){ msg += spc + "[Paused Refresh] :"; }else{ msg += spc + "[Paused / DOM時刻] :"; } msg += spc + "   青:自動更新停止中"; }else{ msg += spc + "   緑:(通知ページは常時自動動作)"; } msg += spc + "表示内容に依存しない色替 :"; msg += spc + "   水:手動更新の抑制期間中"; msg += spc + "   紫:強制リロード直前"; msg += spc + "------------------------------------"; msg += spc + BTN_TXT_INT + "(明色) / " + BTN_TXT_HLP + "(暗色) :"; msg += spc + "   基本色は情報表示部に準ずる"; msg += spc + BTN_TXT_SRT + " :"; msg += spc + "  短周期タイマ都度停止中"; msg += spc + "   基本色は情報表示部に準ずる"; msg += spc + "   灰:短周期タイマ停止中"; msg += spc + "  短周期タイマ常時稼動中"; msg += spc + "   基本色は情報表示部に準ずる"; if(!ntf){ msg += spc + BTN_TXT_AUT + " :"; msg += spc + "  自動更新の通常動作中"; msg += spc + "   基本色は情報表示部に準ずる"; msg += spc + "  自動更新の強制停止中"; msg += spc + "   灰:自動更新強制停止中"; } msg += spc + "------------------------------------"; msg += spc + "http://coltpythonkingcobra.g1.xrea.com/pseudoTweetdeck/"; window.alert(msg); } //情報表示ダイアログ(既読ポイント情報) function dispReadDialog(){ if( ! enableReadPointCheckBox || numberReadArticles < 0 || mode == MODE_NOTI ) return; var idx, dsp=0; var msg = spt + "【X(Twitter)WEB 自動更新スクリプト】"; msg += spc + "------------------------------------"; msg += spc + "既読ポイント数 : " + numberReadArticles; msg += spc + "表示タイプ : " + Number(readEnabledColorMode + 1) + " / " + readColorPattern; msg += spc + "------------------------------------"; for( idx = 0; idx < numberReadArticles; idx++ ){ if( readArticleURL[idx] != "" ){ msg += spc + "既読ポイント" + Number(idx + 1) + ":(" + readTimeStamp[idx] + ")"; msg += spc + readArticleURL[idx]; msg += spc + "------------------------------------"; dsp++; }} if( dsp == 0 ){ msg += spc + "現在既読ポイントは設定されていません"; msg += spc + "------------------------------------"; } msg += spc + "http://coltpythonkingcobra.g1.xrea.com/pseudoTweetdeck/"; window.alert(msg); } //インターバル値を入力させる function dispIntervalSetting(){ if( mode == MODE_NOTI ) return; //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); var ret = window.prompt( spt + "自動更新間隔" + spc + spc + "URL = /" + u + spc + "設定値: " + manualUpdateInterval + "〜3600(sec)", updateInterval[mode] /1000); if( ret === null ) return; ret = Number(ret); if( ret < manualUpdateInterval || ret > 3600 ) return; //console.log("★InputValue = " + ret); updateInterval[mode] = ret * 1000; updateCount = 0; localStorage.setItem( INTERVAL_HEADER + u, ret ); //console.log("★SaveSetting: [" + u + "] = " + ret); } //強制リロード値を入力させる function dispReloadSetting(){ //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); var ret = window.prompt( spt + "最後に更新があがってきてから" + spc + "強制リロード発動までの時間" + spc + spc + "URL = /" + u + spc + "設定値: " + ((updateInterval[mode] /1000) * 2) + "〜7200(sec)" + spc + "(※不使用時は 0 に設定)", reloadTimeout); if( ret === null ) return; ret = Number(ret); if( ret != 0 && ( ret < ((updateInterval[mode] /1000) * 2) || ret > 7200 ) ) return; //console.log("★InputValue = " + ret); reloadTimeout = ret; localStorage.setItem( RELOAD_HEADER + u, ret ); //console.log("★SaveSetting: [" + u + "] = " + ret); } //手動更新禁止期間値を入力させる function dispManualSetting(){ //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); var ret = window.prompt( spt + "更新リクエスト発行から" + spc + "次の更新までの禁止時間" + spc + spc + "URL = /" + u + spc + "設定値: 5〜60(sec)", manualUpdateInterval); if( ret === null ) return; ret = Number(ret); if( ret < 5 || ret > 60 ) return; //console.log("★InputValue = " + ret); manualUpdateInterval = ret; localStorage.setItem( MANUAL_HEADER + u, ret ); //console.log("★SaveSetting: [" + u + "] = " + ret); } //自動更新をON/OFFする(OFFでは強制停止、ONの時はスクロールバーの位置やエディット入力にフォーカスがあるか無いかで判定する従来の動作をする) function switchAutoButton(){ var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); if( elmBtnAutoOff === null ) return; if( autoOff == 1 ){ //AUTO = ONにする autoOff = 0; if(u.length > 2){ localStorage.setItem( AUTOOFF_HEADER + u, 0 ); if(displayChgDialog){ window.alert("  自動更新を通常モードに切り替えました  "); } } }else{ //AUTO = OFFにする autoOff = 1; if(u.length > 2){ localStorage.setItem( AUTOOFF_HEADER + u, 1 ); if(displayChgDialog){ window.alert("  自動更新を強制OFFモードに切り替えました  "); } } } } //短周期タイマの常時動作切り替え function idleTimerChange(){ if( idleShortTimer == 0 ){ idleShortTimer = 1; if(displayChgDialog){ window.alert("  短周期タイマを都度停止するモードに切り替えました  "); } }else{ idleShortTimer = 0; if(displayChgDialog){ window.alert("  短周期タイマを常時稼動モードに切り替えました  "); } } var u = window.location.href; if(u.length < (TWITTER_URL.length + 4)) return; u = u.replace(TWITTER_URL, ""); if(u.length > 2){ localStorage.setItem( IDLETM_HEADER + u, idleShortTimer ); } //どちらの設定であっても切り替えられたら短周期タイマを再開させる if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = new Date(); //console.log("★shortTimer: START[idleTimerChange]"); } } //ローカルストレージに記憶するキーを変更したので自動移行処理 ※一定の期間が過ぎたらコメントアウトにします (function() { const OLD_INTERVAL_HEADER = "INTERVAL-"; const OLD_AUTOOFF_HEADER = "AUTOOFF-"; const OLD_IDLETM_HEADER = "IDLETM-"; var OLD_READ_HEADER = []; for( def_idx=0; def_idx < 100; def_idx++ ){ if( def_idx == 0 ){ OLD_READ_HEADER[def_idx] = "READPOINT-"; }else{ OLD_READ_HEADER[def_idx] = "READPOINT" + def_idx + "-"; } READ_HEADER[def_idx] = "PTD_RD" + ("0" + def_idx).slice(-2) + "-"; } var idx; var imax = window.localStorage.length; var keys; var delkeylist = []; var repkeylist = []; var repvallist = []; var listidx = 0; var val; for( idx = imax - 1; idx > -1; idx-- ){ keys = "" + window.localStorage.key(idx); if( keys.indexOf(OLD_INTERVAL_HEADER) == 0 ){ val = "" + window.localStorage.getItem(keys); delkeylist[listidx] = keys; repkeylist[listidx] = keys.replace(OLD_INTERVAL_HEADER,INTERVAL_HEADER); repvallist[listidx] = val; listidx++; } if( keys.indexOf(OLD_AUTOOFF_HEADER) == 0 ){ val = "" + window.localStorage.getItem(keys); delkeylist[listidx] = keys; repkeylist[listidx] = keys.replace(OLD_AUTOOFF_HEADER,AUTOOFF_HEADER); repvallist[listidx] = val; listidx++; } if( keys.indexOf(OLD_IDLETM_HEADER) == 0 ){ val = "" + window.localStorage.getItem(keys); delkeylist[listidx] = keys; repkeylist[listidx] = keys.replace(OLD_IDLETM_HEADER,IDLETM_HEADER); repvallist[listidx] = val; listidx++; } for( var i=0; i < 100; i++ ){ if( keys.indexOf(OLD_READ_HEADER[i]) == 0 ){ val = "" + window.localStorage.getItem(keys); delkeylist[listidx] = keys; repkeylist[listidx] = keys.replace(OLD_READ_HEADER[i],READ_HEADER[i]); repvallist[listidx] = val; listidx++; } } } for( idx=0; idx < listidx; idx++ ){ window.localStorage.setItem(repkeylist[idx], repvallist[idx]); window.localStorage.removeItem(delkeylist[idx]); //console.log("NewKey:" + repkeylist[idx] + " OldKey:" + delkeylist[idx] + " value=" + repvallist[idx]); } })(); //メイン処理 (function() { //初期処理の遅延実行 window.setTimeout(clearElements, initWait); //短周期処理開始 shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = new Date(); //console.log("★shortTimer: START[FIRST]"); //更新処理開始 window.setInterval(updateTimeline, timerInterval); })(); //---------------------------- //ローカル保存データの削除方法 //---------------------------- // // このスクリプトを実行することで(数値入力やクリックなどで設定を行うと) // 「自動更新の強制OFFモード」「短周期タイマの動作設定」「自動再起動までのインターバル設定」「既読ポイント」 // がローカルに保存されています。タブを並べて閲覧する対象はそんな滅多に増減しないと思うので無尽蔵に増え続けて // 破綻みたいなことは滅多にないと思うけど、一応掃除の仕方を書いておきます。 // // 下のブロックコメントアウト始端 「/*」 と 終端「*/」 を削除して保存し本スクリプトを実行する(Twitterページを // リロードする)と本スクリプトで追加したローカル保存データを全クリアします。 // // 初期化するかどうかダイアログが出たら「はい」「YES」「OK」「了解」「ダー」などを押します。 // 削除が終わったら何件削除したのかダイアログが出るのでスクリプトを終了させ、「/*」 と 「*/」を元に戻して無効に // して下さい。 // // この削除処理は読込序盤でダイアログが表示されてページロードを阻害する(コンソールログ見てると色々タイムアウト // してるっぽい?)のでコメントアウトを戻してセーブしたら、少なくともタブのリロードを、出来ればVivaldi自体を再 // 起動したほうがいいと思います。 // // マーク件数を変更した後に削除する場合は、numberReadArticles値を一旦大きい値にして実行することで全部消えます。 // /* //------------ // 全削除 //------------ (function() { if( window.confirm("  自動更新スクリプトが保存した\r\n  全ローカルデータを初期しますか?") == false ) return; var idx; var imax = window.localStorage.length; var keys; var removeCount = 0; for( idx = imax - 1; idx > -1; idx-- ){ keys = "" + window.localStorage.key(idx); if( keys.indexOf(INTERVAL_HEADER) == 0 || keys.indexOf(AUTOOFF_HEADER) == 0 || keys.indexOf(IDLETM_HEADER) == 0 || keys.indexOf("PTD_") == 0 ){ window.localStorage.removeItem(keys); removeCount++; } for( var i=0; i < numberReadArticles; i++ ){ if( keys.indexOf(READ_HEADER[i]) == 0 ){ window.localStorage.removeItem(keys); removeCount++; } } } window.alert("  " + removeCount + "件のデータを削除しました"); })(); //------------ */