Stig's Flickr Fixr

Updates your Flickr-experience. Show photographer's albums on photostream-pages, Increase display-size and quality of "old" photos, Photographer's other photos by tag-links, A fix to actually show a geotagged photo on the linked map - - - And maybe more to come later...

目前為 2015-08-29 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Stig's Flickr Fixr
// @namespace   dk.rockland.userscript.flickr.fixr
// @description Updates your Flickr-experience. Show photographer's albums on photostream-pages, Increase display-size and quality of "old" photos, Photographer's other photos by tag-links, A fix to actually show a geotagged photo on the linked map - - - And maybe more to come later...
// @author      Stig Nygaard, http://www.rockland.dk, https://www.flickr.com/photos/stignygaard/
// @homepageURL http://www.rockland.dk/userscript/flickr/fixr/
// @supportURL  http://www.rockland.dk/userscript/flickr/fixr/#support
// @icon        http://www.rockland.dk/img/fixr32.png
// @icon64      http://www.rockland.dk/img/fixr64.png
// @include     https://www.flickr.com/*
// @include     https://flickr.com/*
// @match       https://*.flickr.com/*
// @version     2015.08.29.0
// @grant       none
// @run-at      document-start
// ==/UserScript==

var DEBUG = false;
function log(s) {
  if (DEBUG && window.console) { window.console.log(s); }
}
if (DEBUG) {
  if ('loading' === document.readyState) {
    log("This userscript is running at document-start time.");
  } else {
    log("This userscript is running with document.readyState: " + document.readyState);
  }
  window.addEventListener('DOMContentLoaded', function () {log('(onDOMContentLoaded)'); }, false);
  window.addEventListener('focus', function () {log('(onfocus)'); }, false);
  window.addEventListener('load', function () {log('(onload)'); }, false);
  window.addEventListener('pageshow', function () {log('(onpageshow)'); }, false);
  window.addEventListener('resize', function () {log('(onresize)'); }, false);
  window.addEventListener('hashchange', function () {log('(onhashchange)'); }, false);
}

