您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Browse trough the articles with left and right cursor keys
// ==UserScript== // @name Better Movie-blog // @namespace hoehleg.userscripts.private // @version 0.5.14 // @description Browse trough the articles with left and right cursor keys // @author Gerrit Höhle // @match http://movie-blog.at/ // @match http://movie-blog.at/page/* // @match http://movie-blog.at/category/* // @match http://movie-blog.sx/ // @match http://movie-blog.sx/page/* // @match http://movie-blog.sx/category/* // @match http://www.movie-blog.org/ // @match http://www.movie-blog.org/page/* // @match http://www.movie-blog.org/category/* // @match http://movie-blog.to/ // @match http://movie-blog.to/page/* // @match http://movie-blog.to/category/* // @match http://movie-blog.tv/ // @match http://movie-blog.tv/page/* // @match http://movie-blog.tv/category/* // @match https://movie-blog.tv/ // @match https://movie-blog.tv/page/* // @match https://movie-blog.tv/category/* // @require https://greasyfork.org/scripts/390752-comparator/code/Comparator.js?version=744703 // @require https://greasyfork.org/scripts/391608-privateproperty/code/PrivateProperty.js?version=744693 // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript== /* jshint esversion: 6 */ /* globals Comparator, PrivateProperty */ (function() { 'use strict'; const CONFIG = { // number of pages to load Beitraege to one page PAGES_LOAD_COUNT: 2, // unit for row "Groesse" in beitraege table TABLE_BEITRAEGE_SIZE_DEFAULT_UNIT: "GB", // position of sidebar (below image) SIDEBAR_POSITION_TOP_PX : 670 }; const CONST = (() => { const URL_PATH = (() => { const path = document.location.pathname.split("/").filter(str => str.length > 0); return path === null ? [] : path; })(); const DATA_SIZE_UNITS = (() => { const DataSize = function(factor, shortText, longText) { this.factor = factor; this.shortText = shortText; this.longText= longText; Object.freeze(this); }; return [ new DataSize(1<<0, "B", "Byte"), new DataSize(1<<10, "KB", "Kilobyte"), new DataSize(1<<20, "MB", "Megabyte"), new DataSize(1<<30, "GB", "Gigabyte"), new DataSize(1<<40, "TB", "Terabyte"), ].reduce((map, dataSize) => map.set(dataSize.shortText, dataSize), new Map()); })(); const TABLE_BEITRAEGE_HEADER_NAMES = { NUM: "#", RATING: "IMDb", SIZE: "Größe", LINK: "Link", DATE: "Datum", TYPE: "Typ", JAHR: "Jahr", DURATION: "Dauer" }; const HTML_CLASSES = { DATE: "date", BEITRAG: "beitrag4", TABLE_BEITRAEGE_COLUMN_NUM: "col-num", TABLE_BEITRAEGE_COLUMN_RATING: "col-rating", TABLE_BEITRAEGE_COLUMN_SIZE: "col-size", TABLE_BEITRAEGE_COLUMN_LINK: "col-link", TABLE_BEITRAEGE_COLUMN_DATE: "col-date", TABLE_BEITRAEGE_COLUMN_TYPE: "col-type", TABLE_BEITRAEGE_COLUMN_JAHR: "col-jahr", TABLE_BEITRAEGE_COLUMN_DURATION: "col-duration", TABLE_ROW_HIGHLIGHTED: "row-highlighted", TABLE_COLUMN_SORTABLE: "sortable", TABLE_COLUMN_SORTED_ASC: "sorted-asc", TABLE_COLUMN_SORTED_DESC: "sorted-desc", PREVIOUS: "previous", NEXT: "next", }; const HTML_IDS = { SHOW_CONTAINER: "show-container", BEITRAG_INFO: "beitrag-info", PAGE_NUMBER: "page-number", BEITRAG_NUMBER: "beitrag-number", BEITRAG_COUNT: "beitrag-count", FILM_DATA_IMDB_RATING: "film-data-imdb-rating", FILM_DATA_GROESSE: "film-data-groesse", FILM_DATA_DAUER: "film-data-dauer", BUTTON_PAGE_PREV: "btn-page-prev", BUTTON_PAGE_NEXT: "btn-page-next", BUTTON_BEITRAG_PREV: "btn-beitrag-prev", BUTTON_BEITRAG_NEXT: "btn-beitrag-next", LISTE_BEITRAEGE: "liste-beitraege", HEAD: "head", CONTENT: "content", TABLE_TR_NUM: "tr-num", SIDEBAR: "sidebar", CUR_BEITRAG_IMG: "cur-beitrag-img" }; if (URL_PATH && URL_PATH[0] === "category") { Object.assign(HTML_CLASSES, { DATE: "date_x", BEITRAG : "beitrag2" }); Object.assign(HTML_IDS, { CONTENT : "archiv" }); } // Container let CSS = ` #page { background-size: 98%; width: 100%; height: auto; } #${HTML_IDS.CONTENT} { margin: 0px 0px 0px 0px; padding: 0px 0px 0px 3.25%; width: 67.5%; height: 5000px } #${HTML_IDS.SIDEBAR} { position: absolute; top: ${CONFIG.SIDEBAR_POSITION_TOP_PX}px; right: 0px; } #${HTML_IDS.SHOW_CONTAINER} { position: relative; top: 30px; } #${HTML_IDS.LISTE_BEITRAEGE} { position: relative; width: 100%; } `; // Beitrag CSS += ` #${HTML_IDS.CUR_BEITRAG_IMG} { position: fixed; top: 2px; left: 72%; max-width: 400px; z-index: 1; } #${HTML_IDS.BEITRAG_INFO} { position: fixed; top: 2px; left: 80px; } .${HTML_CLASSES.BEITRAG} { position: relative; width: 100%; margin: 1.3em 0 0 0; } .${HTML_CLASSES.PREVIOUS}, .${HTML_CLASSES.NEXT} { display: inline-block; padding: 20; margin: 0; text-decoration: none; text-align: center; font-weight: bold; } `; // Table of Beitraege CSS += ` #${HTML_IDS.LISTE_BEITRAEGE} > table { width: 100%; border: none; border-collapse: collapse; white-space: nowrap; font-size: smaller; } #${HTML_IDS.LISTE_BEITRAEGE} > table td, #${HTML_IDS.LISTE_BEITRAEGE} > table th { border-left: thin solid lightgray; border-right: thin solid lightgray; border-top: thin solid lightgray; padding: 5px 5px; } #${HTML_IDS.LISTE_BEITRAEGE} > table td:first-child, #${HTML_IDS.LISTE_BEITRAEGE} > table th:first-child { border-left: none; } #${HTML_IDS.LISTE_BEITRAEGE} > table td:last-child, #${HTML_IDS.LISTE_BEITRAEGE} > table th:last-child { border-right: none; } .${HTML_CLASSES.TABLE_ROW_HIGHLIGHTED} { background: rgba(255, 240, 200, 0.5); padding: 3px 5px; margin: -3px -5px; line-height: 1.7; border-radius: 3px; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_NUM} { width: 1em; max-width: 1.5em; text-align: right; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_RATING} { width: 1em; max-width: 3em; text-align: center; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_JAHR} { width: 1em; max-width: 3em; text-align: center; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK} { width: auto; max-width: 8em; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK} > a { color: black; letter-spacing: 1px; } .${HTML_CLASSES.TABLE_ROW_HIGHLIGHTED} .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK} > a { font-weight: 900; color: black; letter-spacing: 3px; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DURATION} { width: 1em; max-width: 3.5em; text-align: right; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_SIZE} { width: 1em; max-width: 5em; text-align: right; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_TYPE} { width: 1em; max-width: 5em; text-align: left; } .${HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DATE} { width: 1em; max-width: 9em; text-align: left; font-size: smaller; } `; // table sort indicators CSS += ` th.${HTML_CLASSES.TABLE_COLUMN_SORTABLE} { position: relative; cursor: pointer; } th.${HTML_CLASSES.TABLE_COLUMN_SORTABLE}::before { content: "\\21D5\\0020"; position: relative; left: 0px; color: #999; } th.${HTML_CLASSES.TABLE_COLUMN_SORTABLE}:hover::before { color: #333; } th.${HTML_CLASSES.TABLE_COLUMN_SORTED_ASC}::before { content: "\\21D3\\0020"; } th.${HTML_CLASSES.TABLE_COLUMN_SORTED_DESC}::before { content: "\\21D1\\0020"; } `; CSS += ` input[type="number"] { -webkit-appearance: textfield; -moz-appearance: textfield; appearance: textfield; } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; } `; const MONTH_NAMES = ["JANUAR", "FEBRUAR", "MÄRZ", "APRIL", "MAI", "JUNI", "JULI", "AUGUST", "SEPTEMBER" ,"OKTOBER" ,"NOVEMBER" ,"DEZEMBER"]; const REGEX = { DOKU: /\.DO[KC]U/i, SERIE: /\.S\d\d?(E\d\d?)?(-S\d\d?(E\d\d?)?)?\./i }; return { URL_PATH: URL_PATH, TABLE_BEITRAEGE_HEADER_NAMES: TABLE_BEITRAEGE_HEADER_NAMES, HTML_CLASSES: HTML_CLASSES, HTML_IDS: HTML_IDS, CSS: CSS, MONTH_NAMES: MONTH_NAMES, DATA_SIZE_UNITS: DATA_SIZE_UNITS, REGEX: REGEX }; })(); const Util = (() => { return { getPageAsync: (url, onSuccess, onError = () => {}) => { return GM_xmlhttpRequest({ method: 'GET', url: url, onload: (resp) => { switch (resp.status) { case 200: case 304: resp.html = new DOMParser().parseFromString(resp.responseText, 'text/html'); onSuccess(resp); return; default: onError(resp); return; } }, onerror: onError }); }, createInputElement: (id, type, width, maxLength = 3, textAlign = "left") => { const inputElement = document.createElement("INPUT"); inputElement.id = id; inputElement.type = type; inputElement.maxLength = maxLength; inputElement.style = `text-align:${textAlign};width:${width}`; return inputElement; }, createShowContainer: () => { const oldContainer = document.getElementById(CONST.HTML_IDS.SHOW_CONTAINER); if (!oldContainer) { const divHead = document.getElementById(CONST.HTML_IDS.HEAD); divHead.parentNode.removeChild(divHead); const divContent = document.getElementById(CONST.HTML_IDS.CONTENT); divContent.innerHTML = ""; const newContainer = divContent.insertBefore(document.createElement("DIV"), divContent.firstChild); newContainer.id = CONST.HTML_IDS.SHOW_CONTAINER; const divBeitragInfo = newContainer.appendChild(document.createElement("DIV")); divBeitragInfo.id = CONST.HTML_IDS.BEITRAG_INFO; divBeitragInfo.appendChild(document.createTextNode("Seite: ")); Object.assign(divBeitragInfo.appendChild(document.createElement("BUTTON")), { innerHTML: "‹", className: CONST.HTML_CLASSES.PREVIOUS, id: CONST.HTML_IDS.BUTTON_PAGE_PREV }); divBeitragInfo.appendChild(Util.createInputElement(CONST.HTML_IDS.PAGE_NUMBER, "number", "2em", 3, "center")); Object.assign(divBeitragInfo.appendChild(document.createElement("BUTTON")), { innerHTML: "›", className: CONST.HTML_CLASSES.NEXT, id: CONST.HTML_IDS.BUTTON_PAGE_NEXT }); divBeitragInfo.appendChild(document.createTextNode(", Beitrag: ")); Object.assign(divBeitragInfo.appendChild(document.createElement("BUTTON")), { innerHTML: "‹",className: CONST.HTML_CLASSES.PREVIOUS, id: CONST.HTML_IDS.BUTTON_BEITRAG_PREV }); divBeitragInfo.appendChild(Util.createInputElement(CONST.HTML_IDS.BEITRAG_NUMBER, "number", "2em", 2, "center")); Object.assign(divBeitragInfo.appendChild(document.createElement("BUTTON")), { innerHTML: "›", className: CONST.HTML_CLASSES.NEXT, id: CONST.HTML_IDS.BUTTON_BEITRAG_NEXT }); divBeitragInfo.appendChild(document.createTextNode(" / ")); divBeitragInfo.appendChild(Util.createInputElement(CONST.HTML_IDS.BEITRAG_COUNT, "number", "2em", 2, "center")).disabled = true; divBeitragInfo.appendChild(document.createTextNode(", IMDb: ")); divBeitragInfo.appendChild(Util.createInputElement(CONST.HTML_IDS.FILM_DATA_IMDB_RATING, "text", "3em", 4, "center")).disabled = true; divBeitragInfo.appendChild(document.createTextNode(", Größe: ")); divBeitragInfo.appendChild(Util.createInputElement(CONST.HTML_IDS.FILM_DATA_GROESSE, "text", "7em", 9, "center")).disabled = true; return true; } else { for (let divBeitragOld of oldContainer.querySelectorAll(`DIV.${CONST.HTML_CLASSES.BEITRAG}`)) { oldContainer.removeChild(divBeitragOld); } return false; } }, parseInt: (str, maxIntPlaces = 1, fallbackValue = null) => { let regex = "\\d"; if (maxIntPlaces > 1) { regex += "{1," + maxIntPlaces + "}"; } return Util.parseIntWithRegex(str, new RegExp(regex), fallbackValue); }, parseFloat: (str, maxIntPlaces = 1, maxDecPlaces = 0, fallbackValue = null) => { let regex = "\\d"; if (maxIntPlaces > 1) { regex += "{1," + maxIntPlaces + "}"; } if (maxDecPlaces > 0) { regex += "\\s?[,\.]?\\s?\\d{0," + maxDecPlaces + "}"; } return Util.parseFloatWithRegex(str, new RegExp(regex), fallbackValue); }, parseIntWithRegex: (str, regex, fallbackValue = null) => { const match = Util.parseStrWithRegex(str, regex); const value = parseInt(match); return Number.isNaN(value) ? fallbackValue : value; }, parseFloatWithRegex: (str, regex, fallbackValue = null) => { const match = Util.parseStrWithRegex(str, regex, "").replace(",","."); const value = parseFloat(match); return Number.isNaN(value) ? fallbackValue : value; }, parseStrWithRegex: (str, regex, fallbackValue = null) => { const match = regex.exec(str || ""); return (match && match.length >= 1) ? match[0] : fallbackValue; }, monthNameToNumber: (monthName) => { return CONST.MONTH_NAMES.indexOf(String(monthName).toUpperCase()); }, createTable(data, cellAttributes = {}, headerAttributes = cellAttributes) { function configRow(cellElement, content, attributes) { cellElement.appendChild(content instanceof Element ? content : document.createTextNode(String(content))); for (let attrName of Object.keys(attributes)) { cellElement[attrName] = attributes[attrName]; } } const table = document.createElement("TABLE"); if (data) { for (let element of data) { const row = table.insertRow(); for (let key of Object.keys(element)) { const td = row.insertCell(); const content = element[key]; const attributes = cellAttributes[key] || {}; configRow(td, content, attributes); } } const thead = table.createTHead(); const row = thead.insertRow(); for (let key of Object.keys(data[0])) { const th = document.createElement("TH"); const attributes = headerAttributes[key] || {}; row.appendChild(th); configRow(th, key, attributes); } } return table; }, }; })(); const Beitrag = (() => { const computeImdbLink = (containerElement, fallbackValue = null) => { for (let a of Array.from(containerElement.getElementsByTagName("A"))) { const linkTextUpper = a.textContent.toUpperCase(); if (linkTextUpper.includes("IMDB")) { return a; } } for (let elStrong of containerElement.querySelectorAll("STRONG, B")) { if (elStrong.textContent.toUpperCase().includes("IMDB")) { const nextEl = elStrong.nextSibling; if (nextEl && nextEl.tagName === "A") { return nextEl; } } } return fallbackValue; }; const computeImdbRating = (containerElement, fallbackValue = null) => { const a = computeImdbLink(containerElement); if (a !== null) { let rating = Util.parseFloat(a.textContent.toUpperCase(), 2, 1); if (rating === null) { const nextEl = a.nextSibling; if (nextEl && nextEl.nodeType === Node.TEXT_NODE) { rating = Util.parseFloat(nextEl.textContent.toUpperCase(), 2, 1); } } if (rating !== null) { return rating; } } return fallbackValue; }; const computeDauer = (containerElement, fallbackValue = null) => { for (let elStrong of containerElement.querySelectorAll("STRONG, B")) { if (elStrong.textContent.toUpperCase().includes("DAUER")) { const nextEl = elStrong.nextSibling; if (nextEl) { const textContentUpper = nextEl.textContent.toUpperCase(); const hours = Util.parseFloatWithRegex(textContentUpper, /(\d{1,2}\s?[,\.]?\s?\d{0,2})\s?(H|STUNDEN|HOURS)/); const minutes = Util.parseIntWithRegex(textContentUpper, /(\d{1,4})\s?(M|MIN|MINUTEN|MINUTES)/); if (hours !== null || minutes !== null) { return (hours === null ? 0 : hours * 60) + (minutes === null ? 0 : minutes); } } } } return fallbackValue; }; const computeMirrors = (containerElement) => { return Array.from(containerElement.querySelectorAll("STRONG + A, B + A")).filter(a => { const strong = a.previousElementSibling; const strongTextContentUpper = strong.textContent.toUpperCase(); return ["DOWNLOAD", "MIRROR"].some(str => strongTextContentUpper.includes(str)); }).filter(a => { const isWerbeLink = !a.hostname || a.hostname.toUpperCase().includes("MOVIE-BLOG"); return !isWerbeLink; }); }; const computeDatum = (containerElement, fallbackValue = null) => { const reDay = "\\d{1,2}"; const reMonth = CONST.MONTH_NAMES.reduce((a, b) => a + "|" + b); const reYear = "\\d{4}"; const reHour = "\\d{1,2}"; const reMin = "\\d{2}"; for (let elDatum of containerElement.getElementsByClassName(CONST.HTML_CLASSES.DATE)) { const match = new RegExp("(" + reDay + ")\\.\\s(" + reMonth + ")\\s(" + reYear + ")\\s(" + reHour + "):(" + reMin + ")", "i").exec(elDatum.textContent); if (match && match.length === 6) { const day = Number.parseInt(match[1]); const month = Util.monthNameToNumber(match[2]); const year = Number.parseInt(match[3]); const hour = Number.parseInt(match[4]); const minute = Number.parseInt(match[5]); return (new Date(year, month, day, hour, minute)); } } return fallbackValue; }; const computeLink = (containerElement, fallbackValue = null) => { const datum = computeDatum(containerElement); if (datum !== null) { for (let h1 of containerElement.getElementsByTagName("H1")) { for (let a of h1.getElementsByTagName("A")) { if (a.title && a.title.includes("Permanent Link to")) { return a.cloneNode(true); } } } } return fallbackValue; }; const computeGroesse = (containerElement, fallbackValue = null) => { for (let elStrong of containerElement.querySelectorAll("STRONG, B")) { if (/GR(OE|Ö)(SS|ß)E/i.test(elStrong.textContent)) { const nextEl = elStrong.nextSibling; if (nextEl) { const textContentUpper = nextEl.textContent.toUpperCase(); const gb = Util.parseFloatWithRegex(textContentUpper, /(\d{1,4}\s?[,\.]?\s?\d{0,3})\s?(GB|GIGABYTE)/); const mb = Util.parseIntWithRegex(textContentUpper, /(\d{1,7}?)\s?(MB|MEGABYTE)/); if (gb !== null || mb !== null) { return (gb === null ? 0 : gb * 1024) + (mb === null ? 0 : mb); } } } } return fallbackValue; }; const computeImgElement = (containerElement, fallbackValue = null) => { const img = containerElement.querySelectorAll("P > IMG"); return img.length > 0 ? img[0] : fallbackValue; }; const defaultCompare = new Comparator(...[ beitrag => [Beitrag.TYPEN.FILM, Beitrag.TYPEN.DOKU, Beitrag.TYPEN.SERIE, Beitrag.TYPEN.WERBUNG].indexOf(beitrag.typ), beitrag => beitrag.groesse > 25 * 1024, new Comparator(beitrag => beitrag.imdbRating).reverse() ]).compareFnc; const TYPEN = Object.freeze({ FILM: "Film", SERIE: "Serie", DOKU: "Doku", WERBUNG: "Werbung" }); // private fields const _divElement = new PrivateProperty(); const _imdbLink = new PrivateProperty(); const _imdbRating = new PrivateProperty(); const _imdbId = new PrivateProperty(); const _dauer = new PrivateProperty(); const _groesse = new PrivateProperty(); const _link = new PrivateProperty(); const _mirrors = new PrivateProperty(); const _imgElement = new PrivateProperty(); const _typ = new PrivateProperty(); const _datum = new PrivateProperty(); const _releasedYear = new PrivateProperty(); class Beitrag { constructor(divElement) { _divElement.set(this, divElement); } get divElement() { return _divElement.get(this); } get imdbLink() { return _imdbLink.getOrCompute(this, () => computeImdbLink(this.divElement, null)); } get imdbRating() { return _imdbRating.getOrCompute(this, () => computeImdbRating(this.divElement, -1)); } get imdbRatingAsText() { return this.imdbRating === -1 ? "" : this.imdbRating.toFixed(1); } get imdbId() { return _imdbId.getOrCompute(this, () => this.imdbLink === null ? "" : Util.parseStrWithRegex(this.imdbLink, /tt\d{7,8}/, "")); } get dauer() { return _dauer.getOrCompute(this, () => computeDauer(this.divElement, 0)); } get dauerAsText() { if (this.dauer === 0) { return ""; } const hours = String(Math.floor(this.dauer / 60)); const minutes = String(this.dauer % 60); return `${hours}:${minutes.length === 1 ? "0" + minutes : minutes}`; } get groesse() { return _groesse.getOrCompute(this, () => computeGroesse(this.divElement, 0)); } /** * Returns value "groesse" of this object converted to a specified unit. * * @param {string} [unit] - Unit ("B", "KB", "MB", "GB", "TB"), see CONST.DATA_SIZE_UNITS * @returns {number} - value converted for specified unit */ getGroesseInUnit(unit) { const defaultDataSize = CONST.DATA_SIZE_UNITS.get("MB"); const targetDataSize = CONST.DATA_SIZE_UNITS.get(unit) || defaultDataSize; return this.groesse * (defaultDataSize.factor / targetDataSize.factor); } /** * Returns a formatted string of attribute "groesse" of this object * * @param {string} [unit] - Unit ("B", "KB", "MB", "GB", "TB"), see CONST.DATA_SIZE_UNITS * @param {number} [decimalPlaces] - Number of fixed digits after the decimal point * @returns {string} - formatted string: "value unit", e.g. "2.61 GB" */ getGroesseAsText(unit, decimalPlaces) { if (this.groesse === 0) { return ""; } let value = this.groesse; const defaultDataSize = CONST.DATA_SIZE_UNITS.get("MB"); let dataSize = CONST.DATA_SIZE_UNITS.get(unit); if (!dataSize) { for (dataSize of Array.from(CONST.DATA_SIZE_UNITS.values()).filter(ds => ds.factor >= defaultDataSize.factor)) { if (dataSize.factor > defaultDataSize.factor) { value = value / 1024; if (typeof decimalPlaces === "undefined") { decimalPlaces = 1; } } if (value < 1024) { break; } } } else { value *= (defaultDataSize.factor / dataSize.factor); } return Number(Math.round(value + 'e1') + 'e-1').toFixed(decimalPlaces || 0) + " " + dataSize.shortText; } get link() { return _link.getOrCompute(this, () => computeLink(this.divElement, null)); } get linkText() { return this.link ? this.link.textContent : ""; } get mirrors() { return _mirrors.getOrCompute(this, () => computeMirrors(this.divElement)); } get imgElement() { return _imgElement.getOrCompute(this, () => computeImgElement(this.divElement)); } get typ() { return _typ.getOrCompute(this, () => { if (this.mirrors.length === 0 || this.linkText.toUpperCase().includes('-HELD')) { return Beitrag.TYPEN.WERBUNG; } if (CONST.REGEX.DOKU.test(this.linkText)) { return Beitrag.TYPEN.DOKU; } if (CONST.REGEX.SERIE.test(this.linkText)) { return Beitrag.TYPEN.SERIE; } return ""; }); } get datum() { return _datum.getOrCompute(this, () => computeDatum(this.divElement, null)); } get releasedYear() { return _releasedYear.getOrCompute(this, () => { const match = /\.(\d{4})\./.exec(this.linkText); if (!match || match.length <= 1) { return null; } return parseInt(match[1]) || null; }); } static get TYPEN() { return TYPEN; } static get defaultCompare() { return defaultCompare; } } return Beitrag; })(); const View = (() => { const addBeitraegeAsync = (beitraege, fromNumOfPage, toNumOfPage, onSuccess, onError) => { const urlPath = document.location.href.replace('page/', '').replace('#',''); Util.getPageAsync(fromNumOfPage == 0 ? urlPath : `${urlPath}page/${fromNumOfPage + 1}/`, resp => { // add Beitraege beitraege.push(...Array.from(resp.html.querySelectorAll(`DIV.${CONST.HTML_CLASSES.BEITRAG}`)).map(div => new Beitrag(div))); if (fromNumOfPage < toNumOfPage) { // load further Beitraege addBeitraegeAsync(beitraege, fromNumOfPage + 1, toNumOfPage, onSuccess, onError); return; } // Beitreage of last page loaded successfully onSuccess(); }, onError); }; return class { constructor(numOfPage = 0, numOfBeitrag = 0, pageSteps = CONFIG.PAGES_LOAD_COUNT) { this.pageSteps = pageSteps; this.numOfPage = numOfPage; this.numOfBeitrag = numOfBeitrag; this.beitraege = []; this.gotoPage(); } get beitragCount() { return this.beitraege.length; } setBrowsingEnabled(isBrowsingEnabled) { this.isPageLoading = !isBrowsingEnabled; [ document.getElementById(CONST.HTML_IDS.PAGE_NUMBER), document.getElementById(CONST.HTML_IDS.BEITRAG_NUMBER), document.getElementById(CONST.HTML_IDS.BUTTON_PAGE_PREV), document.getElementById(CONST.HTML_IDS.BUTTON_PAGE_NEXT), document.getElementById(CONST.HTML_IDS.BUTTON_BEITRAG_PREV), document.getElementById(CONST.HTML_IDS.BUTTON_BEITRAG_NEXT), ].forEach(inputElement => { if (inputElement) { inputElement.disabled = !isBrowsingEnabled; } }); } gotoPage(numOfPage = this.numOfPage, numOfBeitrag = -1) { // disable page browsing this.setBrowsingEnabled(false); const onSuccess = () => { if (0 <= numOfBeitrag && numOfBeitrag < this.beitragCount) { this.numOfBeitrag = numOfBeitrag; } else { this.numOfBeitrag = (numOfPage < this.numOfPage) ? this.beitragCount - 1 : 0; } this.numOfPage = numOfPage; // distinct for links const beitraegeByLinks = new Map(this.beitraege.map(b => [ b.link.href, b ])); this.beitraege = Array.from(beitraegeByLinks.values()); // sort Beitraege by IMDb rating this.beitraege.sort((a, b) => Beitrag.defaultCompare(a, b)); this.showBeitrag(); this.showTable(); this.highlightTableRow(); this.setBrowsingEnabled(true); }; const onError = resp => this.setBrowsingEnabled(true); // merge Beitraege of 'pageSteps' pages this.beitraege = []; let numOfPageFirst = numOfPage * this.pageSteps; let numOfPageLast = numOfPageFirst + this.pageSteps - 1; addBeitraegeAsync(this.beitraege, numOfPageFirst, numOfPageLast, onSuccess, onError); } indexOfBeitrag(beitrag) { return this.beitraege.indexOf(beitrag); } gotoBeitrag(numOfBeitrag = this.numOfBeitrag) { this.highlightTableRow(this.numOfBeitrag, false); this.numOfBeitrag = numOfBeitrag; this.showBeitrag(); this.highlightTableRow(this.numOfBeitrag, true); } showBeitrag() { // get a clean show container const isNewShowContainer = Util.createShowContainer(); // clone Beitrag into show container const beitrag = this.beitraege[this.numOfBeitrag]; const divBeitrag = beitrag ? beitrag.divElement : null; if (divBeitrag) { const beitragContent = document.getElementById(CONST.HTML_IDS.SHOW_CONTAINER); const beitragInfo = document.getElementById(CONST.HTML_IDS.BEITRAG_INFO); const inputPageNum = document.getElementById(CONST.HTML_IDS.PAGE_NUMBER); inputPageNum.value = this.numOfPage + 1; inputPageNum.min = 1; inputPageNum.max = 9999; const inputBeitragNum = document.getElementById(CONST.HTML_IDS.BEITRAG_NUMBER); inputBeitragNum.value = this.numOfBeitrag + 1; inputBeitragNum.min = 1; inputBeitragNum.max = this.beitragCount; const btnPagePrev = document.getElementById(CONST.HTML_IDS.BUTTON_PAGE_PREV); const btnPageNext = document.getElementById(CONST.HTML_IDS.BUTTON_PAGE_NEXT); const btnBeitragPrev = document.getElementById(CONST.HTML_IDS.BUTTON_BEITRAG_PREV); const btnBeitragNext = document.getElementById(CONST.HTML_IDS.BUTTON_BEITRAG_NEXT); const gotoPage = (pageNum) => { if (!this.isPageLoading) { this.gotoPage(Math.max(0, pageNum), 0); } }; const gotoBeitrag = (beitragNum) => { if (!this.isPageLoading) { beitragNum = Math.max(beitragNum, 0); beitragNum = Math.min(beitragNum, this.beitragCount - 1); this.gotoBeitrag(beitragNum); } }; if (isNewShowContainer) { inputPageNum.addEventListener("change", () => { gotoPage(parseInt(inputPageNum.value) - 1); }); btnPagePrev.addEventListener("click", () => { gotoPage(parseInt(inputPageNum.value) - 1 - 1); }); btnPageNext.addEventListener("click", () => { gotoPage(parseInt(inputPageNum.value) - 1 + 1); }); inputBeitragNum.addEventListener("change", () => { gotoBeitrag(parseInt(inputBeitragNum.value) - 1); }); btnBeitragPrev.addEventListener("click", () => { gotoBeitrag(parseInt(inputBeitragNum.value) - 1 - 1); }); btnBeitragNext.addEventListener("click", () => { gotoBeitrag(parseInt(inputBeitragNum.value) - 1 + 1); }); } document.getElementById(CONST.HTML_IDS.BEITRAG_COUNT).value = this.beitragCount; document.getElementById(CONST.HTML_IDS.FILM_DATA_IMDB_RATING).value = beitrag.imdbRating; document.getElementById(CONST.HTML_IDS.FILM_DATA_GROESSE).value = beitrag.getGroesseAsText(); // remove former image from sidebar const curImage = document.getElementById(CONST.HTML_IDS.CUR_BEITRAG_IMG); if (curImage !== null) { curImage.parentElement.removeChild(curImage); } // insert image to sidebar const divSidebar = document.getElementById(CONST.HTML_IDS.SIDEBAR); const imgElement = beitrag.imgElement; if (divSidebar && imgElement) { imgElement.id = CONST.HTML_IDS.CUR_BEITRAG_IMG; divSidebar.insertBefore(imgElement, divSidebar.firstChild); window.onscroll = () => { imgElement.style.display = ((window.pageYOffset || document.documentElement.scrollTop) < CONFIG.SIDEBAR_POSITION_TOP_PX - 175) ? 'block' : 'none'; }; } return beitragContent.appendChild(divBeitrag) ? true : false; } return false; } highlightTableRow(numOfBeitrag = this.numOfBeitrag, isHighlight = true) { const tableRow = document.getElementById(`${CONST.HTML_IDS.TABLE_TR_NUM}_${numOfBeitrag + 1}`); if (tableRow) { tableRow.classList[isHighlight ? "add" : "remove"](CONST.HTML_CLASSES.TABLE_ROW_HIGHLIGHTED); } } showTable() { const sortByColumn = ({containerElement, columnClass, sortFunction, defaultSortOrderAsc = true} = {}) => { const curBeitrag = this.beitraege[this.numOfBeitrag]; if (this.sortedColumnClass !== columnClass) { const thSortedPrevious = this.sortedColumnClass ? null : containerElement.querySelector(`th.${this.sortedColumnClass}`); if (thSortedPrevious) { thSortedPrevious.classList.remove(CONST.HTML_CLASSES.TABLE_COLUMN_SORTED_ASC, CONST.HTML_CLASSES.TABLE_COLUMN_SORTED_DESC); } this.beitraege.sort(sortFunction); this.sortedColumnClass = columnClass; this.sortOrder = defaultSortOrderAsc; } else { this.beitraege.reverse(); this.sortOrder = !this.sortOrder; } this.showTable(); this.gotoBeitrag(this.indexOfBeitrag(curBeitrag)); const thSort = containerElement.querySelector(`th.${this.sortedColumnClass}`); if (thSort) { thSort.classList.add(this.sortOrder ? CONST.HTML_CLASSES.TABLE_COLUMN_SORTED_ASC : CONST.HTML_CLASSES.TABLE_COLUMN_SORTED_DESC); } }; let tableDiv = document.getElementById(CONST.HTML_IDS.LISTE_BEITRAEGE); if (!tableDiv) { const beitragInfoDiv = document.getElementById(CONST.HTML_IDS.BEITRAG_INFO); const beitragContent = document.getElementById(CONST.HTML_IDS.SHOW_CONTAINER); tableDiv = beitragInfoDiv.parentNode.insertBefore(document.createElement("DIV"), beitragInfoDiv); tableDiv.id = CONST.HTML_IDS.LISTE_BEITRAEGE; } else { while (tableDiv.firstChild) { tableDiv.removeChild(tableDiv.firstChild); } } const tableData = this.beitraege.map((beitrag, i) => ({ [CONST.TABLE_BEITRAEGE_HEADER_NAMES.NUM]: i + 1, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.RATING]: (() => { const textContent = beitrag.imdbRatingAsText; if (beitrag.imdbId) { const link = document.createElement("A"); link.target = "_blank"; link.href = `https://www.imdb.com/title/${beitrag.imdbId}/`; link.textContent = textContent; return link; } return textContent; })(), [CONST.TABLE_BEITRAEGE_HEADER_NAMES.JAHR]: beitrag.releasedYear || "", [CONST.TABLE_BEITRAEGE_HEADER_NAMES.LINK]: (() => { if (!beitrag.link) { return ""; } const link = beitrag.link.cloneNode(true); // Textformatierungen entfernen while(link.firstChild) { link.removeChild(link.firstChild); } link.textContent = beitrag.linkText; return link; })(), [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DURATION]: beitrag.dauerAsText, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.SIZE]: beitrag.getGroesseAsText(CONFIG.TABLE_BEITRAEGE_SIZE_DEFAULT_UNIT, 1), [CONST.TABLE_BEITRAEGE_HEADER_NAMES.TYPE]: beitrag.typ, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DATE]: (() => { const date = beitrag.datum.toLocaleDateString(undefined, { dateStyle: "short" }); const time = beitrag.datum.toLocaleTimeString(undefined, { timeStyle: "short" }); return [ date, time ].join(" "); })(), })); const thisView = this; const tdAttributes = { [CONST.TABLE_BEITRAEGE_HEADER_NAMES.NUM]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_NUM, onmouseover: function() { const lineNumber = parseInt(this.innerText); if (Number.isInteger(lineNumber)) { thisView.gotoBeitrag(lineNumber - 1); } }}, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.RATING]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_RATING }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.JAHR]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_JAHR }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.LINK]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DURATION]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DURATION }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.SIZE]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_SIZE }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.TYPE]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_TYPE }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DATE]: { className: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DATE }, }; const thAttributes = { [CONST.TABLE_BEITRAEGE_HEADER_NAMES.NUM]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_NUM, sortFunction: Beitrag.defaultCompare, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_NUM, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.RATING]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_RATING, sortFunction: new Comparator(beitrag => beitrag.imdbRating).reverse().compareFnc, defaultSortOrderAsc: false }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_RATING, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.JAHR]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_JAHR, sortFunction: new Comparator(beitrag => beitrag.releasedYear).compareFnc, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_JAHR, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.LINK]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK, sortFunction: new Comparator(beitrag => beitrag.linkText).compareFnc, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_LINK, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DURATION]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DURATION, sortFunction: new Comparator(beitrag => beitrag.dauer).compareFnc, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DURATION, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.SIZE]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_SIZE, sortFunction: new Comparator(beitrag => beitrag.groesse).reverse().compareFnc, defaultSortOrderAsc: false }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_SIZE, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.TYPE]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_TYPE, sortFunction: new Comparator(beitrag => beitrag.typ).compareFnc, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_TYPE, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, [CONST.TABLE_BEITRAEGE_HEADER_NAMES.DATE]: { onclick: () => sortByColumn({ containerElement: tableDiv, columnClass: CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DATE, sortFunction: new Comparator(beitrag => beitrag.datum.valueOf()).compareFnc, defaultSortOrderAsc: true }), className: [ CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_DATE, CONST.HTML_CLASSES.TABLE_COLUMN_SORTABLE ].join(" ") }, }; const table = Util.createTable(tableData, tdAttributes, thAttributes); for (let tr of table.getElementsByTagName("TR")) { const tdNum = tr.getElementsByClassName(CONST.HTML_CLASSES.TABLE_BEITRAEGE_COLUMN_NUM); const num = tdNum.length > 0 ? Util.parseInt(tdNum[0].textContent, 3) : null; if (num !== null) { tr.id = `${CONST.HTML_IDS.TABLE_TR_NUM}_${num}`; } } tableDiv.appendChild(table); } }; })(); const view = new View(); GM_addStyle(CONST.CSS); const browseBeitrag = function(distance) { if (view.isPageLoading) { return; } const numOfNextBeitrag = view.numOfBeitrag + distance; if (numOfNextBeitrag < 0) { if (view.numOfPage > 0) { view.gotoPage(view.numOfPage - 1); } } else if (numOfNextBeitrag >= view.beitragCount) { view.gotoPage(view.numOfPage + 1); } else { view.gotoBeitrag(numOfNextBeitrag); } }; document.onkeydown = function (e) { switch (e.keyCode) { case 37: // key left browseBeitrag(-1); return false; case 39: // key right browseBeitrag(1); return false; case 33: // page up view.gotoPage(Math.max(0, view.numOfPage - 1), 0); return false; case 34: // page down view.gotoPage(view.numOfPage + 1, 0); return false; } }; })();