CSFD Movie Preview

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

当前为 2016-01-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name CSFD Movie Preview
  3. // @namespace http://csfd.cz
  4. // @description Při najetí myší na odkaz na film se zobrazí náhled jeho profilu.
  5. // @match http://www.csfd.cz/*
  6. // @exclude http://www.csfd.cz/uzivatel/*/profile-edit/
  7. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @version 1.1
  13. // ==/UserScript==
  14.  
  15. // CHANGES
  16. // -------
  17. // 1.1 - výměna jQuery.ajax(), který ve Firefoxu přestal fungovat, za GM_xmlhttpRequest()
  18. // 1.0 - první verze
  19.  
  20. $ = this.jQuery = jQuery.noConflict(true);
  21.  
  22. $('<div id="movie-preview" style="display: none; z-index: 999; width: 400px; background-color: #efefef; padding: 6px; ' +
  23. 'border-radius: 4px; box-shadow: 0 0 10px 4px #777777"><table border="1"><tr><td id="movie-preview-poster" width="120" ' +
  24. 'style="text-align: center"></td><td id="movie-preview-content" style="vertical-align: top; padding-left: 7px"></td>' +
  25. '</tr></table></div>').appendTo('body');
  26.  
  27. var cacheExpires = 7; // days
  28. var doPrefetch = false;
  29.  
  30. var movieBox = $('div#movie-preview');
  31. var movieBoxPoster = movieBox.find('#movie-preview-poster');
  32. var movieBoxContent = movieBox.find('#movie-preview-content');
  33.  
  34. var movieLinkSelector = 'a[href*="/film/"], a[href*="/film.php"]';
  35.  
  36. var thisPageMovieId = parseMovieId(window.location.href);
  37. var currentMovieId = null;
  38. var movies = [];
  39.  
  40. var timerId = -1;
  41.  
  42. // Greasmonkey-only section start
  43.  
  44. if (typeof GM_registerMenuCommand == 'function' && isStorageSupported()) {
  45. doPrefetch = GM_getValue("doPrefetch", false);
  46.  
  47. GM_registerMenuCommand("Přepnout automatické nahrávání náhledů filmů", function() {
  48. doPrefetch = !GM_getValue("doPrefetch", false);
  49.  
  50. GM_setValue("doPrefetch", doPrefetch);
  51.  
  52. alert("Automatické nahrávání náhledů filmů " + (doPrefetch? "zapnuto": "vypnuto") + ".\nZměna nastavení se projeví po obnovení stránky.");
  53. });
  54. }
  55.  
  56. // Greasmonkey-only section end
  57.  
  58. function isStorageSupported() {
  59. return typeof(Storage) !== void(0);
  60. }
  61.  
  62. function parseMovieId(movieURL) {
  63. var match = movieURL.match(/\/film(?:\.php\?|\/)([\d]+)/);
  64. return match && match.length >= 2? 'm' + match[1]: null;
  65. }
  66.  
  67. function getDiffDays(date1, date2) {
  68. return Math.round(Math.abs(date1 - date2) / (1000 * 3600 * 24));
  69. }
  70.  
  71. var storage = isStorageSupported()?
  72. { // local storage
  73. getStoredItem: function(movieURL) {
  74. return localStorage[parseMovieId(movieURL)];
  75. },
  76. setStoredItem: function(movieURL, value) {
  77. try {
  78. localStorage[parseMovieId(movieURL)] = value;
  79. } catch (ex) {
  80. // "Persistent storage maximum size reached" -> remove 10 random items
  81. for (i=0; i < 10; i++) {
  82. var index = Math.floor(Math.random() * localStorage.length);
  83. var key = localStorage.key(index);
  84.  
  85. localStorage.removeItem(key);
  86. }
  87.  
  88. return this.setStoredItem(movieURL, value);
  89. }
  90. },
  91. cleanExpiredData: function() {
  92. var lastCleanup = localStorage["last-cleanup"]? Date.parse(localStorage["last-cleanup"]): new Date(0);
  93.  
  94. // run cleanup only once per day
  95. if (getDiffDays(new Date(), lastCleanup) < 1) return;
  96.  
  97. for(var key in localStorage) {
  98. if (key.match(/m\d+/)) {
  99. var cached = JSON.parse(localStorage[key]);
  100. if (getDiffDays(new Date(), Date.parse(cached.timestamp)) > cacheExpires) {
  101. localStorage.removeItem(key);
  102. }
  103. }
  104. }
  105.  
  106. localStorage["last-cleanup"] = new Date();
  107. }
  108. }:
  109. { // dummy storage
  110. getStoredItem: function(movieURL) {
  111. return null;
  112. },
  113. setStoredItem: function(movieURL, value) {
  114. // noop
  115. },
  116. cleanExpiredData: function() {
  117. // noop
  118. }
  119. };
  120.  
  121. function getMovieBoxPosition(event) {
  122. var boxWidth = movieBox.width() + 10;
  123. var tPosX = boxWidth - event.clientX + 30 > 0? event.pageX + 30: event.pageX - boxWidth - 30;
  124. var tPosY = event.pageY + event.clientY;
  125.  
  126. if (event.clientY > 30) {
  127. var winHeight = $(window).height();
  128. var boxHeight = movieBox.height() > winHeight? winHeight - 60: movieBox.height();
  129. var overflowY = event.clientY + boxHeight - winHeight;
  130. tPosY = overflowY > 0? event.pageY - overflowY - 50: event.pageY - 30;
  131. }
  132.  
  133. return { X: tPosX, Y: tPosY };
  134. }
  135. function showMovieBox(event, profile, rating) {
  136. var poster = profile.find("#poster img");
  137. var title = "<h1 style='text-transform: none'>" + profile.find(".info h1").text().trim() + "</h1>";
  138. var genre = profile.find(".genre");
  139. var origin = profile.find(".origin");
  140. var creators = profile.find(".creators");
  141.  
  142. movieBoxPoster.html('');
  143. movieBoxPoster.append(poster.css('width', 120));
  144. movieBoxPoster.append('<br><h1 style="font-size: 32px">' + rating + '</h1>');
  145.  
  146. movieBoxContent.html('');
  147. movieBoxContent.append(title);
  148. movieBoxContent.append(genre.css('font-weight', 'bold'));
  149. movieBoxContent.append(origin.css('font-weight', 'bold'));
  150. movieBoxContent.append('<br>');
  151. movieBoxContent.append(creators);
  152.  
  153. var pos = getMovieBoxPosition(event);
  154. movieBox.css({ 'position': 'absolute', 'top': pos.Y, 'left': pos.X }).show();
  155. }
  156.  
  157. function getCachedData(movieURL) {
  158. var cached = storage.getStoredItem(movieURL);
  159.  
  160. if (cached) {
  161. cached = JSON.parse(cached);
  162.  
  163. if (getDiffDays(new Date(), Date.parse(cached.timestamp)) <= cacheExpires)
  164. return { "profile": $(cached.profile), "rating": cached.rating };
  165. }
  166.  
  167. return null;
  168. }
  169. function loadMovieBox(movieURL, doneCallback, errorCallback, redirectMovieURL) {
  170. if (!redirectMovieURL) redirectMovieURL = movieURL;
  171.  
  172. console.log("[CSFD Movie Preview] Loading movie page: " + redirectMovieURL);
  173.  
  174. GM_xmlhttpRequest({
  175. method: "GET",
  176. url: redirectMovieURL,
  177. onload: function(response) {
  178. try {
  179. if (false /* TODO: handle redirect */) {
  180. loadMovieBox(movieURL, doneCallback, errorCallback, response.redirect);
  181. } else {
  182. response = $(response.responseText);
  183.  
  184. var profile = response.find("div#profile").html().replace(/[\t\n]+/mg, '');
  185. var rating = response.find("div#rating .average").text().trim();
  186. storage.setStoredItem(movieURL, JSON.stringify({ "profile": profile, "rating": rating, "timestamp": new Date() }));
  187. if (doneCallback) doneCallback($(profile), rating);
  188. }
  189. } catch(ex) {
  190. console.log("[CSFD Movie Preview] Error in AJAX handler: " + ex.message);
  191.  
  192. if (errorCallback) errorCallback();
  193. }
  194. },
  195. onerror: function(response) {
  196. if (errorCallback) errorCallback();
  197. }
  198. });
  199. }
  200.  
  201. $(movieLinkSelector).hover(function(event) {
  202. var movieURL = $(this).attr("href").trim();
  203. var movieId = parseMovieId(movieURL);
  204.  
  205. // prevent previews of the movie on its page
  206. if (thisPageMovieId == movieId) return;
  207.  
  208. currentMovieId = movieId;
  209.  
  210. var cached = getCachedData(movieURL);
  211. if (cached) {
  212. showMovieBox(event, cached.profile, cached.rating);
  213. } else {
  214. clearTimeout(timerId);
  215.  
  216. timerId = setTimeout(function() {
  217. loadMovieBox(movieURL, function(profile, rating) {
  218. if (currentMovieId == movieId) showMovieBox(event, profile, rating);
  219. });
  220. }, 30);
  221. }
  222. }, function() {
  223. clearTimeout(timerId);
  224. timerId = -1;
  225. currentMovieId = null;
  226.  
  227. movieBox.hide();
  228. });
  229.  
  230. if (doPrefetch && isStorageSupported()) {
  231. var movieURL;
  232.  
  233. $(movieLinkSelector).each(function() {
  234. movieURL = $(this).attr("href").trim();
  235. movies.push(movieURL);
  236. });
  237. function prefetchMovies() {
  238. if (movieURL = movies.shift()) {
  239. setTimeout(function() {
  240. if (!getCachedData(movieURL)) {
  241. loadMovieBox(movieURL, prefetchMovies, prefetchMovies);
  242. } else {
  243. prefetchMovies();
  244. }
  245. }, 300);
  246. }
  247. }
  248. prefetchMovies();
  249. }
  250.  
  251. // clean old movies
  252. storage.cleanExpiredData();