// ==UserScript== // @name HomeTL/List/User/Searchページを自動更新する // @namespace http://coltpythonkingcobra.g1.xrea.com/pseudoTweetdeck/ // @version 1.0.09042000 // @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"; */ //長時間更新が上がってこない時に強制リロードさせる=1, 強制リロードなしで手動リロード運用する=0 const useForceReload = 1; //短周期タイマを一定時間後に停止する=1, 停止させず回ったままにする=0 (▲のクリックで変更可能) const defalutIdleShortTimer = 1; var idleShortTimer = defalutIdleShortTimer; //自動更新OFFモード スクロール位置やエディットのフォーカスによらず強制OFF=1, 従来の動作=0 const defaultAutoOff = 0; var autoOff = defaultAutoOff; //短周期タイマの常時動作ON/自動更新強制OFF の切り替え時に確認ダイアログを出す=1, 出さない=0 const displayChgDialog = 0; //更新間隔カウンタ var updateCount = 0; //更新間隔定義(秒)=これを初期値として個別ページで■をクリックすることで値の入力・保存が可能 const updateIntervalHome = 30; const updateIntervalList = 300; const updateIntervalUser = 600; const updateIntervalSearch = 180; //更新間隔 [ unknown(未使用), Home, List, User、Notification(未使用), Search ] const defaultInterval = [1000,updateIntervalHome * 1000,updateIntervalList * 1000,updateIntervalUser * 1000,1000,updateIntervalSearch * 1000] var updateInterval = defaultInterval; //監視間隔タイマの間隔(ミリ秒) const timerInterval = 1000; //短周期タイマオブジェクト var shortTimer = null; //短周期カウンタ var shortCount = 0; //短周期タイマの間隔(ミリ秒) const shortInterval = 250; //短周期タイマを止めるまでの経過時間(ミリ秒) const shortTimerIdle = 10000; //起動時待ち合わせ時間(ミリ秒) const initWait = 10000; //自動更新が停止中 var displayPaused = 0; //スクロール位置の前回値 var lastYOffset = 0; //スクロール位置が先頭に戻った後の短縮更新待ちあわせ時間(ミリ秒) const onTopWait = 3000; //強制リロード時の待ち合わせ時間(ミリ秒) const reloadWait = 3000; //強制リロード実施間隔(秒) const reloadTimeoutMin = 3600; //強制リロード乱数補正半値(秒) const reloadTimeoutRnd = 300; //強制リロード乱数補正(秒) const reloadTimeoutSub = reloadTimeoutRnd * 2; //強制リロード実施間隔ミリ秒(各タブで同時に走らないように乱数使用) 3600±300秒 const reloadTimeout = Math.trunc(reloadTimeoutMin + Math.random() * reloadTimeoutSub - reloadTimeoutRnd); //初期化が必要=0, 初期化済み=1 var initComplete = 0; //前回処理URL var lastURL = null; //表示中のページ種別 0:unknown, 1:Home, 2:List, 3:User, 4:Notification, 5:Search var mode = 0; //ログを取る場合などの種別プリフィクス用文字列 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; //情報表示部の色 const COLOR_GLAY = "rgba(127,127,127,0.5)";//初期色(非稼働) 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_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_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_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)";//短周期タイマ適宜停止モード状態で自動更新の一時停止状態の色 //自動更新ON/OFFボタンの色:OFFモードで点灯,ONモードで消灯の負論理 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時で自動更新の一時停止状態の色 //情報表示部のフォントサイズ 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 elmInfo = null; //ボタンコントロールのエレメント var elmBtnParent = null; var elmBtnShort = null; var elmBtnInterval = null; var elmBtnAutoOff = 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; //localStorage保存キーのプリフィクス const INTERVAL_HEADER = "INTERVAL-"; const AUTOOFF_HEADER = "AUTOOFF-"; const IDLETM_HEADER = "IDLETM-"; //操作ボタンコントロールのID const PARENT_BLOCK = "XXX_PARENT_BLOCK"; const INFO_BLOCK = "XXX_INFO_BLOCK" const SHORT_BLOCK = "XXX_SHORT_BLOCK"; const INTERVAL_BLOCK = "XXX_INTERVAL_BLOCK"; const AUTO_BLOCK = "XXX_AUTO_BLOCK"; //日付時刻の整形 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; } //MutationObserverでDOM更新が上がってきた最終タイムスタンプ保持 var timeObj = new Date(); var timeStr = toFormatedTimeString(timeObj); //短時間タイマ開始時間を保持 var timeShort = null; /* //一般ファイル吐き出しタイプのログ(ダウンロード処理によって代替するのでダウンロードフォルダへの自動保存設定である必要あり、とても重い) 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 == "https://twitter.com/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("https://twitter.com/i/lists/") != -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.indexOf("https://twitter.com/home") != -1 || hr.indexOf("https://twitter.com/i/lists/") != -1 || hr.indexOf("https://twitter.com/notifications") != -1 || hr.indexOf("https://twitter.com/messages") != -1 || hr.indexOf("https://twitter.com/compose") != -1 || hr.indexOf("https://twitter.com/settings") != -1 || hr.indexOf("https://twitter.com/explore") != -1 || hr.indexOf("https://twitter.com/i/bookmarks") != -1 || hr.indexOf("https://twitter.com/i/circles") != -1 || hr.indexOf("https://twitter.com/i/connect_people") != -1 || hr.indexOf("https://twitter.com/i/display") != -1 || hr.indexOf("https://twitter.com/ja/") != -1 || hr.indexOf("https://twitter.com/en/") != -1 || (hr.indexOf("https://twitter.com/") != -1 && hr.indexOf("/status/") != -1) || (hr.indexOf("https://twitter.com/") != -1 && hr.indexOf("/lists") != -1) || hr.indexOf("https://twitter.com/search") != -1){ //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("https://twitter.com/notifications") != -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("https://twitter.com/search") != -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; if(idleShortTimer){ //停止していた短周期タイマを再開 if( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = new Date(); //console.log("★shortTimer: START[clearElements]"); } } //初期処理 clearElements(); //URLに対応する更新時間設定があるか検索 findInterval(); //URLに対応する自動更新OFF設定があるか検索 findAutoOffSetting(); //URLに対応する短周期タイマ常時ON設定があるか検索 findIdleTimerSetting(); return; } //自動更新に該当しないページは何もせず戻る if( mode == MODE_NULL ){ return; } if(idleShortTimer){ //短周期タイマ開始から時間が経過したらタイマを停止させる if( shortTimer !== null ){ tmpObj = new Date(); 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) > 5.0) && shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = new Date(); } lastYOffset = window.pageYOffset; } } //console.log("☆PageY-Offset=" + window.pageYOffset); if(window.pageYOffset <= 5.0) { if(!shouldNotUpdate()) { if(updateCount >= updateInterval[mode]) { //フォーカス判定が外れた状態からの復帰 if( initComplete == 0 ){ clearElements(); } if(useForceReload){ //一定時間以上DOM更新が無い場合はリロードする tmpObj = new Date(); if( ( tmpObj.getTime() - timeObj.getTime() ) > (reloadTimeout * 1000) ){ //if(filePfx!==null) logSave( modename[mode] + " RELOAD [" + ("000" + reloadCount).slice(-3) + "] " + timeStr ); window.setTimeout( function(){ window.location.reload(); }, reloadWait ); displayPaused = 0; updateCount = 0; if(elm0 !== null){ elmInfo.style.color = COLOR_UPDATE; elmBtnAutoOff.style.color = COLOR_AUT_UPDATE; elmBtnShort.style.color = COLOR_IDL_UPDATE; elmBtnInterval.style.color = COLOR_INT_UPDATE; elmInfo.textContent = "Reload Page..." } initComplete = 0; return; } } if( mode == MODE_HOME ){ //メニューのHomeを押す事で更新トリガさせる var homeButton = document.body.querySelectorAll('a[data-testid="AppTabBar_Home_Link"]'); if(homeButton.length > 0) { homeButton[0].click(); //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 ); //if(filePfx!==null) logSave(modename[mode] + " @" + (window.location.href.slice(28) + " ").substr(0,19) + " SCROLL [" + ("000" + reloadCount).slice(-3) + "] " + timeStr ); //console.log("★Scroll Update"); } 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; updateCount = updateInterval[mode] - onTopWait; if(updateCount < 0 ) updateCount = 0; //console.log("☆Wait for window top [" + updateCount + "]" ); } //更新に関する情報を表示する if( initComplete != 0 || displayPaused ){ //カウンタ表示用のエレメントを捜索 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 ){ /* elm1 = elm0[0].nextElementSibling; if( elm1 !== null ){ //console.log("★find: Element"); } */ elm4 = elm0[0].firstElementChild; if( elm4 !== null ){ setupElement(elm4); /* elm5 = elm0[0].nextElementSibling; if(elm5 !== null){ //リスト制作者名の非表示 elm5.style.display = "none"; } CSSで消す方法に変更 */ } } }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 ){ //通知ページは勝手に更新が上がってくるので更新間隔の定義なしだから■◆なし 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"); } } } } } } } } } //カウンタを更新 if( elmInfo !== null ){ tmp = updateInterval[mode] - updateCount; if(tmp < 0 ) tmp = 0; var colorType = 0; const TYPE_UPDATE = 1; const TYPE_UPSOON = 2; const TYPE_ACTIVE = 3; const TYPE_PAUSED = 4; //フォーカスを持ったタブのみカウントを都度更新(全部に対して行うと重すぎる) //設定内容を設定先と比較して違ったら代入するようにしているのは、代入によって内部でDOM更新が走り処理が重くなるため if( document.visibilityState === "visible" && document.hasFocus() === true && mode != MODE_NOTI ){ if( ! displayPaused ){ str = " [ " + ("0000" + (tmp / 1000)).slice(-4) + " / " + 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(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{ str = " [Paused Refresh]"; 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(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{ str = " [Paused Refresh]"; 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_UPDATE){ if(elmBtnInterval.style.color != COLOR_INT_UPDATE) elmBtnInterval.style.color = COLOR_INT_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( 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( 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( 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; } } } } } //要素の初回書き換わりを監視 window.addEventListener('load', async 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( shortTimer === null ){ shortTimer = window.setInterval(replaceEtements, shortInterval); timeShort = new Date(); //console.log("★shortTimer: START[DOM]"); } //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(){ //画面遷移が発生したので動作モードの決定 var hr = window.location.href; if( hr.indexOf("https://twitter.com/home") != -1 ){ mode = MODE_HOME; }else if( hr.indexOf("https://twitter.com/i/lists/") != -1 ){ mode = MODE_LIST; }else if( hr.indexOf("https://twitter.com/notifications") != -1 ){ mode = MODE_NOTI; }else if( hr.indexOf("https://twitter.com/search") != -1 ){ mode = MODE_SEAR; }else if( hr.indexOf("https://twitter.com/messages") != -1 || hr.indexOf("https://twitter.com/compose") != -1 || hr.indexOf("https://twitter.com/settings") != -1 || hr.indexOf("https://twitter.com/explore") != -1 || hr.indexOf("https://twitter.com/i/bookmarks") != -1 || hr.indexOf("https://twitter.com/i/circles") != -1 || hr.indexOf("https://twitter.com/i/connect_people") != -1 || hr.indexOf("https://twitter.com/i/display") != -1 || hr.indexOf("https://twitter.com/ja/") != -1 || hr.indexOf("https://twitter.com/en/") != -1 || (hr.indexOf("https://twitter.com/") != -1 && hr.indexOf("/status/") != -1) || (hr.indexOf("https://twitter.com/") != -1 && hr.indexOf("/lists") != -1)){ mode = MODE_NULL; lastURL = ""; window.setTimeout(clearElements, initWait); return; }else if( hr.indexOf("https://twitter.com/") != -1 ){ //ユーザーアカウント名で終わるURLなので消去法で各種ページを除外していかないと判定が出来ない mode = MODE_USER; } //各種カウンタの初期化 updateCount = 0; displayPaused = 0; shortCount = 0; elm0 = elm1 = elm2 = elm3 = elm4 = 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^="タイムライン:"]'); }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; } //既に張られているovserverを消去 observer.disconnect(); //要素の変化監視をスタート observer.observe(elm0, configObserver); //表示更新用エレメントの再取得 elmInfo = null; initComplete = 1; //if(filePfx!==null) logSave(modename[mode] + " CLEAR "); //console.log("★clear: Element"); } //短周期処理(タイムスタンプ置換、表示タイトル変更、自動押下) async function replaceEtements() { //タイムスタンプ置換 document.querySelectorAll('main div[data-testid="primaryColumn"] section article a[href*="/status/"] time').forEach(function(e) { let a = e.parentNode; let span = document.createElement('span'); let s0 = e.getAttribute('datetime'); let s1 = toFormatedDateString(new Date(s0)); span.setAttribute('datetime', s0); span.setAttribute('local-datetime', s1); span.textContent = s1; a.appendChild(span); a.removeChild(e); }); //console.log("★ 1 Title=" + document.title); if( document.title.indexOf('@') != -1 ){ let t, idx1, idx2; // Listページ タブタイトルの変更 「@USERNAME/ListName」→「ListName」 if( window.location.href.indexOf("https://twitter.com/i/lists/") !== -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件のツイートを表示 を自動クリック var e, e1, e2, e3, e4; e = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-1777fci r-1pl7oy7 r-1ny4l3l r-o7ynqc r-6416eg r-13qz1uu"]'); if( e.length !== 0 ){ //console.log("★Trigger1"); e[0].click(); } e = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-16y2uox r-19u6a5r r-1ny4l3l r-m2pi6t r-o7ynqc r-6416eg"]'); if( e.length !== 0 ){ //console.log("★Trigger2"); e[0].click(); } e = document.querySelectorAll('[class="css-18t94o4 css-1dbjc4n r-1777fci r-dfe81l r-1ny4l3l r-o7ynqc r-6416eg r-13qz1uu"]'); if( e.length !== 0 ){ e1 = e[0].firstElementChild; if( e1 !== null ){ e2 = e1.firstElementChild; if( e2 !== null ){ e3 = e2.firstElementChild; if( e3 !== null ){ e4 = e3.textContent; if( e4.indexOf("さらに") > 0 || e4.indexOf("More") > 0 || e4.indexOf("more") > 0 ){ //console.log("★Trigger3"); e[0].click(); return; } } } } //console.log("★offset = " + window.pageYOffset + " height = " + window.innerHeight); if( window.pageYOffset <= 5.0 ) { //console.log("★Trigger3"); e[0].click(); } if( window.pageYOffset > window.innerHeight / 2 ) { //console.log("★Trigger4"); e[0].click(); } } } } //保存されている設定値を読み込む function findInterval(){ //インターバル値を一旦初期状態に戻す updateInterval = defaultInterval; //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < 24) return; u = u.replace("https://twitter.com/", ""); var v = localStorage.getItem( INTERVAL_HEADER + u ); //console.log("★LoadSetting: [" + u + "] = " + v); if( v === null ) return; v = Number(v); if( v < 10 || v > 3600 ) return; updateInterval[mode] = v * 1000; updateCount = 0; } //自動更新OFFの設定保存値を読み込む function findAutoOffSetting(){ //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < 24) return; u = u.replace("https://twitter.com/", ""); var v = localStorage.getItem( AUTOOFF_HEADER + u ); if( v === null ) return; v = Number(v); if( v == 0 ){ //AUTO = ONにする autoOff = 0; }else{ //AUTO = OFFにする autoOff = 1; displayPaused = 1; } } //短周期タイマの常時ON設定保存値を読み込む function findIdleTimerSetting(){ //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < 24) return; u = u.replace("https://twitter.com/", ""); var v = localStorage.getItem( IDLETM_HEADER + u ); if( v === null ) return; v = Number(v); if( v == 0 ){ //ずっと動作させ続けて停止させない idleShortTimer = 0; }else{ //時間がたつと停止 idleShortTimer = 1; } } //クリックで入力ダイアログを入力させるボタン部と自動更新のON/OFFをするボタン部を生成 function setupElement(parentElm){ //既にあったら消す if( elmBtnAutoOff !== null ) elmBtnAutoOff.remove(); if( elmBtnInterval !== null ) elmBtnInterval.remove(); if( elmBtnShort !== null ) elmBtnShort.remove(); if( elmInfo !== null ) elmInfo.remove(); if( elmBtnParent !== null ) elmBtnParent.remove(); //基準となる元エレメントのプロパティ parentElm.style.display = "flex"; parentElm.style.fontSize = titleSize[mode]; //親エレメント生成 elmBtnParent = document.createElement("div"); elmBtnParent.setAttribute("id",PARENT_BLOCK); elmBtnParent.style.display = "flex"; elmBtnParent.style.marginLeft = "3px"; elmBtnParent.style.marginRight = "3px"; elmBtnParent.style.backgroundColor = "rgba(31,31,31,1.0)"; elmBtnParent.style.border = "rgba(0,0,0,1.0)"; elmBtnParent.style.borderStyle = "solid"; elmBtnParent.style.borderWidth = "1px"; elmBtnParent.onclick = function(e){e.stopPropagation();return false;}; parentElm.appendChild(elmBtnParent); //自動更新情報表示のエレメント生成 elmInfo = document.createElement("div"); elmInfo.setAttribute("id",INFO_BLOCK); elmInfo.style.display = "flex"; elmInfo.style.color = COLOR_GLAY; elmInfo.style.fontSize = titleSize[mode]; elmInfo.style.marginLeft = "1px"; elmInfo.style.marginRight = "1px"; elmInfo.textContent = ""; elmInfo.onclick = dispInfoDialog; elmBtnParent.appendChild(elmInfo); //インターバル設定ボタンのエレメント生成 elmBtnInterval = document.createElement("div"); elmBtnInterval.setAttribute("id",INTERVAL_BLOCK); elmBtnInterval.style.display = "flex"; elmBtnInterval.style.color = COLOR_GLAY; elmBtnInterval.style.fontSize = buttonSize[mode]; elmBtnInterval.style.marginLeft = "1px"; elmBtnInterval.style.marginRight = "1px"; elmBtnInterval.textContent = "■"; elmBtnInterval.onclick = dispSetting; elmBtnParent.appendChild(elmBtnInterval); //短周期タイマ設定ボタンのエレメント生成 elmBtnShort = document.createElement("div"); elmBtnShort.setAttribute("id",SHORT_BLOCK); elmBtnShort.style.display = "flex"; elmBtnShort.style.color = COLOR_IDL_DISABL; elmBtnShort.style.fontSize = buttonSize[mode]; elmBtnShort.style.marginLeft = "1px"; elmBtnShort.style.marginRight = "1px"; elmBtnShort.textContent = "▲"; elmBtnShort.onclick = 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.fontSize = buttonSize[mode]; elmBtnAutoOff.style.marginLeft = "1px"; elmBtnAutoOff.style.marginRight = "1px"; elmBtnAutoOff.textContent = "◆"; elmBtnAutoOff.onclick = switchAutoButton; elmBtnParent.appendChild(elmBtnAutoOff); } //情報表示ダイアログ function dispInfoDialog(){ var spc = "\r\n  "; 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 = (useForceReload) ? rtt : "----"; var pat = window.location.href.replace("https://twitter.com/", ""); var siz = window.innerWidth + "x" + window.innerHeight; var obj = new Date(); var ntf = (pat == "notifications") ? true : false; var msg = spc + "【X(Twitter)WEB 自動更新スクリプト】"; msg += spc + "------------------------------------"; msg += spc + "自動更新インターバル = " + updateInterval[mode] / 1000 + "(sec)"; msg += spc + "自動更新の強制停止モード = " + aut; msg += spc + "短周期タイマ常時稼動モード = " + sht; msg += spc + "DOM更新受信最終時刻 = " + timeStr; msg += spc + "強制再起動までの待機時間 = " + reb; msg += spc + "URL = /" + pat; msg += spc + "TabSize = " + siz; msg += spc + "------------------------------------"; if(!ntf){ msg += spc + "■ : 自動更新インターバルの変更"; } msg += spc + "▲ : 短周期タイマ常時稼動切り替え"; if(!ntf){ msg += spc + "◆ : 自動更新の強制OFF切り替え"; } msg += spc + "------------------------------------"; if(!ntf){ msg += spc + "[COUNT / DOM時刻] , "; } msg += spc + "[Latest / DOM時刻] :"; if(!ntf){ msg += spc + "   緑:自動更新トリガ有効"; msg += spc + "   黄:自動更新トリガまで10秒以内"; msg += spc + "   赤:自動更新トリガ発行中"; msg += spc + "[Paused Refresh] :"; msg += spc + "   青:自動更新停止中"; }else{ msg += spc + "   緑:(通知ページは常時自動動作)"; } msg += spc + "------------------------------------"; msg += spc + "■ :"; msg += spc + "   緑:自動更新トリガ有効"; msg += spc + "   黄:自動更新トリガまで10秒以内"; msg += spc + "   赤:自動更新トリガ発行中"; msg += spc + "   青:自動更新停止中"; msg += spc + "▲ :"; msg += spc + "  短周期タイマ都度停止中"; msg += spc + "   緑:自動更新トリガ有効"; msg += spc + "   黄:自動更新トリガまで10秒以内"; msg += spc + "   赤:自動更新トリガ発行中"; msg += spc + "   青:自動更新停止中"; msg += spc + "   灰:短周期タイマ停止中"; msg += spc + "  短周期タイマ常時稼動中"; msg += spc + "   暗緑:自動更新トリガ有効"; msg += spc + "   暗黄:自動更新トリガまで10秒以内"; msg += spc + "   暗赤:自動更新トリガ発行中"; msg += spc + "   暗青:自動更新停止中"; if(!ntf){ msg += spc + "◆ :"; msg += spc + "  自動更新の通常動作中"; msg += spc + "   緑:自動更新トリガ有効"; msg += spc + "   黄:自動更新トリガまで10秒以内"; msg += spc + "   赤:自動更新トリガ発行中"; msg += spc + "   青:自動更新停止中"; msg += spc + "  自動更新の強制停止中"; msg += spc + "   灰:自動更新強制停止中"; } msg += spc + "------------------------------------"; msg += spc + "http://coltpythonkingcobra.g1.xrea.com/pseudoTweetdeck/"; window.alert(msg); } //インターバル値を入力させる function dispSetting(){ //URLを確認 最低限"https://twitter.com/"だけで20文字あるはず var u = window.location.href; if(u.length < 24) return; u = u.replace("https://twitter.com/", ""); var ret = window.prompt( "  自動更新間隔\r\n  URL = /" + u + "\r\n  設定値: 10〜3600(sec)", updateInterval[mode] /1000); if( ret === null ) return; ret = Number(ret); if( ret < 10 || ret > 3600 ) return; //console.log("★InputValue = " + ret); updateInterval[mode] = ret * 1000; updateCount = 0; localStorage.setItem( INTERVAL_HEADER + u, ret ); //console.log("★SaveSetting: [" + u + "] = " + ret); } //自動更新をON/OFFする(OFFでは強制停止、ONの時はスクロールバーの位置やエディット入力にフォーカスがあるか無いかで判定する従来の動作をする) function switchAutoButton(){ var u = window.location.href; if(u.length < 24) return; u = u.replace("https://twitter.com/", ""); 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 < 24) return; u = u.replace("https://twitter.com/", ""); 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() { //初期処理の遅延実行 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自体を再 // 起動したほうがいいと思います。 /* //------------ // 全削除 //------------ (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){ window.localStorage.removeItem(keys); removeCount++; } } window.alert("  " + removeCount + "件のデータを削除しました"); })(); //------------ */