// FIXR page-tracker start
var fixr = {
  context: {
    pageType: '',
    userId: '',
    photographerId: '', // value might be delayed (If uninitialized, try call initPhotographerId())
    photographerIcon: '',
    photographerAlias: '', // (pathalias) bonus-info sometimes initialized (from url) when initializing photoId
    photographerName: '',
    photoId: '',
    albumId: '',
    groupId: '',
    galleryId: ''
  },
  content: null,
  pageactionsCount: 0,
  timerResizeActionDelayed: 0,
  onPageHandlers: [],
  onResizeHandlers: [],
  initPhotographerId: function () { // photographer/attribution id
    var elem;
    if (document.querySelector('div.photostream-page-view')) {
      // photostream
      elem = document.querySelector('div.photostream-page-view div.fluid-photostream-coverphoto-view div.avatar.person');
    } else if (document.querySelector('div.photo-page-scrappy-view')) {
      // photopage
      elem = document.querySelector('div.photo-page-scrappy-view div.sub-photo-view div.avatar.person');
    } else if (document.querySelector('div.photo-page-lightbox-scrappy-view')) {
      // photopage lightbox
      elem = document.querySelector('div.photo-page-lightbox-scrappy-view div.photo-well-view div.photo-attribution div.avatar.person');
    } else if (document.querySelector('div.album-page-view')) {
      // album page
      elem = document.querySelector('div.album-page-view div.album-container div.album-header-view div.album-attribution div.avatar.person');
    } else {
      log('we do not look for photographerId on this page');
      return true;
    }
    // album oversigt
    // etc...
    // men minus f.eks. favorites oversigt!
    if (!elem) {
      log('fixr.initPhotographerId() - Attribution elem NOT found - returning false');
      return false;
    } // re-run a little later???
    log('fixr.initPhotographerId() - Attribution elem found');
    // (div.avatar.person).style.backgroundImage=url(https://s.yimg.com/pw/images/buddyicon07_r.png#44504567@N00)
    //                    .style.backgroundImage=url(//c4.staticflickr.com/8/7355/buddyicons/10259776@N00_r.jpg?1372021232#10259776@N00)
    if (elem.style.backgroundImage) {
      log('fixr.initPhotographerId() - elem has style.backgroundImage "' + elem.style.backgroundImage + '", now looking for the attribution id...');
      var pattern = /url[^#\?]+(\/\/[^#\?]+\.com\/[^#\?]+\/buddyicon[^\?\#]+)[^#]*#(\d+\@N\d{2})/i;
      // var pattern = /\/buddyicons\/(\d+\@N\d{2})\D+/i;
      var result = elem.style.backgroundImage.match(pattern);
      if (result) {
        log('fixr.initPhotographerId() - Attribution pattern match found: ' + result[0]);
        log('fixr.initPhotographerId() - the attribution icon is ' + result[1]);
        log('fixr.initPhotographerId() - the attribution id is ' + result[2]);
        fixr.context.photographerIcon = result[1];
        fixr.context.photographerId = result[2];
      } else {
        log('fixr.initPhotographerId() - attribution pattern match not found');
        return false;
      }
    } else {
      log('fixr.initPhotographerId() - elem.style.backgroundImage not found');
      return false;
    }
    log('fixr.initPhotographerId() - returning true...');
    return true;
  },
  initPhotoId: function () { // Photo Id
    //  *flickr.com/photos/user/PId/*
    var pattern = /^\/photos\/([^\/]+)\/([\d]{2,})/i;
    var result = window.location.pathname.match(pattern);
    if (result) {
      log('url match med photoId=' + result[2]);
      log('url match med photographerAlias=' + result[1]);
      fixr.context.photoId = result[2];
      fixr.context.photographerAlias = result[1];
      return true;
    }
    return false;
  },
  pageActions: function () {
    if (fixr.content) {
      log('fixr.pageActions() has started with fixr.content defined');
    } else {
      log('fixr.pageActions() was called, but fixr.content NOT defined');
      return;
    }
    fixr.pageactionsCount++;
    for (var p in fixr.context) {  // reset context on new page
      fixr.context[p] = ''; // filter ?
    }
    if (fixr.content.querySelector('div.photostream-page-view')) {
      if (fixr.content.querySelector('div.slideshow-view')) {
        fixr.context.pageType = 'PHOTOSTREAM SLIDESHOW';
      } else {
        fixr.context.pageType = 'PHOTOSTREAM';
      }
    } else if (fixr.content.querySelector('div.photo-page-scrappy-view')) {
      fixr.context.pageType = 'PHOTOPAGE';
    } else if (fixr.content.querySelector('div.photo-page-lightbox-scrappy-view')) {
      fixr.context.pageType = 'PHOTOPAGE LIGHTBOX';
    } else if (fixr.content.querySelector('div.albums-list-page-view')) {
      fixr.context.pageType = 'ALBUMSLIST';
    } else if (fixr.content.querySelector('div.album-page-view')) {
      if (fixr.content.querySelector('div.slideshow-view')) {
        fixr.context.pageType = 'ALBUM SLIDESHOW';
      } else {
        fixr.context.pageType = 'ALBUM';
      }
    } else if (fixr.content.querySelector('div.cameraroll-page-view')) {
      fixr.context.pageType = 'CAMERAROLL';
    } else if (fixr.content.querySelector('div.explore-page-view')) {
      fixr.context.pageType = 'EXPLORE';
    } else if (fixr.content.querySelector('div.favorites-page-view')) {
      if (fixr.content.querySelector('div.slideshow-view')) {
        fixr.context.pageType = 'FAVORITES SLIDESHOW';
      } else {
        fixr.context.pageType = 'FAVORITES';
      }
    } else if (fixr.content.querySelector('div.groups-list-view')) {
      fixr.context.pageType = 'GROUPSLIST'; // personal grouplist
    } else if (fixr.content.querySelector('div#activityFeed')) { // id=main i stedet for id=fixr.content
      fixr.context.pageType = 'ACTIVITYFEED'; // aka. front page
    } else {
      // fixr.context.pageType = ''; // unknown
    }

    log('fixr.context.pageType = ' + fixr.context.pageType);
    if (fixr.initPhotographerId()) {
      log('fixr.initPhotographerId() returned true');
    } else {
      log('fixr.initPhotographerId() returned false - re-running delayed...');
      setTimeout(fixr.initPhotographerId, 2000);
    }
    if (fixr.initPhotoId()) {
      log('fixr.initPhotoId() returned true');
    } else {
      log('fixr.initPhotoId() returned false');
    }
    if (fixr.content.querySelector('a.owner-name')) {
      fixr.context.photographerName = fixr.content.querySelector('a.owner-name').textContent;
    }

    // Now run the page handlers....
    if (fixr.onPageHandlers && fixr.onPageHandlers !== null && fixr.onPageHandlers.length) {
      log('We have ' + fixr.onPageHandlers.length + ' onPage handlers starting now...');
      for (var f = 0; f < fixr.onPageHandlers.length; f++) {
        fixr.onPageHandlers[f]();
      }
    }
  },
  setupContent: function () {
    if (document.getElementById('content')) {
      fixr.content = document.getElementById('content');
    } else if (document.getElementById('main')) {
      fixr.content = document.getElementById('main');    // frontpage
    }
    if (fixr.content && fixr.content.id) {
      log('fixr.content.id = ' + fixr.content.id);
    } else {
      log('content or main element NOT found!');
    }
  },
  runPageActionsIfMissed: function () {
    if (fixr.pageactionsCount === 0) {
      log('Vi kører fixr.pageActions() på bagkant via onload...');
      fixr.setupContent();
      if (fixr.content === null) {
        log('Vi kan IKKE køre fixr.pageActions() på bagkant, da fixr.content ikke er defineret');
        return;
      }
      fixr.pageActions();
    } else {
      log('ej nødvendigt at køre fixr.pageActions() på bagkant i dette tilfælde...');
    }
  },
  runDelayedPageActionsIfMissed: function () {
    setTimeout(fixr.runPageActionsIfMissed, 2000);
  },
  resizeActions: function () {
    if (fixr.onResizeHandlers && fixr.onResizeHandlers !== null && fixr.onResizeHandlers.length) {
      for (var f = 0; f < fixr.onResizeHandlers.length; f++) {
        fixr.onResizeHandlers[f]();
      }
    }
  },
  resizeActionsDelayed: function () { // or "preburner"
    clearTimeout(fixr.timerResizeActionDelayed);
    fixr.timerResizeActionDelayed = setTimeout(fixr.resizeActions, 250);
  },
  setupObserver: function () {
    log('fixr.setupObserve INITIALIZATION START');
    fixr.setupContent();
    if (fixr.content === null) {
      log('Init fails because content not defined');
      return;
    }
    // create an observer instance
    var observer = new MutationObserver(function (mutations) {
      log('NEW PAGE MUTATION!');
      //mutations.forEach(function(mutation) {
      //  log('MO: '+mutation.type); // might check for specific type of "mutations" (MutationRecord)
      //});
      fixr.pageActions();
    }); // MutationObserver end
    // configuration of the observer:
    var config = {attributes: false, childList: true, subtree: false, characterData: false};
    observer.observe(fixr.content, config);
    log('fixr.setupObserve INITIALIZATION DONE');
  },
  init: function (onPageHandlerArray, onResizeHandlerArray) {
    // General page-change observer setup:
    window.addEventListener('DOMContentLoaded', fixr.setupObserver, false); // Page on DOMContentLoaded
    window.addEventListener('load', fixr.runDelayedPageActionsIfMissed, false); // Page on load
    window.addEventListener('resize', fixr.resizeActionsDelayed, false); // også på resize
    if (onPageHandlerArray && onPageHandlerArray !== null && onPageHandlerArray.length) {
      fixr.onPageHandlers = onPageHandlerArray; // Replace by adding with a one-by-one by "helper" for flexibility?
    }
    if (onResizeHandlerArray && onResizeHandlerArray !== null && onResizeHandlerArray.length) {
      fixr.onResizeHandlers = onResizeHandlerArray; // Replace by adding with a one-by-one by "helper" for flexibility?
    }
  }
};
// FIXR page-tracker end


var _timerMaplink = 0;
function updateMapLink() {
  if (fixr.context.pageType !== 'PHOTOPAGE') {
    return; // exit if not photopage
  }
  log('updateMapLink() running at readystate=' + document.readyState + ' and with photoId=' + fixr.context.photoId);
  if (fixr.context.photoId) {
    var maplink = fixr.content.querySelector('a.static-maps');
    if (maplink) {
      if (maplink.getAttribute('href') && (maplink.getAttribute('href').indexOf('map/?') > 0) && (maplink.getAttribute('href').indexOf('&photo=') === -1)) {
        maplink.setAttribute('href', maplink.getAttribute('href') + '&photo=' + fixr.context.photoId);
        log('link is updated by updateMapLink() at readystate=' + document.readyState);
      } else {
        log('link NOT updated by updateMapLink(). Invalid element or already updated. readystate=' + document.readyState);
      }
    } else {
      log('NO maplink found at readystate=' + document.readyState + '. Re-try later?');
    }
  } else {
    log('NO photoId found at readystate=' + document.readyState);
  }
}
function updateMapLinkDelayed() {
  if (fixr.context.pageType !== 'PHOTOPAGE') {
    return;
  } // exit if not photopage
  log('updateMapLinkDelayed() running... with pageType=' + fixr.context.pageType);
  //clearTimeout(_timerMaplink);
  _timerMaplink = setTimeout(updateMapLink, 2000); // make maplink work better on photopage
}

var albums = { // cache albums to avoid repeating requests
  ownerId: '',
  html: '',
  count: 0
};
function getAlbumlist() {
  var _reqAlbumlist = null;
  if (window.XMLHttpRequest) {
    _reqAlbumlist = new XMLHttpRequest();
    if (typeof _reqAlbumlist.overrideMimeType !== 'undefined') {
      _reqAlbumlist.overrideMimeType('text/html');
    }

    _reqAlbumlist.onreadystatechange = function () {
      if (_reqAlbumlist.readyState === 4 && _reqAlbumlist.status === 200) {
        log('_reqAlbumlist returned status=' + _reqAlbumlist.status); // + ', \ntext:\n' + _reqAlbumlist.responseText);
        var doc = document.implementation.createHTMLDocument("sizeDoc");
        doc.documentElement.innerHTML = _reqAlbumlist.responseText;

        albums.ownerId = fixr.context.photographerId;
        albums.html = '';
        albums.count = 0;
        var e = doc.body.querySelectorAll('div.photo-list-album-view');
        var imgPattern = /url\([\'\"]*([^\)\'\"]+)(\.[jpgtifn]{3,4})[\'\"]*\)/i;
        if (e && e.length > 0) {
          albums.count = e.length;
          for (var i = 0; i < Math.min(10, e.length); i++) {
            var imgUrl = '';
            log(e[i].outerHTML);
            log('A7 (' + i + ') : '+e[i].style.backgroundImage);
            // var result = e[i].style.backgroundImage.match(imgPattern); // strangely not working in Chrome
            var result = (e[i].outerHTML).match(imgPattern); // quick work-around for above (works for now)
            if (result) {
              imgUrl = result[1].replace(/_[a-z]$/, '') + '_s' + result[2];
              log('imgUrl=' + imgUrl);
            } else {
              log('No match on imgPattern');
            }
            var a = e[i].querySelector('a[href][title]'); // sub-element
            if (a && a!==null) {
              log('Album title: ' + a.title);
              log('Album url: ' + a.getAttribute('href'));
              albums.html += '<div><a href="//www.flickr.com' + a.getAttribute('href') + '"><img src="' + imgUrl + '" alt="" /><div style="margin:0 0 .8em 0">' + a.title + '</div></a></div>';
            } else {
              log('a element not found?');
            }
          }
        }
        if (document.getElementById('albumTeaser')) {
          document.getElementById('albumTeaser').innerHTML = '<div style="margin:0 0 .8em 0">Albums</div>' + albums.html + '<div><i><a href="/photos/' + (fixr.context.photographerAlias !== '' ? fixr.context.photographerAlias : fixr.context.photographerId) + '/albums/">' + (albums.count > 10 ? 'More albums...' : (albums.count == 0 ? 'No albums found...' : '')) + '</a></i></div>';
        } else {
          log('albumTeaser NOT FOUND!?!');
        }
      } else {
        // wait for the call to complete
      }
    };

    if (fixr.context.photographerId === albums.ownerId && fixr.context.photographerId !== '') {
      log('Usinging CACHED albumlist!...');
      document.getElementById('albumTeaser').innerHTML = '<div style="margin:0 0 .8em 0">Albums</div>' + albums.html + '<div><i><a href="/photos/' + (fixr.context.photographerAlias !== '' ? fixr.context.photographerAlias : fixr.context.photographerId) + '/albums/">' + (albums.count > 10 ? 'More albums...' : (albums.count == 0 ? 'No albums found...' : '')) + '</a></i></div>';
    } else if (fixr.context.photographerId) {
      var url = 'https://www.flickr.com/photos/' + (fixr.context.photographerAlias !== '' ? fixr.context.photographerAlias : fixr.context.photographerId) + '/albums';
      _reqAlbumlist.open('GET', url, true);
      _reqAlbumlist.send(null);
    } else {
      log('Attribution user (photographer) not found');
    }
  } else {
    log('understøtter ikke XMLHttpRequest');
  }
}
function albumTeaser() {
  if (fixr.context.pageType !== 'PHOTOSTREAM') {
    return; // exit if not photostream
  }
  log('albumTeaser() running');
  var dpc = document.querySelector('div.photolist-container');
  if (!dpc) {
    return;
  }
  // to-do: check om personlig photostream?
  // to-do: check padding-right er mindst 130px?
  log('AlbumTeaser found div.photolist-container');
  if (!document.getElementById('albumTeaser')) {
    dpc.style.position = "relative";
    dpc.insertAdjacentHTML('afterbegin', '<div id="albumTeaser" style="border:none;margin:0;padding:0;position:absolute;top:0;right:10px;width:100px"></div>');
  }
  if (document.getElementById('albumTeaser')) {
    getAlbumlist();  // også check på fixr.context.photographerId ?
  }
}
var _timerAlbumTeaserDelayed;
function albumTeaserDelayed() {
  if (fixr.context.pageType !== 'PHOTOSTREAM') {
    return; // exit if not photostream
  }
  log('albumTeaserDelayed() running...');
  clearTimeout(_timerAlbumTeaserDelayed);
  _timerAlbumTeaserDelayed = setTimeout(albumTeaser, 1500);
}

function getUserId() { // Id of logged-in user
  return null; // to-do
}

var scaler = {
  photoId: '',
  mf: null,   // document.querySelector('img.main-photo') for (re-)re-scale
  lrf: null,  // document.querySelector('img.low-res-photo') for (re-)re-scale
  maxSizeUrl: '',
  hasOriginal: false,
  scaleToWidth: 0,
  scaleToHeight: 0,
  run: function () {
    if (fixr.context.pageType !== 'PHOTOPAGE' && fixr.context.pageType !== 'PHOTOPAGE LIGHTBOX') {
      return; // exit if not photopage or lightbox
    }
    log('scaler.run() running...');
    // var that = this;
    var scale = function () {
      if (fixr.context.pageType !== 'PHOTOPAGE' && fixr.context.pageType !== 'PHOTOPAGE LIGHTBOX') {
        return;
      } // exit if not photopage or lightbox
      log('scaler.scale() running... (scale to:' + scaler.scaleToWidth + 'x' + scaler.scaleToHeight + ')');
      scaler.mf = document.querySelector('img.main-photo');  // for en sikkerheds skyld
      scaler.lrf = document.querySelector('img.low-res-photo');  // for en sikkerheds skyld
      if (scaler.mf && scaler.mf !== null && scaler.lrf && scaler.lrf !== null && scaler.scaleToWidth > 0 && scaler.scaleToHeight > 0) {
        log('[scaler] do scaling WORK. Height from ' + scaler.mf.height + ' to ' + scaler.scaleToHeight);
        scaler.mf.height = scaler.scaleToHeight;
        log('[scaler] do scaling WORK. Width from ' + scaler.mf.width + ' to ' + scaler.scaleToWidth);
        scaler.mf.width = scaler.scaleToWidth;
        scaler.lrf.height = scaler.mf.height;
        scaler.lrf.width = scaler.mf.width;
      }
    };
    var replace = function () { // and (re-)scale?
      if (fixr.context.pageType !== 'PHOTOPAGE' && fixr.context.pageType !== 'PHOTOPAGE LIGHTBOX') {
        return; // exit if not photopage or lightbox
      }
      log('scaler.run.replace() running...');
      scaler.mf = document.querySelector('img.main-photo');  // for en sikkerheds skyld
      if (scaler.mf && scaler.mf !== null) {
        scaler.mf.src = scaler.maxSizeUrl; // only if original
        scale();
      }
    };
    var getSizes = function () {
      log('scaler.run.getSizes() running...');
      var _reqAllSizes = null;
      if (window.XMLHttpRequest) {
        _reqAllSizes = new XMLHttpRequest();
        if (typeof _reqAllSizes.overrideMimeType !== 'undefined') {
          _reqAllSizes.overrideMimeType('text/html');
        }
        _reqAllSizes.onreadystatechange = function () {
          if (_reqAllSizes.readyState === 4 && _reqAllSizes.status === 200) {
            log('[scaler] _reqAllSizes returned status=' + _reqAllSizes.status); // + ', \ntext:\n' + _reqAllSizes.responseText);
            var doc = document.implementation.createHTMLDocument("sizeDoc");
            doc.documentElement.innerHTML = _reqAllSizes.responseText;
            if (doc.body.querySelector('div#allsizes-photo>img')) {
              scaler.maxSizeUrl = doc.body.querySelector('div#allsizes-photo>img').src;
              log('[scaler] Largest image size: ' + scaler.maxSizeUrl);
            }
            var e = doc.body.querySelectorAll('ol.sizes-list li ol li');
            if (e && e.length > 0) {
              var s = e[e.length - 1].textContent.replace(/\s{2,}/g, ' ');
              if (s.indexOf('Original') > -1) {
                scaler.hasOriginal = true;
                log('[scaler] ' + s);
                replace();
              } else {
                log('[scaler] Bruger tillader ikke adgang til original');
              }
              // udtræk evt sizes fra s?
            }
          } else {
            // wait for the call to complete
          }
        };
        var url = 'https://www.flickr.com/photos/' + (fixr.context.photographerAlias !== '' ? fixr.context.photographerAlias : fixr.context.photographerId) + '/' + fixr.context.photoId + '/sizes/o';
        _reqAllSizes.open('GET', url, true);
        _reqAllSizes.send(null);
      } else {
        log('[scaler] understøtter ikke XMLHttpRequest');
      }
    };
    if (scaler.photoId === '') {
      scaler.photoId = fixr.context.photoId;
    } else if (scaler.photoId !== fixr.context.photoId) {
      scaler.photoId = fixr.context.photoId;
      scaler.mf = null;
      scaler.lrf = null;
      scaler.maxSizeUrl = '';
      scaler.hasOriginal = false;
      scaler.scaleToWidth = 0;
      scaler.scaleToHeight = 0;
    }
    var roomHeight = 0;
    var roomWidth = 0;
    var roomPaddingHeight = 0;
    var roomPaddingWidth = 0;

    // Fortsæt kun hvis PhotoId!!!?

    var dpev = document.querySelector('div.photo-engagement-view');
    var pwv = document.querySelector('div.photo-well-view');
    if (pwv) {
      log('[scaler] height-controller: height=' + pwv.clientHeight + ' (padding=70?), width=' + pwv.clientWidth + ' (padding=80?).'); // hc.style.padding: 20px 40px 50px
      if (roomHeight === 0) {
        roomHeight = pwv.clientHeight;
      }
      if (roomWidth === 0) {
        roomWidth = pwv.clientWidth;
      }
      roomPaddingHeight += (parseInt(window.getComputedStyle(pwv, null).getPropertyValue('padding-top'), 10) + parseInt(window.getComputedStyle(pwv, null).getPropertyValue('padding-bottom'), 10));
      roomPaddingWidth += (parseInt(window.getComputedStyle(pwv, null).getPropertyValue('padding-left'), 10) + parseInt(window.getComputedStyle(pwv, null).getPropertyValue('padding-right'), 10));
    }
    var hc = document.querySelector('div.height-controller');
    if (hc) {
      log('[scaler] height-controller: height=' + hc.clientHeight + ' (padding=70?), width=' + hc.clientWidth + ' (padding=80?).'); // hc.style.padding: 20px 40px 50px
      if (roomHeight === 0) {
        roomHeight = hc.clientHeight;
      }
      if (roomWidth === 0) {
        roomWidth = hc.clientWidth;
      }
      roomPaddingHeight += (parseInt(window.getComputedStyle(hc, null).getPropertyValue('padding-top'), 10) + parseInt(window.getComputedStyle(hc, null).getPropertyValue('padding-bottom'), 10));
      roomPaddingWidth += (parseInt(window.getComputedStyle(hc, null).getPropertyValue('padding-left'), 10) + parseInt(window.getComputedStyle(hc, null).getPropertyValue('padding-right'), 10));
    }
    var pwmsv = document.querySelector('div.photo-well-media-scrappy-view');
    if (pwmsv) {
      log('[scaler] div.photo-well-media-scrappy-view: height=' + pwmsv.clientHeight + ' (padding=70?), width=' + pwmsv.clientWidth + ' (padding=80?).'); // pwmsv.style.padding: 20px 40px 50px
      if (roomHeight === 0) {
        roomHeight = pwmsv.clientHeight;
      }
      if (roomWidth === 0) {
        roomWidth = pwmsv.clientWidth;
      }
      roomPaddingHeight += (parseInt(window.getComputedStyle(pwmsv, null).getPropertyValue('padding-top'), 10) + parseInt(window.getComputedStyle(pwmsv, null).getPropertyValue('padding-bottom'), 10));
      roomPaddingWidth += (parseInt(window.getComputedStyle(pwmsv, null).getPropertyValue('padding-left'), 10) + parseInt(window.getComputedStyle(pwmsv, null).getPropertyValue('padding-right'), 10));
    }
    scaler.mf = document.querySelector('img.main-photo');
    scaler.lrf = document.querySelector('img.low-res-photo');
    // var zl = document.querySelector('img.zoom-large'); // currently not used
    // var zs = document.querySelector('img.zoom-small'); // currently not used
    if (scaler.mf) {
      log('[scaler] main-photo: h=' + scaler.mf.height + ', w=' + scaler.mf.width + '.  -  Room: (h=' + (roomHeight - roomPaddingHeight) + ',w=' + (roomWidth - roomPaddingWidth) + ')');
      if (roomPaddingWidth === 0) { // hack
        roomPaddingWidth = 120;
        log('[scaler] roomPaddingWidth=120 hack used');
      }
      if (((roomHeight - roomPaddingHeight) > scaler.mf.height + 5) && ((roomWidth - roomPaddingWidth) > scaler.mf.width + 5)) {
        log('[scaler] ALLRIGHT - WE ARE READY FOR SCALING!...');
        if (((roomHeight - roomPaddingHeight) / scaler.mf.height) < ((roomWidth - roomPaddingWidth) / scaler.mf.width)) {
          scaler.scaleToWidth = Math.floor(scaler.mf.width * ((roomHeight - roomPaddingHeight) / scaler.mf.height));
          scaler.scaleToHeight = roomHeight - roomPaddingHeight;
        } else {
          scaler.scaleToHeight = Math.floor(scaler.mf.height * ((roomWidth - roomPaddingWidth) / scaler.mf.width));
          scaler.scaleToWidth = roomWidth - roomPaddingWidth;
        }
        log('[scaler] now calling scale()... [' + scaler.scaleToWidth + ', ' + scaler.scaleToWidth + ']');
        scale();
        log('[scaler] ...AND CONTINUE LOOKING FOR ORIGINAL...');
        if (dpev) { // if (document.querySelector('ul.sizes'))
          var org = document.querySelector('ul.sizes li.Original a.download-image-size');
          if (org) { // når vi bladrer?
            scaler.hasOriginal = true; // ??? kun hvis original
            scaler.maxSizeUrl = (org.href).replace(/^https\:/i, '').replace(/_d\./i, '.');
            replace();
          } else {
            // vi kan finde original "inline"
            var target = document.querySelector('div.photo-engagement-view');
            // if(!target) return; ???
            if (target) {
              var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                  log('[scaler] MO size: ' + mutation.type); // might check for specific "mutations"?
                });
                var org = document.querySelector('ul.sizes li.Original a.download-image-size');
                if (org) {
                  scaler.hasOriginal = true; // ??? kun hvis original
                  scaler.maxSizeUrl = (org.href).replace(/^https\:/i, '').replace(/_d\./i, '.');
                  replace();
                } else {
                  log('Original photo not available for download on this photographer');
                }
                observer.disconnect();
              });
              // configuration of the observer:
              var config = {attributes: false, childList: true, subtree: false, characterData: false};
              observer.observe(target, config);
            }
          }
        } else {
          getSizes(); // resize (& replace) from/when size-list
        }
      }
    }
  }
};

