Nyaa.si Batch downloader

Batch download torrents from nyaa.si

当前为 2018-10-14 提交的版本,查看 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Nyaa.si Batch downloader
// @namespace   Autodownload
// @author      Victorique
// @description Batch download torrents from nyaa.si
// @include     *://nyaa.si/user/*?q=*
// @include     *://nyaa.si/user/*?f=*&c=*&q=*
// @version     7.1
// @icon        https://i.imgur.com/nx5ejHb.png
// @license     MIT
// @run-at      document-idle
// @grant       none
// @require     https://greasyfork.org/scripts/19117-jsutils/code/JsUtils.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js
// ==/UserScript==
"use strict";

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Episode = function () {
    function Episode(res, downloadLink, seeds, leechers, uid, resides, title, size) {
        _classCallCheck(this, Episode);

        this._res = res;
        this._downloadLink = downloadLink;
        this._seeds = seeds;
        this._leechers = leechers;
        this._uid = uid;
        this._resides = resides;
        this._title = title;
        this._size = size;
    }

    _createClass(Episode, [{
        key: 'equals',
        value: function equals(ep) {
            return this.uid === ep.uid;
        }
    }, {
        key: 'res',
        get: function get() {
            return this._res;
        }
    }, {
        key: 'downloadLink',
        get: function get() {
            return this._downloadLink;
        }
    }, {
        key: 'seeds',
        get: function get() {
            return this._seeds;
        }
    }, {
        key: 'leechers',
        get: function get() {
            return this._leechers;
        }
    }, {
        key: 'uid',
        get: function get() {
            return this._uid;
        }
    }, {
        key: 'resides',
        get: function get() {
            return this._resides;
        }
    }, {
        key: 'title',
        get: function get() {
            return this._title;
        }
    }, {
        key: 'size',
        get: function get() {
            return this._size;
        }
    }]);

    return Episode;
}();

var _AbstractEps = function () {
    function _AbstractEps() {
        _classCallCheck(this, _AbstractEps);
    }

    _createClass(_AbstractEps, null, [{
        key: 'abstractGetEps',
        value: function abstractGetEps(skipSeedLimit) {
            var minSeeds = Number.parseInt(Localstore.getMinSeedsFromStore());
            if (minSeeds > -1 && skipSeedLimit === false) {
                var arrayOfEps = [];
                for (var i = 0, len = _AbstractEps.eps.length; i < len; i++) {
                    var currentEp = _AbstractEps.eps[i];
                    if (currentEp.seeds < minSeeds) {
                        continue;
                    }
                    arrayOfEps.push(currentEp);
                }
                return arrayOfEps;
            } else {
                return _AbstractEps.eps;
            }
        }
    }, {
        key: 'addEp',
        value: function addEp(ep) {
            if (Anime.isValidRes(ep.res) === false) {
                throw new TypeError('The Episode supplied does not have a valid resolution');
            }
            for (var i = 0, len = _AbstractEps.eps.length; i < len; i++) {
                var epi = _AbstractEps.eps[i];
                if (epi.equals(ep)) {
                    console.warn('The episode supplied already exsists, this episode has been ignored');
                    return;
                }
            }
            _AbstractEps.eps.push(ep);
        }
    }, {
        key: 'removeEpisodeFromAnime',
        value: function removeEpisodeFromAnime(obj) {
            var arr = _AbstractEps.eps;
            var i = arr.length;
            while (i--) {
                if (arr[i] === obj) {
                    arr.splice(i, 1);
                }
            }
        }
    }]);

    return _AbstractEps;
}();

_AbstractEps.eps = []; // this is the main array that holds all the episodes per anime

