您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show all the large photos on a listing
// ==UserScript== // @name TradeMe Show All The Photos (Tampermonkey only) // @namespace http://drsr/ // @version 0.9.6 // @run-at document-idle // @description Show all the large photos on a listing // @include /https:\/\/www\.trademe\.co\.nz\/.*\/[Ll]isting.*/ // @include /https:\/\/www\.trademe\.co\.nz\/.*\/auction-.*/ // @include /https:\/\/www\.trademe\.co\.nz\/a\.aspx.*/ // @include /https:\/\/www\.trademe\.co\.nz\/[0-9]*/ // @grant GM_addStyle // @grant GM_getResourceURL // @copyright public domain // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js // @require http://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js // @require https://greasyfork.org/scripts/2723-bpopup/code/bPopup.js?version=7539 // ==/UserScript== // v0.9.3: support for new tg and gallery formats // v0.9.2: rough support for a new lightbox format found in realestate // v0.9: https // v0.8: update for new listing format // v0.7: external bPopup to comply with greasyfork rules // v0.6: fix for new large image URLs, don't show link when there's only one photo // v0.5: when browser window is resized or maximized, recentre and adjust number of columns // v0.4: tweak layout, Greasemonkey 1.0 // v0.3: minor tidyup // v0.2: compact and recenter image grid when images are smaller than max size // TODO: // lazy load order is strange, image#1 gets loaded last? // replace trademe's JS error handler window.onerror=function(msg, url, linenumber){ console.log('Error message: '+msg+'\nURL: '+url+'\nLine Number: '+linenumber); return true; }; // make sure the JQuery is the one loaded by the @require var myJQ = jQuery.noConflict(); var initialised = false; var initialHeight = 0; var saveTop = 0; var resizeTimer = null; var fullImageUrls = [] var TG_THUMB_SELECTOR = ".tm-marketplace-listing-photos__thumbnail-slider-item,.tm-listing-photos__thumbnail-slider-item"; function init() { if (initialised) { return; } initialHeight = myJQ(document).height(); GM_addStyle("\ .tmsatp_hide {\ position: absolute;\ top: -9999px;\ left: -9999px;\ background-color: #000000;\ }\ .tmsatp_closebutton {\ border-radius: 12px 12px 12px 12px;\ box-shadow: none;\ font: 20px sans-serif;\ padding: 0 20px 4px;\ position: absolute;\ right: -10px;\ top: -6px;\ color: white;\ background-color: #7A0000;\ cursor: pointer;\ display: inline-block;\ text-align: center;\ text-decoration: none;\ }\ .tmsatp_closebutton:hover {\ background-color: #9A0000;\ }\ .tmsatp_imgcell {\ text-align:center;\ vertical-align:middle;\ padding: 0;\ border: 4px solid black;\ }\ .tmsatp_largerCell {\ width:800px;\ min-width:800px;\ height:600px;\ min-height:600px;\ }\ .tmsatp_smallerCell {\ width:669px;\ min-width:669px;\ height:502px;\ min-height:502px;\ }\ .tmsatp_table {\ border-width: 0;\ border-spacing: 0;\ border-collapse: collapse;\ }\ .tmsatp_lazy {\ margin:0;\ padding:0;\ }\ #tmsatp_spacer {\ height: 20px;\ clear: both;\ background: #494949\ "); myJQ("#showallthephotos").after( '<div id="allthephotosctr" class="tmsatp_hide">\ <span class="tmsatp_closebutton" title="Close">x</span>\ <div id="allthephotos"/>\ </div>'); myJQ("#allthephotosctr") .append('<div id="tmsatp_spacer"/>'); initialised = true; } var IMAGE_WIDTH_PADDING = 4; var IMAGE_HEIGHT_PADDING = 4; var SCROLLER_TOP_MARGIN = 20; var SCROLLER_LEFT_MARGIN = 20; // max image dimensions for the current category, images may be (much) smaller function imageDimensions() { // TODO doesn't work with new #thumbs container because it doesn't have the data-photo-max-size attribute on the li if (myJQ(".tm-marketplace-listing-photos__thumbnail-slider-item")) { // todo scrape o-aspect-ratio? return {width:640, height:480, cellClass:"tmsatp_imgcell"}; } else if (myJQ("#Photobox_OtherPhotos")) { // new format, calculate max of data-photo-max-size width and height var maxWidth = 1; var maxHeight = 1; myJQ("#Photobox_thumbs li").each(function(index, value) { var photoMaxSize = myJQ(value).attr("data-photo-max-size"); if (photoMaxSize) { var photoWidth = 0+photoMaxSize.split(",")[0]; var photoHeight = 0+photoMaxSize.split(",")[1]; maxWidth = Math.max(maxWidth, photoWidth); maxHeight = Math.max(maxHeight, photoHeight); } }); return {width:maxWidth, height:maxHeight, cellClass:"tmsatp_largerCell"}; } else { // old format, always assume 800x600 max return {width:800, height:600, cellClass:"tmsatp_largerCell"}; } } // calculate URL for large image from thumbnail <img> function imageLargeUrl(img) { var retUrl; // try new photobox format first, parent LI has a data-photo-id attribute var imageId = myJQ(img).parent().attr("data-photo-id"); if (imageId) { // TODO the "plus" image still appears to exist even if there is no zoom box, any exceptions? retUrl = myJQ("#Photobox_PhotoserverPlusUrlString,#PhotoserverPlusUrlString").attr("value") + imageId + ".jpg"; } else { // old format // thumbnail link has class "lbt_nnnnn" imageId = myJQ(img).parent().attr("class").substring(4); // TODO is "photoStartIdNewDir" still used in the old-format listings? // This is what TM does in their own script, comparing the current image ID to the ID where they started storing the images in a new path. var isNewImage = (unsafeWindow.photoStartIdNewDir ? unsafeWindow.photoStartIdNewDir > imageId : false); if (isNewImage) { retUrl = img.src.replace("/thumb", "").replace(".jpg", "_full.jpg"); } else { retUrl = img.src.replace("/thumb", "/full"); } } console.log(retUrl); return retUrl; } // Scrape images from Tangram Carousel https://tangram.nz/product/carousel into fullImageUrls function genImagesFromTgThumb() { fullImageUrls = [] // TODO this doesn't always work as one select for some reason var allThumbs = myJQ("tg-aspect-ratio",myJQ(TG_THUMB_SELECTOR)) if (allThumbs) { allThumbs.each(function(index, value) { var imageUrl = myJQ(value).css("background-image").replace("/thumb/", "/full/").replace("url(\"","").replace("\")",""); console.log("imageUrl=" + imageUrl); fullImageUrls.push(imageUrl); }); } console.log(fullImageUrls); } // scrape images from lightbox/photobox on old-style listings into fullImageUrls function genImagesFromLightbox() { fullImageUrls = [] // Get all the lightbox thumbs var allImages = myJQ(".lbThumb img,#Photobox_thumbs img,#thumbs img"); if (allImages) { allImages.each(function(index,value) { var imageUrl = imageLargeUrl(value); console.log("img imageUrl=" + imageUrl); fullImageUrls.push(imageUrl); }); } } // scrape images from realestate gallery into fullImageUrls function genImagesFromGallery() { fullImageUrls = [] var allImages = myJQ(".tm-gallery-view__thumbnail"); if (allImages) { allImages.each(function(index,value) { var imageUrl = value.src.replace("64x64m","plus"); console.log("img imageUrl=" + imageUrl); fullImageUrls.push(imageUrl); }); } } function renderImages() { var imageCount = fullImageUrls.length; var dimensions = imageDimensions(); var padded = {width: dimensions.width + IMAGE_WIDTH_PADDING, height: dimensions.height + IMAGE_HEIGHT_PADDING}; var jqWindow = myJQ(window); // TODO if the images are > 800 wide as can happen with the new format, get horizontal scrolling, is this necessarily a bad thing? var columns = ((jqWindow.width() > (padded.width*2 + SCROLLER_LEFT_MARGIN)) && imageCount > 2) ? 2 : 1; var grey_pixel = "data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="; // use a table for easy centering var imgTable = myJQ('<table class="tmsatp_table"/>'); var i = 0; while (i<imageCount) { var row = myJQ("<tr/>"); for (var col=0; col < columns; col++) { if (i >= imageCount) break; var td; // if last image is odd-numbered, span both columns in the last row if (columns === 2 && i===(imageCount-1) && (i%2===0)) { td = myJQ('<td/>', {class: "tmsatp_imgcell", colspan:"2"}); } else { td = myJQ('<td/>', {class: "tmsatp_imgcell " + dimensions.cellClass}); } td.append(myJQ("<img />", {class: "tmsatp_lazy", src: grey_pixel, // fixed size because we don't know image dimensions yet, see lazyLoadDone width: dimensions.width, height: dimensions.height, "data-original": fullImageUrls[i], title: "Photo " + (i+1) + " of " + imageCount })); row.append(td); i++; } row.appendTo(imgTable); } myJQ("#allthephotos").empty().append(imgTable); } function lazyLoadDone(elements_left, settings) { // use real image size once the real image is loaded myJQ(this).removeAttr("width").removeAttr("height").removeAttr("style"); // remove the min-width and height from the cell so the cell will fit the image myJQ(this).parent().removeClass(imageDimensions().cellClass); // changing image sizes may have de-centered the table, recentre it after re-layout if (elements_left ===0) { window.setTimeout(recentre, 200); } } function addSpacer() { // Show the spacer at the bottom if the scroller height is greater than the document height, // so the last photo isn't stuck on the bottom of the window. var scrollerHeight = myJQ("#allthephotosctr").height(); if (scrollerHeight > initialHeight) { myJQ("#tmsatp_spacer").show(); } else { myJQ("#tmsatp_spacer").hide(); } } function centeredPosition() { var $window = myJQ(window); return { // center vertically in viewport top: Math.max(SCROLLER_TOP_MARGIN,($window.height() - myJQ("#allthephotosctr").height()) / 2) + saveTop, left: Math.max(SCROLLER_LEFT_MARGIN, ($window.width() - myJQ("#allthephotosctr").width()) / 2) }; } function recentre() { var pos = centeredPosition(); myJQ("#allthephotosctr").animate({top: "" + pos.top +"px", left: "" + pos.left + "px"}); } function startLoading() { myJQ("img.tmsatp_lazy").lazyload({effect : "fadeIn", container: myJQ("#allthephotos"), load: lazyLoadDone}); } function genImages() { if (myJQ(TG_THUMB_SELECTOR).length > 0) { genImagesFromTgThumb(); } else if (myJQ(".tm-gallery-thumbnail-slider__item").length > 0) { genImagesFromGallery(); } else { genImagesFromLightbox(); } } function relayout() { genImages(); renderImages(); addSpacer(); recentre(); startLoading(); } function showAllThePhotos() { init(); saveTop = myJQ(window).scrollTop(); genImages(); renderImages(); addSpacer(); var pos = centeredPosition(); myJQ("#allthephotosctr").bPopup({ follow: [false, false], position: [pos.left,pos.top], closeClass: "tmsatp_closebutton", onClose: function() { if (resizeTimer) clearTimeout(resizeTimer); myJQ(window).scrollTop(saveTop); } }); startLoading(); // handle resize, with timer to limit repeated calls while resizing myJQ(window).resize(function() { if (resizeTimer) clearTimeout(resizeTimer); resizeTimer = setTimeout(relayout, 100); }); } function addMainLink() { if(myJQ("#showallthephotos").length == 0) { // Only show "all photos" link if there's more than 1 photo if (myJQ(".lbThumb img,#Photobox_thumbs li,#thumbs li,.tm-gallery-thumbnail-slider__item," + TG_THUMB_SELECTOR).length > 1) { myJQ("#viewFullSize,#pager,#OtherPhotosContainer,.tm-marketplace-buyer-options__commerce-box,.tm-motors-buyer-options__commerce-box,.tm-gallery-view__carousel--premium,.tm-propert-gallery-view__carousel-container").after(myJQ("<a />", {id: "showallthephotos", href: "javascript:void(0)", title: "View all the full size photos (Tampermonkey script)", text: "View all photos", click: showAllThePhotos})) .after("<br />"); } } // keep timer running even if link already added because TM repaint clears it sometimes } window.setInterval(addMainLink, 500);