ProjectFreeTV Episode Links

Enhance Project Free TV viewing page with links to previous and next episodes

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        ProjectFreeTV Episode Links
// @namespace   pftvepisodelinks
// @include     http*://projectfreetv.im/watch/*
// @version     1
// @grant       none
// @description Enhance Project Free TV viewing page with links to previous and next episodes
// ==/UserScript==

(function () {
  "use strict";

  // Global utility methods
  HTMLCollection.prototype.any = function() {
    return this.length > 0;
  };
  HTMLCollection.prototype.first = function() {
    if (this == null) { throw new Error("The source is null"); }
    if (!this.any()) { throw new Error("The source sequence is empty"); }
    return this[0];
  };
  HTMLCollection.prototype.firstOrDefault = function(criteria) {
    for (var i = 0; i < this.length; i++) {
      if (criteria(this[i])) {
        return this[i];
      }
    }
    return null;
  };
  
  // HTTP client class
  var HttpClient = function() {
  };
  HttpClient.getAsync = function(url) {
    return new Promise(function(resolve, reject) {
      var request = new XMLHttpRequest();
      request.open("GET", url);
      
      request.onload = function() {
        if (request.status != 200) {
          reject(new Error("Non-success status code. Status:" + request.status + ", Text:" + request.statusText));
          return;
        }
        
        resolve(request.response);
      };
      
      request.onerror = function() {
        reject(new Error("Network error"));
      }
      
      request.send();
    });
  };
  HttpClient.getHtmlDocAsync = function(url) {
    return HttpClient.getAsync(url).then(function(response) {
      var doc = document.implementation.createHTMLDocument("response");
      doc.documentElement.innerHTML = response;
      return doc;
    });
  };
  
  // Episode class
  var Episode = function(series, season, episode) {
    this.series = series;
    this.season = season;
    this.episode = episode;
  };
  
  Episode.prototype.getAdjacentEpisodesAsync = function() {
    return HttpClient.getHtmlDocAsync(this.getLink()).then(function(response) {
      var div = response.getElementsByClassName("navlinks").first();
      var links = div.getElementsByTagName("a");
      var prevAnchor = links.firstOrDefault(function(a) { return a.rel == "prev"; });
      var nextAnchor = links.firstOrDefault(function(a) { return a.rel == "next"; });
      
      return {
        previous: Episode.createFromLink(prevAnchor ? prevAnchor.href : null),
        next: Episode.createFromLink(nextAnchor ? nextAnchor.href : null)
      };
    }).catch(function(error) {
      return {
        previous: null,
        next: null
      };
    });
  };
  
  Episode.prototype.getNextEpisodeOrDefault = function() {
    return new Episode(this.series, this.season, this.episode + 1);
  };
  
  Episode.prototype.getPreviousEpisodeOrDefault = function() {
    if (this.episode < 1) {
      return null;
    }
    return new Episode(this.series, this.season, this.episode - 1);
  };
  
  Episode.prototype.getLink = function() {
    return "http://projectfreetv.im/episode/" + this.toString().toLowerCase().replace(/ /g, "-");
  };
  
  Episode.prototype.toString = function() {
    return this.series + " Season " + this.season + " Episode " + this.episode;
  };
  
  Episode.prototype.toShortString = function() {
    return "S" + this.season + "E" + this.episode;
  }
  
  // Static helper methods
  Episode.createFromTitle = function(title) {
    var parsedEpisode = title.match(/(.+) Season (\d+) Episode (\d+)/);
    var series = parsedEpisode[1];
    var season = Number.parseInt(parsedEpisode[2]);
    var episode = Number.parseInt(parsedEpisode[3]);
    return new Episode(series, season, episode);
  };
  
  Episode.createFromLink = function(link) {
    if (link == null) {
      return null;
    }
    
    var parsedEpisode = link.match(/\/episode\/(\S+)/);
    var title = parsedEpisode[1].replace(/-|\//g, " ").trim().toLowerCase().replace(/\b[a-z]/g, String.toUpperCase);
    return Episode.createFromTitle(title);
  }
  
  function createEpisodeAnchor(episode, text, float) {
    var anchor = document.createElement("a");
    anchor.href = episode.getLink();
    anchor.innerText = text + episode.toShortString();
    anchor.style.float = float;
    return anchor;
  };
  
  function run() {
    try {
      var title = document.getElementsByClassName("title").first();
      var curr = Episode.createFromTitle(title.innerText);
      
      var header = title.parentElement;
      title.style.margin = 0; // so that links sit flush with episode title

      curr.getAdjacentEpisodesAsync().then(function(episodes) {
        if (episodes.previous != null) {
          header.appendChild(createEpisodeAnchor(episodes.previous, "Previous ", "left"));
        }
        if (episodes.next != null) {
          header.appendChild(createEpisodeAnchor(episodes.next, "Next ", "right"));
        }
      });
    } catch (err) {
      console.error(err);
    }
  };

  run();
}) ();