function insertStyle() {
  log('tagStyle()');
  if (!document.getElementById('fixrStyle')) {
    var style = document.createElement('style');
    style.type = 'text/css';
    style.id = 'fixrStyle';
    style.innerHTML = 'ul.tags-list>li.tag>a.fixrTag,ul.tags-list>li.autotag>a.fixrTag{display:none;} ul.tags-list>li.tag:hover>a.fixrTag,ul.tags-list>li.autotag:hover>a.fixrTag{display:inline;}';
    document.getElementsByTagName('head')[0].appendChild(style);
    /*
     if(!(style.sheet||{}).insertRule) {
     (style.styleSheet || style.sheet).addRule(selector, declarations);
     } else {
     style.sheet.insertRule(selector+"{"+declarations+"}",0);
     }
     */
    log('fixrStyle has been ADDED');
  } else {
    log('fixrStyle was already present');
  }
}
function updateTags() {
  if (fixr.context.pageType !== 'PHOTOPAGE') {
    return; // exit if not photopage
  }
  log('updateTags()');
  if (document.querySelector('ul.tags-list')) {
    var tags = document.querySelectorAll('ul.tags-list>li');
    if (tags && tags !== null && tags.length > 0) {
      for (var i = 0; i < tags.length; i++) {
        var atag = tags[i].querySelector('a[title][href*="?tags="],a[title][href*="?q="]');
        if (atag) {
          var realtag = (atag.href.match(/\?(tags|q)\=([\S]+)$/i))[2];
          if (!(tags[i].querySelector('a.fixrTag'))) {
            //log('updateTags() '+i+' fixr.context.photographerIcon: '+fixr.context.photographerIcon);
            var icon = fixr.context.photographerIcon.match(/^([^_]+)(_\w)?\.[jpgntif]{3,4}$/)[1] + '' + fixr.context.photographerIcon.match(/^[^_]+(_\w)?(\.[jpgntif]{3,4})$/)[2]; // do we know for sure it is square?
            //log('updateTags() '+i+' icon: '+icon); //
            tags[i].insertAdjacentHTML('afterbegin', '<a class="fixrTag" href="/photos/' + (fixr.context.photographerAlias !== '' ? fixr.context.photographerAlias : fixr.context.photographerId) + '/tags/' + realtag + '/" title="' + atag.title + ' by ' + fixr.context.photographerName + '"><img src="' + icon + '" style="width:1em;height:1em;margin:0;padding:0;position:relative;top:3px" alt="*" /></a>');
          }
        }
      }
    } else {
      log('no tags defined (yet?)');
    }
  } else {
    log('taglist container not found');
  }
}
function updateTagsDelayed() {
  if (fixr.context.pageType !== 'PHOTOPAGE') {
    return; // exit if not photopage
  }
  log('updateTagsDelayed() running... with pageType=' + fixr.context.pageType);
  //clearTimeout(_timerMaplink);
  if (fixr.context.pageType === 'PHOTOPAGE') {
    setTimeout(updateTags, 2500);
  }
}