var Anime = function (_AbstractEps2) {
    _inherits(Anime, _AbstractEps2);

    function Anime() {
        _classCallCheck(this, Anime);

        return _possibleConstructorReturn(this, (Anime.__proto__ || Object.getPrototypeOf(Anime)).call(this));
    }

    _createClass(Anime, null, [{
        key: 'addSupportedRes',
        value: function addSupportedRes(res) {
            Anime._supportedRes.push(res);
        }
    }, {
        key: 'addAvailableResolutions',
        value: function addAvailableResolutions(res, fullRes) {
            if (Anime._resExists(res)) {
                return;
            }
            Anime._availableRes.push({ 'res': res, 'fullRes': fullRes });
        }
    }, {
        key: 'removeAvailableResolutions',
        value: function removeAvailableResolutions(resToRemove) {
            for (var i = 0; i < Anime._availableRes.length; i++) {
                var currentRes = Anime._availableRes[i];
                if (currentRes.res === resToRemove || currentRes.fullRes === resToRemove) {
                    Anime._availableRes.splice(i, 1);
                }
            }
        }
    }, {
        key: '_resExists',
        value: function _resExists(_res) {
            for (var i = 0; i < Anime._availableRes.length; i++) {
                var currentRes = Anime._availableRes[i];
                if (currentRes.res === _res || currentRes.fullRes === _res) {
                    return true;
                }
            }
            return false;
        }
    }, {
        key: 'getTdFromTable',
        value: function getTdFromTable(table, index) {
            return table.find('td:nth-child(' + index + ')');
        }
    }, {
        key: 'avgSeedsForRes',
        value: function avgSeedsForRes(res, skipSeedLimit) {
            var seedCount = 0;
            var epCount = Anime.getAmountOfEpsFromRes(res, skipSeedLimit);
            if (epCount === 0) {
                return 0;
            }
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, skipSeedLimit);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                if (currentEp.res === res) {
                    seedCount += currentEp.res;
                }
            }
            return Math.round(seedCount / epCount);
        }
    }, {
        key: 'avgPeersForRes',
        value: function avgPeersForRes(res, skipSeedLimit) {
            var leechCount = 0;
            var epCount = Anime.getAmountOfEpsFromRes(res, skipSeedLimit);
            if (epCount === 0) {
                return 0;
            }
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, skipSeedLimit);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                if (currentEp.res === res) {
                    leechCount += currentEp.leechers;
                }
            }
            return Math.round(leechCount / epCount);
        }
    }, {
        key: 'getTotalSizeForRes',
        value: function getTotalSizeForRes(res, skipSeedLimit) {
            var decimals = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3;

            var eps = Anime.getEpsForRes(res, skipSeedLimit);
            return Utils.getHumanReadableSize(eps, decimals);
        }
    }, {
        key: 'getAmountOfEpsFromRes',
        value: function getAmountOfEpsFromRes(res, skipSeedLimit) {
            return Anime.getEpsForRes(res, skipSeedLimit).length;
        }
    }, {
        key: 'getEpsForRes',
        value: function getEpsForRes(res, skipSeedLimit) {
            var arrayOfEps = [];
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, skipSeedLimit);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                if (currentEp.res === res) {
                    arrayOfEps.push(currentEp);
                }
            }
            return arrayOfEps;
        }
    }, {
        key: 'isValidRes',
        value: function isValidRes(res) {
            return Anime._resExists(res);
        }
    }, {
        key: 'addAllEps',
        value: function addAllEps(eps) {
            for (var i = 0; i < eps.length; i++) {
                _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'addEp', this).call(this, eps[i]);
            }
        }
    }, {
        key: 'getUidFromJqueryObject',
        value: function getUidFromJqueryObject(obj) {
            if (obj.is('tr')) {
                return function () {
                    var currectTd = Anime.getNameTr(obj);
                    var tableRows = currectTd.find('a:not(a.comments)');
                    if (tableRows.length > 1) {
                        throw 'Object must be unique';
                    }
                    return Anime._getUidFromAnchor(tableRows.get(0));
                }();
            }
            return null;
        }
    }, {
        key: 'getNameTr',
        value: function getNameTr(obj) {
            return obj.find('td:nth-child(2)');
        }
    }, {
        key: 'getEpisodeFromAnchor',
        value: function getEpisodeFromAnchor(anchor) {
            var link = function () {
                if (Utils.isjQueryObject(anchor)) {
                    return anchor.get(0);
                }
                return anchor;
            }();
            var uid = Anime._getUidFromAnchor(link);
            return Anime.getEpisodeFromUid(uid, true);
        }
    }, {
        key: 'getEpisodeFromUid',
        value: function getEpisodeFromUid(uid, skipSeedLimit) {
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, skipSeedLimit);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                if (currentEp.uid === uid) {
                    return currentEp;
                }
            }
            return null;
        }
    }, {
        key: '_getUidFromAnchor',
        value: function _getUidFromAnchor(anchor) {
            if (typeof anchor === 'string') {
                if (anchor.indexOf(".torrent") > -1) {
                    return anchor.replace(".torrent", "").split("/").pop();
                }
                return anchor.split("/").pop();
            }
            return anchor.href.split("/").pop();
        }
    }, {
        key: 'getEpisodesFromResidence',
        value: function getEpisodesFromResidence(resides, exclude, skipSeedLimit) {
            var arrayOfEps = [];
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, skipSeedLimit);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                if (exclude === true) {
                    if (currentEp.resides !== resides) {
                        arrayOfEps.push(currentEp);
                    }
                } else {
                    if (currentEp.resides === resides) {
                        arrayOfEps.push(currentEp);
                    }
                }
            }
            return arrayOfEps;
        }
    }, {
        key: 'getPageUrls',
        value: function getPageUrls() {
            function range(start, end) {
                // @ts-ignore
                return Array(end - start + 1).fill().map(function (_, idx) {
                    return start + idx;
                });
            }
            var pages = $(".center > ul.pagination a");
            if (pages.length === 0) {
                return [];
            }
            var firstPage = Utils.getElementFromJqueryArray(pages, 1);
            var lastPage = Utils.getElementFromJqueryArray(pages, pages.length - 2);
            var firstPageNumber = Number.parseInt(firstPage.text());
            var lastPageNumber = Number.parseInt(lastPage.text());
            var rangeBetween = range(firstPageNumber, lastPageNumber);
            var baseUrl = window.location.href;
            var urls = [];
            var currentPage = QueryString.p === undefined ? 1 : QueryString.p;
            for (var i = 0; i < rangeBetween.length; i++) {
                var num = String(rangeBetween[i]);
                if (num == currentPage) {
                    continue;
                }
                var newUrl = Utils.addParameter(baseUrl, "p", num.toString());
                urls.push(newUrl);
            }
            return urls;
        }
    }, {
        key: 'removeEpisodesFromUid',
        value: function removeEpisodesFromUid(uid) {
            var episode = Anime.getEpisodeFromUid(uid, true);
            _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'removeEpisodeFromAnime', this).call(this, episode);
        }
    }, {
        key: 'removeEpisodesFromResidence',
        value: function removeEpisodesFromResidence(resides, exclude) {
            var eps = Anime.getEpisodesFromResidence(resides, exclude, true);
            for (var i = 0, len = eps.length; i < len; i++) {
                var currentEp = eps[i];
                this.removeEpisodeFromAnime(currentEp);
            }
        }
    }, {
        key: 'getAmountOfEps',
        value: function getAmountOfEps() {
            return _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, true).length;
        }
    }, {
        key: 'getEpisodeFromUrl',
        value: function getEpisodeFromUrl(url) {
            var eps = _get(Anime.__proto__ || Object.getPrototypeOf(Anime), 'abstractGetEps', this).call(this, true);
            for (var i = 0, len = eps.length; i < len; i++) {
                var ep = eps[i];
                if (ep.downloadLink === url) {
                    return ep;
                }
            }
            return null;
        }
    }, {
        key: 'currentAnime',
        get: function get() {
            return Anime._currentAnime;
        },
        set: function set(value) {
            Anime._currentAnime = value;
        }
    }, {
        key: 'currentSubber',
        get: function get() {
            return Anime._currentSubber;
        },
        set: function set(value) {
            Anime._currentSubber = value;
        }
    }, {
        key: 'supportedRes',
        get: function get() {
            return Anime._supportedRes;
        }
    }, {
        key: 'availableRes',
        get: function get() {
            return Anime._availableRes;
        }
    }]);

    return Anime;
}(_AbstractEps);

Anime._availableRes = [];
Anime._supportedRes = [{ "res": 1080, "fullRes": "1920x1080" }, {
    "res": 720,
    "fullRes": "1280x720"
}, { "res": 480, "fullRes": "640x480" }, { "res": 360, "fullRes": "640x360" }];

