Greasy Fork

Greasy Fork is available in English.

TradeMe Show All The Photos (Tampermonkey only)

Show all the large photos on a listing

当前为 2021-06-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name       TradeMe Show All The Photos (Tampermonkey only)
// @namespace  http://drsr/
// @version    0.9.3
// @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    http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
// @require    http://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js
// @require    http://greasyfork.icu/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 = []
    var allThumbs = myJQ(TG_THUMB_SELECTOR +" tg-aspect-ratio")
    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) {
        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 />");
    }
}

// Only show "all photos" link if there's more than 1 photo (2nd path is for new photobox format, 3rd is for realestate, 4th is for tg photobox)
if (myJQ(".lbThumb img,#Photobox_thumbs li,#thumbs li,.tm-gallery-thumbnail-slider__item," + TG_THUMB_SELECTOR).length > 1) {
    console.log("tmsatp found photobox with more than one photo");
    // on a timer because TM repaint clears it
    window.setInterval(addMainLink, 500);
} else {
    console.log("tmsatp no photobox or only one photo");
}