FlickrMorePhotosByModel

By clickykbd Last update Jul 12, 2009 — Installed 510 times.

There are 5 previous versions of this script.

// FlickrMorePhotosByModel
// version 0.2.2
// 2009-07-11
// Copyright (c) 2009, Ryan Gallagher <http://mailhide.recaptcha.net/d?k=01s3QDaQrXNmFOMTttoahPPg==&c=45a75vsPJHUAbaWaf7hEep0XEaP0u0DkmwFiSB8MBeM=>
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// --------------------------------------------------------------------
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// IF YOU ARE UPGRADING FROM A PREVIOUS VERSION OF THIS SCRIPT, go to
// Tools/Manage User Scripts and manually uninstall the previous
// version before installing this one.  This script lacks an auto-update
// prompt.
//
// Go here for latest version:
// http://userscripts.org/scripts/show/53489 
// 
// To uninstall, go to Tools/Manage User Scripts, select this script,
// and click Uninstall.
//
// --------------------------------------------------------------------
// What It Does:
//
// www.flickr.com (photo page):
//
// This script adds a bit of text and a link under the "Additional Info"
// section when the camera data is exposed (a flickr user preference).
// The link takes you to "more photos by the same user with the same
// camera", a little known URL hack of the main search interface.
//
// Default sorting (recent, interesting, relevent) and display mode
// (thumbnails vs details) can be configured at the top of the script.
//
// --------------------------------------------------------------------
// Change Log:
//
// v0.2.2 2007-07-11
//    * Added first translations (French + Spanish) Thanks!
//    * moved away from innerHTML, hopefull to remove safari bugs.
// v0.2.1 2007-07-11
//    * Resorted to z=t display default, z=e only valid for people
//      seeing the beta/testing search result UI.// 
// v0.2 2007-07-11
//    * Changed check for unsafeWindow, Avoid error in Safari GreaseKit?
//      Tested with user-scripts in Opera 9 (PC).
//    * Modified include/exclude to *flickr.*/ from *flickr.com/
//    * Added top level check for flickr + photo page
//      (since include/exclude sucks)
//    * Added additional display choice for medium photos
//      (flickr updated results interface to allow this), made it new default.
//    * Added Change Log + Todo
// v0.1 2007-07-08
//    * Initial Version
//
// --------------------------------------------------------------------
// To Do:
//
//  * Confirm working in Safari Greasekit?
//  * Populate remaining translations
//  * Test in Chromium build with greasemonkey
//  * Add check for updated version (would love with all my scripts)
//  * Handle bug with known make unknown model (Polaroid Example)
//    http://www.flickr.com/photos/flickrwegian/2437536744
//  * Add make/model restriction search option to the search dropdown?
//  * Add toggle transform tag links to make make/model specific?
//
// --------------------------------------------------------------------
// ==UserScript==
// @name          FlickrMorePhotosByModel
// @namespace     http://www.ryangallagher.name
// @description   Insert a link to more photos by a camera model for that user when EXIF model data is present and visible.
// @include       http://*flickr.*/photos/*/*
//
// @exclude       http://*flickr.*/photos/organize/*
// @exclude       http://*flickr.*/photos/friends/*
// @exclude       http://*flickr.*/photos/tags/*
//
// @exclude       http://*flickr.*/photos/*/sets*
// @exclude       http://*flickr.*/photos/*/friends*
// @exclude       http://*flickr.*/photos/*/archives*
// @exclude       http://*flickr.*/photos/*/tags*
// @exclude       http://*flickr.*/photos/*/alltags*
// @exclude       http://*flickr.*/photos/*/multitags*
// @exclude       http://*flickr.*/photos/*/map*
// @exclude       http://*flickr.*/photos/*/favorites*
// @exclude       http://*flickr.*/photos/*/popular*
// @exclude       http://*flickr.*/photos/*/with*
// @exclude       http://*flickr.*/photos/*/stats*
// @exclude       http://*flickr.*/photos/*/page*
//
// @exclude       http://*flickr.*/photos/*/*/sizes/*
// @exclude       http://*flickr.*/photos/*/*/stats*
// ==/UserScript==

