Greasy Fork is available in English.

CSFD Movie Preview

Při najetí myší na odkaz na film se zobrazí náhled jeho profilu.

Verze ze dne 02. 08. 2014. Zobrazit nejnovější verzi.

// ==UserScript==
// @name        CSFD Movie Preview
// @namespace   http://csfd.cz
// @description Při najetí myší na odkaz na film se zobrazí náhled jeho profilu.
// @match       http://www.csfd.cz/*
// @exclude     http://www.csfd.cz/uzivatel/*/profile-edit/
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @grant       GM_registerMenuCommand
// @grant       GM_getValue
// @grant       GM_setValue
// @version     1.0
// ==/UserScript==

$ = this.jQuery = jQuery.noConflict(true);

$('<div id="movie-preview" style="display: none; z-index: 999; width: 400px; background-color: #efefef; padding: 6px; border-radius: 4px; box-shadow: 0 0 10px 4px #777777">' + 
  '<table border="1"><tr><td id="movie-preview-poster" width="120" style="text-align: center"></td><td id="movie-preview-content" style="vertical-align: top; padding-left: 7px"></td></tr></table></div>').appendTo('body');

var cacheExpires = 7; // days
var doPrefetch = false;

var movieBox = $('div#movie-preview');
var movieBoxPoster = movieBox.find('#movie-preview-poster');
var movieBoxContent = movieBox.find('#movie-preview-content');

var movieLinkSelector = 'a[href*="/film/"], a[href*="/film.php"]';

var thisPageMovieId = parseMovieId(window.location.href);
var currentMovieId = null;
var movies = [];

var timerId = -1;

// Greasmonkey-only section start

if (typeof GM_registerMenuCommand == 'function' && isStorageSupported()) {
	doPrefetch = GM_getValue("doPrefetch", false);

	GM_registerMenuCommand("Přepnout automatické nahrávání náhledů filmů", function() {
		doPrefetch = !GM_getValue("doPrefetch", false);

		GM_setValue("doPrefetch", doPrefetch);

		alert("Automatické nahrávání náhledů filmů " + (doPrefetch? "zapnuto": "vypnuto") + ".\nZměna nastavení se projeví po obnovení stránky.");
	});
}

// Greasmonkey-only section end

function isStorageSupported() {
    return typeof(Storage) !== void(0);
}

function parseMovieId(movieURL) {
    var match = movieURL.match(/\/film(?:\.php\?|\/)([\d]+)/);
    
    return match && match.length >= 2? 'm' + match[1]: null;
}

function getDiffDays(date1, date2) {
	return Math.round(Math.abs(date1 - date2) / (1000 * 3600 * 24));
}

var storage = isStorageSupported()?
	{ // local storage
		getStoredItem: function(movieURL) {
    		return localStorage[parseMovieId(movieURL)];
		},
		setStoredItem: function(movieURL, value) {
			try {
				localStorage[parseMovieId(movieURL)] = value;
			} catch (ex) {
				// "Persistent storage maximum size reached" -> remove 10 random items
				for (i=0; i < 10; i++) {
					var index = Math.floor(Math.random() * localStorage.length);
					var key = localStorage.key(index);

					localStorage.removeItem(key);
				}

				return this.setStoredItem(movieURL, value);
			}
		},
        cleanExpiredData: function() {
			var lastCleanup = localStorage["last-cleanup"]? Date.parse(localStorage["last-cleanup"]): new Date(0);

			// run cleanup only once per day
			if (getDiffDays(new Date(), lastCleanup) < 1) return;

            for(var key in localStorage) {
				if (key.match(/m\d+/)) {
					var cached = JSON.parse(localStorage[key]);
					
					if (getDiffDays(new Date(), Date.parse(cached.timestamp)) > cacheExpires) {
						localStorage.removeItem(key);
					}
				}
			}

			localStorage["last-cleanup"] = new Date();
        }
    }:
	{ // dummy storage
		getStoredItem: function(movieURL) {
    		return null;
		},
		setStoredItem: function(movieURL, value) {
    		// noop
		},
        cleanExpiredData: function() {
            // noop
        }
    };

