├── README.md
├── goodtwitter2.user.js
├── love.png
└── twitter2015.user.css
/README.md:
--------------------------------------------------------------------------------
1 | # Don't use this!! Use [OldTwitter](https://github.com/dimdenGD/OldTwitter) instead!!
2 | # Don't use this!! Use [OldTwitter](https://github.com/dimdenGD/OldTwitter) instead!!
3 | # Don't use this!! Use [OldTwitter](https://github.com/dimdenGD/OldTwitter) instead!!
4 |
5 | # Twitter2015
6 | Finally, a way to return old and great Twitter's look.
7 | 
8 |
9 | ## Installation
10 | 1) Install [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ru)
11 | 2) Click [here](https://github.com/dimdenGD/Twitter2015/raw/main/goodtwitter2.user.js) and install userscript
12 | 3) Install [Stylus](https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=en)
13 | 4) Click [here](https://github.com/dimdenGD/Twitter2015/raw/main/twitter2015.user.css) and install userstyle
14 | 5) Go to Twitter settings, GoodTwitter2 tab and set:
15 | * Legacy Profile Layout - ✅
16 | * Square Avatars - ✅
17 | * Left Sidebar Media - ✅
18 | * Use Custom Font - "sans-serif" (without quotes)
19 | * Rosetta Icons - ✅
20 | 6) Finally, good Twitter is back!
21 |
--------------------------------------------------------------------------------
/goodtwitter2.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name GoodTwitter 2 - Electric Boogaloo
3 | // @version 0.0.31.1
4 | // @description A try to make Twitter look good again
5 | // @author schwarzkatz
6 | // @license MIT
7 | // @match https://twitter.com/*
8 | // @exclude https://twitter.com/i/cards/*
9 | // @exclude https://twitter.com/i/release_notes
10 | // @exclude https://twitter.com/*/privacy
11 | // @exclude https://twitter.com/*/tos
12 | // @exclude https://twitter.com/account/access
13 | // @grant GM_deleteValue
14 | // @grant GM_getResourceText
15 | // @grant GM_getResourceURL
16 | // @grant GM_getValue
17 | // @grant GM_setValue
18 | // @grant GM_info
19 | // @grant GM_xmlhttpRequest
20 | // @connect api.twitter.com
21 | // @resource css https://github.com/Bl4Cc4t/GoodTwitter2/raw/master/twitter.gt2eb.style.css
22 | // @resource emojiRegex https://github.com/mathiasbynens/emoji-regex/raw/main/es2015/index.js
23 | // @resource pickrCss https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css
24 | // @require https://github.com/Bl4Cc4t/GoodTwitter2/raw/master/twitter.gt2eb.i18n.js
25 | // @require https://github.com/Bl4Cc4t/GoodTwitter2/raw/master/twitter.gt2eb.polyfills.js
26 | // @require https://code.jquery.com/jquery-3.5.1.min.js
27 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js
28 | // @require https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.es5.min.js
29 | // @updateURL https://github.com/Bl4Cc4t/GoodTwitter2/raw/master/twitter.gt2eb.user.js
30 | // @downloadURL https://github.com/Bl4Cc4t/GoodTwitter2/raw/master/twitter.gt2eb.user.js
31 | // ==/UserScript==
32 |
33 | (function($, waitForKeyElements) {
34 | "use strict"
35 |
36 | // do not execute on these pages
37 | if (getPath().match(/^login(\?.*)?$/) || (!isLoggedIn() && getPath().match(/^(\?.*)?$/))) {
38 | return
39 | }
40 |
41 |
42 |
43 | // ###########################
44 | // # convenience functions #
45 | // ###########################
46 |
47 |
48 | // seperate number with commas
49 | Number.prototype.humanize = function() {
50 | let t = this.toString().split("")
51 | let out = ""
52 | let c = 1
53 | for (let i=t.length-1; i>=0; i--) {
54 | out = `${t[i]}${out}`
55 | if (c++ % 3 == 0 && i-1 >= 0) {
56 | out = `,${out}`
57 | }
58 | }
59 | return out
60 | }
61 |
62 |
63 | // shorter version: 1.4M, 23.4K, etc
64 | Number.prototype.humanizeShort = function() {
65 | let t = this.toString()
66 | if (this >= 1000000) {
67 | t = t.slice(0, -5)
68 | return `${t.slice(0, -1)}${t.slice(-1) != 0 ? `.${t.slice(-1)}` : ""}M`
69 | } else if (this >= 10000) {
70 | t = t.slice(0, -2)
71 | return `${t.slice(0, -1)}${t.slice(-1) != 0 ? `.${t.slice(-1)}` : ""}K`
72 | } else return this.humanize()
73 | }
74 |
75 |
76 | // get kebab case (thisIsAString -> this-is-a-string)
77 | String.prototype.toKebab = function() {
78 | let out = ""
79 | for (let e of this.toString().split("")) {
80 | out += e == e.toUpperCase() ? `-${e.toLowerCase()}` : e
81 | }
82 | return out
83 | }
84 |
85 | String.prototype.replaceAt = function(index, length, text) {
86 | return `${[...this.toString()].slice(0, index).join("")}${text}${[...this.toString()].slice(index + length).join("")}`
87 | }
88 |
89 | String.prototype.insertAt = function(index, text) {
90 | return this.toString().replaceAt(index, 0, text)
91 | }
92 |
93 | const defaultAvatarUrl = "https://abs.twimg.com/sticky/default_profile_images/default_profile.png"
94 |
95 |
96 | // get account information
97 | function getInfo() {
98 | let sel = "#react-root ~ script"
99 | let infoScript = $(sel).text()
100 | function x(reg, defaultVal="") {
101 | let m = infoScript.match(reg)
102 | return m ? m[1] : defaultVal
103 | }
104 | return {
105 | bannerUrl: x(/profile_banner_url\":\"(.+?)\",/),
106 | avatarUrl: x(/profile_image_url_https\":\"(.+?)\",/, defaultAvatarUrl),
107 | screenName: x(/screen_name\":\"(.+?)\",/, "youarenotloggedin"),
108 | name: x(/name\":\"(.+?)\",/, "Anonymous"),
109 | id: x(/id_str\":\"(\d+)\"/, "0"),
110 | stats: {
111 | tweets: parseInt(x(/statuses_count\":(\d+),/, "0")),
112 | followers: parseInt(x(/\"followers_count\":(\d+),/, "0")),
113 | following: parseInt(x(/friends_count\":(\d+),/, "0")),
114 | }
115 | }
116 | }
117 |
118 |
119 | // get current display language
120 | function getLang() {
121 | return $("html").attr("lang").trim()
122 | }
123 |
124 |
125 | // check if the user is logged in
126 | function isLoggedIn() {
127 | return document.cookie.match(/ twid=/)
128 | }
129 |
130 |
131 | // get localized version of a string.
132 | // defaults to english version.
133 | function getLocStr(key) {
134 | let lang = getLang()
135 | lang = Object.keys(i18n).includes(lang) ? lang : "en"
136 | if (Object.keys(i18n[lang]).includes(key) && !i18n[lang][key].startsWith("*NEW*")) {
137 | return i18n[lang][key]
138 | } else {
139 | return i18n.en[key]
140 | }
141 | }
142 |
143 |
144 | // current path
145 | function getPath() {
146 | return window.location.href.replace(/.*?twitter\.com\//, "")
147 | }
148 |
149 |
150 | // svg convenience
151 | function getSvg(key) {
152 | let svgs = {
153 | lightning: ` `,
154 | caret: ` `,
155 | tick: ` `,
156 | moon: ` `,
157 | x: ` `,
158 | google: ` `,
159 | arrow: ` `,
160 | location: ` `,
161 | url: ` `,
162 | calendar: ` `,
163 | balloon: ` `,
164 | }
165 | return `
166 |
167 | ${svgs[key]}
168 | `
169 | }
170 |
171 |
172 | // request headers
173 | function getRequestHeaders(additionalHeaders) {
174 | // found in https://abs.twimg.com/responsive-web/web/main.5c0baa34.js
175 | let publicBearer = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
176 | let csrf = window.document.cookie.match(/ct0=([^;]+)(;|$)/)[1]
177 |
178 | let out = {
179 | authorization: `Bearer ${publicBearer}`,
180 | origin: "https://twitter.com",
181 | referer: window.location.href,
182 | "x-twitter-client-language": getLang(),
183 | "x-csrf-token": csrf,
184 | "x-twitter-active-user": "yes",
185 | // "x-twitter-auth-type": "OAuth2Session"
186 | }
187 | Object.assign(out, additionalHeaders)
188 | return out
189 | }
190 |
191 |
192 | function getRequestURL(base, param) {
193 | let out = base
194 | for (let [key, val] of Object.entries(param)) {
195 | if (typeof val === "object") val = encodeURIComponent(JSON.stringify(val))
196 | out += `&${key}=${val}`
197 | }
198 | return `${out.replace("&", "?")}`
199 | }
200 |
201 |
202 | function requestTweet(id, cb) {
203 | GM_xmlhttpRequest({
204 | method: "GET",
205 | url: getRequestURL("https://twitter.com/i/api/1.1/statuses/show.json", {
206 | id,
207 | tweet_mode: "extended",
208 | trim_user: true,
209 | include_cards: 1
210 | }),
211 | headers: getRequestHeaders(),
212 | onload: function(res) {
213 | if (res.status == "200") {
214 | cb(JSON.parse(res.response))
215 | } else {
216 | console.warn(res)
217 | }
218 | }
219 | })
220 | }
221 |
222 |
223 | function requestUser(screenName, cb) {
224 | GM_xmlhttpRequest({
225 | method: "GET",
226 | url: getRequestURL(`https://twitter.com/i/api/graphql/jMaTS-_Ea8vh9rpKggJbCQ/UserByScreenName`, {
227 | variables: {
228 | screen_name: screenName,
229 | withHighlightedLabel: true
230 | }
231 | }),
232 | headers: getRequestHeaders(),
233 | onload: function(res) {
234 | if (res.status == "200") {
235 | cb(JSON.parse(res.response))
236 | } else {
237 | console.warn(res)
238 | }
239 | }
240 | })
241 | }
242 |
243 |
244 | function blockUser(user_id, block, cb) {
245 | GM_xmlhttpRequest({
246 | method: "POST",
247 | url: getRequestURL(`https://api.twitter.com/1.1/blocks/${block ? "create" : "destroy"}.json`, {
248 | user_id,
249 | skip_status: true
250 | }),
251 | headers: getRequestHeaders(),
252 | onload: function(res) {
253 | if (res.status == "200") {
254 | cb()
255 | } else {
256 | console.warn(res)
257 | }
258 | }
259 | })
260 | }
261 |
262 |
263 | // adds links from an entities object to a text
264 | String.prototype.populateWithEntities = function(entities) {
265 | let text = this.toString()
266 | let out = text
267 |
268 | let toReplace = []
269 |
270 | // urls
271 | if (entities.urls) {
272 | for (let url of entities.urls) {
273 | toReplace.push({
274 | [url.indices[0]]: `${url.display_url} `
276 | })
277 | }
278 | }
279 |
280 | // users
281 | if (entities.user_mentions) {
282 | for (let user of entities.user_mentions) {
283 | let x = text.slice(user.indices[0], user.indices[0]+1) == "@" ? 0 : 1
284 | toReplace.push({
285 | [user.indices[0]+x]: ``,
286 | [user.indices[1]+x]: ` `
287 | })
288 | }
289 | }
290 |
291 | // hashtags
292 | if (entities.hashtags) {
293 | for (let hashtag of entities.hashtags) {
294 | let x = text.slice(hashtag.indices[0], hashtag.indices[0]+1) == "#" ? 0 : 1
295 | toReplace.push({
296 | [hashtag.indices[0]+x]: ``,
297 | [hashtag.indices[1]+x]: ` `
298 | })
299 | }
300 | }
301 |
302 | // sort array
303 | toReplace = toReplace.sort((a, b) => parseInt(Object.keys(a)[0]) - parseInt(Object.keys(b)[0]))
304 |
305 | // replace values
306 | let offset = 0
307 | for (let e of toReplace) {
308 | for (let [index, value] of Object.entries(e)) {
309 | out = out.insertAt(parseInt(index) + offset, value)
310 | offset += value.length
311 | }
312 | }
313 |
314 | if (GM_getValue("opt_gt2").expandTcoShortlinks) {
315 | let re = /href="(https:\/\/t\.co\/[^"]+)"/
316 | let match
317 | while ((match = re.exec(out)) != null) {
318 | out = out.replace(new RegExp(`href="${match[1]}"`), `href="${entities.urls.find(e => e.url == match[1]).expanded_url}"`)
319 | }
320 | }
321 |
322 | return out
323 | }
324 |
325 |
326 | // replace emojis with the twitter svgs
327 | String.prototype.replaceEmojis = function() {
328 | let text = this.toString()
329 |
330 | let out = text
331 | let re = new RegExp(`(${GM_getResourceText("emojiRegex").match(/return \/(.*)\/gu/)[1]})`, "gu")
332 | let match
333 | let offset = 0
334 | while ((match = re.exec(text)) != null) {
335 | let e = match[1]
336 | // get unicode of emoji
337 | let uni = e.codePointAt(0).toString(16)
338 | if (e.length == 4) {
339 | uni += `-${e.codePointAt(2).toString(16)}`
340 | }
341 |
342 | // replace with image
343 | let img = ` `
344 | out = out.replaceAt(match.index + offset, e.length, img)
345 |
346 | offset += img.length - e.length
347 | }
348 |
349 | return out
350 | }
351 |
352 |
353 |
354 | // ###################
355 | // # GT2 settings #
356 | // ###################
357 |
358 |
359 | // custom options and their default values
360 | const opt_gt2 = {
361 | forceLatest: false,
362 | disableAutoRefresh: false,
363 | keepTweetsInTL: true,
364 | biggerPreviews: true,
365 |
366 | hideTranslateTweetButton: false,
367 | tweetIconsPullLeft: false,
368 |
369 | stickySidebars: true,
370 | smallSidebars: false,
371 | hideTrends: false,
372 | leftTrends: true,
373 | show10Trends: false,
374 |
375 | legacyProfile: true,
376 | squareAvatars: true,
377 | enableQuickBlock: false,
378 | leftMedia: false,
379 |
380 | hideFollowSuggestions: false,
381 | hideFollowSuggestionsSel: 7,
382 | fontOverride: true,
383 | fontOverrideValue: "sans-serif",
384 | colorOverride: false,
385 | colorOverrideValue: "85, 102, 68",
386 | hideMessageBox: true,
387 | rosettaIcons: true,
388 | favoriteLikes: false,
389 |
390 | updateNotifications: false,
391 | expandTcoShortlinks: true,
392 | }
393 |
394 | // set default options
395 | if (GM_getValue("opt_gt2") == undefined) GM_setValue("opt_gt2", opt_gt2)
396 |
397 | // add previously non existant options
398 | if (JSON.stringify(Object.keys(GM_getValue("opt_gt2"))) != JSON.stringify(Object.keys(opt_gt2))) {
399 | let old = GM_getValue("opt_gt2")
400 |
401 | // remove default options that are modified
402 | for (let k of Object.keys(opt_gt2)) {
403 | if (Object.keys(old).includes(k)) delete opt_gt2[k]
404 | }
405 |
406 | // remove old options
407 | for (let k of Object.keys(old)) {
408 | if (Object.keys(opt_gt2).includes(k)) delete old[k]
409 | }
410 |
411 | Object.assign(old, opt_gt2)
412 | GM_setValue("opt_gt2", old)
413 | }
414 |
415 |
416 | // toggle opt_gt2 value
417 | function toggleGt2Opt(key) {
418 | let x = GM_getValue("opt_gt2")
419 | x[key] = !x[key]
420 | GM_setValue("opt_gt2", x)
421 | }
422 |
423 |
424 | // insert the menu item
425 | function addSettingsToggle() {
426 | if (!$(".gt2-toggle-settings").length) {
427 | $(`main section[aria-labelledby=root-header] div[role=tablist],
428 | main > div > div > div > div:last-child > div[role=tablist],
429 | main div[data-testid=loggedOutPrivacySection]`).append(`
430 |
431 |
432 | GoodTwitter2
433 | ${getSvg("caret")}
434 |
435 |
436 | `)
437 | }
438 | }
439 |
440 |
441 | // toggle settings display
442 | $("body").on("click", ".gt2-toggle-settings", function(event) {
443 | event.preventDefault()
444 | window.history.pushState({}, "", $(this).attr("href"))
445 | addSettings()
446 | changeSettingsTitle()
447 | })
448 |
449 |
450 | // disable settings display again when clicking on another menu item
451 | $("body").on("click", `main section[aria-labelledby=root-header] div[role=tablist] a:not(.gt2-toggle-settings),
452 | main section[aria-labelledby=root-header] div[data-testid=loggedOutPrivacySection] a:not(.gt2-toggle-settings)`, () => {
453 | $(".gt2-settings-header, .gt2-settings").remove()
454 | })
455 |
456 |
457 | // get html for a gt2 toggle (checkbox)
458 | function getSettingTogglePart(name, additionalHTML="") {
459 | let d = `${name}Desc`
460 | return `
461 |
462 |
463 |
${getLocStr(name)}
464 |
465 |
466 |
467 | ${getSvg("tick")}
468 |
469 |
470 |
471 | ${additionalHTML}
472 | ${getLocStr(d) ? `
${getLocStr(d)} ` : ""}
473 |
`
474 | }
475 |
476 |
477 | // add the settings to the display (does not yet work on screens smaller than 1050px)
478 | function addSettings() {
479 | if (!$(".gt2-settings").length) {
480 | let elem = `
481 |
488 |
489 |
490 | ${getSettingTogglePart("forceLatest")}
491 | ${getSettingTogglePart("disableAutoRefresh")}
492 | ${getSettingTogglePart("keepTweetsInTL")}
493 | ${getSettingTogglePart("biggerPreviews")}
494 |
495 |
496 |
497 | ${getSettingTogglePart("hideTranslateTweetButton")}
498 | ${getSettingTogglePart("tweetIconsPullLeft")}
499 |
500 |
501 |
502 | ${getSettingTogglePart("stickySidebars")}
503 | ${getSettingTogglePart("smallSidebars")}
504 | ${getSettingTogglePart("hideTrends")}
505 | ${getSettingTogglePart("leftTrends")}
506 | ${getSettingTogglePart("show10Trends")}
507 |
508 |
509 |
510 | ${getSettingTogglePart("legacyProfile")}
511 | ${getSettingTogglePart("squareAvatars")}
512 | ${getSettingTogglePart("enableQuickBlock")}
513 | ${getSettingTogglePart("leftMedia")}
514 |
515 |
516 |
517 | ${getSettingTogglePart("hideFollowSuggestions", `
518 |
519 | ${["topics", "users", "navLists"].map((e, i) => {
520 | let x = Math.pow(2, i)
521 | return `
522 |
${getLocStr(e)}
523 |
524 |
525 |
${getSvg("tick")}
526 |
527 |
528 | `}).join("")}
529 |
530 | `)}
531 | ${getSettingTogglePart("fontOverride", `
532 |
533 |
534 |
535 | `)}
536 | ${getSettingTogglePart("colorOverride", `
`)}
537 | ${getSettingTogglePart("hideMessageBox")}
538 | ${getSettingTogglePart("rosettaIcons")}
539 | ${getSettingTogglePart("favoriteLikes")}
540 |
541 |
542 |
543 | ${getSettingTogglePart("updateNotifications")}
544 | ${getSettingTogglePart("expandTcoShortlinks")}
545 |
546 | `
547 | let $s = $("main section[aria-labelledby=detail-header]")
548 | if ($s.length) {
549 | $s.prepend(elem)
550 | } else {
551 | $("main > div > div > div").append(`
552 |
553 | `)
554 | }
555 | // add color pickr
556 | Pickr.create({
557 | el: ".gt2-pickr",
558 | theme: "classic",
559 | lockOpacity: true,
560 | useAsButton: true,
561 | appClass: "gt2-color-override-pickr",
562 | inline: true,
563 | default: `rgb(${GM_getValue("opt_gt2").colorOverrideValue})`,
564 | components: {
565 | preview: true,
566 | hue: true,
567 | interaction: {
568 | hex: true,
569 | rgba: true,
570 | hsla: true,
571 | hsva: true,
572 | cmyk: true,
573 | input: true
574 | }
575 | }
576 | })
577 | .on("change", e => {
578 | let val = e.toRGBA().toString(0).slice(5, -4)
579 | GM_setValue("opt_gt2", Object.assign(GM_getValue("opt_gt2"), { colorOverrideValue: val}))
580 | document.documentElement.style.setProperty("--color-override", val)
581 | })
582 | disableTogglesIfNeeded()
583 | }
584 | }
585 |
586 |
587 | // change the title to display GoodTwitter2
588 | function changeSettingsTitle() {
589 | let t = $("title").html()
590 | $("title").html(`${t.startsWith("(") ? `${t.split(" ")[0]} ` : ""}GoodTwitter2 / Twitter`)
591 | }
592 |
593 |
594 | // handler for the toggles
595 | $("body").on("click", ".gt2-setting-toggle:not(.gt2-disabled)", function() {
596 | $(this).toggleClass("gt2-active")
597 | if ($(this).is("[data-setting-name]")) {
598 | let name = $(this).attr("data-setting-name").trim()
599 | toggleGt2Opt(name)
600 | $("body").toggleClass(`gt2-opt-${name.toKebab()}`)
601 | }
602 |
603 | // hide follow suggestions
604 | if ($(this).is("[data-hfs-type]")) {
605 | let opt = GM_getValue("opt_gt2")
606 | GM_setValue("opt_gt2", Object.assign(opt, { ["hideFollowSuggestionsSel"]: opt.hideFollowSuggestionsSel ^ parseInt($(this).attr("data-hfs-type")) }))
607 | }
608 | disableTogglesIfNeeded()
609 | })
610 |
611 | // handler for inputs
612 | $("body").on("keyup", ".gt2-setting-input input", function() {
613 | let name = $(this).parent().attr("data-setting-name").trim()
614 | let val = $(this).val().trim()
615 |
616 | GM_setValue("opt_gt2", Object.assign(GM_getValue("opt_gt2"), { [name]: val}))
617 | document.documentElement.style.setProperty(`--${name.replace("Value", "").toKebab()}`, val)
618 | })
619 |
620 |
621 | function disableTogglesIfNeeded() {
622 | // when autoRefresh is on, keepTweetsInTL must also be on and can not be deactivated (it is disabled)
623 | let $t = $("div[data-setting-name=keepTweetsInTL]")
624 | if (GM_getValue("opt_gt2").disableAutoRefresh) {
625 | if (!GM_getValue("opt_gt2").keepTweetsInTL) {
626 | $t.click()
627 | }
628 | $t.addClass("gt2-disabled")
629 | } else {
630 | $t.removeClass("gt2-disabled")
631 | }
632 |
633 | // other trend related toggles are not needed when the trends are disabled
634 | $("div[data-setting-name=leftTrends], div[data-setting-name=show10Trends]")
635 | [GM_getValue("opt_gt2").hideTrends ? "addClass" : "removeClass"]("gt2-disabled")
636 |
637 | // hide font input if fontOverride is disabled
638 | $("[data-setting-name=fontOverrideValue]")
639 | [GM_getValue("opt_gt2").fontOverride ? "removeClass" : "addClass"]("gt2-hidden")
640 |
641 | // hide color input if colorOverride is disabled
642 | $(".gt2-color-override-pickr")
643 | [GM_getValue("opt_gt2").colorOverride ? "removeClass" : "addClass"]("gt2-hidden")
644 |
645 | // hide follow suggestions
646 | $("[data-setting-name=hideFollowSuggestionsSel]")
647 | [GM_getValue("opt_gt2").hideFollowSuggestions ? "removeClass" : "addClass"]("gt2-hidden")
648 | }
649 |
650 |
651 | // click on the back button
652 | $("body").on("click", ".gt2-settings-back", () => window.history.back())
653 |
654 |
655 |
656 | // #######################
657 | // # various functions #
658 | // #######################
659 |
660 |
661 | // add navbar
662 | function addNavbar() {
663 | waitForKeyElements(`nav > a[href="/home"]`, () => {
664 | if ($(".gt2-nav").length) return
665 |
666 | $("main").before(`
667 |
668 |
669 |
672 |
673 |
674 |
675 |
676 |
677 |
${getLocStr("composeNewTweet")}
678 |
679 |
680 |
681 | `)
682 |
683 | // home, notifications, messages
684 | for (let e of [
685 | "Home",
686 | "Notifications",
687 | "Messages",
688 | window.innerWidth < 1005 ? "Explore" : null
689 | ]) {
690 | if (!e) continue
691 | let $e = $(`nav > a[href="/${e.toLowerCase()}"]`)
692 | if (!e.length) continue
693 | $e.appendTo(".gt2-nav-left")
694 | $(`.gt2-nav a[href="/${e.toLowerCase()}"] > div`)
695 | .append(`
696 |
699 | `)
700 | .attr("data-gt2-color-override-ignore", "")
701 | }
702 |
703 | // highlight current location
704 | $(`.gt2-nav a[href^='/${getPath().split("/")[0]}']`).addClass("active")
705 |
706 | // twitter logo
707 | $("h1 a[href='/home'] svg")
708 | .appendTo(".gt2-nav-center a")
709 | })
710 | }
711 |
712 | // add navbar
713 | function addNavbarLoggedOut() {
714 | waitForKeyElements("nav > a[data-testid=AppTabBar_Explore_Link]", () => {
715 | if ($(".gt2-nav").length) return
716 |
717 | $("body").prepend(`
718 |
719 |
720 |
723 |
726 |
727 |
728 | `)
729 |
730 | // explore and settings
731 | $(`nav > a[data-testid=AppTabBar_Explore_Link],
732 | nav > a[href="/settings"]`)
733 | .appendTo(".gt2-nav-left")
734 | $(`.gt2-nav a[data-testid=AppTabBar_Explore_Link] > div`)
735 | .append(`
736 |
739 | `)
740 | $(`.gt2-nav a[href="/settings"] > div`)
741 | .append(`
742 |
745 | `)
746 |
747 | // highlight current location
748 | $(`.gt2-nav a[href^='/${getPath().split("/")[0]}']`).addClass("active")
749 |
750 | // twitter logo
751 | $("header h1 a[href='/'] svg")
752 | .appendTo(".gt2-nav-center a")
753 | })
754 | }
755 |
756 |
757 | // add search
758 | function addSearch() {
759 | let search = "div[data-testid=sidebarColumn] > div > div:nth-child(2) > div > div > div > div:nth-child(1)"
760 | waitForKeyElements(`${search} input[data-testid=SearchBox_Search_Input]`, () => {
761 | // remove if added previously
762 | $(".gt2-search").empty()
763 | // add search
764 | $(search)
765 | .prependTo(".gt2-search")
766 | $("body").addClass("gt2-search-added")
767 | })
768 | }
769 |
770 |
771 | // add element to sidebar
772 | function addToSidebar(elements) {
773 | let w = window.innerWidth
774 | let insertAt = ".gt2-left-sidebar"
775 |
776 | // insert into the right sidebar
777 | if ((!GM_getValue("opt_gt2").smallSidebars && w <= 1350) ||
778 | ( GM_getValue("opt_gt2").smallSidebars && w <= 1230)) {
779 | insertAt = "div[data-testid=sidebarColumn] > div > div:nth-child(2) > div > div > div"
780 | }
781 |
782 | elements.unshift(`
`)
783 | waitForKeyElements(insertAt, () => {
784 | if (!$(insertAt).find(".gt2-legacy-profile-info").length) {
785 | for (let elem of elements.slice().reverse()) {
786 | if (insertAt.startsWith(".gt2")) {
787 | $(insertAt).prepend(elem)
788 | } else {
789 | $(`${insertAt} > div:empty:not(.gt2-legacy-profile-info)`).after(elem)
790 | }
791 | }
792 | }
793 | if ($(".gt2-dashboard-profile").length > 1) {
794 | $(".gt2-dashboard-profile").last().remove()
795 | }
796 |
797 | })
798 | }
799 |
800 |
801 | // profile view left sidebar
802 | function getDashboardProfile() {
803 | let i = getInfo()
804 | // console.log(`userInformation:\n${JSON.stringify(i, null, 2)}`)
805 | let href = isLoggedIn() ? "href" : "data-href"
806 | return `
807 |
808 |
809 |
810 |
811 |
812 |
813 |
819 |
820 |
821 | ${getSvg(isLoggedIn() ? "caret" : "moon")}
822 |
823 |
845 |
846 |
847 | `
848 | }
849 |
850 |
851 | // gt2 update notice
852 | function getUpdateNotice() {
853 | let v = GM_info.script.version
854 | return `
855 |
872 | `
873 | }
874 |
875 |
876 | // recreate the legacy profile layout
877 | function rebuildLegacyProfile() {
878 | let currentScreenName = getPath().split("/")[0].split("?")[0].split("#")[0]
879 | console.log(`rebuild: ${currentScreenName}`)
880 |
881 |
882 | let profileSel = "div[data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(1) > div:nth-child(2)"
883 |
884 | waitForKeyElements(`div[data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(1)`, () => {
885 | // observe changes
886 | let lplMut = new MutationObserver(mut => {
887 | mut.forEach(m => {
888 | console.log(m.target)
889 | })
890 | }).observe($(`div[data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(1)`)[0], {
891 | childList: true,
892 | subtree: true
893 | })
894 | })
895 |
896 | waitForKeyElements(`a[href='/${currentScreenName}/photo' i] img`, () => {
897 | // remove previously added profile
898 | if ($(".gt2-legacy-profile-nav").length) {
899 | $(".gt2-legacy-profile-banner, .gt2-legacy-profile-nav").remove()
900 | $(".gt2-legacy-profile-info").empty()
901 | }
902 |
903 |
904 | let $profile = $(profileSel)
905 |
906 | // information (constant)
907 | const i = {
908 | $banner: $("a[href$='/header_photo'] img"),
909 | avatarUrl: $("a[href$='/photo'] img"),
910 | screenName: $profile.find("> div:nth-child(2) > div > div > div:nth-child(2) > div:nth-child(1) > span").text().slice(1),
911 | followsYou: $profile.find("> div:nth-child(2) > div > div > div:nth-child(2) > div:nth-child(2)"),
912 | nameHTML: $profile.find("> div:nth-child(2) > div > div > div:nth-child(1) > div").html(),
913 | joinDateHTML: $profile.find("div[data-testid=UserProfileHeader_Items] > span:last-child").html(),
914 | followingRnd: $profile.find(`a[href$="/following"] > span:first-child, > div:not(:first-child) div:nth-child(1) > [role=button]:first-child:last-child > span:first-child`).first().text().trim(),
915 | followersRnd: $profile.find(`a[href$="/followers"] > span:first-child, > div:not(:first-child) div:nth-child(2) > [role=button]:first-child:last-child > span:first-child`).first().text().trim(),
916 | screenNameOnly: false
917 | }
918 |
919 | if (i.screenName == "") {
920 | i.screenNameOnly = true
921 | i.screenName = $(i.nameHTML).text().trim().slice(1)
922 | }
923 |
924 |
925 | if (!$(".gt2-legacy-profile-banner").length) {
926 | $("header").before(`
927 |
928 | ${i.$banner.length ? `
` : ""}
929 |
930 |
931 |
932 |
933 |
934 |
${i.nameHTML}
935 |
936 | ${i.screenNameOnly ? "" : `
937 |
938 | @${i.screenName}
939 |
940 | `}
941 | ${i.followsYou.length ? i.followsYou.prop("outerHTML") : ""}
942 |
943 |
944 |
945 |
973 |
974 |
975 | `)
976 | }
977 |
978 | // add like and tweet count
979 | requestUser(i.screenName, res => {
980 | let profileData = res.data.user
981 | let pleg = profileData.legacy
982 |
983 | // profile id
984 | $(".gt2-legacy-profile-info").attr("data-profile-id", profileData.rest_id)
985 |
986 | // change stats
987 | for (let tmp of [
988 | [i.screenName, "statuses_count"],
989 | ["following", "friends_count"],
990 | ["followers", "followers_count"],
991 | ["likes", "favourites_count"]
992 | ]) {
993 | $(`.gt2-legacy-profile-nav-center a[href$="/${tmp[0]}"]`)
994 | .attr("title", pleg[tmp[1]].humanize())
995 | .find("div:nth-child(2)").html(pleg[tmp[1]].humanizeShort())
996 | }
997 |
998 | // expand t.co links
999 | if (GM_getValue("opt_gt2").expandTcoShortlinks) {
1000 | let urls = pleg.entities.description.urls.concat(pleg.entities.url ? pleg.entities.url.urls : [])
1001 | $(`.gt2-legacy-profile-info a[href^="https://t.co"]`).each(function() {
1002 | $(this).attr("href", urls.find(e => e.url == $(this).attr("href").split("?")[0]).expanded_url)
1003 | })
1004 | }
1005 | })
1006 |
1007 | // sidebar profile information
1008 | waitForKeyElements(`[href="/${getPath().split("/")[0].split("?")[0].split("#")[0]}/following" i]`, () => {
1009 | $(".gt2-legacy-profile-info").data("alreadyFound", false)
1010 | waitForKeyElements(".gt2-legacy-profile-info", () => {
1011 | if (!$(".gt2-legacy-profile-info .gt2-legacy-profile-name").length) {
1012 | // elements
1013 | let e = {
1014 | $description: $profile.find("div[data-testid=UserDescription]"),
1015 | $items: $profile.find("div[data-testid=UserProfileHeader_Items]"),
1016 | $fyk: $profile.find("> div:last-child:not(:nth-child(2)) > div:last-child:first-child")
1017 | }
1018 | i.screenName = $profile.find("> div:nth-child(2) > div > div > div:nth-child(2) > div:nth-child(1) > span").text().slice(1)
1019 | i.followsYou = $profile.find("> div:nth-child(2) > div > div > div:nth-child(2) > div:nth-child(2)")
1020 | i.nameHTML = $profile.find("> div:nth-child(2) > div > div > div:nth-child(1) > div").html()
1021 | if (i.screenName == "") {
1022 | i.screenNameOnly = true
1023 | i.screenName = $(i.nameHTML).text().trim().slice(1)
1024 | }
1025 |
1026 | $(".gt2-legacy-profile-info").append(`
1027 | ${i.nameHTML}
1028 |
1029 | ${i.screenNameOnly ? "" : `
1030 |
1031 | @${i.screenName}
1032 |
1033 | `}
1034 | ${i.followsYou.length ? i.followsYou.prop("outerHTML") : ""}
1035 |
1036 | ${e.$description.length ? `${e.$description.parent().html()}
` : ""}
1037 | ${e.$items.length ? e.$items.html() : ""}
1038 | ${e.$fyk.length ? `${e.$fyk.prop("outerHTML")}
` : ""}
1039 | `)
1040 |
1041 | GM_setValue("hasRun_InsertFYK", false)
1042 | waitForKeyElements(`a[href$="/followers_you_follow"] img`, e => {
1043 | if (!GM_getValue("hasRun_InsertFYK")) {
1044 | $(".gt2-legacy-profile-fyk").html($(e).parents(`a[href$="/followers_you_follow"]`).prop("outerHTML"))
1045 | GM_setValue("hasRun_InsertFYK", true)
1046 | }
1047 | })
1048 | }
1049 | })
1050 | })
1051 |
1052 | // buttons
1053 | if (!$(".gt2-legacy-profile-nav-right > div").length) {
1054 | $profile.find("> div:nth-child(1) > div").detach().appendTo(".gt2-legacy-profile-nav-right")
1055 | }
1056 |
1057 | })
1058 |
1059 | // profile suspended / not found / temporarily restricted (first view)
1060 | waitForKeyElements([
1061 | `[data-testid=emptyState] > div:nth-child(2) > *:not(a)`, // not found
1062 | `[data-testid=emptyState] [href="https://support.twitter.com/articles/18311"]`, // suspended
1063 | `[data-testid=emptyState] [href="https://support.twitter.com/articles/20169222"]`, // withheld in country
1064 | `[data-testid=UserDescription] [href="https://support.twitter.com/articles/20169199"]` // temporarily unavailable (Media Policy Violation)
1065 | ].join(", "), () => {
1066 | let $tmp = $(profileSel).find("> div:nth-child(2) > div > div")
1067 | let i = {
1068 | screenName: $tmp.find("> div:nth-last-child(1)").text().trim().slice(1),
1069 | nameHTML: $tmp.find("> div").length > 1 ? $tmp.find("> div:nth-child(1)").html() : null,
1070 | avatarUrl: defaultAvatarUrl
1071 | }
1072 | $("body").addClass("gt2-profile-not-found")
1073 | $("header").before(`
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 |
1090 |
1091 |
1109 |
1110 |
1111 | `)
1112 | waitForKeyElements(".gt2-legacy-profile-info", () => {
1113 | $(".gt2-legacy-profile-info").append(`
1114 | ${i.nameHTML ? i.nameHTML : `@${i.screenName}`}
1115 | ${i.nameHTML ? `
1116 |
1121 | ` : ""}
1122 | `)
1123 | })
1124 | })
1125 | }
1126 |
1127 |
1128 | // force latest tweets view.
1129 | function forceLatest() {
1130 | let sparkOptToggle = "div[data-testid=primaryColumn] > div > div:nth-child(1) > div:nth-child(1) > div > div > div > div > div:nth-child(2) div[aria-haspopup]"
1131 | let sparkOpt = "#react-root h2 + div > div:nth-child(2) > div > div > div > div:nth-child(2) > div:nth-child(3)"
1132 |
1133 | GM_setValue("hasRun_forceLatest", false)
1134 | waitForKeyElements(sparkOptToggle, () => {
1135 | if (!GM_getValue("hasRun_forceLatest")) {
1136 | $(sparkOptToggle).click()
1137 | $("body").addClass("gt2-hide-spark-opt")
1138 | }
1139 |
1140 | waitForKeyElements(`${sparkOpt} a[href='/settings/content_preferences']`, () => {
1141 | if (!GM_getValue("hasRun_forceLatest")) {
1142 | GM_setValue("hasRun_forceLatest", true)
1143 | if ($(sparkOpt).find("> div:nth-child(1) path").length == 3) {
1144 | $(sparkOpt).children().eq(1).click()
1145 | } else {
1146 | $(sparkOptToggle).click()
1147 | }
1148 | $("body").removeClass("gt2-hide-spark-opt")
1149 | }
1150 | })
1151 | })
1152 | }
1153 |
1154 |
1155 | // handle trends (wrap, move and show10)
1156 | function handleTrends() {
1157 | let w = window.innerWidth
1158 | let trends = `div[data-testid=trend]:not(.gt2-trend-wrapped),
1159 | section[aria-labelledby^=accessible-list] a[href="/explore/tabs/for-you"] > div > span:not(.gt2-trend-wrapped)`
1160 |
1161 | waitForKeyElements(trends, () => {
1162 |
1163 | // actions for the whole container
1164 | if (!$(trends).parents("section").hasClass("gt2-trends-handled")
1165 | && $(trends).parents("div[data-testid=sidebarColumn]").length
1166 | ) {
1167 | $(trends).parents("section").addClass("gt2-trends-handled")
1168 |
1169 | // hide trends
1170 | if (GM_getValue("opt_gt2").hideTrends) {
1171 | $(trends).parents("section").parent().parent().parent().remove()
1172 | return
1173 | }
1174 |
1175 | // move trends
1176 | if (GM_getValue("opt_gt2").leftTrends
1177 | && ((!GM_getValue("opt_gt2").smallSidebars && w > 1350)
1178 | || (GM_getValue("opt_gt2").smallSidebars && w > 1230))) {
1179 | if ($(".gt2-trends").length) $(".gt2-trends").remove()
1180 |
1181 | $(trends).parents("section").parent().parent().parent()
1182 | .detach().addClass("gt2-trends")
1183 | .appendTo(".gt2-left-sidebar")
1184 | }
1185 |
1186 | // show 10 trends
1187 | if (GM_getValue("opt_gt2").show10Trends) {
1188 | if ($(trends).parent().parent().find("> div").length == 7) {
1189 | $(trends).parent().parent().find("> div[role=button]").click()
1190 | }
1191 | }
1192 | }
1193 |
1194 | // wrap trends in anchors
1195 | $(trends).each(function() {
1196 | let $toWrap = $(this).find("> div > div:nth-child(2) > span[dir]")
1197 | if ($toWrap.length) {
1198 | $(this).addClass("gt2-trend-wrapped")
1199 | let txt = $toWrap.text()
1200 | let query = encodeURIComponent($toWrap.text().replace(/%/g, "%25"))
1201 | .replace(/'/g, "%27")
1202 | .replace(/(^\"|\"$)/g, "")
1203 |
1204 | $toWrap.html(`${txt} `)
1205 | }
1206 | })
1207 | })
1208 | }
1209 |
1210 |
1211 | function getFollowersYouKnowHTML(screenName, profileID, callback) {
1212 | GM_xmlhttpRequest({
1213 | method: "GET",
1214 | url: getRequestURL("https://twitter.com/i/api/1.1/friends/following/list.json", {
1215 | include_profile_interstitial_type: 1,
1216 | include_blocking: 1,
1217 | include_blocked_by: 1,
1218 | include_followed_by: 1,
1219 | include_want_retweets: 1,
1220 | include_mute_edge: 1,
1221 | include_can_dm: 1,
1222 | include_can_media_tag: 1,
1223 | skip_status: 1,
1224 | cursor: -1,
1225 | user_id: profileID,
1226 | count: 3,
1227 | with_total_count: true
1228 | }),
1229 | headers: getRequestHeaders(),
1230 | onload: res => {
1231 | if (res.status == 200) {
1232 |
1233 | // followers you know
1234 | let fyk = JSON.parse(res.response)
1235 |
1236 | let fykText
1237 | if (fyk.total_count < 4) {
1238 | fykText = getLocStr(`followedBy${fyk.total_count}`)
1239 | .replace("$p1$", fyk.users.length > 0 ? fyk.users[0].name : "")
1240 | .replace("$p2$", fyk.users.length > 1 ? fyk.users[1].name : "")
1241 | .replace("$p3$", fyk.users.length > 2 ? fyk.users[2].name : "")
1242 | } else {
1243 | fykText = getLocStr("followedBy4Plus")
1244 | .replace("$p1$", fyk.users[0].name)
1245 | .replace("$p2$", fyk.users[1].name)
1246 | .replace("$nr$", fyk.total_count - 2)
1247 | }
1248 |
1249 | let fykImg = ""
1250 | for (let u of fyk.users) {
1251 | fykImg += ` `
1252 | }
1253 |
1254 | callback(`
1255 |
1256 | ${fykImg}
1257 |
1258 | ${fykText.replaceEmojis()}
1259 |
1260 |
1261 | `)
1262 | } else if (res.status == 401) {
1263 | callback("")
1264 | }
1265 | }
1266 | })
1267 | }
1268 |
1269 | // display standard information for blocked profile
1270 | function displayBlockedProfileData() {
1271 | let screenName = getPath().split("/")[0].split("?")[0].split("#")[0]
1272 |
1273 | requestUser(screenName, res => {
1274 | let profileData = res.data.user
1275 |
1276 | // get “x persons you follow follow this account” stuff
1277 | getFollowersYouKnowHTML(screenName, profileData.rest_id, fykHTML => {
1278 |
1279 | let pleg = profileData.legacy
1280 |
1281 | // join date
1282 | let joinDate = new Date(pleg.created_at)
1283 |
1284 | let p = {
1285 | description: pleg.description
1286 | .populateWithEntities(pleg.entities.description)
1287 | .replaceEmojis(),
1288 | location: pleg.location != "" ? `
1289 |
1290 | ${getSvg("location")}
1291 | ${pleg.location.replaceEmojis()}
1292 |
` : null,
1293 | url: pleg.url ? `
1294 |
1295 | ${getSvg("url")}
1296 | ${pleg.entities.url.urls[0].display_url}
1297 | ` : null,
1298 | joinDate: `
1299 | ${getSvg("calendar")}
1300 |
1301 | ${
1302 | getLocStr("joinDate")
1303 | .replace("$date$", joinDate.toLocaleDateString(getLang(), { month: "long", year: "numeric" }))
1304 | }
1305 |
1306 |
`,
1307 | birthday: profileData.legacy_extended_profile && profileData.legacy_extended_profile.birthdate ? (() => {
1308 | let bd = profileData.legacy_extended_profile.birthdate
1309 | let bdText
1310 | let date = new Date(Date.UTC(bd.year || 1970, bd.month || 1, bd.day || 1))
1311 | if (bd.year && !bd.month && !bd.day) {
1312 | bdText = getLocStr("bornYear").replace("$year$", date.toLocaleDateString(getLang(), { year: "numeric"}))
1313 | } else {
1314 | let opt = {}
1315 | if (bd.year) opt.year = "numeric"
1316 | if (bd.month) opt.month = "long"
1317 | if (bd.day) opt.day = "numeric"
1318 | bdText = getLocStr("bornDate").replace("$date$", date.toLocaleDateString(getLang(), opt))
1319 | }
1320 |
1321 | return `
1322 |
1323 | ${getSvg("balloon")}
1324 | ${bdText}
1325 |
`
1326 | })() : null
1327 |
1328 | }
1329 |
1330 | // description: add links for mentioned users
1331 | for (let m of p.description.match(/(@[0-9A-Za-z_]+)/g) || []) {
1332 | p.description = p.description.replace(m, `${m} `)
1333 | }
1334 |
1335 | // add profile info
1336 | $("a[href$='/header_photo'] + div > div:nth-child(2)").after(`
1337 | ${p.description}
1338 |
1339 | ${p.location ? p.location : ""}
1340 | ${p.url ? p.url : ""}
1341 | ${p.birthday ? p.birthday : ""}
1342 | ${p.joinDate}
1343 |
1344 | `)
1345 |
1346 | // add followers/following count
1347 | if (!$(`.gt2-blocked-profile-items + div [href$="/following"]`).length) {
1348 | $(".gt2-blocked-profile-items").after(`
1349 |
1357 | `)
1358 | }
1359 |
1360 | // followersYouKnow
1361 | $(".gt2-blocked-profile-items + div").after(fykHTML)
1362 |
1363 |
1364 | // add legacy sidebar profile information
1365 | waitForKeyElements(".gt2-legacy-profile-name", () => {
1366 | if (!$(".gt2-legacy-profile-info .gt2-legacy-profile-fyk").length) {
1367 | $(".gt2-legacy-profile-info .gt2-legacy-profile-items").append(`
1368 | ${p.description ? `${p.description}
` : ""}
1369 | ${p.location ? `${p.location}
` : ""}
1370 | ${p.url ? `${p.url}
` : ""}
1371 | ${p.birthday ? `${p.birthday}
` : ""}
1372 | ${p.joinDate}
1373 | ${fykHTML}
1374 | `)
1375 | }
1376 | })
1377 |
1378 | // profile id
1379 | $(".gt2-legacy-profile-info").attr("data-profile-id", profileData.rest_id)
1380 | })
1381 | })
1382 | }
1383 |
1384 |
1385 | // removeChild interception
1386 | /*
1387 | Element.prototype.removeChild = (function(fun) {
1388 | return function(child) {
1389 | // if ([
1390 | // "a[data-testid=AppTabBar_Home_Link]",
1391 | // "a[data-testid=AppTabBar_Notifications_Link]",
1392 | // "a[data-testid=AppTabBar_DirectMessage_Link]"
1393 | // ].some(e => $(child).parent().parent().is(e))) {
1394 | // return child
1395 | // }
1396 |
1397 | return fun.apply(this, arguments)
1398 | }
1399 | }(Element.prototype.removeChild))
1400 | */
1401 |
1402 |
1403 | //
1404 | // $("body").on("ended", "video[poster='https://pbs.twimg.com/ext_tw_video_thumb/']", function(e) {
1405 | // e.preventDefault()
1406 | // console.log("test");
1407 | // console.log(this);
1408 | // })
1409 |
1410 |
1411 |
1412 | // ##################################
1413 | // # translate tweets in timelime #
1414 | // ##################################
1415 |
1416 |
1417 | // add translate button
1418 | if (!GM_getValue("opt_gt2").hideTranslateTweetButton) {
1419 | waitForKeyElements("[data-testid=tweet] [lang], [data-testid=tweet] + div > div:nth-child(2) [role=link] [lang]", function(e) {
1420 | let $e = $(e)
1421 | let tweetLang = $e.attr("lang")
1422 | let userLang = getLang()
1423 | userLang = userLang == "en-GB" ? "en" : userLang
1424 | if (tweetLang != userLang && tweetLang != "und") {
1425 | $e.first().after(`
1426 |
1429 | `)
1430 | }
1431 | })
1432 | }
1433 |
1434 |
1435 | // translate a tweet or LPL bio
1436 | $("body").on("click", ".gt2-translate-tweet, .gt2-legacy-profile-info [data-testid=UserDescription] + [role=button]", function(event) {
1437 | event.preventDefault()
1438 |
1439 | // already translated
1440 | if ($(this).parent().find(".gt2-translated-tweet").length) {
1441 | $(this).addClass("gt2-hidden")
1442 | $(this).parent().find(".gt2-translated-tweet, .gt2-translated-tweet-info").removeClass("gt2-hidden")
1443 | return
1444 | }
1445 |
1446 | let id = $(this).parents("article").find("div[data-testid=tweet]").length
1447 | ? $(this).parents("article").find(`div[data-testid=tweet] > div:nth-child(2) > div:nth-child(1) a[href*='/status/'],
1448 | div[data-testid=tweet] + div > div:nth-child(3) a[href*='/status/']`).attr("href").split("/")[3]
1449 | : null
1450 |
1451 | // embedded tweet
1452 | if ($(this).parents("[role=link]").parents("article").find("[data-testid=tweet]").length) {
1453 | requestTweet(id, res => translateTweet(this, res.quoted_status_id_str))
1454 |
1455 | // normal tweet with embedded one
1456 | } else if ($(this).parents("article").find("[data-testid=tweet] [role=link] [lang]").length) {
1457 | console.log("aaa");
1458 | requestTweet(id, res => translateTweet(this, id, res.quoted_status_id_str))
1459 |
1460 | // normal tweet or bio
1461 | } else {
1462 | translateTweet(this, id)
1463 | }
1464 | })
1465 |
1466 |
1467 | function translateTweet(e, id, quoteId) {
1468 | let isTweet = $(e).is(".gt2-translate-tweet")
1469 | GM_setValue("tmp_translatedTweetInfo", getLocStr("translatedTweetInfo"))
1470 |
1471 | GM_xmlhttpRequest({
1472 | method: "GET",
1473 | url: `https://twitter.com/i/api/1.1/strato/column/None/${isTweet ? `tweetId=${id}` : `profileUserId=${$(".gt2-legacy-profile-info").data("profile-id")}`},destinationLanguage=None,translationSource=Some(Google),feature=None,timeout=None,onlyCached=None/translation/service/translate${isTweet ? "Tweet" : "Profile"}`,
1474 | headers: getRequestHeaders(isTweet ? {
1475 | referer: `https://twitter.com/i/status/${id}`
1476 | } : {}),
1477 | onload: function(res) {
1478 | if (res.status == "200") {
1479 | let o = JSON.parse(res.response)
1480 | if (!isTweet) o = o.profileTranslation
1481 | console.log(o)
1482 | let out = o.translation
1483 |
1484 | // handle entities in tweet
1485 | if (o.entities) {
1486 | // remove embedded url if applicable (https://twitter.com/ella_hollywood/status/1395290916303212544)
1487 | if (quoteId && o.entities.urls) {
1488 | let tco = o.entities.urls.find(x => x.expanded_url.endsWith(quoteId))
1489 | if (tco) {
1490 | out = out.replace(` ${tco.url}`, "")
1491 | o.entities.urls = o.entities.urls.filter(x => !x.expanded_url.endsWith(quoteId))
1492 | }
1493 | }
1494 | out = out.populateWithEntities(o.entities)
1495 | }
1496 |
1497 | $(e).addClass("gt2-hidden")
1498 | $(e).after(`
1499 |
1509 |
1512 | `)
1513 | } else {
1514 | console.error("Error occurred while translating.")
1515 | console.error(res)
1516 | }
1517 | }
1518 | })
1519 | }
1520 |
1521 |
1522 | // hide translation
1523 | $("body").on("click", ".gt2-translated-tweet-info", function(event) {
1524 | event.preventDefault()
1525 |
1526 | $(this).parent().find(".gt2-translated-tweet, .gt2-translated-tweet-info").addClass("gt2-hidden")
1527 | $(this).prevAll(".gt2-translate-tweet, [role=button]").removeClass("gt2-hidden")
1528 | })
1529 |
1530 |
1531 |
1532 |
1533 | // ########################
1534 | // # disableAutoRefresh #
1535 | // ########################
1536 |
1537 |
1538 | // russian numbering
1539 | function getRusShowNew(nr) {
1540 | let end
1541 | let t1 = nr.toString().slice(-1)
1542 | let t2 = nr.toString().slice(-2)
1543 |
1544 | if (t1 == 1) end = "новый твит"
1545 | if (t1 >= 2 && t1 <= 4) end = "новых твита"
1546 | if (t1 == 0 || (t1 >= 5 && t1 <= 9)) end = "новых твитов"
1547 | if (t2 >= 11 && t2 <= 14) end = "новый твит"
1548 | return `Посмотреть ${nr} ${end}`
1549 | }
1550 |
1551 | // add counter for new tweets
1552 | function updateNewTweetDisplay() {
1553 | let nr = $(".gt2-hidden-tweet").length
1554 | let text = nr == 1 ? getLocStr("showNewSingle") : getLocStr("showNewMulti").replace("$", nr)
1555 |
1556 | // exception for russian
1557 | if (getLang() == "ru") {
1558 | text = getRusShowNew(nr)
1559 | }
1560 |
1561 | if (nr) {
1562 | // add button
1563 | if ($(".gt2-show-hidden-tweets").length == 0) {
1564 | if (window.location.href.split("/")[3].startsWith("home")) {
1565 | $("div[data-testid=primaryColumn] > div > div:nth-child(3)").addClass("gt2-show-hidden-tweets")
1566 | } else {
1567 | $("div[data-testid='primaryColumn'] section > div > div > div > div:nth-child(1)").append(`
1568 |
1569 | `)
1570 | }
1571 | }
1572 | $(".gt2-show-hidden-tweets").html(text)
1573 | let t = $("title").text()
1574 | $("title").text(`[${nr}] ${t.startsWith("(") ? t.split(") ")[1] : t.startsWith("[") ? t.split("] ")[1] : t}`)
1575 | } else {
1576 | $(".gt2-show-hidden-tweets").empty().removeClass("gt2-show-hidden-tweets")
1577 | resetTitle()
1578 | }
1579 | }
1580 |
1581 |
1582 | // show new tweets
1583 | $("body").on("click", ".gt2-show-hidden-tweets", () => {
1584 | let topTweet = $("div[data-testid=tweet]").eq(0).find("> div:nth-child(2) > div:nth-child(1) > div > div > div:nth-child(1) > a").attr("href")
1585 | GM_setValue("topTweet", topTweet)
1586 | $(".gt2-hidden-tweet").removeClass("gt2-hidden-tweet")
1587 | $(".gt2-hidden-tweet-part").removeClass("gt2-hidden-tweet-part")
1588 | console.log(`topTweet: ${topTweet}`)
1589 | updateNewTweetDisplay()
1590 | })
1591 |
1592 |
1593 | // change title to display X new tweets
1594 | function resetTitle() {
1595 | let t = $("title").text()
1596 | let notifications = ".gt2-nav-left a[href='/notifications'] > div > div:nth-child(1) > div:nth-child(2)"
1597 | let messages = ".gt2-nav-left a[href='/messages'] > div > div:nth-child(1) > div:nth-child(2)"
1598 | let nr = 0
1599 | if ($(notifications).length) nr += parseInt($(notifications).text())
1600 | if ($(messages).length) nr += parseInt($(messages).text())
1601 | $("title").text(`${nr > 0 ? `(${nr}) ` : ""}${t.startsWith("[") ? t.split("] ")[1] : t}`)
1602 | }
1603 |
1604 |
1605 | // observe and hide auto refreshed tweets
1606 | function hideTweetsOnAutoRefresh() {
1607 | let obsTL = new MutationObserver(mutations => {
1608 | mutations.forEach(m => {
1609 | if (m.addedNodes.length == 1) {
1610 | let $t = $(m.addedNodes[0])
1611 | if ($t.find("div > div > div > div > article div[data-testid=tweet]").length && $t.nextAll().find(`a[href='${GM_getValue("topTweet")}']`).length) {
1612 | if ($t.find("div[data-testid=tweet] > div:nth-child(1) > div:nth-child(2)").length && (!$("> div > div > div > a[href^='/i/status/']").length || $t.next().find("> div > div > div > a[href^='/i/status/']").length)) {
1613 | $t.addClass("gt2-hidden-tweet-part")
1614 | } else {
1615 | console.log($t);
1616 | $t.addClass("gt2-hidden-tweet")
1617 | updateNewTweetDisplay()
1618 | }
1619 | } else if ($t.find("> div > div > div > a[href^='/i/status/']").length) {
1620 | console.log($t);
1621 | $t.addClass("gt2-hidden-tweet-part")
1622 | }
1623 | }
1624 | })
1625 | })
1626 | let tlSel = `div[data-testid=primaryColumn] > div > div:nth-child(4) section > div > div > div,
1627 | div[data-testid=primaryColumn] > div > div:nth-child(2) section > div > div > div`
1628 | waitForKeyElements(tlSel, () => {
1629 | // memorize last tweet
1630 | let topTweet = $(tlSel).find("> div:nth-child(1) div[data-testid=tweet] > div:nth-child(2) > div:nth-child(1) > div > div > div:nth-child(1) > a").attr("href")
1631 | GM_setValue("topTweet", topTweet)
1632 | console.log(`topTweet: ${topTweet}`)
1633 | obsTL.observe($(tlSel)[0], {
1634 | childList: true,
1635 | subtree: true
1636 | })
1637 | })
1638 | }
1639 |
1640 |
1641 | // keep the site from removing tweets (not working)
1642 | function keepTweetsInTL() {
1643 | let o = Element.prototype.removeChild
1644 | Element.prototype.removeChild = function(child) {
1645 | // check if element is a tweet
1646 | if ($(child).not("[class]") && $(child).find("> div > div > div > div > article > div > div[data-testid=tweet]").length) {
1647 | console.log($(child)[0])
1648 | return child
1649 | } else {
1650 | return o.apply(this, arguments)
1651 | }
1652 | }
1653 | }
1654 |
1655 |
1656 |
1657 | // ##########################
1658 | // # misc event handlers #
1659 | // ##########################
1660 |
1661 |
1662 | // compose tweet button
1663 | $("body").on("click", ".gt2-nav .gt2-compose", () => {
1664 | $("header a[href='/compose/tweet'] > div").click()
1665 | })
1666 |
1667 |
1668 | // add elements to navbar dropdow menu
1669 | $("body").on("click", ".gt2-toggle-navbar-dropdown", () => {
1670 | console.log("navbar toggled");
1671 | let i = getInfo()
1672 | $("header nav > div[data-testid=AppTabBar_More_Menu]").click()
1673 | let more = "div[role=menu][style^='max-height: calc'].r-ipm5af > div > div > div"
1674 |
1675 | waitForKeyElements(`${more} `, () => {
1676 | if ($(more).find("a[href='/explore']").length) return
1677 | let $hr = $(more).find("> div:empty") // seperator line
1678 | $hr.clone().prependTo(more)
1679 | // items from left menu to attach
1680 | let toAttach = [
1681 | {
1682 | sel: `a[href='/explore']`,
1683 | name: "Explore"
1684 | }, {
1685 | sel: `a[href='/i/bookmarks']`,
1686 | name: "Bookmarks"
1687 | }, {
1688 | sel: `a[href='/${i.screenName}/lists']`,
1689 | name: "Lists"
1690 | }, {
1691 | sel: `a[href='/${i.screenName}']`,
1692 | name: "Profile"
1693 | }
1694 | ]
1695 | for (let e of toAttach) {
1696 | let $tmp = $("header nav").find(e.sel).clone()
1697 | $tmp.children().append(`${getLocStr(`nav${e.name}`)} `)
1698 | $tmp.prependTo(more)
1699 | }
1700 |
1701 | $hr.clone().appendTo(more)
1702 | $(`Logout `).appendTo(more)
1703 | })
1704 |
1705 | })
1706 |
1707 |
1708 | // acc switcher dropdown
1709 | $("body").on("click", ".gt2-toggle-acc-switcher-dropdown", function() {
1710 | $("body").addClass("gt2-acc-switcher-active")
1711 | $("div[data-testid=SideNav_AccountSwitcher_Button]").click()
1712 |
1713 | // change dropdown position
1714 | $(".gt2-style-acc-switcher-dropdown").remove()
1715 | let pos = $(".gt2-toggle-acc-switcher-dropdown")[0].getBoundingClientRect()
1716 | $("html").prepend(`
1717 |
1724 | `)
1725 | })
1726 |
1727 |
1728 | // remove class on next click
1729 | $("body").on("click", ":not(.gt2-toggle-acc-switcher-dropdown), :not(div[data-testid=SideNav_AccountSwitcher_Button])", function() {
1730 | setTimeout(function () {
1731 | if (!$("a[href='/account/add']").length) {
1732 | $("body").removeClass("gt2-acc-switcher-active")
1733 | }
1734 | }, 2000)
1735 | })
1736 |
1737 |
1738 | // expand the “What’s happening?” tweet field (minimized by default)
1739 | $("body").on("click", "div[data-testid=primaryColumn] > div > div:nth-child(2)", e => $(e.currentTarget).addClass("gt2-compose-large"))
1740 |
1741 |
1742 | // loggedOut nightmode
1743 | $("body").on("click", ".gt2-toggle-lo-nightmode", () => {
1744 | let nm = document.cookie.match(/night_mode=1/) ? 0 : 1
1745 | // delete old cookie
1746 | document.cookie = "night_mode=; Max-Age=0;"
1747 | // create new cookie
1748 | let d = new Date()
1749 | d.setDate(d.getDate() + 500)
1750 | document.cookie = `night_mode=${nm}; expires=${d.toUTCString()}; path=/; domain=.twitter.com`
1751 | window.location.reload()
1752 | })
1753 |
1754 |
1755 | // close sidebar notice
1756 | $("body").on("click", ".gt2-sidebar-notice-close", function() {
1757 | if ($(this).parents(".gt2-sidebar-notice").hasClass("gt2-update-notice")) {
1758 | GM_setValue(`sb_notice_ack_update_${GM_info.script.version}`, true)
1759 | }
1760 | $(this).parents(".gt2-sidebar-notice").remove()
1761 | })
1762 |
1763 |
1764 | // remove blocked profile stuff on unblock
1765 | $("body").on("click", `div[data-testid=placementTracking] div[data-testid$="-unblock"]`, () => $("[class^=gt2-blocked-profile]").remove())
1766 |
1767 |
1768 | // [LPL] unusual activity button: make elements clickable again
1769 | $(document).on("click", `.gt2-profile-not-found [data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(2) > div[role=button]`, () => $("body").removeClass("gt2-profile-not-found"))
1770 |
1771 |
1772 | // expand t.co shortlinks (tweets)
1773 | $(document).on("mouseover", `.gt2-opt-expand-tco-shortlinks div:not([data-testid=placementTracking]) > div > article [data-testid=tweet]:not(.gt2-tco-expanded),
1774 | .gt2-opt-expand-tco-shortlinks.gt2-page-tweet [data-testid=primaryColumn] section > h1 + div > div > div:nth-child(1) article:not(.gt2-tco-expanded)`, function() {
1775 | let $tweet = $(this)
1776 | $tweet.addClass("gt2-tco-expanded")
1777 |
1778 | // exit if tweet has no links
1779 | if (!$tweet.find(`a[href^="http://t.co"], a[href^="https://t.co"], [data-testid="card.wrapper"]`).length) return
1780 |
1781 | let id = $tweet.is("article")
1782 | ? getPath().split("/")[2].split("?")[0].split("#")[0]
1783 | : $tweet.find(`time`).parent().attr("href").split("/status/")[1]
1784 |
1785 | requestTweet(id, res => {
1786 | $tweet.find(`a[href^="http://t.co"], a[href^="https://t.co"]`).each(function() {
1787 | $(this).attr("href", res.entities.urls.find(e => e.url == $(this).attr("href").split("?")[0]).expanded_url)
1788 | })
1789 | $tweet.find(`[data-testid="card.layoutSmall.media"]`).each(function() {
1790 | console.log(res);
1791 | $(this).next().wrap(` `)
1792 | })
1793 | })
1794 | })
1795 |
1796 |
1797 | // expand t.co shortlinks (profile, not legacy)
1798 | $(document).on("mouseover", `.gt2-opt-expand-tco-shortlinks.gt2-page-profile:not(.gt2-opt-legacy-profile) [data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(1):not(.gt2-tco-expanded), .gt2-opt-expand-tco-shortlinks [data-testid=UserCell]`, function() {
1799 | let $profile = $(this)
1800 | $profile.addClass("gt2-tco-expanded")
1801 | // exit if profile has no links
1802 | if (!$profile.find(`a[href^="http://t.co"], a[href^="https://t.co"]`).length) return
1803 |
1804 | let screenName = $profile.is("[data-testid=UserCell]")
1805 | ? $profile.find("> div > div:nth-child(2) > div:nth-child(1) a").attr("href").slice(1)
1806 | : getPath().split("/")[0].split("?")[0].split("#")[0]
1807 |
1808 | requestUser(screenName, res => {
1809 | let ent = res.data.user.legacy.entities
1810 | let urls = []
1811 | if (ent.description) urls.push(...ent.description.urls)
1812 | if (ent.url) urls.push(...ent.url.urls)
1813 | $profile.find(`a[href^="http://t.co"], a[href^="https://t.co"]`).each(function() {
1814 | $(this).attr("href", urls.find(e => e.url == $(this).attr("href").split("?")[0].split("#")[0]).expanded_url)
1815 | })
1816 | })
1817 | })
1818 |
1819 |
1820 | // block/unblock account on holding follow button for 3 seconds
1821 | if (GM_getValue("opt_gt2").enableQuickBlock) {
1822 | let qbOffer
1823 | $("body").on("mouseover", `[data-testid$="-follow"]:not([data-gt2-qb-state])`, e => {
1824 | let $b = $(e.target).parents(`[data-testid$="-follow"]`)
1825 | $b.attr("data-gt2-qb-state", "offer-pending")
1826 | qbOffer = setTimeout(() => {
1827 | $b.attr("data-gt2-qb-state", "offer")
1828 | $b.find("> div > span").append(`
1829 | ${getLocStr("qbBlock")}
1830 | ${getLocStr("qbBlocked")}
1831 | ${getLocStr("qbUnblock")}
1832 | `)
1833 | }, 3e3)
1834 | })
1835 | $("body").on("click", `[data-testid$="-follow"][data-gt2-qb-state=offer]`, e => {
1836 | e.stopImmediatePropagation()
1837 | let $b = $(e.target).parents(`[data-testid$="-follow"]`)
1838 | let user_id = $b.attr("data-testid").slice(0, -7)
1839 | blockUser(user_id, true, () => {
1840 | console.log(`quickblock: ${user_id}`)
1841 | $b.attr("data-gt2-qb-state", "blocked")
1842 | })
1843 | })
1844 | $("body").on("click", `[data-testid$="-follow"][data-gt2-qb-state=blocked]`, e => {
1845 | e.stopImmediatePropagation()
1846 | let $b = $(e.target).parents(`[data-testid$="-follow"]`)
1847 | let user_id = $b.attr("data-testid").slice(0, -7)
1848 | blockUser(user_id, false, () => {
1849 | console.log(`quickunblock: ${user_id}`)
1850 | $b.removeAttr("data-gt2-qb-state")
1851 | $b.find("[class^=gt2-qb]").remove()
1852 | })
1853 | })
1854 | $("body").on("mouseleave", `[data-testid$="-follow"][data-gt2-qb-state^=offer],
1855 | [data-testid$="-unfollow"][data-gt2-qb-state^=offer]`, e => {
1856 | let $b = $(e.target).parents(`[data-testid$="-follow"]`)
1857 | $b.removeAttr("data-gt2-qb-state")
1858 | $b.find("[class^=gt2-qb]").remove()
1859 | clearTimeout(qbOffer)
1860 | })
1861 | }
1862 |
1863 |
1864 |
1865 | // ########################
1866 | // # display settings #
1867 | // ########################
1868 |
1869 |
1870 | // high contrast
1871 | $("body").on("click", `[data-testid="accessibilityScreen"] > div:nth-child(3) label [aria-labelledby]`, function() {
1872 | GM_setValue("opt_display_highContrast", !$(this).find("input").is("[checked]"))
1873 | updateCSS()
1874 | })
1875 |
1876 |
1877 | // user color
1878 | waitForKeyElements(`body:not(.gt2-opt-color-override) [data-testid=SideNav_NewTweet_Button]`, e => {
1879 | let userColor = $(e).css("background-color")
1880 | if (userColor != GM_getValue("opt_display_userColor")) {
1881 | GM_setValue("opt_display_userColor", userColor)
1882 | updateCSS()
1883 | }
1884 | })
1885 |
1886 | // background color
1887 | new MutationObserver(mut => {
1888 | mut.forEach(m => {
1889 | let bgColor = m.target[m.attributeName]["background-color"]
1890 | if (m.oldValue && bgColor != "") {
1891 | GM_setValue("opt_display_bgColor", bgColor)
1892 | updateCSS()
1893 | }
1894 | })
1895 | }).observe($("body")[0], {
1896 | attributes: true,
1897 | attributeOldValue: true,
1898 | attributeFilter: ["style"]
1899 | })
1900 |
1901 |
1902 | // font increment
1903 | new MutationObserver(mut => {
1904 | mut.forEach(m => {
1905 | let fs = m.target[m.attributeName]["font-size"]
1906 | let fsOld = m.oldValue.match(/font-size: (\d+px);/)
1907 | if (fsOld && fs != "" && fs != fsOld[1]) {
1908 | GM_setValue("opt_display_fontSize", fs)
1909 | updateCSS()
1910 | }
1911 | })
1912 | }).observe($("html")[0], {
1913 | attributes: true,
1914 | attributeOldValue: true,
1915 | attributeFilter: ["style"]
1916 | })
1917 |
1918 |
1919 | // minimize DMDrawer if hideMessageBox is set
1920 | if (GM_getValue("opt_gt2").hideMessageBox) {
1921 | waitForKeyElements(`.gt2-opt-hide-message-box [data-testid=DMDrawer] path[d^="M12 19.344l-8.72"]`, e => {
1922 | console.log("Minimized DMDrawer")
1923 | $(e).parents("[role=button]").click()
1924 | })
1925 | }
1926 |
1927 |
1928 | // hide timeline follow suggestions
1929 | if (GM_getValue("opt_gt2").hideFollowSuggestions) {
1930 | function hideTLFS($p) {
1931 | if (!$p) return $p
1932 | if ($p.prev().length) {
1933 | $p = $p.prev()
1934 | if ($p.find("article").length) return
1935 | $p.addClass("gt2-hidden")
1936 | } else {
1937 | if (window.scrollY < 500) return
1938 | setTimeout(() => {
1939 | $p = hideTLFS($p)
1940 | }, 100)
1941 | }
1942 | return $p
1943 | }
1944 |
1945 | // small follow topic above tweets
1946 | // if ((GM_getValue("opt_gt2").hideFollowSuggestionsSel & 1) == 1) {
1947 | // waitForKeyElements(`[data-gt2-path=home] [data-testid=primaryColumn] section article > div > div > div > div:not([data-testid=tweet]) > div > div > div`, e => $(e).addClass("gt2-hidden"))
1948 | // }
1949 |
1950 | // big follow boxes
1951 | waitForKeyElements(
1952 | ["topics/picker", "connect_people", "lists/suggested"]
1953 | .filter((e, i) => (GM_getValue("opt_gt2").hideFollowSuggestionsSel & Math.pow(2, i)) == Math.pow(2, i))
1954 | .map(e => `[data-testid=primaryColumn] section [href^="/i/${e}"]`)
1955 | .join(", "), e => {
1956 |
1957 | let $p = $(e).parent().parent().addClass("gt2-hidden")
1958 | if ($p.next().length) $p.next().addClass("gt2-hidden")
1959 | if ($p.next().next().find("div > div:empty").length) $p.next().next().addClass("gt2-hidden")
1960 | for (let i=0; i < 6; i++) {
1961 | $p = hideTLFS($p)
1962 | }
1963 | })
1964 | }
1965 |
1966 |
1967 | // do not colorOverride these elements (reply/like/retweet/share on tweets and verified badge)
1968 | waitForKeyElements(`[data-testid=tweet] [role=group]`, e => $(e).find("[role=button] *").attr("data-gt2-color-override-ignore", ""))
1969 | waitForKeyElements(`path[d^="M22.5 12.5c0-1.58-.875"]`, e => $(e).parents("svg").attr("data-gt2-color-override-ignore", ""))
1970 | waitForKeyElements(`[data-gt2-path-modal="i/display"] div:nth-last-child(2) > div > [role=radiogroup],
1971 | [data-gt2-path="settings/display"] div:nth-last-child(2) > div > [role=radiogroup]`, e => {
1972 | let $e = $(e).parents("[aria-labelledby]")
1973 | $e.find("[name*=COLOR_PICKER]").parents("label").parent().find("*").attr("data-gt2-color-override-ignore", "")
1974 | $e.find("[dir]:nth-child(3) + div:not([dir]) > div > div > div[dir] + div *").attr("data-gt2-color-override-ignore", "")
1975 | })
1976 |
1977 | // do not add dividers to tweet inline threads
1978 | waitForKeyElements(`[data-testid=tweet] > div:nth-child(1) > div:nth-child(2):empty`, e => $(e).parents(`[style*="position: absolute"]`).children().attr("data-gt2-divider-add-ignore", ""))
1979 |
1980 | // color notifications bell
1981 | waitForKeyElements(`path[d^="M23.61.15c-.375"]`, e => $(e).parents("[role=button]").attr("data-gt2-bell-full-color", ""))
1982 | waitForKeyElements(`path[d^="M23.24 3.26h-2.425V"]`, e => $(e).parents("[role=button]").removeAttr("data-gt2-bell-full-color", ""))
1983 |
1984 |
1985 | // ################
1986 | // # Update CSS #
1987 | // ################
1988 |
1989 |
1990 | // get scrollbar width (https://stackoverflow.com/q/8079187)
1991 | function getScrollbarWidth() {
1992 | if ($("html").is("[data-minimalscrollbar]")) {
1993 | return 0
1994 | }
1995 | let $t = $("
").css({
1996 | position: "absolute",
1997 | top: "-100px",
1998 | overflowX: "hidden",
1999 | overflowY: "scroll"
2000 | }).prependTo("body")
2001 | let out = $t[0].offsetWidth - $t[0].clientWidth
2002 | $t.remove()
2003 | return out
2004 | }
2005 |
2006 |
2007 | // update inserted CSS
2008 | function updateCSS() {
2009 | // bgColor schemes
2010 | let bgColors = {
2011 | // default (white)
2012 | "rgb(255, 255, 255)": {
2013 | bg: "#e6ecf0",
2014 | elem: "rgb(255, 255, 255)",
2015 | elemSel: "rgb(247, 249, 250)",
2016 | gray: "rgb(91, 112, 131)",
2017 | grayDark: "#e6ecf0",
2018 | grayDark2: "rgb(196, 207, 214)",
2019 | grayLight: "rgb(101, 119, 134)",
2020 | navbar: "#ffffff",
2021 | text: "rgb(20, 23, 26)",
2022 | text2: "white",
2023 | shadow: "rgba(101, 119, 134, 0.15)",
2024 | backdrop: "rgba(0, 0, 0, 0.4)"
2025 | },
2026 | // dim
2027 | "rgb(21, 32, 43)": {
2028 | bg: "#10171e",
2029 | elem: "rgb(21, 32, 43)",
2030 | elemSel: "rgb(25, 39, 52)",
2031 | gray: "rgb(101, 119, 134)",
2032 | grayDark: "#38444d",
2033 | grayDark2: "rgb(61, 84, 102)",
2034 | grayLight: "rgb(136, 153, 166)",
2035 | navbar: "#1c2938",
2036 | text: "rgb(255, 255, 255)",
2037 | text2: "white",
2038 | shadow: "rgba(136, 153, 166, 0.15)",
2039 | backdrop: "rgba(91, 112, 131, 0.4)"
2040 | },
2041 | // lightsOut
2042 | "rgb(0, 0, 0)": {
2043 | bg: "#000000",
2044 | elem: "#000000",
2045 | elemSel: "rgb(21, 24, 28)",
2046 | gray: "#657786",
2047 | grayDark: "#38444d",
2048 | grayDark2: "rgb(47, 51, 54)",
2049 | grayLight: "rgb(110, 118, 125)",
2050 | navbar: "rgb(21, 24, 28)",
2051 | text: "rgb(217, 217, 217)",
2052 | text2: "white",
2053 | shadow: "rgba(255, 255, 255, 0.15)",
2054 | backdrop: "rgba(91, 112, 131, 0.4)"
2055 | }
2056 | }
2057 |
2058 | // high contrast color overrides
2059 | let bgColorsHC = {
2060 | // default (white)
2061 | "rgb(255, 255, 255)": {
2062 | gray: "rgb(59, 76, 92)",
2063 | grayDark: "rgb(170, 184, 194)",
2064 | grayLight: "rgb(59, 76, 92)",
2065 | text: "rgb(20, 29, 38)"
2066 | },
2067 | // dim
2068 | "rgb(21, 32, 43)": {
2069 | elemSel: "rgb(24, 36, 48)",
2070 | gray: "rgb(184, 203, 217)",
2071 | grayDark: "rgb(56, 68, 88)",
2072 | grayLight: "rgb(184, 203, 217)",
2073 | text2: "rgb(15, 20, 25)"
2074 | },
2075 | // lightsOut
2076 | "rgb(0, 0, 0)": {
2077 | bg: "rgb(5, 5, 5)",
2078 | elem: "rgb(5, 5, 5)",
2079 | elemSel: "rgb(14, 16, 18)",
2080 | gray: "rgb(146, 156, 166)",
2081 | grayDark: "rgb(61, 65, 69)",
2082 | grayLight: "rgb(146, 156, 166)",
2083 | text: "rgb(255, 255, 255)",
2084 | text2: "rgb(15, 20, 25)"
2085 | }
2086 | }
2087 |
2088 | let baseColors = {
2089 | // normal white hc // dim/lo hc
2090 | blue: ["29, 161, 242", "38, 74, 157", "112, 200, 255"],
2091 | green: ["23, 191, 99", "9, 102, 51", "102, 211, 151"],
2092 | red: ["224, 36, 94", "159, 12, 58", "240, 152, 179"],
2093 | redDark: ["202, 32, 85", "169, 36, 78", "216, 137, 161"],
2094 | yellow: ["255, 173, 31", "121, 80, 11", "255, 203, 112"]
2095 | }
2096 |
2097 | // initialize with the current settings
2098 | if (GM_getValue("gt2_initialized") == undefined && isLoggedIn()) {
2099 | waitForKeyElements(`h2 > a[href="/i/keyboard_shortcuts"] span`, () => {
2100 | GM_setValue("opt_display_userColor", $(`a[href="/i/keyboard_shortcuts"]`).css("color"))
2101 | GM_setValue("opt_display_bgColor", $("body").css("background-color"))
2102 | GM_setValue("opt_display_highContrast", false)
2103 | GM_setValue("opt_display_fontSize", $("html").css("font-size"))
2104 | GM_setValue("gt2_initialized", true)
2105 | window.location.reload()
2106 | })
2107 |
2108 | } else {
2109 | // add gt2-options to body for the css to take effect
2110 | for (let [key, val] of Object.entries(GM_getValue("opt_gt2"))) {
2111 | if (val) $("body").addClass(`gt2-opt-${key.toKebab()}`)
2112 | }
2113 |
2114 | // remove unneeded classes
2115 | $("body").removeClass("gt2-acc-switcher-active")
2116 |
2117 | // delete old stylesheet
2118 | if ($(".gt2-style").length) {
2119 | $(".gt2-style").remove()
2120 | }
2121 |
2122 | let opt_display_bgColor = GM_getValue("opt_display_bgColor")
2123 | let opt_display_highContrast = GM_getValue("opt_display_highContrast")
2124 | let opt_display_fontSize = GM_getValue("opt_display_fontSize")
2125 | let opt_display_userColor = GM_getValue("opt_display_userColor")
2126 |
2127 | // options to set if not logged in
2128 | if (!isLoggedIn()) {
2129 | // get bgColor from cookie
2130 | opt_display_bgColor = document.cookie.match(/night_mode=1/) ? "rgb(21, 32, 43)" : "rgb(255, 255, 255)"
2131 | opt_display_highContrast = false
2132 | opt_display_fontSize = "15px"
2133 | opt_display_userColor = "rgb(29, 161, 242)"
2134 | }
2135 |
2136 | // highContrast lightsOut
2137 | if (opt_display_bgColor == "rgb(5, 5, 5)") opt_display_bgColor = "rgb(0, 0, 0)"
2138 |
2139 | // insert new stylesheet
2140 | $("html").prepend(`
2141 |
2162 | `
2163 | )
2164 | }
2165 |
2166 | // add navbar
2167 | if (!$("gt2-nav").length) {
2168 | if (isLoggedIn()) {
2169 | addNavbar()
2170 | } else {
2171 | addNavbarLoggedOut()
2172 | }
2173 | }
2174 | }
2175 |
2176 |
2177 |
2178 | // ##############
2179 | // # resizing #
2180 | // ##############
2181 |
2182 |
2183 | // things to do when resizing the window
2184 | $(window).on("resize", () => {
2185 | let w = window.innerWidth
2186 | if ((!GM_getValue("opt_gt2").smallSidebars && w <= 1350) ||
2187 | ( GM_getValue("opt_gt2").smallSidebars && w <= 1230)) {
2188 | // move dash profile to right sidebar
2189 | $(".gt2-dashboard-profile")
2190 | .prependTo("div[data-testid=sidebarColumn] > div > div:nth-child(2) > div > div > div")
2191 | // remove trends
2192 | $(".gt2-trends").remove()
2193 | } else {
2194 | $(".gt2-dashboard-profile").prependTo(".gt2-left-sidebar")
2195 | }
2196 | })
2197 |
2198 |
2199 |
2200 | // ###############
2201 | // # scrolling #
2202 | // ###############
2203 |
2204 |
2205 | // things to do when scrolling
2206 | ;(function() {
2207 | let prev = window.pageYOffset
2208 | let bannerHeight = (window.innerWidth - getScrollbarWidth()) / 3 - 15
2209 |
2210 | $(window).on("scroll", () => {
2211 | let curr = window.pageYOffset
2212 |
2213 | // prevent scroll to top
2214 | if (prev > 1500 && curr == 0) {
2215 | window.scroll(0, prev)
2216 | return
2217 | }
2218 |
2219 | if (prev < curr) {
2220 | $("body").addClass("gt2-scrolled-down")
2221 | } else {
2222 | $("body").removeClass("gt2-scrolled-down")
2223 | }
2224 | prev = curr
2225 |
2226 | // legacy profile banner parallax
2227 | if (curr > bannerHeight) {
2228 | $("body").addClass("gt2-scrolled-down-banner")
2229 | } else {
2230 | $("body").removeClass("gt2-scrolled-down-banner")
2231 | $(".gt2-legacy-profile-banner img").css("transform", `translate3d(0px, ${curr / bannerHeight * 42}%, 0px)`)
2232 | }
2233 | })
2234 | }())
2235 |
2236 |
2237 |
2238 | // ################
2239 | // # URL change #
2240 | // ################
2241 |
2242 |
2243 | function beforeUrlChange(path) {
2244 | // [LPL] reattach buttons to original position
2245 | if (!_isModal(path)) {
2246 | let $b = $("div[data-testid=primaryColumn] > div > div:nth-child(2) > div > div > div:nth-child(1) > div:nth-child(2) > div:nth-child(1)")
2247 | if (!$b.find("> div").length && $("body").attr("data-gt2-prev-path") != path) {
2248 | $(".gt2-legacy-profile-nav-right > div").appendTo($b)
2249 | }
2250 | }
2251 | }
2252 |
2253 |
2254 | // path helper functions
2255 | function _onPage(path, ...top) {
2256 | return top.some(e => e == path.split("/")[0])
2257 | }
2258 | function _onSubPage(path, top, sub) {
2259 | return (top == null ? true : _onPage(path, top)) && path.includes("/") && sub.some(e => e == path.split("/")[1])
2260 | }
2261 | function _isModal(path) {
2262 | return _onSubPage(path, "i", ["display", "keyboard_shortcuts", "flow"])
2263 | || _onSubPage(path, "settings", ["trends", "profile"])
2264 | || _onSubPage(path, "compose", ["tweet"])
2265 | || _onSubPage(path, "account", ["add", "switch"])
2266 | || _onPage(path, "search-advanced")
2267 | || path.match(/\/(photo|video)\/\d\/?$/)
2268 | }
2269 |
2270 |
2271 | // stuff to do when url changes
2272 | function urlChange(changeType, changePath) {
2273 | let path = () => (changePath || getPath()).split("?")[0].split("#")[0]
2274 | let onPage = (...top) => _onPage(path(), ...top)
2275 | let onSubPage = (top, sub) => _onSubPage(path(), top, sub)
2276 | let isModal = _isModal(path())
2277 |
2278 | console.log(`[${changeType}]${isModal ? " [modal]" : ""} ${path()}`)
2279 |
2280 |
2281 | $("body").attr(`data-gt2-path${isModal ? "-modal" : ""}`, path())
2282 | let $realPath = $("link[hreflang=default][data-rh=true]")
2283 | if ($realPath.length) $("body").attr("data-gt2-path", $realPath.attr("href"))
2284 |
2285 | // do a reload on these pages
2286 | if (onPage("login") || (!isLoggedIn() && onPage(""))) {
2287 | window.location.reload()
2288 | }
2289 |
2290 |
2291 | // update css
2292 | if (!$("body").hasClass("gt2-css-inserted")) {
2293 | updateCSS()
2294 | $("body").addClass("gt2-css-inserted")
2295 | }
2296 |
2297 |
2298 | let mainView = "main > div > div > div"
2299 | waitForKeyElements(mainView, () => {
2300 | // insert left sidebar
2301 | if (!$(".gt2-left-sidebar").length) {
2302 | $(mainView).prepend(``)
2303 | }
2304 |
2305 | // on error page
2306 | if ($(mainView).find("h1[data-testid=error-detail]").length && !path().startsWith("settings/gt2")) {
2307 | $("body").addClass("gt2-page-error")
2308 | } else if (!isModal) {
2309 | $("body").removeClass("gt2-page-error")
2310 | }
2311 |
2312 | if (onPage("settings")) {
2313 | waitForKeyElements(`main a[href="/settings/about"]`, addSettingsToggle)
2314 | if (path().startsWith("settings/gt2")) {
2315 | addSettings()
2316 | changeSettingsTitle()
2317 | }
2318 | }
2319 | })
2320 |
2321 |
2322 | // add navbar
2323 | if ($("body").attr("data-gt2-prev-path") == "i/moment_maker") $(".gt2-nav").remove()
2324 | if (!$(".gt2-nav").length) {
2325 | if (isLoggedIn()) {
2326 | addNavbar()
2327 | } else {
2328 | addNavbarLoggedOut()
2329 | }
2330 | }
2331 |
2332 | // highlight current location in left bar
2333 | if (!isModal) {
2334 | $(`.gt2-nav-left > a`).removeClass("active")
2335 | $(`.gt2-nav-left > a[href^='/${path().split("/")[0]}']`).addClass("active")
2336 | }
2337 |
2338 | // hide/add search
2339 | if (onPage("search", "explore")) {
2340 | $(".gt2-search").empty()
2341 | $("body").removeClass("gt2-search-added")
2342 | } else if (!isModal) {
2343 | addSearch()
2344 | }
2345 |
2346 | if (!isLoggedIn()) {
2347 | $("body").addClass("gt2-not-logged-in")
2348 | }
2349 |
2350 |
2351 | // handle stuff in sidebars
2352 | handleTrends()
2353 | if (GM_getValue("opt_gt2").hideFollowSuggestions) {
2354 | let sel = GM_getValue("opt_gt2").hideFollowSuggestionsSel
2355 |
2356 | // topic suggestions
2357 | if ((sel & 1) == 1) waitForKeyElements(`div[data-testid=sidebarColumn] section [href^="/i/topics/"]`, e => $(e).parents("section").parent().parent().remove())
2358 |
2359 | // user suggestions (Who to follow, You might like)
2360 | if ((sel & 2) == 2) waitForKeyElements(`div[data-testid=sidebarColumn] aside [data-testid=UserCell]`, e => $(e).parents("aside").parent().remove())
2361 | }
2362 |
2363 |
2364 | // settings
2365 | if (onPage("settings") && !isModal) {
2366 | if (path().startsWith("settings/gt2")) {
2367 | } else {
2368 | if (window.innerWidth < 1005) {
2369 | $("main section").remove()
2370 | }
2371 | $(".gt2-settings-header, .gt2-settings").remove()
2372 | }
2373 | } else if (!isModal) {
2374 | $(".gt2-settings-header, .gt2-settings").remove()
2375 | }
2376 |
2377 |
2378 | // tweet
2379 | if (onSubPage(null, ["status"])) {
2380 | $("body").addClass("gt2-page-tweet")
2381 | // scroll up on load
2382 | waitForKeyElements("[data-testid=tweet] + div [href$=source-labels]", () => window.scroll(0, window.pageYOffset - 56.79999923706055))
2383 | } else if (!isModal) {
2384 | $("body").removeClass("gt2-page-tweet")
2385 | }
2386 |
2387 |
2388 | // notifications
2389 | // if (onPage("notifications")) {
2390 | // $("body").on("auxclick", `[data-testid=primaryColumn] section > div > div > div:not(.gt2-handled)`, function(e) {
2391 | // e.preventDefault()
2392 | // e.stopImmediatePropagation()
2393 | // console.log(this);
2394 | // })
2395 | // $("body").on("mouseover", `[data-testid=primaryColumn] section > div > div > div`, function(e) {
2396 | // console.log("a");
2397 | // $(e).addClass("gt2-handled")
2398 | // $(e).off("mousedown")
2399 | // })
2400 | // }
2401 |
2402 |
2403 | // sidebar
2404 | let sidebarContent = []
2405 |
2406 | // update changelog
2407 | if (!GM_getValue(`sb_notice_ack_update_${GM_info.script.version}`)
2408 | && GM_getValue("opt_gt2").updateNotifications) {
2409 | sidebarContent.push(getUpdateNotice())
2410 | }
2411 | sidebarContent.push(getDashboardProfile())
2412 |
2413 |
2414 | // assume profile page
2415 | if (!isModal) {
2416 | if (!(onPage("", "explore", "home", "hashtag", "i", "messages", "notifications", "places", "search", "settings")
2417 | || onSubPage(null, ["followers", "followers_you_follow", "following", "lists", "moments", "status", "topics"]))) {
2418 | $("body").addClass("gt2-page-profile").removeClass("gt2-profile-not-found")
2419 | $("[class^=gt2-blocked-profile-]").remove()
2420 | $(".gt2-tco-expanded").removeClass("gt2-tco-expanded")
2421 | if (GM_getValue("opt_gt2").legacyProfile) {
2422 | if ($("body").attr("data-gt2-prev-path") != path()) {
2423 | $("a[href$='/photo'] img").data("alreadyFound", false)
2424 | }
2425 | rebuildLegacyProfile()
2426 | }
2427 | if (GM_getValue("opt_gt2").leftMedia
2428 | && ((!GM_getValue("opt_gt2").smallSidebars && window.innerWidth > 1350)
2429 | || (GM_getValue("opt_gt2").smallSidebars && window.innerWidth > 1230))) {
2430 |
2431 | waitForKeyElements("[data-testid=sidebarColumn] a:nth-child(1) [data-testid=tweetPhoto]", e => {
2432 | if ($(".gt2-profile-media").length) $(".gt2-profile-media").remove()
2433 | let $mediaContainer = $(e).parents("a[role=link]").parent().parent().parent().parent().parent()
2434 | if ($mediaContainer.parent().children().length == 1) $mediaContainer = $mediaContainer.parent()
2435 | $mediaContainer.detach().addClass("gt2-profile-media")
2436 | .appendTo(".gt2-left-sidebar")
2437 | })
2438 |
2439 | }
2440 | } else {
2441 | $("body").removeClass("gt2-page-profile")
2442 | $(".gt2-legacy-profile-banner, .gt2-legacy-profile-nav").remove()
2443 | $(".gt2-legacy-profile-info").remove()
2444 | }
2445 | }
2446 |
2447 |
2448 | // add elements to sidebar
2449 | addToSidebar(sidebarContent)
2450 |
2451 |
2452 | // blocked profile page
2453 | waitForKeyElements(`div[data-testid=placementTracking] div[data-testid$="-unblock"],
2454 | [data-testid=emptyState] [href="https://support.twitter.com/articles/20172060"]`, displayBlockedProfileData)
2455 |
2456 |
2457 | // disableAutoRefresh
2458 | if (GM_getValue("opt_gt2").disableAutoRefresh &&
2459 | (path().split("/")[0] == "home" || path().match(/^[^\/]+\/lists/)) ) {
2460 | hideTweetsOnAutoRefresh()
2461 | }
2462 |
2463 |
2464 | // force latest
2465 | if (GM_getValue("opt_gt2").forceLatest && path().split("/")[0] == "home") {
2466 | forceLatest()
2467 | }
2468 |
2469 | if (!isModal) $("body").attr("data-gt2-prev-path", path())
2470 | }
2471 | urlChange("init")
2472 |
2473 |
2474 | // run urlChange() when history changes
2475 | // https://github.com/Bl4Cc4t/GoodTwitter2/issues/96
2476 | const exportFunc = typeof exportFunction === "function" ? exportFunction : (fn => fn)
2477 | const pageWindow = unsafeWindow.wrappedJSObject || unsafeWindow
2478 | const pageHistory = pageWindow.History.prototype
2479 |
2480 | const origPush = exportFunc(pageHistory.pushState, pageWindow)
2481 | pageHistory.pushState = exportFunc(function () {
2482 | let path = arguments[2].slice(1)
2483 | beforeUrlChange(path)
2484 | origPush.apply(this, arguments)
2485 | urlChange("push", path)
2486 | }, pageWindow)
2487 |
2488 | const origRepl = exportFunc(pageHistory.replaceState, pageWindow)
2489 | pageHistory.replaceState = exportFunc(function () {
2490 | let path = arguments[2].slice(1)
2491 | beforeUrlChange(path)
2492 | origRepl.apply(this, arguments)
2493 | urlChange("replace", path)
2494 | }, pageWindow)
2495 |
2496 | window.addEventListener("popstate", function() {
2497 | beforeUrlChange(getPath())
2498 | urlChange("pop", getPath())
2499 | })
2500 |
2501 | })(jQuery, waitForKeyElements)
2502 |
--------------------------------------------------------------------------------
/love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimdenGD/Twitter2015/bca9a31c208739de8efde60aca710c2740845423/love.png
--------------------------------------------------------------------------------
/twitter2015.user.css:
--------------------------------------------------------------------------------
1 | /* ==UserStyle==
2 | @name Twitter 2015
3 | @author dimden.dev
4 | @description Returns great Twitter's style from 2015. Requires GoodTwitter2!
5 | @namespace github.com/dimdenGD/Twitter2015
6 | @version 1.0.0
7 | @license MIT
8 | ==/UserStyle== */
9 |
10 | @-moz-document domain("twitter.com") {
11 | .r-1niwhzg::before {
12 | color: #ccd6dd;
13 | }
14 | div[data-testid="unlike"] .r-1niwhzg::before {
15 | color: rgb(224, 36, 94);
16 | }
17 |
18 | div[data-testid="unretweet"] .r-1niwhzg::before {
19 | color: rgb(23, 191, 99);
20 | }
21 |
22 | .r-xoduu5 {
23 | color: #8899a6;
24 | }
25 |
26 | div[aria-label^="Follow"], .r-1niwhzg.r-1ets6dv.r-sdzlij {
27 | background-image: linear-gradient(#fff,#f5f8fa);
28 | background-color: rgb(15, 20, 25) !important;
29 | border: 1px solid #e1e8ed;
30 | }
31 |
32 | div[aria-label^="Follow"] > div > span, .r-1niwhzg.r-1ets6dv.r-sdzlij > div > span {
33 | color: #292f33;
34 | }
35 | .r-18jsvk2 {
36 | font-weight: 400;
37 | font-size: 16px;
38 | line-height: 22px;
39 | }
40 | .r-14j79pv {
41 | color: #8899a6;
42 | font-size: 13px;
43 | }
44 | .r-14j79pv.r-1loqt21.r-1q142lx {
45 | place-self: center;
46 | }
47 | .r-1awozwy.r-18jsvk2.r-6koalj {
48 | font-weight: bold;
49 | color: #292f33;
50 | }
51 | .gt2-legacy-profile-name {
52 | font-size: 22px;
53 | font-weight: 700;
54 | line-height: 1;
55 | text-rendering: optimizeLegibility;
56 | font-family: sans-serif !important;
57 | }
58 | .gt2-legacy-profile-nav-center {
59 | color: #66757f;
60 | font-size: 11px;
61 | letter-spacing: .02em;
62 | text-transform: uppercase;
63 | }
64 | .gt2-opt-legacy-profile.gt2-page-profile .gt2-legacy-profile-nav .gt2-legacy-profile-nav-left>img {
65 | border-radius: 10px !important;
66 | }
67 | .gt2-toggle-navbar-dropdown>img {
68 | border-radius: 3px !important;
69 | }
70 | button {
71 | border-radius: 3px !important;
72 | }
73 | .r-18jsvk2.r-6koalj.r-eqz5dr, .r-14j79pv.r-6koalj.r-eqz5dr.r-37j5jr {
74 | font-size: 18px;
75 | }
76 | .r-18jsvk2.r-6koalj.r-eqz5dr {
77 | color: black;
78 | }
79 | .r-1awozwy.r-1loqt21.r-6koalj:after {
80 | background-color: transparent !important;
81 | }
82 | .r-14j79pv.r-6koalj.r-eqz5dr.r-37j5jr {
83 | color: rgb(27, 149, 224);
84 | }
85 | .r-1awozwy.r-1ro0kt6.r-18u37iz, div[aria-label^='Timeline: '] {
86 | border: 1px solid #e1e8ed;
87 | }
88 |
89 | .public-DraftStyleDefault-block {
90 | color: black;
91 | }
92 |
93 | .gt2-nav .gt2-nav-right .gt2-compose, .r-1867qdf {
94 | border-radius: 3px;
95 | }
96 | .r-1f1sjgu {
97 | padding-top: 8px;
98 | padding-bottom: 8px;
99 | }
100 | }
101 |
102 | @-moz-document url-prefix("https://twitter.com/messages") {
103 | .gt2-legacy-profile-banner, .gt2-legacy-profile-nav {
104 | display: none;
105 | }
106 | }
107 |
108 | @-moz-document domain("twitter.com") {
109 | .r-sdzlij, img.actioned-user-profile-img, .Tweet-avatar{
110 | border-radius: 5px;
111 | }
112 | html.dark .avatar {
113 | border-radius: 5px;
114 | }
115 | .avatar, .Avatar, .nav .session .dropdown-toggle, .ProfileAvatar, .ProfileAvatar-image, .ProfileAvatar-placeholderImage, .DashboardProfileCard-avatarImage, .ProfileUserList .Avatar, .ProfileCard-avatarImage, .AdaptiveNewsHeadlineDetails-userImage, .ProfileCard-avatarLink, .ProfileCardMini-avatarImage, .MomentCapsuleItemTweet--withText .MomentUserByline-avatar, .ProfileCard-avatarLink, .nav .session .dropdown-toggle::before, .MomentCapsuleCover .MomentUserByline-avatar, .avatar--circular, .ProfileAvatarEditing, .ProfileAvatarEditing-button, .ProfileAvatarEditing-overlay, .Gallery.with-tweet.without-tweet .Gallery-media, #activity-popup-dialog:not(.reply-users-popup) .activity-popup-dialog-users .account .avatar, #session h2 img, .subject .photo, .facepile li img, .tweet-image img, .DashboardProfileCard-avatarLink, .u04__user-profile-img, #activity-popup-dialog:not(.reply-users-popup) .activity-popup-dialog-users .account .avatar, .DMAvatar{
116 | border-radius: 5px;
117 | }
118 | .EdgeButton, .EdgeButton:visited, .RichEditor, .TwitterCard .EdgeButton, .TwitterCard .EdgeButton:visited {
119 | border-radius: 5px;
120 | }
121 | .global-nav .search-input, .trends-search-locations.trend-locations-section input, .FollowButton--edge {
122 | border-radius: 5px !important;
123 | }
124 | }
125 |
126 | @-moz-document domain("mobile.twitter.com") {
127 | .r-sdzlij{
128 | border-radius: 5px !important;
129 | }
130 |
131 | .r-sdzlij {
132 | border-radius: 5px !important;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------