Extension to return old Twitter layout from 2015.
at master 2172 lines 120 kB view raw
1let user = {}; 2let pageUser = {}; 3let timeline = { 4 data: [], 5 dataToUpdate: [], 6 toBeUpdated: 0 7} 8let seenThreads = []; 9let averageLikeCount = 1; 10let pinnedTweet, followersYouFollow; 11let previousLastTweet, stopLoad = false; 12let tweetsCursor, favoritesCursor, followingCursor, followersCursor, followersYouKnowCursor, mediaCursor; 13let cssEligibleAuto = false, cssEligible = false; 14 15// Util 16 17let subpage; 18let user_handle = location.pathname.slice(1).split("?")[0].split('#')[0]; 19user_handle = user_handle.split('/')[0]; 20let user_protected = false; 21let user_blocked_by = false; 22let user_blocking = false; 23function updateSubpage() { 24 previousLastTweet = undefined; stopLoad = false; 25 averageLikeCount = 1; 26 user_handle = location.pathname.slice(1).split("?")[0].split('#')[0]; 27 if(user_handle.split('/').length === 1) { 28 subpage = 'profile'; 29 } else { 30 if(user_handle.endsWith('/with_replies')) { 31 subpage = 'replies'; 32 } else if(user_handle.endsWith('/media')) { 33 subpage = 'media'; 34 } else if(user_handle.endsWith('/likes')) { 35 subpage = 'likes'; 36 } else if(user_handle.endsWith('/following')) { 37 subpage = 'following'; 38 } else if(user_handle.endsWith('/followers')) { 39 subpage = 'followers'; 40 } else if(user_handle.endsWith('/followers_you_follow')) { 41 subpage = 'followers_you_follow'; 42 } else if(user_handle.endsWith('/lists')) { 43 subpage = 'lists'; 44 } 45 } 46 user_handle = user_handle.split('/')[0]; 47} 48 49function updateSelection() { 50 document.getElementById('style-hide-retweets').innerHTML = ''; 51 document.getElementById('tweet-nav-more-menu-hr').checked = false; 52 document.getElementById('tweet-nav-more-menu-hnr').checked = false; 53 54 let activeStats = Array.from(document.getElementsByClassName('profile-stat-active')); 55 for(let i in activeStats) { 56 if(activeStats[i].classList.contains('profile-stat-active')) { 57 activeStats[i].classList.remove('profile-stat-active'); 58 } 59 } 60 let activeNavs = Array.from(document.getElementsByClassName('tweet-nav-active')); 61 for(let i in activeNavs) { 62 if(activeNavs[i].classList.contains('tweet-nav-active')) { 63 activeNavs[i].classList.remove('tweet-nav-active'); 64 } 65 } 66 67 if(subpage === "profile") { 68 document.getElementById('tweet-nav').hidden = false; 69 document.getElementById('timeline').hidden = false; 70 document.getElementById('following-list').hidden = true; 71 document.getElementById('followers-list').hidden = true; 72 document.getElementById('following-more').hidden = true; 73 document.getElementById('followers-more').hidden = true; 74 document.getElementById('followers_you_follow-list').hidden = true; 75 document.getElementById('followers_you_follow-more').hidden = true; 76 document.getElementById('lists-list').hidden = true; 77 78 document.getElementById('profile-stat-tweets-link').classList.add('profile-stat-active'); 79 document.getElementById('tweet-nav-tweets').classList.add('tweet-nav-active'); 80 } else if(subpage === "likes") { 81 document.getElementById('tweet-nav').hidden = false; 82 document.getElementById('timeline').hidden = false; 83 document.getElementById('following-list').hidden = true; 84 document.getElementById('followers-list').hidden = true; 85 document.getElementById('following-more').hidden = true; 86 document.getElementById('followers-more').hidden = true; 87 document.getElementById('followers_you_follow-list').hidden = true; 88 document.getElementById('followers_you_follow-more').hidden = true; 89 document.getElementById('lists-list').hidden = true; 90 91 document.getElementById('profile-stat-favorites-link').classList.add('profile-stat-active'); 92 } else if(subpage === "replies") { 93 document.getElementById('tweet-nav').hidden = false; 94 document.getElementById('timeline').hidden = false; 95 document.getElementById('following-list').hidden = true; 96 document.getElementById('followers-list').hidden = true; 97 document.getElementById('following-more').hidden = true; 98 document.getElementById('followers-more').hidden = true; 99 document.getElementById('followers_you_follow-list').hidden = true; 100 document.getElementById('followers_you_follow-more').hidden = true; 101 document.getElementById('lists-list').hidden = true; 102 103 document.getElementById('profile-stat-tweets-link').classList.add('profile-stat-active'); 104 document.getElementById('tweet-nav-replies').classList.add('tweet-nav-active'); 105 } else if(subpage === "media") { 106 document.getElementById('tweet-nav').hidden = false; 107 document.getElementById('timeline').hidden = false; 108 document.getElementById('following-list').hidden = true; 109 document.getElementById('followers-list').hidden = true; 110 document.getElementById('following-more').hidden = true; 111 document.getElementById('followers-more').hidden = true; 112 document.getElementById('followers_you_follow-list').hidden = true; 113 document.getElementById('followers_you_follow-more').hidden = true; 114 document.getElementById('lists-list').hidden = true; 115 116 document.getElementById('profile-stat-media-link').classList.add('profile-stat-active'); 117 document.getElementById('tweet-nav-media').classList.add('tweet-nav-active'); 118 } else if(subpage === "following") { 119 document.getElementById('tweet-nav').hidden = true; 120 document.getElementById('timeline').hidden = true; 121 document.getElementById('following-list').hidden = false; 122 document.getElementById('followers-list').hidden = true; 123 document.getElementById('following-more').hidden = false; 124 document.getElementById('followers-more').hidden = true; 125 document.getElementById('followers_you_follow-list').hidden = true; 126 document.getElementById('followers_you_follow-more').hidden = true; 127 document.getElementById('lists-list').hidden = true; 128 129 document.getElementById('profile-stat-following-link').classList.add('profile-stat-active'); 130 } else if(subpage === "followers") { 131 document.getElementById('tweet-nav').hidden = true; 132 document.getElementById('timeline').hidden = true; 133 document.getElementById('following-list').hidden = true; 134 document.getElementById('followers-list').hidden = false; 135 document.getElementById('following-more').hidden = true; 136 document.getElementById('followers-more').hidden = false; 137 document.getElementById('followers_you_follow-list').hidden = true; 138 document.getElementById('followers_you_follow-more').hidden = true; 139 document.getElementById('lists-list').hidden = true; 140 141 document.getElementById('profile-stat-followers-link').classList.add('profile-stat-active'); 142 } else if(subpage === "followers_you_follow") { 143 document.getElementById('tweet-nav').hidden = true; 144 document.getElementById('timeline').hidden = true; 145 document.getElementById('following-list').hidden = true; 146 document.getElementById('followers-list').hidden = true; 147 document.getElementById('following-more').hidden = true; 148 document.getElementById('followers-more').hidden = true; 149 document.getElementById('followers_you_follow-list').hidden = false; 150 document.getElementById('followers_you_follow-more').hidden = false; 151 document.getElementById('lists-list').hidden = true; 152 153 document.getElementById('profile-stat-followers-link').classList.add('profile-stat-active'); 154 } else if(subpage === "lists") { 155 document.getElementById('tweet-nav').hidden = true; 156 document.getElementById('timeline').hidden = true; 157 document.getElementById('following-list').hidden = true; 158 document.getElementById('followers-list').hidden = true; 159 document.getElementById('following-more').hidden = true; 160 document.getElementById('followers-more').hidden = true; 161 document.getElementById('followers_you_follow-list').hidden = true; 162 document.getElementById('followers_you_follow-more').hidden = true; 163 document.getElementById('lists-list').hidden = false; 164 } 165 document.getElementById('profile-stat-tweets-link').href = `https://twitter.com/${pageUser.screen_name}`; 166 document.getElementById('profile-stat-following-link').href = `https://twitter.com/${pageUser.screen_name}/following`; 167 document.getElementById('profile-stat-followers-link').href = `https://twitter.com/${pageUser.screen_name}/followers`; 168 document.getElementById('profile-stat-favorites-link').href = `https://twitter.com/${pageUser.screen_name}/likes`; 169 document.getElementById('profile-stat-media-link').href = `https://twitter.com/${pageUser.screen_name}/media`; 170 document.getElementById('tweet-nav-tweets').href = `https://twitter.com/${pageUser.screen_name}`; 171 document.getElementById('tweet-nav-replies').href = `https://twitter.com/${pageUser.screen_name}/with_replies`; 172 document.getElementById('tweet-nav-media').href = `https://twitter.com/${pageUser.screen_name}/media`; 173 174 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies')) { 175 document.getElementById('trends').hidden = true; 176 document.getElementById('no-tweets').hidden = false; 177 document.getElementById('no-tweets').innerHTML = ` 178 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 179 <p>${LOC.when_theyll_tweet.message}</p> 180 `; 181 } else { 182 document.getElementById('trends').hidden = false; 183 document.getElementById('no-tweets').hidden = true; 184 document.getElementById('no-tweets').innerHTML = ``; 185 } 186} 187 188function updateUserData() { 189 return new Promise(async (resolve, reject) => { 190 document.getElementsByTagName('title')[0].innerText = `${user_handle} - ` + LOC.twitter.message; 191 let [pageUserData, followersYouFollowData, oldUser, u] = await Promise.allSettled([ 192 API.user.getV2(user_handle), 193 API.user.friendsFollowing(user_handle, false), 194 API.user.get(user_handle, false), 195 API.account.verifyCredentials() 196 ]).catch(e => { 197 document.getElementById('loading-box').hidden = false; 198 if(String(e).includes('User has been suspended.')) { 199 return document.getElementById('loading-box-error').innerHTML = `${LOC.user_was_suspended.message}<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 200 } 201 if(String(e).includes("reading 'result'")) { 202 return document.getElementById('loading-box-error').innerHTML = `${LOC.user_was_not_found.message}<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 203 } 204 return document.getElementById('loading-box-error').innerHTML = `${String(e)}.<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 205 }); 206 if(oldUser.reason) { 207 let e = oldUser.reason; 208 if(String(e).includes('User has been suspended.')) { 209 document.getElementById('loading-box').hidden = false; 210 return document.getElementById('loading-box-error').innerHTML = `${LOC.user_was_suspended.message}<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 211 } 212 } 213 if(pageUserData.reason) { 214 let e = pageUserData.reason; 215 document.getElementById('loading-box').hidden = false; 216 if(String(e).includes("reading 'result'")) { 217 return document.getElementById('loading-box-error').innerHTML = `${LOC.user_was_not_found.message}<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 218 } 219 return document.getElementById('loading-box-error').innerHTML = `${String(e)}.<br><a href="https://twitter.com/home">${LOC.go_homepage.message}</a>`; 220 } 221 followersYouFollowData = followersYouFollowData.value; 222 oldUser = oldUser.value; 223 u = u.value; 224 user = u; 225 pageUserData = pageUserData.value; 226 //default value 227 user_blocked_by = false; 228 user_blocking = false; 229 user_protected = false; 230 if (pageUserData.blocked_by) { 231 user_blocked_by = true; 232 } 233 if (pageUserData.blocking) { 234 user_blocking = true; 235 } 236 if (pageUserData.protected && !pageUserData.following && pageUserData.id_str !== user.id_str) { 237 user_protected = true; 238 } 239 userDataFunction(u); 240 const event2 = new CustomEvent('updatePageUserData', { detail: oldUser }); 241 document.dispatchEvent(event2); 242 pageUser = pageUserData; 243 pageUser.protected = oldUser.protected; 244 let r = document.querySelector(':root'); 245 let usedProfileColor = vars && vars.linkColor ? vars.linkColor : '#4595B5'; 246 r.style.setProperty('--link-color', usedProfileColor); 247 let sc = makeSeeableColor(oldUser.profile_link_color); 248 if(oldUser.profile_link_color && oldUser.profile_link_color !== '1DA1F2') { 249 customSet = true; 250 r.style.setProperty('--link-color', sc); 251 usedProfileColor = oldUser.profile_link_color; 252 document.getElementById('color-years-ago').hidden = false; 253 } else { 254 document.getElementById('color-years-ago').hidden = true; 255 } 256 257 const profileLinkColor = document.getElementById('profile-link-color'); 258 const colorPreviewLight = document.getElementById('color-preview-light'); 259 const colorPreviewDark = document.getElementById('color-preview-dark'); 260 const colorPreviewBlack = document.getElementById('color-preview-black'); 261 const darkModeVars = document.getElementById('dark-mode-vars'); 262 const lightModeVars = document.getElementById('light-mode-vars'); 263 const cssTextArea = document.getElementById('profile-css-textarea'); 264 265 profileLinkColor.value = `#${usedProfileColor}`; 266 profileLinkColor.addEventListener('input', () => { 267 let color = profileLinkColor.value; 268 if(color.startsWith('#')) color = color.slice(1); 269 let sc = makeSeeableColor(color); 270 customSet = true; 271 r.style.setProperty('--link-color', sc); 272 273 colorPreviewLight.style.color = makeSeeableColor(`#${color}`, "#ffffff"); 274 colorPreviewDark.style.color = makeSeeableColor(`#${color}`, "#1b2836"); 275 colorPreviewBlack.style.color = makeSeeableColor(`#${color}`, "#000000"); 276 }); 277 278 colorPreviewLight.style.color = makeSeeableColor(`#${usedProfileColor}`, "#ffffff"); 279 colorPreviewDark.style.color = makeSeeableColor(`#${usedProfileColor}`, "#1b2836"); 280 colorPreviewBlack.style.color = makeSeeableColor(`#${usedProfileColor}`, "#000000"); 281 282 let profileCustomCSSData = {}; 283 let pccss = await new Promise(resolve => { 284 chrome.storage.local.get(["profileCustomCSS"], async data => { 285 if(!data.profileCustomCSS) { 286 data.profileCustomCSS = {}; 287 } 288 profileCustomCSSData = data.profileCustomCSS; 289 if(data.profileCustomCSS[pageUser.id_str]) { 290 resolve(data.profileCustomCSS[pageUser.id_str]); 291 } else { 292 resolve({}); 293 } 294 }); 295 }); 296 if(pccss.darkModeVars && isDarkModeEnabled) { 297 let vars = parseVariables(pccss.darkModeVars); 298 for(let i in vars) { 299 r.style.setProperty(i, vars[i]); 300 } 301 } else if(pccss.lightModeVars && !isDarkModeEnabled) { 302 let vars = parseVariables(pccss.lightModeVars); 303 for(let i in vars) { 304 r.style.setProperty(i, vars[i]); 305 } 306 } else { 307 await switchDarkMode(isDarkModeEnabled); 308 } 309 if(pccss.css) { 310 if(!customCSS) { 311 customCSS = document.createElement('style'); 312 customCSS.id = 'oldtwitter-custom-css'; 313 document.head.appendChild(customCSS); 314 } 315 customCSS.innerHTML = pccss.css; 316 } else { 317 updateCustomCSS(); 318 } 319 320 getLinkColors(pageUserData.id_str).then(data => { 321 let color = data[0]; 322 if(color) color = color.color; 323 324 if(color && color !== 'none') { 325 let sc = makeSeeableColor(color); 326 customSet = true; 327 r.style.setProperty('--link-color', sc); 328 usedProfileColor = color; 329 } 330 fetch("https://dimden.dev/services/twitter_link_colors/v2/get_data/"+pageUserData.id_str).then(r => r.json()).then(async data => { 331 if(data.color !== 'none' && data.color !== '4595b5') { 332 if(data.color !== color) { 333 chrome.storage.local.get(["linkColors"], async lc => { 334 let linkColors = lc.linkColors || {}; 335 linkColors[pageUserData.id_str] = data.color; 336 chrome.storage.local.set({ linkColors }); 337 }); 338 let sc = makeSeeableColor(data.color); 339 customSet = true; 340 usedProfileColor = data.color; 341 r.style.setProperty('--link-color', sc); 342 } 343 let pc = `#${data.color}`; 344 345 profileLinkColor.value = pc; 346 347 colorPreviewLight.style.color = makeSeeableColor(pc, "#ffffff"); 348 colorPreviewDark.style.color = makeSeeableColor(pc, "#1b2836"); 349 colorPreviewBlack.style.color = makeSeeableColor(pc, "#000000"); 350 } 351 chrome.storage.local.get(["adminpass"], async a => { 352 if(a.adminpass) { 353 let adminControls = document.getElementById('admin-controls'); 354 if(adminControls) adminControls.remove(); 355 adminControls = document.createElement('div'); 356 adminControls.id = 'admin-controls'; 357 console.log(data.css_eligible); 358 adminControls.innerHTML = ` 359 <br> 360 Eligible for custom profile CSS: <span id="admin-css-eligible">${data.css_eligible ? 'yes' : 'no'}</span><br> 361 Can get access automatically: <span id="admin-css-eligible-auto">${data.css_eligible_auto ? 'yes' : 'no'}</span><br> 362 Has custom profile CSS: ${data.css || data.css_vars_dark || data.css_vars_light ? 'yes' : 'no'}<br> 363 Has custom color: ${data.color !== 'none' ? 'yes' : 'no'}<br><br> 364 <button id="admin-controls-switch" style="background-color: var(--menu-bg);border: 1px solid var(--border);color: var(--almost-black);cursor: pointer;">Switch</button> 365 <br><br> 366 `; 367 document.getElementById('about').appendChild(adminControls); 368 document.getElementById('admin-controls-switch').addEventListener('click', () => { 369 fetch(`https://dimden.dev/services/twitter_link_colors/v2/admin/switch_access`, { 370 method: 'POST', 371 headers: { 372 'Content-Type': 'application/json' 373 }, 374 body: JSON.stringify({ 375 password: a.adminpass, 376 user: pageUserData.id_str 377 }) 378 }).then(r => r.json()).then(data => { 379 if(data.error) { 380 return alert(data.error); 381 } 382 if(data.eligible) { 383 document.getElementById('admin-css-eligible').innerText = 'yes'; 384 document.getElementById('admin-css-eligible-auto').innerText = 'no'; 385 } else { 386 document.getElementById('admin-css-eligible').innerText = 'no'; 387 document.getElementById('admin-css-eligible-auto').innerText = 'no'; 388 } 389 }); 390 }); 391 } 392 }); 393 if(data.css_eligible && !vars.disableProfileCustomizations) { 394 if(pccss.css !== data.css || pccss.darkModeVars !== data.css_vars_dark || pccss.lightModeVars !== data.css_vars_light) { 395 pccss.css = data.css; 396 pccss.darkModeVars = data.css_vars_dark; 397 pccss.lightModeVars = data.css_vars_light; 398 profileCustomCSSData[pageUser.id_str] = pccss; 399 chrome.storage.local.set({ profileCustomCSS: profileCustomCSSData }); 400 } 401 let styled = false; 402 if(data.css) { 403 styled = true; 404 profileCSS = true; 405 if(!customCSS) { 406 customCSS = document.createElement('style'); 407 customCSS.id = 'oldtwitter-custom-css'; 408 document.head.appendChild(customCSS); 409 } 410 customCSS.innerHTML = data.css; 411 } else { 412 profileCSS = false; 413 updateCustomCSS(); 414 } 415 if(data.css_vars_dark && isDarkModeEnabled) { 416 styled = true; 417 let vars = parseVariables(data.css_vars_dark); 418 for(let i in vars) { 419 r.style.setProperty(i, vars[i]); 420 } 421 } else if(data.css_vars_light && !isDarkModeEnabled) { 422 styled = true; 423 let vars = parseVariables(data.css_vars_light); 424 for(let i in vars) { 425 r.style.setProperty(i, vars[i]); 426 } 427 } else { 428 await switchDarkMode(isDarkModeEnabled); 429 } 430 if(pageUser.id_str !== user.id_str && styled) { 431 let additionalThing = document.createElement("span"); 432 additionalThing.innerHTML = `<a href="https://dimden.dev/ot/custom-css/" target="_blank">${LOC.styled_profile.message}</a>`; 433 additionalThing.className = "profile-additional-thing profile-additional-styled"; 434 document.getElementById("profile-additional").appendChild(additionalThing); 435 } 436 } else { 437 profileCSS = false; 438 if(profileCustomCSSData[pageUser.id_str]) { 439 delete profileCustomCSSData[pageUser.id_str]; 440 chrome.storage.local.set({ profileCustomCSS: profileCustomCSSData }); 441 } 442 updateCustomCSS(); 443 await switchDarkMode(isDarkModeEnabled); 444 } 445 if(pageUserData.id_str === user.id_str) { 446 darkModeVars.value = data.css_vars_dark || ''; 447 lightModeVars.value = data.css_vars_light || ''; 448 cssTextArea.value = data.css || ''; 449 cssEligibleAuto = data.css_eligible_auto; 450 cssEligible = data.css_eligible; 451 if(data.css_eligible || (data.css_eligible_auto && user.followers_count >= 5000)) { 452 document.getElementById('custom-css-eligible').hidden = false; 453 document.getElementById('custom-css-not-eligible').hidden = true; 454 if(!vars.acknowledgedCssAccess && !data.css && !data.css_vars_dark && !data.css_vars_light) { 455 let modal = createModal(` 456 <div style="color:var(--almost-black);max-width:500px"> 457 <h2 class="nice-header">${LOC.profile_custom_css.message}</h2><br> 458 <span>${LOC.pccss_congrats.message}</span> 459 <br><br> 460 <div style="display:inline-block;float: right;"> 461 <button class="nice-button">${LOC.yay.message}</button> 462 </div> 463 </div> 464 `, 'css-congrats-modal', () => { 465 chrome.storage.sync.set({ acknowledgedCssAccess: true }); 466 }, () => false); 467 modal.querySelector('button').addEventListener('click', () => { 468 modal.removeModal(); 469 }); 470 } 471 } else { 472 document.getElementById('custom-css-eligible').hidden = true; 473 document.getElementById('custom-css-not-eligible').hidden = false; 474 } 475 } else { 476 document.getElementById('custom-css-eligible').hidden = true; 477 document.getElementById('custom-css-not-eligible').hidden = true; 478 } 479 }); 480 }); 481 482 if(pageUser.id_str !== user.id_str) { 483 followersYouFollow = followersYouFollowData; 484 document.getElementById('profile-friends-text').style.display = 'unset'; 485 } else { 486 followersYouFollow = undefined; 487 document.getElementById('profile-friends-text').style.display = 'none'; 488 } 489 renderProfile(); 490 resolve(u); 491 }).catch(e => { 492 if (e === "Not logged in") { 493 window.location.href = "https://twitter.com/i/flow/login?newtwitter=true"; 494 } 495 console.error(e); 496 reject(e); 497 }); 498} 499 500async function updateTimeline() { 501 seenThreads = []; 502 if (timeline.data.length === 0) document.getElementById('timeline').innerHTML = ` 503 <div class="loading-data" id="tweets-loader"> 504 <img src="${chrome.runtime.getURL(`images/loading.svg`)}" width="64" height="64"> 505 </div>`; 506 let tl; 507 if(subpage === "likes") { 508 let data = await API.user.getFavorites(pageUser.id_str); 509 tl = data.tl; 510 favoritesCursor = data.cursor; 511 } else { 512 try { 513 if (!user_protected && !user_blocked_by) { 514 if (subpage === "media") { 515 tl = await API.user.getMediaTweets(pageUser.id_str); 516 mediaCursor = tl.cursor; 517 tl = tl.tweets; 518 } else { 519 tl = await API.user.getTweetsV2( 520 pageUser.id_str, 521 undefined, 522 subpage !== "profile" 523 ); 524 pinnedTweet = tl.pinnedTweet; 525 tweetsCursor = tl.cursor; 526 tl = tl.tweets; 527 } 528 } else if(user_blocked_by) { 529 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.blocked_by_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.why_you_cant_see_block_user.message.replaceAll("$SCREEN_NAME$",pageUser.screen_name)}</p></div>`; 530 return; 531 }else if(user_protected) { 532 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.user_protected.message}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.follow_to_see.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p></div>`; 533 return; 534 }/*else if(user_blocking) { 535 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.you_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.do_you_want_see_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p><button class="nice-button" id="see-tweet-btn">${LOC.I_want_see_blocked_user.message}</button> </div>`; 536 return; 537 }*/ 538 } catch(e) { 539 console.error(e); 540 document.getElementById('timeline').innerHTML = `<div style="padding: 100px;color: var(--darker-gray);">${escapeHTML(String(e))}</div>`; 541 return; 542 } 543 // if(subpage === 'media') { 544 // tl = tl.filter(t => t.extended_entities && t.extended_entities.media && t.extended_entities.media.length > 0 && !t.retweeted_status); 545 // } 546 } 547 if(tl.error === "Not authorized.") { 548 document.getElementById('tweet-nav').hidden = true; 549 document.getElementById('loading-box').hidden = true; 550 document.getElementById('timeline').innerHTML = pageUser.statuses_count === 0 ? '' : `<div style="padding: 100px;color: var(--darker-gray);">${LOC.timeline_not_authorized.message}</div>`; 551 return; 552 } 553 tl.forEach(t => { 554 let oldTweet = timeline.data.find(tweet => tweet.id_str === t.id_str); 555 let tweetElement = document.getElementById(`tweet-${t.id_str}`); 556 if (oldTweet) { 557 oldTweet.favorite_count = t.favorite_count; 558 oldTweet.retweet_count = t.retweet_count; 559 oldTweet.reply_count = t.reply_count; 560 oldTweet.favorited = t.favorited; 561 oldTweet.retweeted = t.retweeted; 562 } 563 if (tweetElement) { 564 tweetElement.querySelector('.tweet-interact-favorite ').innerText = t.favorite_count; 565 tweetElement.querySelector('.tweet-interact-retweet').innerText = t.retweet_count; 566 tweetElement.querySelector('.tweet-interact-reply').innerText = t.reply_count; 567 tweetElement.querySelector('.tweet-interact-favorite').classList.toggle('tweet-interact-favorited', t.favorited); 568 tweetElement.querySelector('.tweet-interact-retweet').classList.toggle('tweet-interact-retweeted', t.retweeted); 569 } 570 }); 571 // first update 572 timeline.data = tl; 573 averageLikeCount = timeline.data.filter(t => !t.retweeted_status).map(t => t.favorite_count).sort((a, b) => a - b)[Math.floor(timeline.data.length/2)]; 574 renderTimeline(); 575 previousLastTweet = timeline.data[timeline.data.length - 1]; 576} 577 578async function renderFollowing(clear = true, cursor) { 579 loadingFollowing = true; 580 let userList = document.getElementById('following-list'); 581 if(clear) { 582 if(pageUser.id_str === user.id_str) { 583 userList.innerHTML = ` 584 <h1 class="nice-header">${LOC.following.message}</h1> 585 <a href="/old/unfollows/following" style="float: right;font-size: 14px;">${LOC.unfollowings.message}</a> 586 `; 587 } else { 588 userList.innerHTML = `<h1 class="nice-header">${LOC.following.message}</h1>`; 589 } 590 } 591 let following; 592 try { 593 following = await API.user.getFollowing(pageUser.id_str, cursor); 594 } catch(e) { 595 loadingFollowing = false; 596 followingMoreBtn.innerText = LOC.load_more.message; 597 console.error(e); 598 return; 599 } 600 followingCursor = following.cursor; 601 following = following.list; 602 if(following.length === 0) { 603 followingMoreBtn.hidden = true; 604 } else { 605 followingMoreBtn.hidden = false; 606 } 607 following.forEach(u => { 608 appendUser(u, userList); 609 }); 610 document.getElementById('loading-box').hidden = true; 611 loadingFollowing = false; 612 followingMoreBtn.innerText = LOC.load_more.message; 613} 614async function renderFollowers(clear = true, cursor) { 615 loadingFollowers = true; 616 let userList = document.getElementById('followers-list'); 617 if(clear) { 618 if(pageUser.id_str === user.id_str) { 619 userList.innerHTML = ` 620 <h1 class="nice-header">${LOC.followers.message}</h1> 621 <a href="/old/unfollows/followers" style="float: right;font-size: 14px;">${LOC.unfollowers.message}</a> 622 `; 623 } else { 624 userList.innerHTML = `<h1 class="nice-header">${LOC.followers.message}</h1>`; 625 } 626 } 627 let following; 628 try { 629 following = await API.user.getFollowers(pageUser.id_str, cursor) 630 } catch(e) { 631 loadingFollowers = false; 632 followersMoreBtn.innerText = LOC.load_more.message; 633 console.error(e); 634 return; 635 } 636 followersCursor = following.cursor; 637 following = following.list; 638 if(following.length === 0) { 639 followersMoreBtn.hidden = true; 640 } else { 641 followersMoreBtn.hidden = false; 642 } 643 following.forEach(u => { 644 appendUser(u, userList); 645 }); 646 document.getElementById('loading-box').hidden = true; 647 loadingFollowers = false; 648 followersMoreBtn.innerText = LOC.load_more.message; 649} 650async function renderFollowersYouFollow(clear = true, cursor) { 651 loadingFollowersYouKnow = true; 652 let userList = document.getElementById('followers_you_follow-list'); 653 if(clear) { 654 if(LOC.followers_you_know.message.includes("$NUMBER$")) { 655 userList.innerHTML = `<h1 class="nice-header">${LOC.followers_you_know.message.replace("$NUMBER$", followersYouFollow.total_count)}</h1>`; 656 } else { 657 userList.innerHTML = `<h1 class="nice-header">${followersYouFollow.total_count} ${LOC.followers_you_know.message}</h1>`; 658 } 659 } 660 let following; 661 try { 662 following = await API.user.getFollowersYouFollow(pageUser.id_str, cursor); 663 } catch(e) { 664 console.error(e); 665 loadingFollowersYouKnow = false; 666 followersYouFollowMoreBtn.innerText = LOC.load_more.message; 667 return; 668 } 669 followersYouKnowCursor = following.cursor; 670 following = following.list; 671 if(following.length === 0) { 672 followersYouFollowMoreBtn.hidden = true; 673 } else { 674 followersYouFollowMoreBtn.hidden = false; 675 } 676 following.forEach(u => { 677 appendUser(u, userList); 678 }); 679 document.getElementById('loading-box').hidden = true; 680 loadingFollowersYouKnow = false; 681 followersYouFollowMoreBtn.innerText = LOC.load_more.message; 682} 683async function renderLists() { 684 let lists = pageUser.id_str === user.id_str ? await API.list.getMyLists() : await API.user.getLists(pageUser.id_str); 685 let listsList = document.getElementById('lists-list'); 686 listsList.innerHTML = `<h1 class="nice-header">${LOC.lists.message}</h1>`; 687 if(pageUser.id_str === user.id_str) { 688 listsList.innerHTML += `<h1 class="nice-header" style="float:right;cursor:pointer" id="create-list">${LOC.create_btn.message}</h1>`; 689 document.getElementById('create-list').addEventListener('click', () => { 690 let modal = createModal(` 691 <div id="list-creator"> 692 <h1 class="cool-header">${LOC.create_list.message}</h1><br> 693 <span id="list-editor-error" style="color:red"></span><br> 694 ${LOC.name.message}:<br><input maxlength="25" type="text" id="list-name-input"><br><br> 695 ${LOC.description.message}:<br><textarea maxlength="100" type="text" id="list-description-input"></textarea><br> 696 <br> 697 ${LOC.is_private.message}: <input type="checkbox" style="width: 15px;" id="list-private-input"><br> 698 <br> 699 <button class="nice-button" id="list-btn-create">${LOC.create.message}</button> 700 </div> 701 `, 'list-creator-modal'); 702 document.getElementById('list-btn-create').addEventListener('click', async () => { 703 let list; 704 try { 705 list = await API.list.create(document.getElementById('list-name-input').value, document.getElementById('list-description-input').value, document.getElementById('list-private-input').checked); 706 } catch(e) { 707 return document.getElementById('list-editor-error').innerText = e && e.message ? e.message : e; 708 } 709 location.href = `https://twitter.com/i/lists/${list.id_str}`; 710 }); 711 }); 712 } 713 for(let i in lists) { 714 let l = lists[i]; 715 if(!l) continue; 716 let listElement = document.createElement('div'); 717 listElement.classList.add('list-item'); 718 listElement.innerHTML = ` 719 <div> 720 <a href="https://twitter.com/i/lists/${l.id_str}" class="following-item-link"> 721 <img style="object-fit: cover;" src="${l.custom_banner_media ? l.custom_banner_media.media_info.original_img_url : l.default_banner_media.media_info.original_img_url}" alt="${l.name}" class="following-item-avatar tweet-avatar" width="48" height="48"> 722 <div class="following-item-text" style="position: relative;bottom: 12px;"> 723 <span class="tweet-header-name following-item-name${l.mode === 'Private' ? ' user-protected' : ''}" style="font-size: 18px;">${escapeHTML(l.name)}</span><br> 724 <span style="color:var(--darker-gray);font-size:14px;margin-top:2px">${l.description ? escapeHTML(l.description).slice(0, 52) : LOC.no_description.message}</span> 725 </div> 726 </a> 727 </div> 728 `; 729 listsList.appendChild(listElement); 730 } 731 document.getElementById('loading-box').hidden = true; 732} 733 734let months = []; 735let everAddedAdditional = false; 736let toAutotranslate = false; 737async function renderProfile() { 738 document.getElementById('profile-banner').src = pageUser.profile_banner_url ? pageUser.profile_banner_url : 'https://abs.twimg.com/images/themes/theme1/bg.png'; 739 let attempts = 0; 740 document.getElementById('profile-avatar').addEventListener('error', () => { 741 if(attempts > 3) return document.getElementById('profile-avatar').src = `${vars.useOldDefaultProfileImage ? chrome.runtime.getURL(`images/default_profile_images/default_profile_400x400.png`) : 'https://abs.twimg.com/sticky/default_profile_images/default_profile_400x400.png'}`; 742 attempts++; 743 setTimeout(() => { 744 document.getElementById('profile-avatar').src = `${(pageUser.default_profile_image && vars.useOldDefaultProfileImage) ? chrome.runtime.getURL(`images/default_profile_images/default_profile_${Number(pageUser.id_str) % 7}_normal.png`): pageUser.profile_image_url_https}`.replace('_normal.', '_400x400.'); 745 }, 500); 746 }); 747 let autotranslateProfiles = await new Promise(resolve => { 748 chrome.storage.sync.get(['autotranslateProfiles'], data => { 749 resolve(data.autotranslateProfiles); 750 }); 751 }); 752 if(!autotranslateProfiles) { 753 autotranslateProfiles = []; 754 } 755 toAutotranslate = autotranslateProfiles.includes(pageUser.id_str); 756 document.getElementById('profile-avatar').src = `${(pageUser.default_profile_image && vars.useOldDefaultProfileImage) ? chrome.runtime.getURL(`images/default_profile_images/default_profile_${Number(pageUser.id_str) % 7}_normal.png`): pageUser.profile_image_url_https}`.replace('_normal.', '_400x400.'); 757 document.getElementById('nav-profile-avatar').src = `${(pageUser.default_profile_image && vars.useOldDefaultProfileImage) ? chrome.runtime.getURL(`images/default_profile_images/default_profile_${Number(pageUser.id_str) % 7}_normal.png`): pageUser.profile_image_url_https}`.replace('_normal.', '_bigger.'); 758 document.getElementById('profile-name').innerText = pageUser.name.replace(/\n/g, ' '); 759 document.getElementById('nav-profile-name').innerText = pageUser.name.replace(/\n/g, ' '); 760 document.getElementById('profile-avatar-link').href = `${(pageUser.default_profile_image && vars.useOldDefaultProfileImage) ? chrome.runtime.getURL(`images/default_profile_images/default_profile_${Number(pageUser.id_str) % 7}_normal.png`): pageUser.profile_image_url_https}`.replace('_normal.', '_400x400.'); 761 if(LOC.tweet_to.message.includes("$SCREEN_NAME$")) { 762 document.getElementById('tweet-to').innerText = LOC.tweet_to.message.replace("$SCREEN_NAME$", pageUser.screen_name.replace(/\n/g, ' ')); 763 } else { 764 document.getElementById('tweet-to').innerText = `${LOC.tweet_to.message} ${pageUser.name.replace(/\n/g, ' ')}`; 765 } 766 if(vars.heartsNotStars) { 767 document.getElementById('profile-stat-text-favorites').innerText = LOC.likes.message; 768 } 769 let stats = Array.from(document.getElementsByClassName('profile-stat')); 770 stats.forEach(s => { 771 s.classList.toggle('profile-stat-disabled', (pageUser.protected && !pageUser.following) && pageUser.id_str !== user.id_str); //BUG:pageUser.blocked_by works strangly only here... 772 }); 773 774 document.getElementById('profile-name').className = ""; 775 if(pageUser.verified || pageUser.verified_type || pageUser.id_str === '1123203847776763904') { 776 if(!(!vars.twitterBlueCheckmarks && pageUser.verified_type === "Blue")) document.getElementById('profile-name').classList.add('user-verified'); 777 if(pageUser.id_str === '1123203847776763904') document.getElementById('profile-name').classList.add('user-verified-green'); 778 if(vars.twitterBlueCheckmarks && pageUser.verified_type === "Blue") document.getElementById('profile-name').classList.add('user-verified-blue'); 779 if(pageUser.verified_type === "Government") document.getElementById('profile-name').classList.add('user-verified-gray'); 780 if(pageUser.verified_type === "Business") document.getElementById('profile-name').classList.add('user-verified-yellow'); 781 } 782 if(pageUser.protected) { 783 document.getElementById('profile-name').classList.add('user-protected'); 784 } 785 if(pageUser.muting) { 786 document.getElementById('profile-name').classList.add('user-muted'); 787 } 788 document.getElementById('profile-username').innerText = `@${pageUser.screen_name}`; 789 document.getElementById('nav-profile-username').innerText = `@${pageUser.screen_name}`; 790 document.getElementById('profile-media-text').href = `https://twitter.com/${pageUser.screen_name}/media`; 791 792 updateSelection(); 793 794 document.getElementById('profile-bio').innerHTML = escapeHTML(pageUser.description).replace(/\n\n\n\n/g, "\n").replace(/((http|https|ftp):\/\/[\w?=.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, '<a href="$1">$1</a>').replace(/(?<!\w)@([\w+]{1,15}\b)/g, `<a href="https://twitter.com/$1">@$1</a>`).replace(hashtagRegex, `<a href="https://twitter.com/hashtag/$2">#$2</a>`).replace(/\n/g, '<br>'); 795 let strippedDownText = pageUser.description 796 .replace(/(?:https?|ftp):\/\/[\n\S]+/g, '') //links 797 .replace(/(?<!\w)@([\w+]{1,15}\b)/g, '') //mentions 798 .replace(/[\p{Extended_Pictographic}]/gu, '') //emojis (including ones that arent colored) 799 .replace(/[\u200B-\u200D\uFE0E\uFE0F]/g, '') //sometimes emojis leave these behind 800 .replace(/\d+/g, '') //numbers 801 .trim(); 802 let detectedLanguage = strippedDownText.length < 1 ? {languages:[{language:LANGUAGE, percentage:100}]} : await chrome.i18n.detectLanguage(strippedDownText); 803 if(!detectedLanguage.languages[0]) detectedLanguage = {languages:[{language:LANGUAGE, percentage:100}]}; 804 let isEnglish = detectedLanguage.languages[0] && detectedLanguage.languages[0].percentage > 60 && detectedLanguage.languages[0].language.startsWith(LANGUAGE); 805 let at = false; 806 if(!isEnglish) { 807 let translateBtn = document.createElement('span'); 808 translateBtn.className = "translate-bio"; 809 translateBtn.addEventListener('click', async () => { 810 if(at) return; 811 at = true; 812 let translated = await API.user.translateBio(pageUser.id_str); 813 let span = document.createElement('span'); 814 let translatedMessage; 815 if(LOC.translated_from.message.includes("$LANGUAGE$")) { 816 translatedMessage = LOC.translated_from.message.replace("$LANGUAGE$", `[${translated.localizedSourceLanguage}]`); 817 } else { 818 translatedMessage = `${LOC.translated_from.message} [${translated.localizedSourceLanguage}]`; 819 } 820 span.innerHTML = ` 821 <br> 822 <span class='piu-a'>${translatedMessage}:</span> 823 <span>${escapeHTML(translated.translation).replace(/((http|https|ftp):\/\/[\w?=.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, '<a href="$1">$1</a>').replace(/(?<!\w)@([\w+]{1,15}\b)/g, `<a href="https://twitter.com/$1">@$1</a>`).replace(hashtagRegex, `<a href="https://twitter.com/hashtag/$2">#$2</a>`).replace(/\n/g, '<br>')}</span> 824 `; 825 translateBtn.hidden = true; 826 document.getElementById('profile-bio').append(span); 827 let links = Array.from(span.getElementsByTagName('a')); 828 links.forEach(link => { 829 let realLink = pageUser.entities.description.urls.find(u => u.url === link.href); 830 if (realLink) { 831 link.href = realLink.expanded_url; 832 if(!link.href.startsWith('https://twitter.com/')) link.target = '_blank'; 833 link.innerText = realLink.display_url; 834 } 835 }); 836 if(vars.enableTwemoji) twemoji.parse(span); 837 }); 838 translateBtn.innerText = LOC.translate_bio.message; 839 document.getElementById('profile-bio').append(document.createElement('br'), translateBtn); 840 } 841 842 if(vars.enableTwemoji) twemoji.parse(document.getElementById('profile-name')); 843 844 document.getElementById('profile-stat-tweets-value').innerText = Number(pageUser.statuses_count).toLocaleString().replace(/\s/g, ','); 845 document.getElementById('profile-stat-following-value').innerText = Number(pageUser.friends_count).toLocaleString().replace(/\s/g, ','); 846 document.getElementById('profile-stat-followers-value').innerText = Number(pageUser.followers_count).toLocaleString().replace(/\s/g, ','); 847 document.getElementById('profile-stat-favorites-value').innerText = Number(pageUser.favourites_count).toLocaleString().replace(/\s/g, ','); 848 document.getElementById('profile-stat-media-value').innerText = Number(pageUser.media_count).toLocaleString().replace(/\s/g, ','); 849 850 document.getElementById('tweet-nav').hidden = pageUser.statuses_count === 0 || user_blocked_by || user_protected || !(subpage === 'profile' || subpage === 'replies' || subpage === 'media'); 851 document.getElementById('profile-stat-tweets-link').hidden = pageUser.statuses_count === 0; 852 document.getElementById('profile-stat-following-link').hidden = pageUser.friends_count === 0; 853 document.getElementById('profile-stat-followers-link').hidden = pageUser.followers_count === 0; 854 document.getElementById('profile-stat-favorites-link').hidden = pageUser.favourites_count === 0; 855 document.getElementById('profile-stat-media-link').hidden = pageUser.media_count === 0 || !vars.showMediaCount; 856 857 if((pageUser.statuses_count === 0 && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) || ((pageUser.protected || pageUser.blocked_by) && !pageUser.following && pageUser.id_str !== user.id_str)) { 858 document.getElementById('trends').hidden = true; 859 setTimeout(() => { 860 let list = document.getElementById('wtf-list'); 861 while(list.childElementCount > 3) list.removeChild(list.lastChild); 862 }, 500); 863 } else { 864 document.getElementById('trends').hidden = false; 865 } 866 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) { 867 document.getElementById('no-tweets').hidden = false; 868 document.getElementById('no-tweets').innerHTML = ` 869 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 870 <p>${LOC.when_theyll_tweet.message}</p> 871 `; 872 } else { 873 document.getElementById('no-tweets').hidden = true; 874 document.getElementById('no-tweets').innerHTML = ``; 875 } 876 if(pageUser.blocking && !pageUser.blocked_by) { 877 document.getElementById('no-tweets').hidden = false; 878 document.getElementById('no-tweets').innerHTML = `<div dir="auto" style="color: var(--darker-gray); font-size: 20px;"><h2>${LOC.you_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.do_you_want_see_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p><button class="nice-button" id="see-tweet-btn">${LOC.I_want_see_blocked_user.message}</button> </div>`; 879 document.getElementById('timeline').hidden = true; 880 document.getElementById('tweet-nav').hidden = true; 881 document.getElementById('see-tweet-btn').addEventListener('click', async () => { 882 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) { 883 document.getElementById('trends').hidden = true; 884 document.getElementById('no-tweets').hidden = false; 885 document.getElementById('no-tweets').innerHTML = ` 886 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 887 <p>${LOC.when_theyll_tweet.message}</p> 888 `; 889 } 890 else { 891 document.getElementById('no-tweets').hidden = true; 892 document.getElementById('no-tweets').innerHTML = ``; 893 document.getElementById('timeline').hidden = false; 894 if(!pageUser.protected){ 895 document.getElementById('trends').hidden = false; 896 document.getElementById('tweet-nav').hidden = false; 897 } 898 } 899 900 }); 901 } 902 903 if(pageUser.followed_by) { 904 document.getElementById('follows-you').hidden = false; 905 } else { 906 document.getElementById('follows-you').hidden = true; 907 } 908 909 if(followersYouFollow && followersYouFollow.total_count > 0) { 910 let friendsFollowing = document.getElementById('profile-friends-following'); 911 let friendsFollowingList = document.getElementById('profile-friends-div'); 912 let friendsFollowingText = document.getElementById('profile-friends-text'); 913 if(LOC.followers_you_know.message.includes("$NUMBER$")) { 914 friendsFollowingText.innerText = LOC.followers_you_know.message.replace("$NUMBER$", followersYouFollow.total_count); 915 } else { 916 friendsFollowingText.innerText = `${followersYouFollow.total_count} ${LOC.followers_you_know.message}`; 917 } 918 friendsFollowingText.href = `https://twitter.com/${pageUser.screen_name}/followers_you_follow`; 919 friendsFollowingText 920 followersYouFollow.users.forEach(u => { 921 let a = document.createElement('a'); 922 a.href = `/${u.screen_name}`; 923 let avatar = document.createElement('img'); 924 avatar.src = `${(u.default_profile_image && vars.useOldDefaultProfileImage) ? chrome.runtime.getURL(`images/default_profile_images/default_profile_${Number(u.id_str) % 7}_normal.png`): u.profile_image_url_https}`.replace('_normal', '_bigger'); 925 avatar.width = 45; 926 avatar.height = 45; 927 avatar.title = u.name + ' (@' + u.screen_name + ')'; 928 avatar.classList.add('profile-friends-avatar'); 929 a.append(avatar); 930 friendsFollowingList.append(a); 931 }); 932 friendsFollowing.hidden = false; 933 } else { 934 let friendsFollowing = document.getElementById('profile-friends-following'); 935 friendsFollowing.hidden = true; 936 } 937 938 let buttonsElement = document.getElementById('profile-nav-buttons'); 939 document.getElementById('pin-profile').classList.toggle('menu-active', pageUser.id_str === user.id_str && !location.pathname.includes('/lists')); 940 document.getElementById('pin-lists').classList.toggle('menu-active', location.pathname.startsWith(`/${pageUser.screen_name}/lists`)); 941 let styling = document.getElementById('styling'); 942 if(pageUser.id_str === user.id_str) { 943 buttonsElement.innerHTML = /*html*/` 944 <a class="nice-button" id="edit-profile" target="_blank" href="https://twitter.com/settings/profile?newtwitter=true">${LOC.edit_profile.message}</a> 945 <button class="profile-additional-thing nice-button" id="profile-style"></button> 946 `; 947 let profileStyleActive = false; 948 let profileStyle = document.getElementById('profile-style'); 949 profileStyle.addEventListener('click', () => { 950 profileStyle.classList.toggle('profile-style-active'); 951 profileStyleActive = !profileStyleActive; 952 styling.hidden = !profileStyleActive; 953 }); 954 chrome.storage.local.get(['otPrivateTokens'], data => { 955 document.getElementById('private-profile-warn').hidden = !user.protected || (data.otPrivateTokens && data.otPrivateTokens[user.id_str]); 956 }); 957 } else { 958 document.getElementById('private-profile-warn').hidden = true; 959 styling.hidden = true; 960 document.getElementById('tweet-to-bg').hidden = false; 961 buttonsElement.innerHTML = /*html*/` 962 <button ${(pageUser.blocking || pageUser.blocked_by) ? 'hidden' : ''} class="nice-button ${pageUser.following || pageUser.follow_request_sent ? 'following' : 'follow'} control-btn" id="control-follow">${pageUser.following || (pageUser.protected && pageUser.follow_request_sent) ? ((pageUser.protected && pageUser.follow_request_sent) ? LOC.follow_request_sent.message : LOC.following_btn.message) : LOC.follow.message}</button> 963 <button class="nice-button control-btn" id="control-unblock" ${pageUser.blocking ? '' : 'hidden'}>${LOC.unblock.message}</button> 964 <a ${pageUser.can_dm && !pageUser.blocking && !pageUser.blocked_by ? '' : 'hidden'} class="nice-button" id="message-user"></a> 965 `; 966 if(!pageUser.following) { 967 pageUser.want_retweets = true; 968 } 969 let blockUserText, unblockUserText; 970 if(LOC.block_user.message.includes('$SCREEN_NAME$') && LOC.unblock_user.message.includes('$SCREEN_NAME$')) { 971 blockUserText = `${LOC.block_user.message.replace('$SCREEN_NAME$', pageUser.screen_name)}`; 972 unblockUserText = `${LOC.unblock_user.message.replace('$SCREEN_NAME$', pageUser.screen_name)}`; 973 } else { 974 blockUserText = `${LOC.block_user.message} @${pageUser.screen_name}`; 975 unblockUserText = `${LOC.unblock_user.message} @${pageUser.screen_name}`; 976 } 977 buttonsElement.innerHTML += /*html*/` 978 <span class="profile-additional-thing" id="profile-settings"></span> 979 <div id="profile-settings-div" class="dropdown-menu" hidden> 980 <span ${!pageUser.following || pageUser.blocking ? 'hidden' : ''} id="profile-settings-notifications" class="${pageUser.notifications ? 'profile-settings-offnotifications' : 'profile-settings-notifications'}">${pageUser.notifications ? LOC.stop_notifications.message : LOC.receive_notifications.message}</span> 981 <span id="profile-settings-block" class="${pageUser.blocking ? 'profile-settings-unblock' : 'profile-settings-block'}">${pageUser.blocking ? unblockUserText : blockUserText}</span> 982 <span ${pageUser.blocking || ((pageUser.protected || pageUser.blocked_by) && !pageUser.following) ? 'hidden' : ''} id="profile-settings-mute" class="${pageUser.muting ? 'profile-settings-unmute' : 'profile-settings-mute'}">${pageUser.muting ? LOC.unmute.message : LOC.mute.message}</span> 983 ${pageUser.followed_by ? /*html*/`<span id="profile-settings-removefollowing">${LOC.remove_from_followers.message}</span>` : ''} 984 <span id="profile-settings-lists-action" ${pageUser.blocking || ((pageUser.protected || pageUser.blocked_by) && !pageUser.following) ? 'hidden' : ''}>${LOC.from_list.message}</span> 985 <span id="profile-settings-autotranslate">${toAutotranslate ? LOC.dont_autotranslate.message : LOC.autotranslate_tweets.message}</span> 986 <span id="profile-settings-retweets" ${pageUser.following ? '' : 'hidden'}>${pageUser.want_retweets ? LOC.turn_off_retweets.message : LOC.turn_on_retweets.message}</span> 987 <hr> 988 <span id="profile-settings-lists" ${(pageUser.protected || pageUser.blocked_by) && !pageUser.following ? 'hidden' : ''}>${LOC.see_lists.message}</span> 989 <span id="profile-settings-share">${LOC.share_user.message}</span> 990 <span id="profile-settings-copy">${LOC.copy_profile_link.message}</span> 991 ${vars.developerMode ? /*html*/`<span id="profile-settings-copy-id">${LOC.copy_user_id.message}</span>` : ''} 992 </div> 993 `; 994 let messageUser = document.getElementById('message-user'); 995 messageUser.addEventListener('click', () => { 996 let event = new CustomEvent('messageUser', { detail: { id: `${user.id_str}-${pageUser.id_str}`, user: pageUser } }); 997 document.dispatchEvent(event); 998 }); 999 let clicked = false; 1000 let controlFollow = document.getElementById('control-follow'); 1001 controlFollow.addEventListener('click', async () => { 1002 if (controlFollow.className.includes('following')) { 1003 try { 1004 pageUser.protected && pageUser.follow_request_sent ? await API.user.cancelFollowRequest(pageUser.screen_name) : await API.user.unfollow(pageUser.screen_name); 1005 } catch(e) { 1006 console.error(e); 1007 alert(e); 1008 return; 1009 } 1010 controlFollow.classList.remove('following'); 1011 controlFollow.classList.add('follow'); 1012 controlFollow.innerText = LOC.follow.message; 1013 pageUser.following = false; 1014 document.getElementById("profile-settings-retweets").hidden = true; 1015 document.getElementById('profile-stat-followers-value').innerText = Number(parseInt(document.getElementById('profile-stat-followers-value').innerText.replace(/\s/g, '').replace(/,/g, '')) - 1).toLocaleString().replace(/\s/g, ','); 1016 document.getElementById('profile-settings-notifications').hidden = true; 1017 } else { 1018 try { 1019 await API.user.follow(pageUser.screen_name); 1020 } catch(e) { 1021 console.error(e); 1022 alert(e); 1023 return; 1024 } 1025 controlFollow.classList.add('following'); 1026 controlFollow.classList.remove('follow'); 1027 controlFollow.innerText = pageUser.protected ? LOC.follow_request_sent.message : LOC.following_btn.message; 1028 pageUser.following = true; 1029 if(!pageUser.protected) { 1030 document.getElementById('profile-settings-notifications').hidden = false; 1031 document.getElementById("profile-settings-retweets").hidden = false; 1032 document.getElementById('profile-stat-followers-value').innerText = Number(parseInt(document.getElementById('profile-stat-followers-value').innerText.replace(/\s/g, '').replace(/,/g, '')) + 1).toLocaleString().replace(/\s/g, ','); 1033 } 1034 } 1035 }); 1036 document.getElementById('profile-settings-retweets').addEventListener('click', async e => { 1037 if(pageUser.want_retweets) { 1038 await API.user.switchRetweetsVisibility(pageUser.id_str, false); 1039 pageUser.want_retweets = false; 1040 e.target.innerText = LOC.turn_on_retweets.message; 1041 } else { 1042 await API.user.switchRetweetsVisibility(pageUser.id_str, true); 1043 pageUser.want_retweets = true; 1044 e.target.innerText = LOC.turn_off_retweets.message; 1045 } 1046 }); 1047 document.getElementById('profile-settings').addEventListener('click', () => { 1048 document.getElementById('profile-settings-div').hidden = false; 1049 setTimeout(() => { 1050 if(clicked) return; 1051 clicked = true; 1052 document.addEventListener('click', () => { 1053 setTimeout(() => { 1054 clicked = false; 1055 document.getElementById('profile-settings-div').hidden = true; 1056 }, 100); 1057 }, { once: true }); 1058 }, 100); 1059 }); 1060 document.getElementById('profile-settings-notifications').addEventListener('click', async () => { 1061 if(!pageUser.notifications) { 1062 await API.user.receiveNotifications(pageUser.id_str, true); 1063 pageUser.notifications = true; 1064 document.getElementById('profile-settings-notifications').classList.remove('profile-settings-notifications'); 1065 document.getElementById('profile-settings-notifications').classList.add('profile-settings-offnotifications'); 1066 document.getElementById('profile-settings-notifications').innerText = LOC.stop_notifications.message; 1067 } else { 1068 await API.user.receiveNotifications(pageUser.id_str, false); 1069 pageUser.notifications = false; 1070 document.getElementById('profile-settings-notifications').classList.remove('profile-settings-offnotifications'); 1071 document.getElementById('profile-settings-notifications').classList.add('profile-settings-notifications'); 1072 document.getElementById('profile-settings-notifications').innerText = LOC.stop_notifications.message; 1073 } 1074 }); 1075 1076 1077 document.getElementById('profile-settings-block').addEventListener('click', async () => { 1078 if(pageUser.blocking) { 1079 await API.user.unblock(pageUser.id_str); 1080 pageUser.blocking = false; 1081 document.getElementById('profile-settings-block').classList.remove('profile-settings-unblock'); 1082 document.getElementById('profile-settings-block').classList.add('profile-settings-block'); 1083 if(LOC.block_user.message.includes("$SCREEN_NAME$")) { 1084 document.getElementById('profile-settings-block').innerText = LOC.block_user.message.replace("$SCREEN_NAME$", pageUser.screen_name); 1085 } else { 1086 document.getElementById('profile-settings-block').innerText = `${LOC.block_user.message} @${pageUser.screen_name}`; 1087 } 1088 document.getElementById('control-unblock').hidden = true; 1089 if(!pageUser.blocked_by) { 1090 document.getElementById('control-follow').hidden = false; 1091 document.getElementById("profile-settings-notifications").hidden = false; 1092 document.getElementById("profile-settings-mute").hidden = false; 1093 document.getElementById('message-user').hidden = !pageUser.can_dm; 1094 //enable timeline 1095 //recycle no-tweets 1096 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) { 1097 document.getElementById('trends').hidden = true; 1098 document.getElementById('no-tweets').hidden = false; 1099 document.getElementById('no-tweets').innerHTML = ` 1100 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 1101 <p>${LOC.when_theyll_tweet.message}</p> 1102 `; 1103 } 1104 else { 1105 document.getElementById('trends').hidden = false; 1106 document.getElementById('no-tweets').hidden = true; 1107 document.getElementById('no-tweets').innerHTML = ``; 1108 document.getElementById('timeline').hidden = false; 1109 if(!pageUser.protected) 1110 document.getElementById('tweet-nav').hidden = false; 1111 } 1112 } 1113 } else { 1114 let blockMessage; 1115 if(LOC.block_sure.message.includes("$SCREEN_NAME$")) { 1116 blockMessage = LOC.block_sure.message.replace("$SCREEN_NAME$", pageUser.screen_name); 1117 } else { 1118 blockMessage = `${LOC.block_sure.message} @${pageUser.screen_name}?`; 1119 } 1120 let blockMessageDesc; 1121 if(LOC.block_sure_desc.message.includes("$SCREEN_NAME$")) { 1122 blockMessageDesc = LOC.block_sure_desc.message.replace("$SCREEN_NAME$", pageUser.screen_name); 1123 } else { 1124 blockMessageDesc = `${LOC.block_sure_desc.message} @${pageUser.screen_name}?`; 1125 } 1126 let modal = createModal(` 1127 <h1 class="cool-header">${blockMessage}</span> 1128 <br><br> 1129 <span style='font-size:14px;color:var(--almost-black)'>${blockMessageDesc}</h1> 1130 <br> 1131 <div style="display:inline-block;float: right;margin-top: 5px;"> 1132 <button class="nice-button nice-red-button">${LOC.block.message}</button> 1133 </div> 1134 `) 1135 modal.getElementsByClassName('nice-button')[0].addEventListener('click', async () => { 1136 await API.user.block(pageUser.id_str); 1137 pageUser.blocking = true; 1138 document.getElementById('profile-settings-block').classList.add('profile-settings-unblock'); 1139 document.getElementById('profile-settings-block').classList.remove('profile-settings-block'); 1140 if(LOC.unblock_user.message.includes("$SCREEN_NAME$")) { 1141 document.getElementById('profile-settings-block').innerText = LOC.unblock_user.message.replace("$SCREEN_NAME$", pageUser.screen_name); 1142 } else { 1143 document.getElementById('profile-settings-block').innerText = `${LOC.unblock_user.message} @${pageUser.screen_name}`; 1144 } 1145 document.getElementById('control-unblock').hidden = false; 1146 document.getElementById('control-follow').hidden = true; 1147 document.getElementById('message-user').hidden = true; 1148 document.getElementById("profile-settings-notifications").hidden = true; 1149 document.getElementById("profile-settings-mute").hidden = true; 1150 if(!pageUser.blocked_by) { 1151 //disable timeline 1152 //recycle no-tweets 1153 document.getElementById('no-tweets').hidden = false; 1154 document.getElementById('no-tweets').innerHTML = `<div dir="auto" style="color: var(--darker-gray); font-size: 20px;"><h2>${LOC.you_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.do_you_want_see_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p><button class="nice-button" id="see-tweet-btn">${LOC.I_want_see_blocked_user.message}</button> </div>`; 1155 document.getElementById('timeline').hidden = true; 1156 document.getElementById('tweet-nav').hidden = true; 1157 document.getElementById('see-tweet-btn').addEventListener('click', async () => { 1158 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) { 1159 document.getElementById('trends').hidden = true; 1160 document.getElementById('no-tweets').hidden = false; 1161 document.getElementById('no-tweets').innerHTML = ` 1162 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 1163 <p>${LOC.when_theyll_tweet.message}</p> 1164 `; 1165 } 1166 else { 1167 document.getElementById('no-tweets').hidden = true; 1168 document.getElementById('no-tweets').innerHTML = ``; 1169 document.getElementById('timeline').hidden = false; 1170 if(!pageUser.protected){ 1171 document.getElementById('trends').hidden = false; 1172 document.getElementById('tweet-nav').hidden = false; 1173 } 1174 } 1175 1176 }); 1177 } 1178 modal.removeModal(); 1179 }); 1180 } 1181 }); 1182 document.getElementById('control-unblock').addEventListener('click', async () => { 1183 if(pageUser.blocking) { 1184 await API.user.unblock(pageUser.id_str); 1185 pageUser.blocking = false; 1186 document.getElementById('profile-settings-block').classList.remove('profile-settings-unblock'); 1187 document.getElementById('profile-settings-block').classList.add('profile-settings-block'); 1188 if(LOC.block_user.message.includes("$SCREEN_NAME$")) { 1189 document.getElementById('profile-settings-block').innerText = LOC.block_user.message.replace("$SCREEN_NAME$", pageUser.screen_name); 1190 } else { 1191 document.getElementById('profile-settings-block').innerText = `${LOC.block_user.message} @${pageUser.screen_name}`; 1192 } 1193 document.getElementById('control-unblock').hidden = true; 1194 if(!pageUser.blocked_by) { 1195 document.getElementById('control-follow').hidden = false; 1196 document.getElementById("profile-settings-notifications").hidden = false; 1197 document.getElementById("profile-settings-mute").hidden = false; 1198 document.getElementById('message-user').hidden = !pageUser.can_dm; 1199 //enable timeline 1200 //recycle no-tweets 1201 if(pageUser.statuses_count === 0 && !( pageUser.blocked_by || pageUser.blocking || pageUser.protected ) && (subpage === 'profile' || subpage === 'replies' || subpage === 'media')) { 1202 document.getElementById('trends').hidden = true; 1203 document.getElementById('no-tweets').hidden = false; 1204 document.getElementById('no-tweets').innerHTML = ` 1205 <h3>${LOC.hasnt_tweeted.message.replace('$SCREEN_NAME$', `<span>${pageUser.screen_name}</span>`)}</h3> 1206 <p>${LOC.when_theyll_tweet.message}</p> 1207 `; 1208 } 1209 else { 1210 document.getElementById('trends').hidden = false; 1211 document.getElementById('no-tweets').hidden = true; 1212 document.getElementById('no-tweets').innerHTML = ``; 1213 document.getElementById('timeline').hidden = false; 1214 if(!pageUser.protected) 1215 document.getElementById('tweet-nav').hidden = false; 1216 } 1217 } 1218 } else { 1219 } 1220 1221 1222 }); 1223 document.getElementById('profile-settings-autotranslate').addEventListener('click', async () => { 1224 let autotranslateProfiles = await new Promise(resolve => { 1225 chrome.storage.sync.get(['autotranslateProfiles'], data => { 1226 resolve(data.autotranslateProfiles); 1227 }); 1228 }); 1229 if(!autotranslateProfiles) { 1230 autotranslateProfiles = []; 1231 } 1232 if(autotranslateProfiles.includes(pageUser.id_str)) { 1233 autotranslateProfiles.splice(autotranslateProfiles.indexOf(pageUser.id_str), 1); 1234 document.getElementById('profile-settings-autotranslate').innerText = LOC.dont_autotranslate.message; 1235 toAutotranslate = false; 1236 } else { 1237 autotranslateProfiles.push(pageUser.id_str); 1238 document.getElementById('profile-settings-autotranslate').innerText = LOC.autotranslate_tweets.message; 1239 toAutotranslate = true; 1240 } 1241 chrome.storage.sync.set({ autotranslateProfiles }); 1242 setTimeout(() => { 1243 location.reload(); 1244 }, 100) 1245 }); 1246 document.getElementById('profile-settings-mute').addEventListener('click', async () => { 1247 if(pageUser.muting) { 1248 await API.user.unmute(pageUser.id_str); 1249 pageUser.muting = false; 1250 document.getElementById('profile-settings-mute').classList.remove('profile-settings-unmute'); 1251 document.getElementById('profile-settings-mute').classList.add('profile-settings-mute'); 1252 document.getElementById('profile-settings-mute').innerText = LOC.mute.message; 1253 document.getElementById('profile-name').classList.remove('user-muted'); 1254 } else { 1255 await API.user.mute(pageUser.id_str); 1256 pageUser.muting = true; 1257 document.getElementById('profile-settings-mute').classList.add('profile-settings-unmute'); 1258 document.getElementById('profile-settings-mute').classList.remove('profile-settings-mute'); 1259 document.getElementById('profile-settings-mute').innerText = LOC.unmute.message; 1260 document.getElementById('profile-name').classList.add('user-muted'); 1261 } 1262 }); 1263 if(document.getElementById('profile-settings-removefollowing')) document.getElementById('profile-settings-removefollowing').addEventListener('click', async () => { 1264 let modal = createModal(` 1265 <h1 class="cool-header">${LOC.remove_from_followers_sure.message}</h1><br> 1266 <span style='font-size:14px;color:var(--almost-black)'> 1267 ${LOC.able_in_future.message} 1268 <br><br> 1269 ${LOC.remove_from_followers_warn.message} 1270 </span> 1271 <br><br> 1272 <div style="display:inline-block;float: right;margin-top: 5px;"> 1273 <button class="nice-button nice-red-button">${LOC.remove_from_followers_button.message}</button> 1274 </div> 1275 `.replace('$SCREEN_NAME$', pageUser.screen_name)); 1276 modal.getElementsByClassName('nice-button')[0].addEventListener('click', async () => { 1277 await API.user.removeFollower(pageUser.id_str); 1278 pageUser.followed_by = false; 1279 document.getElementById('profile-settings-removefollowing').hidden = true; 1280 document.getElementById('follows-you').hidden = true; 1281 modal.removeModal(); 1282 }); 1283 }); 1284 document.getElementById('profile-settings-lists-action').addEventListener('click', async () => { 1285 let lists = await API.list.getOwnerships(user.id_str, pageUser.id_str); 1286 let modal = createModal(` 1287 <h1 class="cool-header">${LOC.from_list.message}</h1> 1288 <div id="modal-lists"></div> 1289 `); 1290 let container = document.getElementById('modal-lists'); 1291 for(let i in lists) { 1292 let l = lists[i]; 1293 let listElement = document.createElement('div'); 1294 listElement.classList.add('list-item'); 1295 listElement.innerHTML = ` 1296 <div style="display:inline-block;"> 1297 <a href="https://twitter.com/i/lists/${l.id_str}" class="following-item-link"> 1298 <img style="object-fit: cover;" src="${l.custom_banner_media ? l.custom_banner_media.media_info.original_img_url : l.default_banner_media.media_info.original_img_url}" alt="${l.name}" class="following-item-avatar tweet-avatar" width="48" height="48"> 1299 <div class="following-item-text" style="position: relative;bottom: 12px;"> 1300 <span class="tweet-header-name following-item-name" style="font-size: 18px;">${escapeHTML(l.name)}</span><br> 1301 <span style="color:var(--darker-gray);font-size:14px;margin-top:2px">${l.description ? escapeHTML(l.description).slice(0, 52) : LOC.no_description.message}</span> 1302 </div> 1303 </a> 1304 </div> 1305 <div style="display:inline-block;float: right;margin-top: 5px;"> 1306 <button class="nice-button">${l.is_member ? LOC.remove.message : LOC.add.message}</button> 1307 </div> 1308 `; 1309 container.appendChild(listElement); 1310 listElement.getElementsByClassName('nice-button')[0].addEventListener('click', async () => { 1311 if(l.is_member) { 1312 await API.list.removeMember(l.id_str, pageUser.id_str); 1313 l.is_member = false; 1314 listElement.getElementsByClassName('nice-button')[0].innerText = LOC.add.message; 1315 } else { 1316 await API.list.addMember(l.id_str, pageUser.id_str); 1317 l.is_member = true; 1318 listElement.getElementsByClassName('nice-button')[0].innerText = LOC.remove.message; 1319 } 1320 l.is_member = !l.is_member; 1321 }); 1322 } 1323 }); 1324 document.getElementById('profile-settings-lists').addEventListener('mousedown', e => { 1325 if(e.button === 1) { 1326 openInNewTab(`https://twitter.com/${pageUser.screen_name}/lists`); 1327 } 1328 }); 1329 document.getElementById('profile-settings-lists').addEventListener('click', async () => { 1330 // document.getElementById('loading-box').hidden = false; 1331 history.pushState({}, null, `https://twitter.com/${pageUser.screen_name}/lists`); 1332 everAddedAdditional = false; 1333 mediaToUpload = []; 1334 document.getElementById('profile-media-div').innerHTML = ''; 1335 document.getElementById('tweet-to-bg').hidden = true; 1336 document.getElementById('profile-additional').innerHTML = ''; 1337 document.getElementById('profile-friends-div').innerHTML = ''; 1338 updateSubpage(); 1339 updateSelection(); 1340 renderLists(); 1341 }); 1342 document.getElementById('profile-settings-share').addEventListener('click', async () => { 1343 navigator.share({ url: `https://twitter.com/${pageUser.screen_name}` }); 1344 }); 1345 document.getElementById('profile-settings-copy').addEventListener('click', async () => { 1346 navigator.clipboard.writeText(`https://twitter.com/${pageUser.screen_name}`); 1347 }); 1348 if(document.getElementById('profile-settings-copy-id')) document.getElementById('profile-settings-copy-id').addEventListener('click', async () => { 1349 navigator.clipboard.writeText(pageUser.id_str); 1350 }); 1351 } 1352 1353 let links = Array.from(document.getElementById('profile-bio').getElementsByTagName('a')); 1354 links.forEach(link => { 1355 let realLink = pageUser.entities.description.urls.find(u => u.url === link.href); 1356 if (realLink) { 1357 link.href = realLink.expanded_url; 1358 if(!link.href.startsWith('https://twitter.com/')) link.target = '_blank'; 1359 link.innerText = realLink.display_url; 1360 } 1361 }); 1362 1363 if(everAddedAdditional) return; 1364 everAddedAdditional = true; 1365 let additionalInfo = document.getElementById('profile-additional'); 1366 if(pageUser.location) { 1367 let location = document.createElement('span'); 1368 location.classList.add('profile-additional-thing', 'profile-additional-location'); 1369 location.innerText = pageUser.location.replace(/\n\n\n\n/g, "\n"); 1370 additionalInfo.appendChild(location); 1371 if(vars.enableTwemoji) twemoji.parse(location); 1372 } 1373 if(pageUser.affiliates_highlighted_label) { 1374 let aff = document.createElement('span'); 1375 aff.classList.add('profile-additional-thing', 'profile-additional-affiliates'); 1376 aff.innerHTML = ` 1377 <img style="display: inline-block;vertical-align: top;image-rendering: pixelated;" src="${pageUser.affiliates_highlighted_label.badge.url}" width="20" height="20"> 1378 <a style="color:var(--almost-black)!important" href="${pageUser.affiliates_highlighted_label.url ? pageUser.affiliates_highlighted_label.url.url : '#'}">${pageUser.affiliates_highlighted_label.description}</a> 1379 `; 1380 additionalInfo.appendChild(aff); 1381 } 1382 if(pageUser.url) { 1383 let url = document.createElement('a'); 1384 url.classList.add('profile-additional-thing', 'profile-additional-url'); 1385 let realUrl = pageUser.entities.url.urls[0]; 1386 url.innerText = realUrl.display_url; 1387 url.href = realUrl.expanded_url; 1388 if(!url.href.startsWith('https://twitter.com/')) url.target = "_blank"; 1389 additionalInfo.appendChild(url); 1390 } 1391 if(pageUser.professional && pageUser.professional.category && pageUser.professional.category[0]) { 1392 let prof = document.createElement('span'); 1393 prof.classList.add('profile-additional-thing', 'profile-additional-professional'); 1394 prof.innerText = pageUser.professional.category[0].name; 1395 additionalInfo.appendChild(prof); 1396 if(vars.enableTwemoji) twemoji.parse(prof); 1397 } 1398 let joined = document.createElement('span'); 1399 joined.classList.add('profile-additional-thing', 'profile-additional-joined'); 1400 joined.innerText = `${LOC.joined.message} ${new Date(pageUser.created_at).toLocaleDateString(LANGUAGE.replace("_", "-"), {month: 'long', year: 'numeric', day: 'numeric'})}`; 1401 additionalInfo.appendChild(joined); 1402 if(pageUser.birthdate) { 1403 let birth = document.createElement('span'); 1404 birth.classList.add('profile-additional-thing', 'profile-additional-birth'); 1405 if(user.id_str === pageUser.id_str) { 1406 birth.classList.add('profile-additional-birth-me'); 1407 } 1408 if(pageUser.birthdate.year && typeof pageUser.birthdate.month === 'number') { 1409 birth.innerText = `${LOC.born.message} ${months[pageUser.birthdate.month-1].replace("$NUMBER$", pageUser.birthdate.day)}, ${pageUser.birthdate.year}`; 1410 } else if(typeof pageUser.birthdate.month === 'number') { 1411 birth.innerText = `${LOC.born.message} ${months[pageUser.birthdate.month-1].replace("$NUMBER$", pageUser.birthdate.day)}`; 1412 } else if(pageUser.birthdate.year) { 1413 birth.innerText = `${LOC.born.message} ${pageUser.birthdate.year}`; 1414 } 1415 let date = new Date(); 1416 if(pageUser.birthdate.month-1 === date.getMonth() && pageUser.birthdate.day === date.getDate()) { 1417 birth.innerText += ' ' + LOC.birthday_today.message; 1418 birth.classList.add('profile-additional-birth-today'); 1419 } 1420 additionalInfo.appendChild(birth); 1421 } 1422 1423 setTimeout(() => { 1424 document.getElementById('loading-box').hidden = true; 1425 }, 10); 1426}; 1427 1428async function renderTimeline(append = false, sliceAmount = 0) { 1429 let timelineContainer = document.getElementById('timeline'); 1430 if(!append) timelineContainer.innerHTML = ''; 1431 let data = timeline.data.slice(sliceAmount, timeline.data.length);; 1432 if(pinnedTweet && subpage === "profile" && !append) await appendTweet(pinnedTweet, timelineContainer, { 1433 top: { 1434 text: LOC.pinned_tweet.message, 1435 icon: "\uf003", 1436 color: "var(--link-color)", 1437 class: 'pinned' 1438 }, 1439 bigFont: false 1440 }) 1441 for(let i in data) { 1442 let t = data[i]; 1443 if(!t) continue; 1444 if(pinnedTweet && t.id_str === pinnedTweet.id_str) continue; 1445 if (t.retweeted_status) { 1446 if(pageUser.id_str === user.id_str) t.retweeted_status.current_user_retweet = t; 1447 await appendTweet(t.retweeted_status, timelineContainer, { 1448 top: { 1449 text: `<a href="https://twitter.com/${t.user.screen_name}">${escapeHTML(t.user.name)}</a> ${LOC.retweeted.message}`, 1450 icon: "\uf006", 1451 color: "#77b255", 1452 class: 'retweet-label' 1453 } 1454 }); 1455 } else { 1456 if (t.self_thread) { 1457 let selfThreadTweet = timeline.data.find(tweet => tweet.id_str === t.self_thread.id_str); 1458 if (selfThreadTweet && selfThreadTweet.id_str !== t.id_str && seenThreads.indexOf(selfThreadTweet.id_str) === -1) { 1459 await appendTweet(selfThreadTweet, timelineContainer, { 1460 selfThreadContinuation: true, 1461 bigFont: selfThreadTweet.favorite_count > averageLikeCount*1.2 && selfThreadTweet.favorite_count > 3 && (!selfThreadTweet.full_text || selfThreadTweet.full_text.length < 250) 1462 }); 1463 await appendTweet(t, timelineContainer, { 1464 noTop: true, 1465 bigFont: t.favorite_count > averageLikeCount*1.2 && t.favorite_count > 3 && (!t.full_text || t.full_text.length < 250) 1466 }); 1467 seenThreads.push(selfThreadTweet.id_str); 1468 } else { 1469 await appendTweet(t, timelineContainer, { 1470 selfThreadButton: true, 1471 bigFont: t.favorite_count > averageLikeCount*1.2 && t.favorite_count > 3 && (!t.full_text || t.full_text.length < 250) 1472 }); 1473 } 1474 } else { 1475 await appendTweet(t, timelineContainer, { 1476 bigFont: t.favorite_count > averageLikeCount*1.2 && t.favorite_count > 3 && (!t.full_text || t.full_text.length < 250) 1477 }); 1478 } 1479 } 1480 }; 1481 document.getElementById('loading-box').hidden = true; 1482 loadingNewTweets = false; 1483 return true; 1484} 1485function renderNewTweetsButton() { 1486 if (timeline.toBeUpdated > 0) { 1487 document.getElementById('new-tweets').hidden = false; 1488 document.getElementById('new-tweets').innerText = LOC.see_new_tweets.message; 1489 } else { 1490 document.getElementById('new-tweets').hidden = true; 1491 } 1492} 1493 1494document.addEventListener('clearActiveTweet', () => { 1495 if(activeTweet) { 1496 activeTweet.classList.remove('tweet-active'); 1497 } 1498 activeTweet = undefined; 1499}); 1500document.addEventListener('findActiveTweet', () => { 1501 let tweets = Array.from(document.getElementsByClassName('tweet')); 1502 if(activeTweet) { 1503 activeTweet.classList.remove('tweet-active'); 1504 } 1505 let scrollPoint = scrollY + innerHeight/2; 1506 activeTweet = tweets.find(t => scrollPoint > t.offsetTop && scrollPoint < t.offsetTop + t.offsetHeight); 1507 if(activeTweet) { 1508 activeTweet.classList.add('tweet-active'); 1509 } 1510}); 1511let loadingNewTweets = true; 1512let lastTweetDate = 0; 1513let activeTweet; 1514let tweetsToLoad = {}; 1515let lastScroll = Date.now(); 1516let loadingFollowing = false; 1517let loadingFollowers = false; 1518let loadingFollowersYouKnow = false; 1519let followingMoreBtn, followersMoreBtn, followersYouFollowMoreBtn; 1520 1521setTimeout(async () => { 1522 if(!vars) { 1523 await loadVars(); 1524 } 1525 while(!LOC || !LOC.january) { 1526 await sleep(10); 1527 } 1528 months = [LOC.january.message, LOC.february.message, LOC.march.message, LOC.april.message, LOC.may.message, LOC.june.message, LOC.july.message, LOC.august.message, LOC.september.message, LOC.october.message, LOC.november.message, LOC.december.message]; 1529 1530 // weird bug 1531 if(!document.getElementById('new-tweets')) { 1532 return setTimeout(() => location.reload(), 500); 1533 } 1534 try { 1535 document.getElementById('new-tweets').addEventListener('click', () => { 1536 timeline.toBeUpdated = 0; 1537 timeline.data = timeline.dataToUpdate; 1538 timeline.dataToUpdate = []; 1539 renderNewTweetsButton(); 1540 renderTimeline(); 1541 }); 1542 } catch(e) { 1543 setTimeout(() => location.reload(), 500); 1544 console.error(e); 1545 return; 1546 } 1547 1548 // mouse 1549 let banner = document.getElementById('profile-banner'); 1550 let navProfileInfo = document.getElementById('nav-profile-info'); 1551 document.addEventListener('scroll', async () => { 1552 lastScroll = Date.now(); 1553 // find active tweet by scroll amount 1554 if(Date.now() - lastTweetDate > 50) { 1555 lastTweetDate = Date.now(); 1556 let tweets = Array.from(document.getElementsByClassName('tweet')); 1557 1558 let scrollPoint = scrollY + innerHeight/2; 1559 let newActiveTweet = tweets.find(t => scrollPoint > t.offsetTop && scrollPoint < t.offsetTop + t.offsetHeight); 1560 if(!activeTweet || (newActiveTweet && !activeTweet.className.startsWith(newActiveTweet.className))) { 1561 if(activeTweet) { 1562 activeTweet.classList.remove('tweet-active'); 1563 } 1564 if(newActiveTweet) newActiveTweet.classList.add('tweet-active'); 1565 if(vars.autoplayVideos && !document.getElementsByClassName('modal')[0]) { 1566 if(activeTweet) { 1567 let video = activeTweet.querySelector('.tweet-media > video[controls]'); 1568 if(video) { 1569 video.pause(); 1570 } 1571 } 1572 if(newActiveTweet) { 1573 let newVideo = newActiveTweet.querySelector('.tweet-media > video[controls]'); 1574 let newVideoOverlay = newActiveTweet.querySelector('.tweet-media > .tweet-media-video-overlay'); 1575 if(newVideo && !newVideo.ended) { 1576 newVideo.play(); 1577 } else if(newVideoOverlay && !newVideoOverlay.style.display) { 1578 newVideoOverlay.click(); 1579 } 1580 } 1581 } 1582 activeTweet = newActiveTweet; 1583 } 1584 } 1585 1586 // make user nav appear 1587 if(window.scrollY >= 600) { 1588 if(!navProfileInfo.style.opacity) { 1589 navProfileInfo.style.opacity = 1; 1590 } 1591 } else { 1592 if(navProfileInfo.style.opacity) { 1593 navProfileInfo.style.opacity = ''; 1594 } 1595 } 1596 1597 // banner scroll 1598 banner.style.top = `${5+Math.min(window.scrollY/4, 470/4)}px`; 1599 1600 // load more stuff 1601 if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 1000) { 1602 if(subpage === 'following') { 1603 if(!loadingFollowing) followingMoreBtn.click(); 1604 return; 1605 } 1606 if(subpage === 'followers') { 1607 if(!loadingFollowers) followersMoreBtn.click(); 1608 return; 1609 } 1610 if(subpage === 'followers_you_follow') { 1611 if(!loadingFollowersYouKnow) followersYouFollowMoreBtn.click(); 1612 return; 1613 } 1614 if (loadingNewTweets || timeline.data.length === 0 || stopLoad) return; 1615 loadingNewTweets = true; 1616 let tl; 1617 try { 1618 if (!user_protected && !user_blocked_by) { 1619 if(subpage === "likes") { 1620 let data = await API.user.getFavorites(pageUser.id_str, favoritesCursor); 1621 tl = data.tl; 1622 favoritesCursor = data.cursor; 1623 } else { 1624 if(subpage === 'media') { 1625 tl = await API.user.getMediaTweets(pageUser.id_str, mediaCursor); 1626 mediaCursor = tl.cursor; 1627 tl = tl.tweets; 1628 } else { 1629 tl = await API.user.getTweetsV2(pageUser.id_str, tweetsCursor, subpage !== 'profile'); 1630 tweetsCursor = tl.cursor; 1631 tl = tl.tweets; 1632 } 1633 } 1634 } else if (user_blocked_by) { 1635 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.blocked_by_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.why_you_cant_see_block_user.message.replaceAll("$SCREEN_NAME$",pageUser.screen_name)}</p></div>`; 1636 return; 1637 } else if (user_protected) { 1638 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.user_protected.message}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.follow_to_see.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p></div>`; 1639 return; 1640 } /*else if (user_blocking) { 1641 document.getElementById("timeline").innerHTML = `<div dir="auto" style="padding: 50px;color: var(--darker-gray); font-size: 20px;"><h2>${LOC.you_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</h2><p style="font-size: 15px;" href="https://twitter.com/${pageUser.screen_name}">${LOC.do_you_want_see_blocked_user.message.replace("$SCREEN_NAME$",pageUser.screen_name)}</p><button class="nice-button" id="see-tweet-btn">${LOC.I_want_see_blocked_user.message}</button> </div>`; 1642 1643 return; 1644 }*/ 1645 } catch (e) { 1646 console.error(e); 1647 loadingNewTweets = false; 1648 return; 1649 } 1650 let originalLength = timeline.data.length; 1651 timeline.data = timeline.data.concat(tl); 1652 averageLikeCount = timeline.data.filter(t => !t.retweeted_status).map(t => t.favorite_count).sort((a, b) => a - b)[Math.floor(timeline.data.length/2)]; 1653 if(subpage === 'profile') { 1654 if(!tweetsCursor) { 1655 stopLoad = true; 1656 } 1657 } else { 1658 if(previousLastTweet && previousLastTweet.id_str === timeline.data[timeline.data.length - 1].id_str) return stopLoad = true; 1659 } 1660 previousLastTweet = timeline.data[timeline.data.length - 1]; 1661 await renderTimeline(true, originalLength); 1662 } 1663 }, { passive: true }); 1664 // document.addEventListener('mousemove', e => { 1665 // if(Date.now() - lastScroll > 10) { 1666 // let t = e.target; 1667 // if(t.className.includes('tweet ') || t.className === 'tweet-interact' || t.className === 'tweet-body' || t.className === 'tweet-media') { 1668 // if(t.className === 'tweet-interact' || t.className === 'tweet-media') t = t.parentElement.parentElement; 1669 // else if(t.className === 'tweet-body') t = t.parentElement; 1670 // let id = t.className.split('id-')[1]; 1671 // if(!id) return; 1672 // id = id.split(' ')[0]; 1673 // if(!tweetsToLoad[id]) tweetsToLoad[id] = 1; 1674 // else tweetsToLoad[id]++; 1675 // if(tweetsToLoad[id] === 15) { 1676 // API.tweet.getRepliesV2(id); 1677 // API.tweet.getLikers(id); 1678 // t.classList.add('tweet-preload'); 1679 // console.log(`Preloading ${id}`); 1680 // } 1681 // } 1682 // } 1683 // }); 1684 1685 // buttons 1686 document.getElementById('tweet-to').addEventListener('click', () => { 1687 document.getElementById('navbar-tweet-button').click(); 1688 setTimeout(() => { 1689 document.getElementsByClassName('navbar-new-tweet-text')[0].value = `@${pageUser.screen_name} `; 1690 }, 10); 1691 }); 1692 let tweetNavMoreMenu = document.getElementById('tweet-nav-more-menu'); 1693 let tweetNavClicked = false; 1694 let tweetNavMoreMenuHR = document.getElementById('tweet-nav-more-menu-hr'); 1695 let tweetNavMoreMenuHNR = document.getElementById('tweet-nav-more-menu-hnr'); 1696 document.getElementById('tweet-nav-more').addEventListener('click', () => { 1697 if (tweetNavMoreMenu.hidden) { 1698 tweetNavMoreMenu.hidden = false; 1699 if(subpage === 'replies') { 1700 tweetNavMoreMenu.style.height = '77px'; 1701 tweetNavMoreMenuHNR.hidden = false; 1702 document.getElementById('tweet-nav-more-menu-hnr-label').hidden = false; 1703 } 1704 } 1705 if(tweetNavClicked) return; 1706 tweetNavClicked = true; 1707 setTimeout(() => { 1708 function closeMenu(e) { 1709 if(e.target.closest('#tweet-nav-more-menu')) { 1710 return; 1711 } 1712 tweetNavClicked = false; 1713 setTimeout(() => { 1714 tweetNavMoreMenu.hidden = true; 1715 tweetNavMoreMenu.style.height = ''; 1716 tweetNavMoreMenuHNR.hidden = true; 1717 document.getElementById('tweet-nav-more-menu-hnr-label').hidden = true; 1718 }, 50); 1719 document.body.removeEventListener('click', closeMenu); 1720 } 1721 document.body.addEventListener('click', closeMenu); 1722 }, 50); 1723 }); 1724 function updateHideStyle() { 1725 let style = ''; 1726 if(tweetNavMoreMenuHR.checked) { 1727 style += `.tweet-top-retweet-label { display: none !important; }`; 1728 } 1729 if(tweetNavMoreMenuHNR.checked) { 1730 style += `.tweet-non-reply { display: none !important; }`; 1731 } 1732 document.getElementById('style-hide-retweets').innerHTML = style; 1733 } 1734 tweetNavMoreMenuHR.addEventListener('change', updateHideStyle); 1735 tweetNavMoreMenuHNR.addEventListener('change', updateHideStyle); 1736 document.getElementById('wtf-refresh').addEventListener('click', async () => { 1737 renderDiscovery(false); 1738 }); 1739 followingMoreBtn = document.getElementById('following-more'); 1740 followingMoreBtn.addEventListener('click', async e => { 1741 if(!followingCursor || loadingFollowing) return; 1742 e.target.innerText = LOC.loading.message; 1743 renderFollowing(false, followingCursor); 1744 }); 1745 followersMoreBtn = document.getElementById('followers-more'); 1746 followersMoreBtn.addEventListener('click', async e => { 1747 if(!followersCursor || loadingFollowers) return; 1748 e.target.innerText = LOC.loading.message; 1749 renderFollowers(false, followersCursor); 1750 }); 1751 followersYouFollowMoreBtn = document.getElementById('followers_you_follow-more'); 1752 followersYouFollowMoreBtn.addEventListener('click', async e => { 1753 if(!followersYouKnowCursor || loadingFollowersYouKnow) return; 1754 e.target.innerText = LOC.loading.message; 1755 renderFollowersYouFollow(false, followersYouKnowCursor); 1756 }); 1757 function updatePath(e) { 1758 if(e.target.closest('.tweet-nav-active') || e.target.classList.contains('profile-stat-active') || e.target.closest('.profile-stat-disabled')) { 1759 return e.preventDefault(); 1760 } 1761 e.preventDefault(); 1762 let el = e.target; 1763 if(!el) return; 1764 if(!el.href) el = el.parentElement; 1765 history.pushState({}, null, el.href); 1766 updateSubpage(); 1767 updateSelection(); 1768 timeline = { 1769 data: [], 1770 dataToUpdate: [], 1771 toBeUpdated: 0 1772 } 1773 seenThreads = []; 1774 pinnedTweet = undefined; 1775 tweetsCursor = undefined; 1776 favoritesCursor = undefined; 1777 followersCursor = undefined; 1778 followingCursor = undefined; 1779 followersYouKnowCursor = undefined; 1780 mediaCursor = undefined; 1781 if(window.scrollY > 400) window.scrollTo(0, 400); 1782 if(subpage === 'following') { 1783 renderFollowing(); 1784 } else if(subpage === 'followers') { 1785 renderFollowers(); 1786 } else if(subpage === 'followers_you_follow') { 1787 renderFollowersYouFollow(); 1788 } else if(subpage === 'lists') { 1789 renderLists(); 1790 } else { 1791 loadingNewTweets = true; 1792 updateTimeline(); 1793 } 1794 } 1795 document.getElementById('tweet-nav-tweets').addEventListener('click', updatePath); 1796 document.getElementById('tweet-nav-replies').addEventListener('click', updatePath); 1797 document.getElementById('tweet-nav-media').addEventListener('click', updatePath); 1798 if(document.getElementById('profile-media-text')) document.getElementById('profile-media-text').addEventListener('click', updatePath); 1799 document.getElementById('profile-stat-tweets-link').addEventListener('click', updatePath); 1800 document.getElementById('profile-stat-following-link').addEventListener('click', updatePath); 1801 document.getElementById('profile-stat-followers-link').addEventListener('click', updatePath); 1802 document.getElementById('profile-stat-favorites-link').addEventListener('click', updatePath); 1803 document.getElementById('profile-stat-media-link').addEventListener('click', updatePath); 1804 document.getElementById('profile-friends-text').addEventListener('click', updatePath); 1805 document.addEventListener('click', async e => { 1806 let el = e.target; 1807 if(!el) return; 1808 if(el.tagName !== 'A') el = el.closest('a'); 1809 if(!el) return; 1810 if(el.tagName === "A") { 1811 let path; 1812 try { 1813 let url = new URL(el.href); 1814 path = url.pathname; 1815 if(url.hostname !== 'twitter.com') return; 1816 } catch(e) { 1817 return; 1818 } 1819 if(/^\/[A-z-0-9-_]{1,15}$/.test(path) && ["/home", "/", "/notifications", "/messages", "/settings", "/search", "/explore", "/login", "/register", "/logout"].indexOf(path) === -1) { 1820 if(document.querySelector(".modal")) return; 1821 e.preventDefault(); 1822 window.scrollTo(0, 0); 1823 mediaToUpload = []; 1824 loadingNewTweets = true; 1825 document.getElementById('loading-box').hidden = false; 1826 everAddedAdditional = false; 1827 document.getElementById('timeline').innerHTML = ` 1828 <div class="loading-data" id="tweets-loader"> 1829 <img src="${chrome.runtime.getURL(`images/loading.svg`)}" width="64" height="64"> 1830 </div>`; 1831 document.getElementById('profile-media-div').innerHTML = ''; 1832 document.getElementById('tweet-to-bg').hidden = true; 1833 document.getElementById('profile-additional').innerHTML = ''; 1834 document.getElementById('profile-friends-div').innerHTML = ''; 1835 history.pushState({}, null, `https://twitter.com/${path.substring(1)}`); 1836 updateSubpage(); 1837 updateSelection(); 1838 await updateUserData(); 1839 updateTimeline(); 1840 renderDiscovery(); 1841 } 1842 } 1843 }); 1844 window.addEventListener("popstate", async () => { 1845 if(document.querySelector('.tweet-viewer')) return; 1846 let path = location.pathname; 1847 if(path.endsWith("/")) path = path.substring(0, path.length - 1); 1848 if(isProfilePath(path) || (path.split('/').length === 3 && location.pathname.endsWith('/following') || location.pathname.endsWith('/followers') || location.pathname.endsWith('/followers_you_follow') || location.pathname.endsWith('/lists') || location.pathname.endsWith('/media') || location.pathname.endsWith('/likes') || location.pathname.endsWith('/with_replies'))) { 1849 document.getElementById('loading-box').hidden = false; 1850 everAddedAdditional = false; 1851 loadingNewTweets = true; 1852 mediaToUpload = []; 1853 document.getElementById('profile-media-div').innerHTML = ''; 1854 document.getElementById('tweet-to-bg').hidden = true; 1855 document.getElementById('profile-additional').innerHTML = ''; 1856 document.getElementById('profile-friends-div').innerHTML = ''; 1857 updateSubpage(); 1858 updateSelection(); 1859 document.getElementById('timeline').innerHTML = ''; 1860 await updateUserData(); 1861 updateTimeline(); 1862 renderDiscovery(); 1863 } 1864 }); 1865 1866 document.getElementById('user-search-input').addEventListener('keydown', e => { 1867 if(e.key === 'Enter') { 1868 document.getElementById('user-search-icon').click(); 1869 } 1870 }); 1871 document.getElementById('user-search-icon').addEventListener("click", () => { 1872 document.getElementById('search-input').value = document.getElementById('user-search-input').value + ` from:${pageUser.screen_name}`; 1873 document.getElementById('search-icon').click(); 1874 }) 1875 1876 let mediaDiv = document.getElementById('profile-media-div'); 1877 let mediaText = document.getElementById('profile-media-text'); 1878 let mediaObserver = new MutationObserver(() => { 1879 mediaText.hidden = mediaDiv.childElementCount === 0; 1880 }) 1881 mediaObserver.observe(mediaDiv, { 1882 childList: true 1883 }); 1884 1885 // Update dates every minute 1886 setInterval(() => { 1887 let tweetDates = Array.from(document.getElementsByClassName('tweet-time')); 1888 let tweetQuoteDates = Array.from(document.getElementsByClassName('tweet-time-quote')); 1889 let all = [...tweetDates, ...tweetQuoteDates]; 1890 all.forEach(date => { 1891 date.innerText = timeElapsed(+date.dataset.timestamp); 1892 }); 1893 }, 60000); 1894 1895 // Custom events 1896 document.addEventListener('newTweet', e => { 1897 if(pageUser.id_str === user.id_str) { 1898 let tweet = e.detail; 1899 if(pinnedTweet) { 1900 let firstTweet = document.getElementById('timeline').firstChild; 1901 appendTweet(tweet, document.getElementById('timeline'), { after: firstTweet, disableAfterReplyCounter: true, bigFont: tweet.favorite_count > averageLikeCount*1.2 && tweet.favorite_count > 3 }); 1902 } else { 1903 appendTweet(tweet, document.getElementById('timeline'), { prepend: true, bigFont: tweet.favorite_count > averageLikeCount*1.2 && tweet.favorite_count > 3 }); 1904 } 1905 } 1906 }); 1907 1908 // Customization 1909 let r = document.querySelector(':root'); 1910 let profileLinkColor = document.getElementById('profile-link-color'); 1911 let colorSyncButton = document.getElementById('color-sync-button'); 1912 let cssSyncButton = document.getElementById('css-sync-button'); 1913 let cssLoadButton = document.getElementById('css-load-button'); 1914 let darkModeVars = document.getElementById('dark-mode-vars'); 1915 let lightModeVars = document.getElementById('light-mode-vars'); 1916 let cssTextArea = document.getElementById('profile-css-textarea'); 1917 let saveDraft = document.getElementById('save-draft'); 1918 let loadDraft = document.getElementById('load-draft'); 1919 1920 darkModeVars.addEventListener('change', async () => { 1921 if(isDarkModeEnabled) { 1922 await switchDarkMode(true); 1923 let vars = parseVariables(darkModeVars.value); 1924 for(let i in vars) { 1925 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 1926 } 1927 } 1928 }); 1929 lightModeVars.addEventListener('change', async () => { 1930 if(!isDarkModeEnabled) { 1931 await switchDarkMode(false); 1932 let vars = parseVariables(lightModeVars.value); 1933 for(let i in vars) { 1934 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 1935 } 1936 } 1937 }); 1938 cssTextArea.addEventListener('change', () => { 1939 if(!customCSS) { 1940 customCSS = document.createElement('style'); 1941 customCSS.id = 'oldtwitter-custom-css'; 1942 document.head.appendChild(customCSS); 1943 } 1944 customCSS.innerHTML = cssTextArea.value; 1945 }); 1946 darkModeVars.addEventListener('keydown', async e => { 1947 if(e.keyCode === 83 && e.ctrlKey) { 1948 e.preventDefault(); 1949 e.stopImmediatePropagation(); 1950 if(isDarkModeEnabled) { 1951 await switchDarkMode(true); 1952 let vars = parseVariables(darkModeVars.value); 1953 for(let i in vars) { 1954 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 1955 } 1956 } 1957 } 1958 }); 1959 lightModeVars.addEventListener('keydown', async e => { 1960 if(e.keyCode === 83 && e.ctrlKey) { 1961 e.preventDefault(); 1962 e.stopImmediatePropagation(); 1963 if(!isDarkModeEnabled) { 1964 await switchDarkMode(false); 1965 let vars = parseVariables(lightModeVars.value); 1966 for(let i in vars) { 1967 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 1968 } 1969 } 1970 } 1971 }); 1972 cssTextArea.addEventListener('keydown', e => { 1973 if(e.key === "Tab") { 1974 e.preventDefault(); 1975 e.stopImmediatePropagation(); 1976 let pos = cssTextArea.selectionStart; 1977 cssTextArea.value = cssTextArea.value.slice(0, pos) + " " + cssTextArea.value.slice(pos); 1978 cssTextArea.selectionStart = cssTextArea.selectionEnd = pos + 2; 1979 } else if(e.keyCode === 83 && e.ctrlKey) { 1980 e.preventDefault(); 1981 e.stopImmediatePropagation(); 1982 if(!customCSS) { 1983 customCSS = document.createElement('style'); 1984 customCSS.id = 'oldtwitter-custom-css'; 1985 document.head.appendChild(customCSS); 1986 } 1987 customCSS.innerHTML = cssTextArea.value; 1988 } 1989 }); 1990 saveDraft.addEventListener('click', () => { 1991 chrome.storage.local.set({ 1992 cssDraft: { 1993 darkVars: darkModeVars.value, 1994 lightVars: lightModeVars.value, 1995 css: cssTextArea.value 1996 } 1997 }); 1998 toast.info(LOC.draft_saved.message); 1999 }); 2000 loadDraft.addEventListener('click', () => { 2001 chrome.storage.local.get(['cssDraft'], async d => { 2002 if(d.cssDraft) { 2003 if(!d.cssDraft.darkVars && !d.cssDraft.lightVars && !d.cssDraft.css) return toast.error(LOC.no_draft.message); 2004 darkModeVars.value = d.cssDraft.darkVars; 2005 lightModeVars.value = d.cssDraft.lightVars; 2006 cssTextArea.value = d.cssDraft.css; 2007 toast.info(LOC.draft_loaded.message); 2008 2009 if(!customCSS) { 2010 customCSS = document.createElement('style'); 2011 customCSS.id = 'oldtwitter-custom-css'; 2012 document.head.appendChild(customCSS); 2013 } 2014 customCSS.innerHTML = cssTextArea.value; 2015 2016 if(isDarkModeEnabled) { 2017 await switchDarkMode(true); 2018 let vars = parseVariables(darkModeVars.value); 2019 for(let i in vars) { 2020 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 2021 } 2022 } else { 2023 await switchDarkMode(false); 2024 let vars = parseVariables(lightModeVars.value); 2025 for(let i in vars) { 2026 if(r.style.getPropertyValue(i)) r.style.setProperty(i, vars[i]); 2027 } 2028 } 2029 } else { 2030 toast.error(LOC.no_draft.message); 2031 } 2032 }); 2033 }); 2034 cssSyncButton.addEventListener('click', async () => { 2035 cssSyncButton.disabled = true; 2036 try { 2037 let res = await fetch(`https://dimden.dev/services/twitter_link_colors/v2/setcss`, { 2038 method: 'POST', 2039 headers: { 2040 'Content-Type': 'application/json' 2041 }, 2042 body: JSON.stringify({ 2043 css: cssTextArea.value, 2044 dark_mode_vars: darkModeVars.value, 2045 light_mode_vars: lightModeVars.value, 2046 private_token: await getOtAuthToken(!(user.followers_count >= 5000 && cssEligibleAuto && !cssEligible)) 2047 }) 2048 }).then(i => i.text()); 2049 if(res === 'auth_error') { 2050 chrome.storage.local.get(["otPrivateTokens"], data => { 2051 delete data.otPrivateTokens[pageUser.id_str]; 2052 chrome.storage.local.set({ otPrivateTokens: data.otPrivateTokens }); 2053 cssSyncButton.disabled = false; 2054 alert(LOC.invalid_auth_token.message); 2055 }); 2056 } else if(res === 'set') { 2057 chrome.storage.local.get(["profileCustomCSS"], async data => { 2058 if(!data.profileCustomCSS) { 2059 data.profileCustomCSS = {}; 2060 } 2061 data.profileCustomCSS[pageUser.id_str] = { 2062 css: cssTextArea.value, 2063 darkModeVars: darkModeVars.value, 2064 lightModeVars: lightModeVars.value 2065 }; 2066 chrome.storage.local.set({ profileCustomCSS: data.profileCustomCSS }); 2067 }); 2068 alert(LOC.css_set.message); 2069 } else { 2070 alert(res); 2071 } 2072 } catch(e) { 2073 console.error(e); 2074 alert(LOC.error_setting_css.message); 2075 } finally { 2076 cssSyncButton.disabled = false; 2077 } 2078 }); 2079 cssLoadButton.addEventListener('click', async () => { 2080 fetch(`https://dimden.dev/services/twitter_link_colors/v2/get_data/${pageUser.id_str}`).then(r => r.json()).then(data => { 2081 cssTextArea.value = data.css; 2082 darkModeVars.value = data.css_vars_dark; 2083 lightModeVars.value = data.css_vars_light; 2084 2085 if(data.css) { 2086 if(!customCSS) { 2087 customCSS = document.createElement('style'); 2088 customCSS.id = 'oldtwitter-custom-css'; 2089 document.head.appendChild(customCSS); 2090 } 2091 customCSS.innerHTML = data.css; 2092 } 2093 if(data.css_vars_dark && isDarkModeEnabled) { 2094 let vars = parseVariables(data.css_vars_dark); 2095 for(let i in vars) { 2096 r.style.setProperty(i, vars[i]); 2097 } 2098 } else if(data.css_vars_light && !isDarkModeEnabled) { 2099 let vars = parseVariables(data.css_vars_light); 2100 for(let i in vars) { 2101 r.style.setProperty(i, vars[i]); 2102 } 2103 } else { 2104 switchDarkMode(isDarkModeEnabled); 2105 } 2106 }); 2107 }); 2108 colorSyncButton.addEventListener('click', async () => { 2109 colorSyncButton.disabled = true; 2110 let color = profileLinkColor.value; 2111 if(color.startsWith('#')) color = color.slice(1); 2112 try { 2113 let res = await fetch(`https://dimden.dev/services/twitter_link_colors/v2/setcolor`, { 2114 method: 'POST', 2115 headers: { 2116 'Content-Type': 'application/json' 2117 }, 2118 body: JSON.stringify({ 2119 color, 2120 private_token: await getOtAuthToken() 2121 }) 2122 }).then(i => i.text()); 2123 if(res === 'auth_error') { 2124 chrome.storage.local.get(["otPrivateTokens"], data => { 2125 delete data.otPrivateTokens[pageUser.id_str]; 2126 chrome.storage.local.set({ otPrivateTokens: data.otPrivateTokens }); 2127 colorSyncButton.disabled = false; 2128 alert(LOC.invalid_auth_token.message); 2129 }); 2130 } else if(res === 'error') { 2131 alert(LOC.error_setting_color.message); 2132 } else { 2133 alert(LOC.link_color_set.message); 2134 chrome.storage.local.get(["linkColors"], async lc => { 2135 let linkColors = lc.linkColors || {}; 2136 linkColors[user.id_str] = color; 2137 chrome.storage.local.set({ linkColors }); 2138 }); 2139 } 2140 } catch(e) { 2141 console.error(e); 2142 alert(LOC.error_setting_color.message); 2143 } finally { 2144 colorSyncButton.disabled = false; 2145 } 2146 }); 2147 2148 // Run 2149 updateSubpage(); 2150 await updateUserData(); 2151 if(subpage !== 'following' && subpage !== 'followers' && subpage !== 'followers_you_follow' && subpage !== 'lists') updateTimeline(); 2152 else if(subpage === 'following') { 2153 renderFollowing(); 2154 } else if(subpage === 'followers') { 2155 renderFollowers(); 2156 } else if(subpage === 'followers_you_follow') { 2157 renderFollowersYouFollow(); 2158 } else if(subpage === 'lists') { 2159 renderLists(); 2160 } 2161 if(location.hash === "#dm") { 2162 setTimeout(() => { 2163 let event = new CustomEvent('messageUser', { detail: { id: `${user.id_str}-${pageUser.id_str}`, user: pageUser } }); 2164 document.dispatchEvent(event); 2165 location.hash = ""; 2166 }, 1000); 2167 } 2168 renderDiscovery(); 2169 renderTrends(true); 2170 setInterval(() => renderDiscovery(false), 60000 * 5); 2171 setInterval(() => renderTrends(true), 60000 * 5); 2172}, 50);