Flickr: Show All Sizes

By Jason Tank Last update May 23, 2013 — Installed 28,057 times.

There are 14 previous versions of this script.

// ==UserScript==
// @name        Flickr: Show All Sizes
// @namespace   Jason Tank/Druidic
// @description Adds links to every available size of a Flickr image.
// @version     3.2
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js
// @match       *://*.flickr.com/photos/*
// ==/UserScript==

// Summary: Links appear below the image's description on individual Flickr photo pages. They are fetched in the background, so an animated 'loading' message will appear until the links are ready.

// Based (somewhat) on http://userscripts.org/scripts/show/103073

// v1.0   - Initial script.
// v1.1   - Small visual tweaks.
//        - Added links to new "Small 320" and "Medium 800" formats.
// v1.2   - Minor change to loading message.
//        - More documentation in-script.
// v1.3   - "Large 1600" and "Large 2048" now supported.
//        - More documentation.
//        - Image links now rearrange by size if too many are available.
// v1.4   - More robust image-checking.
// v1.4.1 - Square 150 support.
//        - Even MORE robust image-checking.
// v1.5   - Reorganized script, links. Trying to keep output on one line.
//        - Links now have tooltips.
//        - Redid image-checking using HTML to save info instead of the sometimes-buggy .data() jQuery method.
//        - Thumbnail is now simply "Small 100". (But remains "Thumbnail" in tooltip.)
// v2.0   - Changed methodology. Rather than brute-force trying all possible files, just look at ONE image page and figure out what's available.
//        - Moved from jQuery 1.6.2 to 1.7.2
// v2.1   - Fixed bug where Firefox would not show links. (Bug probably existed since 1.5!)
//        - More documentation
// v2.2   - Even more documentation!!
//        - Fixed an unclosed tag in the Medium 640 link.
// v2.3   - Clarified the error message and altered its appearance.
//        - Simplied code.
//        - Added a check for jQuery, so we don't have to load a second instance if it's already installed.
//        - Added support for secure (https) pages.
// v2.4   - @namespace added.
//        - Moved the NotOkToDeLoad variable initialization to a place where it would actually initialize! (oops)
//        - @require added.
//        - Added "?zz=1" to Medium 640 images so older photos can resize correctly.
// v2.5   - Compacted three @match commands into one.
//        - Bumped up to jQuery 1.8.2
//        - Tweaked error message, directing users to http://userscripts.org/scripts/discuss/111902
// v3.0   - Bumped up to jQuery 1.8.3
//        - Turned off loading of larger sizes by default.
// v3.1   - Minor coding tweaks. (Flickr updated its interface, this extension still works with it!)
// v3.2   - Changed colors to match current Flickr look.
//        - Minor CSS tweak to push Hack outside of a neighbor element's bounding box.
//        - Bumped up to jQuery 2.0.0



// a function that loads jQuery and calls a callback function when jQuery has finished loading
//    (This is necessary for Chromium, which doesn't support @require)
function addJQuery(mainScript) {
  var script = document.createElement("script");
  script.setAttribute("src", "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js");
  script.setAttribute('type', 'text/javascript');
  script.addEventListener('load', function() {
    var script = document.createElement("script");
    script.textContent = "(" + mainScript.toString() + ")();";
    document.body.appendChild(script);
  }, false);
  document.body.appendChild(script);
}