var Utils = function () {
    function Utils() {
        _classCallCheck(this, Utils);
    }

    _createClass(Utils, null, [{
        key: 'downloadViaJavaScript',
        value: function downloadViaJavaScript(url, data, callBack) {
            var _type = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "POST";

            var fileName = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;

            var xhr = new XMLHttpRequest();
            xhr.open(_type, url, true);
            xhr.responseType = "blob";
            xhr.withCredentials = true;
            if (_type == "POST") {
                xhr.setRequestHeader("Content-type", "application/json");
            }
            var hasError = false;
            var mediaType = null;
            xhr.onreadystatechange = function () {
                var error = null;
                if (xhr.readyState === XMLHttpRequest.DONE) {
                    if (hasError) {
                        if (xhr.response != null && xhr.response.length > 0) {
                            error = xhr.response;
                        } else {
                            error = "internal server error";
                        }
                    }
                    var contentDispositionHeader = xhr.getResponseHeader("Content-Disposition");
                    if (fileName == null && contentDispositionHeader != null && contentDispositionHeader.indexOf("filename") > -1) {
                        fileName = contentDispositionHeader.split("filename").pop();
                        fileName = fileName.replace("=", "");
                        fileName = fileName.trim();
                        fileName = fileName.replace(/"/g, "");
                    }
                    var mediaTypeHeader = xhr.getResponseHeader("Content-Type");
                    if (mediaTypeHeader != null) {
                        mediaType = mediaTypeHeader;
                    } else {
                        mediaType = "application/octet-stream";
                    }
                    var blob = xhr.response;
                    /*let returnFunction = function save() {
                        if (hasError) {
                            let error:string = "Unable to download file: '" +fileName+ "' Please download this file manually";
                            alert(error);
                            return;
                        }
                    }.bind(this, );*/
                    var saveAsFunc = saveAs.bind(this, blob, fileName, true);
                    callBack.call(this, blob, fileName, hasError, error, saveAsFunc);
                } else if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
                    if (xhr.status !== 200) {
                        xhr.responseType = "text";
                        hasError = true;
                    }
                }
            };
            if (_type === "POST") {
                xhr.send(JSON.stringify(data));
            } else {
                xhr.send();
            }
        }
    }, {
        key: 'sortSelect',
        value: function sortSelect(selElem) {
            var tmpAry = [];
            for (var i = 0, length = selElem.options.length; i < length; i++) {
                tmpAry[i] = [];
                tmpAry[i][0] = selElem.options[i].text;
                tmpAry[i][1] = selElem.options[i].dataset.url;
            }
            tmpAry.sort(function (a, b) {
                // @ts-ignore
                return a[0].toUpperCase().localeCompare(b[0].toUpperCase());
            });
            selElem.innerHTML = "";
            for (var _i = 0, len = tmpAry.length; _i < len; _i++) {
                var op = new Option(tmpAry[_i][0]);
                op.dataset.url = tmpAry[_i][1];
                selElem.options[_i] = op;
            }
        }
    }, {
        key: 'injectCss',
        value: function injectCss(css) {
            function _isUrl(url) {
                var matcher = new RegExp(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/);
                return matcher.test(url);
            }
            if (_isUrl(css)) {
                $("<link>").prop({
                    "type": "text/css",
                    "rel": "stylesheet"
                }).attr("href", css).appendTo("head");
            } else {
                $("<style>").prop("type", "text/css").html(css).appendTo("head");
            }
        }
    }, {
        key: 'addParameter',
        value: function addParameter(url, parameterName, parameterValue) {
            var atStart = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

            var replaceDuplicates = true;
            var urlhash = void 0;
            var cl = void 0;
            if (url.indexOf('#') > 0) {
                cl = url.indexOf('#');
                urlhash = url.substring(url.indexOf('#'), url.length);
            } else {
                urlhash = '';
                cl = url.length;
            }
            var sourceUrl = url.substring(0, cl);
            var urlParts = sourceUrl.split("?");
            var newQueryString = "";
            if (urlParts.length > 1) {
                var parameters = urlParts[1].split("&");
                for (var i = 0; i < parameters.length; i++) {
                    var parameterParts = parameters[i].split("=");
                    if (!(replaceDuplicates && parameterParts[0] == parameterName)) {
                        if (newQueryString == "") {
                            newQueryString = "?";
                        } else {
                            newQueryString += "&";
                        }
                        newQueryString += parameterParts[0] + "=" + (parameterParts[1] ? parameterParts[1] : '');
                    }
                }
            }
            if (newQueryString == "") {
                newQueryString = "?";
            }
            if (atStart) {
                newQueryString = '?' + parameterName + "=" + parameterValue + (newQueryString.length > 1 ? '&' + newQueryString.substring(1) : '');
            } else {
                if (newQueryString !== "" && newQueryString != '?') newQueryString += "&";
                newQueryString += parameterName + "=" + (parameterValue ? parameterValue : '');
            }
            return urlParts[0] + newQueryString + urlhash;
        }
    }, {
        key: 'getElementFromJqueryArray',
        value: function getElementFromJqueryArray(elm, index) {
            return elm.filter(function (i) {
                return i === index;
            });
        }
    }, {
        key: 'getTable',
        value: function getTable() {
            return $('table');
        }
    }, {
        key: 'getQueryFromUrl',
        value: function getQueryFromUrl(url) {
            return url.split("&").reduce(function (prev, curr, i, arr) {
                var p = curr.split("=");
                prev[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
                return prev;
            }, {});
        }
    }, {
        key: 'isjQueryObject',
        value: function isjQueryObject(obj) {
            return obj instanceof jQuery || 'jquery' in Object(obj);
        }
    }, {
        key: 'disableButton',
        value: function disableButton(button) {
            button.prop('disabled', true);
        }
    }, {
        key: 'enableButton',
        value: function enableButton(button) {
            button.prop('disabled', false);
        }
        // @ts-ignore

    }, {
        key: 'doDownloads',
        value: function doDownloads(event) {
            $('#crossPage').prop('disabled', true);
            var type = $(event.target).data('type');
            var html = UI.builDownloadAlert(type);
            var urlsToDownload = [];
            $('#alertUser').html(html).slideDown("slow").show();
            if (type === 'downloadSelected') {
                $.each($('.checkboxes:checked').prev('a'), function () {
                    var ep = Anime.getEpisodeFromAnchor(this);
                    urlsToDownload.push(ep.downloadLink);
                });
            } else if (type === 'downloadSelects') {
                $.each($('#animeSelection option:selected'), function () {
                    var url = this.dataset.url;
                    urlsToDownload.push(url);
                });
            } else {
                var eps = Anime.getEpsForRes(parseInt($('#downloadRes').val()), false);
                for (var i = 0, len = eps.length; i < len; i++) {
                    urlsToDownload.push(eps[i].downloadLink);
                }
            }
            bindAlertControls();
            function bindAlertControls() {
                $('#alertButtonCancel').on('click', function () {
                    $('#alertUser').slideUp('slow');
                    $('#crossPage').prop('disabled', false);
                });
                $('#alertButton').on('click', function () {
                    doIt(urlsToDownload, false);
                });
                $("#downloadZip").on("click", function () {
                    doIt(urlsToDownload, true);
                });
            }
            function doIt(urls, asZip) {
                var ajaxPromiseMap = new Map();
                var arrayd = [];
                for (var _i2 = 0; _i2 < urls.length; _i2++) {
                    var d = $.Deferred();
                    ajaxPromiseMap.set(urls[_i2], d);
                    arrayd.push(d);
                }
                $.when.apply($, arrayd).done(function () {
                    if (!asZip) {
                        return;
                    }
                    // @ts-ignore
                    var zip = new JSZip();
                    var errors = [];
                    for (var _i3 = 0; _i3 < arguments.length; _i3++) {
                        var arg = arguments[_i3];
                        var blob = arg[0];
                        var fileName = arg[1];
                        var error = arg[2];
                        if (error !== null) {
                            errors.push(fileName);
                        }
                        if (errors.length > 0) {
                            var errorMessage = "Unable to download the following files: \n" + errors.join("\n") + "\n Please download these files manually";
                            alert(errorMessage);
                        }
                        zip.file(fileName, blob);
                    }
                    $("#progressStatus").text("Generating zip file...");
                    zip.generateAsync({ type: "blob" }).then(function (blob) {
                        saveAs(blob, Anime.currentAnime + ".zip");
                        $("#loadingContainer").hide();
                        $("#progressStatus").text(null);
                        $("#progressBarForZip").width(0);
                        $('#alertUser').slideUp('slow');
                    }, function (err) {
                        alert(err);
                    });
                });
                var timerOffset = 0;
                if (asZip) {
                    $("#loadingContainer").show();
                }
                var count = 0;

                var _loop = function _loop(currentUrl, deferr) {
                    var ep = Anime.getEpisodeFromUrl(currentUrl);
                    var fileName = ep.title.replace(/ /g, "_") + ".torrent";
                    setTimeout(function () {
                        count++;
                        Utils.downloadViaJavaScript(currentUrl, undefined, function (blob, fileName, hasError, error, saveFunc) {
                            if (!asZip) {
                                saveFunc();
                            } else {
                                var percent = Math.floor(100 * count / ajaxPromiseMap.size);
                                var doneAsString = percent + "%";
                                $("#progressStatus").text("Downloading torrents: " + doneAsString);
                                $("#progressBarForZip").width(doneAsString);
                            }
                            if (hasError) {
                                deferr.resolve(blob, fileName, error);
                            } else {
                                deferr.resolve(blob, fileName, null);
                            }
                        }, "GET", fileName);
                    }, timerOffset);
                    timerOffset += 450;
                };

                var _iteratorNormalCompletion = true;
                var _didIteratorError = false;
                var _iteratorError = undefined;

                try {
                    for (var _iterator = ajaxPromiseMap[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                        var _ref = _step.value;

                        var _ref2 = _slicedToArray(_ref, 2);

                        var currentUrl = _ref2[0];
                        var deferr = _ref2[1];

                        _loop(currentUrl, deferr);
                    }
                } catch (err) {
                    _didIteratorError = true;
                    _iteratorError = err;
                } finally {
                    try {
                        if (!_iteratorNormalCompletion && _iterator.return) {
                            _iterator.return();
                        }
                    } finally {
                        if (_didIteratorError) {
                            throw _iteratorError;
                        }
                    }
                }

                if (!asZip) {
                    $('#alertUser').slideUp('slow');
                }
                $('#crossPage').prop('disabled', false);
            }
        }
    }, {
        key: 'checkBoxValid',
        value: function checkBoxValid(checkbox) {
            return checkbox.is(':checked');
        }
    }, {
        key: '_minSeedsSet',
        value: function _minSeedsSet() {
            var seeds = Localstore.getMinSeedsFromStore();
            if (seeds !== null && seeds.length > 0) {
                return Number.parseInt(seeds) !== 1;
            }
            return false;
        }
    }, {
        key: 'getCurrentPageOffset',
        value: function getCurrentPageOffset() {
            return parseInt(typeof QueryString.p === 'undefined' ? 1 : QueryString.p);
        }
    }, {
        key: 'arrayCopy',
        value: function arrayCopy(array) {
            var deep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

            return $.extend(deep, [], array);
        }
    }, {
        key: 'cleanAvailableResolutions',
        value: function cleanAvailableResolutions() {
            var avRes = Utils.arrayCopy(Anime.availableRes, true);
            for (var i = 0, len = avRes.length; i < len; i++) {
                var currentRes = avRes[i].res;
                if (Anime.getAmountOfEpsFromRes(currentRes, true) === 0) {
                    Anime.removeAvailableResolutions(currentRes);
                }
            }
        }
    }, {
        key: 'sortAllControls',
        value: function sortAllControls() {
            Utils.sortSelect(document.getElementById('animeSelection'));
            Utils.sortSelect(document.getElementById('downloadRes'));
            // @ts-ignore
            $('#info').sortTable(0);
        }
    }, {
        key: 'reBindSelectFilters',
        value: function reBindSelectFilters() {
            // @ts-ignore
            $('input[name=\'filterSelect\']').offOn('change', handleSelect);
            // @ts-ignore
            $('#clearResOptions').offOn('click', handleSelect);
            // @ts-ignore
            $("#animeSelection").offOn("click", function () {
                UI.autoEnableAcceptSelect();
            });
            // @ts-ignore
            $("#selectAllFromControl").offOn("click", function () {
                var allSelected = $("#animeSelection option:selected").length === $("#animeSelection option").length;
                if (allSelected) {
                    $(this).text("Select all");
                    $("#animeSelection option").prop("selected", false);
                } else {
                    $(this).text("deselect all");
                    $("#animeSelection option").prop("selected", true);
                }
                UI.autoEnableAcceptSelect();
            });
            function handleSelect(event) {
                var resTOFilter = $(event.target).data('set');
                $('#selectAnime').html(UI.buildSelect(resTOFilter));
                Utils.sortAllControls();
                var searchApplied = UI.getAppliedSearch();
                if (searchApplied !== '') {
                    UI.applySearch(searchApplied);
                }
                Utils.reBindSelectFilters();
            }
        }
    }, {
        key: 'getHumanReadableSize',
        value: function getHumanReadableSize(from) {
            var decimals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;

            var bits = 0;
            if (Array.isArray(from)) {
                for (var i = 0; i < from.length; i++) {
                    var ep = from[i];
                    bits += ep.size;
                }
            } else if (typeof from === 'number') {
                bits = from;
            } else {
                bits += from.size;
            }
            function formatBytes(bytes, decimals) {
                if (bytes == 0) {
                    return '0 Byte';
                }
                var k = 1024;
                var dm = decimals + 1 || 3;
                var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i];
            }
            return formatBytes(bits, decimals);
        }
    }]);

    return Utils;
}();

var UI = function () {
    function UI() {
        _classCallCheck(this, UI);
    }

    _createClass(UI, null, [{
        key: 'buildTable',
        value: function buildTable() {
            var html = '';
            html += '<table class="table table-bordered table-hover table-striped torrent-list" style=\'width: 100%\' id=\'info\'>';
            html += '<caption>Download infomation</caption>';
            html += '<thead>';
            html += '<tr>';
            html += '<th>resolution</th>';
            html += '<th>Episode count</th>';
            html += '<th>Average seeds</th>';
            html += '<th>Average leechers</th>';
            html += '<th>Total size</th>';
            html += '</tr>';
            html += '</thead>';
            html += '<tbody>';
            var allRes = Anime.availableRes;
            for (var i = 0; i < allRes.length; i++) {
                var currRes = allRes[i];
                var localRes = currRes.res;
                html += '<tr>';
                html += '<td>' + (localRes === -1 ? 'Others' : localRes + 'p') + '</td>';
                html += '<td>' + Anime.getAmountOfEpsFromRes(localRes, true) + '</td>';
                html += '<td>' + Anime.avgSeedsForRes(localRes, true) + '</td>';
                html += '<td>' + Anime.avgPeersForRes(localRes, true) + '</td>';
                html += '<td>' + Anime.getTotalSizeForRes(localRes, true) + ' (aprox)</td>';
                html += '</tr>';
            }
            html += '</tbody>';
            html += '</table>';
            return html;
        }
    }, {
        key: 'stateChangeAcceptSelect',
        value: function stateChangeAcceptSelect(state) {
            // @ts-ignore
            $("#acceptSelect").enableButton(state);
        }
    }, {
        key: 'autoEnableAcceptSelect',
        value: function autoEnableAcceptSelect() {
            var selection = $("#animeSelection option:selected");
            UI.stateChangeAcceptSelect(selection.length > 0);
        }
    }, {
        key: 'buildDropdownSelections',
        value: function buildDropdownSelections() {
            var html = '';
            html += '<select class="form-control" style="margin-right:5px;display: inline;width: auto;" id="downloadRes">';
            var allRes = Anime.availableRes;
            for (var i = 0; i < allRes.length; i++) {
                var currRes = allRes[i];
                var localRes = currRes.res;
                html += '<option value=' + localRes + '>' + (localRes === -1 ? 'Others' : localRes + 'p') + '</option>';
            }
            html += '</select>';
            return html;
        }
    }, {
        key: 'builDownloadAlert',
        value: function builDownloadAlert(type) {
            var amountOfAnime = 0;
            var selectedRes = Number.parseInt($('#downloadRes').val());
            var res = null;
            var totalSize = null;
            if (type === 'downloadSelected') {
                amountOfAnime = $('.checkboxes:checked').length;
                res = 'custom';
            } else if (type === 'downloadSelects') {
                amountOfAnime = $('#animeSelection option:selected').length;
                totalSize = Utils.getHumanReadableSize(function () {
                    var localSize = 0;
                    $('#animeSelection option:selected').each(function () {
                        var url = this.dataset.url;
                        localSize += Anime.getEpisodeFromAnchor(url).size;
                    });
                    return localSize;
                }());
                res = 'custom';
            } else {
                amountOfAnime = Anime.getAmountOfEpsFromRes(selectedRes, false);
                res = selectedRes === -1 ? 'Others' : selectedRes + 'p';
                totalSize = Anime.getTotalSizeForRes(parseInt(res), false);
            }
            var seedLimit = Localstore.getMinSeedsFromStore();
            if (seedLimit === "-1") {
                seedLimit = "none";
            }
            var html = '';
            html += '<div class=\'alert alert-success\'>';
            html += '<div><strong>Download: ' + res + '</strong></div> <br />';
            html += '<div><strong>Seed Limit: ' + seedLimit + '</strong></div>';
            if (totalSize !== null) {
                html += '<br /><div><strong>Total size: ' + totalSize + ' (aprox)</strong></div>';
            }
            html += '<p>You are about to download ' + amountOfAnime + ' ep(s)</p>';
            html += '<p>This will cause ' + amountOfAnime + ' download pop-up(s) Are you sure you want to continue?</p>';
            html += '<p>If there are a lot of eps, your browser might stop responding for a while. This is normal. If you are on Google Chrome, it will ask you to allow multiple-downloads</p>';
            html += '<button type="button" class="btn btn-warning downloadButton" id=\'alertButtonCancel\'>Cancel</button>';
            html += '<button type="button" class="btn btn-success downloadButton" id=\'alertButton\'>Okay</button>';
            html += '<button type="button" class="btn btn-success downloadButton" id=\'downloadZip\'>Download as zip</button>';
            html += "<div class='hidden' id='loadingContainer'>";
            html += "<hr />";
            html += "<div class=\"progress\">";
            html += "<div id='progressBarForZip' class=\"progress-bar progress-bar-striped active\" role=\"progressbar\" aria-valuenow=\"45\" aria-valuemin=\"0\" aria-valuemax=\"100\" style='width: 100%;'>Current status: <span id='progressStatus'></span></div>";
            html += "</div>";
            html += "</div>";
            html += '</div>';
            return html;
        }
    }, {
        key: 'showAjaxErrorAlert',
        value: function showAjaxErrorAlert(ajaxInfo) {
            var parseError = $('#parseErrors');
            if (!parseError.is(':hidden')) {
                return null;
            }
            parseError.html('');
            var html = '';
            html += '<div class=\'alert alert-danger\'>';
            html += '<p>There was an error in getting the information from page: \'' + ajaxInfo.error.pageAtError + '\'</p>';
            html += '<p>The last successful page parsed was page number ' + (ajaxInfo.currentPage === null ? 1 : ajaxInfo.currentPage) + ' </p>';
            html += '<button id=\'errorClose\' class="btn btn-primary"> close </button>';
            html += '</div>';
            parseError.show();
            parseError.html(html);
            $('#errorClose').off('click').on('click', function () {
                $('#parseErrors').slideUp('slow', function () {
                    $(this).html('');
                });
            });
        }
    }, {
        key: 'buildSelect',
        value: function buildSelect() {
            var resTOFilter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "none";

            var html = '';
            UI.epsInSelect = [];
            var minSeeds = Number.parseInt(Localstore.getMinSeedsFromStore());
            html += '<div id=\'selectWrapper\'>';
            html += '<div id=\'selectContainer\'>';
            html += '<p>Or you can select episodes here:</p>';
            html += '<p>Seed limit: ' + (minSeeds === -1 ? 'None' : minSeeds) + '</p>';
            html += '<select class="form-control" id=\'animeSelection\' multiple size=\'20\'>';
            var allRes = Anime.availableRes;
            for (var i = 0; i < allRes.length; i++) {
                var currRes = allRes[i];
                var localRes = currRes.res;
                var eps = Anime.getEpsForRes(localRes, false);
                for (var j = 0, len = eps.length; j < len; j++) {
                    var currentEp = eps[j];
                    if (resTOFilter == 'none' || currentEp.res == resTOFilter) {
                        html += '<option data-url=\'' + currentEp.downloadLink + '\'>';
                        html += currentEp.title + ' - Seeders: ' + currentEp.seeds;
                        UI.epsInSelect.push(currentEp);
                        html += '</option>';
                    } else {
                        break;
                    }
                }
            }
            html += '</select>';
            html += '<span>Filter select control: </span>';
            var checked = false;
            for (var _i4 = 0; _i4 < allRes.length; _i4++) {
                if (resTOFilter == allRes[_i4].res) {
                    checked = true;
                }
                html += '<input type=\'radio\' ' + (checked ? 'checked' : '') + ' data-set= \'' + allRes[_i4].res + '\' name=\'filterSelect\'/>' + '<label class="filterLabel">' + (allRes[_i4].res === -1 ? 'Others' : allRes[_i4].res + 'p') + '</label>';
                checked = false;
            }
            html += '<a id=\'clearResOptions\' data-set=\'none\' >clear resolution filter</a>';
            html += '<a id=\'selectAllFromControl\'>Select all</a>';
            html += '</div>';
            html += '</div>';
            // @ts-ignore
            $("#acceptSelect").enableButton(false);
            return html;
        }
    }, {
        key: 'applySearch',
        value: function applySearch(textToFilter) {
            var opts = UI.epsInSelect;
            var rxp = new RegExp(textToFilter);
            var optlist = $('#animeSelection').empty();
            for (var i = 0, len = opts.length; i < len; i++) {
                var ep = opts[i];
                if (rxp.test(ep.title)) {
                    optlist.append('<option data-url=\'' + ep.downloadLink + '\'>' + ep.title + ' - Seeders: ' + ep.seeds + '</option>');
                }
            }
            UI.searchApplied = textToFilter;
            Utils.sortSelect(document.getElementById("animeSelection"));
            UI.autoEnableAcceptSelect();
        }
    }, {
        key: 'getAppliedSearch',
        value: function getAppliedSearch() {
            return UI.searchApplied;
        }
    }, {
        key: 'getEpsInSelect',
        value: function getEpsInSelect() {
            return UI.epsInSelect;
        }
    }]);

    return UI;
}();

UI.epsInSelect = [];
UI.searchApplied = '';

var DataParser = function () {
    function DataParser(table) {
        _classCallCheck(this, DataParser);

        this.table = null;
        this.table = table;
    }

    _createClass(DataParser, [{
        key: 'parseTable',
        value: function parseTable(currentPage) {
            var trRow = this.table.find('img[src*=\'/static/img/icons/nyaa/1_2.png\']').closest('tr');
            var eps = [];
            $.each($(trRow), function () {
                var resInfo = parseRes(this);
                if (resInfo === null) {
                    Anime.addAvailableResolutions(-1, null);
                } else {
                    Anime.addAvailableResolutions(resInfo.res, resInfo.fullRes);
                }
                var info = getEpisodeInfo(this);
                eps.push(new Episode(typeof resInfo.res === 'undefined' ? -1 : resInfo.res, info.currentDownloadLink, info.seeds, info.leech, info.uid, currentPage, info.title, info.size));
            });
            return eps;
            function parseRes(eventContent) {
                var supportedRes = Anime.supportedRes;
                for (var i = 0; i < supportedRes.length; i++) {
                    var currRes = supportedRes[i].res;
                    var currFullRes = supportedRes[i].fullRes;
                    if ($(eventContent).children('td:nth-child(2)').text().indexOf(currRes + 'p') > -1 || $(eventContent).children('td:nth-child(2)').text().indexOf(currFullRes) > -1) {
                        return supportedRes[i];
                    }
                }
            }
            function getEpisodeInfo(eventContent) {
                var _eventContent = $(eventContent);
                var currentDownloadLink = Anime.getTdFromTable(_eventContent, 3).find("a")[0].href;
                function getTextContent(idx) {
                    return isNaN(parseInt(Anime.getTdFromTable(_eventContent, idx).text())) ? 0 : parseInt(Anime.getTdFromTable(_eventContent, idx).text());
                }
                function convertToString(ev) {
                    var sizeValue = Anime.getTdFromTable(ev, 4).text();
                    var sizeText = $.trim(sizeValue.split(' ').pop());
                    var intValue = parseInt(sizeValue);
                    switch (sizeText) {
                        case "MiB":
                            return Math.pow(2, 20) * intValue;
                        case "GiB":
                            return intValue * 1073741824;
                        default:
                            return 0;
                    }
                }
                var seeds = getTextContent(6);
                var leech = getTextContent(7);
                var title = Anime.getTdFromTable(_eventContent, 2).text().trim().substring(1).trim();
                var uid = Anime.getUidFromJqueryObject(_eventContent);
                var size = convertToString(_eventContent);
                return {
                    'currentDownloadLink': currentDownloadLink,
                    'seeds': seeds,
                    'leech': leech,
                    'title': title,
                    'uid': uid,
                    "size": size
                };
            }
        }
    }]);

    return DataParser;
}();

var Localstore = function () {
    function Localstore() {
        _classCallCheck(this, Localstore);
    }

    _createClass(Localstore, null, [{
        key: 'getMinSeedsFromStore',
        value: function getMinSeedsFromStore() {
            var lo = localStorage.getItem('minSeeds');
            return lo === null ? "-1" : lo;
        }
    }, {
        key: 'setMinSeedsFromStore',
        value: function setMinSeedsFromStore(seeds) {
            localStorage.setItem('minSeeds', seeds.toString());
        }
    }, {
        key: 'removeMinSeedsFromStore',
        value: function removeMinSeedsFromStore() {
            localStorage.removeItem('minSeeds');
        }
    }]);

    return Localstore;
}();

var QueryString = function () {
    if (typeof window == "undefined") {
        return {};
    }
    var query_string = {};
    var query = window.location.search.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (typeof query_string[pair[0]] === "undefined") {
            query_string[pair[0]] = pair[1];
        } else if (typeof query_string[pair[0]] === "string") {
            query_string[pair[0]] = [query_string[pair[0]], pair[1]];
        } else {
            query_string[pair[0]].push(pair[1]);
        }
    }
    return query_string;
}();
// Download fix for firefox
HTMLElement.prototype.click = function () {
    var evt = this.ownerDocument.createEvent('MouseEvents');
    evt.initMouseEvent('click', true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
    this.dispatchEvent(evt);
};

var Main = function () {
    function Main() {
        _classCallCheck(this, Main);
    }

    _createClass(Main, null, [{
        key: 'main',
        value: function main() {
            Main.setAnimeObj();
            Main.buildUi();
            Main.bindListeners();
        }
    }, {
        key: 'setAnimeObj',
        value: function setAnimeObj() {
            if (QueryString.q !== '') {
                Anime.currentAnime = decodeURIComponent(QueryString.q).split('+').join(' ');
            } else {
                Anime.currentAnime = "unknown";
            }
            var paths = window.location.pathname.split("/");
            Anime.currentSubber = paths[2];
            var instance = new DataParser(Utils.getTable());
            var eps = instance.parseTable(Utils.getCurrentPageOffset());
            Anime.addAllEps(eps);
        }
    }, {
        key: 'buildUi',
        value: function buildUi() {
            makeStyles();
            buildPanel();
            afterBuild();
            function makeStyles() {
                var styles = '';
                styles += '.collapsem{cursor: pointer; position: absolute; right: 4px; top: 2px;}';
                styles += '.panel-success > .panel-heading {position: relative;}';
                styles += '.avgSeeds{floar:left; padding-right:10px; color:#3c763d;}';
                styles += '.checkboxes{left:1px; margin:0; padding:0; position: relative; top: 1px; z-index: 1;}';
                styles += '#topbar{z-index: 2 !important;}';
                styles += 'label[for=\'MinSeeds\']{ display: block; margin-top: 10px;}';
                styles += '.filterLabel{margin-right: 10px;}';
                styles += '.alert {position:relative;}';
                styles += '#alertUser, #parseErrors{margin-top: 15px;}';
                styles += ".downloadButton {position:absolute; bottom:5px;}";
                styles += '#alertButton{right:78px;}';
                styles += '#alertButtonCancel{right: 5px;}';
                styles += '#downloadZip{right: 140px;}';
                styles += '#errorClose{position:absolute; bottom:5px; right: 11px;}';
                styles += '#animeSelection{width: 100%;}';
                styles += '#clearResOptions{margin-left: 10px; margin-right: 10px ;cursor: pointer;}';
                styles += '#selectAllFromControl{cursor: pointer;}';
                styles += '#downloadCustomButton{float:right;}';
                styles += '#findEp{float: right; position: relative; bottom: 20px; width: 180px;}';
                styles += "#loadingContainer{margin-top: 15px; margin-bottom: 45px;}";
                Utils.injectCss('https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css');
                Utils.injectCss(styles);
            }
            function buildPanel() {
                var html = '';
                html += '<div class="panel panel-success">';
                html += '<div class="panel-heading">';
                html += '<h3 id="panel-title" class="panel-title"></h3>';
                html += '<i class="fa fa-minus collapsem" id="collapseToggle" title="Hide"></i>';
                html += '</div>';
                html += '<div class="panel-body" id="pannelContent"></div>';
                html += '</div>';
                $('.container > .row > h3').after(html);
                buildPanelContent();
                function buildPanelContent() {
                    var html = '';
                    html += '<div>';
                    $('#panel-title').html('<span> Download "' + Anime.currentAnime + ' (' + Anime.currentSubber + ')"</span>');
                    if (Anime.getAmountOfEps() === 0) {
                        html += '<span> No translated anime found or error occured</span>';
                        html += '</div>';
                        $('#pannelContent').html(html);
                        return;
                    }
                    html += '<span>Pick a resolution: </span>';
                    html += '<span id=\'selectDownload\'>';
                    html += UI.buildDropdownSelections();
                    html += '</span>';
                    html += '<button class="btn btn-default" type="button" data-type=\'downloadAll\' id="downloadAll">Download all</button>';
                    html += '<button class="btn btn-default" type=\'button\' id=\'downloadCustomButton\' data-type=\'downloadSelected\' >download your selected items</button>';
                    html += '</div>';
                    html += '<div id=\'options\'>';
                    html += '<div class="checkbox">';
                    html += "<label>";
                    html += '<input type=\'checkbox\' id=\'crossPage\' /> ';
                    html += "include Cross pages";
                    html += "</label>";
                    html += "</div>";
                    html += '<div class="input-group">';
                    html += '<input placeholder="Minimum seeders" class="form-control" type=\'number\' min=\'0\' id=\'MinSeeds\' title=\'Any episode that is below this limit will be excluded from the download.\'/>';
                    html += '<span class="input-group-btn">';
                    html += '<button class="btn btn-default" type=\'button\' id=\'SaveMinSeeds\'>Save</button>';
                    html += "</span>";
                    html += "</div>";
                    html += '<div id=\'tableInfo\'>';
                    html += UI.buildTable();
                    html += '</div>';
                    html += '<div id=\'alertUser\' class=\'hide\'></div>';
                    html += '<div class=\'selectAnime\' id=\'selectAnime\'>';
                    html += UI.buildSelect();
                    html += '</div>';
                    html += '<input class="form-control" type=\'text\' id=\'findEp\' placeholder=\'Search Select (or use regex) \' />';
                    html += '<button class="btn btn-default" disabled id=\'acceptSelect\' data-type=\'downloadSelects\'>Select for download</button>';
                    html += '<div id=\'parseErrors\' class =\'hide\'></div>';
                    $('#pannelContent').html(html);
                }
            }
            function afterBuild() {
                makeCheckBoxes();
                sortLists();
                function makeCheckBoxes() {
                    $('.tlistdownload > a').after('<input class=\'checkboxes\' type=\'checkbox\'/>');
                }
                function sortLists() {
                    Utils.sortAllControls();
                }
            }
        }
    }, {
        key: 'bindListeners',
        value: function bindListeners() {
            Utils.reBindSelectFilters();
            $('#downloadAll').on('click', function (e) {
                Utils.doDownloads(e);
            });
            $('.checkboxes').on('click', function (e) {
                if (Utils.checkBoxValid($('.checkboxes'))) {
                    Utils.enableButton($('#downloadCustomButton'));
                } else {
                    Utils.disableButton($('#downloadCustomButton'));
                }
            });
            $('#crossPage').on('click', function (e) {
                preformParsing(Anime.getPageUrls());
                function preformParsing(urls) {
                    if (urls.length === 0) {
                        return;
                    }
                    if (Utils.checkBoxValid($('#crossPage'))) {
                        (function () {
                            $('#tableInfo').html('<p>Please wait while we parse each page...</p>');
                            $('#selectAnime').html('');
                            $('#acceptSelect').hide();
                            $('#crossPage, #downloadAll').prop('disabled', true);
                            $('#parseErrors').slideUp('fast', function () {
                                $(this).html('');
                            });
                            var AjaxInfo = {
                                error: {
                                    pageAtError: null
                                },
                                currentPage: null
                            };
                            var ajaxPromiseMap = new Map();
                            var arrayd = [];
                            for (var i = 0; i < urls.length; i++) {
                                var d = $.Deferred();
                                ajaxPromiseMap.set(urls[i], d);
                                arrayd.push(d);
                            }
                            $.when.apply($, arrayd).done(function () {
                                if (AjaxInfo.error.pageAtError !== null) {
                                    UI.showAjaxErrorAlert(AjaxInfo);
                                }
                                $('#tableInfo').html(UI.buildTable());
                                $('#downloadRes').html(UI.buildDropdownSelections());
                                $('#selectAnime').html(UI.buildSelect());
                                Utils.sortAllControls();
                                $('#acceptSelect').show();
                                Utils.reBindSelectFilters();
                                $('#crossPage, #downloadAll').prop('disabled', false);
                            });
                            var timeOut = 0;

                            var _loop2 = function _loop2(cur, deferr) {
                                var currentPage = 0;
                                var queryObjet = Utils.getQueryFromUrl(cur);
                                if (queryObjet.p) {
                                    currentPage = queryObjet.p;
                                } else {
                                    currentPage = 1;
                                }
                                setTimeout(function () {
                                    var ajax = $.ajax(cur);
                                    ajax.done(function (data) {
                                        AjaxInfo.currentPage = currentPage;
                                        var table = $(data).find("table");
                                        var parser = new DataParser(table);
                                        $('#tableInfo').append('<div>Page ' + currentPage + ' Done </div>');
                                        Anime.addAllEps(parser.parseTable(currentPage));
                                    }).fail(function () {
                                        AjaxInfo.error.pageAtError = Number.parseInt(cur.split('=').pop());
                                        $('#tableInfo').append('<div>Page ' + currentPage + ' Done </div>');
                                    }).always(function () {
                                        deferr.resolve();
                                    });
                                }, timeOut);
                                timeOut += 300;
                            };

                            var _iteratorNormalCompletion2 = true;
                            var _didIteratorError2 = false;
                            var _iteratorError2 = undefined;

                            try {
                                for (var _iterator2 = ajaxPromiseMap[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                                    var _ref3 = _step2.value;

                                    var _ref4 = _slicedToArray(_ref3, 2);

                                    var cur = _ref4[0];
                                    var deferr = _ref4[1];

                                    _loop2(cur, deferr);
                                }
                            } catch (err) {
                                _didIteratorError2 = true;
                                _iteratorError2 = err;
                            } finally {
                                try {
                                    if (!_iteratorNormalCompletion2 && _iterator2.return) {
                                        _iterator2.return();
                                    }
                                } finally {
                                    if (_didIteratorError2) {
                                        throw _iteratorError2;
                                    }
                                }
                            }
                        })();
                    } else {
                        // noinspection JSMismatchedCollectionQueryUpdate
                        var tableInfoElm = $('#tableInfo');
                        tableInfoElm.html('<p>Please wait while we re-calculate the Episodes</p>');
                        var _currentPage = Utils.getCurrentPageOffset();
                        Anime.removeEpisodesFromResidence(_currentPage, true);
                        Utils.cleanAvailableResolutions();
                        $('#downloadRes').html(UI.buildDropdownSelections());
                        tableInfoElm.html(UI.buildTable());
                        $('#selectAnime').html(UI.buildSelect());
                        Utils.reBindSelectFilters();
                        Utils.sortAllControls();
                    }
                }
            });
            $('#SaveMinSeeds').on('click', function (e) {
                if (parseInt($('#MinSeeds').val()) < 0) {
                    alert('number cannot be negative');
                    return;
                }
                var value = void 0;
                value = $('#MinSeeds').val() === '' ? parseInt("-1") : parseInt($('#MinSeeds').val());
                Localstore.setMinSeedsFromStore(value);
                if (value === -1) {
                    alert('Minimum seeds have been cleared');
                } else {
                    alert('Minimum seeds now set to: ' + value);
                }
                $('#selectAnime').html(UI.buildSelect());
                Utils.sortAllControls();
                Utils.reBindSelectFilters();
            });
            $('#collapseToggle').on('click', function () {
                $('#pannelContent').stop(true, true).slideToggle('slow', function () {
                    var elm = $('#collapseToggle');
                    if ($(this).is(':hidden')) {
                        elm.removeClass('fa-minus').addClass('fa-plus');
                        elm.attr('title', 'Show');
                    } else {
                        elm.addClass('fa-minus').removeClass('fa-plus');
                        elm.attr('title', 'Hide');
                    }
                });
            });
            $('#acceptSelect').on('click', function (e) {
                Utils.doDownloads(e);
            });
            $('#findEp').on('keyup', function () {
                UI.applySearch($(this).val());
            });
        }
    }]);

    return Main;
}();

Main.main();