// FIXR fixr.init(onPageHandlers, onResizeHandlers)
fixr.init([scaler.run, insertStyle, albumTeaserDelayed, updateMapLinkDelayed, updateTagsDelayed], [scaler.run]);



/* OLD STUFF...


// Call Flickr REST API to get available photo sizes - Method currently NOT used:
function wsGetSizes(photoId) {
  var _reqGetSizes = null;
  if (window.XMLHttpRequest) {
    _reqGetSizes = new XMLHttpRequest();
    if ( typeof _reqGetSizes.overrideMimeType !== 'undefined') {
      _reqGetSizes.overrideMimeType('application/json');
    }
    _reqGetSizes.onreadystatechange = function() {
      if (_reqGetSizes.readyState === 4 && _reqGetSizes.status === 200) {
        // do something with the results
        // var myObj = eval ( _reqGetSizes.responseText );
        log('webservice photos.getSizes returned status=' + _reqGetSizes.status + ', text: ' + _reqGetSizes.responseText);
        var obj = JSON.parse(_reqGetSizes.responseText);
        if (obj.stat === "ok") {
          log("ok");
          if (obj.sizes.candownload==1) {
            log("can download");
            var array = obj.sizes.size;
            if (array && array.length>0) {
              log("array length="+array.length);
              var elem = array[array.length-1];
              if (elem) {
                log("last elem is: "+elem.label+" with source="+elem.source);
                if (elem.label==="Original" && elem.source && elem.source.length>0) {
                  // make sure photoId matches source
                  photoOrg(elem.source);
                  log("Original from webservice was used");
                }
              }
            }
          } else {
            log('Originals not available on user');
          }
        }
        // Hvis sizeList && original tilgængelig
        //   photoOrg(url); // Update image now!!!
        // ellers hvis sizelist
        //   log('Originals not available on user');
        // ellers hvis sizelist
        //   log('Error fetching original');
      } else {
        // wait for the call to complete
      }
    };

    _reqGetSizes.open('GET', 'https://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key=9b8140dc97b93a5c80751a9dad552bd4&photo_id=' + photoId + '&format=json&nojsoncallback=1', true);
    _reqGetSizes.send(null);

  } else {
    log('understøtter ikke XMLHttpRequest');
  }
}


// Call Flickr REST API to get albums - Method currently NOT used:
function wsGetAlbums() {
  var _reqGetAlbums = null;
  if (window.XMLHttpRequest) {
    _reqGetAlbums = new XMLHttpRequest();
    if ( typeof _reqGetAlbums.overrideMimeType != 'undefined') {
      _reqGetAlbums.overrideMimeType('application/json');
    }
    _reqGetAlbums.onreadystatechange = function() {
      if (_reqGetAlbums.readyState == 4 && _reqGetAlbums.status == 200) {
        log('Webservice photosets.getList returned status=' + _reqGetAlbums.status + _reqGetAlbums.responseText);
        var obj = JSON.parse(_reqGetAlbums.responseText);
        if (obj && obj.stat == "ok") {
          log("ok");
          albums.ownerId = fixr.context.photographerId;
          albums.set = [];
          albums.html = '';
          albums.pathalias = '';
          if (obj.photosets) {
            if(parseInt(obj.photosets.total,10)===0) {
              // keep empty
            } else if (obj.photosets.photoset) {
              albums.set = obj.photosets.photoset;
              var elem;
              for(var i=0; i<obj.photosets.photoset.length; i++) {
                elem = obj.photosets.photoset[i];  // should loop through multiple
                log("elem is: "+elem.id+" with title="+elem.title._content);
                if (elem.primary_photo_extras && elem.primary_photo_extras.url_sq && elem.primary_photo_extras.url_sq.length>0) {
                  log('photoset ikon url = ' + elem.primary_photo_extras.url_sq);
                  albums.html += '<div><a href="//www.flickr.com/photos/' + elem.primary_photo_extras.pathalias + '/sets/' + elem.id + '"><img src="' + elem.primary_photo_extras.url_sq +'" alt="" /><div style="margin:0 0 .8em 0">' + elem.title._content + '</div></a></div>';
                }
              }
              albums.pathalias = elem.primary_photo_extras.pathalias;
            } else {
              log('why are we here?');
            }
          }
        } else {
          // error parse
        }
        document.getElementById('albumTeaser').innerHTML = '<div style="margin:0 0 .8em 0">Albums</div>'+albums.html+'<div><i><a href="/photos/'+albums.pathalias+'/albums/">More albums...</a></i></div>';
      } else {
        // wait for the call to complete
      }
    };
    if (fixr.context.photographerId === albums.ownerId && fixr.context.photographerId!='') {
      // use cached
      document.getElementById('albumTeaser').innerHTML = '<div style="margin:0 0 .8em 0">Albums</div>'+albums.html+'<div><i><a href="/photos/'+albums.pathalias+'/albums/">More albums...</a></i></div>';
      log('Using CACHED albumlist!');
    } else if (fixr.context.photographerId) {
      _reqGetAlbums.open('GET', 'https://api.flickr.com/services/rest/?method=flickr.photosets.getList&api_key=9b8140dc97b93a5c80751a9dad552bd4&user_id=' + fixr.context.photographerId + '&page=1&per_page=10&primary_photo_extras=geo%2C+path_alias%2C+url_sq&format=json&nojsoncallback=1', true);
      _reqGetAlbums.send(null);
    } else {
      log('Attribution user (photographer) not found');
    }
  } else {
    log('Understøtter ikke XMLHttpRequest');
  }
}
*/