// ==UserScript==
// @name Stig's Flickr Fixr
// @namespace dk.rockland.userscript.flickr.fixr
// @description Show photographer's albums on photostream-pages, Increase display-size and quality of "old" uploads, Photographer's other photos by tag-links, Links to album-map and album-comments, Actually show a geotagged photo on the associated map, Top-pagers - And more to come?...
// @author Stig Nygaard, https://www.rockland.dk, https://www.flickr.com/photos/stignygaard/
// @homepageURL https://www.flickr.com/groups/flickrhacks/discuss/72157655601688753/
// @supportURL https://www.flickr.com/groups/flickrhacks/discuss/72157655601688753/
// @icon https://raw.githubusercontent.com/StigNygaard/Stigs_Flickr_Fixr/master/icons/fixr32.png
// @icon64 https://raw.githubusercontent.com/StigNygaard/Stigs_Flickr_Fixr/master/icons/fixr64.png
// @match https://*.flickr.com/*
// @match *://*.flickr.net/*
// @exclude *://api.flickr.com/*
// @exclude *://identify.flickr.com/*
// @exclude *://*.flickr.com/signin/*
// @exclude *://*.flickr.com/signup/*
// @exclude *://*.flickr.com/account/*
// @version 2020.06.22.0
// @run-at document-start
// @grant none
// @noframes
// @downloadURL none
// ==/UserScript==
// CHANGELOG - The most recent or important updates/versions:
var changelog = [
{version: '2020.06.22.0', description: 'Removing 90% of map-fix for showing geolocation of a photo. Finally Flickr has mostly fixed issue themselves. Restoring insertion of Google Maps link which broke by the Flickr update.'},
{version: '2020.06.21.0', description: 'A little bit of cleaning, a warning to userscript users - and "sub-options" for the tag-links feature (in webextension version)'},
{version: '2020.05.31.0', description: 'Improved fix to show location of geotagged photo (Zoom in). Some code cleaning...'},
{version: '2020.05.18.0', description: 'Fix for missing album column in Chrome when on flickr.com instead of www.flickr.com (cross-domain error)'},
{version: '2020.01.15.0', description: 'Fix for extra menuitems on pages with the old header design'},
{version: '2020.01.14.0', description: 'Internal changes: Remove potential unsafe or unnecessary use of innerHTML and insertAdjacentHTML'},
{version: '2019.12.09.0', description: 'Album comments are back again!'},
{version: '2019.11.03.0', description: 'Fix for scaling/replace showing low res photos (Adapt to a site change).'},
{version: '2019.10.20.0', description: 'Fix for use of original in scaling/replace.'},
{version: '2019.10.19.0', description: 'Adjusting to Flickr 2019 updates.'},
{version: '2019.05.18.0', description: 'Also show feed links on status.flickr.net.'},
{version: '2019.02.02.0', description: 'Improved map-fix.'},
{version: '2018.11.29.0', description: 'New feature: Show available RSS/Atom newsfeeds on pages.'},
{version: '2018.10.15.1', description: 'Add Options page to Firefox and Chrome browser extensions, to enable or disable individual features of Flickr Fixr (Userscript version is still all or nothing).'},
{version: '2018.10.15.0', description: 'New feature: Added Collections and Map to topmenus.'},
{version: '2018.08.19.0', description: 'New features: Added link leading to Tags page in topmenus. Added display of full Taken and Upload time, plus link for photographer\'s other photos from (approx.) same day.'},
{version: '2018.05.20.0', description: 'New feature: Added a subtle warning if photostreams are shown in Date-taken order instead of Date-uploaded order.'},
{version: '2017.07.31.0', description: 'New feature: Adding a Google Maps link on geotagged photos. Also: Removing unused code. Development code now in GitHub repository: https://github.com/StigNygaard/Stigs_Flickr_Fixr'},
{version: '2016.06.12.3', description: 'An "un-scale button" to align image-size with (native) notes (on photo-pages, but not in lightbox mode).'},
{version: '2016.03.11.1', description: 'New features: A link to "recent uploads page" added on the Explore page. Ctrl-click fix for opening tabs in background on search pages (Firefox-only problem?).'},
{version: '2016.02.09.0', description: 'New feature: Link to Explore Calendar added to Explore page.'},
{version: '2016.02.06.2', description: 'New feature: Top-pagers! Hover the mouse in the center just above photostreams to show a pagination-bar.'},
{version: '2015.11.28.1', description: 'New feature: Album-headers are now updated with links to album-map and album-comments.'},
{version: '2015.08.26.4', description: 'Initial userscript release version. Photo scale/replace, album column and tag-link feature.'}
];
var DEBUG = false;
function log(s) {
if (DEBUG && console) {
console.log(s);
}
}
if (DEBUG) {
if ('loading' === document.readyState) {
log("This script is running at document-start time.");
} else {
log("This script 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);
window.addEventListener('blur', function(){log('(onblur)');}, false);
}
// FIXR page-tracker
var fixr = fixr || {
context: {
pageType: '',
pageSubType: '',
userId: '',
photographerId: '', // value might be delayed (If uninitialized, try call initPhotographerId())
photographerIcon: '',
photographerAlias: '', // (pathalias) bonus-info sometimes initialized (from url) when initializing photoId or albumId
photographerName: '',
photoId: '',
albumId: '',
groupId: '',
galleryId: ''
},
content: null,
pageactionsCount: 0,
timerResizeActionDelayed: 0,
onPageHandlers: [],
onResizeHandlers: [],
onFocusHandlers: [],
onStandaloneHandlers: [],
runningDirty: function() { // In-development and extra experiments enabled?
return (DEBUG && (fixr.context.userId==='10259776@N00'));
},
timer: {
_test: 0 // TODO
},
style: {
_declarations: '',
add: function (decl) {
fixr.style._declarations += decl + ' ';
},
init() {
if (!document.getElementById('fixrStyle')) {
let styleElem = createRichElement('style', {type: 'text/css', id: 'fixrStyle'}, fixr.style._declarations);
document.getElementsByTagName('head')[0].appendChild(styleElem);
log('fixrStyle has been ADDED');
} else {
log('fixrStyle was already present');
}
}
},
clock: {
_d: null,
_pst: null, // Pacific Standard Time
_explore: null,
tick: function () {
this._d = new Date();
this._pst = new Date(this._d);
this._pst.setHours(this._d.getHours() - 8); // PST = UTC-08
this._explore = new Date(this._d);
this._explore.setHours(this._d.getHours() - 28); // Explore beat, yesterday UTC-4
// this._y.setDate(this._y.getDate() - 1);
return this._pst;
},
pst: function () { // yyyy-mm-dd tt:mm PST
return (this._pst || this.tick()).toISOString().substring(0,16).replace('T',' ')+' PST';
},
explore: function () { // yyyy-mm-dd tt:mm Explore beat!
if (this._explore===null) {
this.tick();
}
return this._explore.toISOString().substring(0,16).replace('T',' ')+' Explore beat!';
}
},
isWebExtension: function() {
return (typeof GM_info === 'undefined') && (typeof GM === 'undefined');
},
isUserscript: function() {
return !fixr.isWebExtension();
},
initUserId: function () {
if (window.auth && window.auth.user && window.auth.user.nsid) {
fixr.context.userId = window.auth.user.nsid;
return true;
}
return false;
},
initPhotographerName: function () {
if (fixr.content.querySelector('a.owner-name')) {
fixr.context.photographerName = fixr.content.querySelector('a.owner-name').innerText;
return true;
}
return false;
},
initPhotographerId: function () { // photographer/attribution id
// todo: This needs a rewrite some day...
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 if (document.querySelector('div.coverphoto-content div.avatar.person')) {
// fallback, modern design pages
elem = document.querySelector('div.coverphoto-content div.avatar.person');
} else if (document.querySelector('div.subnav-middle div.sn-avatar > img')) {
// fallback, old design pages
elem = document.querySelector('div.subnav-middle div.sn-avatar > img');
} else {
log('we do not look for photographerId on this page');
return true;
}
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)
var result;
if (elem.tagName.toUpperCase() === 'IMG' && elem.src) {
result = elem.src.match(/https:(\/\/[^#\?]+\.com\/[^#\?]+\/buddyicon[^\?\#]+)[^#]*#(\d+\@N\d{2})/i);
} else if (elem.style.backgroundImage) {
log('fixr.initPhotographerId() - elem has style.backgroundImage "' + elem.style.backgroundImage + '", now looking for the attribution id...');
// var pattern = /\/buddyicons\/(\d+\@N\d{2})\D+/i;
result = elem.style.backgroundImage.match(/url[^#\?]+(\/\/[^#\?]+\.com\/[^#\?]+\/buddyicon[^\?\#]+)[^#]*#(\d+\@N\d{2})/i);
}
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];
log('fixr.initPhotographerId() - returning true...');
return true;
} else {
log('fixr.initPhotographerId() - attribution pattern match not found');
return false;
}
},
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;
} else {
log('*** initPhotoId() returnerer false! reg-pattern fandt ikke match i pathname='+window.location.pathname);
}
return false;
},
initAlbumId: function () {
// *flickr.com/photos/user/albums/AId/*
// *flickr.com/photos/user/sets/AId/*
var pattern = /^\/photos\/([^\/]+)\/albums\/([\d]{2,})/i;
var result = window.location.pathname.match(pattern);
if (!result) {
pattern = /^\/photos\/([^\/]+)\/sets\/([\d]{2,})/i;
result = window.location.pathname.match(pattern);
}
if (result) {
log('url match med albumId=' + result[2]);
log('url match med photographerAlias=' + result[1]);
fixr.context.albumId = result[2];
fixr.context.photographerAlias = result[1];
return true;
}
return false;
},
pageActions: function () {
fixr.clock.tick();
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
if (fixr.context.hasOwnProperty(p)) {
fixr.context[p] = '';
}
}
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';
if (fixr.content.querySelector('div.vr-overlay-view') && fixr.content.querySelector('div.vr-overlay-view').hasChildNodes()) {
fixr.context.pageSubType = 'VR'; // maybe I can find a better way to detect, not sure how reliable this is?
} else if (fixr.content.querySelector('div.videoplayer')) {
fixr.context.pageSubType='VIDEO';
} else {
fixr.context.pageSubType='PHOTO';
}
} else if (fixr.content.querySelector('div.photo-page-lightbox-scrappy-view')) {
fixr.context.pageType = 'PHOTOPAGE LIGHTBOX';
if (fixr.content.querySelector('div.vr-overlay-view') && fixr.content.querySelector('div.vr-overlay-view').hasChildNodes()) {
fixr.context.pageSubType='VR'; // VR-mode currently not supported in lightbox?
} else if (fixr.content.querySelector('div.videoplayer')) {
fixr.context.pageSubType='VIDEO';
} else {
fixr.context.pageSubType='PHOTO';
}
} 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 -> UPDATES ?
} else if (fixr.content.querySelector('div#allsizes-photo')) {
fixr.context.pageType = 'SIZES'; // View all sizes - page
} else {
// fixr.context.pageType = ''; // unknown
}
log('fixr.context.pageType = ' + fixr.context.pageType);
log('fixr.context.pageSubType = '+fixr.context.pageSubType);
if (fixr.initUserId()) {
log('fixr.initUserId() returned with succes: '+fixr.context.userId);
} else {
log('fixr.initUserId() returned FALSE!');
}
if (fixr.initPhotographerId()) {
log('fixr.initPhotographerId() returned true in first try...');
} else {
log('fixr.initPhotographerId() returned false - re-running delayed...');
setTimeout(fixr.initPhotographerId, 1800);
}
if (fixr.initPhotoId()) {
log('fixr.initPhotoId() returned true in first try...');
} else {
log('fixr.initPhotoId() returned false - re-running delayed...');
setTimeout(fixr.initPhotoId, 1500);
}
if (fixr.initAlbumId()) {
log('fixr.initAlbumId() returned true in first try...');
}
if (fixr.initPhotographerName()) {
log('fixr.initPhotographerName() returned true in first try...');
} else {
setTimeout(fixr.initPhotographerName, 1500);
}
// 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...');
}
},
runIfStandalonePage: function () {
if (fixr.content === null && fixr.pageactionsCount === 0) { // if really looks like a "standalone page"...
// Now run the standalone handlers
if (fixr.onStandaloneHandlers && fixr.onStandaloneHandlers !== null && fixr.onStandaloneHandlers.length) {
log('We have ' + fixr.onStandaloneHandlers.length + ' standalone handlers starting now...');
for (var f = 0; f < fixr.onStandaloneHandlers.length; f++) {
fixr.onStandaloneHandlers[f]();
}
}
}
},
runDelayedPageActionsIfMissed: function () {
setTimeout(fixr.runPageActionsIfMissed, 2000);
setTimeout(fixr.runIfStandalonePage, 500);
},
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);
},
focusActions: function () {
if (fixr.onFocusHandlers && fixr.onFocusHandlers !== null && fixr.onFocusHandlers.length) {
for (var f = 0; f < fixr.onFocusHandlers.length; f++) {
fixr.onFocusHandlers[f]();
}
}
},
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 (runNow, onPageHandlerArray, onResizeHandlerArray, onFocusHandlerArray, onStandaloneHandlerArray) {
// General page-change observer setup:
if (document.readyState === 'interactive') { // already late?
fixr.setupObserver();
}
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
window.addEventListener('focus', fixr.focusActions, false);
if (onPageHandlerArray && onPageHandlerArray !== null && onPageHandlerArray.length) {
fixr.onPageHandlers = onPageHandlerArray; // Replace by adding with a one-by-one by "helper" for flexibility?
}
fixr.onPageHandlers.push(fixr.style.init); // styles
if (onResizeHandlerArray && onResizeHandlerArray !== null && onResizeHandlerArray.length) {
fixr.onResizeHandlers = onResizeHandlerArray; // Replace by adding with a one-by-one by "helper" for flexibility?
}
if (onFocusHandlerArray && onFocusHandlerArray !== null && onFocusHandlerArray.length) {
fixr.onFocusHandlers = onFocusHandlerArray;
}
if (onStandaloneHandlerArray && onStandaloneHandlerArray !== null && onStandaloneHandlerArray.length) { // on standalone pages, not part of "single page application"
fixr.onStandaloneHandlers = onStandaloneHandlerArray;
fixr.onStandaloneHandlers.push(fixr.style.init); // styles
}
if (runNow && runNow.length) {
log('We have ' + runNow.length + ' early running methods starting now at document.readyState = ' + document.readyState);
for (var f = 0; f < runNow.length; f++) {
runNow[f]();
}
}
}
};
// FIXR page-tracker end
const fkey="9b8140dc97b93a5c80751a9dad552bd4"; // This api key is for Flickr Fixr only. Get your own key for free at https://www.flickr.com/services/apps/create/
function escapeHTML(str) {
return str.replace(/[&"'<>]/g, (m) => ({ "&": "&", '"': """, "'": "'", "<": "<", ">": ">" })[m]);
}
function createRichElement(tagName, attributes, ...content) {
let element = document.createElement(tagName);
if (attributes) {
for (const [attr, value] of Object.entries(attributes)) {
element.setAttribute(attr, value);
}
}
if (content && content.length) {
element.append(...content);
}
return element;
}
function insertGMapLink() {
if (fixr.context.pageType !== 'PHOTOPAGE') {
return; // exit if not photopage
}
log('insertGMapLink() 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 (!document.getElementById('googlemapslink') && maplink.getAttribute('href') && (maplink.getAttribute('href').includes('map/?'))) {
try {
let lat = maplink.getAttribute('href').match(/Lat=(\-?[\d\.]+)/i)[1];
let lon = maplink.getAttribute('href').match(/Lon=(\-?[\d\.]+)/i)[1];
let gmaplink = createRichElement('a', {href: 'https://www.google.com/maps/search/?api=1&query=' + lat + ',' + lon, id: 'googlemapslink'} , 'Show location on Google Maps');
fixr.content.querySelector('li.c-charm-item-location').insertAdjacentElement('beforeend', createRichElement('div', {class: 'location-data-container'}, gmaplink));
}
catch (e) {
log('Failed creating Google Maps link: ' + e);
}
} else {
log('link NOT inserted by insertGMapLink(). Invalid element or link already created. 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 insertGMapLinkDelayed() {
if (fixr.context.pageType === 'PHOTOPAGE') {
log('insertGMapLinkDelayed() running... with pageType=' + fixr.context.pageType);
setTimeout(insertGMapLink, 1500); // make maplink work better on photopage
setTimeout(insertGMapLink, 3500); // Twice. Photopage is sometimes a bit slow building
setTimeout(insertGMapLink, 8000); // Triple. Photopage is sometimes very slow building
}
}
function mapInitializer() {
if (window.location.href.includes('flickr.com/map/?')) {
// https://developer.mozilla.org/en-US/docs/Web/API/URL
const url = new URL(window.location.href);
// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
const imgId = url.searchParams.get('photo');
if (imgId) {
const focusImg = document.getElementById('f_img_thumb_' + imgId);
if (focusImg) {
focusImg.click(); // close and ...
focusImg.click(); // reopen to highlight position on map
}
}
}
}
const topMenuItems_style = '.fluid-subnav .extraitems a {padding: 12px 10px !important} .subnav-refresh ul.nav-links.extraitems li.sn-navitem a {padding: 13px 10px 12px 10px !important}';
function topMenuItems() {
// User dropdown menu
var m = document.querySelector('li[data-context=you] > ul.gn-submenu') || document.querySelector('li[data-context=you] div#you-panel ul');
if (m) {
var gid = null;
if (m.querySelector('a[data-track=gnYouGroupsClick]')) {
gid = m.querySelector('a[data-track=gnYouGroupsClick]').parentElement;
}
if (!gid && m.querySelector('a[data-track=You-groups]')) {
gid = m.querySelector('a[data-track=You-groups]').parentElement;
}
var aad = m.querySelector('a[data-track=gnYouSetsClick]') || m.querySelector('a[data-track=You-sets]');
if (aad && gid) {
if (gid.hasAttribute('aria-label') && !m.querySelector('li[aria-label=Tags]')) {
// latest design
gid.insertAdjacentElement('afterend', createRichElement('li',{class: 'menuitem', role: 'menuitem', 'aria-label': 'Tags'},createRichElement('a',{'data-track': 'gnYouTagsClick', href: '/photos/me/tags'}, 'Tags')));
aad.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'menuitem', role: 'menuitem', 'aria-label': 'Map'}, createRichElement('a', {'data-track': 'gnYouMapClick', href: '/photos/me/map'}, 'Map')));
aad.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'menuitem', role: 'menuitem', 'aria-label': 'Collections'}, createRichElement('a', {'data-track': 'gnYouCollectionsClick', href: '/photos/me/collections'}, 'Collections')));
} else if (gid.classList.contains('gn-subnav-item') && !m.querySelector('a[data-track=You-tags]')) {
// earlier design
gid.insertAdjacentElement('afterend', createRichElement('li', {class: 'gn-subnav-item'}, createRichElement('a',{'data-track': 'You-tags', href: '/photos/me/tags'}, 'Tags')));
aad.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'gn-subnav-item'}, createRichElement('a', {'data-track': 'You-map', href: '/photos/me/map'}, 'Map')));
aad.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'gn-subnav-item'}, createRichElement('a', {'data-track': 'You-collections', href: '/photos/me/collections'}, 'Collections')));
}
}
}
// Photographer menu bar
m = document.querySelector('ul.links[role=menubar]') || document.querySelector('ul.nav-links');
if (m) {
var gib = m.querySelector('li#groups') || m.querySelector('li.sn-groups');
var aab = m.querySelector('li#albums a') || m.querySelector('li.sn-navitem-sets a');
if (aab && gib) {
m.classList.add('extraitems'); // mark extra items being added (so adjust spacing in style)
if (gib.id === 'groups' && !m.querySelector('li#tags')) {
// latest design
gib.insertAdjacentElement('afterend', createRichElement('li', {id: 'tags', class: 'link', role: 'menuitem'}, createRichElement('a', {href: '/photos/'+fixr.context.photographerId+'/tags'}, createRichElement('span', {}, 'Tags'))));
aab.parentElement.insertAdjacentElement('afterend', createRichElement('li', {id: 'map', class: 'menuitem', role: 'menuitem'}, createRichElement('a', {href: '/photos/' + fixr.context.photographerId + '/map'}, createRichElement('span', {}, 'Map'))));
aab.parentElement.insertAdjacentElement('afterend', createRichElement('li', {id: 'collections', class: 'menuitem', role: 'menuitem'}, createRichElement('a', {href: '/photos/' + fixr.context.photographerId + '/collections'}, createRichElement('span', {}, 'Collections'))));
} else if (gib.classList.contains('sn-groups') && !m.querySelector('li.sn-tags')) {
// earlier design
gib.insertAdjacentElement('afterend', createRichElement('li', {class: 'sn-navitem sn-tags'}, createRichElement('a', {'data-track': 'YouSubnav-tags', href: '/photos/' + fixr.context.photographerId + '/tags'}, 'Tags')));
aab.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'sn-navitem sn-map'}, createRichElement('a', {'data-track': 'YouSubnav-map', href: '/photos/' + fixr.context.photographerId + '/map'}, 'Map')));
aab.parentElement.insertAdjacentElement('afterend', createRichElement('li', {class: 'sn-navitem sn-collections'}, createRichElement('a', {'data-track': 'YouSubnav-collections', href: '/photos/' + fixr.context.photographerId + '/collections'}, 'Collections')));
}
}
}
}
var album = { // cache to avoid repeating requests
albumId: '',
commentCount: 0,
comment: [],
description: ''
};
// function updateAlbumCommentCount() {
// var _reqAlbumComments = null;
// if (window.XMLHttpRequest) {
// _reqAlbumComments = new XMLHttpRequest();
// if (typeof _reqAlbumComments.overrideMimeType !== 'undefined') {
// _reqAlbumComments.overrideMimeType('text/html');
// }
// _reqAlbumComments.onreadystatechange = function () {
// if (_reqAlbumComments.readyState === 4 && _reqAlbumComments.status === 200) {
// log('_reqAlbumComments returned status=' + _reqAlbumComments.status);
// var doc = document.implementation.createHTMLDocument("sizeDoc");
// doc.documentElement.innerHTML = _reqAlbumComments.responseText;
// album.albumId = fixr.context.albumId;
// album.commentCount = -1;
// var e = doc.body.querySelectorAll('span.LinksNew b.Here');
// if (e && e.length === 1) {
// var n = parseInt(e[0].innerText, 10);
// if (Number.isNaN(n)) {
// album.commentCount = 0;
// } else {
// album.commentCount = n;
// }
// } else {
// album.commentCount = -1;
// log('b.Here??? ');
// }
// if (document.getElementById('albumCommentCount')) {
// if (album.commentCount === -1) {
// document.getElementById('albumCommentCount').innerText = '?';
// } else {
// document.getElementById('albumCommentCount').innerText = String(album.commentCount);
// }
// } else {
// log('albumCommentCount element not found');
// }
// } else {
// // wait for the call to complete
// }
// };
// if (fixr.context.albumId === album.albumId && fixr.context.albumId !== '' && album.commentCount !== -1) {
// log('Usinging CACHED album count!...');
// document.getElementById('albumCommentCount').innerText = String(album.commentCount);
// } else if (fixr.context.albumId !== '') {
// var url = 'https://www.flickr.com/photos/' + (fixr.context.photographerAlias || fixr.context.photographerId) + '/sets/' + fixr.context.albumId + '/comments/'; // /sets/* urls works, /albums/* urls doesn't currently work
// _reqAlbumComments.open('GET', url, true);
// _reqAlbumComments.send(null);
// } else {
// log('albumId not initialized');
// }
// } else {
// log('understøtter ikke XMLHttpRequest');
// }
// }
//
// var _wsGetPhotosetInfoLock = 0;
// function wsGetPhotosetInfo() {
// var diff = Date.now() - _wsGetPhotosetInfoLock;
// if ((_wsGetPhotosetInfoLock > 0) && (diff < 50)) {
// log('Skipping wsGetPhotosetInfo() because already running?: ' + diff);
// // *** maybe add a check to see if we are still on same album?!
// return;
// }
// _wsGetPhotosetInfoLock = Date.now();
// function handleResponse(response) {
// if (response.ok) {
// if (response.headers.get('content-type') && response.headers.get('content-type').includes('application/json')) {
// return response.json()
// }
// throw new Error('Response was not in expected json format.');
// }
// throw new Error('Network response was not ok.');
// }
// function handleResult(obj) {
// if (obj.stat === "ok") {
// if (obj.photoset && obj.photoset && obj.photoset.description) {
//
// document.getElementById('albumDescription').innerHTML = obj.photoset.description._content.replace(/\n/g, '
');
//
// }
// } else {
// log('flickr.photosets.getInfo returned an ERROR: obj.stat=' + obj.stat + ', obj.code=' + obj.code + ', obj.message=' + obj.message);
// }
// }
// function handleError(error) {
// console.log('There has been a problem with your fetch operation: ', error.message);
// log('There has been a problem with your fetch operation: ' + error);
// }
// if (fixr.isWebExtension()) {
// // Call fetch() from background-script in WebExtensions, because changes in Chrome/Chromium https://www.chromium.org/Home/chromium-security/extension-content-script-fetches
// browser.runtime.sendMessage({msgtype: "flickrservice", method: "flickr.photosets.getInfo", fkey: fkey, options: {photoset_id: fixr.context.albumId, user_id: fixr.context.photographerId}}).then(handleResult).catch(handleError);
// } else { // Userscript (So far it still works, also on Chrome/Tampermonkey...)
// fetch('https://api.flickr.com/services/rest/?method=flickr.photosets.getInfo&api_key=' + fkey + '&photoset_id=' + fixr.context.albumId + '&user_id=' + fixr.context.photographerId + '&format=json&nojsoncallback=1').then(handleResponse).then(handleResult).catch(handleError);
// }
// }
var _wsGetPhotosetCommentsLock = 0;
function wsGetPhotosetComments() { // Call Flickr REST API to get album comments
var diff = Date.now() - _wsGetPhotosetCommentsLock;
if ((_wsGetPhotosetCommentsLock > 0) && (diff < 50)) {
log('Skipping wsGetPhotosetComments() because already running?: ' + diff);
// *** maybe add a check to see if we are still on same album?!
return;
}
_wsGetPhotosetCommentsLock = Date.now();
function handleResponse(response) {
if (response.ok) {
if (response.headers.get('content-type') && response.headers.get('content-type').includes('application/json')) {
return response.json()
}
throw new Error('Response was not in expected json format.');
}
throw new Error('Network response was not ok.');
}
function handleResult(obj) {
album.albumId = fixr.context.albumId;
album.commentCount = -1;
if (obj.stat === "ok") {
log("flickr.photosets.comments.getList returned ok");
if (obj.comments && obj.comments.photoset_id) {
album.albumId = obj.comments.photoset_id;
}
if (obj.comments && obj.comments.comment) {
album.commentCount = obj.comments.comment.length;
} else {
album.commentCount = 0;
}
} else {
log('flickr.photosets.comments.getList returned an ERROR: obj.stat=' + obj.stat + ', obj.code=' + obj.code + ', obj.message=' + obj.message);
}
if (document.getElementById('albumCommentCount')) {
if (album.commentCount === -1) {
document.getElementById('albumCommentCount').innerText = '?';
} else {
document.getElementById('albumCommentCount').innerText = String(album.commentCount);
}
} else {
log('albumCommentCount element not found');
}
// if (obj.comments.comment) {
// let comments = '';
// for (let comment of obj.comments.comment) {
// comments += '
' + comment._content.replace(/\n/g, '
').replace(/http:\/\/farm\d+\.static\.flickr\.com/g, 'https://live.staticflickr.com').replace(/http:\/\/(www\.)?flickr\.com/g, 'https://www.flickr.com') + '
' + takenDateStr + uploadDateStr + '
'; // NOTICE how takenDateStr and uploadDateStr are defined above. This should be safe. // elem.innerHTML = (DEBUG ? '' + debugstr + '
' : '') + '' + takenDateStr + uploadDateStr + '
'; var withTitle = elem.parentElement.querySelector('span[title]'); if (withTitle) { withTitle.removeAttribute('title'); } } } } else { if (elem) { elem.textContent = 'Cannot fetch detailed date details on private photos'; } log('flickr.photos.getInfo returned an ERROR: obj.stat=' + obj.stat + ', obj.code=' + obj.code + ', obj.message=' + obj.message); } _wsGetPhotoInfoLock = 0; } function handleError(error) { console.log('There has been a problem with your fetch operation: ', error.message); log('There has been a problem with your fetch operation: ' + error); var elem = document.querySelector('.date-info'); if (elem) { elem.textContent = 'There was an error fetching detailed date details...'; } } if (fixr.isWebExtension()) { // Call fetch() from background-script in WebExtensions, because changes in Chrome/Chromium https://www.chromium.org/Home/chromium-security/extension-content-script-fetches browser.runtime.sendMessage({msgtype: "flickrservice", method: "flickr.photos.getInfo", fkey: fkey, options: {photo_id: fixr.context.photoId}}).then(handleResult).catch(handleError); } else { // Userscript (So far it still works, also on Chrome/Tampermonkey...) fetch('https://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=' + fkey + '&photo_id=' + fixr.context.photoId + '&format=json&nojsoncallback=1').then(handleResponse).then(handleResult).catch(handleError); } } function stereotest() { var self = "flickrfixruserscript"; var other = "flickrfixrwebextension"; if (fixr.isWebExtension()) { self = "flickrfixrwebextension"; other = "flickrfixruserscript"; } document.body.classList.add(self); if (document.body.classList.contains(other)) { alert("It looks like you are running both Stigs Flickr Fixr userscript and Flickr Fixr browser extension at once. Please uninstall or disable one of them to avoid errors and unpredictable behaviors!"); } } function runEarly() { //localStorage.setItem('filterFeedEvents', 'people'); // Try to make People feed default. } function userscriptWarning() { if (!document.body.classList.contains("flickrfixrwebextension")) { // Skip warning if both versions are installed (There will be another warning from stereotest()). let info = (GM_info ? GM_info : (typeof GM === 'object' && GM !== null && typeof GM.info === 'object' ? GM.info : null) ); if (info) { let prevDate = localStorage.getItem("fixr_userscript_warning_timestamp"); let prevVersion = localStorage.getItem("fixr_userscript_warning_version"); if (!prevVersion) { localStorage.setItem("fixr_userscript_warning_timestamp", Date.now().toString()); localStorage.setItem("fixr_userscript_warning_version", info.script.version); alert('\nYou are running the userscript-version of Flickr Fixr via ' + info.scriptHandler + '. Did you know that Flickr Fixr is also available as a regular browser extension for most webbrowsers? \n\nSupport (and test) of userscript-version is limited and some features might stop working. It is highly recommended to use the regular browser extensions instead. \n\nYou can find Flickr Fixr browser extension in the Add-on webstores for Chrome, Firefox and (the new Chromium based version of) Edge.\n'); } } } } const shared_style = 'img.asquare {width:75px;height:75px;border:none;margin:0;padding:0;transition:all 0.3s ease} a:hover>img.asquare{transform:scale(1.3)}'; // used by multiple features function handlerInitFixr(options) { // Webextension init let runNow = []; let onPageHandlers = []; let onResizeHandlers = []; let onFocusHandlers = []; let onStandaloneHandlers = []; fixr.style.add(shared_style); onPageHandlers.push(stereotest); if (options.scaler) { fixr.style.add(scaler.style); onPageHandlers.push(scaler.run); onResizeHandlers.push(scaler.run); } if (options.topMenuItems) { fixr.style.add(topMenuItems_style); onPageHandlers.push(topMenuItems); onStandaloneHandlers.push(topMenuItems); } if (options.ctrlClicking) { onPageHandlers.push(ctrlClicking); } if (options.albumExtras) { fixr.style.add(albumExtras_style); onPageHandlers.push(albumExtras); } if (options.topPagination) { fixr.style.add(topPagination_style); onPageHandlers.push(topPagination); } if (options.shootingSpaceballs) { onPageHandlers.push(shootingSpaceballs); } if (options.orderWarning) { fixr.style.add(orderwarning_style); onPageHandlers.push(orderWarning); } if (options.newsfeedLinks) { fixr.style.add(newsfeedLinks_style); onPageHandlers.push(newsfeedLinks); onStandaloneHandlers.push(newsfeedLinks); } if (options.photoDates) { fixr.style.add(photoDates_style); onPageHandlers.push(photoDatesDelayed); } if (options.ctrlClicking) { onPageHandlers.push(ctrlClickingDelayed); } if (options.exploreCalendar) { onPageHandlers.push(exploreCalendarDelayed); } if (options.albumTeaser) { fixr.style.add(albumTeaser_style); onPageHandlers.push(albumTeaserDelayed); } if (options.insertGMapLink) { onPageHandlers.push(insertGMapLinkDelayed); onStandaloneHandlers.push(mapInitializer); } if (options.updateTags) { if (options.updateTags_tagmode === 'updateTags_persist') { fixr.style.add(updateTags_style_persist); } else { fixr.style.add(updateTags_style_hover); } onPageHandlers.push(updateTagsDelayed); } fixr.init(runNow, onPageHandlers, onResizeHandlers, onFocusHandlers, onStandaloneHandlers); } if (window.location.href.includes('flickr.com\/services\/api\/explore\/')) { // We are on Flickr API Explorer (WAS used for note handling before Flickr returned native note-support) and outside "normal" flickr page flow. fixr wont do here... } else { if (fixr.isWebExtension()) { log('WebExtension - init with options...'); withOptionsDo(handlerInitFixr); // Load selected features and run fixr.init with them... } else { log('Userscript - fixr.init...'); fixr.style.add(shared_style); fixr.style.add(scaler.style); fixr.style.add(albumExtras_style); fixr.style.add(topPagination_style); fixr.style.add(orderwarning_style); fixr.style.add(topMenuItems_style); fixr.style.add(photoDates_style); fixr.style.add(newsfeedLinks_style); fixr.style.add(albumTeaser_style); fixr.style.add(updateTags_style_hover); // FIXR fixr.init([runNow], [onPageHandlers], [onResizeHandlers], [onFocusHandlers], [onStandaloneHandlers]) fixr.init([/* runEarly */], [stereotest, scaler.run, topMenuItems, ctrlClicking, albumExtras, topPagination, shootingSpaceballs, orderWarning, newsfeedLinks, photoDatesDelayed, ctrlClickingDelayed, exploreCalendarDelayed, albumTeaserDelayed, insertGMapLinkDelayed, updateTagsDelayed, userscriptWarning], [scaler.run], [], [topMenuItems, newsfeedLinks, mapInitializer]); } }