function getMovieBoxPosition(event) {
	var boxWidth = movieBox.width() + 10;
	var tPosX = boxWidth - event.clientX + 30 > 0? event.pageX + 30: event.pageX - boxWidth - 30;
	var tPosY = event.pageY + event.clientY;

	if (event.clientY > 30) {
		var winHeight = $(window).height();
		var boxHeight = movieBox.height() > winHeight? winHeight - 60: movieBox.height();
		var overflowY = event.clientY + boxHeight - winHeight;
		tPosY = overflowY > 0? event.pageY - overflowY - 50: event.pageY - 30;
	}

    return { X: tPosX, Y: tPosY };
}
    
function showMovieBox(event, profile, rating) {
    var poster   = profile.find("#poster img");
    var title    = "<h1 style='text-transform: none'>" + profile.find(".info h1").text().trim() + "</h1>";
    var genre    = profile.find(".genre");
    var origin   = profile.find(".origin");
	var creators = profile.find(".creators");

    movieBoxPoster.html('');
    movieBoxPoster.append(poster.css('width', 120));
    movieBoxPoster.append('<br><h1 style="font-size: 32px">' + rating + '</h1>');

    movieBoxContent.html('');
    movieBoxContent.append(title);
    movieBoxContent.append(genre.css('font-weight', 'bold'));
    movieBoxContent.append(origin.css('font-weight', 'bold'));
    movieBoxContent.append('<br>');
    movieBoxContent.append(creators);

    var pos = getMovieBoxPosition(event);
    
    movieBox.css({ 'position': 'absolute', 'top': pos.Y, 'left': pos.X }).show();
}

function getCachedData(movieURL) {
    var cached = storage.getStoredItem(movieURL);

	if (cached) {
		cached = JSON.parse(cached);

		if (getDiffDays(new Date(), Date.parse(cached.timestamp)) <= cacheExpires)
			return { "profile": $(cached.profile), "rating": cached.rating };
	}

	return null;
}
    
function loadMovieBox(movieURL, doneCallback, errorCallback, redirectMovieURL) {
	if (!redirectMovieURL) redirectMovieURL = movieURL;

	console.log("Loading movie page: " + redirectMovieURL);

	$.ajax(redirectMovieURL).done(function(response) {
		try {
			if (typeof(response) === 'object') {
				loadMovieBox(movieURL, doneCallback, errorCallback, response.redirect);
			} else {
				response = $(response);

				var profile = response.find("div#profile").html().replace(/[\t\n]+/mg, '');
				var rating  = response.find("div#rating .average").text().trim();
				
				storage.setStoredItem(movieURL, JSON.stringify({ "profile": profile, "rating": rating, "timestamp": new Date() }));
				
				if (doneCallback) doneCallback($(profile), rating);
			}
		} catch(ex) {
			console.log("Error in AJAX handler: " + ex.message);

			if (errorCallback) errorCallback();
		}
	}).error(function(jqXHR, textStatus, errorThrown) {
		if (errorCallback) errorCallback();
	});
}

$(movieLinkSelector).hover(function(event) {
    var movieURL = $(this).attr("href").trim();
	var movieId  = parseMovieId(movieURL);

	// prevent previews of the movie on its page
	if (thisPageMovieId == movieId) return;

	currentMovieId = movieId;

    var cached = getCachedData(movieURL);
  
    if (cached) {
		showMovieBox(event, cached.profile, cached.rating);
    } else {
		clearTimeout(timerId);

		timerId = setTimeout(function() {
			loadMovieBox(movieURL, function(profile, rating) {
				if (currentMovieId == movieId) showMovieBox(event, profile, rating);
			});
		}, 30);
	}
}, function() {
	clearTimeout(timerId);
	timerId = -1;
	
	currentMovieId = null;

    movieBox.hide();
});

if (doPrefetch && isStorageSupported()) {
    var movieURL;

    $(movieLinkSelector).each(function() {
        movieURL = $(this).attr("href").trim();
        movies.push(movieURL);
    });
    
    function prefetchMovies() {
        if (movieURL = movies.shift()) {
			setTimeout(function() {
				if (!getCachedData(movieURL)) {
					loadMovieBox(movieURL, prefetchMovies, prefetchMovies);
				} else {
					prefetchMovies();
				}
			}, 300);
        }
    }
    
    prefetchMovies();
}

// clean old movies
storage.cleanExpiredData();