// --------------------------------------------------------------------
// User Configurable Preferences

// set the sorting method:
//  'int'=interesting
//  'rec'=recent
//  'rel'=relevance (NOTE: useless method, not tag/text searching)
var SORTING = 'int';

// set the display method:
//  'e' = medium photos (new flickr search UI only, currently in testing)
//  't' = thumbnails (default)
//  'm' = details
//  's' = slideshow (NOTE: FAILS, same is true of the URL flickr builds)
var DISPLAY = 't';

// --------------------------------------------------------------------
// Definitions.

// container for imported globals from page/window.
var FLICKR_GLOBALS = new Array();
var CSS_DEFAULT = '';

// --------------------------------------------------------------------
// Script Proper.

window.addEventListener("load", function() { FlickrMorePhotosByModel() }, false);

var FlickrMorePhotosByModel = function() {

  // Quick, double check we are on an appropriate flickr page
  if (!(location.hostname.match(/\.*flickr\.[a-zA-Z]+$/))) return;
  if (!(location.href.match(/\/photos\/[^\/]+\/[0-9]+/))) return;


  // default strings
  var paraText = 'More ';
  var linkText = 'by ${author_name} with this camera';
  var toolText = 'See more photos by ${author_name} taken with the ${camera_name}';

  // French (I think it's close) Thanks to a friend Liz W.
  // Plus de photo par [user] avec cette camera
  // Voir plus de photo par [user] prises avec la [camera]

  // Spanish Thanks Curtis! http://userscripts.org/topics/30619
  // 'Más por ${author_name} con esta cámara'
  // 'Ver más fotos por ${author_name} sacadas con la ${camera_name}'

  var strings = new Array();

  // fixme, add remaining language strings.
  strings[paraText] = new Array();
  strings[paraText]['en-us'] = 'More ';
  strings[paraText]['zh-hk'] = paraText;
  strings[paraText]['de-de'] = paraText;
  strings[paraText]['es-us'] = 'Más ';
  strings[paraText]['fr-fr'] = 'Plus ';
  strings[paraText]['ko-kr'] = paraText;
  strings[paraText]['pt-br'] = paraText;
  strings[linkText] = new Array();
  strings[linkText]['en-us'] = 'by ${author_name} with this camera';
  strings[linkText]['zh-hk'] = linkText;
  strings[linkText]['de-de'] = linkText;
  strings[linkText]['es-us'] = 'por ${author_name} con esta cámara';
  strings[linkText]['fr-fr'] = 'de photo par ${author_name} avec cette camera';
  strings[linkText]['ko-kr'] = linkText;
  strings[linkText]['pt-br'] = linkText;
  strings[toolText] = new Array();
  strings[toolText]['en-us'] = 'See more photos by ${author_name} taken with the ${camera_name}';
  strings[toolText]['zh-hk'] = toolText;
  strings[toolText]['de-de'] = toolText;
  strings[toolText]['es-us'] = 'Ver más fotos por ${author_name} sacadas con la ${camera_name}';
  strings[toolText]['fr-fr'] = 'Voir plus de photo para ${author_name} prises avec la ${camera_name}';
  strings[toolText]['ko-kr'] = toolText;
  strings[toolText]['pt-br'] = toolText;

  // script globals
  var defaultLang = 'en-us'; // a fall-back
  var lang = 'en-us';
  var author = new Array();
  var camera = false;
  var modelUrl, makeUrl = 'http://' + location.hostname + '/search/';

  // function inserts newNode after referenceNode
  var insertAfter = function (referenceNode, newNode) {
    referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling );
  }

  // function for appending a style block containing css definitions
  var appendStyles = function (css) {
    var myStyle = document.createElement('style');
    myStyle.setAttribute('type', 'text/css');

    //multi-line string
    myStyle.appendChild(document.createTextNode('<!-- ' + css + ' -->'));

    head = document.getElementsByTagName('HEAD')[0];
    head.appendChild(myStyle);
    return;
  }

  // function for getting useful globals set by flickr
  var getFlickrGlobals = function() {

    // find the unsafeWindow, differs depending on browser, hope this doesn't
    // throw errors on most browsers.
    if (!(typeof unsafeWindow == "undefined")) var w = unsafeWindow;
      else var w = window;

    // Included all flickr uses, uncommented are ones this script uses.
    var r = new Array();
    if (w.global_magisterLudi) {
      //r['magisterLudi'] = w.global_magisterLudi;
      //r['auth_hash'] = w.global_auth_hash;
      //r['auth_token'] = w.global_auth_token;
      //r['flickr_secret'] = w.global_flickr_secret;
      //r['slideShowVersion'] = w.global_slideShowVersion;
      //r['slideShowCodeVersion'] = w.global_slideShowCodeVersion;
      //r['slideShowVersionV2'] = w.global_slideShowVersionV2;
      //r['slideShowCodeVersionV2'] = w.global_slideShowCodeVersionV2;
      //r['slideShowTextVersion'] = w.global_slideShowTextVersion;
      //r['slideShowVersionV3'] = w.global_slideShowVersionV3;
      //r['slideShowVersionV4'] = w.global_slideShowVersionV4;
      //r['disable_slideshow4'] = w.global_disable_slideshow4;
      //r['nsid'] = w.global_nsid;
      //r['ispro'] = w.global_ispro;
      //r['dbid'] = w.global_dbid;
      r['name'] = w.global_name;
      //r['expire'] = w.global_expire;
      //r['icon_url'] = w.global_icon_url;
      //r['tag_limit'] = w.global_tag_limit;
      //r['collection_depth_limit'] = w.global_collection_depth_limit;
      //r['stewartVersion'] = w.global_stewartVersion;
      //r['contact_limit'] = w.global_contact_limit;
      //r['eighteen'] = w.global_eighteen;
      //r['group_limit'] = w.global_group_limit;
      //r['photos_ids_limit'] = w.global_photos_ids_limit;
      //r['one_photo_show_belongs'] = w.one_photo_show_belongs;
      //r['prefs_isset_viewgeo'] = w.prefs_isset_viewgeo;
      //r['photos_url'] = w.photos_url;
      //r['disable_stewart'] = w.disable_stewart;
      //r['disable_geo'] = w.disable_geo;
      //r['rper'] = w.global_rper;
      //r['widget_carets'] = w.global_widget_carets;
      //r['explore_search'] = w.global_explore_search;
      r['intl_lang'] = w.global_intl_lang;
      //r['_photo_root'] = w._photo_root;
      //r['_site_root'] = w._site_root;
      //r['_images_root'] = w._images_root;
      //r['_intl_images_root'] = w._intl_images_root;
      //r['do_bbml'] = w.do_bbml;
    }
    return r;
  }

  // function translation lookup, takes string index, source array.
  var _strings = function(idx, lang_id, src) {
    if (src[idx][lang_id]) return src[idx][lang];
    else return idx;
  }

  // function locate camera link & extract cm make, cm model, & display name.
  var getCamera = function() {
    var camera = new Array();
    var xp = '//li[@class="Stats"]/descendant::a[starts-with(@href, "/cameras/")]';
    var xpr = document.evaluate(xp, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if (xpr.snapshotLength != 1) return false;

    var re = new RegExp('^\/cameras\/([^\/]+)\/([^\/]+)\/$');
    var match = re.exec(xpr.snapshotItem(0).getAttribute('href'));
    if (match[1]) camera['make'] = match[1];
    if (match[2]) camera['model'] = match[2];
    camera['name'] = xpr.snapshotItem(0).firstChild.nodeValue;

    return camera;
  }

  // function to get author NSID & display name.
  var getAuthor = function() {
    var author = new Array();

    // dig the author nsid out of the user-icon next to the foaf:name
    // Note: was gonna use dc:creator here, but is only exposed on CC content.
    var xp = '//div[@class="Widget"]/descendant::b[contains(@property, "foaf:name")]/ancestor::a[contains(@href, "/photos/")]/preceding-sibling::a/img[contains(@src, "buddyicons")]';
    // document DIV "Widget" contains B with attrib property=foaf:name has parent A with attrib href containing "/photos/" has a preceeding sibling A with child IMG with attrib src containing "buddyicons"
    var xpr = document.evaluate(xp, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if (xpr.snapshotLength != 1) return false; // whoops, ummm?
    var re = new RegExp('buddyicons\/([0-9]+@N[0-9]+)\.jpg');
    var match = re.exec(xpr.snapshotItem(0).getAttribute('src'));
    if (match[1]) author['nsid'] = match[1];

    // dig the author name from foaf:name in the same area
    var xp = '//div[@class="Widget"]/descendant::b[contains(@property, "foaf:name")]';
    // document DIV "Widget" contains B with attrib property=foaf:name
    var xpr = document.evaluate(xp, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    author['name'] = xpr.snapshotItem(0).textContent;

    return author;
  }

  // if we don't have an author, quit.
  author = getAuthor();
  if (!author['nsid']) return;

  // if we don't have a camera, quit.
  camera = getCamera();
  if (false === camera) return;

  // set the lang if discovered from flickr's global variables.
  FLICKR_GLOBALS = getFlickrGlobals();
  if (FLICKR_GLOBALS['intl_lang']) lang = FLICKR_GLOBALS['intl_lang'];

  // get the other bits needed for the url building.
  var photoId = location.pathname.split('/')[3];
  author['urlName'] = location.pathname.split('/')[2];

  // append any script styles.
  appendStyles(CSS_DEFAULT); 

  // build the urls to the search results.
  // Note: only the full "make/model" cm seems to work, despite having valid
  // pages for make & make/model in the camera finder.
  var slideshowOption = '';
  if (DISPLAY === 's') slideshowOption = 'show/';
  makeUrl += slideshowOption
    + '?w=' + author['nsid']
    + '&z=' + DISPLAY
    + '&s=' + SORTING
    + '&cm=' + camera['make']
  ;
  modelUrl = makeUrl + '/' + camera['model'];

  // replace variable strings in the strings array
  // Note: We only need one but this seemed easier because they were already in an array here. fixme?
  for (var l in strings) {
    for (var t in strings[l]) {
      strings[l][t] = strings[l][t].replace(/\$\{author_name\}/, author['name']);
      strings[l][t] = strings[l][t].replace(/\$\{camera_name\}/, camera['name']);
    }
  }

  // build the non-linked text bits.
  var para = document.createElement('span');
  para.appendChild(document.createTextNode(_strings(paraText, lang, strings)));

  // build the link elements.
  var modelLink = document.createElement('a');
  modelLink.setAttribute('href', modelUrl);
  modelLink.setAttribute('class', 'Plain');
  modelLink.setAttribute('title', _strings(toolText, lang, strings));
  modelLink.appendChild(document.createTextNode(_strings(linkText, lang, strings)));

  // locate insert point.
  var xp = '//li[@class="Stats"]/descendant::a[starts-with(@href, "/cameras/")]/following-sibling::br';
  // LI "Stats" contains A (with href ^/cameras/) followed by BR
  var xpr = document.evaluate(xp, document, null, XPathResult.ANY_TYPE, null);
  var node = xpr.iterateNext();

  // insert.
  if (!node.parentNode) return;
  node.parentNode.appendChild(document.createElement('br'));
  node.parentNode.appendChild(para);
  node.parentNode.appendChild(modelLink);
}