// ==UserScript==
// @name Zoom In Twitter Image
// @version 1.1.2
// @description Support 2019 new UI Twitter only.
// @author Hayao-Gai
// @namespace https://github.com/HayaoGai
// @icon https://i.imgur.com/M9oO8K9.png
// @include https://twitter.com/*
// @require http://code.jquery.com/jquery-3.4.1.slim.min.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
const $ = window.jQuery;
const urlLoad = "https://media.giphy.com/media/kDSfTo0HP8v6POpYO3/giphy.gif";
let canvas, loading, push, showing = false, waiting = false;
$(document).ready(() => {
createCanvas();
createLoading();
observeSystem();
detectAddress();
detectSwitch();
});
// 定義 Canvas
function createCanvas() {
canvas = $("<img/>").css({
"position": "fixed",
"width": document.documentElement.clientWidth + "px",
"height": document.documentElement.clientHeight + "px",
"pointerEvents": "none",
"display": "inline",
"opacity": "0",
"transition": "opacity 0.4s"
}).appendTo("body");
}
// 定義 Loading
function createLoading() {
loading = $("<img/>", {
src: urlLoad
}).css({
"position": "fixed",
"width": "44px",
"top": "44px",
"left": (document.documentElement.clientWidth / 2 - 22) + "px",
"top": (document.documentElement.clientHeight / 2 - 22) + "px",
"display": "inline",
}).appendTo("body");
loading.hide();
}
// 觀察文件是否產生變化
function observeSystem() {
setTimeout(() => {
const h1 = $("section").find("H1");
const title = $("title");
if (h1.length !== 2 || title.length === 0) observeSystem();
else {
// 獲取目標
const target1 = [...h1[0].parentElement.childNodes].filter(child => child.tagName !== "H1")[0].childNodes[0].childNodes[0];
const target2 = title[0];
// 先執行一次
setTimeout(addListener, 1000);
// 建立觀察者,文件有變化就執行下列函式
const mutation = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; //前綴
const observer = new mutation(addListener);
// 設定觀察選項
const config = { attributes: true, childList: true, characterData: true };
// 開始觀察
observer.observe(target1, config); //時間軸
observer.observe(target2, config); //標籤頁
}
}, 500);
}
// 偵測是否僅切換地址
function detectAddress() {
let pushState = history.pushState;
history.pushState = function () {
pushState.apply(history, arguments);
observeSystem();
};
}
// 偵測上一頁、下一頁
function detectSwitch() {
window.addEventListener("popstate", observeSystem);
}
// 增加監聽的物件
function addListener() {
if (location.href.match("photo/1")) return; //點進圖片後,不做放大效果
// 縮圖
$(".css-9pa8cd").each((index, image) => {
$(image).on("mousemove", showImage);
$(image).on("mouseleave", hideImage);
});
// 展開按鈕
$('[role="button"]').each((index, button) => {
$(button).on("click", observeSystem);
});
// 上方標籤
$('[role="tab"]').each((index, tab) => {
$(tab).on("click", () => { setTimeout(() => observeSystem, 3000); });
});
}
// 顯示圖片
function showImage() {
// 避免多次呼叫
if (showing) return;
showing = true;
// 取得圖片原始尺寸的網址
const url = getImage(this.src);
if (url === null) return;
// 設定圖片網址
canvas.attr("src", url);
setImage();
}
// 設定圖片
function setImage() {
// 等待至取得圖片寬高為止
if (canvas[0].naturalWidth === 0)
{
// 避免 loading 不消失
if (!waiting) loading.show();
waiting = true;
setTimeout(setImage, 20);
return;
}
// 關掉 Loading
loading.hide();
// 調整圖片大小
const w = canvas[0].naturalWidth;
const h = canvas[0].naturalHeight;
const clientW = document.documentElement.clientWidth;
const clientH = document.documentElement.clientHeight;
const condition1 = w > clientW;
const condition2 = h > clientH;
if (condition1 && condition2) {
const rate = clientH / h;
const new_w = w * rate;
const new_h = clientH;
if (new_w > clientW) {
const rate2 = clientW / new_w;
const new_w2 = clientW;
const new_h2 = new_h * rate2;
canvas.css({ "width": new_w2 + "px", "height": new_h2 + "px" });
} else {
canvas.css({ "width": new_w + "px", "height": new_h + "px" });
}
} else if (condition1) {
const rate3 = clientW / w;
const new_h3 = h * rate3;
canvas.css({ "width": clientW + "px", "height": new_h3 + "px" });
} else if (condition2) {
const rate4 = clientH / h;
const new_w4 = w * rate4;
canvas.css({ "width": new_w4 + "px", "height": clientH + "px" });
} else {
canvas.css({ "width": w + "px", "height": h + "px" });
}
// 圖片位置
let left = clientW / 2 - canvas.width() / 2;
let top = clientH / 2 - canvas.height() / 2;
if (left < 0) left = 0;
if (top < 0) top = 0;
// 調整圖片屬性
canvas.css({
"left": left + "px",
"top": top + "px",
"display": "inline",
"opacity": "1"
});
}
// 取得圖片網址
function getImage(url) {
// 如果沒網址就直接跳開
if (url === null) return null;
// 網域
const m1 = url.split("/");
let newUrl = "https://pbs.twimg.com/";
// 狀況 1:一般圖
if (m1[3].match("media") !== null) {
for (let i = 3; i < m1.length; i++) {
if (i !== m1.length - 1) newUrl += m1[i] + "/";
else newUrl += m1[i].split("&")[0] + "&name=orig";
}
}
// 狀況 2:使用者頭像
else if (m1[3].match("profile") !== null) {
for (let i = 3; i < m1.length; i++) {
if (i !== m1.length - 1) newUrl += m1[i] + "/";
else {
const m2 = m1[i].split("_");
for (let i = 0; i < m2.length; i++) {
if (i === 0) newUrl += m2[i];
else if (i !== m2.length - 1) newUrl += "_" + m2[i];
else newUrl += "." + m2[i].split(".")[1];
}
}
}
}
// 狀況 3:影片 => 不放大縮圖
else {
newUrl = null;
}
return newUrl;
}
// 隱藏圖片
function hideImage() {
showing = false;
waiting = false;
// 重置 畫布
canvas.attr("src", null);
canvas.css({
"width": "0px",
"height": "0px",
"display": "none",
"opacity": "0"
});
// 重置 Loading
setTimeout(() => { loading.hide(); }, 50);
}
})();