// the guts of this userscript
function flickrSizeHackMain() {

  // Check if we're on the right type of page.
  if($("div#main div#primary-column div#meta").length <= 0) {
    return;   // Not a photo page. No need to run.
  }


  //*//*//*//*//*//*//*//*//*//*//*//*//*//
  //            LOCAL STORAGE            //
  //*//*//*//*//*//*//*//*//*//*//*//*//*//

  // Test for local storage.
  function supports_html5_storage() {
    try { return 'localStorage' in window && window['localStorage'] !== null; }
    catch (e) { return false; }
  }

  // Determine if we're showing or hiding Larger sizes.
  if(supports_html5_storage()) {
      // We have localStorage. Load the stored info.
      HideLARGE = localStorage["FlickrSizeHackHIDE"];
      if(!HideLARGE) {
        // There was nothing to load. Default to hiding Larger sizes.
        HideLARGE = 1;
        // Store this info.
        localStorage["FlickrSizeHackHIDE"] = 1;
      }
  } else {
      // No local storage. Default to showing Larger sizes (sorta).
      HideLARGE = 0;
  }
  // Set aside variable for saving Larger image info for later retrieval.
  var LargerImages = new Array();


  //*//*//*//*//*//*//*//*//*//*//*//*//*//
  //         HTML AND CSS DISPLAY        //
  //*//*//*//*//*//*//*//*//*//*//*//*//*//

  // Create the HTML for display.
  var flickrSizeHackHTML = '<div id="FlickrSizeHack">' +
      '<span id="Square" class="HackGroup">Square: ' +
          '<a id="Hacksq" title="_s.jpg Square 75"></a>' +
          '<a id="Hackq" title="_q.jpg Square 150"></a></span>' +
      '<span id="Small" class="HackGroup">Small: ' +
          '<a id="Hackt" title="_t.jpg Small 100 Thumbnail"></a>' +
          '<a id="Hacks" title="_m.jpg Small 240"></a>' +
          '<a id="Hackn" title="_n.jpg Small 320"></a></span>' +
      '<span id="Medium" class="HackGroup">Medium: ' +
          '<a id="Hackm" title=".jpg Medium 500"></a>' +
          '<a id="Hackz" title="_z.jpg?zz=1 Medium 640"></a>' +
          '<a id="Hackc" title="_c.jpg Medium 800"></a></span>' +
      '<span id="Large" class="HackGroup">Large: ' +
          '<a id="Hackl" title="_b.jpg Large 1024"></a>' +
          '<a id="Hackh" title="_h.jpg Large 1600"></a>' +
          '<a id="Hackk" title="_k.jpg Large 2048"></a></span>' +
      '<span id="Original" class="HackGroup"> <a id="Hacko" title="_o.jpg Original Original Original"></a></span>' +
      '<span class="HackLoading"><span class="L1">Loading</span> <span class="L2">all</span> <span class="L3">sizes...</span></span>' +
    '</div><div id="FlickrSizeHackTemp"></div><div id="FlickrSizeHackMsg"></div>';

  // Place the HTML after the photo's description.
  $("div#main div#primary-column div#meta").after(flickrSizeHackHTML);
  
  // Create HTML for the More/Toggle links.
  var flickrSizeHackLargerHTML = '<span id="HackMore" title="Click to show links for larger images."></span>';
  var flickrSizeHackToggleHTML = '<span id="HackTrue" class="HackToggle" title="Currently hiding larger images. Click to show them.">+</span><span id="HackFalse" class="HackToggle" title="Currently showing larger images. Click to hide them.">-</span>';


  // CSS presentation for the grouping as a whole.
  var wholeStyle = {"margin-bottom" : "10px"};

  // CSS presentation for the links.
  var linkStyle = {"border-width"  : "1px",
                   "border-style"  : "solid",
                   "border-color"  : "#000",
                   "padding-left"  : "2px",
                   "padding-right" : "2px",
                   "font-family"   : "arial,sans-serif",
                   "background"    : "#fff",
                   "margin-right"  : "5px"};

  // CSS presentation for the link groupings ("HackGroup").
  var hackStyle = {"white-space"  : "nowrap",
                   "background"   : "#222",
                   "color"        : "#fff",
                   "padding-left" : "2px"};

  // Apply the CSS to the links and groups.
  $("div#FlickrSizeHack").css(wholeStyle);
  $("div#FlickrSizeHack a, div#FlickrSizeHack span.HackLoading").css(linkStyle);
  $("div#FlickrSizeHack span.HackGroup").css(hackStyle);

  // Fix link hovering color-change to behave like other links.
  $("div#FlickrSizeHack a").hover(function() { $(this).css("background-color", "#0063dc"); }, function() { $(this).css("background-color", "#fff"); });

  // Hide links and groups until we're ready to display them.
  $("div#FlickrSizeHack a, span.HackGroup, div#FlickrSizeHackTemp").css("display", "none");

  // CSS presentation for the Toggle/More links.
  var HackMoreToggleCSS = {"background"    : "#222",
                           "color"         : "#fff",
                           "cursor"        : "pointer",
                           "font-family"   : "arial,sans-serif",
                           "padding-left"  : "2px",
                           "padding-right" : "2px",
                           "margin-left"   : "7px",
                           "margin-right"  : "2px"};


      // Animation of the 'loading' block.

      var Lone = $("div#FlickrSizeHack span.L1");
      var Ltwo = $("div#FlickrSizeHack span.L2");
      var Lthr = $("div#FlickrSizeHack span.L3");
      function doFading() {
          Lone.fadeOut(200);
          Lone.fadeIn(200);

          Ltwo.delay(100);
          Ltwo.fadeOut(200);
          Ltwo.fadeIn(200);

          Lthr.delay(300);
          Lthr.fadeOut(200);
          Lthr.fadeIn(200, doFading);
      }
      doFading();
        


  //*//*//*//*//*//*//*//*//*//*//*//*//*//
  //      LOAD IMAGE INFO TO PAGE        //
  //*//*//*//*//*//*//*//*//*//*//*//*//*//

  // Flickr size pages are accessed via URLs ending in sq, q, t, s, n, m, z, c, l, k, h, o.
  var sizes = "qtsnmzcl";  // These sizes (and sq) all follow the same image filename format.
  var otherSizes = "kho";  // These sizes have different image filename formats.

  // Parse the current URL into usable chunks.
  var Url = window.location + '';
  var mm = Url.split('/');
  var domain = mm[2];
  var user = mm[4];
  var photo = mm[5];

  // Create a base URL to add sizes to.
  var URL = '//' + domain + '/photos/' + user + '/' + photo + '/sizes/';

  // Set a counter variable. (If positive, there are more images to be dealt with.)
  var NotOkToDeLoad = 0;

  // Load Square 75 size (the most likely to exist). Only grab the list of sizes and the image.
  Url = URL + "sq";
  $("#FlickrSizeHackTemp").load(Url + " ol.sizes-list, div#allsizes-photo", function() { parseSizesPage() });

  // This script is called after the 'Square 75' page is loaded.
  function parseSizesPage() {
      var url = $("#FlickrSizeHackTemp").find("img").attr("src");  // Find the SRC of the Square 75 image
      var pattern = new RegExp("(^.*?)_[a-z]\.jpg");               // Find the basic image filename
      var l = url.length;
      var mm = pattern.exec(url);
      if(mm) {                                                     // See corresponding 'else' below
        var srcBase = url.substr(0, l-6);      
        activateLink("sq", srcBase);                                     // Load Square 75 link.
        $("#FlickrSizeHackTemp").find("a").each(function() {             // Loop thorough each link from the sizes page.
          var url = $(this).attr("href");                                    // Find the link to the image's size page
          var pattern = new RegExp("sizes/([" + sizes + "])/$");
          var otherPattern = new RegExp("sizes/([" + otherSizes + "])/$");
          var sz = pattern.exec(url);
          if(sz) {                                      // See if the size is one of the standard-format pages
            activateLink(sz[1], srcBase);               // ... and turn it on
          } else {
            sz = otherPattern.exec(url);                // ... otherwise, try and see if it's one of the other formats
            if(sz && (HideLARGE < 1)) {                 //     (load them now if we're toggled that way)
              NotOkToDeLoad++;                          // Increment counter variable
              $("a#Hack" + sz[1]).load(URL + sz[1] + " div#allsizes-photo", function() { activateBigLink(sz[1], 1); });
            } else if (sz) {
              LargerImages.push(sz[1]);                 //     (save image info for later use if we're toggled THAT way)
            }
          }
        });
        if(LargerImages.length > 0) {
          // We have some larger images we can show later. Display [More] link.
          $("div#FlickrSizeHack").append(flickrSizeHackLargerHTML);
          $("span#HackMore").css(HackMoreToggleCSS).text(String(LargerImages.length) + " More");
          $("span#HackMore").click(function() { showLargerImages(); } );
        }
      } else {
            //
           //
          // This is highly unlikely, but means that Flickr has greatly changed its filename format!!
         //
        //
        $("#FlickrSizeHack").html("<em><span>Flickr: Show All Sizes</span> had an error: could not extract image URLs. Please consider <a href=\"http://userscripts.org/scripts/discuss/111902\">reporting</a> this error to the script author.</em>");
        $("#FlickrSizeHack em span").css("font-weight", "bold");
        $("#FlickrSizeHack em").css({"background"   : "#eee",
                                     "border-width" : "1px",
                                     "border-color" : "#cce",
                                     "border-style" : "solid",
                                     "padding"      : "1px",
                                     "font-style"   : "italic"});
      }
      $("#FlickrSizeHackTemp").remove();         // Delete Temp block.
      if(!NotOkToDeLoad) {
        $("#FlickrSizeHack span.HackLoading").remove(); // Delete 'loading' block.
        doToggleHTML();
      }
  }


  //*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//
  //  'ACTIVATE' FUNCTIONS (used by parseSizePages)  //
  //*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//

  // Used to set up the basic-format links
  function activateLink(sz, src) {
      var SPAN = "a#Hack" + sz;                       // Figure out which <a> we're affecting
      var info = $(SPAN).attr("title").split(" ");    // Get the data in the title
      doIt(SPAN, info, src + info[0])      // Call doIt to set all the info
  }


  // Used to set up the alternate-format links
  function activateBigLink(sz, flag) {
      var SPAN = "a#Hack" + sz;                        // Figure out which <a> we're affecting
          var SRC = $(SPAN).find("img").attr("src");   // Find the <img>'s src
          $(SPAN).find("div").remove();                // Remove the <div> (with <img> inside)
      var info = $(SPAN).attr("title").split(" ");     // Get the data in the title
      doIt(SPAN, info, SRC);                         // Call doIt to set all the info
      NotOkToDeLoad--;                                 // Decrement counter variable
      if(flag && !NotOkToDeLoad) {
        $("#FlickrSizeHack span.HackLoading").remove();       // ... if down to 0, then delete 'loading' block
        doToggleHTML();
      }
  }


  // Internally used by both 'activate' functions to do all the dirty work.
  function doIt(SPAN, info, href) {
      $(SPAN).attr("href", href);                      // Set the href to the src
      $(SPAN).text(info[2]);                           // Set the link text to the appropriate info
      $("#"+info[1]).css("display", "inline");         // Make the HackGroup visible (Square, Small, etc.)
      if(info[3]) {
        title = info[3];                               // If there's a special tooltip, use it
      } else {
        title = info[1] + " " + info[2];               //  ... otherwise, the tooltip is the group + size
      }
      $(SPAN).attr("title", title);                    // Set the tooltip
      $(SPAN).css("display", "inline");                // Make the <a> visible
  }


  //*//*//*//*//*//*//*//*//*//*//*//*//*//
  //        MORE AND TOGGLE LINKS.       //
  //*//*//*//*//*//*//*//*//*//*//*//*//*//

  // Used by [More] link to load the larger images.
  function showLargerImages() {
      // Internal function, used to provide 'closure' for the sz variable.
      function doActivateBig(sz) {
          $("a#Hack" + sz).load(URL + sz + " div#allsizes-photo", function() { activateBigLink(sz, 0); });
      }
      // Go through each detected image and load them.
      for(var x = 0;x < LargerImages.length;++x) {
        var sz = LargerImages[x];
        doActivateBig(sz);
      }
      LargerImages = 0;
      // Get rid of the [More] link.
      $("#HackMore").css("background", "#f08").fadeOut(450);
  }


  // Display the [-] [+] toggle links.
  function doToggleHTML() {
      // No need to call if we don't have localStorage.
      if(HideLARGE) {
        $("#FlickrSizeHack").append(flickrSizeHackToggleHTML);  // Display the links
        if(HideLARGE > 0) {
          $("#HackFalse").css("display", "none");               // If 1, we're hiding. Show [+]
        } else {
          $("#HackTrue").css("display", "none");                // If -1, we're showing. Show [-]
        }
        $(".HackToggle").css(HackMoreToggleCSS).click(function() { doToggle(); } );
      }
  }


  // Used by the [-] [+] toggle links.
  function doToggle() {
      var msg;
      if(HideLARGE > 0) {
        HideLARGE = -1;                // If 1, we're currently hiding. Hide [+], show [-]
        $("#HackTrue").css("display", "none");
        $("#HackFalse").css("display", "inline").css(HackMoreToggleCSS);
        msg = "Larger images, when available, will now always be displayed.";
        if(LargerImages) {
          showLargerImages();          // Show hidden images.
        }
      } else {
        HideLARGE = 1;                 // If -1, we're currently showing. Hide [-], show [+]
        $("#HackTrue").css("display", "inline").css(HackMoreToggleCSS);
        $("#HackFalse").css("display", "none");
        msg = "Larger images, when available, will no longer be displayed. (You can hit the 'More' link to see them.)";
      }
      // Save the new info to localStorage.
      localStorage["FlickrSizeHackHIDE"] = HideLARGE;
      // Display a message to the user.
      displayToggleMsg(msg);
  }


  // Tell the user they clicked on the Toggle link, and what that now means.
  function displayToggleMsg(msg) {
      var w = Math.max(1, ($(window).width() / 2) - 140);    // Calculate left position
      var h = Math.max(1, ($(window).height() / 2) - 100);   // Calculate top position.
      $("#FlickrSizeHackMsg").stop(true, true).css({position: "fixed", top: h + "px", left: w + "px", zIndex: "10",
                                                    backgroundColor: "#eee", border: "2px solid #cce", padding: "10px",
                                                    color: "#000", fontSize: "16px", fontWeight: "bold", textAlign: "center",
                                                    cursor: "help", width: "280px"}).text(msg);
      $("#FlickrSizeHackMsg").fadeIn(250).delay(3000).fadeOut(2000);  // Show for three seconds, then fade for two.
      $("#FlickrSizeHackMsg").click(function() {
          $(this).stop(true).fadeOut(50);                    // Click to disappear immediately.
      });
  }


}


// load jQuery (if needed) and execute the main function
if(typeof jQuery == 'undefined') {
  addJQuery(flickrSizeHackMain);
} else {
  flickrSizeHackMain();
}