微博图片全显示

同屏显示多图微博的全部大图。

// ==UserScript==
// @name        微博图片全显示
// @namespace   hzhbest
// @include     http://weibo.com/*
// @include     https://weibo.com/*
// @include     http://www.weibo.com/*
// @include     https://www.weibo.com/*
// @description    同屏显示多图微博的全部大图。
// @version     4.22
// @grant       GM_xmlhttpRequest
// @grant       unsafeWindow
// @grant       GM_getValue
// @grant       GM_setValue
// @run-at      document-end
// @license     GNU GPLv3
// ==/UserScript==

(function () {
	// --这里是设置区-- //
	var topheight = 60; //微博顶栏高度
	var topspare = 125; //滚动预留顶部高度
	var autorefresh = true; //是否定时自动检测页面变化
	var loadLargeGif = GM_getValue('WBimgAll') || false; //是否载入大型动图

	// --以下是代码区,请不要随意改动-- //

	// http://weibo.com/2710065263/BmxiVDCgt?from=page_1005052710065263_profile&wvr=6&mod=weibotime&type=comment#_rnd1436436058420
	// http://weibo.com/2328516855/CnYCvixUq?type=comment#_rnd1436493435761
	var regex = /weibo\.com\/\d{8,10}\/[a-z0-9A-Z]{9}\??/;
	var cur = -1	// “当前图片”序
	pinit();

	function pinit() {
		//检查是否已开大图,否则进入初始化进程;如果已开大图但网址已非单一微博网址则去除按钮;一秒检测一次
		var bpimg = document.querySelector("img.big_pic");
		console.log("matched?: ", regex.test(document.location.href));
		if (regex.test(document.location.href)) {
			//console.log(document.location.href);
			if (!bpimg) init();
		} else {
			var buttonbox = document.querySelector(".big_pic_b");
			if (!!buttonbox) buttonbox.parentNode.removeChild(buttonbox);
		}
		if (autorefresh) {
			setTimeout(pinit, 1000); //console.log("wait 1 sec");
		}
	}

	function init() {
		//通过评论框架确定页面载入完成,通过附加媒体容器确定有需要展开大图的情况,都成立时进入处理进程
		var list_ul = document.querySelector("div.vue-recycle-scroller__item-view"); //评论框架
		var expbox = document.querySelector('div[class*="picture-box_row_"'); //附加媒体容器
		document.querySelector('main>div[class^="Main_full_"]').style =
			"width: 800px;";
		if (!list_ul && !expbox) {
			console.log("no1"); // * [no1]未加载评论框架,等候
			setTimeout(init, 1000);
			return;
		} else if (!!list_ul && !expbox) {
			console.log("no2", expbox); // * [no2]评论框架已加载却无附加媒体容器,退出
			//getlongtext()
			return;
		} else {
			// * [go]一切正常,开始处理
			// 应对超过九图的情况
			var nyimg = document.querySelector(
				'[class*="woo-box-justifyCenter picture_mask_"]'
			);
			if (!!nyimg) {	//识别出超九图特征,将预览容器展开,以预览容器来获取图片
				nyimg.click();
				setTimeout(() => {
					expbox = document.querySelector('div[class*="picture-viewer_wrap_"]');
					console.log("go9");
					go(expbox);
					//console.log(expbox);
				}, 600);
			} else {
				console.log("go");
				go(expbox);
			}
		}
	}

	function go(expbox) {
		var feedbox = document.querySelector(".vue-recycle-scroller__item-wrapper"); //评论区容器元素
		var appbox = document.querySelector("WB_app_view"); //应用容器??
		var videobox = document.querySelector('div[class*="card-video_videoBox_"'); //视频容器
		var maintextimgs = document.querySelectorAll(
			'[class^="detail_wbtext_"]>a[target]'
		); //正文中的图片??
		var dbox =
			document.body.getElementsByTagName("main")[0].parentNode.parentNode; //主容器
		dbox.style.maxWidth = "none";

		// Insert CSS
		var headID = document.getElementsByTagName("head")[0];
		var cssNode = creaElemIn("style", headID);
		cssNode.type = "text/css";
		cssNode.innerHTML = [
			".big_pic{max-width: 890px;}",
			".big_pic_n{max-width: 500px;}",
			".big_pic:hover, .big_pic_n:hover{box-shadow: 0 0 30px 2px #f1ecdf;}",
			".big_pic_poster{outline: 2px dashed #fcde44; outline-offset: -2px; cursor: pointer;}",
			".big_pic_v{max-width: 90%; max-height: 80vh; cursor: pointer;}"
		].join(""); //大图样式
		cssNode.innerHTML += [
			'main>div[class^="Main_full_"] {width: auto !important;min-width:800px;}',
			".WB_frame_c {width: auto !important; max-width: 920px; min-width: 600px;}",
			".WB_text.W_f14, .repeat_list .list_box .WB_text, .WB_expand>.WB_text{width: 490px;}",
			".WB_frame_c .media_box{display: none !important;}",
			'div[node-type="comment_list"] .media_box{display: block !important;}',
			'div[id^="Pl_Core_RecommendFeed__"]{right: 150px; width: 100px !important; max-height: 35px; overflow: hidden; transition: all ease 0.2s 0.5s;}',
			'div[id^="Pl_Core_RecommendFeed__"]:hover{width: 300px !important; max-height: 1000px;}',
			'div[id^="Pl_Core_RecommendFeed__"] .opt_box{display:none;}',
			'div[id^="Pl_Core_RecommendFeed__"]:hover .opt_box{display: inline-block;}'
		].join(""); //微博自身框架样式
		cssNode.innerHTML += [
			".big_pic_b{position: fixed; left: 10px; top: 200px;}",
			".big_pic_btn{height: 20px; min-width: 50px; width: fit-content; padding: 3px; margin-bottom: 20px; border: 1px solid white; color: white; background: rgba(133,133,133,0.6); cursor: pointer; user-select: none;}",
			".big_pic_btn:hover{background: rgba(133,133,200,0.6);}",
			".big_pic_ns{margin-bottom: 20px; width: 62px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-gap: 3px;}",
			".big_pic_ns > div{font-size: 10px; line-height: 28px; text-align: right; height: 15px; width: 15px; background-clip: border-box; background-position: center; background-size: cover; padding: 3px; border: 1px solid #7a7a7a; color: white; text-shadow: 0 0 2px black,0 0 2px black,0 0 2px black; cursor: pointer; user-select: none; opacity: 0.7;}",
			".big_pic_ns > div:hover{font-size: 0px; outline: 1px solid #f8f87b; opacity: 1;}",
			".big_pic_ns > div.curr{outline: 3px solid #f87bce; opacity: 0.9;}"
		].join(""); //按钮样式

		var buttonbox = creaElemIn("div", document.body);
		buttonbox.className = "big_pic_b";
		var sclink = creaElemIn("div", buttonbox); //直达评论链接
		var tplink = creaElemIn("div", buttonbox); //直达页顶链接
		var nclink = creaElemIn("div", buttonbox); //图片限宽链接
		var n1link = creaElemIn("div", buttonbox); //首个图片链接
		var n2link = creaElemIn("div", buttonbox); //上个图片链接
		var nslink = creaElemIn("div", buttonbox); //图片导航按钮
		var n3link = creaElemIn("div", buttonbox); //下个图片链接
		var swmode = creaElemIn("div", buttonbox); //切换动图模式链接

		sclink.className = "big_pic_btn";
		sclink.innerHTML = "直达评论";
		sclink.addEventListener(
			"click",
			function () {
				var commentbox = document.querySelector("div.wbpro-tab3") || document.querySelector("#composerEle");
				scrollto(getTop(commentbox) - topheight * 2);
			},
			false
		);

		tplink.className = "big_pic_btn";
		tplink.innerHTML = "回到页顶";
		tplink.addEventListener(
			"click",
			function () {
				var headerbox = document.querySelector("header");
				scrollto(getTop(headerbox) - topheight * 2);
			},
			false
		);

		if (!!videobox) {
			cssNode.innerHTML = [
				".big_pic_sc{position: fixed; left:10px; padding: 3px; border: 1px solid white; color: white; background: rgba(133,133,133,0.6); cursor: pointer;} ",
				".big_pic_sc{top: 430px}",
			].join("");
			return;
		}

		if (!!appbox) {
			//检测到应用容器(微博文章或视频)时退出
			// box.appendChild(appbox);
			// if (!!wrpbox) expbox.removeChild(wrpbox);
			// cssNode.innerHTML = [
			// '.media_box{display: none !important;}',
			// '.big_pic_sc{position: fixed; left:10px; padding: 3px; border: 1px solid white; color: white; background: rgba(133,133,133,0.6); cursor: pointer;} ',
			// '.big_pic_sc{top: 430px}'].join("");
			return;
		}

		if (!!maintextimgs) {
			for (const i in maintextimgs) {
				if (/sinaimg.c(om|n)\/large/.test(maintextimgs[i].href)) {
					var mtimg = document.createElement("img");
					maintextimgs[i].parentNode.insertBefore(mtimg, maintextimgs[i]);
					mtimg.src = maintextimgs[i].href;
					mtimg.style =
						"border: 3px dotted #64882e; width: auto !important; height: auto !important; max-width: 500px;";
					maintextimgs[i].parentNode.removeChild(maintextimgs[i]);
				}
			}
		}

		//超过九图的应对
		var imgboxes;
		if (!!expbox.querySelector('[class*="picture-viewer_preview_"]')) {
			console.log("nine imgs");
			imgboxes = expbox.querySelectorAll(
				'[class*="picture-viewer_listContent_"]>div>div'
			);
			console.log(imgboxes.length);
		} else {
			imgboxes = expbox.querySelectorAll(
				'div[class*="woo-box-item-inlineBlock picture_item_"]'
			);
		}

		// 建立大图框架,用于插入大图
		var bpboxes = [],
			imgsrc,
			imgn,
			imgl = imgboxes.length;
		var _limited = false;
		var root = expbox.parentNode;
		nslink.className = "big_pic_ns";
		var nslinks = [];

		var j = 0;
		for (var i = 0; i < imgl; i++) {
			//提取大图
			// https://wx2.sinaimg.cn/orj360/006QkcF9ly1gz75df9qmuj30nn061dj1.jpg
			// https://wx2.sinaimg.cn/large/006QkcF9ly1gz75df9qmuj30nn061dj1.jpg
			// https://wx4.sinaimg.cn/large/002MwiQagy1gz7le15xtyj60k06851kx02.jpg
			// https://wx3.sinaimg.cn/large/003nJ9EBly1hakhpjuqugj60dc336n5l02.jpg
			bpboxes[i] = creaElemIn("div", root);
			nslinks[i] = creaElemIn("div", nslink);
			nslinks[i].innerHTML = (i + 1);
			nslinks[i].name = i;
			creaElemIn("br", root);
			var imgnode = imgboxes[i].querySelector("img");
			var imgvnode = imgboxes[i].querySelector("video"); //“动图”(实际上是mp4视频)
			// console.log('imgtest: #',i,/sinaimg.c(om|n)/.test(imgnode.src));
			if (/sinaimg.c(om|n)/.test(imgnode.src)) {	// 普通大图提取
				if (/sinaimg.c(om|n)\/(orj|thumb)\d{3}/.test(imgnode.src)) {
					imgsrc = imgnode.src.replace(
						/(sinaimg\.c(om|n)\/)(orj|thumb)\d{3}/,
						"$1large"
					);
				} else if (/sinaimg.c(om|n)\/large/.test(imgnode.src)) {
					imgsrc = imgnode.src;
				}
				imgn = creaElemIn("img", bpboxes[i]);
				imgn.src = imgsrc;
				imgn.className = "big_pic";
				imgn.title = "[ " + (i + 1) + " / " + imgl + " ]";
				nslinks[i].style.backgroundImage = 'url("' + imgnode.src + '")';
			} else if (!!imgvnode) {
				if (/sinaimg.c(om|n)\/(orj|thumb)\d{3}/.test(imgvnode.poster)) {	// 动图大图提取
					imgsrc = loadLargeGif
						? imgvnode.poster.replace(	// 动大图模式,使用封面的大图
							/(sinaimg\.c(om|n)\/)(orj|thumb)\d{3}/,
							"$1large"
						)
						: imgvnode.poster;	// 封面模式,直接用封面
				} else if (/sinaimg.c(om|n)\/large/.test(imgvnode.poster)) {	// 封面直接就是大图
					imgsrc = imgvnode.poster;
				}
				imgn = creaElemIn("img", bpboxes[i]);
				imgn.src = imgsrc;
				imgn.className = "big_pic" + ((loadLargeGif) ? "" : " big_pic_poster");
				imgn.title = "[ " + (i + 1) + " / " + imgl + " ] 点击以视频方式播放";
				imgn.onclick = function (event) {
					let pnode = event.target;
					let vnode = pnode.parentNode.getElementsByTagName("video")[0];
					pnode.style.display = "none";
					vnode.style.display = "block";
					vnode.play();
					console.log("p: ", "played");
				};
				nslinks[i].style.backgroundImage = 'url("' + imgvnode.poster + '")';
				bpboxes[i].appendChild(imgvnode);	// 将动图视频附在动图大图上,点击显示
				imgvnode.className = "big_pic_v";
				imgvnode.controls = true;
				imgvnode.style.display = "none";
				imgvnode.addEventListener("ended", function (event) {
					let vnode = event.target;
					let pnode = vnode.parentNode.getElementsByTagName("img")[0];
					console.log("p: ", pnode);
					vnode.style.display = "none";
					pnode.style.display = "block";
				});
				// imgvnode.onclick = function (event) {
				// 	console.log("v: ", "clicked");
				// 	//(vnode.paused)? vnode.play() : vnode.pause();
				// };
			} else {
				j += 1;
				continue;
			}
			nslinks[i].addEventListener(
				"click",
				function (e) {
					scrollto(getTop(bpboxes[e.target.name]) - topspare + 25);
				},
				false
			);
			if (j == imgl) return;
		}
		if (j == imgl) {
			//没找到符合条件的大图,退出
			cssNode.innerHTML =
				".big_pic_sc{position: fixed; left:10px; padding: 3px; border: 1px solid white; color: white; background: rgba(133,133,133,0.6); cursor: pointer;} .big_pic_sc{top: 430px}";
			return;
		}
		imgl = bpboxes.length;
		if (!!root) root.removeChild(expbox);

		nclink.className = "big_pic_btn";
		nclink.innerHTML = "图片限宽";
		nclink.addEventListener(
			"click",
			function () {
				var i;
				if (_limited) {
					for (i = 0; i < imgl; i++) {
						bpboxes[i].querySelector("img").className = "big_pic";
					}
					_limited = false;
				} else {
					for (i = 0; i < imgl; i++) {
						bpboxes[i].querySelector("img").className = "big_pic_n";
					}
					_limited = true;
				}
			},
			false
		);

		n1link.className = "big_pic_btn";
		n1link.innerHTML = "△首个图片";
		n1link.addEventListener(
			"click",
			function () {
				scrollto(getTop(bpboxes[0]) - topspare +25);
			},
			false
		);

		n2link.className = "big_pic_btn";
		n2link.innerHTML = "▲上个图片";
		n2link.addEventListener(
			"click",
			function () {
				var t = document.documentElement.scrollTop;
				for (var j = imgl - 1; j >= 0; j--) {
					if (t > getTop(bpboxes[j]) + bpboxes[j].offsetHeight - topspare) {
						scrollto(getTop(bpboxes[j]) - topspare + 25);
						return;
					}
				}
			},
			false
		);

		n3link.className = "big_pic_btn";
		n3link.innerHTML = "▼下个图片";
		n3link.addEventListener(
			"click",
			function () {
				var t = document.documentElement.scrollTop;
				for (var j = 0; j < imgl; j++) {
					if (t < getTop(bpboxes[j]) - topspare) {
						scrollto(getTop(bpboxes[j]) - topspare + 25);
						return;
					}
				}
			},
			false
		);

		swmode.className = "big_pic_btn";
		swmode.innerHTML = "↔切换动图模式";
		swmode.title = "切换为" + ((loadLargeGif) ? "显示封面静图" : "显示动图大图") + "并刷新页面";
		swmode.addEventListener(
			"click",
			function () {
				loadLargeGif = !loadLargeGif;
				GM_setValue('WBimgAll', loadLargeGif); 
				location.reload();
			},
			false
		);

		waitscroll();

		document.onscroll = function () {
			var t = document.documentElement.scrollTop; // 当前滚动位置(视框顶y)
			var w = window.innerHeight;	// 当前视框高度
			var percentage = 1 / 4;		// 视框内“注视框”距视框顶底距离(1-2*percentage 为注视框高度占比)
			var linetop = t + w * percentage - topspare;	// 注视框顶位置
			var linebtm = t + w * (1 - percentage);	// 注视框底位置
			var j, vh, vhmax = 0;	// 检查图片序、图片在注视框内高度、注视框内最大高度
			if (getTop(bpboxes[0]) >= linebtm || getTop(bpboxes[imgl - 1]) + bpboxes[imgl - 1].offsetHeight <= linetop) {
				cur = -1;	// 若首图在注视框底之下或末图在注视框顶之上,则无当前图
			} else {
				for (j = imgl - 1; j >= 0; j--) {	// 从底部检查各图片
					let ti = getTop(bpboxes[j]), hi = bpboxes[j].offsetHeight;	// 检查图片位置、检查图片高度
					if (ti < linebtm && (ti + hi) > linetop) {	// 若图片顶端在注视框底之上、底端在注视框顶之下
						vh = Math.min(linebtm, ti + hi) - Math.max(linetop, ti);	// 计算注视框内高度
						if (vh >= vhmax) {	// 当检查图片拥有更大的注视框内高度,则其为“当前图片”(等高则更前)
							vhmax = vh;
							cur = j;
						}
					}
				}
			}
			if (cur !== -1) {
				for (j = imgl - 1; j >= 0; j--) {
					if (j !== cur) {
						nslinks[j].classList.remove("curr");
					} else {
						nslinks[j].classList.add("curr");
					}
				}
			} else {
				for (j = imgl - 1; j >= 0; j--) {
					if (j !== cur) {
						nslinks[j].classList.remove("curr");
					}
				}
			}
		};
	}

	function waitscroll() {
		//等待页面完全载入再滚动
		var list_ul = document.querySelector("div.list_ul"); //↓等待评论框架载入,如果评论框架就位就等待评论区或无评论提示载入,再视滚动位置判断
		if (
			!list_ul ||
			!list_ul.getElementsByTagName("div")[0] ||
			!document.querySelector("div.tips_rederror") ||
			document.documentElement.scrollTop < topheight + 70
		) {
			setTimeout(waitscroll, 300); //console.log("wait");
			return;
		} else {
			scrollto(topheight); //console.log("scroll");
		}
	}

	function scrollto(pos) {
		//滚动
		document.documentElement.scrollTop = pos;
	}

	// Create an element
	function creaElemIn(tagname, destin) {
		var theElem = destin.appendChild(document.createElement(tagname));
		return theElem;
	}
	// Get the first element by xpath
	function getElem(xpath) {
		return document
			.evaluate(
				xpath,
				document,
				null,
				XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
				null
			)
			.snapshotItem(0);
	}
	// Get the absolute top of an element
	function getTop(e) {
		var offset = e.offsetTop;
		if (e.offsetParent != null) offset += getTop(e.offsetParent);
		return offset;
	}

})();