// ==UserScript==
// @author Jimmy Chin
// @name Twitter Original Images Extractor
// @version 1.0.11
// @include https://twitter.com*
// @description Extract original size images for Twitter.
// @namespace https://greasyfork.org/users/241557
// ==/UserScript==
javascript:(function(){
const SELECTOR = {
ARTICLE_CLASS: "css-1dbjc4n r-18u37iz r-1ny4l3l r-1udh08x r-1qhn6m8 r-i023vh",
BUTTON_BAR_CLASS_ON_TIMELINE: ".css-1dbjc4n.r-1kbdv8c.r-18u37iz.r-1wtj0ep.r-1s2bzr4.r-hzcoqn",
BUTTON_BAR_CLASS_ON_ARTICLE_IN_DIM_BACKGROUND: ".css-1dbjc4n.r-1oszu61.r-1ila09b.r-rull8r.r-qklmqi.r-1kfrmmb.r-1efd50x.r-5kkj8d.r-1kbdv8c.r-18u37iz.r-h3s6tt.r-1wtj0ep.r-3qxfft.r-s1qlax",
BUTTON_BAR_CLASS_ON_ARTICLE_IN_DEFAULT_BACKGROUND: ".css-1dbjc4n.r-1oszu61.r-j5o65s.r-rull8r.r-qklmqi.r-1dgieki.r-1efd50x.r-5kkj8d.r-1kbdv8c.r-18u37iz.r-h3s6tt.r-1wtj0ep.r-3qxfft.r-s1qlax",
BUTTON_BAR_CLASS_ON_ARTICLE_IN_LIGHT_OUT_BACKGROUND: ".css-1dbjc4n.r-1oszu61.r-1igl3o0.r-rull8r.r-qklmqi.r-2sztyj.r-1efd50x.r-5kkj8d.r-1kbdv8c.r-18u37iz.r-h3s6tt.r-1wtj0ep.r-3qxfft.r-s1qlax",
TIME_CLASS_ON_ARTICLE: "css-4rbku5.css-18t94o4.css-901oao.css-16my406.r-111h2gw.r-1loqt21.r-poiln3.r-bcqeeo.r-qvutc0"
}
const ATTRIBUTE = {
BUTTON_CLASS: "css-1dbjc4n r-18u37iz r-1h0z5md r-3qxfft r-h4g966 r-rjfia"
}
const BACKGROUND_COLOR = {
DEFAULT: "rgb(255, 255, 255)",
DIM: "rgb(21, 32, 43)",
LIGHTS_OUT: "rgb(0, 0, 0)"
}
const PATTERN = {
IMG: /^https:\/\/pbs.twimg.com\/media\//
}
init();
function init() {
getTweetData();
setDOMObserver();
}
function setDOMObserver() {
const DOMObserver = new MutationObserver(() => getTweetData());
DOMObserver.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});
}
function getTweetData() {
const tweets = document.querySelectorAll("article");
if (!isEmpty(tweets)) {
tweets.forEach(tweet => {
setTimeout(() => {
if (!hasButtonBeenCreated(tweet)) {
if (isArticle(tweet)) {
createButtonForArticle(tweet);
} else {
createButtonForTimeline(tweet);
}
}
}, 300);
});
}
}
function isArticle(tweet) {
if (!isEmpty(tweet)) {
return tweet.getAttribute("class") == SELECTOR.ARTICLE_CLASS;
}
}
function hasButtonBeenCreated(tweet) {
if (isArticle(tweet)) {
return document.querySelector("#buttonForArticle");
} else {
return document.querySelector(`#button${getTweetIdFromTweet(tweet)}`);
}
}
function createButtonForArticle(tweet) {
if (!isEmpty(tweet)) {
const buttonBar = document.querySelector(getButtonBarClassOnArticle());
if (!isEmpty(buttonBar)) {
const button = document.createElement("div");
const lastButton = buttonBar.lastChild;
const buttonWidth = lastButton.clientWidth;
const buttonHeight = lastButton.clientHeight;
button.setAttribute("class", ATTRIBUTE.BUTTON_CLASS);
button.innerHTML = getButtonInnerHtml(buttonWidth, buttonHeight);
button.id = "buttonForArticle";
buttonBar.appendChild(button);
button.addEventListener("click", () => {
const imgs = getImageFromTweet(tweet);
if (!isEmpty(imgs)) {
imgs.forEach(url => window.open(url));
}
});
}
}
}
function createButtonForTimeline(tweet) {
if (!isEmpty(tweet)) {
const buttonBar = tweet.querySelector(SELECTOR.BUTTON_BAR_CLASS_ON_TIMELINE);
if (!isEmpty(buttonBar)) {
const button = document.createElement("div");
const buttonCss = buttonBar.firstChild.getAttribute("class");
const lastButton = buttonBar.lastChild;
const buttonWidth = lastButton.clientWidth;
const buttonHeight = lastButton.clientHeight;
lastButton.setAttribute("class", buttonCss)
button.setAttribute("class", buttonCss);
button.innerHTML = getButtonInnerHtml(buttonWidth, buttonHeight);
button.id = `button${getTweetIdFromTweet(tweet)}`;
buttonBar.appendChild(button);
button.addEventListener("click", () => {
const imgs = getImageFromTweet(tweet);
if (!isEmpty(imgs)) {
imgs.forEach(url => window.open(url));
}
});
}
}
}
function getTweetIdFromTweet(tweet) {
let result = null;
if (!isEmpty(tweet)) {
const timeObj = tweet.querySelector("time");
if (!isEmpty(timeObj)) {
const href = timeObj.parentNode.href;
const lastSlashIndex = href.lastIndexOf("/");
result = href.substr(lastSlashIndex + 1);
}
}
return result;
}
function getImageFromTweet(tweet) {
let result = null;
if (!isEmpty(tweet)) {
const imgs = [...tweet.querySelectorAll("img")];
if (!isEmpty(imgs)) {
const srcArray = [];
imgs.filter(img => img.src.match(PATTERN.IMG))
.forEach(img => srcArray.push(img.src));
result = transImageUrlToOrig(srcArray);
}
}
return result;
}
function transImageUrlToOrig(imgs) {
const result = [];
if (!isEmpty(imgs)) {
imgs.forEach(img => {
/* get the "name" param index of the query string*/
const nameParamKeyOfQueryString = img.indexOf("name=");
const origImageUrl = img.substr(0, nameParamKeyOfQueryString) + "name=orig";
result.push(origImageUrl);
});
}
return result;
}
function isEmpty(data) {
let result = false;
if (data != undefined && data != null) {
if (Array.isArray(data)) {
if (data.length == 0) {
result = true;
}
} else if (typeof data == "string") {
if (data.trim() == "") {
result = true;
}
}
} else {
result = true;
}
return result;
}
function getBGColor() {
return document.body.style.backgroundColor;
}
function getButtonInnerHtml(buttonWidth, buttonHeight) {
let result = null;
switch (getBGColor()) {
case BACKGROUND_COLOR.DEFAULT:
result = getDefaultButtonInnerHtml(buttonWidth, buttonHeight);
break;
case BACKGROUND_COLOR.DIM:
result = getDimButtonInnerHtml(buttonWidth, buttonHeight);
break;
case BACKGROUND_COLOR.LIGHTS_OUT:
result = getLightsOutButtonInnerHtml(buttonWidth, buttonHeight);
break;
}
return result;
}
function getButtonBarClassOnArticle() {
let result = null;
switch (getBGColor()) {
case BACKGROUND_COLOR.DEFAULT:
result = SELECTOR.BUTTON_BAR_CLASS_ON_ARTICLE_IN_DEFAULT_BACKGROUND;
break;
case BACKGROUND_COLOR.DIM:
result = SELECTOR.BUTTON_BAR_CLASS_ON_ARTICLE_IN_DIM_BACKGROUND;
break;
case BACKGROUND_COLOR.LIGHTS_OUT:
result = SELECTOR.BUTTON_BAR_CLASS_ON_ARTICLE_IN_LIGHT_OUT_BACKGROUND;
break;
}
return result;
}
function getDefaultButtonInnerHtml(buttonWidth, buttonHeight) {
return `<a>
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="${buttonWidth}" height="${buttonHeight}"
viewBox="0 0 172 172"
style=" fill:#000000;"><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g fill="#657786"><path d="M99.33,37.84c-1.73344,0.215 -3.02344,1.69313 -3.01,3.44v24.295c-39.60031,1.20938 -60.9525,22.17188 -71.4875,43c-10.68281,21.11031 -11.05906,41.69656 -11.0725,42.355v0.215v0.215c-0.05375,1.89469 1.43781,3.49375 3.3325,3.5475c1.89469,0.05375 3.49375,-1.43781 3.5475,-3.3325v-0.215c0.02688,-0.3225 0.79281,-10.73656 10.6425,-21.8225c9.675,-10.87094 28.55469,-21.83594 65.0375,-22.575v23.7575c0,1.31688 0.7525,2.52625 1.94844,3.10406c1.19594,0.57781 2.60688,0.41656 3.64156,-0.41656l55.04,-44.72c0.81969,-0.65844 1.29,-1.63937 1.29,-2.6875c0,-1.04812 -0.47031,-2.02906 -1.29,-2.6875l-55.04,-44.72c-0.72562,-0.59125 -1.65281,-0.86 -2.58,-0.7525zM103.2,48.4825l46.1175,37.5175l-46.1175,37.5175v-20.3175c0,-1.89469 -1.54531,-3.44 -3.44,-3.44c-40.43344,0 -62.22906,12.42969 -73.6375,25.2625c-0.43,0.48375 -0.67187,0.91375 -1.075,1.3975c1.45125,-4.73 3.35938,-9.7825 5.9125,-14.835c10.03781,-19.83375 29.19969,-39.345 68.8,-39.345c1.89469,0 3.44,-1.54531 3.44,-3.44z"></path></g></g>
</svg>
<div class="css-1dbjc4n r-sdzlij r-1p0dtai r-xoduu5 r-1d2f490 r-xf4iuw r-1ny4l3l r-u8s1d r-zchlnj r-ipm5af r-o7ynqc r-6416eg"></div>
<a>`;
}
function getDimButtonInnerHtml(buttonWidth, buttonHeight) {
return `<a>
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="${buttonWidth}" height="${buttonHeight}"
viewBox="0 0 172 172"
style=" fill:#000000;"><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g fill="#8899a6"><path d="M99.33,37.84c-1.73344,0.215 -3.02344,1.69313 -3.01,3.44v24.295c-39.60031,1.20938 -60.9525,22.17188 -71.4875,43c-10.68281,21.11031 -11.05906,41.69656 -11.0725,42.355v0.215v0.215c-0.05375,1.89469 1.43781,3.49375 3.3325,3.5475c1.89469,0.05375 3.49375,-1.43781 3.5475,-3.3325v-0.215c0.02688,-0.3225 0.79281,-10.73656 10.6425,-21.8225c9.675,-10.87094 28.55469,-21.83594 65.0375,-22.575v23.7575c0,1.31688 0.7525,2.52625 1.94844,3.10406c1.19594,0.57781 2.60688,0.41656 3.64156,-0.41656l55.04,-44.72c0.81969,-0.65844 1.29,-1.63937 1.29,-2.6875c0,-1.04812 -0.47031,-2.02906 -1.29,-2.6875l-55.04,-44.72c-0.72562,-0.59125 -1.65281,-0.86 -2.58,-0.7525zM103.2,48.4825l46.1175,37.5175l-46.1175,37.5175v-20.3175c0,-1.89469 -1.54531,-3.44 -3.44,-3.44c-40.43344,0 -62.22906,12.42969 -73.6375,25.2625c-0.43,0.48375 -0.67187,0.91375 -1.075,1.3975c1.45125,-4.73 3.35938,-9.7825 5.9125,-14.835c10.03781,-19.83375 29.19969,-39.345 68.8,-39.345c1.89469,0 3.44,-1.54531 3.44,-3.44z"></path></g></g>
</svg>
<div class="css-1dbjc4n r-sdzlij r-1p0dtai r-xoduu5 r-1d2f490 r-xf4iuw r-1ny4l3l r-u8s1d r-zchlnj r-ipm5af r-o7ynqc r-6416eg"></div>
<a>`;
}
function getLightsOutButtonInnerHtml(buttonWidth, buttonHeight) {
return `<a>
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="${buttonWidth}" height="${buttonHeight}"
viewBox="0 0 172 172"
style=" fill:#000000;"><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g fill="#6e767d"><path d="M99.33,37.84c-1.73344,0.215 -3.02344,1.69313 -3.01,3.44v24.295c-39.60031,1.20938 -60.9525,22.17188 -71.4875,43c-10.68281,21.11031 -11.05906,41.69656 -11.0725,42.355v0.215v0.215c-0.05375,1.89469 1.43781,3.49375 3.3325,3.5475c1.89469,0.05375 3.49375,-1.43781 3.5475,-3.3325v-0.215c0.02688,-0.3225 0.79281,-10.73656 10.6425,-21.8225c9.675,-10.87094 28.55469,-21.83594 65.0375,-22.575v23.7575c0,1.31688 0.7525,2.52625 1.94844,3.10406c1.19594,0.57781 2.60688,0.41656 3.64156,-0.41656l55.04,-44.72c0.81969,-0.65844 1.29,-1.63937 1.29,-2.6875c0,-1.04812 -0.47031,-2.02906 -1.29,-2.6875l-55.04,-44.72c-0.72562,-0.59125 -1.65281,-0.86 -2.58,-0.7525zM103.2,48.4825l46.1175,37.5175l-46.1175,37.5175v-20.3175c0,-1.89469 -1.54531,-3.44 -3.44,-3.44c-40.43344,0 -62.22906,12.42969 -73.6375,25.2625c-0.43,0.48375 -0.67187,0.91375 -1.075,1.3975c1.45125,-4.73 3.35938,-9.7825 5.9125,-14.835c10.03781,-19.83375 29.19969,-39.345 68.8,-39.345c1.89469,0 3.44,-1.54531 3.44,-3.44z"></path></g></g>
</svg>
<div class="css-1dbjc4n r-sdzlij r-1p0dtai r-xoduu5 r-1d2f490 r-xf4iuw r-1ny4l3l r-u8s1d r-zchlnj r-ipm5af r-o7ynqc r-6416eg"></div>
<a>`;
}
})();